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
Binary file modified docs/gallery/assets/vse-cut-list-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/vse-cut-list-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.
2 changes: 1 addition & 1 deletion docs/gallery/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@ <h2><a href="uv-layer-grid/">uv-layer-grid</a></h2>
</article>
<article class="card" data-tags="sequencer rendering">
<a class="card-media" href="vse-cut-list/" tabindex="-1" aria-hidden="true">
<img src="assets/vse-cut-list-hero.webp" alt="A monitor on a plinth showing a sequencer frame of four colour blocks with a cut-list caption strip." loading="lazy" decoding="async" />
<img src="assets/vse-cut-list-hero.webp" alt="A monitor on a desk labelled PROGRAM, showing a sequencer frame of four colour blocks with a cut-list caption strip." loading="lazy" decoding="async" />
</a>
<div class="card-body">
<h2><a href="vse-cut-list/">vse-cut-list</a></h2>
Expand Down
34 changes: 24 additions & 10 deletions docs/gallery/vse-cut-list/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
<meta property="og:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/vse-cut-list-hero.webp" />
<meta property="og:image:width" content="1280" />
<meta property="og:image:height" content="720" />
<meta property="og:image:alt" content="A monitor on a plinth showing a sequencer frame of four colour blocks with a cut-list caption strip." />
<meta property="og:image:alt" content="A monitor on a desk labelled PROGRAM, showing a sequencer frame of four colour blocks with a cut-list caption strip." />
<meta property="og:site_name" content="Blender Developer Tools" />
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="vse-cut-list — Examples — Blender Developer Tools" />
<meta name="twitter:description" content="The sequencer API rename from 4.5 LTS to 5.x — strips (never .sequences), new_effect ending in length= vs frame_end=, and left_handle/right_handle/duration replacing the deprecated frame_final_*. A deterministic cut list — color programs, a clamped GAMMA_CROSS, a scene strip, a text strip — asserted before and after save/reload." />
<meta name="twitter:image" content="https://tmhsdigital.github.io/Blender-Developer-Tools/gallery/assets/vse-cut-list-hero.webp" />
<meta name="twitter:image:alt" content="A monitor on a plinth showing a sequencer frame of four colour blocks with a cut-list caption strip." />
<meta name="twitter:image:alt" content="A monitor on a desk labelled PROGRAM, showing a sequencer frame of four colour blocks with a cut-list caption strip." />
<style>
/* fonts are deployed by the landing build (docs/fonts/) */
@font-face { font-family: 'Barlow Condensed'; font-weight: 600; font-display: swap;
Expand Down Expand Up @@ -268,8 +268,8 @@ <h1>vse-cut-list</h1>
<p>The sequencer API rename from 4.5 LTS to 5.x — strips (never .sequences), new_effect ending in length= vs frame_end=, and left_handle/right_handle/duration replacing the deprecated frame_final_*. A deterministic cut list — color programs, a clamped GAMMA_CROSS, a scene strip, a text strip — asserted before and after save/reload.</p>
</header>
<main id="main">
<button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: A monitor on a plinth showing a sequencer frame of four colour blocks with a cut-list caption strip.">
<img src="../assets/vse-cut-list-hero.webp" alt="A monitor on a plinth showing a sequencer frame of four colour blocks with a cut-list caption strip." width="1280" height="720" />
<button class="detail-hero" id="heroZoom" type="button" aria-label="View full size: A monitor on a desk labelled PROGRAM, showing a sequencer frame of four colour blocks with a cut-list caption strip.">
<img src="../assets/vse-cut-list-hero.webp" alt="A monitor on a desk labelled PROGRAM, showing a sequencer frame of four colour blocks with a cut-list caption strip." width="1280" height="720" />
</button>
<p class="zoom-hint">Rendered headless by the example itself — click to zoom.</p>
<div class="callout"><span class="tag">witnesses</span> Each side hard-fails the other&#x27;s spelling: TypeError on the wrong end kwarg, AttributeError on .sequences. The cross clamps to the source overlap, effect strips consume their inputs only when stacked above them, and a same-scene scene strip renders transparent.</div>
Expand All @@ -287,6 +287,7 @@ <h1>vse-cut-list</h1>
<ul><li>A GAMMA_CROSS asked to outlast its inputs&#x27; overlap is <strong>silently clamped</strong> to the overlap — request length 9 over a (25, 33) overlap, get (25, 33).</li><li>A scene strip pointing at its <strong>own</strong> scene is a feedback loop and renders transparent (alpha 0) — the &quot;stage&quot; is silently absent. Source a separate scene.</li><li>Effect strips <strong>consume</strong> their inputs: input strips never composite on their own channel, and the effect&#x27;s transform applies on top of the inputs&#x27; transforms. Consumption requires the effect on a channel <strong>above</strong> its inputs; below, they keep painting independently.</li><li>An empty <code>bpy_prop_collection</code> is falsy — <code>se.strips or se.sequences</code> silently falls through to the legacy accessor on an empty timeline. Always branch on <code>hasattr</code>.</li></ul>
<p><strong>Version divergence:</strong> the whole example is the divergence — gated on the <code>bpy.app.version</code> tuple (<code>&gt;= (5, 0, 0)</code>), never on <code>version_string</code> (<code>&quot;4.5.11 LTS&quot;</code> is not bare semver). Each side asserts its own canonical contract plus the other side&#x27;s removal/bridge state. Measured values are identical on 4.5.11 and 5.1.2, including the pixel witness. <strong>5.2 LTS COLOR strips bake readonly <code>width</code>/<code>height</code> from the scene render resolution at <code>new_effect</code> time</strong> (4.5/5.1 have no intrinsic size — scale is a fraction of the output frame). The pixel check sets 96×54 <em>before</em> building the cut list; creating at factory 1920×1080 then rendering 96×54 makes 0.36-scaled cells larger than the output, C covers the frame, and TL samples amber <code>(0.949, 0.62, 0.102)</code> instead of crimson.</p>
<p><strong>Render:</strong> the program wall <em>is</em> the sequencer output at frame 29 (mid cross) — crimson A, teal B, amber long-runner C, and the 50/50 cross blend, over the Stage scene strip showing the dark studio. An off-by-one in end-exclusive span math drops its cell to the dark stage; the caption strip carries the closed form. The hero presents that authentic frame on a reference monitor in a dark-studio editing bay — the pixels on the screen are the genuine sequencer output (evidence); only the bay around them is staged (presentation). Rendered locally with EEVEE (GPU host); the checks and <code>--check-pixels</code> need no GPU (Cycles CPU).</p>
<p>In the bay, each part bites the one it rests on. The monitor&#x27;s neck rises 0.10 m into the bezel, the stand base sits 5 mm into the desk, and the &quot;PROGRAM&quot; caption stands on the desk&#x27;s front face. The neck used to butt its top face onto the bezel&#x27;s bottom on one plane, with half its depth hanging out behind, and the caption lay flat on the desk top 6 mm up, where the camera read it as a smear. None of this touches the sequencer frame or any check.</p>
<h2>Framing measurement</h2>
<p>The gallery still renders from the dedicated <code>Bay</code> scene (camera <code>BayCam</code>), not <code>bpy.context.scene</code> — a framing measurement with <code>examples/gallery_framing.py</code> must be handed that (scene, camera) pair explicitly.</p>
<h2>Run</h2>
Expand Down Expand Up @@ -790,10 +791,20 @@ <h2>Source</h2>
deskmat = pbr(<span class="s">&quot;DeskTop&quot;</span>, (<span class="n">0.05</span>, <span class="n">0.05</span>, <span class="n">0.06</span>), <span class="n">0.0</span>, <span class="n">0.80</span>)

<span class="c"># desk slab, stand, and the monitor bezel (screen face toward -Y)</span>
box(<span class="s">&quot;Desk&quot;</span>, (<span class="n">2.80</span>, <span class="n">1.10</span>, <span class="n">0.72</span>), (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">0.36</span>), deskmat)
box(<span class="s">&quot;StandBase&quot;</span>, (<span class="n">0.70</span>, <span class="n">0.50</span>, <span class="n">0.06</span>), (<span class="n">0.0</span>, <span class="n">0.05</span>, <span class="n">0.75</span>), gunmetal)
box(<span class="s">&quot;Stand&quot;</span>, (<span class="n">0.18</span>, <span class="n">0.12</span>, <span class="n">0.50</span>), (<span class="n">0.0</span>, <span class="n">0.05</span>, <span class="n">0.99</span>), gunmetal)
box(<span class="s">&quot;Bezel&quot;</span>, (<span class="n">2.06</span>, <span class="n">0.09</span>, <span class="n">1.30</span>), (<span class="n">0.0</span>, <span class="n">0.0</span>, <span class="n">1.89</span>), gunmetal)
<span class="c"># Each part bites the one it rests on. The neck used to butt its top face</span>
<span class="c"># onto the bezel&#x27;s bottom face (one plane, no joint) with half its depth</span>
<span class="c"># hanging out behind, and the base sat exactly on the desk top.</span>
desk_top, base_h, base_bite = <span class="n">0.72</span>, <span class="n">0.06</span>, <span class="n">0.005</span>
bezel_z, bezel_h, bezel_d = <span class="n">1.89</span>, <span class="n">1.30</span>, <span class="n">0.09</span>
neck_bite, neck_d = <span class="n">0.10</span>, <span class="n">0.07</span>
base_z0 = desk_top - base_bite
neck_z0 = base_z0 + base_h - base_bite
neck_z1 = bezel_z - bezel_h / <span class="n">2</span> + neck_bite
box(<span class="s">&quot;Desk&quot;</span>, (<span class="n">2.80</span>, <span class="n">1.10</span>, desk_top), (<span class="n">0.0</span>, <span class="n">0.0</span>, desk_top / <span class="n">2</span>), deskmat)
box(<span class="s">&quot;StandBase&quot;</span>, (<span class="n">0.70</span>, <span class="n">0.50</span>, base_h), (<span class="n">0.0</span>, <span class="n">0.05</span>, base_z0 + base_h / <span class="n">2</span>), gunmetal)
box(<span class="s">&quot;Stand&quot;</span>, (<span class="n">0.18</span>, neck_d, neck_z1 - neck_z0),
(<span class="n">0.0</span>, bezel_d / <span class="n">2</span> - neck_d / <span class="n">2</span> + <span class="n">0.02</span>, <span class="n">0.5</span> * (neck_z0 + neck_z1)), gunmetal)
box(<span class="s">&quot;Bezel&quot;</span>, (<span class="n">2.06</span>, bezel_d, bezel_h), (<span class="n">0.0</span>, <span class="n">0.0</span>, bezel_z), gunmetal)

<span class="c"># the screen: one unlit quad sampling the authentic frame end to end —</span>
<span class="c"># emission only, so the VSE pixels read exactly as the sequencer wrote them</span>
Expand Down Expand Up @@ -836,7 +847,10 @@ <h2>Source</h2>
cu.size = <span class="n">0.10</span>
cu.extrude = <span class="n">0.004</span>
cob = bpy.data.objects.new(<span class="s">&quot;BayCaption&quot;</span>, cu)
cob.location = (<span class="n">0.0</span>, -<span class="n">0.40</span>, <span class="n">0.73</span>)
<span class="c"># upright on the desk&#x27;s front face, set 1 mm into it: laid flat on the</span>
<span class="c"># top it floated 6 mm up and read as a smear at the camera&#x27;s grazing angle</span>
cob.location = (<span class="n">0.0</span>, -<span class="n">0.55</span> - cu.extrude + <span class="n">0.001</span>, desk_top - <span class="n">0.16</span>)
cob.rotation_euler = (math.radians(<span class="n">90.0</span>), <span class="n">0.0</span>, <span class="n">0.0</span>)
cob.data.materials.append(cmat)
sc.collection.objects.link(cob)

Expand Down Expand Up @@ -1054,7 +1068,7 @@ <h2>Source</h2>
</main>
<dialog class="lightbox" id="lightbox" aria-label="Full-size render">
<button class="lightbox-close" type="button" autofocus>Close</button>
<img src="../assets/vse-cut-list-hero.webp" alt="A monitor on a plinth showing a sequencer frame of four colour blocks with a cut-list caption strip." />
<img src="../assets/vse-cut-list-hero.webp" alt="A monitor on a desk labelled PROGRAM, showing a sequencer frame of four colour blocks with a cut-list caption strip." />
</dialog>
<footer>
<div class="statusbar">
Expand Down
2 changes: 1 addition & 1 deletion examples/gallery.json
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@
"name": "vse-cut-list",
"dir": "examples/vse-cut-list",
"teaches": "The sequencer API rename from 4.5 LTS to 5.x — strips (never .sequences), new_effect ending in length= vs frame_end=, and left_handle/right_handle/duration replacing the deprecated frame_final_*. A deterministic cut list — color programs, a clamped GAMMA_CROSS, a scene strip, a text strip — asserted before and after save/reload.",
"alt": "A monitor on a plinth showing a sequencer frame of four colour blocks with a cut-list caption strip.",
"alt": "A monitor on a desk labelled PROGRAM, showing a sequencer frame of four colour blocks with a cut-list caption strip.",
"witnessesFix": "Each side hard-fails the other's spelling: TypeError on the wrong end kwarg, AttributeError on .sequences. The cross clamps to the source overlap, effect strips consume their inputs only when stacked above them, and a same-scene scene strip renders transparent.",
"hero": "docs/gallery/assets/vse-cut-list-hero.webp",
"preview": "examples/vse-cut-list/preview.webp",
Expand Down
8 changes: 8 additions & 0 deletions examples/vse-cut-list/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,14 @@ are the genuine sequencer output (evidence); only the bay around them is
staged (presentation). Rendered locally with EEVEE (GPU host); the checks
and `--check-pixels` need no GPU (Cycles CPU).

In the bay, each part bites the one it rests on. The monitor's neck rises
0.10 m into the bezel, the stand base sits 5 mm into the desk, and the
"PROGRAM" caption stands on the desk's front face. The neck used to butt its
top face onto the bezel's bottom on one plane, with half its depth hanging
out behind, and the caption lay flat on the desk top 6 mm up, where the
camera read it as a smear. None of this touches the sequencer frame or any
check.

## Framing measurement

The gallery still renders from the dedicated `Bay` scene (camera `BayCam`),
Expand Down
Binary file modified examples/vse-cut-list/preview.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
23 changes: 18 additions & 5 deletions examples/vse-cut-list/vse_cut_list.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,10 +475,20 @@ def pbr(name, base, metallic, roughness):
deskmat = pbr("DeskTop", (0.05, 0.05, 0.06), 0.0, 0.80)

# desk slab, stand, and the monitor bezel (screen face toward -Y)
box("Desk", (2.80, 1.10, 0.72), (0.0, 0.0, 0.36), deskmat)
box("StandBase", (0.70, 0.50, 0.06), (0.0, 0.05, 0.75), gunmetal)
box("Stand", (0.18, 0.12, 0.50), (0.0, 0.05, 0.99), gunmetal)
box("Bezel", (2.06, 0.09, 1.30), (0.0, 0.0, 1.89), gunmetal)
# Each part bites the one it rests on. The neck used to butt its top face
# onto the bezel's bottom face (one plane, no joint) with half its depth
# hanging out behind, and the base sat exactly on the desk top.
desk_top, base_h, base_bite = 0.72, 0.06, 0.005
bezel_z, bezel_h, bezel_d = 1.89, 1.30, 0.09
neck_bite, neck_d = 0.10, 0.07
base_z0 = desk_top - base_bite
neck_z0 = base_z0 + base_h - base_bite
neck_z1 = bezel_z - bezel_h / 2 + neck_bite
box("Desk", (2.80, 1.10, desk_top), (0.0, 0.0, desk_top / 2), deskmat)
box("StandBase", (0.70, 0.50, base_h), (0.0, 0.05, base_z0 + base_h / 2), gunmetal)
box("Stand", (0.18, neck_d, neck_z1 - neck_z0),
(0.0, bezel_d / 2 - neck_d / 2 + 0.02, 0.5 * (neck_z0 + neck_z1)), gunmetal)
box("Bezel", (2.06, bezel_d, bezel_h), (0.0, 0.0, bezel_z), gunmetal)

# the screen: one unlit quad sampling the authentic frame end to end —
# emission only, so the VSE pixels read exactly as the sequencer wrote them
Expand Down Expand Up @@ -521,7 +531,10 @@ def pbr(name, base, metallic, roughness):
cu.size = 0.10
cu.extrude = 0.004
cob = bpy.data.objects.new("BayCaption", cu)
cob.location = (0.0, -0.40, 0.73)
# upright on the desk's front face, set 1 mm into it: laid flat on the
# top it floated 6 mm up and read as a smear at the camera's grazing angle
cob.location = (0.0, -0.55 - cu.extrude + 0.001, desk_top - 0.16)
cob.rotation_euler = (math.radians(90.0), 0.0, 0.0)
cob.data.materials.append(cmat)
sc.collection.objects.link(cob)

Expand Down
Loading