Skip to content

Commit daf3cf3

Browse files
committed
Fix simulation issues
1 parent 0953598 commit daf3cf3

File tree

9 files changed

+220
-178
lines changed

9 files changed

+220
-178
lines changed

src/common/vector2.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ export const vector2 = {
1818
x: a.x / b,
1919
y: a.y / b,
2020
}),
21+
size: (a: Vector2): number => Math.hypot(a.x, a.y),
22+
distance: (a: Vector2, b: Vector2): number => vector2.size(vector2.sub(a, b)),
2123
fromDomEvent: (e: { offsetX: number; offsetY: number }): Vector2 => ({
2224
x: e.offsetX,
2325
y: e.offsetY,

src/hooks/drag.ts

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { type PointerEvent, useRef } from "react";
2+
import { type Vector2, vector2 } from "../common/vector2";
3+
4+
const DRAG_THRESHOLD = 5;
5+
6+
export type UseDraggableProps = {
7+
onClick?(e: PointerEvent): void;
8+
onDragStart?(e: PointerEvent): void;
9+
onDrag?(e: PointerEvent): void;
10+
onDragEnd?(e: PointerEvent): void;
11+
};
12+
13+
export function useDraggable(props: UseDraggableProps) {
14+
const { onClick, onDragStart, onDrag, onDragEnd } = props;
15+
16+
const dragStateRef = useRef<{
17+
pointerId: number;
18+
initialPosition: Vector2;
19+
isDragging: boolean;
20+
} | null>(null);
21+
22+
return {
23+
onPointerDown: (e: PointerEvent) => {
24+
if (!(e.buttons & 1)) return;
25+
dragStateRef.current = {
26+
pointerId: e.pointerId,
27+
initialPosition: vector2.fromDomEvent(e.nativeEvent),
28+
isDragging: false,
29+
};
30+
onDragStart?.(e);
31+
e.currentTarget.setPointerCapture(e.pointerId);
32+
},
33+
onPointerMove: (e: PointerEvent) => {
34+
if (!dragStateRef.current) return;
35+
if (dragStateRef.current.pointerId !== e.pointerId) return;
36+
if (dragStateRef.current.isDragging) {
37+
onDrag?.(e);
38+
return;
39+
}
40+
41+
const currentPosition = vector2.fromDomEvent(e.nativeEvent);
42+
const distance = vector2.distance(
43+
dragStateRef.current.initialPosition,
44+
currentPosition,
45+
);
46+
if (distance > DRAG_THRESHOLD) {
47+
dragStateRef.current.isDragging = true;
48+
onDragStart?.(e);
49+
onDrag?.(e);
50+
}
51+
},
52+
onPointerUp: (e: PointerEvent) => {
53+
if (!dragStateRef.current) return;
54+
if (dragStateRef.current.pointerId !== e.pointerId) return;
55+
e.currentTarget.releasePointerCapture(e.pointerId);
56+
},
57+
onLostPointerCapture: (e: PointerEvent) => {
58+
if (!dragStateRef.current) return;
59+
if (dragStateRef.current.isDragging) onDragEnd?.(e);
60+
else onClick?.(e);
61+
dragStateRef.current = null;
62+
},
63+
};
64+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export function CCComponentEditorNodePinPropertyEditor() {}

src/pages/edit/Editor/hooks/drag.ts

Lines changed: 0 additions & 37 deletions
This file was deleted.

src/pages/edit/Editor/renderer/NodePin.tsx

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import nullthrows from "nullthrows";
33
import { type PointerEvent, type ReactNode, useState } from "react";
44
import { theme } from "../../../../common/theme";
55
import { type Vector2, vector2 } from "../../../../common/vector2";
6+
import { useDraggable } from "../../../../hooks/drag";
67
import { CCConnectionStore } from "../../../../store/connection";
78
import type { CCNodePinId } from "../../../../store/nodePin";
89
import { useStore } from "../../../../store/react";
@@ -33,40 +34,6 @@ export default function CCComponentEditorRendererNodePin({
3334
cursorPosition: Vector2;
3435
nodePinPositionKDTree: KDTree<CCNodePinId>;
3536
} | null>(null);
36-
const onDrag = (e: PointerEvent) => {
37-
let nodePinPositionKDTree = draggingState?.nodePinPositionKDTree;
38-
if (!nodePinPositionKDTree) {
39-
const nodes = store.nodes.getManyByParentComponentId(
40-
node.parentComponentId,
41-
);
42-
nodePinPositionKDTree = KDTree.from(
43-
nodes
44-
.filter((yourNode) => yourNode.id !== node.id)
45-
.flatMap((yourNode) => [
46-
...getCCComponentEditorRendererNodeGeometry(store, yourNode.id)
47-
.nodePinPositionById,
48-
])
49-
.flatMap(([yourNodePinId, yourNodePinPosition]) => {
50-
const yourNodePin = nullthrows(store.nodePins.get(yourNodePinId));
51-
const yourComponentPin = nullthrows(
52-
store.componentPins.get(yourNodePin.componentPinId),
53-
);
54-
if (yourComponentPin.type === componentPin.type) return [];
55-
return [
56-
[yourNodePinId, [yourNodePinPosition.x, yourNodePinPosition.y]],
57-
] as const;
58-
}),
59-
2,
60-
);
61-
}
62-
setDraggingState({
63-
cursorPosition: componentEditorState.fromCanvasToStage(
64-
vector2.fromDomEvent(e.nativeEvent),
65-
),
66-
nodePinPositionKDTree,
67-
});
68-
};
69-
7037
let draggingView: ReactNode = null;
7138
let nodePinIdToConnect: CCNodePinId | null = null;
7239
if (draggingState) {
@@ -100,6 +67,57 @@ export default function CCComponentEditorRendererNodePin({
10067
/>
10168
);
10269
}
70+
const draggableProps = useDraggable({
71+
onDrag: (e: PointerEvent) => {
72+
let nodePinPositionKDTree = draggingState?.nodePinPositionKDTree;
73+
if (!nodePinPositionKDTree) {
74+
const nodes = store.nodes.getManyByParentComponentId(
75+
node.parentComponentId,
76+
);
77+
nodePinPositionKDTree = KDTree.from(
78+
nodes
79+
.filter((yourNode) => yourNode.id !== node.id)
80+
.flatMap((yourNode) => [
81+
...getCCComponentEditorRendererNodeGeometry(store, yourNode.id)
82+
.nodePinPositionById,
83+
])
84+
.flatMap(([yourNodePinId, yourNodePinPosition]) => {
85+
const yourNodePin = nullthrows(store.nodePins.get(yourNodePinId));
86+
const yourComponentPin = nullthrows(
87+
store.componentPins.get(yourNodePin.componentPinId),
88+
);
89+
if (yourComponentPin.type === componentPin.type) return [];
90+
return [
91+
[yourNodePinId, [yourNodePinPosition.x, yourNodePinPosition.y]],
92+
] as const;
93+
}),
94+
2,
95+
);
96+
}
97+
setDraggingState({
98+
cursorPosition: componentEditorState.fromCanvasToStage(
99+
vector2.fromDomEvent(e.nativeEvent),
100+
),
101+
nodePinPositionKDTree,
102+
});
103+
},
104+
onDragEnd: () => {
105+
if (nodePinIdToConnect) {
106+
const route = {
107+
input: { from: nodePinIdToConnect, to: nodePin.id },
108+
output: { from: nodePin.id, to: nodePinIdToConnect },
109+
}[componentPin.type];
110+
store.connections.register(
111+
CCConnectionStore.create({
112+
parentComponentId: node.parentComponentId,
113+
...route,
114+
bentPortion: 0.5,
115+
}),
116+
);
117+
}
118+
setDraggingState(null);
119+
},
120+
});
103121

104122
const isSimulationMode = useComponentEditorStore()(
105123
(s) => s.editorMode === "play",
@@ -119,7 +137,10 @@ export default function CCComponentEditorRendererNodePin({
119137
let nodePinValue: SimulationValue;
120138
let nodePinValueAsString: string | null = null;
121139
if (isSimulationMode && hasNoConnection) {
122-
if (implementationComponentPin) {
140+
if (
141+
implementationComponentPin &&
142+
implementationComponentPin.type === "input"
143+
) {
123144
nodePinValue = nullthrows(
124145
componentEditorState.getInputValue(implementationComponentPin.id),
125146
);
@@ -131,11 +152,15 @@ export default function CCComponentEditorRendererNodePin({
131152
nodePinValueAsString = simulationValueToString(nodePinValue);
132153
}
133154
const updateInputValue = () => {
134-
if (!implementationComponentPin) return;
155+
if (
156+
!implementationComponentPin ||
157+
implementationComponentPin.type !== "input"
158+
)
159+
return;
135160
const updatedPinValue = [...nodePinValue];
136161
updatedPinValue[0] = !updatedPinValue[0];
137162
componentEditorState.setInputValue(
138-
implementationComponentPin.id,
163+
implementationComponentPin?.id,
139164
updatedPinValue,
140165
);
141166
};
@@ -152,30 +177,7 @@ export default function CCComponentEditorRendererNodePin({
152177
{nodePinValueAsString}
153178
</text>
154179
)}
155-
<g
156-
onPointerDown={(e) => {
157-
e.currentTarget.setPointerCapture(e.pointerId);
158-
onDrag(e);
159-
}}
160-
onPointerMove={draggingState ? onDrag : undefined}
161-
onPointerUp={() => {
162-
if (!nodePinIdToConnect) return;
163-
const route = {
164-
input: { from: nodePinIdToConnect, to: nodePin.id },
165-
output: { from: nodePin.id, to: nodePinIdToConnect },
166-
}[componentPin.type];
167-
store.connections.register(
168-
CCConnectionStore.create({
169-
parentComponentId: node.parentComponentId,
170-
...route,
171-
bentPortion: 0.5,
172-
}),
173-
);
174-
}}
175-
onLostPointerCapture={() => {
176-
setDraggingState(null);
177-
}}
178-
>
180+
<g {...draggableProps} style={{ cursor: "pointer" }}>
179181
<rect
180182
x={position.x - 5}
181183
y={position.y - 5}

src/store/componentEvaluator.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,10 @@ import type { CCNodeId } from "./node";
1212
import type { CCNodePin, CCNodePinId } from "./nodePin";
1313

1414
function createInput(
15+
store: CCStore,
1516
nodePins: CCNodePin[],
1617
inputPin: Record<string, CCComponentPin>,
17-
inputValues: Map<CCNodePinId, SimulationValue>,
18+
inputValues: Map<CCNodePinId, SimulationValue> | null,
1819
): Record<string, SimulationValue[]> {
1920
const input: Partial<Record<string, SimulationValue[]>> = {};
2021
for (const key in inputPin) {
@@ -23,9 +24,19 @@ function createInput(
2324
(nodePin: CCNodePin) => componentPin.id === nodePin.componentPinId,
2425
);
2526
targetNodePins.sort((a, b) => a.order - b.order);
26-
const values = targetNodePins.map((nodePin: CCNodePin) =>
27-
nullthrows(inputValues.get(nodePin.id)),
28-
);
27+
const values = inputValues
28+
? targetNodePins.map((nodePin: CCNodePin) =>
29+
nullthrows(inputValues.get(nodePin.id)),
30+
)
31+
: targetNodePins.map((nodePin: CCNodePin) => {
32+
const multiplexability = store.nodePins.getNodePinMultiplexability(
33+
nodePin.id,
34+
);
35+
const bitWidth = multiplexability.isMultiplexable
36+
? 1
37+
: multiplexability.multiplicity;
38+
return Array<boolean>(bitWidth).fill(false);
39+
});
2940
input[key] = values;
3041
}
3142
return input as Record<string, SimulationValue[]>;
@@ -66,18 +77,27 @@ function simulateIntrinsic(
6677
invariant(componentDefinition);
6778
const inputPin = componentDefinition.inputPin;
6879
const outputPin = componentDefinition.outputPin;
69-
const _input = createInput(nodePins, inputPin, inputValues);
80+
const _input = createInput(store, nodePins, inputPin, inputValues);
7081
const outputShape = createOutputShape(store, nodePins, outputPin);
71-
const previousInputValues = new Map<CCNodePinId, SimulationValue>();
72-
for (const nodePin of nodePins) {
73-
const previousValue = parentPreviousFrame?.nodes
74-
.get(nodeId)
75-
?.pins.get(nodePin.id);
76-
if (previousValue) {
77-
previousInputValues.set(nodePin.id, previousValue);
82+
const previousInputValues = parentPreviousFrame
83+
? new Map<CCNodePinId, SimulationValue>()
84+
: null;
85+
if (previousInputValues) {
86+
for (const nodePin of nodePins) {
87+
const previousValue = parentPreviousFrame?.nodes
88+
.get(nodeId)
89+
?.pins.get(nodePin.id);
90+
if (previousValue) {
91+
previousInputValues.set(nodePin.id, previousValue);
92+
}
7893
}
7994
}
80-
const previousInput = createInput(nodePins, inputPin, previousInputValues);
95+
const previousInput = createInput(
96+
store,
97+
nodePins,
98+
inputPin,
99+
previousInputValues,
100+
);
81101
const output = componentDefinition.evaluate(
82102
_input,
83103
outputShape,

0 commit comments

Comments
 (0)