Skip to content

Commit 3eb3eac

Browse files
authored
feat: add degenerate-bevel-weld example (#112)
Isolates the half-dimension bevel collapse found authoring gltf-export-roundtrip: a slab beveled at offset == min_dim/2 pinches its band into exactly 12 zero-area faces (4 min-axis edges x 3 segments) with 16 coincident verts (4 x (segments+1)) and min_area down 3.2e5x, while the safe offset yields none. The export angle is the point: a stdlib re-parse of the shipped GLB recomputes every triangle area from raw POSITION + indices buffers and counts 32 degenerate triangles crossing the boundary (MEASURED regression constant, re-measure recipe in check) versus 0 for the safe mesh — the loops an engine merge-by-distance welds. Probed five regimes to ground the closed forms (clamp on/off x safe/half/over, modifier vs bmesh path). Byte-identical on 4.5.11 and 5.1.2. Falsified: safe offset at threshold (exit 3), degenerate offset below it (exit 4), coincident closed form corrupted (exit 6), GLB census off (exit 8). Render path gated with check_framing (0.794x/0.356y, margins >= 0.100); dual tray with live-data seam markers. Count prose bumped to 40 across README/CLAUDE/AGENTS. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
1 parent 2974086 commit 3eb3eac

14 files changed

Lines changed: 1207 additions & 7 deletions

File tree

.cursor-plugin/plugin.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
"examples/curve-bevel-arc",
7070
"examples/custom-normals-shade",
7171
"examples/damped-track-aim",
72+
"examples/degenerate-bevel-weld",
7273
"examples/depsgraph-export",
7374
"examples/driver-wave",
7475
"examples/gltf-export-roundtrip",

.github/workflows/blender-smoke.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -554,3 +554,15 @@ jobs:
554554
# non-zero on failure.
555555
xvfb-run -a "$BLENDER" --background \
556556
--python examples/attribute-domain-shear/attribute_domain_shear.py --
557+
558+
- name: Shipped example - degenerate bevel weld (half-dimension collapse)
559+
run: |
560+
set -euo pipefail
561+
# Check only (no render): a 1.6x0.4x1.0 slab beveled at safe (0.10)
562+
# and threshold (0.20 == min/2) offsets; asserts zero vs exactly 12
563+
# zero-area faces (4 min-axis edges x 3 segments), 16 coincident
564+
# verts (4 x (segments+1)), min_area collapse > 1e5x, and a stdlib
565+
# GLB re-parse counting 32 degenerate triangles shipped versus 0.
566+
# Exits non-zero on failure.
567+
xvfb-run -a "$BLENDER" --background \
568+
--python examples/degenerate-bevel-weld/degenerate_bevel_weld.py --

AGENTS.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ a `.cursor-plugin/plugin.json` manifest so the ecosystem drift checker
2020
classifies it as a `cursor-plugin`. This is content the AI loads when the user
2121
asks Blender questions or works on Blender add-ons in Cursor or Claude Code.
2222

23-
The content base is 12 skills, 6 rules, 2 templates, 17 snippets, and 39
23+
The content base is 12 skills, 6 rules, 2 templates, 17 snippets, and 40
2424
examples (counts are CI-enforced against README.md and the manifest). The full
2525
inventory tables and per-item purposes live in `CLAUDE.md`. Example anatomy
2626
and authoring rules: copy `examples/bmesh-gear/`; the render look is specified
@@ -35,7 +35,7 @@ Blender-Developer-Tools/
3535
rules/<rule-name>.mdc # 6 rule files
3636
templates/<template-name>/ # 2 starter templates
3737
snippets/<snippet-name>.py # 17 standalone Python snippets
38-
examples/<name>/ # 39 runnable smoke-gated examples (+ gallery.json)
38+
examples/<name>/ # 40 runnable smoke-gated examples (+ gallery.json)
3939
examples/gallery_framing.py # shared Layer 1 framing measurement (render path only)
4040
scripts/build_gallery.py # generates docs/gallery/ (stdlib only)
4141
scripts/site/ # vendored landing-page build (build_site.py + template)

CLAUDE.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ skills/<skill-name>/SKILL.md - AI workflow definitions, 12 total
2121
rules/<rule-name>.mdc - Anti-pattern rules, 6 total
2222
templates/<template-name>/ - Starter projects, 2 total
2323
snippets/<snippet-name>.py - Standalone code patterns, 17 total
24-
examples/<name>/ - Runnable smoke-gated examples, 39 total (+ gallery.json)
24+
examples/<name>/ - Runnable smoke-gated examples, 40 total (+ gallery.json)
2525
scripts/build_gallery.py - Regenerates docs/gallery/ from gallery.json (stdlib only)
2626
scripts/site/ - Vendored landing-page build (Jinja2)
2727
docs/gallery/ - Committed generated gallery pages + hero renders
@@ -82,11 +82,11 @@ v0.1.0: canonical object creation and deletion, depsgraph evaluated mesh, bmesh
8282

8383
v0.2.0: Principled BSDF material, driver-with-custom-function via `driver_namespace`, application handler registration, shader node group with cross-version `interface` API, `foreach_get` bulk vertex read, version-branch skeleton, and USD export with `evaluation_mode='RENDER'`.
8484

85-
## Examples (39)
85+
## Examples (40)
8686

8787
Runnable scripts at `examples/<name>/`, each asserting a real API contract with
8888
deterministic checks (exit non-zero on failure) and optionally rendering a still via
89-
`--output`. All thirty-nine run headless on Blender 4.5 LTS and 5.1 in `blender-smoke.yml`;
89+
`--output`. All forty run headless on Blender 4.5 LTS and 5.1 in `blender-smoke.yml`;
9090
their renders ship in the site gallery at `docs/gallery/`. `examples/gallery.json` is the
9191
gallery's source of truth. When authoring a new one, copy the anatomy of
9292
`examples/bmesh-gear/` (script structure, README shape, dark-studio render recipe) and

README.md

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</p>
1919

2020
<p align="center">
21-
<strong>12 skills</strong> &nbsp;&bull;&nbsp; <strong>6 rules</strong> &nbsp;&bull;&nbsp; <strong>2 templates</strong> &nbsp;&bull;&nbsp; <strong>17 snippets</strong> &nbsp;&bull;&nbsp; <strong>39 examples</strong>
21+
<strong>12 skills</strong> &nbsp;&bull;&nbsp; <strong>6 rules</strong> &nbsp;&bull;&nbsp; <strong>2 templates</strong> &nbsp;&bull;&nbsp; <strong>17 snippets</strong> &nbsp;&bull;&nbsp; <strong>40 examples</strong>
2222
</p>
2323

2424
<p align="center">
@@ -760,6 +760,26 @@ palette[K−1], and the measured shear matches the palette closed form
760760
`active_color`, the shader Attribute node). The broken state is in-frame:
761761
the right pinwheel is the falsification variant.
762762

763+
</td>
764+
</tr>
765+
<tr>
766+
<td width="46%" valign="middle">
767+
<a href="examples/degenerate-bevel-weld/"><img src="examples/degenerate-bevel-weld/preview.webp" alt="Degenerate bevel weld: two teal display trays side by side — left with a soft rounded bevel, right collapsed to a knife edge with orange seam markers at the zero-area face centroids, proving the half-dimension bevel collapse ships degenerate triangles" /></a>
768+
</td>
769+
<td valign="middle">
770+
771+
### [degenerate-bevel-weld](examples/degenerate-bevel-weld/)
772+
773+
The half-dimension bevel collapse, isolated. A slab beveled at
774+
`offset == min_dim/2` pinches its band into exactly **12 zero-area faces**
775+
(4 min-axis edges × 3 segments) with 16 coincident verts and `min_area`
776+
down 3.2e5× — and a stdlib re-parse of the exported GLB counts **32
777+
degenerate triangles shipped** to disk, where an engine merge-by-distance
778+
welds their loops. Companion to
779+
[`gltf-export-roundtrip`](examples/gltf-export-roundtrip/) (where the count
780+
check first caught the weld) and [`mesh-hygiene-audit`](examples/mesh-hygiene-audit/)
781+
(topology gates). Seam markers are placed from live mesh data.
782+
763783
</td>
764784
</tr>
765785
</table>

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
109109
- ~~Triangulate + tangent-space witness~~ **SHIPPED** as `examples/triangulate-tangents/``calc_tangents` aborts on any ngon (back cap must be an explicit fan); mikktspace matches the edge/UV-delta formula within welding tolerance on smooth fields (2.3e-6 measured); planar UVs on a cylindrical wall collapse tangents onto normals (dot 0.998); `MeshUVLoopLayer` handle dangles across `calc_tangents()` on 4.5 (471 phantom flips, silent exit 0) while the mikktspace math is byte-identical on 4.5.11 and 5.1.2
110110
- UV-handle lifetime snippet: re-fetch attribute/UV layers by name after any CustomData-reallocating call (`calc_tangents`, `VertexGroup.add`, modifier edits) — held handles dangle silently on 4.5, survive by luck on 5.1 (found authoring `triangulate-tangents`)
111111
- glTF skinned-mesh export witness: follow-up to `gltf-export-roundtrip` + `vertex-weight-limit`**SHIPPED** as `examples/gltf-skin-roundtrip/``skins[0].joints` names every bone, JOINTS_0/WEIGHTS_0 with unit sums (3e-8), weights bit-exact, rest matrices to 2.4e-07, deformation to 4.8e-07; exporter welds duplicate loops (sorted-multiset comparisons mispair — compare by rest-key); unparented skinned meshes make the exporter bind an armature by name
112-
- Degenerate-bevel weld hazard (snippet or rule): bevel width ≥ half a box dimension creates zero-area faces whose loops weld on glTF export (found authoring `gltf-export-roundtrip`, where the count check caught a 36-vertex weld)
112+
- ~~Degenerate-bevel weld hazard~~ **SHIPPED** as `examples/degenerate-bevel-weld/` — at `offset == min_dim/2` the bevel band collapses to exactly 12 zero-area faces (4 min-axis edges × 3 segments) with 16 coincident verts (4 × (segments+1)) and min_area down 3.2e5×; a stdlib GLB re-parse counts **32 degenerate triangles crossing the export boundary** (MEASURED regression constant) versus 0 for the safe mesh — the loops an engine merge-by-distance welds; byte-identical on 4.5.11 and 5.1.2
113113
- ~~GAMMA_CROSS blend-curve witness~~ **SHIPPED** as `examples/vse-gamma-cross/` — the cross blends in a gamma-0.5 space: `((1-t)·√A + t·√B)²` with `t = (frame − start)/duration`, never 1 inside the effect; mid-cross dips 0.115 below the sRGB lerp from crimson/teal (closed form (0.341, 0.349, 0.463) confirmed per frame); AgX-default sampling poisons the fit (0.146 red-channel error, `view_transform='Standard'` mandatory); deleting a consumed input orphans-and-deletes the effect — follow-up to `vse-cut-list`
114114
- Falsy `bpy_prop_collection` trap snippet: an empty collection is falsy, so `editor.strips or editor.sequences` silently falls through to the legacy accessor on an empty timeline — always branch on `hasattr`; likely generalizes across the API (found authoring `vse-cut-list`)
115115
- ~~Collision compound witness~~ **SHIPPED** as `examples/collision-hull-proxy/` — game-prop collision as a compound of convex pieces, each a `bmesh.ops.convex_hull` of a coarse `sec(π/n)`-inflated cage (containment 4.4e-08, watertight, positive signed volume, Euler 2, per-piece 255-face budget: body 70, caps 60×3, compound 250); a hull of the dense render mesh measures 380 faces — over budget — which is why pipelines hull cages; proud details cost cage rows, concave grooves are free; byte-identical on 4.5.11 and 5.1.2
9.09 KB
Loading
35.3 KB
Loading

0 commit comments

Comments
 (0)