wiring
This commit is contained in:
parent
0ec608ee54
commit
6a5b051872
@ -1,12 +1,22 @@
|
||||
import type { Selection } from "./Cx";
|
||||
import type { Renderer } from "./Renderer";
|
||||
import { pointInsideRect, rectsCollide, v2, V2 } from "./V2";
|
||||
import {
|
||||
lineSegmentPointDistance,
|
||||
pointInsideRect,
|
||||
rectsCollide,
|
||||
v2,
|
||||
V2,
|
||||
} from "./V2";
|
||||
|
||||
export class Board {
|
||||
private components: PlacedComponent[] = [];
|
||||
private components: Component[] = [];
|
||||
private joints: Joint[] = [];
|
||||
private wires: Wire[] = [];
|
||||
|
||||
private hoveredOverInput: [PlacedComponent, number] | null = null;
|
||||
private hoveredOverOutput: [PlacedComponent, number] | null = null;
|
||||
private hoveredOverInput: [Component, number] | null = null;
|
||||
private hoveredOverOutput: [Component, number] | null = null;
|
||||
private hoveredOverJoint: Joint | null = null;
|
||||
private hoveredOverWire: Wire | null = null;
|
||||
|
||||
constructor() {}
|
||||
|
||||
@ -17,7 +27,7 @@ export class Board {
|
||||
}
|
||||
|
||||
placeComponent(kind: ComponentKind, pos: V2) {
|
||||
this.components.push({ kind: kind, pos });
|
||||
this.components.push(new Component(kind, pos));
|
||||
}
|
||||
|
||||
render(r: Renderer, selection: Selection | null) {
|
||||
@ -29,7 +39,23 @@ export class Board {
|
||||
r.drawComponentBody(pos, kind);
|
||||
}
|
||||
|
||||
for (const { i, pinOffset } of kind.inputPinIter()) {
|
||||
for (const wire of this.wires) {
|
||||
if (this.hoveredOverWire == wire) {
|
||||
r.drawWireHovered(wire.beginPos(), wire.endPos());
|
||||
} else {
|
||||
r.drawWire(wire.beginPos(), wire.endPos());
|
||||
}
|
||||
}
|
||||
|
||||
for (const joint of this.joints) {
|
||||
r.drawJoint(joint.pos);
|
||||
|
||||
if (this.hoveredOverJoint === joint) {
|
||||
r.drawJointHover(joint.pos);
|
||||
}
|
||||
}
|
||||
|
||||
for (const [i, pinOffset] of kind.inputPinOffsets().entries()) {
|
||||
if (kind.inputs[i] !== null) {
|
||||
throw new Error("pin text not implemented");
|
||||
}
|
||||
@ -43,7 +69,7 @@ export class Board {
|
||||
}
|
||||
}
|
||||
|
||||
for (const { i, pinOffset } of kind.outputPinIter()) {
|
||||
for (const [i, pinOffset] of kind.outputPinOffsets().entries()) {
|
||||
if (kind.outputs[i] !== null) {
|
||||
throw new Error("pin text not implemented");
|
||||
}
|
||||
@ -62,83 +88,93 @@ export class Board {
|
||||
updateMouseHover(pos: V2) {
|
||||
this.hoveredOverInput = null;
|
||||
this.hoveredOverOutput = null;
|
||||
this.hoveredOverJoint = null;
|
||||
this.hoveredOverWire = null;
|
||||
|
||||
for (const comp of this.components) {
|
||||
const {
|
||||
pos: { x, y },
|
||||
kind: {
|
||||
size: { x: w },
|
||||
},
|
||||
} = comp;
|
||||
const mouseOverResult = comp.mouseOver(pos);
|
||||
switch (mouseOverResult?.tag) {
|
||||
case "InputPin":
|
||||
this.hoveredOverInput = [comp, mouseOverResult.i];
|
||||
return;
|
||||
case "OutputPin":
|
||||
this.hoveredOverOutput = [comp, mouseOverResult.i];
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!pointInsideRect(
|
||||
pos,
|
||||
comp.pos.sub(v2(5, 5)),
|
||||
comp.kind.size.add(v2(10, 10)),
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
for (const { i, pinOffset } of comp.kind.inputPinIter()) {
|
||||
if (v2(x, y + pinOffset).distance(pos) < 6) {
|
||||
this.hoveredOverInput = [comp, i];
|
||||
for (const joint of this.joints) {
|
||||
if (joint.isMouseOver(pos)) {
|
||||
this.hoveredOverJoint = joint;
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (const { i, pinOffset } of comp.kind.outputPinIter()) {
|
||||
if (v2(x + w, y + pinOffset).distance(pos) < 6) {
|
||||
this.hoveredOverOutput = [comp, i];
|
||||
}
|
||||
|
||||
for (const wire of this.wires) {
|
||||
if (wire.isMouseOver(pos)) {
|
||||
this.hoveredOverWire = wire;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleMouseClick(
|
||||
pos: V2,
|
||||
inputPinClicked: (comp: PlacedComponent, i: number) => void,
|
||||
outputPinClicked: (comp: PlacedComponent, i: number) => void,
|
||||
componentClicked: (comp: PlacedComponent) => void,
|
||||
actions: {
|
||||
onInputPinClicked?(comp: Component, i: number): void;
|
||||
onOutputPinClicked?(comp: Component, i: number): void;
|
||||
onComponentClicked?(comp: Component): void;
|
||||
onJointClicked?(joint: Joint): void;
|
||||
onWireClicked?(wire: Wire): void;
|
||||
},
|
||||
): "handled" | "not handled" {
|
||||
for (const comp of this.components) {
|
||||
const {
|
||||
pos: { x, y },
|
||||
kind: {
|
||||
size: { x: w },
|
||||
},
|
||||
} = comp;
|
||||
const mouseOverResult = comp.mouseOver(pos);
|
||||
switch (mouseOverResult?.tag) {
|
||||
case "Component":
|
||||
actions.onComponentClicked?.(comp);
|
||||
return "handled";
|
||||
case "InputPin":
|
||||
actions.onInputPinClicked?.(comp, mouseOverResult.i);
|
||||
return "handled";
|
||||
case "OutputPin":
|
||||
actions.onOutputPinClicked?.(comp, mouseOverResult.i);
|
||||
return "handled";
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
!pointInsideRect(
|
||||
pos,
|
||||
comp.pos.sub(v2(5, 5)),
|
||||
comp.kind.size.add(v2(10, 10)),
|
||||
)
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
for (const { i, pinOffset } of comp.kind.inputPinIter()) {
|
||||
if (v2(x, y + pinOffset).distance(pos) < 6) {
|
||||
inputPinClicked(comp, i);
|
||||
for (const joint of this.joints) {
|
||||
if (joint.isMouseOver(pos)) {
|
||||
actions.onJointClicked?.(joint);
|
||||
return "handled";
|
||||
}
|
||||
}
|
||||
for (const { i, pinOffset } of comp.kind.outputPinIter()) {
|
||||
if (v2(x + w, y + pinOffset).distance(pos) < 6) {
|
||||
outputPinClicked(comp, i);
|
||||
|
||||
for (const wire of this.wires) {
|
||||
if (wire.isMouseOver(pos)) {
|
||||
actions.onWireClicked?.(wire);
|
||||
return "handled";
|
||||
}
|
||||
}
|
||||
componentClicked(comp);
|
||||
return "handled";
|
||||
}
|
||||
return "not handled";
|
||||
}
|
||||
|
||||
componentsInRect(pos: V2, size: V2): PlacedComponent[] {
|
||||
componentsInRect(pos: V2, size: V2): Component[] {
|
||||
return this.components.filter((comp) =>
|
||||
rectsCollide(pos, size, comp.pos, comp.kind.size),
|
||||
);
|
||||
}
|
||||
|
||||
addJoint(pos: V2): Joint {
|
||||
const t = new Joint(pos);
|
||||
this.joints.push(t);
|
||||
return t;
|
||||
}
|
||||
addWire(begin: WireConnection, end: WireConnection): Wire {
|
||||
const wire = new Wire(begin, end);
|
||||
this.wires.push(wire);
|
||||
return wire;
|
||||
}
|
||||
}
|
||||
|
||||
export class ComponentRepo {
|
||||
@ -167,10 +203,55 @@ export class ComponentRepo {
|
||||
}
|
||||
}
|
||||
|
||||
export type PlacedComponent = {
|
||||
kind: ComponentKind;
|
||||
pos: V2;
|
||||
};
|
||||
export class Component {
|
||||
constructor(
|
||||
public kind: ComponentKind,
|
||||
public pos: V2,
|
||||
) {}
|
||||
|
||||
mouseOver(pos: V2): ComponentMouseOverResult | null {
|
||||
const {
|
||||
pos: { x, y },
|
||||
kind: {
|
||||
size: { x: w },
|
||||
},
|
||||
} = this;
|
||||
|
||||
if (
|
||||
!pointInsideRect(
|
||||
pos,
|
||||
this.pos.sub(v2(5, 5)),
|
||||
this.kind.size.add(v2(10, 10)),
|
||||
)
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const [i, pinOffset] of this.kind.inputPinOffsets().entries()) {
|
||||
if (v2(x, y + pinOffset).distance(pos) < 6) {
|
||||
return { tag: "InputPin", i };
|
||||
}
|
||||
}
|
||||
for (const [i, pinOffset] of this.kind.outputPinOffsets().entries()) {
|
||||
if (v2(x + w, y + pinOffset).distance(pos) < 6) {
|
||||
return { tag: "OutputPin", i };
|
||||
}
|
||||
}
|
||||
return { tag: "Component" };
|
||||
}
|
||||
|
||||
inputPinPos(i: number): V2 {
|
||||
return this.pos.add(v2(0, this.kind.inputPinOffsets()[i]));
|
||||
}
|
||||
|
||||
outputPinPos(i: number): V2 {
|
||||
return this.pos.add(v2(this.kind.size.x, this.kind.outputPinOffsets()[i]));
|
||||
}
|
||||
}
|
||||
|
||||
type ComponentMouseOverResult =
|
||||
| { tag: "Component" }
|
||||
| { tag: "InputPin" | "OutputPin"; i: number };
|
||||
|
||||
export class ComponentKind {
|
||||
constructor(
|
||||
@ -180,20 +261,66 @@ export class ComponentKind {
|
||||
public outputs: (string | null)[],
|
||||
) {}
|
||||
|
||||
inputPinIter(): { i: number; pinOffset: number }[] {
|
||||
return this.inputs.map((_, i) => ({
|
||||
i,
|
||||
pinOffset: ((i + 1) * this.size.y) / (this.inputs.length + 1),
|
||||
}));
|
||||
inputPinOffsets(): number[] {
|
||||
return this.inputs.map(
|
||||
(_, i) => ((i + 1) * this.size.y) / (this.inputs.length + 1),
|
||||
);
|
||||
}
|
||||
outputPinIter(): { i: number; pinOffset: number }[] {
|
||||
return this.outputs.map((_, i) => ({
|
||||
i,
|
||||
pinOffset: ((i + 1) * this.size.y) / (this.outputs.length + 1),
|
||||
}));
|
||||
outputPinOffsets(): number[] {
|
||||
return this.outputs.map(
|
||||
(_, i) => ((i + 1) * this.size.y) / (this.outputs.length + 1),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export class Joint {
|
||||
constructor(public pos: V2) {}
|
||||
|
||||
isMouseOver(pos: V2): boolean {
|
||||
return this.pos.distance(pos) < 6;
|
||||
}
|
||||
}
|
||||
|
||||
export class Wire {
|
||||
constructor(
|
||||
private begin: WireConnection,
|
||||
private end: WireConnection,
|
||||
) {}
|
||||
|
||||
isMouseOver(pos: V2): boolean {
|
||||
const distance = lineSegmentPointDistance(
|
||||
this.beginPos(),
|
||||
this.endPos(),
|
||||
pos,
|
||||
);
|
||||
return distance !== null && distance < 6;
|
||||
}
|
||||
|
||||
beginPos(): V2 {
|
||||
return this.connectionPos(this.begin);
|
||||
}
|
||||
|
||||
endPos(): V2 {
|
||||
return this.connectionPos(this.end);
|
||||
}
|
||||
|
||||
private connectionPos(connection: WireConnection): V2 {
|
||||
switch (connection.tag) {
|
||||
case "InputPin":
|
||||
return connection.comp.inputPinPos(connection.i);
|
||||
case "OutputPin":
|
||||
return connection.comp.outputPinPos(connection.i);
|
||||
case "Joint":
|
||||
return connection.joint.pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type WireConnection =
|
||||
| { tag: "InputPin"; comp: Component; i: number }
|
||||
| { tag: "OutputPin"; comp: Component; i: number }
|
||||
| { tag: "Joint"; joint: Joint };
|
||||
|
||||
const defaultDefs = [
|
||||
{
|
||||
label: "input",
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { Board, ComponentRepo, type PlacedComponent } from "./Board";
|
||||
import {
|
||||
Board,
|
||||
ComponentRepo,
|
||||
Joint,
|
||||
type Component,
|
||||
type WireConnection,
|
||||
} from "./Board";
|
||||
import { Renderer } from "./Renderer";
|
||||
import * as states from "./states";
|
||||
import { v2, V2 } from "./V2";
|
||||
@ -133,9 +139,10 @@ export class Cx {
|
||||
}
|
||||
|
||||
export class SelectionBox {
|
||||
public size = v2(0, 0);
|
||||
|
||||
constructor(public pos: V2) {}
|
||||
constructor(
|
||||
public pos: V2,
|
||||
public size = v2(0, 0),
|
||||
) {}
|
||||
|
||||
render(r: Renderer) {
|
||||
r.drawSelectionBox(this.pos, this.size);
|
||||
@ -168,13 +175,13 @@ export class ComponentPlacer {
|
||||
}
|
||||
|
||||
export class Selection {
|
||||
selectedComponents = new Set<PlacedComponent>();
|
||||
selectedComponents = new Set<Component>();
|
||||
|
||||
addComponent(comp: PlacedComponent) {
|
||||
addComponent(comp: Component) {
|
||||
this.selectedComponents.add(comp);
|
||||
}
|
||||
|
||||
toggleComponent(comp: PlacedComponent) {
|
||||
toggleComponent(comp: Component) {
|
||||
if (this.selectedComponents.has(comp)) {
|
||||
this.selectedComponents.delete(comp);
|
||||
} else {
|
||||
@ -182,7 +189,7 @@ export class Selection {
|
||||
}
|
||||
}
|
||||
|
||||
isComponentSelected(comp: PlacedComponent) {
|
||||
isComponentSelected(comp: Component) {
|
||||
return this.selectedComponents.has(comp);
|
||||
}
|
||||
}
|
||||
@ -197,6 +204,11 @@ export class ConnectingWire {
|
||||
switch (this.kind.tag) {
|
||||
case "InputPin":
|
||||
case "OutputPin":
|
||||
break;
|
||||
case "Intermediary":
|
||||
this.kind.prev.render(r);
|
||||
r.drawConnectingWirePoint(this.beginPos());
|
||||
break;
|
||||
}
|
||||
r.drawConnectingWire(this.beginPos(), this.pos);
|
||||
}
|
||||
@ -205,24 +217,62 @@ export class ConnectingWire {
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
connectToInput(board: Board, comp: Component, i: number) {
|
||||
this.pushWire(board, { tag: "InputPin", comp, i });
|
||||
}
|
||||
|
||||
connectToOutput(board: Board, comp: Component, i: number) {
|
||||
this.pushWire(board, { tag: "OutputPin", comp, i });
|
||||
}
|
||||
|
||||
private pushWire(board: Board, end: WireConnection) {
|
||||
switch (this.kind.tag) {
|
||||
case "InputPin":
|
||||
case "OutputPin": {
|
||||
const { tag, comp, i } = this.kind;
|
||||
board.addWire({ tag, comp, i }, end);
|
||||
break;
|
||||
}
|
||||
case "Intermediary": {
|
||||
const joint = board.addJoint(this.kind.pos);
|
||||
board.addWire({ tag: "Joint", joint }, end);
|
||||
this.kind.prev.pushWire(board, { tag: "Joint", joint });
|
||||
break;
|
||||
}
|
||||
case "Joint": {
|
||||
const joint = this.kind.joint;
|
||||
board.addWire({ tag: "Joint", joint }, end);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
this.kind satisfies never;
|
||||
}
|
||||
}
|
||||
|
||||
private beginPos(): V2 {
|
||||
switch (this.kind.tag) {
|
||||
case "InputPin":
|
||||
return v2(
|
||||
this.kind.comp.pos.x,
|
||||
this.kind.comp.pos.y +
|
||||
this.kind.comp.kind.inputPinIter()[this.kind.i].pinOffset,
|
||||
this.kind.comp.kind.inputPinOffsets()[this.kind.i],
|
||||
);
|
||||
case "OutputPin":
|
||||
return v2(
|
||||
this.kind.comp.pos.x + this.kind.comp.kind.size.x,
|
||||
this.kind.comp.pos.y +
|
||||
this.kind.comp.kind.outputPinIter()[this.kind.i].pinOffset,
|
||||
this.kind.comp.kind.outputPinOffsets()[this.kind.i],
|
||||
);
|
||||
case "Intermediary":
|
||||
return this.kind.pos;
|
||||
case "Joint":
|
||||
return this.kind.joint.pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type ConnectingWireKind =
|
||||
| { tag: "InputPin"; comp: PlacedComponent; i: number }
|
||||
| { tag: "OutputPin"; comp: PlacedComponent; i: number };
|
||||
| { tag: "InputPin"; comp: Component; i: number }
|
||||
| { tag: "OutputPin"; comp: Component; i: number }
|
||||
| { tag: "Intermediary"; prev: ConnectingWire; pos: V2 }
|
||||
| { tag: "Joint"; joint: Joint };
|
||||
|
||||
@ -146,6 +146,53 @@ export class Renderer {
|
||||
c.stroke();
|
||||
}
|
||||
|
||||
drawWire(begin: V2, end: V2) {
|
||||
const { c, offset } = this;
|
||||
const { x: x0, y: y0 } = begin.add(offset);
|
||||
const { x: x1, y: y1 } = end.add(offset);
|
||||
|
||||
c.strokeStyle = `#333333`;
|
||||
c.lineWidth = 3;
|
||||
c.beginPath();
|
||||
c.moveTo(x0, y0);
|
||||
c.lineTo(x1, y1);
|
||||
c.stroke();
|
||||
}
|
||||
|
||||
drawWireHovered(begin: V2, end: V2) {
|
||||
const { c, offset } = this;
|
||||
const { x: x0, y: y0 } = begin.add(offset);
|
||||
const { x: x1, y: y1 } = end.add(offset);
|
||||
|
||||
c.strokeStyle = `#444444`;
|
||||
c.lineWidth = 3;
|
||||
c.beginPath();
|
||||
c.moveTo(x0, y0);
|
||||
c.lineTo(x1, y1);
|
||||
c.stroke();
|
||||
}
|
||||
|
||||
drawJoint(pos: V2) {
|
||||
const { c, offset } = this;
|
||||
const { x: x0, y: y0 } = pos.add(offset);
|
||||
|
||||
c.fillStyle = `#333333`;
|
||||
c.beginPath();
|
||||
c.arc(x0, y0, 3, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
}
|
||||
|
||||
drawJointHover(pos: V2) {
|
||||
const { c, offset } = this;
|
||||
const { x, y } = pos.add(offset);
|
||||
|
||||
c.strokeStyle = `#eee`;
|
||||
c.lineWidth = 2;
|
||||
c.beginPath();
|
||||
c.arc(x, y, 5, 0, Math.PI * 2);
|
||||
c.stroke();
|
||||
}
|
||||
|
||||
drawConnectingWire(begin: V2, end: V2) {
|
||||
const { c, offset } = this;
|
||||
const { x: x0, y: y0 } = begin.add(offset);
|
||||
@ -158,4 +205,14 @@ export class Renderer {
|
||||
c.lineTo(x1, y1);
|
||||
c.stroke();
|
||||
}
|
||||
|
||||
drawConnectingWirePoint(pos: V2) {
|
||||
const { c, offset } = this;
|
||||
const { x: x0, y: y0 } = pos.add(offset);
|
||||
|
||||
c.fillStyle = `#333333`;
|
||||
c.beginPath();
|
||||
c.arc(x0, y0, 3, 0, Math.PI * 2);
|
||||
c.fill();
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,14 +17,36 @@ export class V2 {
|
||||
len(): number {
|
||||
return Math.sqrt(this.x ** 2 + this.y ** 2);
|
||||
}
|
||||
|
||||
distance(other: V2) {
|
||||
return this.rsub(other).len();
|
||||
}
|
||||
|
||||
abs(): V2 {
|
||||
return new V2(Math.abs(this.x), Math.abs(this.y));
|
||||
}
|
||||
}
|
||||
|
||||
export const v2 = (x: number, y: number): V2 => new V2(x, y);
|
||||
|
||||
export function lineSegmentPointDistance(p1: V2, p2: V2, p: V2): number | null {
|
||||
const len = p2.sub(p1).len();
|
||||
const dist1 = p1.sub(p).len();
|
||||
const dist2 = p2.sub(p).len();
|
||||
|
||||
return dist1 < len && dist2 < len ? linePointDistance(p1, p2, p) : null;
|
||||
}
|
||||
|
||||
export function linePointDistance(p1: V2, p2: V2, p: V2): number {
|
||||
const { x: x1, y: y1 } = p1;
|
||||
const { x: x2, y: y2 } = p2;
|
||||
const { x, y } = p;
|
||||
|
||||
return (
|
||||
Math.abs((y2 - y1) * x - (x2 - x1) * y + x2 * y1 - y2 * x1) /
|
||||
Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
|
||||
);
|
||||
}
|
||||
|
||||
export function rectsCollide(
|
||||
{ x: ax, y: ay }: V2,
|
||||
{ x: aw, y: ah }: V2,
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { type ComponentKind } from "./Board";
|
||||
import { ConnectingWire, Selection } from "./Cx";
|
||||
import { ConnectingWire, Selection, type ConnectingWireKind } from "./Cx";
|
||||
import { SelectionBox, type Cx, type Tool } from "./Cx";
|
||||
import { v2, type V2 } from "./V2";
|
||||
|
||||
@ -15,50 +15,55 @@ export interface State {
|
||||
}
|
||||
|
||||
export class Normal implements State {
|
||||
private dragStart = v2(0, 0);
|
||||
private isMouseDown = false;
|
||||
|
||||
constructor(private cx: Cx) {}
|
||||
|
||||
onMouseDown(pos: V2): void {
|
||||
if (
|
||||
this.cx.board.handleMouseClick(
|
||||
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),
|
||||
(comp, i) => {
|
||||
console.log({ comp, i });
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{
|
||||
tag: "InputPin",
|
||||
comp,
|
||||
i,
|
||||
},
|
||||
pos,
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
},
|
||||
(comp, i) => {
|
||||
onOutputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{
|
||||
tag: "OutputPin",
|
||||
comp,
|
||||
i,
|
||||
},
|
||||
pos,
|
||||
{ tag: "OutputPin", comp, i },
|
||||
pos.sub(this.cx.offset),
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
},
|
||||
(comp) => {
|
||||
onComponentClicked: (comp) => {
|
||||
this.cx.selection = new Selection();
|
||||
this.cx.selection.addComponent(comp);
|
||||
this.cx.transitionTo(new Selecting(this.cx));
|
||||
},
|
||||
) === "handled"
|
||||
onJointClicked: (joint) => {
|
||||
this.cx.connectingWire = new ConnectingWire(
|
||||
{ tag: "Joint", joint },
|
||||
pos.sub(this.cx.offset),
|
||||
);
|
||||
this.cx.transitionTo(new Wiring(this.cx));
|
||||
},
|
||||
}) !== "handled"
|
||||
) {
|
||||
return;
|
||||
} else {
|
||||
this.cx.selectionBox = new SelectionBox(pos);
|
||||
this.cx.transitionTo(new SelectingBox(this.cx));
|
||||
this.isMouseDown = true;
|
||||
this.dragStart = pos;
|
||||
}
|
||||
}
|
||||
|
||||
onMouseMove(_deltaPos: V2, pos: V2): void {
|
||||
if (this.isMouseDown && this.dragStart.sub(pos).len() > 40) {
|
||||
this.cx.selectionBox = new SelectionBox(
|
||||
this.dragStart,
|
||||
pos.sub(this.dragStart),
|
||||
);
|
||||
this.cx.transitionTo(new SelectingBox(this.cx));
|
||||
}
|
||||
this.cx.board.updateMouseHover(pos.sub(this.cx.offset));
|
||||
}
|
||||
|
||||
@ -159,11 +164,8 @@ export class Selecting implements State {
|
||||
|
||||
onMouseDown(pos: V2): void {
|
||||
if (
|
||||
this.cx.board.handleMouseClick(
|
||||
pos.sub(this.cx.offset),
|
||||
(_comp, _i) => {},
|
||||
(_comp, _i) => {},
|
||||
(comp) => {
|
||||
this.cx.board.handleMouseClick(pos.sub(this.cx.offset), {
|
||||
onComponentClicked: (comp) => {
|
||||
if (this.cx.keysPressed.has("Control")) {
|
||||
this.cx.selection?.toggleComponent(comp);
|
||||
} else {
|
||||
@ -171,10 +173,8 @@ export class Selecting implements State {
|
||||
this.cx.selection.addComponent(comp);
|
||||
}
|
||||
},
|
||||
) === "handled"
|
||||
}) !== "handled"
|
||||
) {
|
||||
return;
|
||||
} else {
|
||||
if (this.cx.keysPressed.has("Control")) {
|
||||
this.cx.selectionBox = new SelectionBox(pos);
|
||||
this.cx.transitionTo(new SelectingBox(this.cx));
|
||||
@ -225,11 +225,35 @@ export class SelectingBox implements State {
|
||||
export class Wiring implements State {
|
||||
constructor(private cx: Cx) {}
|
||||
|
||||
onMouseDown(pos: V2): void {
|
||||
if (
|
||||
this.cx.board.handleMouseClick(pos.sub(this.cx.offset), {
|
||||
onInputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire!.connectToInput(this.cx.board, comp, i);
|
||||
this.cx.connectingWire = null;
|
||||
this.cx.transitionTo(new Normal(this.cx));
|
||||
},
|
||||
onOutputPinClicked: (comp, i) => {
|
||||
this.cx.connectingWire!.connectToInput(this.cx.board, comp, i);
|
||||
this.cx.connectingWire = null;
|
||||
this.cx.transitionTo(new Normal(this.cx));
|
||||
},
|
||||
}) !== "handled"
|
||||
) {
|
||||
const kind: ConnectingWireKind = {
|
||||
tag: "Intermediary",
|
||||
prev: this.cx.connectingWire!,
|
||||
pos: pos.sub(this.cx.offset),
|
||||
};
|
||||
this.cx.connectingWire = new ConnectingWire(kind, pos);
|
||||
}
|
||||
}
|
||||
|
||||
onMouseMove(_deltaPos: V2, pos: V2): void {
|
||||
if (!this.cx.connectingWire) {
|
||||
throw new Error("expected connectingWire to be active");
|
||||
}
|
||||
this.cx.connectingWire.move(pos);
|
||||
this.cx.connectingWire.move(pos.sub(this.cx.offset));
|
||||
this.cx.board.updateMouseHover(pos.sub(this.cx.offset));
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user