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