ReUseX  0.0.5
3D Point Cloud Processing for Building Reuse
Loading...
Searching...
No Matches
CellComplex.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2025 Povl Filip Sonne-Frederiksen
2//
3// SPDX-License-Identifier: GPL-3.0-or-later
4
5#pragma once
6#include "reusex/core/logging.hpp"
7#include "reusex/geometry/Registry.hpp"
8#include "reusex/types.hpp"
9
10#include <Eigen/StdVector>
11#include <boost/graph/adjacency_list.hpp>
12#include <boost/graph/graph_traits.hpp>
13#include <boost/iterator/filter_iterator.hpp>
14#include <fmt/format.h>
15#include <pcl/pcl_base.h>
16#include <pcl/point_cloud.h>
17#include <pcl/point_types.h>
18#include <range/v3/range/concepts.hpp>
19#include <range/v3/view/zip.hpp>
20
21#include <set>
22#include <string>
23#include <vector>
24
25namespace reusex::geometry {
26
27// Tags to distinguish node types in CellComplex
28enum class CellNodeType { cell, face, vertex };
29
31struct FaceData {
33};
34struct CellData {
35 std::set<int> wall_ids{};
36};
37
38// Generic vertex bundle. `id` is shared across all node types; it is treated
39// as a per-type counter (vertex/face/cell) by the construction code. Moving
40// it into CellVertexData/FaceData/CellData and accessing through std::visit
41// would be more type-safe but would force every existing `(*this)[v].id`
42// call site to thread a visitor — not currently worth the churn.
45 int id;
46 Eigen::Vector3d pos;
47 std::variant<CellVertexData, FaceData, CellData> data;
48};
49
51 bool is_main = false;
52};
53
55 : public boost::adjacency_list<boost::vecS, // edge container
56 boost::vecS, // vertex container
57 boost::undirectedS, // cells–faces are
58 // bidirectional
59 // boost::directedS,
60 VertexProperties, // vertex bundle
61 CellEdgeData // edge bundle
62 >,
63 public Registry {
64 protected:
65 using Graph =
66 boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
68
69 public:
70 using Vertex = boost::graph_traits<Graph>::vertex_descriptor;
71 using GraphIter = boost::graph_traits<CellComplex>::vertex_iterator;
72 using AdjacencyIter = boost::graph_traits<CellComplex>::adjacency_iterator;
73
74 protected:
75 template <CellNodeType T> struct Is_Type {
76 const CellComplex *g;
77 Is_Type() = delete;
78 explicit Is_Type(const CellComplex *graph) : g(graph) {}
79 bool operator()(CellComplex::vertex_descriptor vd) const {
80 auto const &prop = (*g)[vd];
81 return prop.type == T;
82 }
83 };
88 const CellComplex *g;
90 // cppcheck-suppress uninitMemberVar
91 explicit IsFaceBetweenCells(const CellComplex *graph) : g(graph) {}
92 bool operator()(CellComplex::vertex_descriptor vd) const {
93 auto const &prop = (*g)[vd];
94 int n_adj = 0;
95 auto [begin, end] = boost::adjacent_vertices(vd, *g);
96 for (auto it = begin; it != end; ++it)
97 if ((*g)[*it].type == CellNodeType::cell)
98 ++n_adj;
99 return prop.type == CellNodeType::face && n_adj >= 2;
100 }
101 };
102
103 using VertexIterator = boost::filter_iterator<IsVertex, GraphIter>;
104 using FaceIterator = boost::filter_iterator<IsFace, GraphIter>;
106 boost::filter_iterator<IsFaceBetweenCells, GraphIter>;
107 using CellIterator = boost::filter_iterator<IsCell, GraphIter>;
108 using VertexOnFaceIterator = boost::filter_iterator<IsVertex, AdjacencyIter>;
109 using FaceOnCellIterator = boost::filter_iterator<IsFace, AdjacencyIter>;
110 using CellOnFaceIterator = boost::filter_iterator<IsCell, AdjacencyIter>;
111
112 private:
113 int vertex_count = 0;
114 int face_count = 0;
115 int cell_count = 0;
116
117 public:
119
120 CellComplex() = delete;
121
123 std::vector<Eigen::Vector4d, Eigen::aligned_allocator<Eigen::Vector4d>>
124 &planes,
125 std::vector<size_t> &verticals,
126 const std::vector<size_t> &horizontals,
127 std::vector<std::pair<size_t, size_t>> &pairs,
128 std::array<double, 2> min_xy, std::array<double, 2> max_xy,
129 std::optional<
130 std::function<void(size_t, std::vector<std::array<double, 3>> const &,
131 std::vector<int> const &)>>
132 viz_func = std::nullopt);
133
134 // Per-face coverage values are written to the "f:support_probability"
135 // vertex property map and queried from there by downstream code.
136 template <typename PointT = pcl::PointXYZ>
137 auto compute_face_coverage(pcl::PointCloud<PointT>::ConstPtr cloud,
139 std::vector<pcl::IndicesPtr> &inliers,
140 const double grid_size = 0.2) -> void;
141
142 template <typename PointT = pcl::PointXYZ, typename PointN = pcl::Normal,
143 typename PointL = pcl::Label>
144 auto compute_room_probabilities(pcl::PointCloud<PointT>::ConstPtr cloud,
145 pcl::PointCloud<PointN>::ConstPtr normals,
146 pcl::PointCloud<PointL>::ConstPtr labels,
147 const double grid_size = 0.2) -> void;
148
149 inline Vertex add_vertex(Eigen::Vector3d pos) {
150 auto v = boost::add_vertex(*this);
151 (*this)[v].type = CellNodeType::vertex;
152 (*this)[v].id = vertex_count++;
153 (*this)[v].pos = pos;
154 (*this)[v].data = CellVertexData{};
155 return v;
156 };
157
158 template <typename T>
159 inline Vertex add_face(Eigen::Vector3d pos, T begin, T end,
160 int plane_id = -1) {
161 auto f = boost::add_vertex(*this);
162 (*this)[f].type = CellNodeType::face;
163 (*this)[f].id = face_count++;
164 (*this)[f].pos = pos;
165 (*this)[f].data = FaceData{plane_id};
166 for (auto it = begin; it != end; ++it)
167 boost::add_edge(*it, f, *this);
168 return f;
169 };
170 template <typename Range>
171 inline Vertex add_face(Eigen::Vector3d pos, Range vertices,
172 int plane_id = -1) {
173 return add_face(pos, std::begin(vertices), std::end(vertices), plane_id);
174 };
175
176 template <typename T>
177 inline Vertex add_cell(Eigen::Vector3d pos, T begin, T end) {
178 auto c = boost::add_vertex(*this);
179 (*this)[c].type = CellNodeType::cell;
180 (*this)[c].id = cell_count++;
181 (*this)[c].pos = pos;
182 (*this)[c].data = CellData{};
183 for (auto it = begin; it != end; ++it)
184 boost::add_edge(*it, c, *this);
185 return c;
186 };
187 template <typename Range>
188 inline Vertex add_cell(Eigen::Vector3d pos, Range faces) {
189 return add_cell(pos, std::begin(faces), std::end(faces));
190 };
191
192 size_t num_vertices() const;
193 size_t num_faces() const;
194 size_t num_cells() const;
195
196 std::ostream &operator<<(std::ostream &os) const;
197
200
201 auto faces_begin() const -> FaceIterator;
202 auto faces_end() const -> FaceIterator;
203
206
207 auto cells_begin() const -> CellIterator;
208 auto cells_end() const -> CellIterator;
209
210 // CGAL-style adjacency iterators: "<elements>_of_<container>".
213
214 // auto cells_of_face_begin(Vertex f) const -> CellOnFaceIterator;
215 // auto cells_of_face_end(Vertex f) const -> CellOnFaceIterator;
216
219
220 auto get_a(Vertex f) const -> Vertex;
221 auto get_b(Vertex f) const -> Vertex;
222};
223} // namespace reusex::geometry
224
225template <> struct fmt::formatter<reusex::geometry::CellComplex> {
226 // Parse function (optional)
227 template <typename ParseContext> constexpr auto parse(ParseContext &ctx) {
228 return ctx.begin();
229 }
230
231 // Format function
232 template <typename FormatContext>
234 FormatContext &ctx) const {
235 return fmt::format_to(ctx.out(), "[{}c {}f {}v {}r {}w]", obj.num_cells(),
236 obj.num_faces(), obj.num_vertices(), obj.n_rooms,
237 obj.n_walls);
238 }
239};
240
241// Implementation files
242#include "reusex/geometry/CellComplexFaceCoverage.hpp"
243#include "reusex/geometry/CellComplexRoomProbabilities.hpp"
boost::graph_traits< CellComplex >::adjacency_iterator AdjacencyIter
boost::filter_iterator< IsVertex, GraphIter > VertexIterator
Is_Type< CellNodeType::vertex > IsVertex
auto get_a(Vertex f) const -> Vertex
boost::filter_iterator< IsFace, GraphIter > FaceIterator
auto compute_face_coverage(pcl::PointCloud< PointT >::ConstPtr cloud, EigenVectorContainer< double, 4 > &planes, std::vector< pcl::IndicesPtr > &inliers, const double grid_size=0.2) -> void
auto faces_between_cells_end() const -> FaceBetweenCellIterator
boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, CellEdgeData > Graph
auto cells_begin() const -> CellIterator
std::ostream & operator<<(std::ostream &os) const
Vertex add_cell(Eigen::Vector3d pos, Range faces)
auto vertices_end() const -> VertexIterator
CellComplex(std::vector< Eigen::Vector4d, Eigen::aligned_allocator< Eigen::Vector4d > > &planes, std::vector< size_t > &verticals, const std::vector< size_t > &horizontals, std::vector< std::pair< size_t, size_t > > &pairs, std::array< double, 2 > min_xy, std::array< double, 2 > max_xy, std::optional< std::function< void(size_t, std::vector< std::array< double, 3 > > const &, std::vector< int > const &)> > viz_func=std::nullopt)
auto faces_end() const -> FaceIterator
boost::graph_traits< CellComplex >::vertex_iterator GraphIter
boost::graph_traits< Graph >::vertex_descriptor Vertex
auto get_b(Vertex f) const -> Vertex
auto vertices_of_face_end(Vertex f) const -> VertexOnFaceIterator
boost::filter_iterator< IsFaceBetweenCells, GraphIter > FaceBetweenCellIterator
auto faces_between_cells_begin() const -> FaceBetweenCellIterator
auto faces_of_cell_end(Vertex c) const -> FaceOnCellIterator
Is_Type< CellNodeType::cell > IsCell
boost::filter_iterator< IsCell, GraphIter > CellIterator
auto faces_begin() const -> FaceIterator
Vertex add_face(Eigen::Vector3d pos, Range vertices, int plane_id=-1)
auto faces_of_cell_begin(Vertex c) const -> FaceOnCellIterator
Is_Type< CellNodeType::face > IsFace
Vertex add_cell(Eigen::Vector3d pos, T begin, T end)
auto vertices_of_face_begin(Vertex f) const -> VertexOnFaceIterator
Vertex add_face(Eigen::Vector3d pos, T begin, T end, int plane_id=-1)
auto vertices_begin() const -> VertexIterator
boost::filter_iterator< IsFace, AdjacencyIter > FaceOnCellIterator
auto compute_room_probabilities(pcl::PointCloud< PointT >::ConstPtr cloud, pcl::PointCloud< PointN >::ConstPtr normals, pcl::PointCloud< PointL >::ConstPtr labels, const double grid_size=0.2) -> void
auto cells_end() const -> CellIterator
boost::filter_iterator< IsCell, AdjacencyIter > CellOnFaceIterator
Vertex add_vertex(Eigen::Vector3d pos)
boost::filter_iterator< IsVertex, AdjacencyIter > VertexOnFaceIterator
pcl::PointXYZRGB PointT
Definition types.hpp:17
std::vector< Eigen::Matrix< Scalar, Rows, 1 >, Eigen::aligned_allocator< Eigen::Matrix< Scalar, Rows, 1 > > > EigenVectorContainer
Definition types.hpp:43
auto format(const reusex::geometry::CellComplex &obj, FormatContext &ctx) const
constexpr auto parse(ParseContext &ctx)
bool operator()(CellComplex::vertex_descriptor vd) const
bool operator()(CellComplex::vertex_descriptor vd) const
Is_Type(const CellComplex *graph)
std::variant< CellVertexData, FaceData, CellData > data