topola/src/mesh.rs

215 lines
5.4 KiB
Rust

use enum_dispatch::enum_dispatch;
use fixedbitset::FixedBitSet;
use geo::{point, Point};
use petgraph::stable_graph::NodeIndex;
use petgraph::visit::{self, NodeIndexable};
use spade::{
handles::FixedVertexHandle, DelaunayTriangulation, HasPosition, InsertionError, Point2,
Triangulation,
};
use crate::{
graph::{FixedBendIndex, FixedDotIndex, GetNodeIndex, Index, LooseBendIndex},
layout::Layout,
};
use crate::{primitive::MakeShape, shape::ShapeTrait};
#[derive(Debug, Clone)]
struct Vertex {
graph_index: VertexIndex,
x: f64,
y: f64,
}
#[enum_dispatch(GetNodeIndex)]
#[derive(Debug, Hash, Clone, Copy, PartialEq, Eq)]
pub enum VertexIndex {
FixedDot(FixedDotIndex),
FixedBend(FixedBendIndex),
LooseBend(LooseBendIndex),
}
impl HasPosition for Vertex {
type Scalar = f64;
fn position(&self) -> Point2<Self::Scalar> {
Point2::new(self.x, self.y)
}
}
#[derive(Debug, Clone)]
pub struct Mesh {
triangulation: DelaunayTriangulation<Vertex>,
vertex_to_handle: Vec<Option<FixedVertexHandle>>,
}
impl Mesh {
pub fn new() -> Self {
Self {
triangulation: DelaunayTriangulation::new(),
vertex_to_handle: Vec::new(),
}
}
pub fn triangulate(&mut self, layout: &Layout) -> Result<(), InsertionError> {
self.triangulation.clear();
self.vertex_to_handle = Vec::new();
self.vertex_to_handle
.resize(layout.graph.node_bound(), None);
for node in layout.nodes() {
if let Index::FixedDot(dot) = node {
let center = layout.primitive(dot).shape().center();
self.vertex_to_handle[dot.node_index().index()] =
Some(self.triangulation.insert(Vertex {
graph_index: dot.into(),
x: center.x(),
y: center.y(),
})?);
}
}
Ok(())
}
pub fn vertex(&self, handle: FixedVertexHandle) -> VertexIndex {
self.triangulation.vertex(handle).as_ref().graph_index
}
pub fn handle(&self, graph_index: VertexIndex) -> FixedVertexHandle {
self.vertex_to_handle[graph_index.node_index().index()].unwrap()
}
pub fn position(&self, vertex: VertexIndex) -> Point {
let position = self.triangulation.vertex(self.handle(vertex)).position();
point! {x: position.x, y: position.y}
}
}
impl visit::GraphBase for Mesh {
type NodeId = VertexIndex;
type EdgeId = (VertexIndex, VertexIndex);
}
pub struct MeshVisitMap {
fixedbitset: FixedBitSet,
}
impl MeshVisitMap {
pub fn with_capacity(bits: usize) -> Self {
Self {
fixedbitset: FixedBitSet::with_capacity(bits),
}
}
pub fn clear(&mut self) {
self.fixedbitset.clear();
}
pub fn grow(&mut self, bits: usize) {
self.fixedbitset.grow(bits);
}
}
impl<T: GetNodeIndex> visit::VisitMap<T> for MeshVisitMap {
fn visit(&mut self, a: T) -> bool {
!self.fixedbitset.put(a.node_index().index())
}
fn is_visited(&self, a: &T) -> bool {
self.fixedbitset.contains(a.node_index().index())
}
}
impl visit::Visitable for Mesh {
type Map = MeshVisitMap;
fn visit_map(&self) -> Self::Map {
// FIXME: This seems wrong, but pathfinding works for some reason. Investigate.
MeshVisitMap::with_capacity(self.triangulation.num_vertices())
}
fn reset_map(&self, map: &mut Self::Map) {
map.clear();
map.grow(self.triangulation.num_vertices());
}
}
impl visit::Data for Mesh {
type NodeWeight = ();
type EdgeWeight = ();
}
#[derive(Clone, Copy)]
pub struct MeshEdgeReference {
from: VertexIndex,
to: VertexIndex,
}
impl<'a> visit::EdgeRef for MeshEdgeReference {
type NodeId = VertexIndex;
type EdgeId = (VertexIndex, VertexIndex);
type Weight = ();
fn source(&self) -> Self::NodeId {
self.from
}
fn target(&self) -> Self::NodeId {
self.to
}
fn weight(&self) -> &Self::Weight {
&()
}
fn id(&self) -> Self::EdgeId {
(self.from, self.to)
}
}
impl<'a> visit::IntoEdgeReferences for &'a Mesh {
type EdgeRef = MeshEdgeReference;
type EdgeReferences = Box<dyn Iterator<Item = MeshEdgeReference> + 'a>;
fn edge_references(self) -> Self::EdgeReferences {
Box::new(
self.triangulation
.directed_edges()
.map(|edge| MeshEdgeReference {
from: self.vertex(edge.from().fix()),
to: self.vertex(edge.to().fix()),
}),
)
}
}
impl<'a> visit::IntoNeighbors for &'a Mesh {
type Neighbors = Box<dyn Iterator<Item = VertexIndex> + 'a>;
fn neighbors(self, vertex: Self::NodeId) -> Self::Neighbors {
Box::new(
self.triangulation
.vertex(self.handle(vertex))
.out_edges()
.map(|handle| self.vertex(handle.to().fix())),
)
}
}
impl<'a> visit::IntoEdges for &'a Mesh {
type Edges = Box<dyn Iterator<Item = MeshEdgeReference> + 'a>;
fn edges(self, a: Self::NodeId) -> Self::Edges {
Box::new(
self.triangulation
.vertex(self.handle(a))
.out_edges()
.map(|edge| MeshEdgeReference {
from: self.vertex(edge.from().fix()),
to: self.vertex(edge.to().fix()),
}),
)
}
}