Skip to main content

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}