Turns a click in the 3D tab into a point or a height in drawing units, and lays the 2D drawing on the ground as edges the 3D snaps use.
How the precision is kept
The 3D model works in meters and the drawing in its own units; every value is multiplied by k (meters per drawing unit) on the way in and divided by k on the way out, in 64-bit numbers. The ray and plane work is done in m3d/infer.js. Arcs of the 2D drawing are handed to the 3D snaps as chords of at most 7.5 degrees, so a snap on an arc lands on a chord.
Calculations under this policy
5 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.
Where the ray under the pointer meets the plane at height z.
plane: n = (0, 0, 1), d = z × k; point = ray hit ÷ k
The hit comes from rayPlane in m3d/infer.js in meters and x and y are divided by k. The returned z is the z that was asked for, not the computed one, so it is exact.
ws3d-core.js
lines 265 to 270
CadCraft 0.8.2
/** Where the cursor's ray meets the level plane z (drawing units). */
groundAt(ev, z = 0) {
const k = this.k, ray = this.cam.ray(ev.x, ev.y);
const g = rayPlane(ray.o, ray.d, { n: [0, 0, 1], d: z * k });
return g && g.t > 0 ? { x: g.p[0] / k, y: g.p[1] / k, z } : null;
}
Height following the cursor
64-bit
A height above a base outline, taken from where the pointer's ray passes nearest the vertical line through the outline's middle.
c = Σ ring points ÷ n; height = s ÷ k, with s the distance along the vertical line from (cₓ·k, cᵧ·k, z₀·k)
The middle is the plain mean of the first ring's corners, summed in order and divided once. The value given to the command is s ÷ k unrounded; only the tip beside the cursor is rounded.
ws3d-core.js
lines 271 to 280
CadCraft 0.8.2
/** A height along Z from a base (the first ring's middle at z0) following the cursor (drawing units). */
heightAt(ev, spec) {
const k = this.k;
const ring = spec?.rings?.[0];
let cx = 0, cy = 0;
if (ring?.length) { for (const [x, y] of ring) { cx += x; cy += y; } cx /= ring.length; cy /= ring.length; }
else if (spec?.base) { cx = spec.base.x; cy = spec.base.y; }
const a = alongLine(this.cam, ev.x, ev.y, [cx * k, cy * k, (spec?.z0 || 0) * k], [0, 0, 1]);
return a ? a.s / k : null;
}
2D drawing as snap edges
64-bit
The visible lines and arcs of the 2D drawing as edges on the ground, in meters, for the 3D snaps.
Line ends are multiplied by k once. An arc is cut into chords of at most 7.5 degrees, so an endpoint or midpoint snap on an arc in the 3D view lands on a chord vertex or chord middle. Only the first 50,000 pieces are kept.
ws3d-core.js
lines 118 to 135
CadCraft 0.8.2
/** The model space's visible 2D lines as scene edges (meters, z = 0, soft; arcs in short pieces), at most MAX_SNAP_2D. */
lines2d() {
const k = this.k, out = [];
let n = 0;
const seg = (x1, y1, x2, y2) => { if (n++ < MAX_SNAP_2D) out.push({ path: [], def: null, id: '2d' + n, a: [x1 * k, y1 * k, 0], b: [x2 * k, y2 * k, 0], soft: true, flat2d: true }); };
walk(this.doc, this.doc.model.values(), {
stroke: (c, ps) => {
for (const q of ps) {
if (q.k === 'L') { seg(q.x1, q.y1, q.x2, q.y2); continue; }
let sw = q.a1 - q.a0; while (sw <= 0) sw += 2 * Math.PI;
const m = Math.max(2, Math.ceil(sw / (Math.PI / 24)));
for (let i = 0; i < m; i++) { const a = q.a0 + sw * i / m, b = q.a0 + sw * (i + 1) / m; seg(q.cx + q.r * Math.cos(a), q.cy + q.r * Math.sin(a), q.cx + q.r * Math.cos(b), q.cy + q.r * Math.sin(b)); }
}
},
fill() {}, text() {}, point: (c, x, y) => seg(x, y, x, y),
});
return out;
}
Height shown beside the cursor
64-bit
The text of the height tip while a height is being picked.
|n| < 1e-9 → 0; else n to 4 decimals, trailing zeros removed
toFixed(4) is applied to the label only. The height passed on to the command is the unrounded 64-bit number.
Ray from the camera through the pointer met with a level plane, or the nearest point on a vertical line through the base.
Number types
64-bit numbers; points are arrays [x, y, z] in meters or {x, y, z} in drawing units.
Tolerances
A plane hit is used only in front of the camera (t > 0). The height shown beside the cursor prints 0 below 1e-9 and otherwise 4 decimals.
Limits
At most 50,000 line pieces of the 2D drawing are offered to the snaps. Arcs are chords of 7.5 degrees or less. A value multiplied by k and divided by k again can differ from the original in the last bit.
Approach: 64-bit, stated limit
The arithmetic is 64-bit, but the method or the data sets the accuracy: a solver tolerance, a mesh, a step size, a datum shift, a table. The limit is stated where the result is shown.