mirror of
https://github.com/OMGeeky/logisim.git
synced 2026-02-23 15:49:52 +01:00
improve simulation structure & start implementing wires
This commit is contained in:
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user