Skip to content

Commit 2e5c91f

Browse files
TMHSDigitalclaude
andcommitted
fix: cross-version-property-delete presentation, tags hang from the stand
Render path only: the tags hang from the bar on rods instead of floating in front of it. Contract and exit codes unchanged on 4.5.11, 5.1.2 and 5.2.1. Also regenerates the gallery index for all four items. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Signed-off-by: TMHSDigital <154358121+TMHSDigital@users.noreply.github.com>
1 parent 0c56a8a commit 2e5c91f

9 files changed

Lines changed: 79 additions & 18 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ sources are gone, and local Z spans all three steps.
744744
</tr>
745745
<tr>
746746
<td width="46%" valign="middle">
747-
<a href="examples/cross-version-property-delete/"><img src="examples/cross-version-property-delete/preview.webp" alt="Cross-version property delete: two brass specimen tags on a dark studio stand — left tag with a cyan emissive enamel inlay (ID property kept), right tag with an empty grey pocket (ID property deleted)" /></a>
747+
<a href="examples/cross-version-property-delete/"><img src="examples/cross-version-property-delete/preview.webp" alt="Cross-version property delete: two brass specimen tags hanging on rods from a dark studio stand — left tag with a cyan emissive enamel inlay (ID property kept), right tag with an empty grey pocket (ID property deleted)" /></a>
748748
</td>
749749
<td valign="middle">
750750

7.43 KB
Loading
-28.2 KB
Loading

‎docs/gallery/cross-version-property-delete/index.html‎

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@
1616
<meta property="og:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/cross-version-property-delete-hero.webp" />
1717
<meta property="og:image:width" content="1280" />
1818
<meta property="og:image:height" content="720" />
19-
<meta property="og:image:alt" content="Two framed display panels on a stand: the left shows a lit cyan screen, the right a blank grey one." />
19+
<meta property="og:image:alt" content="Two brass tags hanging from a stand on steel rods: the left shows a lit cyan inlay, the right a blank grey pocket." />
2020
<meta property="og:site_name" content="Blender Developer Tools" />
2121
<meta name="twitter:card" content="summary_large_image" />
2222
<meta name="twitter:title" content="cross-version-property-delete — Examples — Blender Developer Tools" />
2323
<meta name="twitter:description" content="Custom ID properties are removed with del, not property_unset. The IDs are built through bpy.data.objects.new so the check does not depend on active_object." />
2424
<meta name="twitter:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/cross-version-property-delete-hero.webp" />
25-
<meta name="twitter:image:alt" content="Two framed display panels on a stand: the left shows a lit cyan screen, the right a blank grey one." />
25+
<meta name="twitter:image:alt" content="Two brass tags hanging from a stand on steel rods: the left shows a lit cyan inlay, the right a blank grey pocket." />
2626
<style>
2727
/* fonts are deployed by the landing build (docs/fonts/) */
2828
@font-face { font-family: 'Barlow Condensed'; font-weight: 600; font-display: swap;
@@ -268,8 +268,8 @@ <h1>cross-version-property-delete</h1>
268268
<p>Custom ID properties are removed with del, not property_unset. The IDs are built through bpy.data.objects.new so the check does not depend on active_object.</p>
269269
</header>
270270
<main id="main">
271-
<button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: Two framed display panels on a stand: the left shows a lit cyan screen, the right a blank grey one.">
272-
<img src="../assets/cross-version-property-delete-hero.webp" alt="Two framed display panels on a stand: the left shows a lit cyan screen, the right a blank grey one." width="1280" height="720" />
271+
<button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: Two brass tags hanging from a stand on steel rods: the left shows a lit cyan inlay, the right a blank grey pocket.">
272+
<img src="../assets/cross-version-property-delete-hero.webp" alt="Two brass tags hanging from a stand on steel rods: the left shows a lit cyan inlay, the right a blank grey pocket." width="1280" height="720" />
273273
</button>
274274
<p class="zoom-hint">Rendered headless by the example itself — click to zoom.</p>
275275
<div class="callout"><span class="tag">witnesses</span> Factory-empty has no active_object; property_unset raises TypeError and leaves the key; del removes it (clear_has=False). --skip-delete and --unset-instead exit 7.</div>
@@ -283,6 +283,8 @@ <h1>cross-version-property-delete</h1>
283283
<p><strong>What it witnesses:</strong> <code>property_unset</code> is a TypeError on a custom ID key and does <strong>not</strong> remove it. <code>del id_block[key]</code> does. After factory-empty, there is no <code>active_object</code>.</p>
284284
<p><strong>What failure each check would catch:</strong></p>
285285
<ul><li>exit 2 — someone set an active object; the snippet <code>__main__</code> would have been the only path</li><li>exit 3 — the ID property never landed</li><li>exit 4 — <code>property_unset</code> removed the key or failed to raise</li><li>exit 7 — <code>del</code> did not run (<code>--skip-delete</code> / <code>--unset-instead</code> falsify this: measured <code>clear_has=True</code>)</li></ul>
286+
<h2>Staging</h2>
287+
<p>The two tags now hang from the stand&#x27;s bar on steel rods. The bar used to run 0.17 m behind the plates with nothing joining them, and the &quot;rings&quot; were solid discs, so the plates hung on air. The bar spans both plates, the post rises to it, and the base is as wide as what it carries. Render path only: the check reads ID properties, never geometry.</p>
286288
<h2>Run</h2>
287289
<pre><code>blender --background --python cross_version_property_delete.py --
288290
blender --background --python cross_version_property_delete.py -- --output tags.png</code></pre>
@@ -508,6 +510,31 @@ <h2>Source</h2>
508510
<span class="k">for</span> ob <span class="k">in</span> stand:
509511
assign(ob, steel)
510512

513+
<span class="c"># Render-only staging: the check reads ID properties, never geometry.</span>
514+
<span class="c"># The build left the bar 0.17 m behind the plates with nothing joining</span>
515+
<span class="c"># them, and the &quot;rings&quot; are solid discs, so the plates hung on air.</span>
516+
<span class="c"># Here the bar runs in the plates&#x27; plane above their tops, spans both,</span>
517+
<span class="c"># and each plate hangs from it on two rods; the post rises to the bar</span>
518+
<span class="c"># and the base is as wide as what it carries.</span>
519+
post, base, bar = stand
520+
plate_top = keep_plate.location.z + <span class="n">0.525</span>
521+
bar_z = plate_top + <span class="n">0.16</span>
522+
bar.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, bar_z)
523+
bar.scale.x = <span class="n">4.4</span> / <span class="n">2.6</span>
524+
post.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.5</span> * bar_z)
525+
post.scale.z = bar_z / <span class="n">1.55</span>
526+
base.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.06</span>)
527+
base.scale.x = <span class="n">4.6</span> / <span class="n">3.4</span>
528+
hangers = []
529+
<span class="k">for</span> plate <span class="k">in</span> (keep_plate, clear_plate):
530+
<span class="k">for</span> dx <span class="k">in</span> (-<span class="n">0.62</span>, <span class="n">0.62</span>):
531+
rod = cylinder(<span class="s">f&quot;</span><span class="s">Hanger</span>{len(hangers)}<span class="s">&quot;</span>, <span class="n">0.018</span>, bar_z - plate_top + <span class="n">0.05</span>,
532+
(plate.location.x + dx, <span class="n">0.0</span>, <span class="n">0.5</span> * (plate_top - <span class="n">0.03</span> + bar_z)), segs=<span class="n">12</span>)
533+
assign(rod, steel)
534+
hangers.append(rod)
535+
<span class="k">for</span> ring <span class="k">in</span> (keep_ring, clear_ring):
536+
ring.hide_render = <span class="k">True</span>
537+
511538
inlay = <span class="k">None</span>
512539
<span class="k">if</span> KEY <span class="k">in</span> keep_plate.keys():
513540
inlay = add_inlay(<span class="s">&quot;Keep&quot;</span>, keep_plate)
@@ -554,13 +581,13 @@ <h2>Source</h2>
554581
light(<span class="s">&quot;Glint&quot;</span>, (<span class="n">1.6</span>, -<span class="n">5.0</span>, <span class="n">6.0</span>), <span class="n">850.0</span>, <span class="n">0.9</span>, (<span class="n">1.0</span>, <span class="n">0.9</span>, <span class="n">0.7</span>), (<span class="n">40</span>, <span class="n">0</span>, <span class="n">18</span>))
555582

556583
aim = bpy.data.objects.new(<span class="s">&quot;Aim&quot;</span>, <span class="k">None</span>)
557-
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">1.15</span>)
584+
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.95</span>)
558585
aim.hide_render = <span class="k">True</span>
559586
scene.collection.objects.link(aim)
560587
cam_data = bpy.data.cameras.new(<span class="s">&quot;Cam&quot;</span>)
561588
cam_data.lens = <span class="n">50.0</span>
562589
cam = bpy.data.objects.new(<span class="s">&quot;Cam&quot;</span>, cam_data)
563-
cam.location = (<span class="n">3.55</span>, -<span class="n">6.59</span>, <span class="n">3.30</span>)
590+
cam.location = (<span class="n">3.95</span>, -<span class="n">7.35</span>, <span class="n">3.3</span>)
564591
scene.collection.objects.link(cam)
565592
scene.camera = cam
566593
track = cam.constraints.new(<span class="s">&quot;TRACK_TO&quot;</span>)
@@ -582,7 +609,7 @@ <h2>Source</h2>
582609
scene.render.filepath = path
583610
scene.view_settings.view_transform = <span class="s">&quot;Standard&quot;</span>
584611

585-
hero = [keep_plate, clear_plate, keep_ring, clear_ring] + list(stand)
612+
hero = [keep_plate, clear_plate] + list(stand) + hangers
586613
<span class="k">if</span> inlay <span class="k">is</span> <span class="k">not</span> <span class="k">None</span>:
587614
hero.append(inlay)
588615
fcode = gallery_framing.check_framing(
@@ -646,7 +673,7 @@ <h2>Source</h2>
646673
</main>
647674
<dialog class="lightbox" id="lightbox" aria-label="Full-size render">
648675
<button class="lightbox-close" type="button" autofocus>Close</button>
649-
<img src="../assets/cross-version-property-delete-hero.webp" alt="Two framed display panels on a stand: the left shows a lit cyan screen, the right a blank grey one." />
676+
<img src="../assets/cross-version-property-delete-hero.webp" alt="Two brass tags hanging from a stand on steel rods: the left shows a lit cyan inlay, the right a blank grey pocket." />
650677
</dialog>
651678
<footer>
652679
<div class="statusbar">

‎docs/gallery/index.html‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ <h2><a href="armature-bend/">armature-bend</a></h2>
552552
</article>
553553
<article class="card" data-tags="curves depsgraph rendering">
554554
<a class="card-media" href="text-version-stamp/" tabindex="-1" aria-hidden="true">
555-
<img src="assets/text-version-stamp-hero.webp" alt="The word BLENDER above the Blender version number in large gold 3D characters." loading="lazy" decoding="async" />
555+
<img src="assets/text-version-stamp-hero.webp" alt="The Blender version number in large gold 3D characters standing on a dark plinth, the word BLENDER set into its face." loading="lazy" decoding="async" />
556556
</a>
557557
<div class="card-body">
558558
<h2><a href="text-version-stamp/">text-version-stamp</a></h2>
@@ -849,7 +849,7 @@ <h2><a href="vertex-color-ao/">vertex-color-ao</a></h2>
849849
</article>
850850
<article class="card" data-tags="context properties">
851851
<a class="card-media" href="cross-version-property-delete/" tabindex="-1" aria-hidden="true">
852-
<img src="assets/cross-version-property-delete-hero.webp" alt="Two framed display panels on a stand: the left shows a lit cyan screen, the right a blank grey one." loading="lazy" decoding="async" />
852+
<img src="assets/cross-version-property-delete-hero.webp" alt="Two brass tags hanging from a stand on steel rods: the left shows a lit cyan inlay, the right a blank grey pocket." loading="lazy" decoding="async" />
853853
</a>
854854
<div class="card-body">
855855
<h2><a href="cross-version-property-delete/">cross-version-property-delete</a></h2>
@@ -1047,12 +1047,12 @@ <h2><a href="wheelbarrow/">wheelbarrow</a></h2>
10471047
</article>
10481048
<article class="card" data-tags="mesh export showcase">
10491049
<a class="card-media" href="anvil/" tabindex="-1" aria-hidden="true">
1050-
<img src="assets/anvil-hero.webp" alt="A black iron anvil mounted on a hooped timber stump." loading="lazy" decoding="async" />
1050+
<img src="assets/anvil-hero.webp" alt="A dark forged-steel anvil with a flared, squared waist, mounted on a hooped timber stump." loading="lazy" decoding="async" />
10511051
</a>
10521052
<div class="card-body">
10531053
<h2><a href="anvil/">anvil</a></h2>
10541054
<p class="teaches">A procedural blacksmith anvil on a timber stump through UVs, bake, LOD, collider, and Unity glTF, asserting recomputed budgets rather than an API contract.</p>
1055-
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 2988 tris, two materials with 1040 wood and 822 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.621×0.414×0.489 m, LOD ratios in band, convex collider 134 tris, hygiene 0, 16 grounded staves, hoop bite 0.003 m, foot-on-head gap 0.003 m, non-empty glTF. --skip-decimate exits 9; --stray-vert 15; --lift-z/--short-staves 16; --float-anvil 17; --round-band 18.</p>
1055+
<p class="witnesses"><span class="tag">witnesses</span> Recomputed: 3052 tris, two materials with 1040 wood and 854 metal faces, UVs in 0..1 with zero AABB overlap, outer AABB 0.621×0.414×0.489 m, LOD ratios in band, convex collider 134 tris, hygiene 0, 16 grounded staves, hoop bite 0.003 m, foot-on-head gap 0.003 m, waist corner reach 0.8572, non-empty glTF. --skip-decimate exits 9; --stray-vert 15; --lift-z/--short-staves 16; --float-anvil 17; --round-band 18; --round-waist 19.</p>
10561056
<a class="card-link" href="anvil/">View showcase piece<span class="sr-only"> anvil</span> <span aria-hidden="true">&rarr;</span></a>
10571057
</div>
10581058
</article>
@@ -1102,7 +1102,7 @@ <h2><a href="hand-pump/">hand-pump</a></h2>
11021102
</article>
11031103
<article class="card" data-tags="mesh export showcase">
11041104
<a class="card-media" href="signpost/" tabindex="-1" aria-hidden="true">
1105-
<img src="assets/signpost-hero.webp" alt="A wooden signpost with two pointed finger boards and dark iron bands on the post." loading="lazy" decoding="async" />
1105+
<img src="assets/signpost-hero.webp" alt="A wooden signpost with two pointed finger boards in worn cream paint and rusted iron bands on the post." loading="lazy" decoding="async" />
11061106
</a>
11071107
<div class="card-body">
11081108
<h2><a href="signpost/">signpost</a></h2>

‎examples/cross-version-property-delete/README.md‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,15 @@ no `active_object`.
2424
- exit 7 — `del` did not run (`--skip-delete` / `--unset-instead` falsify
2525
this: measured `clear_has=True`)
2626

27+
## Staging
28+
29+
The two tags now hang from the stand's bar on steel rods. The bar used
30+
to run 0.17 m behind the plates with nothing joining them, and the
31+
"rings" were solid discs, so the plates hung on air. The bar spans both
32+
plates, the post rises to it, and the base is as wide as what it
33+
carries. Render path only: the check reads ID properties, never
34+
geometry.
35+
2736
## Run
2837

2938
```bash

‎examples/cross-version-property-delete/cross_version_property_delete.py‎

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,31 @@ def render_still(keep_parts, clear_parts, stand, path, engine):
207207
for ob in stand:
208208
assign(ob, steel)
209209

210+
# Render-only staging: the check reads ID properties, never geometry.
211+
# The build left the bar 0.17 m behind the plates with nothing joining
212+
# them, and the "rings" are solid discs, so the plates hung on air.
213+
# Here the bar runs in the plates' plane above their tops, spans both,
214+
# and each plate hangs from it on two rods; the post rises to the bar
215+
# and the base is as wide as what it carries.
216+
post, base, bar = stand
217+
plate_top = keep_plate.location.z + 0.525
218+
bar_z = plate_top + 0.16
219+
bar.location = (0.0, 0.0, bar_z)
220+
bar.scale.x = 4.4 / 2.6
221+
post.location = (0.0, 0.0, 0.5 * bar_z)
222+
post.scale.z = bar_z / 1.55
223+
base.location = (0.0, 0.0, 0.06)
224+
base.scale.x = 4.6 / 3.4
225+
hangers = []
226+
for plate in (keep_plate, clear_plate):
227+
for dx in (-0.62, 0.62):
228+
rod = cylinder(f"Hanger{len(hangers)}", 0.018, bar_z - plate_top + 0.05,
229+
(plate.location.x + dx, 0.0, 0.5 * (plate_top - 0.03 + bar_z)), segs=12)
230+
assign(rod, steel)
231+
hangers.append(rod)
232+
for ring in (keep_ring, clear_ring):
233+
ring.hide_render = True
234+
210235
inlay = None
211236
if KEY in keep_plate.keys():
212237
inlay = add_inlay("Keep", keep_plate)
@@ -253,13 +278,13 @@ def light(name, loc, energy, size, col, rot):
253278
light("Glint", (1.6, -5.0, 6.0), 850.0, 0.9, (1.0, 0.9, 0.7), (40, 0, 18))
254279

255280
aim = bpy.data.objects.new("Aim", None)
256-
aim.location = (0.0, 0.0, 1.15)
281+
aim.location = (0.0, 0.0, 0.95)
257282
aim.hide_render = True
258283
scene.collection.objects.link(aim)
259284
cam_data = bpy.data.cameras.new("Cam")
260285
cam_data.lens = 50.0
261286
cam = bpy.data.objects.new("Cam", cam_data)
262-
cam.location = (3.55, -6.59, 3.30)
287+
cam.location = (3.95, -7.35, 3.3)
263288
scene.collection.objects.link(cam)
264289
scene.camera = cam
265290
track = cam.constraints.new("TRACK_TO")
@@ -281,7 +306,7 @@ def light(name, loc, energy, size, col, rot):
281306
scene.render.filepath = path
282307
scene.view_settings.view_transform = "Standard"
283308

284-
hero = [keep_plate, clear_plate, keep_ring, clear_ring] + list(stand)
309+
hero = [keep_plate, clear_plate] + list(stand) + hangers
285310
if inlay is not None:
286311
hero.append(inlay)
287312
fcode = gallery_framing.check_framing(
6.79 KB
Loading

‎examples/gallery.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@
632632
"name": "cross-version-property-delete",
633633
"dir": "examples/cross-version-property-delete",
634634
"teaches": "Custom ID properties are removed with del, not property_unset. The IDs are built through bpy.data.objects.new so the check does not depend on active_object.",
635-
"alt": "Two framed display panels on a stand: the left shows a lit cyan screen, the right a blank grey one.",
635+
"alt": "Two brass tags hanging from a stand on steel rods: the left shows a lit cyan inlay, the right a blank grey pocket.",
636636
"witnessesFix": "Factory-empty has no active_object; property_unset raises TypeError and leaves the key; del removes it (clear_has=False). --skip-delete and --unset-instead exit 7.",
637637
"hero": "docs/gallery/assets/cross-version-property-delete-hero.webp",
638638
"preview": "examples/cross-version-property-delete/preview.webp",

0 commit comments

Comments
 (0)