Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
- UV atlas pack witness — lightmap/unique UV island packing with non-overlap + utilization closed forms (deferred behind hygiene/origin; overlaps `uv-layer-grid` / `triangulate-tangents` / `gltf-export-roundtrip` UV arc)
- Attribute domain witness: writing a POINT-domain color attribute and reading it as if it were CORNER (or vice versa) silently shears colors across shared verts — companion to `color-attribute-wheel`

- Gallery framing recomposition worklist (corrected survey after the survey scene-capture fix; these need real reframing, not a camera nudge — the seven marginal cases were swept mechanically and wired to `check_framing`): **depsgraph-export** (fill 0.984x over; left cube amputated by left+bottom edges — unclipping it changes the composition substantially), **vertex-weight-limit** (fill 0.247x/0.861y; base cropped at bottom edge, off-center with dead space left), **text-version-stamp** (max fill 0.663 under; dead lower third — needs vertical rebalance), **triangulate-tangents** (fill 0.334x/0.661y under; buckler small in a large stage, needs a 2x-class move-in). Already handled: the seven marginal cases (curve-bevel-arc, png-exr-alpha, swatch-grid, driver-wave, shader-node-group, gp-lineart-contour, vse-cut-list) were camera-nudged into the band and gated; deviations documented in #108 (damped-track-aim, wave-displace); sky-texture-sun-elevation is a measurement artifact (world subject — report-only if wired).
- ~~Gallery framing recomposition worklist~~ **DRAINED**: all four recomposition cases restaged and gated with `check_framing` — depsgraph-export (0.984x/touch→0.812x; cube+dome recentered, aim-based camera), vertex-weight-limit (touch B→0.867y; more frontal camera, lower aim, base in frame), triangulate-tangents (0.334x→0.833y; 2x-class move-in plus Key/Glint trim that also cleared a pre-existing dome blowout), text-version-stamp (0.663x→0.719x; moved in with aim on the stamp center, dead third gone). The seven marginal cases were camera-nudged in the preceding sweep; deviations documented in #108 (damped-track-aim, wave-displace); sky-texture-sun-elevation is a measurement artifact (world subject — report-only if wired). Remaining: nothing on the framing worklist — future examples inherit the gate at authoring time.

## Future (uncommitted)

Expand Down
Binary file modified docs/gallery/assets/depsgraph-export-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/assets/text-version-stamp-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/assets/triangulate-tangents-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/assets/vertex-weight-limit-hero.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/contact-sheets/depsgraph-export-contact-sheet.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/gallery/contact-sheets/vertex-weight-limit-contact-sheet.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
40 changes: 34 additions & 6 deletions docs/gallery/depsgraph-export/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ <h2>Run</h2>
# Write the exported OBJ to a specific path:
blender --background --python depsgraph_export.py -- --obj exported.obj</code></pre>
<p>It exits non-zero on failure (modifier not applied, or exported count ≠ evaluated count). The <code>blender-smoke</code> workflow runs this check on Blender 4.5 LTS and 5.1: base 8 → evaluated/exported 98 vertices with a 2-level SUBSURF.</p>
<p>The <code>--output</code> render path additionally measures framing against the Layer 1 band via <code>examples/gallery_framing.py</code> (exit 10 on violation) before writing the still.</p>
</section>
<section class="detail-section src">
<h2>Source</h2>
Expand All @@ -219,6 +220,11 @@ <h2>Source</h2>
&quot;&quot;&quot;</span>
<span class="k">import</span> bpy, bmesh, sys, os, math, argparse, tempfile

<span class="c"># Shared Layer 1 framing measurement (render path only) — see gallery_framing.py</span>
sys.path.insert(<span class="n">0</span>, os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
sys.dont_write_bytecode = <span class="k">True</span> <span class="c"># keep examples/__pycache__ out of the repo tree</span>
<span class="k">import</span> gallery_framing


<span class="k">def</span> build():
bpy.ops.wm.read_factory_settings(use_empty=<span class="k">True</span>)
Expand Down Expand Up @@ -353,9 +359,18 @@ <h2>Source</h2>
cam_data = bpy.data.cameras.new(<span class="s">&quot;Cam&quot;</span>)
cam_data.lens = <span class="n">50.0</span>
cam = bpy.data.objects.new(<span class="s">&quot;Cam&quot;</span>, cam_data)
cam.location = (<span class="n">0.0</span>, -<span class="n">7.4</span>, <span class="n">3.0</span>)
cam.rotation_euler = (math.radians(<span class="n">76</span>), <span class="n">0.0</span>, <span class="n">0.0</span>)
<span class="c"># Reframed: the rotated base cube spans ~5.9 units with the evaluated</span>
<span class="c"># sphere, wider than the old (0,-7.4,3.0) frame — the cube was amputated</span>
<span class="c"># at the left edge. Aim-based framing on the pair&#x27;s center.</span>
cam.location = (<span class="n">0.0</span>, -<span class="n">9.6</span>, <span class="n">2.6</span>)
scene.collection.objects.link(cam)
aim = bpy.data.objects.new(<span class="s">&quot;Aim&quot;</span>, <span class="k">None</span>)
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.9</span>)
scene.collection.objects.link(aim)
con = cam.constraints.new(<span class="s">&#x27;TRACK_TO&#x27;</span>)
con.target = aim
con.track_axis = <span class="s">&#x27;TRACK_NEGATIVE_Z&#x27;</span>
con.up_axis = <span class="s">&#x27;UP_Y&#x27;</span>
scene.camera = cam

scene.render.engine = <span class="s">&#x27;CYCLES&#x27;</span> <span class="k">if</span> engine == <span class="s">&#x27;cycles&#x27;</span> <span class="k">else</span> eevee_engine_id()
Expand All @@ -373,8 +388,21 @@ <h2>Source</h2>
<span class="c"># AgX would flatten the graphite-vs-green contrast this still hinges on</span>
<span class="c"># (docs/VISUAL-STYLE.md)</span>
scene.view_settings.view_transform = <span class="s">&#x27;Standard&#x27;</span>
<span class="c"># Layer 1 framing gate (silhouette matte) — exit 10 on violation, before</span>
<span class="c"># the beauty render so a defective composition ships no artifact.</span>
fcode = gallery_framing.check_framing(
scene, cam,
hero=[base_obj, obj],
elements=[base_obj, obj],
stage=[floor, wall],
)
<span class="k">if</span> fcode:
<span class="k">return</span> fcode
bpy.ops.render.render(write_still=<span class="k">True</span>)
<span class="k">return</span> os.path.exists(path) <span class="k">and</span> os.path.getsize(path) &gt; <span class="n">0</span>
<span class="k">if</span> <span class="k">not</span> (os.path.exists(path) <span class="k">and</span> os.path.getsize(path) &gt; <span class="n">0</span>):
print(<span class="s">&quot;ERROR: render produced no file&quot;</span>, file=sys.stderr)
<span class="k">return</span> <span class="n">6</span>
<span class="k">return</span> <span class="n">0</span>


<span class="k">def</span> main():
Expand All @@ -393,9 +421,9 @@ <h2>Source</h2>
<span class="k">return</span> code

<span class="k">if</span> args.output:
<span class="k">if</span> <span class="k">not</span> render_still(obj, os.path.abspath(args.output), args.engine):
print(<span class="s">&quot;ERROR: render produced no file&quot;</span>, file=sys.stderr)
<span class="k">return</span> <span class="n">6</span>
rcode = render_still(obj, os.path.abspath(args.output), args.engine)
<span class="k">if</span> rcode:
<span class="k">return</span> rcode
print(<span class="s">f&quot;</span><span class="s">rendered still </span>{args.output}<span class="s">&quot;</span>)

print(<span class="s">&quot;depsgraph-export OK&quot;</span>)
Expand Down
41 changes: 35 additions & 6 deletions docs/gallery/text-version-stamp/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ <h2>Run</h2>
blender --background --python text_version_stamp.py -- --output stamp.png
blender --background --python text_version_stamp.py -- --output stamp.png --engine cycles</code></pre>
<p>It exits non-zero on failure (wrong subclass, missing font, body/version mismatch, non-planar flat text, extrude/bevel closed form off, geometry not regenerating, or a <code>to_mesh_clear()</code> reference surviving). The <code>blender-smoke</code> workflow runs the check on Blender 4.5 LTS and 5.1. The render scales the stamp to a constant width, so the frame holds for any version-string length.</p>
<p>The <code>--output</code> render path additionally measures framing against the Layer 1 band via <code>examples/gallery_framing.py</code> (exit 10 on violation) before writing the still.</p>
</section>
<section class="detail-section src">
<h2>Source</h2>
Expand Down Expand Up @@ -226,6 +227,11 @@ <h2>Source</h2>
&quot;&quot;&quot;</span>
<span class="k">import</span> bpy, sys, os, math, argparse

<span class="c"># Shared Layer 1 framing measurement (render path only) — see gallery_framing.py</span>
sys.path.insert(<span class="n">0</span>, os.path.join(os.path.dirname(os.path.abspath(__file__)), os.pardir))
sys.dont_write_bytecode = <span class="k">True</span> <span class="c"># keep examples/__pycache__ out of the repo tree</span>
<span class="k">import</span> gallery_framing

EXTRUDE = <span class="n">0.06</span>
BEVEL = <span class="n">0.02</span>
TOL = <span class="n">1e-4</span>
Expand Down Expand Up @@ -433,9 +439,18 @@ <h2>Source</h2>
cam_data = bpy.data.cameras.new(<span class="s">&quot;Cam&quot;</span>)
cam_data.lens = <span class="n">50.0</span>
cam = bpy.data.objects.new(<span class="s">&quot;Cam&quot;</span>, cam_data)
cam.location = (<span class="n">0.0</span>, -<span class="n">8.5</span>, <span class="n">1.15</span>)
cam.rotation_euler = (math.radians(<span class="n">86.5</span>), <span class="n">0.0</span>, <span class="n">0.0</span>)
<span class="c"># Reframed: the old (0,-8.5,1.15) fixed 86.5° pitch left the stamp at</span>
<span class="c"># 0.663 fill with a dead lower third; moved in with an aim on the stamp&#x27;s</span>
<span class="c"># vertical center so text, caption, and underline bar balance the frame.</span>
cam.location = (<span class="n">0.0</span>, -<span class="n">6.0</span>, <span class="n">1.5</span>)
scene.collection.objects.link(cam)
aim = bpy.data.objects.new(<span class="s">&quot;Aim&quot;</span>, <span class="k">None</span>)
aim.location = (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">1.05</span>)
scene.collection.objects.link(aim)
con = cam.constraints.new(<span class="s">&#x27;TRACK_TO&#x27;</span>)
con.target = aim
con.track_axis = <span class="s">&#x27;TRACK_NEGATIVE_Z&#x27;</span>
con.up_axis = <span class="s">&#x27;UP_Y&#x27;</span>
scene.camera = cam

scene.render.engine = <span class="s">&#x27;CYCLES&#x27;</span> <span class="k">if</span> engine == <span class="s">&#x27;cycles&#x27;</span> <span class="k">else</span> eevee_engine_id()
Expand All @@ -450,8 +465,22 @@ <h2>Source</h2>
scene.render.resolution_y = <span class="n">720</span>
scene.render.image_settings.file_format = <span class="s">&#x27;PNG&#x27;</span>
scene.render.filepath = path
<span class="c"># Layer 1 framing gate (silhouette matte) — exit 10 on violation, before</span>
<span class="c"># the beauty render so a defective composition ships no artifact. The hero</span>
<span class="c"># is the version stamp; caption and underline bar count for margins.</span>
fcode = gallery_framing.check_framing(
scene, cam,
hero=[obj],
elements=[obj, cap_obj, bar],
stage=[floor, wall],
)
<span class="k">if</span> fcode:
<span class="k">return</span> fcode
bpy.ops.render.render(write_still=<span class="k">True</span>)
<span class="k">return</span> os.path.exists(path) <span class="k">and</span> os.path.getsize(path) &gt; <span class="n">0</span>
<span class="k">if</span> <span class="k">not</span> (os.path.exists(path) <span class="k">and</span> os.path.getsize(path) &gt; <span class="n">0</span>):
print(<span class="s">&quot;ERROR: render produced no file&quot;</span>, file=sys.stderr)
<span class="k">return</span> <span class="n">9</span>
<span class="k">return</span> <span class="n">0</span>


<span class="k">def</span> main():
Expand All @@ -468,9 +497,9 @@ <h2>Source</h2>
<span class="k">return</span> code

<span class="k">if</span> args.output:
<span class="k">if</span> <span class="k">not</span> render_still(obj, os.path.abspath(args.output), args.engine):
print(<span class="s">&quot;ERROR: render produced no file&quot;</span>, file=sys.stderr)
<span class="k">return</span> <span class="n">9</span>
rcode = render_still(obj, os.path.abspath(args.output), args.engine)
<span class="k">if</span> rcode:
<span class="k">return</span> rcode
print(<span class="s">f&quot;</span><span class="s">rendered still </span>{args.output}<span class="s">&quot;</span>)

print(<span class="s">&quot;text-version-stamp OK&quot;</span>)
Expand Down
Loading
Loading