The linesolid node = trunkdashed node = fork

assetfarmer Fork 05 · planes & plates layers.py — 397 lines · scenes.py · composite.py overlay maths

entry: farmer environment / farmer scenevar · no chroma key anywhere on this branch

Depth from a flat plate, and a night that can be tweened

Environments take a different road entirely. There is no magenta, no grid, no slicing. A plate is cut into layers along bounding boxes the agent chose by looking at it, the holes left behind are filled, and the result is checked by recompositing it and diffing against the original. The lighting branch does something more unusual: one generated night plate yields both the baked variant and a pair of maps that reproduce it at any strength.

  1. 05a

    Cutting the plate

    farmer/layers.py:302

    The input is a plate PNG and a JSON list of named regions. Every region gets a full-plate-resolution RGBA layer, cropped to its bbox plus a four-pixel margin, named {z:02d}-{name}.png so the directory listing sorts into draw order. The union of all cut regions becomes one hole, and that hole is filled once, in the base plate.

    Cutting the lab plate into five parallax planes with an inpainted base On the left, the 1536 by 1024 lab plate with five labelled bounding boxes drawn on it: whiteboard-presenter and telescope-desk at z equals 2, gpu-racks at z equals 3, students-desks at z equals 4 and railing at z equals 5. In the middle, the same five regions drawn as a stack of separated layers in draw order, each annotated with its file name, z value and parallax factor, with the inpainted base plate at the bottom carrying a base parallax of 0.1. On the right, three panels: the feathered rectangle mask and its two-pixel gaussian ramp, the iterative neighbour-mean inpaint followed by a blurred interior blend, and the composite check that recomposites the base plus every layer and diffs it against the source plate, writing composite-check.png and composite-diff.png. scenes/lab — 5 planes, one 1536 × 1024 plate whiteboard-presenter telescope-desk gpu-racks students-desks railing source: v4-lab-proof.png (stored as a portable path) bboxes come from regions.json — the agent picks them by looking draw order, near to far 05-railing.png z 5 · parallax 1.0 · 1103 × 103 04-students-desks.png z 4 · parallax 0.8 · 618 × 223 03-gpu-racks.png z 3 · parallax 0.6 · 413 × 398 02-telescope-desk.png z 2 · parallax 0.45 · 308 × 233 02-whiteboard-presenter.png z 2 · parallax 0.45 · 293 × 398 base-plate.png base_parallax 0.1 · every hole inpainted drawn later = nearer the camera how each layer is made rect mode — the default The bbox becomes a float mask, gaussian-blurred by FEATHER_PX / 2 = 1.0 so the edge ramps instead of stair-stepping, then the interior is clamped back to fully opaque so only the border is soft. _feathered_mask() — layers.py:239 the inpaint behind the cuts 1. iterative 4-neighbour mean fill spreads known colour inward, up to 4000 iterations 2. that leaves directional streaks over big holes, so a GaussianBlur(18) copy is blended into the hole interior only — the ~4 px rim keeps the neighbour-matched fill so the seam stays invisible inpaint() — layers.py:254 composite_check() Reopens the base, alpha-composites every plane at its recorded x and y, and diffs against the source plate: mean_abs_diff, max_abs_diff, pct_pixels_off_by_8+ and writes composite-check.png plus a 4×-amplified composite-diff.png next to them. parallax: given, or derived When a region omits it, the value is round(0.2 + 0.8 * z / zmax, 2) The lab's regions supplied their own — 0.45 at z 2 rather than the 0.52 the formula would give — which is what the per-region override is for. Honest scope of -‌-refine silhouette Kmeans colour models plus an edge-blocked flood carve, in pure numpy. It removes background corners and slivers around an object; it does not recover see-through gaps, keeps foreground occluders that sit inside the bbox, and returns the plain rectangle when the carve leaves under 25% of the box.
    Figure 11 — the plane factory. Layer geometry, z values and parallax factors are read from scenes/lab/layers/planes.json as committed. Note that those entries carry no refine key, which cut_planes() always writes today — the committed manifest predates that parameter.
    The lab scene's committed planes, from layers/planes.json
    Planezparallax bbox (x, y, w, h)stored layer
    whiteboard-presenter20.45462, 380, 285, 390293 × 398
    telescope-desk20.451070, 565, 300, 225308 × 233
    gpu-racks30.6695, 380, 405, 390413 × 398
    students-desks40.8175, 595, 610, 215618 × 223
    railing51.0278, 728, 1095, 951103 × 103

    Every stored layer is exactly eight pixels wider and taller than its bbox: the crop margin is FEATHER_PX * 2 = 4 on each side, so the feathered ramp is not clipped off. The manifest records the crop origin separately from the bbox, which is what composite_check() uses to put each layer back.

    A deliberate portability choice

    planes.json stores the source plate as a path relative to the working directory, or just the filename when it sits outside — never an absolute machine path. The consequence is stated in the error message: composite_check() raises source plate … not found — pass source= explicitly (planes.json stores a portable, not absolute, path), and the CLI always passes it.

    “The inpainted base plate behind large cut regions is a smooth blur, not a plausible reconstruction — fine when hidden behind planes, visible if a plane moves far.”

    README.md, honest limitations
  2. adopt scene → farmer scenevar lab night
  3. 05b

    One generation, two products

    farmer/scenes.py:32

    The variant pass is an edit-with-reference generation against the canonical day plate, carrying four keep-clauses verbatim in the prompt. What makes it more than a second image is what happens afterwards: the day and night plates are divided pixel by pixel into a multiply map and a screen map, which together reproduce the night plate — and, at intermediate strengths, every dusk between them.

    Decomposing a generated night plate into reusable multiply and screen maps A flow from left to right. The day plate is the anchor. One edit-reference generation produces the night plate. Both feed extract_lighting_overlay, which computes a multiply map as night divided by day clipped to zero and one, with black pixels forced neutral, and a screen map as night minus day over one minus day. Both maps are gaussian blurred slightly. The two maps then drive apply_lighting_overlay at any strength t, whose formula interpolates the multiply map toward one and scales the screen map, producing a continuous ramp from the day plate at t equals zero to a reconstruction of the night plate at t equals one. A comparison strip is rendered showing day, overlay at 50 percent, overlay at 100 percent, and the generated night. A fidelity panel gives the measured reconstruction error against the generated plate: mean absolute difference 2.076, maximum 135, and 7.81 percent of pixels off by eight or more. farmer scenevar lab night day plate (anchor) scenes/lab/plate.png · 1536 × 1024 generated night plate one edit-ref generation, keep-clauses verbatim extract_lighting_overlay(day, night) multiply = clip(night / clip(day, eps, 1), 0, 1) where day is below eps the ratio is undefined → forced to 1 (neutral) screen   = clip((night − day) / clip(1 − day, eps, 1), 0, 1) both maps gaussian-blurred by 2.0 — kills ratio speckle on near-black pixels night_multiply.png darkening night_screen.png the warm windows apply_lighting_overlay(day, mult, screen, strength=t) m_t = 1 + (multiply − 1) * t s_t = screen * t out = 1 − (1 − day * m_t) * (1 − s_t) t = 0 returns the day plate exactly; t = 1 reconstructs the variant overlay-check.png — the strip rendered for judgment day (anchor) overlay @50% — a free dusk overlay @100% generated night strengths come from -‌-strengths, default "0.5,1.0" measured reconstruction fidelity overlay @100% versus the generated night plate, recorded in scenes/lab/identity.json mean_abs_diff2.076 max_abs_diff135 pct_pixels_off_by_8+7.81%
    Figure 12 — one generation, a baked plate and a rig. The blur that makes the maps tweenable is also what costs fidelity: the module says so directly — exact reproduction is traded for that smoothness and measured by the caller. The caller does measure it, and the numbers are stored in the scene's identity.

    The keep-clauses

    Every scene adopted into the registry carries a style contract whose default keep-clauses go into the variant prompt verbatim (registry.py:168–173):

    clause 1
    keep the exact camera angle, framing, and canvas composition
    clause 2
    keep every wall, window, floor plane, and piece of furniture in its exact position and shape
    clause 3
    keep all object silhouettes, proportions, and outlines pixel-registered to the original
    clause 4
    change only the lighting, palette, and mood described

    Three variant descriptions ship as presets in VARIANT_DELTASnight, dusk and dawn. Any other variant name falls back to the scene as its <name> variant unless --delta supplies the description.

    Why this is worth the arithmetic

    A baked night plate is one asset. A multiply map and a screen map are a rig: the same pair drives a sunset that animates over thirty seconds, a lightning flash, or a lamp being switched off in a room — all by moving a single scalar, with no further generation. The 50% column in the strip is not a generated dusk. It is arithmetic between two images that already existed.