hpgl/
lib.rs

1use std::io;
2
3pub mod hp7470a;
4
5pub trait PlotterWriteable {
6    fn write<W>(&self, sink: &mut W) -> io::Result<()>
7    where
8        W: io::Write;
9}
10
11#[derive(Debug)]
12pub struct HpglProgram(Vec<HpglCommand>);
13impl HpglProgram {
14    pub fn new(commands: Vec<HpglCommand>) -> Self {
15        Self(commands)
16    }
17}
18
19impl PlotterWriteable for HpglProgram {
20    fn write<W>(&self, sink: &mut W) -> io::Result<()>
21    where
22        W: io::Write,
23    {
24        self.0
25            .iter()
26            .map(|command| command.write(sink))
27            .collect::<_>()
28    }
29}
30
31impl From<Vec<HpglCommand>> for HpglProgram {
32    fn from(inner: Vec<HpglCommand>) -> Self {
33        Self(inner)
34    }
35}
36
37/// Raw coordinate (can represent either absolute or relative, non-/plotter).
38///
39/// When plotting in _plotter coordinates_, x ∈ [0, 10900], y ∈ [0, 7650]
40#[derive(Clone, Copy, Debug)]
41pub struct Coordinate {
42    pub x: f32,
43    pub y: f32,
44}
45impl Coordinate {
46    pub const MAX_X_A4: f32 = 10900.;
47    pub const MAX_X_US: f32 = 10300.;
48    pub const MAX_Y: f32 = 7650.;
49}
50
51#[derive(Debug)]
52pub struct CoordinateChain(pub Vec<Coordinate>);
53
54impl CoordinateChain {
55    pub fn write<W>(&self, sink: &mut W) -> io::Result<()>
56    where
57        W: io::Write,
58    {
59        let mut iter = self.0.iter().peekable();
60        while let Some(coord) = iter.next() {
61            write!(sink, "{},{}", coord.x, coord.y)?;
62            if let Some(_) = iter.peek() {
63                write!(sink, ",")?;
64            }
65        }
66
67        Ok(())
68    }
69}
70
71impl From<Vec<Coordinate>> for CoordinateChain {
72    fn from(inner: Vec<Coordinate>) -> Self {
73        Self(inner)
74    }
75}
76
77impl From<Coordinate> for CoordinateChain {
78    fn from(inner: Coordinate) -> Self {
79        Self(vec![inner])
80    }
81}
82
83#[derive(Debug)]
84pub enum HpglCommand {
85    DefaultSettings,
86    InitializePlotter,
87    SelectPen {
88        pen: usize,
89    },
90    VelocitySelect {
91        velocity: f32,
92    },
93    /// Raises the pen. _Note_: **Deliberately** does not support moving the pen as part of the same command.
94    PenUp,
95    /// Lowers the pen. _Note_: **Deliberately** does not support moving the pen as part of the same command.
96    PenDown,
97    PlotAbsolute(CoordinateChain),
98}
99
100impl PlotterWriteable for HpglCommand {
101    fn write<W>(&self, sink: &mut W) -> io::Result<()>
102    where
103        W: io::Write,
104    {
105        use HpglCommand::*;
106        match self {
107            DefaultSettings => {
108                sink.write(b"DF;")?;
109            }
110            InitializePlotter => {
111                sink.write(b"IN;")?;
112            }
113            SelectPen { pen } => {
114                sink.write(b"SP")?;
115                write!(sink, "{}", pen)?;
116                sink.write(b";")?;
117            }
118            VelocitySelect { velocity } => {
119                sink.write(b"VS")?;
120                write!(sink, "{}", velocity)?;
121                sink.write(b";")?;
122            }
123            PenUp => {
124                sink.write(b"PU;")?;
125            }
126            PenDown => {
127                sink.write(b"PD;")?;
128            }
129            PlotAbsolute(coord) => {
130                sink.write(b"PA")?;
131                coord.write(sink)?;
132                sink.write(b";")?;
133            }
134        }
135
136        Ok(())
137    }
138}
139
140#[cfg(test)]
141mod test {
142    use super::*;
143
144    #[test]
145    fn single_point_in_chain() {
146        let chain: CoordinateChain = vec![Coordinate { x: 69., y: 420. }].into();
147        let mut buf: Vec<u8> = Vec::new();
148
149        chain.write(&mut buf).unwrap();
150        assert_eq!(buf, b"69,420");
151    }
152
153    #[test]
154    fn multipoint_in_chain() {
155        let chain: CoordinateChain = vec![
156            Coordinate { x: 69., y: 420. },
157            Coordinate { x: 666., y: 69. },
158        ]
159        .into();
160        let mut buf: Vec<u8> = Vec::new();
161
162        chain.write(&mut buf).unwrap();
163        assert_eq!(buf, b"69,420,666,69");
164    }
165}