Skip to content

Commit cc6a1f2

Browse files
committed
Implement React Flow nodes for processor and display
1 parent 0930b6b commit cc6a1f2

20 files changed

+961
-193
lines changed

mindy-website/src/lib.rs

Lines changed: 132 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
#![allow(clippy::boxed_local)]
22

3-
use std::time::Duration;
3+
use std::{collections::HashMap, time::Duration};
44

55
use embedded_graphics::pixelcolor::Rgb888;
66
use embedded_graphics_web_simulator::{
77
display::WebSimulatorDisplay, output_settings::OutputSettings,
88
};
9+
use js_sys::JsString;
910
use mindy::{
1011
parser::LogicParser,
1112
types::{PackedPoint2, ProcessorConfig, ProcessorLinkConfig, content},
1213
vm::{
1314
Building, BuildingData, EmbeddedDisplayData, InstructionResult, LVar, LogicVM,
14-
LogicVMBuilder,
1515
buildings::{HYPER_PROCESSOR, LOGIC_PROCESSOR, MICRO_PROCESSOR, WORLD_PROCESSOR},
1616
variables::Constants,
1717
},
@@ -36,6 +36,18 @@ pub fn init() {
3636
console_error_panic_hook::set_once();
3737
}
3838

39+
#[wasm_bindgen]
40+
pub fn pack_point(x: i16, y: i16) -> u32 {
41+
((y as u32) << 16) | (x as u32)
42+
}
43+
44+
fn unpack_point(position: u32) -> PackedPoint2 {
45+
PackedPoint2 {
46+
x: position as i16,
47+
y: (position >> 16) as i16,
48+
}
49+
}
50+
3951
#[wasm_bindgen]
4052
pub struct WebLogicVM {
4153
vm: LogicVM,
@@ -44,48 +56,90 @@ pub struct WebLogicVM {
4456
prev_timestamp: Option<f64>,
4557
}
4658

59+
#[wasm_bindgen]
4760
impl WebLogicVM {
48-
fn new(vm: LogicVM, globals: Constants) -> Self {
61+
#[wasm_bindgen(constructor)]
62+
pub fn new() -> Self {
4963
Self {
50-
vm,
51-
globals,
64+
vm: LogicVM::new(),
65+
globals: LVar::create_global_constants(),
5266
logic_parser: LogicParser::new(),
5367
prev_timestamp: None,
5468
}
5569
}
56-
}
5770

58-
#[wasm_bindgen]
59-
impl WebLogicVM {
60-
pub fn do_tick(&mut self, timestamp: f64) {
61-
// convert to seconds
62-
let timestamp = timestamp / 1000.;
63-
64-
// nominal 60 ticks per second
65-
let delta = match self.prev_timestamp {
66-
Some(prev_timestamp) => (timestamp - prev_timestamp) * 60.,
67-
None => 1.,
68-
}
69-
.min(MAX_DELTA);
71+
#[wasm_bindgen(getter)]
72+
pub fn time(&self) -> f64 {
73+
self.vm.time().as_secs_f64()
74+
}
7075

71-
self.prev_timestamp = Some(timestamp);
76+
pub fn add_processor(
77+
&mut self,
78+
position: u32,
79+
kind: ProcessorKind,
80+
code: String,
81+
) -> Result<(), String> {
82+
let position = unpack_point(position);
7283
self.vm
73-
.do_tick_with_delta(Duration::from_secs_f64(timestamp), delta);
84+
.add_building(
85+
Building::from_processor_config(
86+
kind.name(),
87+
position,
88+
&ProcessorConfig {
89+
code,
90+
links: vec![],
91+
},
92+
&self.vm,
93+
)
94+
.map_err(|e| e.to_string())?,
95+
&self.globals,
96+
)
97+
.map_err(|e| e.to_string())
7498
}
7599

76-
#[wasm_bindgen(getter)]
77-
pub fn time(&self) -> f64 {
78-
self.vm.time().as_secs_f64()
100+
pub fn add_display(
101+
&mut self,
102+
position: u32,
103+
width: u32,
104+
height: u32,
105+
parent: &Element,
106+
) -> Result<(), String> {
107+
let display = WebSimulatorDisplay::<Rgb888>::new(
108+
(width, height),
109+
&OutputSettings::default(),
110+
Some(parent),
111+
);
112+
113+
let display_data = EmbeddedDisplayData::new(
114+
display,
115+
Some(Box::new(|display| {
116+
display.flush().expect("failed to flush display");
117+
InstructionResult::Ok
118+
})),
119+
)
120+
.expect("failed to initialize display");
121+
122+
self.vm
123+
.add_building(
124+
Building::new(
125+
content::blocks::FROM_NAME["tile-logic-display"],
126+
unpack_point(position),
127+
display_data.into(),
128+
),
129+
&self.globals,
130+
)
131+
.map_err(|e| e.to_string())
79132
}
80133

81134
pub fn set_processor_config(
82135
&mut self,
83-
position: PackedPoint2,
136+
position: u32,
84137
code: &str,
85-
links: Option<Box<[PackedPoint2]>>,
86-
) -> Result<(), String> {
138+
links: Box<[u32]>,
139+
) -> Result<LinkNames, String> {
87140
let ast = self.logic_parser.parse(code).map_err(|e| e.to_string())?;
88141

142+
let position = unpack_point(position);
89143
let building = self
90144
.vm
91145
.building(position)
@@ -101,88 +155,60 @@ impl WebLogicVM {
101155
processor
102156
.update_config(
103157
ast,
104-
links
105-
.map(|v| {
106-
v.iter()
107-
.map(|p| {
108-
ProcessorLinkConfig::unnamed(p.x - position.x, p.y - position.y)
109-
})
110-
.collect::<Vec<_>>()
111-
})
112-
.as_deref(),
158+
Some(
159+
&links
160+
.iter()
161+
.map(|p| {
162+
let p = unpack_point(*p);
163+
ProcessorLinkConfig::unnamed(p.x - position.x, p.y - position.y)
164+
})
165+
.collect::<Vec<_>>(),
166+
),
113167
&self.vm,
114168
building,
115169
&self.globals,
116170
)
117-
.map_err(|e| e.to_string())
171+
.map_err(|e| e.to_string())?;
172+
173+
Ok(LinkNames(
174+
processor
175+
.state
176+
.links()
177+
.iter()
178+
.map(|l| {
179+
(
180+
pack_point(l.building.position.x, l.building.position.y),
181+
l.name.clone(),
182+
)
183+
})
184+
.collect(),
185+
))
118186
}
119-
}
120187

121-
#[wasm_bindgen]
122-
pub struct WebLogicVMBuilder {
123-
builder: LogicVMBuilder,
124-
}
125-
126-
#[wasm_bindgen]
127-
impl WebLogicVMBuilder {
128-
#[wasm_bindgen(constructor)]
129-
pub fn new() -> Self {
130-
Self {
131-
builder: LogicVMBuilder::new(),
132-
}
133-
}
134-
135-
pub fn add_processor(&mut self, position: PackedPoint2, kind: ProcessorKind) {
136-
self.builder.add_building(
137-
Building::from_processor_config(
138-
kind.name(),
139-
position,
140-
&ProcessorConfig::default(),
141-
&self.builder,
142-
)
143-
.expect("failed to create processor"),
144-
);
188+
pub fn building_name(&self, position: u32) -> Option<JsString> {
189+
self.vm
190+
.building(unpack_point(position))
191+
.map(|b| JsString::from_char_code(b.block.name.as_u16str().as_slice()))
145192
}
146193

147-
pub fn add_display(
148-
&mut self,
149-
position: PackedPoint2,
150-
width: u32,
151-
height: u32,
152-
parent: &Element,
153-
) {
154-
let display = WebSimulatorDisplay::<Rgb888>::new(
155-
(width, height),
156-
&OutputSettings::default(),
157-
Some(parent),
158-
);
159-
160-
let display_data = EmbeddedDisplayData::new(
161-
display,
162-
Some(Box::new(|display| {
163-
display.flush().expect("failed to flush display");
164-
InstructionResult::Yield
165-
})),
166-
)
167-
.expect("failed to initialize display");
168-
169-
self.builder.add_building(Building::new(
170-
content::blocks::FROM_NAME["tile-logic-display"],
171-
position,
172-
display_data.into(),
173-
));
174-
}
194+
pub fn do_tick(&mut self, timestamp: f64) {
195+
// convert to seconds
196+
let timestamp = timestamp / 1000.;
175197

176-
pub fn build(self) -> Result<WebLogicVM, String> {
177-
let globals = LVar::create_global_constants();
178-
match self.builder.build_with_globals(&globals) {
179-
Ok(vm) => Ok(WebLogicVM::new(vm, globals)),
180-
Err(e) => Err(e.to_string()),
198+
// nominal 60 ticks per second
199+
let delta = match self.prev_timestamp {
200+
Some(prev_timestamp) => (timestamp - prev_timestamp) * 60.,
201+
None => 1.,
181202
}
203+
.min(MAX_DELTA);
204+
205+
self.prev_timestamp = Some(timestamp);
206+
self.vm
207+
.do_tick_with_delta(Duration::from_secs_f64(timestamp), delta);
182208
}
183209
}
184210

185-
impl Default for WebLogicVMBuilder {
211+
impl Default for WebLogicVM {
186212
fn default() -> Self {
187213
Self::new()
188214
}
@@ -206,3 +232,14 @@ impl ProcessorKind {
206232
}
207233
}
208234
}
235+
236+
#[wasm_bindgen]
237+
pub struct LinkNames(HashMap<u32, String>);
238+
239+
#[wasm_bindgen]
240+
impl LinkNames {
241+
#[wasm_bindgen(indexing_getter)]
242+
pub fn get(&self, position: u32) -> Option<String> {
243+
self.0.get(&position).cloned()
244+
}
245+
}

mindy-website/www/eslint.config.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,16 @@ export default tseslint.config([
2121
reactDom.configs.recommended,
2222
],
2323
rules: {
24-
"no-unused-vars": "warn",
25-
"@typescript-eslint/no-unused-vars": "warn",
24+
"no-unused-vars": "off",
25+
"@typescript-eslint/no-unused-vars": [
26+
"warn",
27+
{
28+
argsIgnorePattern: "^_[^_].*$|^_$",
29+
varsIgnorePattern: "^_[^_].*$|^_$",
30+
caughtErrorsIgnorePattern: "^_[^_].*$|^_$",
31+
},
32+
],
33+
"@typescript-eslint/consistent-type-definitions": "off",
2634
},
2735
languageOptions: {
2836
parserOptions: {

mindy-website/www/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
"dependencies": {
1414
"@mantine/core": "^8.2.4",
1515
"@mantine/hooks": "^8.2.4",
16+
"@xyflow/react": "^12.8.3",
1617
"mindy-website": "file:../pkg",
1718
"react": "^19.1.1",
18-
"react-dom": "^19.1.1"
19+
"react-dom": "^19.1.1",
20+
"react-icons": "^5.5.0"
1921
},
2022
"devDependencies": {
2123
"@eslint/js": "^9.33.0",

mindy-website/www/src/App.module.css

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

0 commit comments

Comments
 (0)