Skip to content

Commit 1df4159

Browse files
chelproctaka231Rn86222
committed
202560509
Co-authored-by: Takashi Nagatomi <taka231@users.noreply.github.com> Co-authored-by: Raito Nakajima <Rn86222@users.noreply.github.com>
1 parent c935b26 commit 1df4159

10 files changed

Lines changed: 266 additions & 227 deletions

File tree

src/pages/edit/Editor/components/NodePinPropertyEditor.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import nullthrows from "nullthrows";
44
import { useState } from "react";
55
import invariant from "tiny-invariant";
66
import { rect } from "../../../../common/rect";
7+
import { CCConnectionStore } from "../../../../store/connection";
78
import { IntrinsicComponentDefinition } from "../../../../store/intrinsics/base";
89
import { CCNodePinStore } from "../../../../store/nodePin";
910
import { useStore } from "../../../../store/react";
@@ -126,10 +127,28 @@ export function CCComponentEditorNodePinPropertyEditor() {
126127
connection.from === nodePin.id
127128
? connection.to
128129
: connection.from;
130+
const fromNodePinId =
131+
connection.from === nodePin.id
132+
? nodePin.id
133+
: anotherNodePinId;
134+
const toNodePinId =
135+
connection.from === nodePin.id
136+
? anotherNodePinId
137+
: nodePin.id;
138+
const parentComponentId = connection.parentComponentId;
139+
store.connections.unregister([connection.id]);
129140
if (
130-
!store.nodePins.isConnectable(nodePin.id, anotherNodePinId)
141+
store.nodePins.isConnectable(nodePin.id, anotherNodePinId)
131142
) {
132-
store.connections.unregister([connection.id]);
143+
// reconnect if still connectable after bit width change
144+
store.connections.register(
145+
CCConnectionStore.create({
146+
parentComponentId,
147+
from: fromNodePinId,
148+
to: toNodePinId,
149+
bentPortion: 0.5,
150+
}),
151+
);
133152
}
134153
}
135154
}

src/pages/edit/Editor/renderer/ComponentPin/index.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,24 @@ export default function CCComponentEditorRendererComponentPin({
3030
label: stringifySimulationValue(
3131
type === "input"
3232
? nullthrows(
33-
componentEditorState.getInputValue(interfaceComponentPin.id),
33+
componentEditorState.getInputValue([
34+
interfaceComponentPin.id,
35+
componentEditorState.timeStep,
36+
]),
3437
)
3538
: nullthrows(componentEditorState.getNodePinValue(nodePinId)),
3639
),
3740
onClick:
3841
type === "input"
3942
? () => {
4043
const nodePinValue = nullthrows(
41-
componentEditorState.getInputValue(
44+
componentEditorState.getInputValue([
4245
interfaceComponentPin.id,
43-
),
46+
componentEditorState.timeStep,
47+
]),
4448
);
4549
componentEditorState.setInputValue(
46-
interfaceComponentPin.id,
50+
[interfaceComponentPin.id, componentEditorState.timeStep],
4751
wrappingIncrementSimulationValue(nodePinValue),
4852
);
4953
}

src/pages/edit/Editor/renderer/Node/components/Display/index.tsx

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,27 @@
1+
import nullthrows from "nullthrows";
12
import { theme } from "../../../../../../../common/theme";
3+
import { display } from "../../../../../../../store/intrinsics/definitions";
24
import type { CCIntrinsicComponentDisplaySpec } from "../../../../../../../store/intrinsics/types";
3-
import type { CCNode } from "../../../../../../../store/node";
5+
import { useStore } from "../../../../../../../store/react";
6+
import { useComponentEditorStore } from "../../../../store";
47
import type { CCComponentEditorRendererNodeRendererProps } from "../../types";
58

69
export function CCComponentEditorRendererNodeDisplayRenderer(
710
props: CCComponentEditorRendererNodeRendererProps,
811
) {
9-
const node = props.node as CCNode<CCIntrinsicComponentDisplaySpec>;
12+
const { store } = useStore();
13+
const config = props.node.config as CCIntrinsicComponentDisplaySpec["config"];
14+
const inputNodePin = nullthrows(
15+
store.nodePins
16+
.getManyByNodeId(props.node.id)
17+
.find((pin) => pin.componentPinId === display.inputPin.Pixels.id),
18+
`Display node ${props.node.id} is missing input pin`,
19+
);
20+
const editorState = useComponentEditorStore()();
21+
const inputValue =
22+
editorState.editorMode === "play"
23+
? editorState.getNodePinValue(inputNodePin.id)
24+
: undefined;
1025

1126
return (
1227
<>
@@ -38,12 +53,12 @@ export function CCComponentEditorRendererNodeDisplayRenderer(
3853
fontSize={16}
3954
fill={theme.palette.textPrimary}
4055
>
41-
{node.config.resolution.x}x{node.config.resolution.y}
56+
{config.resolution.x}x{config.resolution.y}
4257
</text>
43-
{Array(node.config.resolution.y)
58+
{Array(config.resolution.y)
4459
.keys()
4560
.map((y) =>
46-
Array(node.config.resolution.x)
61+
Array(config.resolution.x)
4762
.keys()
4863
.map((x) => (
4964
<rect
@@ -52,7 +67,14 @@ export function CCComponentEditorRendererNodeDisplayRenderer(
5267
y={props.geometry.rect.position.y + 8 + y * 12}
5368
width={12}
5469
height={12}
55-
fill={theme.palette.white}
70+
fill={
71+
inputValue?.[
72+
config.resolution.x * config.resolution.y -
73+
(1 + x + config.resolution.x * y)
74+
]
75+
? theme.palette.black
76+
: theme.palette.white
77+
}
5678
stroke={theme.palette.editorGrid}
5779
/>
5880
))

src/pages/edit/Editor/store/slices/core/index.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
// import type { CCComponentId } from "../../../../../../store/component";
1212
import simulateComponent from "../../../../../../store/simulation";
1313
import type { ComponentEditorSliceCreator } from "../../types";
14-
import type { EditorStoreCoreSlice } from "./types";
14+
import type { EditorStoreCoreSlice, InputValueKey } from "./types";
1515

1616
export function stringifySimulationValue(value: SimulationValue): string {
1717
const binary = value.map((v) => (v ? "1" : "0")).join("");
@@ -43,11 +43,20 @@ export const createComponentEditorStoreCoreSlice: ComponentEditorSliceCreator<
4343
},
4444
/** @private */
4545
inputValues: new Map(),
46-
getInputValue(componentPinId: CCComponentPinId) {
47-
const value = get().inputValues.get(componentPinId);
46+
getInputValue(inputValueKey: InputValueKey) {
47+
const value = get().inputValues.get(JSON.stringify(inputValueKey));
4848
if (!value) {
49+
const previousTimeStepValue = get().inputValues.get(
50+
JSON.stringify([inputValueKey[0], inputValueKey[1] - 1]),
51+
);
52+
if (previousTimeStepValue) {
53+
get().setInputValue(inputValueKey, previousTimeStepValue);
54+
return previousTimeStepValue;
55+
}
4956
const bitWidthStatus =
50-
store.componentPins.getComponentPinBitWidthStatus(componentPinId);
57+
store.componentPins.getComponentPinBitWidthStatus(
58+
inputValueKey[0],
59+
);
5160
if (bitWidthStatus.isFixed) {
5261
const newValue = new Array(bitWidthStatus.bitWidth).fill(false);
5362
return newValue;
@@ -60,15 +69,12 @@ export const createComponentEditorStoreCoreSlice: ComponentEditorSliceCreator<
6069
}
6170
return value;
6271
},
63-
setInputValue(
64-
componentPinId: CCComponentPinId,
65-
value: SimulationValue,
66-
) {
72+
setInputValue(inputValueKey: InputValueKey, value: SimulationValue) {
6773
set((state) => {
6874
return {
6975
...state,
7076
inputValues: new Map(state.inputValues).set(
71-
componentPinId,
77+
JSON.stringify(inputValueKey),
7278
value,
7379
),
7480
};
@@ -170,7 +176,10 @@ export const createComponentEditorStoreCoreSlice: ComponentEditorSliceCreator<
170176
for (const pin of pins) {
171177
invariant(pin.implementation);
172178
if (pin.type === "input") {
173-
inputValues.set(pin.id, editorState.getInputValue(pin.id));
179+
inputValues.set(
180+
pin.id,
181+
editorState.getInputValue([pin.id, timeStep]),
182+
);
174183
}
175184
}
176185
simulationCachedFrames.push(

src/pages/edit/Editor/store/slices/core/types.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ export type EditorModePlay = "play";
1111

1212
export type RangeSelect = { start: Vector2; end: Vector2 } | null;
1313

14-
export type InputValueKey = CCComponentPinId;
14+
export type TimeStep = number;
15+
16+
export type InputValueKey = [CCComponentPinId, TimeStep];
1517

1618
export type NodePinPropertyEditorTarget = {
1719
nodeId: CCNodeId;
@@ -29,9 +31,9 @@ export type EditorStoreCoreSlice = {
2931
setNodePinPropertyEditorTarget(
3032
target: NodePinPropertyEditorTarget | null,
3133
): void;
32-
inputValues: Map<InputValueKey, SimulationValue>;
33-
getInputValue(componentPinId: CCComponentPinId): SimulationValue;
34-
setInputValue(componentPinId: CCComponentPinId, value: SimulationValue): void;
34+
inputValues: Map<string, SimulationValue>;
35+
getInputValue(inputValueKey: InputValueKey): SimulationValue;
36+
setInputValue(inputValueKey: InputValueKey, value: SimulationValue): void;
3537
setEditorMode(mode: EditorMode): void;
3638
setTimeStep(timeStep: number): void;
3739
selectNode(ids: CCNodeId[], exclusive: boolean): void;

src/pages/home/index.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
Add as AddIcon,
33
Download as DownloadIcon,
44
MoreVert as MoreVertIcon,
5-
NoteAdd as NoteAddIcon,
65
Upload as UploadIcon,
76
} from "@mui/icons-material";
87
import {
@@ -76,9 +75,6 @@ export default function HomePage({ onComponentSelected }: HomePageProps) {
7675
File
7776
</Typography>
7877
<Box sx={{ display: "flex", gap: 1 }}>
79-
<Button variant="outlined" startIcon={<NoteAddIcon />} disabled>
80-
New File
81-
</Button>
8278
<Button
8379
variant="outlined"
8480
color="inherit"
@@ -101,6 +97,21 @@ export default function HomePage({ onComponentSelected }: HomePageProps) {
10197
>
10298
Import
10399
</Button>
100+
<div style={{ flexGrow: 1 }} />
101+
<Button
102+
variant="text"
103+
color="error"
104+
onClick={() => {
105+
if (
106+
confirm(
107+
"Are you sure you want to reset the store? This action cannot be undone.",
108+
)
109+
)
110+
resetStore();
111+
}}
112+
>
113+
Reset
114+
</Button>
104115
</Box>
105116
<Box sx={{ display: "flex", alignItems: "center", mt: 4 }}>
106117
<div style={{ flexGrow: 1 }}>

src/store/componentPin.ts

Lines changed: 42 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,9 @@ import invariant from "tiny-invariant";
44
import type { Opaque } from "type-fest";
55
import type CCStore from ".";
66
import type { CCComponentId } from "./component";
7-
import {
8-
aggregate,
9-
and,
10-
broadcast,
11-
decompose,
12-
false_,
13-
flipflop,
14-
input,
15-
not,
16-
or,
17-
output,
18-
true_,
19-
xor,
20-
} from "./intrinsics/definitions";
7+
import { IntrinsicComponentDefinition } from "./intrinsics/base";
8+
import { aggregate, decompose, input, output } from "./intrinsics/definitions";
219
import type { CCNodePinId } from "./nodePin";
22-
// import { IntrinsicComponentDefinition } from "./intrinsics/base";
2310

2411
export type CCComponentPin = {
2512
readonly id: CCComponentPinId;
@@ -228,71 +215,51 @@ export class CCComponentPinStore extends EventEmitter<CCComponentPinStoreEvents>
228215
const pin = this.#pins.get(pinId);
229216
invariant(pin);
230217

231-
// const intrinsicPinAttributes =
232-
// IntrinsicComponentDefinition.getPinAttributesByPinId(pin.id);
233-
// if (intrinsicPinAttributes) {
234-
// if (intrinsicPinAttributes.bitWidthPolicy.type === "inferred")
235-
// return { isFixed: false, fixMode: "automatic" };
236-
// if (intrinsicPinAttributes.bitWidthPolicy.type === "configurable")
237-
// return { isFixed: false, fixMode: "manual" };
238-
// if (intrinsicPinAttributes.bitWidthPolicy.type === "fixed") {
239-
// const definition = nullthrows(IntrinsicComponentDefinition.getByComponentId(pin.componentId));
240-
// }
241-
// throw new Error(`Unknown bit width policy: ${intrinsicPinAttributes.bitWidthPolicy}`);
242-
// }
243-
244-
// TODO: Remove hardcoded intrinsic component pin IDs and replace with a more flexible system, such as metadata on the component definitions.
245-
switch (pin.id) {
246-
case nullthrows(and.inputPin.A.id):
247-
case nullthrows(and.inputPin.B.id):
248-
case nullthrows(and.outputPin.Out.id):
249-
case nullthrows(or.inputPin.A.id):
250-
case nullthrows(or.inputPin.B.id):
251-
case nullthrows(or.outputPin.Out.id):
252-
case nullthrows(not.inputPin.In.id):
253-
case nullthrows(not.outputPin.Out.id):
254-
case nullthrows(xor.inputPin.A.id):
255-
case nullthrows(xor.inputPin.B.id):
256-
case nullthrows(xor.outputPin.Out.id):
257-
case nullthrows(input.outputPin.Out.id):
258-
case nullthrows(output.inputPin.In.id):
259-
case nullthrows(flipflop.inputPin.In.id):
260-
case nullthrows(flipflop.outputPin.Out.id):
261-
case nullthrows(true_.outputPin.Out.id):
262-
case nullthrows(false_.outputPin.Out.id): {
263-
return { isFixed: false, fixMode: "automatic" };
264-
}
265-
case nullthrows(aggregate.inputPin.In.id): {
218+
// Intrinsic components
219+
const intrinsicPinAttributes =
220+
IntrinsicComponentDefinition.getPinAttributesByPinId(pin.id);
221+
if (intrinsicPinAttributes) {
222+
// TODO: This is a temporary workaround to allow the bit width of aggregate and decompose pins to be calculated outside of the normal inference process.
223+
// We should eventually refactor the bit width inference process to handle these cases more elegantly.
224+
if (pin.id === aggregate.outputPin.Out.id)
266225
return { isFixed: false, fixMode: "manual" };
267-
}
268-
case nullthrows(aggregate.outputPin.Out.id): {
226+
if (pin.id === decompose.inputPin.In.id)
269227
return { isFixed: false, fixMode: "manual" };
270-
}
271-
case nullthrows(decompose.outputPin.Out.id): {
272-
return { isFixed: false, fixMode: "manual" };
273-
}
274-
case nullthrows(decompose.inputPin.In.id): {
275-
return { isFixed: false, fixMode: "manual" };
276-
}
277-
case nullthrows(broadcast.inputPin.In.id): {
278-
return { isFixed: true, bitWidth: 1 };
279-
}
280-
case nullthrows(broadcast.outputPin.Out.id): {
228+
229+
if (intrinsicPinAttributes.bitWidthPolicy.type === "inferred")
230+
return { isFixed: false, fixMode: "automatic" };
231+
if (intrinsicPinAttributes.bitWidthPolicy.type === "configurable")
281232
return { isFixed: false, fixMode: "manual" };
282-
}
283-
default: {
284-
if (pin.implementation === null) {
285-
throw new Error("unreachable");
286-
}
287-
const bitWidthStatus = this.#store.nodePins.getNodePinBitWidthStatus(
288-
pin.implementation,
233+
if (intrinsicPinAttributes.bitWidthPolicy.type === "fixed") {
234+
const definition = nullthrows(
235+
IntrinsicComponentDefinition.getByComponentId(pin.componentId),
236+
`Intrinsic component definition not found for component ID: ${pin.componentId}`,
289237
);
290-
if (bitWidthStatus.isFixed) {
291-
return bitWidthStatus;
292-
} else {
293-
return { isFixed: false, fixMode: "automatic" };
294-
}
238+
return {
239+
isFixed: true,
240+
bitWidth: intrinsicPinAttributes.bitWidthPolicy.calculateBitWidth(
241+
definition.initialConfig,
242+
{},
243+
),
244+
};
295245
}
246+
throw new Error(
247+
`Unknown bit width policy: ${intrinsicPinAttributes.bitWidthPolicy satisfies never}`,
248+
);
249+
}
250+
251+
// User-defined components
252+
invariant(
253+
pin.implementation,
254+
"Pin implementation must be defined for user-defined components",
255+
);
256+
const bitWidthStatus = this.#store.nodePins.getNodePinBitWidthStatus(
257+
pin.implementation,
258+
);
259+
if (bitWidthStatus.isFixed) {
260+
return bitWidthStatus;
261+
} else {
262+
return { isFixed: false, fixMode: "automatic" };
296263
}
297264
}
298265

0 commit comments

Comments
 (0)