1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
//! An ILDA file consists of sections which either contain a frame or a color palette. Eachsection
//! consists of a fixed length header followed by a variable number of data records, which are
//! either frame points or color palette colors.

use zerocopy::{AsBytes, FromBytes, Unaligned};

/// The endianness of bytes read from and written to the format.
pub type Endianness = byteorder::BigEndian;
type I16 = zerocopy::byteorder::I16<Endianness>;
type U16 = zerocopy::byteorder::U16<Endianness>;

/// The type and data format of the section is defined by the format code.
///
/// There are five different formats currently defined.
///
/// Format 3 was proposed within the ILDA Technical Committee but was never approved. Therefore,
/// format 3 is omitted in this ILDA standard.
///
/// Formats 0, 1, 4 and 5 define point data. Each point includes X and Y coordinates, andcolor
/// information. The 3D formats 0 and 4 also include Z (depth) information.
///
/// The indexed color formats 0 and 1 use a data format where each point has a Color Indexbetween 0
/// and 255 used as an index into a color palette. Format 2 specifies the colorpalette for use with
/// indexed color frames. The true color formats 4 and 5 use a red,green and blue color component
/// of 8 bits for each point. ILDA files may contain a mix offrames with several
/// different format codes.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Format(pub u8);

#[derive(Copy, Clone, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Name(pub [u8; 8]);

/// Describes the layout of a section of IDTF.
#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Header {
    /// The ASCII letters ILDA, identifying an ILDA format header.
    pub ilda: [u8; 4],
    /// Reserved for future use. Must be zeroed.
    pub reserved: [u8; 3],
    /// One of the format codes defined in the Format Codes section.
    pub format: Format,
    /// Eight ASCII characters with the name of this frame or color palette. If abinary zero is
    /// encountered, than any characters following the zero SHALL be ignored.
    pub data_name: Name,
    /// Eight ASCII characters with the name of the company who created theframe. If a binary zero
    /// is encountered, than any characters following the zero SHALL beignored.
    pub company_name: Name,
    /// Total number of data records (points or colors) that will follow this headerexpressed as an
    /// unsigned integer (0 – 65535). If the number of records is 0, then this is to be taken as
    /// the end of file header and nomore data will follow this header. For color palettes, the
    /// number of records SHALL be between 2 and 256.
    pub num_records: U16,
    /// Frame or color palette number. If the frame is part of a group such as an animation
    /// sequence, thisrepresents the frame number. Counting begins with frame 0. Range is 0 –
    /// 65534.
    pub data_number: U16,
    /// Total frames in this group or sequence. Range is 1 – 65535.
    ///
    /// For colorpalettes this SHALL be 0.
    pub color_or_total_frames: U16,
    /// The projector number that this frame is to be displayed on. Range is 0 – 255. For single
    /// projector files this SHOULD be set 0.
    pub projector_number: u8,
    /// Reserved for future use. Must be zeroed.
    pub reserved2: u8,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Coords3d {
    /// left negative, right positive.
    pub x: I16,
    /// down negative, up positive.
    pub y: I16,
    /// far negative, near positive.
    pub z: I16,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Coords2d {
    /// left negative, right positive.
    pub x: I16,
    /// down negative, up positive.
    pub y: I16,
}

bitflags! {
    #[derive(AsBytes, FromBytes, Unaligned)]
    #[repr(C)]
    pub struct Status: u8 {
        /// This bit SHALL be set to 0 for all points except the lastpoint of the image.
        const LAST_POINT = 0b10000000;
        /// If this is a 1, then the laser is off (blank). If this is a 0, then thelaser is on
        /// (draw). Note that all systems SHALL write this bit, even if a particular systemuses the
        /// color index for blanking/color information.
        ///
        /// When reading files, the blanking bit takes precedence over the color from the
        /// colorpalette or the points RGB values. If the blanking bit is set, all RGB values
        /// SHOULD betreated as zero.
        const BLANKING = 0b01000000;
    }
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Color {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Coords3dIndexedColor {
    pub coords: Coords3d,
    pub status: Status,
    pub color_index: u8,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Coords2dIndexedColor {
    pub coords: Coords2d,
    pub status: Status,
    pub color_index: u8,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct ColorPalette {
    pub color: Color,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Coords3dTrueColor {
    pub coords: Coords3d,
    pub status: Status,
    pub color: Color,
}

#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq, AsBytes, FromBytes, Unaligned)]
#[repr(C)]
pub struct Coords2dTrueColor {
    pub coords: Coords2d,
    pub status: Status,
    pub color: Color,
}

impl Format {
    pub const COORDS_3D_INDEXED_COLOR: Self = Self(0);
    pub const COORDS_2D_INDEXED_COLOR: Self = Self(1);
    pub const COLOR_PALETTE: Self = Self(2);
    pub const COORDS_3D_TRUE_COLOR: Self = Self(4);
    pub const COORDS_2D_TRUE_COLOR: Self = Self(5);
}

impl Name {
    /// Read the ascii bytes as a UTF8 str.
    pub fn as_str(&self) -> Result<&str, std::str::Utf8Error> {
        let len = self.0.iter().position(|&b| b == 0).unwrap_or(self.0.len());
        std::str::from_utf8(&self.0[..len])
    }
}

impl Header {
    pub const ILDA: [u8; 4] = [0x49, 0x4c, 0x44, 0x41];
}

impl Status {
    pub fn is_blanking(&self) -> bool {
        self.contains(Self::BLANKING)
    }

    pub fn is_last_point(&self) -> bool {
        self.contains(Self::LAST_POINT)
    }
}

impl std::fmt::Debug for Name {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.as_str() {
            Ok(s) => std::fmt::Debug::fmt(s, f),
            _ => std::fmt::Debug::fmt(&self.0, f),
        }
    }
}

impl std::fmt::Display for Name {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self.as_str() {
            Ok(s) => std::fmt::Display::fmt(s, f),
            _ => std::fmt::Display::fmt("invalid", f),
        }
    }
}