From db2c162a7900a6b84e6808a115d85ad9f010d41f Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Mon, 12 Oct 2026 19:23:16 +0200 Subject: [PATCH] Spike boolean-weld round 3: seam on the plane, hull flips, cap CDT, local ownership with corners kept Self's calls (flat seams stay on the cutter plane, hull vertices survive, hull-side edge flips, cap CDT, 5 deg guard) and ruling weld-owner-local-corners-kept (per-vertex ownership, cutter and crease corners never merged or moved). Box cut-ins added. Seam off plane 3e-16, crack 0, cap worst angle 0.058 to 5.5 deg; the hull-side worst angle is pinned by near-coincident seam/hull vertex pairs. All corners kept. Co-Authored-By: Claude Opus 5.5 --- docs/research/spike-boolean-weld.md | 117 ++++++++ .../r3_longinus_vs_split.jpg | Bin 0 -> 32801 bytes .../spike-boolean-weld/r3_r3boxflank.jpg | Bin 0 -> 20924 bytes .../spike-boolean-weld/r3_r3boxridge.jpg | Bin 0 -> 14020 bytes .../spike-boolean-weld/r3_r3clamp0.jpg | Bin 0 -> 20537 bytes .../spike-boolean-weld/r3_r3crease.jpg | Bin 0 -> 24249 bytes .../spike-boolean-weld/r3_r3port16.jpg | Bin 0 -> 7506 bytes .../spike-boolean-weld/r3_r3split.jpg | Bin 0 -> 22065 bytes .../spike-boolean-weld/r3_r3splitwide.jpg | Bin 0 -> 19456 bytes .../spike-boolean-weld/r3_r3switch.jpg | Bin 0 -> 8715 bytes spikes/boolean-weld/cutters/cases.json | 88 ++++++ .../boolean-weld/cutters/probe_box_flank.obj | 20 ++ .../boolean-weld/cutters/probe_box_ridge.obj | 20 ++ spikes/boolean-weld/py/case_obox.py | 20 ++ spikes/boolean-weld/py/owner_weld.py | 41 ++- spikes/boolean-weld/py/render_blender.py | 3 +- spikes/boolean-weld/py/round3.py | 280 ++++++++++++++++++ spikes/boolean-weld/py/run2.py | 2 + spikes/boolean-weld/py/weld.py | 6 + .../results/round3/table2-owner-r3a5.md | 24 ++ .../results/round3/table3-boxes-flip.md | 6 + .../round3/table3-short-merge-and-boxes.md | 18 ++ .../results/round3/table3-splits-flip-cdt.md | 20 ++ 23 files changed, 659 insertions(+), 6 deletions(-) create mode 100644 docs/research/spike-boolean-weld/r3_longinus_vs_split.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3boxflank.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3boxridge.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3clamp0.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3crease.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3port16.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3split.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3splitwide.jpg create mode 100644 docs/research/spike-boolean-weld/r3_r3switch.jpg create mode 100644 spikes/boolean-weld/cutters/probe_box_flank.obj create mode 100644 spikes/boolean-weld/cutters/probe_box_ridge.obj create mode 100644 spikes/boolean-weld/py/case_obox.py create mode 100644 spikes/boolean-weld/py/round3.py create mode 100644 spikes/boolean-weld/results/round3/table2-owner-r3a5.md create mode 100644 spikes/boolean-weld/results/round3/table3-boxes-flip.md create mode 100644 spikes/boolean-weld/results/round3/table3-short-merge-and-boxes.md create mode 100644 spikes/boolean-weld/results/round3/table3-splits-flip-cdt.md diff --git a/docs/research/spike-boolean-weld.md b/docs/research/spike-boolean-weld.md index 0fd6612..3f444e2 100644 --- a/docs/research/spike-boolean-weld.md +++ b/docs/research/spike-boolean-weld.md @@ -7,6 +7,123 @@ Agent `spike-bt-boolean-weld`, 2026-10-12. A spike, not a cut: the code under `s Her words, the spec of the rule: "the surface with the tighter curve defines the silhouette of the cut, and the spare vertices that come from the flatter surface all get merged away." +## Round 3: Self's calls and ruling weld-owner-local-corners-kept + +Inputs to this round: + +- Self's calls: a flat split keeps its seam exactly on the cutter's plane; hull vertices survive; hull-side slivers + along a flat seam are repaired by edge flips; the cap faces a cut leaves are ours and get a constrained Delaunay + (CDT); worst-angle guard 5 deg. +- Ruling `weld-owner-local-corners-kept` (operator): ownership is local along the loop, and a hard-edged cutter's + corners always survive. + +How it was run: + +- **Code:** `py/round3.py` for the flat seams; `py/owner_weld.py` with `NOSNAP_FLAT=1 MIN_ANGLE=5` for the curved + cutters. +- **Ownership:** per loop vertex, by curvature along the loop. Nothing in the code takes a loop-wide vote. +- **Protected corners:** loop vertices on a sharp cutter edge (dihedral > 30 deg) or on a hull crease edge. They are + never a merge source and never moved. +- **Edge flips:** greedy. A flip must raise the pair's worst angle and keep both normals within 20 deg. Seam and + crease edges are constrained. +- **CDT:** Shewchuk's Triangle through the `triangle` package (Python wrapper LGPL-3.0; the Triangle core's licence + bars commercial use, so it is fine for this spike and not shippable). Variants: + - `cdt`: plain CDT, loop as the boundary. + - `cdt-q`: CDT with a 20 deg quality bound, interior Steiner points only (`pq20Y`), so the seam is never split. +- **Short seam merge (`short`):** consecutive seam vertices closer than the 0.001 bevel width merge, jointly on both + pieces. Both endpoints lie on the plane, so the seam stays on it. Only boolean-inserted vertices are merged away. +- **New probes:** a box cut into the curved flank (`probe_box_flank`, half-width 0.02) and a box across the dorsal + ridge (`probe_box_ridge`, half-width 0.06). Each is rotated off the tessellation, with its sharp vertical edges on + the loop. + +Tables: `spikes/boolean-weld/results/round3/`. + +### Flat seams (seam on the plane; hull side and cap reported separately) + +| case | variant | hull slivers | hull worst deg | hull p5 deg | cap slivers | cap worst deg | flips | seam off plane | seam crack | seam-adjacent edges < bevel width | +|---|---|---|---|---|---|---|---|---|---|---| +| panel split, subtract piece | naive | 37 | 0.676 | 3.86 | 85 / 122 | 0.058 | 0 | 3e-16 | 0 | 15 | +| | flip + cdt | 28 | 0.676 | 4.69 | 80 / 122 | 0.122 | 0 | 3e-16 | 0 | 15 | +| | flip + cdt-q | 28 | 0.676 | 4.69 | 0 / 332 | 6.76 | 0 | 3e-16 | 0 | 23 | +| | short + flip + cdt-q | 26 | 0.676 | 5.30 | 1 / 313 | 5.50 | 0 | 3e-16 | 0 | 16 | +| panel split, intersect piece | naive | 32 | 0.539 | 4.36 | 85 / 122 | 0.056 | 0 | 3e-16 | 0 | 14 | +| | short + flip + cdt-q | 17 | 0.539 | 6.69 | 1 / 313 | 5.50 | 0 | 3e-16 | 0 | 16 | +| box across the crease, subtract piece | naive | 22 | 0.377 | 3.73 | 22 / 82 | 0.033 | 0 | 7e-16 | 0 | 12 | +| | short + flip + cdt-q | 12 | 2.41 | 6.64 | 3 / 147 | 0.714 | 0 | 7e-16 | 0 | 6 | +| box across the crease, intersect piece | naive | 25 | 0.0405 | 5.61 | 22 / 82 | 0.033 | 0 | 7e-16 | 0 | 11 | +| | short + flip + cdt-q | 10 | 0.0405 | 7.14 | 3 / 147 | 0.714 | 0 | 7e-16 | 0 | 5 | + +- **Flips applied:** 138 and 99 on the split; 65 and 79 on the crease box. +- **Short seam merges:** 3 on the split, 4 refused by the 5 deg guard; 5 on the crease box, 1 refused. They move the + seam polyline by at most 0.0069 % and 0.0040 % of the diagonal, within the plane. +- **The seam stays on the plane to float** (3e-16), the two pieces' seams coincide (crack 0), and nothing flips. +- **The cap CDT with interior refinement removes the cap's slivers.** Cap worst angle 0.058 to 5.5 deg (6.8 deg + without the short merge); the crease box's cap reaches only 0.71 deg. Plain CDT cannot: the cap is a long thin + polygon, and with no interior points its worst angle only reaches 0.12 deg. +- **The hull side's worst angle does not move** (0.676, 0.539 and 0.0405 deg). It is a triangle whose two vertices + are a boolean-inserted seam vertex and a hull vertex almost on top of it. Flips cannot remove the edge between + them: two nearly coincident points stay connected in any Delaunay-like triangulation. With both points pinned (the + seam on the plane, hull vertices surviving), that angle stays. The seam-adjacent edges shorter than the bevel width + remain for the same reason (15 to 16). +- **Render** (`r3_longinus_vs_split.jpg`, `r3_r3split.jpg`): the round-3 seam is straight, crisp and continuous for + most of its length. That is better than round 2's kinks, and its seam no longer shows round 1's sliver highlight. + Where the short seam-adjacent edges sit, the bevel narrows and a light wedge appears: Bevel's overlap clamp (on in + the cage's own modifier) caps the width at the shortest adjacent edge. Turning the clamp off (`r3_r3clamp0.jpg`) + gives a constant-width groove but overlapping bevel geometry: a spike on the round-3 seam, sawtooth on the naive + one. So her uniform Longinus groove is not reached yet. Reaching it needs those near-coincident seam/hull vertex + pairs resolved. Either the hull vertex moves onto the seam within the hull's own surface (it survives, displaced + by less than the bevel width), or Self's no-move call is relaxed for vertices within the bevel width. + +### Curved and hard-edged cutters (owner rule, local ownership, corners kept, 5 deg) + +| case | variant | slivers | worst deg | p5 deg | flips | silhouette dev max | corners kept / total | +|---|---|---|---|---|---|---|---| +| gun port 16 (regression) | naive | 32 | 0.007 | 1.14 | 0 | 0.0198 % | 0 / 0 | +| | owner r3 | 5 | 5.09 | 6.68 | 0 | 0.0198 % | 0 / 0 | +| ownership switch r 0.08 | naive | 112 | 1.2e-6 | 0.62 | 0 | 0.0128 % | | +| | owner r3 | 53 | 1.2e-6 | 2.65 | 0 | 0.323 % | 4 / 4 | +| crease notch 16 | owner r3 | 10 | 4.31 | 6.83 | 0 | 0.061 % | 2 / 2 | +| box in flank (corners on loop) | naive | 3 | 0.285 | 6.62 | 0 | 0 | | +| | owner r3 | 2 | 0.285 | 9.61 | 0 | 0.0108 % | 4 / 4 | +| | hull-side flips (24) | hull 0 (was 2) | hull 6.0 (was 0.95) | hull 12.1 | 0 | 0 | 4 / 4 | +| box across ridge | owner r3 | 66 | 0.070 | 1.41 | 0 | 0.0014 % | 6 / 6 | +| | hull-side flips (118) | hull 7 (was 19) | hull 1.75 (was 0.72) | hull 7.24 | 0 | 0 | 6 / 6 | + +- **Corners:** no corner was moved or lost in any case. `corners_kept_in_place` equals `corners` everywhere, and the + guard turned away 4 merges on the flank box and 6 on the ridge box (`protected_corner`). + - On the ownership-switch probe, the 4 corners are where the r 0.08 cylinder's floor rim breaks through the ridge. + - On the crease notch they are the 2 rim-crease crossings; on the flat splits, the 4 crease crossings. +- **Ownership on the box cut-ins:** the hull owns every stretch (box faces have zero curvature along the loop), no + vertex snaps (flat), and the box contributes its corners. That matches her "keep the first's corners". +- **The ridge box's pocket walls keep 48 slivers.** They are the cutter's own faces, trimmed by the hull. They are + not a planar cap, so this round did not re-triangulate them. +- **The port regression is unchanged** from round 2: slivers 5, worst 5.09 deg, silhouette unchanged. +- **The ownership switch** keeps its owner-side 1.2e-6 deg needle and round 2's 0.32 % loop movement on the + hull-owned stretches, where curved-cutter snapping is still on. + +### Renders, round 3 + +![Longinus vs split, round 3](spike-boolean-weld/r3_longinus_vs_split.jpg) +![split seam](spike-boolean-weld/r3_r3split.jpg) +![split wide](spike-boolean-weld/r3_r3splitwide.jpg) +![clamp off](spike-boolean-weld/r3_r3clamp0.jpg) +![crease box](spike-boolean-weld/r3_r3crease.jpg) +![box in flank](spike-boolean-weld/r3_r3boxflank.jpg) +![box across ridge](spike-boolean-weld/r3_r3boxridge.jpg) +![port regression](spike-boolean-weld/r3_r3port16.jpg) +![switch](spike-boolean-weld/r3_r3switch.jpg) + +### Questions for Self (round 3, text only) + +1. A flat seam's residual worst angle and short bevel-clamped edges come from a boolean-inserted seam vertex lying + almost on a hull vertex. Under "seam on the plane, hull vertices survive" both are pinned. Should a hull vertex + within the bevel width of the seam be allowed to slide onto the seam inside the hull's surface? It still survives, + moved by less than 0.001. +2. Curved-cutter snapping on hull-owned stretches still moves the loop (switch probe 0.32 %). Should the round-3 + no-snap apply to every hull-owned stretch, curved cutter or not? +3. The CDT here uses Triangle, whose licence bars commercial use. A shippable cap CDT needs an owned implementation + or a permissive crate (for example `spade`, a Rust CDT; not evaluated here). + ## Round 2: ruling weld-owner-defines-silhouette The operator answered round 1's question (ruling `weld-owner-defines-silhouette`, superseding diff --git a/docs/research/spike-boolean-weld/r3_longinus_vs_split.jpg b/docs/research/spike-boolean-weld/r3_longinus_vs_split.jpg new file mode 100644 index 0000000000000000000000000000000000000000..9c0e5b6b3cc6ff38164271b0478197c64e6ebce1 GIT binary patch literal 32801 zcmce-1z1$=_BXtTR+LZ>kQgPTMCpb>k(Ta8Is~LagdrrPL6MZ9Tac2HkXAstySp1^ zhHvBZoO7P%od0{?_lxTru00nsv-iH&TKCG|TKjVJauy(xm6VYLAP@k6fPcW{1RxGz zV`5=pVPb=C*x1-OxHs@{!51Mu{%h=KH+VwkR

)q9xyU7vvBk9^6?7@ia(Q(l#-T_RaH~h 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 literal 0 HcmV?d00001 diff --git a/docs/research/spike-boolean-weld/r3_r3boxflank.jpg b/docs/research/spike-boolean-weld/r3_r3boxflank.jpg new file mode 100644 index 0000000000000000000000000000000000000000..aeb8f3650a788778a22e7a0152aa87cd8b5f9dda GIT binary patch literal 20924 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 literal 0 HcmV?d00001 diff --git a/docs/research/spike-boolean-weld/r3_r3boxridge.jpg b/docs/research/spike-boolean-weld/r3_r3boxridge.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b3bf654ddeca22c346e4b18fc45cc700ef2f8323 GIT binary patch literal 14020 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 literal 0 HcmV?d00001 diff --git a/docs/research/spike-boolean-weld/r3_r3clamp0.jpg b/docs/research/spike-boolean-weld/r3_r3clamp0.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c5667a12de3dcfd268469c92de68810a96dde0db GIT binary patch literal 20537 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 literal 0 HcmV?d00001 diff --git a/docs/research/spike-boolean-weld/r3_r3crease.jpg b/docs/research/spike-boolean-weld/r3_r3crease.jpg new file mode 100644 index 0000000000000000000000000000000000000000..481bebf53542f85a48515122cf4f55be9ceb11bc GIT binary patch literal 24249 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 BdpQ6A literal 0 HcmV?d00001 diff --git a/docs/research/spike-boolean-weld/r3_r3port16.jpg b/docs/research/spike-boolean-weld/r3_r3port16.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7a9a0ce28d93b4c9a25a27dc8171b07823472b23 GIT binary patch literal 7506 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

C7LiLx_#Gm>a95m<6;0vPgMX2qWjR6Bfm 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 literal 0 HcmV?d00001 diff --git a/docs/research/spike-boolean-weld/r3_r3switch.jpg b/docs/research/spike-boolean-weld/r3_r3switch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..43399703fdfeb8762f9584bc7026df249a0f60c2 GIT binary patch literal 8715 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 L1X09wl|A|op>zN_ literal 0 HcmV?d00001 diff --git a/spikes/boolean-weld/cutters/cases.json b/spikes/boolean-weld/cutters/cases.json index b5c84cc..649e9d5 100644 --- a/spikes/boolean-weld/cutters/cases.json +++ b/spikes/boolean-weld/cutters/cases.json @@ -253,5 +253,93 @@ "seg": 48, "depth": 0.15, "note": "vertical cylinder r 0.03 through rim crease vertex 35" + }, + { + "name": "probe_box_flank", + "kind": "obox", + "op": "sub", + "cutter": "cutters/probe_box_flank.obj", + "p": [ + 0.17, + 0.0, + 0.10491874328928341 + ], + "n": [ + 0.16691628816232545, + 0.2153707407240696, + 0.9621613153656081 + ], + "R": [ + [ + -0.7999919715756821, + -0.5763261213588748, + 0.16691628816232545 + ], + [ + 0.5999939786817615, + -0.770469123057891, + 0.2153707407240696 + ], + [ + 0.004480062508814215, + 0.27244363133286853, + 0.9621613153656081 + ] + ], + "lo": [ + -0.02, + -0.02, + -0.02 + ], + "hi": [ + 0.02, + 0.02, + 0.03 + ], + "note": "oriented box half-width 0.02" + }, + { + "name": "probe_box_ridge", + "kind": "obox", + "op": "sub", + "cutter": "cutters/probe_box_ridge.obj", + "p": [ + 0.0, + -0.05, + 0.1680719189037999 + ], + "n": [ + -0.09574350308538447, + 0.1106194787646127, + 0.9892403714642789 + ], + "R": [ + [ + -0.7917740521194535, + -0.6032636504939445, + -0.09574350308538447 + ], + [ + 0.5938305390895902, + -0.7969495729106153, + 0.1106194787646127 + ], + [ + -0.14303545446814095, + 0.03073021689327954, + 0.9892403714642789 + ] + ], + "lo": [ + -0.06, + -0.06, + -0.02 + ], + "hi": [ + 0.06, + 0.06, + 0.03 + ], + "note": "oriented box half-width 0.06" } ] \ No newline at end of file diff --git a/spikes/boolean-weld/cutters/probe_box_flank.obj b/spikes/boolean-weld/cutters/probe_box_flank.obj new file mode 100644 index 0000000..c8b9120 --- /dev/null +++ b/spikes/boolean-weld/cutters/probe_box_flank.obj @@ -0,0 +1,20 @@ +v 0.194188036 -0.000897912 0.080137043 +v 0.202533851 0.009870625 0.128245109 +v 0.171134991 -0.031716677 0.091034788 +v 0.179480806 -0.020948140 0.139142854 +v 0.162188357 0.023101847 0.080316246 +v 0.170534172 0.033870384 0.128424311 +v 0.139135312 -0.007716918 0.091213991 +v 0.147481127 0.003051619 0.139322057 +f 1 2 4 +f 5 7 8 +f 1 5 6 +f 3 4 8 +f 1 3 7 +f 2 6 8 +f 1 4 3 +f 5 8 6 +f 1 6 2 +f 3 8 7 +f 1 7 5 +f 2 8 4 diff --git a/spikes/boolean-weld/cutters/probe_box_ridge.obj b/spikes/boolean-weld/cutters/probe_box_ridge.obj new file mode 100644 index 0000000..4c6610a --- /dev/null +++ b/spikes/boolean-weld/cutters/probe_box_ridge.obj @@ -0,0 +1,20 @@ +v 0.085617132 -0.040025248 0.155025426 +v 0.080829957 -0.034494274 0.204487444 +v 0.013225494 -0.135659196 0.158713052 +v 0.008438319 -0.130128222 0.208175070 +v -0.009395754 0.031234417 0.137861171 +v -0.014182929 0.036765391 0.187323190 +v -0.081787392 -0.064399532 0.141548797 +v -0.086574567 -0.058868558 0.191010816 +f 1 2 4 +f 5 7 8 +f 1 5 6 +f 3 4 8 +f 1 3 7 +f 2 6 8 +f 1 4 3 +f 5 8 6 +f 1 6 2 +f 3 8 7 +f 1 7 5 +f 2 8 4 diff --git a/spikes/boolean-weld/py/case_obox.py b/spikes/boolean-weld/py/case_obox.py new file mode 100644 index 0000000..d3e32d3 --- /dev/null +++ b/spikes/boolean-weld/py/case_obox.py @@ -0,0 +1,20 @@ +"""Round 3 case: an oriented box cut into the curved flank (a square port; its 4 vertical edges cross the hull, +putting 4 cutter corners on the loop). Appends to cases.json. Usage: case_obox.py """ +import sys, os, json, numpy as np +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from geo import save_obj +cf, outd = sys.argv[1:3] +cases = [c for c in json.load(open(cf)) if c['kind'] != 'obox'] +base = next(c for c in cases if c['name'] == 'port_flank_s16') +for name, src, half in (('probe_box_flank', base, 0.02), ('probe_box_ridge', next(c for c in cases if c['name'] == 'probe_switch_s48'), 0.06)): + p, n = np.array(src['p']), np.array(src['n']); n /= np.linalg.norm(n) + a = np.cross(n, [0.6, 0.8, 0]); a /= np.linalg.norm(a); b = np.cross(n, a) # rotated off the tessellation + R = np.stack([a, b, n], 1); lo, hi = np.array([-half, -half, -0.02]), np.array([half, half, 0.03]) + Vl = np.array([[x, y, z] for x in (lo[0], hi[0]) for y in (lo[1], hi[1]) for z in (lo[2], hi[2])]) + Vb = p + Vl @ R.T + Q = [[0, 1, 3, 2], [4, 6, 7, 5], [0, 4, 5, 1], [2, 3, 7, 6], [0, 2, 6, 4], [1, 5, 7, 3]] + Tb = np.array([[q[0], q[1], q[2]] for q in Q] + [[q[0], q[2], q[3]] for q in Q]) + if np.einsum('ij,ij->i', Vb[Tb[:, 0]], np.cross(Vb[Tb[:, 1]], Vb[Tb[:, 2]])).sum() < 0: Tb = Tb[:, ::-1] + fn = f'{outd}/{name}.obj'; save_obj(fn, Vb, Tb) + cases.append(dict(name=name, kind='obox', op='sub', cutter=fn, p=p.tolist(), n=n.tolist(), R=R.tolist(), lo=lo.tolist(), hi=hi.tolist(), note=f'oriented box half-width {half}')) +json.dump(cases, open(cf, 'w'), indent=1); print('ok') diff --git a/spikes/boolean-weld/py/owner_weld.py b/spikes/boolean-weld/py/owner_weld.py index 7e177cf..b263de3 100644 --- a/spikes/boolean-weld/py/owner_weld.py +++ b/spikes/boolean-weld/py/owner_weld.py @@ -17,6 +17,29 @@ MIN_ANGLE = float(os.environ.get('MIN_ANGLE', '10')) SNAP = float(os.environ.get('SNAP', '0.25')) # SNAPDIR=reverse: the hull vertex slides onto the loop vertex instead (the loop stays on the cutter; spike variant) SNAPDIR = os.environ.get('SNAPDIR', 'loop-to-hull') +# Round 3 (ruling weld-owner-local-corners-kept + Self's calls): no snapping where the cutter is planar (the seam stays on +# the cutter's plane), and feature corners on the loop always survive: loop vertices on a sharp cutter edge (dihedral +# > CORNER_DEG) or on a hull crease edge are never a merge source and never moved. +NOSNAP_FLAT = os.environ.get('NOSNAP_FLAT', '0') == '1' +CORNER_DEG = 30.0 + + +def sharp_cutter_edges(cut): + from geo import edge_faces, face_normals + FN, _ = face_normals(cut.V, cut.T); out = set() + for e, fs in edge_faces(cut.T).items(): + if len(fs) == 2 and FN[fs[0]] @ FN[fs[1]] < np.cos(np.radians(CORNER_DEG)): + out.add(e) + return out + + +def corners_of(P, info, hull, cut, tol): + sharp = sharp_cutter_edges(cut); out = [] + for v, inf in info.items(): + ce = cut.edge_of(P.V[v], tol) + if (ce is not None and (min(ce), max(ce)) in sharp) or inf['crease']: + out.append(P.V[v].copy()) + return out class Piece: @@ -91,7 +114,7 @@ class Piece: return s.V, np.array([s.faces[i] for i in keep]), np.array([s.prov[i] for i in keep]) -def candidates(P, info, loops, hull, tol, scope_ring=True): +def candidates(P, info, loops, hull, tol, scope_ring=True, cut_is_flat=False): """Operations as (dist, v_pos, w_pos, kind): v merges into w.""" cand = []; loopset = set(info) for L in loops: @@ -116,7 +139,7 @@ def candidates(P, info, loops, hull, tol, scope_ring=True): d = np.linalg.norm(P.V[x] - P.V[v]) if own == 1 and scope_ring: cand.append((d, P.V[x].copy(), P.V[v].copy(), 'ring')) - elif own == 0 and inf['origin'] == 'H' and hull.kd.query(P.V[v])[0] > tol and hull.kd.query(P.V[x])[0] < tol: + elif own == 0 and not (NOSNAP_FLAT and cut_is_flat) and inf['origin'] == 'H' and hull.kd.query(P.V[v])[0] > tol and hull.kd.query(P.V[x])[0] < tol: if d < SNAP * hull.vlen[hull.kd.query(P.V[x])[1]]: if SNAPDIR == 'reverse': cand.append((d, P.V[x].copy(), P.V[v].copy(), 'snap')) @@ -129,10 +152,11 @@ def owner_weld(pieces, hull, cut, tol): """pieces: list of (V, T, prov); one for a cut-in, two for a split. Returns welded pieces, stats, done ops, info.""" Ps = [Piece(*p) for p in pieces] infos = [] - cand = [] + cand = []; corners = [] for P in Ps: loops, le, info = analyse_loops(P.V, np.array([f for f in P.faces]), np.array(P.prov), hull, cut, 'along', tol) - infos.append((loops, info)); cand += candidates(P, info, loops, hull, tol) + infos.append((loops, info)); cand += candidates(P, info, loops, hull, tol, cut_is_flat=cut.kind in ('box', 'obox')) + corners.extend(corners_of(P, info, hull, cut, tol)) cand.sort(key=lambda z: z[0]) stats = defaultdict(int); done = []; seen = set() for d, pv, pw, kind in cand: @@ -140,6 +164,8 @@ def owner_weld(pieces, hull, cut, tol): if key in seen: continue seen.add(key); stats['cand_' + kind] += 1 + if any(np.linalg.norm(pv - q) < tol for q in corners): + stats['protected_corner'] += 1; continue plan, reason = [], None for P in Ps: v = P.at(pv, tol); w = P.at(pw, tol) @@ -165,4 +191,9 @@ def owner_weld(pieces, hull, cut, tol): else: P.apply_move(v, w) stats['applied_' + kind] += 1; done.append((pv, pw)) - return [P.result() for P in Ps], dict(stats), done, infos + res = [P.result() for P in Ps] + kept = 0 + for q in corners: + kept += int(any(np.linalg.norm(Vr[np.unique(Tr)] - q, axis=1).min() < tol for Vr, Tr, _ in res)) + stats['corners'] = len(corners); stats['corners_kept_in_place'] = kept + return res, dict(stats), done, infos diff --git a/spikes/boolean-weld/py/render_blender.py b/spikes/boolean-weld/py/render_blender.py index ee46fe6..0a9eba4 100644 --- a/spikes/boolean-weld/py/render_blender.py +++ b/spikes/boolean-weld/py/render_blender.py @@ -22,7 +22,7 @@ for e in me.edges: w = att.data[e.index].value if att else 0.0 if w > 0: segs.append((cage.matrix_world @ me.vertices[e.vertices[0]].co, cage.matrix_world @ me.vertices[e.vertices[1]].co, w)) -print('CAGE weighted edges', len(segs), 'smooth', [p.use_smooth for p in me.polygons][:1]) +print('CAGE clamp_overlap', bev_src.use_clamp_overlap, 'harden', bev_src.harden_normals, 'WN', wn_src.mode, wn_src.keep_sharp, 'weighted edges', len(segs), 'smooth', [p.use_smooth for p in me.polygons][:1]) # Diagnostic: compare the cage's bevel-weight edges with the crease edges exported with the hull fixture. fx = os.path.join(os.path.dirname(os.path.abspath(__file__)), '..', 'fixtures') HV = [Vector(tuple(map(float, l.split()[1:4]))) for l in open(fx + '/hull_nobevel.obj') if l.startswith('v ')] @@ -102,6 +102,7 @@ def build(stem, tubes): for k in ('width', 'segments', 'limit_method', 'offset_type', 'profile', 'harden_normals', 'miter_outer', 'miter_inner', 'clamp_overlap', 'loop_slide'): if hasattr(bev_src, k): setattr(b, k, getattr(bev_src, k)) + b.use_clamp_overlap = bev_src.use_clamp_overlap if os.environ.get('CLAMP0') != '1' else False w = ob.modifiers.new('WeightedNormal', 'WEIGHTED_NORMAL') for k in ('mode', 'weight', 'thresh', 'keep_sharp', 'use_face_influence'): if hasattr(wn_src, k): diff --git a/spikes/boolean-weld/py/round3.py b/spikes/boolean-weld/py/round3.py new file mode 100644 index 0000000..2e14938 --- /dev/null +++ b/spikes/boolean-weld/py/round3.py @@ -0,0 +1,280 @@ +"""Round 3 (Self's calls): a flat split keeps its seam exactly on the cutter plane. Hull vertices survive; hull-side +slivers along the seam get edge flips only (loop and crease edges constrained); each cap polygon is re-triangulated +with a constrained Delaunay (loop as boundary), plain ('p') or with interior-only Steiner refinement ('pqY'). +Usage: round3.py . Scratch. Uses the `triangle` package (Shewchuk's Triangle).""" +import sys, os, json, numpy as np +from collections import defaultdict +from scipy.spatial import cKDTree +import triangle +sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) +from geo import * +from weld import Hull, Cutter, loops_of, densify, angles + +fx, casesf, runs, outd = sys.argv[1:5] +os.makedirs(outd, exist_ok=True) +hull = Hull(fx); TOL = 1e-6 * hull.diag +cases = {c['name']: c for c in json.load(open(casesf))} +FLIP_DIHEDRAL_COS = np.cos(np.radians(20)) + + +def load_run(name): + V, F = load_obj(f'{runs}/boolmesh_{name}.obj'); T = triangulate(F) + return V, T, np.array([int(x) for x in open(f'{runs}/boolmesh_{name}.prov').read().split()]) + + +def tri_min_angle(V, t): + return min(angles(V[list(t)])) + + +def flip_hull(V, T, prov, loopedges, band_faces): + """Greedy Delaunay-style flips on hull-provenance faces near the loop. Constrained: loop edges, hull crease edges. + A flip must raise the pair's worst angle, keep both normals within 20 deg of the old ones, and not duplicate an edge.""" + T = [list(t) for t in T]; nflip = 0 + crease_pos = hull.crease + for _ in range(20): + ef = defaultdict(list) + for fi, t in enumerate(T): + for k in range(3): + a, b = t[k], t[(k + 1) % 3]; ef[(min(a, b), max(a, b))].append(fi) + changed = False + for e, fs in ef.items(): + if len(fs) != 2 or e in loopedges: + continue + f1, f2 = fs + if prov[f1] or prov[f2] or not (f1 in band_faces or f2 in band_faces): + continue + he = hull.edge_of(V[e[0]], TOL), hull.edge_of(V[e[1]], TOL) + if any(h is not None and h in crease_pos for h in he) and he[0] == he[1]: + continue + a, b = e; c = [x for x in T[f1] if x not in e][0]; d = [x for x in T[f2] if x not in e][0] + if (min(c, d), max(c, d)) in ef: + continue + old = min(tri_min_angle(V, T[f1]), tri_min_angle(V, T[f2])) + # keep orientation: T[f1] has a->b in some order; build new faces with the same winding + t1 = T[f1]; i = t1.index(c); n1 = [t1[i], t1[(i + 1) % 3], t1[(i + 2) % 3]] # c, p, q + p, q = n1[1], n1[2] + new1, new2 = [c, p, d], [c, d, q] + new = min(tri_min_angle(V, new1), tri_min_angle(V, new2)) + if new <= old + 1e-9: + continue + on = [np.cross(V[t[1]] - V[t[0]], V[t[2]] - V[t[0]]) for t in (T[f1], T[f2])] + nn = [np.cross(V[t[1]] - V[t[0]], V[t[2]] - V[t[0]]) for t in (new1, new2)] + on = [x / np.linalg.norm(x) for x in on]; nn = [x / max(np.linalg.norm(x), 1e-300) for x in nn] + if min(o @ n for o in on for n in nn) < FLIP_DIHEDRAL_COS: + continue + T[f1], T[f2] = new1, new2; nflip += 1; changed = True + break # edge map is stale after a flip + if not changed: + break + return np.array(T), nflip + + +def flip_hull_all(V, T, prov, loopedges, band_faces, cap=4000): + total = 0 + for _ in range(cap // 20): + T, n = flip_hull(V, T, prov, loopedges, band_faces); total += n + if n == 0: + break + return T, total + + +def cdt_caps(V, T, prov, cut, opts): + """Replace every cap (cutter-provenance) face set with a CDT of its boundary loop in the cutter plane.""" + loops, _ = loops_of(T, prov) + capF = np.where(prov == 1)[0] + old_n = face_normals(V, T[capF])[0].mean(0) + keep = [i for i in range(len(T)) if prov[i] == 0] + newV = [V]; newT = []; nv = len(V) + for L in loops: + P = V[L]; n, _ = cut.frame(P.mean(0)) + e1 = np.cross(n, [1, 0, 0] if abs(n[0]) < .9 else [0, 1, 0]); e1 /= np.linalg.norm(e1); e2 = np.cross(n, e1) + o = P.mean(0); P2 = np.stack([(P - o) @ e1, (P - o) @ e2], 1) + seg = np.array([[i, (i + 1) % len(L)] for i in range(len(L))]) + r = triangle.triangulate({'vertices': P2, 'segments': seg}, opts) + rv, rt = r['vertices'], r['triangles'] + assert np.allclose(rv[:len(L)], P2), 'Triangle moved a boundary vertex' + idx = list(L) + list(range(nv, nv + len(rv) - len(L))) + if len(rv) > len(L): + newV.append(o + rv[len(L):, :1] * e1 + rv[len(L):, 1:2] * e2); nv += len(rv) - len(L) + for t in rt: + tt = [idx[k] for k in t]; newT.append(tt) + V2 = np.vstack(newV); NT = np.array(newT) + nn = face_normals(V2, NT)[0].mean(0) + if nn @ old_n < 0: + NT = NT[:, ::-1] + T2 = np.vstack([T[keep], NT]); P2 = np.concatenate([prov[keep], np.ones(len(NT), int)]) + return V2, T2, P2 + + +def side_measures(V, T, prov, ref_loop_pts, plane=None): + loops, le = loops_of(T, prov); lv = sorted({v for L in loops for v in L}) + h = float(np.median(hull.vlen[hull.kd.query(V[lv])[1]])) + kd = cKDTree(densify(V, loops)); cen = V[T].mean(1); inb = kd.query(cen)[0] < 2 * h + FN, FA = face_normals(V, T); lmax = np.linalg.norm(V[T] - V[np.roll(T, -1, 1)], axis=2).max(1) + sl = (lmax ** 2 / np.maximum(2 * FA, 1e-300) > 10) | (FA < 1e-4 * FA[prov == 0].mean()) + ang = np.array([tri_min_angle(V, t) for t in T]) + hs, cs = inb & (prov == 0), prov == 1 + flips = 0 + for fi in np.where(hs)[0]: + n, _, _ = hull.frame(cen[fi]); flips += int(FN[fi] @ n < 0) + out = dict(h=h, hull_band_tris=int(hs.sum()), hull_slivers=int((sl & hs).sum()), hull_min_angle=float(ang[hs].min()), + hull_p5=float(np.percentile(ang[hs], 5)), cap_tris=int(cs.sum()), cap_slivers=int((sl & cs).sum()), + cap_min_angle=float(ang[cs].min()) if cs.any() else None, flipped_hull=flips, + loop_verts=len(lv), loop_to_naive_max=float(cKDTree(ref_loop_pts).query(V[lv])[0].max())) + if plane is not None: + n, d = plane; out['seam_off_plane_max'] = float(np.abs(V[lv] @ n - d).max()) + ls = [np.linalg.norm(V[a] - V[b]) for a, b in edge_faces(T) if a in set(lv) or b in set(lv)] + out['loop_edges_under_bevel'] = int(sum(l < 0.001 for l in ls)) + return out + + +def save(stem, V, T, prov): + save_obj(f'{outd}/{stem}.obj', V, T) + _, le = loops_of(T, prov) + with open(f'{outd}/{stem}.loop', 'w') as f: + for a, b in le: + f.write(f'{a} {b} 0\n') + + +def boundary_loops(T): + nxt = {} + ef = edge_faces(T) + for t in T: + for k in range(3): + a, b = int(t[k]), int(t[(k + 1) % 3]) + if len(ef[(min(a, b), max(a, b))]) == 1: + nxt[a] = b + loops, seen = [], set() + for s0 in list(nxt): + if s0 in seen: + continue + L = [s0]; seen.add(s0); x = nxt[s0] + while x != s0 and x not in seen: + L.append(x); seen.add(x); x = nxt[x] + loops.append(L) + return loops + + +def short_loop_merge(pieces, bevel_w=0.001): + """Split only: merge consecutive seam vertices closer than the bevel width, both pieces jointly. Both endpoints lie + on the cutter plane, so the seam stays on it; only boolean-inserted seam vertices are merge sources (hull vertices + survive). Caps are dropped first (rebuilt afterwards by the CDT), so the guard sees hull faces only.""" + import owner_weld as OW + Ps = [OW.Piece(V, T[p == 0], np.zeros(int((p == 0).sum()), int)) for V, T, p in pieces] + L = boundary_loops(np.array([f for f in Ps[0].faces])) + st = defaultdict(int); n = 0 + for lp in L: + for i in range(len(lp)): + a, b = lp[i], lp[(i + 1) % len(lp)] + pa, pb = Ps[0].V[a], Ps[0].V[b] + if not (Ps[0].alive[a] and Ps[0].alive[b]) or np.linalg.norm(pa - pb) >= bevel_w: + continue + src, dst = (pb, pa) if hull.kd.query(pb)[0] > TOL else (pa, pb) + if hull.kd.query(src)[0] < TOL: + st['refused_both_hull_vertices'] += 1; continue + plan = [] + for P in Ps: + v, w = P.at(src, TOL), P.at(dst, TOL) + if v is None or w is None: + plan = None; st['refused_missing'] += 1; break + r = P.check_collapse(v, w) + if r: + plan = None; st['refused_' + r] += 1; break + plan.append((P, v, w)) + if plan: + for P, v, w in plan: + P.apply_collapse(v, w) + n += 1 + out = [] + for P in Ps: + V, T, _ = P.result(); out.append((V, T)) + return out, n, dict(st) + + +def cdt_boundary(V, T, cut, opts, flip_orient): + newV, newT, nv = [V], [], len(V) + for L in boundary_loops(T): + P = V[L]; n, _ = cut.frame(P.mean(0)) + e1 = np.cross(n, [1, 0, 0] if abs(n[0]) < .9 else [0, 1, 0]); e1 /= np.linalg.norm(e1); e2 = np.cross(n, e1) + o = P.mean(0); P2 = np.stack([(P - o) @ e1, (P - o) @ e2], 1) + r = triangle.triangulate({'vertices': P2, 'segments': np.array([[i, (i + 1) % len(L)] for i in range(len(L))])}, opts) + idx = list(L) + list(range(nv, nv + len(r['vertices']) - len(L))) + if len(r['vertices']) > len(L): + newV.append(o + r['vertices'][len(L):, :1] * e1 + r['vertices'][len(L):, 1:2] * e2); nv += len(r['vertices']) - len(L) + # boundary runs a->b in the hull faces, so the cap must run b->a: orient each cap triangle against the loop + tri = [[idx[k] for k in t] for t in r['triangles']] + newT += tri + V2 = np.vstack(newV); NT = np.array(newT) + # orientation: the cap must use each boundary edge in the opposite direction to the hull face that owns it + hd = {(int(t[k]), int(t[(k + 1) % 3])) for t in T for k in range(3)} + bad = sum(1 for t in NT for k in range(3) if (int(t[k]), int(t[(k + 1) % 3])) in hd) + if bad: + NT = NT[:, ::-1] + return V2, np.vstack([T, NT]), np.concatenate([np.zeros(len(T), int), np.ones(len(NT), int)]) + + +rows = [] +for base in [b for b in ('split_fwd', 'probe_crease') if os.environ.get('SKIP_SPLITS') != '1']: + c = cases[base + '_sub']; cut = Cutter(c) + lo, hi = np.array(c['lo']), np.array(c['hi']) + pieces = {'sub': load_run(base + '_sub'), 'int': load_run(base + '_int')} + V0, T0, p0 = pieces['sub']; L0, _ = loops_of(T0, p0); refl = V0[sorted({v for L in L0 for v in L})] + n, _ = cut.frame(refl.mean(0)); plane = (n, float(refl.mean(0) @ n)) + out = {} + for variant, opts in ((('naive', None), ('flip+cdt', 'p'), ('flip+cdt-q', 'pq20Y')) if os.environ.get('ONLY_SHORT') != '1' else (('naive', None),)): + for pk, (V, T, p) in pieces.items(): + if variant != 'naive': + loops, le = loops_of(T, p); lset = {(min(a, b), max(a, b)) for a, b in le} + kd = cKDTree(densify(V, loops)); h = float(np.median(hull.vlen)) + band = set(np.where(kd.query(V[T].mean(1))[0] < 2 * 0.0256)[0].tolist()) + T1, nf = flip_hull_all(V, T, p, lset, band) + V2, T2, P2 = cdt_caps(V, T1, p, cut, opts) + else: + V2, T2, P2, nf = V, T, p, 0 + m = side_measures(V2, T2, P2, refl, plane) + out[(variant, pk)] = (V2, T2, P2) + rows.append(dict(case=f'{base}_{pk}', variant=variant, flips_applied=nf, **m)) + save(f'{base}_{pk}_{variant}', V2, T2, P2) + A, B = out[(variant, 'sub')], out[(variant, 'int')] + la = A[0][sorted({v for L in loops_of(A[1], A[2])[0] for v in L})]; lb = B[0][sorted({v for L in loops_of(B[1], B[2])[0] for v in L})] + rows.append(dict(case=base, variant=f'{variant} seam crack', crack=float(max(cKDTree(lb).query(la)[0].max(), cKDTree(la).query(lb)[0].max())))) + merged, nm, stm = short_loop_merge([pieces['sub'], pieces['int']]) + naive_dense = cKDTree(densify(V0, L0, 64)); res = {} + for pk, (Vm, Tm) in zip(('sub', 'int'), merged): + p0 = np.zeros(len(Tm), int); L = boundary_loops(Tm) + lset = {(min(a, b), max(a, b)) for lp in L for a, b in zip(lp, lp[1:] + lp[:1])} + kd = cKDTree(densify(Vm, L)); band = set(np.where(kd.query(Vm[Tm].mean(1))[0] < 2 * 0.0256)[0].tolist()) + T1, nf = flip_hull_all(Vm, Tm, p0, lset, band) + V2, T2, P2 = cdt_boundary(Vm, T1, cut, 'pq20Y', None) + m = side_measures(V2, T2, P2, refl, plane) + chord = float(naive_dense.query(densify(V2, loops_of(T2, P2)[0], 16))[0].max()) + rows.append(dict(case=f'{base}_{pk}', variant='short+flip+cdt-q', flips_applied=nf, merges=nm, merge_stats=stm, + seam_chord_dev=chord, seam_chord_dev_pct=chord / hull.diag * 100, **m)) + save(f'{base}_{pk}_short+flip+cdt-q', V2, T2, P2); res[pk] = (V2, T2, P2) + A, B = res['sub'], res['int'] + la = A[0][sorted({v for L in loops_of(A[1], A[2])[0] for v in L})]; lb = B[0][sorted({v for L in loops_of(B[1], B[2])[0] for v in L})] + rows.append(dict(case=base, variant='short+flip+cdt-q seam crack', crack=float(max(cKDTree(lb).query(la)[0].max(), cKDTree(la).query(lb)[0].max())))) + print('done', base, nm, stm, flush=True) + +# Box cut-ins into the curved hull: the flat faces own nothing (the hull owns every stretch, corners kept), so the +# hull-side repair is the same edge flips; the pocket walls and floor are the cutter's own faces, not a cap. +for base in [b for b in ('probe_box_flank', 'probe_box_ridge') if b in cases]: + c = cases[base]; cut = Cutter(c); V, T, p = load_run(base) + L0, le = loops_of(T, p); refl = V[sorted({v for L in L0 for v in L})] + lset = {(min(a, b), max(a, b)) for a, b in le} + kd = cKDTree(densify(V, L0)); band = set(np.where(kd.query(V[T].mean(1))[0] < 2 * 0.0256)[0].tolist()) + for variant in ('naive', 'flip'): + T2, nf = (T, 0) if variant == 'naive' else flip_hull_all(V, T, p, lset, band) + m = side_measures(V, T2, p, refl) + rows.append(dict(case=base, variant=variant, flips_applied=nf, **m)); save(f'{base}_{variant}', V, T2, p) + print('done', base, flush=True) + +json.dump(rows, open(f'{outd}/measures3.json', 'w'), indent=1, default=float) +cols = ['case', 'variant', 'merges', 'seam_chord_dev_pct', 'flips_applied', 'hull_band_tris', 'hull_slivers', 'hull_min_angle', 'hull_p5', 'cap_tris', 'cap_slivers', + 'cap_min_angle', 'flipped_hull', 'seam_off_plane_max', 'loop_to_naive_max', 'loop_edges_under_bevel', 'crack'] +f = lambda v: '' if v is None else (f'{v:.3g}' if isinstance(v, float) else str(v)) +with open(f'{outd}/table3.md', 'w') as o: + o.write('| ' + ' | '.join(cols) + ' |\n|' + '---|' * len(cols) + '\n') + for r in rows: + o.write('| ' + ' | '.join(f(r.get(k)) for k in cols) + ' |\n') +print(open(f'{outd}/table3.md').read()) diff --git a/spikes/boolean-weld/py/run2.py b/spikes/boolean-weld/py/run2.py index 960783b..ca3e6d1 100644 --- a/spikes/boolean-weld/py/run2.py +++ b/spikes/boolean-weld/py/run2.py @@ -46,6 +46,8 @@ def row(name, variant, V, T, prov, c, ref, extra): for name, c in cases.items(): + if os.environ.get('CASES') and name not in os.environ['CASES'].split(','): + continue if c['kind'] == 'box' and c['op'] == 'int': continue cut = Cutter(c) diff --git a/spikes/boolean-weld/py/weld.py b/spikes/boolean-weld/py/weld.py index 660569d..cad4b44 100644 --- a/spikes/boolean-weld/py/weld.py +++ b/spikes/boolean-weld/py/weld.py @@ -87,6 +87,8 @@ class Cutter: def __init__(s, c): s.c = c; s.kind = c['kind'] Vc, Fc = load_obj(c['cutter']); s.V, s.T = Vc, triangulate(Fc); s.adj = vertex_adj(s.T, len(Vc)) + if s.kind == 'obox': + s.p = np.array(c['p']); s.R = np.array(c['R']); s.lo = np.array(c['lo']); s.hi = np.array(c['hi']) if s.kind == 'cyl': s.p, s.r = np.array(c['p']), c['r'] s.a = np.array(c['n']) / np.linalg.norm(c['n']) @@ -107,6 +109,10 @@ class Cutter: d = x - s.p; er = d - (d @ s.a) * s.a; er = er / np.linalg.norm(er) et = np.cross(s.a, er) return er, np.outer(et, et) / s.r + if s.kind == 'obox': + xl = s.R.T @ (x - s.p); dd = np.concatenate([xl - s.lo, s.hi - xl]); i = int(np.argmin(np.abs(dd))) + nl = np.zeros(3); nl[i % 3] = -1 if i < 3 else 1 + return s.R @ nl, np.zeros((3, 3)) lo, hi = np.array(s.c['lo']), np.array(s.c['hi']) dd = np.concatenate([x - lo, hi - x]); i = int(np.argmin(np.abs(dd))) n = np.zeros(3); n[i % 3] = -1 if i < 3 else 1 diff --git a/spikes/boolean-weld/results/round3/table2-owner-r3a5.md b/spikes/boolean-weld/results/round3/table2-owner-r3a5.md new file mode 100644 index 0000000..9b35120 --- /dev/null +++ b/spikes/boolean-weld/results/round3/table2-owner-r3a5.md @@ -0,0 +1,24 @@ +| case | variant | applied | tris_band | slivers_band | min_angle_band | p5_angle_band | flipped_hull_band | dev_max_pct | dev_max_h | outside_band_moved | shortest_loop_edge | loop_edges_under_bevel | hull_owned | cutter_owned | split_loop_gap | hull_vertex_surface_dev_h | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| port_flank_s16 | naive | | 157 | 32 | 0.00707 | 1.14 | 0 | 0.0198 | 0.00961 | 0 | 5.29e-05 | 6 | | | | | +| port_flank_s16 | owner-weld[r3a5] | 12 | 133 | 5 | 5.09 | 6.68 | 0 | 0.0198 | 0.00961 | 0 | 0.00226 | 0 | 0 | 36 | | 0.0175 | +| split_fwd_sub | naive | | 507 | 97 | 0.0584 | 1.46 | 0 | 0 | 0 | 0 | 0.000263 | 15 | | | | | +| split_fwd_sub | owner-weld[r3a5] | 0 | 507 | 97 | 0.0584 | 1.46 | 0 | 0 | 0 | 0 | 0.000263 | 15 | 124 | 0 | | 0 | +| split_fwd_int | naive | | 445 | 92 | 0.0555 | 1.52 | 0 | 0 | 0 | 0 | 0.000263 | 14 | | | | | +| split_fwd_int | owner-weld[r3a5] | 0 | 445 | 92 | 0.0555 | 1.52 | 0 | 0 | 0 | 0 | 0.000263 | 14 | 124 | 0 | | 0 | +| split_fwd_sub | seam-gap-naive[r3a5] | | | | | | | | | | | | | | 0 | | +| split_fwd_sub | seam-gap-welded[r3a5] | | | | | | | | | | | | | | 0 | | +| probe_crease_sub | naive | | 342 | 44 | 0.0326 | 2.41 | 0 | 0 | 0 | 0 | 1.46e-05 | 12 | | | | | +| probe_crease_sub | owner-weld[r3a5] | 0 | 342 | 44 | 0.0326 | 2.41 | 0 | 0 | 0 | 0 | 1.46e-05 | 12 | 84 | 0 | | 0 | +| probe_crease_int | naive | | 319 | 47 | 0.0326 | 2.7 | 0 | 0 | 0 | 0 | 1.46e-05 | 11 | | | | | +| probe_crease_int | owner-weld[r3a5] | 0 | 319 | 47 | 0.0326 | 2.7 | 0 | 0 | 0 | 0 | 1.46e-05 | 11 | 84 | 0 | | 0 | +| probe_crease_sub | seam-gap-naive[r3a5] | | | | | | | | | | | | | | 0 | | +| probe_crease_sub | seam-gap-welded[r3a5] | | | | | | | | | | | | | | 0 | | +| probe_switch_s48 | naive | | 488 | 112 | 1.21e-06 | 0.624 | 0 | 0.0128 | 0.00624 | 0 | 6.38e-06 | 31 | | | | | +| probe_switch_s48 | owner-weld[r3a5] | 65 | 364 | 53 | 1.21e-06 | 2.65 | 0 | 0.323 | 0.157 | 0 | 4.71e-05 | 16 | 24 | 108 | | 0.021 | +| probe_crease_cyl_s16 | naive | | 206 | 27 | 0.208 | 2.6 | 0 | 0.0434 | 0.0146 | 0 | 0.000247 | 4 | | | | | +| probe_crease_cyl_s16 | owner-weld[r3a5] | 27 | 133 | 10 | 4.31 | 6.83 | 0 | 0.0609 | 0.0229 | 0 | 0.00135 | 0 | 6 | 35 | | 0.00665 | +| probe_box_flank | naive | | 105 | 3 | 0.285 | 6.62 | 0 | 0 | 0 | 0 | 0.0025 | 0 | | | | | +| probe_box_flank | owner-weld[r3a5] | 2 | 103 | 2 | 0.285 | 9.61 | 0 | 0.0108 | 0.00534 | 0 | 0.0025 | 0 | 16 | 0 | | 0 | +| probe_box_ridge | naive | | 314 | 67 | 0.0703 | 1.36 | 0 | 0 | 0 | 0 | 0.000637 | 4 | | | | | +| probe_box_ridge | owner-weld[r3a5] | 1 | 312 | 66 | 0.0703 | 1.41 | 0 | 0.00142 | 0.000687 | 0 | 0.000637 | 3 | 69 | 0 | | 0 | diff --git a/spikes/boolean-weld/results/round3/table3-boxes-flip.md b/spikes/boolean-weld/results/round3/table3-boxes-flip.md new file mode 100644 index 0000000..b71509f --- /dev/null +++ b/spikes/boolean-weld/results/round3/table3-boxes-flip.md @@ -0,0 +1,6 @@ +| case | variant | flips_applied | hull_band_tris | hull_slivers | hull_min_angle | hull_p5 | cap_tris | cap_slivers | cap_min_angle | flipped_hull | seam_off_plane_max | loop_to_naive_max | loop_edges_under_bevel | crack | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| probe_box_flank | naive | 0 | 83 | 2 | 0.952 | 8.55 | 22 | 1 | 0.285 | 0 | | 0 | 0 | | +| probe_box_flank | flip | 24 | 84 | 0 | 6 | 12.1 | 22 | 1 | 0.285 | 0 | | 0 | 0 | | +| probe_box_ridge | naive | 0 | 243 | 19 | 0.715 | 4.9 | 71 | 48 | 0.0703 | 0 | | 0 | 4 | | +| probe_box_ridge | flip | 118 | 243 | 7 | 1.75 | 7.24 | 71 | 48 | 0.0703 | 0 | | 0 | 4 | | diff --git a/spikes/boolean-weld/results/round3/table3-short-merge-and-boxes.md b/spikes/boolean-weld/results/round3/table3-short-merge-and-boxes.md new file mode 100644 index 0000000..59f9f82 --- /dev/null +++ b/spikes/boolean-weld/results/round3/table3-short-merge-and-boxes.md @@ -0,0 +1,18 @@ +| case | variant | merges | seam_chord_dev_pct | flips_applied | hull_band_tris | hull_slivers | hull_min_angle | hull_p5 | cap_tris | cap_slivers | cap_min_angle | flipped_hull | seam_off_plane_max | loop_to_naive_max | loop_edges_under_bevel | crack | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| split_fwd_sub | naive | | | 0 | 410 | 37 | 0.676 | 3.86 | 122 | 85 | 0.0584 | 0 | 3.05e-16 | 0 | 15 | | +| split_fwd_int | naive | | | 0 | 348 | 32 | 0.539 | 4.36 | 122 | 85 | 0.0555 | 0 | 3.05e-16 | 0 | 14 | | +| split_fwd | naive seam crack | | | | | | | | | | | | | | | 0 | +| split_fwd_sub | short+flip+cdt-q | 3 | 0.00692 | 138 | 407 | 26 | 0.676 | 5.3 | 313 | 1 | 5.5 | 0 | 3.05e-16 | 0 | 16 | | +| split_fwd_int | short+flip+cdt-q | 3 | 0.00692 | 99 | 343 | 17 | 0.539 | 6.69 | 313 | 1 | 5.5 | 0 | 3.05e-16 | 0 | 16 | | +| split_fwd | short+flip+cdt-q seam crack | | | | | | | | | | | | | | | 0 | +| probe_crease_sub | naive | | | 0 | 260 | 22 | 0.377 | 3.73 | 82 | 22 | 0.0326 | 0 | 7.22e-16 | 0 | 12 | | +| probe_crease_int | naive | | | 0 | 237 | 25 | 0.0405 | 5.61 | 82 | 22 | 0.0326 | 0 | 7.22e-16 | 0 | 11 | | +| probe_crease | naive seam crack | | | | | | | | | | | | | | | 0 | +| probe_crease_sub | short+flip+cdt-q | 5 | 0.00404 | 65 | 255 | 12 | 2.41 | 6.64 | 147 | 3 | 0.714 | 0 | 7.22e-16 | 0 | 6 | | +| probe_crease_int | short+flip+cdt-q | 5 | 0.00404 | 79 | 234 | 10 | 0.0405 | 7.14 | 147 | 3 | 0.714 | 0 | 7.22e-16 | 0 | 5 | | +| probe_crease | short+flip+cdt-q seam crack | | | | | | | | | | | | | | | 0 | +| probe_box_flank | naive | | | 0 | 83 | 2 | 0.952 | 8.55 | 22 | 1 | 0.285 | 0 | | 0 | 0 | | +| probe_box_flank | flip | | | 24 | 84 | 0 | 6 | 12.1 | 22 | 1 | 0.285 | 0 | | 0 | 0 | | +| probe_box_ridge | naive | | | 0 | 243 | 19 | 0.715 | 4.9 | 71 | 48 | 0.0703 | 0 | | 0 | 4 | | +| probe_box_ridge | flip | | | 118 | 243 | 7 | 1.75 | 7.24 | 71 | 48 | 0.0703 | 0 | | 0 | 4 | | diff --git a/spikes/boolean-weld/results/round3/table3-splits-flip-cdt.md b/spikes/boolean-weld/results/round3/table3-splits-flip-cdt.md new file mode 100644 index 0000000..f2804ad --- /dev/null +++ b/spikes/boolean-weld/results/round3/table3-splits-flip-cdt.md @@ -0,0 +1,20 @@ +| case | variant | flips_applied | hull_band_tris | hull_slivers | hull_min_angle | hull_p5 | cap_tris | cap_slivers | cap_min_angle | flipped_hull | seam_off_plane_max | loop_to_naive_max | loop_edges_under_bevel | crack | +|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---| +| split_fwd_sub | naive | 0 | 410 | 37 | 0.676 | 3.86 | 122 | 85 | 0.0584 | 0 | 3.05e-16 | 0 | 15 | | +| split_fwd_int | naive | 0 | 348 | 32 | 0.539 | 4.36 | 122 | 85 | 0.0555 | 0 | 3.05e-16 | 0 | 14 | | +| split_fwd | naive seam crack | | | | | | | | | | | | | 0 | +| split_fwd_sub | flip+cdt | 138 | 410 | 28 | 0.676 | 4.69 | 122 | 80 | 0.122 | 0 | 3.05e-16 | 0 | 15 | | +| split_fwd_int | flip+cdt | 99 | 346 | 18 | 0.539 | 5.97 | 122 | 80 | 0.122 | 0 | 3.05e-16 | 0 | 14 | | +| split_fwd | flip+cdt seam crack | | | | | | | | | | | | | 0 | +| split_fwd_sub | flip+cdt-q | 138 | 410 | 28 | 0.676 | 4.69 | 332 | 0 | 6.76 | 0 | 3.05e-16 | 0 | 23 | | +| split_fwd_int | flip+cdt-q | 99 | 346 | 18 | 0.539 | 5.97 | 332 | 0 | 6.76 | 0 | 3.05e-16 | 0 | 22 | | +| split_fwd | flip+cdt-q seam crack | | | | | | | | | | | | | 0 | +| probe_crease_sub | naive | 0 | 260 | 22 | 0.377 | 3.73 | 82 | 22 | 0.0326 | 0 | 7.22e-16 | 0 | 12 | | +| probe_crease_int | naive | 0 | 237 | 25 | 0.0405 | 5.61 | 82 | 22 | 0.0326 | 0 | 7.22e-16 | 0 | 11 | | +| probe_crease | naive seam crack | | | | | | | | | | | | | 0 | +| probe_crease_sub | flip+cdt | 67 | 260 | 19 | 0.377 | 4.25 | 82 | 21 | 0.575 | 0 | 7.22e-16 | 0 | 13 | | +| probe_crease_int | flip+cdt | 79 | 238 | 11 | 0.0405 | 7.1 | 82 | 21 | 0.575 | 0 | 7.22e-16 | 0 | 12 | | +| probe_crease | flip+cdt seam crack | | | | | | | | | | | | | 0 | +| probe_crease_sub | flip+cdt-q | 67 | 260 | 19 | 0.377 | 4.25 | 162 | 4 | 0.714 | 0 | 7.22e-16 | 0 | 18 | | +| probe_crease_int | flip+cdt-q | 79 | 238 | 11 | 0.0405 | 7.1 | 162 | 4 | 0.714 | 0 | 7.22e-16 | 0 | 17 | | +| probe_crease | flip+cdt-q seam crack | | | | | | | | | | | | | 0 |