Two compilers that walk a graph the same way walk it through one emitter (#85)
Refactoring round two: the particle and shader compilers share emitGraph.ts (cycle detection, duplicate names, emit order) and keep only their rules. FontLibrary and TextureLibrary sit on FileLibrary (base URL, loading/failed bookkeeping, listeners); SoundLibrary keeps its own contract. The three Create*Command share ImportFileCommand, the three renames share RenameCommand - class names stay. bandBounds() in framing.ts and portNameField() in portSection.ts replace in-file clones, and three comments that told history now state the rule. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bf0be66c9d
commit
d3563897d8
16 files changed
+376
-379
No files matched your search
@@ -110,22 +110,24 @@ export function buildTextureAsset(name: string, image: TextureImage, id = newId(
|
||||
}
|
||||
|
||||
/**
|
||||
* The image is uploaded before the command runs; this only records the result, so undo and redo
|
||||
* The file is uploaded before the command runs; this only records the result, so undo and redo
|
||||
* never touch the network.
|
||||
*/
|
||||
export class CreateTextureCommand implements Command {
|
||||
abstract class ImportFileCommand<A extends Asset & { file: string }> implements Command {
|
||||
readonly label: string;
|
||||
readonly asset: TextureAsset;
|
||||
|
||||
constructor(name: string, image: TextureImage) {
|
||||
this.asset = buildTextureAsset(name, image);
|
||||
this.label = `Import texture "${name}"`;
|
||||
constructor(
|
||||
readonly asset: A,
|
||||
private readonly fileNoun: string,
|
||||
private readonly isStored: (file: string) => boolean,
|
||||
) {
|
||||
this.label = `Import ${asset.type} "${asset.name}"`;
|
||||
}
|
||||
|
||||
execute(project: Project): void {
|
||||
// The name is the hash the server derived from the bytes; anything else was never uploaded.
|
||||
if (!isTextureFile(this.asset.file))
|
||||
throw new CommandError(`"${this.asset.file}" is not a stored image file`);
|
||||
if (!this.isStored(this.asset.file))
|
||||
throw new CommandError(`"${this.asset.file}" is not a stored ${this.fileNoun} file`);
|
||||
project.assets.push(this.asset);
|
||||
}
|
||||
|
||||
@@ -134,28 +136,19 @@ export class CreateTextureCommand implements Command {
|
||||
}
|
||||
}
|
||||
|
||||
export class CreateTextureCommand extends ImportFileCommand<TextureAsset> {
|
||||
constructor(name: string, image: TextureImage) {
|
||||
super(buildTextureAsset(name, image), 'image', isTextureFile);
|
||||
}
|
||||
}
|
||||
|
||||
export function buildFontAsset(name: string, file: string, id = newId()): FontAsset {
|
||||
return { id, name, props: defaultsFor('font'), type: 'font', file };
|
||||
}
|
||||
|
||||
/** The typeface is uploaded before the command runs, as an image is for `CreateTextureCommand`. */
|
||||
export class CreateFontCommand implements Command {
|
||||
readonly label: string;
|
||||
readonly asset: FontAsset;
|
||||
|
||||
export class CreateFontCommand extends ImportFileCommand<FontAsset> {
|
||||
constructor(name: string, file: string) {
|
||||
this.asset = buildFontAsset(name, file);
|
||||
this.label = `Import font "${name}"`;
|
||||
}
|
||||
|
||||
execute(project: Project): void {
|
||||
if (!isFontFile(this.asset.file))
|
||||
throw new CommandError(`"${this.asset.file}" is not a stored font file`);
|
||||
project.assets.push(this.asset);
|
||||
}
|
||||
|
||||
undo(project: Project): void {
|
||||
project.assets = removeById(project.assets, this.asset.id);
|
||||
super(buildFontAsset(name, file), 'font', isFontFile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,24 +156,9 @@ export function buildSoundAsset(name: string, file: string, id = newId()): Sound
|
||||
return { id, name, props: defaultsFor('sound'), type: 'sound', file };
|
||||
}
|
||||
|
||||
/** The sound is uploaded before the command runs, as a typeface is for `CreateFontCommand`. */
|
||||
export class CreateSoundCommand implements Command {
|
||||
readonly label: string;
|
||||
readonly asset: SoundAsset;
|
||||
|
||||
export class CreateSoundCommand extends ImportFileCommand<SoundAsset> {
|
||||
constructor(name: string, file: string) {
|
||||
this.asset = buildSoundAsset(name, file);
|
||||
this.label = `Import sound "${name}"`;
|
||||
}
|
||||
|
||||
execute(project: Project): void {
|
||||
if (!isSoundFile(this.asset.file))
|
||||
throw new CommandError(`"${this.asset.file}" is not a stored sound file`);
|
||||
project.assets.push(this.asset);
|
||||
}
|
||||
|
||||
undo(project: Project): void {
|
||||
project.assets = removeById(project.assets, this.asset.id);
|
||||
super(buildSoundAsset(name, file), 'sound', isSoundFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user