Resources Numerical accuracy Calculation policy

Edits and settings from the main window

Places the mirror plane for a flipped copy, turns a drawn curve into a curve object, bakes node meshes into the model, guesses storey heights, reads the sun settings, and hands a room to add-ons.

How the precision is kept

These are short 64-bit calculations in the app's main file. Several round or truncate on purpose before a value is stored or handed on: storey heights to 0.01, a room's height to a whole millimeter, the sun's month and day to whole numbers, the sketch scale to a whole number.

Calculations under this policy

7 calculations in 1 source file. 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.

modelcraft.js ModelCraft 0.8.1 64-bit 7 calculations Reviewed

Tests None found by the review

Mirror plane for a flipped copy

64-bit

Puts the mirror plane on the far side of the selection's box so the copy lands beside the original.

far = max over the box's 8 corners of (corner − c)·a; plane point = c + a × far

c is the selection's center and a the chosen axis of the open group. The largest projection of the eight box corners is taken, starting from 0.

  • modelcraft.js
  • lines 484 to 487
  • ModelCraft 0.8.1
const bb = tools.selBox(), a = tools.axes()[axis];
let far = 0;
for (const x of [bb.min[0], bb.max[0]]) for (const y of [bb.min[1], bb.max[1]]) for (const z of [bb.min[2], bb.max[2]]) far = Math.max(far, (x - c[0]) * a[0] + (y - c[1]) * a[1] + (z - c[2]) * a[2]);
at = [c[0] + a[0] * far, c[1] + a[1] * far, c[2] + a[2] * far];

Curve object from a drawn curve

64-bit

Makes a curve object from a drawn arc, circle, polygon or curve, flat when all its points share a height.

closed if |first − last| < 10⁻⁹; flat if every |z − z₀| < 10⁻⁹, then z ← 0 and the placement carries z₀; bevel depth = max(10⁻⁴, length ÷ 100)

Both tolerances are absolute in model units. In a flat curve every point's height is replaced by the first point's, so points up to 1e-9 off that height are moved onto it.

  • modelcraft.js
  • lines 1329 to 1339
  • ModelCraft 0.8.1
if (closed && !sp && pts.length > 2 && dist(pts[0], pts[pts.length - 1]) < 1e-9) pts = pts.slice(0, -1);
// flat on the ground plane: a flat curve, its plane at z = 0 of its own placement
const z0 = pts[0][2], flat = pts.every((p) => Math.abs(p[2] - z0) < 1e-9);
if (flat) pts = pts.map((p) => [p[0], p[1], 0]);
const spline = { type: ci.kind === 'polygon' ? 'poly' : 'bezier', cyclic: closed, resolution: 12, order: 4, points: pts.map((p) => ({ p, hl: p.slice(), hr: p.slice(), h: 'auto', w: 1, radius: 1, tilt: 0 })) };
const curve = { dim: flat ? '2d' : '3d', fill: false, extrude: 0, bevel: { depth: Math.max(1e-4, ci.length / 100), resolution: 4 }, caps: true, taper: [1], splines: [spline] };
const name = t('app.curve.kind.' + (closed && ci.round ? 'bezier-circle' : 'bezier'));
model.change(t('app.curve.undo.add'), (m) => {
  const d = m.newMeshDef(new Mesh(), name);
  d.curve = curve;
  m.place(m.context(host.path).def, d.id, [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, flat ? z0 : 0, 1]);

Node meshes baked into the model

64-bit

Converts each baked mesh from millimeters to the model's meters.

x_m = x_mm ÷ 1000

Each coordinate is divided by 1000 once; the quotient is the nearest 64-bit number, so a whole number of millimeters does not in general give an exact binary value in meters.

  • modelcraft.js
  • lines 2828 to 2829
  • ModelCraft 0.8.1
const P = me.positions, polys = [];
for (let i = 0; i + 2 < me.indices.length; i += 3) polys.push([0, 1, 2].map((k) => { const v = me.indices[i + k] * 3; return [P[v] / 1000, P[v + 1] / 1000, P[v + 2] / 1000]; }));

Guessing storey heights

64-bit

Proposes a storey for each different height the top-level groups and components stand on.

height = round(box min z × 100) ÷ 100

The heights are rounded to 0.01 model units so placements within that distance share a storey. The rounded value is what goes into the storey list if the person accepts it.

  • modelcraft.js
  • lines 3710 to 3711
  • ModelCraft 0.8.1
const zs = new Set();
for (const inst of model.root.insts.values()) { const d = model.def(inst.def), bb = d && model.bbox(d, inst.m); if (bb) zs.add(Math.round(bb.min[2] * 100) / 100); }

Sun settings from the panel

64-bit

Reads month, day, hour, latitude, longitude and time zone from the Style panel into the model's sun.

value = clamp(parseFloat(text), lo, hi); month, day = value | 0

Month and day are truncated toward zero with the bitwise | 0, so a typed 6.9 becomes 6; the clamp to 1–12 and 1–31 before it keeps the value far inside the 32-bit range. Hour, latitude, longitude and time zone keep the parsed 64-bit value.

  • modelcraft.js
  • lines 1880 to 1893
  • ModelCraft 0.8.1
function uiToStyle() {
  const n = (x, lo, hi, d) => { const v = parseFloat(x); return Number.isFinite(v) ? Math.min(hi, Math.max(lo, v)) : d; };
  const st = view.style;
  view.style = {
    mode: ui.mode.value, edges: ui.edges.checked, shadows: ui.shadows.checked,
    sun: {
      month: n(ui.sunMonth.value, 1, 12, st.sun.month) | 0, day: n(ui.sunDay.value, 1, 31, st.sun.day) | 0, hour: n(ui.sunHour.value, 0, 23.99, st.sun.hour),
      lat: n(ui.sunLat.value, -90, 90, st.sun.lat), lon: n(ui.sunLon.value, -180, 180, st.sun.lon), tz: n(ui.sunTz.value, -14, 14, st.sun.tz),
    },
  };
  ui.sun.disabled = !view.style.shadows;
  sunText();
  view.invalidate();
}

Room handed to add-ons

64-bit

The room made by the selected floor face, as add-ons receive it: outline in millimeters, height and area.

z = round(room height × 1000); area (mm²) = area (m²) × 10⁶

The height is rounded to a whole millimeter before it is handed on. The area was divided by 10⁶ in roomOfFace and is multiplied by 10⁶ here, two roundings that can change its last digit.

  • modelcraft.js
  • lines 3282 to 3288
  • ModelCraft 0.8.1
async rooms() {
  const s = host.sel.length === 1 && host.sel[0].t === 'f' ? host.sel[0] : null;
  const F = await import('./furnish-mc.js');
  const room = s && F.roomOfFace(model, host.path, s.id);
  if (!room) return [];
  const open = F.openingsNear(model, room);
  return [{ name: fileName, poly: room.poly, holes: room.holes, z: Math.round(room.z * 1000), area: room.area * 1e6, doors: open.doors, windows: open.windows }];

Scale of a photographed sketch

64-bit

Reads the 1 : n scale the person types for a sketch photo.

n = round(Number(text)); accepted if 1 ≤ n ≤ 100 000

The typed scale is rounded to a whole number before it is used, so 1 : 2.5 is read as 1 : 3 (Math.round rounds halves up).

  • modelcraft.js
  • lines 3309 to 3310
  • ModelCraft 0.8.1
const n = Math.round(Number(scale.value));
if (!(n >= 1 && n <= 100000)) { print(t('mc.sketch.bad_scale')); return; }

In detail

Method
Direct expressions; unit changes by × 1000 or ÷ 1000.
Number types
64-bit numbers; a bitwise | 0 on the sun's month and day.
Tolerances
Curve object: end points closer than 1e-9 count as closed, and heights within 1e-9 of the first point count as flat (absolute, model units).

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