Skip to content

Commit a65cb38

Browse files
authored
fix: recompose the four genuine framing defects (worklist drained) (#110)
* fix: recompose depsgraph-export so the base cube is no longer amputated The rotated base cube plus the evaluated dome spanned ~5.9 units against a 5.3-unit frame: the cube was cut by the left and bottom edges (fill 0.984x, touch L+B). Reframed to an aim-based camera at (0,-9.6,2.6) tracking (0,0,0.9) — the full pair now sits at fill 0.812x/0.622y with margins L0.069 R0.119 B0.178 T0.200, and the faceted-base vs subsurf-dome comparison that is the contract reads undiminished. Render path gated with check_framing (exit 10); check values unchanged (base=8, eval=98, exported=98). Gate falsified at lens 64 (fill 0.988 FAIL, exit 10) and restored. Pass-2 variant (subsurf cleared) renders two cubes — the broken contract is visible in the reframe. Identical framing numbers on 4.5.11. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com> * fix: recompose vertex-weight-limit so the arm base clears the frame The mech arm's round base was cropped by the bottom edge and the subject drifted right-of-center with dead space (touch B, fill 0.247x/0.861y). Reframed more frontal at (4.6,-8.2,2.3) with the aim dropped 1.5 -> 1.25, landing fill 0.206x/0.867y with margins L0.378 R0.416 B0.050 T0.083 — base fully in frame, lean balanced, teal pruned-weight zones and claw still legible. The x-fill stays narrow by subject geometry; the band's single-axis rule is met on y. Render path gated with check_framing (exit 10); check values unchanged (influence counts, unit sums, pose preservation, LBS error all print as before). Gate falsified at lens 66 (fill 1.000 FAIL, exit 10) and restored. Pass-2 variant (no prune) is pixel-identical by design — pruning preserves the pose, so the influence-cap contract is numeric and the still witnesses the rig and zones; reported as such. Identical framing numbers on 4.5.11. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com> * fix: recompose triangulate-tangents and clear the dome blowout The buckler sat at 0.334x fill adrift in the stage. A 2x-class move-in iterations (4.1/4.8/5.6 overshot the leaning disc's vertical span into B+T edge touch; settled at (0.6,-6.35,2.62) aim (0,0,1.42)) lands fill 0.459x/0.833y with margins L0.278 R0.262 B0.039 T0.039, and the concentric tangent rings now read across the whole face. More than reframe, disclosed: the larger subject exposed a pre-existing blown region on the evidence dome (30.7% of the dome region >= 252) — Key 500->320 and Glint 400->150 clear it (max 231, near-white 0.00%) with the anisotropic sweep still driving the rings; the old hero clipped in the same region at smaller scale. Render path gated with check_framing (exit 10); check values unchanged (tris=720, unit/dot/btn/uv errors all print as before). Gate falsified at lens 70 (fill 0.972 FAIL, exit 10) and restored. Pass-2 variant (cap dissolved to an ngon, no calc_tangents) is pixel-identical — the ring sweep is UV/bump-driven and the mikktspace contract is numeric; reported as such. Identical framing numbers on 4.5.11. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com> * fix: recompose text-version-stamp so the stamp fills the frame The stamp sat at 0.663x fill with a dead lower third (fixed-pitch camera at (0,-8.5,1.15)). Reframed to an aim-based camera at (0,-6.0,1.5) tracking the stamp's vertical center — hero fill 0.719x/0.478y (the bar is the widest element, so side margins bind at L/R 0.025; the stamp's constant-width scaling keeps any version-string length in frame, confirmed on 4.5.11's longer "4.5.11 LTS" body at 0.716x). Caption, stamp, and underline bar now balance the frame and the live version string reads unmistakably. Render path gated with check_framing (exit 10); check values unchanged (body, flat vs solid extents, faces=2917). Gate falsified at lens 64 (fill 0.922 FAIL, exit 10) and restored. Pass-2 variant (stale "9.9.9" body) is unmistakable at the reframed size. The pre-existing AgX default and lighter studio variant are left untouched (accepted look, flagged). Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com> * docs: drain the framing recomposition worklist All four recomposition cases are restaged and gated; the worklist entry flips to DRAINED with the before/after numbers. Nothing framing-related remains: marginal cases swept, deviations documented, the sky-texture artifact report-only, and future examples inherit the gate at authoring time. Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com> --------- Signed-off-by: fOuttaMyPaint <TMhospitalitystrategies@gmail.com>
1 parent 40e9600 commit a65cb38

25 files changed

Lines changed: 261 additions & 53 deletions

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ Not committed; target list for the next content version. (v0.3.0 shipped the smo
129129
- 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)
130130
- 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`
131131

132-
- 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).
132+
- ~~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.
133133

134134
## Future (uncommitted)
135135

-412 Bytes
Loading
-490 Bytes
Loading
2.25 KB
Loading
-168 Bytes
Loading
-457 KB
Loading
39.1 KB
Loading
1.23 KB
Loading
-122 Bytes
Loading

docs/gallery/depsgraph-export/index.html

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ <h2>Run</h2>
196196
# Write the exported OBJ to a specific path:
197197
blender --background --python depsgraph_export.py -- --obj exported.obj</code></pre>
198198
<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>
199+
<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>
199200
</section>
200201
<section class="detail-section src">
201202
<h2>Source</h2>
@@ -219,6 +220,11 @@ <h2>Source</h2>
219220
&quot;&quot;&quot;</span>
220221
<span class="k">import</span> bpy, bmesh, sys, os, math, argparse, tempfile
221222

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

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

361376
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()
@@ -373,8 +388,21 @@ <h2>Source</h2>
373388
<span class="c"># AgX would flatten the graphite-vs-green contrast this still hinges on</span>
374389
<span class="c"># (docs/VISUAL-STYLE.md)</span>
375390
scene.view_settings.view_transform = <span class="s">&#x27;Standard&#x27;</span>
391+
<span class="c"># Layer 1 framing gate (silhouette matte) — exit 10 on violation, before</span>
392+
<span class="c"># the beauty render so a defective composition ships no artifact.</span>
393+
fcode = gallery_framing.check_framing(
394+
scene, cam,
395+
hero=[base_obj, obj],
396+
elements=[base_obj, obj],
397+
stage=[floor, wall],
398+
)
399+
<span class="k">if</span> fcode:
400+
<span class="k">return</span> fcode
376401
bpy.ops.render.render(write_still=<span class="k">True</span>)
377-
<span class="k">return</span> os.path.exists(path) <span class="k">and</span> os.path.getsize(path) &gt; <span class="n">0</span>
402+
<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>):
403+
print(<span class="s">&quot;ERROR: render produced no file&quot;</span>, file=sys.stderr)
404+
<span class="k">return</span> <span class="n">6</span>
405+
<span class="k">return</span> <span class="n">0</span>
378406

379407

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

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

401429
print(<span class="s">&quot;depsgraph-export OK&quot;</span>)

0 commit comments

Comments
 (0)