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