Skip to content

Commit d1aa4e7

Browse files
committed
Add support for adding/removing buildings
1 parent 9747d34 commit d1aa4e7

File tree

10 files changed

+214
-67
lines changed

10 files changed

+214
-67
lines changed

mindy-website/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ embedded-graphics = "0.8.1"
1212
embedded-graphics-web-simulator = { version = "0.4.0", git = "https://github.com/object-Object/embedded-graphics-web-simulator", branch = "offscreen_canvas" }
1313
getrandom = { version = "0.3.3", features = ["wasm_js"] }
1414
js-sys = "0.3.77"
15-
mindy = { path = "..", features = ["embedded_graphics", "wasm"] }
15+
mindy = { path = "..", default-features = false, features = ["std", "embedded_graphics", "wasm"] }
1616
wasm-bindgen = "0.2.100"
1717
wee_alloc = "0.4.5"
1818

mindy-website/src/lib.rs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -97,22 +97,14 @@ impl WebLogicVM {
9797
self.vm.time().as_secs_f64()
9898
}
9999

100-
pub fn add_processor(
101-
&mut self,
102-
position: u32,
103-
kind: ProcessorKind,
104-
code: String,
105-
) -> Result<(), String> {
100+
pub fn add_processor(&mut self, position: u32, kind: ProcessorKind) -> Result<(), String> {
106101
let position = unpack_point(position);
107102
self.vm
108103
.add_building(
109104
Building::from_processor_config(
110105
kind.name(),
111106
position,
112-
&ProcessorConfig {
113-
code,
114-
links: vec![],
115-
},
107+
&ProcessorConfig::default(),
116108
&self.vm,
117109
)
118110
.map_err(|e| e.to_string())?,
@@ -124,6 +116,7 @@ impl WebLogicVM {
124116
pub fn add_display(
125117
&mut self,
126118
position: u32,
119+
kind: DisplayKind,
127120
width: u32,
128121
height: u32,
129122
canvas: &OffscreenCanvas,
@@ -146,7 +139,7 @@ impl WebLogicVM {
146139
self.vm
147140
.add_building(
148141
Building::new(
149-
content::blocks::FROM_NAME["tile-logic-display"],
142+
content::blocks::FROM_NAME[kind.name()],
150143
unpack_point(position),
151144
display_data.into(),
152145
),
@@ -275,6 +268,23 @@ impl ProcessorKind {
275268
}
276269
}
277270

271+
#[wasm_bindgen]
272+
pub enum DisplayKind {
273+
Logic,
274+
Large,
275+
Tiled,
276+
}
277+
278+
impl DisplayKind {
279+
fn name(&self) -> &str {
280+
match self {
281+
Self::Logic => "logic-display",
282+
Self::Large => "large-logic-display",
283+
Self::Tiled => "tile-logic-display",
284+
}
285+
}
286+
}
287+
278288
#[wasm_bindgen]
279289
pub struct LinkNames(HashMap<u32, String>);
280290

mindy-website/www/src/App.tsx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,20 @@ export default function App() {
4747
>
4848
Source
4949
</Text>
50+
<Text size="sm" c="dimmed" px={6}>
51+
52+
</Text>
53+
<Text
54+
size="sm"
55+
c="dimmed"
56+
td="underline"
57+
component="a"
58+
href="https://reactflow.dev/"
59+
target="_blank"
60+
rel="noopener noreferrer"
61+
>
62+
React Flow
63+
</Text>
5064
</Flex>
5165
</AppShell.Footer>
5266
</AppShell>
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import { ActionIcon, Menu } from "@mantine/core";
2+
import { useReactFlow, useStoreApi } from "@xyflow/react";
3+
import { FaPlus } from "react-icons/fa6";
4+
5+
import { DisplayKind, ProcessorKind } from "mindy-website";
6+
7+
import { createNode } from "../utils";
8+
import type { LogicVMNode } from "./LogicVMFlow";
9+
10+
export default function AddBuildingMenu() {
11+
const reactFlow = useReactFlow<LogicVMNode>();
12+
const store = useStoreApi<LogicVMNode>();
13+
14+
// https://github.com/xyflow/xyflow/issues/3391#issuecomment-2590673868
15+
function locateNode() {
16+
const { domNode } = store.getState();
17+
const boundingRect = domNode?.getBoundingClientRect();
18+
if (boundingRect == null) {
19+
return [null, null];
20+
}
21+
22+
const center = reactFlow.screenToFlowPosition({
23+
x: boundingRect.x + boundingRect.width / 2,
24+
y: boundingRect.y + boundingRect.height / 2,
25+
});
26+
27+
return [center, { x: reactFlow.getNodes().length, y: 0 }];
28+
}
29+
30+
function addProcessor(kind: ProcessorKind) {
31+
const [nodePosition, buildingPosition] = locateNode();
32+
if (nodePosition == null || buildingPosition == null) return;
33+
34+
reactFlow.addNodes(
35+
createNode({
36+
type: "processor",
37+
position: nodePosition,
38+
data: {
39+
position: buildingPosition,
40+
kind,
41+
},
42+
}),
43+
);
44+
}
45+
46+
function addDisplay(
47+
kind: DisplayKind,
48+
displayWidth: number,
49+
displayHeight?: number,
50+
) {
51+
const [nodePosition, buildingPosition] = locateNode();
52+
if (nodePosition == null || buildingPosition == null) return;
53+
54+
reactFlow.addNodes(
55+
createNode({
56+
type: "display",
57+
position: nodePosition,
58+
data: {
59+
position: buildingPosition,
60+
kind,
61+
displayWidth,
62+
displayHeight: displayHeight ?? displayWidth,
63+
},
64+
}),
65+
);
66+
}
67+
68+
return (
69+
<Menu position="top-end">
70+
<Menu.Target>
71+
<ActionIcon
72+
className="nodrag nopan"
73+
size="xl"
74+
radius="xl"
75+
pos="absolute"
76+
right={0}
77+
bottom={0}
78+
m="md"
79+
style={{ zIndex: 999 }}
80+
>
81+
<FaPlus />
82+
</ActionIcon>
83+
</Menu.Target>
84+
85+
<Menu.Dropdown className="nodrag nopan nowheel">
86+
<Menu.Label>Processors</Menu.Label>
87+
88+
<Menu.Item onClick={() => addProcessor(ProcessorKind.Micro)}>
89+
Micro Processor
90+
</Menu.Item>
91+
<Menu.Item onClick={() => addProcessor(ProcessorKind.Logic)}>
92+
Logic Processor
93+
</Menu.Item>
94+
<Menu.Item onClick={() => addProcessor(ProcessorKind.Hyper)}>
95+
Hyper Processor
96+
</Menu.Item>
97+
<Menu.Item onClick={() => addProcessor(ProcessorKind.World)}>
98+
World Processor
99+
</Menu.Item>
100+
101+
<Menu.Label>Displays</Menu.Label>
102+
103+
<Menu.Item onClick={() => addDisplay(DisplayKind.Logic, 80)}>
104+
Logic Display
105+
</Menu.Item>
106+
<Menu.Item onClick={() => addDisplay(DisplayKind.Large, 176)}>
107+
Large Logic Display
108+
</Menu.Item>
109+
<Menu.Item onClick={() => addDisplay(DisplayKind.Tiled, 256)}>
110+
Tiled Logic Display (8x8)
111+
</Menu.Item>
112+
<Menu.Item onClick={() => addDisplay(DisplayKind.Tiled, 512)}>
113+
Tiled Logic Display (16x16)
114+
</Menu.Item>
115+
</Menu.Dropdown>
116+
</Menu>
117+
);
118+
}

mindy-website/www/src/components/LogicVMFlow.tsx

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,11 @@ import {
1313
} from "@xyflow/react";
1414
import { useCallback, useEffect } from "react";
1515

16-
import { ProcessorKind } from "mindy-website";
16+
import { DisplayKind, ProcessorKind } from "mindy-website";
1717

1818
import { useLogicVM } from "../hooks";
19-
import { packPoint } from "../utils";
19+
import { createNode } from "../utils";
20+
import AddBuildingMenu from "./AddBuildingMenu";
2021
import BuildingLinkConnectionLine from "./BuildingLinkConnectionLine";
2122
import BuildingLinkEdge from "./BuildingLinkEdge";
2223
import DisplayNode, { type DisplayNodeType } from "./nodes/DisplayNode";
@@ -38,33 +39,13 @@ const defaultEdgeOptions: Partial<Edge> = {
3839
markerEnd: { type: MarkerType.Arrow },
3940
};
4041

41-
function createNode<N, D>(
42-
node: N & { position: { x: number; y: number }; data: D },
43-
): N & {
44-
id: string;
45-
position: { x: number; y: number };
46-
data: D & { position: number };
47-
} {
48-
const {
49-
position: { x, y },
50-
} = node;
51-
const position = packPoint(x, y);
52-
return {
53-
...node,
54-
id: position.toString(),
55-
position: { x: x * 400, y: y * 400 },
56-
data: {
57-
...node.data,
58-
position,
59-
},
60-
};
61-
}
62-
6342
const defaultNodes: LogicVMNode[] = [
6443
createNode({
6544
type: "display",
66-
position: { x: 1, y: 0 },
45+
position: { x: 400, y: 0 },
6746
data: {
47+
position: { x: 0, y: 0 },
48+
kind: DisplayKind.Tiled,
6849
displayWidth: 256,
6950
displayHeight: 256,
7051
},
@@ -74,6 +55,7 @@ const defaultNodes: LogicVMNode[] = [
7455
type: "processor",
7556
position: { x: 0, y: 0 },
7657
data: {
58+
position: { x: 1, y: 0 },
7759
kind: ProcessorKind.World,
7860
defaultCode: `
7961
sensor x display1 @displayWidth
@@ -109,13 +91,6 @@ export default function LogicVMFlow() {
10991

11092
const [edges, setEdges, onEdgesChange] = useEdgesState<Edge>(defaultEdges);
11193

112-
// abort deletions that would remove nodes
113-
const onBeforeDelete = useCallback(
114-
// eslint-disable-next-line @typescript-eslint/require-await
115-
async ({ nodes }: { nodes: LogicVMNode[] }) => nodes.length === 0,
116-
[],
117-
);
118-
11994
const onConnect = useCallback(
12095
(params: Edge | Connection) => {
12196
// https://github.com/xyflow/xyflow/blob/a75087e8d3a6ea0731f5bd2331027dc89edce85c/packages/system/src/utils/edges/general.ts#L91
@@ -208,13 +183,15 @@ export default function LogicVMFlow() {
208183
defaultEdgeOptions={defaultEdgeOptions}
209184
onNodesChange={onNodesChange}
210185
onEdgesChange={onEdgesChange}
211-
onBeforeDelete={onBeforeDelete}
212186
onConnect={onConnect}
213187
connectionLineComponent={BuildingLinkConnectionLine}
188+
proOptions={{ hideAttribution: true }}
189+
nodeOrigin={[0.5, 0.5]}
214190
fitView
215191
>
216192
<Background variant={BackgroundVariant.Dots} />
217193
<Controls />
194+
<AddBuildingMenu />
218195
</ReactFlow>
219196
);
220197
}

mindy-website/www/src/components/nodes/BuildingNode.tsx

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Card, Flex, Group, Text } from "@mantine/core";
1+
import { Card, Divider, Flex, Group, Text } from "@mantine/core";
22
import {
33
Handle,
44
Position,
@@ -45,25 +45,28 @@ export default function BuildingNode({
4545
)}
4646
<Card className={classes.node} withBorder radius="md">
4747
<Card.Section withBorder>
48-
<Group justify="space-between" align="stretch" gap="xs">
48+
<Group justify="flex-start" align="stretch" gap={0}>
4949
<Text px="sm" py={6} fw={500} ff="monospace">
5050
{name ?? "unknown-building"}
5151
</Text>
5252
{linkSource && (
53-
<Flex
54-
px="xs"
55-
justify="center"
56-
align="center"
57-
pos="relative"
58-
>
59-
<Handle
60-
className={classes.sourceHandle}
61-
position={Position.Right}
62-
type="source"
63-
isConnectableEnd={false}
64-
/>
65-
<TbPlugConnected />
66-
</Flex>
53+
<>
54+
<Divider orientation="vertical" ml="auto" />
55+
<Flex
56+
px="sm"
57+
justify="center"
58+
align="center"
59+
pos="relative"
60+
>
61+
<Handle
62+
className={classes.sourceHandle}
63+
position={Position.Right}
64+
type="source"
65+
isConnectableEnd={false}
66+
/>
67+
<TbPlugConnected />
68+
</Flex>
69+
</>
6770
)}
6871
</Group>
6972
</Card.Section>

mindy-website/www/src/components/nodes/DisplayNode.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@ import { Box, Card } from "@mantine/core";
22
import type { Node, NodeProps } from "@xyflow/react";
33
import { useEffect, useRef } from "react";
44

5+
import type { DisplayKind } from "mindy-website";
6+
57
import { useLogicVM } from "../../hooks";
68
import BuildingNode, { type BuildingNodeData } from "./BuildingNode";
79
import classes from "./DisplayNode.module.css";
810

911
type DisplayNodeData = BuildingNodeData & {
12+
kind: DisplayKind;
1013
displayWidth: number;
1114
displayHeight: number;
1215
};
@@ -15,7 +18,7 @@ export type DisplayNodeType = Node<DisplayNodeData, "display">;
1518

1619
export default function DisplayNode(props: NodeProps<DisplayNodeType>) {
1720
const {
18-
data: { position, displayWidth, displayHeight },
21+
data: { position, kind, displayWidth, displayHeight },
1922
} = props;
2023

2124
const vm = useLogicVM();
@@ -31,6 +34,7 @@ export default function DisplayNode(props: NodeProps<DisplayNodeType>) {
3134
{
3235
type: "addDisplay",
3336
position,
37+
kind,
3438
width: displayWidth,
3539
height: displayHeight,
3640
canvas: offscreenCanvas,
@@ -41,7 +45,7 @@ export default function DisplayNode(props: NodeProps<DisplayNodeType>) {
4145
return () => {
4246
vm.postMessage({ type: "removeBuilding", position });
4347
};
44-
}, [vm, position, displayWidth, displayHeight]);
48+
}, [vm, position, kind, displayWidth, displayHeight]);
4549

4650
return (
4751
<BuildingNode {...props}>

0 commit comments

Comments
 (0)