From 2f06dcc11f6192b27525d6fc37c835884c15f103 Mon Sep 17 00:00:00 2001 From: Austin Gill Date: Sun, 25 Feb 2024 15:33:26 -0600 Subject: [PATCH] Create C++ GeometryGraph from a Rust GeometryGraph --- generative/cxxbridge/geometry_graph.hpp | 25 +++++++++ generative/cxxbridge/geometry_graph_ffi.rs | 5 ++ generative/cxxbridge/mod.rs | 23 ++++++++- generative/graph.rs | 51 +++++++++---------- .../generative/noding/geometry-graph.h | 4 ++ generative/noding/geometry-graph.cpp | 22 ++++++-- generative/noding/mod.rs | 4 ++ 7 files changed, 101 insertions(+), 33 deletions(-) diff --git a/generative/cxxbridge/geometry_graph.hpp b/generative/cxxbridge/geometry_graph.hpp index 98d1c1b..11358de 100644 --- a/generative/cxxbridge/geometry_graph.hpp +++ b/generative/cxxbridge/geometry_graph.hpp @@ -2,6 +2,8 @@ #include "generative/generative/cxxbridge/coord_ffi.rs.h" #include +#include +#include #include #include @@ -46,3 +48,26 @@ class GeometryGraphShim private: generative::noding::GeometryGraph m_inner; }; + +[[nodiscard]] std::unique_ptr +from_nodes_edges(rust::Slice nodes, rust::Slice edges) noexcept +{ + auto factory = geos::geom::GeometryFactory::create(); + auto graph = generative::noding::GeometryGraph(*factory); + + for (const auto& node : nodes) + { + const auto coord = geos::geom::CoordinateXY{node.x, node.y}; + // Ignore the created node's index, because we're creating them in the order of the nodes + // slice, which the edges slice requires in order to be valid. + const auto index = graph.add_node(coord); + (void)index; + } + + for (const auto& edge : edges) + { + graph.add_edge(edge.src, edge.dst); + } + + return std::make_unique(std::move(graph)); +} diff --git a/generative/cxxbridge/geometry_graph_ffi.rs b/generative/cxxbridge/geometry_graph_ffi.rs index 2fb9829..89d99c5 100644 --- a/generative/cxxbridge/geometry_graph_ffi.rs +++ b/generative/cxxbridge/geometry_graph_ffi.rs @@ -9,6 +9,11 @@ pub mod ffi { fn nodes(self: &GeometryGraphShim) -> Vec; fn edges(self: &GeometryGraphShim) -> Vec; + + fn from_nodes_edges( + nodes: &[CoordShim], + edges: &[GraphEdge], + ) -> UniquePtr; } impl UniquePtr {} diff --git a/generative/cxxbridge/mod.rs b/generative/cxxbridge/mod.rs index 2bc5fe7..0435b02 100644 --- a/generative/cxxbridge/mod.rs +++ b/generative/cxxbridge/mod.rs @@ -6,5 +6,26 @@ mod noder_ffi; pub use coord_ffi::ffi::{CoordShim, GraphEdge, LineStringShim, PolygonShim}; pub use geometry_collection::GeometryCollectionShim; -pub use geometry_graph_ffi::ffi::GeometryGraphShim; +pub use geometry_graph_ffi::ffi::{from_nodes_edges, GeometryGraphShim}; pub use noder_ffi::ffi::node; + +pub fn to_ffi_graph( + graph: &crate::graph::GeometryGraph, +) -> cxx::UniquePtr { + let nodes: Vec<_> = graph + .node_weights() + .map(|w| CoordShim { x: w.0.x, y: w.0.y }) + .collect(); + let mut edges = Vec::with_capacity(graph.edge_count()); + for i in 0..graph.edge_count() { + let edge = graph + .edge_endpoints(petgraph::graph::EdgeIndex::new(i)) + .unwrap(); + edges.push(GraphEdge { + src: edge.0.index(), + dst: edge.1.index(), + }); + } + + from_nodes_edges(&nodes, &edges) +} diff --git a/generative/graph.rs b/generative/graph.rs index 5ddb619..7605398 100644 --- a/generative/graph.rs +++ b/generative/graph.rs @@ -8,35 +8,32 @@ pub type GeometryGraph = petgraph::Graph; #[cfg(feature = "cxx-bindings")] -mod wrapper { - use super::*; - use crate::cxxbridge::{GeometryGraphShim, GraphEdge}; +impl From<&crate::cxxbridge::GeometryGraphShim> + for GeometryGraph +{ + fn from(ffi_graph: &crate::cxxbridge::GeometryGraphShim) -> GeometryGraph { + let nodes = ffi_graph.nodes(); + // The edges are indices into the nodes array. + let edges = ffi_graph.edges(); - impl From<&GeometryGraphShim> for GeometryGraph { - fn from(ffi_graph: &GeometryGraphShim) -> GeometryGraph { - let nodes = ffi_graph.nodes(); - // The edges are indices into the nodes array. - let edges = ffi_graph.edges(); + let mut graph = GeometryGraph::default(); + graph.reserve_exact_nodes(nodes.len()); + graph.reserve_exact_edges(edges.len()); - let mut graph = GeometryGraph::default(); - graph.reserve_exact_nodes(nodes.len()); - graph.reserve_exact_edges(edges.len()); - - for (_cxx_node_index, node) in nodes.iter().enumerate() { - let point = Point::new(node.x, node.y); - // We rely on the implementation detail of petgraph::Graph that when you insert nodes in - // order, the node indices are generated in the same order. - let _node_index = graph.add_node(point); - debug_assert_eq!(_node_index.index(), _cxx_node_index); - } - for edge in &edges { - let GraphEdge { src, dst } = edge; - let src = petgraph::graph::NodeIndex::new(*src); - let dst = petgraph::graph::NodeIndex::new(*dst); - let _edge_index = graph.add_edge(src, dst, ()); - } - - graph + for (_cxx_node_index, node) in nodes.iter().enumerate() { + let point = Point::new(node.x, node.y); + // We rely on the implementation detail of petgraph::Graph that when you insert nodes in + // order, the node indices are generated in the same order. + let _node_index = graph.add_node(point); + debug_assert_eq!(_node_index.index(), _cxx_node_index); } + for edge in &edges { + let crate::cxxbridge::GraphEdge { src, dst } = edge; + let src = petgraph::graph::NodeIndex::new(*src); + let dst = petgraph::graph::NodeIndex::new(*dst); + let _edge_index = graph.add_edge(src, dst, ()); + } + + graph } } diff --git a/generative/include/generative/noding/geometry-graph.h b/generative/include/generative/noding/geometry-graph.h index 3b2d2eb..0e2902b 100644 --- a/generative/include/generative/noding/geometry-graph.h +++ b/generative/include/generative/noding/geometry-graph.h @@ -70,6 +70,10 @@ class GeometryGraph //! @note The nodes at the indices @p src and @p dst must exist. void add_edge(std::size_t src, std::size_t dst); + //! @brief Add the given node to the graph. + //! @returns the index of the created node. + [[nodiscard]] std::size_t add_node(geos::geom::CoordinateXY coord) noexcept; + [[nodiscard]] std::vector> get_edge_pairs() const; //! @brief Get the graph edges. diff --git a/generative/noding/geometry-graph.cpp b/generative/noding/geometry-graph.cpp index 69a53fd..4f7e200 100644 --- a/generative/noding/geometry-graph.cpp +++ b/generative/noding/geometry-graph.cpp @@ -3,6 +3,7 @@ #include "generative/geometry-flattener.h" #include "generative/io/wkt.h" +#include #include #include #include @@ -29,6 +30,18 @@ GeometryGraph::GeometryGraph(std::vector&& nodes, { } +std::size_t GeometryGraph::add_node(geos::geom::CoordinateXY coord) noexcept +{ + const auto new_index = m_nodes.size(); + + auto point = m_factory.createPoint(coord); + auto node = GeometryGraph::Node(new_index, std::move(point)); + + m_nodes.push_back(std::move(node)); + + return new_index; +} + void GeometryGraph::add_edge(std::size_t src, std::size_t dst) { m_nodes[src].adjacencies.emplace(dst); @@ -81,12 +94,11 @@ GeometryGraph::find_or_insert(Nodes_t& inserted_coords, const geos::geom::Coordi // This isn't a coordinate we know about. if (iter == inserted_coords.end()) { - auto point = std::unique_ptr(m_factory.createPoint(coord)); - GeometryGraph::Node new_node(m_nodes.size(), std::move(point)); - LOG4CPLUS_TRACE(s_logger, "Adding new node " << new_node.index << "\t" << new_node.point); - auto result = inserted_coords.emplace(coord, new_node.index); + const auto new_index = this->add_node(coord); + LOG4CPLUS_TRACE(s_logger, + "Adding new node " << new_index << "\t" << m_nodes[new_index].point); + auto result = inserted_coords.emplace(coord, new_index); iter = result.first; - m_nodes.push_back(std::move(new_node)); } return m_nodes.at(iter->second); diff --git a/generative/noding/mod.rs b/generative/noding/mod.rs index 51841c7..2a5487f 100644 --- a/generative/noding/mod.rs +++ b/generative/noding/mod.rs @@ -20,6 +20,10 @@ where (&*ffi_graph).into() } +pub fn polygonize(graph: &GeometryGraph) { + let ffi_graph = cxxbridge::to_ffi_graph(graph); +} + #[cfg(test)] mod tests { use geo::Point;