1use crate::*;
26
27use std::{
28 fmt,
29 io::{BufRead, Error as ioError, Read, Write},
30};
31
32use fnv::FnvHashMap;
33
34use super::{byte_reader::*, from_bytes::*, types::*, utils::*};
35
36const MAX_TRIANGLES_BINARY: u32 = 1_000_000_000;
41
42pub fn save_stl_ascii<M, P, W>(write: &mut W, mesh: &M) -> StlResult<()>
46where
47 M: IsMesh3D<P>,
48 P: IsBuildable3D,
49 W: Write,
50{
51 write.write_all(b"solid STL generated by rust-3d\n")?;
52
53 for i in 0..mesh.num_faces() {
54 let [v1, v2, v3] = mesh.face_vertices(FId { val: i }).unwrap(); let n = mesh.face_normal(FId { val: i }).unwrap(); let buffer = "facet normal ".to_string()
57 + &str_exp(&n)
58 + "\n"
59 + " outer loop\n"
60 + " vertex "
61 + &str_exp(&v1)
62 + "\n"
63 + " vertex "
64 + &str_exp(&v2)
65 + "\n"
66 + " vertex "
67 + &str_exp(&v3)
68 + "\n"
69 + " endloop\n"
70 + "endfacet\n";
71 write.write_all(buffer.as_bytes())?;
72 }
73 write.write_all(b"endsolid STL generated by rust-3d\n")?;
74 Ok(())
75}
76
77pub fn load_stl_mesh_duped<EM, P, R, IPN>(
81 read: &mut R,
82 format: StlFormat,
83 mesh: &mut EM,
84 face_normals: &mut IPN,
85) -> StlIOResult<()>
86where
87 EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
88 P: IsBuildable3D + Clone,
89 R: BufRead,
90 IPN: IsPushable<P>,
91{
92 if is_ascii(read, format).simple()? {
93 load_stl_mesh_duped_ascii(read, mesh, face_normals)
94 } else {
95 load_stl_mesh_duped_binary(read, mesh, face_normals).simple()
96 }
97}
98
99pub fn load_stl_mesh_unique<EM, P, R, IPN>(
103 read: &mut R,
104 format: StlFormat,
105 mesh: &mut EM,
106 face_normals: &mut IPN,
107) -> StlIOResult<()>
108where
109 EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
110 P: IsBuildable3D + Clone,
111 R: BufRead,
112 IPN: IsPushable<P>,
113{
114 if is_ascii(read, format).simple()? {
115 load_stl_mesh_unique_ascii(read, mesh, face_normals)
116 } else {
117 load_stl_mesh_unique_binary(read, mesh, face_normals).simple()
118 }
119}
120
121pub fn load_stl_triplets<IP, P, R, IPN>(
125 read: &mut R,
126 format: StlFormat,
127 ip: &mut IP,
128 face_normals: &mut IPN,
129) -> StlIOResult<()>
130where
131 IP: IsPushable<P>,
132 P: IsBuildable3D,
133 R: BufRead,
134 IPN: IsPushable<P>,
135{
136 if is_ascii(read, format).simple()? {
137 load_stl_triplets_ascii(read, ip, face_normals)
138 } else {
139 load_stl_triplets_binary(read, ip, face_normals).simple()
140 }
141}
142
143fn is_ascii<R>(read: &mut R, format: StlFormat) -> StlResult<bool>
148where
149 R: BufRead,
150{
151 let solid = "solid".as_bytes();
152 let mut buffer = [0u8; 5];
153
154 let mut result = true;
155 read.read_exact(&mut buffer)?;
156
157 for i in 0..5 {
158 if buffer[i] != solid[i] {
159 result = false
160 }
161 }
162
163 Ok(match format {
165 StlFormat::Ascii => true,
166 StlFormat::Binary => false,
167 StlFormat::Auto => result,
168 })
169}
170
171fn load_stl_mesh_duped_ascii<EM, P, R, IPN>(
174 read: &mut R,
175 mesh: &mut EM,
176 face_normals: &mut IPN,
177) -> StlIOResult<()>
178where
179 EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
180 P: IsBuildable3D + Clone,
181 R: BufRead,
182 IPN: IsPushable<P>,
183{
184 let mut i_line = 0;
185 let mut line_buffer = Vec::new();
186
187 read.read_until(b'\n', &mut line_buffer).index(i_line)?;
189 i_line += 1;
190
191 loop {
192 match read_stl_facet(read, &mut line_buffer, &mut i_line) {
193 Ok([a, b, c, n]) => {
194 mesh.add_face(a, b, c);
195 face_normals.push(n);
196 }
197 Err(WithLineInfo::None(StlError::LoadFileEndReached))
198 | Err(WithLineInfo::Index(_, StlError::LoadFileEndReached))
199 | Err(WithLineInfo::Line(_, _, StlError::LoadFileEndReached)) => break,
200 Err(x) => return Err(x),
201 }
202 }
203
204 Ok(())
205}
206
207struct StlTriangle {
208 pub n: [f32; 3],
209 pub x: [f32; 3],
210 pub y: [f32; 3],
211 pub z: [f32; 3],
212}
213
214#[inline(always)]
215fn read_stl_triangle<R>(read: &mut R) -> StlResult<StlTriangle>
216where
217 R: Read,
218{
219 let mut buffer = [0u8; 50];
221 read.read_exact(&mut buffer)?;
222
223 Ok(StlTriangle {
224 n: array_from_bytes_le!(f32, 3, &buffer[0..12])?,
225 x: array_from_bytes_le!(f32, 3, &buffer[12..24])?,
226 y: array_from_bytes_le!(f32, 3, &buffer[24..36])?,
227 z: array_from_bytes_le!(f32, 3, &buffer[36..48])?,
228 })
229}
230
231fn load_stl_mesh_duped_binary<EM, P, R, IPN>(
234 read: &mut R,
235 mesh: &mut EM,
236 face_normals: &mut IPN,
237) -> StlResult<()>
238where
239 EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
240 P: IsBuildable3D + Clone,
241 R: Read,
242 IPN: IsPushable<P>,
243{
244 {
246 let mut buffer = [0u8; 75];
247 read.read_exact(&mut buffer)?;
248 }
249
250 let n_triangles = LittleReader::read_u32(read)?;
251 if n_triangles > MAX_TRIANGLES_BINARY {
252 return Err(StlError::InvalidFaceCount);
253 }
254
255 mesh.reserve_vertices(3 * n_triangles as usize);
256 mesh.reserve_faces(n_triangles as usize);
257
258 face_normals.reserve(n_triangles as usize);
259
260 for _ in 0..n_triangles {
261 let t = read_stl_triangle(read)?;
262
263 let n = P::new(t.n[0] as f64, t.n[1] as f64, t.n[2] as f64);
264 let x = P::new(t.x[0] as f64, t.x[1] as f64, t.x[2] as f64);
265 let y = P::new(t.y[0] as f64, t.y[1] as f64, t.y[2] as f64);
266 let z = P::new(t.z[0] as f64, t.z[1] as f64, t.z[2] as f64);
267
268 mesh.add_face(x, y, z);
269 face_normals.push(n)
270 }
271
272 Ok(())
273}
274
275fn load_stl_mesh_unique_ascii<EM, P, R, IPN>(
278 read: &mut R,
279 mesh: &mut EM,
280 face_normals: &mut IPN,
281) -> StlIOResult<()>
282where
283 EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
284 P: IsBuildable3D + Clone,
285 R: BufRead,
286 IPN: IsPushable<P>,
287{
288 let mut i_line = 0;
289 let mut line_buffer = Vec::new();
290
291 read.read_until(b'\n', &mut line_buffer).index(i_line)?;
293 i_line += 1;
294
295 let mut map = FnvHashMap::default();
296
297 loop {
298 match read_stl_facet::<P, _>(read, &mut line_buffer, &mut i_line) {
299 Ok([a, b, c, n]) => {
300 let id_a = *map.entry(a.clone()).or_insert_with(|| {
301 let value = mesh.num_vertices();
302 mesh.add_vertex(a);
303 value
304 });
305
306 let id_b = *map.entry(b.clone()).or_insert_with(|| {
307 let value = mesh.num_vertices();
308 mesh.add_vertex(b);
309 value
310 });
311
312 let id_c = *map.entry(c.clone()).or_insert_with(|| {
313 let value = mesh.num_vertices();
314 mesh.add_vertex(c);
315 value
316 });
317
318 match mesh.try_add_connection(
321 VId { val: id_a },
322 VId { val: id_b },
323 VId { val: id_c },
324 ) {
325 Ok(_) => {
326 face_normals.push(n);
327 }
328 Err(_) => (),
329 }
330 }
331 Err(WithLineInfo::None(StlError::LoadFileEndReached))
332 | Err(WithLineInfo::Index(_, StlError::LoadFileEndReached))
333 | Err(WithLineInfo::Line(_, _, StlError::LoadFileEndReached)) => break,
334 Err(x) => return Err(x),
335 }
336 }
337
338 Ok(())
339}
340
341fn load_stl_mesh_unique_binary<EM, P, R, IPN>(
344 read: &mut R,
345 mesh: &mut EM,
346 face_normals: &mut IPN,
347) -> StlResult<()>
348where
349 EM: IsFaceEditableMesh<P, Face3> + IsVertexEditableMesh<P, Face3>,
350 P: IsBuildable3D + Clone,
351 R: Read,
352 IPN: IsPushable<P>,
353{
354 {
356 let mut buffer = [0u8; 75];
357 read.read_exact(&mut buffer)?;
358 }
359
360 let n_triangles = LittleReader::read_u32(read)?;
361 if n_triangles > MAX_TRIANGLES_BINARY {
362 return Err(StlError::InvalidFaceCount);
363 }
364
365 mesh.reserve_vertices((0.5 * n_triangles as f64) as usize);
366 mesh.reserve_faces(n_triangles as usize);
367
368 face_normals.reserve(n_triangles as usize);
369
370 let mut map = FnvHashMap::default();
371
372 for _ in 0..n_triangles {
373 let t = read_stl_triangle(read)?;
374
375 let n = P::new(t.n[0] as f64, t.n[1] as f64, t.n[2] as f64);
376 let x = P::new(t.x[0] as f64, t.x[1] as f64, t.x[2] as f64);
377 let y = P::new(t.y[0] as f64, t.y[1] as f64, t.y[2] as f64);
378 let z = P::new(t.z[0] as f64, t.z[1] as f64, t.z[2] as f64);
379
380 let id_x = *map.entry(x.clone()).or_insert_with(|| {
381 let value = mesh.num_vertices();
382 mesh.add_vertex(x);
383 value
384 });
385
386 let id_y = *map.entry(y.clone()).or_insert_with(|| {
387 let value = mesh.num_vertices();
388 mesh.add_vertex(y);
389 value
390 });
391
392 let id_z = *map.entry(z.clone()).or_insert_with(|| {
393 let value = mesh.num_vertices();
394 mesh.add_vertex(z);
395 value
396 });
397
398 match mesh.try_add_connection(VId { val: id_x }, VId { val: id_y }, VId { val: id_z }) {
401 Ok(_) => {
402 face_normals.push(n);
403 }
404 Err(_) => (),
405 }
406 }
407
408 Ok(())
409}
410
411fn load_stl_triplets_ascii<IP, P, R, IPN>(
414 read: &mut R,
415 ip: &mut IP,
416 face_normals: &mut IPN,
417) -> StlIOResult<()>
418where
419 IP: IsPushable<P>,
420 P: IsBuildable3D,
421 R: BufRead,
422 IPN: IsPushable<P>,
423{
424 let mut i_line = 0;
425 let mut line_buffer = Vec::new();
426
427 read.read_until(b'\n', &mut line_buffer).index(i_line)?;
429 i_line += 1;
430
431 loop {
432 match read_stl_facet(read, &mut line_buffer, &mut i_line) {
433 Ok([a, b, c, n]) => {
434 ip.push(a);
435 ip.push(b);
436 ip.push(c);
437 face_normals.push(n);
438 }
439 Err(WithLineInfo::None(StlError::LoadFileEndReached))
440 | Err(WithLineInfo::Index(_, StlError::LoadFileEndReached))
441 | Err(WithLineInfo::Line(_, _, StlError::LoadFileEndReached)) => break,
442 Err(x) => return Err(x),
443 }
444 }
445
446 Ok(())
447}
448
449fn load_stl_triplets_binary<IP, P, R, IPN>(
452 read: &mut R,
453 ip: &mut IP,
454 face_normals: &mut IPN,
455) -> StlResult<()>
456where
457 IP: IsPushable<P>,
458 P: IsBuildable3D,
459 R: Read,
460 IPN: IsPushable<P>,
461{
462 {
464 let mut buffer = [0u8; 75];
465 read.read_exact(&mut buffer)?;
466 }
467
468 let n_triangles = LittleReader::read_u32(read)?;
469 if n_triangles > MAX_TRIANGLES_BINARY {
470 return Err(StlError::InvalidFaceCount);
471 }
472
473 ip.reserve(3 * n_triangles as usize);
474 face_normals.reserve(n_triangles as usize);
475
476 for _ in 0..n_triangles {
477 let t = read_stl_triangle(read)?;
478
479 let n = P::new(t.n[0] as f64, t.n[1] as f64, t.n[2] as f64);
480 let x = P::new(t.x[0] as f64, t.x[1] as f64, t.x[2] as f64);
481 let y = P::new(t.y[0] as f64, t.y[1] as f64, t.y[2] as f64);
482 let z = P::new(t.z[0] as f64, t.z[1] as f64, t.z[2] as f64);
483
484 ip.push(x);
485 ip.push(y);
486 ip.push(z);
487
488 face_normals.push(n)
489 }
490
491 Ok(())
492}
493
494fn read_stl_facet<P, R>(
497 read: &mut R,
498 line_buffer: &mut Vec<u8>,
499 i_line: &mut usize,
500) -> StlIOResult<[P; 4]>
501where
502 P: IsBuildable3D,
503 R: BufRead,
504{
505 let mut line: &[u8];
506
507 line = trim_start(fetch_line(read, line_buffer).index(*i_line)?);
508 *i_line += 1;
509
510 if line.starts_with(b"endsolid") {
511 return Err(StlError::LoadFileEndReached).line(*i_line, line);
512 }
513
514 if !line.starts_with(b"facet") {
515 return Err(StlError::Facet).line(*i_line, line);
516 }
517
518 let n = read_stl_normal(&line).unwrap_or(P::new(0.0, 0.0, 1.0));
519
520 line = trim_start(fetch_line(read, line_buffer).index(*i_line)?);
521 *i_line += 1;
522
523 if !line.starts_with(b"outer loop") {
524 return Err(StlError::Loop).line(*i_line, line);
525 }
526
527 line = fetch_line(read, line_buffer).index(*i_line)?;
528 *i_line += 1;
529
530 let a = read_stl_vertex(&line)
531 .ok_or(StlError::Vertex)
532 .line(*i_line, line)?;
533
534 line = fetch_line(read, line_buffer).index(*i_line)?;
535 *i_line += 1;
536
537 let b = read_stl_vertex(&line)
538 .ok_or(StlError::Vertex)
539 .line(*i_line, line)?;
540
541 line = fetch_line(read, line_buffer).index(*i_line)?;
542 *i_line += 1;
543
544 let c = read_stl_vertex(&line)
545 .ok_or(StlError::Vertex)
546 .line(*i_line, line)?;
547
548 line = trim_start(fetch_line(read, line_buffer).index(*i_line)?);
549 *i_line += 1;
550
551 if !line.starts_with(b"endloop") {
552 return Err(StlError::EndLoop).line(*i_line, line);
553 }
554
555 line = trim_start(fetch_line(read, line_buffer).index(*i_line)?);
556 *i_line += 1;
557
558 if !line.starts_with(b"endfacet") {
559 return Err(StlError::EndFacet).line(*i_line, line);
560 }
561
562 Ok([a, b, c, n])
563}
564
565fn read_stl_vertex<P>(line: &[u8]) -> Option<P>
568where
569 P: IsBuildable3D,
570{
571 let mut words = to_words_skip_empty(line);
572
573 words.next()?;
575
576 let x = from_ascii(words.next()?)?;
577 let y = from_ascii(words.next()?)?;
578 let z = from_ascii(words.next()?)?;
579
580 Some(P::new(x, y, z))
581}
582
583fn read_stl_normal<P>(line: &[u8]) -> Option<P>
586where
587 P: IsBuildable3D,
588{
589 let mut words = to_words_skip_empty(line);
590
591 words.next()?;
593
594 words.next()?;
596
597 let i = from_ascii(words.next()?)?;
598 let j = from_ascii(words.next()?)?;
599 let k = from_ascii(words.next()?)?;
600
601 Some(P::new(i, j, k))
602}
603
604fn str_exp<P>(p: &P) -> String
607where
608 P: Is3D,
609{
610 format!("{:e} {:e} {:e}", p.x(), p.y(), p.z()).to_string()
611}
612
613#[derive(Copy, Clone)]
617pub enum StlFormat {
618 Ascii,
619 Binary,
620 Auto,
621}
622
623impl Default for StlFormat {
624 fn default() -> Self {
625 Self::Auto
626 }
627}
628
629pub enum StlError {
633 LoadFileEndReached,
634 AccessFile,
635 BinaryData,
636 InvalidFaceCount,
637 Facet,
638 EndFacet,
639 Vertex,
640 Loop,
641 EndLoop,
642}
643
644pub type StlResult<T> = std::result::Result<T, StlError>;
646
647pub type StlIOResult<T> = IOResult<T, StlError>;
649
650impl fmt::Debug for StlError {
651 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652 match self {
653 Self::LoadFileEndReached => write!(f, "Unexpected reach of .stl file end"),
654 Self::AccessFile => write!(f, "Unable to access file"),
655 Self::BinaryData => write!(f, "Binary data seems to be invalid"),
656 Self::InvalidFaceCount => write!(f, "Containing an invalid face count"),
657 Self::Facet => write!(f, "Unable to parse facet"),
658 Self::EndFacet => write!(f, "Unable to parse endfacet"),
659 Self::Vertex => write!(f, "Unable to parse vertex"),
660 Self::Loop => write!(f, "Unable to parse loop"),
661 Self::EndLoop => write!(f, "Unable to parse endloop"),
662 }
663 }
664}
665
666impl fmt::Display for StlError {
667 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
668 write!(f, "{:?}", self)
669 }
670}
671
672impl From<ioError> for StlError {
673 fn from(_error: ioError) -> Self {
674 StlError::AccessFile
675 }
676}
677
678impl From<WithLineInfo<ioError>> for WithLineInfo<StlError> {
679 fn from(other: WithLineInfo<ioError>) -> Self {
680 match other {
681 WithLineInfo::<ioError>::None(x) => WithLineInfo::None(StlError::from(x)),
682 WithLineInfo::<ioError>::Index(i, x) => WithLineInfo::Index(i, StlError::from(x)),
683 WithLineInfo::<ioError>::Line(i, l, x) => WithLineInfo::Line(i, l, StlError::from(x)),
684 }
685 }
686}
687
688impl From<WithLineInfo<FetchLineError>> for WithLineInfo<StlError> {
689 fn from(other: WithLineInfo<FetchLineError>) -> Self {
690 match other {
691 WithLineInfo::<FetchLineError>::None(x) => WithLineInfo::None(StlError::from(x)),
692 WithLineInfo::<FetchLineError>::Index(i, x) => {
693 WithLineInfo::Index(i, StlError::from(x))
694 }
695 WithLineInfo::<FetchLineError>::Line(i, l, x) => {
696 WithLineInfo::Line(i, l, StlError::from(x))
697 }
698 }
699 }
700}
701
702impl From<std::array::TryFromSliceError> for StlError {
703 fn from(_error: std::array::TryFromSliceError) -> Self {
704 StlError::BinaryData
705 }
706}
707impl From<FromBytesError> for StlError {
708 fn from(_error: FromBytesError) -> Self {
709 StlError::BinaryData
710 }
711}
712
713impl From<FetchLineError> for StlError {
714 fn from(_error: FetchLineError) -> Self {
715 StlError::LoadFileEndReached
716 }
717}