Builtin Library
- ..
- .metadata290 B
- README.md4.5 KB
- init.luau29.6 KB
- readings.module
README.md
geometry.boolean
Combine two closed meshes into one closed surface: "union" keeps everything either encloses, "intersect" only what both enclose and "subtract" what a encloses and b does not.
Each input is triangulated and welded (core.weld, each merged point standing where its lowest-numbered point stood), then held to what a solid is (one number read back says whether either falls short, and the figures that explain it are read when one does), and refused by name when it is not: an input with fewer than four triangles, one carrying degenerate triangles (collapsed by the weld, or enclosing less area than weld squared), one that is not closed (an edge used by one triangle), not manifold (an edge shared by more than two), wound inconsistently (two triangles run an edge the same way), enclosing no volume, or wound inside-out (negative signed volume). The two are then cut against each other with the intersect kernels, which cut the curve where the surfaces cross and the regions where they coincide:
- The bounding volume hierarchy of
b(geometry.bvh), thenintersect_pairs(a count stage, a prefix sum, a write stage) lists the entries of the cut, three words each (a,b, part), ordered by the triangle ofa. intersect_cutswrites the segment each entry is cut along;intersect_unifygives every endpoint that names the same crossing one identity, so the pieces ofaand ofbshare their cut points. A cut point's position comes from the record that names it: the plane crossing of an edge and a triangle, the crossing of two coplanar edges, or a mesh point.intersect_retriretriangulates every triangle an entry reaches (side0 fora, with the entries as they stand;side1 forb, with the entries sorted by their triangle ofb).- Each piece is classified by the winding number of the other mesh just outside and just inside its centre (
ray, winding); the kernels' README holds the table of what each operation keeps, which also settles the faces the two meshes share. - The kept pieces are joined and welded. A triangle the weld leaves with two corners at one point encloses nothing and is dropped, and so are two triangles that stand on the same three points: where the cut runs straight across a corner within the weld distance of the line between its neighbours, each mesh keeps the sliver beside that line, and the two slivers face opposite ways over one another. The triangles are put in the order of their sorted corners (
sortthree times, by the highest, the middle and the lowest corner) so that triangles on the same points stand next to each other. - The surface is refused when it has no triangle (the result is empty) or when it does not close (an edge used by one triangle, by more than two, or run the same way by both), and otherwise every face is given three points of its own and its own normal, so a shaded read of the result agrees with its winding.
Two meshes that never cross skip the cutting and are classified whole.
Inputs
a(Surface). The first closed mesh.b(Surface). The second closed mesh.op("union", "subtract" or "intersect"). Which part of each cut surface survives; swappingaandbchanges the result of a subtract. There is no default: a missingopis refused.weld(Number, default 0). The distance below which two positions are one point while the inputs are cleaned and the pieces reassembled. 0 derives it as a hundred-thousandth of the diagonal of the smaller of the two inputs, since a mesh's coordinate precision scales with its size. May not exceedepsilon.epsilon(Number, default 0). The thickness of a plane: how far either side of a piece the other solid is probed is at least this. 0 derives it as the weld distance. At leastweld.
Outputs
geometry(Geometry). The combined surface: triangles of three points each, with a flat normal on every face.report(Data). Theoperation, theweldandepsilonthe run used, the result'spointCountandtriangleCount(the points and triangles ofgeometry, three points a face), and the volumes enclosed by the two inputs (volumeA,volumeB) and by the result (volume), summed on the device in single precision. It is measured only when asked for.
local cut = g.geometry.boolean({ a = g.mesh.box({}), b = g.geometry.transform({ geometry = g.mesh.box({}), translate = vector.create(0.5, 0.5, 0.5) }), op = "subtract" })