Joins NURBS patches into one closed triangle mesh, mirrors and caps it, and works out its volume.
How the precision is kept
Patches are sampled at (n + 1) × (n + 1) parameter points (n = 24 by default) through cc-nurbs in 64-bit and kept in a Float64Array. The mesh, and so the volume, follows the surfaces only as closely as the sampling allows. Corners closer than the weld distance are merged.
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.
The parameter values at which each surface is evaluated.
uᵢ = u₀ + (u₁ − u₀) × i ÷ N, N from 2 to 200
The same fractions are used on every patch, so patches that share an edge and are parametrized alike give identical points there. Points come from cc-nurbs tessellate as 64-bit positions.
One multiplication and one subtraction on the mirror axis; the other two coordinates are copied.
hull/solid.js
lines 41 to 42
PartCraft 0.8.1
const k = AX[mirror], pts = raw.length / 3;
for (let i = 0; i < pts; i++) { const p = [raw[i * 3], raw[i * 3 + 1], raw[i * 3 + 2]]; p[k] = 2 * at - p[k]; raw.push(...p); }
Welding corners
64-bit
Merges corners of neighboring patches that lie within the weld distance.
merge when √(Δx² + Δy² + Δz²) ≤ eps
A corner joins the first earlier corner within eps by Math.hypot, searched in the 27 grid cells around it. Which corner is kept depends on the order of the points; positions are not averaged.
hull/solid.js
lines 51 to 58
PartCraft 0.8.1
const x = raw[i * 3], y = raw[i * 3 + 1], z = raw[i * 3 + 2], cx = Math.floor(x / eps), cy = Math.floor(y / eps), cz = Math.floor(z / eps);
let found = -1;
for (let dx = -1; dx <= 1 && found < 0; dx++) for (let dy = -1; dy <= 1 && found < 0; dy++) for (let dz = -1; dz <= 1 && found < 0; dz++) {
const list = grid.get(key(cx + dx, cy + dy, cz + dz));
if (list) for (const j of list) if (Math.hypot(pos[j * 3] - x, pos[j * 3 + 1] - y, pos[j * 3 + 2] - z) <= eps) { found = j; break; }
}
if (found < 0) { found = pos.length / 3; pos.push(x, y, z); const kk = key(cx, cy, cz); (grid.get(kk) || grid.set(kk, []).get(kk)).push(found); }
map[i] = found;
Planar opening test
64-bit
Decides whether an opening lies in a plane and can be capped.
n by Newell's sums; deviation = max |(p − centroid)·n|; cap when deviation ≤ max(4 eps, 2e-4 × size)
The loop's normal is summed over its edges and made unit, its centroid is the mean of its points, and the largest distance of any loop point from that plane is compared with the tolerance. The cap is a fan from the centroid.
Each triangle's signed tetrahedron to the origin is divided by 6 and added in order. For a hull far from the origin the terms are large and nearly cancel. A negative total flips every triangle and its sign.
hull/solid.js
lines 135 to 140
PartCraft 0.8.1
let vol = 0;
for (const [a, b, c] of T) {
const A = a * 3, B = b * 3, C = c * 3;
vol += (pos[A] * (pos[B + 1] * pos[C + 2] - pos[B + 2] * pos[C + 1]) - pos[A + 1] * (pos[B] * pos[C + 2] - pos[B + 2] * pos[C]) + pos[A + 2] * (pos[B] * pos[C + 1] - pos[B + 1] * pos[C])) / 6;
}
if (vol < 0) { T = T.map((t) => [t[0], t[2], t[1]]); vol = -vol; }
In detail
Method
Uniform parameter sampling; weld on a grid of cell size eps, searching 27 cells; one winding by flood fill; planar openings closed by a centroid fan after a Newell-normal planarity test; volume by signed tetrahedra to the origin.
Weld distance 1e-4 × the set's diagonal unless given. Planarity: largest deviation ≤ max(4 × weld distance, 2e-4 × diagonal). A loop whose Newell normal is shorter than 1e-12 is skipped.
Limits
The volume is that of the triangle mesh, not of the surfaces themselves. A welded corner takes the position of the first corner found within the weld distance.
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.