improve simulation structure & start implementing wires

This commit is contained in:
OMGeeky
2025-04-14 22:08:29 +02:00
parent 830bfe1c64
commit 1ee983e430

View File

@@ -43,11 +43,27 @@ pub struct BlockVisuals {
color: Color, color: Color,
} }
#[derive(Component, Debug)] #[derive(Component, Debug)]
pub struct ConnectionReference(Entity);
#[derive(Component, Debug)]
pub struct WireReference(Entity);
#[derive(Component, Debug)]
pub struct Block { pub struct Block {
inputs: Vec<Connection>, inputs: Vec<ConnectionReference>,
outputs: Vec<Connection>, outputs: Vec<ConnectionReference>,
wires: Vec<WireReference>,
} }
#[derive(Debug)] #[derive(Component, Debug)]
#[require(Transform)]
pub struct Wire {
connections: Vec<ConnectionReference>,
}
#[derive(Component, Debug)]
pub struct InputConnection;
#[derive(Component, Debug)]
pub struct OutputConnection;
#[derive(Component, Debug)]
#[require(Transform)]
pub struct Connection { pub struct Connection {
size: u32, size: u32,
values: Vec<bool>, values: Vec<bool>,
@@ -56,8 +72,16 @@ pub struct Connection {
pub struct LogicSimPlugin; pub struct LogicSimPlugin;
impl Plugin for LogicSimPlugin { impl Plugin for LogicSimPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.add_systems(Startup, setup) app.add_systems(Startup, setup).add_systems(
.add_systems(Update, (render_blocks, scale_labels)); Update,
(
(update_connection_positions, draw_connections).chain(),
render_blocks,
scale_labels,
draw_wires,
update_connection_states,
),
);
} }
} }
fn setup(mut commands: Commands, asset_server: Res<AssetServer>) { fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
@@ -69,44 +93,80 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
}; };
let size = IVec2::new(50, 80); 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 commands
.spawn(BlockBundle { .spawn(BlockBundle {
// name: Text2d("AND".into()),
block_visuals: BlockVisuals { block_visuals: BlockVisuals {
size, size,
color: Color::from(RED), color: Color::from(RED),
}, },
block: Block { block: Block {
inputs: vec![ inputs: vec![ConnectionReference(input1), ConnectionReference(input2)],
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],
},
],
outputs: vec![ outputs: vec![
Connection { ConnectionReference(output1),
size: 1, ConnectionReference(output2),
values: vec![false], ConnectionReference(output3),
}, ConnectionReference(output4),
Connection {
size: 2,
values: vec![true, false],
},
Connection {
size: 3,
values: vec![true, false, true],
},
Connection {
size: 4,
values: vec![true, false, true, false],
},
], ],
wires: vec![WireReference(wire1)],
}, },
global_transform: GlobalTransform::default(), global_transform: GlobalTransform::default(),
transform: Transform::default(), transform: Transform::default(),
@@ -114,9 +174,22 @@ fn setup(mut commands: Commands, asset_server: Res<AssetServer>) {
.with_child(BlockLabelBundle::new("AND", size, text_font)); .with_child(BlockLabelBundle::new("AND", size, text_font));
} }
fn render_blocks( fn render_blocks(
blocks: Query<(&BlockVisuals, &Block, &GlobalTransform)>, blocks: Query<(&BlockVisuals, &Block, &Transform)>,
connections: Query<&mut Connection>,
canvas: Res<Canvas>, canvas: Res<Canvas>,
mut gizmos: Gizmos, 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<Connection>>,
canvas: Res<Canvas>,
) { ) {
for (block_visual, block, transform) in blocks.iter() { for (block_visual, block, transform) in blocks.iter() {
let size = block_visual.size.as_vec2() * canvas.zoom; let size = block_visual.size.as_vec2() * canvas.zoom;
@@ -125,41 +198,56 @@ fn render_blocks(
let top = center.y + half_size.y; let top = center.y + half_size.y;
let left = center.x - half_size.x; let left = center.x - half_size.x;
let right = 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); update_connection_position(
draw_connections(right, top, size.y, &block.outputs, &mut gizmos, &canvas); 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<Item = &'a ConnectionReference>,
connections: &mut Query<&mut Transform, With<Connection>>,
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( fn draw_connections(
x: f32, connections: Query<(&Connection, &GlobalTransform)>,
y: f32, canvas: Res<Canvas>,
available_height: f32, mut gizmos: Gizmos,
connections: &[Connection],
gizmos: &mut Gizmos,
canvas: &Canvas,
) { ) {
let input_count = connections.len(); for (connection, transform) in connections.iter() {
let input_spacing = available_height / (input_count + 1) as f32; let pos = transform.translation().xy();
// println!("{} -> {}", input_count, input_spacing); draw_connection(pos, connection, &canvas, &mut gizmos);
for (i, connection) in connections.iter().enumerate() {
draw_connection(x, y, gizmos, input_spacing, i, connection, canvas);
} }
} }
fn draw_connection(pos: Vec2, connection: &Connection, canvas: &Canvas, gizmos: &mut Gizmos) {
fn draw_connection(
x: f32,
y: f32,
gizmos: &mut Gizmos,
input_spacing: f32,
i: usize,
connection: &Connection,
canvas: &Canvas,
) {
const CONNECTION_BIT_SIZE: f32 = 10.0; const CONNECTION_BIT_SIZE: f32 = 10.0;
let connection_bit_size = CONNECTION_BIT_SIZE * canvas.zoom; let connection_bit_size = CONNECTION_BIT_SIZE * canvas.zoom;
let connection_bit_half_size = connection_bit_size * 0.5; 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 rows = if connection.size > 8 { 2 } else { 1 };
let columns = (connection.size as f32 / rows as f32).ceil() as u32; let columns = (connection.size as f32 / rows as f32).ceil() as u32;
@@ -174,8 +262,7 @@ fn draw_connection(
if index >= connection.size { if index >= connection.size {
break 'rows; break 'rows;
} }
let pos = connection_pos let pos = pos + Vec2::new(x as f32, (rows - y - 1) as f32) * connection_bit_size
+ Vec2::new(x as f32, (rows - y - 1) as f32) * connection_bit_size
- half_offset - half_offset
+ half_one_size; + half_one_size;
let value = connection.values[index as usize]; let value = connection.values[index as usize];
@@ -184,14 +271,63 @@ fn draw_connection(
} }
} }
gizmos.rect_2d( gizmos.rect_2d(
connection_pos, pos,
Vec2::new(columns as f32, rows as f32) * connection_bit_size, Vec2::new(columns as f32, rows as f32) * connection_bit_size,
WHITE, WHITE,
); );
} }
fn draw_wires(
wires: Query<(&Wire, &GlobalTransform)>,
connections: Query<&GlobalTransform, With<Connection>>,
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<CanvasText>>, canvas: Res<Canvas>) { fn scale_labels(mut labels: Query<&mut Transform, With<CanvasText>>, canvas: Res<Canvas>) {
for mut transform in labels.iter_mut() { for mut transform in labels.iter_mut() {
transform.scale = Vec3::splat(canvas.zoom) * LABEL_SCALING_FACTOR; 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();
}
}
}
}
}