pedalkernel_layout/types.rs
1//! Output types for the schematic layout engine.
2//!
3//! All types derive [`serde::Serialize`] and [`serde::Deserialize`] so the
4//! layout can be written to `.pedal_layout` JSON and loaded at runtime by the
5//! signal-flow shader.
6
7use serde::{Deserialize, Serialize};
8
9/// Complete schematic layout — the output of [`crate::generate_layout`].
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct Layout {
12 /// Format version (currently 1).
13 pub version: u32,
14 /// Canvas bounds in layout units.
15 pub bounds: Bounds,
16 /// Positioned components with symbol and group assignments.
17 pub components: Vec<PlacedComponent>,
18 /// Routed wires connecting component pins.
19 pub wires: Vec<Wire>,
20 /// Functional groups (gain stages, tone stacks, etc.).
21 pub groups: Vec<Group>,
22 /// Supply rail positions.
23 pub supply_rails: Vec<SupplyRail>,
24 /// Component indices in signal-path order (input → output).
25 pub signal_path_order: Vec<usize>,
26}
27
28/// Canvas dimensions.
29#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
30pub struct Bounds {
31 pub width: f32,
32 pub height: f32,
33}
34
35/// A component placed on the schematic.
36#[derive(Debug, Clone, Serialize, Deserialize)]
37pub struct PlacedComponent {
38 /// Component ID from the DSL (e.g., `"V1a"`, `"R_plate"`).
39 pub name: String,
40 /// Component kind string (e.g., `"triode"`, `"resistor"`).
41 pub kind: String,
42 /// X position in layout units.
43 pub x: f32,
44 /// Y position in layout units.
45 pub y: f32,
46 /// Rotation in degrees (0, 90, 180, 270).
47 pub orientation: u16,
48 /// Symbol identifier for rendering.
49 pub symbol: String,
50 /// Functional group this component belongs to.
51 pub group: String,
52 /// Human-readable value label (e.g., `"100k"`, `"220nF"`).
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub label: Option<String>,
55 /// If this component is monitored, the monitor index for glow mapping.
56 #[serde(skip_serializing_if = "Option::is_none")]
57 pub monitor_index: Option<usize>,
58}
59
60/// A routed wire (sequence of orthogonal segments).
61#[derive(Debug, Clone, Serialize, Deserialize)]
62pub struct Wire {
63 /// Net name (e.g., `"net_3"`, `"vcc"`, `"gnd"`).
64 pub net: String,
65 /// Ordered waypoints `[x, y]` defining the wire path.
66 pub points: Vec<[f32; 2]>,
67 /// Whether this wire is on the primary signal path.
68 pub signal_path: bool,
69 /// Monitor index at the start of this wire segment (for glow interpolation).
70 #[serde(skip_serializing_if = "Option::is_none")]
71 pub monitor_index_start: Option<usize>,
72 /// Monitor index at the end of this wire segment.
73 #[serde(skip_serializing_if = "Option::is_none")]
74 pub monitor_index_end: Option<usize>,
75}
76
77/// A functional group bounding box.
78#[derive(Debug, Clone, Serialize, Deserialize)]
79pub struct Group {
80 /// Internal group name (e.g., `"preamp_1"`).
81 pub name: String,
82 /// Human-readable label (e.g., `"V1a Preamp"`).
83 pub label: String,
84 /// Bounding rectangle.
85 pub bounds: GroupBounds,
86}
87
88/// Bounding rectangle for a group.
89#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
90pub struct GroupBounds {
91 pub x: f32,
92 pub y: f32,
93 pub w: f32,
94 pub h: f32,
95}
96
97/// A supply rail (horizontal line across the schematic).
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct SupplyRail {
100 /// Rail name (`"vcc"` or `"gnd"`).
101 pub name: String,
102 /// Y position of this rail.
103 pub y: f32,
104}
105
106// ---------------------------------------------------------------------------
107// Internal placement helpers
108// ---------------------------------------------------------------------------
109
110/// Point in 2D layout space.
111#[derive(Debug, Clone, Copy, PartialEq)]
112pub struct Point {
113 pub x: f32,
114 pub y: f32,
115}
116
117impl Point {
118 pub fn new(x: f32, y: f32) -> Self {
119 Self { x, y }
120 }
121}
122
123/// Axis-aligned rectangle (used for overlap detection).
124#[derive(Debug, Clone, Copy)]
125pub struct Rect {
126 pub x: f32,
127 pub y: f32,
128 pub w: f32,
129 pub h: f32,
130}
131
132impl Rect {
133 pub fn overlaps(&self, other: &Rect) -> bool {
134 self.x < other.x + other.w
135 && self.x + self.w > other.x
136 && self.y < other.y + other.h
137 && self.y + self.h > other.y
138 }
139
140 pub fn contains_point(&self, p: Point) -> bool {
141 p.x >= self.x && p.x <= self.x + self.w && p.y >= self.y && p.y <= self.y + self.h
142 }
143}