geolib2
Shape.cpp
Go to the documentation of this file.
1 #include "geolib/Shape.h"
2 #include "geolib/Box.h"
3 
4 #include <geolib/serialization.h>
5 
6 #include <console_bridge/console.h>
7 
8 #include <cmath>
9 #include <stdexcept>
10 
11 namespace geo {
12 
21 bool check_linesegment(const Vector3& p, const double radius2, const Vector3& a, const Vector3& ab) {
22  geo::Vector3 ap = p-a;
23  double d1_2 = ap.length2(); // Distance between a and p, squared
24  double d2 = ab.dot(ap); // Dot product between ab and ap; projection of p on linesegment ab
25 
26  if (d2 < 0)
27  // If ab dot ap < 0, there can't be any intersection, because the orientated linesegment points away
28  return false;
29 
30  double d2_2 = d2*d2 / ab.length2(); // Distance between a and the projection of p on linesegment ab, squared
31  return d2_2 <= ab.length2() * (1 + 1e-9) && d1_2-d2_2 <= radius2 * (1 + 1e-9); // To prevent any numerical issues
32 }
33 
38 {
39 public:
40  LineSegment(const geo::Vector3& a, const geo::Vector3& b) : a_(a), b_(b)
41  {
42  U_ = b_-a_;
43  V_ = a_.cross(b_);
44  }
45 
46  inline const geo::Vector3& a() const { return a_; }
47  inline const geo::Vector3& b() const { return b_; }
48  inline const geo::Vector3& U() const { return U_; }
49  inline const geo::Vector3& V() const { return V_; }
50 
51 protected:
52  const geo::Vector3& a_, b_;
54 };
56 
68 double side_product(const geo::LS& p, const geo::LS& q) {
69  return p.U().dot(q.V()) + q.U().dot(p.V());
70 }
71 
80 bool line_linesegment_intersection(const geo::LS& l, const geo::LS& ls, const geo::Vector3& outside)
81 {
82  double s = side_product(l, ls);
83  if (s !=0 ) {
84  return false;
85  }
86 
87  double s1 = side_product(l, geo::LS(outside, ls.a()));
88  double s2 = side_product(l, geo::LS(ls.b(), outside));
89 
90  return s1 * s2 >= 0;
91 }
92 
100 bool compute_2D_intersection(const geo::Triangle& t, const geo::LS& line)
101 {
102  double s2,s3;
103 
104  // Skipping s1, as we don't change v1 and v2, so that edge, doesn't change
105 
106  geo::Vector3 p3;
107  for (uint i=0; i<3; ++i) {
108  p3 = geo::Vector3(t.p3());
109  p3[i] += 1; // Move point outside the plane
110 
111  s2 = side_product(line, geo::LS(t.p2(), p3));
112  s3 = side_product(line, geo::LS(p3, t.p1()));
113 
114  if (!(std::abs(s2)<1e-16 && std::abs(s3)<1e-16)) // s2==0 && s3==0
115  break;
116  }
117 
118  for (uint i=0; i<3; ++i) {
119  const geo::Vector3& p1 = t[i];
120  const geo::Vector3& p2 = t[(i+1) % 3];
121  s2 = side_product(line, geo::LS(p2, p3));
122  s3 = side_product(line, geo::LS(p3, p1));
123 
124  if (s2*s3 >= 0) { // s2 and s3 have the same sign and are non zero
125  for (uint i=0; i<3; ++i) {
126  const geo::Vector3& v1 = t[i];
127  const geo::Vector3& v2 = t[(i+1) % 3];
128  if (line_linesegment_intersection(line, geo::LS(v1, v2), p3)) {
129  return true;
130  }
131  }
132  }
133  }
134  return false;
135 }
136 
137 const std::string Shape::TYPE = "mesh";
138 
139 Shape::Shape() : mesh_(), bounding_box_cache_valid_(false) {
140 }
141 
143 }
144 
145 Shape* Shape::clone() const {
146  return new Shape(*this);
147 }
148 
149 bool Shape::intersect(const Ray& /*r*/, float /*t0*/, float /*t1*/, double& /*distance*/) const {
150  throw std::logic_error("intersect(Ray, float, float, double) not implemented");
151  return false;
152 }
153 
160 bool Shape::intersect(const Vector3& p, const double radius) const {
161  const Mesh& mesh = getMesh();
162  if (p.length()-radius > mesh.getMaxRadius()) {
163  return false;
164  }
165 
166  if (radius > 0.) {
167  const double radius2 = radius*radius;
168  // load triangles
169  const std::vector<geo::Vector3>& t_points = mesh.getPoints();
170  const std::vector<TriangleI>& triangles_i = mesh.getTriangleIs();
171  for (std::vector<TriangleI>::const_iterator it = triangles_i.cbegin(); it != triangles_i.cend(); ++it) {
172  const Vector3 &v1 = t_points[it->i1_];
173  const Vector3 &v2 = t_points[it->i2_];
174  const Vector3 &v3 = t_points[it->i3_];
175 
176  // check endpoints
177  if ((v1-p).length2() < radius2)
178  return true;
179  if ((v2-p).length2() < radius2)
180  return true;
181  if ((v3-p).length2() < radius2)
182  return true;
183 
184  Vector3 e1 = v2 - v1;
185  Vector3 e2 = v3 - v2;
186  Vector3 e3 = v1 - v3;
187 
188  // check line segments
189  if (check_linesegment(p, radius2, v1, e1))
190  return true;
191  if (check_linesegment(p, radius2, v2, e2))
192  return true;
193  if (check_linesegment(p, radius2, v3, e3))
194  return true;
195 
196  // check surface
197  Vector3 n = e1.cross(e2); // normal vector
198  double projected_distance2 = (v1-p).dot(n); // projected_distance^2 = ((p-v1) dot n)^2 / |n|^2
199  projected_distance2 = projected_distance2 * projected_distance2 / n.length2();
200 
201  if (projected_distance2 > radius2)
202  // If projection distance is too big, no intersection for this triangle
203  continue;
204 
205  // check that the projection falls within the triangle
206  Vector3 q = p - sqrt(projected_distance2) * n.normalized();
207 
208  Vector3 cross1 = e1.cross(q-v1);
209  Vector3 cross2 = e2.cross(q-v2);
210  Vector3 cross3 = e3.cross(q-v3);
211 
212  double dot1 = cross1.dot(cross2);
213  double dot2 = cross2.dot(cross3);
214  double dot3 = cross3.dot(cross1);
215 
216  if (dot1 > 0 && dot2 > 0 && dot3 > 0)
217  return true;
218  }
219  }
220  return contains(p);
221 }
222 
229 bool Shape::contains(const Vector3& p) const {
230  const Mesh& mesh = getMesh();
231  if (p.length2() > mesh.getSquaredMaxRadius()) {
232  return false;
233  }
234 
235  int intersect_count = 0;
236 
237  // determine plucker coordinates of line p
238  Vector3 p_out = Vector3(1.1 * mesh.getMaxRadius(), 0, 0);
239  geo::LS line(p, p_out);
240 
241  // create hit maps
243  std::map<uint, int> vertex_hit_map;
244 
245  // load triangles
246  const std::vector<geo::Vector3>& t_points = mesh.getPoints();
247  const std::vector<TriangleI>& triangles_i = mesh.getTriangleIs();
248  for (auto it = triangles_i.cbegin(); it != triangles_i.cend(); ++it) {
249  const Vector3 &v1 = t_points[it->i1_];
250  const Vector3 &v2 = t_points[it->i2_];
251  const Vector3 &v3 = t_points[it->i3_];
252 
253  geo::LS e1(v1, v2);
254  geo::LS e2(v2, v3);
255  geo::LS e3(v3, v1);
256 
257  double s1 = side_product(line, e1);
258  double s2 = side_product(line, e2);
259  double s3 = side_product(line, e3);
260 
261  // Determine whether v1, v2 and v3 circle line p (counter) clockwise.
262  bool clockwise = s1 >= -1e-16 && s2 >= -1e-16 && s3 >= -1e-16; // >=0
263  ushort clockwise_nz = (s1 > 1e-16) + (s2 > 1e-16) + (s3 > 1e-16); // >0
264  bool counterclockwise = s1 <= 1e-16 && s2 <= 1e-16 && s3 <= 1e-16; // <=0
265  ushort counterclockwise_nz = (s1 < -1e-16) + (s2 < -1e-16) + (s3 < -1e-16); // <0
266 
267 
268  if (!clockwise && !counterclockwise) {
269  // No intersection
270  CONSOLE_BRIDGE_logDebug("No intersection");
271  continue;
272  }
273  else if (clockwise && counterclockwise) { // s1=0; s2=0; s3=0
274  // Coplanar
275  CONSOLE_BRIDGE_logDebug("Coplanar");
276  if (compute_2D_intersection(Triangle(v1, v2, v3), line)) {
277  CONSOLE_BRIDGE_logDebug("Coplanar in the triangle");
278  return true;
279  }
280  CONSOLE_BRIDGE_logDebug("Coplanar outside the triangle");
281  continue;
282  }
283  else if ((clockwise && clockwise_nz > 0) || (counterclockwise && counterclockwise_nz > 0)) {
284  // 3 same sign -> proper intersection
285  // 2 same sign, 1 zero -> intersection on edge
286  // 2 zero, 1 non-zero -> intersection at vertex
287 
288  // Now check whether the intersection lies in the line segment
289  geo::LS l4(p_out, v1);
290  geo::LS l5(v1, p);
291 
292  double s4 = side_product(l4, e2);
293  double s5 = side_product(l5, e2);
294 
295  if (s4*s5>=-1e-16) { // s4*s5 >= 0
296  if (clockwise_nz == 2 || counterclockwise_nz == 2) {
297  // Edge intersection
298  CONSOLE_BRIDGE_logDebug("Edge intersection");
299  uint i1, i2;
300  if (std::abs(s1) < 1e-16) {
301  i1 = it->i1_;
302  i2 = it->i2_;
303  }
304  else if (std::abs(s2) < 1e-16) {
305  i1 = it->i2_;
306  i2 = it->i3_;
307  }
308  else { // if (std::abs(s3) < 1e-16)
309  i1 = it->i3_;
310  i2 = it->i1_;
311  }
312  uint& i_min = i1, i_max = i2;
313  if (i_max < i_min)
314  std::swap(i_min, i_max);
315  if (std::abs((p-t_points[i_min]).length() + (t_points[i_max]-p).length() - (t_points[i_max]-t_points[i_min]).length()) <= 1e-12) { // For numerical issues
316  CONSOLE_BRIDGE_logDebug("Point is on an edge of the mesh");
317  return true;
318  }
319 
320  // Make sure map entries exist
321  auto search = edge_hit_map.find(i_min);
322  if (search == edge_hit_map.end()) {
323  edge_hit_map[i_min] = std::map<uint, int>();
324  }
325  auto search2 = edge_hit_map[i_min].find(i_max-i_min-1);
326  if (search2 == edge_hit_map[i_min].end()) {
327  edge_hit_map[i_min][i_max-i_min-1] = 0;
328  }
329 
330  int& hit_entry = edge_hit_map[i_min][i_max-i_min-1];
331  if (hit_entry == clockwise-counterclockwise) {
332  // Don't count an edge multiple times with the same direction
333  continue;
334  }
335  else {
336  hit_entry += clockwise-counterclockwise;
337  }
338  }
339  else if (clockwise_nz == 1 || counterclockwise_nz == 1) {
340  // vertex intersection
341  CONSOLE_BRIDGE_logDebug("Vertex intersection");
342  // vertex intersection
343  uint i;
344  if (std::abs(s2) > 1e-16)
345  i = it->i1_;
346  else if (std::abs(s3) > 1e-16)
347  i = it->i2_;
348  else // if (std::abs(s1) > 1e-16)
349  i = it->i3_;
350 
351  if (t_points[i] == p) {
352  CONSOLE_BRIDGE_logDebug("Point is a vertex of the mesh");
353  return true;
354  }
355 
356  // Make sure map entries exist
357  auto search = vertex_hit_map.find(i);
358  if (search == vertex_hit_map.end()) {
359  vertex_hit_map[i] = 0;
360  }
361 
362  int& hit_entry = vertex_hit_map[i];
363  if (hit_entry == clockwise-counterclockwise) {
364  // Don't count a vertex multiple times with the same direction
365  continue;
366  }
367  else {
368  hit_entry += clockwise-counterclockwise;
369  }
370  }
371  else {
372  // Proper intersection
373  CONSOLE_BRIDGE_logDebug("Proper intersection");
374  // https://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/
375  double s = geo::triangleArea(v1, v2, v3);
376  if (s < 1e-12)
377  CONSOLE_BRIDGE_logError("Triangle has a zero area");
378 
379  double s1 = geo::triangleArea(v2, p, v3);
380  double s2 = geo::triangleArea(p, v1, v3);
381  double s3 = geo::triangleArea(p, v1, v2);
382 
383  if (std::abs(s1 + s2 + s3 - s) < 1e-12) {
384  CONSOLE_BRIDGE_logDebug("Point is on a triangle");
385  return true;
386  }
387  }
388  // Update intersect count
389  intersect_count += clockwise-counterclockwise;
390  }
391  }
392  }
393 
394  if (intersect_count < 0 || intersect_count > 1) {
395  CONSOLE_BRIDGE_logError("intersect_count is %i, it should be 0 or 1! Is your shape constructed correctly?", intersect_count);
396  return false;
397  }
398 
399  return intersect_count > 0;
400 }
401 
404  {
405  const Mesh& mesh = getMesh();
406  const std::vector<geo::Vector3>& points = mesh.getPoints();
407  double x_min = 1e9, y_min = 1e9, z_min = 1e9;
408  double x_max = -1e9, y_max = -1e9, z_max = -1e9;
409  for (auto it = points.cbegin(); it != points.cend(); ++it)
410  {
411  x_min = std::min<double>(it->x, x_min);
412  x_max = std::max<double>(it->x, x_max);
413  y_min = std::min<double>(it->y, y_min);
414  y_max = std::max<double>(it->y, y_max);
415  z_min = std::min<double>(it->z, z_min);
416  z_max = std::max<double>(it->z, z_max);
417  }
418  bounding_box_min_cache_ = geo::Vector3(x_min, y_min, z_min);
419  bounding_box_max_cache_ = geo::Vector3(x_max, y_max, z_max);
421  }
423 }
424 
425 const Mesh& Shape::getMesh() const {
426  return mesh_;
427 }
428 
429 void Shape::setMesh(const Mesh& mesh) {
431  mesh_ = mesh;
432 }
433 
434 double Shape::getMaxRadius() const {
435  const Mesh& mesh = getMesh();
436  return mesh.getMaxRadius();
437 }
438 
439 bool Shape::write(std::ostream& output) const {
440  std::string type = "mesh ";
441  output.write(type.c_str(), type.size());
442 
443  const Mesh& mesh = getMesh();
444  const std::vector<geo::Vector3>& points = mesh.getPoints();
445  int p_size = points.size();
446  output.write((char*)&p_size, sizeof(p_size));
447  for(std::vector<geo::Vector3>::const_iterator it = points.begin(); it != points.end(); ++it) {
448  const geo::Vector3& v = *it;
449  double x = v.x;
450  double y = v.y;
451  double z = v.z;
452 
453  output.write((char*)&x, sizeof(x));
454  output.write((char*)&y, sizeof(y));
455  output.write((char*)&z, sizeof(z));
456  }
457 
458  const std::vector<geo::TriangleI> triangles = mesh.getTriangleIs();
459  int t_size = triangles.size();
460  output.write((char*)&t_size, sizeof(t_size));
461  for(std::vector<geo::TriangleI>::const_iterator it = triangles.begin(); it != triangles.end(); ++it) {
462  const geo::TriangleI& t = *it;
463  output.write((char*)&t.i1_, sizeof(t.i1_));
464  output.write((char*)&t.i2_, sizeof(t.i2_));
465  output.write((char*)&t.i3_, sizeof(t.i3_));
466  }
467 
468  return true;
469 }
470 
472  ShapePtr shape(new Shape());
473 
474  int p_size;
475  input.read((char*)&p_size, sizeof(p_size));
476 
477  for(int i = 0; i < p_size; ++i) {
478  double x, y, z;
479  input.read((char*)&x, sizeof(x));
480  input.read((char*)&y, sizeof(y));
481  input.read((char*)&z, sizeof(z));
482  shape->mesh_.addPoint(x, y, z);
483  }
484 
485  int t_size;
486  input.read((char*)&t_size, sizeof(t_size));
487 
488  for(int i = 0; i < t_size; ++i) {
489  int i1, i2, i3;
490  input.read((char*)&i1, sizeof(i1));
491  input.read((char*)&i2, sizeof(i2));
492  input.read((char*)&i3, sizeof(i3));
493  shape->mesh_.addTriangle(i1, i2, i3);
494  }
495 
496  return shape;
497 }
498 
499 }
geo::Vector3::y
const real & y() const
Definition: matrix.h:32
geo::Shape::intersect
virtual bool intersect(const Ray &r, float t0, float t1, double &distance) const
intersect: currently always throws a logic error
Definition: Shape.cpp:149
Shape.h
geo::Vector3
Vec3 Vector3
Definition: datatypes.h:32
geo::Shape::setMesh
virtual void setMesh(const Mesh &mesh)
set the Mesh Any child classes should throw a std::logic_error in case the mesh should not be changed...
Definition: Shape.cpp:429
geo::Shape::bounding_box_max_cache_
Vector3 bounding_box_max_cache_
Definition: Shape.h:118
std::string
std::shared_ptr
geo
Definition: Box.h:6
geo::Mesh::getTriangleIs
const std::vector< TriangleI > & getTriangleIs() const
Definition: Mesh.cpp:46
t
Timer t
a
void a()
std::vector< geo::Vector3 >
std::map::find
T find(T... args)
std::string::size
T size(T... args)
geo::Box
Definition: Box.h:15
geo::Shape::read
static ShapePtr read(std::istream &input)
read serialised data from an input stream and create a shape
Definition: Shape.cpp:471
geo::LineSegment::a_
const geo::Vector3 & a_
Definition: Shape.cpp:52
std::istream::read
T read(T... args)
geo::LS
geo::LineSegment LS
Definition: Shape.cpp:55
geo::LineSegment::a
const geo::Vector3 & a() const
Definition: Shape.cpp:46
geo::Mesh::getMaxRadius
double getMaxRadius() const
Definition: Mesh.cpp:125
geo::Vector3::length
real length() const
Definition: matrix.h:43
geo::Shape::getMesh
virtual const Mesh & getMesh() const
return the mesh defining the shape
Definition: Shape.cpp:425
cmath
std::ostream::write
T write(T... args)
geo::Mesh::getSquaredMaxRadius
double getSquaredMaxRadius() const
Definition: Mesh.cpp:116
geo::LineSegment::V
const geo::Vector3 & V() const
Definition: Shape.cpp:49
geo::LineSegment::LineSegment
LineSegment(const geo::Vector3 &a, const geo::Vector3 &b)
Definition: Shape.cpp:40
stdexcept
geo::Shape::write
virtual bool write(std::ostream &output) const
write, serialise the shape
Definition: Shape.cpp:439
geo::TriangleI
Definition: Mesh.h:11
geo::Mesh::getPoints
const std::vector< geo::Vector3 > & getPoints() const
Definition: Mesh.cpp:42
geo::check_linesegment
bool check_linesegment(const Vector3 &p, const double radius2, const Vector3 &a, const Vector3 &ab)
Check whether a point p is within distance radius of the line segment. The starting point is describe...
Definition: Shape.cpp:21
geo::side_product
double side_product(const geo::LS &p, const geo::LS &q)
Determining the direction in which q passes around p. The direction is taken from looking from the ba...
Definition: Shape.cpp:68
geo::Shape::getMaxRadius
virtual double getMaxRadius() const
Calculate the maximum distance from the origin of the shape to any point of the shape.
Definition: Shape.cpp:434
geo::triangleArea
double triangleArea(const Vector3 &p1, const Vector3 &p2, const Vector3 &p3)
Definition: Triangle.cpp:5
geo::Vector3::dot
real dot(const Vector3 &v) const
Definition: matrix.h:56
std::ostream
geo::compute_2D_intersection
bool compute_2D_intersection(const geo::Triangle &t, const geo::LS &line)
Check if a line that is in the same plane as the triangle does actually intersect with the triangle h...
Definition: Shape.cpp:100
geo::Triangle
Definition: Triangle.h:10
std::string::c_str
T c_str(T... args)
geo::Shape::TYPE
static const std::string TYPE
Definition: Shape.h:98
geo::Ray
Definition: Ray.h:10
std::logic_error
geo::Vector3
Definition: matrix.h:12
std::map
geo::Vector3::z
const real & z() const
Definition: matrix.h:33
geo::Shape::getBoundingBox
virtual Box getBoundingBox() const
Returns the smallest box which includes all mesh points. Box is not rotated, but matches the axis of ...
Definition: Shape.cpp:402
std::swap
T swap(T... args)
geo::LineSegment::b_
const geo::Vector3 b_
Definition: Shape.cpp:52
std::vector::cbegin
T cbegin(T... args)
geo::Shape::bounding_box_min_cache_
Vector3 bounding_box_min_cache_
Definition: Shape.h:117
geo::Shape::contains
virtual bool contains(const Vector3 &p) const
Determines whether a point p lies within the shape.
Definition: Shape.cpp:229
geo::Vector3::x
const real & x() const
Definition: matrix.h:31
serialization.h
geo::LineSegment::V_
geo::Vector3 V_
Definition: Shape.cpp:53
Box.h
std::vector::cend
T cend(T... args)
geo::Vector3::length2
real length2() const
Definition: matrix.h:47
geo::line_linesegment_intersection
bool line_linesegment_intersection(const geo::LS &l, const geo::LS &ls, const geo::Vector3 &outside)
Check if linesegment does intersect with line https://members.loria.fr/SLazard/ARC-Visi3D/Pant-projec...
Definition: Shape.cpp:80
geo::LineSegment::U_
geo::Vector3 U_
Definition: Shape.cpp:53
geo::Shape::mesh_
Mesh mesh_
Should not be read or written to directly in general. Use setMesh and getMesh to write respectively r...
Definition: Shape.h:112
geo::Shape::bounding_box_cache_valid_
bool bounding_box_cache_valid_
Definition: Shape.h:116
geo::Shape::Shape
Shape()
Definition: Shape.cpp:139
geo::LineSegment::U
const geo::Vector3 & U() const
Definition: Shape.cpp:48
std::istream
geo::Mesh
Definition: Mesh.h:25
geo::Shape::~Shape
virtual ~Shape()
Definition: Shape.cpp:142
geo::Shape
A geometric description of a shape.
Definition: Shape.h:19
geo::LineSegment
Definition: Shape.cpp:37
geo::LineSegment::b
const geo::Vector3 & b() const
Definition: Shape.cpp:47
geo::Shape::clone
virtual Shape * clone() const
Definition: Shape.cpp:145