Resources Numerical accuracy Calculation policy

Sizes for new curves and meshes, remesh grid

Chooses a round starting size for a new curve or mesh from what the view shows, and checks that a voxel remesh is not too fine.

How the precision is kept

The starting size is a fifth of the view's height rounded to 1, 2 or 5 times a power of ten. The remesh takes the voxel size the person types and counts the grid cells it would need before starting. All 64-bit.

Calculations under this policy

4 calculations in 2 source files. The code is read from the released app when this page loads. It is the calculation people run, not a copy kept for this page.

ws-curve.js ModelCraft 0.8.1 64-bit 2 calculations Reviewed

Tests None found by the review

Round starting size

64-bit

Rounds a size to 1, 2, 5 or 10 times a power of ten.

p = 10^⌊log₁₀ x⌋; m = x ÷ p; result = (1 if m < 1.5, 2 if m < 3.5, 5 if m < 7.5, else 10) × p

If Math.log10 returns a value just under a whole number for an exact power of ten, m comes out as 10 and the result is still that power of ten. A size that is not above zero returns 1.

  • ws-curve.js
  • lines 148 to 152
  • ModelCraft 0.8.1
function niceSize(x) {
  if (!(x > 0)) return 1;
  const p = 10 ** Math.floor(Math.log10(x)), m = x / p;
  return (m < 1.5 ? 1 : m < 3.5 ? 2 : m < 7.5 ? 5 : 10) * p;
}

Text as a curve object

64-bit

The letters' outlines as closed flat splines, filled and given a thickness.

extrude = height × 0.05; each outline point [x, y] becomes a spline point [x, y, 0]

The outline points are taken from the library's toPolygons as they come, without rounding (the 3D text tool rounds the same outlines to 1e-6). The thickness is a fixed twentieth of the height.

  • ws-curve.js
  • lines 153 to 163
  • ModelCraft 0.8.1
/** Text as a curve object: the letters' outlines (our single-stroke font given a pen width) as closed flat splines, filled. */
export async function textCurve(host, str, height, weight = 0.14) {
  let M, T;
  try { host.print(host.t('app.text3d.loading')); [T, M] = await Promise.all([import('./text3d.js'), import('./solids.js').then((L) => L.manifold())]); }
  catch (e) { host.print(host.t('app.curve.text.failed')); return null; }
  const cs = T.textOutlines(M, str, { height, weight }), loops = cs.toPolygons();
  cs.delete?.();
  if (!loops.length) { host.print(host.t('app.curve.text.empty')); return null; }
  const splines = loops.map((L) => ({ type: 'poly', cyclic: true, resolution: 1, order: 4, points: L.map(([x, y]) => ({ p: [x, y, 0], hl: [x, y, 0], hr: [x, y, 0], h: 'vector', w: 1, radius: 1, tilt: 0 })) }));
  return { dim: '2d', fill: true, extrude: height * 0.05, bevel: { depth: 0, resolution: 2 }, caps: true, taper: [1], splines, text: { string: str, height, weight } };
}
ws-mesh.js ModelCraft 0.8.1 64-bit 2 calculations Reviewed

Tests None found by the review

Voxel remesh grid size

64-bit

Suggests a voxel size and counts the grid cells the typed size would need.

suggested = niceSize(largest extent ÷ 40); cells = Π (⌈(extentₖ + 4·voxel) ÷ (voxel ÷ 2)⌉ + 1)

The voxel size is the typed length as parsed by the units module. The count is a product of three whole numbers and is compared with 24 000 000; above that the remesh is refused and a coarser size is suggested.

  • ws-mesh.js
  • lines 174 to 180
  • ModelCraft 0.8.1
const size = Math.max(b.max[0] - b.min[0], b.max[1] - b.min[1], b.max[2] - b.min[2]);
const txt = await host.askText(t('app.mesh.remesh.h'), t('app.mesh.remesh.size'), formatLength(niceSize(size / 40) / 1, host.units));
if (!txt) return;
const voxel = parseLength(txt, host.units);
if (!(voxel > 0)) { host.print(t('app.mesh.remesh.bad')); return; }
const cells = [0, 1, 2].reduce((n, k) => n * (Math.ceil((b.max[k] - b.min[k] + 4 * voxel) / (voxel / 2)) + 1), 1);
if (cells > 24e6) { host.print(t('app.mesh.remesh.too_fine', { size: formatLength(size / 140, host.units) })); return; }

Size of a new mesh shape

64-bit

The size given to a new plane, cube, cylinder, sphere or torus.

s = niceSize(view height ÷ 5); radius = s ÷ 2; torus tube = s ÷ 8

Halving and dividing by 8 are exact in binary, so the radii are exact fractions of the rounded size.

  • ws-mesh.js
  • lines 287 to 292
  • ModelCraft 0.8.1
function addPrimitive(host, p) {
  const s = niceSize(host.cam.height ? host.cam.height / 5 : 1);
  const opts = { plane: { size: s }, grid: { size: s, x: 10 }, cube: { size: s }, circle: { r: s / 2 }, cylinder: { r: s / 2, h: s }, cone: { r: s / 2, h: s }, uvsphere: { r: s / 2 }, icosphere: { r: s / 2 }, torus: { R: s / 2, r: s / 8 } }[p];
  const path = host.placeMesh(makePrimitive(p, opts), host.t('app.mesh.prim.' + p));
  if (path) start(host, path);
}

In detail

Method
1-2-5 rounding by the base-10 logarithm; cell count as a product of rounded-up extents.
Number types
64-bit numbers.
Tolerances
A remesh needing more than 24 000 000 cells is refused.
Limits
Powers of ten below 1 are the nearest 64-bit numbers, so a size such as 0.002 is not exact in binary.

Approach: 64-bit, direct

Every value is a 64-bit number from input to result. Formulas are closed form or a direct solve, and every comparison uses a stated tolerance.

Every policy that takes this approach