1use crate::{
25 dcos, dim2, dsin, polyhedron, triangulate2d, triangulate2d_rev, triangulate3d,
26 triangulate3d_rev, Faces, Indices, Mt4, Pt2s, Pt3, Pt3s, Scad, ScadOp,
27};
28
29#[derive(Clone)]
35pub struct Polyhedron {
36 pub points: Pt3s,
37 pub faces: Faces,
38}
39
40impl Polyhedron {
41 pub fn into_scad(self) -> Scad {
43 polyhedron!(self.points, self.faces)
44 }
45
46 pub fn into_scad_with_convexity(self, convexity: u64) -> Scad {
48 polyhedron!(self.points, self.faces, convexity)
49 }
50
51 pub fn translate(&mut self, point: Pt3) {
53 self.points.translate(point);
54 }
55
56 pub fn apply_matrix(&mut self, matrix: &Mt4) {
58 self.points.apply_matrix(matrix);
59 }
60
61 pub fn rotate_x(&mut self, degrees: f64) -> &mut Self {
63 self.points.rotate_x(degrees);
64 self
65 }
66
67 pub fn rotate_y(&mut self, degrees: f64) -> &mut Self {
69 self.points.rotate_y(degrees);
70 self
71 }
72
73 pub fn rotate_z(&mut self, degrees: f64) -> &mut Self {
75 self.points.rotate_z(degrees);
76 self
77 }
78
79 pub fn linear_extrude(points: &Pt2s, height: f64) -> Polyhedron {
83 let indices = triangulate2d_rev(points);
84 let mut vertices = Pt3s::with_capacity(points.len() * 2);
85 for point in points.iter() {
86 vertices.push(point.as_pt3(0.0));
87 }
88
89 let mut faces = Faces::with_capacity((points.len() - 2) * 2 + points.len());
90 for i in (0..indices.len()).step_by(3) {
91 faces.push(Indices::from_indices(vec![
92 indices[i],
93 indices[i + 1],
94 indices[i + 2],
95 ]));
96 }
97
98 let mut end_points = points.iter().map(|p| p.as_pt3(height)).collect();
99 vertices.append(&mut end_points);
100 let indices = triangulate2d(points);
101 for i in (0..indices.len()).step_by(3) {
102 faces.push(Indices::from_indices(vec![
103 indices[i] + points.len() as u64,
104 indices[i + 1] + points.len() as u64,
105 indices[i + 2] + points.len() as u64,
106 ]));
107 }
108
109 for i in 0..points.len() {
110 let p0 = i;
111 let p1 = (i + 1) % points.len();
112 let p2 = (i + 1) % points.len() + points.len();
113 let p3 = i + points.len();
114
115 faces.push(Indices::from_indices(vec![
116 p0 as u64, p1 as u64, p2 as u64, p3 as u64,
117 ]));
118 }
119
120 Polyhedron {
121 points: vertices,
122 faces,
123 }
124 }
125
126 pub fn rotate_extrude(profile: &Pt2s, degrees: f64, segments: usize) -> Self {
130 assert!((0.0..360.0).contains(°rees));
131 assert!(segments >= 3);
132 let not_closed = degrees != 360.0;
133 let profile: Pt3s =
134 Pt3s::from_pt3s(profile.iter().map(|p| Pt3::new(p.x, 0.0, p.y)).collect());
135 let profile_len = profile.len();
136 let a = degrees / segments as f64;
137 let mut points = profile.clone();
138 let mut faces = Faces::new();
139
140 if not_closed {
141 let triangles = triangulate3d(&profile, Pt3::new(0.0, -1.0, 0.0));
143 for i in (0..triangles.len()).step_by(3) {
144 faces.push(Indices::from_indices(vec![
145 triangles[i] as u64,
146 triangles[i + 1] as u64,
147 triangles[i + 2] as u64,
148 ]));
149 }
150 }
151
152 for segment in 1..segments {
153 let s = dsin(a * segment as f64);
154 let c = dcos(a * segment as f64);
155 for p in 0..profile_len {
156 points.push(Pt3::new(profile[p].x * c, profile[p].x * s, profile[p].z));
157 let p0 = (segment - 1) * profile_len + p;
158 let p1 = (segment - 1) * profile_len + ((p + 1) % profile_len);
159 let p2 = segment * profile_len + ((p + 1) % profile_len);
160 let p3 = segment * profile_len + p;
161 faces.push(Indices::from_indices(vec![
162 p0 as u64, p1 as u64, p2 as u64, p3 as u64,
163 ]));
164 }
165 }
166
167 if not_closed {
168 let s = dsin(a * segments as f64);
169 let c = dcos(a * segments as f64);
170 for p in 0..profile_len {
171 points.push(Pt3::new(profile[p].x * c, profile[p].x * s, profile[p].z));
172 let p0 = (segments - 1) * profile_len + p;
173 let p1 = (segments - 1) * profile_len + ((p + 1) % profile_len);
174 let p2 = segments * profile_len + ((p + 1) % profile_len);
175 let p3 = segments * profile_len + p;
176 faces.push(Indices::from_indices(vec![
177 p0 as u64, p1 as u64, p2 as u64, p3 as u64,
178 ]));
179 }
180 let nml = Pt3::new(0.0, -1.0, 0.0).rotated_z(degrees + 180.0);
181 let triangles = triangulate3d_rev(&profile, nml);
182 for i in (0..triangles.len()).step_by(3) {
183 faces.push(Indices::from_indices(vec![
184 triangles[i] as u64 + (segments * profile_len) as u64,
185 triangles[i + 1] as u64 + (segments * profile_len) as u64,
186 triangles[i + 2] as u64 + (segments * profile_len) as u64,
187 ]));
188 }
189 } else {
190 for p in 0..profile_len {
191 let p0 = (segments - 1) * profile_len + p;
192 let p1 = (segments - 1) * profile_len + ((p + 1) % profile_len);
193 let p2 = (p + 1) % profile_len;
194 let p3 = p;
195 faces.push(Indices::from_indices(vec![
196 p0 as u64, p1 as u64, p2 as u64, p3 as u64,
197 ]));
198 }
199 }
200 Polyhedron { points, faces }
201 }
202
203 pub fn loft(lower_profile: &Pt2s, upper_profile: &Pt2s, height: f64) -> Self {
207 if lower_profile.len() != upper_profile.len() {
208 panic!(
209 "lower and upper profile lengths differ, lower len = {} and upper len = {}",
210 lower_profile.len(),
211 upper_profile.len()
212 );
213 }
214 let n_pts = lower_profile.len();
215 let mut points = Pt3s::with_capacity(n_pts * 2);
216 for pt in lower_profile.iter() {
217 points.push(pt.as_pt3(0.0));
218 }
219 for pt in upper_profile.iter() {
220 points.push(pt.as_pt3(height));
221 }
222
223 let mut faces = Faces::with_capacity((n_pts - 2) * 2 + n_pts);
224 let indices = triangulate2d_rev(lower_profile);
225 for i in (0..indices.len()).step_by(3) {
226 faces.push(Indices::from_indices(vec![
227 indices[i],
228 indices[i + 1],
229 indices[i + 2],
230 ]));
231 }
232
233 let indices = triangulate2d(upper_profile);
234 for i in (0..indices.len()).step_by(3) {
235 faces.push(Indices::from_indices(vec![
236 indices[i] + n_pts as u64,
237 indices[i + 1] + n_pts as u64,
238 indices[i + 2] + n_pts as u64,
239 ]));
240 }
241
242 for i in 0..n_pts {
243 faces.push(Indices::from_indices(vec![
244 i as u64,
245 ((i + 1) % n_pts) as u64,
246 ((i + 1) % n_pts + n_pts) as u64,
247 (i + n_pts) as u64,
248 ]));
249 }
250
251 Polyhedron { points, faces }
252 }
253
254 pub fn sweep(profile: &Pt2s, path: &Pt3s, twist_degrees: f64, closed: bool) -> Self {
258 let profile = Pt3s::from_pt3s(profile.iter().map(|p| p.as_pt3(0.0)).collect());
259 let profile_len = profile.len();
260 let path_len = path.len();
261 let mut points = Pt3s::new();
262 let mut faces = Faces::new();
263 let twist_angle = if closed {
264 twist_degrees / path.len() as f64
265 } else {
266 twist_degrees / (path.len() - 1) as f64
267 };
268
269 let m = if closed {
270 Mt4::look_at_matrix_lh(path[path.len() - 1], path[1], Pt3::new(0.0, 0.0, 1.0))
271 } else {
272 Mt4::look_at_matrix_lh(path[0], path[1], Pt3::new(0.0, 0.0, 1.0))
273 };
274 for p in profile.iter() {
275 points.push((m * p.as_pt4(1.0)).as_pt3() + path[0]);
276 }
277 if !closed {
278 let indices = triangulate3d_rev(&profile, path[1] - path[0]);
279 for i in (0..indices.len()).step_by(3) {
280 faces.push(Indices::from_indices(vec![
281 indices[i],
282 indices[i + 1],
283 indices[i + 2],
284 ]));
285 }
286 }
287
288 for path_index in 1..path_len - 1 {
289 let m = Mt4::look_at_matrix_lh(
290 path[path_index - 1],
291 path[path_index + 1],
292 Pt3::new(0.0, 0.0, 1.0),
293 );
294 for profile_index in 0..profile_len {
295 let point = profile[profile_index].rotated_z(twist_angle * path_index as f64);
296 points.push((m * point.as_pt4(0.0)).as_pt3() + path[path_index]);
297 let p0 = (path_index - 1) * profile_len + profile_index;
298 let p1 = (path_index - 1) * profile_len + ((profile_index + 1) % profile_len);
299 let p2 = path_index * profile_len + ((profile_index + 1) % profile_len);
300 let p3 = path_index * profile_len + profile_index;
301 faces.push(Indices::from_indices(vec![
302 p0 as u64, p1 as u64, p2 as u64, p3 as u64,
303 ]));
304 }
305 }
306
307 let m = if closed {
308 Mt4::look_at_matrix_lh(path[path_len - 2], path[0], Pt3::new(0.0, 0.0, 1.0))
309 } else {
310 Mt4::look_at_matrix_lh(
311 path[path_len - 2],
312 path[path_len - 1],
313 Pt3::new(0.0, 0.0, 1.0),
314 )
315 };
316 let mut last_points = Pt3s::with_capacity(profile_len);
317 for profile_index in 0..profile_len {
318 let point = profile[profile_index].rotated_z(twist_angle * (path_len - 1) as f64);
319 let p = (m * point.as_pt4(0.0)).as_pt3() + path[path_len - 1];
320 points.push(p);
321 last_points.push(p);
322 let p0 = (path_len - 2) * profile_len + profile_index;
323 let p1 = (path_len - 2) * profile_len + ((profile_index + 1) % profile_len);
324 let p2 = (path_len - 1) * profile_len + ((profile_index + 1) % profile_len);
325 let p3 = (path_len - 1) * profile_len + profile_index;
326 faces.push(Indices::from_indices(vec![
327 p0 as u64, p1 as u64, p2 as u64, p3 as u64,
328 ]));
329 }
330
331 if !closed {
332 let indices = triangulate3d(&last_points, path[path_len - 1] - path[path_len - 2]);
333 for i in (0..indices.len()).step_by(3) {
334 faces.push(Indices::from_indices(vec![
335 indices[i] + points.len() as u64 - profile_len as u64,
336 indices[i + 1] + points.len() as u64 - profile_len as u64,
337 indices[i + 2] + points.len() as u64 - profile_len as u64,
338 ]));
339 }
340 } else {
341 for profile_index in 0..profile_len {
342 let p0 = (path_len - 1) * profile_len + profile_index;
343 let p1 = (path_len - 1) * profile_len + ((profile_index + 1) % profile_len);
344 let p2 = (profile_index + 1) % profile_len;
345 let p3 = profile_index;
346 faces.push(Indices::from_indices(vec![
347 p0 as u64, p1 as u64, p2 as u64, p3 as u64,
348 ]));
349 }
350 }
351
352 Self { points, faces }
353 }
354
355 pub fn cylinder(radius: f64, height: f64, segments: u64) -> Self {
357 Self::linear_extrude(&dim2::circle(radius, segments), height)
358 }
359}
360
361pub fn quadratic_bezier(start: Pt3, control: Pt3, end: Pt3, segments: u64) -> Pt3s {
365 let delta = 1.0 / segments as f64;
366 let mut points = Pt3s::new();
367 for i in 0..(segments + 1) {
368 let t = i as f64 * delta;
369 points.push(start * (1.0 - t) * (1.0 - t) + control * t * (1.0 - t) * 2.0 + end * t * t);
370 }
371 points
372}
373
374pub fn cubic_bezier(start: Pt3, control1: Pt3, control2: Pt3, end: Pt3, segments: u64) -> Pt3s {
378 let delta = 1.0 / segments as f64;
379 let mut points = Pt3s::new();
380 for i in 0..(segments + 1) {
381 let t = i as f64 * delta;
382 points.push(
383 start * (1.0 - t) * (1.0 - t) * (1.0 - t)
384 + control1 * t * (1.0 - t) * (1.0 - t) * 3.0
385 + control2 * t * t * (1.0 - t) * 3.0
386 + end * t * t * t,
387 );
388 }
389 points
390}
391
392#[derive(Clone, Copy)]
394pub struct QuadraticBezier3D {
395 pub start: Pt3,
396 pub control: Pt3,
397 pub end: Pt3,
398 pub segments: u64,
399}
400
401impl QuadraticBezier3D {
402 pub fn new(start: Pt3, control: Pt3, end: Pt3, segments: u64) -> Self {
404 Self {
405 start,
406 control,
407 end,
408 segments,
409 }
410 }
411
412 pub fn gen_points(&self) -> Pt3s {
414 quadratic_bezier(self.start, self.control, self.end, self.segments)
415 }
416}
417
418#[derive(Clone, Copy)]
420pub struct CubicBezier3D {
421 pub start: Pt3,
422 pub control1: Pt3,
423 pub control2: Pt3,
424 pub end: Pt3,
425 pub segments: u64,
426}
427
428impl CubicBezier3D {
429 pub fn new(start: Pt3, control1: Pt3, control2: Pt3, end: Pt3, segments: u64) -> Self {
431 Self {
432 start,
433 control1,
434 control2,
435 end,
436 segments,
437 }
438 }
439
440 pub fn gen_points(&self) -> Pt3s {
442 cubic_bezier(
443 self.start,
444 self.control1,
445 self.control2,
446 self.end,
447 self.segments,
448 )
449 }
450}
451
452#[derive(Clone)]
454pub struct CubicBezierChain3D {
455 pub curves: Vec<CubicBezier3D>,
456 closed: bool,
457}
458
459impl CubicBezierChain3D {
460 pub fn new(start: Pt3, control1: Pt3, control2: Pt3, end: Pt3, segments: u64) -> Self {
462 Self {
463 curves: vec![CubicBezier3D {
464 start,
465 control1,
466 control2,
467 end,
468 segments,
469 }],
470 closed: false,
471 }
472 }
473
474 pub fn add(
476 &mut self,
477 control1_length: f64,
478 control2: Pt3,
479 end: Pt3,
480 segments: u64,
481 ) -> &mut Self {
482 let chain_end = &self.curves[self.curves.len() - 1];
483 self.curves.push(CubicBezier3D {
484 start: chain_end.end,
485 control1: chain_end.end
486 + (chain_end.end - chain_end.control2).normalized() * control1_length,
487 control2,
488 end,
489 segments,
490 });
491 self
492 }
493
494 pub fn close(
496 &mut self,
497 control1_length: f64,
498 control2: Pt3,
499 start_control1_len: f64,
500 segments: u64,
501 ) {
502 self.closed = true;
503 self.add(control1_length, control2, self.curves[0].start, segments);
504 let chain_end = &self.curves[self.curves.len() - 1];
505 self.curves[0].control1 =
506 chain_end.end + (chain_end.end - chain_end.control2).normalized() * start_control1_len;
507 }
508
509 pub fn gen_points(&self) -> Pt3s {
511 let mut pts = Pt3s::from_pt3s(vec![Pt3::new(0.0, 0.0, 0.0)]);
512 for i in 0..self.curves.len() {
513 pts.pop();
514 pts.append(&mut cubic_bezier(
515 self.curves[i].start,
516 self.curves[i].control1,
517 self.curves[i].control2,
518 self.curves[i].end,
519 self.curves[i].segments,
520 ));
521 }
522 if self.closed {
523 pts.pop();
524 }
525 pts
526 }
527}