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
use std::io;

pub mod hp7470a;

pub trait PlotterWriteable {
    fn write<W>(&self, sink: &mut W) -> io::Result<()>
    where
        W: io::Write;
}

#[derive(Debug)]
pub struct HpglProgram(Vec<HpglCommand>);
impl HpglProgram {
    pub fn new(commands: Vec<HpglCommand>) -> Self {
        Self(commands)
    }
}

impl PlotterWriteable for HpglProgram {
    fn write<W>(&self, sink: &mut W) -> io::Result<()>
    where
        W: io::Write,
    {
        self.0
            .iter()
            .map(|command| command.write(sink))
            .collect::<_>()
    }
}

impl From<Vec<HpglCommand>> for HpglProgram {
    fn from(inner: Vec<HpglCommand>) -> Self {
        Self(inner)
    }
}

/// Raw coordinate (can represent either absolute or relative, non-/plotter).
///
/// When plotting in _plotter coordinates_, x ∈ [0, 10900], y ∈ [0, 7650]
#[derive(Clone, Copy, Debug)]
pub struct Coordinate {
    pub x: f32,
    pub y: f32,
}
impl Coordinate {
    pub const MAX_X_A4: f32 = 10900.;
    pub const MAX_X_US: f32 = 10300.;
    pub const MAX_Y: f32 = 7650.;
}

#[derive(Debug)]
pub struct CoordinateChain(pub Vec<Coordinate>);

impl CoordinateChain {
    pub fn write<W>(&self, sink: &mut W) -> io::Result<()>
    where
        W: io::Write,
    {
        let mut iter = self.0.iter().peekable();
        while let Some(coord) = iter.next() {
            write!(sink, "{},{}", coord.x, coord.y)?;
            if let Some(_) = iter.peek() {
                write!(sink, ",")?;
            }
        }

        Ok(())
    }
}

impl From<Vec<Coordinate>> for CoordinateChain {
    fn from(inner: Vec<Coordinate>) -> Self {
        Self(inner)
    }
}

impl From<Coordinate> for CoordinateChain {
    fn from(inner: Coordinate) -> Self {
        Self(vec![inner])
    }
}

#[derive(Debug)]
pub enum HpglCommand {
    DefaultSettings,
    InitializePlotter,
    SelectPen {
        pen: usize,
    },
    VelocitySelect {
        velocity: f32,
    },
    /// Raises the pen. _Note_: **Deliberately** does not support moving the pen as part of the same command.
    PenUp,
    /// Lowers the pen. _Note_: **Deliberately** does not support moving the pen as part of the same command.
    PenDown,
    PlotAbsolute(CoordinateChain),
}

impl PlotterWriteable for HpglCommand {
    fn write<W>(&self, sink: &mut W) -> io::Result<()>
    where
        W: io::Write,
    {
        use HpglCommand::*;
        match self {
            DefaultSettings => {
                sink.write(b"DF;")?;
            }
            InitializePlotter => {
                sink.write(b"IN;")?;
            }
            SelectPen { pen } => {
                sink.write(b"SP")?;
                write!(sink, "{}", pen)?;
                sink.write(b";")?;
            }
            VelocitySelect { velocity } => {
                sink.write(b"VS")?;
                write!(sink, "{}", velocity)?;
                sink.write(b";")?;
            }
            PenUp => {
                sink.write(b"PU;")?;
            }
            PenDown => {
                sink.write(b"PD;")?;
            }
            PlotAbsolute(coord) => {
                sink.write(b"PA")?;
                coord.write(sink)?;
                sink.write(b";")?;
            }
        }

        Ok(())
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn single_point_in_chain() {
        let chain: CoordinateChain = vec![Coordinate { x: 69., y: 420. }].into();
        let mut buf: Vec<u8> = Vec::new();

        chain.write(&mut buf).unwrap();
        assert_eq!(buf, b"69,420");
    }

    #[test]
    fn multipoint_in_chain() {
        let chain: CoordinateChain = vec![
            Coordinate { x: 69., y: 420. },
            Coordinate { x: 666., y: 69. },
        ]
        .into();
        let mut buf: Vec<u8> = Vec::new();

        chain.write(&mut buf).unwrap();
        assert_eq!(buf, b"69,420,666,69");
    }
}