brep_kernel/construction/
polygon_solids.rs1use crate::{face_from_surface_trimmed, make_line, make_plane, solid_from_sheets, BrepSolid, KernelRefusal, KernelStage, OrRefuse, Vec3};
4
5fn input(what: &'static str, message: String) -> KernelRefusal {
6 KernelRefusal::input(KernelStage::Collect, what, message)
7}
8
9fn polygon_face(corners: &[Vec3], index: usize) -> Result<BrepSolid, KernelRefusal> {
12 if corners.len() < 3 {
13 return Err(input("polygon_corners", format!("solidFromPolygons: polygon {index} has fewer than three corners")));
14 }
15 let origin = corners[0];
16 let arms: Vec<Vec3> = corners.iter().map(|corner| corner.sub(origin)).collect();
17 let size = arms.iter().map(|arm| arm.length()).fold(0.0, f64::max);
18 let flat = || input("polygon_plane", format!("solidFromPolygons: polygon {index} does not span a plane"));
19 let longest = arms.iter().copied().fold(Vec3::new(0.0, 0.0, 0.0), |best, arm| if arm.length() > best.length() { arm } else { best });
21 let e1 = longest.normalized().map_err(|_| flat())?;
22 let off = |arm: Vec3| arm.sub(e1.scale(arm.dot(e1)));
23 let widest = arms.iter().map(|arm| off(*arm)).fold(Vec3::new(0.0, 0.0, 0.0), |best, arm| if arm.length() > best.length() { arm } else { best });
24 if widest.length() <= 1e-9 * size {
25 return Err(flat());
26 }
27 let e2 = widest.normalized().map_err(|_| flat())?;
28 let normal = e1.cross(e2);
29 if arms.iter().any(|arm| arm.dot(normal).abs() > 1e-9 * size) {
30 return Err(input("polygon_plane", format!("solidFromPolygons: polygon {index}'s corners do not lie in one plane")));
31 }
32 let reach = |axis: Vec3| arms.iter().map(|arm| arm.dot(axis)).fold((0.0f64, 0.0f64), |(low, high), at| (low.min(at), high.max(at)));
33 let ((low1, high1), (low2, high2)) = (reach(e1), reach(e2));
34 let corner = origin.add(e1.scale(low1 - 0.5 * size)).add(e2.scale(low2 - 0.5 * size));
35 let plane = make_plane(corner, e1, e2, high1 - low1 + size, high2 - low2 + size).or_refuse(KernelStage::Collect, "solidFromPolygons.plane")?;
36 let sides = (0..corners.len())
37 .map(|k| make_line(corners[k], corners[(k + 1) % corners.len()]))
38 .collect::<Result<Vec<_>, _>>()
39 .or_refuse(KernelStage::Collect, "solidFromPolygons.side")?;
40 face_from_surface_trimmed(&plane, &[sides])
41}
42
43pub fn solid_from_polygons(faces: &[Vec<Vec3>]) -> Result<BrepSolid, KernelRefusal> {
49 if faces.len() < 4 {
50 return Err(input("polygon_count", "solidFromPolygons: a solid has at least four faces".into()));
51 }
52 let sheets = faces.iter().enumerate().map(|(index, corners)| polygon_face(corners, index)).collect::<Result<Vec<_>, _>>()?;
53 let size = faces.iter().flatten().map(|corner| corner.sub(faces[0][0]).length()).fold(0.0, f64::max);
54 solid_from_sheets(&sheets, (1e-8 * size).max(1e-7))
55}
56
57pub fn make_pyramid_on_polygon(base: &[Vec3], apex: Vec3) -> Result<BrepSolid, KernelRefusal> {
61 if base.len() < 3 {
62 return Err(input("pyramid_base", "makePyramidOnPolygon: the base has fewer than three corners".into()));
63 }
64 let mut faces = vec![base.to_vec()];
65 faces.extend((0..base.len()).map(|k| vec![base[k], base[(k + 1) % base.len()], apex]));
66 solid_from_polygons(&faces)
67}
68