pub struct Face { /* private fields */ }Expand description
A face of a shape
A Face is a bounded area of a Surface, the Surface itself being an
infinite 2-dimensional object in 3D space. Faces are bound by one exterior
cycle, which defines the outer boundary, and an arbitrary number of interior
cycles (i.e. holes).
Face has a defined orientation, a front and a back side. When faces are
combined into Shells, the face orientation defines what is inside and
outside of the shell. This stands in contrast to Surface, which has no
defined orientation.
You can look at a Face from two directions: front and back. The winding of
the exterior cycle will be clockwise or counter-clockwise, depending on your
perspective. The front side of the face, is the side where from which the
exterior cycle appear counter-clockwise.
Interior cycles must have the opposite winding of the exterior cycle,
meaning on the front side of the face, they must appear clockwise. This
means that all HalfEdges that bound a Face have the interior of the
face on their left side (on the face’s front side).
Implementations§
source§impl Face
impl Face
sourcepub fn new(
exterior: Handle<Cycle>,
interiors: impl IntoIterator<Item = Handle<Cycle>>,
color: Color
) -> Self
pub fn new(
exterior: Handle<Cycle>,
interiors: impl IntoIterator<Item = Handle<Cycle>>,
color: Color
) -> Self
Construct an instance of Face
Examples found in repository?
More examples
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
fn transform_with_cache(
self,
transform: &Transform,
objects: &mut Service<Objects>,
cache: &mut TransformCache,
) -> Self {
// Color does not need to be transformed.
let color = self.color();
let exterior = self
.exterior()
.clone()
.transform_with_cache(transform, objects, cache);
let interiors = self.interiors().cloned().map(|interior| {
interior.transform_with_cache(transform, objects, cache)
});
Self::new(exterior, interiors, color)
}sourcepub fn surface(&self) -> &Handle<Surface>
pub fn surface(&self) -> &Handle<Surface>
Access the surface of the face
Examples found in repository?
More examples
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78
fn check_surface_identity(face: &Face) -> Result<(), Self> {
let surface = face.surface();
for interior in face.interiors() {
if surface.id() != interior.surface().id() {
return Err(Self::SurfaceMismatch {
surface: surface.clone(),
interior: interior.clone(),
face: face.clone(),
});
}
}
Ok(())
}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
pub fn compute(
faces: [&Face; 2],
objects: &mut Service<Objects>,
) -> Option<Self> {
let surfaces = faces.map(|face| face.surface().clone());
let intersection_curves =
match SurfaceSurfaceIntersection::compute(surfaces, objects) {
Some(intersection) => intersection.intersection_curves,
None => return None,
};
let curve_face_intersections = intersection_curves
.each_ref_ext()
.into_iter_fixed()
.zip(faces)
.map(|(curve, face)| CurveFaceIntersection::compute(curve, face))
.collect::<[_; 2]>();
let intersection_intervals = {
let [a, b] = curve_face_intersections;
a.merge(&b)
};
if intersection_intervals.is_empty() {
return None;
}
Some(Self {
intersection_curves,
intersection_intervals,
})
}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
fn sweep_with_cache(
self,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
objects: &mut Service<Objects>,
) -> Self::Swept {
let path = path.into();
let mut faces = Vec::new();
let is_negative_sweep = {
let u = match self.surface().geometry().u {
GlobalPath::Circle(_) => todo!(
"Sweeping from faces defined in round surfaces is not \
supported"
),
GlobalPath::Line(line) => line.direction(),
};
let v = self.surface().geometry().v;
let normal = u.cross(&v);
normal.dot(&path) < Scalar::ZERO
};
let bottom_face = {
if is_negative_sweep {
self.clone()
} else {
self.clone().reverse(objects)
}
};
faces.push(bottom_face);
let top_face = {
let mut face = self.clone().translate(path, objects);
if is_negative_sweep {
face = face.reverse(objects);
};
face
};
faces.push(top_face);
// Generate side faces
for cycle in self.all_cycles() {
for half_edge in cycle.half_edges() {
let half_edge = if is_negative_sweep {
half_edge.clone().reverse(objects)
} else {
half_edge.clone()
};
let face = (half_edge, self.color())
.sweep_with_cache(path, cache, objects);
faces.push(face);
}
}
let faces = faces.into_iter().map(Partial::from).collect();
PartialShell { faces }.build(objects).insert(objects)
}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
fn intersect(self) -> Option<Self::Intersection> {
let (ray, face) = self;
let plane = match face.surface().geometry().u {
GlobalPath::Circle(_) => todo!(
"Casting a ray against a swept circle is not supported yet"
),
GlobalPath::Line(line) => Plane::from_parametric(
line.origin(),
line.direction(),
face.surface().geometry().v,
),
};
if plane.is_parallel_to_vector(&ray.direction()) {
let a = plane.origin();
let b = plane.origin() + plane.u();
let c = plane.origin() + plane.v();
let d = ray.origin;
let [a, b, c, d] = [a, b, c, d]
.map(|point| [point.x, point.y, point.z])
.map(|point| point.map(Scalar::into_f64));
if robust_predicates::orient3d(&a, &b, &c, &d) == 0. {
return Some(RayFaceIntersection::RayHitsFaceAndAreParallel);
} else {
return None;
}
}
// The pattern in this assertion resembles `ax*by = ay*bx`, which holds
// true if the vectors `a = (ax, ay)` and `b = (bx, by)` are parallel.
//
// We're looking at the plane's direction vectors here, but we're
// ignoring their x-components. By doing that, we're essentially
// projecting those vectors into the yz-plane.
//
// This means that the following assertion verifies that the projections
// of the plane's direction vectors into the yz-plane are not parallel.
// If they were, then the plane could only be parallel to the x-axis,
// and thus our ray.
//
// We already handled the case of the ray and plane being parallel
// above. The following assertion should thus never be triggered.
assert_ne!(
plane.u().y * plane.v().z,
plane.u().z * plane.v().y,
"Plane and ray are parallel; should have been ruled out previously"
);
// Let's figure out the intersection between the ray and the plane.
let (t, u, v) = {
// The following math would get *very* unwieldy with those
// full-length variable names. Let's define some short-hands.
let orx = ray.origin.x;
let ory = ray.origin.y;
let orz = ray.origin.z;
let opx = plane.origin().x;
let opy = plane.origin().y;
let opz = plane.origin().z;
let d1x = plane.u().x;
let d1y = plane.u().y;
let d1z = plane.u().z;
let d2x = plane.v().x;
let d2y = plane.v().y;
let d2z = plane.v().z;
// Let's figure out where the intersection between the ray and the
// plane is. By equating the parametric equations of the ray and the
// plane, we get a vector equation, which in turn gives us a system
// of three equations with three unknowns: `t` (for the ray) and
// `u`/`v` (for the plane).
//
// Since the ray's direction vector is `(1, 0, 0)`, it works out
// such that `t` is not in the equations for y and z, meaning we can
// solve those equations for `u` and `v` independently.
//
// By doing some math, we get the following solutions:
let v = (d1y * (orz - opz) + (opy - ory) * d1z)
/ (d1y * d2z - d2y * d1z);
let u = (ory - opy - d2y * v) / d1y;
let t = opx - orx + d1x * u + d2x * v;
(t, u, v)
};
if t < Scalar::ZERO {
// Ray points away from plane.
return None;
}
let point = Point::from([u, v]);
let intersection = match (face, &point).intersect()? {
FacePointIntersection::PointIsInsideFace => {
RayFaceIntersection::RayHitsFace
}
FacePointIntersection::PointIsOnEdge(edge) => {
RayFaceIntersection::RayHitsEdge(edge)
}
FacePointIntersection::PointIsOnVertex(vertex) => {
RayFaceIntersection::RayHitsVertex(vertex)
}
};
Some(intersection)
}sourcepub fn exterior(&self) -> &Handle<Cycle>
pub fn exterior(&self) -> &Handle<Cycle>
Access the cycle that bounds the face on the outside
Examples found in repository?
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
pub fn surface(&self) -> &Handle<Surface> {
self.exterior().surface()
}
/// Access the cycle that bounds the face on the outside
pub fn exterior(&self) -> &Handle<Cycle> {
&self.exterior
}
/// Access the cycles that bound the face on the inside
///
/// Each of these cycles defines a hole in the face.
pub fn interiors(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_ {
self.interiors.iter()
}
/// Access all cycles of the face
pub fn all_cycles(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_ {
[self.exterior()].into_iter().chain(self.interiors())
}
/// Access the color of the face
pub fn color(&self) -> Color {
self.color
}
/// Determine handed-ness of the face's front-side coordinate system
///
/// A face is defined on a surface, which has a coordinate system. Since
/// surfaces aren't considered to have an orientation, their coordinate
/// system can be considered to be left-handed or right-handed, depending on
/// which side of the surface you're looking at.
///
/// Faces *do* have an orientation, meaning they have definite front and
/// back sides. The front side is the side, where the face's exterior cycle
/// is wound counter-clockwise.
pub fn coord_handedness(&self) -> Handedness {
match self.exterior().winding() {
Winding::Ccw => Handedness::RightHanded,
Winding::Cw => Handedness::LeftHanded,
}
}More examples
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
fn transform_with_cache(
self,
transform: &Transform,
objects: &mut Service<Objects>,
cache: &mut TransformCache,
) -> Self {
// Color does not need to be transformed.
let color = self.color();
let exterior = self
.exterior()
.clone()
.transform_with_cache(transform, objects, cache);
let interiors = self.interiors().cloned().map(|interior| {
interior.transform_with_cache(transform, objects, cache)
});
Self::new(exterior, interiors, color)
}12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
fn reverse(self, objects: &mut Service<Objects>) -> Self {
let mut cache = FullToPartialCache::default();
let exterior = Partial::from_full(
self.exterior().clone().reverse(objects),
&mut cache,
);
let interiors = self
.interiors()
.map(|cycle| {
Partial::from_full(cycle.clone().reverse(objects), &mut cache)
})
.collect::<Vec<_>>();
let face = PartialFace {
exterior,
interiors,
color: Some(self.color()),
};
face.build(objects).insert(objects)
}80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101
fn check_interior_winding(face: &Face) -> Result<(), Self> {
let exterior_winding = face.exterior().winding();
for interior in face.interiors() {
let interior_winding = interior.winding();
if exterior_winding == interior_winding {
return Err(Self::InvalidInteriorWinding {
exterior_winding,
interior_winding,
face: face.clone(),
});
}
assert_ne!(
exterior_winding,
interior.winding(),
"Interior cycles must have opposite winding of exterior cycle"
);
}
Ok(())
}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
fn approx_with_cache(
self,
tolerance: impl Into<Tolerance>,
cache: &mut Self::Cache,
) -> Self::Approximation {
let tolerance = tolerance.into();
// Curved faces whose curvature is not fully defined by their edges
// are not supported yet. For that reason, we can fully ignore `face`'s
// `surface` field and just pass the edges to `Self::for_edges`.
//
// An example of a curved face that is supported, is the cylinder. Its
// curvature is fully defined be the edges (circles) that border it. The
// circle approximations are sufficient to triangulate the surface.
//
// An example of a curved face that is currently not supported, and thus
// doesn't need to be handled here, is a sphere. A spherical face would
// would need to provide its own approximation, as the edges that bound
// it have nothing to do with its curvature.
let exterior = self.exterior().approx_with_cache(tolerance, cache);
let mut interiors = BTreeSet::new();
for cycle in self.interiors() {
let cycle = cycle.approx_with_cache(tolerance, cache);
interiors.insert(cycle);
}
FaceApprox {
exterior,
interiors,
color: self.color(),
coord_handedness: self.coord_handedness(),
}
}sourcepub fn interiors(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_
pub fn interiors(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_
Access the cycles that bound the face on the inside
Each of these cycles defines a hole in the face.
Examples found in repository?
More examples
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
fn check_surface_identity(face: &Face) -> Result<(), Self> {
let surface = face.surface();
for interior in face.interiors() {
if surface.id() != interior.surface().id() {
return Err(Self::SurfaceMismatch {
surface: surface.clone(),
interior: interior.clone(),
face: face.clone(),
});
}
}
Ok(())
}
fn check_interior_winding(face: &Face) -> Result<(), Self> {
let exterior_winding = face.exterior().winding();
for interior in face.interiors() {
let interior_winding = interior.winding();
if exterior_winding == interior_winding {
return Err(Self::InvalidInteriorWinding {
exterior_winding,
interior_winding,
face: face.clone(),
});
}
assert_ne!(
exterior_winding,
interior.winding(),
"Interior cycles must have opposite winding of exterior cycle"
);
}
Ok(())
}11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
fn transform_with_cache(
self,
transform: &Transform,
objects: &mut Service<Objects>,
cache: &mut TransformCache,
) -> Self {
// Color does not need to be transformed.
let color = self.color();
let exterior = self
.exterior()
.clone()
.transform_with_cache(transform, objects, cache);
let interiors = self.interiors().cloned().map(|interior| {
interior.transform_with_cache(transform, objects, cache)
});
Self::new(exterior, interiors, color)
}12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
fn reverse(self, objects: &mut Service<Objects>) -> Self {
let mut cache = FullToPartialCache::default();
let exterior = Partial::from_full(
self.exterior().clone().reverse(objects),
&mut cache,
);
let interiors = self
.interiors()
.map(|cycle| {
Partial::from_full(cycle.clone().reverse(objects), &mut cache)
})
.collect::<Vec<_>>();
let face = PartialFace {
exterior,
interiors,
color: Some(self.color()),
};
face.build(objects).insert(objects)
}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
fn approx_with_cache(
self,
tolerance: impl Into<Tolerance>,
cache: &mut Self::Cache,
) -> Self::Approximation {
let tolerance = tolerance.into();
// Curved faces whose curvature is not fully defined by their edges
// are not supported yet. For that reason, we can fully ignore `face`'s
// `surface` field and just pass the edges to `Self::for_edges`.
//
// An example of a curved face that is supported, is the cylinder. Its
// curvature is fully defined be the edges (circles) that border it. The
// circle approximations are sufficient to triangulate the surface.
//
// An example of a curved face that is currently not supported, and thus
// doesn't need to be handled here, is a sphere. A spherical face would
// would need to provide its own approximation, as the edges that bound
// it have nothing to do with its curvature.
let exterior = self.exterior().approx_with_cache(tolerance, cache);
let mut interiors = BTreeSet::new();
for cycle in self.interiors() {
let cycle = cycle.approx_with_cache(tolerance, cache);
interiors.insert(cycle);
}
FaceApprox {
exterior,
interiors,
color: self.color(),
coord_handedness: self.coord_handedness(),
}
}sourcepub fn all_cycles(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_
pub fn all_cycles(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_
Access all cycles of the face
Examples found in repository?
More examples
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
pub fn compute(curve: &Curve, face: &Face) -> Self {
let half_edges = face.all_cycles().flat_map(|cycle| cycle.half_edges());
let mut intersections = Vec::new();
for half_edge in half_edges {
let intersection = CurveEdgeIntersection::compute(curve, half_edge);
if let Some(intersection) = intersection {
match intersection {
CurveEdgeIntersection::Point { point_on_curve } => {
intersections.push(point_on_curve);
}
CurveEdgeIntersection::Coincident { points_on_curve } => {
intersections.extend(points_on_curve);
}
}
}
}
assert!(intersections.len() % 2 == 0);
intersections.sort();
let intervals = intersections
.as_slice()
.array_chunks_ext()
.map(|&[start, end]| CurveFaceIntersectionInterval { start, end })
.collect();
Self { intervals }
}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
fn sweep_with_cache(
self,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
objects: &mut Service<Objects>,
) -> Self::Swept {
let path = path.into();
let mut faces = Vec::new();
let is_negative_sweep = {
let u = match self.surface().geometry().u {
GlobalPath::Circle(_) => todo!(
"Sweeping from faces defined in round surfaces is not \
supported"
),
GlobalPath::Line(line) => line.direction(),
};
let v = self.surface().geometry().v;
let normal = u.cross(&v);
normal.dot(&path) < Scalar::ZERO
};
let bottom_face = {
if is_negative_sweep {
self.clone()
} else {
self.clone().reverse(objects)
}
};
faces.push(bottom_face);
let top_face = {
let mut face = self.clone().translate(path, objects);
if is_negative_sweep {
face = face.reverse(objects);
};
face
};
faces.push(top_face);
// Generate side faces
for cycle in self.all_cycles() {
for half_edge in cycle.half_edges() {
let half_edge = if is_negative_sweep {
half_edge.clone().reverse(objects)
} else {
half_edge.clone()
};
let face = (half_edge, self.color())
.sweep_with_cache(path, cache, objects);
faces.push(face);
}
}
let faces = faces.into_iter().map(Partial::from).collect();
PartialShell { faces }.build(objects).insert(objects)
}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
fn intersect(self) -> Option<Self::Intersection> {
let (face, point) = self;
let ray = HorizontalRayToTheRight { origin: *point };
let mut num_hits = 0;
for cycle in face.all_cycles() {
// We need to properly detect the ray passing the boundary at the
// "seam" of the polygon, i.e. the vertex between the last and the
// first segment. The logic in the loop properly takes care of that,
// as long as we initialize the `previous_hit` variable with the
// result of the last segment.
let mut previous_hit = cycle
.half_edges()
.last()
.cloned()
.and_then(|edge| (&ray, &edge).intersect());
for half_edge in cycle.half_edges() {
let hit = (&ray, half_edge).intersect();
let count_hit = match (hit, previous_hit) {
(
Some(RaySegmentIntersection::RayStartsOnSegment),
_,
) => {
// If the ray starts on the boundary of the face,
// there's nothing to else check.
return Some(FacePointIntersection::PointIsOnEdge(
half_edge.clone()
));
}
(Some(RaySegmentIntersection::RayStartsOnOnFirstVertex), _) => {
let vertex = half_edge.vertices()[0].clone();
return Some(
FacePointIntersection::PointIsOnVertex(vertex)
);
}
(Some(RaySegmentIntersection::RayStartsOnSecondVertex), _) => {
let vertex = half_edge.vertices()[1].clone();
return Some(
FacePointIntersection::PointIsOnVertex(vertex)
);
}
(Some(RaySegmentIntersection::RayHitsSegment), _) => {
// We're hitting a segment right-on. Clear case.
true
}
(
Some(RaySegmentIntersection::RayHitsUpperVertex),
Some(RaySegmentIntersection::RayHitsLowerVertex),
)
| (
Some(RaySegmentIntersection::RayHitsLowerVertex),
Some(RaySegmentIntersection::RayHitsUpperVertex),
) => {
// If we're hitting a vertex, only count it if we've hit
// the other kind of vertex right before.
//
// That means, we're passing through the polygon
// boundary at where two edges touch. Depending on the
// order in which edges are checked, we're seeing this
// as a hit to one edge's lower/upper vertex, then the
// other edge's opposite vertex.
//
// If we're seeing two of the same vertices in a row,
// we're not actually passing through the polygon
// boundary. Then we're just touching a vertex without
// passing through anything.
true
}
(Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel), _) => {
// A parallel edge must be completely ignored. Its
// presence won't change anything, so we can treat it as
// if it wasn't there, and its neighbors were connected
// to each other.
continue;
}
_ => {
// Any other case is not a valid hit.
false
}
};
if count_hit {
num_hits += 1;
}
previous_hit = hit;
}
}
if num_hits % 2 == 1 {
Some(FacePointIntersection::PointIsInsideFace)
} else {
None
}
}sourcepub fn color(&self) -> Color
pub fn color(&self) -> Color
Access the color of the face
Examples found in repository?
More examples
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
fn transform_with_cache(
self,
transform: &Transform,
objects: &mut Service<Objects>,
cache: &mut TransformCache,
) -> Self {
// Color does not need to be transformed.
let color = self.color();
let exterior = self
.exterior()
.clone()
.transform_with_cache(transform, objects, cache);
let interiors = self.interiors().cloned().map(|interior| {
interior.transform_with_cache(transform, objects, cache)
});
Self::new(exterior, interiors, color)
}12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
fn reverse(self, objects: &mut Service<Objects>) -> Self {
let mut cache = FullToPartialCache::default();
let exterior = Partial::from_full(
self.exterior().clone().reverse(objects),
&mut cache,
);
let interiors = self
.interiors()
.map(|cycle| {
Partial::from_full(cycle.clone().reverse(objects), &mut cache)
})
.collect::<Vec<_>>();
let face = PartialFace {
exterior,
interiors,
color: Some(self.color()),
};
face.build(objects).insert(objects)
}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
fn approx_with_cache(
self,
tolerance: impl Into<Tolerance>,
cache: &mut Self::Cache,
) -> Self::Approximation {
let tolerance = tolerance.into();
// Curved faces whose curvature is not fully defined by their edges
// are not supported yet. For that reason, we can fully ignore `face`'s
// `surface` field and just pass the edges to `Self::for_edges`.
//
// An example of a curved face that is supported, is the cylinder. Its
// curvature is fully defined be the edges (circles) that border it. The
// circle approximations are sufficient to triangulate the surface.
//
// An example of a curved face that is currently not supported, and thus
// doesn't need to be handled here, is a sphere. A spherical face would
// would need to provide its own approximation, as the edges that bound
// it have nothing to do with its curvature.
let exterior = self.exterior().approx_with_cache(tolerance, cache);
let mut interiors = BTreeSet::new();
for cycle in self.interiors() {
let cycle = cycle.approx_with_cache(tolerance, cache);
interiors.insert(cycle);
}
FaceApprox {
exterior,
interiors,
color: self.color(),
coord_handedness: self.coord_handedness(),
}
}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
fn sweep_with_cache(
self,
path: impl Into<Vector<3>>,
cache: &mut SweepCache,
objects: &mut Service<Objects>,
) -> Self::Swept {
let path = path.into();
let mut faces = Vec::new();
let is_negative_sweep = {
let u = match self.surface().geometry().u {
GlobalPath::Circle(_) => todo!(
"Sweeping from faces defined in round surfaces is not \
supported"
),
GlobalPath::Line(line) => line.direction(),
};
let v = self.surface().geometry().v;
let normal = u.cross(&v);
normal.dot(&path) < Scalar::ZERO
};
let bottom_face = {
if is_negative_sweep {
self.clone()
} else {
self.clone().reverse(objects)
}
};
faces.push(bottom_face);
let top_face = {
let mut face = self.clone().translate(path, objects);
if is_negative_sweep {
face = face.reverse(objects);
};
face
};
faces.push(top_face);
// Generate side faces
for cycle in self.all_cycles() {
for half_edge in cycle.half_edges() {
let half_edge = if is_negative_sweep {
half_edge.clone().reverse(objects)
} else {
half_edge.clone()
};
let face = (half_edge, self.color())
.sweep_with_cache(path, cache, objects);
faces.push(face);
}
}
let faces = faces.into_iter().map(Partial::from).collect();
PartialShell { faces }.build(objects).insert(objects)
}sourcepub fn coord_handedness(&self) -> Handedness
pub fn coord_handedness(&self) -> Handedness
Determine handed-ness of the face’s front-side coordinate system
A face is defined on a surface, which has a coordinate system. Since surfaces aren’t considered to have an orientation, their coordinate system can be considered to be left-handed or right-handed, depending on which side of the surface you’re looking at.
Faces do have an orientation, meaning they have definite front and back sides. The front side is the side, where the face’s exterior cycle is wound counter-clockwise.
Examples found in repository?
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
fn approx_with_cache(
self,
tolerance: impl Into<Tolerance>,
cache: &mut Self::Cache,
) -> Self::Approximation {
let tolerance = tolerance.into();
// Curved faces whose curvature is not fully defined by their edges
// are not supported yet. For that reason, we can fully ignore `face`'s
// `surface` field and just pass the edges to `Self::for_edges`.
//
// An example of a curved face that is supported, is the cylinder. Its
// curvature is fully defined be the edges (circles) that border it. The
// circle approximations are sufficient to triangulate the surface.
//
// An example of a curved face that is currently not supported, and thus
// doesn't need to be handled here, is a sphere. A spherical face would
// would need to provide its own approximation, as the edges that bound
// it have nothing to do with its curvature.
let exterior = self.exterior().approx_with_cache(tolerance, cache);
let mut interiors = BTreeSet::new();
for cycle in self.interiors() {
let cycle = cycle.approx_with_cache(tolerance, cache);
interiors.insert(cycle);
}
FaceApprox {
exterior,
interiors,
color: self.color(),
coord_handedness: self.coord_handedness(),
}
}Trait Implementations§
source§impl Approx for &Face
impl Approx for &Face
§type Approximation = FaceApprox
type Approximation = FaceApprox
§type Cache = CurveCache
type Cache = CurveCache
source§fn approx_with_cache(
self,
tolerance: impl Into<Tolerance>,
cache: &mut Self::Cache
) -> Self::Approximation
fn approx_with_cache(
self,
tolerance: impl Into<Tolerance>,
cache: &mut Self::Cache
) -> Self::Approximation
source§impl HasPartial for Face
impl HasPartial for Face
§type Partial = PartialFace
type Partial = PartialFace
source§impl Ord for Face
impl Ord for Face
source§impl PartialEq<Face> for Face
impl PartialEq<Face> for Face
source§impl PartialOrd<Face> for Face
impl PartialOrd<Face> for Face
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
self and other) and is used by the <=
operator. Read moresource§impl TransformObject for Face
impl TransformObject for Face
source§fn transform_with_cache(
self,
transform: &Transform,
objects: &mut Service<Objects>,
cache: &mut TransformCache
) -> Self
fn transform_with_cache(
self,
transform: &Transform,
objects: &mut Service<Objects>,
cache: &mut TransformCache
) -> Self
source§fn transform(self, transform: &Transform, objects: &mut Service<Objects>) -> Self
fn transform(self, transform: &Transform, objects: &mut Service<Objects>) -> Self
source§impl Validate for Face
impl Validate for Face
§type Error = FaceValidationError
type Error = FaceValidationError
source§fn validate_with_config(&self, _: &ValidationConfig) -> Result<(), Self::Error>
fn validate_with_config(&self, _: &ValidationConfig) -> Result<(), Self::Error>
impl Eq for Face
impl StructuralEq for Face
impl StructuralPartialEq for Face
Auto Trait Implementations§
impl !RefUnwindSafe for Face
impl Send for Face
impl Sync for Face
impl Unpin for Face
impl !UnwindSafe for Face
Blanket Implementations§
§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
§fn into_any(self: Box<T, Global>) -> Box<dyn Any + 'static, Global>
fn into_any(self: Box<T, Global>) -> Box<dyn Any + 'static, Global>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can
then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any + 'static>
Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be
further downcast into Rc<ConcreteType> where ConcreteType implements Trait.§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.§impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
impl<SS, SP> SupersetOf<SS> for SPwhere
SS: SubsetOf<SP>,
§fn to_subset(&self) -> Option<SS>
fn to_subset(&self) -> Option<SS>
self from the equivalent element of its
superset. Read more§fn is_in_subset(&self) -> bool
fn is_in_subset(&self) -> bool
self is actually part of its subset T (and can be converted to it).§fn to_subset_unchecked(&self) -> SS
fn to_subset_unchecked(&self) -> SS
self.to_subset but without any property checks. Always succeeds.§fn from_subset(element: &SS) -> SP
fn from_subset(element: &SS) -> SP
self to the equivalent element of its superset.