1#![allow(clippy::needless_range_loop)]
6#[allow(unused_imports)]
7use super::functions::*;
8#[derive(Debug, Clone)]
12pub struct IgesReader {
13 pub entities: Vec<IgesEntity>,
15 pub global_params: Vec<String>,
17 pub units: LengthUnit,
19}
20impl IgesReader {
21 pub fn new() -> Self {
23 Self {
24 entities: Vec::new(),
25 global_params: Vec::new(),
26 units: LengthUnit::Millimeter,
27 }
28 }
29 #[allow(dead_code)]
34 pub fn parse(&mut self, content: &str) -> Result<(), String> {
35 self.entities.clear();
36 self.global_params.clear();
37 let lines: Vec<&str> = content.lines().collect();
38 let mut _start_lines = Vec::new();
39 let mut global_lines = Vec::new();
40 let mut directory_lines = Vec::new();
41 let mut parameter_lines = Vec::new();
42 for line in &lines {
43 if line.len() < 73 {
44 continue;
45 }
46 let section = line.chars().nth(72).unwrap_or(' ');
47 match section {
48 'S' => _start_lines.push(*line),
49 'G' => global_lines.push(*line),
50 'D' => directory_lines.push(*line),
51 'P' => parameter_lines.push(*line),
52 _ => {}
53 }
54 }
55 let global_text: String = global_lines
56 .iter()
57 .map(|l| &l[..72.min(l.len())])
58 .collect::<Vec<_>>()
59 .join("");
60 self.global_params = global_text
61 .split(',')
62 .map(|s| s.trim().to_string())
63 .collect();
64 let mut i = 0;
65 while i + 1 < directory_lines.len() {
66 let line1 = directory_lines[i];
67 let _line2 = directory_lines[i + 1];
68 let entity_type_str = line1[0..8.min(line1.len())].trim();
69 if let Ok(etype_num) = entity_type_str.parse::<u32>() {
70 let etype = IgesEntityType::from_type_number(etype_num);
71 let seq = i / 2 + 1;
72 let entity = IgesEntity::new(seq, etype);
73 self.entities.push(entity);
74 }
75 i += 2;
76 }
77 let param_text: String = parameter_lines
78 .iter()
79 .map(|l| &l[..64.min(l.len())])
80 .collect::<Vec<_>>()
81 .join("");
82 let records: Vec<&str> = param_text.split(';').collect();
83 for (idx, record) in records.iter().enumerate() {
84 if idx >= self.entities.len() {
85 break;
86 }
87 let values: Vec<f64> = record
88 .split(',')
89 .filter_map(|s| {
90 let s = s.trim();
91 if s.is_empty() {
92 None
93 } else {
94 s.parse::<f64>().ok()
95 }
96 })
97 .collect();
98 self.entities[idx].parameters = values;
99 }
100 Ok(())
101 }
102 #[allow(dead_code)]
104 pub fn find_entities(&self, etype: IgesEntityType) -> Vec<&IgesEntity> {
105 self.entities
106 .iter()
107 .filter(|e| e.entity_type == etype)
108 .collect()
109 }
110 #[allow(dead_code)]
112 pub fn entity_count(&self) -> usize {
113 self.entities.len()
114 }
115 #[allow(dead_code)]
117 pub fn extract_points(&self) -> Vec<[f64; 3]> {
118 let mut points = Vec::new();
119 for entity in self.find_entities(IgesEntityType::Point) {
120 if entity.parameters.len() >= 3 {
121 points.push([
122 entity.parameters[0],
123 entity.parameters[1],
124 entity.parameters[2],
125 ]);
126 }
127 }
128 points
129 }
130 #[allow(dead_code)]
132 pub fn extract_lines(&self) -> Vec<([f64; 3], [f64; 3])> {
133 let mut lines = Vec::new();
134 for entity in self.find_entities(IgesEntityType::Line) {
135 if entity.parameters.len() >= 6 {
136 let p1 = [
137 entity.parameters[0],
138 entity.parameters[1],
139 entity.parameters[2],
140 ];
141 let p2 = [
142 entity.parameters[3],
143 entity.parameters[4],
144 entity.parameters[5],
145 ];
146 lines.push((p1, p2));
147 }
148 }
149 lines
150 }
151 #[allow(dead_code)]
153 pub fn extract_circular_arcs(&self) -> Vec<IgesCircularArc> {
154 let mut arcs = Vec::new();
155 for entity in self.find_entities(IgesEntityType::CircularArc) {
156 if entity.parameters.len() >= 6 {
157 arcs.push(IgesCircularArc {
158 z_displacement: entity.parameters[0],
159 center: [entity.parameters[1], entity.parameters[2]],
160 start: [entity.parameters[3], entity.parameters[4]],
161 end: if entity.parameters.len() >= 7 {
162 [entity.parameters[5], entity.parameters[6]]
163 } else {
164 [entity.parameters[3], entity.parameters[4]]
165 },
166 });
167 }
168 }
169 arcs
170 }
171}
172#[derive(Debug, Clone)]
174pub struct IgesCircularArc {
175 pub z_displacement: f64,
177 pub center: [f64; 2],
179 pub start: [f64; 2],
181 pub end: [f64; 2],
183}
184impl IgesCircularArc {
185 #[allow(dead_code)]
187 pub fn radius(&self) -> f64 {
188 let dx = self.start[0] - self.center[0];
189 let dy = self.start[1] - self.center[1];
190 (dx * dx + dy * dy).sqrt()
191 }
192 #[allow(dead_code)]
194 pub fn center_3d(&self) -> [f64; 3] {
195 [self.center[0], self.center[1], self.z_displacement]
196 }
197}
198#[derive(Debug, Clone)]
200pub struct AssemblyComponent {
201 pub name: String,
203 pub solid: BrepSolid,
205 pub transform: AssemblyTransform,
207 pub children: Vec<AssemblyComponent>,
209}
210impl AssemblyComponent {
211 pub fn new(name: &str, solid: BrepSolid, transform: AssemblyTransform) -> Self {
213 Self {
214 name: name.to_string(),
215 solid,
216 transform,
217 children: Vec::new(),
218 }
219 }
220 #[allow(dead_code)]
222 pub fn add_child(&mut self, child: AssemblyComponent) {
223 self.children.push(child);
224 }
225 #[allow(dead_code)]
227 pub fn total_components(&self) -> usize {
228 1 + self
229 .children
230 .iter()
231 .map(|c| c.total_components())
232 .sum::<usize>()
233 }
234 #[allow(dead_code)]
236 pub fn world_bounding_box(&self) -> BoundingBox {
237 let mut bb = BoundingBox::empty();
238 for v in &self.solid.vertices {
239 let world_pos = self.transform.apply(v.position);
240 bb.include_point(world_pos);
241 }
242 for child in &self.children {
243 let child_bb = child.world_bounding_box();
244 bb.include_box(&child_bb);
245 }
246 bb
247 }
248 #[allow(dead_code)]
250 pub fn leaf_names(&self) -> Vec<String> {
251 if self.children.is_empty() {
252 vec![self.name.clone()]
253 } else {
254 self.children.iter().flat_map(|c| c.leaf_names()).collect()
255 }
256 }
257}
258#[derive(Debug, Clone)]
260pub struct Assembly {
261 pub name: String,
263 pub root: AssemblyComponent,
265 pub units: LengthUnit,
267}
268impl Assembly {
269 pub fn new(name: &str, root: AssemblyComponent, units: LengthUnit) -> Self {
271 Self {
272 name: name.to_string(),
273 root,
274 units,
275 }
276 }
277 #[allow(dead_code)]
279 pub fn total_components(&self) -> usize {
280 self.root.total_components()
281 }
282 #[allow(dead_code)]
284 pub fn bounding_box(&self) -> BoundingBox {
285 self.root.world_bounding_box()
286 }
287 #[allow(dead_code)]
289 pub fn to_stl(&self) -> String {
290 let exporter = StlExporter::new(&self.name);
291 let mut mesh = TriangleMesh::new();
292 self.collect_meshes(&self.root, &AssemblyTransform::identity(), &mut mesh);
293 exporter.to_ascii_stl(&mesh)
294 }
295 #[allow(dead_code)]
297 fn collect_meshes(
298 &self,
299 component: &AssemblyComponent,
300 parent_transform: &AssemblyTransform,
301 mesh: &mut TriangleMesh,
302 ) {
303 let world_transform = parent_transform.compose(&component.transform);
304 let component_mesh = tessellate_brep(&component.solid, 10);
305 let offset = mesh.vertices.len();
306 for v in &component_mesh.vertices {
307 mesh.vertices.push(world_transform.apply(*v));
308 }
309 for tri in &component_mesh.triangles {
310 mesh.triangles
311 .push([tri[0] + offset, tri[1] + offset, tri[2] + offset]);
312 }
313 for child in &component.children {
314 self.collect_meshes(child, &world_transform, mesh);
315 }
316 }
317}
318#[derive(Debug, Clone)]
323pub struct StepParser {
324 pub entities: Vec<StepEntity>,
326 pub description: String,
328 pub schema: String,
330}
331impl StepParser {
332 pub fn new() -> Self {
334 Self {
335 entities: Vec::new(),
336 description: String::new(),
337 schema: String::new(),
338 }
339 }
340 #[allow(dead_code)]
342 pub fn parse(&mut self, content: &str) -> Result<(), String> {
343 self.entities.clear();
344 self.description.clear();
345 self.schema.clear();
346 let mut in_data = false;
347 for line in content.lines() {
348 let trimmed = line.trim();
349 if trimmed == "DATA;" {
350 in_data = true;
351 continue;
352 }
353 if trimmed == "ENDSEC;" {
354 if in_data {
355 in_data = false;
356 }
357 continue;
358 }
359 if trimmed.starts_with("FILE_DESCRIPTION") {
360 self.description = trimmed.to_string();
361 continue;
362 }
363 if trimmed.starts_with("FILE_SCHEMA") {
364 self.schema = trimmed.to_string();
365 continue;
366 }
367 if in_data
368 && trimmed.starts_with('#')
369 && let Some(entity) = self.parse_entity_line(trimmed)
370 {
371 self.entities.push(entity);
372 }
373 }
374 Ok(())
375 }
376 #[allow(dead_code)]
378 fn parse_entity_line(&self, line: &str) -> Option<StepEntity> {
379 let line = line.trim_end_matches(';');
380 let eq_pos = line.find('=')?;
381 let id_str = &line[1..eq_pos];
382 let id: usize = id_str.parse().ok()?;
383 let rest = &line[eq_pos + 1..];
384 let paren_pos = rest.find('(');
385 let (entity_type, params) = if let Some(pos) = paren_pos {
386 let etype = rest[..pos].trim();
387 let params = &rest[pos..];
388 (etype, params)
389 } else {
390 (rest.trim(), "")
391 };
392 Some(StepEntity::new(id, entity_type, params))
393 }
394 #[allow(dead_code)]
396 pub fn find_entities(&self, entity_type: &str) -> Vec<&StepEntity> {
397 self.entities
398 .iter()
399 .filter(|e| e.entity_type == entity_type)
400 .collect()
401 }
402 #[allow(dead_code)]
404 pub fn find_by_id(&self, id: usize) -> Option<&StepEntity> {
405 self.entities.iter().find(|e| e.id == id)
406 }
407 #[allow(dead_code)]
409 pub fn entity_count(&self) -> usize {
410 self.entities.len()
411 }
412 #[allow(dead_code)]
414 pub fn extract_cartesian_points(&self) -> Vec<(usize, [f64; 3])> {
415 let mut points = Vec::new();
416 for entity in self.find_entities("CARTESIAN_POINT") {
417 if let Some(coords) = self.parse_point_coordinates(&entity.parameters) {
418 points.push((entity.id, coords));
419 }
420 }
421 points
422 }
423 #[allow(dead_code)]
425 fn parse_point_coordinates(&self, params: &str) -> Option<[f64; 3]> {
426 let inner_start = params.rfind('(')?;
427 let inner_end = params[inner_start..].find(')')? + inner_start;
428 let coord_str = ¶ms[inner_start + 1..inner_end];
429 let coords: Vec<f64> = coord_str
430 .split(',')
431 .filter_map(|s| s.trim().parse::<f64>().ok())
432 .collect();
433 if coords.len() >= 3 {
434 Some([coords[0], coords[1], coords[2]])
435 } else if coords.len() == 2 {
436 Some([coords[0], coords[1], 0.0])
437 } else {
438 None
439 }
440 }
441 #[allow(dead_code)]
443 pub fn extract_directions(&self) -> Vec<(usize, [f64; 3])> {
444 let mut dirs = Vec::new();
445 for entity in self.find_entities("DIRECTION") {
446 if let Some(dir) = self.parse_point_coordinates(&entity.parameters) {
447 dirs.push((entity.id, dir));
448 }
449 }
450 dirs
451 }
452}
453#[derive(Debug, Clone, Copy)]
455pub struct BoundingBox {
456 pub min: [f64; 3],
458 pub max: [f64; 3],
460}
461impl BoundingBox {
462 pub fn empty() -> Self {
464 Self {
465 min: [f64::INFINITY; 3],
466 max: [f64::NEG_INFINITY; 3],
467 }
468 }
469 pub fn new(min: [f64; 3], max: [f64; 3]) -> Self {
471 Self { min, max }
472 }
473 #[allow(dead_code)]
475 pub fn include_point(&mut self, p: [f64; 3]) {
476 for i in 0..3 {
477 self.min[i] = self.min[i].min(p[i]);
478 self.max[i] = self.max[i].max(p[i]);
479 }
480 }
481 #[allow(dead_code)]
483 pub fn include_box(&mut self, other: &BoundingBox) {
484 for i in 0..3 {
485 self.min[i] = self.min[i].min(other.min[i]);
486 self.max[i] = self.max[i].max(other.max[i]);
487 }
488 }
489 #[allow(dead_code)]
491 pub fn from_points(points: &[[f64; 3]]) -> Self {
492 let mut bb = Self::empty();
493 for p in points {
494 bb.include_point(*p);
495 }
496 bb
497 }
498 #[allow(dead_code)]
500 pub fn center(&self) -> [f64; 3] {
501 [
502 (self.min[0] + self.max[0]) * 0.5,
503 (self.min[1] + self.max[1]) * 0.5,
504 (self.min[2] + self.max[2]) * 0.5,
505 ]
506 }
507 #[allow(dead_code)]
509 pub fn size(&self) -> [f64; 3] {
510 [
511 self.max[0] - self.min[0],
512 self.max[1] - self.min[1],
513 self.max[2] - self.min[2],
514 ]
515 }
516 #[allow(dead_code)]
518 pub fn diagonal(&self) -> f64 {
519 let s = self.size();
520 (s[0] * s[0] + s[1] * s[1] + s[2] * s[2]).sqrt()
521 }
522 #[allow(dead_code)]
524 pub fn volume(&self) -> f64 {
525 let s = self.size();
526 s[0] * s[1] * s[2]
527 }
528 #[allow(dead_code)]
530 pub fn surface_area(&self) -> f64 {
531 let s = self.size();
532 2.0 * (s[0] * s[1] + s[1] * s[2] + s[2] * s[0])
533 }
534 #[allow(dead_code)]
536 pub fn contains_point(&self, p: [f64; 3]) -> bool {
537 p[0] >= self.min[0]
538 && p[0] <= self.max[0]
539 && p[1] >= self.min[1]
540 && p[1] <= self.max[1]
541 && p[2] >= self.min[2]
542 && p[2] <= self.max[2]
543 }
544 #[allow(dead_code)]
546 pub fn intersects(&self, other: &BoundingBox) -> bool {
547 self.min[0] <= other.max[0]
548 && self.max[0] >= other.min[0]
549 && self.min[1] <= other.max[1]
550 && self.max[1] >= other.min[1]
551 && self.min[2] <= other.max[2]
552 && self.max[2] >= other.min[2]
553 }
554 #[allow(dead_code)]
556 pub fn is_valid(&self) -> bool {
557 self.min[0] <= self.max[0] && self.min[1] <= self.max[1] && self.min[2] <= self.max[2]
558 }
559 #[allow(dead_code)]
561 pub fn convert_units(&self, converter: &UnitConverter) -> BoundingBox {
562 BoundingBox {
563 min: converter.convert_point(self.min),
564 max: converter.convert_point(self.max),
565 }
566 }
567}
568#[derive(Debug, Clone)]
570pub struct BrepVertex {
571 pub id: usize,
573 pub position: [f64; 3],
575 pub tolerance: f64,
577}
578impl BrepVertex {
579 pub fn new(id: usize, position: [f64; 3]) -> Self {
581 Self {
582 id,
583 position,
584 tolerance: 1e-6,
585 }
586 }
587}
588#[derive(Debug, Clone)]
590pub struct BrepEdge {
591 pub id: usize,
593 pub start_vertex: usize,
595 pub end_vertex: usize,
597 pub curve_type: CurveType,
599 pub parameter_range: [f64; 2],
601 pub control_points: Vec<[f64; 3]>,
603}
604impl BrepEdge {
605 pub fn line(id: usize, start: usize, end: usize) -> Self {
607 Self {
608 id,
609 start_vertex: start,
610 end_vertex: end,
611 curve_type: CurveType::Line,
612 parameter_range: [0.0, 1.0],
613 control_points: Vec::new(),
614 }
615 }
616 pub fn arc(id: usize, start: usize, end: usize, center: [f64; 3]) -> Self {
618 Self {
619 id,
620 start_vertex: start,
621 end_vertex: end,
622 curve_type: CurveType::CircularArc,
623 parameter_range: [0.0, 1.0],
624 control_points: vec![center],
625 }
626 }
627 #[allow(dead_code)]
629 pub fn evaluate(&self, t: f64, vertices: &[BrepVertex]) -> [f64; 3] {
630 let p0 = vertices[self.start_vertex].position;
631 let p1 = vertices[self.end_vertex].position;
632 match self.curve_type {
633 CurveType::Line => lerp3(p0, p1, t),
634 CurveType::CircularArc => {
635 if let Some(center) = self.control_points.first() {
636 let r0 = sub3(p0, *center);
637 let r1 = sub3(p1, *center);
638 let angle = t * std::f64::consts::PI * 0.5;
639 let cos_a = angle.cos();
640 let sin_a = angle.sin();
641 let r = add3(scale3(r0, cos_a), scale3(r1, sin_a));
642 let radius = (len3(r0) + len3(r1)) * 0.5;
643 let r_len = len3(r);
644 if r_len < 1e-15 {
645 *center
646 } else {
647 add3(*center, scale3(r, radius / r_len))
648 }
649 } else {
650 lerp3(p0, p1, t)
651 }
652 }
653 CurveType::BSpline | CurveType::Nurbs => lerp3(p0, p1, t),
654 }
655 }
656 #[allow(dead_code)]
658 pub fn approximate_length(&self, vertices: &[BrepVertex]) -> f64 {
659 let n = 20;
660 let mut length = 0.0;
661 let mut prev = self.evaluate(0.0, vertices);
662 for i in 1..=n {
663 let t = i as f64 / n as f64;
664 let curr = self.evaluate(t, vertices);
665 length += len3(sub3(curr, prev));
666 prev = curr;
667 }
668 length
669 }
670}
671#[derive(Debug, Clone, Copy, PartialEq)]
673pub enum SurfaceType {
674 Plane,
676 Cylinder,
678 Sphere,
680 Cone,
682 Torus,
684 BSpline,
686}
687#[derive(Debug, Clone)]
689pub struct BrepSolid {
690 pub name: String,
692 pub vertices: Vec<BrepVertex>,
694 pub edges: Vec<BrepEdge>,
696 pub faces: Vec<BrepFace>,
698}
699impl BrepSolid {
700 pub fn new(name: &str) -> Self {
702 Self {
703 name: name.to_string(),
704 vertices: Vec::new(),
705 edges: Vec::new(),
706 faces: Vec::new(),
707 }
708 }
709 #[allow(dead_code)]
711 pub fn add_vertex(&mut self, position: [f64; 3]) -> usize {
712 let id = self.vertices.len();
713 self.vertices.push(BrepVertex::new(id, position));
714 id
715 }
716 #[allow(dead_code)]
718 pub fn add_line_edge(&mut self, start: usize, end: usize) -> usize {
719 let id = self.edges.len();
720 self.edges.push(BrepEdge::line(id, start, end));
721 id
722 }
723 #[allow(dead_code)]
725 pub fn add_planar_face(
726 &mut self,
727 edge_loop: Vec<usize>,
728 normal: [f64; 3],
729 origin: [f64; 3],
730 ) -> usize {
731 let id = self.faces.len();
732 self.faces
733 .push(BrepFace::planar(id, edge_loop, normal, origin));
734 id
735 }
736 #[allow(dead_code)]
738 pub fn bounding_box(&self) -> BoundingBox {
739 BoundingBox::from_points(&self.vertices.iter().map(|v| v.position).collect::<Vec<_>>())
740 }
741 #[allow(dead_code)]
743 pub fn validate(&self) -> Result<(), String> {
744 let n_verts = self.vertices.len();
745 let n_edges = self.edges.len();
746 for edge in &self.edges {
747 if edge.start_vertex >= n_verts {
748 return Err(format!(
749 "Edge {} has invalid start vertex {}",
750 edge.id, edge.start_vertex
751 ));
752 }
753 if edge.end_vertex >= n_verts {
754 return Err(format!(
755 "Edge {} has invalid end vertex {}",
756 edge.id, edge.end_vertex
757 ));
758 }
759 }
760 for face in &self.faces {
761 for loop_edges in &face.edge_loops {
762 for &eidx in loop_edges {
763 if eidx >= n_edges {
764 return Err(format!("Face {} references invalid edge {}", face.id, eidx));
765 }
766 }
767 }
768 }
769 Ok(())
770 }
771 #[allow(dead_code)]
773 pub fn euler_characteristic(&self) -> i64 {
774 self.vertices.len() as i64 - self.edges.len() as i64 + self.faces.len() as i64
775 }
776 #[allow(dead_code)]
778 pub fn create_box(name: &str, sx: f64, sy: f64, sz: f64) -> Self {
779 let mut solid = Self::new(name);
780 let v0 = solid.add_vertex([0.0, 0.0, 0.0]);
781 let v1 = solid.add_vertex([sx, 0.0, 0.0]);
782 let v2 = solid.add_vertex([sx, sy, 0.0]);
783 let v3 = solid.add_vertex([0.0, sy, 0.0]);
784 let v4 = solid.add_vertex([0.0, 0.0, sz]);
785 let v5 = solid.add_vertex([sx, 0.0, sz]);
786 let v6 = solid.add_vertex([sx, sy, sz]);
787 let v7 = solid.add_vertex([0.0, sy, sz]);
788 let e0 = solid.add_line_edge(v0, v1);
789 let e1 = solid.add_line_edge(v1, v2);
790 let e2 = solid.add_line_edge(v2, v3);
791 let e3 = solid.add_line_edge(v3, v0);
792 let e4 = solid.add_line_edge(v4, v5);
793 let e5 = solid.add_line_edge(v5, v6);
794 let e6 = solid.add_line_edge(v6, v7);
795 let e7 = solid.add_line_edge(v7, v4);
796 let e8 = solid.add_line_edge(v0, v4);
797 let e9 = solid.add_line_edge(v1, v5);
798 let e10 = solid.add_line_edge(v2, v6);
799 let e11 = solid.add_line_edge(v3, v7);
800 solid.add_planar_face(vec![e0, e1, e2, e3], [0.0, 0.0, -1.0], [0.0, 0.0, 0.0]);
801 solid.add_planar_face(vec![e4, e5, e6, e7], [0.0, 0.0, 1.0], [0.0, 0.0, sz]);
802 solid.add_planar_face(vec![e0, e9, e4, e8], [0.0, -1.0, 0.0], [0.0, 0.0, 0.0]);
803 solid.add_planar_face(vec![e2, e11, e6, e10], [0.0, 1.0, 0.0], [0.0, sy, 0.0]);
804 solid.add_planar_face(vec![e3, e8, e7, e11], [-1.0, 0.0, 0.0], [0.0, 0.0, 0.0]);
805 solid.add_planar_face(vec![e1, e10, e5, e9], [1.0, 0.0, 0.0], [sx, 0.0, 0.0]);
806 solid
807 }
808}
809#[derive(Debug, Clone)]
811pub struct BrepFace {
812 pub id: usize,
814 pub edge_loops: Vec<Vec<usize>>,
816 pub surface_type: SurfaceType,
818 pub normal: [f64; 3],
820 pub origin: [f64; 3],
822 pub params: Vec<f64>,
824}
825impl BrepFace {
826 pub fn planar(id: usize, edge_loop: Vec<usize>, normal: [f64; 3], origin: [f64; 3]) -> Self {
828 Self {
829 id,
830 edge_loops: vec![edge_loop],
831 surface_type: SurfaceType::Plane,
832 normal,
833 origin,
834 params: Vec::new(),
835 }
836 }
837 pub fn cylindrical(
839 id: usize,
840 edge_loop: Vec<usize>,
841 axis: [f64; 3],
842 origin: [f64; 3],
843 radius: f64,
844 ) -> Self {
845 Self {
846 id,
847 edge_loops: vec![edge_loop],
848 surface_type: SurfaceType::Cylinder,
849 normal: axis,
850 origin,
851 params: vec![radius],
852 }
853 }
854 pub fn spherical(id: usize, edge_loop: Vec<usize>, center: [f64; 3], radius: f64) -> Self {
856 Self {
857 id,
858 edge_loops: vec![edge_loop],
859 surface_type: SurfaceType::Sphere,
860 normal: [0.0, 0.0, 1.0],
861 origin: center,
862 params: vec![radius],
863 }
864 }
865 #[allow(dead_code)]
867 pub fn outer_loop(&self) -> &[usize] {
868 &self.edge_loops[0]
869 }
870 #[allow(dead_code)]
872 pub fn hole_loops(&self) -> &[Vec<usize>] {
873 if self.edge_loops.len() > 1 {
874 &self.edge_loops[1..]
875 } else {
876 &[]
877 }
878 }
879}
880#[derive(Debug, Clone, Copy, PartialEq)]
882pub enum IgesEntityType {
883 CircularArc,
885 CompositeCurve,
887 ConicArc,
889 CopiousData,
891 Plane,
893 Line,
895 ParametricSpline,
897 ParametricSplineSurface,
899 Point,
901 RuledSurface,
903 SurfaceOfRevolution,
905 TabulatedCylinder,
907 TransformationMatrix,
909 RationalBSplineCurve,
911 RationalBSplineSurface,
913 OffsetCurve,
915 OffsetSurface,
917 CurveOnSurface,
919 TrimmedSurface,
921 Unknown(u32),
923}
924impl IgesEntityType {
925 #[allow(dead_code)]
927 pub fn from_type_number(num: u32) -> Self {
928 match num {
929 100 => IgesEntityType::CircularArc,
930 102 => IgesEntityType::CompositeCurve,
931 104 => IgesEntityType::ConicArc,
932 106 => IgesEntityType::CopiousData,
933 108 => IgesEntityType::Plane,
934 110 => IgesEntityType::Line,
935 112 => IgesEntityType::ParametricSpline,
936 114 => IgesEntityType::ParametricSplineSurface,
937 116 => IgesEntityType::Point,
938 118 => IgesEntityType::RuledSurface,
939 120 => IgesEntityType::SurfaceOfRevolution,
940 122 => IgesEntityType::TabulatedCylinder,
941 124 => IgesEntityType::TransformationMatrix,
942 126 => IgesEntityType::RationalBSplineCurve,
943 128 => IgesEntityType::RationalBSplineSurface,
944 130 => IgesEntityType::OffsetCurve,
945 140 => IgesEntityType::OffsetSurface,
946 142 => IgesEntityType::CurveOnSurface,
947 144 => IgesEntityType::TrimmedSurface,
948 _ => IgesEntityType::Unknown(num),
949 }
950 }
951 #[allow(dead_code)]
953 pub fn to_type_number(&self) -> u32 {
954 match self {
955 IgesEntityType::CircularArc => 100,
956 IgesEntityType::CompositeCurve => 102,
957 IgesEntityType::ConicArc => 104,
958 IgesEntityType::CopiousData => 106,
959 IgesEntityType::Plane => 108,
960 IgesEntityType::Line => 110,
961 IgesEntityType::ParametricSpline => 112,
962 IgesEntityType::ParametricSplineSurface => 114,
963 IgesEntityType::Point => 116,
964 IgesEntityType::RuledSurface => 118,
965 IgesEntityType::SurfaceOfRevolution => 120,
966 IgesEntityType::TabulatedCylinder => 122,
967 IgesEntityType::TransformationMatrix => 124,
968 IgesEntityType::RationalBSplineCurve => 126,
969 IgesEntityType::RationalBSplineSurface => 128,
970 IgesEntityType::OffsetCurve => 130,
971 IgesEntityType::OffsetSurface => 140,
972 IgesEntityType::CurveOnSurface => 142,
973 IgesEntityType::TrimmedSurface => 144,
974 IgesEntityType::Unknown(n) => *n,
975 }
976 }
977 #[allow(dead_code)]
979 pub fn is_curve(&self) -> bool {
980 matches!(
981 self,
982 IgesEntityType::CircularArc
983 | IgesEntityType::CompositeCurve
984 | IgesEntityType::ConicArc
985 | IgesEntityType::Line
986 | IgesEntityType::ParametricSpline
987 | IgesEntityType::RationalBSplineCurve
988 | IgesEntityType::OffsetCurve
989 )
990 }
991 #[allow(dead_code)]
993 pub fn is_surface(&self) -> bool {
994 matches!(
995 self,
996 IgesEntityType::Plane
997 | IgesEntityType::RuledSurface
998 | IgesEntityType::SurfaceOfRevolution
999 | IgesEntityType::TabulatedCylinder
1000 | IgesEntityType::RationalBSplineSurface
1001 | IgesEntityType::ParametricSplineSurface
1002 | IgesEntityType::OffsetSurface
1003 | IgesEntityType::TrimmedSurface
1004 )
1005 }
1006}
1007#[derive(Debug, Clone, Copy, PartialEq)]
1009pub enum LengthUnit {
1010 Meter,
1012 Millimeter,
1014 Centimeter,
1016 Inch,
1018 Foot,
1020 Micrometer,
1022}
1023impl LengthUnit {
1024 #[allow(dead_code)]
1026 pub fn to_meters_factor(&self) -> f64 {
1027 match self {
1028 LengthUnit::Meter => 1.0,
1029 LengthUnit::Millimeter => 0.001,
1030 LengthUnit::Centimeter => 0.01,
1031 LengthUnit::Inch => 0.0254,
1032 LengthUnit::Foot => 0.3048,
1033 LengthUnit::Micrometer => 1e-6,
1034 }
1035 }
1036 #[allow(dead_code)]
1038 pub fn convert(&self, value: f64, to: LengthUnit) -> f64 {
1039 value * self.to_meters_factor() / to.to_meters_factor()
1040 }
1041 #[allow(dead_code)]
1043 pub fn convert_point(&self, point: [f64; 3], to: LengthUnit) -> [f64; 3] {
1044 let factor = self.to_meters_factor() / to.to_meters_factor();
1045 [point[0] * factor, point[1] * factor, point[2] * factor]
1046 }
1047}
1048#[derive(Debug, Clone)]
1050pub struct UnitConverter {
1051 pub from: LengthUnit,
1053 pub to: LengthUnit,
1055}
1056impl UnitConverter {
1057 pub fn new(from: LengthUnit, to: LengthUnit) -> Self {
1059 Self { from, to }
1060 }
1061 #[allow(dead_code)]
1063 pub fn factor(&self) -> f64 {
1064 self.from.to_meters_factor() / self.to.to_meters_factor()
1065 }
1066 #[allow(dead_code)]
1068 pub fn convert_scalar(&self, value: f64) -> f64 {
1069 value * self.factor()
1070 }
1071 #[allow(dead_code)]
1073 pub fn convert_point(&self, point: [f64; 3]) -> [f64; 3] {
1074 let f = self.factor();
1075 [point[0] * f, point[1] * f, point[2] * f]
1076 }
1077 #[allow(dead_code)]
1079 pub fn convert_points(&self, points: &[[f64; 3]]) -> Vec<[f64; 3]> {
1080 let f = self.factor();
1081 points
1082 .iter()
1083 .map(|p| [p[0] * f, p[1] * f, p[2] * f])
1084 .collect()
1085 }
1086}
1087#[derive(Debug, Clone)]
1089pub struct StlExporter {
1090 pub solid_name: String,
1092}
1093impl StlExporter {
1094 pub fn new(solid_name: &str) -> Self {
1096 Self {
1097 solid_name: solid_name.to_string(),
1098 }
1099 }
1100 #[allow(dead_code)]
1102 pub fn to_ascii_stl(&self, mesh: &TriangleMesh) -> String {
1103 let mut output = format!("solid {}\n", self.solid_name);
1104 for (i, tri) in mesh.triangles.iter().enumerate() {
1105 let p0 = mesh.vertices[tri[0]];
1106 let p1 = mesh.vertices[tri[1]];
1107 let p2 = mesh.vertices[tri[2]];
1108 let normal = if i < mesh.normals.len() {
1109 mesh.normals[i]
1110 } else {
1111 let e1 = sub3(p1, p0);
1112 let e2 = sub3(p2, p0);
1113 normalize3(cross3(e1, e2))
1114 };
1115 output += &format!(
1116 " facet normal {:.6} {:.6} {:.6}\n",
1117 normal[0], normal[1], normal[2]
1118 );
1119 output += " outer loop\n";
1120 output += &format!(" vertex {:.6} {:.6} {:.6}\n", p0[0], p0[1], p0[2]);
1121 output += &format!(" vertex {:.6} {:.6} {:.6}\n", p1[0], p1[1], p1[2]);
1122 output += &format!(" vertex {:.6} {:.6} {:.6}\n", p2[0], p2[1], p2[2]);
1123 output += " endloop\n";
1124 output += " endfacet\n";
1125 }
1126 output += &format!("endsolid {}\n", self.solid_name);
1127 output
1128 }
1129 #[allow(dead_code)]
1131 pub fn to_binary_stl(&self, mesh: &TriangleMesh) -> Vec<u8> {
1132 let mut data = Vec::new();
1133 let mut header = [0u8; 80];
1134 let name_bytes = self.solid_name.as_bytes();
1135 let copy_len = name_bytes.len().min(80);
1136 header[..copy_len].copy_from_slice(&name_bytes[..copy_len]);
1137 data.extend_from_slice(&header);
1138 let n_tri = mesh.triangles.len() as u32;
1139 data.extend_from_slice(&n_tri.to_le_bytes());
1140 for (i, tri) in mesh.triangles.iter().enumerate() {
1141 let p0 = mesh.vertices[tri[0]];
1142 let p1 = mesh.vertices[tri[1]];
1143 let p2 = mesh.vertices[tri[2]];
1144 let normal = if i < mesh.normals.len() {
1145 mesh.normals[i]
1146 } else {
1147 let e1 = sub3(p1, p0);
1148 let e2 = sub3(p2, p0);
1149 normalize3(cross3(e1, e2))
1150 };
1151 for &c in &normal {
1152 data.extend_from_slice(&(c as f32).to_le_bytes());
1153 }
1154 for p in &[p0, p1, p2] {
1155 for &c in p {
1156 data.extend_from_slice(&(c as f32).to_le_bytes());
1157 }
1158 }
1159 data.extend_from_slice(&0u16.to_le_bytes());
1160 }
1161 data
1162 }
1163 #[allow(dead_code)]
1165 pub fn export_brep_ascii(&self, solid: &BrepSolid) -> String {
1166 let mesh = tessellate_brep(solid, 10);
1167 self.to_ascii_stl(&mesh)
1168 }
1169}
1170#[derive(Debug, Clone)]
1172pub struct StepEntity {
1173 pub id: usize,
1175 pub entity_type: String,
1177 pub parameters: String,
1179}
1180impl StepEntity {
1181 pub fn new(id: usize, entity_type: &str, parameters: &str) -> Self {
1183 Self {
1184 id,
1185 entity_type: entity_type.to_string(),
1186 parameters: parameters.to_string(),
1187 }
1188 }
1189}
1190#[derive(Debug, Clone)]
1192pub struct IgesEntity {
1193 pub sequence_number: usize,
1195 pub entity_type: IgesEntityType,
1197 pub parameter_pointer: usize,
1199 pub status: u32,
1201 pub label: String,
1203 pub parameters: Vec<f64>,
1205}
1206impl IgesEntity {
1207 pub fn new(seq: usize, etype: IgesEntityType) -> Self {
1209 Self {
1210 sequence_number: seq,
1211 entity_type: etype,
1212 parameter_pointer: 0,
1213 status: 0,
1214 label: String::new(),
1215 parameters: Vec::new(),
1216 }
1217 }
1218}
1219#[derive(Debug, Clone)]
1221pub struct AssemblyTransform {
1222 pub rotation: [[f64; 3]; 3],
1224 pub translation: [f64; 3],
1226}
1227impl AssemblyTransform {
1228 pub fn identity() -> Self {
1230 Self {
1231 rotation: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
1232 translation: [0.0; 3],
1233 }
1234 }
1235 pub fn translation(t: [f64; 3]) -> Self {
1237 Self {
1238 rotation: [[1.0, 0.0, 0.0], [0.0, 1.0, 0.0], [0.0, 0.0, 1.0]],
1239 translation: t,
1240 }
1241 }
1242 #[allow(dead_code)]
1244 pub fn apply(&self, p: [f64; 3]) -> [f64; 3] {
1245 let rotated = [
1246 self.rotation[0][0] * p[0] + self.rotation[0][1] * p[1] + self.rotation[0][2] * p[2],
1247 self.rotation[1][0] * p[0] + self.rotation[1][1] * p[1] + self.rotation[1][2] * p[2],
1248 self.rotation[2][0] * p[0] + self.rotation[2][1] * p[1] + self.rotation[2][2] * p[2],
1249 ];
1250 add3(rotated, self.translation)
1251 }
1252 #[allow(dead_code)]
1254 pub fn compose(&self, other: &AssemblyTransform) -> AssemblyTransform {
1255 let mut new_rot = [[0.0; 3]; 3];
1256 for i in 0..3 {
1257 for j in 0..3 {
1258 for k in 0..3 {
1259 new_rot[i][j] += self.rotation[i][k] * other.rotation[k][j];
1260 }
1261 }
1262 }
1263 let new_trans = self.apply(other.translation);
1264 AssemblyTransform {
1265 rotation: new_rot,
1266 translation: new_trans,
1267 }
1268 }
1269 #[allow(dead_code)]
1271 pub fn inverse(&self) -> AssemblyTransform {
1272 let r_inv = [
1273 [
1274 self.rotation[0][0],
1275 self.rotation[1][0],
1276 self.rotation[2][0],
1277 ],
1278 [
1279 self.rotation[0][1],
1280 self.rotation[1][1],
1281 self.rotation[2][1],
1282 ],
1283 [
1284 self.rotation[0][2],
1285 self.rotation[1][2],
1286 self.rotation[2][2],
1287 ],
1288 ];
1289 let t_inv = [
1290 -(r_inv[0][0] * self.translation[0]
1291 + r_inv[0][1] * self.translation[1]
1292 + r_inv[0][2] * self.translation[2]),
1293 -(r_inv[1][0] * self.translation[0]
1294 + r_inv[1][1] * self.translation[1]
1295 + r_inv[1][2] * self.translation[2]),
1296 -(r_inv[2][0] * self.translation[0]
1297 + r_inv[2][1] * self.translation[1]
1298 + r_inv[2][2] * self.translation[2]),
1299 ];
1300 AssemblyTransform {
1301 rotation: r_inv,
1302 translation: t_inv,
1303 }
1304 }
1305}
1306#[derive(Debug, Clone)]
1308pub struct TriangleMesh {
1309 pub vertices: Vec<[f64; 3]>,
1311 pub triangles: Vec<[usize; 3]>,
1313 pub normals: Vec<[f64; 3]>,
1315}
1316impl TriangleMesh {
1317 pub fn new() -> Self {
1319 Self {
1320 vertices: Vec::new(),
1321 triangles: Vec::new(),
1322 normals: Vec::new(),
1323 }
1324 }
1325 #[allow(dead_code)]
1327 pub fn add_vertex(&mut self, position: [f64; 3]) -> usize {
1328 let idx = self.vertices.len();
1329 self.vertices.push(position);
1330 idx
1331 }
1332 #[allow(dead_code)]
1334 pub fn add_triangle(&mut self, v0: usize, v1: usize, v2: usize) {
1335 self.triangles.push([v0, v1, v2]);
1336 }
1337 #[allow(dead_code)]
1339 pub fn compute_normals(&mut self) {
1340 self.normals.clear();
1341 for tri in &self.triangles {
1342 let p0 = self.vertices[tri[0]];
1343 let p1 = self.vertices[tri[1]];
1344 let p2 = self.vertices[tri[2]];
1345 let e1 = sub3(p1, p0);
1346 let e2 = sub3(p2, p0);
1347 let n = normalize3(cross3(e1, e2));
1348 self.normals.push(n);
1349 }
1350 }
1351 #[allow(dead_code)]
1353 pub fn bounding_box(&self) -> BoundingBox {
1354 BoundingBox::from_points(&self.vertices)
1355 }
1356 #[allow(dead_code)]
1358 pub fn surface_area(&self) -> f64 {
1359 let mut area = 0.0;
1360 for tri in &self.triangles {
1361 let p0 = self.vertices[tri[0]];
1362 let p1 = self.vertices[tri[1]];
1363 let p2 = self.vertices[tri[2]];
1364 let e1 = sub3(p1, p0);
1365 let e2 = sub3(p2, p0);
1366 area += len3(cross3(e1, e2)) * 0.5;
1367 }
1368 area
1369 }
1370 #[allow(dead_code)]
1372 pub fn vertex_count(&self) -> usize {
1373 self.vertices.len()
1374 }
1375 #[allow(dead_code)]
1377 pub fn triangle_count(&self) -> usize {
1378 self.triangles.len()
1379 }
1380 #[allow(dead_code)]
1382 pub fn merge(&mut self, other: &TriangleMesh) {
1383 let offset = self.vertices.len();
1384 self.vertices.extend_from_slice(&other.vertices);
1385 for tri in &other.triangles {
1386 self.triangles
1387 .push([tri[0] + offset, tri[1] + offset, tri[2] + offset]);
1388 }
1389 self.normals.extend_from_slice(&other.normals);
1390 }
1391}
1392#[derive(Debug, Clone, Copy, PartialEq)]
1394pub enum CurveType {
1395 Line,
1397 CircularArc,
1399 BSpline,
1401 Nurbs,
1403}