use glam::Vec2;
use serde::{Deserialize, Serialize};
pub type NodeId = u32;
pub type EdgeId = u32;
pub type BlockId = u32;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RoadType {
Major,
Minor,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoadNode {
pub position: Vec2,
pub elevation: f32,
pub edges: Vec<EdgeId>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RoadEdge {
pub start: NodeId,
pub end: NodeId,
pub road_type: RoadType,
pub active: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CityBlock {
pub perimeter: Vec<NodeId>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RoadGraph {
pub nodes: Vec<RoadNode>,
pub edges: Vec<RoadEdge>,
pub blocks: Vec<CityBlock>,
}
impl RoadGraph {
pub fn add_node(&mut self, position: Vec2) -> NodeId {
let id = self.nodes.len() as NodeId;
self.nodes.push(RoadNode {
position,
elevation: 0.0,
edges: Vec::new(),
});
id
}
pub fn add_node_with_elevation(&mut self, position: Vec2, elevation: f32) -> NodeId {
let id = self.nodes.len() as NodeId;
self.nodes.push(RoadNode {
position,
elevation,
edges: Vec::new(),
});
id
}
pub fn add_edge(&mut self, start: NodeId, end: NodeId, road_type: RoadType) -> EdgeId {
let id = self.edges.len() as EdgeId;
self.edges.push(RoadEdge {
start,
end,
road_type,
active: true,
});
self.nodes[start as usize].edges.push(id);
self.nodes[end as usize].edges.push(id);
id
}
pub fn split_edge(&mut self, edge_id: EdgeId, split_pos: Vec2) -> (NodeId, EdgeId, EdgeId) {
let edge = &self.edges[edge_id as usize];
let start = edge.start;
let end = edge.end;
let road_type = edge.road_type;
self.edges[edge_id as usize].active = false;
self.nodes[start as usize].edges.retain(|&e| e != edge_id);
self.nodes[end as usize].edges.retain(|&e| e != edge_id);
let start_pos = self.nodes[start as usize].position;
let end_pos = self.nodes[end as usize].position;
let seg_len = (end_pos - start_pos).length();
let t = if seg_len > 1e-6 {
((split_pos - start_pos).length() / seg_len).clamp(0.0, 1.0)
} else {
0.5
};
let start_elev = self.nodes[start as usize].elevation;
let end_elev = self.nodes[end as usize].elevation;
let mid_elev = start_elev + t * (end_elev - start_elev);
let mid = self.add_node_with_elevation(split_pos, mid_elev);
let ea = self.add_edge(start, mid, road_type);
let eb = self.add_edge(mid, end, road_type);
(mid, ea, eb)
}
pub fn opposite(&self, edge_id: EdgeId, node_id: NodeId) -> NodeId {
let edge = &self.edges[edge_id as usize];
if edge.start == node_id {
edge.end
} else {
edge.start
}
}
pub fn node_pos(&self, id: NodeId) -> Vec2 {
self.nodes[id as usize].position
}
}