Commit Graph
8 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 9e869df063 Free a redrawn picture only once no shader names it (#293)
game-maker / gate (push) Successful in 2m48s
game-maker / suite (push) Successful in 13m44s
Editing a vector drawing made Pixi warn "destroyed while still bound
to a shader", twice with every change, once it had begun. It began
where an object wears the drawing through a shader asset. Each
change draws the picture anew and destroyed the one before it, but
the mesh of that object keeps its picture bound until it syncs. The
issue also asks for the button "Add shape" to go: a shape without
points cannot be given any, the Pen draws a new one.

- GeneratedPictures takes two more callbacks. inUse says that a
  picture cannot be freed yet, soon runs a retry once whoever asked
  is done.
- A replaced picture that is still in use waits in a list of its
  own. It is freed right after the redraw that asked, as a rule. On
  a later ask where its wearer was not part of that redraw.
- TextureLibrary tells a bound picture by the listeners for change
  on its source. Under WebGL the bind groups are the only ones that
  listen there. The retry is a microtask.
- The rule is asked for every drawn picture alike, whoever wears it.
- The inspector of a drawing and of a shape has no "Add shape" any
  more. "Delete shapes" for several selected shapes stays.

Decided here:

- Under the list of shapes a second note says "The Pen draws a new
  shape." while only the asset is selected.

Found on the way:

- Reproduced on a throwaway copy of the project the report came
  from: 6 warnings for 3 changes with the shader override on the
  object, none without it. After the change none, and no replaced
  picture is left waiting.
- The fixture project of the tests never showed the warning. No
  object there wore a drawing through a shader.

Tests:
- Gate on melektaus-atx: 211 files, 3219 tests (3209 + 10), exit 0
- e2e 47,48 on melektaus-atx: 113 checks, all pass
- e2e 28,30,31,39,40,47,48: 350 checks, all pass, run before the
  wording of the new note changed
- The first gate was red: an existing test read the wording of the
  note that had changed. Only three test files had been run after
  the change.
- No full run and no smokes. The sum of 1668 (1667 + 1) is reckoned,
  1667 is what CI counted for ec2fd90.
- Ten unit tests, five of them against the real bind group of Pixi
  in tests/viewport/bound-pictures.test.ts
- One e2e check in block 47b-ii of module 47: a drawing worn through
  a shader, three changes, no warning. The check on "Add shape" now
  reads that there is no such button.
- Nine unit counter-probes and two browser counter-probes all fail
  their tests. Without the change the new check counts 6 warnings.
- Not checked: a tilemap or a particle emitter holding a picture
  bound the same way. Nothing was looked at after the build, the
  new note included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 13:03:37 +02:00
adminandClaude Opus 5.5 345289982b Scale, turn and mirror the shapes of a vector drawing (#291)
game-maker / gate (push) Successful in 2m57s
game-maker / suite (push) Successful in 14m25s
A shape could only be moved and reshaped point by point. Whoever
wanted it larger or turned pulled every point alone, and for several
shapes or a group there was no way at all. The answers in the issue
settle the rest: the change is baked into the points, nothing of it
is remembered, and the line width stays.

- With Select the selection stands in a frame of eight squares and a
  knob, drawn like the one of the scene view. It holds one shape,
  several shapes or a group, the same selection that is dragged as
  one.
- A corner square scales from the opposite corner, a square on an
  edge in its own direction. Shift keeps the proportions, Alt scales
  about the middle.
- The knob turns round the middle of the frame, with Shift in steps
  of 15 degrees. After the pull the frame stands upright again.
- The inspector has a band TRANSFORM with X, Y, Width, Height and
  Rotate by, and the buttons Flip horizontal and Flip vertical.
- Points, handles, gradients and texture tiles are taken through the
  same matrix. The rule is a pure function in vectorTransform.ts,
  transformShapesCommand makes one undo step of any number of
  shapes.
- A press goes to a point or a handle first, then to the frame, then
  to a line. shapeFrame.ts holds where the handles are and what a
  pull of each means.

Decided here:

- The frame stands 15 pixels off the shapes. The points of a shape
  lie on its bounds, so a square on the bounds would cover them.
- The knob only turns. In the scene view it scales as well.
- A side cannot be pulled through the opposite one, the frame keeps
  at least 1 pixel.
- Coordinates are rounded to a thousandth of a pixel, so a quarter
  turn is exact.
- A texture tile stays upright under a turn and grows to the bounds
  of the turned tile. The bands of a gradient stay square to its
  axis under an uneven scale.
- The switch Scale stroke of the proposal is not built.

Found on the way:

- The inspector of a shape is now taller than the window. Two checks
  of module 47 no longer found their row or button on the screen and
  use the new hook inspectorButton, which scrolls there first.
- Not solved: with a shape at the top edge of the view the knob lies
  outside the tab. The wiki names it under the limits.

Taken over from #217: the e2e check for two selected shapes without
a group, and unit tests for the snap of the common offset and for
the gradients of the shapes dragged along.

Tests:
- Gate on melektaus-atx: 209 files, 3176 tests (3121 + 55), exit 0
- e2e 47,48 on melektaus-atx: 112 checks, all pass
- No full run and no smokes. The sum of 1664 (1660 + 4) is reckoned,
  1660 is what CI counted for 6d79cf5.
- 55 unit tests: 25 in tests/core/vector-transform.test.ts and 30 in
  tests/editor/shape-frame.test.ts
- Four e2e checks in block 47k of module 47: pull a corner, turn
  with Shift, type width and rotation, drag two shapes without a
  group
- 47 unit counter-probes and four browser counter-probes all fail
  their tests. Four of the unit ones did not at first: two showed
  dead code, which is gone, two got tests.
- Not checked in the browser: the edge squares, Alt, the cursors,
  the flip buttons and the rows X, Y and Height

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:20:55 +02:00
adminandClaude Opus 5.5 2ff3a78607 Put the shapes of a vector drawing on layers and in groups (#190)
game-maker / gate (push) Successful in 2m41s
game-maker / suite (push) Failing after 7h24m17s
A vector drawing was one flat list of shapes. With many shapes the
list got hard to read, and several shapes could not be kept or moved
together. Now the shapes stand on layers, as in pixel art, and can be
put into groups that nest.

- A drawing has layers, each with a name and an eye. A hidden layer
  is neither in the tab nor in the picture a sprite wears, and its
  shapes cannot be pressed. One layer is the one worked on: the pen
  and Add shape draw onto it, and SHAPES lists what is on it.
- The inspector has a list LAYERS over the shapes. Its plus adds a
  layer, the cross on a row removes it with all on it, the last one
  stays. A double-click renames, a drag sorts.
- Group (Ctrl+G) puts the selected shapes into a group, Ungroup
  (Ctrl+Shift+G) takes it away. A group stands over its rows in the
  list, the rows one step in, and a row is dragged into a group or
  out of it.
- In the tab a press on a shape takes its outermost group, and a drag
  moves the group as one undo step.
- The model has layers with a tree of shapes and groups instead of a
  flat shapes array. The rules are pure functions in
  core/model/vectorTree.ts. A drawing saved before opens with all its
  shapes on one layer.
- SortableList takes rows with a depth and drops into a tree. The
  flat lists of tilemap and pixel art run as before.

Decided here:

- A group has no transformation of its own and cannot be hidden or
  locked. Rotating and scaling is the follow-up #291.
- A group is never empty: the one that loses its last shape goes
  with it.
- Several selected shapes are dragged together, grouped or not, and
  a click on a shape of the selection keeps the selection. This is
  what #217 asked for.
- Shift+click selects like Ctrl+click. With Select, Shift pans only
  beside the shapes, as in the scene view. The middle button pans
  anywhere, and with the pen and Paint Shift pans as before.
- A row of the list picks one shape of a group alone.
- Shapes change their layer through a row Layer in the inspector,
  which is there from two layers on. Rows are not dragged between
  layers.
- A group cannot span two layers, the button is off then.
- The pen does not draw onto a hidden layer, the hint says so.
- Groups are always shown unfolded.
- The four order buttons work inside the own group or layer.

Tests:
- Gate: 204 files, 3067 tests (3008 + 59), exit 0
- e2e 47,48: 108 checks. e2e 39,40,47,48 before the Shift change:
  209 checks. No full run, the sum of 1645 (1639 + 6) is reckoned
  and CI measures it.
- 59 unit tests: 36 in tests/core/vector-tree.test.ts, 23 in
  tests/editor/vector-layers.test.ts
- Six e2e checks with a real mouse in block 47j
- 47 unit counter-probes for layers and groups and three for the
  Shift click all fail tests, run on stein2
- Three browser counter-probes fail their checks
- Two screenshots looked at (group in the list, hidden layer)
- Only unit tested: the row Layer, sorting and removing layers,
  copying a group, the Shift pan beside the shapes

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 23:13:43 +02:00
adminandClaude Opus 5 ae0247d86f Lists add and delete items right in the list header and rows
The layer, frame and shape lists still had their add and delete
buttons scattered below the list, each in its own layout. They now
live in the shared SortableList, so every list works the same way.

- SortableList gets an optional header with the title on the left
  and a + on the right, plus an optional x per row next to the eye.
- The keepOne option hides the x on the last remaining row, so a
  pixel art image and a tilemap always keep one layer.
- Pixel art layers and frames and tilemap layers use header, + and
  x. Only Merge down and Duplicate stay below the lists.
- Vector art shapes get the x but no +. Add shape stays below the
  list and Delete shapes stays for a multi-selection.
- The section headings in the pixel art column use the same style
  as the list titles and the inspector.

Findings:

- The wait loop added to check 47i for #196 only waited for the
  right half of the shape. If the texture landed between the two
  reads, the left half was still blue and the check failed. It now
  waits for both halves.

Tests:

- ./check.sh: 138 files, 2052 tests, exit 0.
- GM_STEPS=39,40,47,48 (fresh): ALL PASSED, 156 checks (was 154).
- GM_STEPS=47,48 (fresh, final): ALL PASSED, 86 checks.
- Two new e2e checks: x on a shape (47) and x on a frame (48). The
  tilemap check in 40 now uses the x instead of Delete layer.
- Counter-check: with the x not reporting to its owner, exactly the
  two new checks fail.
- Check total 1266 in README and wiki is computed (1264 + 2).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 21:18:26 +02:00
adminandClaude Opus 5 5b9b57fd19 Layer, frame and shape lists all reorder by drag and drop
Reordering shapes by dragging worked well in the vector tab. Pixel
art layers and frames and tilemap layers still used up/down buttons.
All three lists now share one widget built from the shape list.

- SortableList in the UI toolkit, with the drag rules next to it in
  src/ui/sortable.ts. A row has an optional thumbnail, a name, a hint
  on the right and an eye toggle. Every drop is one undo step.
- The shape list is a thin wrapper around it. shapeRows.ts is gone,
  its tests moved to the new rules.
- Pixel art layers show the layer alone on the current frame, frames
  show the whole frame. Up/Down, Hide, Earlier and Later are gone.
- Tilemap layers get the eye instead of the checkbox. The per-row
  up, down and delete buttons are replaced by Delete layer next to
  Add layer.
- A row is selected on release instead of press. Selecting rebuilt
  the inspector or tilemap panel and cancelled a drag that started
  on a row not yet selected.

Tests:
- ./check.sh: 138 files, 2052 tests, exit 0 (before 137 / 2051).
- GM_STEPS=39,40,47,48 (fresh seed): ALL PASSED, 154 checks.
- 5 new e2e checks. Check total 1264 in the README is computed
  (1259 + 5), CI never measured 1259.
- Counter-checks bite: dropMove ignoring reversed fails a unit test,
  a tilemap drop not applied fails the drag check, a layer thumbnail
  showing all layers fails the thumbnail check.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 18:48:50 +02:00
adminandClaude Opus 5 592ea942d9 Vector shapes can fill with gradients and textures (#186)
game-maker / gate (push) Successful in 2m14s
game-maker / suite (push) Failing after 7m14s
A vector shape could only be filled and outlined with one flat colour.
Shading, glows and patterned surfaces had to be faked with stacks of
shapes. Every shape now has a paint type for its fill and its outline.

- Fill type: solid colour, linear gradient, radial gradient or
  texture. Line type offers the same without texture.
- Colour stops are edited on a bar in the inspector, as agreed in the
  issue. A click on the bar adds a stop in the colour the gradient
  already has there, so adding never changes the picture. At least two
  stops remain.
- The new Paint tool in the tab shows two handles for the selected
  shape: axis start and end, centre and radius, or the corners of one
  texture tile. "Handles: Fill / Outline" picks which paint is edited.
- Moving a whole shape moves its gradients and tile with it. Every
  gesture is one undo step.
- A texture fill always tiles, because Pixi turns a non-repeating
  pattern into a repeating one anyway. When switched on, one tile
  covers the whole shape.
- Only uploaded textures and frames can be a fill, never a drawing.
  A drawing filled with itself would rasterise itself mid-raster.
- The fingerprint of a drawing includes the texture and frame a fill
  points to, so the picture redraws when the image is swapped.

Findings:

- graphics.destroy(true) also destroyed the textures of the fills.
  With a texture fill it deleted the tile slice and the next redraw
  crashed on reading 'scaleMode' of null. It is now destroy().
- The gradient cache destroyed old gradients itself, which gave 20
  Pixi warnings "destroyed while still bound". They are now handed to
  Pixi's texture GC with autoGarbageCollect switched on. The console
  of module 47 is empty again, the last CI log had 0 of them.

Tests:

- ./check.sh: 135 files, 2011 tests, exit 0 (before 134 / 1986)
- npm run build: exit 0
- GM_STEPS=47, freshly seeded: ALL PASSED, 54 checks (before 44),
  0 console warnings
- 25 new unit tests, 10 new e2e checks in block 47i
- Four counter-checks bite, originals restored and verified with cmp
- Check sum 1232 in README and wiki, calculated (1222 + 10)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 13:05:02 +02:00
adminandClaude Opus 5 1116818011 Vector shapes can be combined by union, subtract and more (#187)
game-maker / gate (push) Successful in 2m13s
game-maker / suite (push) Successful in 7m51s
Building a shape out of simpler ones meant drawing the result point by
point. The Vector Art tab now selects several shapes at once and
combines them with a boolean operation into one ordinary shape.

- Ctrl/Cmd+click in the tab and Ctrl or Shift+click in the shape list
  add a shape to the selection. Shift stays pan in the canvas.
- A COMBINE block in the inspector offers Union, Subtract, Intersect
  and Exclude. Subtract removes every upper shape from the lowest one.
- The result replaces the lowest selected shape and keeps its name,
  colours, stroke and place in the order. It is one undo step.
- Curves are flattened to 0.2 canvas pixels, a circle gets about 48
  points. Open lines and single shapes cannot be combined, the buttons
  are off and a hint gives the reason. An empty result is rejected.
- Delete now removes every selected shape, and the selection drops
  shapes that no longer exist.
- The geometry comes from clipper2-ts 2.0.1, pinned. It has no
  dependencies and speaks even-odd directly. Only the engine module
  is imported, the bundle grows by 55 kB instead of 110 kB.

Findings:

- clipper2-js was tried and dropped. It pulls in Angular 15 with a
  high npm audit finding and no fix.
- Pixi finds holes by containment, not by even-odd depth. An island
  inside a hole would be drawn as a hole again. Result rings are
  emitted outer ring before its islands, and a test mirrors Pixi's
  rule.
- A depth sort turned out to be dead code in a counter-check and was
  removed. A pixel check in 47h stayed green without the subtract
  and now checks both colours.

Tests:

- ./check.sh: 134 files, 1986 tests, exit 0 (before 133 / 1968)
- npm run build: exit 0
- GM_STEPS=47, freshly seeded: ALL PASSED, 44 checks (before 39)
- 18 new unit tests, 5 new e2e checks in block 47h
- Five counter-checks bite, originals restored and verified with cmp
- Check sum 1222 in README and wiki, calculated (1217 + 5)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-21 11:56:41 +02:00
adminandClaude Opus 5 78595ec80f A selected vector shape has its own inspector rows (#129)
game-maker / gate (push) Successful in 2m12s
game-maker / suite (push) Successful in 7m45s
Stage 4 of #129. Until now the inspector showed the rows of the asset
when a shape was selected, so there was no way to give a shape a colour,
an outline or a place in the drawing order without editing the project
file. The rows for a shape now sit where every other selection puts its
rows.

- SHAPES lists every shape of the asset, topmost row drawn last, the
  same way a layer list reads. Selecting the asset itself shows the same
  list below its own rows, with no shape selected.
- Each row paints its shape with the commands the rasterizer uses
  (shapeDrawing plus paintDrawing) into a 22 px swatch, the whole canvas
  scaled in, so the row also says where on the canvas the shape sits.
- The four order buttons from the issue (forward, to front, backward, to
  back) sit in two rows of two, and a row can be dragged to another
  place with a marker line. They are disabled when there is nothing to
  move.
- VECTOR SHAPE carries name, fill, opacity and an outline checkbox, the
  checkbox adding and removing the stroke instead of hiding it; line
  colour and width appear with it. A dragged colour slider is one undo
  step, the style rows going through previewingHandlers like every other
  row.
- A line width of 0 is no stroke, which the command rejects, so the row
  clamps at 0.1.

Findings:

- locate('Outline') aims beside the control, because it gives the centre
  of the label text and a wider label pushes a fixed offset out of the
  field. __gm.inspectorField(label) knows the centre of the control.
- A click into a text field sets the cursor and selects nothing, so a
  typed hex landed appended to the old one and parseHex returned null
  without anything failing - typeText only checks that the text is
  contained. A Ctrl+A goes first now.
- Four buttons side by side cut "Backward" to "Backw...", which only the
  screenshot showed. Two rows of two now, as in the tilemap panel.

Tests: ./check.sh 128 files, 1897 tests (was 127/1883). Full e2e suite
locally instead of only CI because the change sits in the inspector and
touches every selection: ALL PASSED - 1179 checks (was 1172), module 47
at 24 checks (was 17). Three counter-checks hold: not reversing the list
fails 1 unit test and 2 e2e checks, dropping the gap correction when
dropping a row fails 2 unit tests and the drag check, and a constant
signature kills module 47 because the outline rows never appear.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-20 22:29:40 +02:00