A frame's collision outline can be generated from its pixels (#161)
Drawing an outline by hand for every frame of a sheet is slow, and the outline mostly follows the opaque part of the image anyway. The texture tab now traces it: one click, a corner budget, and every Frame collision collider and tile using that frame blocks with the result. - "Generate collision..." sits in the texture tab header and is off while the texture has no frames. - The dialog has Max points (default 8, 3 to 64), Solid from ... % alpha (default 50) and All frames of the texture. A live line says what Generate will write, and Generate stays off when nothing is solid enough. - traceCollision walks each island of solid pixels along the pixel edges, then drops the least important corners until at most N are left and no stair step remains. A slope becomes one straight edge. - An outline never crosses itself. Where the image would force that, the island's convex hull is taken instead. - Crumbs under 1 % of the frame area are dropped unless they are the only island. Holes are not reproduced. - Existing outlines are replaced. One frame or all frames is a single undo step. - TextureLibrary hands out the sheet's RGBA data, and a pure function cuts a frame's region from it. Findings: - The wedge in the e2e fixture gets 4 corners, not 3. Its tip is one pixel wide in the image, and that real edge stays while corners are still free. Tests: - ./check.sh: 131 files, 1941 tests, CHECK EXIT 0 (was 130 / 1931). - 10 new unit tests in tests/core/trace-collision.test.ts, including 150 random shapes that must all be valid and within the budget. - GM_STEPS=42, freshly seeded: ALL PASSED, 20 checks (was 16). - Three counter-probes bite: no hull fallback, no stair threshold, dialog ignoring the alpha threshold. - Check sum 1202 in README is computed (1198 + 4), CI measures it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f4ec23d3d3
commit
2d79230b55
10 files changed
+705
-5
No files matched your search
@@ -150,6 +150,33 @@ export class SetFrameCollisionCommand implements Command {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the outlines of several frames at once – what "Generate collision…" writes (#161), one
|
||||
* undo step whether it covered one frame or the whole sheet.
|
||||
*/
|
||||
export class SetFramesCollisionCommand implements Command {
|
||||
readonly label = 'Generate collision';
|
||||
private previous = new Map<string, Vec2[][]>();
|
||||
|
||||
constructor(
|
||||
private readonly assetId: string,
|
||||
private readonly outlines: ReadonlyMap<string, readonly (readonly Vec2[])[]>,
|
||||
) {}
|
||||
|
||||
execute(project: Project): void {
|
||||
const asset = requireTexture(project, this.assetId);
|
||||
if (this.outlines.size === 0) throw new CommandError('No frame to generate a collision outline for');
|
||||
const frames = [...this.outlines.keys()].map((id) => requireFrame(asset, id));
|
||||
this.previous = new Map(frames.map((frame) => [frame.id, frameCollision(frame)]));
|
||||
for (const frame of frames) setFrameCollision(frame, this.outlines.get(frame.id)!);
|
||||
}
|
||||
|
||||
undo(project: Project): void {
|
||||
const asset = requireTexture(project, this.assetId);
|
||||
for (const [id, outlines] of this.previous) setFrameCollision(requireFrame(asset, id), outlines);
|
||||
}
|
||||
}
|
||||
|
||||
export class RenameFrameCommand implements Command {
|
||||
readonly label = 'Rename frame';
|
||||
private previous = '';
|
||||
|
||||
Reference in new issue
Block a user