Unites, subtracts or intersects the mesh with another object and returns the result as a new mesh.
How the precision is kept
Both meshes are triangulated in 64-bit, but their vertex positions are converted to a Float32Array for the Manifold library and the result's positions are read back from the library's vertex array into a Float64Array. The mesh that shows and exports after a boolean modifier therefore has positions that passed through 32-bit.
Open item. Hand the positions to the boolean in 64-bit and read the result back in 64-bit instead of converting them with Float32Array.from.
Calculations under this policy
2 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.
mesh/modifiers.jsModelCraft 0.8.1 32-bit in part2 calculationsReviewed
Tests tools/modelcraft-test/mesh.mjs
Boolean of two meshes
32-bit in part
Runs the union, intersection or difference and copies the result's vertex positions.
Float32Array.from rounds every input coordinate of both meshes to 32-bit before Manifold sees it. The result is copied from the library's vertex array into a Float64Array, which stores what it is given but cannot restore the digits dropped on the way in, so the positions of the modified mesh carry about 7 digits, not 15.
Builds a mesh from triangle positions and indices, welds coincident vertices and marks sharp edges; used after a boolean and after decimate.
weld within 1e-9; sharp when n₁·n₂ < cos(30°)
Vertices closer than 1e-9 (absolute) are merged with mergeByDistance. An edge is flagged sharp when the dot product of its two faces' unit normals is below cos(deg × π ÷ 180), with deg = 30 by default; this is a flag for shading, positions are not changed by it.
mesh/modifiers.js
lines 138 to 148
ModelCraft 0.8.1
/** A mesh from triangles (positions, indices), sharp where faces bend more than `deg`. */
function fromTris(pos, tri, deg = 30) {
const pts = [], faces = [];
for (let i = 0; i < pos.length / 3; i++) pts.push([pos[i * 3], pos[i * 3 + 1], pos[i * 3 + 2]]);
for (let i = 0; i < tri.length; i += 3) faces.push([tri[i], tri[i + 1], tri[i + 2]]);
const m = Mesh.fromLists(pts, faces);
O.mergeByDistance(m, 1e-9, null);
const lim = Math.cos((deg * Math.PI) / 180), A = [0, 0, 0], B = [0, 0, 0];
for (let h = 0; h < m.nh; h++) { const t = m.hTw[h]; if (m.heDead(h) || t < h) continue; m.faceNormal(m.hF[h], A); m.faceNormal(m.hF[t], B); if (dot(A, B) < lim) { m.hFl[h] |= FL_SHARP; m.hFl[t] |= FL_SHARP; } }
return m;
}
In detail
Method
Triangles of both meshes are handed to the Manifold library (union, intersection or difference); the result's triangles become a mesh, vertices at the same place are welded and edges are marked sharp by the angle between their faces.
Number types
Float64Array positions before and after; Float32Array for the vertex properties passed to Manifold; Uint32Array indices.
Tolerances
Weld of the result: 1e-9 (absolute, mesh units). Sharp edge: the faces' unit normals have a dot product below cos 30°.
Limits
Every coordinate of the two inputs is rounded to the nearest 32-bit number before the boolean runs, about 7 significant digits: near 100 m from the mesh's origin neighboring 32-bit numbers are 2⁻¹⁷ m apart, about 7.6 micrometers.
Approach: 32-bit step, flagged
One step stores or exchanges 32-bit values that reach a number a person reads. It is listed here with its open item, and the step is either moved to 64-bit or the app will warn where the number is read.