microcad_core/geo3d/
extrude.rs1use std::f64::consts::PI;
7
8use cgmath::{Matrix, Point3, SquareMatrix, Transform, Vector3};
9
10use geo::TriangulateEarcut;
11
12use crate::*;
13
14pub trait Extrude {
16 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh;
18
19 fn cap(&self, _m: &Mat4, _bottom: bool) -> TriangleMesh {
21 TriangleMesh::default()
22 }
23
24 fn linear_extrude(&self, height: Scalar) -> WithBounds3D<TriangleMesh> {
26 let m_a = Mat4::identity();
27 let m_b = Mat4::from_translation(Vec3::new(0.0, 0.0, height));
28 let mut mesh = self.extrude_slice(&m_a, &m_b);
29 mesh.append(&self.cap(&m_a, true));
30 mesh.append(&self.cap(&m_b, false));
31 let bounds = mesh.calc_bounds_3d();
32 mesh.repair(&bounds);
33 WithBounds3D::new(mesh, bounds)
34 }
35
36 fn revolve_extrude(&self, angle_rad: Angle, segments: usize) -> WithBounds3D<TriangleMesh> {
38 let mut mesh = TriangleMesh::default();
39 if segments < 2 {
40 return WithBounds3D::default();
41 }
42
43 let delta = angle_rad / segments as Scalar;
44
45 let transforms: Vec<_> = (0..=segments)
47 .map(|i| {
48 let a = delta * i as Scalar;
49 let mut mat = Mat4::from_angle_y(a);
50 mat.swap_rows(2, 1); mat
52 })
53 .collect();
54
55 for i in 0..segments {
57 let m_a = &transforms[i];
58 let m_b = &transforms[i + 1];
59 let slice = self.extrude_slice(m_a, m_b);
60 mesh.append(&slice);
61 }
62
63 if angle_rad.0 < PI * 2.0 {
65 let m_start = &transforms[0];
66 let m_end = transforms.last().expect("Transform");
67 mesh.append(&self.cap(m_start, true));
68 mesh.append(&self.cap(m_end, false));
69 }
70
71 let bounds = mesh.calc_bounds_3d();
72 mesh.repair(&bounds);
73 WithBounds3D::new(mesh, bounds)
74 }
75}
76
77impl Extrude for LineString {
78 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
79 let mut mesh = TriangleMesh::default();
80
81 let points = self.points();
82 let len = points.len();
83 if len < 2 {
84 return mesh; }
86 mesh.positions.reserve(len * 2); mesh.triangle_indices.reserve(len * 2); let m_a: cgmath::Matrix4<f32> = m_a.cast().expect("Successful cast");
91 let m_b: cgmath::Matrix4<f32> = m_b.cast().expect("Successful cast");
92
93 let transform_point =
94 |p: &cgmath::Point3<f32>, m: &cgmath::Matrix4<f32>| -> cgmath::Vector3<f32> {
95 m.transform_point(*p).to_homogeneous().truncate()
96 };
97
98 for point in points {
100 let point = cgmath::Point3::new(point.x() as f32, point.y() as f32, 0.0_f32);
101 mesh.positions.push(transform_point(&point, &m_a)); mesh.positions.push(transform_point(&point, &m_b)); }
104
105 let range = if self.is_closed() {
106 0..len
107 } else {
108 0..(len - 1)
109 };
110
111 for i in range {
112 let next = (i + 1) % len;
113
114 let bl = (i * 2) as u32;
115 let br = (next * 2) as u32;
116 let tl = bl + 1;
117 let tr = br + 1;
118 mesh.triangle_indices.push(Triangle(bl, br, tr));
119 mesh.triangle_indices.push(Triangle(bl, tr, tl));
120 }
121
122 mesh
123 }
124}
125
126impl Extrude for Polygon {
127 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
128 let mut mesh = TriangleMesh::default();
129 mesh.append(&self.exterior().extrude_slice(m_a, m_b));
130 for interior in self.interiors() {
131 mesh.append(&interior.extrude_slice(m_a, m_b));
132 }
133 mesh
134 }
135
136 fn cap(&self, m: &Mat4, flip: bool) -> TriangleMesh {
137 let raw_triangulation = self.earcut_triangles_raw();
138 let m: cgmath::Matrix4<f32> = m.cast().expect("Successful cast");
139
140 TriangleMesh {
141 positions: raw_triangulation
142 .vertices
143 .as_slice()
144 .chunks_exact(2)
145 .map(|chunk| {
146 let p = Point3::new(chunk[0] as f32, chunk[1] as f32, 0.0_f32);
147 let p = m.transform_point(p);
148 Vector3::<f32>::new(p.x, p.y, p.z)
149 })
150 .collect(),
151 normals: None,
152 triangle_indices: raw_triangulation
153 .triangle_indices
154 .as_slice()
155 .chunks_exact(3)
156 .map(|chunk| match flip {
157 true => Triangle(chunk[2] as u32, chunk[1] as u32, chunk[0] as u32),
158 false => Triangle(chunk[0] as u32, chunk[1] as u32, chunk[2] as u32),
159 })
160 .collect(),
161 }
162 }
163}
164
165impl Extrude for MultiPolygon {
166 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
167 let mut mesh = TriangleMesh::default();
168 self.iter().for_each(|polygon| {
169 mesh.append(&polygon.extrude_slice(m_a, m_b));
170 });
171 mesh
172 }
173
174 fn cap(&self, m: &Mat4, flip: bool) -> TriangleMesh {
175 let mut mesh = TriangleMesh::default();
176 self.iter().for_each(|polygon| {
177 mesh.append(&polygon.cap(m, flip));
178 });
179 mesh
180 }
181}
182
183impl Extrude for Geometries2D {
184 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
185 self.to_multi_polygon().extrude_slice(m_a, m_b)
186 }
187
188 fn cap(&self, m: &Mat4, flip: bool) -> TriangleMesh {
189 self.to_multi_polygon().cap(m, flip)
190 }
191}