Cut each body by the later bodies that reach it, not by all of them (#177) - #178
Merged
Merged
Conversation
…177) splitBodiesForExport's multi-colour path subtracted every body against a running union of all the bodies after it. That union grows as the loop goes, so an n-body export did n booleans whose second operand reached the size of the whole model -- quadratic in body count, however little the parts actually touched. Only a later body that intersects this one can remove anything from it, and bounding boxes settle that in nanoseconds. Cutting against just those leaves the result identical, because subtracting a disjoint solid removes no volume, and leaves the work proportional to how much the model really overlaps rather than to how many parts it has. Measured on 100 coloured overlapping spheres, ~212,000 facets: bodies 3MF before 3MF after 10 1.714s 1.472s 25 2.599s 1.615s 50 4.103s 1.750s 100 6.582s 1.843s 3.6x at 100 bodies, and near-flat in body count where it had been quadratic. BelfrySCAD's Dalek model (119 bodies, 224k facets) goes 5.96s -> 2.07s. STL is untouched: it is not a multi-object format and never reaches this path. Identical output, checked rather than assumed. Every STL is byte-identical. Dalek's 3MF keeps all 6 objects and the same bounding boxes, with total volume 403426.1883 -> 403426.1882 and 48 fewer triangles out of 151,074 -- slivers the old cut against the big union left behind. The 100-sphere case matches object for object to 4e-15. The two new tests pin semantics, not speed, and both pass on the old code: they are there to stop a future rewrite getting this wrong. The obvious cheaper idea -- union same-coloured bodies first, then subtract per colour -- is wrong, because red/blue/red overlapping in that order must let the blue beat the first red; LaterWinsAcrossAnInterveningColour is exactly that case.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #177.
The problem
splitBodiesForExport's multi-colour path subtracted every body against a running union of all the bodies after it:claimedgrows monotonically, so by body k it holds the union of all n−k bodies after it. That is n booleans whose second operand reaches the size of the whole model — quadratic in body count, regardless of how much the parts actually touch. TheboxesOverlapguard stops discriminating as soon as the union spans the model, which it does almost immediately.The fix
Only a later body that actually intersects this one can remove anything from it, and bounding boxes settle that in nanoseconds. Each body is now cut against just those:
The result is identical — subtracting a disjoint solid removes no volume — and the work becomes proportional to how much the model overlaps rather than to how many parts it has. The O(n²) bounding-box scan that replaces it is nanoseconds per pair.
Skipping the subtraction entirely when nothing overlaps also preserves the triangle list, so per-triangle colours survive on bodies that nothing cuts. That is the same property the old bounding-box guard existed to protect, now applied per body instead of against the accumulated blob.
Measurements
100 coloured overlapping spheres, ~212,000 facets held constant, only the body count varying:
3.6x at 100 bodies, and near-flat in body count where it had been quadratic. BelfrySCAD's Dalek model (119 bodies, 224,220 facets) goes 5.96 s → 2.07 s.
STL is unaffected in both directions: it is not a multi-object format and never reaches this path.
Output is identical, checked rather than assumed
Tests
SplitColors.AFarAwayBodyChangesNothingandSplitColors.LaterWinsAcrossAnInterveningColour.Both pass on the old code as well — they pin semantics, not speed, and I am not claiming they would have caught this. They are there to stop a future rewrite getting it wrong. In particular the obvious cheaper idea, "union the same-coloured bodies first and subtract per colour", is wrong: red/blue/red overlapping in that order must let the blue beat the first red, which per-colour grouping cannot express. That is exactly what the second test encodes.
The speed claim rests on the measurements above, not on a unit test.
Full C++ suite: 1262 passed. Python bindings: 42 passed.
🤖 Generated with Claude Code