use crate::helper::ReadWrite; use glam::{Vec3, vec3}; use std::io::{Read, Write}; #[derive(Debug, Default, Clone)] pub struct GraphicsPart(Vec); impl ReadWrite for GraphicsPart { fn write(&self, w: &mut dyn Write) -> anyhow::Result<()> { w.write_all(&self.0)?; Ok(()) } fn read(r: &mut dyn Read) -> anyhow::Result { let mut buf = Vec::new(); r.read_to_end(&mut buf)?; Ok(Self(buf)) } } impl<'a> IntoIterator for &'a GraphicsPart { type Item = GraphicsCommand; type IntoIter = CommandIter<'a>; fn into_iter(self) -> Self::IntoIter { CommandIter(&self.0) } } pub struct CommandIter<'a>(&'a [u8]); #[derive(Debug, Clone, Copy, PartialEq)] pub enum GraphicsCommand { NoFill, Fill(u8), NoStroke, Stroke(u8), StrokeWidth(f32), StrokeDash(f32, f32), Line(Vec3), Close, Point(Vec3), Unknown, } impl<'a> Iterator for CommandIter<'a> { type Item = GraphicsCommand; fn next(&mut self) -> Option { if self.0.len() < 2 { return None; } let step = self.0[0]; let kind = self.0[1]; self.0 = &self.0[2..]; if self.0.len() < step as usize { return None; } let data = &self.0[..step as usize]; self.0 = &self.0[step as usize..]; use GraphicsCommand::*; Some(match (kind, step) { (0, 0) => NoFill, (0, 1) => Fill(data[0]), (1, 0) => NoStroke, (1, 1) => Stroke(data[0]), (2, 4) => StrokeWidth(f32::from_le_bytes([data[0], data[1], data[2], data[3]])), (3, 8) => StrokeDash( f32::from_le_bytes([data[0], data[1], data[2], data[3]]), f32::from_le_bytes([data[4], data[5], data[6], data[7]]), ), (4, 12) => Line(get_vec3(data)), (6, 12) => Point(get_vec3(data)), (5, 0) => Close, _ => Unknown, }) } } impl GraphicsPart { pub fn push(&mut self, cmd: GraphicsCommand) { match cmd { GraphicsCommand::NoFill => self.0.extend([0, 0]), GraphicsCommand::Fill(n) => self.0.extend([1, 0, n]), GraphicsCommand::NoStroke => self.0.extend([0, 1]), GraphicsCommand::Stroke(n) => self.0.extend([1, 1, n]), GraphicsCommand::StrokeWidth(w) => { self.0.extend([4, 2]); self.0.extend(w.to_le_bytes()); } GraphicsCommand::StrokeDash(a, b) => { self.0.extend([8, 3]); self.0.extend(a.to_le_bytes()); self.0.extend(b.to_le_bytes()); } GraphicsCommand::Line(p) => { self.0.extend([12, 4]); self.0.extend(p.x.to_le_bytes()); self.0.extend(p.y.to_le_bytes()); self.0.extend(p.z.to_le_bytes()); } GraphicsCommand::Point(p) => { self.0.extend([12, 6]); self.0.extend(p.x.to_le_bytes()); self.0.extend(p.y.to_le_bytes()); self.0.extend(p.z.to_le_bytes()); } GraphicsCommand::Close => self.0.extend([0, 5]), GraphicsCommand::Unknown => self.0.extend([0, 255]), } } pub fn size(&self) -> usize { self.0.len() } } fn get_vec3(data: &[u8]) -> Vec3 { vec3( f32::from_le_bytes([data[0], data[1], data[2], data[3]]), f32::from_le_bytes([data[4], data[5], data[6], data[7]]), f32::from_le_bytes([data[8], data[9], data[10], data[11]]), ) } #[test] #[cfg(test)] fn test_ser() { use GraphicsCommand::*; let cmds = [ NoFill, Line(Vec3::NEG_ONE), Close, Stroke(4), StrokeWidth(5.), StrokeDash(0.5, 1.), ]; let mut p = GraphicsPart::default(); for c in cmds { p.push(c); } for (i, c) in p.into_iter().enumerate() { assert_eq!(c, cmds[i], "index={i}"); } }