A frame that keeps its corners can be stretched to any size (#41)
game-maker / suite (push) Successful in 6m21s
game-maker / suite (push) Successful in 6m21s
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>
This commit is contained in:
1 parent
b9f1a9d319
commit
be8f2331de
17 files changed
+1091
-31
No files matched your search
+22
-8
@@ -227,6 +227,15 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
const holder = findObjectHolder(state.project, holderId);
|
||||
return holder ? resolveObjects(state.project, holder) : [];
|
||||
};
|
||||
/** The colour actually on screen at a point, as `#rrggbb` – what was drawn, not what the model says. */
|
||||
const pixelAt = (x: number, y: number): string => {
|
||||
const { pixels } = app.renderer.extract.pixels({
|
||||
target: app.stage,
|
||||
frame: new Rectangle(x, y, 1, 1),
|
||||
});
|
||||
const hex = (value: number | undefined) => (value ?? 0).toString(16).padStart(2, '0');
|
||||
return `#${hex(pixels[0])}${hex(pixels[1])}${hex(pixels[2])}`;
|
||||
};
|
||||
(window as unknown as { __gm: unknown }).__gm = {
|
||||
state,
|
||||
/** The Pixi application, for probes that need the renderer or the stage. */
|
||||
@@ -490,14 +499,19 @@ export function installDebugHook(app: Application, state: EditorState, editor: E
|
||||
hierarchyRowColor: (id: string) => hierarchy.rowColor(id),
|
||||
hierarchyExpandAll: () => hierarchy.expandAll(),
|
||||
hierarchyCollapseAll: () => hierarchy.collapseAll(),
|
||||
/** The colour actually on screen at a point, as `#rrggbb` – what was drawn, not what the model says. */
|
||||
pixelAt: (x: number, y: number) => {
|
||||
const { pixels } = app.renderer.extract.pixels({
|
||||
target: app.stage,
|
||||
frame: new Rectangle(x, y, 1, 1),
|
||||
});
|
||||
const hex = (value: number | undefined) => (value ?? 0).toString(16).padStart(2, '0');
|
||||
return `#${hex(pixels[0])}${hex(pixels[1])}${hex(pixels[2])}`;
|
||||
pixelAt,
|
||||
/**
|
||||
* A horizontal run of pixels in one read. A check that has to tell a repeated pattern from a
|
||||
* stretched one needs dozens of samples, and dozens of round trips would cost more than the
|
||||
* step it belongs to.
|
||||
*/
|
||||
pixelRow: (x: number, y: number, count: number) => {
|
||||
const width = Math.max(1, Math.min(1024, Math.round(count)));
|
||||
const row: string[] = [];
|
||||
// One extract per pixel, not one strip: a wider frame comes back resampled, and a row that
|
||||
// blends its neighbours cannot tell a hard border from a soft one – which is what it is for.
|
||||
for (let i = 0; i < width; i++) row.push(pixelAt(x + i, y));
|
||||
return row;
|
||||
},
|
||||
/** The filter and wrap the GPU sampler actually got, next to the model's – only the driver knows the former. */
|
||||
textureFilter: (file: string, rect: FrameRect | null = null) => {
|
||||
|
||||
Reference in new issue
Block a user