Skip to content

Commit 00f67f6

Browse files
committed
Add power button
1 parent 6ca541d commit 00f67f6

File tree

2 files changed

+151
-24
lines changed

2 files changed

+151
-24
lines changed

src/app.rs

Lines changed: 135 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,6 @@ impl Instance {
4141
match self.ty {
4242
InstanceType::Gate(g) => {
4343
let target = g.pos + move_vec;
44-
// Gates are clamped with half size margins
4544
let clamped_x = target
4645
.x
4746
.clamp(rect.left() + half_extent.x, rect.right() - half_extent.x);
@@ -50,20 +49,27 @@ impl Instance {
5049
.clamp(rect.top() + half_extent.y, rect.bottom() - half_extent.y);
5150
vec2(clamped_x - g.pos.x, clamped_y - g.pos.y)
5251
}
52+
InstanceType::Power(p) => {
53+
let target = p.pos + move_vec;
54+
let clamped_x = target
55+
.x
56+
.clamp(rect.left() + half_extent.x, rect.right() - half_extent.x);
57+
let clamped_y = target
58+
.y
59+
.clamp(rect.top() + half_extent.y, rect.bottom() - half_extent.y);
60+
vec2(clamped_x - p.pos.x, clamped_y - p.pos.y)
61+
}
5362
InstanceType::Wire(w) => {
54-
// Wires: both endpoints must remain inside the rect
5563
let ts = w.start + move_vec;
5664
let te = w.end + move_vec;
5765
let sx = ts.x.clamp(rect.left(), rect.right());
5866
let sy = ts.y.clamp(rect.top(), rect.bottom());
5967
let ex = te.x.clamp(rect.left(), rect.right());
6068
let ey = te.y.clamp(rect.top(), rect.bottom());
61-
// Compute back the maximal safe delta that achieves those clamped targets
6269
let safe_dx_start = sx - w.start.x;
6370
let safe_dy_start = sy - w.start.y;
6471
let safe_dx_end = ex - w.end.x;
6572
let safe_dy_end = ey - w.end.y;
66-
// We must respect both endpoints; take the limiting deltas
6773
let safe_dx = if move_vec.x.is_sign_positive() {
6874
safe_dx_start.min(safe_dx_end)
6975
} else {
@@ -109,6 +115,16 @@ impl Instance {
109115
});
110116
}
111117
}
118+
InstanceType::Power(power_instance) => {
119+
for (i, pin) in power_instance.graphics().pins.iter().enumerate() {
120+
let pin_pos = power_instance.pos + pin.offset;
121+
pins.push(Pin {
122+
pos: pin_pos,
123+
ins: self.id,
124+
index: i as u32,
125+
});
126+
}
127+
}
112128
InstanceType::Wire(wire_instance) => {
113129
pins.push(Pin {
114130
pos: wire_instance.start,
@@ -135,6 +151,9 @@ impl Instance {
135151
InstanceType::Gate(gate) => {
136152
gate.pos += move_vec;
137153
}
154+
InstanceType::Power(power) => {
155+
power.pos += move_vec;
156+
}
138157
}
139158
}
140159

@@ -152,6 +171,10 @@ impl Instance {
152171
let move_vec = new_pos - pin.pos;
153172
gate.pos += move_vec;
154173
}
174+
InstanceType::Power(power) => {
175+
let move_vec = new_pos - pin.pos;
176+
power.pos += move_vec;
177+
}
155178
}
156179
}
157180
}
@@ -160,6 +183,7 @@ impl Instance {
160183
pub enum InstanceType {
161184
Gate(GateInstance),
162185
Wire(WireInstance),
186+
Power(PowerInstance),
163187
}
164188

165189
impl InstanceType {
@@ -173,6 +197,9 @@ impl InstanceType {
173197
fn new_gate(kind: GateKind, pos: Pos2) -> Self {
174198
Self::Gate(GateInstance { kind, pos })
175199
}
200+
fn new_power(pos: Pos2, on: bool) -> Self {
201+
Self::Power(PowerInstance { pos, on })
202+
}
176203
}
177204

178205
#[derive(serde::Deserialize, serde::Serialize, Copy, Debug, Clone)]
@@ -197,6 +224,23 @@ impl GateKind {
197224
}
198225
}
199226

227+
#[derive(serde::Deserialize, serde::Serialize, Copy, Debug, Clone)]
228+
pub struct PowerInstance {
229+
// Center position
230+
pos: Pos2,
231+
on: bool,
232+
}
233+
234+
impl PowerInstance {
235+
fn graphics(&self) -> &assets::InstanceGraphics {
236+
if self.on {
237+
&assets::POWER_ON_GRAPHICS
238+
} else {
239+
&assets::POWER_OFF_GRAPHICS
240+
}
241+
}
242+
}
243+
200244
#[derive(serde::Deserialize, serde::Serialize, Copy, Debug, Clone)]
201245
pub struct WireInstance {
202246
pub start: Pos2,
@@ -408,6 +452,16 @@ impl TemplateApp {
408452

409453
ui.add_space(8.0);
410454

455+
let pwr_image = egui::Image::new(assets::POWER_ON_GRAPHICS.svg.clone()).max_height(70.0);
456+
let pwr_resp = ui.add(egui::ImageButton::new(pwr_image).sense(Sense::click_and_drag()));
457+
if pwr_resp.dragged()
458+
&& let Some(pos) = ui.ctx().pointer_interact_pos()
459+
{
460+
self.panel_drag = Some(PanelDrag::new(InstanceType::new_power(pos, true)));
461+
}
462+
463+
ui.add_space(8.0);
464+
411465
let wire_resp = ui.add(
412466
Button::new("Wire")
413467
.sense(Sense::click_and_drag())
@@ -450,14 +504,22 @@ impl TemplateApp {
450504
self.canvas_config.base_gate_size.x * 0.5,
451505
self.canvas_config.base_gate_size.y * 0.5,
452506
);
453-
// compute move from current preview pos to mouse, then clamp via temporary instance view
454507
let desired = mouse - g.pos;
455508
let tmp = Instance::new(InstanceId(0), InstanceType::Gate(*g));
456509
let delta = tmp.clamp_move(desired, canvas_rect, half_extent);
457510
g.pos += delta;
458511
}
512+
InstanceType::Power(p) => {
513+
let half_extent = vec2(
514+
self.canvas_config.base_gate_size.x * 0.5,
515+
self.canvas_config.base_gate_size.y * 0.5,
516+
);
517+
let desired = mouse - p.pos;
518+
let tmp = Instance::new(InstanceId(0), InstanceType::Power(*p));
519+
let delta = tmp.clamp_move(desired, canvas_rect, half_extent);
520+
p.pos += delta;
521+
}
459522
InstanceType::Wire(w) => {
460-
// Move so that start follows mouse, but clamp as a wire move
461523
let desired = mouse - w.start;
462524
let tmp = Instance::new(InstanceId(0), InstanceType::Wire(*w));
463525
let delta = tmp.clamp_move(desired, canvas_rect, vec2(0.0, 0.0));
@@ -469,6 +531,7 @@ impl TemplateApp {
469531
if inside_rect(&canvas_rect, &pd.ty) {
470532
match pd.ty {
471533
InstanceType::Gate(gate) => self.draw_gate(ui, &gate),
534+
InstanceType::Power(power) => self.draw_power(ui, &power),
472535
InstanceType::Wire(wire) => Self::draw_wire(ui, &wire),
473536
}
474537
}
@@ -487,6 +550,21 @@ impl TemplateApp {
487550
}
488551
let pointer_pos = ui.input(|i| i.pointer.interact_pos());
489552
let pointer_pressed = ui.input(|i| i.pointer.primary_down());
553+
let right_clicked = ui.input(|i| i.pointer.secondary_clicked());
554+
555+
if right_clicked
556+
&& let Some(mouse_pos) = pointer_pos
557+
&& self.panel_drag.is_none()
558+
&& self.canvas_drag.is_none()
559+
&& self.resize.is_none()
560+
&& let Some(instance) = self.interacted_instance(mouse_pos)
561+
&& let InstanceType::Power(_) = instance.ty
562+
{
563+
let id = instance.id;
564+
if let InstanceType::Power(pi) = &mut self.get_instance_mut(id).ty {
565+
pi.on = !pi.on;
566+
}
567+
}
490568

491569
if pointer_pressed
492570
&& let Some(mouse_pos) = pointer_pos
@@ -496,14 +574,19 @@ impl TemplateApp {
496574
{
497575
let i = self.interacted_instance(mouse_pos);
498576
if let Some(instance) = i {
499-
log::debug!("canvas drag on {instance:?}");
500577
match instance.ty {
501578
InstanceType::Gate(gate) => {
502579
self.canvas_drag = Some(CanvasDrag::new(
503580
instance.id,
504581
gate.pos.to_vec2() - mouse_pos.to_vec2(),
505582
));
506583
}
584+
InstanceType::Power(power) => {
585+
self.canvas_drag = Some(CanvasDrag::new(
586+
instance.id,
587+
power.pos.to_vec2() - mouse_pos.to_vec2(),
588+
));
589+
}
507590
InstanceType::Wire(wire) => {
508591
if mouse_pos.distance(wire.end) < 10.0 {
509592
self.resize = Some(Resize {
@@ -538,6 +621,10 @@ impl TemplateApp {
538621
let desired = (mouse_pos + offset) - gate.pos;
539622
self.mov_component_with_connected(id, desired, canvas_rect);
540623
}
624+
InstanceType::Power(power) => {
625+
let desired = (mouse_pos + offset) - power.pos;
626+
self.mov_component_with_connected(id, desired, canvas_rect);
627+
}
541628
InstanceType::Wire(wire) => {
542629
let desired = (mouse_pos + offset) - wire.end;
543630
self.mov_component_with_connected(id, desired, canvas_rect);
@@ -664,11 +751,15 @@ impl TemplateApp {
664751
InstanceType::Gate(gate) => {
665752
self.draw_gate(ui, &gate);
666753
}
754+
InstanceType::Power(power) => {
755+
self.draw_power(ui, &power);
756+
}
667757
InstanceType::Wire(wire) => {
668758
Self::draw_wire(ui, &wire);
669759
}
670760
}
671761
}
762+
672763
if mouse_up {
673764
self.resize = None;
674765
self.canvas_drag = None;
@@ -696,22 +787,22 @@ impl TemplateApp {
696787
};
697788
ui.painter()
698789
.circle_filled(pin_pos, self.canvas_config.base_pin_size, color);
699-
// paint connected pins
700-
let mut conns: Vec<_> = self.connections.iter().collect();
701-
conns.sort_by_key(|c| {
702-
(
703-
c.pin1.pos.x.to_bits(),
704-
c.pin1.pos.y.to_bits(),
705-
c.pin2.pos.x.to_bits(),
706-
c.pin2.pos.y.to_bits(),
707-
)
708-
});
709-
for conn in conns {
710-
if conn.pin1.pos == pin_pos || conn.pin2.pos == pin_pos {
711-
// ui.painter()
712-
// .circle_filled(pin_pos, 10.0, Color32::LIGHT_YELLOW);
713-
}
714-
}
790+
}
791+
}
792+
793+
fn draw_power(&self, ui: &mut Ui, power: &PowerInstance) {
794+
let rect = Rect::from_center_size(power.pos, self.canvas_config.base_gate_size);
795+
let image = Image::new(power.graphics().svg.clone()).fit_to_exact_size(rect.size());
796+
ui.put(rect, image);
797+
798+
for pin in power.graphics().pins {
799+
let pin_pos = power.pos + pin.offset;
800+
let color = match pin.kind {
801+
assets::PinKind::Input => self.canvas_config.base_input_pin_color,
802+
assets::PinKind::Output => self.canvas_config.base_output_pin_color,
803+
};
804+
ui.painter()
805+
.circle_filled(pin_pos, self.canvas_config.base_pin_size, color);
715806
}
716807
}
717808

@@ -727,6 +818,14 @@ impl TemplateApp {
727818
break;
728819
}
729820
}
821+
InstanceType::Power(power) => {
822+
let size = self.canvas_config.base_gate_size;
823+
let rect = egui::Rect::from_center_size(power.pos, size);
824+
if rect.contains(mouse_pos) {
825+
i = Some(instance);
826+
break;
827+
}
828+
}
730829
InstanceType::Wire(wire) => {
731830
let dist = distance_point_to_segment(mouse_pos, wire.start, wire.end);
732831
if dist < 10.0 {
@@ -743,6 +842,7 @@ impl TemplateApp {
743842
fn inside_rect(canvas_rect: &Rect, ty: &InstanceType) -> bool {
744843
match ty {
745844
InstanceType::Gate(gate_instance) => canvas_rect.contains(gate_instance.pos),
845+
InstanceType::Power(power_instance) => canvas_rect.contains(power_instance.pos),
746846
InstanceType::Wire(wire_instance) => {
747847
canvas_rect.contains(wire_instance.start) && canvas_rect.contains(wire_instance.end)
748848
}
@@ -752,8 +852,8 @@ fn inside_rect(canvas_rect: &Rect, ty: &InstanceType) -> bool {
752852
impl TemplateApp {
753853
fn get_wire_mut(&mut self, id: InstanceId) -> &mut WireInstance {
754854
match &mut self.get_instance_mut(id).ty {
755-
InstanceType::Gate(_) => panic!("Should not happen"),
756855
InstanceType::Wire(wire_instance) => wire_instance,
856+
InstanceType::Gate(_) | InstanceType::Power(_) => panic!("Should not happen"),
757857
}
758858
}
759859

@@ -825,6 +925,17 @@ impl TemplateApp {
825925
dy_min = dy_min.max(top - q.y);
826926
dy_max = dy_max.min(bottom - q.y);
827927
}
928+
InstanceType::Power(p) => {
929+
let q = p.pos;
930+
let left = rect.left() + half_w;
931+
let right = rect.right() - half_w;
932+
let top = rect.top() + half_h;
933+
let bottom = rect.bottom() - half_h;
934+
dx_min = dx_min.max(left - q.x);
935+
dx_max = dx_max.min(right - q.x);
936+
dy_min = dy_min.max(top - q.y);
937+
dy_max = dy_max.min(bottom - q.y);
938+
}
828939
InstanceType::Wire(w) => {
829940
for q in [w.start, w.end] {
830941
let left = rect.left();

src/assets.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,19 @@ pub static NAND_GRAPHICS: InstanceGraphics = InstanceGraphics {
3535
},
3636
],
3737
};
38+
39+
pub static POWER_ON_GRAPHICS: InstanceGraphics = InstanceGraphics {
40+
svg: include_image!("../assets/switch-on.svg"),
41+
pins: &[PinInfo {
42+
kind: PinKind::Output,
43+
offset: Vec2::new(40.0, 0.0),
44+
}],
45+
};
46+
47+
pub static POWER_OFF_GRAPHICS: InstanceGraphics = InstanceGraphics {
48+
svg: include_image!("../assets/switch-off.svg"),
49+
pins: &[PinInfo {
50+
kind: PinKind::Output,
51+
offset: Vec2::new(40.0, 0.0),
52+
}],
53+
};

0 commit comments

Comments
 (0)