1use crate::Point3;
2use crate::{curve, CurveGroup, Face};
3
4pub struct Model<F: Face> {
5 pub faces: Vec<F>,
6 pub curves: Vec<CurveGroup>,
7}
8
9impl<F: Face> Model<F> {
10 pub fn new() -> Model<F> {
11 Model {
12 faces: Vec::new(),
13 curves: Vec::new(),
14 }
15 }
16
17 pub fn add_face(&mut self, face: F) {
18 self.faces.push(face);
19 }
20
21 pub fn add_curve(&mut self, curve: CurveGroup) {
22 self.curves.push(curve);
23 }
24
25 pub fn save_as_stl<P: AsRef<std::path::Path>>(&self, filename: P) -> std::io::Result<()> {
26 use std::io::{Seek, SeekFrom, Write};
27 let path = std::path::Path::new(filename.as_ref());
28 let name = path.file_stem().unwrap().to_string_lossy();
29 let file = std::fs::File::create(path)?;
30 let mut writer = std::io::BufWriter::new(file);
31
32 let header_size = 80;
33 let mut header = Vec::with_capacity(header_size);
34 writeln!(header, "Binary STL file\nName: {:57}", name)?;
35 header.truncate(header_size);
36 writer.write(&header)?;
37
38 let mut triangle_count: usize = 0;
39 writer.write(&(triangle_count as u32).to_le_bytes())?;
40
41 for face in &self.faces {
42 let mesh = face.get_triangle_mesh();
43 triangle_count += mesh.triangle_count();
44 for triangle in mesh.triangles.chunks(3) {
45 writer.write(&0.0_f32.to_le_bytes())?;
47 writer.write(&0.0_f32.to_le_bytes())?;
48 writer.write(&0.0_f32.to_le_bytes())?;
49 for index in triangle {
51 let point = &mesh.vertices[*index as usize];
52 writer.write(&(point.x as f32).to_le_bytes())?;
53 writer.write(&(point.y as f32).to_le_bytes())?;
54 writer.write(&(point.z as f32).to_le_bytes())?;
55 }
56 writer.write(&[0u8, 0u8])?;
58 }
59 }
60 writer.seek(SeekFrom::Start(header_size as u64))?;
61 dbg!(triangle_count);
62 writer.write(&(triangle_count as u32).to_le_bytes())?;
63 Ok(())
64 }
65
66 pub fn save_as_obj<P: AsRef<std::path::Path>>(&self, filename: P) -> std::io::Result<()> {
67 use std::io::Write;
68 let file = std::fs::File::create(filename)?;
69 let mut writer = std::io::LineWriter::new(file);
70
71 let mut start = 1;
72 for face in &self.faces {
73 let mesh = face.get_triangle_mesh();
74 for point in &mesh.vertices {
75 writeln!(writer, "v {} {} {}", point.x, point.y, point.z)?;
76 }
77 for triangle in mesh.triangles.chunks(3) {
78 writeln!(
79 writer,
80 "f {} {} {}",
81 start + triangle[0],
82 start + triangle[1],
83 start + triangle[2]
84 )?;
85 }
86 start += mesh.vertices.len() as u32;
87 }
88 Ok(())
89 }
90
91 pub fn save_as_svg<P: AsRef<std::path::Path>>(
92 &self,
93 filename: P,
94 (width, height): (f64, f64),
95 ) -> std::io::Result<()> {
96 use curve::Curve;
97 use svg::{
98 node::element::{Group, Path},
99 node::Node,
100 Document,
101 };
102 let mut document = Document::new()
103 .set("width", format!("{}mm", width))
104 .set("height", format!("{}mm", height))
105 .set("viewBox", (0, 0, width, height));
106 let mut group = Group::new()
107 .set("transform", format!("matrix(1 0 0 -1 0 {:.0})", height))
108 .set("fill", "none")
109 .set("stroke", "black")
110 .set("stroke-linecap", "round");
111
112 for curve in &self.curves {
113 let mut data = String::new();
114 for segment in &curve.segments {
115 if let Some(line) = segment.curve.downcast_ref::<curve::Line>() {
116 let (u0, u1) = segment.parameter_range;
117 let start = line.get_point(u0);
118 let end = line.get_point(u1);
119 data.push_str(&format!("M {:.2},{:.2}", start.x, start.y));
120 data.push_str(&format!(" L {:.2},{:.2}", end.x, end.y));
121 continue;
122 }
123 if let Some(polyline) = segment.curve.downcast_ref::<curve::Polyline>() {
124 for (index, point) in polyline.vertices.iter().enumerate() {
125 if index == 0 {
126 data.push_str(&format!("M {:.2},{:.2}", point.x, point.y));
127 } else {
128 data.push_str(&format!(" L {:.2},{:.2}", point.x, point.y));
129 }
130 }
131 continue;
132 }
133 if let Some(bspline) = segment.curve.downcast_ref::<curve::BSplineCurve<Point3>>() {
134 if bspline.degree == 3 {
135 for (index, bezier_curve) in
136 bspline.to_piecewise_bezier().into_iter().enumerate()
137 {
138 let start = bezier_curve.control_points[0];
139 let cp1 = bezier_curve.control_points[1];
140 let cp2 = bezier_curve.control_points[2];
141 let end = bezier_curve.control_points[3];
142 if index == 0 {
143 data.push_str(&format!("M {:.2},{:.2}", start.x, start.y));
144 data.push_str(&format!(
145 " C {:.2},{:.2} {:.2},{:.2} {:.2},{:.2}",
146 cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y
147 ));
148 } else {
149 data.push_str(&format!(
150 " {:.2},{:.2} {:.2},{:.2} {:.2},{:.2}",
151 cp1.x, cp1.y, cp2.x, cp2.y, end.x, end.y
152 ));
153 }
154 }
155 continue;
156 }
157 }
158
159 let points = segment.get_points();
160 for point in points {
161 if data.len() == 0 {
162 data.push_str(&format!("M {:.2},{:.2}", point.x, point.y));
163 } else {
164 data.push_str(&format!(" L {:.2},{:.2}", point.x, point.y));
165 }
166 }
167 }
168 let path = Path::new().set("d", data);
169 group.append(path);
170 }
171 document.append(group);
172 svg::save(filename, &document)?;
173 Ok(())
174 }
175}
176
177mod step_reader;
178pub use step_reader::ModelReader as StepReader;