use super::functions::*;
use super::functions::{FaceId, HalfEdgeId, Point3, VertexId};
#[derive(Debug, Clone)]
pub struct VoronoiCell2D {
pub site: usize,
pub circumcenters: Vec<Point2>,
}
#[derive(Debug, Clone)]
pub struct ArrangementFace {
pub vertex_indices: Vec<usize>,
}
#[derive(Debug, Clone, Default)]
pub struct LineArrangement {
pub lines: Vec<Line2D>,
pub vertices: Vec<ArrangementVertex>,
}
impl LineArrangement {
pub fn build(lines: &[Line2D]) -> Self {
let n = lines.len();
let mut vertices: Vec<ArrangementVertex> = Vec::new();
let merge_eps = 1e-9;
for i in 0..n {
for j in (i + 1)..n {
if let Some(pt) = line_intersect_2d(&lines[i], &lines[j]) {
let existing = vertices
.iter_mut()
.find(|v| v.point.dist_sq(pt) < merge_eps);
if let Some(v) = existing {
if !v.line_indices.contains(&i) {
v.line_indices.push(i);
}
if !v.line_indices.contains(&j) {
v.line_indices.push(j);
}
} else {
vertices.push(ArrangementVertex {
point: pt,
line_indices: vec![i, j],
});
}
}
}
}
Self {
lines: lines.to_vec(),
vertices,
}
}
pub fn num_vertices(&self) -> usize {
self.vertices.len()
}
pub fn euler_face_count(&self) -> usize {
let n = self.lines.len();
n * (n - 1) / 2 + n + 1
}
pub fn vertices_on_line(&self, line_idx: usize) -> Vec<usize> {
let mut idxs: Vec<usize> = self
.vertices
.iter()
.enumerate()
.filter(|(_, v)| v.line_indices.contains(&line_idx))
.map(|(i, _)| i)
.collect();
let l = &self.lines[line_idx];
let dir = Point2::new(-l.b, l.a);
idxs.sort_by(|&a, &b| {
let ta = dir.dot(self.vertices[a].point);
let tb = dir.dot(self.vertices[b].point);
ta.partial_cmp(&tb).unwrap_or(std::cmp::Ordering::Equal)
});
idxs
}
}
#[derive(Debug, Clone)]
pub struct HEFace {
pub half_edge: HalfEdgeId,
}
#[derive(Debug, Clone)]
pub struct HalfEdge {
pub origin: VertexId,
pub twin: HalfEdgeId,
pub next: HalfEdgeId,
pub prev: HalfEdgeId,
pub face: Option<FaceId>,
}
#[derive(Debug, Clone, Default)]
pub struct VisibilityGraph {
pub nodes: Vec<Point2>,
pub edges: Vec<Vec<usize>>,
}
impl VisibilityGraph {
pub fn build(obstacles: &[Obstacle2D]) -> Self {
let mut nodes: Vec<Point2> = Vec::new();
for obs in obstacles {
for &v in &obs.vertices {
nodes.push(v);
}
}
let all_edges: Vec<(Point2, Point2)> = obstacles.iter().flat_map(|o| o.edges()).collect();
let n = nodes.len();
let mut edges = vec![Vec::new(); n];
for i in 0..n {
for j in 0..n {
if i == j {
continue;
}
if segment_visible(nodes[i], nodes[j], &all_edges) {
edges[i].push(j);
}
}
}
Self { nodes, edges }
}
pub fn add_query_point(&mut self, pt: Point2, obstacles: &[Obstacle2D]) {
let all_edges: Vec<(Point2, Point2)> = obstacles.iter().flat_map(|o| o.edges()).collect();
let n = self.nodes.len();
let new_id = n;
self.nodes.push(pt);
self.edges.push(Vec::new());
for i in 0..n {
if segment_visible(pt, self.nodes[i], &all_edges) {
self.edges[new_id].push(i);
self.edges[i].push(new_id);
}
}
}
pub fn num_nodes(&self) -> usize {
self.nodes.len()
}
pub fn shortest_path(&self, src: usize, dst: usize) -> Option<Vec<usize>> {
use std::cmp::Reverse;
use std::collections::BinaryHeap;
let n = self.nodes.len();
let mut dist = vec![f64::INFINITY; n];
let mut prev = vec![usize::MAX; n];
dist[src] = 0.0;
let mut heap: BinaryHeap<Reverse<(u64, usize)>> = BinaryHeap::new();
heap.push(Reverse((0, src)));
while let Some(Reverse((d_raw, u))) = heap.pop() {
let d = d_raw as f64 / 1e9;
if d > dist[u] + 1e-12 {
continue;
}
if u == dst {
break;
}
for &v in &self.edges[u] {
let nd = dist[u] + self.nodes[u].dist(self.nodes[v]);
if nd < dist[v] - 1e-12 {
dist[v] = nd;
prev[v] = u;
heap.push(Reverse(((nd * 1e9) as u64, v)));
}
}
}
if dist[dst].is_infinite() {
return None;
}
let mut path = Vec::new();
let mut cur = dst;
while cur != usize::MAX {
path.push(cur);
cur = prev[cur];
}
path.reverse();
Some(path)
}
}
#[derive(Debug, Clone)]
pub struct HEVertex {
pub position: Point3,
pub half_edge: Option<HalfEdgeId>,
}
#[derive(Debug, Clone, Copy)]
pub struct Line2D {
pub a: f64,
pub b: f64,
pub c: f64,
}
impl Line2D {
pub fn through(p: Point2, q: Point2) -> Self {
let a = q.y - p.y;
let b = p.x - q.x;
let c = a * p.x + b * p.y;
Self { a, b, c }
}
pub fn signed_dist(&self, p: Point2) -> f64 {
let norm = (self.a * self.a + self.b * self.b).sqrt();
if norm < 1e-15 {
return 0.0;
}
(self.a * p.x + self.b * p.y - self.c) / norm
}
pub fn side(&self, p: Point2) -> f64 {
self.a * p.x + self.b * p.y - self.c
}
}
#[derive(Debug, Clone)]
pub struct Trapezoid {
pub x_left: f64,
pub x_right: f64,
pub bottom_seg: usize,
pub top_seg: usize,
pub face: Option<usize>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DelaunayTri {
pub a: usize,
pub b: usize,
pub c: usize,
}
impl DelaunayTri {
pub fn new(a: usize, b: usize, c: usize) -> Self {
Self { a, b, c }
}
}
#[derive(Debug, Clone, Copy)]
pub struct Segment2D {
pub left: Point2,
pub right: Point2,
}
impl Segment2D {
pub fn new(a: Point2, b: Point2) -> Self {
if a.x <= b.x {
Self { left: a, right: b }
} else {
Self { left: b, right: a }
}
}
pub fn y_at(&self, x: f64) -> Option<f64> {
let dx = self.right.x - self.left.x;
if dx.abs() < 1e-15 {
return None;
}
let t = (x - self.left.x) / dx;
if !(0.0..=1.0).contains(&t) {
return None;
}
Some(self.left.y + t * (self.right.y - self.left.y))
}
}
#[derive(Debug, Clone, Default)]
pub struct SlabPointLocator {
pub slab_xs: Vec<f64>,
pub slab_segments: Vec<Vec<usize>>,
pub segments: Vec<Segment2D>,
pub slab_faces: Vec<Vec<Option<usize>>>,
}
impl SlabPointLocator {
pub fn build(segments: &[Segment2D], _face_labels: &[Option<usize>]) -> Self {
let mut xs: Vec<f64> = Vec::new();
for s in segments {
xs.push(s.left.x);
xs.push(s.right.x);
}
xs.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
xs.dedup_by(|a, b| (*a - *b).abs() < 1e-12);
let n_slabs = if xs.len() >= 2 { xs.len() - 1 } else { 0 };
let mut slab_segments = vec![Vec::new(); n_slabs];
let mut slab_faces = vec![Vec::new(); n_slabs];
for slab_i in 0..n_slabs {
let x_mid = (xs[slab_i] + xs[slab_i + 1]) / 2.0;
let mut active: Vec<(f64, usize)> = segments
.iter()
.enumerate()
.filter_map(|(idx, s)| s.y_at(x_mid).map(|y| (y, idx)))
.collect();
active.sort_by(|a, b| b.0.partial_cmp(&a.0).unwrap_or(std::cmp::Ordering::Equal));
slab_segments[slab_i] = active.iter().map(|(_, idx)| *idx).collect();
slab_faces[slab_i] = active.iter().map(|_| None).collect();
}
Self {
slab_xs: xs,
slab_segments,
segments: segments.to_vec(),
slab_faces,
}
}
pub fn locate(&self, p: Point2) -> Option<usize> {
let slab_i = self
.slab_xs
.partition_point(|&x| x <= p.x)
.saturating_sub(1);
if slab_i >= self.slab_segments.len() {
return None;
}
for &seg_idx in &self.slab_segments[slab_i] {
if let Some(y) = self.segments[seg_idx].y_at(p.x)
&& y >= p.y
{
return Some(seg_idx);
}
}
None
}
}
#[derive(Debug, Clone)]
pub struct ConvexFace3D {
pub verts: [usize; 3],
pub normal: Point3,
}
#[derive(Debug, Clone)]
pub struct ArrangementVertex {
pub point: Point2,
pub line_indices: Vec<usize>,
}
#[derive(Debug, Clone)]
pub struct Obstacle2D {
pub vertices: Vec<Point2>,
}
impl Obstacle2D {
pub fn new(vertices: Vec<Point2>) -> Self {
Self { vertices }
}
pub fn edges(&self) -> Vec<(Point2, Point2)> {
let n = self.vertices.len();
(0..n)
.map(|i| (self.vertices[i], self.vertices[(i + 1) % n]))
.collect()
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Point2 {
pub x: f64,
pub y: f64,
}
impl Point2 {
pub fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
pub fn scale(self, t: f64) -> Self {
Self::new(self.x * t, self.y * t)
}
pub fn cross(self, other: Self) -> f64 {
self.x * other.y - self.y * other.x
}
pub fn dot(self, other: Self) -> f64 {
self.x * other.x + self.y * other.y
}
pub fn dist_sq(self, other: Self) -> f64 {
let d = self - other;
d.dot(d)
}
pub fn dist(self, other: Self) -> f64 {
self.dist_sq(other).sqrt()
}
pub fn cross2(p0: Self, p1: Self, p2: Self) -> f64 {
(p1 - p0).cross(p2 - p0)
}
}
impl std::ops::Sub for Point2 {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self::new(self.x - other.x, self.y - other.y)
}
}
impl std::ops::Add for Point2 {
type Output = Self;
fn add(self, other: Self) -> Self {
Self::new(self.x + other.x, self.y + other.y)
}
}
#[derive(Debug, Clone, Default)]
pub struct HalfEdgeMesh {
pub half_edges: Vec<HalfEdge>,
pub vertices: Vec<HEVertex>,
pub faces: Vec<HEFace>,
}
impl HalfEdgeMesh {
pub fn new() -> Self {
Self::default()
}
pub fn add_vertex(&mut self, pos: Point3) -> VertexId {
let id = self.vertices.len();
self.vertices.push(HEVertex {
position: pos,
half_edge: None,
});
id
}
pub fn add_triangle(&mut self, v0: VertexId, v1: VertexId, v2: VertexId) -> FaceId {
let face_id = self.faces.len();
let he0 = self.half_edges.len();
let he1 = he0 + 1;
let he2 = he0 + 2;
self.half_edges.push(HalfEdge {
origin: v0,
twin: usize::MAX,
next: he1,
prev: he2,
face: Some(face_id),
});
self.half_edges.push(HalfEdge {
origin: v1,
twin: usize::MAX,
next: he2,
prev: he0,
face: Some(face_id),
});
self.half_edges.push(HalfEdge {
origin: v2,
twin: usize::MAX,
next: he0,
prev: he1,
face: Some(face_id),
});
self.faces.push(HEFace { half_edge: he0 });
if self.vertices[v0].half_edge.is_none() {
self.vertices[v0].half_edge = Some(he0);
}
if self.vertices[v1].half_edge.is_none() {
self.vertices[v1].half_edge = Some(he1);
}
if self.vertices[v2].half_edge.is_none() {
self.vertices[v2].half_edge = Some(he2);
}
face_id
}
pub fn face_vertices(&self, fid: FaceId) -> Vec<VertexId> {
let start = self.faces[fid].half_edge;
let mut verts = Vec::new();
let mut cur = start;
loop {
verts.push(self.half_edges[cur].origin);
cur = self.half_edges[cur].next;
if cur == start {
break;
}
}
verts
}
pub fn vertex_faces(&self, vid: VertexId) -> Vec<FaceId> {
let start_he = match self.vertices[vid].half_edge {
Some(h) => h,
None => return vec![],
};
let mut faces = Vec::new();
let mut cur = start_he;
loop {
if let Some(f) = self.half_edges[cur].face {
faces.push(f);
}
let twin = self.half_edges[cur].twin;
if twin == usize::MAX {
break;
}
cur = self.half_edges[twin].next;
if cur == start_he {
break;
}
}
faces
}
pub fn build_twin_links(&mut self) {
use std::collections::HashMap;
let n = self.half_edges.len();
let mut edge_map: HashMap<(usize, usize), usize> = HashMap::new();
for i in 0..n {
let origin = self.half_edges[i].origin;
let dest = self.half_edges[self.half_edges[i].next].origin;
edge_map.insert((origin, dest), i);
}
for i in 0..n {
let origin = self.half_edges[i].origin;
let dest = self.half_edges[self.half_edges[i].next].origin;
if let Some(&twin) = edge_map.get(&(dest, origin)) {
self.half_edges[i].twin = twin;
}
}
}
pub fn num_faces(&self) -> usize {
self.faces.len()
}
pub fn num_vertices(&self) -> usize {
self.vertices.len()
}
pub fn face_normal(&self, fid: FaceId) -> Point3 {
let verts = self.face_vertices(fid);
if verts.len() < 3 {
return [0.0; 3];
}
let p0 = self.vertices[verts[0]].position;
let p1 = self.vertices[verts[1]].position;
let p2 = self.vertices[verts[2]].position;
let e1 = sub3(p1, p0);
let e2 = sub3(p2, p0);
normalize3(cross3(e1, e2))
}
pub fn face_centroid(&self, fid: FaceId) -> Point3 {
let verts = self.face_vertices(fid);
let n = verts.len() as f64;
let mut acc = [0.0f64; 3];
for vid in &verts {
let p = self.vertices[*vid].position;
acc[0] += p[0];
acc[1] += p[1];
acc[2] += p[2];
}
[acc[0] / n, acc[1] / n, acc[2] / n]
}
}
#[derive(Debug, Clone)]
pub struct ConvexHull3D {
pub vertices: Vec<Point3>,
pub faces: Vec<ConvexFace3D>,
}
impl ConvexHull3D {
pub fn volume(&self) -> f64 {
let mut vol = 0.0f64;
for face in &self.faces {
let a = self.vertices[face.verts[0]];
let b = self.vertices[face.verts[1]];
let c = self.vertices[face.verts[2]];
vol += dot3(a, cross3(b, c));
}
(vol / 6.0).abs()
}
pub fn surface_area(&self) -> f64 {
let mut area = 0.0f64;
for face in &self.faces {
let a = self.vertices[face.verts[0]];
let b = self.vertices[face.verts[1]];
let c = self.vertices[face.verts[2]];
let ab = sub3(b, a);
let ac = sub3(c, a);
area += 0.5 * mag3(cross3(ab, ac));
}
area
}
}
#[derive(Debug, Clone)]
pub struct ArtGalleryResult {
pub guards: Vec<usize>,
pub covered: Vec<bool>,
}