Add SurfaceGeom::triangle_at

This commit is contained in:
Hanno Braun 2024-07-30 19:43:55 +02:00
parent e75225a0de
commit ee755ac5e8

View File

@ -1,6 +1,8 @@
//! The geometry that defines a surface
use fj_math::{Line, Point, Transform, Vector};
use fj_math::{Line, Point, Scalar, Transform, Triangle, Vector};
use crate::algorithms::approx::{PathApproxParams, Tolerance};
use super::GlobalPath;
@ -26,6 +28,92 @@ pub enum SurfaceGeom {
}
impl SurfaceGeom {
/// # Return the triangle at the provided point on the surface
///
/// Select a triangle of the surface's triangle mesh representation, the one
/// at the provided surface point. Return that triangle, as well as the
/// barycentric coordinates of the provided point on the triangle.
///
/// ## Triangle Size and Validity
///
/// If a surface is curved along both axes, the triangle's size is chosen
/// such, that it approximates the surface, with the maximum allowed
/// deviation of the actual surface defined by the provided tolerance
/// argument.
///
/// Otherwise, the size of the returned triangle is at least partially
/// arbitrary. Take the extreme case of a plane: Since it is not curved at
/// all, the returned triangle can be arbitrarily large.
///
/// However, since surfaces are infinite, and we can't represent infinite
/// triangles, there is no sensible upper bound for the size. Instead, to
/// prevent an arbitrary choice for the size of triangles, which would imply
/// properties of the surface that are not true, and might therefore be
/// confusing, the triangles returned by this function have a length of zero
/// along axes that do not require approximation.
///
/// The most extreme case would be a plane, for which the returned triangle
/// is collapsed to a point. For a cylinder, the triangle would have the
/// appropriate width to approximate the curved axis given the provided
/// tolerance, while having zero height.
///
/// ## Implementation Note
///
/// At the time this was written, there was no dedicated type to represent
/// barycentric coordinates. Nor any other code that used them, I think.
///
/// If this changes, and a special type for barycentric coordinates is
/// added, it would make sense to return that here.
pub fn triangle_at(
&self,
point_surface: impl Into<Point<2>>,
tolerance: impl Into<Tolerance>,
) -> (Triangle<3>, [Scalar; 3]) {
let point_surface = point_surface.into();
let Self::Basic { u, v } = self;
match u {
GlobalPath::Circle(circle) => {
let params = PathApproxParams::for_circle(circle, tolerance);
let a = point_surface.u - params.increment();
let b = point_surface.u + params.increment();
let c = a; // triangle is degenerate, as per function docs
let triangle_points_in_circle_space = [a, b, c];
let triangle_points_in_global_space =
triangle_points_in_circle_space
.map(|point_circle| {
circle.point_from_circle_coords([point_circle])
})
.map(|point_global| {
point_global + *v * point_surface.v
});
let triangle = Triangle::from(triangle_points_in_global_space);
let barycentric_coords = [0.5, 0.5, 0.0].map(Into::into);
(triangle, barycentric_coords)
}
GlobalPath::Line(line) => {
let a = line.direction();
let b = *v;
let point_global =
line.origin() + a * point_surface.u + b * point_surface.v;
// We don't need to approximate a plane, so our triangle can be
// arbitrarily large or small. Here we choose the smallest
// possible size (it is collapsed to a point), as per the
// documentation of this function.
let triangle = Triangle::from([point_global; 3]);
let barycentric_coords = [1. / 3.; 3].map(Into::into);
(triangle, barycentric_coords)
}
}
}
/// Convert a point in surface coordinates to model coordinates
pub fn point_from_surface_coords(
&self,