Works out the depth, position, axis and angles of the solid each extrude, revolve, hole and pattern adds to or cuts from the part.
How the precision is kept
The dimensions are computed in 64-bit numbers from the feature's values and the solved sketch, then handed to the manifold-3d module, which builds and combines the solids; its internal arithmetic was not part of this review. Round shapes are polygons with the part's resolution as the number of segments per turn, so the built solid is a faceted approximation. Through-all cuts and holes are made longer than the body by a margin on purpose.
Calculations under this policy
9 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.
The height of the extruded tool and where it starts along the sketch normal, for blind, two-sided, mid-plane and through-all ends.
two-sided: h = depth + depth₂; mid-plane: z₀ = −h ÷ 2; through all: h = extent + 1 + 0.01 × (max − min)
Blind and mid-plane values are the feature's depths combined by one addition or one halving. A through-all cut takes the body's extent along the normal and adds a margin of 1 plus 1 % of the extent, so that tool is longer than the body by design. Whether there is material on the cutting side is tested against ±1e-9, an absolute value.
kernel.js
lines 476 to 489
PartCraft 0.8.1
const sign = (f.op === 'cut' ? -1 : 1) * (f.reverse ? -1 : 1);
let h = f.depth, z0;
if (f.two && f.end === 'blind') { h = f.depth + f.depth2; z0 = sign > 0 ? -f.depth2 : -f.depth; }
else if (f.end === 'blind') z0 = sign > 0 ? 0 : -h;
else if (f.end === 'mid') z0 = -h / 2;
else {
if (f.op !== 'cut') fail(t('model.kernel.through_all_cuts'));
const rg = bodyRange(ctx, frame);
if (!rg) fail(t('model.kernel.nothing_to_cut'));
const margin = 1 + 0.01 * (rg[1] - rg[0]);
if (sign > 0) { if (rg[1] <= 1e-9) fail(t('model.kernel.nothing_to_cut_side')); z0 = 0; h = rg[1] + margin; }
else { if (rg[0] >= -1e-9) fail(t('model.kernel.nothing_to_cut_side')); h = -rg[0] + margin; z0 = -h; }
}
if (!(h > 0)) fail(t('model.kernel.depth_zero'));
Revolve profile in axis coordinates
64-bit
Turns each profile point into a radius from the axis and a height along it before the profile is revolved.
r = max(0, d·n); z = d·u; d = p − a, u = unit axis, n = unit normal to the axis on the profile's side
The axis direction is normalized by one Math.hypot and a division; an axis shorter than 1e-12 is refused. A profile with points more than 1e-7 × scale on both sides of the axis is refused. The radius is clamped at 0, so a point that rounding puts slightly across the axis is moved onto it.
kernel.js
lines 535 to 557
PartCraft 0.8.1
const ul = Math.hypot(b2[0] - a2[0], b2[1] - a2[1]);
if (ul < 1e-12) fail(t('model.kernel.axis_no_length'));
const u = [(b2[0] - a2[0]) / ul, (b2[1] - a2[1]) / ul];
// the profile without the axis line itself when it is a solid line
const skNoAxis = axis.construction ? sk : { ...sk, entities: sk.entities.map((e) => (e.id === axis.id ? { ...e, construction: true } : e)) };
let pr = profile(skNoAxis, ctx.part.resolution);
if (pr.error && !axis.construction) pr = profile(sk, ctx.part.resolution);
if (pr.error) fail(pr.error);
if (!pr.loops.length) fail(t('model.kernel.no_profile_revolve'));
const scale = Math.max(1, ...pr.loops.flat().map((p) => Math.hypot(p[0] - a2[0], p[1] - a2[1])));
let pos = 0, neg = 0;
for (const p of pr.loops.flat()) {
const s = -u[1] * (p[0] - a2[0]) + u[0] * (p[1] - a2[1]);
if (s > 1e-7 * scale) pos++; else if (s < -1e-7 * scale) neg++;
}
if (pos && neg) fail(t('model.kernel.profile_crosses_axis'));
const side = neg ? -1 : 1;
const n2 = [-u[1] * side, u[0] * side];
const loops = pr.loops.map((loop) => loop.map((p) => {
const d = [p[0] - a2[0], p[1] - a2[1]];
return [Math.max(0, d[0] * n2[0] + d[1] * n2[1]), d[0] * u[0] + d[1] * u[1]];
}));
const cs = crossSection(ctx, loops);
Plane, cylinder or cone swept by a profile line
64-bit
Classifies the surface a straight profile line sweeps and, for a cone, finds its apex and slope.
A line is a flat ring when |z₂ − z₁| ≤ 1e-9 × scale and a cylinder when |r₂ − r₁| ≤ 1e-9 × scale; both divisions for the cone are reached only after those two tests fail. For a nearly cylindrical line the apex is far away and r₁ ÷ slope is large.
The depth of a drilled hole, including a through hole's length past the body.
through: depth = extent on the drilling side + 1 + 0.01 × (max − min)
A blind hole uses the feature's depth unchanged. A through hole uses the body's bounding-box extent along the sketch normal plus the same margin as a through-all cut, so its tool is longer than the body.
kernel.js
lines 614 to 622
PartCraft 0.8.1
let depth = f.depth;
if (f.through) {
const rg = bodyRange(ctx, frame);
if (!rg) fail(t('model.kernel.nothing_to_drill'));
const margin = 1 + 0.01 * (rg[1] - rg[0]);
depth = sign < 0 ? -rg[0] + margin : rg[1] + margin;
if (depth <= margin) fail(t('model.kernel.nothing_to_drill_side'));
}
if (!(depth > 0)) fail(t('model.kernel.hole_depth_zero'));
Countersink cone height
64-bit
The height of a countersink from its diameter, the hole diameter and the included angle.
h = (R − r) ÷ tan(angle × π ÷ 360)
One 64-bit expression with Math.PI and Math.tan. The angle is not range-checked in this function, so an angle whose half-tangent is 0 would divide by zero. The cone is a faceted disk extruded with its top scaled by r ÷ R.
The inner and outer radius of a tapped hole's thread, used when parts are checked against each other.
r₀ = min(drill, d − 1.082532 × p) ÷ 2; r₁ = d ÷ 2
1.082532 is 5√3 ÷ 8 (1.0825317547…) rounded to seven digits, so the minor diameter carries that rounding. A through hole's thread depth is taken as the tool depth minus 1.
The angle between copies of a circular pattern and the rotation applied to each copy.
full turn: step = 360 ÷ count; otherwise step = angle ÷ (count − 1); copy i turned by step × i
Each copy's angle is step × i, computed fresh from the index, so the rounding does not accumulate from copy to copy. The pattern is a full turn when |angle − 360| < 1e-9. The rotation matrix comes from rotationAbout in vec.js, which this review did not cover.
How far the body reaches in front of and behind a sketch plane, for through-all cuts and through holes.
range = min and max of (corner − o)·n over the 8 corners of the bounding box
The range is that of the body's axis-aligned bounding box, not of the body itself, so for a tilted plane it is at least as large as the true extent.
kernel.js
lines 81 to 91
PartCraft 0.8.1
/** Extent of the body along a plane normal, relative to the plane: [min, max] or null. */
function bodyRange(ctx, frame) {
if (!ctx.hasBody()) return null;
const bb = ctx.bodyBox();
let lo = Infinity, hi = -Infinity;
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]]) {
const d = V.dot(V.sub([x, y, z], frame.o), frame.n);
lo = Math.min(lo, d); hi = Math.max(hi, d);
}
return [lo, hi];
}
Reference stored for a picked flat face
64-bit
Chooses the stable face reference for a picked flat triangle, or stores a fixed plane when none matches.
match: c.n·n > 1 − 1e-6 and |(p − c.o)·c.n| < 1e-4 × max(1, |p|); else o rounded to 9 decimals, n to 12
A fixed plane is stored with toFixed(9) on its origin and toFixed(12) on its normal, so the plane kept in the part is rounded from the picked values. The match tolerance on distance scales with the point's distance from the origin.
kernel.js
lines 1094 to 1103
PartCraft 0.8.1
/** The stable face reference for a picked planar triangle (feature-made faces), else a fixed plane. */
export function faceRefAt(regen, featureId, normal, point) {
const n = V.norm(normal);
for (const c of regen.candidates.get(featureId) || []) {
if (V.dot(c.frame.n, n) > 1 - 1e-6 && Math.abs(V.dot(V.sub(point, c.frame.o), c.frame.n)) < 1e-4 * Math.max(1, V.len(point))) {
return { kind: 'face', feature: featureId, face: c.ref.face, ...(c.ref.entity ? { entity: c.ref.entity } : {}) };
}
}
return { kind: 'fixed', o: point.map((v) => +v.toFixed(9)), n: n.map((v) => +v.toFixed(12)) };
}
In detail
Method
Profiles extruded or revolved as polygon cross-sections; booleans by manifold-3d. Through-all depth from the body's bounding box along the sketch normal plus a margin of 1 + 1 % of the extent.
Number types
64-bit numbers for all dimensions and frames in this file's own arithmetic.
Tolerances
Profile area under 1e-12 is refused (absolute). Axis or line shorter than 1e-12 is refused (absolute). Revolve: a profile point within 1e-7 × scale of the axis counts as on it; plane, cylinder or cone decided at 1e-9 × scale, where scale is the largest distance of a profile point from the axis start, at least 1. Full-turn test |angle − 360| < 1e-9. Picked-face match: normal within 1 − 1e-6 and distance under 1e-4 × max(1, |point|).
Limits
The thread zone's minor diameter uses the factor 1.082532, which is 5√3 ÷ 8 rounded to seven digits. A fixed plane taken from a picked face is stored with its origin rounded to 9 decimals and its normal to 12.
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.