1use std::f64::consts::PI;
7
8use cgmath::{InnerSpace, Matrix, Point3, SquareMatrix, Transform};
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, _normal: &Vec3, _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, &-m_a.z.truncate(), true));
30 mesh.append(&self.cap(&m_b, &m_b.z.truncate(), 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 let normal_start = m_start.x.truncate(); let normal_end = -m_end.x.truncate(); mesh.append(&self.cap(m_start, &normal_start, true));
71 mesh.append(&self.cap(m_end, &normal_end, false));
72 }
73
74 let bounds = mesh.calc_bounds_3d();
75 mesh.repair(&bounds);
76 WithBounds3D::new(mesh, bounds)
77 }
78}
79
80impl Extrude for LineString {
81 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
82 let mut mesh = TriangleMesh::default();
83
84 let points = self.points();
85 let len = points.len();
86 if len < 2 {
87 return mesh; }
89
90 let transform_point = |p: &Point, m: &Mat4| -> Vec3 {
91 let local = Point3::new(p.x(), p.y(), 0.0);
92 m.transform_point(local).to_homogeneous().truncate()
93 };
94
95 let mut bottom_indices = Vec::with_capacity(len);
96 let mut top_indices = Vec::with_capacity(len);
97
98 for point in points {
100 let bottom_pos = transform_point(&point, m_a);
101 let top_pos = transform_point(&point, m_b);
102
103 let bottom_index = mesh.vertices.len() as u32;
104 mesh.vertices.push(Vertex {
105 pos: bottom_pos,
106 normal: Vec3::new(0.0, 0.0, 0.0),
107 });
108
109 let top_index = mesh.vertices.len() as u32;
110 mesh.vertices.push(Vertex {
111 pos: top_pos,
112 normal: Vec3::new(0.0, 0.0, 0.0),
113 });
114
115 bottom_indices.push(bottom_index);
116 top_indices.push(top_index);
117 }
118
119 let range = if self.is_closed() {
120 0..len
121 } else {
122 0..(len - 1)
123 };
124
125 for i in range {
126 let next = (i + 1) % len;
127
128 let bl = bottom_indices[i];
129 let br = bottom_indices[next];
130 let tl = top_indices[i];
131 let tr = top_indices[next];
132
133 mesh.triangle_indices.push(Triangle(bl, br, tr));
135 Vertex::accumulate_normal(&mut mesh.vertices, bl, br, tr);
136
137 mesh.triangle_indices.push(Triangle(bl, tr, tl));
139 Vertex::accumulate_normal(&mut mesh.vertices, bl, tr, tl);
140 }
141
142 mesh.vertices
144 .iter_mut()
145 .for_each(|v| v.normal = v.normal.normalize());
146
147 mesh
148 }
149}
150
151impl Extrude for Polygon {
152 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
153 let mut mesh = TriangleMesh::default();
154 mesh.append(&self.exterior().extrude_slice(m_a, m_b));
155 for interior in self.interiors() {
156 mesh.append(&interior.extrude_slice(m_a, m_b));
157 }
158 mesh
159 }
160
161 fn cap(&self, m: &Mat4, normal: &Vec3, flip: bool) -> TriangleMesh {
162 let raw_triangulation = self.earcut_triangles_raw();
163
164 TriangleMesh {
165 vertices: raw_triangulation
166 .vertices
167 .as_slice()
168 .chunks_exact(2)
169 .map(|chunk| {
170 let p = Point3::new(chunk[0], chunk[1], 0.0);
171 let p = m.transform_point(p);
172 Vertex {
173 pos: Vec3::new(p.x, p.y, p.z),
174 normal: *normal,
175 }
176 })
177 .collect(),
178 triangle_indices: raw_triangulation
179 .triangle_indices
180 .as_slice()
181 .chunks_exact(3)
182 .map(|chunk| match flip {
183 true => Triangle(chunk[2] as u32, chunk[1] as u32, chunk[0] as u32),
184 false => Triangle(chunk[0] as u32, chunk[1] as u32, chunk[2] as u32),
185 })
186 .collect(),
187 }
188 }
189}
190
191impl Extrude for MultiPolygon {
192 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
193 let mut mesh = TriangleMesh::default();
194 self.iter().for_each(|polygon| {
195 mesh.append(&polygon.extrude_slice(m_a, m_b));
196 });
197 mesh
198 }
199
200 fn cap(&self, m: &Mat4, normal: &Vec3, flip: bool) -> TriangleMesh {
201 let mut mesh = TriangleMesh::default();
202 self.iter().for_each(|polygon| {
203 mesh.append(&polygon.cap(m, normal, flip));
204 });
205 mesh
206 }
207}
208
209impl Extrude for Geometries2D {
210 fn extrude_slice(&self, m_a: &Mat4, m_b: &Mat4) -> TriangleMesh {
211 self.to_multi_polygon().extrude_slice(m_a, m_b)
212 }
213
214 fn cap(&self, m: &Mat4, normal: &Vec3, flip: bool) -> TriangleMesh {
215 self.to_multi_polygon().cap(m, normal, flip)
216 }
217}