From 91e8d7dc6ce6a9ca40c44141ef6ebca00400ceaa Mon Sep 17 00:00:00 2001 From: sfja Date: Mon, 19 May 2025 15:31:20 +0200 Subject: [PATCH] start rework coordinates --- src/geometry.ts | 19 +++++ src/simulator.ts | 178 +++++++++++++++++++++++++++++++++-------------- 2 files changed, 146 insertions(+), 51 deletions(-) diff --git a/src/geometry.ts b/src/geometry.ts index 0f28466..b1c6b38 100644 --- a/src/geometry.ts +++ b/src/geometry.ts @@ -46,6 +46,7 @@ export class V2 { export interface Geometry { pointInside(thisPos: V2, pointPos: V2): boolean; collidesWith(thisPos: V2, other: Geometry, otherPos: V2): boolean; + boundingRectSize(): V2; render(r: Renderer, pos: V2, color: string): void; } @@ -77,6 +78,10 @@ export class Rect implements Geometry { return other.collidesWith(otherPos, this, thisPos); } + boundingRectSize(): V2 { + return new V2(this.width, this.height); + } + render(r: Renderer, pos: V2, color: string): void { r.fillRect(pos.x, pos.y, this.width, this.height, color); } @@ -114,6 +119,10 @@ export class Circle implements Geometry { return other.collidesWith(otherPos, other, thisPos); } + boundingRectSize(): V2 { + return new V2(this.radius, this.radius); + } + render(r: Renderer, pos: V2, color: string): void { r.fillCirc(pos.x, pos.y, this.radius, color); } @@ -138,6 +147,16 @@ export class Shape implements Geometry { ); } + boundingRectSize(): V2 { + return this.innerShapes + .reduce((size, [pos, shape]) => { + const shapeSize = shape.boundingRectSize(); + size.x = Math.max(size.x, pos.x + shapeSize.x); + size.y = Math.max(size.y, pos.y + shapeSize.y); + return size; + }, new V2(0, 0)); + } + render(r: Renderer, pos: V2, color: string): void { for (const [shapePos, shape] of this.innerShapes) { shape.render(r, shapePos.add(pos), color); diff --git a/src/simulator.ts b/src/simulator.ts index 53c55ae..632203a 100644 --- a/src/simulator.ts +++ b/src/simulator.ts @@ -39,10 +39,15 @@ export class Simulator { } } +type Tool = { + component: Component; + offsetX: number; +}; + class Toolbar { - private tools: Component[] = [ - new SwitchComponent(), - new LedComponent(), + private tools: Tool[] = [ + { component: new SwitchComponent(), offsetX: 32 }, + { component: new LedComponent(), offsetX: 256 }, ]; private lastWidth = 0; @@ -50,6 +55,8 @@ class Toolbar { private hoveringComponentIdx?: number; + private scale = 64; + constructor( private mouse: Mouse, private tooltip: Tooltip, @@ -64,14 +71,16 @@ class Toolbar { return "bubble"; } - for (const [i, component] of this.tools.entries()) { + for (const [i, tool] of this.tools.entries()) { + const compWidth = tool.component.width * this.scale; + const compHeight = tool.component.height * this.scale; if ( - x >= i * 128 + 96 - 48 && + x >= tool.offsetX && y >= this.lastHeight - 100 - 48 && - x < i * 128 + 96 + component.width * 32 + 32 && - y < this.lastHeight - 100 + component.height * 32 + 32 + x < tool.offsetX + compWidth && + y < this.lastHeight - 100 + compHeight ) { - this.tooltip.select(this.tools[i]); + this.tooltip.select(this.tools[i].component); return "stop"; } } @@ -82,12 +91,14 @@ class Toolbar { hover(): "continue" | "break" { const { x, y } = this.mouse; - for (const [i, component] of this.tools.entries()) { + for (const [i, tool] of this.tools.entries()) { + const compWidth = tool.component.width * this.scale; + const compHeight = tool.component.height * this.scale; if ( - x >= i * 128 + 96 - 48 && - y >= this.lastHeight - 100 - 48 && - x < i * 128 + 96 + component.width * 32 + 32 && - y < this.lastHeight - 100 + component.height * 32 + 32 + x >= tool.offsetX && + y >= this.lastHeight - 100 && + x < tool.offsetX + compWidth && + y < this.lastHeight - 100 + compHeight ) { this.hoveringComponentIdx = i; document.body.style.cursor = "pointer"; @@ -103,25 +114,28 @@ class Toolbar { this.lastHeight = r.height; r.fillRect(0, r.height - 200, r.width, 200, "#aaa"); - for (const [i, component] of this.tools.entries()) { + + for (const [i, tool] of this.tools.entries()) { + const compWidth = tool.component.width * this.scale; + const compHeight = tool.component.height * this.scale; r.strokeRect( - i * 128 + 96 - 48, - r.height - 100 - 48, - 96, - 96, + tool.offsetX, + r.height - 100, + compWidth, + compHeight, "#777", 1, ); if (i === this.hoveringComponentIdx) { r.fillRect( - i * 128 + 96 - 48, - r.height - 100 - 48, - component.height * 32 + 64, - component.width * 32 + 64, + tool.offsetX, + r.height - 100, + compWidth, + compHeight, "#00000088", ); } - component.render(r, i * 128 + 96, r.height - 100); + tool.component.render(r, tool.offsetX, r.height - 100); } } } @@ -200,12 +214,16 @@ class Tooltip { } render(r: Renderer): void { - const x = Math.floor(this.mouse.x / 32); - const y = Math.floor(this.mouse.y / 32); if (this.selectedComponent) { + const x = Math.floor(this.mouse.x / 32) * 32; + const y = Math.floor(this.mouse.y / 32) * 32; if (this.shouldHover) { this.selectedComponent - .renderTransparent(r, x * 32 + 16, y * 32 + 16); + .renderTransparent( + r, + x - this.selectedComponent.offsetX * 32, + y - this.selectedComponent.height * 32, + ); } } else if (!this.isWiring) { const output = this.selectedOutputTerm; @@ -355,7 +373,11 @@ class Circuit { r.strokeLine(source.x, source.y, dest.x, dest.y, "black", 3); } for (const { component, x, y } of this.components) { - component.render(r, x * 32 + 16, y * 32 + 16); + component.render( + r, + x * 32 - component.offsetX * 32, + y * 32 - component.offsetY * 32, + ); } } } @@ -384,6 +406,8 @@ class ComponentWireTerm implements WireSource, WireDest { } interface Component { + get offsetX(): number; + get offsetY(): number; get width(): number; get height(): number; @@ -404,19 +428,45 @@ class SwitchComponent implements Component { private switchOn = false; - public width = 1; - public height = 1; - - private geometry = new Rect(2, 1); + private geometry = new Rect(1.5, 1); constructor() { - this.graphicOff.fillRect(64, 30, 16, 4, "black"); - this.graphicOff.fillCirc(48 + 32, 32, 8, "black"); - this.graphicOff.fillCirc(48, 32, 16, "black"); + this.graphicOff.strokeRect( + 0, + 0, + this.graphicOff.width, + this.graphicOff.height, + "#88000088", + 2, + ); + this.graphicOff.fillRect(48, 30, 16, 4, "black"); + this.graphicOff.fillCirc(64, 32, 8, "black"); + this.graphicOff.fillCirc(32, 32, 16, "black"); - this.graphicOn.fillRect(64, 30, 16, 4, "black"); - this.graphicOn.fillCirc(48 + 32, 32, 8, "black"); - this.graphicOn.fillCirc(48, 32, 16, "red"); + this.graphicOn.strokeRect( + 0, + 0, + this.graphicOn.width, + this.graphicOn.height, + "#88000088", + 2, + ); + this.graphicOn.fillRect(48, 30, 16, 4, "black"); + this.graphicOn.fillCirc(64, 32, 8, "black"); + this.graphicOn.fillCirc(32, 32, 16, "red"); + } + + get offsetX(): number { + return 0.5; + } + get offsetY(): number { + return 0.5; + } + get width(): number { + return this.geometry.width; + } + get height(): number { + return this.geometry.height; } clone(): Component { @@ -433,12 +483,12 @@ class SwitchComponent implements Component { render(r: Renderer, x: number, y: number): void { const graphic = this.switchOn ? this.graphicOn : this.graphicOff; - graphic.render(r, x - 48, y - 32); + graphic.render(r, x, y); } renderTransparent(r: Renderer, x: number, y: number): void { const graphic = this.switchOn ? this.graphicOn : this.graphicOff; - graphic.render(r, x - 48, y - 32, 0.5); + graphic.render(r, x, y, 0.5); } click(x: number, y: number): void { @@ -478,19 +528,45 @@ class LedComponent implements Component { private switchOn = false; - public width = 1; - public height = 1; - - private geometry = new Rect(2, 1); + private geometry = new Rect(1.5, 1); constructor() { - this.graphicOff.fillRect(16, 30, 16, 4, "black"); - this.graphicOff.fillCirc(16, 32, 8, "black"); - this.graphicOff.fillCirc(48, 32, 16, "black"); + this.graphicOff.strokeRect( + 0, + 0, + this.graphicOff.width, + this.graphicOff.height, + "#88000088", + 2, + ); + this.graphicOff.fillRect(32, 30, 16, 4, "black"); + this.graphicOff.fillCirc(32, 32, 8, "black"); + this.graphicOff.fillCirc(64, 32, 16, "black"); - this.graphicOn.fillRect(16, 30, 16, 4, "black"); - this.graphicOn.fillCirc(16, 32, 8, "black"); - this.graphicOn.fillCirc(48, 32, 16, "red"); + this.graphicOn.strokeRect( + 0, + 0, + this.graphicOn.width, + this.graphicOn.height, + "#88000088", + 2, + ); + this.graphicOn.fillRect(32, 30, 16, 4, "black"); + this.graphicOn.fillCirc(32, 32, 8, "black"); + this.graphicOn.fillCirc(64, 32, 16, "red"); + } + + get offsetX(): number { + return 1.5; + } + get offsetY(): number { + return 0.5; + } + get width(): number { + return this.geometry.width; + } + get height(): number { + return this.geometry.height; } clone(): Component { @@ -511,12 +587,12 @@ class LedComponent implements Component { render(r: Renderer, x: number, y: number): void { const graphic = this.switchOn ? this.graphicOn : this.graphicOff; - graphic.render(r, x - 48, y - 32); + graphic.render(r, x, y); } renderTransparent(r: Renderer, x: number, y: number): void { const graphic = this.switchOn ? this.graphicOn : this.graphicOff; - graphic.render(r, x - 48, y - 32, 0.5); + graphic.render(r, x, y, 0.5); } hoveredInputTerminal(