From 0343ded6c86f4e7a1ecd3aca57041ccc057607f3 Mon Sep 17 00:00:00 2001 From: sfja Date: Wed, 13 May 2026 03:17:16 +0200 Subject: [PATCH] add component defs --- editor/src/editor/Board.ts | 87 +++++++++++++++++++---------- editor/src/editor/Cx.ts | 18 +++--- editor/src/editor/Editor.ts | 2 +- editor/src/editor/states/Normal.ts | 3 +- editor/src/editor/states/Placing.ts | 14 +++-- 5 files changed, 77 insertions(+), 47 deletions(-) diff --git a/editor/src/editor/Board.ts b/editor/src/editor/Board.ts index 5b77c2f..36db2df 100644 --- a/editor/src/editor/Board.ts +++ b/editor/src/editor/Board.ts @@ -1,59 +1,88 @@ -import { rectsCollide, type V2 } from "./V2"; +import { rectsCollide, V2 } from "./V2"; export class Board { private components: Component[] = []; - canPlaceComponent(pos: V2, size: V2): boolean { + canPlaceComponent(def: ComponentDef, pos: V2): boolean { return !this.components.some((comp) => - rectsCollide(comp.pos, comp.size, pos, size), + rectsCollide(comp.pos, comp.def.size, pos, def.size), ); } - placeComponent(pos: V2, size: V2, label: string) { - this.components.push({ pos, size, label }); + placeComponent(def: ComponentDef, pos: V2) { + this.components.push({ def, pos }); } - render( - canvas: HTMLCanvasElement, - c: CanvasRenderingContext2D, - offset: V2, - gridSize: Readonly, - ) { + render(_canvas: HTMLCanvasElement, c: CanvasRenderingContext2D, offset: V2) { for (const comp of this.components) { const { + def: { + size: { x: w, y: h }, + label, + }, pos: { x, y }, - size: { x: w, y: h }, } = comp; - c.fillStyle = `#0088cc`; - c.fillRect( - x * gridSize.x + offset.x, - y * gridSize.y + offset.y, - w * gridSize.x, - h * gridSize.y, - ); + c.fillStyle = `#6abbde`; + c.fillRect(x + offset.x, y + offset.y, w, h); c.strokeStyle = `#333333`; c.lineWidth = 2; - c.strokeRect( - x * gridSize.x + offset.x, - y * gridSize.y + offset.y, - w * gridSize.x, - h * gridSize.y, - ); + c.strokeRect(x + offset.x, y + offset.y, w, h); c.fillStyle = `#333333`; c.font = "bold 16px monospace"; - const textMetrix = c.measureText(comp.label); + const textMetrix = c.measureText(label); c.fillText( - comp.label, - x * gridSize.x + offset.x + (w * gridSize.x) / 2 - textMetrix.width / 2, - y * gridSize.y + offset.y + 13 + (h * gridSize.y) / 2 - 16 / 2, + label, + x + offset.x + w / 2 - textMetrix.width / 2, + y + offset.y + 13 + h / 2 - 16 / 2, ); } } } +export class ComponentRepo { + private defs = new Map(); + + static withDefaults(): ComponentRepo { + const repo = new ComponentRepo(); + + repo.add("and", { + label: "and", + size: V2(80, 40), + inputs: [null, null], + outputs: [null], + }); + repo.add("or", { + label: "or", + size: V2(80, 40), + inputs: [null, null], + outputs: [null], + }); + + return repo; + } + + add(ident: string, def: ComponentDef) { + this.defs.set(ident, def); + } + + get(ident: string): ComponentDef { + const def = this.defs.get(ident); + if (!def) { + throw new Error("should be defined"); + } + return def; + } +} + type Component = { + def: ComponentDef; pos: V2; +}; + +export type ComponentDef = { size: V2; label: string; + inputs: (string | null)[]; + outputs: (string | null)[]; }; diff --git a/editor/src/editor/Cx.ts b/editor/src/editor/Cx.ts index 44f9945..d9509c3 100644 --- a/editor/src/editor/Cx.ts +++ b/editor/src/editor/Cx.ts @@ -1,4 +1,4 @@ -import { Board } from "./Board"; +import { Board, ComponentRepo } from "./Board"; import type { State } from "./State"; import { Normal } from "./states/Normal"; import { V2 } from "./V2"; @@ -9,12 +9,11 @@ export class Cx { private state = new Normal(this) as State; private updateActions: (() => void)[] = []; - public gridSize = Object.freeze(V2(20, 20)); - private selectionRect: SelectionRect | null = null; private componentPlacer: ComponentPlacer | null = null; public board = new Board(); + public componentRepo = ComponentRepo.withDefaults(); render(canvas: HTMLCanvasElement) { const c = canvas.getContext("2d")!; @@ -23,8 +22,8 @@ export class Cx { c.fillStyle = "#666"; c.fillRect(0, 0, canvas.width, canvas.height); - const gridSize = this.gridSize; const dotSize = { x: 2, y: 2 }; + const gridSize = V2(20, 20); c.fillStyle = "#111"; for (let y = 0; y < canvas.width / gridSize.x + 1; ++y) { @@ -38,7 +37,7 @@ export class Cx { } } - this.board.render(canvas, c, this.offset, gridSize); + this.board.render(canvas, c, this.offset); if (this.selectionRect) { const { @@ -61,7 +60,7 @@ export class Cx { c.strokeStyle = `#ffffff`; c.lineWidth = 2; - c.strokeRect(x - (x % gridSize.x), y - (y % gridSize.y), w, h); + c.strokeRect(x, y, w, h); } } @@ -168,10 +167,7 @@ export class Cx { canvasPosToBoard(pos: V2): V2 { const absX = pos.x - this.offset.x; const absY = pos.y - this.offset.y; - return V2( - (absX - (absX % this.gridSize.x)) / this.gridSize.x, - (absY - (absY % this.gridSize.y)) / this.gridSize.y, - ); + return V2(absX, absY); } } @@ -185,4 +181,4 @@ export type ComponentPlacer = { size: V2; }; -export type Tool = "select" | "pan" | "and"; +export type Tool = "select" | "pan" | "and" | "or"; diff --git a/editor/src/editor/Editor.ts b/editor/src/editor/Editor.ts index b88f8cf..a9b239a 100644 --- a/editor/src/editor/Editor.ts +++ b/editor/src/editor/Editor.ts @@ -41,7 +41,7 @@ export class Editor { } tools(): Tool[] { - return ["select", "pan", "and"]; + return ["select", "pan", "and", "or"]; } addUpdateAction(action: () => void): object { diff --git a/editor/src/editor/states/Normal.ts b/editor/src/editor/states/Normal.ts index ffcbc3b..d9fba23 100644 --- a/editor/src/editor/states/Normal.ts +++ b/editor/src/editor/states/Normal.ts @@ -14,7 +14,8 @@ export class Normal implements State { this.cx.transitionTo(new Panning(this.cx)); break; case "and": - this.cx.transitionTo(new Placing(this.cx, "and")); + case "or": + this.cx.transitionTo(new Placing(this.cx, tool)); } } diff --git a/editor/src/editor/states/Placing.ts b/editor/src/editor/states/Placing.ts index 8bb4b31..41763f6 100644 --- a/editor/src/editor/states/Placing.ts +++ b/editor/src/editor/states/Placing.ts @@ -2,15 +2,20 @@ import { type Cx, type Tool } from "../Cx"; import { V2 } from "../V2"; import type { State } from "../State"; import { Normal } from "./Normal"; +import type { ComponentDef } from "../Board"; export class Placing implements State { + private compDef: ComponentDef; + constructor( private cx: Cx, private tool: Tool, - ) {} + ) { + this.compDef = this.cx.componentRepo.get(this.tool); + } enterState(): void { - this.cx.addComponentPlacer(V2(0, 0), V2(20 * 4, 20 * 2)); + this.cx.addComponentPlacer(V2(0, 0), this.compDef.size); } leaveState(): void { @@ -19,9 +24,8 @@ export class Placing implements State { onMouseDown(pos: V2): void { const boardPos = this.cx.canvasPosToBoard(pos); - if (this.cx.board.canPlaceComponent(boardPos, V2(4, 2))) { - console.log("place"); - this.cx.board.placeComponent(boardPos, V2(4, 2), "AND"); + if (this.cx.board.canPlaceComponent(this.compDef, boardPos)) { + this.cx.board.placeComponent(this.compDef, boardPos); this.cx.transitionTo(new Normal(this.cx)); } }