Trim needless comments and fix the picker width
Remove comments that only restate the code, keeping the section banners and the notes that carry a reason, a measured fact or an external contract. ButtonOptions.width claimed to be a fixed width but only set minWidth, so the Graph and Texture pickers grew with the name they showed. It is now what it promises, with a separate minWidth for the buttons that have two forms. Narrow buttons shrink their padding, otherwise a 24px icon button had 8px left for its glyph and truncate dropped it entirely. __gm.labelsDropped() guards that class: the overflow guard only saw text that was too wide, never a label truncated to nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f16801aef3
commit
4d84231b9f
115 files changed
+707
-1564
No files matched your search
+15
-60
@@ -2,18 +2,13 @@ import type { TextureAsset, TextureFrame } from './Asset';
|
||||
import type { PropertyBag } from './Properties';
|
||||
import { findAsset, type Project } from './Project';
|
||||
|
||||
/**
|
||||
* A stored image file name: `{sha256}.{png|jpg|webp|gif}`. Mirrors `TextureStore::isValidName`
|
||||
* on the server – the name is derived from the bytes there, so anything else in a document
|
||||
* was hand-written and must not be turned into a request.
|
||||
*/
|
||||
/** Mirrors `TextureStore::isValidName` on the server, where the name is derived from the bytes. */
|
||||
const FILE_PATTERN = /^[a-f0-9]{64}\.(png|jpg|webp|gif)$/;
|
||||
|
||||
export function isTextureFile(name: string): boolean {
|
||||
return FILE_PATTERN.test(name);
|
||||
}
|
||||
|
||||
/** A rectangle in image pixels: the part of a texture a frame cuts out. */
|
||||
export interface FrameRect {
|
||||
x: number;
|
||||
y: number;
|
||||
@@ -21,40 +16,29 @@ export interface FrameRect {
|
||||
h: number;
|
||||
}
|
||||
|
||||
/** Smallest frame that is still a picture rather than a slip of the mouse. */
|
||||
export const MIN_FRAME_SIZE = 1;
|
||||
|
||||
/**
|
||||
* A picture ready to be drawn: which file, whether to smooth it when scaled, and which part
|
||||
* of it. `rect` is null for the whole image – not a rectangle covering everything, because
|
||||
* the renderer can then skip the texture matrix entirely (Pixi fits an unmatrixed fill to the
|
||||
* shape by itself) and because "the whole picture" survives the image being re-uploaded at a
|
||||
* different size.
|
||||
* `rect` is null for the whole image rather than a rectangle covering it: the renderer can then
|
||||
* skip the texture matrix (Pixi fits an unmatrixed fill to the shape) and the meaning survives
|
||||
* the image being re-uploaded at a different size.
|
||||
*/
|
||||
export interface TextureRef {
|
||||
file: string;
|
||||
/** False for pixel art, where interpolation is the difference between crisp and mush. */
|
||||
smooth: boolean;
|
||||
/** Sub-rectangle in image pixels, or null for the whole image. */
|
||||
rect: FrameRect | null;
|
||||
/** Size of the whole image in pixels – the reference frame `rect` is expressed in. */
|
||||
sourceWidth: number;
|
||||
sourceHeight: number;
|
||||
}
|
||||
|
||||
/** A texture asset together with one of its frames. */
|
||||
export interface FrameRef {
|
||||
asset: TextureAsset;
|
||||
frame: TextureFrame;
|
||||
}
|
||||
|
||||
/**
|
||||
* The frame with that id, wherever in the project it lives.
|
||||
*
|
||||
* Frame ids come from the same generator as asset ids and are unique across the document,
|
||||
* which is what lets a sprite's `texture` property hold either kind: an asset id means the
|
||||
* whole picture, a frame id means one cut-out of it. One property, one picker, and every
|
||||
* sprite written before frames existed keeps meaning exactly what it meant.
|
||||
* Frame ids share the generator with asset ids and are unique across the document, which lets a
|
||||
* sprite's `texture` property hold either: an asset id means the whole picture, a frame id a cut-out.
|
||||
*/
|
||||
export function findFrame(project: Project, frameId: string): FrameRef | null {
|
||||
if (!frameId) return null;
|
||||
@@ -71,12 +55,6 @@ export function textureRefIds(asset: TextureAsset): string[] {
|
||||
return [asset.id, ...asset.frames.map((f) => f.id)];
|
||||
}
|
||||
|
||||
/**
|
||||
* The picture a bag of properties points at, or null when it points at nothing usable –
|
||||
* no reference, a dangling one (the texture or the frame was deleted), or one whose upload
|
||||
* never finished. Callers draw their plain colour in that case, which is what a sprite
|
||||
* looked like before textures existed.
|
||||
*/
|
||||
export function resolveTexture(project: Project, props: PropertyBag): TextureRef | null {
|
||||
const id = String(props.texture ?? '');
|
||||
if (!id) return null;
|
||||
@@ -98,12 +76,8 @@ function refFor(asset: TextureAsset, frame: TextureFrame | null): TextureRef | n
|
||||
}
|
||||
|
||||
/**
|
||||
* A rectangle rounded to whole pixels and forced inside the image.
|
||||
*
|
||||
* Frames are pixel coordinates into a bitmap, so a fractional one has no meaning – it would
|
||||
* sample half of a neighbouring pixel along every edge, which on a sprite sheet is the
|
||||
* classic seam of foreign colour. Dragging produces fractions constantly (the view is
|
||||
* zoomed), so this is applied on the way into the model rather than trusted from callers.
|
||||
* Rounds to whole pixels and forces the rectangle inside the image: a fractional frame samples
|
||||
* half of a neighbouring pixel along every edge, the classic sprite-sheet seam.
|
||||
*/
|
||||
export function normalizeFrameRect(rect: FrameRect, imageWidth: number, imageHeight: number): FrameRect {
|
||||
// An image of unknown size (upload still running) cannot constrain anything; only round.
|
||||
@@ -121,14 +95,13 @@ function clampInt(value: number, min: number, max: number): number {
|
||||
return Math.max(min, Math.min(max, n));
|
||||
}
|
||||
|
||||
/** How a sheet is cut into a regular grid – the shape of nearly every sprite sheet. */
|
||||
export interface SliceOptions {
|
||||
columns: number;
|
||||
rows: number;
|
||||
/** Pixels skipped at the left/top edge of the sheet before the first cell. */
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
/** Pixels between neighbouring cells (sheets exported with padding). */
|
||||
/** Pixels between neighbouring cells. */
|
||||
spacingX: number;
|
||||
spacingY: number;
|
||||
}
|
||||
@@ -143,20 +116,13 @@ export const DEFAULT_SLICE: SliceOptions = {
|
||||
};
|
||||
|
||||
/**
|
||||
* The rectangles a grid cuts a sheet into, in reading order (left to right, top to bottom) –
|
||||
* which is the order sprite sheets are authored in and therefore the order an animation will
|
||||
* want them.
|
||||
*
|
||||
* Cell size is derived from the image rather than asked for: everyone knows how many tiles
|
||||
* their sheet has, almost nobody knows the pixel size off-hand, and deriving it cannot
|
||||
* disagree with the picture. A cell that would reach past the image is dropped instead of
|
||||
* clipped – a clipped one looks like a frame and is not one.
|
||||
* The rectangles a grid cuts a sheet into, in reading order. Cell size is derived from the image;
|
||||
* a cell that would reach past the edge is dropped rather than clipped.
|
||||
*/
|
||||
export function sliceFrames(imageWidth: number, imageHeight: number, options: SliceOptions): FrameRect[] {
|
||||
const columns = Math.floor(options.columns);
|
||||
const rows = Math.floor(options.rows);
|
||||
// NaN has to be rejected explicitly: every comparison against it is false, so a NaN column
|
||||
// count would pass `columns < 1` and only produce nothing by accident of the loop condition.
|
||||
// NaN passes `columns < 1` (every comparison against it is false), so reject it explicitly.
|
||||
if (!Number.isFinite(columns) || !Number.isFinite(rows)) return [];
|
||||
if (!Object.values(options).every((value) => Number.isFinite(value))) return [];
|
||||
if (columns < 1 || rows < 1 || imageWidth <= 0 || imageHeight <= 0) return [];
|
||||
@@ -175,7 +141,6 @@ export function sliceFrames(imageWidth: number, imageHeight: number, options: Sl
|
||||
return rects;
|
||||
}
|
||||
|
||||
/** The rectangle part of a frame, and the way to write one back – the only glue between the two. */
|
||||
export function frameRect(frame: TextureFrame): FrameRect {
|
||||
return { x: frame.x, y: frame.y, w: frame.w, h: frame.h };
|
||||
}
|
||||
@@ -187,14 +152,7 @@ export function setFrameRect(frame: TextureFrame, rect: FrameRect): void {
|
||||
frame.h = rect.h;
|
||||
}
|
||||
|
||||
/**
|
||||
* A frame name that is free within its texture: `base`, else "base 2", "base 3" … – the same
|
||||
* shape `nextAssetName` produces, because a name the user reads should not be numbered by two
|
||||
* conventions in one document.
|
||||
*
|
||||
* Names matter more here than they look: they are what the sprite's picker shows, so two
|
||||
* frames called "idle" would be one choice repeated rather than two.
|
||||
*/
|
||||
/** A name free within the texture: `base`, else "base 2", "base 3" … – the shape `nextAssetName` produces. */
|
||||
export function uniqueFrameName(asset: TextureAsset, base: string, ignoreId = ''): string {
|
||||
const taken = takenFrameNames(asset, ignoreId);
|
||||
const stem = base || 'Frame';
|
||||
@@ -202,11 +160,8 @@ export function uniqueFrameName(asset: TextureAsset, base: string, ignoreId = ''
|
||||
}
|
||||
|
||||
/**
|
||||
* `count` free names numbered from 1 – what slicing a sheet wants, where the number is the
|
||||
* cell's position and "Frame 1" next to "Frame 2" is the whole point.
|
||||
*
|
||||
* Numbers already taken are skipped rather than suffixed again: re-slicing a sheet that keeps
|
||||
* its old frames must not produce "Frame 2 1".
|
||||
* `count` free names numbered from 1. Taken numbers are skipped rather than suffixed again, so
|
||||
* re-slicing a sheet that keeps its old frames cannot produce "Frame 2 1".
|
||||
*/
|
||||
export function nextFrameNames(asset: TextureAsset, count: number, base = 'Frame'): string[] {
|
||||
const taken = takenFrameNames(asset);
|
||||
|
||||
Reference in new issue
Block a user