Skip to main content

geop_core_topology/
uv_loop.rs

1//! A face's loop in its surface's parameter plane: the signed area it
2//! encloses, integrated exactly from its pcurves ([`loop_signed_area`]),
3//! and a polygon sampled along them, for the rasterizer to triangulate
4//! ([`sample_loop_to_polygon`]).
5//!
6//! Ask the area which way a loop runs, never a sampled polygon: the gap
7//! between a polygon and the pcurves is part of no enclosure (see
8//! [`geop_core_geometry::nurb_curve::loop_signed_area`]).
9
10use geop_core_geometry::nurb_curve;
11use geop_core_math::{geop_error::GeopResult, scalars::Scalar, vector::Vector2};
12
13use crate::{CoedgeId, Curve2, Model};
14
15/// The signed area, in `(u, v)`, of the loop anchored at `first`: positive
16/// counter-clockwise, negative clockwise.
17pub fn loop_signed_area(model: &Model, first: CoedgeId) -> GeopResult<Scalar> {
18    let pcurves = model
19        .iterate_loop_coedges(first)
20        .map(|coedge| Ok(&model.get_coedge(coedge)?.pcurve))
21        .collect::<GeopResult<Vec<_>>>()?;
22    nurb_curve::loop_signed_area(pcurves)
23}
24
25/// Sample the pcurves of the loop anchored at `first` (in traversal order)
26/// into a closed `(u, v)` polygon, `n` samples per coedge (the last sample of
27/// each coedge is dropped, since it coincides with the next coedge's first
28/// sample).
29pub fn sample_loop_to_polygon(
30    model: &Model,
31    first: CoedgeId,
32    n: usize,
33) -> GeopResult<Vec<Vector2>> {
34    sample_pcurves_to_polygon(
35        model
36            .iterate_loop_coedges(first)
37            .map(|coedge| &model.coedges[&coedge].pcurve),
38        n - 1,
39    )
40}
41
42/// Sample `pcurves`, a loop's in order, into a closed `(u, v)` polygon: `n`
43/// points evenly along each one's domain, its end left out, as the next
44/// one starts there.
45pub fn sample_pcurves_to_polygon<'a>(
46    pcurves: impl IntoIterator<Item = &'a Curve2>,
47    n: usize,
48) -> GeopResult<Vec<Vector2>> {
49    let mut poly = Vec::new();
50    for pcurve in pcurves {
51        let (t0, t1) = pcurve.domain();
52        for i in 0..n {
53            let frac = Scalar::from_ratio(i as i64, n as i64)?;
54            let t = t0.add(t1.sub(t0).mul(frac));
55            poly.push(pcurve.evaluate(t)?);
56        }
57    }
58    Ok(poly)
59}