1use nalgebra::{Point, Point3, Vector3};
2use slotmap::SlotMap;
3
4use crate::metadata::*;
5use crate::*;
6
7#[derive(Clone, Debug, Default)]
9pub struct FaceKeyRemap(pub std::collections::HashMap<FaceKey, FaceKey>);
10
11#[derive(Clone, Debug)]
29pub struct CsgFactory<T: Number> {
30 pub primitives: SlotMap<PrimitiveKey, PrimitiveMeta>,
31 pub faces: SlotMap<FaceKey, FaceMeta<T>>,
32 pub edges: SlotMap<EdgeKey, EdgeMeta<T>>,
33}
34
35impl<T: Number> Default for CsgFactory<T> {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl<T: Number> CsgFactory<T> {
42 pub fn new() -> Self {
43 Self {
44 primitives: SlotMap::with_key(),
45 faces: SlotMap::with_key(),
46 edges: SlotMap::with_key(),
47 }
48 }
49
50 pub fn absorb(&mut self, other: &Self) -> FaceKeyRemap {
56 let mut prim_remap: std::collections::HashMap<PrimitiveKey, PrimitiveKey> =
57 std::collections::HashMap::with_capacity(other.primitives.len());
58 let mut face_remap =
59 FaceKeyRemap(std::collections::HashMap::with_capacity(other.faces.len()));
60 let mut edge_remap: std::collections::HashMap<EdgeKey, EdgeKey> =
61 std::collections::HashMap::with_capacity(other.edges.len());
62
63 for (old_pk, meta) in &other.primitives {
65 let new_pk = self.primitives.insert(meta.clone());
66 prim_remap.insert(old_pk, new_pk);
67 }
68
69 for (old_fk, meta) in &other.faces {
71 let mut meta = meta.clone();
72 if let Some(&new_pk) = prim_remap.get(&meta.primitive_key) {
73 meta.primitive_key = new_pk;
74 }
75 let new_fk = self.faces.insert(meta);
76 face_remap.0.insert(old_fk, new_fk);
77 }
78
79 for (old_ek, meta) in &other.edges {
81 let mut meta = meta.clone();
82 if let Some(&new_pk) = prim_remap.get(&meta.primitive_key) {
83 meta.primitive_key = new_pk;
84 }
85 meta.face_keys = meta
86 .face_keys
87 .map(|fk| face_remap.0.get(&fk).copied().unwrap_or(fk));
88 let new_ek = self.edges.insert(meta);
89 edge_remap.insert(old_ek, new_ek);
90 }
91
92 for new_pk in prim_remap.values() {
94 if let Some(pm) = self.primitives.get_mut(*new_pk) {
95 pm.face_keys = pm
96 .face_keys
97 .iter()
98 .map(|fk| face_remap.0.get(fk).copied().unwrap_or(*fk))
99 .collect();
100 pm.edge_keys = pm
101 .edge_keys
102 .iter()
103 .map(|ek| edge_remap.get(ek).copied().unwrap_or(*ek))
104 .collect();
105 }
106 }
107
108 face_remap
109 }
110
111 pub fn cast<T2: Number>(self) -> (CsgFactory<T2>, FaceKeyRemap)
115 where
116 T: num_traits::cast::AsPrimitive<T2>,
117 {
118 let primitives = self.primitives;
120
121 let mut face_remap = FaceKeyRemap::default();
122 let mut faces: SlotMap<FaceKey, FaceMeta<T2>> = SlotMap::with_key();
123 for (old_fk, meta) in self.faces {
124 let new_fk = faces.insert(FaceMeta {
125 primitive_key: meta.primitive_key,
126 normal: meta.normal.map(|x| x.as_()),
127 label: meta.label,
128 });
129 face_remap.0.insert(old_fk, new_fk);
130 }
131
132 let mut edges: SlotMap<EdgeKey, EdgeMeta<T2>> = SlotMap::with_key();
133 for (_, meta) in self.edges {
134 edges.insert(EdgeMeta {
135 primitive_key: meta.primitive_key,
136 vertices: (
137 meta.vertices.0.map(|x| x.as_()),
138 meta.vertices.1.map(|x| x.as_()),
139 ),
140 face_keys: meta
141 .face_keys
142 .map(|fk| face_remap.0.get(&fk).copied().unwrap_or(fk)),
143 });
144 }
145
146 (
147 CsgFactory {
148 primitives,
149 faces,
150 edges,
151 },
152 face_remap,
153 )
154 }
155
156 pub fn face(&self, key: FaceKey) -> Option<&FaceMeta<T>> {
159 self.faces.get(key)
160 }
161
162 pub fn edge(&self, key: EdgeKey) -> Option<&EdgeMeta<T>> {
163 self.edges.get(key)
164 }
165
166 pub fn primitive(&self, key: PrimitiveKey) -> Option<&PrimitiveMeta> {
167 self.primitives.get(key)
168 }
169
170 pub fn polygons_sharing_face(&self, csg: &Csg<T>, face_key: FaceKey) -> Vec<usize> {
172 csg.polygons()
173 .iter()
174 .enumerate()
175 .filter(|(_, p)| p.face_key() == Some(face_key))
176 .map(|(i, _)| i)
177 .collect()
178 }
179
180 pub fn cube(&mut self, size: T) -> Csg<T> {
184 self.cuboid(size, size, size)
185 }
186
187 pub fn cuboid(&mut self, x: T, y: T, z: T) -> Csg<T> {
189 let two = T::one() + T::one();
190 let hx = x / two;
191 let hy = y / two;
192 let hz = z / two;
193
194 let v = [
195 Point::from([-hx, -hy, -hz]), Point::from([hx, -hy, -hz]), Point::from([hx, hy, -hz]), Point::from([-hx, hy, -hz]), Point::from([-hx, -hy, hz]), Point::from([hx, -hy, hz]), Point::from([hx, hy, hz]), Point::from([-hx, hy, hz]), ];
204
205 #[allow(clippy::type_complexity)]
207 let face_defs: [([Point<T, 3>; 4], Vector3<T>, T, &str, [[usize; 2]; 4]); 6] = [
208 (
209 [v[3], v[2], v[1], v[0]],
210 -Vector3::z(),
211 hz,
212 "back",
213 [[3, 2], [2, 1], [1, 0], [0, 3]],
214 ),
215 (
216 [v[4], v[5], v[6], v[7]],
217 Vector3::z(),
218 hz,
219 "front",
220 [[4, 5], [5, 6], [6, 7], [7, 4]],
221 ),
222 (
223 [v[3], v[0], v[4], v[7]],
224 -Vector3::x(),
225 hx,
226 "left",
227 [[3, 0], [0, 4], [4, 7], [7, 3]],
228 ),
229 (
230 [v[1], v[2], v[6], v[5]],
231 Vector3::x(),
232 hx,
233 "right",
234 [[1, 2], [2, 6], [6, 5], [5, 1]],
235 ),
236 (
237 [v[0], v[1], v[5], v[4]],
238 -Vector3::y(),
239 hy,
240 "bottom",
241 [[0, 1], [1, 5], [5, 4], [4, 0]],
242 ),
243 (
244 [v[2], v[3], v[7], v[6]],
245 Vector3::y(),
246 hy,
247 "top",
248 [[2, 3], [3, 7], [7, 6], [6, 2]],
249 ),
250 ];
251
252 let prim_key = self.primitives.insert(PrimitiveMeta {
254 kind: PrimitiveKind::Cuboid,
255 face_keys: Vec::new(),
256 edge_keys: Vec::new(),
257 color: None,
258 });
259
260 let mut all_face_keys = Vec::with_capacity(6);
261 let mut all_edge_keys = Vec::new();
262
263 let face_keys_arr: Vec<FaceKey> = face_defs
265 .iter()
266 .map(|(_, normal, _, label, _)| {
267 self.faces.insert(FaceMeta {
268 primitive_key: prim_key,
269 normal: *normal,
270 label: label.to_string(),
271 })
272 })
273 .collect();
274 all_face_keys.extend_from_slice(&face_keys_arr);
275
276 let mut edge_map: std::collections::BTreeMap<(usize, usize), (EdgeKey, bool)> =
279 std::collections::BTreeMap::new();
280
281 for (face_idx, (_, _, _, _, edge_pairs)) in face_defs.iter().enumerate() {
282 for &[a, b] in edge_pairs {
283 let canonical = if a < b { (a, b) } else { (b, a) };
284 edge_map
285 .entry(canonical)
286 .and_modify(|(ek, _)| {
287 if let Some(em) = self.edges.get_mut(*ek) {
289 em.face_keys[1] = face_keys_arr[face_idx];
290 }
291 })
292 .or_insert_with(|| {
293 let ek = self.edges.insert(EdgeMeta {
294 primitive_key: prim_key,
295 vertices: (v[a], v[b]),
296 face_keys: [face_keys_arr[face_idx], face_keys_arr[face_idx]],
297 });
298 all_edge_keys.push(ek);
299 (ek, true)
300 });
301 }
302 }
303
304 if let Some(pm) = self.primitives.get_mut(prim_key) {
306 pm.face_keys = all_face_keys;
307 pm.edge_keys = all_edge_keys;
308 }
309
310 let polygons = face_defs.iter().zip(face_keys_arr.iter()).flat_map(
312 |((verts, normal, dist, _, _), &fk)| {
313 Polygon::from_points_and_plane(verts.to_vec(), Plane::new(*normal, *dist))
314 .ok()
315 .map(|mut p| {
316 p.set_face_key(Some(fk));
317 p
318 })
319 },
320 );
321
322 Csg::from_polygons(polygons)
323 }
324
325 pub fn cylinder(&mut self, radius: T, height: T, segments: usize) -> Csg<T> {
327 let segments = segments.max(3);
328 let two = T::one() + T::one();
329 let half_h = height / two;
330 let pi = T::PI();
331 let two_pi = pi + pi;
332 let seg_t = T::from(segments).expect("BUG: f64 must represent segment count");
333
334 let circle_point = |j: usize, y: T| -> Point<T, 3> {
335 let angle = two_pi * T::from(j).expect("BUG: f64 must represent loop index") / seg_t;
336 Point::from([
337 radius * num_traits::real::Real::cos(angle),
338 y,
339 radius * num_traits::real::Real::sin(angle),
340 ])
341 };
342
343 let top_pts: Vec<Point<T, 3>> = (0..segments).map(|j| circle_point(j, half_h)).collect();
344 let bot_pts: Vec<Point<T, 3>> = (0..segments).map(|j| circle_point(j, -half_h)).collect();
345
346 let prim_key = self.primitives.insert(PrimitiveMeta {
347 kind: PrimitiveKind::Cylinder,
348 face_keys: Vec::new(),
349 edge_keys: Vec::new(),
350 color: None,
351 });
352
353 let top_fk = self.faces.insert(FaceMeta {
355 primitive_key: prim_key,
356 normal: Vector3::y(),
357 label: "top".to_string(),
358 });
359
360 let bot_fk = self.faces.insert(FaceMeta {
362 primitive_key: prim_key,
363 normal: -Vector3::y(),
364 label: "bottom".to_string(),
365 });
366
367 let mut all_face_keys = vec![top_fk, bot_fk];
368 let mut all_edge_keys = Vec::new();
369 let mut polygons = Vec::new();
370
371 if let Ok(mut p) =
373 Polygon::from_points_and_plane(top_pts.clone(), Plane::new(Vector3::y(), half_h))
374 {
375 p.set_face_key(Some(top_fk));
376 polygons.push(p);
377 }
378
379 let mut bot_cap = bot_pts.clone();
381 bot_cap.reverse();
382 if let Ok(mut p) =
383 Polygon::from_points_and_plane(bot_cap, Plane::new(-Vector3::y(), half_h))
384 {
385 p.set_face_key(Some(bot_fk));
386 polygons.push(p);
387 }
388
389 for j in 0..segments {
391 let jn = (j + 1) % segments;
392
393 let mid_angle =
394 (two * T::from(j).expect("BUG: f64 must represent loop index") + T::one()) * pi
395 / seg_t;
396 let normal = Vector3::new(
397 num_traits::real::Real::cos(mid_angle),
398 T::zero(),
399 num_traits::real::Real::sin(mid_angle),
400 );
401 let dist = radius * num_traits::real::Real::cos(pi / seg_t);
402
403 let side_fk = self.faces.insert(FaceMeta {
404 primitive_key: prim_key,
405 normal,
406 label: format!("side_{j}"),
407 });
408 all_face_keys.push(side_fk);
409
410 let top_ek = self.edges.insert(EdgeMeta {
412 primitive_key: prim_key,
413 vertices: (top_pts[j], top_pts[jn]),
414 face_keys: [side_fk, top_fk],
415 });
416 all_edge_keys.push(top_ek);
417
418 let bot_ek = self.edges.insert(EdgeMeta {
420 primitive_key: prim_key,
421 vertices: (bot_pts[j], bot_pts[jn]),
422 face_keys: [side_fk, bot_fk],
423 });
424 all_edge_keys.push(bot_ek);
425
426 let verts = [top_pts[j], top_pts[jn], bot_pts[jn], bot_pts[j]];
427 if let Ok(mut p) = Polygon::from_points_and_plane(verts, Plane::new(normal, dist)) {
428 p.set_face_key(Some(side_fk));
429 polygons.push(p);
430 }
431 }
432
433 for j in 0..segments {
435 let jn = (j + 1) % segments;
436 let side_fk_j = all_face_keys[2 + j];
439 let side_fk_jn = all_face_keys[2 + jn];
440 let ek = self.edges.insert(EdgeMeta {
441 primitive_key: prim_key,
442 vertices: (top_pts[jn], bot_pts[jn]),
443 face_keys: [side_fk_j, side_fk_jn],
444 });
445 all_edge_keys.push(ek);
446 }
447
448 if let Some(pm) = self.primitives.get_mut(prim_key) {
449 pm.face_keys = all_face_keys;
450 pm.edge_keys = all_edge_keys;
451 }
452
453 Csg::from_polygons(polygons)
454 }
455
456 pub fn cone(&mut self, radius: T, height: T, segments: usize) -> Csg<T> {
458 let segments = segments.max(3);
459 let two = T::one() + T::one();
460 let half_h = height / two;
461 let two_pi = two * T::PI();
462 let seg_t = T::from(segments).expect("BUG: f64 must represent segment count");
463
464 let apex = Point::from([T::zero(), half_h, T::zero()]);
465 let base: Vec<Point<T, 3>> = (0..segments)
466 .map(|j| {
467 let angle =
468 two_pi * T::from(j).expect("BUG: f64 must represent loop index") / seg_t;
469 Point::from([
470 radius * num_traits::real::Real::cos(angle),
471 -half_h,
472 radius * num_traits::real::Real::sin(angle),
473 ])
474 })
475 .collect();
476
477 let prim_key = self.primitives.insert(PrimitiveMeta {
478 kind: PrimitiveKind::Cone,
479 face_keys: Vec::new(),
480 edge_keys: Vec::new(),
481 color: None,
482 });
483
484 let base_fk = self.faces.insert(FaceMeta {
485 primitive_key: prim_key,
486 normal: -Vector3::y(),
487 label: "base".to_string(),
488 });
489
490 let mut all_face_keys = vec![base_fk];
491 let mut all_edge_keys = Vec::new();
492 let mut polygons = Vec::new();
493
494 let mut base_cap = base.clone();
496 base_cap.reverse();
497 if let Ok(mut p) =
498 Polygon::from_points_and_plane(base_cap, Plane::new(-Vector3::y(), half_h))
499 {
500 p.set_face_key(Some(base_fk));
501 polygons.push(p);
502 }
503
504 for j in 0..segments {
506 let jn = (j + 1) % segments;
507
508 let side_fk = self.faces.insert(FaceMeta {
509 primitive_key: prim_key,
510 normal: {
511 let mid = (base[j].coords + base[jn].coords) / two
513 - nalgebra::Vector3::new(T::zero(), -half_h, T::zero());
514 mid.normalize()
515 },
516 label: format!("side_{j}"),
517 });
518 all_face_keys.push(side_fk);
519
520 let base_ek = self.edges.insert(EdgeMeta {
522 primitive_key: prim_key,
523 vertices: (base[j], base[jn]),
524 face_keys: [side_fk, base_fk],
525 });
526 all_edge_keys.push(base_ek);
527
528 if let Ok(mut p) = Polygon::from_points(vec![apex, base[jn], base[j]]) {
529 p.set_face_key(Some(side_fk));
530 polygons.push(p);
531 }
532 }
533
534 for j in 0..segments {
536 let jp = if j == 0 { segments - 1 } else { j - 1 };
537 let side_fk_prev = all_face_keys[1 + jp];
538 let side_fk_curr = all_face_keys[1 + j];
539 let ek = self.edges.insert(EdgeMeta {
540 primitive_key: prim_key,
541 vertices: (apex, base[j]),
542 face_keys: [side_fk_prev, side_fk_curr],
543 });
544 all_edge_keys.push(ek);
545 }
546
547 if let Some(pm) = self.primitives.get_mut(prim_key) {
548 pm.face_keys = all_face_keys;
549 pm.edge_keys = all_edge_keys;
550 }
551
552 Csg::from_polygons(polygons)
553 }
554
555 pub fn torus(
557 &mut self,
558 major_radius: T,
559 minor_radius: T,
560 major_segments: usize,
561 minor_segments: usize,
562 ) -> Csg<T> {
563 let major_segments = major_segments.max(3);
564 let minor_segments = minor_segments.max(3);
565 let two_pi = T::PI() + T::PI();
566 let maj_t = T::from(major_segments).expect("BUG: f64 must represent segment count");
567 let min_t = T::from(minor_segments).expect("BUG: f64 must represent segment count");
568
569 let vertex = |i: usize, j: usize| -> Point<T, 3> {
570 let theta = two_pi * T::from(i).expect("BUG: f64 must represent loop index") / maj_t;
571 let phi = two_pi * T::from(j).expect("BUG: f64 must represent loop index") / min_t;
572 let r = major_radius + minor_radius * num_traits::real::Real::cos(phi);
573 Point::from([
574 r * num_traits::real::Real::cos(theta),
575 minor_radius * num_traits::real::Real::sin(phi),
576 r * num_traits::real::Real::sin(theta),
577 ])
578 };
579
580 let prim_key = self.primitives.insert(PrimitiveMeta {
581 kind: PrimitiveKind::Torus,
582 face_keys: Vec::new(),
583 edge_keys: Vec::new(),
584 color: None,
585 });
586
587 let mut all_face_keys = Vec::new();
588 let all_edge_keys = Vec::new();
589 let mut polygons = Vec::new();
590
591 for i in 0..major_segments {
593 let in_ = (i + 1) % major_segments;
594 for j in 0..minor_segments {
595 let jn = (j + 1) % minor_segments;
596
597 let v00 = vertex(i, j);
598 let v01 = vertex(i, jn);
599 let v10 = vertex(in_, j);
600 let v11 = vertex(in_, jn);
601
602 let face_fk = self.faces.insert(FaceMeta {
603 primitive_key: prim_key,
604 normal: {
605 let e1 = v01 - v00;
606 let e2 = v10 - v00;
607 e1.cross(&e2).normalize()
608 },
609 label: format!("quad_{i}_{j}"),
610 });
611 all_face_keys.push(face_fk);
612
613 if let Ok(mut p) = Polygon::from_points(vec![v00, v01, v11, v10]) {
614 p.set_face_key(Some(face_fk));
615 polygons.push(p);
616 }
617 }
618 }
619
620 if let Some(pm) = self.primitives.get_mut(prim_key) {
621 pm.face_keys = all_face_keys;
622 pm.edge_keys = all_edge_keys;
623 }
624
625 Csg::from_polygons(polygons)
626 }
627
628 pub fn rhomboid(&mut self, a: Vector3<T>, b: Vector3<T>, c: Vector3<T>) -> Csg<T> {
630 let two = T::one() + T::one();
631 let offset = (a + b + c) / two;
632
633 let corners = [
634 Vector3::zeros(), a, a + b, b, c, a + c, a + b + c, b + c, ];
643 let v: [Point<T, 3>; 8] = corners.map(|corner| Point::from(corner - offset));
644
645 let face_defs: [([usize; 4], &str); 6] = [
646 ([0, 3, 2, 1], "-c"),
647 ([4, 5, 6, 7], "+c"),
648 ([0, 4, 7, 3], "-a"),
649 ([1, 2, 6, 5], "+a"),
650 ([0, 1, 5, 4], "-b"),
651 ([3, 7, 6, 2], "+b"),
652 ];
653
654 let four = T::from(4).expect("BUG: f64 must represent 4");
655
656 let prim_key = self.primitives.insert(PrimitiveMeta {
657 kind: PrimitiveKind::Rhomboid,
658 face_keys: Vec::new(),
659 edge_keys: Vec::new(),
660 color: None,
661 });
662
663 let mut all_face_keys = Vec::new();
664 let mut all_edge_keys = Vec::new();
665 let mut polygons = Vec::new();
666
667 let mut face_keys_arr = Vec::with_capacity(6);
668
669 for (idx, label) in face_defs.iter() {
670 let verts: Vec<Point<T, 3>> = idx.iter().map(|&i| v[i]).collect();
671 let face_center =
672 (verts[0].coords + verts[1].coords + verts[2].coords + verts[3].coords) / four;
673
674 let poly = match Polygon::from_points(verts) {
675 Ok(p) => p,
676 Err(_) => {
677 face_keys_arr.push(None);
678 continue;
679 }
680 };
681
682 let (poly, normal) = if poly.plane().normal().dot(&face_center) < T::zero() {
683 let flipped = poly.flip();
684 let n = flipped.plane().normal();
685 (flipped, n)
686 } else {
687 let n = poly.plane().normal();
688 (poly, n)
689 };
690
691 let fk = self.faces.insert(FaceMeta {
692 primitive_key: prim_key,
693 normal,
694 label: label.to_string(),
695 });
696 face_keys_arr.push(Some(fk));
697 all_face_keys.push(fk);
698
699 let mut poly = poly;
700 poly.set_face_key(Some(fk));
701 polygons.push(poly);
702 }
703
704 let edge_indices: [[[usize; 2]; 4]; 6] = [
706 [[0, 3], [3, 2], [2, 1], [1, 0]],
707 [[4, 5], [5, 6], [6, 7], [7, 4]],
708 [[0, 4], [4, 7], [7, 3], [3, 0]],
709 [[1, 2], [2, 6], [6, 5], [5, 1]],
710 [[0, 1], [1, 5], [5, 4], [4, 0]],
711 [[3, 7], [7, 6], [6, 2], [2, 3]],
712 ];
713
714 let mut edge_map: std::collections::BTreeMap<(usize, usize), EdgeKey> =
715 std::collections::BTreeMap::new();
716
717 for (face_idx, edges) in edge_indices.iter().enumerate() {
718 let Some(fk) = face_keys_arr[face_idx] else {
719 continue;
720 };
721 for &[ea, eb] in edges {
722 let canonical = if ea < eb { (ea, eb) } else { (eb, ea) };
723 edge_map
724 .entry(canonical)
725 .and_modify(|ek| {
726 if let Some(em) = self.edges.get_mut(*ek) {
727 em.face_keys[1] = fk;
728 }
729 })
730 .or_insert_with(|| {
731 let ek = self.edges.insert(EdgeMeta {
732 primitive_key: prim_key,
733 vertices: (v[ea], v[eb]),
734 face_keys: [fk, fk],
735 });
736 all_edge_keys.push(ek);
737 ek
738 });
739 }
740 }
741
742 if let Some(pm) = self.primitives.get_mut(prim_key) {
743 pm.face_keys = all_face_keys;
744 pm.edge_keys = all_edge_keys;
745 }
746
747 Csg::from_polygons(polygons)
748 }
749
750 pub fn sphere(&mut self, radius: T, segments: usize, stacks: usize) -> Csg<T> {
752 use itertools::*;
753
754 let segments = segments.max(3);
755 let stacks = stacks.max(2);
756
757 let pi = T::PI();
758 let two = T::one() + T::one();
759 let two_pi = two * pi;
760
761 let stacks_t = T::from(stacks).expect("BUG: failed to convert stacks to T");
762 let segments_t = T::from(segments).expect("BUG: failed to convert segments to T");
763
764 let point_on_surface = |frac_seg: T, frac_stack: T| {
765 let lat = pi * frac_stack;
766 let stack_radius = radius * num_traits::real::Real::sin(lat);
767 let x = stack_radius * num_traits::real::Real::sin(two_pi * frac_seg);
768 let y = radius * num_traits::real::Real::cos(lat);
769 let z = stack_radius * num_traits::real::Real::cos(two_pi * frac_seg);
770 Point3::new(x, y, z)
771 };
772
773 let seg_fracs: Vec<T> = (0..segments)
774 .flat_map(|v| T::from(v))
775 .map(|v| v / segments_t)
776 .collect();
777
778 let stack_pairs: Vec<(T, T)> = (1..stacks)
779 .flat_map(|v| T::from(v))
780 .map(|v| v / stacks_t)
781 .tuple_windows()
782 .collect();
783
784 let first_ring = T::one() / stacks_t;
785 let last_ring = T::from(stacks - 1).expect("BUG: stacks > 1") / stacks_t;
786
787 let prim_key = self.primitives.insert(PrimitiveMeta {
788 kind: PrimitiveKind::Sphere,
789 face_keys: Vec::new(),
790 edge_keys: Vec::new(),
791 color: None,
792 });
793
794 let mut all_face_keys = Vec::new();
795 let mut polygons = Vec::new();
796
797 let top_fk = self.faces.insert(FaceMeta {
799 primitive_key: prim_key,
800 normal: Vector3::y(),
801 label: "top_cap".to_string(),
802 });
803 all_face_keys.push(top_fk);
804
805 if let Ok(mut p) =
806 Polygon::from_points(seg_fracs.iter().map(|&s| point_on_surface(s, first_ring)))
807 {
808 p.set_face_key(Some(top_fk));
809 polygons.push(p);
810 }
811
812 let bot_fk = self.faces.insert(FaceMeta {
814 primitive_key: prim_key,
815 normal: -Vector3::y(),
816 label: "bottom_cap".to_string(),
817 });
818 all_face_keys.push(bot_fk);
819
820 if let Ok(mut p) = Polygon::from_points(
821 seg_fracs
822 .iter()
823 .rev()
824 .map(|&s| point_on_surface(s, last_ring)),
825 ) {
826 p.set_face_key(Some(bot_fk));
827 polygons.push(p);
828 }
829
830 for (left, right) in seg_fracs.iter().copied().circular_tuple_windows::<(_, _)>() {
832 for &(top, bottom) in &stack_pairs {
833 let face_fk = self.faces.insert(FaceMeta {
834 primitive_key: prim_key,
835 normal: {
836 let center = point_on_surface((left + right) / two, (top + bottom) / two);
837 center.coords.normalize()
838 },
839 label: "body".to_string(),
840 });
841 all_face_keys.push(face_fk);
842
843 if let Ok(mut p) = Polygon::from_points([
844 point_on_surface(left, top),
845 point_on_surface(left, bottom),
846 point_on_surface(right, bottom),
847 point_on_surface(right, top),
848 ]) {
849 p.set_face_key(Some(face_fk));
850 polygons.push(p);
851 }
852 }
853 }
854
855 if let Some(pm) = self.primitives.get_mut(prim_key) {
856 pm.face_keys = all_face_keys;
857 pm.edge_keys = Vec::new();
859 }
860
861 Csg::from_polygons(polygons)
862 }
863}