geop_core_math/polygon.rs
1//! Generic 2-D polygon area — no topology or rendering dependency, so it
2//! lives here rather than in `geop-ops-rasterize`: `geop-core-topology`'s
3//! own edit code (`splice_edge_into_face`'s loop-orientation check) needs
4//! it too, and topology sits below rasterize in the dependency order.
5
6use crate::{scalars::Scalar, vector::Vector2};
7
8/// Twice the signed area of `poly` (shoelace formula): positive for CCW,
9/// negative for CW.
10fn signed_area2<S: Scalar>(poly: &[Vector2<S>]) -> S {
11 let n = poly.len();
12 let mut sum = S::ZERO;
13 for i in 0..n {
14 let j = (i + 1) % n;
15 sum = sum.add(poly[i][0].mul(poly[j][1]).sub(poly[j][0].mul(poly[i][1])));
16 }
17 sum
18}
19
20/// Signed area of `poly` (positive for CCW, negative for CW).
21pub fn polygon_signed_area<S: Scalar>(poly: &[Vector2<S>]) -> S {
22 signed_area2(poly).div(S::TWO).unwrap_or(S::ZERO)
23}