[{"data":1,"prerenderedAt":346},["ShallowReactive",2],{"algo-list":3},[4],{"id":5,"title":6,"author":7,"body":8,"date":318,"description":319,"extension":320,"keywords":321,"meta":336,"navigation":337,"path":338,"published":337,"seo":339,"stem":340,"tags":341,"__hash__":345},"algorithms\u002Falgorithms\u002Fexact-mesh-arrangements-and-booleans-in-real-time.md","Exact Mesh Arrangements and Booleans in Real-Time","Žiga Sajovic",{"type":9,"value":10,"toc":291},"minimark",[11,15,19,27,33,38,41,46,49,53,59,63,66,70,73,85,89,92,96,103,106,109,113,116,120,126,129,133,136,140,143,147,154,158,161,165,168,172,176,180,183,187,192,196,199,220,224,227,231,235,238,242,246,249,253,264,287],[12,13,14],"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.",[16,17],"headline-numbers",{":items":18},"[{\"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\"}]",[12,20,21,22,26],{},"Real meshes are not ideal manifolds. They carry non-manifold flaps, inconsistent winding, coplanar faces, and accumulated pipeline artifacts. ",[23,24,25],"code",{},"trueform"," handles all of them.",[28,29],"data-table",{":headers":30,":highlight":31,":rows":32},"[\"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\"]]",[34,35,37],"h2",{"id":36},"exactness","Exactness",[12,39,40],{},"Two kinds of exactness matter in mesh arrangements: geometric and topological.",[42,43,45],"h3",{"id":44},"geometric-exactness","Geometric exactness",[12,47,48],{},"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.",[28,50],{":headers":51,":highlight":31,":rows":52},"[\"\",\"Coordinates\",\"Intermediate\",\"Predicates\"]","[[\"int32 base (default)\",\"int32\",\"int64\",\"int128\"],[\"int64 base (extended)\",\"int64\",\"int128\",\"int256 (custom)\"]]",[12,54,55,58],{},[23,56,57],{},"orient3d_sign"," returns −1, 0, or +1 — exactly. Zero means coplanar, not \"close to coplanar.\"",[42,60,62],{"id":61},"topological-exactness","Topological exactness",[12,64,65],{},"An intersection point can land on a vertex, on an edge, or at the meeting point of two coplanar edges. Five distinct configurations:",[28,67],{":headers":68,":highlight":31,":rows":69},"[\"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\"]]",[12,71,72],{},"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.",[12,74,75,77,78,80,81,84],{},[23,76,25],{}," classifies all five exactly — ",[23,79,57],{}," separates coplanar from non-coplanar, ",[23,82,83],{},"orient2d_sign"," resolves the coplanar cases. Each configuration is resolved to its canonical form.",[34,86,88],{"id":87},"arrangements","Arrangements",[12,90,91],{},"Arrangements resolve multiple mutually intersecting meshes simultaneously.",[42,93,95],{"id":94},"indirect-predicates","Indirect predicates",[12,97,98,99,102],{},"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 ",[23,100,101],{},"orient2d","-based interpolation in integer arithmetic, only when needed.",[104,105],"algo-arrangement-diagram",{},[12,107,108],{},"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.",[42,110,112],{"id":111},"stage-1-pairwise-intersection-contours","Stage 1: pairwise intersection contours",[12,114,115],{},"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.",[42,117,119],{"id":118},"stage-2-per-face-contour-intersections","Stage 2: per-face contour intersections",[12,121,122,123,125],{},"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 ",[23,124,101],{},". The result is an intersection graph embedded in the face, defining how it will be split.",[12,127,128],{},"Each face has few edges — trivial to process. Each face is independent — all processed in parallel. Points are identified across faces via their triplets.",[34,130,132],{"id":131},"booleans","Booleans",[12,134,135],{},"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.",[42,137,139],{"id":138},"components","Components",[12,141,142],{},"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.",[42,144,146],{"id":145},"wedge-classification","Wedge classification",[12,148,149,150,153],{},"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 ",[23,151,152],{},"orient3d",":",[28,155],{":headers":156,":highlight":31,":rows":157},"[\"Result\",\"Meaning\",\"Vote\"]","[[\"on_negative_side\",\"Test point inside wedge\",\"inside\"],[\"on_positive_side\",\"Test point outside wedge\",\"outside\"],[\"on_boundary\",\"Coplanar — skip\",\"—\"]]",[12,159,160],{},"Coplanar face pairs are classified directly as aligned or opposing boundary based on normal direction.",[42,162,164],{"id":163},"bayesian-classification","Bayesian classification",[12,166,167],{},"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.",[42,169,171],{"id":170},"operation-mapping","Operation mapping",[28,173],{":headers":174,":highlight":31,":rows":175},"[\"Operation\",\"Keep from A\",\"Keep from B\"]","[[\"Union\",\"outside + aligned boundary\",\"outside\"],[\"Intersection\",\"inside + aligned boundary\",\"inside\"],[\"Difference\",\"outside + opposing boundary\",\"inside\"]]",[34,177,179],{"id":178},"comparison-with-existing-sdks","Comparison with existing SDKs",[12,181,182],{},"Four libraries are commonly used for mesh booleans: CGAL, libigl, MeshLib, and trueform. This is how they compare.",[42,184,186],{"id":185},"capabilities","Capabilities",[28,188],{":headers":189,":highlight":190,":rows":191},"[\"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\"]]",[42,193,195],{"id":194},"performance","Performance",[12,197,198],{},"Stanford Dragon at varying resolutions. Linear scaling is maintained through 2M+ polygons.",[12,200,201,207,208,207,212,207,216],{},[202,203,206],"span",{"className":204},[205],"tag-pill","Apple M4 Max"," ",[202,209,211],{"className":210},[205],"16 threads",[202,213,215],{"className":214},[205],"Clang -O3",[202,217,219],{"className":218},[205],"mimalloc",[42,221,223],{"id":222},"boolean-union","Boolean union",[12,225,226],{},"trueform uses exact predicates and canonical topology. MeshLib uses SoS with int32. CGAL uses EPIC kernel. libigl uses EPECK (GMP).",[28,228],{":headers":229,":highlight":190,":rows":230},"[\"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\"]]",[42,232,234],{"id":233},"polygon-arrangements","Polygon arrangements",[12,236,237],{},"Two copies concatenated into a single self-intersecting mesh. MeshLib not benchmarked — refuses nested contour crossings.",[28,239],{":headers":240,":highlight":190,":rows":241},"[\"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\"]]",[42,243,245],{"id":244},"intersection-curves","Intersection curves",[12,247,248],{},"Standalone extraction of crossing polylines. Most SDKs only provide curves as a side-effect of a full boolean operation.",[28,250],{":headers":251,":highlight":190,":rows":252},"[\"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\"]]",[12,254,255,256,263],{},"Interactive charts, full methodology, and source code: ",[257,258,262],"a",{"href":259,"rel":260},"https:\u002F\u002Ftrueform.polydera.com\u002Fcpp\u002Fbenchmarks",[261],"nofollow","trueform benchmarks",".",[12,265,266,271,272,271,277,271,282],{},[257,267,270],{"href":268,"rel":269},"https:\u002F\u002Ftrueform.polydera.com\u002Flive-examples\u002Fboolean",[261],"Try it live"," · ",[257,273,276],{"href":274,"rel":275},"https:\u002F\u002Ftrueform.polydera.com",[261],"Documentation",[257,278,281],{"href":279,"rel":280},"https:\u002F\u002Fgithub.com\u002Fpolydera\u002Ftrueform",[261],"GitHub",[257,283,286],{"href":284,"rel":285},"https:\u002F\u002Flunar.polydera.com",[261],"Open Lunar",[288,289],"cite-as",{"author":290,"title":6},"Sajovic, {\\v{Z}}iga",{"title":292,"searchDepth":293,"depth":293,"links":294},"",2,[295,300,305,311],{"id":36,"depth":293,"text":37,"children":296},[297,299],{"id":44,"depth":298,"text":45},3,{"id":61,"depth":298,"text":62},{"id":87,"depth":293,"text":88,"children":301},[302,303,304],{"id":94,"depth":298,"text":95},{"id":111,"depth":298,"text":112},{"id":118,"depth":298,"text":119},{"id":131,"depth":293,"text":132,"children":306},[307,308,309,310],{"id":138,"depth":298,"text":139},{"id":145,"depth":298,"text":146},{"id":163,"depth":298,"text":164},{"id":170,"depth":298,"text":171},{"id":178,"depth":293,"text":179,"children":312},[313,314,315,316,317],{"id":185,"depth":298,"text":186},{"id":194,"depth":298,"text":195},{"id":222,"depth":298,"text":223},{"id":233,"depth":298,"text":234},{"id":244,"depth":298,"text":245},"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",[322,323,324,325,326,327,328,329,330,331,332,333,334,335],"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":6,"description":319},"algorithms\u002Fexact-mesh-arrangements-and-booleans-in-real-time",[342,343,344,131,87],"topology","exact-arithmetic","parallel","zol2epJQyW9u0dD0SL361wgQ89GaMaTgmBOBxcHz9G0",1776780745121]