PolyFEM
Loading...
Searching...
No Matches
Selection.cpp
Go to the documentation of this file.
1#include "Selection.hpp"
2
7
9
10#include <memory>
11
12namespace polyfem::utils
13{
14 using namespace polyfem::mesh;
15
16 std::shared_ptr<Selection> Selection::build(
17 const json &selection,
18 const Selection::BBox &mesh_bbox,
19 const std::string &root_path)
20 {
21 std::shared_ptr<Selection> res = nullptr;
22 if (selection.contains("box"))
23 res = std::make_shared<BoxSelection>(selection, mesh_bbox);
24 else if (selection.contains("threshold"))
25 res = std::make_shared<BoxSideSelection>(selection, mesh_bbox);
26 else if (selection.contains("center"))
27 res = std::make_shared<SphereSelection>(selection, mesh_bbox);
28 else if (selection.contains("radius"))
29 res = std::make_shared<CylinderSelection>(selection, mesh_bbox);
30 else if (selection.contains("axis"))
31 res = std::make_shared<AxisPlaneSelection>(selection, mesh_bbox);
32 else if (selection.contains("normal"))
33 res = std::make_shared<PlaneSelection>(selection, mesh_bbox);
34 else if (selection["id"].is_string()) // assume ID is a file path
35 res = std::make_shared<FileSelection>(
36 resolve_path(selection["id"], root_path), selection.value("id_offset", 0));
37 else if (selection["id"].is_number_integer()) // assume ID is uniform
38 res = std::make_shared<UniformSelection>(selection["id"]);
39 else
40 log_and_throw_error("Selection not recognized: {}", selection.dump());
41
42 return res;
43 }
44
45 std::vector<std::shared_ptr<Selection>> Selection::build_selections(
46 const json &j_selections,
47 const Selection::BBox &mesh_bbox,
48 const std::string &root_path)
49 {
50 std::vector<std::shared_ptr<Selection>> selections;
51 if (j_selections.is_number_integer())
52 {
53 selections.push_back(std::make_shared<UniformSelection>(j_selections.get<int>()));
54 }
55 else if (j_selections.is_string())
56 {
57 selections.push_back(std::make_shared<FileSelection>(resolve_path(j_selections, root_path)));
58 }
59 else if (j_selections.is_object())
60 {
61 selections.push_back(build(j_selections, mesh_bbox));
62 }
63 else if (j_selections.is_array())
64 {
65 for (const json &s : j_selections.get<std::vector<json>>())
66 {
67 selections.push_back(build(s, mesh_bbox));
68 }
69 }
70 else if (!j_selections.is_null())
71 {
72 log_and_throw_error("Invalid selections: {}", j_selections);
73 }
74 return selections;
75 }
76
77 // ------------------------------------------------------------------------
78
80 const json &selection,
81 const Selection::BBox &mesh_bbox)
82 : Selection(selection["id"].get<int>())
83 {
84 auto bboxj = selection["box"];
85
86 const int dim = bboxj[0].size();
87 assert(bboxj[1].size() == dim);
88
89 bbox_[0] = bboxj[0];
90 bbox_[1] = bboxj[1];
91
92 if (selection.value("relative", false))
93 {
94 RowVectorNd mesh_width = mesh_bbox[1] - mesh_bbox[0];
95 bbox_[0] = mesh_width.cwiseProduct(bbox_[0]) + mesh_bbox[0];
96 bbox_[1] = mesh_width.cwiseProduct(bbox_[1]) + mesh_bbox[0];
97 }
98 }
99
100 bool BoxSelection::inside(const size_t p_id, const std::vector<int> &vs, const RowVectorNd &p) const
101 {
102 assert(bbox_[0].size() == p.size());
103 assert(bbox_[1].size() == p.size());
104 bool inside = true;
105
106 for (int d = 0; d < p.size(); ++d)
107 {
108 if (p[d] < bbox_[0][d] || p[d] > bbox_[1][d])
109 {
110 inside = false;
111 break;
112 }
113 }
114
115 return inside;
116 }
117
118 // ------------------------------------------------------------------------
119
121 const json &selection,
122 const Selection::BBox &mesh_bbox)
123 : Selection(0), mesh_bbox_(mesh_bbox)
124 {
125 tolerance_ = selection.at("threshold");
126 if (tolerance_ < 0)
127 tolerance_ = mesh_bbox.size() == 3 ? 1e-2 : 1e-7;
128 id_offset_ = selection.at("id_offset");
129 }
130
131 int BoxSideSelection::id(const size_t p_id, const std::vector<int> &vs, const RowVectorNd &p) const
132 {
133 assert(p.size() == 2 || p.size() == 3);
134 assert(mesh_bbox_[0].size() == p.size());
135 assert(mesh_bbox_[1].size() == p.size());
136 const auto &[min_corner, max_corner] = mesh_bbox_;
137
138 if (std::abs(p(0) - min_corner(0)) < tolerance_)
139 return 1 + id_offset_; // left
140 else if (std::abs(p(1) - min_corner(1)) < tolerance_)
141 return 2 + id_offset_; // bottom
142 else if (std::abs(p(0) - max_corner(0)) < tolerance_)
143 return 3 + id_offset_; // right
144 else if (std::abs(p(1) - max_corner(1)) < tolerance_)
145 return 4 + id_offset_; // top
146 else if (p.size() == 3 && std::abs(p(2) - min_corner(2)) < tolerance_)
147 return 5 + id_offset_; // back
148 else if (p.size() == 3 && std::abs(p(2) - max_corner(2)) < tolerance_)
149 return 6 + id_offset_; // front
150 else
151 return 7 + id_offset_; // all other sides
152 }
153
154 // ------------------------------------------------------------------------
155
157 const json &selection,
158 const Selection::BBox &mesh_bbox)
159 : Selection(selection["id"].get<int>())
160 {
161 center_ = selection["center"];
162 radius2_ = selection["radius"];
163
164 if (selection.value("relative", false))
165 {
166 RowVectorNd mesh_width = mesh_bbox[1] - mesh_bbox[0];
167 center_ = mesh_width.cwiseProduct(center_) + mesh_bbox[0];
168 radius2_ = mesh_width.norm() * radius2_;
169 }
170
172
173 id_ = selection["id"];
174 }
175
176 bool SphereSelection::inside(const size_t p_id, const std::vector<int> &vs, const RowVectorNd &p) const
177 {
178 assert(center_.size() == p.size());
179
180 return (p - center_).squaredNorm() <= radius2_;
181 }
182
183 // ------------------------------------------------------------------------
184
186 const json &selection,
187 const Selection::BBox &mesh_bbox)
188 : Selection(selection["id"].get<int>())
189 {
190 point_ = selection["p1"];
191 RowVectorNd p2 = selection["p2"];
192 radius2_ = selection["radius"];
193
194 if (selection.value("relative", false))
195 {
196 RowVectorNd mesh_width = mesh_bbox[1] - mesh_bbox[0];
197 point_ = mesh_width.cwiseProduct(point_) + mesh_bbox[0];
198 p2 = mesh_width.cwiseProduct(p2) + mesh_bbox[0];
199 radius2_ = mesh_width.norm() * radius2_;
200 }
201
203 height_ = (point_ - p2).norm();
204 axis_ = (p2 - point_).normalized();
205
206 id_ = selection["id"];
207 }
208
209 bool CylinderSelection::inside(const size_t p_id, const std::vector<int> &vs, const RowVectorNd &p) const
210 {
211 assert(point_.size() == p.size());
212
213 const RowVectorNd v = p - point_;
214 const double proj = axis_.dot(v);
215
216 if (proj < 0)
217 return false;
218 if (proj > height_)
219 return false;
220
221 return (v - axis_ * proj).squaredNorm() <= radius2_;
222 }
223
224 // ------------------------------------------------------------------------
225
227 const json &selection,
228 const Selection::BBox &mesh_bbox)
229 : Selection(selection["id"].get<int>())
230 {
231 position_ = selection["position"];
232
233 if (selection["axis"].is_string())
234 {
235 std::string axis = selection["axis"];
236 int sign = axis[0] == '-' ? -1 : 1;
237 int dim = std::tolower(axis.back()) - 'x' + 1;
238 assert(dim >= 1 && dim <= 3);
239 axis_ = sign * dim;
240 }
241 else
242 {
243 assert(selection["axis"].is_number_integer());
244 axis_ = selection["axis"];
245 assert(std::abs(axis_) >= 1 && std::abs(axis_) <= 3);
246 }
247
248 if (selection.value("relative", false))
249 {
250 int dim = std::abs(axis_) - 1;
251 position_ = (mesh_bbox[1][dim] - mesh_bbox[0][dim]) * position_ + mesh_bbox[0][dim];
252 }
253 }
254
255 bool AxisPlaneSelection::inside(const size_t p_id, const std::vector<int> &vs, const RowVectorNd &p) const
256 {
257 const double v = p[std::abs(axis_) - 1];
258
259 if (axis_ > 0)
260 return v >= position_;
261 else
262 return v <= position_;
263 }
264
265 // ------------------------------------------------------------------------
266
268 const json &selection,
269 const Selection::BBox &mesh_bbox)
270 : Selection(selection["id"].get<int>())
271 {
272 normal_ = selection["normal"];
273 normal_.normalize();
274 if (selection.contains("point"))
275 {
276 point_ = selection["point"];
277 if (selection.value("relative", false))
278 point_ = (mesh_bbox[1] - mesh_bbox[0]).cwiseProduct(point_) + mesh_bbox[0];
279 }
280 else
281 point_ = normal_ * selection.value("offset", 0.0);
282 }
283
284 bool PlaneSelection::inside(const size_t p_id, const std::vector<int> &vs, const RowVectorNd &p) const
285 {
286 assert(p.size() == normal_.size());
287 const RowVectorNd pp = p - point_;
288 return pp.dot(normal_) >= 0;
289 }
290
291 // ------------------------------------------------------------------------
292
294 const std::vector<int> &ids)
295 : Selection(0),
296 ids_(ids)
297 {
298 }
299
300 int SpecifiedSelection::id(const size_t element_id, const std::vector<int> &vs, const RowVectorNd &p) const
301 {
302 return ids_.at(element_id);
303 }
304
305 // ------------------------------------------------------------------------
306
308 const std::string &file_path,
309 const int id_offset)
310 {
311 Eigen::MatrixXi mat;
312 const auto ok = io::read_matrix(file_path, mat);
313 if (!ok)
314 {
315 logger().error("Unable to open selection file \"{}\"!", file_path);
316 return;
317 }
318
319 if (mat.cols() == 1)
320 {
321 for (int k = 0; k < mat.size(); ++k)
322 this->ids_.push_back(mat(k) + id_offset);
323 }
324 else
325 {
326 data_.resize(mat.rows());
327
328 for (int i = 0; i < mat.rows(); ++i)
329 {
330 data_[i].first = mat(i, 0) + id_offset;
331
332 for (int j = 1; j < mat.cols(); ++j)
333 {
334 data_[i].second.push_back(mat(i, j));
335 }
336
337 std::sort(data_[i].second.begin(), data_[i].second.end());
338 }
339 }
340 }
341
342 bool FileSelection::inside(const size_t p_id, const std::vector<int> &vs, const RowVectorNd &p) const
343 {
344 if (data_.empty())
345 return SpecifiedSelection::inside(p_id, vs, p);
346
347 std::vector<int> tmp;
348 for (const auto &t : data_)
349 {
350 if (t.second == vs)
351 return true;
352 }
353 return false;
354 }
355
356 int FileSelection::id(const size_t element_id, const std::vector<int> &vs, const RowVectorNd &p) const
357 {
358 if (data_.empty())
359 return SpecifiedSelection::id(element_id, vs, p);
360
361 std::vector<int> tmp;
362 for (const auto &t : data_)
363 {
364 if (t.second == vs)
365 return t.first;
366 }
367 return -1;
368 }
369} // namespace polyfem::utils
AxisPlaneSelection(const json &selection, const BBox &mesh_bbox)
bool inside(const size_t p_id, const std::vector< int > &vs, const RowVectorNd &p) const override
bool inside(const size_t p_id, const std::vector< int > &vs, const RowVectorNd &p) const override
BoxSelection(const json &selection, const BBox &mesh_bbox)
Definition Selection.cpp:79
int id(const size_t element_id, const std::vector< int > &vs, const RowVectorNd &p) const override
BoxSideSelection(const json &selection, const BBox &mesh_bbox)
CylinderSelection(const json &selection, const BBox &mesh_bbox)
bool inside(const size_t p_id, const std::vector< int > &vs, const RowVectorNd &p) const override
int id(const size_t element_id, const std::vector< int > &vs, const RowVectorNd &p) const override
FileSelection(const std::string &file_path, const int id_offset=0)
bool inside(const size_t p_id, const std::vector< int > &vs, const RowVectorNd &p) const override
std::vector< std::pair< int, std::vector< int > > > data_
PlaneSelection(const json &selection, const BBox &mesh_bbox)
bool inside(const size_t p_id, const std::vector< int > &vs, const RowVectorNd &p) const override
std::array< RowVectorNd, 2 > BBox
Definition Selection.hpp:13
static std::shared_ptr< Selection > build(const json &j_selections, const BBox &mesh_bbox, const std::string &root_path="")
Build a selection objects from a JSON selection.
Definition Selection.cpp:16
static std::vector< std::shared_ptr< utils::Selection > > build_selections(const json &j_selections, const BBox &mesh_bbox, const std::string &root_path="")
Build a vector of selection objects from a JSON selection(s).
Definition Selection.cpp:45
virtual bool inside(const size_t p_id, const std::vector< int > &vs, const RowVectorNd &p) const override
virtual int id(const size_t element_id, const std::vector< int > &vs, const RowVectorNd &p) const override
bool inside(const size_t p_id, const std::vector< int > &vs, const RowVectorNd &p) const override
SphereSelection(const json &selection, const BBox &mesh_bbox)
bool read_matrix(const std::string &path, Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > &mat)
Reads a matrix from a file. Determines the file format based on the path's extension.
Definition MatrixIO.cpp:18
std::string resolve_path(const std::string &path, const std::string &input_file_path, const bool only_if_exists=false)
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42
nlohmann::json json
Definition Common.hpp:9
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
void log_and_throw_error(const std::string &msg)
Definition Logger.cpp:71