Skip to main content

altium_format/records/sch/
common.rs

1//! Common types and enums for schematic records.
2
3use bitflags::bitflags;
4
5/// Line width for schematic primitives.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7#[repr(u8)]
8pub enum LineWidth {
9    #[default]
10    Smallest = 0,
11    Small = 1,
12    Medium = 2,
13    Large = 3,
14}
15
16impl LineWidth {
17    pub fn from_int(value: i32) -> Self {
18        match value {
19            0 => LineWidth::Smallest,
20            1 => LineWidth::Small,
21            2 => LineWidth::Medium,
22            3 => LineWidth::Large,
23            _ => LineWidth::Smallest,
24        }
25    }
26
27    pub fn to_int(self) -> i32 {
28        self as i32
29    }
30}
31
32/// Line style for polylines and wires.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
34#[repr(u8)]
35pub enum LineStyle {
36    #[default]
37    Solid = 0,
38    Dashed = 1,
39    Dotted = 2,
40}
41
42impl LineStyle {
43    pub fn from_int(value: i32) -> Self {
44        match value {
45            0 => LineStyle::Solid,
46            1 => LineStyle::Dashed,
47            2 => LineStyle::Dotted,
48            _ => LineStyle::Solid,
49        }
50    }
51
52    pub fn to_int(self) -> i32 {
53        self as i32
54    }
55}
56
57bitflags! {
58    /// Text orientation flags.
59    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
60    pub struct TextOrientations: u8 {
61        const NONE = 0;
62        const ROTATED = 1;
63        const FLIPPED = 2;
64    }
65}
66
67impl TextOrientations {
68    pub fn from_int(value: i32) -> Self {
69        TextOrientations::from_bits_truncate(value as u8)
70    }
71
72    pub fn to_int(self) -> i32 {
73        self.bits() as i32
74    }
75}
76
77/// Text justification (3x3 grid: horizontal x vertical).
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
79pub struct TextJustification(i32);
80
81impl TextJustification {
82    pub const BOTTOM_LEFT: Self = TextJustification(0);
83    pub const BOTTOM_CENTER: Self = TextJustification(1);
84    pub const BOTTOM_RIGHT: Self = TextJustification(2);
85    pub const MIDDLE_LEFT: Self = TextJustification(3);
86    pub const MIDDLE_CENTER: Self = TextJustification(4);
87    pub const MIDDLE_RIGHT: Self = TextJustification(5);
88    pub const TOP_LEFT: Self = TextJustification(6);
89    pub const TOP_CENTER: Self = TextJustification(7);
90    pub const TOP_RIGHT: Self = TextJustification(8);
91
92    pub fn from_int(value: i32) -> Self {
93        TextJustification(value.clamp(0, 8))
94    }
95
96    pub fn to_int(self) -> i32 {
97        self.0
98    }
99
100    /// Horizontal component: Left=0, Center=1, Right=2
101    pub fn horizontal(self) -> i32 {
102        self.0 % 3
103    }
104
105    /// Vertical component: Bottom=0, Middle=1, Top=2
106    pub fn vertical(self) -> i32 {
107        self.0 / 3
108    }
109}
110
111/// Pin electrical type.
112#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
113#[repr(u8)]
114pub enum PinElectricalType {
115    Input = 0,
116    InputOutput = 1,
117    Output = 2,
118    OpenCollector = 3,
119    #[default]
120    Passive = 4,
121    HiZ = 5,
122    OpenEmitter = 6,
123    Power = 7,
124}
125
126impl PinElectricalType {
127    pub fn from_int(value: i32) -> Self {
128        match value {
129            0 => PinElectricalType::Input,
130            1 => PinElectricalType::InputOutput,
131            2 => PinElectricalType::Output,
132            3 => PinElectricalType::OpenCollector,
133            4 => PinElectricalType::Passive,
134            5 => PinElectricalType::HiZ,
135            6 => PinElectricalType::OpenEmitter,
136            7 => PinElectricalType::Power,
137            _ => PinElectricalType::Passive,
138        }
139    }
140
141    pub fn to_int(self) -> i32 {
142        self as i32
143    }
144}
145
146/// Pin symbol type.
147#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
148#[repr(u8)]
149pub enum PinSymbol {
150    #[default]
151    None = 0,
152    Dot = 1,
153    RightLeftSignalFlow = 2,
154    Clock = 3,
155    ActiveLowInput = 4,
156    AnalogSignalIn = 5,
157    NotLogicConnection = 6,
158    PostponedOutput = 8,
159    OpenCollector = 9,
160    HiZ = 10,
161    HighCurrent = 11,
162    Pulse = 12,
163    Schmitt = 13,
164    ActiveLowOutput = 17,
165    OpenCollectorPullUp = 22,
166    OpenEmitter = 23,
167    OpenEmitterPullUp = 24,
168    DigitalSignalIn = 25,
169    ShiftLeft = 30,
170    OpenOutput = 32,
171    LeftRightSignalFlow = 33,
172    BidirectionalSignalFlow = 34,
173}
174
175impl PinSymbol {
176    pub fn from_int(value: i32) -> Self {
177        match value {
178            0 => PinSymbol::None,
179            1 => PinSymbol::Dot,
180            2 => PinSymbol::RightLeftSignalFlow,
181            3 => PinSymbol::Clock,
182            4 => PinSymbol::ActiveLowInput,
183            5 => PinSymbol::AnalogSignalIn,
184            6 => PinSymbol::NotLogicConnection,
185            8 => PinSymbol::PostponedOutput,
186            9 => PinSymbol::OpenCollector,
187            10 => PinSymbol::HiZ,
188            11 => PinSymbol::HighCurrent,
189            12 => PinSymbol::Pulse,
190            13 => PinSymbol::Schmitt,
191            17 => PinSymbol::ActiveLowOutput,
192            22 => PinSymbol::OpenCollectorPullUp,
193            23 => PinSymbol::OpenEmitter,
194            24 => PinSymbol::OpenEmitterPullUp,
195            25 => PinSymbol::DigitalSignalIn,
196            30 => PinSymbol::ShiftLeft,
197            32 => PinSymbol::OpenOutput,
198            33 => PinSymbol::LeftRightSignalFlow,
199            34 => PinSymbol::BidirectionalSignalFlow,
200            _ => PinSymbol::None,
201        }
202    }
203
204    pub fn to_int(self) -> i32 {
205        self as i32
206    }
207}
208
209bitflags! {
210    /// Pin conglomerate flags.
211    #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
212    pub struct PinConglomerateFlags: u8 {
213        const NONE = 0x00;
214        const ROTATED = 0x01;
215        const FLIPPED = 0x02;
216        const HIDE = 0x04;
217        const DISPLAY_NAME_VISIBLE = 0x08;
218        const DESIGNATOR_VISIBLE = 0x10;
219        const UNKNOWN = 0x20;
220        const GRAPHICALLY_LOCKED = 0x40;
221    }
222}
223
224impl PinConglomerateFlags {
225    pub fn from_int(value: i32) -> Self {
226        PinConglomerateFlags::from_bits_truncate(value as u8)
227    }
228
229    pub fn to_int(self) -> i32 {
230        self.bits() as i32
231    }
232}
233
234/// Power object style.
235#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
236#[repr(u8)]
237pub enum PowerObjectStyle {
238    #[default]
239    Arrow = 0,
240    Bar = 1,
241    Wave = 2,
242    Ground = 3,
243    PowerGround = 4,
244    SignalGround = 5,
245    EarthGround = 6,
246    Circle = 7,
247}
248
249impl PowerObjectStyle {
250    pub fn from_int(value: i32) -> Self {
251        match value {
252            0 => PowerObjectStyle::Arrow,
253            1 => PowerObjectStyle::Bar,
254            2 => PowerObjectStyle::Wave,
255            3 => PowerObjectStyle::Ground,
256            4 => PowerObjectStyle::PowerGround,
257            5 => PowerObjectStyle::SignalGround,
258            6 => PowerObjectStyle::EarthGround,
259            7 => PowerObjectStyle::Circle,
260            _ => PowerObjectStyle::Arrow,
261        }
262    }
263
264    pub fn to_int(self) -> i32 {
265        self as i32
266    }
267}
268
269/// Utility to convert DXP integer + fraction to Coord raw value.
270///
271/// Altium stores coordinates as integer mils plus a fractional part.
272/// Raw coord = (integer * 10000) + fraction
273pub fn dxp_frac_to_coord(integer: i32, frac: i32) -> i32 {
274    integer * 10000 + frac
275}
276
277/// Utility to convert Coord raw value to DXP integer + fraction.
278pub fn coord_to_dxp_frac(raw: i32) -> (i32, i32) {
279    (raw / 10000, raw % 10000)
280}
281
282// ============================================================================
283// Trait implementations for derive macro support
284// ============================================================================
285
286use crate::error::Result;
287use crate::traits::{FromParamValue, ToParamValue};
288use crate::types::ParameterValue;
289
290impl FromParamValue for LineWidth {
291    fn from_param_value(value: &ParameterValue) -> Result<Self> {
292        Ok(Self::from_int(value.as_int_or(0)))
293    }
294}
295
296impl ToParamValue for LineWidth {
297    fn to_param_value(&self) -> String {
298        self.to_int().to_string()
299    }
300}
301
302impl FromParamValue for LineStyle {
303    fn from_param_value(value: &ParameterValue) -> Result<Self> {
304        Ok(Self::from_int(value.as_int_or(0)))
305    }
306}
307
308impl ToParamValue for LineStyle {
309    fn to_param_value(&self) -> String {
310        self.to_int().to_string()
311    }
312}
313
314impl FromParamValue for PinElectricalType {
315    fn from_param_value(value: &ParameterValue) -> Result<Self> {
316        Ok(Self::from_int(value.as_int_or(0)))
317    }
318}
319
320impl ToParamValue for PinElectricalType {
321    fn to_param_value(&self) -> String {
322        self.to_int().to_string()
323    }
324}
325
326impl FromParamValue for PinSymbol {
327    fn from_param_value(value: &ParameterValue) -> Result<Self> {
328        Ok(Self::from_int(value.as_int_or(0)))
329    }
330}
331
332impl ToParamValue for PinSymbol {
333    fn to_param_value(&self) -> String {
334        self.to_int().to_string()
335    }
336}
337
338impl FromParamValue for PinConglomerateFlags {
339    fn from_param_value(value: &ParameterValue) -> Result<Self> {
340        Ok(Self::from_int(value.as_int_or(0)))
341    }
342}
343
344impl ToParamValue for PinConglomerateFlags {
345    fn to_param_value(&self) -> String {
346        self.to_int().to_string()
347    }
348}
349
350impl FromParamValue for TextOrientations {
351    fn from_param_value(value: &ParameterValue) -> Result<Self> {
352        Ok(Self::from_int(value.as_int_or(0)))
353    }
354}
355
356impl ToParamValue for TextOrientations {
357    fn to_param_value(&self) -> String {
358        self.to_int().to_string()
359    }
360}
361
362impl FromParamValue for TextJustification {
363    fn from_param_value(value: &ParameterValue) -> Result<Self> {
364        Ok(Self::from_int(value.as_int_or(0)))
365    }
366}
367
368impl ToParamValue for TextJustification {
369    fn to_param_value(&self) -> String {
370        self.to_int().to_string()
371    }
372}
373
374impl FromParamValue for PowerObjectStyle {
375    fn from_param_value(value: &ParameterValue) -> Result<Self> {
376        Ok(Self::from_int(value.as_int_or(0)))
377    }
378}
379
380impl ToParamValue for PowerObjectStyle {
381    fn to_param_value(&self) -> String {
382        self.to_int().to_string()
383    }
384}
385
386#[cfg(test)]
387mod tests {
388    use super::*;
389
390    #[test]
391    fn test_text_justification() {
392        let tj = TextJustification::MIDDLE_CENTER;
393        assert_eq!(tj.horizontal(), 1);
394        assert_eq!(tj.vertical(), 1);
395
396        let tj = TextJustification::TOP_RIGHT;
397        assert_eq!(tj.horizontal(), 2);
398        assert_eq!(tj.vertical(), 2);
399    }
400
401    #[test]
402    fn test_dxp_frac_conversion() {
403        let raw = dxp_frac_to_coord(100, 5000);
404        assert_eq!(raw, 1005000);
405
406        let (integer, frac) = coord_to_dxp_frac(1005000);
407        assert_eq!(integer, 100);
408        assert_eq!(frac, 5000);
409    }
410}