1use axiolid_core::{Point2, Tolerance};
21use axiolid_exact::{certify, Arith, SignExpr};
22use axiolid_guarantees::Sign;
23
24use crate::arc::ArcRing;
25use crate::arrangement::ArcArrangement;
26use crate::region::Region;
27use crate::OverlayError;
28
29#[derive(Debug, Clone, PartialEq, Eq)]
31#[non_exhaustive]
32pub enum VisibilityError {
33 NotInside,
35 Overlay(OverlayError),
37}
38
39impl From<OverlayError> for VisibilityError {
40 fn from(error: OverlayError) -> Self {
41 Self::Overlay(error)
42 }
43}
44
45impl Region {
46 pub fn visibility_polygon(
55 &self,
56 viewpoint: Point2,
57 tolerance: Tolerance,
58 ) -> Result<Self, VisibilityError> {
59 if !viewpoint.is_finite() || !strictly_inside(self, viewpoint) {
60 return Err(VisibilityError::NotInside);
61 }
62 let edges: Vec<(Point2, Point2)> = self
63 .boundary_rings()
64 .iter()
65 .flat_map(|r| {
66 let n = r.points.len();
67 (0..n).map(move |i| (r.points[i], r.points[(i + 1) % n]))
68 })
69 .filter(|(a, b)| a != b)
70 .collect();
71 let v = viewpoint;
72 let mut around: Vec<Point2> = Vec::new();
74 for &(a, _) in &edges {
75 let at = around.partition_point(|&w| before(v, w, a));
76 if at < around.len() && same_direction(v, around[at], a) {
77 continue;
78 }
79 around.insert(at, a);
80 }
81 let k = around.len();
82 let mut ring: Vec<Point2> = Vec::with_capacity(2 * k);
83 for i in 0..k {
84 let (w1, w2) = (around[i], around[(i + 1) % k]);
85 let Some(edge) = nearest(v, w1, w2, &edges) else {
86 return Err(VisibilityError::NotInside);
88 };
89 for w in [w1, w2] {
90 let p = on_ray(v, w, edge);
91 if ring.last() != Some(&p) {
92 ring.push(p);
93 }
94 }
95 }
96 while ring.len() > 1 && ring.first() == ring.last() {
97 ring.pop();
98 }
99 if ring.len() < 3 {
100 return Ok(Self::empty());
101 }
102 let arrangement = ArcArrangement::new(&[ArcRing::from_points(&ring)], tolerance)?;
103 Ok(crate::minkowski::region_of(
104 &arrangement,
105 |f| f[0],
106 true,
107 tolerance,
108 )?)
109 }
110}
111
112fn on_ray(v: Point2, w: Point2, (a, b): (Point2, Point2)) -> Point2 {
115 for end in [a, b] {
116 if sign(&Orient { a: v, b: w, c: end }) == Sign::Zero {
117 return end;
118 }
119 }
120 let (d, e) = (w - v, b - a);
121 let t = (a - v).perp_dot(e) / d.perp_dot(e);
122 v + d * t
123}
124
125fn nearest(
128 v: Point2,
129 w1: Point2,
130 w2: Point2,
131 edges: &[(Point2, Point2)],
132) -> Option<(Point2, Point2)> {
133 let mut best: Option<(Point2, Point2)> = None;
134 for &edge in edges {
135 if sign(&Hits { v, w1, w2, edge }) != Sign::Positive {
136 continue;
137 }
138 if best.is_none_or(|b| {
139 sign(&Nearer {
140 v,
141 w1,
142 w2,
143 near: edge,
144 far: b,
145 }) == Sign::Positive
146 }) {
147 best = Some(edge);
148 }
149 }
150 best
151}
152
153fn before(v: Point2, a: Point2, b: Point2) -> bool {
156 let half = |p: Point2| u8::from(!(p.y > v.y || (p.y == v.y && p.x > v.x)));
157 let (ha, hb) = (half(a), half(b));
158 if ha != hb {
159 return ha < hb;
160 }
161 sign(&Orient { a: v, b: a, c: b }) == Sign::Positive
162}
163
164fn same_direction(v: Point2, a: Point2, b: Point2) -> bool {
165 !before(v, a, b) && !before(v, b, a)
166}
167
168fn strictly_inside(region: &Region, p: Point2) -> bool {
171 let mut winding = 0usize;
172 for ring in region.boundary_rings() {
173 let n = ring.points.len();
174 let mut crossings = 0usize;
175 for i in 0..n {
176 let (a, b) = (ring.points[i], ring.points[(i + 1) % n]);
177 let s = sign(&Orient { a, b, c: p });
178 let between = p.x >= a.x.min(b.x)
179 && p.x <= a.x.max(b.x)
180 && p.y >= a.y.min(b.y)
181 && p.y <= a.y.max(b.y);
182 if s == Sign::Zero && between {
183 return false;
184 }
185 let upward = a.y <= p.y && b.y > p.y;
187 let downward = b.y <= p.y && a.y > p.y;
188 if (upward && s == Sign::Positive) || (downward && s == Sign::Negative) {
189 crossings += 1;
190 }
191 }
192 winding += crossings % 2;
193 }
194 winding % 2 == 1
195}
196
197fn sign<E: SignExpr>(e: &E) -> Sign {
199 certify(e).unwrap_or(Sign::Zero)
200}
201
202struct Orient {
203 a: Point2,
204 b: Point2,
205 c: Point2,
206}
207
208impl SignExpr for Orient {
209 fn sign_in<T: Arith>(&self) -> Option<Sign> {
210 let f = T::from_f64;
211 let (ux, uy) = (f(self.b.x).sub(&f(self.a.x)), f(self.b.y).sub(&f(self.a.y)));
212 let (vx, vy) = (f(self.c.x).sub(&f(self.a.x)), f(self.c.y).sub(&f(self.a.y)));
213 ux.mul(&vy).sub(&uy.mul(&vx)).sign()
214 }
215}
216
217fn diff<T: Arith>(p: Point2, q: Point2) -> [T; 2] {
219 [
220 T::from_f64(p.x).sub(&T::from_f64(q.x)),
221 T::from_f64(p.y).sub(&T::from_f64(q.y)),
222 ]
223}
224
225fn cross<T: Arith>(a: &[T; 2], b: &[T; 2]) -> T {
226 a[0].mul(&b[1]).sub(&a[1].mul(&b[0]))
227}
228
229fn middle<T: Arith>(v: Point2, w1: Point2, w2: Point2) -> [T; 2] {
231 let (p, q) = (diff::<T>(w1, v), diff::<T>(w2, v));
232 [p[0].add(&q[0]), p[1].add(&q[1])]
233}
234
235fn parameter<T: Arith>(v: Point2, u: &[T; 2], (a, b): (Point2, Point2)) -> (T, T) {
238 let e = diff::<T>(b, a);
239 (cross(&diff::<T>(a, v), &e), cross(u, &e))
240}
241
242struct Hits {
245 v: Point2,
246 w1: Point2,
247 w2: Point2,
248 edge: (Point2, Point2),
249}
250
251impl SignExpr for Hits {
252 fn sign_in<T: Arith>(&self) -> Option<Sign> {
253 let u = middle::<T>(self.v, self.w1, self.w2);
254 let (a, b) = self.edge;
255 let sa = cross(&u, &diff::<T>(a, self.v)).sign()?;
256 let sb = cross(&u, &diff::<T>(b, self.v)).sign()?;
257 let straddles = matches!(
258 (sa, sb),
259 (Sign::Positive, Sign::Negative) | (Sign::Negative, Sign::Positive)
260 );
261 if !straddles {
262 return Some(Sign::Negative);
263 }
264 let (n, d) = parameter::<T>(self.v, &u, self.edge);
265 let (sn, sd) = (n.sign()?, d.sign()?);
266 Some(if sn != Sign::Zero && sn == sd {
267 Sign::Positive
268 } else {
269 Sign::Negative
270 })
271 }
272}
273
274struct Nearer {
277 v: Point2,
278 w1: Point2,
279 w2: Point2,
280 near: (Point2, Point2),
281 far: (Point2, Point2),
282}
283
284impl SignExpr for Nearer {
285 fn sign_in<T: Arith>(&self) -> Option<Sign> {
286 let u = middle::<T>(self.v, self.w1, self.w2);
287 let (n1, d1) = parameter::<T>(self.v, &u, self.near);
288 let (n2, d2) = parameter::<T>(self.v, &u, self.far);
289 let gap = n2.mul(&d1).sub(&n1.mul(&d2)).sign()?;
291 let same = d1.sign()? == d2.sign()?;
292 Some(match (gap, same) {
293 (Sign::Zero, _) => Sign::Zero,
294 (s, true) => s,
295 (Sign::Positive, false) => Sign::Negative,
296 (_, false) => Sign::Positive,
297 })
298 }
299}