Skip to content

Commit f7511b1

Browse files
TMHSDigitalclaude
andauthored
feat(examples): add ray-cast-space and lattice-deform (#304)
ray-cast-space: Object.ray_cast takes object-local coordinates; Scene.ray_cast takes world coordinates. The script casts five closed-form rays at a block that is translated, rotated about Z and scaled unevenly. - Scene.ray_cast hits every world point and normal exactly (exit 3). - Object.ray_cast, given matrix_world.inverted() origins and directions mapped through the 3x3 only, hits the same polygons and maps back within 5e-7 m (exit 4). - World coordinates passed straight to Object.ray_cast land at least 0.287 m from the true hits (exit 5). - --world-to-object exits 4 with a 1.125 m error. lattice-deform: a 2x2x2 KEY_LINEAR lattice with two corners moved through co_deform puts all 602 evaluated vertices on the trilinear closed form, max error 5.6e-7, through a translated and unevenly scaled lattice. - An unmoved lattice is the identity (exit 3). - The mesh really does move (exit 5). - --bspline, the default interpolation, misses by 0.2145 and exits 4. Both pass the default check on Blender 4.5.11, 5.1.2 and 5.2.1, and each falsifier is proven on 4.5.11 and 5.2.1. Contact sheets are committed under docs/gallery/contact-sheets/. Counts are updated in README, AGENTS, CLAUDE and new-example-prompt; the AGENTS and CLAUDE showcase counts are corrected from 67 to 72 as well. Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent d800a39 commit f7511b1

75 files changed

Lines changed: 3808 additions & 148 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.cursor-plugin/plugin.json‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@
136136
"examples/vse-cut-list",
137137
"examples/vse-gamma-cross",
138138
"examples/vse-linear-modifiers",
139-
"examples/wave-displace"
139+
"examples/wave-displace",
140+
"examples/ray-cast-space",
141+
"examples/lattice-deform"
140142
],
141143
"showcase": [
142144
"showcase/shipping-crate",

‎AGENTS.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ 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 16 skills, 9 rules, 3 templates, 27 snippets, 60
24-
examples, and 67 showcase pieces (counts are CI-enforced against README.md)
23+
The content base is 16 skills, 9 rules, 3 templates, 27 snippets, 62
24+
examples, and 72 showcase pieces (counts are CI-enforced against README.md)
2525
and the manifest). The full inventory tables and per-item purposes live in
2626
`CLAUDE.md`. Example anatomy and authoring rules: copy `examples/bmesh-gear/`;
2727
showcase conventions: `showcase/README.md`. The render look is specified
@@ -36,7 +36,7 @@ Blender-Developer-Tools/
3636
rules/<rule-name>.mdc # 9 rule files
3737
templates/<template-name>/ # 3 starter templates
3838
snippets/<snippet-name>.py # 27 standalone Python snippets
39-
examples/<name>/ # 60 runnable smoke-gated examples (+ gallery.json)
39+
examples/<name>/ # 62 runnable smoke-gated examples (+ gallery.json)
4040
examples/gallery_framing.py # shared Layer 1 framing measurement (render path only)
4141
showcase/<name>/ # budget-conformance props (sibling of examples/)
4242
showcase/gallery.json # this tree's gallery index; merged into docs/gallery/

‎CLAUDE.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ skills/<skill-name>/SKILL.md - AI workflow definitions, 16 total
2121
rules/<rule-name>.mdc - Anti-pattern rules, 9 total
2222
templates/<template-name>/ - Starter projects, 3 total
2323
snippets/<snippet-name>.py - Standalone code patterns, 27 total
24-
examples/<name>/ - Runnable smoke-gated examples, 60 total (+ gallery.json)
25-
showcase/<name>/ - Budget-conformance props, 67 pieces, each with a gallery `category` (sibling of examples/; see showcase/README.md § Categories)
24+
examples/<name>/ - Runnable smoke-gated examples, 62 total (+ gallery.json)
25+
showcase/<name>/ - Budget-conformance props, 72 pieces, each with a gallery `category` (sibling of examples/; see showcase/README.md § Categories)
2626
scripts/build_gallery.py - Regenerates docs/gallery/ from examples/gallery.json + showcase/gallery.json
2727
scripts/site/ - Vendored landing-page build (Jinja2); tokens.css is the shared palette
2828
tests/check_site_links.py - Internal link/anchor/alt gate over the built site (docs/)
@@ -105,12 +105,12 @@ v0.2.0: Principled BSDF material, driver-with-custom-function via `driver_namesp
105105

106106
AI asset pipeline track: `decimate_to_budget.py`, `convex_hull_collider.py`, `lod_chain.py` (helper duplicated, not imported), `gltf_draco_export.py`, `export_preset_unity.py`, `export_preset_godot.py`, `export_preset_unreal.py`, `setup_bake_target_image.py`, `bake_normal_high_to_low.py`, `save_baked_image.py`.
107107

108-
## Examples (60)
108+
## Examples (62)
109109

110110
Runnable scripts at `examples/<name>/`, each asserting a real API contract with
111111
deterministic checks (exit non-zero on failure) and optionally rendering a still via
112112
`--output`. All of them run headless on Blender 5.2 LTS and 4.5 LTS in `blender-smoke.yml` (5.1 on the weekly cron, the `needs-5.1` PR label, or manual dispatch);
113-
**52 of the 60 ship a render in the site gallery** at `docs/gallery/`. The other
113+
**54 of the 62 ship a render in the site gallery** at `docs/gallery/`. The other
114114
eight are **check-only**: they carry no `--output` path, no gallery entry, and no
115115
hero asset. The criterion is whether the contract is expressible in pixels. An
116116
example is check-only when its witness is a data or state fact that no scene

‎README.md‎

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
</p>
1919

2020
<p align="center">
21-
<strong>16 skills</strong> &nbsp;&bull;&nbsp; <strong>9 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>27 snippets</strong> &nbsp;&bull;&nbsp; <strong>60 examples</strong> &nbsp;&bull;&nbsp; <strong>72 showcase pieces</strong>
21+
<strong>16 skills</strong> &nbsp;&bull;&nbsp; <strong>9 rules</strong> &nbsp;&bull;&nbsp; <strong>3 templates</strong> &nbsp;&bull;&nbsp; <strong>27 snippets</strong> &nbsp;&bull;&nbsp; <strong>62 examples</strong> &nbsp;&bull;&nbsp; <strong>72 showcase pieces</strong>
2222
</p>
2323

2424
<p align="center">
@@ -37,7 +37,7 @@
3737

3838
## Overview
3939

40-
This repository ships **16 skills, 9 rules, 3 templates, 27 snippets, 60 examples, and 72 showcase pieces** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
40+
This repository ships **16 skills, 9 rules, 3 templates, 27 snippets, 62 examples, and 72 showcase pieces** for Blender Python development targeting Blender 5.2 LTS (current stable) with Blender 4.5 LTS fallback support. Blender 5.1 is prior stable.
4141

4242
The content is consumed by AI coding agents reading these files directly from a checkout — **there is no MCP server in this repository, and none is required**. Cursor applies `rules/*.mdc` automatically wherever their scope globs match and takes skills by name in chat; Claude Code reads `skills/` and `rules/` from the project workspace, or from this repo kept as a referenced checkout. Any agent that can read files in a workspace can use it the same way. There is no build step for the content — edit the Markdown and Python files directly.
4343

@@ -75,7 +75,7 @@ blender --background --python examples/bmesh-gear/bmesh_gear.py --
7575

7676
## Falsifiers
7777

78-
Every one of the 60 examples carries a **falsifier**: a flag that changes the
78+
Every one of the 62 examples carries a **falsifier**: a flag that changes the
7979
input so a real assertion fails. It never disables the assertion, skips the
8080
check, or short-circuits to an error — it feeds the script something the
8181
contract says must not pass, and the same check that guards the happy path
@@ -451,7 +451,7 @@ Gallery still is a sky-lit obelisk diptych (8° dusk | 55° midday) so the contr
451451
</details>
452452

453453
<details>
454-
<summary><strong>Mesh, curves &amp; text</strong> — 13 examples</summary>
454+
<summary><strong>Mesh, curves &amp; text</strong> — 15 examples</summary>
455455

456456
<table>
457457
<tr>
@@ -648,6 +648,37 @@ check-only, no gallery still — no geometry
648648
getattr is `AttributeError` on 5.2.1. Version-guarded `hasattr` then read
649649
exits 0 on all three. `--assume-present` is red only on 5.2.
650650

651+
</td>
652+
</tr>
653+
<tr>
654+
<td width="46%" valign="middle">
655+
<a href="examples/ray-cast-space/"><img src="examples/ray-cast-space/preview.webp" alt="Ray cast space: a teal-checkered block turned and stretched on a walnut plinth, five orange rays from brass emitter balls ending in orange hit rings on three of its faces, and one red ray from the world-coordinate trap striking it from somewhere else, on a dark studio floor" /></a>
656+
</td>
657+
<td valign="middle">
658+
659+
### [ray-cast-space](examples/ray-cast-space/)
660+
661+
`Object.ray_cast` vs `Scene.ray_cast` — five closed-form rays on a translated, Z-rotated,
662+
non-uniformly scaled block. `Scene.ray_cast` hits each world point and normal exactly.
663+
`Object.ray_cast`, fed `matrix_world.inverted()` origins and 3×3-only directions, hits the
664+
same polygons and maps back within 5e-7 m. Raw world coordinates handed to `Object.ray_cast`
665+
land 0.287 m or more away (`--world-to-object` exits 4).
666+
667+
</td>
668+
</tr>
669+
<tr>
670+
<td width="46%" valign="middle">
671+
<a href="examples/lattice-deform/"><img src="examples/lattice-deform/preview.webp" alt="Lattice deform: two checker-glazed columns on a walnut plinth, each in a steel-rod cage - the left upright in its rest cage, the right sheared and tapered where two orange cage corners have been pulled out" /></a>
672+
</td>
673+
<td valign="middle">
674+
675+
### [lattice-deform](examples/lattice-deform/)
676+
677+
A 2×2×2 Lattice modifier set to `'KEY_LINEAR'`, with two corners moved through
678+
`LatticePoint.co_deform`. Witnesses that every evaluated vert lands on the trilinear
679+
blend of the eight deformed corners, and that the default `'KEY_BSPLINE'` misses it
680+
(`--bspline` exits 4).
681+
651682
</td>
652683
</tr>
653684
</table>

‎ROADMAP.md‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,13 +163,14 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
163163
- `persistent` app-handler witness — handlers registered without `@bpy.app.handlers.persistent` are dropped by `wm.read_homefile`/file load while persistent ones survive; assert the registered-handler set before and after a reload (silent loss AI code hits)
164164
- Link vs append witness — `bpy.data.libraries.load(link=True)` yields a linked, non-editable datablock (`library` set, `is_editable` False) while append yields a local copy; write a temp .blend with `bpy.data.libraries.write`, then assert both paths
165165
- Orphan purge witness — `bpy.data.orphans_purge(do_recursive=...)` removes exactly the zero-user datablocks computed independently beforehand, and `bpy.data.batch_remove` removes a given set in one call (counts closed-form)
166-
- Local vs world ray-cast witness — `Object.ray_cast` takes object-local origin/direction while `Scene.ray_cast` takes world space; a transformed target hit at closed-form points both ways, and passing world coords to `Object.ray_cast` misses (the trap)
166+
- ~~Local vs world ray-cast witness~~ **SHIPPED** as `examples/ray-cast-space/` — five closed-form rays on a translated, Z-rotated, non-uniformly scaled block: `Scene.ray_cast` exact in world space, `Object.ray_cast` exact through `matrix_world.inverted()` (3×3-only directions, inverse-transpose normals, same polygon index); world coords fed to `Object.ray_cast` land ≥ 0.287 m off; `--world-to-object` exits 4
167167
- `mathutils.bvhtree` witness — `BVHTree.FromObject` on evaluated geometry: `find_nearest` distances and `overlap` pairs match closed forms on a known arrangement
168168
- Curves datablock witness — the modern `bpy.types.Curves` hair API (`add_curves`, `position` attribute, `curve_offset_data`) vs legacy particle hair: point and curve counts closed-form, every root on the emitter surface
169169
- Non-Color normal-map witness — an image used as a normal map must be `colorspace_settings.name = 'Non-Color'`; a baked/sampled value round-trips only then, and sRGB shifts it by the transfer curve (a silent shading error AI code ships)
170170
- Solidify even-thickness witness — `use_even_offset` keeps shell thickness constant at sharp corners, while without it the corner thins by 1/cos(θ/2): measured thickness at a known angle matches both closed forms
171171
- Boolean solver witness — EXACT solver union/difference volumes match closed forms on overlapping primitives, and the result is manifold; FAST on coplanar faces is the documented failure case
172-
- Lattice deform witness — a 2×2×2 lattice with one displaced point deforms interior verts by exact trilinear weights (`interpolation_type_u/v/w = 'KEY_LINEAR'`)
172+
- ~~Lattice deform witness — a 2×2×2 lattice with one displaced point deforms interior verts by exact trilinear weights (`interpolation_type_u/v/w = 'KEY_LINEAR'`)~~ **SHIPPED** as `examples/lattice-deform/` — 602 evaluated verts on the trilinear closed form (5.6e-7) through a translated, non-uniformly scaled lattice; `--bspline` (the default interpolation) exits 4 at 0.2145
173+
- Lattice `use_outside` / outside-vertex witness — verts outside a KEY_LINEAR lattice's volume: assert the documented clamp or extrapolation closed form, and `use_outside` (deform only the outer points) against an interior-only control move
173174
- FBX unit-scale witness — `export_scene.fbx` with `apply_unit_scale` / `apply_scale_options` changes exported coordinates by the closed-form ×100 cm factor; re-import restores metres only on the matching option
174175
- Collection-instance witness — `instance_type = 'COLLECTION'` instances appear only in `depsgraph.object_instances` (never in `scene.objects`); instance world matrices match the closed-form offsets
175176
- Asset-marking witness — `ID.asset_mark()`, `asset_data.tags` and catalog UUIDs written to `blender_assets.cats.txt`, re-read from a saved library file (check-only if no legible still)

‎docs/gallery/armature-bend/index.html‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ <h1>Armature Bend</h1>
4545
<p>Rigging end to end in the data API — edit_bones chain construction, name-bound vertex groups with smoothstep blend zones, posing, and depsgraph evaluation — bending a ribbed bellows hose through rest, half, and full curl.</p>
4646
</header>
4747
<main id="main">
48-
<nav class="pager" aria-label="Examples"><a rel="prev" href="../grease-pencil-rosette/" aria-label="Previous example: grease-pencil-rosette"><span aria-hidden="true">&larr;</span> grease-pencil-rosette</a><span class="pager-pos hud">19 of 52 examples<span class="pager-keys"> &middot; <kbd>&larr;</kbd><kbd>&rarr;</kbd></span></span><a rel="next" href="../text-version-stamp/" aria-label="Next example: text-version-stamp">text-version-stamp <span aria-hidden="true">&rarr;</span></a></nav>
48+
<nav class="pager" aria-label="Examples"><a rel="prev" href="../grease-pencil-rosette/" aria-label="Previous example: grease-pencil-rosette"><span aria-hidden="true">&larr;</span> grease-pencil-rosette</a><span class="pager-pos hud">19 of 54 examples<span class="pager-keys"> &middot; <kbd>&larr;</kbd><kbd>&rarr;</kbd></span></span><a rel="next" href="../text-version-stamp/" aria-label="Next example: text-version-stamp">text-version-stamp <span aria-hidden="true">&rarr;</span></a></nav>
4949
<button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: Three ribbed bellows hoses with brass collars on steel foot flanges, banded teal to amber to coral, standing straight, half curled and fully curled by an armature.">
5050
<img src="../assets/armature-bend-hero.webp" alt="Three ribbed bellows hoses with brass collars on steel foot flanges, banded teal to amber to coral, standing straight, half curled and fully curled by an armature." width="1280" height="720" fetchpriority="high" />
5151
</button>
@@ -1050,7 +1050,7 @@ <h3><a class="stretch" href="../lod-decimate-chain/">LOD Decimate Chain</a></h3>
10501050
</article>
10511051
</div>
10521052
</section>
1053-
<nav class="pager pager-foot" aria-label="Examples"><a rel="prev" href="../grease-pencil-rosette/" aria-label="Previous example: grease-pencil-rosette"><span aria-hidden="true">&larr;</span> grease-pencil-rosette</a><span class="pager-pos hud">19 of 52 examples</span><a rel="next" href="../text-version-stamp/" aria-label="Next example: text-version-stamp">text-version-stamp <span aria-hidden="true">&rarr;</span></a></nav>
1053+
<nav class="pager pager-foot" aria-label="Examples"><a rel="prev" href="../grease-pencil-rosette/" aria-label="Previous example: grease-pencil-rosette"><span aria-hidden="true">&larr;</span> grease-pencil-rosette</a><span class="pager-pos hud">19 of 54 examples</span><a rel="next" href="../text-version-stamp/" aria-label="Next example: text-version-stamp">text-version-stamp <span aria-hidden="true">&rarr;</span></a></nav>
10541054
</main>
10551055
<dialog class="lightbox" id="lightbox" aria-label="Full-size render">
10561056
<div class="lightbox-bar"><button class="lightbox-close" id="lightboxNative" type="button" aria-pressed="false">Actual size</button><button class="lightbox-close" id="lightboxClose" type="button" autofocus>Close</button></div>
20.2 KB
Loading
62.5 KB
Loading
9.73 KB
Loading
29.7 KB
Loading

0 commit comments

Comments
 (0)