#![allow(dead_code)]
use handwired_rs::{
Circuit, Relation, SourceTable, TensorProgram, Wire, emit_and, emit_compare, emit_not, emit_select, event, input,
state_bit,
};
pub const TRACK_WIDTH: f64 = 52.0;
pub const TRACK_HEIGHT: f64 = 32.0;
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum PointerKind {
Down,
Up,
Cancel,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct PointerEvent {
pub kind: PointerKind,
pub x: f32,
pub y: f32,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Output {
pub track_left: f32,
pub track_top: f32,
pub on: f32,
pub armed: f32,
}
struct Pointer {
active: Wire,
x: Wire,
y: Wire,
}
pub struct Toggle {
table: SourceTable,
circuit: Circuit,
program: TensorProgram,
source: Vec<f32>,
viewport: (Wire, Wire),
down: Pointer,
up: Pointer,
cancel: Wire,
outputs: [Wire; 4],
}
impl Default for Toggle {
fn default() -> Self {
Self::new()
}
}
impl Toggle {
pub fn new() -> Toggle {
let mut table = SourceTable::new();
let coordinate = || input(-16_384.0, 16_384.0).grid(1.0 / 64.0);
let viewport_width = table.declare("viewport.width", input(0.0, 16_384.0).grid(1.0).initial(1280.0));
let viewport_height = table.declare("viewport.height", input(0.0, 16_384.0).grid(1.0).initial(800.0));
let mut pointer = |name: &str| Pointer {
active: table.declare(format!("{name}.active"), event()),
x: table.declare(format!("{name}.x"), coordinate()),
y: table.declare(format!("{name}.y"), coordinate()),
};
let down = pointer("down");
let up = pointer("up");
let cancel = table.declare("cancel", event());
let on_cell = table.declare("on", state_bit());
let armed_cell = table.declare("armed", state_bit());
let mut circuit = table.create_circuit();
let c = &mut circuit;
let track_left = c.affine(viewport_width * 0.5 - TRACK_WIDTH / 2.0);
let track_top = c.affine(viewport_height * 0.5 - TRACK_HEIGHT / 2.0);
let track_right = c.affine(track_left + TRACK_WIDTH);
let track_bottom = c.affine(track_top + TRACK_HEIGHT);
let inside_track = |c: &mut Circuit, point: &Pointer| {
let bits = [
emit_compare(c, point.x, Relation::AtLeast, track_left),
emit_compare(c, point.x, Relation::Less, track_right),
emit_compare(c, point.y, Relation::AtLeast, track_top),
emit_compare(c, point.y, Relation::Less, track_bottom),
];
emit_and(c, &bits)
};
let down_inside = inside_track(c, &down);
let armed_after_down = emit_select(c, down.active, armed_cell, down_inside);
let up_inside = inside_track(c, &up);
let flips = emit_and(c, &[up.active, armed_after_down, up_inside]);
let flipped = emit_not(c, on_cell);
let on = emit_select(c, flips, on_cell, flipped);
let no_up = emit_not(c, up.active);
let no_cancel = emit_not(c, cancel);
let armed = emit_and(c, &[armed_after_down, no_up, no_cancel]);
table.next(on_cell, on);
table.next(armed_cell, armed);
table.finish();
let outputs = [track_left, track_top, on, armed];
let mut roots = outputs.to_vec();
roots.extend(table.next_wires());
let program = circuit.compile(Some(&roots));
let source = table.initial();
Toggle {
table,
circuit,
program,
source,
viewport: (viewport_width, viewport_height),
down,
up,
cancel,
outputs,
}
}
pub fn circuit(&self) -> &Circuit {
&self.circuit
}
pub fn program(&self) -> &TensorProgram {
&self.program
}
pub fn step(&mut self, width: f32, height: f32, events: &[PointerEvent]) -> Result<Output, String> {
let mut values = self.source.clone();
values[self.viewport.0.index()] = width;
values[self.viewport.1.index()] = height;
for event in events {
let pointer = match event.kind {
PointerKind::Down => &self.down,
PointerKind::Up => &self.up,
PointerKind::Cancel => {
values[self.cancel.index()] = 1.0;
continue;
}
};
values[pointer.active.index()] = 1.0;
values[pointer.x.index()] = event.x;
values[pointer.y.index()] = event.y;
}
self.table.validate(&values)?;
let result = self.circuit.evaluate_tensor(&values, &self.program).map_err(|error| error.to_string())?;
self.source = self.table.advance(&values, &result);
let [track_left, track_top, on, armed] = self.outputs.map(|wire| result[wire.index()]);
Ok(Output { track_left, track_top, on, armed })
}
}
#[derive(Default)]
pub struct ToggleReference {
on: bool,
armed: bool,
}
impl ToggleReference {
pub fn step(&mut self, width: f64, height: f64, events: &[PointerEvent]) -> Output {
let track_left = width / 2.0 - TRACK_WIDTH / 2.0;
let track_top = height / 2.0 - TRACK_HEIGHT / 2.0;
let inside = |event: &PointerEvent| {
let (x, y) = (event.x as f64, event.y as f64);
x >= track_left && x < track_left + TRACK_WIDTH && y >= track_top && y < track_top + TRACK_HEIGHT
};
let last = |kind| events.iter().rev().find(|event| event.kind == kind);
if let Some(down) = last(PointerKind::Down) {
self.armed = inside(down);
}
let up = last(PointerKind::Up);
if let Some(up) = up
&& self.armed
&& inside(up)
{
self.on = !self.on;
}
if up.is_some() || last(PointerKind::Cancel).is_some() {
self.armed = false;
}
let bit = |value: bool| if value { 1.0 } else { 0.0 };
Output { track_left: track_left as f32, track_top: track_top as f32, on: bit(self.on), armed: bit(self.armed) }
}
}
fn main() {
let mut toggle = Toggle::new();
let program = toggle.program();
println!(
"toggle: {} source cells, {} built operations, {} live in {} layers, {} stored weights",
toggle.circuit().source_size(),
toggle.circuit().operations().len(),
program.live_operation_count(),
program.layers.len(),
program.stored_weight_count(),
);
let press = |kind, x, y| [PointerEvent { kind, x, y }];
let frames: [(&str, Vec<PointerEvent>); 4] = [
("press on the track", press(PointerKind::Down, 640.0, 400.0).to_vec()),
("release on the track", press(PointerKind::Up, 640.0, 400.0).to_vec()),
("press 1/64 px left of the track", press(PointerKind::Down, 613.984_4, 400.0).to_vec()),
("release on the track", press(PointerKind::Up, 640.0, 400.0).to_vec()),
];
for (label, events) in frames {
let output = toggle.step(1280.0, 800.0, &events).unwrap();
println!("{label:32} -> on {} armed {}", output.on, output.armed);
}
}