ATTPCROOT  0.3.0-alpha
A ROOT-based framework for analyzing data from active target detectors
pointcloud.cxx
Go to the documentation of this file.
1 //
2 // pointcloud.cpp
3 // Classes and functions for 3D points and clouds thereof.
4 //
5 // Author: Jens Wilberg, Lukas Aymans, Christoph Dalitz
6 // Date: 2019-04-02
7 // License: see ../LICENSE
8 //
9 
10 #include "pointcloud.h"
11 
12 #include "util.h"
13 
14 #include <algorithm>
15 #include <cmath>
16 #include <exception> // for exception
17 #include <memory> // for allocator_traits<>::value_type
18 #include <numeric>
19 #include <sstream> // IWYU pragma: keep
20 #include <stdexcept>
21 #include <string>
22 
23 #include "kdtree/kdtree.hpp"
24 
25 // a single 3D point
26 Point::Point(const std::vector<double> &point)
27 {
28  if (point.size() != 3) {
29  throw std::invalid_argument("Point::Point(): point must be of dimension 3");
30  }
31  this->x = point[0];
32  this->y = point[1];
33  this->z = point[2];
34 }
35 
36 Point::Point(const std::vector<double> &point, const std::set<size_t> &cluster_ids_in)
37 {
38  if (point.size() != 3) {
39  throw std::invalid_argument("Point::Point(): point must be of dimension 3");
40  }
41  this->x = point[0];
42  this->y = point[1];
43  this->z = point[2];
44  this->cluster_ids = cluster_ids_in;
45 }
46 
47 Point::Point(double xin, double yin, double zin)
48 {
49  this->x = xin;
50  this->y = yin;
51  this->z = zin;
52 }
53 
54 Point::Point(double xin, double yin, double zin, const std::set<size_t> &cluster_ids_in)
55 {
56  this->x = xin;
57  this->y = yin;
58  this->z = zin;
59  this->cluster_ids = cluster_ids_in;
60 }
61 
62 // representation of 3D point as std::vector.
63 std::vector<double> Point::as_vector() const
64 {
65  std::vector<double> point(3);
66  point[0] = this->x;
67  point[1] = this->y;
68  point[2] = this->z;
69  return point;
70 }
71 
72 // Euclidean norm of the point
73 double Point::norm() const
74 {
75  return sqrt((x * x) + (y * y) + (z * z));
76 }
77 
78 // squared euclidean norm of the point
79 double Point::squared_norm() const
80 {
81  return (x * x) + (y * y) + (z * z);
82 }
83 
85 {
86  x = other.x;
87  y = other.y;
88  z = other.z;
89  id = other.id;
90  return *this;
91 }
92 
93 bool Point::operator==(const Point &p) const
94 {
95  return (x == p.x && y == p.y && z == p.z);
96 }
97 
98 // formatted output of the point
99 std::ostream &operator<<(std::ostream &strm, const Point &p)
100 {
101  return strm << p.x << " " << p.y << " " << p.z;
102 }
103 
104 // vector addition
105 Point Point::operator+(const Point &p) const
106 {
107  Point ret;
108  ret.x = this->x + p.x;
109  ret.y = this->y + p.y;
110  ret.z = this->z + p.z;
111  return ret;
112 }
113 
114 // vector subtraction
115 Point Point::operator-(const Point &p) const
116 {
117  Point ret;
118  ret.x = this->x - p.x;
119  ret.y = this->y - p.y;
120  ret.z = this->z - p.z;
121  return ret;
122 }
123 
124 // scalar product (dot product)
125 double Point::operator*(const Point &p) const
126 {
127  return this->x * p.x + this->y * p.y + this->z * p.z;
128 }
129 
130 // scalar division
131 Point Point::operator/(double c) const
132 {
133  return Point(x / c, y / c, z / c);
134 }
135 
136 // scalar multiplication
137 Point operator*(Point x, double c)
138 {
139  Point v(c * x.x, c * x.y, c * x.z);
140  return v;
141 }
142 Point operator*(double c, Point x)
143 {
144  Point v(c * x.x, c * x.y, c * x.z);
145  return v;
146 }
147 
149 {
150  this->points2d = false;
151 }
152 
154 {
155  this->points2d = is2d;
156 }
157 
158 bool PointCloud::is2d() const
159 {
160  return this->points2d;
161 }
162 
163 // Split string *input* into substrings by *delimiter*. The result is
164 // returned in *result*
165 void split(const std::string &input, std::vector<std::string> &result, const char delimiter)
166 {
167  std::stringstream ss(input);
168  std::string element;
169 
170  while (std::getline(ss, element, delimiter)) {
171  result.push_back(element);
172  }
173 }
174 
175 //-------------------------------------------------------------------
176 // Load csv file.
177 // The csv file is split by *delimiter* and saved in *cloud*.
178 // Lines starting with '#' are ignored.
179 // If there are more than 3 columns all other are ignored and
180 // if there are two columns the PointCloud is set to 2D.
181 // Throws invalid_argument exception in case of problems.
182 //-------------------------------------------------------------------
183 void load_csv_file(const char *fname, PointCloud &cloud, const char delimiter, size_t skip)
184 {
185  std::ifstream infile(fname);
186  std::string line;
187  std::vector<std::string> items;
188  size_t count = 0, count2d = 0, skiped = 0, countpoints = 0;
189  if (infile.fail())
190  throw std::exception();
191  for (size_t i = 0; i < skip; ++i) {
192  // skip the header
193  std::getline(infile, line, '\n');
194  skiped++;
195  }
196  while (!infile.eof()) {
197 #ifdef WEBDEMO
198  if (countpoints > 1000)
199  throw std::length_error("Number of points limited to 1000 in demo mode");
200 #endif
201  // std::getline(infile, line, '\n');
202  std::getline(infile, line, '\n');
203  count++;
204 
205  // skip comments and empty lines
206  if (line[0] == '#' || line.empty() || line.find_first_not_of("\n\r\t ") == std::string::npos)
207  continue;
208 
209  countpoints++;
210  Point point;
211  split(line, items, delimiter);
212  if (items.size() < 2) {
213  std::ostringstream oss;
214  oss << "row " << count + skiped << ": "
215  << "To few columns!";
216  throw std::invalid_argument(oss.str());
217  } else if (items.size() == 2) {
218  items.push_back("0"); // z=0 for 2D data (size=2)
219  count2d++;
220  }
221  size_t column = 1;
222  try {
223  // create point
224  point.x = stod(items[0].c_str());
225  column++;
226  point.y = stod(items[1].c_str());
227  column++;
228  point.z = stod(items[2].c_str());
229  cloud.push_back(point);
230  } catch (std::invalid_argument e) {
231  std::ostringstream oss;
232  oss << "row " << count + skiped << " column " << column << ": " << e.what();
233  throw std::invalid_argument(oss.str());
234  }
235  items.clear();
236  }
237 
238  // check if the cloud is 2d or if a problem occurred
239  if (count2d && count2d != cloud.size()) {
240  throw std::invalid_argument("Mixed 2d and 3d points.");
241  } else if (count2d) {
242  cloud.set2d(true);
243  }
244 }
245 
246 //-------------------------------------------------------------------
247 // Smoothing of the PointCloud *cloud*.
248 // For every point the nearest neighbours in the radius *r* is searched
249 // and the centroid of this neighbours is computed. The result is
250 // returned in *result_cloud* and contains these centroids. The
251 // centroids are duplicated in the result cloud, so it has the same
252 // size and order as *cloud*.
253 //-------------------------------------------------------------------
254 void smoothen_cloud(const PointCloud &cloud, PointCloud &result_cloud, double r)
255 {
256  Kdtree::KdNodeVector nodes;
257 
258  // If the smooth-radius is zero return the unsmoothed pointcloud
259  if (r == 0) {
260  result_cloud = cloud;
261  return;
262  }
263 
264  // build kdtree
265  for (size_t i = 0; i < cloud.size(); ++i) {
266  nodes.push_back(cloud[i].as_vector());
267  }
268  Kdtree::KdTree kdtree(&nodes);
269 
270  for (size_t i = 0; i < cloud.size(); ++i) {
271  size_t result_size;
272  Point new_point, point = cloud[i];
273  Kdtree::KdNodeVector result;
274 
275  kdtree.range_nearest_neighbors(point.as_vector(), r, &result);
276  result_size = result.size();
277 
278  // compute the centroid with mean
279  std::vector<double> x_list;
280  std::vector<double> y_list;
281  std::vector<double> z_list;
282 
283  for (Kdtree::KdNodeVector::iterator it = result.begin(); it != result.end(); ++it) {
284  x_list.push_back(it->point[0]);
285  y_list.push_back(it->point[1]);
286  z_list.push_back(it->point[2]);
287  }
288 
289  new_point.x = std::accumulate(x_list.begin(), x_list.end(), 0.0) / result_size;
290 
291  new_point.y = std::accumulate(y_list.begin(), y_list.end(), 0.0) / result_size;
292 
293  new_point.z = std::accumulate(z_list.begin(), z_list.end(), 0.0) / result_size;
294 
295  result_cloud.push_back(new_point);
296  }
297 }
Point::norm
double norm() const
Definition: pointcloud.cxx:73
Point::operator=
Point & operator=(const Point &other)
Definition: pointcloud.cxx:84
Kdtree::KdTree::range_nearest_neighbors
void range_nearest_neighbors(const CoordPoint &point, double r, KdNodeVector *result)
Definition: kdtree.cxx:351
Kdtree::KdNodeVector
std::vector< KdNode > KdNodeVector
Definition: kdtree.hpp:32
Point::operator-
Point operator-(const Point &p) const
Definition: pointcloud.cxx:115
operator<<
std::ostream & operator<<(std::ostream &strm, const Point &p)
Definition: pointcloud.cxx:99
Point::Point
Point()
Definition: pointcloud.h:26
stod
double stod(const char *s)
Definition: util.cxx:20
Kdtree::KdTree
Definition: kdtree.hpp:68
PointCloud::is2d
bool is2d() const
Definition: pointcloud.cxx:158
Point::operator+
Point operator+(const Point &p) const
Definition: pointcloud.cxx:105
PointCloud::PointCloud
PointCloud()
Definition: pointcloud.cxx:148
Point::operator==
bool operator==(const Point &p) const
Definition: main.cpp:83
Point::squared_norm
double squared_norm() const
Definition: pointcloud.cxx:79
Point::id
int id
Definition: pointcloud.h:23
Point::x
float x
Definition: main.cpp:72
Point
Definition: main.cpp:71
operator*
Point operator*(Point x, double c)
Definition: pointcloud.cxx:137
pointcloud.h
Point::cluster_ids
std::set< size_t > cluster_ids
Definition: pointcloud.h:24
split
void split(const std::string &input, std::vector< std::string > &result, const char delimiter)
Definition: pointcloud.cxx:165
PointCloud
Definition: pointcloud.h:60
PointCloud::set2d
void set2d(bool is2d)
Definition: pointcloud.cxx:153
Point::y
float y
Definition: main.cpp:73
load_csv_file
void load_csv_file(const char *fname, PointCloud &cloud, const char delimiter, size_t skip)
Definition: pointcloud.cxx:183
Point::as_vector
std::vector< double > as_vector() const
Definition: pointcloud.cxx:63
smoothen_cloud
void smoothen_cloud(const PointCloud &cloud, PointCloud &result_cloud, double r)
Definition: pointcloud.cxx:254
Point::operator*
double operator*(const Point &p) const
Definition: pointcloud.cxx:125
kdtree.hpp
util.h
c
constexpr auto c
Definition: AtRadialChargeModel.cxx:20
Point::z
float z
Definition: main.cpp:74
Point::operator/
Point operator/(double c) const
Definition: pointcloud.cxx:131