1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
use std::{array, borrow::Borrow};
use fj_interop::ext::ArrayExt;
use fj_math::Point;
use crate::{
objects::{Cycle, Face, HalfEdge, Region, Surface, Vertex},
operations::{
build::{BuildCycle, BuildRegion, BuildSurface},
insert::{Insert, IsInserted, IsInsertedNo},
},
services::Services,
storage::Handle,
};
/// Build a [`Face`]
///
/// See [module-level documentation] for context.
///
/// [module-level documentation]: super
pub trait BuildFace {
/// Build a face with an empty exterior, no interiors, and no color
fn unbound(surface: Handle<Surface>, services: &mut Services) -> Face {
let exterior = Cycle::empty().insert(services);
let region = Region::new(exterior, [], None).insert(services);
Face::new(surface, region)
}
/// Build a triangle
fn triangle(
points: [impl Into<Point<3>>; 3],
services: &mut Services,
) -> Polygon<3> {
let (surface, points_surface) = Surface::plane_from_points(points);
let surface = surface.insert(services);
let face = Face::polygon(surface, points_surface, services);
let half_edges = {
let mut edges =
face.region().exterior().half_edges().iter().cloned();
let array = array::from_fn(|_| edges.next()).map(|edge| {
edge.expect("Just asserted that there are three edges")
});
assert!(edges.next().is_none());
array
};
let vertices = half_edges
.each_ref_ext()
.map(|edge: &Handle<HalfEdge>| edge.start_vertex().clone());
Polygon {
face,
half_edges,
vertices,
}
}
/// Build a polygon
fn polygon<P, Ps>(
surface: Handle<Surface>,
points: Ps,
services: &mut Services,
) -> Face
where
P: Into<Point<2>>,
Ps: IntoIterator<Item = P>,
Ps::IntoIter: Clone + ExactSizeIterator,
{
let region = Region::polygon(points, services).insert(services);
Face::new(surface, region)
}
}
impl BuildFace for Face {}
/// A polygon
///
/// # Implementation Note
///
/// Currently code that deals with `Polygon` might assume that the polygon has
/// no holes. Unless you create a `Polygon` yourself, or modify a `Polygon`'s
/// `face` field to have interior cycles, this should not affect you.
pub struct Polygon<const D: usize, I: IsInserted = IsInsertedNo> {
/// The face that forms the polygon
pub face: I::T<Face>,
/// The half-edges of the polygon
pub half_edges: [Handle<HalfEdge>; D],
/// The vertices of the polygon
pub vertices: [Handle<Vertex>; D],
}
impl<const D: usize, I: IsInserted> Polygon<D, I> {
/// Replace the face of the polygon
///
/// Returns a new instance of `Polygon` with the replaced face. Also updates
/// the other fields of `Polygon` to match the new face.
pub fn replace_face(&self, face: I::T<Face>) -> Self {
let half_edges = array::from_fn(|i| {
face.borrow()
.region()
.exterior()
.half_edges()
.nth(i)
.expect("Operation should not have changed length of cycle")
.clone()
});
let vertices = array::from_fn(|i| {
// The duplicated code here is unfortunate, but unless we get a
// stable `array::each_ref` and something like `array::unzip`, I'm
// not sure how to avoid it.
face.borrow()
.region()
.exterior()
.half_edges()
.nth(i)
.expect("Operation should not have changed length of cycle")
.start_vertex()
.clone()
});
Self {
face,
half_edges,
vertices,
}
}
}