extract offset
This commit is contained in:
parent
9def12cad9
commit
022e8b89f7
@ -12,11 +12,12 @@ import * as ir from "./ir";
|
||||
import { Sim } from "./sim";
|
||||
import { EventBus } from "./events";
|
||||
import { Mouse } from "./Mouse";
|
||||
import { ViewPos } from "./ViewPos";
|
||||
|
||||
export type Tool = string;
|
||||
|
||||
export class Cx {
|
||||
public offset = v2(0, 0);
|
||||
public viewpos: ViewPos;
|
||||
private renderNeeded = false;
|
||||
|
||||
private state = new states.Normal(this) as states.State;
|
||||
@ -34,6 +35,7 @@ export class Cx {
|
||||
public mouse: Mouse;
|
||||
|
||||
constructor(public events: EventBus) {
|
||||
this.viewpos = new ViewPos(events);
|
||||
this.mouse = new Mouse(this.events);
|
||||
|
||||
this.state.enter();
|
||||
@ -57,7 +59,7 @@ export class Cx {
|
||||
}
|
||||
|
||||
render(canvas: HTMLCanvasElement) {
|
||||
const r = new Renderer(canvas, this.offset);
|
||||
const r = new Renderer(canvas, this.viewpos.offset);
|
||||
|
||||
r.clear();
|
||||
r.drawGrid();
|
||||
@ -95,15 +97,10 @@ export class Cx {
|
||||
transitionTo(newState: states.State) {
|
||||
this.state.leave();
|
||||
this.state = newState;
|
||||
console.log(`Entering state ${newState.constructor.name}`);
|
||||
// console.log(`Entering state ${newState.constructor.name}`);
|
||||
this.state.enter();
|
||||
}
|
||||
|
||||
moveOffset(deltaPos: V2) {
|
||||
this.offset.x += deltaPos.x;
|
||||
this.offset.y += deltaPos.y;
|
||||
}
|
||||
|
||||
addComponentPlacer(pos: V2, size: V2) {
|
||||
this.componentPlacer = new ComponentPlacer(pos, size);
|
||||
}
|
||||
@ -118,12 +115,6 @@ export class Cx {
|
||||
}
|
||||
}
|
||||
|
||||
canvasPosToBoard(pos: V2): V2 {
|
||||
const absX = pos.x - this.offset.x;
|
||||
const absY = pos.y - this.offset.y;
|
||||
return v2(absX, absY);
|
||||
}
|
||||
|
||||
runSimulation() {
|
||||
// const comp = this.board.toIr();
|
||||
// console.log("Before optimizing");
|
||||
@ -150,14 +141,14 @@ export class SelectionBox {
|
||||
this.size = this.size.add(deltaPos);
|
||||
}
|
||||
|
||||
normalized(): { pos: V2; size: V2 } {
|
||||
boardRect(viewpos: ViewPos): { pos: V2; size: V2 } {
|
||||
const normalizedAxis = (p: number, s: number): [number, number] =>
|
||||
s >= 0 ? [p, s] : [p + s, -s];
|
||||
|
||||
const [x, w] = normalizedAxis(this.pos.x, this.size.x);
|
||||
const [y, h] = normalizedAxis(this.pos.y, this.size.y);
|
||||
|
||||
return { pos: v2(x, y), size: v2(w, h) };
|
||||
return { pos: v2(x, y).sub(viewpos.offset), size: v2(w, h) };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
34
editor/src/editor/ViewPos.ts
Normal file
34
editor/src/editor/ViewPos.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import type { EventBus } from "./events";
|
||||
import { v2, type V2 } from "./V2";
|
||||
|
||||
export class ViewPos {
|
||||
public offset = v2(0, 0);
|
||||
|
||||
constructor(private events: EventBus) {
|
||||
this.events.subscribe(["MouseDown", "MouseMove"], (ev) => {
|
||||
const absPos = ev.pos;
|
||||
const pos = this.canvasToBoard(absPos);
|
||||
switch (ev.tag) {
|
||||
case "MouseDown":
|
||||
this.events.send({ tag: "MouseDownOffset", pos, absPos });
|
||||
break;
|
||||
case "MouseMove":
|
||||
this.events.send({
|
||||
tag: "MouseMoveOffset",
|
||||
pos,
|
||||
deltaPos: ev.deltaPos,
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
canvasToBoard(pos: V2): V2 {
|
||||
return pos.sub(this.offset);
|
||||
}
|
||||
|
||||
move(deltaPos: V2) {
|
||||
this.offset.x += deltaPos.x;
|
||||
this.offset.y += deltaPos.y;
|
||||
}
|
||||
}
|
||||
@ -15,7 +15,9 @@ export type Event =
|
||||
deltaPos: V2;
|
||||
}
|
||||
| { tag: "KeyDown" | "KeyUp"; key: string }
|
||||
| { tag: "SelectTool" | "ShowSelectedTool"; tool: string };
|
||||
| { tag: "SelectTool" | "ShowSelectedTool"; tool: string }
|
||||
| { tag: "MouseDownOffset"; pos: V2; absPos: V2 }
|
||||
| { tag: "MouseMoveOffset"; pos: V2; deltaPos: V2 };
|
||||
|
||||
export type EventOf<Tag extends Event["tag"]> = Event & { tag: Tag };
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import { v2, type V2 } from "./V2";
|
||||
export interface State {
|
||||
leave(): void;
|
||||
enter(): void;
|
||||
selectedTool?(): Tool | null;
|
||||
}
|
||||
|
||||
export class Normal implements State {
|
||||
@ -17,14 +16,14 @@ export class Normal implements State {
|
||||
|
||||
enter(): void {
|
||||
this.unsubscribe = this.cx.events.subscribe(
|
||||
["MouseDown", "MouseMove", "MouseDragBegin", "KeyDown"],
|
||||
["MouseDownOffset", "MouseMoveOffset", "MouseDragBegin", "KeyDown"],
|
||||
(ev) => {
|
||||
switch (ev.tag) {
|
||||
case "MouseDown":
|
||||
this._onMouseDown(ev.pos);
|
||||
case "MouseDownOffset":
|
||||
this.onMouseDown(ev.pos);
|
||||
break;
|
||||
case "MouseMove":
|
||||
this.cx.board.updateMouseHover(ev.pos.sub(this.cx.offset));
|
||||
case "MouseMoveOffset":
|
||||
this.cx.board.updateMouseHover(ev.pos);
|
||||
break;
|
||||
case "MouseDragBegin": {
|
||||
this.cx.selectionBox = new SelectionBox(ev.pos, ev.deltaPos);
|
||||
@ -50,48 +49,41 @@ export class Normal implements State {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
private _onMouseDown(pos: V2): void {
|
||||
if (
|
||||
this.cx.board.handleMouseClick(pos.sub(this.cx.offset), {
|
||||
onInputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{ tag: "InputPin", comp, i },
|
||||
pos.sub(this.cx.offset),
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
},
|
||||
onOutputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{ tag: "OutputPin", comp, i },
|
||||
pos.sub(this.cx.offset),
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
},
|
||||
onComponentClicked: (comp) => {
|
||||
private onMouseDown(pos: V2): void {
|
||||
this.cx.board.handleMouseClick(pos, {
|
||||
onInputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{ tag: "InputPin", comp, i },
|
||||
pos,
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
},
|
||||
onOutputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{ tag: "OutputPin", comp, i },
|
||||
pos,
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
},
|
||||
onComponentClicked: (comp) => {
|
||||
this.cx.selection = new Selection();
|
||||
this.cx.selection.addComponent(comp);
|
||||
this.cx.transitionTo(new Selecting(this.cx));
|
||||
},
|
||||
onJointClicked: (joint) => {
|
||||
if (this.cx.keysPressed.has("Control")) {
|
||||
this.cx.selection = new Selection();
|
||||
this.cx.selection.addComponent(comp);
|
||||
this.cx.selection.addJoint(joint);
|
||||
this.cx.transitionTo(new Selecting(this.cx));
|
||||
},
|
||||
onJointClicked: (joint) => {
|
||||
if (this.cx.keysPressed.has("Control")) {
|
||||
this.cx.selection = new Selection();
|
||||
this.cx.selection.addJoint(joint);
|
||||
this.cx.transitionTo(new Selecting(this.cx));
|
||||
} else {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{ tag: "Joint", joint },
|
||||
pos.sub(this.cx.offset),
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
}
|
||||
},
|
||||
}) !== "handled"
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
selectedTool(): Tool | null {
|
||||
return "select";
|
||||
} else {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{ tag: "Joint", joint },
|
||||
pos,
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,7 +99,7 @@ export class Panning implements State {
|
||||
switch (ev.tag) {
|
||||
case "MouseDragBegin":
|
||||
case "MouseDrag":
|
||||
this.cx.moveOffset(ev.deltaPos);
|
||||
this.cx.viewpos.move(ev.deltaPos);
|
||||
break;
|
||||
case "KeyDown": {
|
||||
if (ev.key === "Escape") {
|
||||
@ -133,10 +125,6 @@ export class Panning implements State {
|
||||
leave(): void {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
selectedTool(): Tool | null {
|
||||
return "pan";
|
||||
}
|
||||
}
|
||||
|
||||
export class Placing implements State {
|
||||
@ -153,11 +141,11 @@ export class Placing implements State {
|
||||
|
||||
enter(): void {
|
||||
this.unsubscribe = this.cx.events.subscribe(
|
||||
["MouseDown", "MouseMove", "KeyDown"],
|
||||
["MouseDownOffset", "MouseMove", "KeyDown"],
|
||||
(ev) => {
|
||||
switch (ev.tag) {
|
||||
case "MouseDown": {
|
||||
const boardPos = this.cx.canvasPosToBoard(ev.pos);
|
||||
case "MouseDownOffset": {
|
||||
const boardPos = ev.pos;
|
||||
if (this.cx.board.canPlaceComponent(this.compDef, boardPos)) {
|
||||
this.cx.board.placeComponent(this.compDef, boardPos);
|
||||
this.cx.transitionTo(new Normal(this.cx));
|
||||
@ -185,10 +173,6 @@ export class Placing implements State {
|
||||
this.cx.removeComponentPlacer();
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
selectedTool(): Tool | null {
|
||||
return this.tool;
|
||||
}
|
||||
}
|
||||
|
||||
export class Selecting implements State {
|
||||
@ -200,17 +184,17 @@ export class Selecting implements State {
|
||||
|
||||
enter(): void {
|
||||
this.unsubscribe = this.cx.events.subscribe(
|
||||
["MouseDown", "MouseUp", "MouseMove", "KeyDown"],
|
||||
["MouseDownOffset", "MouseUp", "MouseMoveOffset", "KeyDown"],
|
||||
(ev) => {
|
||||
switch (ev.tag) {
|
||||
case "MouseDown":
|
||||
this.onMouseDown(ev.pos);
|
||||
case "MouseDownOffset":
|
||||
this.onMouseDown(ev.pos, ev.absPos);
|
||||
break;
|
||||
case "MouseUp":
|
||||
this.isMouseDown = false;
|
||||
break;
|
||||
case "MouseMove": {
|
||||
this.cx.board.updateMouseHover(ev.pos.sub(this.cx.offset));
|
||||
case "MouseMoveOffset": {
|
||||
this.cx.board.updateMouseHover(ev.pos);
|
||||
if (this.isMouseDown) {
|
||||
this.cx.transitionTo(new Moving(this.cx));
|
||||
}
|
||||
@ -236,9 +220,9 @@ export class Selecting implements State {
|
||||
this.unsubscribe();
|
||||
}
|
||||
|
||||
private onMouseDown(pos: V2): void {
|
||||
private onMouseDown(pos: V2, absPos: V2): void {
|
||||
if (
|
||||
this.cx.board.handleMouseClick(pos.sub(this.cx.offset), {
|
||||
this.cx.board.handleMouseClick(pos, {
|
||||
onComponentClicked: (comp) => {
|
||||
if (this.cx.keysPressed.has("Control")) {
|
||||
this.cx.selection?.toggleComponent(comp);
|
||||
@ -258,11 +242,11 @@ export class Selecting implements State {
|
||||
}) !== "handled"
|
||||
) {
|
||||
if (this.cx.keysPressed.has("Control")) {
|
||||
this.cx.selectionBox = new SelectionBox(pos);
|
||||
this.cx.selectionBox = new SelectionBox(absPos);
|
||||
this.cx.transitionTo(new SelectingBox(this.cx));
|
||||
} else {
|
||||
this.cx.selection = null;
|
||||
this.cx.selectionBox = new SelectionBox(pos);
|
||||
this.cx.selectionBox = new SelectionBox(absPos);
|
||||
this.cx.transitionTo(new SelectingBox(this.cx));
|
||||
}
|
||||
}
|
||||
@ -326,13 +310,10 @@ export class SelectingBox implements State {
|
||||
if (!this.cx.selectionBox) {
|
||||
throw new Error("expected selectionBox to active");
|
||||
}
|
||||
const { pos, size } = this.cx.selectionBox.normalized();
|
||||
const { pos, size } = this.cx.selectionBox.boardRect(this.cx.viewpos);
|
||||
|
||||
const components = this.cx.board.componentsInRect(
|
||||
pos.sub(this.cx.offset),
|
||||
size,
|
||||
);
|
||||
const joints = this.cx.board.jointsInRect(pos.sub(this.cx.offset), size);
|
||||
const components = this.cx.board.componentsInRect(pos, size);
|
||||
const joints = this.cx.board.jointsInRect(pos, size);
|
||||
|
||||
if (components.length > 0 || joints.length > 0) {
|
||||
this.cx.selection ??= new Selection();
|
||||
@ -366,18 +347,18 @@ export class Wiring implements State {
|
||||
|
||||
enter(): void {
|
||||
this.unsubscribe = this.cx.events.subscribe(
|
||||
["MouseDown", "MouseMove", "KeyDown"],
|
||||
["MouseDownOffset", "MouseMoveOffset", "KeyDown"],
|
||||
(ev) => {
|
||||
switch (ev.tag) {
|
||||
case "MouseDown":
|
||||
case "MouseDownOffset":
|
||||
this.onMouseDown(ev.pos);
|
||||
break;
|
||||
case "MouseMove": {
|
||||
case "MouseMoveOffset": {
|
||||
if (!this.cx.connectingWire) {
|
||||
throw new Error("expected connectingWire to be active");
|
||||
}
|
||||
this.cx.connectingWire.move(ev.pos.sub(this.cx.offset));
|
||||
this.cx.board.updateMouseHover(ev.pos.sub(this.cx.offset));
|
||||
this.cx.connectingWire.move(ev.pos);
|
||||
this.cx.board.updateMouseHover(ev.pos);
|
||||
break;
|
||||
}
|
||||
case "KeyDown": {
|
||||
@ -399,7 +380,7 @@ export class Wiring implements State {
|
||||
|
||||
private onMouseDown(pos: V2): void {
|
||||
if (
|
||||
this.cx.board.handleMouseClick(pos.sub(this.cx.offset), {
|
||||
this.cx.board.handleMouseClick(pos, {
|
||||
onInputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire!.connectToInput(this.cx.board, comp, i);
|
||||
this.cx.connectingWire = null;
|
||||
@ -420,12 +401,9 @@ export class Wiring implements State {
|
||||
const kind: ConnectingWireKind = {
|
||||
tag: "Intermediary",
|
||||
prev: this.cx.connectingWire!,
|
||||
pos: pos.sub(this.cx.offset),
|
||||
pos,
|
||||
};
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
kind,
|
||||
pos.sub(this.cx.offset),
|
||||
);
|
||||
this.cx.connectingWire = new ConnectingWire(kind, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user