dxf_tools_rs/types/
transparency.rs

1//! Transparency representation for CAD entities
2
3use std::fmt;
4
5/// Represents transparency in AutoCAD
6///
7/// Transparency is represented as an alpha value where:
8/// - 0 = fully opaque (0% transparent)
9/// - 255 = fully transparent (100% transparent)
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11pub struct Transparency(u8);
12
13impl Transparency {
14    /// Fully opaque (0% transparent)
15    pub const OPAQUE: Transparency = Transparency(0);
16
17    /// Fully transparent (100% transparent)
18    pub const TRANSPARENT: Transparency = Transparency(255);
19
20    /// Transparency ByLayer - uses the layer's transparency
21    pub const BY_LAYER: Transparency = Transparency(0);
22
23    /// Create a new transparency from an alpha value (0-255)
24    pub const fn new(alpha: u8) -> Self {
25        Transparency(alpha)
26    }
27
28    /// Create transparency from a percentage (0.0 = opaque, 1.0 = transparent)
29    pub fn from_percent(percent: f64) -> Self {
30        let alpha = (percent.clamp(0.0, 1.0) * 255.0) as u8;
31        Transparency(alpha)
32    }
33
34    /// Create transparency from DWG alpha value (32-bit format)
35    ///
36    /// The first byte represents the transparency type:
37    /// - 0 = BYLAYER
38    /// - 1 = BYBLOCK  
39    /// - 3 = the transparency value in the last byte
40    pub fn from_alpha_value(value: u32) -> Self {
41        let type_byte = (value >> 24) as u8;
42        match type_byte {
43            0 => Transparency::BY_LAYER,
44            1 => Transparency::OPAQUE, // BYBLOCK = opaque for now
45            3 => Transparency((value & 0xFF) as u8),
46            _ => Transparency::OPAQUE,
47        }
48    }
49
50    /// Get the raw alpha value (0-255)
51    pub const fn alpha(&self) -> u8 {
52        self.0
53    }
54
55    /// Get transparency as a percentage (0.0 = opaque, 1.0 = transparent)
56    pub fn as_percent(&self) -> f64 {
57        self.0 as f64 / 255.0
58    }
59
60    /// Check if fully opaque
61    pub const fn is_opaque(&self) -> bool {
62        self.0 == 0
63    }
64
65    /// Check if fully transparent
66    pub const fn is_transparent(&self) -> bool {
67        self.0 == 255
68    }
69
70    /// Common transparency values
71    pub const T_10: Transparency = Transparency(26);   // 10% transparent
72    pub const T_20: Transparency = Transparency(51);   // 20% transparent
73    pub const T_30: Transparency = Transparency(77);   // 30% transparent
74    pub const T_40: Transparency = Transparency(102);  // 40% transparent
75    pub const T_50: Transparency = Transparency(128);  // 50% transparent
76    pub const T_60: Transparency = Transparency(153);  // 60% transparent
77    pub const T_70: Transparency = Transparency(179);  // 70% transparent
78    pub const T_80: Transparency = Transparency(204);  // 80% transparent
79    pub const T_90: Transparency = Transparency(230);  // 90% transparent
80    
81    /// Convert to DWG alpha value (32-bit format)
82    ///
83    /// The first byte represents the transparency type:
84    /// - 0 = BYLAYER
85    /// - 1 = BYBLOCK
86    /// - 3 = the transparency value in the last byte
87    pub fn to_alpha_value(&self) -> i32 {
88        if self.0 == 0 {
89            // Fully opaque, use BYLAYER type
90            0
91        } else {
92            // Type 3 = explicit value
93            ((3u32 << 24) | self.0 as u32) as i32
94        }
95    }
96}
97
98impl Default for Transparency {
99    fn default() -> Self {
100        Transparency::OPAQUE
101    }
102}
103
104impl From<u8> for Transparency {
105    fn from(alpha: u8) -> Self {
106        Transparency(alpha)
107    }
108}
109
110impl From<Transparency> for u8 {
111    fn from(transparency: Transparency) -> Self {
112        transparency.0
113    }
114}
115
116impl fmt::Display for Transparency {
117    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118        write!(f, "{:.1}%", self.as_percent() * 100.0)
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use super::*;
125
126    #[test]
127    fn test_transparency_creation() {
128        let t = Transparency::new(128);
129        assert_eq!(t.alpha(), 128);
130    }
131
132    #[test]
133    fn test_transparency_from_percent() {
134        let t = Transparency::from_percent(0.5);
135        // 0.5 * 255.0 = 127.5, which rounds down to 127
136        assert_eq!(t.alpha(), 127);
137
138        let t = Transparency::from_percent(0.0);
139        assert_eq!(t.alpha(), 0);
140
141        let t = Transparency::from_percent(1.0);
142        assert_eq!(t.alpha(), 255);
143    }
144
145    #[test]
146    fn test_transparency_as_percent() {
147        assert_eq!(Transparency::OPAQUE.as_percent(), 0.0);
148        assert_eq!(Transparency::TRANSPARENT.as_percent(), 1.0);
149        assert!((Transparency::T_50.as_percent() - 0.5).abs() < 0.01);
150    }
151
152    #[test]
153    fn test_transparency_checks() {
154        assert!(Transparency::OPAQUE.is_opaque());
155        assert!(!Transparency::OPAQUE.is_transparent());
156        assert!(Transparency::TRANSPARENT.is_transparent());
157        assert!(!Transparency::TRANSPARENT.is_opaque());
158    }
159
160    #[test]
161    fn test_transparency_display() {
162        assert_eq!(Transparency::OPAQUE.to_string(), "0.0%");
163        assert_eq!(Transparency::TRANSPARENT.to_string(), "100.0%");
164    }
165
166    #[test]
167    fn test_transparency_conversion() {
168        let alpha: u8 = 100;
169        let t: Transparency = alpha.into();
170        let back: u8 = t.into();
171        assert_eq!(alpha, back);
172    }
173
174    #[test]
175    fn test_default_transparency() {
176        assert_eq!(Transparency::default(), Transparency::OPAQUE);
177    }
178}
179