diff --git a/docs/simple_electronics.wasm b/docs/simple_electronics.wasm index 1f99f17..54ef842 100755 Binary files a/docs/simple_electronics.wasm and b/docs/simple_electronics.wasm differ diff --git a/src/components.rs b/src/components.rs index dbd13b4..757f63f 100644 --- a/src/components.rs +++ b/src/components.rs @@ -1,13 +1,10 @@ -use core::marker::PhantomData; use macroquad::prelude::Vec2; use specs::{prelude::*, Component}; pub mod nodes; -pub trait Node { - fn calculate_state(inputs: [bool; I]) -> [bool; O] - where - Self: Sized; +pub trait Node: Default { + fn calculate_state(&self, inputs: [bool; I]) -> [bool; O]; } #[derive(Component)] @@ -15,18 +12,27 @@ pub struct Connected where N: Node + 'static, { - pub node: PhantomData, + pub node: N, pub inputs: [Option; I], pub outputs: [Option; O], } +impl Connected +where + N: Node + 'static, +{ + pub fn calculate_state(&self, inputs: [bool; I]) -> [bool; O] { + self.node.calculate_state(inputs) + } +} + impl Default for Connected where N: Node + 'static, { fn default() -> Self { Connected { - node: PhantomData::, + node: N::default(), inputs: [None; I], outputs: [None; O], } diff --git a/src/components/nodes.rs b/src/components/nodes.rs index 55f114d..e997c5d 100644 --- a/src/components/nodes.rs +++ b/src/components/nodes.rs @@ -11,7 +11,7 @@ pub struct Wire { } impl Node<1, 1> for Wire { - fn calculate_state(i: [bool; 1]) -> [bool; 1] { + fn calculate_state(&self, i: [bool; 1]) -> [bool; 1] { i } } @@ -28,12 +28,13 @@ pub enum NodeTy { NorNode, XorNode, XnorNode, + SwitchNode, } #[derive(Default)] pub struct OnNode; impl Node<0, 1> for OnNode { - fn calculate_state(_: [bool; 0]) -> [bool; 1] { + fn calculate_state(&self, _: [bool; 0]) -> [bool; 1] { [true] } } @@ -41,7 +42,7 @@ impl Node<0, 1> for OnNode { #[derive(Default)] pub struct OffNode; impl Node<0, 1> for OffNode { - fn calculate_state(_: [bool; 0]) -> [bool; 1] { + fn calculate_state(&self, _: [bool; 0]) -> [bool; 1] { [false] } } @@ -49,7 +50,7 @@ impl Node<0, 1> for OffNode { #[derive(Default)] pub struct NotNode; impl Node<1, 1> for NotNode { - fn calculate_state(input: [bool; 1]) -> [bool; 1] { + fn calculate_state(&self, input: [bool; 1]) -> [bool; 1] { [!input[0]] } } @@ -57,7 +58,7 @@ impl Node<1, 1> for NotNode { #[derive(Default)] pub struct AndNode; impl Node<2, 1> for AndNode { - fn calculate_state(input: [bool; 2]) -> [bool; 1] { + fn calculate_state(&self, input: [bool; 2]) -> [bool; 1] { [input[0] && input[1]] } } @@ -65,7 +66,7 @@ impl Node<2, 1> for AndNode { #[derive(Default)] pub struct OrNode; impl Node<2, 1> for OrNode { - fn calculate_state(input: [bool; 2]) -> [bool; 1] { + fn calculate_state(&self, input: [bool; 2]) -> [bool; 1] { [input[0] || input[1]] } } @@ -73,7 +74,7 @@ impl Node<2, 1> for OrNode { #[derive(Default)] pub struct NandNode; impl Node<2, 1> for NandNode { - fn calculate_state(input: [bool; 2]) -> [bool; 1] { + fn calculate_state(&self, input: [bool; 2]) -> [bool; 1] { [!(input[0] && input[1])] } } @@ -81,7 +82,7 @@ impl Node<2, 1> for NandNode { #[derive(Default)] pub struct NorNode; impl Node<2, 1> for NorNode { - fn calculate_state(input: [bool; 2]) -> [bool; 1] { + fn calculate_state(&self, input: [bool; 2]) -> [bool; 1] { [!(input[0] || input[1])] } } @@ -89,7 +90,7 @@ impl Node<2, 1> for NorNode { #[derive(Default)] pub struct XorNode; impl Node<2, 1> for XorNode { - fn calculate_state(input: [bool; 2]) -> [bool; 1] { + fn calculate_state(&self, input: [bool; 2]) -> [bool; 1] { [input[0] ^ input[1]] } } @@ -97,11 +98,22 @@ impl Node<2, 1> for XorNode { #[derive(Default)] pub struct XnorNode; impl Node<2, 1> for XnorNode { - fn calculate_state(input: [bool; 2]) -> [bool; 1] { + fn calculate_state(&self, input: [bool; 2]) -> [bool; 1] { [!(input[0] ^ input[1])] } } +#[derive(Default)] +pub struct SwitchNode { + pub state: bool, +} + +impl Node<0, 1> for SwitchNode { + fn calculate_state(&self, _input: [bool; 0]) -> [bool; 1] { + [self.state] + } +} + #[macro_export] macro_rules! all_nodes { ($macro:ident) => { @@ -116,6 +128,7 @@ macro_rules! all_nodes { [NorNode, 2, 1], [XorNode, 2, 1], [XnorNode, 2, 1], + [SwitchNode, 0, 1], ) }; } diff --git a/src/main.rs b/src/main.rs index 929eaec..a7effeb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ use crate::nodes::Wire; use crate::resources::UiSignal; -use core::marker::PhantomData; use egui_macroquad; use macroquad::prelude::*; use specs::prelude::*; @@ -99,7 +98,7 @@ async fn main() { world .create_entity() .with(Connected { - node: PhantomData::, + node: nodes::SwitchNode::default(), inputs: [], outputs: [Some(wire_1)], }) @@ -109,7 +108,7 @@ async fn main() { world .create_entity() .with(Connected { - node: PhantomData::, + node: nodes::SwitchNode { state: true }, inputs: [], outputs: [Some(wire_2)], }) @@ -119,7 +118,7 @@ async fn main() { world .create_entity() .with(Connected { - node: PhantomData::, + node: nodes::XorNode::default(), inputs: [Some(wire_1), Some(wire_2)], outputs: [Some(wire_3)], }) @@ -129,7 +128,7 @@ async fn main() { world .create_entity() .with(Connected { - node: PhantomData::, + node: nodes::AndNode::default(), inputs: [Some(wire_2), Some(wire_1)], outputs: [Some(wire_4)], }) @@ -137,6 +136,7 @@ async fn main() { .build(); world.insert(resources::Tick(0)); + world.insert(resources::TickFrames(60)); let mut last_fps = [60i32; 256]; @@ -146,7 +146,7 @@ async fn main() { last_fps[i % last_fps.len()] = get_fps(); // let tick_frames: usize = (last_fps.iter().sum::() / last_fps.len() as i32) as usize; - let tick_frames = 144; + let tick_frames = world.fetch::().0; world.insert(resources::TickProgress( (i % tick_frames) as f64 / tick_frames as f64, diff --git a/src/resources.rs b/src/resources.rs index a7b48d4..91a87a9 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -35,6 +35,9 @@ impl Tick { } } +#[derive(Clone, Copy, Default)] +pub struct TickFrames(pub usize); + #[derive(Eq, PartialEq, Copy, Clone)] pub enum GridMode { Lines, diff --git a/src/systems/draw_systems.rs b/src/systems/draw_systems.rs index 1270f09..28d2814 100644 --- a/src/systems/draw_systems.rs +++ b/src/systems/draw_systems.rs @@ -1,3 +1,4 @@ +use crate::nodes::SwitchNode; use crate::Wire; use crate::{components::nodes::NandNode, nodes::NotNode}; use crate::{components::nodes::NorNode, nodes::OnNode}; @@ -22,7 +23,7 @@ where N: Node + 'static, { node: PhantomData, - draw_fn: Arc, + draw_fn: Arc, input_offsets: [Vec2; I], } @@ -220,7 +221,7 @@ where } } }); - (self.draw_fn)(*self_pos, &textures); + (self.draw_fn)(&node.node, *self_pos, &textures); }); } } @@ -336,21 +337,21 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher .with_thread_local(DrawGridSys) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, _| { + draw_fn: Arc::new(|_, Pos { pos, .. }, _| { draw_circle(pos.x, pos.y, 25.0, RED); }), input_offsets: [], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, _| { + draw_fn: Arc::new(|_, Pos { pos, .. }, _| { draw_circle(pos.x, pos.y, 25.0, WHITE); }), input_offsets: [], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, textures: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, textures: &Textures| { let texture = textures.0.get("NOT_GATE").unwrap(); let w = 50.0; let h = 50.0; @@ -369,7 +370,7 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, textures: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, textures: &Textures| { let texture = textures.0.get("AND_GATE").unwrap(); let w = 75.0; let h = 50.0; @@ -384,11 +385,11 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher }, ); }), - input_offsets: [Vec2::new(-25.0, -10.0), Vec2::new(-25.0, 10.0)], + input_offsets: [Vec2::new(-25.0, -15.0), Vec2::new(-25.0, 15.0)], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, textures: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, textures: &Textures| { let texture = textures.0.get("OR_GATE").unwrap(); let w = 100.0; let h = 75.0; @@ -403,11 +404,11 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher }, ); }), - input_offsets: [Vec2::new(-25.0, -10.0), Vec2::new(-25.0, 10.0)], + input_offsets: [Vec2::new(-25.0, -15.0), Vec2::new(-25.0, 15.0)], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, textures: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, textures: &Textures| { let texture = textures.0.get("NAND_GATE").unwrap(); let w = 100.0; let h = 75.0; @@ -422,11 +423,11 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher }, ); }), - input_offsets: [Vec2::new(-25.0, -10.0), Vec2::new(-25.0, 10.0)], + input_offsets: [Vec2::new(-25.0, -15.0), Vec2::new(-25.0, 15.0)], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, textures: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, textures: &Textures| { let texture = textures.0.get("NOR_GATE").unwrap(); let w = 100.0; let h = 75.0; @@ -441,11 +442,11 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher }, ); }), - input_offsets: [Vec2::new(-25.0, -10.0), Vec2::new(-25.0, 10.0)], + input_offsets: [Vec2::new(-25.0, -15.0), Vec2::new(-25.0, 15.0)], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, textures: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, textures: &Textures| { let texture = textures.0.get("XOR_GATE").unwrap(); let w = 100.0; let h = 75.0; @@ -460,11 +461,11 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher }, ); }), - input_offsets: [Vec2::new(-25.0, -10.0), Vec2::new(-25.0, 10.0)], + input_offsets: [Vec2::new(-26.0, -15.0), Vec2::new(-26.0, 15.0)], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, textures: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, textures: &Textures| { let texture = textures.0.get("XNOR_GATE").unwrap(); let w = 100.0; let h = 75.0; @@ -479,13 +480,23 @@ pub fn add_draw_system<'a, 'b>(builder: DispatcherBuilder<'a, 'b>) -> Dispatcher }, ); }), - input_offsets: [Vec2::new(-25.0, -10.0), Vec2::new(-25.0, 10.0)], + input_offsets: [Vec2::new(-26.5, -15.0), Vec2::new(-26.5, 15.0)], }) .with_thread_local(DrawNodeSys { node: PhantomData::, - draw_fn: Arc::new(|Pos { pos, .. }, _: &Textures| { + draw_fn: Arc::new(|_, Pos { pos, .. }, _: &Textures| { draw_circle(pos.x, pos.y, 10.0, WHITE); }), input_offsets: [Vec2::new(0.0, 0.0)], }) + .with_thread_local(DrawNodeSys { + node: PhantomData::, + draw_fn: Arc::new(|node: &SwitchNode, Pos { pos, .. }, _: &Textures| { + let color = if node.state { RED } else { WHITE }; + draw_rectangle(pos.x - 30.0, pos.y - 30.0, 60.0, 60.0, WHITE); + draw_circle(pos.x, pos.y, 25.0, color); + draw_circle_lines(pos.x, pos.y, 25.0, 2.5, BLACK); + }), + input_offsets: [], + }) } diff --git a/src/systems/simulation_systems.rs b/src/systems/simulation_systems.rs index 189cf4c..140b7d2 100644 --- a/src/systems/simulation_systems.rs +++ b/src/systems/simulation_systems.rs @@ -42,7 +42,7 @@ where } } - let outputs = N::calculate_state(inputs); + let outputs = node.calculate_state(inputs); for (i, output_entity) in node.outputs.iter().enumerate() { if let Some(e) = output_entity { diff --git a/src/systems/ui_systems.rs b/src/systems/ui_systems.rs index 096a2b4..5aa1d41 100644 --- a/src/systems/ui_systems.rs +++ b/src/systems/ui_systems.rs @@ -1,5 +1,10 @@ +use crate::nodes::SwitchNode; +use crate::Connected; +use crate::Pos; use specs::prelude::*; +use macroquad::prelude::*; + use crate::resources::{AddingNode, AddingWire, CurrentModeText}; pub struct CurrentModeSys; @@ -35,3 +40,26 @@ impl<'a> System<'a> for CurrentModeSys { *current_mode = CurrentModeText::default(); } } + +pub struct SwitchClickSys; +impl<'a> System<'a> for SwitchClickSys { + type SystemData = ( + WriteStorage<'a, Connected>, + ReadStorage<'a, Pos>, + ); + + fn run(&mut self, (mut switches, positions): Self::SystemData) { + let mouse_pos = { + let (mx, my) = mouse_position(); + Vec2::new(mx, my) + }; + + let target_switch = (&mut switches, &positions) + .join() + .find(|(_, pos)| dbg!(pos.pos - mouse_pos).length() < 35.0); + + if let Some((s, _)) = target_switch { + s.node.state = !s.node.state; + } + } +} diff --git a/src/ui/mouse_click.rs b/src/ui/mouse_click.rs index e715244..9caa4b4 100644 --- a/src/ui/mouse_click.rs +++ b/src/ui/mouse_click.rs @@ -8,16 +8,22 @@ use crate::nodes; use crate::resources; pub fn handle_mouse_click(world: &mut World) { + crate::systems::ui_systems::SwitchClickSys.run_now(world); + // clear adding wire including removing the wire entity { let adding_wire_state = world.fetch::().0; if let Some((_, wire_entity, _, _)) = adding_wire_state { - dbg!(wire_entity); - world.delete_entity(wire_entity).unwrap(); + let wire_placed = { + let position_storage = world.read_storage::(); + position_storage.get(wire_entity).is_some() + }; - crate::systems::cleanup_sys::run_cleanup_sys(world); + if !wire_placed { + world.delete_entity(wire_entity).unwrap(); - std::mem::drop(adding_wire_state); + crate::systems::cleanup_sys::run_cleanup_sys(world); + } world.insert(AddingWire(None)); } } diff --git a/src/ui/top_panel.rs b/src/ui/top_panel.rs index 1ff232a..f185255 100644 --- a/src/ui/top_panel.rs +++ b/src/ui/top_panel.rs @@ -32,6 +32,7 @@ pub fn render_top_panel(ui: &mut egui::Ui, world: &mut World) { node_button!("Nor Node", NorNode); node_button!("Xor Node", XorNode); node_button!("Xnor Node", XnorNode); + node_button!("Switch Node", SwitchNode); }); if ui.button("Restart Sim").clicked() || is_key_pressed(KeyCode::Space) { @@ -54,9 +55,15 @@ pub fn render_top_panel(ui: &mut egui::Ui, world: &mut World) { }); ui.with_layout(Layout::right_to_left(), |ui| { - ui.label("\t\t"); - let current_mode = world.fetch::(); - ui.label(¤t_mode.0); + { + ui.label("\t\t"); + let current_mode = world.fetch::(); + ui.add(egui::Label::new(¤t_mode.0).wrap(true)); + } + + let mut tick_frames = world.fetch::().0; + ui.add(egui::Slider::usize(&mut tick_frames, 1..=240).text("Frames per Tick")); + world.insert(resources::TickFrames(tick_frames)); }); }); }