Repository navigation
Conversation
|
No actionable comments were generated in the recent review. Walkthrough
Merge Risk:
|
fde91bb to
bb4e890
Compare
…5, 36150)
`delete_face` asks whether the vertices of the face it deleted are still joined
to the shell, and answers by walking the shell from the first of them
(`shell_traversal_with_marks`) and marking everything it reaches. Cutting N
cylinders out of a plate in one `boolean_lists` call deletes 2N discs, each
with one vertex, and each deletion walks the whole shell although the first
vertex use it visits settles the question.
`WorkCounters::shell_traversal_faces` counts each face a shell traversal marks,
where it is marked. The new test cuts 25 and 100 holes with SolidCraft's fixes,
checks the result (N + 6 faces, 3N + 12 edges, the exact volume within 1e-6)
and asks for one face marked for each face deleted: 50 and 200.
Fails until the walk ends when its question is answered:
left: (2475, 36150) (7/2) N^2 + (23/2) N
right: (50, 200) 2 N
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
…swered
After it unlinks a face, `delete_face` has to learn whether the face's vertices
are still joined to the shell: if not, the shell has come apart and is split. It
asked by walking the shell from the first of those vertices, marking everything
the shell joins to it, and then read the marks of the face's vertices. The walk
went on over the whole shell after the last of them was marked, so deleting N
faces of one body marked on the order of N^2 faces.
The walk now takes the vertices it is asked about and ends as it marks the last
of them (`shell_traversal_to_vertices`). It advances the mark once and marks in
the same order as before, as far as it goes; when a vertex is not reached it
covers everything joined to the start, as before, and the split that follows is
unchanged. `shell_traversal` and `shell_traversal_with_marks` are the whole
walk still: their callers use the lists it collects.
Which walk `delete_face` runs is decided by where its marks live:
- In a table of marks (`delete_face`: the boolean's merge step, the stitch, the
primitives, the planar faces, the cutter, fillet removal) the marks are the
deletion's working state, read for the face's vertices and nothing else. The
walk ends at the last of them.
- In the caller's notification records (`delete_face_with_body_identity`: the
offset executive's step gaps and crossing loops, the fillet's insertion) every
mark stays after the call, as upstream leaves it in the objects, and when the
shell holds together nothing advances the mark before the lock is released.
Cutting that walk short would leave fewer records marked, so it stays
upstream's whole walk. A test now pins that: after such a deletion every face
left in the shell is marked.
Results are unchanged: the same bodies and entity ids, and the same sequence of
marks claimed from the context. What differs is the content of a marks table
after `delete_face`, which no caller reads, and on a B-rep with a broken link
in a part of the shell the walk no longer reaches, the error the whole walk met
there.
Counts (plate minus N cylinders in one `boolean_lists` call, SolidCraft's
fixes), faces marked by shell traversals (`WorkCounters::shell_traversal_faces`):
N = 25: 2475 -> 50
N = 100: 36150 -> 200
law: (7/2) N^2 + (23/2) N -> 2 N, one for each face deleted
Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
bb4e890 to
b8ac8df
Compare
Fixes #3259
delete_facemarks one face for each face it deletes in a batch cut, where it marked the whole shell each time: 2N faces in place of (7/2)N² + (23/2)N for a plate minus N cylinders. No result changes.The problem
After it unlinks a face,
delete_facehas to learn whether the face's vertices are still joined to the shell (if not, the shell has come apart and is split). It asked by walking the shell from the first of those vertices (shell_traversal_with_marks, upstream'sShellTraversal), marking everything the shell joins to it, and then read the mark of each of the face's vertices.The walk did not stop when those vertices were marked. Cutting N cylinders out of a plate in one
boolean_listscall deletes 2N discs. Each has one vertex, marked on the walk's first step, and each deletion went on to mark every face of the plate. The same walk is mechanism C of #3249.The change
topology/topology_traverser.rs.shell_traversal_to_verticesis the shell walk for a caller that asks only which of a set of vertices the shell still joins to the start. It advances the mark once and marks in the same order as the whole walk, and ends as it marks the last vertex asked about. Each one is taken out of the set as it is marked, so those left were not reached, and the walk then covered everything joined to the start.shell_traversalandshell_traversal_with_marksare unchanged: their callers use the lists the whole walk collects.topology/brep/p3.rs,delete_face_nesting. The first walk asks about the deleted face's vertices that are not shell vertices already. The walks that follow when the shell has come apart are the whole ones, as before.delete_face) the marks are the deletion's working state, read for the face's vertices and nothing else: the walk ends at the last of them. In the caller's notification records (delete_face_with_body_identity) every mark stays after the call, as upstream leaves it in the objects, so that walk stays the whole one (see "Not in this PR").WorkCounters::shell_traversal_faces. Counts each face a shell traversal marks, for the test.There is no flag, no cache and no second attempt: the walk is one loop with one end condition, and an empty set asked about is the whole walk.
Counts
Plate
20 n x 20 n x 12minus N = n² cylinders r 4 in oneboolean_listscall, SolidCraft's fixes. Faces marked by shell traversals:The law before was fitted on N = 25 to 400 with a probe; 25 and 100 are the committed test's own output before and after the fix. The result has N + 6 faces, 3N + 12 edges and the same volume to the last digit printed, before and after. For scale only: at N = 200 these walks were about 90 ms of a 950 ms boolean.
Why no result changes
Two things do differ, and no operation reads either:
delete_faceits table holds the marks the question needed, not a marking of the shell. Every caller in the tree hands it a table nothing else reads (a freshMarks, orFaceBuildEnv::marks, which onlydelete_facewrites).Two independent reviews were told to break exactness and accepted the change. One correction came out of them: the description of what the marks table holds afterwards, as written above.
Tests
plate_cut_marks_one_face_for_each_face_it_deletes(committed first, failing with(2475, 36150)against(50, 200)): the cut at 25 and 100 holes, its faces, edges and exact volume, and one face marked for each face deleted.a_shell_walk_asked_about_vertices_ends_at_the_last_of_them: on a box, nothing asked marks 6 faces and 8 vertices; the start alone marks 1 face and 1 vertex; every vertex asked leaves none; a vertex the shell does not join is left after the whole walk.delete_face_with_body_identityunder an outer lock: after the deletion every face left in the shell is still marked in the caller's records.work_counters_count_until_reset: the new counter.Verification
On
b8ac8df8d, rebased on maindc5cfd7e7, Linux (WSL Ubuntu 22.04):cargo fmt --all --check,cargo clippy --workspace --all-targets -- -D warnings,check_panic_sites.py,check_record_ids.py,check-frozen-archives.sh,cargo check -p solidcore --target wasm32-unknown-unknown: all exit 0.--all --goldens:goldens: 283 unit(s), 282 passed, 1 failed. The one iscurve.coniccasegen2235(values.r[4]: 3.776586443654626e-14 -> 3.552713678800501e-14), which differs the same way on unchanged main on that machine (checked onba55b2f71and on7b29ffdb5); this change does not touch it.(2475, 36150); at the head it passes with(50, 200).boolean_api,boolean_no_dangling_vertex_refs,issue_1120_boolean_slitspass. The focused lib tests (topology::brep::p3,topology::topology_traverser,kernel::tests) pass: 46.ba55b2f71, together with four other changes on one branch: the whole workspace suite (7181 passed, 113 skipped) and the kernel rig as CI runs it (1081 cases, 803 pass, 0 fail, 15 known-failing, none moved). Not re-run on this head: CI is the gate.Not attached
Not in this PR
delete_face_with_body_identity(the offset executive's step gaps and crossing loops, the fillet's insertion) keeps the whole walk, because its marks stay in the caller's records and the fillet executive's goldens read raw mark values from those records. Whether the marks a finished deletion leaves there are part of its result is yours to say; with that walk cut short the test added here fails, as it should.delete_facewalks the whole shell to answer a question its first step settles: N² faces marked in a batch cut #3259) would bound it by the smaller piece, but visits in another order.delete_edge's wire branch (wire_split_shell) asks the same kind of question with the same walk, but uses the lists the walk collects. Not measured.🤖 Generated with Claude Code
Summary by CodeRabbit