A shader ran only inside the rectangle of what wears it: the quad
has the size of the sprite, and outside of it no fragment runs. An
outline or a glow built with Sample texture was cut off at the
border. The issue still spoke of Filter.padding, but since #77 the
shader is no filter any more, the limit is the geometry.
- The Output node has a number Padding next to Edge: so many pixels
of the picture per side the wearer draws beyond it. 0 is the
default and draws as before, 256 is the most.
- The quad grows and its UVs grow with it, so the picture keeps its
place and size. In the margin the UVs run on below 0 and above 1.
- Transparent is a fourth Edge mode: outside of the picture there
is nothing. It is cut in the fragment (texelAt), so it needs no
copy of the picture with an address mode of its own.
- Edge holds everywhere alike, also for Texture color, Texture
alpha and unconnected inputs.
- Padding above 0 with Edge on Clamp puts the hint "Padding with
Clamp smears the border" on the Output. The shader still
compiles. A chosen Clamp and the default cannot be told apart.
- All four wearers draw the margin. A sprite counts in pixels of
its frame, a text in its units, a particle source grows every
particle about its centre.
- A nine-slice sprite gets a ring around its nine fields, as wide
as so many border pixels. The fields themselves stay.
- Without a picture a pixel is one unit of the wearer's own size,
or of the particle size.
- Particles now take the Edge mode of their shader. It had no
effect on them before.
- An object wearing a padded shader is never culled, or its margin
would vanish at the edge of the screen.
- The preview in the shader tab shows the margin and leaves room
for it.
- Picking, the selection frame, the size and the collider stay with
the rectangle without the margin.
Tests:
- Gate on melektaus-atx right before this commit: 216 files,
3336 tests (3319 + 17), exit 0.
- e2e 28,30,31,37,41 on melektaus-atx: 178 checks, all pass.
e2e 1-37,47,48,58 ran before the last change (no culling with
padding): 1281 checks, all pass.
- 17 new unit tests: the compiler (padding, Transparent, the hint)
and the geometry (quad, nine-slice ring).
- Seven new e2e checks: four at the sprite in module 28, one each
for text, particles and nine-slice.
- 17 unit counter-probes all fail their tests. Three browser
counter-probes fail their checks (5, 3 and 1 of them).
- Three screenshots looked at: the sprite with Clamp and margin in
the scene, the preview in the shader tab, the text.
- The e2e sum of 1687 is counted (1680 + 7), the full suite and
the smokes did not run.
- Not seen: the hint at the node in a picture, particles and
nine-slice in a picture, the game view while playing, sprites
without a picture, vector and pixel art pictures.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A sprite sorted by Y stood on the bottom of its box. With a shadow or
empty rows under the feet that is too low, so a figure slipped in
front of a tree it should stand behind. The line belongs to the frame,
not the texture, because one texture can hold several sprite sheets.
- New field `sortLine` on a frame: whole image pixels up from the
frame's bottom edge, 0 to the frame height. 0 is stored as nothing,
so old projects stay the same. Shrinking a frame keeps the line
inside it.
- `feetOf` in drawOrder.ts lifts a sprite by the same share of its
box. Scene view, game view, play and picking all use it. In an
animation the line of the frame shown right now counts.
- The texture tab draws the selected frame's line dashed in yellow
with a knob left of the frame. At 0 the line shares the bottom edge
with the nine-slice border and a resize handle, so the knob is
tested first. A line inside the frame can be dragged directly,
tested after the nine-slice lines.
- The inspector has a new "Sorting" section with a "Sort line"
field. Dragging or typing is one undo step each.
Limit: frames of one walk cycle need the same value, set by hand.
Otherwise the figure changes its place in the order while walking.
Tests:
- ./check.sh: 160 files, 2356 tests, CHECK EXIT 0 (before 2350).
- 6 new unit tests in tests/viewport/sort-by-y.test.ts.
- e2e 37 and 39-43: ALL PASSED, 162 checks. New check 37b-ii drags
the knob up 6 px and undoes it, plus the inspector row.
- Counter checks: if feetOf ignores the line, exactly the 3 order
tests fail. If the knob cannot be grabbed, 37b-ii fails.
- The e2e sum in the README is 1380, counted, not yet seen in CI.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The fill of a sliced frame defaulted to `Stretch`. It is `Repeat` now, and the Fill dropdown lists
`Repeat` first.
The saved document keeps writing only the fill that differs from the default, so the key it
carries is now `stretch`, and a frame without it repeats. A frame saved before with the key
absent and meaning stretch would therefore tile; none of the stored projects has one - both sliced
frames on the server carry `fill: "repeat"` explicitly, so they draw as before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A sliced frame drew its borders one source pixel to one sprite unit. Pixel-art sheets are small:
a 16px corner on a 400-unit button is a thin line, and the only way around it was a larger image.
The texture now carries `Nine-slice scale` (default 1), next to `Filter`. It sets how many sprite
units one border pixel takes, for every sliced frame of that texture. The UVs stay as they are -
a corner shows the same pixels, only larger or smaller. When the sprite is too small for both
borders they are still scaled down together, now starting from the scaled size. With `Repeat`
the tile size scales too, otherwise a doubled border would sit beside a pattern of the old size.
The value is a schema row, so inspector field, saving and undo come without their own code.
Anything that is not a size above 0 reads as 1, and documents without it draw as before.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
TextureFrame carries optional `border` (left/right/top/bottom) and
`fill` (stretch | repeat); absent keys mean "not sliced", so no format
bump and undo restores the plain frame instead of four zeroes.
The sliced sprite builds its own geometry instead of the shared
UNIT_QUAD: a 4x4 grid, and for `repeat` one quad per tile carrying the
frame cell's real UVs -- tiling in the geometry, not in the sampler, so
a frame out of a sheet cannot pull its neighbour in. Above 64 tiles per
axis it stretches, to bound the buffer.
A sprite narrower than left+right scales both borders down together
until the middle is exactly 0, so the corners meet at a seam and never
overlap.
The texture editor gets four draggable border lines plus the same four
numbers in the inspector. Hit order is resize handles, then the lines,
then the frame body -- handleAt reports 'move' anywhere inside a frame,
so lines placed after it were unreachable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>