[{"data":1,"prerenderedAt":345},["ShallowReactive",2],{"algo-exact-mesh-arrangements-and-booleans-in-real-time":3},{"id":4,"title":5,"author":6,"body":7,"date":317,"description":318,"extension":319,"keywords":320,"meta":335,"navigation":336,"path":337,"published":336,"seo":338,"stem":339,"tags":340,"__hash__":344},"algorithms\u002Falgorithms\u002Fexact-mesh-arrangements-and-booleans-in-real-time.md","Exact Mesh Arrangements and Booleans in Real-Time","Žiga Sajovic",{"type":8,"value":9,"toc":290},"minimark",[10,14,18,26,32,37,40,45,48,52,58,62,65,69,72,84,88,91,95,102,105,108,112,115,119,125,128,132,135,139,142,146,153,157,160,164,167,171,175,179,182,186,191,195,198,219,223,226,230,234,237,241,245,248,252,263,286],[11,12,13],"p",{},"Mesh arrangements decompose overlapping geometry into classified regions — splitting faces along intersection curves so every face belongs to exactly one region. Mesh booleans select which regions to keep: union, intersection, difference.",[15,16],"headline-numbers",{":items":17},"[{\"value\":\"7ms\",\"label\":\"Intersection curves\",\"detail\":\"2×1M polygons · 233× CGAL\"},{\"value\":\"28ms\",\"label\":\"Boolean union\",\"detail\":\"2×1M polygons · 6× MeshLib · 84× CGAL\"},{\"value\":\"173ms\",\"label\":\"Polygon arrangements\",\"detail\":\"2×1M polygons · 32× libigl\"}]",[11,19,20,21,25],{},"Real meshes are not ideal manifolds. They carry non-manifold flaps, inconsistent winding, coplanar faces, and accumulated pipeline artifacts. ",[22,23,24],"code",{},"trueform"," handles all of them.",[27,28],"data-table",{":headers":29,":highlight":30,":rows":31},"[\"Feature\",\"Handling\"]","0","[[\"Convex polygons\",\"Native — not limited to triangles\"],[\"Non-manifold edges\",\"Handled directly\"],[\"Inconsistent winding\",\"Bayesian classifiers per manifold edge-connected component\"],[\"Self-intersecting input\",\"Resolved via polygon arrangements\"],[\"Coplanar primitives\",\"Resolved via topological exactness — aligned\u002Fopposing boundary classification\"],[\"Hole cuts\",\"Hole patching into the face\"],[\"Contour crossings\",\"Resolved via indirect predicates\"]]",[33,34,36],"h2",{"id":35},"exactness","Exactness",[11,38,39],{},"Two kinds of exactness matter in mesh arrangements: geometric and topological.",[41,42,44],"h3",{"id":43},"geometric-exactness","Geometric exactness",[11,46,47],{},"Input coordinates are scaled to exact integer space. All predicates — orientation tests, intersection points, determinants — are computed with exact integer arithmetic through a precision chain. No floating-point operation participates in any geometric decision.",[27,49],{":headers":50,":highlight":30,":rows":51},"[\"\",\"Coordinates\",\"Intermediate\",\"Predicates\"]","[[\"int32 base (default)\",\"int32\",\"int64\",\"int128\"],[\"int64 base (extended)\",\"int64\",\"int128\",\"int256 (custom)\"]]",[11,53,54,57],{},[22,55,56],{},"orient3d_sign"," returns −1, 0, or +1 — exactly. Zero means coplanar, not \"close to coplanar.\"",[41,59,61],{"id":60},"topological-exactness","Topological exactness",[11,63,64],{},"An intersection point can land on a vertex, on an edge, or at the meeting point of two coplanar edges. Five distinct configurations:",[27,66],{":headers":67,":highlight":30,":rows":68},"[\"Type\",\"Configuration\",\"Resolution\"]","[[\"EF\",\"Edge pierces face\",\"Signed volume ratios\"],[\"EE\",\"Coplanar edge crossing\",\"2D orient2d on projected plane\"],[\"VE\",\"Vertex on edge\",\"orient2d + between test\"],[\"VF\",\"Vertex in face interior\",\"2D winding test\"],[\"VV\",\"Coincident vertices\",\"Exact coordinate match\"]]",[11,70,71],{},"Geometric exactness without topological exactness is typically paired with Simulation of Simplicity (SoS). SoS perturbs input so all configurations collapse to EF — deterministic but arbitrary. A square placed into a cube with edges on faces might produce no intersections, partial, or all, depending on perturbation order. These configurations are not edge cases to avoid — they define contour crossings in multi-mesh arrangements.",[11,73,74,76,77,79,80,83],{},[22,75,24],{}," classifies all five exactly — ",[22,78,56],{}," separates coplanar from non-coplanar, ",[22,81,82],{},"orient2d_sign"," resolves the coplanar cases. Each configuration is resolved to its canonical form.",[33,85,87],{"id":86},"arrangements","Arrangements",[11,89,90],{},"Arrangements resolve multiple mutually intersecting meshes simultaneously.",[41,92,94],{"id":93},"indirect-predicates","Indirect predicates",[11,96,97,98,101],{},"An intersection edge is defined by its originating face pair. An intersection point between two edges is defined by the union of their face pair IDs — a unique triplet of three faces. This identifies points topologically, avoiding geometric ambiguity. Coordinates are computed from the triplet via ",[22,99,100],{},"orient2d","-based interpolation in integer arithmetic, only when needed.",[103,104],"algo-arrangement-diagram",{},[11,106,107],{},"Indirect predicates allow the arrangement to be formulated in two stages: compute pairwise intersection contours between all mesh pairs, then intersect those contours within each face.",[41,109,111],{"id":110},"stage-1-pairwise-intersection-contours","Stage 1: pairwise intersection contours",[11,113,114],{},"An AABB tree narrows candidates to overlapping face pairs. For each pair, intersections are computed exactly. Each intersection produces an edge on both faces, tagged with its originating face pair.",[41,116,118],{"id":117},"stage-2-per-face-contour-intersections","Stage 2: per-face contour intersections",[11,120,121,122,124],{},"Each intersected face carries a small number of edges from different mesh pairs — typically 2–5. These are projected to the face plane and intersected with topological exactness: VV, VE, and EE configurations classified via ",[22,123,100],{},". The result is an intersection graph embedded in the face, defining how it will be split.",[11,126,127],{},"Each face has few edges — trivial to process. Each face is independent — all processed in parallel. Points are identified across faces via their triplets.",[33,129,131],{"id":130},"booleans","Booleans",[11,133,134],{},"A boolean is an arrangement with a classification step. After splitting faces along intersection curves, each resulting face must be labeled as inside or outside the other mesh. The boolean operation selects which labels to keep.",[41,136,138],{"id":137},"components","Components",[11,140,141],{},"Faces are grouped into manifold edge-connected components. Two faces belong to the same component if they share a manifold edge that is not an intersection edge. Non-manifold edges and intersection edges act as barriers — they prohibit crossing between components. Each component receives a single label.",[41,143,145],{"id":144},"wedge-classification","Wedge classification",[11,147,148,149,152],{},"For each intersection edge in a component, the two same-mesh faces sharing that edge form a wedge. A test point from the opposite-mesh neighbor is classified against this wedge using exact ",[22,150,151],{},"orient3d",":",[27,154],{":headers":155,":highlight":30,":rows":156},"[\"Result\",\"Meaning\",\"Vote\"]","[[\"on_negative_side\",\"Test point inside wedge\",\"inside\"],[\"on_positive_side\",\"Test point outside wedge\",\"outside\"],[\"on_boundary\",\"Coplanar — skip\",\"—\"]]",[11,158,159],{},"Coplanar face pairs are classified directly as aligned or opposing boundary based on normal direction.",[41,161,163],{"id":162},"bayesian-classification","Bayesian classification",[11,165,166],{},"Each intersection edge contributes an observation to its component — inside or outside. The per-component label is determined by a Beta-Bernoulli classifier over these observations: inside, outside, aligned boundary, or opposing boundary. Components with no intersection edges fall back to signed distance or ray-based containment tests.",[41,168,170],{"id":169},"operation-mapping","Operation mapping",[27,172],{":headers":173,":highlight":30,":rows":174},"[\"Operation\",\"Keep from A\",\"Keep from B\"]","[[\"Union\",\"outside + aligned boundary\",\"outside\"],[\"Intersection\",\"inside + aligned boundary\",\"inside\"],[\"Difference\",\"outside + opposing boundary\",\"inside\"]]",[33,176,178],{"id":177},"comparison-with-existing-sdks","Comparison with existing SDKs",[11,180,181],{},"Four libraries are commonly used for mesh booleans: CGAL, libigl, MeshLib, and trueform. This is how they compare.",[41,183,185],{"id":184},"capabilities","Capabilities",[27,187],{":headers":188,":highlight":189,":rows":190},"[\"Feature\",\"trueform\",\"MeshLib\",\"CGAL\",\"libigl\"]","1","[[\"Convex polygons\",\"✓\",\"Triangles\",\"✓\",\"Triangles\"],[\"Non-manifold edges\",\"✓\",\"Auto-deletes\",\"Requires manifold\",\"Requires manifold\"],[\"Inconsistent winding\",\"Bayesian\",\"✗\",\"✗\",\"✗\"],[\"Self-intersecting input\",\"✓\",\"Limited\",\"✓\",\"✓\"],[\"Coplanar primitives\",\"Exact\",\"SoS\",\"Kernel-dep.\",\"EPECK\"],[\"Hole cuts\",\"✓\",\"✗\",\"✓\",\"✓\"],[\"N-mesh arrangements\",\"✓\",\"✗\",\"✗\",\"✗\"],[\"Curve extraction\",\"Standalone\",\"✗\",\"Indirect\",\"Indirect\"]]",[41,192,194],{"id":193},"performance","Performance",[11,196,197],{},"Stanford Dragon at varying resolutions. Linear scaling is maintained through 2M+ polygons.",[11,199,200,206,207,206,211,206,215],{},[201,202,205],"span",{"className":203},[204],"tag-pill","Apple M4 Max"," ",[201,208,210],{"className":209},[204],"16 threads",[201,212,214],{"className":213},[204],"Clang -O3",[201,216,218],{"className":217},[204],"mimalloc",[41,220,222],{"id":221},"boolean-union","Boolean union",[11,224,225],{},"trueform uses exact predicates and canonical topology. MeshLib uses SoS with int32. CGAL uses EPIC kernel. libigl uses EPECK (GMP).",[27,227],{":headers":228,":highlight":189,":rows":229},"[\"Polygons\",\"trueform\",\"MeshLib\",\"CGAL\",\"libigl\"]","[[\"2 × 58K\",\"6.4 ms\",\"14.9 ms\",\"133 ms\",\"588 ms\"],[\"2 × 142K\",\"9.0 ms\",\"25.1 ms\",\"329 ms\",\"1225 ms\"],[\"2 × 244K\",\"12.4 ms\",\"54.4 ms\",\"573 ms\",\"1973 ms\"],[\"2 × 481K\",\"16.8 ms\",\"124.7 ms\",\"1082 ms\",\"3666 ms\"],[\"2 × 723K\",\"22.7 ms\",\"112.1 ms\",\"1746 ms\",\"5703 ms\"],[\"2 × 1.03M\",\"27.8 ms\",\"161.5 ms\",\"2339 ms\",\"7735 ms\"]]",[41,231,233],{"id":232},"polygon-arrangements","Polygon arrangements",[11,235,236],{},"Two copies concatenated into a single self-intersecting mesh. MeshLib not benchmarked — refuses nested contour crossings.",[27,238],{":headers":239,":highlight":189,":rows":240},"[\"Polygons\",\"trueform\",\"libigl\"]","[[\"117K\",\"12.7 ms\",\"642 ms\"],[\"284K\",\"27.0 ms\",\"1205 ms\"],[\"488K\",\"42.4 ms\",\"1785 ms\"],[\"962K\",\"78.1 ms\",\"2907 ms\"],[\"1.45M\",\"128.4 ms\",\"4382 ms\"],[\"2.05M\",\"173.2 ms\",\"5509 ms\"]]",[41,242,244],{"id":243},"intersection-curves","Intersection curves",[11,246,247],{},"Standalone extraction of crossing polylines. Most SDKs only provide curves as a side-effect of a full boolean operation.",[27,249],{":headers":250,":highlight":189,":rows":251},"[\"Polygons\",\"trueform\",\"CGAL\"]","[[\"2 × 58K\",\"2.3 ms\",\"86.9 ms\"],[\"2 × 142K\",\"3.4 ms\",\"230.7 ms\"],[\"2 × 244K\",\"4.1 ms\",\"416.7 ms\"],[\"2 × 481K\",\"5.4 ms\",\"777.1 ms\"],[\"2 × 723K\",\"7.0 ms\",\"1266.8 ms\"],[\"2 × 1.03M\",\"7.3 ms\",\"1695.6 ms\"]]",[11,253,254,255,262],{},"Interactive charts, full methodology, and source code: ",[256,257,261],"a",{"href":258,"rel":259},"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fbenchmarks",[260],"nofollow","trueform benchmarks",".",[11,264,265,270,271,270,276,270,281],{},[256,266,269],{"href":267,"rel":268},"https:\u002F\u002Ftrueform.polydera.com\u002Flive-examples\u002Fboolean",[260],"Try it live"," · ",[256,272,275],{"href":273,"rel":274},"https:\u002F\u002Ftrueform.polydera.com",[260],"Documentation",[256,277,280],{"href":278,"rel":279},"https:\u002F\u002Fgithub.com\u002Fpolydera\u002Ftrueform",[260],"GitHub",[256,282,285],{"href":283,"rel":284},"https:\u002F\u002Flunar.polydera.com",[260],"Open Lunar",[287,288],"cite-as",{"author":289,"title":5},"Sajovic, {\\v{Z}}iga",{"title":291,"searchDepth":292,"depth":292,"links":293},"",2,[294,299,304,310],{"id":35,"depth":292,"text":36,"children":295},[296,298],{"id":43,"depth":297,"text":44},3,{"id":60,"depth":297,"text":61},{"id":86,"depth":292,"text":87,"children":300},[301,302,303],{"id":93,"depth":297,"text":94},{"id":110,"depth":297,"text":111},{"id":117,"depth":297,"text":118},{"id":130,"depth":292,"text":131,"children":305},[306,307,308,309],{"id":137,"depth":297,"text":138},{"id":144,"depth":297,"text":145},{"id":162,"depth":297,"text":163},{"id":169,"depth":297,"text":170},{"id":177,"depth":292,"text":178,"children":311},[312,313,314,315,316],{"id":184,"depth":297,"text":185},{"id":193,"depth":297,"text":194},{"id":221,"depth":297,"text":222},{"id":232,"depth":297,"text":233},{"id":243,"depth":297,"text":244},"2026-04-20","Arrangements, booleans, and self-intersection resolution at interactive speeds on million-polygon meshes via exact arithmetic and canonical topology. 6× faster than MeshLib, 84× faster than CGAL.","md",[321,322,323,324,325,326,327,328,329,330,331,332,333,334],"fast mesh boolean","fastest mesh boolean","real-time mesh boolean","exact mesh boolean","mesh boolean library","CGAL alternative","mesh boolean comparison","mesh self-intersection","mesh arrangement algorithm","multi mesh arrangements","exact geometry processing","python mesh boolean","typescript mesh boolean","javascript mesh boolean",{},true,"\u002Falgorithms\u002Fexact-mesh-arrangements-and-booleans-in-real-time",{"title":5,"description":318},"algorithms\u002Fexact-mesh-arrangements-and-booleans-in-real-time",[341,342,343,130,86],"topology","exact-arithmetic","parallel","zol2epJQyW9u0dD0SL361wgQ89GaMaTgmBOBxcHz9G0",1776780745121]