add component defs
This commit is contained in:
parent
51934b7cb9
commit
0343ded6c8
@ -1,59 +1,88 @@
|
|||||||
import { rectsCollide, type V2 } from "./V2";
|
import { rectsCollide, V2 } from "./V2";
|
||||||
|
|
||||||
export class Board {
|
export class Board {
|
||||||
private components: Component[] = [];
|
private components: Component[] = [];
|
||||||
|
|
||||||
canPlaceComponent(pos: V2, size: V2): boolean {
|
canPlaceComponent(def: ComponentDef, pos: V2): boolean {
|
||||||
return !this.components.some((comp) =>
|
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) {
|
placeComponent(def: ComponentDef, pos: V2) {
|
||||||
this.components.push({ pos, size, label });
|
this.components.push({ def, pos });
|
||||||
}
|
}
|
||||||
|
|
||||||
render(
|
render(_canvas: HTMLCanvasElement, c: CanvasRenderingContext2D, offset: V2) {
|
||||||
canvas: HTMLCanvasElement,
|
|
||||||
c: CanvasRenderingContext2D,
|
|
||||||
offset: V2,
|
|
||||||
gridSize: Readonly<V2>,
|
|
||||||
) {
|
|
||||||
for (const comp of this.components) {
|
for (const comp of this.components) {
|
||||||
const {
|
const {
|
||||||
|
def: {
|
||||||
|
size: { x: w, y: h },
|
||||||
|
label,
|
||||||
|
},
|
||||||
pos: { x, y },
|
pos: { x, y },
|
||||||
size: { x: w, y: h },
|
|
||||||
} = comp;
|
} = comp;
|
||||||
|
|
||||||
c.fillStyle = `#0088cc`;
|
c.fillStyle = `#6abbde`;
|
||||||
c.fillRect(
|
c.fillRect(x + offset.x, y + offset.y, w, h);
|
||||||
x * gridSize.x + offset.x,
|
|
||||||
y * gridSize.y + offset.y,
|
|
||||||
w * gridSize.x,
|
|
||||||
h * gridSize.y,
|
|
||||||
);
|
|
||||||
c.strokeStyle = `#333333`;
|
c.strokeStyle = `#333333`;
|
||||||
c.lineWidth = 2;
|
c.lineWidth = 2;
|
||||||
c.strokeRect(
|
c.strokeRect(x + offset.x, y + offset.y, w, h);
|
||||||
x * gridSize.x + offset.x,
|
|
||||||
y * gridSize.y + offset.y,
|
|
||||||
w * gridSize.x,
|
|
||||||
h * gridSize.y,
|
|
||||||
);
|
|
||||||
c.fillStyle = `#333333`;
|
c.fillStyle = `#333333`;
|
||||||
c.font = "bold 16px monospace";
|
c.font = "bold 16px monospace";
|
||||||
const textMetrix = c.measureText(comp.label);
|
const textMetrix = c.measureText(label);
|
||||||
c.fillText(
|
c.fillText(
|
||||||
comp.label,
|
label,
|
||||||
x * gridSize.x + offset.x + (w * gridSize.x) / 2 - textMetrix.width / 2,
|
x + offset.x + w / 2 - textMetrix.width / 2,
|
||||||
y * gridSize.y + offset.y + 13 + (h * gridSize.y) / 2 - 16 / 2,
|
y + offset.y + 13 + h / 2 - 16 / 2,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ComponentRepo {
|
||||||
|
private defs = new Map<string, ComponentDef>();
|
||||||
|
|
||||||
|
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 = {
|
type Component = {
|
||||||
|
def: ComponentDef;
|
||||||
pos: V2;
|
pos: V2;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ComponentDef = {
|
||||||
size: V2;
|
size: V2;
|
||||||
label: string;
|
label: string;
|
||||||
|
inputs: (string | null)[];
|
||||||
|
outputs: (string | null)[];
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Board } from "./Board";
|
import { Board, ComponentRepo } from "./Board";
|
||||||
import type { State } from "./State";
|
import type { State } from "./State";
|
||||||
import { Normal } from "./states/Normal";
|
import { Normal } from "./states/Normal";
|
||||||
import { V2 } from "./V2";
|
import { V2 } from "./V2";
|
||||||
@ -9,12 +9,11 @@ export class Cx {
|
|||||||
private state = new Normal(this) as State;
|
private state = new Normal(this) as State;
|
||||||
private updateActions: (() => void)[] = [];
|
private updateActions: (() => void)[] = [];
|
||||||
|
|
||||||
public gridSize = Object.freeze(V2(20, 20));
|
|
||||||
|
|
||||||
private selectionRect: SelectionRect | null = null;
|
private selectionRect: SelectionRect | null = null;
|
||||||
private componentPlacer: ComponentPlacer | null = null;
|
private componentPlacer: ComponentPlacer | null = null;
|
||||||
|
|
||||||
public board = new Board();
|
public board = new Board();
|
||||||
|
public componentRepo = ComponentRepo.withDefaults();
|
||||||
|
|
||||||
render(canvas: HTMLCanvasElement) {
|
render(canvas: HTMLCanvasElement) {
|
||||||
const c = canvas.getContext("2d")!;
|
const c = canvas.getContext("2d")!;
|
||||||
@ -23,8 +22,8 @@ export class Cx {
|
|||||||
c.fillStyle = "#666";
|
c.fillStyle = "#666";
|
||||||
c.fillRect(0, 0, canvas.width, canvas.height);
|
c.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
const gridSize = this.gridSize;
|
|
||||||
const dotSize = { x: 2, y: 2 };
|
const dotSize = { x: 2, y: 2 };
|
||||||
|
const gridSize = V2(20, 20);
|
||||||
|
|
||||||
c.fillStyle = "#111";
|
c.fillStyle = "#111";
|
||||||
for (let y = 0; y < canvas.width / gridSize.x + 1; ++y) {
|
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) {
|
if (this.selectionRect) {
|
||||||
const {
|
const {
|
||||||
@ -61,7 +60,7 @@ export class Cx {
|
|||||||
|
|
||||||
c.strokeStyle = `#ffffff`;
|
c.strokeStyle = `#ffffff`;
|
||||||
c.lineWidth = 2;
|
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 {
|
canvasPosToBoard(pos: V2): V2 {
|
||||||
const absX = pos.x - this.offset.x;
|
const absX = pos.x - this.offset.x;
|
||||||
const absY = pos.y - this.offset.y;
|
const absY = pos.y - this.offset.y;
|
||||||
return V2(
|
return V2(absX, absY);
|
||||||
(absX - (absX % this.gridSize.x)) / this.gridSize.x,
|
|
||||||
(absY - (absY % this.gridSize.y)) / this.gridSize.y,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,4 +181,4 @@ export type ComponentPlacer = {
|
|||||||
size: V2;
|
size: V2;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Tool = "select" | "pan" | "and";
|
export type Tool = "select" | "pan" | "and" | "or";
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export class Editor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tools(): Tool[] {
|
tools(): Tool[] {
|
||||||
return ["select", "pan", "and"];
|
return ["select", "pan", "and", "or"];
|
||||||
}
|
}
|
||||||
|
|
||||||
addUpdateAction(action: () => void): object {
|
addUpdateAction(action: () => void): object {
|
||||||
|
|||||||
@ -14,7 +14,8 @@ export class Normal implements State {
|
|||||||
this.cx.transitionTo(new Panning(this.cx));
|
this.cx.transitionTo(new Panning(this.cx));
|
||||||
break;
|
break;
|
||||||
case "and":
|
case "and":
|
||||||
this.cx.transitionTo(new Placing(this.cx, "and"));
|
case "or":
|
||||||
|
this.cx.transitionTo(new Placing(this.cx, tool));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -2,15 +2,20 @@ import { type Cx, type Tool } from "../Cx";
|
|||||||
import { V2 } from "../V2";
|
import { V2 } from "../V2";
|
||||||
import type { State } from "../State";
|
import type { State } from "../State";
|
||||||
import { Normal } from "./Normal";
|
import { Normal } from "./Normal";
|
||||||
|
import type { ComponentDef } from "../Board";
|
||||||
|
|
||||||
export class Placing implements State {
|
export class Placing implements State {
|
||||||
|
private compDef: ComponentDef;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private cx: Cx,
|
private cx: Cx,
|
||||||
private tool: Tool,
|
private tool: Tool,
|
||||||
) {}
|
) {
|
||||||
|
this.compDef = this.cx.componentRepo.get(this.tool);
|
||||||
|
}
|
||||||
|
|
||||||
enterState(): void {
|
enterState(): void {
|
||||||
this.cx.addComponentPlacer(V2(0, 0), V2(20 * 4, 20 * 2));
|
this.cx.addComponentPlacer(V2(0, 0), this.compDef.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
leaveState(): void {
|
leaveState(): void {
|
||||||
@ -19,9 +24,8 @@ export class Placing implements State {
|
|||||||
|
|
||||||
onMouseDown(pos: V2): void {
|
onMouseDown(pos: V2): void {
|
||||||
const boardPos = this.cx.canvasPosToBoard(pos);
|
const boardPos = this.cx.canvasPosToBoard(pos);
|
||||||
if (this.cx.board.canPlaceComponent(boardPos, V2(4, 2))) {
|
if (this.cx.board.canPlaceComponent(this.compDef, boardPos)) {
|
||||||
console.log("place");
|
this.cx.board.placeComponent(this.compDef, boardPos);
|
||||||
this.cx.board.placeComponent(boardPos, V2(4, 2), "AND");
|
|
||||||
this.cx.transitionTo(new Normal(this.cx));
|
this.cx.transitionTo(new Normal(this.cx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user