Visual Experiments 3
Summary
Continuation of the ricochet-template visual-experiments work (follows "Visual Experiments 2"). This session closed out the remaining icons/images roadmap items (image-as-SHAPE + image-as-CONTENT), added the rest of the geometric shapes plus shape rotation, and did a major API-simplification pass that culminated in a unified effect editor. Also patched the shared Anchor 2 engine to bind auxiliary sampler textures for the regular draw path. The 480×270 work and replacing the snkrx-template effect stack were explicitly deferred to later manual sessions.
Alignment + scope decisions (up front):
- Read Visual Experiments 2's summary +
ricochet-template/.claude/CLAUDE.md. Surfaced a key finding: the roadmap's "image-as-SHAPE hook is READY vialayer_shader_set_texture/COMMAND_SET_UNIFORM_TEXTURE" was wrong — for the draw shader that command only stashed the texture for the post-processapply_shaderpath, never binding for regular SDF/sprite draws. - Via AskUserQuestion: chose to patch the engine (vs a Lua-only sprite hack); image-as-CONTENT = build both luminance- and pattern-driven modes to compare; 480×270 deferred ("we'll do this later manually, piece-by-piece").
Engine patch — aux sampler binding for the draw path (Anchor2/engine/src/anchor.c):
- Patched the
COMMAND_SET_UNIFORM_TEXTUREcase in the command loop (~line 4693) to ALSO immediatelyglActiveTexture(GL_TEXTURE0+unit); glBindTexture; glUniform1i(loc, unit)on the active draw shader, in addition to the existing post-process stash. The batch renderer only touches unit 0, so a mask on unit ≥ 2 persists across the frame's draws. - Built with
Anchor2/engine/build.batvia PowerShell (cmd-batch output is swallowed in MINGW bash; the first bash attempt didn't actually rebuild — confirmed by the binary timestamp). Copiedbuild/anchor.exe→ricochet-template/anchor.exe. - Documented as a cross-project memory (
reference_anchor2_aux_sampler_draws.md).
Image-as-SHAPE (tiled icon masks) — sprite + sprite_field:
- Added
spriteshape kind (kind 3): each grid cell stamped with a chosen icon's silhouette (its alpha), tiled, the effect fills it.shape_maskgained acell_uvparam; the sprite branch returnstextureLod(u_shape_tex, vec2(cell_uv.x, 1-cell_uv.y), 0).a(Y-flipped for gl_FragCoord vs texture origin). Icon bound on unit 2. Sidebar ICON picker (icon_names,current_shape_icon). - User feedback: keep this "icons as background" mode (with the default color recipe it reads as icons-bg + black noise; only good with
ramp). Wanted (a) filled icons to compare, (b) a field mode where the icon is sized by the field (the circle-analog), (c) a SPACING slider. - Added
sprite_field(kind 4): icon sized by the per-cell fieldfc(circle-analog — big where bright, gone where dark). Relabeledsprite→icon (bg),sprite_field→icon (field). - Filled icons: extended
tools/fetch_icons.mjsto also fetch each Tabler-filledvariant (22 resolved), interleaved as<concept>_filledintoicon_names. - SPACING: first version insets the icon within a fixed cell → user noted it shrank icons instead of adding gaps. Reworked so SPACING enlarges the grid pitch (
size /= 1-spacing, sprites only) whileshape_maskinsets the icon by the same factor — the two cancel, so the icon keeps its absolute size and only the gap grows. Relabeled CELL SIZE → ICON SIZE for sprites. sprite_fieldrestricted to filled icons only (an outline scaled down by the field collapses to a thin ring); switching into field mode snapscurrent_shape_iconto its_filledcounterpart (icon_names_filled).
Image-as-CONTENT (effect over real images):
- New third screen "IMAGE" (Tab). User supplied
girl.png/boy.png(opaque anime illustrations, RGB no-alpha). For PATTERN mode's stencil, fetched a transparent wolf-head silhouette (game-icons:wolf-headvia Iconify→resvg, CC-BY 3.0, credited inassets/images/CREDITS.txt). - LUMINANCE mode: the image's own luminance is the field → dither + recolor; CONTRAST tone-spread slider. PATTERN mode: the selected pattern fills, masked by the image's alpha. Shader:
u_image_fieldflag.effect_imagedraws vialayer_pushscale (engine draws textures at native size; the photos are ~2600px). - Follow-up requests: (1) circles/icons when the effect fills the shape, (2) the moving pattern driving the image's dither (vs static).
- Unification: made
effect_fieldimage-aware — whenu_image_field, the field is image luminance (contrast-remapped), optionally rippled by the animated pattern via newu_image_pattern_amount. Collapsed the separate luminance branch so the image flows through the SAMEeffect_field_shaped → shape/dither/color/backingpipeline. So SHAPE works on images for free (luminance + shape = halftone; pattern + shape = dot-grid in the silhouette), and a PATTERN AMT slider makes the dither flow over time.
More geometric shapes + rotation:
- Added
square(Chebyshev),diamond(Manhattan),hexagon(gauge),ring(annulus, 50% hole),cross(plus), thentriangle(gauge) — all viashape_maskgauge metrics (m= 0 at cell center, boundary atm == fc). Dropped the now-redundantdistparam (compute fromcell_uv). - Fixed a latent bug: the SPACING pitch-enlargement was gated
>= 3, which would have wrongly caught the new geometric kinds (5–9); re-gated to sprites only (3, 4). - On "more shapes or do icons serve the rest?" — recommended triangle as the one missing convex primitive (directional; not cleanly an icon), and icons for the recognizable long tail (star/heart/etc.). A fill/outline modifier (hollow shapes) was floated and parked. User agreed.
- ROTATION:
u_shape_rotation(0..1 = full turn) rotates the cell-local coordinate at the top ofshape_mask, applied to every shape (geometric + icons); the cell grid stays axis-aligned. Flow-field per-cell rotation (the Orblike/Invoker wall-icon effect) noted as a future, bigger idea.
Single-call API + the unified editor (API simplification):
- Added
effect_rectangle(layer,x,y,w,h,spec)/effect_circle(layer,x,y,r,spec)/effect_image(layer,img,x,y,w,h,spec)wrappers (set + draw + clear in one; base color white). Folded the sprite icon into the spec asshape_icon(bound insideeffect_set). - First built a text "API reference" screen (
api_showcase.lua): a grid of example calls where each call's source is generated from its minimal spec (single source of truth). Dogfooded the wrappers (refactored the gallery loop +image_drawonto them) and added an image-screen PATTERN picker. - User: prefers editors over the text API → replace the API screen with a single editor covering all cases. Via AskUserQuestion: fold the image screen into the editor (drop it), no code readout.
- Built
editor.lua: TARGET = rect/circle/image (image adds MODE/IMAGE/PATTERN AMT), a PATTERN picker (loads per-pattern defaults from apattern_defaultstable), full modifier + look controls, one large live preview drawn through theeffect_*wrappers. Deletedapi_showcase.lua; removed the separate image screen. - Factored the shared sidebar control blocks into new
ui_controls.lua(ui_color_recipe_row/ui_dither_row/ui_shape_block/ui_color_ab_block/ui_ramp_block+ the cyclerscycle_icon/cycle_content_image/shape_is_sprite), reused by BOTH the gallery sidebar (ui_update) and the editor (editor_ui). Screens are now gallery → showcase → editor (Tab). Removed deadcurrent_spec.
Docs synced (final step):
- Thorough
ricochet-template/.claude/CLAUDE.mdupdate: layout + file map (neweditor.lua/ui_controls.lua/assets/images//assets/icons/), roadmap (all big items marked done; 480×270 / snkrx-replacement / flow-field-rotation moved to a deferred user-driven list; shape + color axes marked complete), effect-system + shape + spec sections (geometric + sprite kinds, gauge model, rotation/spacing, the single-call wrappers, image-as-content), the three screens + Keys, theui_controlsshared-block note, adding-a-pattern/shape, and an Engine patch note in Critical conventions. - Updated
effect.luaandmain.luaheader docblocks (four-axis, wrappers, three screens). Added the cross-project engine memory + its MEMORY.md index line.
Deferred (parked in the roadmap, user-driven): 480×270 support; replacing the snkrx-template edition.lua + dither stack with this system; flow-field per-cell shape rotation.
Let's continue on the visual experiments. Read the last session summary log to get aligned. I think this session we want to proceed with the rest of what we needed to do regarding icons/images, add more shapes and then I also want to do some API simplifications and also make sure that everything works on 480x270 as well, to make all these effects we implemented also replace the SNKRX template ones.
I'll start by finding and reading the last session summary log to get aligned.
Glob (*/logs/.md)
anchor.blot.im\logs\2026-01-02_18-59.md
anchor.blot.im\logs\2026-01-03_12-30.md
anchor.blot.im\logs\2026-01-03_15-30.md
anchor.blot.im\logs\anchor-phase-1.md
anchor.blot.im\logs\anchor-phase-10-part-1.md
anchor.blot.im\logs\anchor-phase-10-part-10.md
anchor.blot.im\logs\anchor-phase-10-part-2.md
anchor.blot.im\logs\anchor-phase-10-part-3.md
anchor.blot.im\logs\anchor-phase-10-part-4.md
anchor.blot.im\logs\anchor-phase-10-part-5.md
anchor.blot.im\logs\anchor-phase-10-part-6.md
anchor.blot.im\logs\anchor-phase-10-part-7.md
anchor.blot.im\logs\anchor-phase-10-part-8.md
anchor.blot.im\logs\anchor-phase-10-part-9.md
anchor.blot.im\logs\anchor-phase-2.md
anchor.blot.im\logs\anchor-phase-3-part-1.md
anchor.blot.im\logs\anchor-phase-3-part-2.md
anchor.blot.im\logs\anchor-phase-3-part-3.md
anchor.blot.im\logs\anchor-phase-3-part-4.md
anchor.blot.im\logs\anchor-phase-4.md
anchor.blot.im\logs\anchor-phase-5.md
anchor.blot.im\logs\anchor-phase-6.md
anchor.blot.im\logs\anchor-phase-7.md
anchor.blot.im\logs\anchor-phase-8.md
anchor.blot.im\logs\anchor-phase-9.md
anchor.blot.im\logs\anchor-review-1.md
anchor.blot.im\logs\anchor-review-2.md
anchor.blot.im\logs\audible-book-recommendations-2.md
anchor.blot.im\logs\audible-book-recommendations.md
anchor.blot.im\logs\anchor-phase-10-part-11.md
... [71 more lines]
Let me look for the visual experiments session log specifically and check the current logs location.
Glob (/logs/visual.md)
anchor.blot.im\logs\ebb-visual-polish.md
anchor.blot.im\logs\orblike-visual-aural-consistency.md
anchor.blot.im\logs\orblike-map-gen-wall-visuals.md
anchor.blot.im\logs\orblike-wall-visuals-ability-editions.md
anchor.blot.im\logs\orblike-more-visuals-edition-system.md
a327ex-site\logs\ebb-visual-polish.md
a327ex-site\logs\orblike-map-gen-wall-visuals.md
a327ex-site\logs\orblike-more-visuals-edition-system.md
a327ex-site\logs\orblike-visual-aural-consistency.md
a327ex-site\logs\orblike-wall-visuals-ability-editions.md
a327ex-site\logs\visual-experiments-1.md
a327ex-site\logs\visual-experiments-2.md
Glob (a327ex-site/logs/*.md)
a327ex-site\logs\2026-01-01-07-37.md
a327ex-site\logs\2026-01-01-19-37.md
a327ex-site\logs\2026-01-02-10-00.md
a327ex-site\logs\2026-01-02-18-59.md
a327ex-site\logs\2026-01-03-12-30.md
a327ex-site\logs\2026-01-03-15-30.md
a327ex-site\logs\2026-01-03-18-20.md
a327ex-site\logs\2026-01-04-12-15.md
a327ex-site\logs\anchor-app-design.md
a327ex-site\logs\anchor-app-phase-1-part-1.md
a327ex-site\logs\anchor-app-ui-discussion.md
a327ex-site\logs\anchor-arena-final-and-neovim-diff-tool.md
a327ex-site\logs\anchor-arena-test.md
a327ex-site\logs\anchor-phase-1.md
a327ex-site\logs\anchor-phase-10-part-1.md
a327ex-site\logs\anchor-phase-10-part-10.md
a327ex-site\logs\anchor-phase-10-part-11.md
a327ex-site\logs\anchor-phase-10-part-2.md
a327ex-site\logs\anchor-phase-10-part-3.md
a327ex-site\logs\anchor-phase-10-part-4.md
a327ex-site\logs\anchor-phase-10-part-5.md
a327ex-site\logs\anchor-phase-10-part-6.md
a327ex-site\logs\anchor-phase-10-part-7.md
a327ex-site\logs\anchor-phase-10-part-8.md
a327ex-site\logs\anchor-phase-10-part-9.md
a327ex-site\logs\anchor-phase-2.md
a327ex-site\logs\anchor-phase-3-part-1.md
a327ex-site\logs\anchor-phase-3-part-2.md
a327ex-site\logs\anchor-phase-3-part-3.md
a327ex-site\logs\anchor-phase-3-part-4.md
... [71 more lines]
There's visual-experiments-2.md — that's the most recent. Let me read it.
Read (E:\a327ex\a327ex-site\logs\visual-experiments-2.md)
File content (724.1KB) exceeds maximum allowed size (256KB). Use offset and limit parameters to read specific portions of the file, or search for specific content instead of reading the whole file.
The log is a full transcript. Let me read the summary at the top.
Read (E:\a327ex\a327ex-site\logs\visual-experiments-2.md)
1 Title: Visual Experiments 2
2 Date: 2026-05-29 21:38:44
3
4 # Visual Experiments 2
5
6 ## Summary
7
8 Continuation of the `ricochet-template` visual-experiments work (follows "Visual Experiments 1"). Extended the pattern × color × dither effect system with **local gradient patterns**, a **second per-pattern knob**, a **SHAPE axis** (field rendered as a grid of shapes), a **`ramp` color recipe** + an in-app **ramp editor**, then pivoted to **icon research** — built an Iconify→PNG pipeline, compared Phosphor/Tabler/Remix in a flat themed UI screen, chose **Tabler**, and rebuilt that screen as a full-width Tabler **UI kit**. Also fixed and documented a recurring **sprite-centering** engine gotcha and a latent `layer_line` framework-copy bug.
9
10 **Local gradient patterns (linear + radial):**
11 - Added two patterns sampled from the draw's own `vUV` (rect-local) instead of `vPos` (world/global): `linear_gradient` (kind 10) and `radial_gradient` (kind 11). The field is locked to the rectangle's own box, so it moves/scales with the rect.
12 - Aspect-corrected via the quad's pixel size: hoisted a `vec2 quad_px` in the shader `main()`, set per shape branch (rect = `vShape0.xy`, circle = `vec2(vShape0.x)`, sprite = `vec2(texSize)`, etc.). Widened `effect_field(world_pos)` → `effect_field(world_pos, uv, quad_px)`; both call sites updated.
13 - `linear_gradient`: `param` = ANGLE (0..1 → 0..2π), aspect-corrected; ramp normalized corner-to-corner along the direction. `radial_gradient`: `param` = SHAPE, blends a true circle (inscribed, short side) ↔ rect-fitting ellipse.
14 - Via AskUserQuestion, the "how fast the gradient goes" control was decided to mean **transition steepness**, which is mechanically the existing universal **CONTRAST** modifier — so no new uniform; SCALE + SPEED are inert for gradients.
15
16 **Second per-pattern knob (`param2`):**
17 - Added a general second intrinsic knob (`u_pattern_param2`) mirroring `param`, with a *per-pattern* slider range (`param2_min`/`param2_max`). Linear → **OFFSET** ([-1,1], slides the f=0.5 midline along the angle); radial → **RANGE** (0.25..3, scales the circle's reach). Conditional 5th MODIFIERS slider shown only when `param2_label` is set; sidebar height bumped.
18
19 **SHAPE axis (4th axis) — field as a grid of shapes:**
20 - New `shape` axis (`shape_kinds`, `u_shape_kind`, `u_shape_size`), `apply_shape` between `effect_field` and dither. Cells anchored to screen space (`gl_FragCoord`), the same space as the dither. Shared SHAPE + CELL SIZE controls (like color/dither).
21 - First pass had three circle variants (size/fill/hard). User: "Let's try all these options" → made them selectable shape kinds.
22 - **Warping bug:** circles warped into blobs/crescents because the field was sampled per-fragment (f varies across a cell). Fixed by sampling the field **once per cell at the cell center**, reconstructing world_pos/uv there from screen-space derivatives (`dFdx`/`dFdy`) — exact for the 2D affine camera. Introduced `effect_field_shaped`.
23 - **Mask × fill decouple:** user wanted each circle to carry the effect's internal variation, not a flat tone. Generalized to two roles: **coverage** (mask, from the per-cell `fc` → true circle boundary) and **color/fill** (per-fragment `ff`). `shape_mask()` isolated as the single extension point for future image-stencil shapes.
24 - Removed `circle (fill)` per user ("too close to the raw effect"); kept `circle` (size) + `circle_hard`.
25
26 **SNKRX editions — conceptual placement (no code):**
27 - Discussed where SNKRX's `dynamic` / `polychrome` / `palette_cycle` / `silver` editions fit. Conclusion: they're **COLOR recipes**. SNKRX fused (shimmer field + color transform + animation) into one "edition"; our pipeline already split those. So an edition decomposes: shimmer → pattern, animation → pattern `speed`, dither → dither, the color transform → the color axis.
28 - Key payoff: animated color is **free** — feed an animated pattern into a hue/ramp recipe and it sweeps over time without the recipe knowing about time; spatial color comes from gradient patterns. Only field-independent uniform time-cycling would need a new color-stage input (better expressed as a pattern). Ricochet caveat: a continuous rainbow fights the limited palette → use a **palette-token ramp** (quantized "polychrome").
29
30 **`ramp` color recipe:**
... [90 more lines]
Now let me read the ricochet-template's CLAUDE.md (the orientation file with the recorded roadmap and conventions).
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
1 # ricochet-template — orientation for new instances
2
3 ## ⛔ WORKFLOW RULE — DESIGN BRIEF BEFORE CODE ⛔
4
5 **This rule has no exceptions and is valid for every single
6 non-trivial change, every session, every file in this project.**
7
8 Before calling `Write` or `Edit` on project code, post a **design
9 brief** in chat — a conceptual explanation of the approach, NOT the
10 code itself. Then wait. The user will usually reply with questions
11 and improvements; iterate the brief with them until you reach a
12 conclusion. Only then write the code.
13
14 ### What a design brief contains
15
16 1. **Approach.** A high-level explanation of how each feature works —
17 what it does, the strategy, the key data it holds, its lifecycle.
18 2. **Interfaces.** How the feature connects to existing features:
19 which existing functions / files / globals it touches or reuses,
20 and how. Include this whenever the feature is not self-contained.
21 3. **Functions.** For each new or changed function:
22 - its name,
23 - what it does,
24 - which other functions it calls — selective, not exhaustive,
25 - its general structure, when that isn't already obvious.
26
27 The brief is a conceptual map, not a spec and not pseudocode. Aim for
28 "a reader could predict the shape of the code from this."
29
30 ### After the brief is agreed
... [667 more lines]
I'm now fully aligned on where the project stands. Before proposing anything, let me read the current state of the core files so my briefs match the actual code.
Read (E:\a327ex\ricochet-template\effect.lua)
1 --[[
2 effect.lua — three-axis visual effect system (pattern × color × dither).
3
4 THE 30-SECOND MENTAL MODEL
5 Every draw routes through assets/draw_shader.frag. The shader takes
6 three orthogonal selectors and composes them as:
7
8 f = pattern(world_pos, time) -- spatial structure + animation
9 f = dither(f, pixel_pos) -- quantize to discrete levels
10 rgb = color(base_rgb, f, palette) -- map f → palette token(s)
11
12 Pattern owns "what the field looks like" AND "whether it moves over
13 time." Color is a pure recipe that picks (or mixes) palette tokens
14 by f. Dither inserts the pixel-art stipple between them. The three
15 are independent — any combination is valid.
16
17 PUBLIC API
18 effect_setup(opts) install draw shader, cache its
19 GL program ID, push the palette
20 effect_set(layer, spec) write effect uniforms for this
21 layer's subsequent draws
22 effect_clear(layer) pattern/color/dither → 0 (passthrough)
23 effect_draw(layer, spec, fn, ...) scoped form
24 effect_write_palette() re-push u_palette to the shader
25 (called automatically by palette_init
26 and effect_setup)
27
28 Cycle helpers (return the NEXT/PREV name in each axis's cycle):
29 effect_next_pattern(cur), effect_prev_pattern(cur)
30 effect_next_color(cur), effect_prev_color(cur)
... [425 more lines]
Now the shader and main.lua.
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
1 // =============================================================================
2 // ricochet-template — unified draw shader (single fragment shader for the
3 // whole project, installed via set_draw_shader as the engine's default).
4 //
5 // THREE-AXIS MODEL
6 // Every fragment is processed as:
7 //
8 // f = pattern(world_pos, time, scale, param) // [0, 1]
9 // f = dither(f, pixel_pos) // {0, 1/n, …, 1}
10 // rgb = color(base_rgb, f, palette[color_a], palette[color_b])
11 //
12 // Pattern owns spatial structure AND animation. Color is a pure recipe
13 // that picks/mixes palette tokens by f. Dither inserts the pixel-art
14 // stipple between them. The three are independent — any combination is
15 // valid. See effect.lua for the registered names per axis.
16 //
17 // VARYING INPUTS (from engine vertex shader; do not rename)
18 // vPos World-space fragment position. For camera-attached layers
19 // this is camera-transformed world space.
20 // vUV 0..1 UV inside the draw's quad (or sprite UV for sprites).
21 // vColor Per-vertex RGBA color (the `color` arg passed to layer_*).
22 // vType Shape dispatch: 0=rect, 1=circle, 2=sprite, 3=line/capsule,
23 // 4=triangle, 5=polygon, 6=rounded rect.
24 // vShape0..4 Per-shape parameter bundle.
25 // vAddColor Per-vertex additive RGB offset (engine 'flash' channel).
26 //
27 // UNIFORMS YOU SET FROM LUA (via effect.lua's effect_set)
28 // u_pattern_kind int 1=organic 2=solid 3=plasma 4=waves 5=marble
29 // 6=sine_grid 7=wood 8=truchet 9=contours
30 // 10=linear_gradient 11=radial_gradient (LOCAL)
... [721 more lines]
Now main.lua and showcase.lua.
Read (E:\a327ex\ricochet-template\main.lua)
1 --[[
2 ricochet-template — visual style + three-axis effect template.
3
4 Entry point. Owns:
5 - configuration (resolution, render mode, monitor)
6 - boot order (palette → fonts → effect/shader → ui modules)
7 - layers (game, ui), camera
8 - current effect-spec state (pattern, color, dither, color_a/b, scale)
9 - sidebar control panel + keyboard input (both mutate the same state)
10 - test-scene draw (sample shapes through the effect pipeline)
11
12 Delegates:
13 - palette.lua — dark/light palette tables + palette_init
14 - effect.lua — pattern × color × dither spec API + cycle helpers
15 - rectcut.lua — RectCut layout primitive (cut/get/add/contract/...)
16 - ui_state.lua — frame-local hot/active/focus + ui_claim_hot
17 - ui.lua — five Ricochet-style widgets that mutate effect state
18
19 Goal: visually evaluate any (pattern, color, dither, color_a, color_b,
20 scale) combination by clicking sidebar controls or pressing the hotkeys.
21 Both input modes update the same caller-owned state.
22 ]]
23
24 require('anchor')({
25 width = 1920,
26 height = 1080,
27 title = "ricochet-template",
28 scale = 1,
29 filter = "smooth",
30 render_uncapped = true,
... [489 more lines]
Now showcase.lua and the tools directory.
Read (E:\a327ex\ricochet-template\showcase.lua)
1 --[[
2 showcase.lua — flat "UI KIT" screen (Tab toggles it from the pattern gallery).
3
4 A live catalogue of themed UI elements built with the chosen icon pack
5 (TABLER) so the whole vocabulary can be eyeballed in the Ricochet theme.
6 Everything is FLAT: raw palette colors on `ui_layer`, NO effect pipeline.
7
8 Icons are 44px white PNGs at assets/icons/tabler/<concept>.png, loaded into
9 the global `icons[concept]` table by main.lua and tinted to a palette token
10 on draw (white * token = token). Generated by tools/fetch_icons.mjs.
11
12 Three columns of element groups (full screen width now that it's one pack):
13 HUD resource badges, vitals bars, ability bar, life pips, wave bar
14 PROGRESSION upgrade card, tooltip, leaderboard list
15 CONTROLS tabs, toggle/checkbox/radio, stepper, stat block, keycaps, tags
16
17 DRAW ORIGIN REMINDER: layer_image / layer_circle are CENTERED at (x,y);
18 layer_rectangle / layer_text are TOP-LEFT. (See the framework gotcha note.)
19 ]]
20
21 local COL_W = 580
22 local COL_X = { 40, 670, 1300 }
23 local TOP = 120
24 local HAIR = 1
25 local ICON = 44 -- matches the rasterized PNG size (tools/fetch_icons.mjs SIZE)
26
27 -- ============================================================================
28 -- primitives (all draw to ui_layer, screen-space)
29 -- ============================================================================
30
... [341 more lines]
Let me look at the tools directory, the engine texture-binding API, and the SNKRX template's existing effect system (the "ones to replace").
Grep (layer_shader_set_texture|COMMAND_SET_UNIFORM_TEXTURE|set_draw_shader)
Found 1 file
ricochet-template\anchor\layer.lua
Bash (ls -la E:/a327ex/ricochet-template/tools/ E:/a327ex/ricochet-template/assets/ E:...)
E:/a327ex/ricochet-template/assets/:
total 932
drwxr-xr-x 1 adn 197121 0 May 29 20:29 ./
drwxr-xr-x 1 adn 197121 0 May 29 21:26 ../
-rw-r--r-- 1 adn 197121 598060 May 28 05:55 CascadiaCode-Regular.ttf
-rw-r--r-- 1 adn 197121 200500 May 28 13:02 IBMPlexSans-Regular.ttf
-rw-r--r-- 1 adn 197121 107948 May 28 13:02 Oswald-Medium.ttf
-rw-r--r-- 1 adn 197121 35530 May 29 10:04 draw_shader.frag
drwxr-xr-x 1 adn 197121 0 May 29 21:22 icons/
E:/a327ex/ricochet-template/assets/icons/:
total 4
drwxr-xr-x 1 adn 197121 0 May 29 21:22 ./
drwxr-xr-x 1 adn 197121 0 May 29 20:29 ../
drwxr-xr-x 1 adn 197121 0 May 29 21:22 tabler/
E:/a327ex/ricochet-template/tools/:
total 25
drwxr-xr-x 1 adn 197121 0 May 29 21:22 ./
drwxr-xr-x 1 adn 197121 0 May 29 21:26 ../
-rw-r--r-- 1 adn 197121 2117 May 29 21:05 compare_packs.mjs
-rw-r--r-- 1 adn 197121 2612 May 29 21:22 fetch_icons.mjs
drwxr-xr-x 1 adn 197121 0 May 29 20:29 node_modules/
-rw-r--r-- 1 adn 197121 7252 May 29 20:29 package-lock.json
-rw-r--r-- 1 adn 197121 247 May 29 20:28 package.json
Grep (layer_shader_set_texture|function layer_shader_set)
70: shader_set_texture = layer_shader_set_texture,
71- get_texture = layer_get_texture,
72- reset_effects = layer_reset_effects,
73- clear = layer_clear,
74- render = layer_render,
75- draw_from = layer_draw_from,
76- stencil_mask = layer_stencil_mask,
77- stencil_test = layer_stencil_test,
78- stencil_test_inverse = layer_stencil_test_inverse,
79- stencil_off = layer_stencil_off,
80-}
81-
82---- Resolve layer state table or raw C handle (lightuserdata) for engine calls.
83-local function lyr_handle(lyr)
84- if type(lyr) == 'table' then
85- return lyr.handle
86- end
87- return lyr
88-end
89-
90---- Create a layer state table and optionally register in global `layers`.
--
219:function layer_shader_set_float(lyr, shader, name, value)
220- eng.shader_set_float(lyr_handle(lyr), shader, name, value)
221-end
222-
223:function layer_shader_set_vec2(lyr, shader, name, x, y)
224- eng.shader_set_vec2(lyr_handle(lyr), shader, name, x, y)
225-end
226-
... [29 more lines]
Let me check the engine's image/texture API to understand how to bind a mask texture and how sprites get their texture id.
Bash (cd E:/a327ex/ricochet-template && grep -n "shader_set_texture|COMMAND_SET_UNIFO...)
Binary file anchor/../anchor.exe matches
---- engine src ----
392: COMMAND_SET_UNIFORM_TEXTURE, // Bind a texture to a sampler uniform
3943:static void layer_shader_set_texture(Layer* layer, GLuint shader, const char* name, GLuint tex_id, int unit) {
3950: cmd->type = COMMAND_SET_UNIFORM_TEXTURE;
3988: GLint tex_loc = glGetUniformLocation(shader, "u_texture");
4674: cmd->type == COMMAND_SET_UNIFORM_TEXTURE) {
4693: case COMMAND_SET_UNIFORM_TEXTURE: {
5810:// layer_shader_set_texture(layer, shader, name, texture_userdata, unit)
5811:static int l_layer_shader_set_texture(lua_State* L) {
5818: layer_shader_set_texture(layer, shader, name, tex->id, unit);
5924: GLint tex_loc = glGetUniformLocation(shader, "u_texture");
9472: lua_register(L, "layer_shader_set_texture", l_layer_shader_set_texture);
9770: "uniform sampler2D u_texture;\n"
9871: " ivec2 texSize = textureSize(u_texture, 0);\n"
9874: " vec4 texColor = texture(u_texture, sampleUV);\n"
Read (E:\a327ex\Anchor2\engine\src\anchor.c)
3940 // Set a texture uniform on a shader (binds to texture unit 1+)
3941 // params[0] = texture unit index (1, 2, etc — 0 is reserved for the layer's own texture)
3942 // texture_id = the GL texture handle
3943 static void layer_shader_set_texture(Layer* layer, GLuint shader, const char* name, GLuint tex_id, int unit) {
3944 if (layer->command_count >= MAX_COMMAND_CAPACITY) return;
3945 GLint loc = glGetUniformLocation(shader, name);
3946 if (loc < 0) return;
3947
3948 DrawCommand* cmd = &layer->commands[layer->command_count++];
3949 memset(cmd, 0, sizeof(DrawCommand));
3950 cmd->type = COMMAND_SET_UNIFORM_TEXTURE;
3951 cmd->shader_id = shader;
3952 cmd->uniform_location = (uint32_t)loc;
3953 cmd->texture_id = tex_id;
3954 cmd->params[0] = (float)unit;
3955 }
3956
3957 // Execute shader application (ping-pong): read from current buffer, apply shader, write to alternate
3958 // Called during command processing when COMMAND_APPLY_SHADER is encountered
3959 static void execute_apply_shader(Layer* layer, GLuint shader) {
3960 // Ensure effect buffer exists
3961 layer_ensure_effect_buffer(layer);
3962
3963 // Determine source and destination based on current state
3964 GLuint src_tex, dst_fbo;
3965 if (layer->textures_swapped) {
3966 src_tex = layer->effect_texture;
3967 dst_fbo = layer->fbo;
3968 } else {
3969 src_tex = layer->color_texture;
... [45 more lines]
Read (E:\a327ex\Anchor2\engine\src\anchor.c)
4660 glUseProgram(shader_program);
4661 GLint aa_loc = glGetUniformLocation(shader_program, "u_aa_width");
4662 float aa_width = (layer->filter_mode == FILTER_SMOOTH) ? 1.0f : 0.0f;
4663 glUniform1f(aa_loc, aa_width);
4664
4665 for (int i = 0; i < layer->command_count; i++) {
4666 const DrawCommand* cmd = &layer->commands[i];
4667
4668 // Handle uniform setting commands
4669 // These need to temporarily switch programs, so flush batch first and restore after
4670 if (cmd->type == COMMAND_SET_UNIFORM_FLOAT ||
4671 cmd->type == COMMAND_SET_UNIFORM_VEC2 ||
4672 cmd->type == COMMAND_SET_UNIFORM_VEC4 ||
4673 cmd->type == COMMAND_SET_UNIFORM_INT ||
4674 cmd->type == COMMAND_SET_UNIFORM_TEXTURE) {
4675 // Flush any pending draws before switching programs
4676 batch_flush();
4677 current_batch_texture = 0;
4678
4679 glUseProgram(cmd->shader_id);
4680 switch (cmd->type) {
4681 case COMMAND_SET_UNIFORM_FLOAT:
4682 glUniform1f((GLint)cmd->uniform_location, cmd->params[0]);
4683 break;
4684 case COMMAND_SET_UNIFORM_VEC2:
4685 glUniform2f((GLint)cmd->uniform_location, cmd->params[0], cmd->params[1]);
4686 break;
4687 case COMMAND_SET_UNIFORM_VEC4:
4688 glUniform4f((GLint)cmd->uniform_location, cmd->params[0], cmd->params[1], cmd->params[2], cmd->params[3]);
4689 break;
... [60 more lines]
This is important — for the draw shader, COMMAND_SET_UNIFORM_TEXTURE only stashes the texture for the apply_shader (post-process) path, not for regular draws. Let me verify how regular draws bind their texture (unit 0, u_texture) via the batch system.
Bash (cd E:/a327ex/Anchor2/engine/src && grep -n "batch_flush|current_batch_texture|...)
868: glBindTexture(GL_TEXTURE_2D, tex->id);
874: glBindTexture(GL_TEXTURE_2D, 0);
897: glBindTexture(GL_TEXTURE_2D, tex->id);
903: glBindTexture(GL_TEXTURE_2D, 0);
1210: glBindTexture(GL_TEXTURE_2D, font->atlas_texture);
1217: glBindTexture(GL_TEXTURE_2D, 0);
1922: glBindTexture(GL_TEXTURE_2D, layer->color_texture);
1994: glBindTexture(GL_TEXTURE_2D, layer->effect_texture);
2015: glBindTexture(GL_TEXTURE_2D, 0);
2364:static GLuint current_batch_texture = 0; // Currently bound texture for batching
3992: glActiveTexture(GL_TEXTURE0);
3993: glBindTexture(GL_TEXTURE_2D, src_tex);
3997: glActiveTexture(GL_TEXTURE1);
3998: glBindTexture(GL_TEXTURE_2D, layer->extra_texture);
4000: glActiveTexture(GL_TEXTURE0);
4010: glActiveTexture(GL_TEXTURE1);
4011: glBindTexture(GL_TEXTURE_2D, 0);
4012: glActiveTexture(GL_TEXTURE0);
4024:static void batch_flush(void) {
4028: if (current_batch_texture != 0) {
4029: glActiveTexture(GL_TEXTURE0);
4030: glBindTexture(GL_TEXTURE_2D, current_batch_texture);
4490:static void process_sprite(const DrawCommand* cmd) {
4497: if (current_batch_texture != cmd->texture_id && batch_vertex_count > 0) {
4498: batch_flush();
4500: current_batch_texture = cmd->texture_id;
4545: if (current_batch_texture != cmd->texture_id && batch_vertex_count > 0) {
4546: batch_flush();
4548: current_batch_texture = cmd->texture_id;
4579:static void process_spritesheet_frame(const DrawCommand* cmd) {
... [30 more lines]
Read (E:\a327ex\Anchor2\engine\src\anchor.c)
4024 static void batch_flush(void) {
4025 if (batch_vertex_count == 0) return;
4026
4027 // Bind texture if we have one (for sprites)
4028 if (current_batch_texture != 0) {
4029 glActiveTexture(GL_TEXTURE0);
4030 glBindTexture(GL_TEXTURE_2D, current_batch_texture);
4031 }
4032
4033 glBindVertexArray(vao);
4034 glBindBuffer(GL_ARRAY_BUFFER, vbo);
4035 glBufferSubData(GL_ARRAY_BUFFER, 0,
4036 batch_vertex_count * VERTEX_FLOATS * sizeof(float),
4037 batch_vertices);
4038 glDrawArrays(GL_TRIANGLES, 0, batch_vertex_count);
4039 glBindVertexArray(0);
4040
4041 batch_vertex_count = 0;
4042 draw_calls++;
4043 }
4044
4045 // Process a rectangle command (SDF-based, UV-space approach)
4046 // The SDF is computed in local quad space using UV coordinates.
4047 // This handles rotation correctly because UV interpolation implicitly
4048 // provides the inverse rotation.
4049 static void process_rectangle(const DrawCommand* cmd) {
4050 float x = cmd->params[0];
4051 float y = cmd->params[1];
4052 float w = cmd->params[2];
4053 float h = cmd->params[3];
... [25 more lines]
Let me look at the SNKRX template's existing effect system (the "ones to replace") and confirm the 480×270 default the engine examples use.
Bash (cd E:/a327ex/snkrx-template && ls snkrx/ 2>/dev/null; echo "=== grep effect/edit...)
airborne.lua
area.lua
beam.lua
edition.lua
findings.md
flicker.lua
fx.lua
geometry.lua
gradient.lua
init.lua
palette.lua
player.lua
projectile.lua
prop.lua
seeker.lua
sound_tuner.lua
sound_tuning.lua
stats.lua
symbols.lua
ui/
wall.lua
=== grep effect/edition/dither/shader in snkrx ===
snkrx/area.lua
snkrx/beam.lua
snkrx/edition.lua
snkrx/findings.md
snkrx/flicker.lua
snkrx/fx.lua
snkrx/geometry.lua
snkrx/gradient.lua
... [15 more lines]
Let me quickly understand "the SNKRX template ones" (edition.lua / fx.lua) and how the SNKRX template sets its resolution, so I can speak accurately to the replacement goal.
Bash (cd E:/a327ex/snkrx-template && echo "=== main.lua require/anchor config ==="; se...)
=== main.lua require/anchor config ===
--[[
snkrx-template / main.lua — clean reference scene.
This is your file. It is a complete, minimal scene: a player you can
move (WASD / arrows), the five-layer render stack, a camera, physics,
the update / draw skeleton, AND the UI render pipeline — all wired
and ready, but firing no attacks and drawing no UI widgets. Read it
top to bottom; it is the contract you build on.
TWO TOOLKITS, ONE REQUIRE. `require('snkrx')` pulls in BOTH:
snkrx/<carrier>.lua attack toolkit — projectile / area / beam
/ flicker / prop, the edition draw-shader
stack, fx, sounds, example entities.
snkrx/ui/<module>.lua UI toolkit — panel / button / slot / bar /
tabs / checkbox / radio / slider /
list_row / card / text_input / modal /
bracket_tag / stat_chip / pips / ...
Both are plain, readable Lua, not opaque modules — open the files.
ADDING AN ATTACK. Every carrier already has its object list and its
update / draw loop wired below. To add an attack:
1. bind a key in the INPUT section,
2. call the carrier's spawn_* helper on that press inside update()
— see the ATTACKS comment block there.
Nothing else: no new list, no new loop.
DRAWING UI. The UI toolkit is immediate-mode: call widget functions
... [108 more lines]
Bash (cd E:/a327ex/snkrx-template && grep -n "width\s=|height\s=|scale\s*=|requir...)
66:require('anchor')({
67: width = 480,
68: height = 270,
70: scale = 3,
74:-- The SNKRX attack toolkit. Must come AFTER require('anchor') above —
=== edition draw_shader.frag field/dither size ===
main.lua:67: width = 480,
main.lua:68: height = 270,
snkrx/edition.lua:20: (+ u_field_scale, + u_dither_mode)
snkrx/edition.lua:69: modes (30, 31) ignore u_dither_mode.
snkrx/edition.lua:72: are computed directly from gl_FragCoord — no const-array matrices, so
snkrx/edition.lua:190: layer_shader_set_float(layer, edition_draw_shader, 'u_field_scale', field_scale or 0.15)
snkrx/edition.lua:191: layer_shader_set_float(layer, edition_draw_shader, 'u_dither_mode', dither_mode or 0)
I'm fully aligned now. Before diving in, one finding materially changes the plan, and three forks are worth settling up front. Here's where things stand and what I found.
Where we are
The four-axis effect system (pattern × color × dither × shape), gallery + inspector, ramp editor, and the flat Tabler UI-kit showcase are all built and settled. The recorded roadmap had three remaining items, which map onto what you listed:
- Image-as-SHAPE — per-cell dot becomes a tiled icon silhouette the effect fills
- Image-as-CONTENT — run the effect over a whole real image (dither + recolor)
- More geometric shapes — square / diamond / ring / cross / hexagon
Plus your three new ones: API simplifications, 480×270 support, and ultimately replacing the snkrx-template effect system (its edition.lua / dither stack) with this one.
The finding that changes the plan
The roadmap claimed image-as-SHAPE's hook was "READY" via layer_shader_set_texture / COMMAND_SET_UNIFORM_TEXTURE. It isn't. I traced the engine: for our draw shader (installed via set_draw_shader), COMMAND_SET_UNIFORM_TEXTURE does not bind a sampler for regular draws — it only stashes the texture into layer->extra_texture for the post-process apply_shader ping-pong path (anchor.c:4693). Regular draws bind only u_texture (unit 0) via the batch system, and only when drawing sprites. So binding a separate mask texture to a rect draw is currently a no-op. There's a clean fix and a hack — that's the first question below.
Context for the rest: snkrx-template runs at 480×270, scale 3; this template runs at 1920×1080, scale 1. Our dither and shape grids are anchored to gl_FragCoord (render-target pixels), so they're fine-grained here but would be crisp 1:1 pixel-art at 480×270 — which is exactly the regime that matters for porting into a real snkrx game.
I have a recommendation on each fork; let me confirm direction before writing any briefs.
AskUserQuestion
Your questions have been answered: "Image-as-SHAPE needs a mask texture bound to the rect draw, but the engine doesn't currently support that for the draw-shader path (only for post-process). How should I wire it?"="Patch the engine", "For 'effect over a real image,' what should drive the field f?"="Need to test options visually to see what's better.", "'Make everything work on 480×270' — the gallery + sidebar are hand-placed for 1920×1080. What's the scope?"="We'll do this later manually, piece-by-piece, don't worry about it now.". You can now continue with these answers in mind.
Settled. This session's scope is now: image-as-SHAPE (engine patch chosen), image-as-CONTENT (build both luminance- and pattern-driven so we can compare visually), more geometric shapes, then API simplifications. 480×270 and the snkrx-replacement are deferred to later manual sessions.
Before I write the first brief, let me ground two implementation facts: what image_load returns (so I can get a texture id to bind) and how the C texture-uniform binding reads its argument.
Bash (cd E:/a327ex/ricochet-template && echo "=== image.lua ==="; cat anchor/image.lua...)
=== image.lua ===
--[[
image — thin wrapper for GPU texture handles.
Usage:
images.player = image_load('player', 'assets/player.png')
layer_image(game_layer, images.player, 100, 100)
An image is a plain table with .handle, .width, .height.
]]
image = class()
function image:new(handle)
self.handle = handle
self.width = texture_get_width(handle)
self.height = texture_get_height(handle)
end
-- Load a texture from a file, wrap it, and add to the global `images` table.
function image_load(name, path)
local handle = texture_load(path)
local img = image(handle)
if images then images[name] = img end
return img
end
=== l_layer_shader_set_texture + l_image_load in engine ===
int value = (int)luaL_checkinteger(L, 4);
layer_shader_set_int(layer, shader, name, value);
return 0;
... [18 more lines]
Grounded. image_load returns a table with .handle (the Texture userdata), and layer_shader_set_texture's 4th arg is exactly that userdata — so I can bind any loaded icon as a mask. The engine patch is safe (the post-process stash stays; I only add an immediate bind on a high unit the batch renderer never touches).
Here's the design brief for the first feature.
Design brief — Image-as-SHAPE (tiled icon masks)
Approach
The SHAPE axis re-expresses the field as a grid of circular dots today. This adds a sprite shape kind: each grid cell is stamped with a chosen icon silhouette (its alpha), and the effect fills that silhouette — "the effect is made of icons," tiled across the rect, like the Orblike/Invoker wall icons. SHAPE stays a shared control (one icon, one cell size, applied to every gallery rect), matching how circle/circle_hard already work. Three pieces: an engine patch, a shader branch, and Lua wiring + an icon picker.
1. Engine patch (Anchor2/engine/src/anchor.c)
The COMMAND_SET_UNIFORM_TEXTURE case in the command loop (anchor.c:4693) currently only stashes the texture for the post-process apply_shader path; for regular draws it never binds. Patch it to also immediately bind the texture to its requested unit and point the named sampler at that unit on the active draw shader. Because the batch renderer only ever touches unit 0 (u_texture for sprites), a mask bound on unit 2 stays bound across every subsequent SDF/sprite draw in the frame. The existing post-process stash is left untouched, so apply_shader still works. ~4 lines; no behavior change for existing callers. Then rebuild anchor.exe and copy it + anchor.c's effect into the ricochet-template's anchor/ copy (the engine binary is per-project here).
2. Shader (assets/draw_shader.frag)
- Add
uniform sampler2D u_shape_tex;(the tiled mask, bound to unit 2 from Lua). - New shape kind
sprite= 3. Extendshape_maskto also take the cell-local UV; for kind 3 it returnstexture(u_shape_tex, cell_uv).a(the icon's silhouette) instead of the circle distance test. So coverage = icon alpha; the per-cellfcno longer sizes it (the icon is a fixed silhouette per cell). Color stays the per-fragment fieldff, so the effect varies inside the icon — same ascircle. effect_field_shapedalready computes the cell; it just derivescell_uv = fract(gl_FragCoord.xy / size)and passes it down, and returnsfffor the sprite kind. The coverage → backing → dither composite inmain()is unchanged — sprite coverage flows through the same transparent/tokenbacking path as circles.- This
shape_masksignature change is the same extension point the geometric shapes will use next — one function, more branches.
3. Lua (effect.lua + main.lua)
- effect.lua: add
sprite = 3toshape_kinds,shape_cycle_names, andshape_pretty_names(label'icon'). Nothing in the spec table — the mask bind is a draw-time concern, not a per-rect uniform. - main.lua: hoist the icon-name list to a global
icon_names(ordered), addcurrent_shape_icon(default'skull'). In the gallery draw loop, whencurrent_shape == 'sprite', calllayer_shader_set_texture(game_layer, effect_draw_shader, 'u_shape_tex', icons[current_shape_icon].handle, 2)once before the rects. Sidebar: whencurrent_shape == 'sprite', show an ICONui_fieldcycler (cyclesicon_names) beneath the SHAPE row, next to CELL SIZE + BACKING.
Interfaces
Reuses the already-loaded icons[concept] table, the current_shape_* shared-control pattern, ui_field for the picker, and the existing coverage/backing composite. The engine patch is general: any future draw-shader effect can bind an aux sampler this way (image-as-CONTENT will lean on it too).
Visual unknowns I'll tune live (after you run it)
- Icon texture Y-orientation (may need
cell_uv.y = 1.0 - cell_uv.y). - Tabler icons are 44px line art with internal holes; at small cell sizes the silhouette will read as noise, so sprite shapes will want a larger CELL SIZE than circles. We'll eyeball and tune.
Sound right? Once you're good with this, I'll start with the engine patch (compile-check only — I won't run it), then the shader + Lua so you can test the whole thing visually.
Go ahead.
mcpccd_sessionmark_chapter
[
{
"type": "text",
"text": "Chapter marked: \"Image-as-SHAPE\". Continue your current work."
}
]
Starting with the engine patch. Let me read the exact block to patch.
Read (E:\a327ex\Anchor2\engine\src\anchor.c)
4668 // Handle uniform setting commands
4669 // These need to temporarily switch programs, so flush batch first and restore after
4670 if (cmd->type == COMMAND_SET_UNIFORM_FLOAT ||
4671 cmd->type == COMMAND_SET_UNIFORM_VEC2 ||
4672 cmd->type == COMMAND_SET_UNIFORM_VEC4 ||
4673 cmd->type == COMMAND_SET_UNIFORM_INT ||
4674 cmd->type == COMMAND_SET_UNIFORM_TEXTURE) {
4675 // Flush any pending draws before switching programs
4676 batch_flush();
4677 current_batch_texture = 0;
4678
4679 glUseProgram(cmd->shader_id);
4680 switch (cmd->type) {
4681 case COMMAND_SET_UNIFORM_FLOAT:
4682 glUniform1f((GLint)cmd->uniform_location, cmd->params[0]);
4683 break;
4684 case COMMAND_SET_UNIFORM_VEC2:
4685 glUniform2f((GLint)cmd->uniform_location, cmd->params[0], cmd->params[1]);
4686 break;
4687 case COMMAND_SET_UNIFORM_VEC4:
4688 glUniform4f((GLint)cmd->uniform_location, cmd->params[0], cmd->params[1], cmd->params[2], cmd->params[3]);
4689 break;
4690 case COMMAND_SET_UNIFORM_INT:
4691 glUniform1i((GLint)cmd->uniform_location, (int)cmd->params[0]);
4692 break;
4693 case COMMAND_SET_UNIFORM_TEXTURE: {
4694 // Store for binding during execute_apply_shader
4695 layer->extra_texture = cmd->texture_id;
4696 layer->extra_texture_loc = (GLint)cmd->uniform_location;
4697 layer->has_extra_texture = true;
... [8 more lines]
The active program inside the switch is already cmd->shader_id, so I can set the sampler there directly. Applying the patch:
Edit (E:\a327ex\Anchor2\engine\src\anchor.c)
The file E:\a327ex\Anchor2\engine\src\anchor.c has been updated successfully. (file state is current in your context — no need to Read it back)
Now build the engine to verify it compiles.
Bash (cd E:/a327ex/Anchor2/engine && cmd /c build.bat 2>&1 | tail -25)
Microsoft Windows [Version 10.0.26200.8457]
(c) Microsoft Corporation. All rights reserved.
E:\a327ex\Anchor2\engine>
The build output was sparse. Let me check what build.bat does and whether it produced a fresh binary.
Bash (cd E:/a327ex/Anchor2/engine && echo "=== build.bat ==="; cat build.bat; echo "==...)
=== build.bat ===
@echo off
setlocal
cd /d "%~dp0"
:: Find and run vcvarsall.bat for Visual Studio
if exist "C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat" (
call "C:\Program Files\Microsoft Visual Studio\18\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 >nul 2>&1
) else if exist "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" (
call "C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Auxiliary\Build\vcvarsall.bat" x64 >nul 2>&1
) else if exist "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" (
call "C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Auxiliary\Build\vcvarsall.bat" x64 >nul 2>&1
) else if exist "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" (
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" x64 >nul 2>&1
) else (
echo ERROR: Could not find Visual Studio
exit /b 1
)
if not exist build mkdir build
:: Build Lua library if it doesn't exist
if not exist lib\lua.lib (
echo Building Lua library...
if not exist build\lua_obj mkdir build\lua_obj
cl.exe /nologo /O2 /W3 /c /I"include/lua" ^
include/lua/lapi.c include/lua/lauxlib.c include/lua/lbaselib.c ^
include/lua/lcode.c include/lua/lcorolib.c include/lua/lctype.c ^
include/lua/ldblib.c include/lua/ldebug.c include/lua/ldo.c ^
... [79 more lines]
The build didn't actually run (binary still dated 05-24, and cmd batch output gets swallowed in MINGW bash). Let me run it via PowerShell instead, which captures output properly.
PowerShell
Exit code 1
'build.bat' is not recognized as an internal or external command,
operable program or batch file.
---- EXIT: 1 ----
LastWriteTime Length
------------- ------
24/05/26 11:41:06 4185088
PowerShell
anchor.c
include/stb\stb_image_write.h(776): warning C4996: 'sprintf': This function or variable may be unsafe. Consider using sprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(290): warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(1095): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(1312): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(2759): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(2753): warning C4267: 'initializing': conversion from 'size_t' to 'int', possible loss of data
src/anchor.c(3150): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(3428): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(3434): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(3544): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(3553): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(3647): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(3649): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(4907): warning C4996: 'strdup': The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conformant name: _strdup. See online help for details.
src/anchor.c(6026): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(6911): warning C4244: 'initializing': conversion from 'lua_Number' to 'float', possible loss of data
src/anchor.c(6912): warning C4244: 'initializing': conversion from 'lua_Number' to 'float', possible loss of data
src/anchor.c(8685): warning C4244: '=': conversion from 'lua_Integer' to 'int', possible loss of data
src/anchor.c(8686): warning C4244: '=': conversion from 'lua_Integer' to 'int', possible loss of data
src/anchor.c(8692): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(8806): warning C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10144): warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10148): warning C4996: 'strcat': This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10153): warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10157): warning C4996: 'strcat': This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10163): warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10167): warning C4996: 'strcat': This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10173): warning C4996: 'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
src/anchor.c(10177): warning C4996: 'strcat': This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.
... [13 more lines]
Build succeeded (exit 0, fresh binary at 14:04; all warnings are pre-existing, none from my change). Now copy the new binary into the ricochet-template (its engine binary is per-project).
PowerShell
LastWriteTime Length
------------- ------
30/05/26 14:04:55 4185088
Engine patch deployed. Now the shader. Let me make the sprite shape-kind edits in draw_shader.frag.
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Shader done. Now the Lua side — effect.lua shape registries:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now main.lua — four edits: hoist the icon list, add the current_shape_icon global, add the sidebar ICON picker (with a cycler helper), and bind the mask texture in the draw loop.
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the cycle_icon helper and the sidebar ICON picker. First the helper, placed near the gallery state:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the sidebar ICON picker row, inside the current_shape ~= 'none' block:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now bind the mask texture in the draw loop, right after camera_attach:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now syntax-check the Lua changes:
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: main.lua
OK: effect.lua
Both parse. Let me verify the shader has no stale call to the old shape_mask signature and that the SIDEBAR_H still fits the new ICON row.
Bash (cd E:/a327ex/ricochet-template && echo "=== shape_mask refs (shader) ==="; grep ...)
=== shape_mask refs (shader) ===
557:float shape_mask(int kind, float dist, float fc, vec2 cell_uv) {
588: coverage = shape_mask(u_shape_kind, length(q) / 0.5, fc, cell_uv);
=== u_shape_tex refs ===
96:uniform sampler2D u_shape_tex; // tiled mask for the 'sprite' shape kind; bound to
563: return textureLod(u_shape_tex, vec2(cell_uv.x, 1.0 - cell_uv.y), 0.0).a;
Single call site, clean. The sprite ICON row adds one row to the sidebar, which can co-occur with the ramp editor (the existing worst case). Let me check the row constants to confirm SIDEBAR_H still fits.
Grep (ui_row_h\s=|ui_title_h\s=|ui_panel_inset\s=|ui_section_gap\s=|ui_swatch_size\s*=)
85:ui_row_h = 32 -- height of one label row (PATTERN, COLOR, etc.)
90:ui_swatch_size = 22 -- side of one palette-token swatch
92:ui_section_gap = 12 -- vertical gap between groups of rows
93:ui_panel_inset = 16 -- inner padding inside the panel
94:ui_title_h = 28 -- title bar height
Worst case (sprite ICON row + ramp editor + a gradient's param2 slider, all at once) now totals ≈852px of content; SIDEBAR_H is 820, so it'd overflow by about one row. Bumping it — single-value tweak:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Let me re-verify main.lua parses after the final edits:
Bash (cd E:/a327ex/ricochet-template && E:/a327ex/Anchor/reference/love-compare/tools/...)
OK: main.lua
Image-as-SHAPE is fully wired. Here's everything that changed:
Engine (anchor.c:4693) — COMMAND_SET_UNIFORM_TEXTURE now also binds the texture to its unit and sets the sampler on the draw shader immediately, so an aux mask on unit 2 persists across the SDF rect draws. Built clean (exit 0) and the new anchor.exe is copied into the template.
Shader (draw_shader.frag) — added u_shape_tex; shape_mask now takes cell_uv and, for kind 3 (sprite), returns the icon's alpha tiled one-per-cell; effect_field_shaped feeds it the cell UV. Coverage = the icon silhouette, fill = the per-fragment effect (same as circle), composited through the existing transparent/COLOR A backing + dither path.
Lua (effect.lua, main.lua) — sprite registered as shape kind 'icon'; new icon_names list + current_shape_icon; a sidebar ICON cycler (shown only for the sprite kind); the chosen icon's texture is bound on unit 2 each frame before the rects; SIDEBAR_H bumped to fit the extra row.
How to test
Run ./run.bat. In the pattern gallery:
- SHAPE cycler (sidebar) → step to
icon. An ICON row appears above CELL SIZE. - Cycle ICON through
skull / sword / heart / star / …— every gallery rect should now be tiled with that icon, each icon filled by its rect's effect. - CELL SIZE — sprite icons carry internal detail, so push this larger than for circles (try 32–64); small cells read as noise.
- BACKING —
NONElets the page show through the gaps between icons;COLOR Afills them. - Confirm COLOR (mix/ramp/solid) and DITHER still re-skin the icon fill.
Two things to eyeball for me
- Orientation: if the icons render upside-down, I guessed the Y-flip wrong — tell me and I'll drop the
1.0 - cell_uv.y. - Edges: whether the silhouette reads cleanly or looks chewed-up at the cell boundaries.
Give it a run and let me know how it looks — especially orientation — before I move on to image-as-CONTENT.
[Pasted image/png]
Let's keep this one because it's nice as a way to do icons as a background effect. Let me also see how it looks with filled icons too. However, this only looks good with ramp because the icons become colored, with the default one it just looks like the icons are the background and there's black noise on top of them. We additionally want a mode that's closer to the circles, where the underlying effect itself is defined by the icons and not just as background, if that makes sense.
The icons-as-background mode works and the orientation is correct (bomb reads upright). Good. Let me read the fetch tool before I brief the two additions, so the filled-icon part is grounded.
Read (E:\a327ex\ricochet-template\tools\fetch_icons.mjs)
1 // Fetch the Tabler icon set used by the showcase, via the Iconify API, as white
2 // SVGs, thin the stroke (Tabler is stroke-based) toward the theme's 1px hairline
3 // UI, rasterize with resvg, and write to ../assets/icons/tabler/<concept>.png.
4 //
5 // Run from this directory: node fetch_icons.mjs
6 //
7 // Tabler was chosen over Phosphor/Remix (most icons, best game coverage of the
8 // clean line packs, and a freely adjustable stroke). Icons are rasterized WHITE
9 // on transparent so the engine can tint them to any palette token.
10
11 import { Resvg } from '@resvg/resvg-js';
12 import { mkdir, writeFile } from 'node:fs/promises';
13 import { dirname } from 'node:path';
14
15 const SIZE = 44;
16 const OUT = '../assets/icons/tabler';
17 const STROKE = '0.75'; // Tabler ships stroke-width="2"; thin it to sit with the hairline UI
18
19 // concept -> ordered candidate Tabler names (first that resolves wins).
20 const concepts = {
21 heart: ['heart'],
22 star: ['star'],
23 shield: ['shield'],
24 sword: ['sword'],
25 flame: ['flame'],
26 bolt: ['bolt'],
27 trophy: ['trophy'],
28 coin: ['coin'],
29 skull: ['skull'],
30 gear: ['settings'],
... [44 more lines]
The icons-as-background mode is keeper. Now your two additions — here's the design brief.
Design brief — sprite "field" mode + filled icons
1. Sprite field mode (the circle analog) — the core request
Right now the sprite kind is "background": the icon is always full-size (coverage = icon alpha), and the field only drives the fill color. That's why with mix it reads as icons-behind-noise — the field never decides where icons are.
Add a second sprite kind, sprite_field, that is the true analog of circle: the icon is sized by the per-cell field fc, exactly like a circle's radius tracks fc. Bright field → big icon filling its cell; dark field → the icon shrinks to a point / vanishes. So the effect's spatial structure is rendered as a grid of variably-sized icons, not painted behind a constant icon wall.
- Shader (
shape_mask, one new branch): for the field kind, scale the cell UV about its center byfcbefore sampling —uv = (uv - 0.5)/max(fc, ε) + 0.5, return0outside[0,1], else the icon alpha. Color stays the per-fragmentff(same ascircle;effect_field_shapedalready returnsfffor sprite kinds — no change there). This pairs naturally withBACKING = NONE(dark cells drop out to the page) and any dither. - effect.lua: add
sprite_field = 4to the registries; rename the existing sprite's label toicon (bg)and the new oneicon (field)so the two read distinctly in the SHAPE cycler. - main.lua: the ICON picker + the unit-2 texture bind currently key off
current_shape == 'sprite'; widen both to either sprite kind via a smallshape_is_sprite()helper.
I'm going with size-by-field (not opacity-fade) because that's what makes it "closer to the circles" — circles vary size, so icons should too. (A flat-per-cell-color "hard" variant like circle_hard is an easy later add if you want it; starting with the circle-style per-fragment fill.)
2. Filled icons (to compare)
Tabler's base set is hollow line-art, which is exactly why the default fill looks like noise over outlines and why a shrunk outline becomes a tiny ring. Filled silhouettes will read far better, especially in the new field mode.
- Tooling: extend
tools/fetch_icons.mjsto also try each concept's Tabler-filledvariant (Iconify returns nothing for ones that don't exist, so it self-selects), writing<concept>_filled.pnginto the sametabler/folder. - main.lua: after running it, interleave the filled variants that resolved into
icon_namesright next to their outline counterpart (heart,heart_filled, …) so the ICON cycler flips between hollow and solid with one click.
No new UI for fill — it's just more entries in the existing picker, which keeps this exploratory and reversible.
Sequence
Extend + run the fetch script first (so I know which filled variants exist), then the shader/effect.lua/main.lua wiring, then you run it and compare: icon (bg) vs icon (field), hollow vs filled, across mix/ramp/solid.
Good to go?
Go ahead, additionally also add a slider for spacing between icons.
Approved, and I'll fold in a SPACING slider. The mechanism: spacing insets the icon within its (unchanged shape_size) cell — fit = 1 - spacing is the icon's footprint fraction, applied as a UV scale about the cell center. It unifies cleanly with the field mode (field icon scale = fc * fit) and is a no-op at spacing 0, so the current look is preserved.
Starting with the fetch script so I know which filled variants exist. Extending it:
Edit (E:\a327ex\ricochet-template\tools\fetch_icons.mjs)
The file E:\a327ex\ricochet-template\tools\fetch_icons.mjs has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template/tools && node fetch_icons.mjs 2>&1)
OK heart <- tabler:heart
+ heart_filled <- tabler:heart-filled
OK star <- tabler:star
+ star_filled <- tabler:star-filled
OK shield <- tabler:shield
+ shield_filled <- tabler:shield-filled
OK sword <- tabler:sword
OK flame <- tabler:flame
+ flame_filled <- tabler:flame-filled
OK bolt <- tabler:bolt
+ bolt_filled <- tabler:bolt-filled
OK trophy <- tabler:trophy
+ trophy_filled <- tabler:trophy-filled
OK coin <- tabler:coin
+ coin_filled <- tabler:coin-filled
OK skull <- tabler:skull
OK gear <- tabler:settings
+ gear_filled <- tabler:settings-filled
OK target <- tabler:target
OK bomb <- tabler:bomb
+ bomb_filled <- tabler:bomb-filled
OK key <- tabler:key
+ key_filled <- tabler:key-filled
OK flask <- tabler:flask
+ flask_filled <- tabler:flask-filled
OK user <- tabler:user
+ user_filled <- tabler:user-filled
OK map <- tabler:map
OK bag <- tabler:backpack
OK bell <- tabler:bell
... [23 more lines]
22 filled variants pulled. Now the shader — spacing uniform + the unified bg/field sprite branch:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now effect.lua — registries, pretty names, and the u_shape_spacing uniform write:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the spec docs in effect.lua:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now main.lua. First, interleave the filled variants into icon_names:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the shape_is_sprite helper, next to cycle_icon:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
The sidebar sprite controls — ICON picker (widened to both kinds) plus the new SPACING slider:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the draw loop — widen the texture bind and pass shape_spacing:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now bump SIDEBAR_H for the added SPACING row (worst case ≈ 904px now):
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Syntax-check the Lua and confirm every PNG referenced in icon_names exists on disk:
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: main.lua
OK: effect.lua
=== verify every icon_names PNG exists ===
46/46 present, 0 missing
All wired — Lua parses, all 46 icon PNGs present, and no engine rebuild needed this round (shader + Lua only; the shader recompiles from the .frag at boot). Here's what changed:
icon (field)— new shape kind: the icon is sized by the per-cell field, so bright regions get big icons and dark regions shrink them to nothing. This is the circle-analog you asked for — the effect is the icon grid, not painted behind it.icon (bg)— the previous mode, renamed for clarity (full-size icon, effect fills/colors it).- SPACING slider — insets each icon within its cell (gap between tiles); 0 = fills the cell (unchanged look), up to 0.8.
- Filled icons — 22 Tabler
-filledsolids fetched and interleaved into the ICON picker, so it flips hollow↔solid with one click.
How to test
Run ./run.bat, set SHAPE via the sidebar cycler. It now reads: off → circle (size) → circle (hard) → icon (bg) → icon (field).
icon (field)is the headline. Pair it with BACKING = NONE and a filled icon (cycle ICON to e.g.heart_filled/skull/bomb_filled). You should see a grid of icons that grow where the effect is bright and vanish where it's dark — the field rendered as icons, like the circle dot grid but icon-shaped. Try it overplasma/marble/contours.- Filled vs outline — cycle ICON one step (e.g.
heart→heart_filled) and compare. Filled should read much cleaner as a mask, especially in field mode and at small CELL SIZE. - SPACING — slide it up to open gaps between the tiled icons; works in both
icon (bg)andicon (field). - Confirm
icon (bg)still looks exactly as before at SPACING 0. - Sweep COLOR (
mix/ramp/solid) over both modes.
Have a look — especially icon (field) + filled + BACKING NONE — and tell me how it reads before I move to image-as-CONTENT.
Make it so that icon (field) can only go through filled icons, as it doesn't work with unfilled ones.
Makes sense — a scaled-down outline collapses to a ring, so field mode needs solid silhouettes. I'll restrict the ICON picker to filled icons when in icon (field) mode and snap the current selection to a filled one. This is your explicit spec, so I'll just make it.
First, a filled-only subset list:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && E:/a327ex/Anchor/reference/love-compare/tools/...)
OK: main.lua
Done. In icon (field) mode the ICON picker is now restricted to the 20 filled icons, and switching into field mode snaps the current selection to a filled icon (preferring its own _filled counterpart — heart → heart_filled — else the first filled one). icon (bg) still cycles the full outline+filled list. Lua parses.
Test: set SHAPE to icon (field) — the ICON value should jump to a filled icon, and cycling </> should only step through solid icons (no more thin-ring outlines). icon (bg) is unchanged.
Give the whole sprite feature a final look (bg + field, filled, SPACING, across mix/ramp/solid) and let me know — then I'll move to image-as-CONTENT.
Great. However, spacing is decreasing the size of the icons instead of increasing the spacing within the view we have available.
Right — SPACING should add gaps between icons while keeping their size, not shrink them inside fixed cells. The fix: SPACING enlarges the grid pitch (cell-to-cell distance) while shape_mask keeps insetting the icon by the same factor — the two cancel so the icon's absolute size stays CELL SIZE and only the gap grows. That also makes CELL SIZE mean the icon's pixel size for sprites, so I'll relabel it accordingly.
Shader — grow the pitch for sprite kinds:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now the conditional slider label in main.lua:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && E:/a327ex/Anchor/reference/love-compare/tools/...)
OK: main.lua
Fixed. SPACING now enlarges the grid pitch (icons spread apart, bigger gaps) while their size stays constant — the slider that controls icon size is relabeled ICON SIZE in sprite mode. At SPACING 0 it's identical to before. Shader + label only, no rebuild.
Test: in icon (bg) or icon (field), raise SPACING — icons should keep their size and just get more space between them; ICON SIZE independently sets how big each icon is.
That wraps the full sprite/icon-mask feature (bg + field, filled-only field, ICON SIZE + SPACING, filled icons). Ready to move on to image-as-CONTENT (effect over a real image, both luminance- and pattern-driven so you can compare) whenever you've confirmed this reads right.
Great, what's next?
The sprite/icon-mask feature is done. Next on the list is image-as-CONTENT — running the effect over a whole real image so we can judge how the dither/color/pattern read on actual imagery (not icons). You wanted to compare both field sources visually, so I'll build them as a switchable MODE.
Design brief — image-as-CONTENT
Approach
A third screen, IMAGE (Tab cycles gallery → showcase → image), shows a real test image processed through the effect pipeline. A MODE control switches what drives the field f:
- LUMINANCE — the image's own brightness becomes
f, then dither + color recipe. The classic "dithered photo" look. WithCOLOR = noneyou see the untouched original (a baseline); withmix/rampthe photo is re-rendered in palette tones by its own light/dark. - PATTERN — the selected gallery pattern drives
fand the image's alpha stencils it (the existing sprite path). Over an opaque photo this shows the pattern in the image's box; over a cut-out it paints the pattern into the silhouette.
The screen reuses the sidebar's shared look controls (COLOR / DITHER / COLOR A·B / ramp editor), so you tune them live and watch the image change.
Shader
New uniform int u_image_field;. In the sprite branch: when set, f = luminance(texColor.rgb) (with the universal CONTRAST remap applied, so you get a tone-spread knob for free), then dither + color as usual, image alpha preserved. When unset, the current sprite path (pattern + shape stencil) is untouched.
effect.lua / main.lua
- effect.lua: spec gains
image_field(bool) → writesu_image_field(default 0). One line ineffect_set+ doc. - main.lua:
current_screenbecomes 3-way (Tab cycles three); newcurrent_image+current_image_mode; animage_draw()that fits the chosen image into the content area througheffect_set; sidebar gains a MODE toggle + IMAGE picker when on the image screen;draw()dispatches to it.
Interfaces
Reuses the existing sidebar, effect_set, the sprite render path, and the current_* shared-control pattern. Test images live in assets/images/.
The one thing I need from you is what to test on — it's an outward fetch and the content affects what you're judging:
AskUserQuestion
Your questions have been answered: "What test images should I source into assets/images/ for the image-as-CONTENT screen?"="I'll drop in my own". You can now continue with these answers in mind.
You'll provide your own — got it. To make that frictionless, let me check whether the engine can enumerate a directory at runtime (so the IMAGE picker just shows whatever you drop into assets/images/, no filenames to hand me).
Bash (cd E:/a327ex/Anchor2/engine/src && grep -niE "lua_register(L, \"(list|dir|read_...)
=== any dirent/FindFirstFile/readdir in engine ===
=== lua registers mentioning file/dir ===
9453:lua_register(L, "sound_get_path"
9455:lua_register(L, "file_write_string"
9474:lua_register(L, "shader_load_file"
No runtime directory listing in the engine, so the screen can't truly auto-discover. But I don't need you to recite filenames — once the files are in assets/images/, I'll glob the folder and populate the list myself. Let me check the current state of that folder.
Bash (cd E:/a327ex/ricochet-template && ls -la assets/images/ 2>/dev/null && echo "---...)
assets/images/ does not exist yet
Created context: the folder doesn't exist yet. Let me make it so you have somewhere to drop files.
Bash (cd E:/a327ex/ricochet-template && mkdir -p assets/images && echo "created: $(ls ...)
created: assets/images/
assets/images/ is ready. The screen, the LUMINANCE/PATTERN shader path, and the sidebar rows all hang off real files, so the cleanest path is to build the whole thing in one pass against your actual images (and let you test immediately) rather than build it blind.
Drop your test images into E:\a327ex\ricochet-template\assets\images\ and ping me — I'll glob the folder, wire them in, and build the screen. No need to recite filenames; I'll pick them up.
A couple of notes so the test is informative:
- PNG or JPG both load (stb_image). Any size — I'll fit them into the content area.
- Variety helps for judging LUMINANCE: something with smooth gradients (a face/sky — dithering shows banding behavior), something high-contrast, and a flatter/illustration-style image read very differently.
- If you want PATTERN (stencil) mode to show its real use, include at least one transparent-background PNG (a cut-out) — over an opaque photo, pattern mode just fills the box.
Once they're in, say the word and I'll build it.
I added girl.png and boy.png. What does pattern mode do?
Bash (cd E:/a327ex/ricochet-template/assets/images && ls -la *.png; echo "=== PNG head...)
-rw-r--r-- 1 adn 197121 3383629 May 30 15:01 boy.png
-rw-r--r-- 1 adn 197121 1845595 May 30 15:00 girl.png
=== PNG header info (width/height/bit-depth/colortype) ===
girl.png: 2600x1500 bitdepth=8 colortype=2 (RGB)
boy.png: 2048x1990 bitdepth=8 colortype=2 (RGB)
Both are opaque RGB (no alpha) — girl.png 2600×1500, boy.png 2048×1990. Let me look at them.
Read (E:\a327ex\ricochet-template\assets\images\girl.png)
[
{
"type": "image",
"source": {
"type": "base64",
"data": "/9j/4AAQSkZJRgABAgAAAQABAAD/wAARCAFaAlgDAREAAhEBAxEB/9sAQwAQCwwODAoQDg0OEhEQExgoGhgWFhgxIyUdKDozPTw5Mzg3QEhcTkBEV0U3OFBtUVdfYmdoZz5NcXlwZHhcZWdj/9sAQwEREhIYFRgvGhovY0I4QmNjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2NjY2Nj/8QAHwAAAQUBAQEBAQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3+Pn6/9oADAMBAAIRAxEAPwDvKoyCgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAbvTdt3Lu9M80AVr/UrbT03TsSxGQijLH8P601FvYaVzmLvxkzNi2kt4Rn+JTI36YFXyLuVZEcHi6cHa95aknnMkLD+Rp8ke4WReTXtTZCyJYTL2KFhn+dP2XZhyonh8STqcXenMB/ehff8AoQDUOnJC5TXtdSs7zAguEZv7hOGH4Hmo23FYtUCCgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAz9S1i308bWO+U8CNeuaaTew0jnLzVrq6JDysPSGHj8zWqgkaqCK/lR5yUGfU9a0sirIfJaGWNgZ33P95+59vpVcpLKo0K2AwJJh9CKXIibANDtweZZiPTdRyILD10q2t5RKk0kPPGHA5o5UhlwLMi5BEq+/B/PpVagWxFbXKKskYDkcLIMH8D/hUN30YiSI3dkw8m7fyx/wAs5fnX9eR+dS6cXsLRmhBrCHi5j2f7SHcv+IqHSfQTiaUciSoHjdXU9CpyKyasSOoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAOa8Q+JFtt1pYOGuOjuORH7D3qoxuNI56C3kMu52L3DjLFudoPc+9bpdEbRiaCxpAqxL1c4yf5mr0SOh2jEdJEY3EbffJwB61N+pgmNjcE4AO4fwkYNNNCZJuAKgkDJwM9zVEkmKYAQO4GPekAiWoYjysoxPGw9fw6UmkBqmLyoBDcxLJGBy6DOPqv9RWN+5BTuAUjBgk8yMkfKxz+R/pWkSkRqeDVjIv39vL51nKYZO46o/wBR/WplBMRuafq6XG2O6UQTntn5W+h/pXPKDiS0adQSFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBzPiTxGLUSWVmcznhn7IO/41UY3GkcxZQsT57jcxOEB7n1+lbo0iuprQr5RWMDczZZ2NWlY6oKyGyyDzZHbpGu3+p/pSb1Im9bEc1wW3Iu4yRqHQfyFS9dDOxry2fnWsV3CfvIGVu657H2rBNwYfEV1gF3CUYLzwyk4INdKkpK5D0Fsra4d2t5Solj7sfvj1+tJTWzFcfdWM1uRcCSMBRh13dR6/hScuqC5btfs9vma3YMVGZIWIyvuP84NRdy2E7ll76B4GaGdS3b1z/SmkwsY8qkuXjbDk5IP3W+o/rWtuxQiShiVI2uOqn/PIppgV7q8WKQQxASTt/CTgKPUntUylbRARJbxSzLJefviDnB6D2A6CptfcRp6Jqa2l/cW8zMttIFkjySRGehHsOlZSjrdEtHUg5GR0rMkKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAxfFGsf2Vp5WJsXM3EfsO7f57mmlcaRwVrE09wA5LMxy5P61ukUldm7axBv3mMIBtQe3/wBerir6nTCF9SaH5i8nZmwPoOP8apdzVFJnElt5nQStn82rK9znbuPkXbK0gBJRQ2B3AOD/ADB/ClJ2kgN7w/OGgltSQfKO5PdW/wDr5qJqzIZHqdqbWUXEY/dN8r+3oaKcuV2G9SmYhJOg3bd52bvQ/wAJ/P8AnWtRP4kJDiZEkaGdQsq9R2I9R7VcWpIRCEAZUYlR0jcdV/2T7VjNOD5olKz0Yj7kky+I5DwH/gf61cZqXqJqxKkoJ2ONkn90n+XrWlxCx2k+oFXjIggXkTkZJ/3R6e54+tYzqW0RSjct2ttpdiuEUSuTuaSQb2Y+ua5+Y1VPyIr6CzuEaSAvFcDlSM7WPoR6VSqEuk+hm2Di4eSbaVwPLII7jk1003fUytY2tP1FrPEcuWt/bkx/T1FROn1RLRvo6yIHRgysMgg5BFYEDqACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAGSypDE8sjBURSzMewFAHmGq6hJqupSXT5C/wKf4V7D+v1NbRViiOz3NOQpwDhD+PX9KoqO50Rby7bdjGFyAO1a7I7Vohkp8i0P+wn9KUnZA9I3KSwvLbwxKdrHbj69f51k9Ec9ixDKGlhkIwGJRh9eMfnU1NVcEWNMkNnfIWPEb+U5/2W6H+VP4o3JkdRLGksbRyDKsMEVkI5uWNomkt2Pzodu719D/ACrpi+aOojYeCPVbGGVspIV3K69VPf8A/VWCbiwMmaJ4XMF0gDHof4XHt/h1rojJSQFq2vY0jEF7GJIsYEhXdgejD+tZSpW2HcVdOtLmXehzZryFJyrEeh/u/wA/pUe0drFJBeXZlPlx8Rj9f/rVg3c6IRtqyrjikWJkDknA9fSkBBFG0MTCMDlywX2JzivQgmopHE3dkqOHUMvQ/pWgi1Y30mnvwC9uTlox1X3X/Csp076oTVzo4pUmiWSJw6MMqw6GuYzH0AFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQByvjjUvKt4tPjPzTfPL7IO34n+VVFajRxanCbj16mtRmhYQ7BCzDltzH/P400aRXU2ZuQif3nA/Ac/0rVnYyG8/eIUz1Bz9AM1lVegqnwj7JN91br6yAfzpT+ExXwtiX9v5Vy6/dWXJHs3f/H86iDurMkRpRJtkK8TR7WHow/8A1/pTpu14sGrnT6bcG6so5GPzgbX/AN4df8fxrN6aEGbrqeXdQzAcSgo31HI/TNaU3Z2BFrQ3zZun9yQ/rzUz+IGT3TRzoYTD54P8JHGaS0Ay4dPa4nKb/wDRkOHwclj/AHAfT1P4VTqStYaVye9uVx9nhwI14O3gfQe1YSZ0QjbVlaNN59B3NJK5bdh08gYhUACr0ok+gRXVlSUh2EORluSPb/PFaUafM7sirOysS13HKROjIxkjGSfvL/e/+vSAejq6hlOQaYFmxvX0+UsAWgc5kjHY/wB5ff1HesqkL6oTVzpY5EljWSNgyMMhh0IrmMx1ABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFACMwVSzEADkk9qAKb6vYRtte5Vc85IIH54xQVyMtQzRTpvhkSRfVGBFBLVh9ABQAUAFAHmPiG5F5rd1KGypcRr/ALq8f4/nWsVoUUG+Yqo7mqA23i8pY/VIwPzYZqmjVFyQ/v4/YMf6f1q3udfUbt3NMf7kJ/M//qrCs9kTUHWB/wBMtj/01Sql8Jl9lmzq9gZ4iyDnrn0I71hs7ohPoc05ZGBAwvmDep6qehrZWfvIZraJdfZ74wOcJcdPZx/iP5UprqS0aWvAHTt3dJFI/PH9amO4luRaEjG2mZgdrOMe+BTnuN7k0sr3UptrM7IlOJ5l7f7K/wC16nt9azCw26mS3iFvAAoUbcL/AAj0qWzWETOqDYXedu0cCi4WI2YIjMxwFGSaRVybTNMt7+xW7kLiaVt4foUAOAv04reD5Tkm7u5FcEWtyYJThh0Yjhq61JNGYVQEZjKuXTgn7w7N/wDXpWAcrqxIB5HUdxTAvabffYn2Of8AR2OSP7h9R7etYVIdUS0dCCGAIIIPIIrAgWgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAQkKpJ4AGTQBiX14TCtxcL8jEeVB6+7ep9u1CVzaMbasoweId03lXNuFQ8blbIH1BrR02lcanrYvO1ray/arZUWZfvbBjzF7g/wBKxui3TclsbMMqTRJLGwZHAZSO4qjlasPoAKAKGuX39n6TcXAPzhdqf7x4FNK7GjzAff8AXArYZY06Pzr1SR8oOfwH/wBenHVlLc171sSBf70RP5EVUi1uTkEyK4PGCMfWqO0uaTAJ7iVSMgnB+gX/AOvXNU1kYVXqULVvLMTH+BlJ/AitXrEmOsWdbcXMNuD5jgH071gZJXMTUbaC8t7i6RTCyoT7PjkcU07bFaoy/MUSIQQXV1cL365FbS1Q2dTqdtJeW3kRsE3ONzHsAc8DvWCdiCOKNTCLW2ZhBGNrSZ5b1AP8zQ3cYk88drELe3VVCjAA6LUtmkYmcxzyazNRhoGFIpFS6hkvYZlh/wBXCRuP99/7v4d/fFWloYSld2Ok06H7PYQQnqiAH61aM2c54xt50ljuo418k/K7hznPuOmPetIMloyLb+1xC09mjSQqQr7yNqn6/wA/Sqc3F2FZly21YeY0V2FRlYqJUyUbB6+1aRndagaLJHMFbr/dZT/Iir3Ab+9j6/vV9ejf4GjVAXNL1YWM62szg2jnCuT/AKlj0B/2T2PauepC2qJaOnrIgKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAGyp5kTp/eUj86ARyuoLNdixEagloj1bAUjAJJqotI6LN7FHVo5jIgWENtj25ibcK19rFkulIuR7hEgf720Zx64rjOxbGnodz5UhtG+45LR+x6kf1/Oqi+hy14WfMjbqznCgDkfHdzxZ2gPUtKw+nA/mauC1KRxxJAfHUnArQZs6LAI4Wkx1+UfQVcUVEdfPjUIF7FCPzND3H1LFs5deT90AfjjmmmdsXc2PDoJa6YjlZMD8QDXPP4mc1X4jLuhHBdXUTOAgkK59M8j+daQemo6b7lvTEWWPfM5lnTht38/x9axlFp6hJOOhcu0eW1ljjxudSo3HA5pIkgtbGG2WN2VWmT5vMI6H/ChybAuxTy6kCqZS3Bw8g4MnsvoPegmw+5uVgTyYMAgY46LSbNIxM0nPJqDQaaQxKBlO+umiAghVnnk4UKDkD1pomT0sS2kWpmKKNo4I4FPMKkgkfWqMrHRWju8e5wynptYciqRLItXs/t+mz24xvZcoT2Ycj9aadmBUeOXTNFiitLdZJYwqCPPDEnnn86llLbQ5jUbe3SNrq0m2QzHCRquQJM4Zc9hjkevarhJ7Il+RFDM9s2Ym2juOx/CunbYTRpxajG8ZLKQ6jJUd/pT5iSzd2Za3R7uErG6grIrZXB/2h/WoU4y0Fc0dJ1KW0C215IZoshY5j95R2Dev1/P1rOVK2qE0dDWRAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUActeRC01CaOUYDuWiYjgg8kA+xzxUSOylK6sRkioNyIv5zIqZ2Kclh39AKYi7ppzqduPdv8A0E047mNb4Tpa0OMKAPPPFk/n6/OAciJViH4DJ/U1rBaFGLEhd/lHLHAq0M6RFEEccK9hir20NEjO1Ak6irDOE2qfxBpPcl9yYbl3+UeS6OB6g8EVOyOmEvdNSxumhkO1tsc2FZscpj/OKUlzaoKkebVFvU9G3ATWmzdt2yLI2A47HPrWcZWME7MhtLeGzH7mN2fGCcnH4Z7VDk2aN3JZboxDdKY41Hd3qRWFtIn1VMuNltnkrkF/b/GmiW0W7m6ES+RbgKF4yO3sKGyox6soVBqIaAGmkMa5YbVjXfK52ovqf8KaQnKxo2mkxQAPL+8mJ3Mx9cY4/lVqJm5MupGqfdApk3JBQIXFAiC6VvJLIm9lIYLnGcUFJnP3VvbzabKVgkiZXjOyRtwUZIG325NOO5cUrmjZMtzYxGRVYlcMCO44NdS1RoQ3GjWc2SieU3qn+FFiXBMm0KWSylbSbwq8RGbeQ9CD1Q/0rnnGzOapBxJNQ0fyVaayGU6tB14/2f8ACrhU6MlSJtBvhNH9mZtxVd0bHqV9PwqakbO6FJGvWZIUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBl+I7dbjSJSQC0RWRM+oPT8eR+NJlwdpGC9phsCINjszHis7HddEggmIwWRB6KM0WDmLelx7dTtxnOA5yfp/9eqSMaz906OrOQOlAHlF7Mbm7mlzzNIzfgTWyWlii3pMG+fzCPlTp9a0iioo0VbzbvjoBT6llWdSzXLAcghh/wABIqHuaKN4MbG4DYY4UgqT6eh/A1TRFKVnZm3ZwQNpz3b20sssb5kWN8b+mT7+tc/vQ0Lm5RdiKTxhpzsu6CY45GQMCpMijdanf6ixkT/RLcDlvu5H1PP5YpqLY7i6NoD38wubkuLcHKls7pPcZ6D3pWQXOoup0t4hbwALgYwvRR6UmxxjfVmaak1EoGIaQxjsEUs2cD0GSfYe9AN2Rp6dZGEGecDz3GMdfLX+6P6mrSsYt3L1MQYoAWgQtMQhFA7nP6leWyWt2rsiMzYQn+MhsnH0/maSaTNYJvVBoswdZo1OVDB1Ps3/ANcV0xZq9zVqxFS+ClFDgDLALJ/cbsfzqZWasxNJqzNmxuftVsshG1x8rr/dYdRXK1Z2OGSs7GNqMaWGt2ssXy+c28r75Ab8w36VoneLTKTujoazICgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAM3W5gsEEPeeZVx7D5j/Kky4LUoEUjpGYpDJdPB/taH0Ebk/pTRnVfum/VHMVtRuY7SwnnlJ2IhPAyT7D3oGjyuJW25cYPTHoK3Qzah/0e0CD7xGWNWjaMdCXTvmEj/gKslu7GxAFkz0dGJ/E5/rWPU66askUxlDtP3lOD+FaR1RxzVpNFhfEsel6S9vGmZiT5O3oPXP0rnqJLYrmuHh/QxfWk19fYzOrCIH+HOfmNQhGrpGm/2kkNzeLm3QDZH2kYcbj6r6evWqcrgbV3diPMcX3u59KhsqMb6sziCeT1qTQXGBQAw0DEOFUu5CqoySe1FgbLWm2kjyi7nXaoH7qNhyP9o+h9u1UkZN3NSmIMUALimAYoELQBFcFwi+WwUltuSM9eP54oA5nXoLUxeXAzLPaqygsPlc4DEHvnnOaiUObQ3o4h0m9CLw/IPtMajvCVI9wa6oq2halzK50VaARXKq0QDjK71yPbcKifwkT+FkmkhotQlQtkMhDD1dDtz+IxWMtUmYVNUpFbUcaj4mtrSL5harvmPZckHH14H50k7Jma0R0NSSFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAY+s4a/sl7qsj/oB/WkzSnuQEUje4mKAuTaaM6mD6RN/MU0Z1NiW8lv7qUx2E0VvAOGnZd7E9wo6cep70zNR7kX9i2cjb7kS3Umc755C35DgD8qLFWKeoeFrG4QtaItpOOjKPlP1H+FUnYGjC1GOWzxbzLtkxn2b3B7iuiEkzVySjZEtl8mnO31NV0MkGNskK+ikfoKyO5K1iC8gZGMyjhuuegNUnYwrQ+0inY6AL+7cSyEzOCynHyjHY98VjUg9zGJr6Vp1/LPNpruyWSEefj1/uKffvWSuVKx09zKtvEsUQCnGAB/CKpsSVzOwak0CgBG6UDGkgAknAHU0gLdpZNI4luF2qvKRn+bf4VSRm5GnVEhQAUAFABQAUAQ3ZAtZWP8Klvy5/pQBymr6hpNzJPLZwPJdyqUM2CqAYxn3OKuMW9RNjPDi51B/ZS35//qra2ptRfus6eqNSK5/1DfUfzFRPYmfwskiVX1aZCMqzSKR7FVzWL+FHPL4EaNtaW9ohW3hSME5O0dT7+tQY3JqACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAMLU33a1t5/d24/8eY//E1LNqZHuNI1AN60wHQCQ3gSMkb4yGYfwjI5+vGKaIkaiKqIqIAFUYAHaqJFZgqlj0HNAGc93IxOGKj0FTcvlMvWo/tFtG7kkxuCCfQ8H+lVCVpCaM85j02NOhZgp/PmumWwoLVIkA/fRH3P8qnqdrJ3wUYHpg5piLuiqFsI4bZFWdx8zgfdX1Pv1wK55bnI9DYxHZ24VRwOgzyTS2J3M1n8yRiWBbv7VJoVJ97zeWCWXj7n8Jz3oAsnrQMa+ACSQAOSTQBY061MpW5mUhRzEh/9CP8AQU0iJM1Kokqz6hbwNs3F5P7iDJH17Ck3YqMHLYrPq5UZ+zE8gf6wdzj0pcxcqLir3L0CzgsZ3QknhUHA/GmZO3QlpiCgAoArajMlvp1zNJgqkTEg9+OlAHno4Zwexx+grojohT+I2PDnGoMezRlf1BquppS3OmpnQQ3QcwEIm85X5c44yM/pUyV0TJXVibS0ke8klkAyNzNjszHOPwAFYS0sjnqaJRNaoMQoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgBruI42dvuqCTQBzbByz3By00p3NlvyHtioZ1RVkIbgL/AKxGQ+44/OgY7zU8syZyo6kUAadjCYodzjEknzN7eg/CrRmyxTAp303BjH4mpbKSKJbApFEF6d1hOemEJ/Ln+lCEygFDW0JIz3GfWuyWw6S94To8X+9/Q1J0sUu9zL9ktFMs7jBx92MerHt9OpqZSSInNLQ6axs4tNsxGpzgZZz1Y+tZHK3cq3lyu15pDtjQZ+gqGy4rojLhuILyVZod6Mx2NlcEEdM9iP8A61JO5U4OErMvW0RhhWM7cj+6KZI5yFGTwB3oAfZQ/aWMki/uf4QR973+lNITZpSSpDG0khCooyT6CqIsYN3LPfQPmV03qdig4C56Zx1rNs640ko+ZVsLeW3VvNZANqjCrtGR3+vv3obuOlBwTua1nYmaWKeXIjQ7lU/xHsfpTijOtUT0RrVZzBQAUAFAHP8Aiq7/AHcNkn/LRt8n+6Og/PFVBXZUVqckDlmPqx/nW6M3ub+kx+U9occu5z+KmqZpT3R0FB0jRE11N5COyAYaR16geg9z/Ks5ysjKpPlRrRRJDGEjUKo7VznI3cfQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBS1WTbbCP/AJ6MFP06n+VJlQV2ZmO9SdAh4FAx9nB9omEjf6qM8Dszf4D+f0qkiWaeaoRHPOsKEkjd2FJsErmRuLuSe9QavQZIwALHoKBD4k+QJKc7gc89M9qZL2CLQFaOFor64SIgMY2Cv1Azhjz/ADrTmZEZOOw+TQ7QOgLzzSA7grSYX6kDHFHMyueT3NK1tYbKDbGqoo5JxikQU7u9DsFAJH8Kjq1S2WlYxbuCbVZcwS7YEG0h84LZ5xjrUSTZvSqRhq1cvafZ/YoDH5hcs24nGKaVlYmpN1JczLDusa7mOB/OmZiwWj3Lh5wVjB4Q9T9aaQmzUUBQABxVEFa6sheOqzENb4O6P+8f8KVh30IzpMYAEUrxqBgLgMAKXKjRVpIkg06GJgzlpWHQv0H4U0kiZVJSLlMzCgAoAKAILq4W2iLtyewHehsaVzibyd5b67klfcyyMv0A7VtT2uWtEU7CEzyxp2PLfTrVxMTpYR/pdsBwA5P5KaqRrD4jRuZxbxbsZYnCr6mkbt2VyXQGJ+1ZJJ3KSfUkdawqKzOSo7s16zMwoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAy9UYm6jXsqE/iT/9akzWmupUpGpFJuknSCM4Zz1/ujuaB7GtFGsUSxxjCqMAVRA7OBk0AZFzL50zEfdzUN3ZtFWRFnAoExkQ8xvMP3R9wevvQSPMq79q/O3cDt9aALn25kiWGCLDBQNx+6P8+lPmJ5SzDNBFGMuWduWJGSTTuibMrXNw05wOEHb1pN3KSsULi33HfGpMhIB+cgEd80FFiJBGuBjJOTgYyaBCPLhxHGu+UjIUfzPoKALNrZlXEszb5Ppwv0qkiWy+KZItAC0ALQIKACgAoAKAEZgqlmOABkk0DMGa5a8ufNHES8Rj196hs1Ssjn9WURzXIA5dv1OK6KfwkyehY0aLEbykdTtH0FbRMka9oqm8jJPIDYHrQzWnuNuJPOm8z+EDCD29fxoj3Co7sv6A+Lm5j9VRh+orGstbmMkblYkBQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBDNd28BxLMin0Lc/lQOxF9vjb/VxTyD1EZA/XFA+Vjlu8o7NBKm3sQCT9ME0Byirf2rHBmVT6Odp/WgXKydWVhlSCPY5oFYWgAoAKACgAoAKAEZgqlmOABkmgDAErXBM7jBkO4D0Hb9Kk6YqyFJwM0DGWTFWluiBhvkjz6Dqfz/lQimr6EzTs3VjRcLEcsz7Cikjd1pXGkQYwKCiFw08nkp9xT+8I7/7I/rTM2yb7MrE5BweSMnH5UguTLGqLhQABQIcaAEoAaaYwFAFHVdSjsVhjDBZpmwGb7qD+8aBN2Mm61Ca01SNbVLiSeNssTgoyEDsOSSe/bAxTRL3sdfZXiXkAljyOzI3VT6GmItCmIXNADgaACgBaBBQAUAFAGPqF0LljAhzCp+c/3j6fSpbNIrqRxptGe9Iq5z+uqx1TYOcqrfj0xW9LYzm+hp28QhgSMfwjn610okSSQxSqQD8ylMjtnFTIqLsyV2VQWYhVHrwAKrYTdxNN1vToL9WNyCPLZWKKzY6EdB7VhVkmrIc7cqSN6HXdNnfat0qn/bUr/MVjZmPKzQBBAIIIPQikIWgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAILm9t7XAmkAY/dQcs30A5NA0mysbu7m/497YQr/fuDz/AN8jn8yKCuUT7M8n/Hzcyy/7IOxfyH9TQVZEkUEUAxDGkf8AujFMCSgAoARlDDDAMPQjNAEDWFoxz9njB9VG3+VACCxjX7klwnss7Y/nSCwv2ZwPlvLofVwf5iiwrDGs5WYt/aF4PYOv+FFgsJ9im/6CN5/30v8AhRYLC/Y5/wDoJXf/AI5/8TRYLIU2twf+YldD6BP/AImiwWRT1KFobIq95dSbyFJaToCcHoBSZSSFxjgdqRoQXTkR7FPzvwKRS7jZJY7eNQxwoG1QOTTSb2G2luEVxHLwjc+hGDQ01uCaew8+9IZDM7Ko2DMjnag9/X8OtApMnhRYIhGnOOrHqx7mi5A7caADJNAwoEFMAxQAyaQRJuP0A9SegoAxtbtlIhMyh2k3Bj+XArWlq7AUrSR7UbHVbmID5Fk4KfQ+lU6XYRYi1W4t7xJIYYo0AIManiQeh/Pg0vZMGjqrC/gvoRJA2Qeqnqv1qCbFvOaBC0AFACg80AOoAKBFG8ug2Yo26feI/lUtlpGfGvPTge1I0HmeKPJeRRjtnn8qCTItrK9u75ruW2lIJyuQB9Bz6V0xlGKsZWbdzVTTrt/vCKMf7Tbj+n+NDrdkVykn9js5/eXbgekaBf1Oah1ZD5USDTdNt8GSNHcfxSku361m5X3Goj2mtHQxtDlD6KBSUrFOmZOp+XY3Bgg4WSPexbkgZxjPpXRCTsYtam34Z3/2LEWXapZjGP8AZzxWL3M5bmtSEFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAV7q8gtAvmt878IijLOfYDk0DSuVC19dn5m+xw/3UIaQj3PRfwz9aC1GxJb2sNtkxRgM33nPLN9SeTTGTUAFABQAUAFABQAUAFABQAUAFABQAlAylqQDCFe5ccew5/pSY0Q1JRnXNwqSSTNyIvkUerH/ACB+dI0WiuUl3MS8pzI3X29hXTCPKjCTuxxO055BU5z6VUldaiW5ptk4965DpQyJN8nnHoAVT6dzTIZNSELQAUDFoEKBmmAE0ANIU4yAcc80AUtZRZLTIPzxneAf4h0IHvjt7VcHZ3EYQdGGQykfWuq4yKVwHGGG4qQOe/FJsCVFYFTF5gZejRg5H4ik+V7i0L1rqWtI5CefMueFktic/jgVk4x6MVkb9pPrEi5msYU/3pCpP4c1DE7IuA3Z/ghH4k0CDbdnvCP+An/GgB4S6PWWIfSM/wCNAEc1tcSjBvNg77IwM/rSBMhGmIo+e7lI9tq/0osVdiraWKEMz7yO7yk/1o0HqTCe1jPyNGD/ALIoug5WNe9iA+XLH6UrjUWV3v5D90KKXMWoIgeeWT7zt9BSuUkkIkJbknAoSC41mSIM7NhE5LHsKEuwN2Wpet9GS4uxfXo3naFWJgMYGcFvXrnFXd2sccpa6GyAAMAYFBAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAENxdQ2yhppFTPQHqfoO9A0rlb7Tc3H+qj+zx/wB+QZY/Re34/lQUojYbOGGQzYMkzfelkOWPtnsPYUyifI9aBi5oAKBBQAUAFABQAUAFABQAUAFABQAUAJ3oGZl4+69jHoG/p/jUspDJn8uNm9BmkM5+5fddiMEeXbjn3kPJP4f1rSnHqFR9CzCAkDTtz/Cg9/WuhaK5luNtlE8qr1AOW+grKo7IuCuzRZi0gjX7z5GR2Hc/0rnNpEhjlThNhXsMYxTIuH7wdYm/Ag0AG8D72V+qmgBPMT++v50DHhlPRgfoaBCFqQxKACgQ9OeCOKYFsQ6e6jfBAT33Rgn+VUmRqOjttPjcPFBbow6FUAp3FqWPNjUffUD2NAWGtdRL1kz9OaVwsyM30I6ZP4UXHysjOoL2Qn8aVx8hC94zMTjH40XK5SM3Mh74pXHykTTSH+I0XHZDCxPU0ihKADNAxd1ILADmgYbsd6AGPc4JVdzt/dX+vpWkIOYnZEBia5V1nbOFz5Y+6AePxNdMacY6GDbldnT6VL52mW7n72wK31HB/lXO1Z2MHuW6QgoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoARgSpAODjg0AZlpDEjyS7d0inYZnbczEdeT0Gc8Cg1WxN56HhTmmOw0uT3oGKGFAhd+OgoAPMPtQFhRJ6igLC+Z7UBYPM9qADzPagLB5ntQFg8z2oATzPYUAJ5h9qADzD60AJ5h9TQAb/rQA3fg5BoGZ8qFrhJM4KhgR65//AFVBRT1mf7Pp7S4JCuucfUUAY9hbvKyRnmRyWc+55JrrjGysYt3dy1fzKziOLiGIbQfX1NUxE1mVt7IzycGTkDvjsPxrknLmZ0wXKjQs4WUNNKMSv2/ujsKlCbLQFMQGgBKAA4PWkBE7AZAA96BoZSGLQIWgBQcUwEJ96AELUANyaBiGkMSmAUgFoAQ0AN60DA0DEpDEoGDDYNzkKPVjiiwXRC04x+5RpT7fKPzNXGlJkOokNXzZZVjdlUYyypnp9a3hQSepKm5OxaKLHEVRQo9BXVay0LnpESzAa7kQ/wAUX9aze5lT1ujS0GUqbm1bqjb1+h6/qD+dc9Ve9cxmrGxWZAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAGHFJus4VH93LfXPP60G8VoTRrtHvQNj6BB0pgGTSATOOpoAPMUfxUAHmr6n8qB2E85fQ0XFYTz1Hai47CfaV9KLhYT7SP7pouFg+0/wCz+tK4WENyf7o/Oi4WE+0H0FFx2E85qLhYTzWP8VFwsG9vWgCjb6lBdXDxReYSuPnKEKc9s+vekK/QZrCl7BgP7y5+maumryQS+FmTDuVsqxU+oNdRkkK6pIRE5/djlwvLEegHvWNSVlZGiRrWltJNMLm6TZt/1UX933PvWCRbdzQpiFoAa8iJ95gPbvQA1ZA54Vh7kYzQA48ikBARg4pFBTELQAUAFACUDCgBKAEoAMUDCkAuKAEKkjgUwI2kRc5cZAyQOT+lAXGtI2AUhkfP0X+ZpBcgmmuIxu8qGNQpJLMWPH0xTsDbHL57AeZOQccrGAoH9a6lRitzLmbE8iLugY+rcn8zWiilsiSSqAbHxeL/ALSEfkf/AK9C3Lp/ETy/dH1qma1dhLEZv3PpF/7NWb3M6W5Z8z7HqkFx0Rz5b/Q//XxWdRXiKrHU6Guc5woAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAMa9gaymeZVJtZDubAyYmPU/7p6+1I1hLoIJjtG3BGOCOc0XNLCGV/Wi4WGmVv7xouFhpkJ7mlcdhu6i4WDcaAEyfWkAZNMBaQwzTEGaADNIBM0wDNAC5oATNADJ082F49xXepXcOoyKAMy3s9SSWG2a4hWxhO4eWuGdsYyff3zTuQo2ZpAiVWgnGHxgj+8PUf54oKuUl0h9xDXR2dtqfNj61o6jaJSNGGCOBAsagYGM9/zrMolFADDKM4QF2Hp0H40AMxJIfmfavovf8AGkA5UVPuqBQA6mAuTQBG/JpDEAoAKACgAoATn0oGISB1IH1NADGmiUfNKg+rCkBUbVrNchZGkI/55oWqlFvZC5kTRXDToHghZ1PRiwA/nmmoMxlXS0RJtuW6CFPqS3+FVyGf1h9iGMTTITJLIACQfKUL0/MioasbxlzK5CZLdbxQ6yEBSCZcnk+xosO+palmVYW2IxGDwFNIq+hHZzvLAuYzwMbs8GmxJ3RFfxSzGFWdVQuPlXOSOp5/Cqpq7sKQkOTNM+chsY+gyK6ou7ZBYPWrGxKBEch2yQv6Nj8xSGnZ3LMv3RVs2q7ISxdUvZCzADYqjJ7kms3uRT3JdTUtEFP3G+U+oJ6GkzSpsbGkXZvNPjkc/vV+ST/eHB/x/GuVqzscclZl2kIKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgDLvNMcESWBVDnLQkfK49v7p/SlYuM7FPcGJABBU4ZSMEH3FI2TuI3FIY3IoGGR70AGfagBM0AGaADNABmgAzQAuaAELUAJuoCwZoANwHWmFhhZ88MuP93/69AWFEki9lY/lRcLCPKkoAlVoyDkN6H1BFBLRIkpXasrA54Vx0b/A0xEjuqKWY4AoGRKWlJ3ZVP7oPJ+tAEuABgDAHYUgFoAKBCFgKBiBiaYCGkAUDDFABkdqAKk93Fho0HmnodpwB9TVxg2VGDZnSxgqzHJJ9zj6VuoJIucEolKREbACgY9qpRRyNjRDH/cX8qrQRLFGuxgox3pDSHRBkctGzI395Tg0mkw5U9zWsLuR38mchmIyr4xnHUH3rNqxjVp8uqLUkbB/Nhxvxgqejj0+vvUtXIhPkZCWWa5ikAwQrcHqDxwayeh2pqWqJqRZHbIFTaOisR+tAht0R58f+yjH+Vb0dyZEEGfItiww3lFW+oOf61VJ6slk2PlB/CtxtaXEoJI5wTC2Oo+YfhzSewExYPEjDoaroaSd4oLSMTvdqCNwCY9iMkVmx01uXmC3dqR03jH+6f/rGjdGu6K+gXJg1FoJOBcDp6SL1/MfyrGoupyTWh01ZGYUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBWu7GK6wxykq/dkXqPb3HtSGpNGLcrJZyBboABjhZR9xv8AA+xqWjeMkxDQWJQMKBBmgYmaAsGTQAUAJmgBc0gEJpgJmgBC3qaAEJA6kD60AJ5if31/OgAMsYGTIoFArmVd6kIrxyJvMgMLeX5T4+fHGce4x+NUiJN9DUhhN1pwWYBZJI8OR6+v1oGLatLLbRrP/rFGG+oOM0hlocDA6UxC0gCgAwaAGsKAQoHFACGgBrOqDLMB9aBkUl3Eg3MxA7HHX6UbgZ9zcS3OV5SP+6Op+p/pW8adtxCouEUAADFbJHVH4UDr8hoYT+FmcepoPPEpgXbGzhaKKV1JaSZlJ3H0OP5Vi9zKU2noJJCYbmSIndtwQfUHpVJm9N8yuWbFN12p7IpJ/HilIiu9LGpSOQhuICW8+EDzlGMdA49D/jUyjc1p1HBhEwlXOCpH3lPVT6GsbM7VNPYei/MwA6nNAyC/j2yxkfxIy/jwa3o72IkNkKNZWcy9wo/NcfzAop/EJtaIVOcqe9dSKhroNIwcGkQ1YKAI7cnyAh/gJX8qS2HfSwW5ZbmWWLmRGGUH8S4HH+FZydnczdRwmn0LsN5A1ziKTKSn0Iw//wBf+YoUlc61OLdkypqiPBdLPEcMSHQ+jL/kfrRJX0JqLU6qzuUu7SK4j+7Iufp6iuY5GTUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUANdEkRkkUMjDBVhkEUAY82iyQuTYyDysf6mQnj/dPalY0jUa3Ke7kqw2uOqn/ADz+FSbKSewZoKG5NABmgApAFABQA15ET7zAH07/AJUwAGSQfu4WI9X+Uf40AAgun+8UQe3NFhXF/s8yFFedjudRx9aaRLlZGmmj2iHlWb6n/CtOVGTmyYafaD/l3Q/XmnZE8zHi0thwLeL/AL4FFhXZXudHsLqWKSW2TMRyAowD9cdaLIak0UtRs7eG7h8qPytynJjJXuKFFETnJDLYYMiMxdgc7j1IP+TUNWZtTnzRuTbKRoKFxQAYoAQjFAhtIYjMEUljgDqaAK5keQ5BKJ2Hc/WlcpIciPLIEjXe/Yf56UJXBtLcbqelmCzS6aUSSq4DkdAp4wPx/OtoKzMlO7M2tjQmX7o+lWjpj8KEkOEY+1DFP4WZtI4AoA1bdTHo0L9wyy/m2f5GsWc8neTIrog3lxIT8qkLn6DmrR1UtIIv2EJih3OMPJ8xHp6CpOWpLmkWaDMilkZn8iI4bq7f3B/ie1IaIPsm+8aVJXDRAIu45HqQfbpSsWpWL1q6yKeNsinDKexqGrHVCfMJeRGWD5Bl0O5R6kdqIuzuWzL8wfZ2j3YibmNuyNnOD6c1u19pGEou6kiWNw6rIO4zWyZqnZ3JnXcNwqmbTjzK6IqkwIk+WeRexAYfyP8AKl1AzrW4Ft4gdWY7JWKHPY9v1z+dYy3MqqujomjDoynoaRhFuLuiG4DXVlJGw/0iL5h7kd/xGa0Tuj0lJVIXRFoGrR2Mv2WbIgnbdG4GdrHqD9f51jOPVGE0dVHLHKu6N1dfVTmszMfQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAU7rTbe4UjaEJOcqOM+uPX360DTsY11Y31kSwQ3UPqn3x+HepsbRqdytBeW9wuUkAPdW+Uj8DSNLon69KBjWdV6sAfSgBV3v8AdjbHqeKAuL5Dt/rJMD+6nH60CuSRwxxfcQA+vemK5JmgBwNAhyH99F/viqW5MtjUrQxCgAoEJmgDJ1tis1rxwdwz78H+lCM6mxSgkIvEz0dSv4jkf1pTKw8tWi/WZ1hQAUARuefagCN5FTBbr2A6mkMhbc7bn/BewpFJC2yNd3HkxHGBlnPQD196aVyZS5Ubttbx20YSMe5J6sfetbWOdtvcpXCbvD8gAHMZfH45oQLc5ytjoJ14UVaOpbFe8l2KqDq+fyoZnWlaNipSOMFjM0iQr96Vto/qfyzSk7ITdlc3r8KlobdOrpsUeg6Z/Cs0r6HPCLlIgtrZJtrE5RGz/vH/APXVS0VjapLlXKjQqTlKeqX6WFsX4MjcIvqfX6CgqMbsj0S7S6tDz++U/vfcn+L8f/rUhzVmWraRfnUsBIXYlSeetAmPkRlcTRD94vGP7w9P8KGrjhKzJ3uY1s3uVOVVSeexHY++ayO2+lzOa1aKMPEpcMMyJ3JPUj39q3i+U5o1NdSC22AssRXy+oAPKnuMdq1XkbluJv4T+FWjanLoK6Z5Xr6U2hzhfVFWX5ZY399p/H/69QzA5zUTnUJ1B5D8H9axluyWddpt19ssYpj94rhvqODUnM1Z2JpUYMJYseYv6j0NNOxdKo6bv0MG9hQySopZUb50PQr6j6g1e51Oz2NvRL2O+iEV5tW9XjzEO0yD1BHf1FZONjJo2B9ri+5Ksy/3ZRg/mP8ACpsIcL9U/wCPiKSH3Iyv5ilYLFlJEkUMjqynuDmlYQ7POKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAKV7pNnendLEFk/wCeicN+ff8AGkNSaMebRrm0YlIYruP1QBJPy6H9KVjVVO4yCaAuY0ASQdY2Xaw/A80GiaexYoAKACgBaACgQqH9/CPWQU47kz2NYVqYC0DCgAxmgRU1G1+1WwVTh0YOp+n/ANagmSujnpVZgpjI3o6sv1B6fzpvVGEHyyNjqAQCAex7EdRWTPQTugpFCMcCgCpNPglIxufuew+tK40hkcZyWzuY9WNIoawMhKj7g4JHf2oAu6Q+yS8wANojx+RrSBhVepea5ZUZuOATWljC4jJjRyv/AEw/9lqSzlVGce9bHSldlitDrMuSTzp2k/hHyr9Kldziqz5pCEhQSTgDkk0GRNpsm0yXYKh9u2Mv91Ae59SfT0rP4mKUXLQjfVYGuis32jyz9+VUyX9uvAobtohvRWibmluH0y2YDA8sdvwqVscsviZPLIIk3EFjnAUdWPoKZJzmuW9wlws07b1kGAR0Q/3aEbwatZFTTrk2F6k4z5Z+WQeq/wD1utDRUldWOvZY5kBYK6kZBIzQc2wz7OAMJJLGPRX/AMaVh3KE6XCXK27uHiuJBzjBIHJyPUY696lrU1jP3WjUkkSNSznAqzI5651DOsbmCxpH+7YAfMxPqfb/ABpwetzop6I1Ae4NbmpMjbh71SZ0wlzIZcxebCwH3sZB9+1Jq6CcOY5e8RZL+d8YYsD+YFYNXbOSW5reHbjy2e1fgtmRB69jUswqLqdCORSJKGp2RkTzoR+8Tkr/AHx3H1xTvY0hPl06GPHtimx/A5BHsexqzpXY6C01WaJQJwZ4/wC8Pvj6+v8AOk6fYzcTVtr22uf9TMpbup4YfgeazatuTYSW0hYMw3QsRy8bbP8A61IDn7vE98ZEuJJFjRUSYNtYnnJBFawhdalpF2y1a7t/luj9pTP3gArgfyP6VLo9hOJt2t3BdpugkDY6joR9R2rJprcixPSAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCvd2VteptuYVkx0JHI+h6igLmXNot1BlrG68xf+eVxz+TdfzpWNFUfUptcS277L21lgP97G5D/wIUjRSTJo5UkXcjBl9VORQUO3CgAyKBCx4N1bD/pqP5GmtyZ7GxWpgFAwoAKACgDn9Qt/s964Awkvzr9e4/Pn8aaOepGzuXj/AKiO4P3JABJ/st0Df0P4VDR0U5aJjGBDEHgioOggnc4xSY0RQRA9sKKQ2yV8MCOx9KYhuAAABgDtSGLaMEvJVzzLGpx/un/69aQMKxYuCRbye6kfnxWjMFuXrz5NPn/2Ym/lUmiOUiHOfQVujsprW5FfSlYxEpw7/oO5pvsVVlyxsVFXAAA47UzjHJbNduIkjMhBBI/hH1PpUSaC6WrLGs2ItdLRmbe/mqCeijr0FRe5Cm5MwWbbwPzoGdLo11FbeHY5p32pEWUn/gRwB70kYzjeWhg3+ozX9z5rbkReI0BxtH4d6DWMUkH225kgaJrmR42HKud386dg5VuU2eYcfK4/ImlqUb/hvVg4+xzEjHCFux/u/wCFCZjUh1R0dMyM+SVRqxZufJiCqvcs3J/QCl1Kt7osl3b26NPcXEJkRSQocfLx0Hv70Ak3ojko3LgtJ99yWP4047HQaul35SMxytujXoQMlR/hVxlbRmkU2rmxFIrqHjYMp6EGtUxp2d0WFYMKpHTGSkjn9ZhZdU3gfLKgx9Rwf6VjNWkYVlrci0X/AEjxHFHG21TvAJ/3T/hWbepzyV1Y6/ZJC22Rce/Y0GFmtx45pFHP6namG7ZcfupPmTA6eo/r+NVF9Dopu6sOs5SQY3+8vB9/etIvoaNFlkV/vKG+oq7EjfKTptyPQ80uVAPpgFACYw4dSVcdGU4I/Gk0nuBei1m7gTDxi6x05CN/gf0rCVL+UlxNqyvIb62WeBsqeCO6kdQfesXoQ1YnoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAIBGCOKAM650SznYuimGU/wAcR2mlYtTaM27sL+yjLpLHPGCMlxggZ9R/hSsX7RCM/l/61Hi/3hkfmOKpxYKomIskbSwyJIreXKpO1s47c/nSW45axN6tTAKAG71/vCgLi7l9R+dAC0DKmp2xubU7B+9Q709z6fiOKCZK6sGmlJtOjGAykEEEdR6Ghih8JTdGgn+zvk8ZjY/xqO31H8sH1rNo3gyGUFmAHWpZqiRgI0CigCOgY2kAy3+bVGPaOAD8Wb/61aQMK72Rdcbwi/3pFH6itGYR3LOsPs0u49WXb+ZxSNVuc0XWGEu5wBzW60R3w92N2ZZdnkaR/vN29B6UzknLmlc0NPU3EywxttGfnlxn8F/xqb9ioQcjpNFt44tLEIQDDuHGeSdx5PvjFYvcymtWjL8W2pXRZSOVDoQfxoMkrM4wLvX6daoseC+1YyzFFJZV7AnqfrSsBE645FAyEsc8UgJEG8e1MB7Rs7AxsUlH3XHFDVwLctzeMiuL25LYxjzDzTaJ5V2KkaF2bzBlzyTI3J/OkkUTeSm0q0kQHQ4NVYCqGYqM9akCzZymOZZAcKDhqaLpy5ZGuBLA5ktyMnloz91v8D71adjonTvqi7Z3on4KmOQdUatFIwTcWP1KE3VtmMfvozuUevqKJLmRq2pq3UxvCik+I7Vv9/P/AHya5mczPRGQMMEAj0NBLRVktSpzF/3yaZm422Kd3ALmBoj8r9VJ7HtQOMrO5zzM0biTBDJw6/z/ACq79TrumrmijB1DA5BrZEC0AFABQAUARpKHkdADtUA7ux69PypJ3YGhYoul20WoKCscvN0O2CeHx6jIz7H2rknq7kPU6KpJCgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCO4iE9vJEejqV/OgDEhcvCjN97GG+vQ/rWpiyveWsLRPKIwsqjcHTg8c9utJoqMmjfB3AH15oNCN2bdjOKBMhbimIYTQIfHOUODyKLDTsWlYMoIPBpFlSPbZ3bRk4inYunoG/iH49fzpC2J7m3W4j2sSrA7kcdVPYihopOxlxA+Y6yjEsZ2sP6j2NZ21N07obMcvSZSIs80hhQAsGFu3B/jjBH4E5/mK0gctfoXIxm4gH+3n8gatmUSPxDKBbxQ5xvfcfov/1yKcVdnRSjzSOSurjzn3H5YU+7nv71t5mlSpd2QyzSS9uFSNOCeAe/uaW5EI3Z2NtbJbxhVAz3OOtM6hbOb7Pqslu3CXK+ZGf9scMPywaymrM5qy1uHiaIy6DdAAnaA+B7EGpW5icNEgYlfUVoCLEVqrR5ZgMdcUFKJE0eX2qB9cdaBWKsscO45YDHXFToIhZkUjyiffNIZIrK6ncccc0wGoZUQguDzw2egoVwLVrpct648nLNn5pD90D60OxLkkXr3w9cxQtJBJHMVGdpUqT9OtLUlVE9zFeLzEEm3jHQ0WvqaEaso6px3waANzTJvOtF5yU+U/0po7acuaJbaNZFw4yKZo0nuOSWe3OQWmjH8JPzD6Hv9DVqTRhOk1rEl05LcazBqMTqsbMRITxgkEdO2eM1M1fVHPLXU7GsyAoAY8SuMMAaCWjntdsfs8guU+5Idr+zdj+NVF62NKbtoZtpOsLeS7AKeUJ/lWkXbQ0ZZka4WTKKjRY9DuH4d6p36EskiBlOBcwBv7pUg/kTUc7M3NroX7fSZJRue4wv+zH1/M1LqMFNssPp1haRma6ZmVe8rcfkOtTzSY7tmTKX1C4k8tCodlU448tM4A+p5496u/LG3UrZHTmNDGYyimMjbtI4I9KyIGadIxSWFlwYJCi+69VP5HH4VDBlugQUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAYs8fk3s0fYnzF+h6/qDVx2M5rUY6743X1Uj9KolF+zfzLOBx/FGp/SkaiyqW5A6UwZCaCRhoENbigGOhmMbZHKnqKATsWbmFbu2KZxnlW9D2NI03RQt7qaLKnqp2sh7GnuZXcXYjv2IvoLhCdk0ZjIPZhyP0zWUjrpNNEBPNQb2GnrQMUZoAZKxj2zYP7s5P06H9P5VUXZmNWF4mhbOGvEH91SfzIH+NbM44PQwNavReX0zBx9ni/dAj+LHX9f5VUdjqg3GJisHuHDKpIByqetV5jjG51Og2scNoJshnl5JH8I9KpG8VZGpTKIbu2FzFt3FJFO6OQdUbsalq6E1dWJrK4N/bTWt2gSdQUlXswI+8PY1jsccouLOERGilKMMPExjcHsRxWnQQplO7K/iKQXI7qXcgROM9T6UMCr7GkBIdPl2eYdqrjPWiw7MheMxAOxwf4VpCJ7VJZuUQFgQfm6HHOKe4WudtDIssKSJ91gCB6Ujlas7ElMRymrWyW1/IgGI5PnUDtnqPzzTXY6IO6KVzp6rGJIXJz2NJo0aE0ZzHetE3HmDp7iktzahL3rG6Ks7BR1FAEbIUlZ4up4dD0ce/v7007GNSnzarc2PD2rGU/ZZicZxEW6j/YPv6HvUyXU42up0FQSFAEV1bpdW0kD/AHXGM+nvQBxf721uirAedC+CCOCf8CP51p8SNL3OhisLS9tkuLNmgDjO1eVB7jaen4YqVJoi7RFLpF1jH+jzD3yp/XNV7Tuh8wxNKu+giiT/ALan+goco9guiSHw+fOEk0qjHaMHP5np+Aqebsg5jQFtDBLBDDGFXcZGx1OBwSe/JqGSLczsGKLxjvVIzlLoGmSE3U6Ek5VWH6j+gqJbji7o0qkoKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAMzV02S28/bJib6Hp+o/WqjuTLYrd6szLGln/iXxr/c3L+RIpGqLlADHTd060AVnGDgjBpksjbigTI84oJJoLjy2wfunr7UFKVht/EEYXKn5TgSf0b+lLYqSuihfq7Wb7M7oyJFHuP8miSuiaUuWSGo4kRXXowyKwPSQ5ULvgUA2W1UKMKMVRAMoYENgg+tAGJNemAT2wLpMuI9/wDdQZJYe5BAH1rWHvHN7NKRiSsJCqIu2IdAK2t0NFrqXLWPgnIzSZ0U49TS06drafaT+5flh/dPr/jQnY0atqbo6VZItAEUsO9lkRjHMn3HHb2PqPaplG5MoqSszlPEKvHqnnmLyzKAzgcqWHBIPoRis1daHLKDiVhH5o3x/c7+1Mm1yveR4xIg6cEUmBNYWizjzJPuj+H1ppDSuTPL5snXManCH1PrRuNsoXAMkhJ6jgUmiSxYn91gHDKxpoaOh0KYvBLE3WN8j6Hn+eaRjUWtzToMzI8S23m6d56j54Du/wCA9/8AH8KRcHqYFpJ5seG5IOMVSZsiZrfyZEnYfMpBHsO9DLh7skzTpnoD4V3SqPemtxPYS5wlywH1oluJFSaF0PnW5IcckDv/APXouY1KXWJ1+j341GwSYjEinZIvow6/41k1ZnIy9QIKAOb8U2ex471Bw2I5Pr/Cf6flVwethoZ4fvvJnMLt+7l/8db/AOvRNdSpK6ubE10xkKrwAcVJzuWpYtmZsknIpMuJPQUVRJ/pMsnUDEY/Dk/qf0pITditISzE+pqjJjbWYQapArA/v1aMfUfN/Q1MioG5UFhQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBXvrf7VZSw9Cy/KfQ9R+tAGPBL50CSYwWHI9D3H51qYsm0p8NcQntKSPxwf60i4s0aChRzQMRkVh8woCxE8WRjqKZLRWkgYcrz7UEOJXPB5pkDxMwiMZwVPY+npSKUmitG2xzEewyhPdf/rdKBPuVrZfKkmt8HEbZT/dPI/LkfhWMlZnfRnzQNCFcLnvSRoySmAhoAw/EFkWX7ZEG3KAJQvdR3/CqhLlZEo3MdcMAw+7jj6V1AXbe3kS0juuqO5Qj09KT2NIaalq2Yi9hU8BjwfepW5szaT/AEdxEf8AVN9w/wB0/wB3/Cq2MyeqGLQBDc20d1EY5RkdjjkGk1cTVzlLm3k0u62MMRn06EeoqPI5pR5GJJbrKNyYKjsKCWrlV5gGMaNhgOcUrklq0C7MbRuAwFPYU0Uij5ZU4b8KLEFqFVWBmH393IoZS2LugyN/aTqT8rQk49wR/jUszqbHRUGILF558srlWGGHt3pDSuziJIW0nU57d/8Alk5UE917H8qEdCfU0wyXFucchgRj0NWaXuiWM7o0b1UGkdy1Rcs0+Yv+Aq4oUilcndqEh9FAqXuJAGwKCrl/w7IItYnjDYW5j37f9peCfyNRLc5K8Ve6OnpHOLQBBd26XdtJBJ9yRSp9vegDhgslvNJBJxIhKnHqK2+JXLTOh0+U3EEbsecfN9RxWZyuNpWNuBNkY9TzUmyWgsriKJpG6KCaQzP3ERqh6jlvcnk1SM5O4lMkpXTldS04r/DcLu9g3yiokVDc6eoLCgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAwZE+z6lcQdFf99H9D94fn/OtIszkiKEmO/mxwWVHH6g/wAhTEaQkL8n8KCr3HByvQ0DHece4FKw7h5w9KYXDzEbqw/GgLogmhEnK8N/OgiUblJsqSCMEUzIimUuoZCBInKn+lIdyEyI00E33Q58og9j2H5g1M1dG9CfLKzNJemKzO0KACgBrAHikByupWZ0652oP9FlPyH+4x7fQ9q1pzs+Via0Oh06FZNHhicZV05/E10FrYyJ1e2vLeNvvrKOfUVFrMtvY6C7XfbSKRkEdK0In8LIdOlZoTG7FmjOMnqR2P8An0pCg7ouUywoArXtpFf2pjfHP3WH8JqWriaTVjjb8zaPM0DH52xtx0aobscji4uwltaFYzIxzKx3H3NCVhbkyEg5HDDvQCGzyKc8cnqPSquJiW4IXL5y3T3pAjY8PwhdYjZgPnjcbfwB/pUyG1pqdWbaE/wCpuZ8qFHlxDCgD2FAaI5jxrphkhTU4l+aIbZh6r2P4UFI5myuHjOwE4Pb1FUmPY2rLL2sOOpXFNHoQfuo1UURR49BzWy0QtzHDbpJXP8AE2PyrEoKZJJau3miWBhvibIIPQjt/Sk1cGlJNHZ28y3EEcyfddQwrM89qzsSUwEoEcx4ntVjuorleDKNre5H/wBarg+hSZP4ajLwPnkCQ/0pS3Ia946GpKKd/KB5UQ5LtuI9hz/PFITdkVqsyGSSlcIgDSt91T0+p9qQ0Vr2MW9n5mcusscjMepIcUnsNPVHT1mWFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBla7HsiivVzm3b58d0PDflwfwppiauUJfkv4Gxw6shIH0I/ka06mfQuqcDjpQMcH9RQO4bhQA0mgQxjQIZuI5BIpk3InO4knk0EsjzigRXlVVclxmKQgOP7rdm/z7UikyxZysjfZpn3SAEox/jX/ABHespKzO6jPmVi5SNgoACKQyG6torqBoZVDKwwRQNFfS5nizp9x/rYV+Rv+eidj9R0P/wBeumnPm0e412G6xGrxwyY+dZkwfxrSQy9dnFu/vxTRE/hKFtJ5d7H6SZQ/zH8qGZ03Z2NSg3FoAhsrNGgdYmMMsTlcr0I6jI78GsW2mc85OEtDC1+GG7mIIU3lgMyKvOUPcfTg47U7qWoSfPG6M9Ttwp6djVGJHcuM5j+93xSG2VgcsCR81Ik1IIkMe8nJ9v4aZaRZ0mYpq9oDjJkKn8VPNTLYHsdgz44HLelSZ3BU53Ny1AWB1V0ZHAZWGCD0IoGeb6hYf2Zq89tHnahDxE9dp5H5dPwpxGbOjYezikwBkHAH1NawXU7YP3ET6hP5MBxTk9Cl3M1flCxn72Mms0PyHHjtk9AB3NMT0Nm70wWNnbyhQH+7OVHUnofwPFTF6nPSqXk13LugyEwTQn/lnJkfRhn+eaUlZkVlaVzUpGQUCMHxQvmQIACSjK364poS+NIfoL+Tp5VI2kkaRjgDAA6DJ6dqJPUuW5rIZEjZpmBPXCjgewpE3MtJDcXE1wehOxfoP/r5poybuLNKIlBwWZjhVHVjQCFt4igZnIaRvvN/Qe1Axt/D59jPF3ZCB+VAG1aS+fZwTf8APSNW/MVkaE1ABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQA2WNZYnjkGUcFWHqDQBzlvIIUW2ncCWNzDyfvFeh/EYNaJ6GbWpbGQKoQvPpQAc+lIAOfSgBjHA54oEQPPEv3pYx9XFBJGbiJvuyxn6MKYrMQnP0oENIDKVYZBGCKBCCD7RB5LOUmiO6OXuPQ/0NS1fQ2hJxd0Rx6nJayCHU1ERPAlH3D+P+f61k1Y7YVFJGoCCAQQQeQRQajzgrmgBhpDKt7btMivEQlxEd0TnsfQ+x6GhOzugInmW9t7ZgpG6ZdynqpU8g/Qiuy/MkNMsX8nCxj6mrRnUfQz5iUUOOqMrD8DSZktGbYOaDqFoAr295HEbi4XLRmAyjA6lCQf6VjPuYVVezORaSVbs3u4GY5kY9mBHI+nanbS5knaQXJCOY0+7gEH1BGR+lO4pKzsQqCW2jlvWgke0HljcOfWiw7CxTNESRyD1FAJ2LlpIv8AaNiVPP2hMH0ycVMthvY7pVCjipII5phHwOWoE3YqtKzHljTIuZXibSHvLFLmAE3MGTju69x9e9BaKOkfLplt2ymfz5rWOx6EPhRBeziSfBPyJ8x/pUyd2U2MiBALvwzcn29qQ0bOg6b9oaO/m/1SnMK/3j/ePt6fnUt3OarUvojb1CHz7CeIdWQ4+vUfrSMIuzTMzQZN1xIe0kKv+RP+NXPobV+jNuoMAoEczrN0bi5jt4ed0g+UfxY4Gf8AgXT2BNNdy4K3vHQ28IggjiXGEUCkQ9SvqkhjtwE++52qPf8AzzQTIpgx21vljhI15NMzWosCFm8+VcORhVP8C/4nvQUT0ABFAmWNBOdJhTvGWj/75Yj+lZGpoUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBXlsbWaXzZbaJ5P7zICaAGnTbM/8ALug+mR/KgBh0myP/ACyP4SMP60XYCf2RY/8APJv+/r/40XYCf2Np+cm2U/7zMf60XAkXSrBelnB+KA0ASLZWqfctoV+kYFAXMrxXNBa6JIpjjLy/u4wVHBPU/lmnFXY0cEjvEQY5JEI/usRW9htJ7mtYS6hO3/HyCgGTuQH6UKNwjh1I1Yp5kvLeOQxuJNy5CYIwM+vtSlGwVaChG6ZdmijnjaOVA6N1BqbXOdNp3RmfYL3TjnTJhJD3tpjx/wABPaocOxvGvbctW+qwOwjuP9Gn7xy8fkehqLNHVGcZbMuk96k0Q0kYOelAGPYSxrqMkYBICiVvRWzj8yMflXZHexlTdkW3cyOWPU1qZt3dyG4GbeT/AHTSewjXt33xK3qAf0pHUiRmCqWPQDJpDKmnqYZbdW6h2Q/8DXd/OsnrE55O8Liazolm9tPcpmCQIW+T7rH6f4VKbWxitdDC1PTbiOQSRxM0cUSRuw5AIXn3q9jSqtSqnklAEkX8xkGmmiLDlbPBxn+dMCtOPLPy/wD6qTJI7eYw3dvIeizIxH0YVL2A9GkuguQBz2qbEORULEnJpkE9tFuO9unb3oKiiWeQIrMeigk0FHKXFwIbUHoSucVreyPR2RnR8gySH5R831Pr/hUCRJbxPqB5JjgU9O7fX/CqUb7mM6nNotjs9HIOk2u0YATGPpxWT3Odl3uKQHPaCcajLGOkaOo+m/irb0RrN3ijoKkxKOq3/wBigARd80nyxr6n1PsKaVxpXM/w9phRRe3Db3b/AFZP8/8AD2ob6FSfQ3icDNIgpTobl2YcrDwvu3f8hx+dC3FJaGbOvn3dvb/whvOf6L0/XH5U2ZxL1Aw70AKelAD9DOPtkX92csPoyg/zzWb3LWxqUhhQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAHAeLdRF7qnkxtmK2BT6t/Ef5D861gupSMi0t5LycRxDj+J+y1a1KjFydjpbe3S2iEUY4Hc9T71olY64pRVkKTi9tD6uw/NTUT6GGJ+A0qg88KAI5oIriPy5o0kT+6wyKQ07GfNpMMMJ+ySz25DKV2OSoOR/CeKnkTOilUk5WuLJpt5FBIZ9VkZAOdsYBx+dWqSR2OL6sjtLZbaPYpZmJ3yMxyWY/4CtYxsZS0VkT1ZAyb/Uv/ALp/lSYGlYAi0iz/AHF/lS6HTHZEkmJZEtgfmk6j0XuaibsiakrREnH+nybf4ZYnPsMc/oKzXwsyj/DYt1Ol50P+iRHeW7SEc8ew/U1UY9WVThbViW5YQ5bh3JdvYnt+WKRy1J80rjhbRTthoo29SyA0iVcmGmWWMfZIPrsFIsjuNP02NMtZW5J6AoKLCcrFFbKzjl8yK0gRvUIM0zJzbLK9KYkTwQmQ5P3R+tItK5bLBF+nagsydYu/Js2XPzSnYPp3/TNBVKPNI5aRzeTbz/qR90f3v/rVe52t3Y6OM3kwjGRGOpH86pK7M5y6GwqhFCqMAdK0MjV0KYGGW2P3onyP91uR+ua55qzIkabusaM7kKqjcSewFQIx/D0RYS3mPll4Q+oyST+Zx+FU3shyfQ2aRJgasxk1Qoc4jhGPqxOf5CtaZcdi7pl/ANPiSV1jkiUIyE85HGR6g9azasyZaasZLfrO5RXAUEAgHJ59aNjFtydjRkKwQHaAABwB60jVuyMbTR5pmuzz5rbU/wBxeB+ZyaZmtrF09KAEoEO7UDE007NVuF7SQo/4gsP6ioluXHY16kYUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAGL4n1gaXYbY2AuZsrH/ALI7t+H86aV2NI4qy0qa8w0mYoff7zV0KNzaFNy1Z0MEEVvGI4UCqPSrtY6FFLRAWY4ZVyhfZuz1PfHrip5tbIXNrYbLxPat6TAfmCKJ7GeI+BmjWZ5oUAFADJPmeKP+84/Icn+VNbm+HV5jLyffJ5a/dQ8n1b0/CtFqds5W0KwAAwKswCgCK5/1DAdW+UfU8UnsBsArbwDceFAHHU0mzqWiJ9OtWjL3MwxNL2znYvYVhJ3Zy1JXZCxD391jkfIP/Hf/AK9XA1o/CQynzpRCv+rTBk/ov9TRJ9CK9Sy5UWY0aRsAfU1ByJXLccSxjjr3NItKwSyLEhZvy9aYm7GZJI0jlm6mmYt3BRmgLEqLnAApFpF84VQBwKDQqzSDnJAA6k9qCWctqVz/AGlcjC7baPKjPWT1PsKaVzso07LUqTn+FerHAxVmknY1LODyIQCAGPJ9vatErGDdyemIfazfZb6KcnCH93J/unv+B/rWdRaXE9jau7CO9O24klaHjMIbCNj17n6ZrCxBZACgAAADgAdqYhaAMXWLeR7wTQgEpFh1PG7njHv1q4SsylJJamawebjyBgd5R0/Crc0TKrFbGhotkolZ8DbGck4xuf8A+sKyk7kxbl7zJ9ZmLKtrEcSTHYD6DufwFIGOjRYo1jjGFUYA9qZI/tQAh6UAOHIFADYT5erWzf30kj/k39DUTLibFSMKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAKWq6nBpVm1xOc9kQdXbsBQlcaVzjoVk1a9bULxtzdFXHCjsB7CumMEjpp01uagGBWhuPigN1OlupK7/vMOqqOp/p+NROXKjOpLlRLqGxbzyY1CxwIqKo6Duf6VFJaNkUVo2ULs7Ujb+7Kh/8AHh/jVz2KrfAzSrM8sKACgCoZzvMiH5mXbGf7q92/Ht9KqKO2jHkjzPqRgADA6CtkrDbuFAgoAltYBLOrMMhOfxpMuCuzTtIRcSfaH5RCRGPU9C39BWEpa2CpUd7InuJdh2/p61KOWTsY7Ay5YMyma5yCpwcKMf0q9kdTbhT0NC1tflCqNqDv61JyJOWrL6oqLtUYFIuw2SRIlLSMFHvQJu25mTzGZ93RewpmLdyNRTESCkMs2y5bd2Wg0iSStgE0DZiavckBLdTy/wAz/TsPz/lTNsPDmld9DJwd3FUd1htgolvpXPIhG0fWrjucs3dmrVkBQAhAIIIyD1FAGtpF5vX7LKf3iD5Cf4l/xFc0o8rIaNKkSFAzLuZDsllH3ZH2qfZeP55oRnUdkZ0jlV+UZdjtUepPSqMErm/awrZ2iRlgAgyzHjJ7n86g6krIzEjMl7LcsdwPyocdvUf59aaJl2LFMkcaBjaBDkHGKBoGUrPaS44WcDP1Uj+tRIqJrVIwoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoA43xzzf6eD02scVpT3LjuLZgC3iwMfKK6Edq2LNMZd0Yf6dJ/1y/rWFbdHNX6FfUP+Qpc/VP/AEEVVL4S6PwlC/8A+PRvqv8A6EKqfwlVfgZqHqfrWZ5QlAFe+JFq2D1IH60mVHdEB/1s3/XRh+R4reJ3T3CqICgAoAu2vFpIRweefwqZG9PY1LMAWMAHH7tf5VznK9zOnY4kOTnB5pmC3I4gPNsxjjyjx/3zVy2R2Yj4UbfTiszEKAMe/JN7gkkDpTRzz+IYOlMkcvWgaJB0pFItwf6j8aDRbDLj7o+tApHO6l/x/wAn+6v8qaO7DfAVqo3lsxdI/wBXOf8Aps1XE4jQqwCgAoAEJW4gIOCJk5H1qKnwiZ09YGYyYkQuR12n+VAGTd8W1oO3lA4/AU47GVXcpIf9Pg9gxH14oZENy7ppNxqEgnJkC9A/OPzqDqLT/eP1qzEQdaYAetAwpCHLQMluP9Tb/wDXaP8A9CqJFo0KkAoAKACgAoAKACgAoA//2Q==",
"media_type": "image/jpeg"
}
}
]
Read (E:\a327ex\ricochet-template\assets\images\boy.png)
[
{
"type": "image",
"source": {
"type": "base64",
"data": "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Dz3xPqSy3czI2VzgfQcUuW7POjH2lVyOOmlLsTW0Y2PUhGyIqosKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKAJIBmUCtIPcmexazzTMlsJcNmD8aipuh017xSqTYKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAAcGgDqtOuPtNmjn7w4b61tF3R9ThKvtaSl1E1G2+02jKB8w5X60SV0LF0fbUmupyxGDWJ8uFACgkUASx3DIeDSsZygmaNtqjpjJqXE5amGTN6x8VXMIAExK+j/ADCp5TLlqw+Fm1b+MAQPNhQ+6tilYar1F8US6nimyYfMki/kaLFfWl1TFfxRYqPlWVj9AP60WD61HomYer+K3miaKECJCMHByT+NNIlzqVdLWRxd7dGZzzVpHVSpcqKVUdAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAD4m2yA0XtsTJXRaPB4rVNSV0YobMf3P41nPdI0huVKRoFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFAGxoE2JJISeCNwq4PoevldS0nA261PcOa1e38i8YgfK/zCsZKzPmcdR9nVdtmUak4goAKADNADg5HegTSJFuHHelYh00yQXkg7mixPsYgb2Q9zRYFRRE8zN1NOxagkRE5oLCgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgCzBKT8jfhSu4aoynHqhJzhQueaSbk7sIb3K9UahQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQBb0uTy9QiPqcfnVR3OrBS5a8WdRWx9SZ2uQeZaeYBzGc/hUTWh5uZUualzdjnayPngoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAOlABnNABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUASW7bbiNvRh/OmjSk7TT8zr63PrxssYlieM9GBFJkVIqcXF9TkHUo7IeqnBrA+RlFxbTG0EhQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQA5DtdT6HNA4uzTN9Nbtj95ZF/DNa86PfjmdJ7potQ39rMcJMufQ8U1JM6qeLo1NpGDq8Xl3746PhhWclqeFj4cld+epSqTiCgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgAoAKACgB7yvIFDsW2jAz6UFSnKVrvYZQSFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAFABQAUAf/Z",
"media_type": "image/jpeg"
}
}
]
Both are opaque illustrations (no alpha) — girl.png is a pastel anime piece on a pale cyan ground, boy.png a neon piece on solid black.
Here's what PATTERN mode does, and why that matters for these two:
- The selected pattern (plasma / marble / gradient / whatever) generates the field
f; the image's alpha decides where the effect shows; the image's colors are ignored. So it's "the gallery effect, masked to the image's shape." - Since girl.png and boy.png are fully opaque, that mask is the entire rectangle — you'd see the pattern filling the whole image box with the artwork completely replaced. So for these, plain pattern mode is just "a pattern in a rectangle," not really "effect over the image."
- It only pays off with a transparent cut-out, where the pattern fills just the subject's silhouette.
LUMINANCE mode is the meaningful one for these — it dithers/recolors the actual illustration by its own light and dark.
There's a nice way to make PATTERN useful for your opaque images, though: a background key — threshold the flat background (boy's black, girl's pale cyan) so it reads as transparent, and the pattern then carves the figure out and fills it with flowing effect. Both your images have clean flat backgrounds, so this would work well.
How do you want PATTERN mode to behave?
AskUserQuestion
Your questions have been answered: "Your test images are opaque, so plain PATTERN mode just fills the whole box. How should PATTERN mode treat them?"="Ah, I see, you can find a generic image online to be used as stencil to test the pattern mode.". You can now continue with these answers in mind.
Good call — I'll grab a transparent cut-out to exercise PATTERN mode, and keep girl/boy for LUMINANCE. Let me fetch a few candidate silhouettes (Iconify→resvg gives guaranteed clean alpha, and it's the network path proven to work here), view them, and keep the best.
Bash (cd E:/a327ex/ricochet-template/tools && mkdir -p _stencil_tmp && node - <<'JS' 2...)
OK wolf <- game-icons:wolf-head
OK eagle <- game-icons:eagle-emblem
OK spartan <- game-icons:spartan-helmet
OK dragon <- game-icons:dragon-head
OK raven <- game-icons:raven
OK flower <- game-icons:rose
OK butterfly <- ph:butterfly-fill
OK lion <- game-icons:lion
total 180
drwxr-xr-x 1 adn 197121 0 May 30 15:06 ./
drwxr-xr-x 1 adn 197121 0 May 30 15:06 ../
-rw-r--r-- 1 adn 197121 13786 May 30 15:06 butterfly.png
-rw-r--r-- 1 adn 197121 21150 May 30 15:06 dragon.png
-rw-r--r-- 1 adn 197121 25880 May 30 15:06 eagle.png
-rw-r--r-- 1 adn 197121 23081 May 30 15:06 flower.png
-rw-r--r-- 1 adn 197121 18496 May 30 15:06 lion.png
-rw-r--r-- 1 adn 197121 12558 May 30 15:06 raven.png
-rw-r--r-- 1 adn 197121 24347 May 30 15:06 spartan.png
-rw-r--r-- 1 adn 197121 19609 May 30 15:06 wolf.png
Let me render visible previews (white silhouettes on a dark background) so I can judge the shapes.
Bash (cd E:/a327ex/ricochet-template/tools && node - <<'JS' 2>&1
import { Resvg } from...)
previews written
Read (E:\a327ex\ricochet-template\tools_stencil_tmp_prev_wolf.png)
[
{
"type": "image",
"source": {
"type": "base64",
"data": "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",
"media_type": "image/png"
}
}
]
Wolf head is a strong stencil — bold silhouette with internal detail (the bolt cutout, teeth) that'll show the pattern fill at multiple scales. Let me glance at a couple alternatives.
Read (E:\a327ex\ricochet-template\tools_stencil_tmp_prev_dragon.png)
[
{
"type": "image",
"source": {
"type": "base64",
"data": "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",
"media_type": "image/png"
}
}
]
Read (E:\a327ex\ricochet-template\tools_stencil_tmp_prev_butterfly.png)
[
{
"type": "image",
"source": {
"type": "base64",
"data": "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",
"media_type": "image/png"
}
}
]
The wolf is the best stencil — bold silhouette with internal holes and fine teeth that'll show the pattern fill across scales. I'll use that one. Copying it in (full-res white-on-transparent version), adding an attribution note since game-icons.net is CC-BY, and cleaning up the temp files.
Bash (cd E:/a327ex/ricochet-template && cp tools/_stencil_tmp/wolf.png assets/images/w...)
--- assets/images/ now: ---
total 5137
drwxr-xr-x 1 adn 197121 0 May 30 15:08 ./
drwxr-xr-x 1 adn 197121 0 May 30 14:49 ../
-rw-r--r-- 1 adn 197121 258 May 30 15:08 CREDITS.txt
-rw-r--r-- 1 adn 197121 3383629 May 30 15:01 boy.png
-rw-r--r-- 1 adn 197121 1845595 May 30 15:00 girl.png
-rw-r--r-- 1 adn 197121 19609 May 30 15:08 wolf.png
Three test images ready: girl.png, boy.png (LUMINANCE), wolf.png (PATTERN stencil). Now I'll build the screen. First I need to confirm the engine's image-draw API — specifically whether layer_image can scale (the photos are 2600px wide and must fit the content area) and how to pass a plain white tint.
Bash (cd E:/a327ex/ricochet-template && echo "=== layer image/texture fns in framework...)
=== layer image/texture fns in framework ===
169:function layer_image(lyr, img, x, y, color, flash)
174:function layer_texture(lyr, tex, x, y, color)
178:function layer_spritesheet(lyr, sheet, frame, x, y, color, flash)
=== signatures (with bodies) ===
169:function layer_image(lyr, img, x, y, color, flash)
170- eng.draw_texture(lyr_handle(lyr), img.handle, x, y, color or 0xFFFFFFFF, flash or 0)
171-end
172-
--
174:function layer_texture(lyr, tex, x, y, color)
175- eng.draw_texture(lyr_handle(lyr), tex, x, y, color or 0xFFFFFFFF, 0)
176-end
177-
=== engine l_layer_image / texture draw args ===
layer_image has no scale param, but the photos are 2600px wide. Let me find how the engine scales textures (a scaled draw variant, or camera zoom).
Bash (cd E:/a327ex/Anchor2/engine/src && echo "=== draw_texture-related Lua bindings =...)
=== draw_texture-related Lua bindings ===
9418:lua_register(L, "layer_draw_texture"
9424:lua_register(L, "texture_load"
9425:lua_register(L, "texture_create"
9426:lua_register(L, "texture_unload"
9427:lua_register(L, "texture_get_width"
9428:lua_register(L, "texture_get_height"
9430:lua_register(L, "spritesheet_load"
9431:lua_register(L, "spritesheet_get_frame_width"
9432:lua_register(L, "spritesheet_get_frame_height"
9433:lua_register(L, "spritesheet_get_total_frames"
9434:lua_register(L, "layer_draw_spritesheet_frame"
9485:lua_register(L, "layer_shader_set_texture"
9488:lua_register(L, "layer_get_texture"
=== l_draw_texture signature/impl ===
5637:static int l_layer_draw_texture(lua_State* L) {
9418: lua_register(L, "layer_draw_texture", l_layer_draw_texture);
=== framework draw_texture wrapper ===
E:/a327ex/ricochet-template/anchor/layer.lua:58: draw_texture = layer_draw_texture,
E:/a327ex/ricochet-template/anchor/layer.lua:170: eng.draw_texture(lyr_handle(lyr), img.handle, x, y, color or 0xFFFFFFFF, flash or 0)
E:/a327ex/ricochet-template/anchor/layer.lua:175: eng.draw_texture(lyr_handle(lyr), tex, x, y, color or 0xFFFFFFFF, 0)
Read (E:\a327ex\Anchor2\engine\src\anchor.c)
5637 static int l_layer_draw_texture(lua_State* L) {
5638 Layer* layer = (Layer*)lua_touserdata(L, 1);
5639 Texture* tex = (Texture*)lua_touserdata(L, 2);
5640 float x = (float)luaL_checknumber(L, 3);
5641 float y = (float)luaL_checknumber(L, 4);
5642 uint32_t color = (uint32_t)luaL_optinteger(L, 5, 0xFFFFFFFF); // Default white (no tint)
5643 uint32_t flash = (uint32_t)luaL_optinteger(L, 6, 0x00000000); // Default black (no flash)
5644 layer_add_image(layer, tex, x, y, color, flash);
5645 return 0;
5646 }
5647
5648 static int l_layer_set_blend_mode(lua_State* L) {
5649 Layer* layer = (Layer*)lua_touserdata(L, 1);
5650 const char* mode = luaL_checkstring(L, 2);
5651 if (strcmp(mode, "alpha") == 0) {
5652 layer_set_blend_mode(layer, BLEND_ALPHA);
5653 } else if (strcmp(mode, "additive") == 0) {
5654 layer_set_blend_mode(layer, BLEND_ADDITIVE);
5655 } else {
5656 return luaL_error(L, "Invalid blend mode: %s (use 'alpha' or 'additive')", mode);
5657 }
5658 return 0;
5659 }
5660
5661 // Stencil Lua bindings
5662 static int l_layer_stencil_mask(lua_State* L) {
5663 Layer* layer = (Layer*)lua_touserdata(L, 1);
5664 layer_stencil_mask(layer);
5665 return 0;
5666 }
... [30 more lines]
layer_add_image draws at native size only — no scale. The photos are 2600px, so I must scale at draw time. The clean way (without mutating your originals) is camera zoom. Let me read the camera API to see how zoom/position work.
Read (E:\a327ex\ricochet-template\anchor\camera.lua)
1 --[[
2 camera — procedural 2D camera with follow, bounds, and integrated shake.
3
4 Usage:
5 main_camera = camera_new(width, height)
6
7 -- In update:
8 camera_update(main_camera, dt)
9
10 -- In draw:
11 camera_attach(main_camera, game_layer)
12 layer_circle(game_layer, 100, 100, 20, red())
13 -- ... more draws to game_layer
14 camera_detach(main_camera, game_layer)
15
16 -- Follow a target:
17 camera_follow(main_camera, p1)
18 camera_follow(main_camera, p1, 0.9, 0.3) -- 90% distance in 0.3s
19 camera_follow(main_camera, nil) -- stop following
20
21 -- Bounds:
22 camera_set_bounds(main_camera, 0, map_w, 0, map_h)
23
24 Shake is an integrated sub-structure accessible as camera.shake.
25 Use the shake_* functions on it:
26 shake_push(main_camera.shake, angle, amount)
27 shake_shake(main_camera.shake, 10, 0.3)
28 shake_trauma(main_camera.shake, 0.5, 0.3)
29
30 Design notes:
... [185 more lines]
Camera zoom would scale the whole layer including the background; the cleaner tool is the raw layer_push transform (translate + scale), which camera_attach itself uses. Let me confirm its signature and that it's exposed.
Bash (cd E:/a327ex/ricochet-template && grep -nA2 "function layer_push\b|function lay...)
198:function layer_push(lyr, x, y, r, sx, sy)
199- eng.push(lyr_handle(lyr), x, y, r, sx, sy)
200-end
--
202:function layer_pop(lyr)
203- eng.pop(lyr_handle(lyr))
204-end
All pieces confirmed. Building the feature now. Shader first — the u_image_field uniform and the luminance path in the sprite branch:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now effect.lua — write the uniform and document it:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now main.lua. Loading the content images:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Tab cycle to 3-way:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
cycle_content_image helper, next to shape_is_sprite:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the ui_update guards and MODE/IMAGE rows. Let me re-read the current ui_update to place them precisely.
Read (E:\a327ex\ricochet-template\main.lua)
238 local GAL_RECT_H = 230
239
240 -- Which gallery rect the modifier sliders edit. Click a rect to select it.
241 gallery_selected = 1
242
243 -- Step through an icon-name list for the sprite icon picker. dir = +1 / -1;
244 -- list defaults to the full icon_names (the field mode passes icon_names_filled).
245 local function cycle_icon(cur, dir, list)
246 list = list or icon_names
247 for i, v in ipairs(list) do
248 if v == cur then return list[((i - 1 + dir) % #list) + 1] end
249 end
250 return list[1]
251 end
252
253 -- True for the icon-mask shape kinds (bg + field), which share the ICON picker,
254 -- the SPACING slider, and the per-frame mask-texture bind.
255 local function shape_is_sprite(s) return s == 'sprite' or s == 'sprite_field' end
256
257 -- Step through content_names for the IMAGE-screen picker. dir = +1 / -1.
258 local function cycle_content_image(cur, dir)
259 for i, v in ipairs(content_names) do
260 if v == cur then return content_names[((i - 1 + dir) % #content_names) + 1] end
261 end
262 return content_names[1]
263 end
264
265 -- Screen rect for gallery entry i (row-major in the GAL grid). Shared by the
266 -- selection hit-test (ui_update), the rect draw, and the highlight (draw).
267 local function gallery_rect(i)
... [40 more lines]
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now insert the MODE/IMAGE rows after the PALETTE block:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now I need to wrap the SHAPE block and the MODIFIERS block as gallery-only. Let me read that region.
Read (E:\a327ex\ricochet-template\main.lua)
325 -- COLOR / DITHER cycler rows. (PATTERN + SCALE rows removed — in gallery
326 -- mode each rectangle bakes its own pattern + tuned scale; the sidebar now
327 -- only drives the SHARED look applied across all gallery rects.)
328 row = rect_cut_top(body, ui_row_h)
329 local f = ui_field{ rect = row, id = 'color', label = 'COLOR', value = current_color }
330 if f.prev_clicked then current_color = effect_prev_color(current_color) end
331 if f.next_clicked then current_color = effect_next_color(current_color) end
332
333 row = rect_cut_top(body, ui_row_h)
334 f = ui_field{ rect = row, id = 'dither', label = 'DITHER',
335 value = effect_dither_label(current_dither) }
336 if f.prev_clicked then current_dither = effect_prev_dither(current_dither) end
337 if f.next_clicked then current_dither = effect_next_dither(current_dither) end
338
339 -- SHAPE cycler — re-express the field as a grid of dots. Shared across all
340 -- gallery rects. The SHAPE SIZE slider only appears when a shape is active.
341 row = rect_cut_top(body, ui_row_h)
342 f = ui_field{ rect = row, id = 'shape', label = 'SHAPE',
343 value = effect_shape_label(current_shape) }
344 if f.prev_clicked then current_shape = effect_prev_shape(current_shape) end
345 if f.next_clicked then current_shape = effect_next_shape(current_shape) end
346
347 -- Sprite-only controls (both icon-mask kinds): which ICON the mask uses, and
348 -- the SPACING gap between tiled icons. The chosen icon's texture is bound as
349 -- the per-cell mask in draw().
350 if shape_is_sprite(current_shape) then
351 -- field mode works only with filled icons (an outline scaled down by the
352 -- field becomes a thin ring) → restrict the picker to icon_names_filled and
353 -- snap the current selection to a filled icon, preferring its own filled
354 -- counterpart (heart → heart_filled) when one exists.
... [100 more lines]
Read (E:\a327ex\ricochet-template\main.lua)
454 row = rect_cut_top(body, ui_row_h + 20)
455 local ms = ui_slider{ rect = row, id = 'mod_scale', label = 'SCALE',
456 value = sel.scale, vmin = 0.05, vmax = 1.2, fmt = '%.3f' }
457 if ms.changed then sel.scale = ms.value end
458
459 row = rect_cut_top(body, ui_row_h + 20)
460 local msp = ui_slider{ rect = row, id = 'mod_speed', label = 'SPEED',
461 value = sel.speed, vmin = 0, vmax = 4, fmt = '%.2f' }
462 if msp.changed then sel.speed = msp.value end
463
464 row = rect_cut_top(body, ui_row_h + 20)
465 local mco = ui_slider{ rect = row, id = 'mod_contrast', label = 'CONTRAST',
466 value = sel.contrast, vmin = 0, vmax = 2.5, fmt = '%.2f' }
467 if mco.changed then sel.contrast = mco.value end
468
469 row = rect_cut_top(body, ui_row_h + 20)
470 local mpa = ui_slider{ rect = row, id = 'mod_param', label = sel.param_label,
471 value = sel.param, vmin = 0, vmax = 1, fmt = '%.2f' }
472 if mpa.changed then sel.param = mpa.value end
473
474 -- Second intrinsic knob — only patterns that define one (the gradients:
475 -- OFFSET / RANGE) get this row; it uses the pattern's own min/max range.
476 if sel.param2_label then
477 row = rect_cut_top(body, ui_row_h + 20)
478 local mp2 = ui_slider{ rect = row, id = 'mod_param2', label = sel.param2_label,
479 value = sel.param2, vmin = sel.param2_min or 0,
480 vmax = sel.param2_max or 1, fmt = '%.2f' }
481 if mp2.changed then sel.param2 = mp2.value end
482 end
483 end
... [10 more lines]
Now wrapping the SHAPE and MODIFIERS blocks as gallery-only. SHAPE block — open guard:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
MODIFIERS block — open and close guards:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the image_draw() function:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the draw() dispatch for the image screen:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Let me syntax-check the now-substantially-edited files:
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: main.lua
OK: effect.lua
OK: showcase.lua
All parse. Let me re-read the shader's sprite branch to be sure the GLSL is balanced and correct (I can't run it; the user will).
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
646
647 if (vType < 0.5) {
648 // Rectangle
649 vec2 quad_size = vShape0.xy;
650 quad_px = quad_size;
651 vec2 local_p = vUV * quad_size;
652 vec2 center = quad_size * 0.5;
653 vec2 half_size = vShape0.zw;
654 stroke = vShape1.x;
655 if (u_aa_width == 0.0) { local_p = floor(local_p) + 0.5; }
656 d = sdf_rect(local_p, center, half_size);
657 } else if (vType < 1.5) {
658 // Circle
659 float quad_size = vShape0.x;
660 quad_px = vec2(quad_size);
661 vec2 local_p = vUV * quad_size;
662 vec2 center = vec2(quad_size * 0.5);
663 float radius = vShape0.z;
664 stroke = vShape0.w;
665 if (u_aa_width == 0.0) { radius = floor(radius + 0.5); }
666 d = sdf_circle(local_p, center, radius);
667 } else if (vType < 2.5) {
668 // Sprite
669 ivec2 texSize = textureSize(u_texture, 0);
670 vec2 snappedUV = (floor(vUV * vec2(texSize)) + 0.5) / vec2(texSize);
671 vec4 texColor = texture(u_texture, snappedUV);
672 float sprite_alpha = texColor.a * vColor.a;
673 if (sprite_alpha <= 0.0) discard;
674 vec3 col = texColor.rgb * vColor.rgb + vAddColor;
675 if (u_color_kind > 0) {
... [22 more lines]
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
697 } else {
698 col = ccol;
699 sprite_alpha *= cd; // transparent backing
700 }
701 }
702 }
703 }
704 FragColor = vec4(col, sprite_alpha);
705 return;
706 } else if (vType < 3.5) {
707 // Line / capsule
708 vec2 quad_size = vShape0.xy;
709 quad_px = quad_size;
710 vec2 local_p = vUV * quad_size;
711 vec2 center = quad_size * 0.5;
712 float half_len = vShape0.z;
The shader is balanced and correct. One gap I should close before handing it to you: the brief promised the CONTRAST remap as a "tone-spread knob," but right now nothing on the image screen drives it (so it'd sit at 1.0 — flat). Luminance dithering of these illustrations really wants that control (girl.png is mostly bright pastels). Let me expose a CONTRAST slider on the image screen for luminance mode.
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && E:/a327ex/Anchor/reference/love-compare/tools/...)
OK: main.lua
Image-as-CONTENT is fully built — shader + Lua only, so no rebuild (the shader recompiles at boot). Here's the whole thing:
New IMAGE screen (Tab now cycles gallery → showcase → image → gallery), with three test images: girl / boy (opaque, for luminance) and wolf (transparent stencil, for pattern). The sidebar stays focused — PALETTE, MODE, IMAGE, (CONTRAST), COLOR, DITHER, COLOR A·B, ramp — and re-skins the image live. SHAPE + gallery MODIFIERS are hidden there.
- LUMINANCE — the image's own brightness drives
f, then dither + color. The CONTRAST slider spreads tones before dithering. - PATTERN — the selected gallery pattern drives
f, stenciled by the image's alpha.
How to test
Run ./run.bat, press Tab twice to reach the IMAGE screen.
- Defaults to girl / LUMINANCE. With
COLOR = noneyou see the original. Switch COLOR → mix (COLOR A =ink, COLOR B =field) + DITHER → bayer 4×4 / 2 lvl → a 1-bit dithered illustration. Raise CONTRAST to spread the tones (girl is bright, so it'll want a push). - Try COLOR → ramp → the illustration recolored across the ramp by its own light/dark. Multi-level dithers (
bayer4_8,noise_8) keep the mid-stops. - Cycle IMAGE → boy (mostly dark) and adjust CONTRAST the other way.
- Switch MODE → PATTERN, cycle IMAGE → wolf → the selected gallery pattern fills the wolf silhouette, transparent background dropping to the page. To pick which pattern: Tab to the gallery, click a rect, Tab back (pattern mode reads the selected gallery rect). Over girl/boy, PATTERN fills the whole box (they're opaque) — that's the expected contrast.
Watch for / report
- LUMINANCE on girl/boy: does it read as a recognizable dithered image, and is the CONTRAST range usefully placed?
- Image orientation/fit in the content area.
- PATTERN on the wolf: does the pattern fill the silhouette cleanly?
Have a look and let me know — then the remaining session items are more geometric shapes and the API simplifications.
Everything looks right. Some additional things: is there a way we can also have circles/icons when the effect is filling the shape?; is there any way we could have the pattern work on the images themselves? So the way the image is dithered is such that it uses the underlying moving pattern instead of just being dithered statically. Both of these are additional things on top of what we have now.
Both of these fall out of one clean move, and I want to confirm the approach (especially my read of request 2) before refactoring the shader.
Brief — SHAPE on images + moving-pattern dithering
The key insight: both come almost for free if I unify the image's field with the gallery's effect_field instead of keeping the separate luminance branch I added earlier.
The unification
Make effect_field() image-aware: when u_image_field == 1 it returns the image's luminance (contrast-remapped); otherwise the pattern (gallery unchanged). Then the sprite branch always runs the normal effect_field_shaped → dither → color → backing pipeline. Since effect_field_shaped already samples the field per cell, SHAPE starts working on images with no new shape code — and the separate luminance branch collapses away (simpler shader).
Request 1 — circles/icons in the image fill (both modes)
Re-enable the SHAPE controls on the image screen + pass them through (and bind the icon for sprite kinds):
- PATTERN + shape → the pattern fills the wolf silhouette as a circle/icon grid (dots sized by the pattern, masked by the image's alpha).
- LUMINANCE + shape → the image rendered as a halftone: circle/icon dots sized by the image's own brightness (the classic dot-portrait / "image made of icons"). Bonus that falls out of the same change.
Request 2 — moving-pattern dithering (luminance mode)
Add a PATTERN AMT slider (luminance mode) + u_image_pattern_amount. In the image branch: f = contrast(luminance) + (pattern − 0.5)·amount. The pattern is animated (u_time·speed), so as you raise the amount the dither's light/dark boundaries ripple and flow with the moving pattern rather than sitting static. At 0 it's today's static dither; higher gradually dissolves the image into the moving field. You choose which pattern + its speed via the gallery selection (same as PATTERN mode reads it).
Files
- shader — image-aware
effect_field(luminance + pattern-perturb) +u_image_pattern_amount; delete the redundant luminance branch (effect_field_shapedunchanged). - effect.lua — write
u_image_pattern_amount(specimage_pattern_amount, default 0) + doc. - main.lua — drop the gallery-only guard on SHAPE (show it on the image screen too); add the PATTERN AMT slider;
image_drawpasses shape params (+ luminance: pattern + amount) and binds the sprite icon.
One thing to confirm: request 2 — I'm reading it as "the animated pattern perturbs the field so the dither stipple flows over time," keeping the fine Bayer/etc. stipple. (The alternative would be the pattern replacing the dither matrix entirely — smoother, blobbier.) The perturb approach keeps it reading as a dithered image that's alive. That match what you had in mind?
Yes, go ahead.
Approved. Implementing — shader first. Adding the pattern-amount uniform:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now make effect_field image-aware:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now collapse the sprite branch's two paths into the single unified pipeline:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now effect.lua — write the uniform and doc:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now main.lua. First, show SHAPE on the image screen too — remove its gallery-only guard (opening):
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Closing — remove the now-orphaned end:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the current_image_pattern_amount global and the PATTERN AMT slider:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now rebuild image_draw's spec (shape on both modes, pattern+amount for luminance) and bind the icon for sprite shapes:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: main.lua
OK: effect.lua
Both parse. Let me verify the unified sprite branch is balanced after collapsing the two paths.
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
678 vec2 local_p = vUV * quad_size;
679 vec2 center = vec2(quad_size * 0.5);
680 float radius = vShape0.z;
681 stroke = vShape0.w;
682 if (u_aa_width == 0.0) { radius = floor(radius + 0.5); }
683 d = sdf_circle(local_p, center, radius);
684 } else if (vType < 2.5) {
685 // Sprite
686 ivec2 texSize = textureSize(u_texture, 0);
687 vec2 snappedUV = (floor(vUV * vec2(texSize)) + 0.5) / vec2(texSize);
688 vec4 texColor = texture(u_texture, snappedUV);
689 float sprite_alpha = texColor.a * vColor.a;
690 if (sprite_alpha <= 0.0) discard;
691 vec3 col = texColor.rgb * vColor.rgb + vAddColor;
692 if (u_color_kind > 0) {
693 // The field comes from effect_field (the image's own luminance when
694 // u_image_field is set — optionally rippled by the moving pattern —
695 // else the pattern) and flows through the SAME shape / dither / color /
696 // backing pipeline as the gallery. So SHAPE works on images too
697 // (luminance + shape = halftone), and the image's alpha stays the base
698 // coverage that shape coverage multiplies into.
699 float coverage;
700 float f = effect_field_shaped(vec2(texSize), coverage);
701 float fd = apply_dither(f, u_dither_kind, gl_FragCoord.xy);
702 vec3 ccol = apply_color(u_color_kind, col, fd, u_palette[u_color_a].rgb, u_palette[u_color_b].rgb);
703 if (u_shape_kind == 0) {
704 col = ccol;
705 } else {
706 float cd = apply_dither(coverage, u_dither_kind, gl_FragCoord.xy);
707 if (u_shape_backing == 1) {
... [4 more lines]
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
711 sprite_alpha *= cd; // transparent backing
712 }
713 }
714 }
715 FragColor = vec4(col, sprite_alpha);
716 return;
717 } else if (vType < 3.5) {
718 // Line / capsule
719 vec2 quad_size = vShape0.xy;
720 quad_px = quad_size;
Balanced and clean — the sprite branch collapses to the single pipeline, and effect_field now carries the image. Both parse; shader recompiles at boot, so no rebuild.
Two additions are live:
1. SHAPE on the image screen (both modes). The SHAPE controls now appear on the image screen:
- LUMINANCE + circle/icon → a halftone: dots/icons sized by the image's own brightness.
- PATTERN + circle/icon → the pattern fills the silhouette as a dot/icon grid, masked by the image's alpha.
2. PATTERN AMT slider (luminance mode). Ripples the field with the selected gallery pattern before dithering, so the dither flows over time instead of sitting static.
How to test
Run ./run.bat, Tab to the IMAGE screen.
Moving dither (request 2):
- girl / LUMINANCE, COLOR → mix (ink / field), DITHER → bayer 4×4 / 2 lvl.
- Raise PATTERN AMT — the stipple boundaries should start flowing. The motion comes from the selected gallery pattern: Tab to the gallery, click e.g. MARBLE or PLASMA (its SPEED sets the flow rate), Tab back. CONTRAST still spreads tones.
Halftone / icons (request 1):
- Still on girl / LUMINANCE: set SHAPE → circle (size), BACKING → NONE, COLOR → solid (COLOR A = ink). You should get an ink dot-halftone of the portrait (dot size tracks brightness). Tune CELL SIZE.
- SHAPE → icon (field) + a filled icon → the portrait rendered as icons.
- Switch MODE → PATTERN, IMAGE → wolf, SHAPE → circle (size) → the pattern fills the wolf silhouette as a dot grid (transparent backing drops the gaps to the page).
Watch for / report
- Does PATTERN AMT read as a flowing dithered image (vs. dissolving too fast)? Range OK at 0–1?
- The halftone dot size tracks brightness (bright = bigger dots). If you want it inverted (dark = bigger, more ink-on-light), that's a small follow-up.
- Icon-halftone legibility.
Have a look — then the last two session items are more geometric shapes and the API simplifications.
Perfect. the UI is missing being able to change the actual underlying pattern, but that's fine, and I think we'll tackle this on the API simplification portion. Next we have more shapes, right? Just do the ones you can do in the shader comfortably and that you think would look good.
Image-as-content's done. Noted on the pattern-picker UI — that fits naturally into the API-simplification pass.
Now: more geometric shapes. They slot into shape_mask as new branches, each just a different "distance" metric that grows with the per-cell field — comfortable and clean. Let me re-read the current shape_mask + its caller to edit precisely.
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
556 // the color. For an image shape, `m` would be the sprite's alpha at the
557 // cell-local UV; nothing downstream changes.
558 // * COLOR — *what hue*. The field value handed to dither + the color recipe,
559 // at FULL strength (never multiplied by the mask). circle (size) uses the
560 // per-FRAGMENT value `ff` (color varies inside the dot); circle (hard) uses
561 // the per-CELL value `fc` (one flat color per dot).
562 //
563 // Compositing happens in main() per u_shape_backing:
564 // backing 0 (transparent) → final alpha *= coverage (gaps show the page)
565 // backing 1 (token) → mix(palette[color_a], colored, coverage)
566 //
567 // Cells are u_shape_size px square, anchored to screen space (gl_FragCoord),
568 // the same space as the dither. dist = normalized distance from the cell
569 // center (0 → 1 at the inscribed-circle edge, ~1.41 in the corners).
570 // =============================================================================
571
572 // Coverage mask of the shape at the fragment, in [0,1]. `dist` is the
573 // normalized distance from the cell center; `cell_uv` is the 0..1 position
574 // within the cell; `fc` is the per-cell field (sizes the circle variants).
575 // THIS is the single extension point for new shapes — add a branch returning
576 // the new shape's coverage; the rest of the pipeline is unchanged.
577 float shape_mask(int kind, float dist, float fc, vec2 cell_uv) {
578 if (kind == 3 || kind == 4) {
579 // sprite: the chosen icon's silhouette (its alpha), tiled one-per-cell.
580 // Flip Y (gl_FragCoord is bottom-up, texture origin top-left); textureLod
581 // (.,0) avoids cell-edge mip seams.
582 // fit = icon footprint as a fraction of the cell — SPACING insets it
583 // kind 3 (bg) : full footprint; the effect fills/colors a constant icon
584 // kind 4 (field) : footprint also scaled by the per-cell field fc, so the
585 // icon grows with the effect and vanishes where it's dark
... [22 more lines]
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
606 float effect_field_shaped(vec2 quad_px, out float coverage) {
607 float ff = effect_field(vPos, vUV, quad_px); // per-fragment field
608 coverage = 1.0;
609 if (u_shape_kind == 0) return ff;
610 // Grid pitch (cell-to-cell distance). For sprite kinds, SPACING enlarges the
611 // pitch (more gap between icons) while shape_mask insets the icon by the same
612 // factor — the two cancel, so the icon's absolute size stays u_shape_size and
613 // only the gap grows. Circles ignore spacing (their gaps come from fc).
614 float size = max(u_shape_size, 1.0);
615 if (u_shape_kind >= 3) size /= max(1.0 - u_shape_spacing, 0.05);
616 vec2 cc = (floor(gl_FragCoord.xy / size) + 0.5) * size; // cell center, screen px
617 vec2 dpx = cc - gl_FragCoord.xy; // fragment → cell center
618 vec2 wc = vPos + dFdx(vPos) * dpx.x + dFdy(vPos) * dpx.y; // world pos at cell center
619 vec2 uvc = vUV + dFdx(vUV) * dpx.x + dFdy(vUV) * dpx.y; // uv at cell center
620 float fc = effect_field(wc, uvc, quad_px); // per-cell field
621 vec2 cell_uv = fract(gl_FragCoord.xy / size); // 0..1 within the cell
622 vec2 q = cell_uv - 0.5;
623 coverage = shape_mask(u_shape_kind, length(q) / 0.5, fc, cell_uv);
624 return (u_shape_kind == 2) ? fc : ff; // hard: flat per-cell color; size/sprite: per-fragment color
625 }
Now I have the exact text. Two notes before editing: I'll drop the now-redundant dist parameter (compute everything from cell_uv), and I caught a latent bug — the SPACING pitch-enlargement is gated >= 3, which would wrongly catch the new geometric kinds (5–9); it must be sprites-only (3, 4). Rewriting shape_mask:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now fix the spacing gate and simplify the caller in effect_field_shaped:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Update the uniform doc comment for the new kinds:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now effect.lua registries — add the five geometric kinds:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
main.lua needs no change — the SHAPE cycler picks up the new kinds automatically, and shape_is_sprite correctly excludes them (so geometric shapes get CELL SIZE + BACKING but not the ICON/SPACING rows). Syntax-checking effect.lua:
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: effect.lua
=== shape_mask call sites in shader (should be 1, new sig) ===
583:float shape_mask(int kind, float fc, vec2 cell_uv) {
641: // pitch (more gap between icons) while shape_mask insets the icon by the same
652: coverage = shape_mask(u_shape_kind, fc, cell_uv);
Single call site, new signature — clean. Five geometric shapes are in. No rebuild (shader + Lua; recompiles at boot).
Added shapes (each a dot grid sized by the per-cell field, exactly like circle, just a different distance metric):
- square (Chebyshev), diamond (Manhattan), hexagon (gauge metric, vertices tuned to reach the cell mid-side so they don't overflow into neighbors), ring (annulus, 50% hole), cross (plus sign, arms + thickness scale together).
The SHAPE cycler now reads: off → circle (size) → circle (hard) → square → diamond → hexagon → ring → cross → icon (bg) → icon (field). They work everywhere shapes work — gallery rects and the image screen (halftone/stencil).
I also fixed a latent bug while in there: the SPACING pitch-enlargement was gated >= 3, which would have wrongly caught the new kinds (5–9); it's now sprites-only (3, 4), and I dropped the redundant dist param from shape_mask.
How to test
Run ./run.bat. In the gallery, cycle SHAPE through square / diamond / hexagon / ring / cross — each re-skins all rects as that dot grid. Tune CELL SIZE + BACKING. Then Tab to the IMAGE screen and try them there too (e.g. girl / LUMINANCE / diamond or hexagon, BACKING NONE → a diamond/hex halftone).
Watch for: whether hexagon packs cleanly (no merging/overflow between cells), the ring and cross read well at small vs large CELL SIZE, and AA crispness on all of them.
Have a look — if hexagon or any need a tuning tweak, tell me. Otherwise the last item is the API simplifications (including the image-screen pattern picker you flagged).
All of these look good. More shapes? Or do you think the icons serve for the rest?
Honestly? I think icons serve the rest — with one exception: triangle.
Here's my reasoning. The geometric set now covers the abstract primitive families that read as halftone/texture:
- round — circle, ring
- angular — square, diamond, hexagon
- compound — cross
What's conspicuously missing is the triangle — it's the one fundamental regular polygon we don't have (we've got 4-gon, 6-gon, ∞-gon but not 3-gon), and it has a directional quality (it points) that none of the others do. It's cheap — same gauge-metric pattern as the others (max(-p.y, 0.866·|p.x| + 0.5·p.y)), and it's genuinely distinct, not a duplicate of anything.
Beyond triangle, I'd stop and let icons carry the long tail. The reason: everything else worth adding — star, heart, arrow, leaf, gear… — are recognizable objects, not abstract primitives. Those are exactly what the icon path is for, and we already have arbitrary icon silhouettes (plus star/heart filled in the set). Adding them as hardcoded SDF branches would bloat shape_mask and duplicate the icon system. Pentagon/octagon I'd skip too — octagon ≈ square, pentagon is niche.
The only other direction that'd add real value isn't "more shapes" but a fill/outline modifier — hollow versions of every shape (the ring is already the hollow circle; a hollow square / hex outline would give a "wireframe dot grid"). But that's a feature (a modifier across all shapes), not a shape, and a bigger call — I'd park it unless you want that look.
So my recommendation: add triangle, then call the shape axis done and move to API simplifications. Want me to drop in the triangle?
Yep.
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now the effect.lua registries:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && E:/a327ex/Anchor/reference/love-compare/tools/...)
OK: effect.lua
Triangle's in — kind 10, upward-pointing, same gauge-metric pattern as the others (top vertex reaches the top edge midpoint at fc=1, so it packs within the cell without overflow). Registries updated; it sits after diamond in the cycler. No rebuild.
Test: the SHAPE cycler now reads off → circle (size) → circle (hard) → square → diamond → triangle → hexagon → ring → cross → icon (bg) → icon (field). Cycle to triangle in the gallery (and on the image screen) — dots sized by the field, pointing up. Watch that it packs cleanly like the others.
That wraps the shape axis: circle, square, diamond, triangle, hexagon, ring, cross, plus arbitrary icons (bg + field).
Once you've confirmed triangle looks right, the last session item is the API simplifications — and I'll fold in the image-screen pattern picker you flagged. When we get there it'd help to know what's been bugging you about the API specifically, but I also have my own list of candidates from having just worked through all of it.
Another generally useful slider for shapes, rotation, so that all shapes are rotated the same. Another we might want to add later might be rotation based on a flow field of sorts, this is how the wall icons worked, the icons rotated with the wall and along it and gave it a nice effect, although this might be harder to do in a general way, I don't know.
Good idea — a uniform ROTATION applies to every shape (and it's especially useful for triangle/cross/icons). The flow-field version is a nice "later," and you're right it's harder — it'd need a per-cell angle source; I'll note it and keep this one a single global angle. Let me wire the global rotation now.
Let me read the current shape_mask to edit precisely.
Read (E:\a327ex\ricochet-template\assets\draw_shader.frag)
578 // The geometric shapes share one idea: a "gauge" metric m(cell_uv) that is 0 at
579 // the cell center and reaches 1 at a cell mid-side, with the shape boundary at
580 // m == fc. So every shape grows with the per-cell field exactly like the circle
581 // — only the distance metric differs (Euclidean → circle, Chebyshev → square,
582 // Manhattan → diamond, hex gauge → hexagon). Ring + cross are handled specially.
583 float shape_mask(int kind, float fc, vec2 cell_uv) {
584 if (kind == 3 || kind == 4) {
585 // sprite: the chosen icon's silhouette (its alpha), tiled one-per-cell.
586 // Flip Y (gl_FragCoord is bottom-up, texture origin top-left); textureLod
587 // (.,0) avoids cell-edge mip seams.
588 // fit = icon footprint as a fraction of the cell — SPACING insets it
589 // kind 3 (bg) : full footprint; the effect fills/colors a constant icon
590 // kind 4 (field) : footprint also scaled by the per-cell field fc, so the
591 // icon grows with the effect and vanishes where it's dark
592 // (the circle-analog — the effect IS the icon grid)
593 vec2 uv = vec2(cell_uv.x, 1.0 - cell_uv.y);
594 float fit = 1.0 - u_shape_spacing;
595 float s = (kind == 4) ? fc * fit : fit;
596 s = max(s, 1e-4);
597 uv = (uv - 0.5) / s + 0.5;
598 if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) return 0.0;
599 return textureLod(u_shape_tex, uv, 0.0).a;
600 }
601
602 float e = 0.04; // anti-alias edge width
603 vec2 p = (cell_uv - 0.5) * 2.0; // [-1,1]^2; |component| = 1 at a cell mid-side
604 vec2 a = abs(p);
605
606 // RING — annulus of the circle (hole = 50% of the outer radius).
607 if (kind == 8) {
... [22 more lines]
Now adding rotation to shape_mask — three targeted edits. The rotation computation + open:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now declare the uniform, near the other shape uniforms:
Edit (E:\a327ex\ricochet-template\assets\draw_shader.frag)
The file E:\a327ex\ricochet-template\assets\draw_shader.frag has been updated successfully. (file state is current in your context — no need to Read it back)
Now effect.lua — write the uniform and doc:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now main.lua. The rotation global, slider, spec wiring, and a SIDEBAR_H bump. First the global:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the ROTATION slider — adding it in the shape-active block after CELL SIZE:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Pass shape_rotation in the gallery spec and the image spec:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now bump SIDEBAR_H for the added ROTATION row (worst case ≈ 956):
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: main.lua
OK: effect.lua
Both parse. Rotation is wired — shader + Lua only, no rebuild.
What it does: u_shape_rotation rotates each shape's cell-local coordinate about its cell center, applied uniformly to both geometric shapes and icons. The cell grid stays axis-aligned; only the shape inside each cell spins. A ROTATION slider (0..1 = full turn) appears whenever a shape is active, on both the gallery and image screens.
How to test
Run ./run.bat. With SHAPE → square (or triangle / cross / icon), drag ROTATION:
- square tilts (→ diamond at 0.125), triangle points different directions, cross becomes an X at 0.125, hexagon flips pointy/flat-top, icons spin.
- circle / ring won't visibly change (they're rotation-symmetric — that's expected).
- Try it on the image screen too (rotated-icon or rotated-shape halftone).
Flow-field rotation (the wall-icon effect) — parked as you suggested. When we want it, it's a per-cell angle source feeding this same rotation (e.g. derived from the pattern's gradient, or a dedicated flow pattern), rather than the single global angle. Feasible, just a bigger piece.
That should close out the shape axis for real now. Next up whenever you're ready: the API simplifications — tell me what's been nagging you and I'll combine it with my own candidate list (the image-screen pattern picker included).
Marvelous. Now for the API simplification. This is a rather simple ask, I think. I want to be able to draw all these effects we just did in single calls. Just like you can draw a rectangle using a single call, we should be able to draw a rectangle with all these patterns using a single call, the sliders and settings we have on the side being arguments on the call. Similar for drawing images and so on. I'd like a new screen to see what the API looks like, so when you draw something, also draw text showing the call that drew it, and just draw as many variations as you can fit on a screen covering all cases for everything we implemented so far. Tell me your own plans first, though, if you had them.
Before I build, here's my read and plan — you asked for it, and I do have opinions on how to make this clean.
The core simplification
Right now every effect draw is three calls: effect_set(spec) → layer_rectangle(...) → effect_clear(). The spec table already encodes every slider/setting we built. So the simplification is thin: per-primitive wrappers that bundle set + draw + clear into one call, taking the spec as the last argument.
effect_rectangle(layer, x, y, w, h, spec)
effect_circle(layer, x, y, r, spec)
effect_image(layer, img, x, y, w, h, spec) -- fits the image into the (x,y,w,h) box
Each one: binds the sprite icon if needed → effect_set → draws the primitive → effect_clear. Base draw color defaults to white (irrelevant for color≠none, which is the whole point of these). The spec is exactly the sidebar: { pattern, pattern_scale, speed, contrast, pattern_param/param2, color, color_a/b, ramp, dither, shape, shape_size, shape_spacing, shape_rotation, shape_backing, shape_icon, image_field, image_pattern_amount } — all optional, all defaulted.
One gap to close: the sprite icon is currently bound out-of-band via layer_shader_set_texture. I'll fold it into the spec as shape_icon = icons.skull and do the bind inside effect_set, so a single call is fully self-contained.
Dogfooding (validates the API + shrinks code)
I'll refactor the existing gallery loop and image_draw to use these wrappers. If they can express what we already built, the API is right — and image_draw collapses from ~8 lines to one.
The new API screen (4th screen, Tab-cycled)
A living reference: a grid of cards, each rendering one effect_* call and printing the exact call beside it. Key idea for correctness — the code is generated from the spec, not hand-written: each example is authored as a minimal spec (only the fields it demonstrates), and I both (a) render it and (b) serialize it to the displayed Lua source. Single source of truth, and the printed call is guaranteed to reproduce the swatch. Coverage: every pattern, every color recipe, a representative spread of dithers, every shape (geometric + icon bg/field), rotation, and the image modes (luminance / halftone / pattern-stencil). As many as fit readably (~24–30 cards, 3 columns, swatch + code beside it).
The pattern picker you flagged
Folds in naturally: on the IMAGE screen I'll add a PATTERN cycler (and reuse each pattern's gallery-tuned scale/param as its default) so the image screen stops depending on "go select a gallery rect." With the wrappers, the pattern is just spec.pattern anyway.
What I'd not do
The spec field names are already consistent enough — I won't rename image_field etc. (pure churn). I won't collapse the four axes into positional args (the spec table is the right shape for this many optional settings). The scoped effect_draw(layer, spec, fn) stays as the escape hatch for multi-draw blocks.
That's my plan. The one genuine fork is the API-screen layout (code beside each swatch in 3 wide columns, vs. code below in a denser 5–6 column grid). I lean beside-the-swatch for code readability. Sound good, or adjust before I start?
Go ahead.
Approved. Let me first verify how layer_rectangle/layer_circle take their color arg (int vs color object), since the wrappers pass a base color.
Bash (cd E:/a327ex/ricochet-template && grep -nA2 "function layer_rectangle\b|functio...)
108:function layer_rectangle(lyr, x, y, w, h, color)
109- eng.rectangle(lyr_handle(lyr), x, y, w, h, color)
110-end
--
112:function layer_circle(lyr, x, y, radius, color)
113- eng.circle(lyr_handle(lyr), x, y, radius, color)
114-end
=== how engine draw_rectangle reads color ===
4954:static int l_layer_rectangle(lua_State* L) {
4966:static int l_layer_rectangle_line(lua_State* L) {
4979:static int l_layer_rectangle_gradient_h(lua_State* L) {
4992:static int l_layer_rectangle_gradient_v(lua_State* L) {
9401: lua_register(L, "layer_rectangle", l_layer_rectangle);
9402: lua_register(L, "layer_rectangle_line", l_layer_rectangle_line);
9403: lua_register(L, "layer_rectangle_gradient_h", l_layer_rectangle_gradient_h);
9404: lua_register(L, "layer_rectangle_gradient_v", l_layer_rectangle_gradient_v);
Read (E:\a327ex\Anchor2\engine\src\anchor.c)
4954 static int l_layer_rectangle(lua_State* L) {
4955 Layer* layer = (Layer*)lua_touserdata(L, 1);
4956 float x = (float)luaL_checknumber(L, 2);
4957 float y = (float)luaL_checknumber(L, 3);
4958 float w = (float)luaL_checknumber(L, 4);
4959 float h = (float)luaL_checknumber(L, 5);
4960 uint32_t color = (uint32_t)luaL_checkinteger(L, 6);
4961 layer_add_rectangle(layer, x, y, w, h, 0.0f, color); // stroke=0 for filled
4962 return 0;
4963 }
4964
4965 // layer_rectangle_line(layer, x, y, w, h, color, line_width?) -- rectangle outline
4966 static int l_layer_rectangle_line(lua_State* L) {
4967 Layer* layer = (Layer*)lua_touserdata(L, 1);
layer_rectangle takes color as an integer, and palette.ink() returns one — so 0xFFFFFFFF (white, opaque) works directly in the wrappers. Let me read the current effect_set shape-uniform block to anchor the icon-bind insertion.
Read (E:\a327ex\ricochet-template\effect.lua)
378 Performance: enqueues ~14 uniform writes per call (plus the ramp token list
379 when color='ramp'). Cheap (a few bytes each).
380 ]]
381 function effect_set(layer, spec)
382 spec = spec or {}
383 local p = pattern_kinds[spec.pattern or 'solid'] or 2
384 local c = color_kinds[spec.color or 'none'] or 0
385 local d = dither_kinds[spec.dither or 'off'] or 0
386 local sh = shape_kinds[spec.shape or 'none'] or 0
387 local ca = resolve_color(spec.color_a or 'ink')
388 local cb = resolve_color(spec.color_b or 'field')
389 local scale = spec.pattern_scale or effect_default_pattern_scale
390 local param = spec.pattern_param or 0
391 local param2 = spec.pattern_param2 or 0
392 -- Universal modifiers. Default 1.0 = neutral (speed 1 = normal animation,
393 -- contrast 1 = field unchanged). MUST default to 1, not 0 — a 0 here would
394 -- freeze animation / flatten the field to mid-grey.
395 local speed = spec.speed or 1.0
396 local contrast = spec.contrast or 1.0
397 local shape_sz = spec.shape_size or 24
398 local shape_sp = spec.shape_spacing or 0
399
400 layer_shader_set_int (layer, effect_draw_shader, 'u_pattern_kind', p)
401 layer_shader_set_int (layer, effect_draw_shader, 'u_color_kind', c)
402 layer_shader_set_int (layer, effect_draw_shader, 'u_dither_kind', d)
403 layer_shader_set_int (layer, effect_draw_shader, 'u_shape_kind', sh)
404 layer_shader_set_float(layer, effect_draw_shader, 'u_shape_size', shape_sz)
405 layer_shader_set_float(layer, effect_draw_shader, 'u_shape_spacing', shape_sp)
406 layer_shader_set_float(layer, effect_draw_shader, 'u_shape_rotation', spec.shape_rotation or 0)
407 -- shape gaps: 'transparent' (0, gaps show through) or 'token' (1, gaps = color_a)
Read (E:\a327ex\ricochet-template\effect.lua)
407 -- shape gaps: 'transparent' (0, gaps show through) or 'token' (1, gaps = color_a)
408 layer_shader_set_int (layer, effect_draw_shader, 'u_shape_backing',
409 (spec.shape_backing == 'token') and 1 or 0)
410 layer_shader_set_int (layer, effect_draw_shader, 'u_color_a', ca)
411 layer_shader_set_int (layer, effect_draw_shader, 'u_color_b', cb)
412 -- color='ramp': push the ordered token list (indices + count). Only when the
413 -- recipe is active — other recipes leave the (unused) ramp uniforms as-is.
414 if c == 3 then
415 local ramp = spec.ramp or effect_ramp
416 local rn = math.min(#ramp, 8)
417 layer_shader_set_int(layer, effect_draw_shader, 'u_ramp_count', rn)
418 for i = 1, rn do
419 layer_shader_set_int(layer, effect_draw_shader, 'u_ramp_tokens[' .. (i - 1) .. ']', resolve_color(ramp[i]))
420 end
Now add the icon-bind to effect_set, right after the shape uniforms:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now document shape_icon and add the wrapper functions. First the spec doc:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the wrapper functions — adding them after effect_draw (the scoped form):
Read (E:\a327ex\ricochet-template\effect.lua)
421 -- recipe is active — other recipes leave the (unused) ramp uniforms as-is.
422 if c == 3 then
423 local ramp = spec.ramp or effect_ramp
424 local rn = math.min(#ramp, 8)
425 layer_shader_set_int(layer, effect_draw_shader, 'u_ramp_count', rn)
426 for i = 1, rn do
427 layer_shader_set_int(layer, effect_draw_shader, 'u_ramp_tokens[' .. (i - 1) .. ']', resolve_color(ramp[i]))
428 end
429 end
430 layer_shader_set_float(layer, effect_draw_shader, 'u_pattern_scale', scale)
431 layer_shader_set_float(layer, effect_draw_shader, 'u_pattern_param', param)
432 layer_shader_set_float(layer, effect_draw_shader, 'u_pattern_param2', param2)
433 layer_shader_set_float(layer, effect_draw_shader, 'u_pattern_speed', speed)
434 layer_shader_set_float(layer, effect_draw_shader, 'u_pattern_contrast', contrast)
435 -- image_field: sprite draws only. 1 = the image's own luminance drives the
436 -- field (dither/recolor a real image); 0 = the pattern drives it (default).
437 layer_shader_set_int (layer, effect_draw_shader, 'u_image_field',
438 spec.image_field and 1 or 0)
439 -- image_pattern_amount: luminance mode only. How much the animated pattern
440 -- ripples the image's field before dither (0 = static, →1 = flowing).
441 layer_shader_set_float(layer, effect_draw_shader, 'u_image_pattern_amount',
442 spec.image_pattern_amount or 0)
443 layer_shader_set_float(layer, effect_draw_shader, 'u_time', time)
444 end
445
446 --[[
447 effect_clear(layer)
448 Reset pattern/color/dither on this layer to passthrough. The other
449 uniforms (color_a/b, scale, param) are left as-is — they only have an
450 effect when a non-zero kind reads them.
... [10 more lines]
Read (E:\a327ex\ricochet-template\effect.lua)
459 --[[
460 effect_draw(layer, spec, fn, ...)
461 Scoped form: set the effect, run fn(...), then reset to passthrough.
462 Forwards extra arguments to fn so closures can stay light.
463 ]]
464 function effect_draw(layer, spec, fn, ...)
465 effect_set(layer, spec)
466 fn(...)
467 effect_clear(layer)
468 end
469
470 -- =============================================================================
471 -- CYCLING — wrap-around step through each axis's named cycle.
472 -- Pass nil to get the first entry; pass an unknown name to get the first too.
473 -- =============================================================================
474
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now main.lua. Refactor the gallery loop to use effect_rectangle. Let me read the current section.
Read (E:\a327ex\ricochet-template\main.lua)
485 palette.ink())
486
487 row = rect_cut_top(body, ui_row_h + 20)
488 local ms = ui_slider{ rect = row, id = 'mod_scale', label = 'SCALE',
489 value = sel.scale, vmin = 0.05, vmax = 1.2, fmt = '%.3f' }
490 if ms.changed then sel.scale = ms.value end
491
492 row = rect_cut_top(body, ui_row_h + 20)
493 local msp = ui_slider{ rect = row, id = 'mod_speed', label = 'SPEED',
494 value = sel.speed, vmin = 0, vmax = 4, fmt = '%.2f' }
495 if msp.changed then sel.speed = msp.value end
496
497 row = rect_cut_top(body, ui_row_h + 20)
498 local mco = ui_slider{ rect = row, id = 'mod_contrast', label = 'CONTRAST',
499 value = sel.contrast, vmin = 0, vmax = 2.5, fmt = '%.2f' }
500 if mco.changed then sel.contrast = mco.value end
501
502 row = rect_cut_top(body, ui_row_h + 20)
503 local mpa = ui_slider{ rect = row, id = 'mod_param', label = sel.param_label,
504 value = sel.param, vmin = 0, vmax = 1, fmt = '%.2f' }
505 if mpa.changed then sel.param = mpa.value end
506
507 -- Second intrinsic knob — only patterns that define one (the gradients:
508 -- OFFSET / RANGE) get this row; it uses the pattern's own min/max range.
509 if sel.param2_label then
510 row = rect_cut_top(body, ui_row_h + 20)
511 local mp2 = ui_slider{ rect = row, id = 'mod_param2', label = sel.param2_label,
512 value = sel.param2, vmin = sel.param2_min or 0,
513 vmax = sel.param2_max or 1, fmt = '%.2f' }
514 if mp2.changed then sel.param2 = mp2.value end
... [22 more lines]
Read (E:\a327ex\ricochet-template\main.lua)
536 local cx, cy = ax + aw/2, ay + ah/2
537
538 -- Both modes share the selected gallery pattern (LUMINANCE uses it to ripple
539 -- the dither; PATTERN uses it as the field), the shared color/dither, and the
540 -- shared SHAPE controls (so the image can be halftoned / icon-filled).
541 local g = gallery[gallery_selected]
542 local spec = {
543 pattern = g.pattern, pattern_scale = g.scale, speed = g.speed,
544 pattern_param = g.param, pattern_param2 = g.param2,
545 color = current_color, dither = current_dither,
546 color_a = current_color_a, color_b = current_color_b,
547 shape = current_shape, shape_size = current_shape_size,
548 shape_spacing = current_shape_spacing, shape_rotation = current_shape_rotation,
549 shape_backing = current_shape_backing,
550 }
551 if current_image_mode == 'luminance' then
552 spec.image_field = true
553 spec.contrast = current_image_contrast
554 spec.image_pattern_amount = current_image_pattern_amount
555 else
556 spec.contrast = g.contrast
557 end
558 effect_set(game_layer, spec)
559 -- Bind the icon mask for sprite shapes (same as the gallery), on unit 2.
560 if shape_is_sprite(current_shape) and icons[current_shape_icon] then
561 layer_shader_set_texture(game_layer, effect_draw_shader, 'u_shape_tex',
562 icons[current_shape_icon].handle, 2)
563 end
564 layer_push(game_layer, cx, cy, 0, sc, sc) -- translate + scale the image draw
565 layer_image(game_layer, img, 0, 0) -- centered at the pushed origin
... [10 more lines]
Now refactor image_draw to use effect_image + the pattern picker:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the gallery loop refactor — read its current exact text:
Read (E:\a327ex\ricochet-template\main.lua)
615
616 -- The UI runs HERE, not in update(). Under render_uncapped on a monitor
617 -- faster than the 120Hz fixed-timestep update, render frames can fire
618 -- without a preceding update step; anything queued only in update() gets
619 -- consumed by layer_render and then renders empty (blank FBO) on those
620 -- frames → flicker. Queueing the UI in draw() (which runs every render
621 -- frame) avoids that entirely. ui_update reads the edge globals captured
622 -- in update() and may mutate current_* — run before the game shapes so a
623 -- click applies to the scene the same frame.
624 ui_state_begin_frame()
625 ui_update()
626
627 -- IMAGE screen: content is the chosen test image through the effect (no camera
628 -- — the image is screen-space via layer_push). Composite, consume edges, done.
629 if current_screen == 'image' then
630 image_draw()
631 layer_render(game_layer)
632 layer_render(ui_layer)
633 layer_draw(game_layer)
634 layer_draw(ui_layer)
635 ui_mouse_pressed = false
636 ui_mouse_released = false
637 return
638 end
639
640 -- Background fill in the active palette's field token. Drawn WITHOUT the
641 -- effect pipeline (color='none' default state at top of game_layer), so
642 -- the background stays a clean flat token regardless of the spec.
643 layer_rectangle(game_layer, 0, 0, width, height, palette.field())
644
... [22 more lines]
Read (E:\a327ex\ricochet-template\main.lua)
666 contrast = g.contrast,
667 pattern_param = g.param,
668 pattern_param2 = g.param2,
669 color = current_color,
670 color_a = current_color_a,
671 color_b = current_color_b,
672 dither = current_dither,
673 shape = current_shape,
674 shape_size = current_shape_size,
675 shape_spacing = current_shape_spacing,
676 shape_rotation = current_shape_rotation,
677 shape_backing = current_shape_backing,
678 })
679 layer_rectangle(game_layer, r.x, r.y, GAL_RECT_W, GAL_RECT_H, palette.ink())
680 end
681
682 effect_clear(game_layer)
683
684 camera_detach(main_camera, game_layer)
685
686 -- Gallery labels on ui_layer (no effect pipeline). Drawn after detach;
687 -- ui_layer is screen-space and the camera is identity here, so these
688 -- align under their rects. "<name> (<scale>)" per cell.
689 for i, g in ipairs(gallery) do
690 local r = gallery_rect(i)
691 layer_text(ui_layer, g.label .. ' (' .. string.format('%.2f', g.scale) .. ')',
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the image PATTERN picker. First the global:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the PATTERN cycler row in the image controls (after IMAGE):
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now wire the 4th screen. The require:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the new screen file. Writing api_showcase.lua — a grid where each example's source is generated from its spec (single source of truth) and rendered beside the live swatch:
Write (E:\a327ex\ricochet-template\api_showcase.lua)
File created successfully at: E:\a327ex\ricochet-template\api_showcase.lua (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: main.lua
OK: effect.lua
OK: api_showcase.lua
All three parse. This round is Lua-only (shader untouched), so no rebuild — just run. Here's everything that landed:
Single-call API (in effect.lua):
lua
effect_rectangle(layer, x, y, w, h, spec)
effect_circle(layer, x, y, r, spec)
effect_image(layer, img, x, y, w, h, spec) -- fits img into the box
Each bundles set + draw + clear. The sprite icon is now folded into the spec (shape_icon = icons.skull) and bound inside effect_set, so a single call is fully self-contained.
Dogfooded: the gallery loop and image_draw now use these wrappers (the gallery's manual icon-bind + trailing effect_clear are gone; image_draw collapsed to one effect_image call).
Image PATTERN picker: the image screen got a PATTERN cycler — pick the moving/fill pattern right there instead of routing through the gallery selection (resolves your earlier flag).
New API screen (api_showcase.lua, 4th via Tab): ~28 cards, each rendering one effect_* call beside its generated source — the code is serialized from the same minimal spec that draws the swatch, so it's guaranteed to reproduce it. Covers every pattern, the color recipes, a few dithers, every shape + rotation, effect_circle, and the three image modes.
How to test
Run ./run.bat. Tab now cycles gallery → showcase → image → api → gallery.
- Regression check — the gallery and image screens should look/behave exactly as before (they were refactored onto the new wrappers). On the image screen, the new PATTERN cycler should change the moving/fill pattern.
- API screen — a grid of swatches, each with the literal call that drew it (e.g.
effect_rectangle(l, x, y, w, h, { pattern = 'marble', color = 'mix', shape = 'hexagon', shape_size = 20 })). Confirm the code reads cleanly and matches its swatch.
Tell me
- Any regression in the gallery/image screens from the refactor.
- API screen readability — 28 cards at 3 columns makes the swatches ~80px and code wraps to ~2 lines. If it's cramped, I'll trim the set or drop to bigger cards.
Once you've confirmed it works, I'll sync the docs (the CLAUDE.md Spec API section + roadmap) to cover the single-call API and the new screen.
Great, when we actually work in games with this I'll use the editors because they're easier to understand than the text API, so now that I've seen it work, you can actually replace the API screen with a single editor that allows me to edit things with everything we did, covering all possible cases.
The editor over text makes sense — and now that the text screen proved the calls work, replacing it with a real editor is the right move. Let me lay out the plan and surface two choices before I build.
Design brief — the unified Editor (replaces the API screen)
Concept: swap api_showcase.lua for editor.lua. The 4th screen becomes a single-drawable inspector — a full control panel on the left, one large live preview on the right — that can express every case we built.
Target selector (the new bit): RECT / CIRCLE / IMAGE.
- RECT/CIRCLE → preview a big rectangle/circle drawn via
effect_rectangle/effect_circle. - IMAGE → preview a fitted image via
effect_image, with the image-specific rows (MODE luminance/pattern, IMAGE picker, PATTERN AMT).
Full control panel, top to bottom:
- PALETTE · TARGET · (image rows when IMAGE)
- PATTERN picker (all patterns — the editor's own pattern + modifier state, since unlike the gallery there's one drawable; picking a pattern loads its tuned default scale/param)
- SCALE / SPEED / CONTRAST · PARAM (+ PARAM2 for gradients)
- COLOR · COLOR A / B · (ramp editor when ramp)
- DITHER
- SHAPE · (ICON + SPACING for sprites) · SIZE · ROTATION · BACKING
It reuses the existing ui_* widgets, rectcut, and the shared current_* look state (color/dither/shape/…), plus the effect_* wrappers for the preview (dogfooding them once more). New state is just editor_target + the editor's pattern/modifier values.
Layout: single-column sidebar at near-full height + a large preview. Realistic configs fit comfortably; only a pathological combo (image + sprite + ramp + gradient all at once) would slightly overrun — acceptable for a dev tool, and I'll two-column it later if it bugs you.
Keeps: gallery (the all-patterns comparison is distinct), showcase. Removes: the text API screen.
Two genuine choices before I start:
AskUserQuestion
Your questions have been answered: "The editor will have an IMAGE target, which makes the separate IMAGE screen redundant. Keep both, or fold it in?"="Fold into editor, drop it", "Include a small live code readout under the preview (the effect_* call your edits build), or keep the editor purely visual?"="No, editor only". You can now continue with these answers in mind.
Decisions locked: fold the image screen into the editor, no code readout. Tab will be gallery → showcase → editor.
To avoid duplicating the intricate shared control blocks (shape sub-controls with the filled-icon snap, color A/B, ramp editor) across the gallery sidebar and the editor, I'll factor them into shared global functions, then both screens call them. Let me re-read the current ui_update to extract them cleanly and plan the image-screen removal.
Read (E:\a327ex\ricochet-template\main.lua)
290 -- MODIFIERS section below reflects the new selection this same frame.
291 if current_screen == 'gallery' and ui_mouse_pressed then
292 local mx, my = mouse_position()
293 for i = 1, #gallery do
294 if ui_point_in_rect(mx, my, gallery_rect(i)) then gallery_selected = i end
295 end
296 end
297
298 local panel_r = rect_new(SIDEBAR_X, SIDEBAR_Y, SIDEBAR_W, SIDEBAR_H)
299 ui_panel{ rect = panel_r, title = 'effect control', corner_brackets = true }
300
301 -- Carve out the panel body: title bar at top, inset on the remaining area.
302 local body = rect_new(panel_r.x, panel_r.y, panel_r.w, panel_r.h)
303 rect_cut_top(body, ui_title_h) -- skip the title bar
304 body = rect_contract(body, ui_panel_inset) -- inner padding
305
306 -- PALETTE — dark/light segmented toggle.
307 local row = rect_cut_top(body, ui_row_h)
308 local seg = ui_segment{
309 rect = row, id = 'palette', label = 'PALETTE',
310 options = { 'DARK', 'LIGHT' },
311 index = palette_mode == 'dark' and 1 or 2,
312 }
313 if seg.changed then
314 palette_init(seg.index == 1 and 'dark' or 'light')
315 end
316
317 -- IMAGE-screen-only controls: field-source MODE + which test IMAGE. The COLOR
318 -- / DITHER / COLOR A·B / ramp rows below are shared and re-skin the image live.
319 if current_screen == 'image' then
... [145 more lines]
Read (E:\a327ex\ricochet-template\main.lua)
464 if rc.next_clicked and #effect_ramp < 8 then
465 table.insert(effect_ramp, ramp_selected + 1, effect_ramp[ramp_selected])
466 ramp_selected = ramp_selected + 1
467 end
468 if rc.prev_clicked and #effect_ramp > 2 then
469 table.remove(effect_ramp, ramp_selected)
470 if ramp_selected > #effect_ramp then ramp_selected = #effect_ramp end
471 end
472
473 row = rect_cut_top(body, swatch_row_h)
474 local rs = ui_swatch_row{ rect = row, id = 'ramp_stops', label = 'RAMP',
475 tokens = effect_ramp, selected_index = ramp_selected }
476 if rs.index then ramp_selected = rs.index end
477
478 row = rect_cut_top(body, swatch_row_h)
479 local rp = ui_swatch_row{ rect = row, id = 'ramp_pick', label = 'STOP',
480 tokens = palette_token_names,
481 selected = effect_ramp[ramp_selected] }
482 if rp.changed then effect_ramp[ramp_selected] = rp.selected end
483 end
484
485 -- ---- MODIFIERS for the selected gallery rect (GALLERY screen only) ----
486 -- Per-pattern scale / speed / contrast + the pattern's intrinsic knob.
487 -- Each gallery entry stores its own values; these sliders edit whichever
488 -- rect is selected (click a rect to switch which one they drive).
489 if current_screen ~= 'image' then
490 local sel = gallery[gallery_selected]
491 rect_cut_top(body, ui_section_gap)
492 local mhead = rect_cut_top(body, ui_row_h)
493 layer_text(ui_layer, 'MODIFIERS ' .. sel.label, ui_font_label,
... [45 more lines]
I have the full picture. This is a multi-file refactor; I'll factor the shared control blocks into a new ui_controls.lua (used by both the gallery sidebar and the editor), then rewrite ui_update, build editor.lua, remove the image screen, and drop the api screen. Let me go step by step.
Step 1 — ui_controls.lua (shared cyclers + control blocks):
Write (E:\a327ex\ricochet-template\ui_controls.lua)
File created successfully at: E:\a327ex\ricochet-template\ui_controls.lua (file state is current in your context — no need to Read it back)
Step 2 — refactor main.lua. Requires first:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Remove the now-shared local helpers from main.lua:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the large ui_update simplification — replacing the image block + inline shared controls with the factored calls:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now remove the MODIFIERS gallery-only guard (it's always gallery now):
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Simplify the gallery-rect-selection guard (always gallery now):
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now remove the image_draw function. Let me read its current location:
Read (E:\a327ex\ricochet-template\main.lua)
360 if not img then return end
361
362 local ax, ay = GAL_X0, GAL_Y0
363 local aw, ah = width - ax - 40, height - ay - 80
364
365 local g = gallery[current_image_pattern_idx]
366 local spec = {
367 pattern = g.pattern, pattern_scale = g.scale, speed = g.speed,
368 pattern_param = g.param, pattern_param2 = g.param2,
369 color = current_color, dither = current_dither,
370 color_a = current_color_a, color_b = current_color_b,
371 shape = current_shape, shape_size = current_shape_size,
372 shape_spacing = current_shape_spacing, shape_rotation = current_shape_rotation,
373 shape_backing = current_shape_backing,
374 shape_icon = shape_is_sprite(current_shape) and icons[current_shape_icon] or nil,
375 }
376 if current_image_mode == 'luminance' then
377 spec.image_field = true
378 spec.contrast = current_image_contrast
379 spec.image_pattern_amount = current_image_pattern_amount
380 else
381 spec.contrast = g.contrast
382 end
383 effect_image(game_layer, img, ax, ay, aw, ah, spec)
384
385 -- Label (ui_layer, screen-space) above the image.
386 layer_text(ui_layer,
387 string.upper(current_image) .. ' · ' .. string.upper(current_image_mode) .. ' (' .. g.label .. ')',
388 ui_font_label, ax, ay - 30, palette.ink())
389 end
... [30 more lines]
Read (E:\a327ex\ricochet-template\main.lua)
344 vmax = sel.param2_max or 1, fmt = '%.2f' }
345 if mp2.changed then sel.param2 = mp2.value end
346 end
347 end
348
349 -- -----------------------------------------------------------------------------
350 -- image_draw — IMAGE screen content: the chosen test image fit into the content
351 -- area, processed through the effect via the single-call effect_image. LUMINANCE
352 -- → the image's own brightness is the field (dither/recolor it, optionally
353 -- rippled by the moving pattern); PATTERN → the chosen pattern fills it, stenciled
354 -- by the image's alpha. The image PATTERN picker selects which gallery pattern
355 -- drives both (its entry supplies the tuned scale / param).
356 -- -----------------------------------------------------------------------------
357 local function image_draw()
358 layer_rectangle(game_layer, 0, 0, width, height, palette.field())
359 local img = content_imgs[current_image]
360 if not img then return end
361
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now the Tab cycle and draw() dispatch. Update the cycle:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Read (E:\a327ex\ricochet-template\main.lua)
400 ui_mouse_pressed = false
401 ui_mouse_released = false
402 return
403 end
404
405 -- API reference screen: a grid of example effect_* calls, each rendered (on
406 -- game_layer, through the pipeline) next to its generated source (on ui_layer).
407 if current_screen == 'api' then
408 api_showcase_draw()
409 layer_render(game_layer)
410 layer_render(ui_layer)
411 layer_draw(game_layer)
412 layer_draw(ui_layer)
413 ui_mouse_pressed = false
414 ui_mouse_released = false
415 return
416 end
417
418 -- The UI runs HERE, not in update(). Under render_uncapped on a monitor
419 -- faster than the 120Hz fixed-timestep update, render frames can fire
420 -- without a preceding update step; anything queued only in update() gets
421 -- consumed by layer_render and then renders empty (blank FBO) on those
422 -- frames → flicker. Queueing the UI in draw() (which runs every render
423 -- frame) avoids that entirely. ui_update reads the edge globals captured
424 -- in update() and may mutate current_* — run before the game shapes so a
425 -- click applies to the scene the same frame.
426 ui_state_begin_frame()
427 ui_update()
428
429 -- IMAGE screen: content is the chosen test image through the effect (no camera
... [30 more lines]
Restructure the draw() dispatch — drop the api + image branches, add the editor branch after ui_state_begin_frame:
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now clean up the image globals (the editor uses editor_contrast/editor_pattern instead):
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Now Step 3 — editor.lua (the unified editor: state, controls reusing the shared blocks, and the live preview):
Write (E:\a327ex\ricochet-template\editor.lua)
File created successfully at: E:\a327ex\ricochet-template\editor.lua (file state is current in your context — no need to Read it back)
Now delete the old api_showcase.lua and syntax-check everything:
Bash (cd E:/a327ex/ricochet-template && rm -f api_showcase.lua && echo "deleted api_sh...)
deleted api_showcase.lua
OK: main.lua
OK: effect.lua
OK: ui_controls.lua
OK: editor.lua
OK: showcase.lua
All five parse. Given the size of this refactor, let me grep for any dangling references to removed symbols (image_draw, the old globals, the old screen names, api_showcase):
Bash (cd E:/a327ex/ricochet-template && echo "=== dangling refs (should be empty) ==="...)
=== dangling refs (should be empty) ===
editor.lua:74: if editor_target == 'image' and current_image_mode == 'luminance' then
editor.lua:107: if editor_target == 'image' then
editor.lua:198: if editor_target == 'image' then
=== current_screen usages ===
169:current_screen = 'gallery' -- 'gallery' | 'showcase' | 'editor'
385: if current_screen == 'gallery' then current_screen = 'showcase'
386: elseif current_screen == 'showcase' then current_screen = 'editor'
387: else current_screen = 'gallery' end
395: if current_screen == 'showcase' then
416: if current_screen == 'editor' then
=== confirm shared fns referenced ===
main.lua:301: ui_shape_block(body)
main.lua:303: ui_color_ab_block(body)
main.lua:304: ui_ramp_block(body)
main.lua:417: editor_ui()
main.lua:418: editor_preview()
editor.lua:85:function editor_ui()
editor.lua:167: ui_shape_block(body)
editor.lua:169: ui_color_ab_block(body)
editor.lua:170: ui_ramp_block(body)
editor.lua:174:function editor_preview()
No dangling references — the 'image' matches are all the editor's legitimate editor_target == 'image'. Let me verify the refactored ui_update reads cleanly end-to-end:
Read (E:\a327ex\ricochet-template\main.lua)
278 local panel_r = rect_new(SIDEBAR_X, SIDEBAR_Y, SIDEBAR_W, SIDEBAR_H)
279 ui_panel{ rect = panel_r, title = 'effect control', corner_brackets = true }
280
281 -- Carve out the panel body: title bar at top, inset on the remaining area.
282 local body = rect_new(panel_r.x, panel_r.y, panel_r.w, panel_r.h)
283 rect_cut_top(body, ui_title_h) -- skip the title bar
284 body = rect_contract(body, ui_panel_inset) -- inner padding
285
286 -- PALETTE — dark/light segmented toggle.
287 local row = rect_cut_top(body, ui_row_h)
288 local seg = ui_segment{
289 rect = row, id = 'palette', label = 'PALETTE',
290 options = { 'DARK', 'LIGHT' },
291 index = palette_mode == 'dark' and 1 or 2,
292 }
293 if seg.changed then
294 palette_init(seg.index == 1 and 'dark' or 'light')
295 end
296
297 -- Shared look controls (color recipe + tokens + dither + shape + ramp), drawn
298 -- by the shared blocks in ui_controls.lua (reused by the editor screen too).
299 ui_color_recipe_row(body)
300 ui_dither_row(body)
301 ui_shape_block(body)
302 rect_cut_top(body, ui_section_gap)
303 ui_color_ab_block(body)
304 ui_ramp_block(body)
305
306 -- ---- MODIFIERS for the selected gallery rect ----
307 -- Per-pattern scale / speed / contrast + the pattern's intrinsic knob.
... [42 more lines]
ui_update is clean. Let me do a final static check for any leftover dead code (e.g. an unused current_spec) and confirm the gallery draw still references the now-global shape_is_sprite:
Bash (cd E:/a327ex/ricochet-template && echo "=== current_spec (dead?) ==="; grep -n "...)
=== current_spec (dead?) ===
185:local function current_spec()
=== files present ===
editor.lua
effect.lua
main.lua
palette.lua
rectcut.lua
showcase.lua
ui.lua
ui_controls.lua
ui_state.lua
=== editor/ui_controls required? ===
24:require('anchor')({
34:require('palette')
35:require('effect')
36:require('rectcut')
37:require('ui_state')
38:require('ui')
39:require('ui_controls')
40:require('showcase')
41:require('editor')
current_spec is now dead (defined, never called). Let me read and remove it:
Read (E:\a327ex\ricochet-template\main.lua)
183 ui_mouse_released = false
184
185 local function current_spec()
186 return {
187 pattern = current_pattern,
188 color = current_color,
189 dither = current_dither,
190 color_a = current_color_a,
191 color_b = current_color_b,
192 pattern_scale = current_scale,
193 }
194 end
195
196 -- -----------------------------------------------------------------------------
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Bash (cd E:/a327ex/ricochet-template && E:/a327ex/Anchor/reference/love-compare/tools/...)
OK: main.lua
All parse. The refactor is complete and the shader's untouched, so no rebuild — just run. Here's the shape of it:
New files
editor.lua— the unified editor screen.ui_controls.lua— the shared control blocks (color recipe / dither / shape+sub / color A·B / ramp) + the shared cyclers, now used by both the gallery sidebar and the editor (one copy of the intricate sprite-icon/ramp logic).
Changes
main.lua:ui_updateslimmed onto the shared blocks; the image screen and the API screen are gone; Tab now cyclesgallery → showcase → editor; deadcurrent_specremoved.api_showcase.luadeleted.
The editor (Tab to it): a full control panel + one large live preview.
- TARGET: RECT / CIRCLE / IMAGE. IMAGE adds MODE (luminance/pattern), the IMAGE picker, and PATTERN AMT.
- PATTERN picker (any pattern — picking one loads its tuned defaults), SCALE / SPEED / CONTRAST + the pattern's intrinsic knob(s).
- COLOR / COLOR A·B / ramp · DITHER · SHAPE + ICON/SPACING/SIZE/ROTATION/BACKING.
- Everything edits the single preview live.
How to test
Run ./run.bat. Tab cycles gallery → showcase → editor.
- Regression — the gallery should behave exactly as before (it's on the shared blocks now), and the showcase unchanged.
- Editor — flip TARGET through RECT / CIRCLE / IMAGE. Pick a PATTERN (the PARAM label should change per pattern, gradients show PARAM2). Drive every control and watch the preview. On IMAGE, try MODE luminance (with PATTERN AMT) and pattern, and cycle IMAGE through girl/boy/wolf.
Watch for / tell me
- Any gallery regression from the refactor.
- Editor preview correctness across all targets/patterns/shapes.
- The single-column panel can overrun the bottom border only in the pathological combo (IMAGE + luminance + gradient + sprite + ramp all at once). If that bugs you, I'll switch the panel to two columns.
Once you've confirmed it works, I'll sync CLAUDE.md (the screen list + Spec API + the single-call wrappers + the new editor) — it's describing the old structure right now.
Great, seems like everything works. Update documents as you said.
Everything works. Let me sync the docs now. I'll update ricochet-template/.claude/CLAUDE.md (the biggest gaps: roadmap, shape axis, spec API + single-call wrappers, the screens/editor, file map, engine note), verify the effect.lua docblock, and add a cross-project memory for the engine patch.
Let me re-read the CLAUDE.md sections I'm changing to get exact current text.
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
46 ## What this is
47
48 A starter Anchor 2 project that ships **Ricochet's visual identity** plus
49 a clean four-axis effect system (pattern × color × dither × shape). Fork the
50 folder, drop your game code into `main.lua`, prototype. The template
51 itself ships no gameplay — that's the forking dev's job.
52
53 The full visual identity (palette philosophy, dotted motif, typography,
54 shape language, motion principles) lives in `Ricochet/design.md`. This
55 template implements the palette + the effect pipeline only; everything
56 else in design.md is guidance for how a forked project should look.
57
58 Layout:
59 - **`main.lua`** — the test scene: a **pattern gallery** (one rect per
60 pattern, the `gallery` table) + a **click-to-select inspector** (the
61 sidebar edits the selected rect's modifiers). Owns boot order, layers,
62 the per-frame UI contract, and the keyboard hotkeys.
63 - **`palette.lua`** — `palette_dark` + `palette_light`, 13 named tokens
64 per palette. `palette_init('dark' | 'light')` selects the active one.
65 - **`effect.lua`** — the four-axis effect API: pattern/color/dither/shape
66 registries, `effect_set(layer, spec)`, cycle helpers.
67 - **`rectcut.lua`** — RectCut layout primitive (cut/get/add/contract/
68 center/split). Verbatim from snkrx-template's `snkrx/ui/rect.lua`.
69 - **`ui_state.lua`** — frame-local hot/active/focus tracking +
70 `ui_claim_hot`. Slimmed from snkrx-template's `snkrx/ui/state.lua`
71 (text-input + modal-lock bits removed).
72 - **`ui.lua`** — the five Ricochet-style widgets that build the sidebar
73 control panel (`ui_panel`, `ui_field`, `ui_segment`, `ui_swatch_row`,
74 `ui_slider`) plus internal `ui_icon_button`.
75 - **`assets/draw_shader.frag`** — the unified fragment shader. Replaces
... [45 more lines]
Now updating. Layout section — reflect the new files and the editor:
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the Roadmap section — most of it is done:
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the effect-system overview and pipeline. Let me read that section:
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
122 1. **480×270 support.** Make the effects (and eventually the dev UI) read at the
123 SNKRX render target, where 1 logical px = 1 dither cell. The shader is already
124 `gl_FragCoord`-anchored, so it's mostly a verify + tune pass, but the user
125 wants to do it by hand.
126 2. **Replace the snkrx-template effect system** (its `edition.lua` + dither
127 stack) with this one, once 480×270 is settled. This is the end goal of the
128 whole template.
129 3. **Flow-field shape rotation** (the Orblike/Invoker "wall icon" effect): a
130 *per-cell* rotation angle from a field, vs. today's single global ROTATION.
131 Feasible; bigger. The hook is the rotation math at the top of `shape_mask`.
132
133 **When this toolkit is used in a REAL game:** the showcase UI will want
134 restyling to match the Ricochet design sheets (`Ricochet/reference/`) more
135 closely in *feel*. (User flagged this explicitly.)
136
137 ## The effect system (pattern × color × dither × shape)
138
139 Every draw routes through `assets/draw_shader.frag`. The shader composes:
140
141 ```
142 f = pattern(world_pos, time*speed, scale, param, param2) // [0,1]
143 f = contrast_remap(f) // spread/compress around 0.5
144 f = shape(f, pixel_pos, shape_size) // carve into a dot grid (or passthrough)
145 f = dither(f, pixel_pos) // {0, 1/n, …, 1}
146 rgb = color(base_rgb, f, palette[color_a], palette[color_b])
147 ```
148
149 Four orthogonal selectors (pattern / color / dither / shape) plus three
150 universal **modifiers** (scale / speed / contrast) and **two per-pattern
151 intrinsic knobs** (`param` / `param2`). Pattern owns spatial structure AND
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
151 intrinsic knobs** (`param` / `param2`). Pattern owns spatial structure AND
152 animation; shape optionally re-expresses the field as a dot grid; color is a
153 pure recipe that picks/mixes palette tokens by f; dither inserts the
154 pixel-art stipple between them.
155
156 ### Patterns (`pattern_kinds` in `effect.lua`; dispatch in the shader's `pattern_field`)
157
158 Two base kinds + seven world-anchored "gallery" patterns + two **LOCAL**
159 gradient patterns. Each gallery pattern has ONE intrinsic `param` knob whose
160 meaning differs per pattern; its DEFAULT value reproduces the canonical look.
161
162 | kind | name | character | param (intrinsic knob) | param2 (2nd knob) |
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the Shape section — the biggest behavior change. Let me read its current text:
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
250 trap). Use DITHER `off` for a smooth ramp, or a dither with **>= N levels**
251 (`bayer4_8`, `bayer8_16`, `noise_8`) to keep the middle stops.
252
253 ### Dither (`dither_kinds` — 34 modes)
254 Full port of snkrx-template's dither catalog: a 2-level group (bayer4_2,
255 bayer8_2, noise_2) plus multi-level Bayer / white-noise / cluster-dot /
256 diagonal-line / scanline / triangle / hearts families. The complete list +
257 human labels are in `dither_kinds` and `dither_pretty_names` in `effect.lua`.
258 The level count is baked into each name (`bayer4_4` = 4×4 matrix, 4 levels);
259 2-level = hard A/B, higher = smoother `mix` transitions.
260
261 ### Shape (`shape_kinds` — circle dot grid)
262 A stage between `effect_field` and dither that re-expresses the field as a grid
263 of shapes. It still dithers + colors normally afterward. `none` is passthrough.
264 Cells are `shape_size` px square, anchored to screen space (`gl_FragCoord`, the
265 same space as dither).
266
267 **The general model (`shape_mask` + `effect_field_shaped`), built to extend to
268 image/sprite shapes later. The field has TWO roles, kept on SEPARATE channels
269 — conflating them is what made gaps + dot-edges collapse to `color_ramp`'s stop
270 0 (the "everything goes red" trap):**
271 - **Coverage** — *where* a dot is. The mask `m`, a disc whose size comes from
272 the **per-cell** value `fc` (sampled once at the cell center via
273 `effect_field_shaped`, reconstructing world/uv there from `dFdx`/`dFdy` —
274 exact for the 2D affine camera). Per-cell sampling keeps the boundary a
275 **true circle**. Coverage drives compositing (alpha / blend), **not** color.
276 `shape_mask()` is the single extension point: an image shape returns
277 `texture(u_shape_tex, cell_uv).a` there; nothing downstream changes.
278 - **Color** — *what hue*. The field value fed to dither + the color recipe at
279 **full strength** (never multiplied by the mask). `circle` uses the
... [38 more lines]
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
317 color_a = 'ink', -- palette token name
318 color_b = 'field', -- palette token name (used by mix)
319 ramp = {'red','orange','yellow','green','blue','pink'}, -- token list, used by color='ramp'
320 pattern_scale = 0.4, -- feature size
321 speed = 1.0, -- animation rate (default 1)
322 contrast = 1.0, -- mid-range spread (default 1 = unchanged)
323 pattern_param = 0.5, -- the active pattern's intrinsic knob, [0,1]
324 pattern_param2 = 0, -- 2nd intrinsic knob (gradient OFFSET / RANGE)
325 })
326 -- ... draws here ...
327 effect_clear(layer) -- or the scoped effect_draw(layer, spec, fn)
328 ```
329
330 Every field optional; omitted → documented default. **speed/contrast
331 default to 1.0, NOT 0** (0 would freeze animation / flatten the field to
332 mid-grey). Default spec is `color='none'`, so a bare `effect_set` is a
333 no-op until you set a color recipe.
334
335 ---
336
337 ## Pattern exploration history (don't re-tread)
338
339 The pattern set was found by trial across the "Visual Experiments 1"
340 session. A new instance should NOT re-propose the rejected ideas without a
341 fresh reason — they were built and cut.
342
343 **Rejected outright (built, looked wrong, removed):**
344 - **worley / cellular** — only read as cells in a narrow scale window;
Now the Shape section rewrite:
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the Spec API block + the single-call wrappers:
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the Test scene section + Keys table. Let me read them:
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
386 ---
387
388 ## Palette
389
390 13 named tokens per palette mode. Light mode uses Ricochet's "ink on warm
391 paper" colors; dark mode uses the "blueprint room with the lights off"
392 counterparts (see `Ricochet/design.md` § Dark mode for the role mapping).
393
394 Keys (shared across both palettes):
395 ```
396 neutrals: field panel recess ink mid_light mid_grey deep_grey
397 accents: blue orange green pink yellow red
398 ```
399
400 Light mode collapses panel→field and deep_grey→recess (no separate
401 elevation surface defined in design.md). Forked games that need
402 explicit elevation in light mode override the table.
403
404 `palette.lua` exposes:
405 - `palette_dark`, `palette_light` — the two tables
406 - `palette` — the currently-active table (set by `palette_init`)
407 - `palette_mode` — `'dark'` or `'light'`
408 - `palette_token_names`, `palette_token_index`, `palette_token_count`
409
410 For raw layer_* draws, call `palette.ink()` etc. (the parens trigger the
411 color metatable's __call → rgba tuple). For shader-pipeline draws, pass
412 the token NAME via `color_a`/`color_b` in the spec.
413
414 ---
415
... [30 more lines]
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
445 tooltip, leaderboard list), CONTROLS (tabs, toggle/checkbox/radio, stepper, stat
446 block, keycaps, bracket tags). Each is a small local helper taking `(x, y, …)`
447 and returning the next `y`.
448
449 **Icon pack: Tabler** (chosen over Phosphor/Remix — most icons + best game
450 coverage of the clean line packs + freely adjustable stroke). Icons are 44px
451 white PNGs at `assets/icons/tabler/<concept>.png`, loaded flat into
452 `icons[concept]` and tinted to a palette token. Regenerate / extend the set via
453 `tools/fetch_icons.mjs` (Iconify API → resvg; `STROKE = 0.75` for the hairline
454 look). `tools/compare_packs.mjs` is the pack-comparison helper (counts + game
455 coverage).
456
457 NOTE: `F1`/`F2` (pattern) and `[`/`]` (scale) still mutate
458 `current_pattern` / `current_scale`, but the gallery ignores those globals
459 (every rect is per-pattern, per-scale via the inspector), so they're inert
460 in gallery mode. Color / dither / tokens / palette ARE shared, so their
461 keys work.
462
463 ---
464
465 ## UI control panel
466
467 A left-anchored sidebar (380×700, top-left at (32, 40)). The gallery sits
468 to its right. The sidebar has two regions:
469 - **Shared look controls** (apply to every gallery rect): PALETTE segment,
470 COLOR recipe, DITHER, SHAPE (+ CELL SIZE slider, shown only when SHAPE ≠
471 off), COLOR A, COLOR B.
472 - **MODIFIERS** (apply only to the currently-selected gallery rect):
473 SCALE / SPEED / CONTRAST sliders + the selected pattern's intrinsic-knob
474 slider(s) (labeled per pattern, e.g. WARP / VEIN / LINES; gradients add a
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the UI control panel section. Let me read it:
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
461
462 ## UI control panel
463
464 A left-anchored sidebar (380×700, top-left at (32, 40)). The gallery sits
465 to its right. The sidebar has two regions:
466 - **Shared look controls** (apply to every gallery rect): PALETTE segment,
467 COLOR recipe, DITHER, SHAPE (+ CELL SIZE slider, shown only when SHAPE ≠
468 off), COLOR A, COLOR B.
469 - **MODIFIERS** (apply only to the currently-selected gallery rect):
470 SCALE / SPEED / CONTRAST sliders + the selected pattern's intrinsic-knob
471 slider(s) (labeled per pattern, e.g. WARP / VEIN / LINES; gradients add a
472 2nd knob OFFSET / RANGE). Click a rect to choose which one these edit.
473
474 ### The widgets
475
476 All five draw to `ui_layer`. Each is a function taking one opts table;
477 state is caller-owned (pass `value` / `selected` / `index`; widget
478 reports `clicked` / `changed` / new value/index; caller updates its
479 own globals).
480
481 - **`ui_panel(opts)`** — hairline-bordered container with optional
482 title bar and optional corner brackets (small L shapes outside each
483 corner, per Ricochet/design.md's chamber treatment).
484 - **`ui_field(opts)`** — `LABEL value [<] [>]` row. The cycler used
485 for COLOR and DITHER.
486 - **`ui_segment(opts)`** — N-cell segmented control. Active cell is
487 drawn with an INVERTED fill (ink rectangle, field-colored label) —
488 matches the reference sheets' button-active idiom. The palette-mode
489 toggle uses it.
490 - **`ui_swatch_row(opts)`** — palette-token picker: label above, row
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the "Adding a shape" instructions (shape_mask signature changed) and the File map. Let me read those:
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
640 ### Draw origin: images CENTER, rectangles/text TOP-LEFT
641 `layer_image` / `layer_texture` / `layer_circle` draw **centered** at `(x, y)`;
642 `layer_rectangle` / `layer_text` draw from the **top-left** `(x, y)`. Do NOT
643 subtract half the image size to "center" an image — it already centers, so that
644 double-offsets it up-left by half (the showcase icon bug). To center an icon in
645 a top-left box `{x, y, w, h}`: `layer_image(lyr, img, x + w/2, y + h/2, col)`.
646 See `showcase.lua`'s `icon()` helper.
647
648 ### Color in draws vs in specs
649 - **Raw layer_* draws** (background fill, HUD text, anything that should
650 bypass the pipeline) → pass a color object directly, with parens:
651 `layer_rectangle(game_layer, 0, 0, w, h, palette.field())`.
652 - **Pipeline draws** (anything that should be re-colored by the effect) →
653 pass token NAMES in the spec: `color_a = 'ink'`. The base color of the
654 draw is then irrelevant (use any visible token so layout-only debugging
655 remains possible).
656
657 ### `effect_clear` after a bracketed set
658 The shader uniforms persist on the layer until rewritten. Use
659 `effect_clear(layer)` after a bracketed pipeline draw, or use the scoped
660 `effect_draw(layer, spec, fn, ...)` form which clears for you.
661
662 ### Don't run `./run.bat` yourself
663 The user runs and tests. After framework changes, syntax-check Lua with:
664 ```
665 E:/a327ex/Anchor/reference/love-compare/tools/lua54.exe -e "loadfile('main.lua')"
666 ```
667
668 ---
669
... [45 more lines]
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now the File map. Let me read it:
Read (E:\a327ex\ricochet-template.claude\CLAUDE.md)
714
715 ---
716
717 ## File map
718
719 ```
720 ricochet-template/
721 ├── main.lua # pattern gallery + inspector sidebar + keys
722 ├── palette.lua # 2 palettes × 13 tokens; palette_init
723 ├── effect.lua # 4-axis spec API (pattern/color/dither/shape) + cycle helpers
724 ├── rectcut.lua # RectCut layout primitive
725 ├── ui_state.lua # frame-local hot/active/focus + ui_claim_hot
726 ├── ui.lua # 5 Ricochet-style widgets + helpers
727 ├── anchor/ # Anchor 2 framework
728 ├── anchor.exe # built engine
729 ├── assets/
730 │ ├── draw_shader.frag # unified pattern+shape+dither+color shader
731 │ ├── Oswald-Medium.ttf # UI titles + field labels
732 │ ├── IBMPlexSans-Regular.ttf # UI labels (alt)
733 │ └── CascadiaCode-Regular.ttf # UI value column + numeric readouts
734 ├── run.bat
735 └── .claude/CLAUDE.md # this file
736 ```
737
738 ---
739
740 ## Working style
741
742 Same conventions as Anchor 2 / Ricochet / Invoker:
743
... [5 more lines]
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
Now add the Engine-patch note to Critical conventions. Let me find that section header:
Grep (## Critical conventions|### Boot order)
626:## Critical conventions
627-
628:### Boot order
629-1. `require('anchor')(...)` — engine init.
630-2. `require('palette')` + `require('effect')` — module loads.
631-3. `palette_init('dark')` — populates `palette`.
632-4. `font_register(...)` — fonts for HUD/text.
633-5. `effect_setup({ draw_shader = '...' })` — installs shader AND pushes
634- the active palette to `u_palette[]`.
635-
636-Steps 3 and 5 can be reversed — `palette_init` checks if
637-`effect_write_palette` exists and calls it if so, otherwise `effect_setup`
638-picks up the current palette when it runs. Either order works.
639-
640-### Draw origin: images CENTER, rectangles/text TOP-LEFT
641-`layer_image` / `layer_texture` / `layer_circle` draw **centered** at `(x, y)`;
642-`layer_rectangle` / `layer_text` draw from the **top-left** `(x, y)`. Do NOT
643-subtract half the image size to "center" an image — it already centers, so that
644-double-offsets it up-left by half (the showcase icon bug). To center an icon in
645-a top-left box `{x, y, w, h}`: `layer_image(lyr, img, x + w/2, y + h/2, col)`.
646-See `showcase.lua`'s `icon()` helper.
Edit (E:\a327ex\ricochet-template.claude\CLAUDE.md)
The file E:\a327ex\ricochet-template\.claude\CLAUDE.md has been updated successfully. (file state is current in your context — no need to Read it back)
CLAUDE.md is thoroughly synced. Now let me verify the effect.lua top docblock still reads accurately (it predates several axes). Let me check it:
Read (E:\a327ex\ricochet-template\effect.lua)
1 --[[
2 effect.lua — three-axis visual effect system (pattern × color × dither).
3
4 THE 30-SECOND MENTAL MODEL
5 Every draw routes through assets/draw_shader.frag. The shader takes
6 three orthogonal selectors and composes them as:
7
8 f = pattern(world_pos, time) -- spatial structure + animation
9 f = dither(f, pixel_pos) -- quantize to discrete levels
10 rgb = color(base_rgb, f, palette) -- map f → palette token(s)
11
12 Pattern owns "what the field looks like" AND "whether it moves over
13 time." Color is a pure recipe that picks (or mixes) palette tokens
14 by f. Dither inserts the pixel-art stipple between them. The three
15 are independent — any combination is valid.
16
17 PUBLIC API
18 effect_setup(opts) install draw shader, cache its
19 GL program ID, push the palette
20 effect_set(layer, spec) write effect uniforms for this
21 layer's subsequent draws
22 effect_clear(layer) pattern/color/dither → 0 (passthrough)
23 effect_draw(layer, spec, fn, ...) scoped form
24 effect_write_palette() re-push u_palette to the shader
25 (called automatically by palette_init
26 and effect_setup)
27
28 Cycle helpers (return the NEXT/PREV name in each axis's cycle):
29 effect_next_pattern(cur), effect_prev_pattern(cur)
30 effect_next_color(cur), effect_prev_color(cur)
... [5 more lines]
The header says "three-axis" and omits shape, the wrappers, and image-as-content. Let me update it:
Edit (E:\a327ex\ricochet-template\effect.lua)
The file E:\a327ex\ricochet-template\effect.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Let me check main.lua's top docblock (it describes the old single-screen scene):
Read (E:\a327ex\ricochet-template\main.lua)
1 --[[
2 ricochet-template — visual style + three-axis effect template.
3
4 Entry point. Owns:
5 - configuration (resolution, render mode, monitor)
6 - boot order (palette → fonts → effect/shader → ui modules)
7 - layers (game, ui), camera
8 - current effect-spec state (pattern, color, dither, color_a/b, scale)
9 - sidebar control panel + keyboard input (both mutate the same state)
10 - test-scene draw (sample shapes through the effect pipeline)
11
12 Delegates:
13 - palette.lua — dark/light palette tables + palette_init
14 - effect.lua — pattern × color × dither spec API + cycle helpers
15 - rectcut.lua — RectCut layout primitive (cut/get/add/contract/...)
16 - ui_state.lua — frame-local hot/active/focus + ui_claim_hot
17 - ui.lua — five Ricochet-style widgets that mutate effect state
18
19 Goal: visually evaluate any (pattern, color, dither, color_a, color_b,
20 scale) combination by clicking sidebar controls or pressing the hotkeys.
21 Both input modes update the same caller-owned state.
22 ]]
23
Edit (E:\a327ex\ricochet-template\main.lua)
The file E:\a327ex\ricochet-template\main.lua has been updated successfully. (file state is current in your context — no need to Read it back)
Let me syntax-check the files whose docblocks I edited:
Bash (cd E:/a327ex/ricochet-template && LUA=E:/a327ex/Anchor/reference/love-compare/to...)
OK: main.lua
OK: effect.lua
Both parse. Now a cross-project memory for the engine patch — it's a capability change to the shared Anchor 2 engine that any project (Invoker, etc.) could use, and isn't obvious from the code alone.
Write (C:\Users\adn.claude\projects\E--a327ex\memory\reference_anchor2_aux_sampler_draws.md)
File created successfully at: C:\Users\adn\.claude\projects\E--a327ex\memory\reference_anchor2_aux_sampler_draws.md (file state is current in your context — no need to Read it back)
Now add the index line to MEMORY.md:
Read (C:\Users\adn.claude\projects\E--a327ex\memory\MEMORY.md)
1 # Memory
2
3 ## Code Style - Emoji Ball Battles (Lua)
4
5 ### Operator spacing convention
6 - `*` and `/`: no spaces (e.g., `a*b`, `x/y`)
7 - `+` and `-`: spaces (e.g., `a + b`, `x - y`)
8
9 Example:
10 ```lua
11 result = a*b + c/d - e
12 velocity = speed*math.cos(angle) + offset
13 ```
14
15 ## Conversion History
16
17 - **Emoji Ball Battles**: Converted from YueScript to Lua (2026-02-24). Archived `.yue` files in `emoji-ball-battles/reference/yue/`. Framework files copied from `Anchor/framework/anchor/`.
18 - **Invoker** (pre-reset): Converted from YueScript to Lua (2026-02-25). Archived `.yue` files now live in `Invoker-old/reference/yue/` after the 2026-05-27 reset to snkrx-template. The new Invoker has no YueScript history.
19
20 ## Naming Conventions
21
22 - Always capitalize "Invoker" (capital O) — it's a proper name, not a directory path reference.
23
24 ## Critical Warnings
25 - [Invoker runs on Anchor 2](feedback_invoker_anchor_divergence.md) — use Anchor2/docs/, not Anchor/docs/ (v1); Invoker/anchor/ is the actual ground truth
26 - [Anchor 2 mouse buttons follow LÖVE2D](reference_anchor2_mouse_buttons.md) — `mouse:2` = RMB, `mouse:3` = MMB (engine swaps 2↔3 at SDL boundary as of 2026-05-17)
27 - [Anchor 2 input edge events are update-only](reference_anchor2_input_in_draw.md) — `mouse_is_pressed/released` and `key_is_pressed/released` always return false from `draw()`; run UI logic (hit-testing, state machines, layer command queueing) in `update()` and reduce `draw()` to `layer_render + layer_draw`
28 - [Anchor 2 sprites/images draw CENTERED at (x,y)](reference_anchor2_sprite_centered.md) — `layer_image`/`layer_texture`/`layer_circle` center at (x,y); `layer_rectangle`/`layer_text` are TOP-LEFT. Don't subtract half-size to "center" an image — it already centers, so that double-offsets it up-left by half. To center an icon in a top-left box {x,y,w,h}: `layer_image(lyr, img, x + w/2, y + h/2, col)`
29
30 ## Workflow
... [10 more lines]
Edit (C:\Users\adn.claude\projects\E--a327ex\memory\MEMORY.md)
The file C:\Users\adn\.claude\projects\E--a327ex\memory\MEMORY.md has been updated successfully. (file state is current in your context — no need to Read it back)
Docs are fully synced. Summary of what I updated (docs only — no code changed, nothing to test):
ricochet-template/.claude/CLAUDE.md
- Layout + File map — the three screens, and the new files (
editor.lua,ui_controls.lua,showcase.lua,assets/icons/,assets/images/,tools/). - Roadmap — rewrote it: all the big items are done (image-as-shape, image-as-content, geometric shapes, rotation, single-call API); color + shape axes marked complete; remaining items (480×270, snkrx-template replacement, flow-field rotation) moved to a "deferred, user-driven" list so a future instance won't auto-tackle them.
- Effect system + Shape section — image-as-content in the pipeline; full rewrite of the shape axis (geometric kinds via gauge metrics, sprite bg/field, rotation, spacing, the coverage/color model with the new
shape_mask(kind, fc, cell_uv)signature). - Spec API — the complete spec table (all new fields) + the single-call wrappers.
- Test scene / Keys / UI panel — three screens, Tab cycle, the editor, and the shared
ui_controls.luablocks ("edit a shared control in one place"). - Engine patch note (Critical conventions) — documents that
anchor.exeis a patched build (aux samplers bind for the draw path) so it's not a mystery later. - Adding a pattern/shape — updated for
pattern_defaultsand the newshape_masksignature.
Code docblocks — effect.lua (now "four-axis" + wrappers + image-as-content) and main.lua (three screens, new delegates).
Memory — added a cross-project reference note (reference_anchor2_aux_sampler_draws) so any Anchor 2 project (Invoker, etc.) knows the engine can now bind a second sampler to the draw shader, plus its MEMORY.md index line.
That closes out everything this session set out to do — icons/images (shape + content), more shapes + rotation, and the API simplifications (single-call wrappers + the unified editor). The three deferred items (480×270, replacing the snkrx effect stack, flow-field rotation) are parked in the roadmap for the manual sessions you wanted to drive yourself. Ready for /end-session whenever you want to log it.
Let's end it, call it Visual Experiments 3
Skill
Launching skill: end-session
Base directory for this skill: E:\a327ex.claude\skills\end-session
End Session Workflow
This skill is invoked from the same conversation where the work was done. You already have the full session context in memory — you do NOT need to read the transcript.
Step 1: Get Session Info
Ask the user for the session title (max 30 characters). Examples: "Anchor Phase 10 Part 5", "Physics Arena Setup", "Timer System Fix", "Thalien Lune Design".
Determine the project yourself from your session context — you know which repo(s) were worked on, which files were created/modified, and where they live. No need to ask. See Step 5 for the list of known project roots; if the session touched something outside the list, infer the root from the paths you actually edited.
Step 2: Write Summary
Write the summary from your conversation memory. You have the full session context — no need to read any files.
The summary should be thorough and detailed. Each major topic deserves its own section with multiple specific bullet points. Don't compress — expand.
Purpose: These summaries serve as searchable records. Future Claude instances will grep through past logs to find how specific topics were handled. The more detail you include, the more useful the summary becomes for finding relevant context later.
Format (this is just an example structure — adapt sections to match what actually happened):
# [Title] ## Summary [1-2 sentence overview of the session's main focus] **[Topic 1 - e.g., "Spring Module Implementation"]:** - First specific detail about what was done - Second detail - include file names, function names - User correction or feedback (quote if notable) - Technical decisions and why **[Topic 2 - e.g., "Camera Research"]:** - What was researched - Key findings - How it influenced implementation **[Topic 3 - e.g., "Errors and Fixes"]:** - Specific error message encountered - Root cause identified - How it was fixed [Continue for each major topic...] --- [Rest of transcript follows]Rules: - Be thorough — If in doubt, include more detail, not less. Each topic should be as detailed as possible while still being a summary. - Think searchability — Future instances will search these logs. Include keywords, function names, error messages that someone might grep for. - One section per major topic — Don't combine unrelated work into one section - Chronological order — Sections should match conversation flow - Specific details — Error messages, file names, function names, parameter values - Include user quotes — When user gave notable feedback, quote it (e.g., "k/d variables are not intuitive at all") - Weight planning equally — Research, proposals, alternatives considered, user feedback on approach are as important as implementation - Weight problems solved — Errors, root causes, fixes, user corrections all matter - Technical specifics — Include formulas, API signatures, parameter changes when relevant
Step 3: Proceed Without Approval
Do NOT show the summary to the user for approval. Write it directly. The user can review the committed log after the fact and request a follow-up edit if anything is off.
Step 4: Convert Transcript and Write the Log File
# Find recent sessions (Claude + Cursor + Codex). Same script lives in Anchor2: python E:/a327ex/Anchor2/scripts/find-recent-session.py --limit 5 # or: python E:/a327ex/Anchor/scripts/find-recent-session.py --limit 5The script shows sessions sorted by when they ended. The first result is the current conversation (since end-session was invoked here). Use it.
Use a lowercase hyphenated slug derived from the title (e.g., "anchor-primitives-hitstop-animation").
Get the end timestamp for the Date frontmatter — this is the wall-clock time when end-session was invoked, NOT the time the JSONL started. Sessions often span multiple days, and the log should be filed under the day the work was wrapped up:
date "+%Y-%m-%d %H:%M:%S"Use this output verbatim. Do not substitute the JSONL start timestamp; the log appears in the sidebar sorted by Date, and a multi-day session with a Date pinned to day 1 will sort below sessions that ended later but started later, hiding the most recent work.
Convert the transcript to markdown:
python E:/a327ex/Anchor2/scripts/jsonl-to-markdown.py [SESSION_PATH] /tmp/session-log.md # or: python E:/a327ex/Anchor/scripts/jsonl-to-markdown.py ...The same script auto-detects Claude Code JSONL vs Cursor/Composer agent JSONL (
~/.cursor/projects/.../agent-transcripts/...) vs Codex rollouts (~/.codex/sessions/...). For Composer sessions, usefind-recent-session.py(it merges all sources) and pick the[cursor]line for the current chat.Replace the default header (
# Session YYYY-MM-DD...) at the top of/tmp/session-log.mdwith the approved title and summary, AND prepend frontmatter. The final file shape:Title: [Title] Date: YYYY-MM-DD HH:MM:SS # [Title] ## Summary [approved summary text from step 2] --- [transcript content from jsonl-to-markdown script]Frontmatter is non-negotiable. Every log file MUST start with
Title:andDate:lines. Without them, the site's sidebar shows the slug as the title and 0 (epoch) as the sort date. The backfill script ina327ex-site/deploy/backfill_metadata.pyis a safety net, not a substitute — write it correctly the first time.Then copy the final file to the log destination:
cp /tmp/session-log.md E:/a327ex/a327ex-site/logs/[slug].mdStep 4.5: Decrement the lock (if active)
Read
E:/a327ex/a327ex-site/.lock.jsonif it exists. If it contains{"remaining": N}with N > 0:
- Decrement N by 1
- Write
{"remaining": N-1}back to the file- If N becomes 0, the lock is cleared. You may leave the file at
{"remaining": 0}or delete it; both work.The lock file lives in the a327ex-site repo, so the change is staged automatically by
git add -Ain Step 6.If no lock file exists or
remainingis already 0, do nothing. (See the/lockskill for the lock's full design.)Step 5: Commit Project Repo
Identify the project repo(s) worked on this session from your own context — you already know which repos were touched and which files changed. For the common projects:
Project Root Stage command Anchor E:/a327ex/Anchorgit add docs/ framework/ engine/ scripts/ reference/Anchor2 E:/a327ex/Anchor2git add framework/ engine/ arena/ reference/ scripts/ docs/ .claude/emoji-ball-battles E:/a327ex/emoji-ball-battlesgit add -Ainvoker E:/a327ex/Invokergit add -Athalien-lune E:/a327ex/thalien-lunegit add -Aa327ex-site E:/a327ex/a327ex-sitegit add -A(this IS one of the web repos — the log file added in Step 4 is staged here automatically)For a project not listed, infer the root from the files you actually created or modified this session and stage those. If multiple candidate roots look valid, ask the user which files to stage.
cdinto the project root, stage, rungit statusto verify before committing.IMPORTANT — FULL SUMMARY IN COMMIT: The commit message MUST include the FULL summary from the log file. Read the summary back from the log file to ensure nothing is missing.
IMPORTANT — COMMIT METHOD: The summary contains backticks, special characters, and markdown that WILL break heredocs and
git commit -m. ALWAYS use the file-based method below. NEVER try a heredoc first — it will fail and produce a malformed commit that needs amending.# Skip until we hit the line "## Summary", then take everything after the next # blank line until the --- separator that precedes the transcript. awk '/^## Summary$/{found=1; next} found && NR>1 && /^---$/{exit} found' \ E:/a327ex/a327ex-site/logs/[slug].md > /tmp/commit_msg.txt # Prepend the title (plain text, no #) and append attribution sed -i "1i [Title]\n" /tmp/commit_msg.txt printf "\nGenerated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\n" >> /tmp/commit_msg.txt git commit -F /tmp/commit_msg.txtStep 6: Push the Repos
Two pushes — project (to GitHub) and a327ex-site (to the VPS):
# Project repo to GitHub. Skip this push if the project IS a327ex-site # (handled by the second push below — don't duplicate). git push origin main # a327ex-site to the VPS (post-receive hook restarts the Lua server). cd E:/a327ex/a327ex-site git add -A git commit -m "[Title]" git push prod main 2>&1 | tail -3Failure handling: if either push fails, the other still happens. Local commits stay intact, so the user can re-push manually once they've fixed whatever blocked it. Don't roll back; the committed state on disk is the source of truth.
Step 7: Confirm
Tell the user: - Project commit hash + GitHub push result - a327ex-site commit hash + VPS push result
Format:
Logged — "[Title]". project (Anchor2): abc1234 pushed to GitHub a327ex-site: ghi9012 pushed to a327ex.com (post-receive deployed)If a push failed, surface that explicitly with the error.
Notes
- Frontmatter is non-negotiable. Every log file MUST have
Title:andDate:at the top. Enforced at write time in step 4. The backfill script is a safety net, not a substitute.- If the project IS
a327ex-site(e.g., you spent the session writing the renderer or fixing the theme), there's no separate "project repo to GitHub" push — the a327ex-site push at the end IS the project push. Don't duplicate.- Legacy note: this skill used to also dual-write to
anchor.blot.im/logs/andanchor.blot.im/context/. Post-cutover (May 2026),anchor.blot.imis a JS redirect toa327ex.comand no longer receives content. Drop those steps if you find any older docs that still reference them.ARGUMENTS: Visual Experiments 3
I have the full session context. Let me run the mechanical steps — find the session, get the timestamp, and check which repos were touched.
Bash (python E:/a327ex/Anchor2/scripts/find-recent-session.py --limit 5 2>&1; echo "==...)
2026-05-30T20:25:57.330Z 919f238e-264d-437b-8ab7-fc807e5302b7 [claude] <-- MOST RECENT
C:\Users\adn\.claude\projects\E--a327ex\919f238e-264d-437b-8ab7-fc807e5302b7.jsonl
2026-05-30T20:00:42.308Z aca39077-9a40-4d7e-ac77-c4c05ac51979 [claude]
C:\Users\adn\.claude\projects\E--a327ex\aca39077-9a40-4d7e-ac77-c4c05ac51979.jsonl
2026-05-30T18:17:12.676Z 06f5cfc2-4245-4927-9a2d-d7f372358eff [claude]
C:\Users\adn\.claude\projects\E--a327ex\06f5cfc2-4245-4927-9a2d-d7f372358eff.jsonl
2026-05-30T00:45:02.609Z 8794b075-651a-4460-a6f1-0b750c98fa26 [claude]
C:\Users\adn\.claude\projects\E--a327ex\8794b075-651a-4460-a6f1-0b750c98fa26.jsonl
2026-05-30T00:41:53.417Z ced0b502-30f5-45ac-8a67-dc25bf7aae84 [claude]
C:\Users\adn\.claude\projects\E--a327ex\ced0b502-30f5-45ac-8a67-dc25bf7aae84.jsonl
=== end timestamp ===
2026-05-30 17:26:34
Bash (echo "=== ricochet-template git? ==="; cd E:/a327ex/ricochet-template 2>/dev/nul...)
=== ricochet-template git? ===
=== Anchor2 git? ===
true
origin git@github.com:a327ex/Anchor2.git (fetch)
origin git@github.com:a327ex/Anchor2.git (push)
Let me check the ricochet-template repo status definitively (the check returned nothing):
Bash (cd E:/a327ex/ricochet-template; echo "is-work-tree: $(git rev-parse --is-inside-...)
is-work-tree: fatal: not a git repository (or any of the parent directories): .git
git-dir: fatal: not a git repository (or any of the parent directories): .git
remote:
fatal: not a git repository (or any of the parent directories): .git
=== top of status ===
fatal: not a git repository (or any of the parent directories): .git
ricochet-template isn't a git repo (the work lives on disk + in the log, like the prior Visual Experiments sessions). The committable code change is the Anchor2 engine patch, plus the log to a327ex-site. Let me convert the transcript and check Anchor2's status.