1use glam::DVec2;
-11use slotmap::{new_key_type, Key, SlotMap};
-12
-13use crate::error::SubdivisionError;
-14use crate::geometry::{segments_properly_intersect, signed_area, EPS_GEOM};
-15
-16new_key_type! {
-17 pub struct NodeId;
-19 pub struct RoadId;
-21}
-22
-23new_key_type! {
-24 pub(crate) struct HalfEdgeId;
-25 pub(crate) struct FaceId;
-26}
-27
-28#[derive(Debug, Clone)]
-29#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-30pub(crate) struct Node {
-31 pub pos: DVec2,
-32 pub outgoing: Vec<HalfEdgeId>,
-35}
-36
-37#[derive(Debug, Clone)]
-38#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-39pub(crate) struct Road {
-40 pub a: NodeId,
-41 pub b: NodeId,
-42 pub half_a_to_b: HalfEdgeId,
-43 pub half_b_to_a: HalfEdgeId,
-44}
-45
-46#[derive(Debug, Clone, Copy)]
-47#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-48pub(crate) struct HalfEdge {
-49 pub origin: NodeId,
-50 pub twin: HalfEdgeId,
-51 pub next: HalfEdgeId,
-52 pub prev: HalfEdgeId,
-53 pub face: Option<FaceId>,
-54 pub road: RoadId,
-55}
-56
-57#[derive(Debug, Clone, Copy)]
-58#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-59pub(crate) struct Face {
-60 pub boundary: HalfEdgeId,
-61 pub is_exterior: bool,
-62}
-63
-64#[derive(Debug, Clone, Default)]
-67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
-68pub struct RoadGraph {
-69 pub(crate) nodes: SlotMap<NodeId, Node>,
-70 pub(crate) roads: SlotMap<RoadId, Road>,
-71 pub(crate) half_edges: SlotMap<HalfEdgeId, HalfEdge>,
-72 pub(crate) faces: SlotMap<FaceId, Face>,
-73 pub(crate) topology_valid: bool,
-75}
-76
-77impl RoadGraph {
-78 #[must_use]
-80 pub fn new() -> Self {
-81 Self::default()
-82 }
-83
-84 pub fn add_node(&mut self, pos: DVec2) -> NodeId {
-88 self.topology_valid = false;
-89 self.nodes.insert(Node {
-90 pos,
-91 outgoing: Vec::new(),
-92 })
-93 }
-94
-95 #[must_use]
-97 pub fn node_position(&self, id: NodeId) -> Option<DVec2> {
-98 self.nodes.get(id).map(|n| n.pos)
-99 }
-100
-101 pub fn add_road(&mut self, a: NodeId, b: NodeId) -> Result<RoadId, SubdivisionError> {
-108 if a == b {
-109 return Err(SubdivisionError::InvalidParams(
-110 "road endpoints must differ".into(),
-111 ));
-112 }
-113 if !self.nodes.contains_key(a) || !self.nodes.contains_key(b) {
-114 return Err(SubdivisionError::InvalidParams("unknown node id".into()));
-115 }
-116 if self.find_road_between(a, b).is_some() {
-117 return Err(SubdivisionError::InvalidParams(
-118 "duplicate road between nodes".into(),
-119 ));
-120 }
-121 self.topology_valid = false;
-122 Ok(self.roads.insert(Road {
-123 a,
-124 b,
-125 half_a_to_b: HalfEdgeId::null(),
-126 half_b_to_a: HalfEdgeId::null(),
-127 }))
-128 }
-129
-130 pub fn road_endpoints(&self) -> impl Iterator<Item = (RoadId, NodeId, NodeId)> + '_ {
-133 self.roads.iter().map(|(rid, r)| (rid, r.a, r.b))
-134 }
-135
-136 #[must_use]
-138 pub fn road_count(&self) -> usize {
-139 self.roads.len()
-140 }
-141
-142 #[must_use]
-144 pub fn node_count(&self) -> usize {
-145 self.nodes.len()
-146 }
-147
-148 pub(crate) fn find_road_between(&self, a: NodeId, b: NodeId) -> Option<RoadId> {
-150 self.roads
-151 .iter()
-152 .find(|(_, r)| (r.a == a && r.b == b) || (r.a == b && r.b == a))
-153 .map(|(rid, _)| rid)
-154 }
-155
-156 pub(crate) fn road_nodes(&self, rid: RoadId) -> Option<(NodeId, NodeId)> {
-158 self.roads.get(rid).map(|r| (r.a, r.b))
-159 }
-160
-161 pub fn rebuild_topology(&mut self) -> Result<(), SubdivisionError> {
-168 if self.topology_valid {
-169 return Ok(());
-170 }
-171 self.check_planarity()?;
-172 self.build_half_edges();
-173 self.sort_outgoing_by_angle();
-174 self.link_next_and_prev();
-175 self.extract_faces()?;
-176 self.topology_valid = true;
-177 Ok(())
-178 }
-179
-180 fn check_planarity(&self) -> Result<(), SubdivisionError> {
-181 let segs: Vec<(RoadId, DVec2, DVec2)> = self
-182 .roads
-183 .iter()
-184 .map(|(rid, r)| (rid, self.nodes[r.a].pos, self.nodes[r.b].pos))
-185 .collect();
-186 let road_endpts: Vec<(NodeId, NodeId)> = self.roads.values().map(|r| (r.a, r.b)).collect();
-187 for (i, &(_, p1, p2)) in segs.iter().enumerate() {
-188 for j in (i + 1)..segs.len() {
-189 let (_, p3, p4) = segs[j];
-190 let (a1, b1) = road_endpts[i];
-191 let (a2, b2) = road_endpts[j];
-192 if a1 == a2 || a1 == b2 || b1 == a2 || b1 == b2 {
-194 continue;
-195 }
-196 if segments_properly_intersect(p1, p2, p3, p4) {
-197 return Err(SubdivisionError::NonPlanarGraph(a2));
-200 }
-201 }
-202 }
-203 Ok(())
-204 }
-205
-206 fn build_half_edges(&mut self) {
-207 self.half_edges.clear();
-209 self.faces.clear();
-210 for (_, n) in &mut self.nodes {
-211 n.outgoing.clear();
-212 }
-213 let road_keys: Vec<RoadId> = self.roads.keys().collect();
-214 for rid in road_keys {
-215 let (a, b) = {
-216 let r = &self.roads[rid];
-217 (r.a, r.b)
-218 };
-219 let h_ab = self.half_edges.insert(HalfEdge {
-220 origin: a,
-221 twin: HalfEdgeId::null(),
-222 next: HalfEdgeId::null(),
-223 prev: HalfEdgeId::null(),
-224 face: None,
-225 road: rid,
-226 });
-227 let h_ba = self.half_edges.insert(HalfEdge {
-228 origin: b,
-229 twin: h_ab,
-230 next: HalfEdgeId::null(),
-231 prev: HalfEdgeId::null(),
-232 face: None,
-233 road: rid,
-234 });
-235 self.half_edges[h_ab].twin = h_ba;
-236 self.nodes[a].outgoing.push(h_ab);
-237 self.nodes[b].outgoing.push(h_ba);
-238 let r = &mut self.roads[rid];
-239 r.half_a_to_b = h_ab;
-240 r.half_b_to_a = h_ba;
-241 }
-242 }
-243
-244 fn sort_outgoing_by_angle(&mut self) {
-245 let node_keys: Vec<NodeId> = self.nodes.keys().collect();
-246 for nid in node_keys {
-247 let pos = self.nodes[nid].pos;
-248 let outgoing: Vec<HalfEdgeId> = self.nodes[nid].outgoing.clone();
-249 let mut with_angles: Vec<(f64, HalfEdgeId)> = Vec::with_capacity(outgoing.len());
-250 for h in outgoing {
-251 let twin = self.half_edges[h].twin;
-252 let target = self.half_edges[twin].origin;
-253 let dir = self.nodes[target].pos - pos;
-254 let angle = dir.y.atan2(dir.x);
-255 with_angles.push((angle, h));
-256 }
-257 with_angles.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal));
-258 self.nodes[nid].outgoing = with_angles.into_iter().map(|(_, h)| h).collect();
-259 }
-260 }
-261
-262 fn link_next_and_prev(&mut self) {
-263 let edge_keys: Vec<HalfEdgeId> = self.half_edges.keys().collect();
-264 for h in edge_keys {
-265 let twin = self.half_edges[h].twin;
-271 let v = self.half_edges[twin].origin;
-272 let v_outgoing = &self.nodes[v].outgoing;
-273 let pos_in_v = v_outgoing
-274 .iter()
-275 .position(|&x| x == twin)
-276 .expect("twin must appear in target's outgoing list");
-277 let next = v_outgoing[(pos_in_v + v_outgoing.len() - 1) % v_outgoing.len()];
-278 self.half_edges[h].next = next;
-279
-280 let u = self.half_edges[h].origin;
-284 let u_outgoing = &self.nodes[u].outgoing;
-285 let pos_in_u = u_outgoing
-286 .iter()
-287 .position(|&x| x == h)
-288 .expect("h must appear in origin's outgoing list");
-289 let succ = u_outgoing[(pos_in_u + 1) % u_outgoing.len()];
-290 let prev = self.half_edges[succ].twin;
-291 self.half_edges[h].prev = prev;
-292 }
-293 }
-294
-295 fn extract_faces(&mut self) -> Result<(), SubdivisionError> {
-296 let edge_keys: Vec<HalfEdgeId> = self.half_edges.keys().collect();
-297 for h_start in edge_keys {
-298 if self.half_edges[h_start].face.is_some() {
-299 continue;
-300 }
-301 let mut cycle = Vec::new();
-303 let mut h = h_start;
-304 let max_iter = self.half_edges.len() + 4;
-305 let mut iters = 0;
-306 loop {
-307 cycle.push(h);
-308 let next = self.half_edges[h].next;
-309 if next.is_null() {
-310 return Err(SubdivisionError::OpenBlock);
-311 }
-312 h = next;
-313 iters += 1;
-314 if iters > max_iter {
-315 return Err(SubdivisionError::OpenBlock);
-316 }
-317 if h == h_start {
-318 break;
-319 }
-320 }
-321 let pts: Vec<DVec2> = cycle
-322 .iter()
-323 .map(|&hid| self.nodes[self.half_edges[hid].origin].pos)
-324 .collect();
-325 let signed = signed_area(&pts);
-326 let is_exterior = signed < 0.0 || signed.abs() < EPS_GEOM;
-327 let face_id = self.faces.insert(Face {
-328 boundary: h_start,
-329 is_exterior,
-330 });
-331 for h in cycle {
-332 self.half_edges[h].face = Some(face_id);
-333 }
-334 }
-335 Ok(())
-336 }
-337}
-338
-339#[cfg(test)]
-340mod tests {
-341 use super::*;
-342
-343 fn square_graph() -> RoadGraph {
-344 let mut g = RoadGraph::new();
-345 let a = g.add_node(DVec2::new(0.0, 0.0));
-346 let b = g.add_node(DVec2::new(1.0, 0.0));
-347 let c = g.add_node(DVec2::new(1.0, 1.0));
-348 let d = g.add_node(DVec2::new(0.0, 1.0));
-349 g.add_road(a, b).unwrap();
-350 g.add_road(b, c).unwrap();
-351 g.add_road(c, d).unwrap();
-352 g.add_road(d, a).unwrap();
-353 g
-354 }
-355
-356 #[test]
-357 fn square_yields_two_faces() {
-358 let mut g = square_graph();
-359 g.rebuild_topology().unwrap();
-360 assert_eq!(g.faces.len(), 2);
-361 let exterior = g.faces.values().filter(|f| f.is_exterior).count();
-362 assert_eq!(exterior, 1);
-363 }
-364
-365 #[test]
-366 fn duplicate_road_rejected() {
-367 let mut g = RoadGraph::new();
-368 let a = g.add_node(DVec2::new(0.0, 0.0));
-369 let b = g.add_node(DVec2::new(1.0, 0.0));
-370 g.add_road(a, b).unwrap();
-371 assert!(g.add_road(a, b).is_err());
-372 assert!(g.add_road(b, a).is_err());
-373 }
-374
-375 #[test]
-376 fn self_loop_rejected() {
-377 let mut g = RoadGraph::new();
-378 let a = g.add_node(DVec2::new(0.0, 0.0));
-379 assert!(g.add_road(a, a).is_err());
-380 }
-381
-382 #[test]
-383 fn non_planar_graph_detected() {
-384 let mut g = RoadGraph::new();
-386 let a = g.add_node(DVec2::new(0.0, 0.0));
-387 let b = g.add_node(DVec2::new(2.0, 2.0));
-388 let c = g.add_node(DVec2::new(0.0, 2.0));
-389 let d = g.add_node(DVec2::new(2.0, 0.0));
-390 g.add_road(a, b).unwrap();
-391 g.add_road(c, d).unwrap();
-392 let err = g.rebuild_topology().unwrap_err();
-393 assert!(matches!(err, SubdivisionError::NonPlanarGraph(_)));
-394 }
-395}