From 1ee983e4307ebcb3fdb024a2b320e3514251a124 Mon Sep 17 00:00:00 2001 From: OMGeeky Date: Mon, 14 Apr 2025 22:08:29 +0200 Subject: [PATCH] improve simulation structure & start implementing wires --- src/logic_sim/mod.rs | 264 ++++++++++++++++++++++++++++++++----------- 1 file changed, 200 insertions(+), 64 deletions(-) diff --git a/src/logic_sim/mod.rs b/src/logic_sim/mod.rs index 329eee4..624a6f6 100644 --- a/src/logic_sim/mod.rs +++ b/src/logic_sim/mod.rs @@ -43,11 +43,27 @@ pub struct BlockVisuals { color: Color, } #[derive(Component, Debug)] +pub struct ConnectionReference(Entity); +#[derive(Component, Debug)] +pub struct WireReference(Entity); +#[derive(Component, Debug)] pub struct Block { - inputs: Vec, - outputs: Vec, + inputs: Vec, + outputs: Vec, + wires: Vec, } -#[derive(Debug)] +#[derive(Component, Debug)] +#[require(Transform)] +pub struct Wire { + connections: Vec, +} +#[derive(Component, Debug)] +pub struct InputConnection; +#[derive(Component, Debug)] +pub struct OutputConnection; + +#[derive(Component, Debug)] +#[require(Transform)] pub struct Connection { size: u32, values: Vec, @@ -56,8 +72,16 @@ pub struct Connection { pub struct LogicSimPlugin; impl Plugin for LogicSimPlugin { fn build(&self, app: &mut App) { - app.add_systems(Startup, setup) - .add_systems(Update, (render_blocks, scale_labels)); + app.add_systems(Startup, setup).add_systems( + Update, + ( + (update_connection_positions, draw_connections).chain(), + render_blocks, + scale_labels, + draw_wires, + update_connection_states, + ), + ); } } fn setup(mut commands: Commands, asset_server: Res) { @@ -69,44 +93,80 @@ fn setup(mut commands: Commands, asset_server: Res) { }; let size = IVec2::new(50, 80); + + //region connections + let input1 = ( + InputConnection, + Connection { + size: 1, + values: vec![false], + }, + ); + let input2 = ( + InputConnection, + Connection { + size: 1, + values: vec![true], + }, + ); + let output1 = ( + OutputConnection, + Connection { + size: 1, + values: vec![false], + }, + ); + let output2 = ( + OutputConnection, + Connection { + size: 1, + values: vec![true], + }, + ); + let output3 = ( + OutputConnection, + Connection { + size: 1, + values: vec![true], + }, + ); + let output4 = ( + OutputConnection, + Connection { + size: 1, + values: vec![false], + }, + ); + let input1 = commands.spawn(input1).id(); + let input2 = commands.spawn(input2).id(); + let output1 = commands.spawn(output1).id(); + let output2 = commands.spawn(output2).id(); + let output3 = commands.spawn(output3).id(); + let output4 = commands.spawn(output4).id(); + //endregion + let wire1 = Wire { + connections: vec![ + ConnectionReference(input1), + ConnectionReference(output1), + ConnectionReference(output2), + ], + }; + let wire1 = commands.spawn(wire1).id(); commands .spawn(BlockBundle { - // name: Text2d("AND".into()), block_visuals: BlockVisuals { size, color: Color::from(RED), }, block: Block { - inputs: vec![ - Connection { - size: 10, - values: vec![ - false, true, false, true, false, true, false, true, false, true, - ], - }, - Connection { - size: 9, - values: vec![false, true, false, true, false, true, false, true, false], - }, - ], + inputs: vec![ConnectionReference(input1), ConnectionReference(input2)], outputs: vec![ - Connection { - size: 1, - values: vec![false], - }, - Connection { - size: 2, - values: vec![true, false], - }, - Connection { - size: 3, - values: vec![true, false, true], - }, - Connection { - size: 4, - values: vec![true, false, true, false], - }, + ConnectionReference(output1), + ConnectionReference(output2), + ConnectionReference(output3), + ConnectionReference(output4), ], + wires: vec![WireReference(wire1)], }, global_transform: GlobalTransform::default(), transform: Transform::default(), @@ -114,9 +174,22 @@ fn setup(mut commands: Commands, asset_server: Res) { .with_child(BlockLabelBundle::new("AND", size, text_font)); } fn render_blocks( - blocks: Query<(&BlockVisuals, &Block, &GlobalTransform)>, + blocks: Query<(&BlockVisuals, &Block, &Transform)>, + connections: Query<&mut Connection>, canvas: Res, mut gizmos: Gizmos, +) { + for (block_visual, block, transform) in blocks.iter() { + let size = block_visual.size.as_vec2() * canvas.zoom; + let center = transform.translation.xy(); + gizmos.rect_2d(center, size, block_visual.color); + } +} + +fn update_connection_positions( + blocks: Query<(&BlockVisuals, &Block, &GlobalTransform)>, + mut connections: Query<&mut Transform, With>, + canvas: Res, ) { for (block_visual, block, transform) in blocks.iter() { let size = block_visual.size.as_vec2() * canvas.zoom; @@ -125,41 +198,56 @@ fn render_blocks( let top = center.y + half_size.y; let left = center.x - half_size.x; let right = center.x + half_size.x; - gizmos.rect_2d(center, size, block_visual.color); - draw_connections(left, top, size.y, &block.inputs, &mut gizmos, &canvas); - draw_connections(right, top, size.y, &block.outputs, &mut gizmos, &canvas); + + update_connection_position( + Vec2::new(left, top), + size.y, + &canvas, + block.inputs.iter(), + &mut connections, + block.inputs.len(), + ); + update_connection_position( + Vec2::new(right, top), + size.y, + &canvas, + block.outputs.iter(), + &mut connections, + block.outputs.len(), + ); + } +} +fn update_connection_position<'a>( + pos: Vec2, + available_height: f32, + canvas: &Canvas, + connection_refs: impl Iterator, + connections: &mut Query<&mut Transform, With>, + connections_count: usize, +) { + let spacing = available_height / (connections_count + 1) as f32; + + for (i, connection) in connection_refs.enumerate() { + let pos = pos - Vec2::Y * spacing * (i + 1) as f32; + if let Ok(mut transform) = connections.get_mut(connection.0) { + transform.translation = pos.extend(0.0); + } } } fn draw_connections( - x: f32, - y: f32, - available_height: f32, - connections: &[Connection], - gizmos: &mut Gizmos, - canvas: &Canvas, + connections: Query<(&Connection, &GlobalTransform)>, + canvas: Res, + mut gizmos: Gizmos, ) { - let input_count = connections.len(); - let input_spacing = available_height / (input_count + 1) as f32; - // println!("{} -> {}", input_count, input_spacing); - for (i, connection) in connections.iter().enumerate() { - draw_connection(x, y, gizmos, input_spacing, i, connection, canvas); + for (connection, transform) in connections.iter() { + let pos = transform.translation().xy(); + draw_connection(pos, connection, &canvas, &mut gizmos); } } - -fn draw_connection( - x: f32, - y: f32, - gizmos: &mut Gizmos, - input_spacing: f32, - i: usize, - - connection: &Connection, - canvas: &Canvas, -) { +fn draw_connection(pos: Vec2, connection: &Connection, canvas: &Canvas, gizmos: &mut Gizmos) { const CONNECTION_BIT_SIZE: f32 = 10.0; let connection_bit_size = CONNECTION_BIT_SIZE * canvas.zoom; let connection_bit_half_size = connection_bit_size * 0.5; - let connection_pos = Vec2::new(x, y - input_spacing * (i + 1) as f32); let rows = if connection.size > 8 { 2 } else { 1 }; let columns = (connection.size as f32 / rows as f32).ceil() as u32; @@ -174,8 +262,7 @@ fn draw_connection( if index >= connection.size { break 'rows; } - let pos = connection_pos - + Vec2::new(x as f32, (rows - y - 1) as f32) * connection_bit_size + let pos = pos + Vec2::new(x as f32, (rows - y - 1) as f32) * connection_bit_size - half_offset + half_one_size; let value = connection.values[index as usize]; @@ -184,14 +271,63 @@ fn draw_connection( } } gizmos.rect_2d( - connection_pos, + pos, Vec2::new(columns as f32, rows as f32) * connection_bit_size, WHITE, ); } +fn draw_wires( + wires: Query<(&Wire, &GlobalTransform)>, + connections: Query<&GlobalTransform, With>, + mut gizmos: Gizmos, +) { + for (wire, transform) in wires.iter() { + let wire_root = transform.translation().xy(); + for connection in wire + .connections + .iter() + .map(|connection| connections.get(connection.0)) + { + if let Ok(connection) = connection { + gizmos.line_2d(wire_root, connection.translation().xy(), WHITE); + } else { + println!("connection not found: {:?}", connection); + } + } + } +} fn scale_labels(mut labels: Query<&mut Transform, With>, canvas: Res) { for mut transform in labels.iter_mut() { transform.scale = Vec3::splat(canvas.zoom) * LABEL_SCALING_FACTOR; } } + +fn update_connection_states( + wires: Query<(&Wire)>, + mut connections: Query<( + &mut Connection, + Option<&InputConnection>, + Option<&OutputConnection>, + )>, +) { + for wire in wires.iter() { + let inputs = wire.connections.iter().find_map(|connection| { + let conn = connections.get(connection.0).unwrap(); + if conn.1.is_some() { Some(conn.0) } else { None } + }); + //TODO: implement multiple inputs on one wire (use filter_map instead of find_map and then combine the values (binary-or?)) + + if let Some(input) = inputs { + let values = input.values.clone(); + for output in wire.connections.iter() { + if let Ok((mut output, _, output_marker)) = connections.get_mut(output.0) { + if output_marker.is_none() { + continue; + } + output.values = values.clone(); + } + } + } + } +}