brep_kernel/geometry/
polygon.rs1use crate::{Vec2, Vec3};
2
3pub(crate) fn newell_normal(points: &[Vec3]) -> Vec3 {
6 let mut normal = Vec3::default();
7 for index in 0..points.len() {
8 let point = points[index];
9 let next = points[(index + 1) % points.len()];
10 normal.x += (point.y - next.y) * (point.z + next.z);
11 normal.y += (point.z - next.z) * (point.x + next.x);
12 normal.z += (point.x - next.x) * (point.y + next.y);
13 }
14 normal
15}
16
17
18#[inline]
20pub fn crosses_ray(p: [f64; 2], a: [f64; 2], b: [f64; 2]) -> bool {
21 if (a[1] > p[1]) != (b[1] > p[1]) {
22 let t = (p[1] - a[1]) / (b[1] - a[1]);
23 let x = a[0] + t * (b[0] - a[0]);
24 x > p[0]
25 } else {
26 false
27 }
28}
29
30#[inline]
32pub fn parity_inside(p: [f64; 2], count: usize, uv: impl Fn(usize) -> [f64; 2]) -> bool {
33 let mut inside = false;
34 for offset in 0..count {
35 if crosses_ray(p, uv(offset), uv((offset + 1) % count)) {
36 inside = !inside;
37 }
38 }
39 inside
40}
41
42#[derive(Clone, Copy, Debug, PartialEq)]
43pub enum PolygonClass {
44 Inside,
45 Outside,
46 Boundary,
47}
48
49
50#[inline]
52pub fn classify(point: Vec2, polygon: &[Vec2], tolerance: f64, segment_guard: f64) -> PolygonClass {
53 for index in 0..polygon.len() {
54 if point.distance_to_segment(polygon[index], polygon[(index + 1) % polygon.len()], segment_guard) <= tolerance {
55 return PolygonClass::Boundary;
56 }
57 }
58 if parity_inside([point.x, point.y], polygon.len(), |i| [polygon[i].x, polygon[i].y]) {
59 PolygonClass::Inside
60 } else {
61 PolygonClass::Outside
62 }
63}
64
65#[inline]
67pub fn area_term(a: [f64; 2], b: [f64; 2]) -> f64 {
68 a[0] * b[1] - b[0] * a[1]
69}
70
71#[inline]
72pub fn twice_signed_area_2d(count: usize, uv: impl Fn(usize) -> [f64; 2]) -> f64 {
73 let mut area = 0.0;
74 for index in 0..count {
75 area += area_term(uv(index), uv((index + 1) % count));
76 }
77 area
78}
79
80#[inline]
81pub fn signed_area_2d(count: usize, uv: impl Fn(usize) -> [f64; 2]) -> f64 {
82 twice_signed_area_2d(count, uv) * 0.5
83}
84