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