ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
SelectionPolygon.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 // clang-format off
9 #include "visualization/utility/GLHelper.h" // must include first!
11 // clang-format on
12 
13 #include <Logging.h>
14 #include <ecvMesh.h>
15 #include <ecvPointCloud.h>
16 
20 
21 namespace cloudViewer {
22 namespace visualization {
23 
25  polygon_.clear();
26  is_closed_ = false;
29  return *this;
30 }
31 
33  // A valid polygon, either close or open, should have at least 2 vertices.
34  return polygon_.size() <= 1;
35 }
36 
37 Eigen::Vector2d SelectionPolygon::GetMin2DBound() const {
38  if (polygon_.empty()) {
39  return Eigen::Vector2d(0.0, 0.0);
40  }
41  auto itr_x = std::min_element(
42  polygon_.begin(), polygon_.end(),
43  [](const Eigen::Vector2d &a, const Eigen::Vector2d &b) {
44  return a(0) < b(0);
45  });
46  auto itr_y = std::min_element(
47  polygon_.begin(), polygon_.end(),
48  [](const Eigen::Vector2d &a, const Eigen::Vector2d &b) {
49  return a(1) < b(1);
50  });
51  return Eigen::Vector2d((*itr_x)(0), (*itr_y)(1));
52 }
53 
54 Eigen::Vector2d SelectionPolygon::GetMax2DBound() const {
55  if (polygon_.empty()) {
56  return Eigen::Vector2d(0.0, 0.0);
57  }
58  auto itr_x = std::max_element(
59  polygon_.begin(), polygon_.end(),
60  [](const Eigen::Vector2d &a, const Eigen::Vector2d &b) {
61  return a(0) < b(0);
62  });
63  auto itr_y = std::max_element(
64  polygon_.begin(), polygon_.end(),
65  [](const Eigen::Vector2d &a, const Eigen::Vector2d &b) {
66  return a(1) < b(1);
67  });
68  return Eigen::Vector2d((*itr_x)(0), (*itr_y)(1));
69 }
70 
72  // Standard scan conversion code. See reference:
73  // http://alienryderflex.com/polygon_fill/
74  if (isEmpty()) return;
75  is_closed_ = true;
77  std::fill(polygon_interior_mask_.data_.begin(),
78  polygon_interior_mask_.data_.end(), 0);
79  std::vector<int> nodes;
80  for (int y = 0; y < height; y++) {
81  nodes.clear();
82  for (size_t i = 0; i < polygon_.size(); i++) {
83  size_t j = (i + 1) % polygon_.size();
84  if ((polygon_[i](1) < y && polygon_[j](1) >= y) ||
85  (polygon_[j](1) < y && polygon_[i](1) >= y)) {
86  nodes.push_back(
87  (int)(polygon_[i](0) +
88  (y - polygon_[i](1)) /
89  (polygon_[j](1) - polygon_[i](1)) *
90  (polygon_[j](0) - polygon_[i](0)) +
91  0.5));
92  }
93  }
94  std::sort(nodes.begin(), nodes.end());
95  for (size_t i = 0; i < nodes.size(); i += 2) {
96  if (nodes[i] >= width) {
97  break;
98  }
99  if (nodes[i + 1] > 0) {
100  if (nodes[i] < 0) nodes[i] = 0;
101  if (nodes[i + 1] > width) nodes[i + 1] = width;
102  for (int x = nodes[i]; x < nodes[i + 1]; x++) {
103  polygon_interior_mask_.data_[x + y * width] = 1;
104  }
105  }
106  }
107  }
108 }
109 
110 std::shared_ptr<ccPointCloud> SelectionPolygon::CropPointCloud(
111  const ccPointCloud &input, const ViewControl &view) {
112  if (isEmpty()) {
113  return std::make_shared<ccPointCloud>();
114  }
115  switch (polygon_type_) {
117  return CropPointCloudInRectangle(input, view);
119  return CropPointCloudInPolygon(input, view);
121  default:
122  return std::shared_ptr<ccPointCloud>();
123  }
124 }
125 
126 std::shared_ptr<ccMesh> SelectionPolygon::CropTriangleMesh(
127  const ccMesh &input, const ViewControl &view) {
128  if (isEmpty()) {
129  return std::make_shared<ccMesh>(nullptr);
130  }
131  if (input.size() == 0 && input.getAssociatedCloud()) {
133  "ccMesh contains vertices, but no triangles; "
134  "cropping will always yield an empty "
135  "ccMesh.");
136  return std::make_shared<ccMesh>(nullptr);
137  }
138  switch (polygon_type_) {
140  return CropTriangleMeshInRectangle(input, view);
142  return CropTriangleMeshInPolygon(input, view);
144  default:
145  return std::shared_ptr<ccMesh>();
146  }
147 }
148 
149 std::shared_ptr<SelectionPolygonVolume>
151  auto volume = std::make_shared<SelectionPolygonVolume>();
152  const auto &editing_view = (const ViewControlWithEditing &)view;
153  if (!editing_view.IsLocked() ||
154  editing_view.GetEditingMode() == ViewControlWithEditing::FreeMode) {
155  return volume;
156  }
157  int idx = 0;
158  switch (editing_view.GetEditingMode()) {
161  volume->orthogonal_axis_ = "X";
162  idx = 0;
163  break;
166  volume->orthogonal_axis_ = "Y";
167  idx = 1;
168  break;
171  volume->orthogonal_axis_ = "Z";
172  idx = 2;
173  break;
174  default:
175  break;
176  }
177  for (const auto &point : polygon_) {
178  auto point3d = gl_util::Unproject(
179  Eigen::Vector3d(point(0), point(1), 1.0), view.GetMVPMatrix(),
180  view.GetWindowWidth(), view.GetWindowHeight());
181  point3d(idx) = 0.0;
182  volume->bounding_polygon_.push_back(point3d);
183  }
184  const auto &boundingbox = view.GetBoundingBox();
185  double axis_len =
186  boundingbox.GetMaxBound()(idx) - boundingbox.GetMinBound()(idx);
187  volume->axis_min_ = boundingbox.GetMinBound()(idx) - axis_len;
188  volume->axis_max_ = boundingbox.GetMaxBound()(idx) + axis_len;
189  return volume;
190 }
191 
192 std::shared_ptr<ccPointCloud> SelectionPolygon::CropPointCloudInRectangle(
193  const ccPointCloud &input, const ViewControl &view) {
194  return input.SelectByIndex(CropInRectangle(input.getPoints(), view));
195 }
196 
197 std::shared_ptr<ccPointCloud> SelectionPolygon::CropPointCloudInPolygon(
198  const ccPointCloud &input, const ViewControl &view) {
199  return input.SelectByIndex(CropInPolygon(input.getPoints(), view));
200 }
201 
202 std::shared_ptr<ccMesh> SelectionPolygon::CropTriangleMeshInRectangle(
203  const ccMesh &input, const ViewControl &view) {
204  return input.SelectByIndex(CropInRectangle(input.getVertices(), view));
205 }
206 
207 std::shared_ptr<ccMesh> SelectionPolygon::CropTriangleMeshInPolygon(
208  const ccMesh &input, const ViewControl &view) {
209  return input.SelectByIndex(CropInPolygon(input.getVertices(), view));
210 }
211 
212 std::vector<size_t> SelectionPolygon::CropInRectangle(
213  const std::vector<CCVector3> &input, const ViewControl &view) {
214  std::vector<size_t> output_index;
215  Eigen::Matrix4d mvp_matrix = view.GetMVPMatrix().cast<double>();
216  double half_width = (double)view.GetWindowWidth() * 0.5;
217  double half_height = (double)view.GetWindowHeight() * 0.5;
218  auto min_bound = GetMin2DBound();
219  auto max_bound = GetMax2DBound();
220  utility::ConsoleProgressBar progress_bar((int64_t)input.size(),
221  "Cropping geometry: ");
222  for (size_t i = 0; i < input.size(); i++) {
223  ++progress_bar;
224  const auto &point = input[i];
225  Eigen::Vector4d pos =
226  mvp_matrix * Eigen::Vector4d(point(0), point(1), point(2), 1.0);
227  if (pos(3) == 0.0) break;
228  pos /= pos(3);
229  double x = (pos(0) + 1.0) * half_width;
230  double y = (pos(1) + 1.0) * half_height;
231  if (x >= min_bound(0) && x <= max_bound(0) && y >= min_bound(1) &&
232  y <= max_bound(1)) {
233  output_index.push_back(i);
234  }
235  }
236  return output_index;
237 }
238 
239 std::vector<size_t> SelectionPolygon::CropInPolygon(
240  const std::vector<CCVector3> &input, const ViewControl &view) {
241  std::vector<size_t> output_index;
242  Eigen::Matrix4d mvp_matrix = view.GetMVPMatrix().cast<double>();
243  double half_width = (double)view.GetWindowWidth() * 0.5;
244  double half_height = (double)view.GetWindowHeight() * 0.5;
245  std::vector<double> nodes;
246  utility::ConsoleProgressBar progress_bar((int64_t)input.size(),
247  "Cropping geometry: ");
248  for (size_t k = 0; k < input.size(); k++) {
249  ++progress_bar;
250  const auto &point = input[k];
251  Eigen::Vector4d pos =
252  mvp_matrix * Eigen::Vector4d(point(0), point(1), point(2), 1.0);
253  if (pos(3) == 0.0) break;
254  pos /= pos(3);
255  double x = (pos(0) + 1.0) * half_width;
256  double y = (pos(1) + 1.0) * half_height;
257  nodes.clear();
258  for (size_t i = 0; i < polygon_.size(); i++) {
259  size_t j = (i + 1) % polygon_.size();
260  if ((polygon_[i](1) < y && polygon_[j](1) >= y) ||
261  (polygon_[j](1) < y && polygon_[i](1) >= y)) {
262  nodes.push_back(polygon_[i](0) +
263  (y - polygon_[i](1)) /
264  (polygon_[j](1) - polygon_[i](1)) *
265  (polygon_[j](0) - polygon_[i](0)));
266  }
267  }
268  std::sort(nodes.begin(), nodes.end());
269  auto loc = std::lower_bound(nodes.begin(), nodes.end(), x);
270  if (std::distance(nodes.begin(), loc) % 2 == 1) {
271  output_index.push_back(k);
272  }
273  }
274  return output_index;
275 }
276 
277 } // namespace visualization
278 } // namespace cloudViewer
int width
utility::CountingProgressReporter * progress_bar
int height
virtual Eigen::Vector3d GetMaxBound() const override
Returns max bounds for geometry coordinates.
Definition: ecvBBox.h:84
Triangular mesh.
Definition: ecvMesh.h:35
std::shared_ptr< ccMesh > SelectByIndex(const std::vector< size_t > &indices, bool cleanup=true) const
const std::vector< CCVector3 > & getVertices() const
ccGenericPointCloud * getAssociatedCloud() const override
Returns the vertices cloud.
Definition: ecvMesh.h:143
virtual unsigned size() const override
Returns the number of triangles.
A 3D cloud and its associated features (color, normals, scalar fields, etc.)
std::shared_ptr< ccPointCloud > SelectByIndex(const std::vector< size_t > &indices, bool invert=false) const
Function to select points from input ccPointCloud into output ccPointCloud.
std::vector< CCVector3 > & getPoints()
Image & Prepare(int width, int height, int num_of_channels, int bytes_per_channel)
Prepare Image properties and allocate Image buffer.
Definition: Image.h:109
std::vector< uint8_t > data_
Image storage buffer.
Definition: Image.h:227
std::vector< Eigen::Vector2d > polygon_
std::shared_ptr< ccMesh > CropTriangleMesh(const ccMesh &input, const ViewControl &view)
virtual Eigen::Vector2d GetMax2DBound() const override
std::shared_ptr< ccPointCloud > CropPointCloud(const ccPointCloud &input, const ViewControl &view)
std::shared_ptr< SelectionPolygonVolume > CreateSelectionPolygonVolume(const ViewControl &view)
virtual Eigen::Vector2d GetMin2DBound() const override
View controller for visualizer.
Definition: ViewControl.h:25
const ccBBox & GetBoundingBox() const
Definition: ViewControl.h:131
gl_util::GLMatrix4f GetMVPMatrix() const
Definition: ViewControl.h:156
#define LogWarning(...)
Definition: Logging.h:72
Eigen::Vector3d Unproject(const Eigen::Vector3d &screen_point, const GLMatrix4f &mvp_matrix, const int width, const int height)
Definition: GLHelper.cpp:97
Generic file read and write utility for python interface.
Definition: lsd.c:149