LibreCAD
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
intersect.cpp
Go to the documentation of this file.
1 #include "intersect.h"
2 
3 #include <cmath>
4 #include <iostream>
6 #include "cad/primitive/arc.h"
7 #include "cad/primitive/circle.h"
9 #include "cad/primitive/line.h"
11 #include "cad/primitive/spline.h"
13 
14 using namespace lc;
15 
16 
17 Intersect::Intersect(Method method, double tolerance) : _method(method), _tolerance(tolerance) {
18 }
19 
20 std::vector<geo::Coordinate> Intersect::result() const {
21  return _intersectionPoints;
22 }
23 
24 // Vector
26  geovisit(v1, v2);
27  return false;
28 }
29 
31  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
32  return false;
33 }
34 
36  geovisit(v, lc::geo::Vector(l.start(), l.end()));
37  return false;
38 }
39 
40 bool Intersect::operator()(const lc::geo::Vector &v, const lc::geo::Circle &circle) {
41  geovisit(v, lc::geo::Arc(circle.center(), circle.radius(), -M_PI, M_PI));
42  return false;
43 }
44 
45 bool Intersect::operator()(const lc::geo::Vector &line, const lc::entity::Arc &arc) {
46  geovisit(line, arc);
47  return false;
48 }
49 
51  // TODO Check if point's are on path
52 
53  // TODO Check if the coords we get back are good
54  return false;
55  auto &&coords = maths::Intersection::LineQuad(v.equation(), e.equation());
56  if (coords.size()>0) {
57  std::cerr << __PRETTY_FUNCTION__ << " TODO Check if point's are on path" << std::endl;
58  }
59  for (auto &i : coords) {
60  _intersectionPoints.push_back(i);
61  }
62  return false;
63 }
64 
66  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
67  return false;
68 }
69 
71  auto list1 = l.asEntities();
72 
73  // Note: The dynamic_pointer_cast won't win a beauty contest, but the plan is to split
74  // the EntityVisitor into a GeoVisitor and EntityVisitor such that a application deciding
75  // to use double dispatch can decide to use a specific implementation.
76  // Once added, we can get rid of the dynamic_pointer_casts and simply
77  // call entity1.visit(entity2);
78  for (auto &entity1 : list1) {
79  if (auto arc = std::dynamic_pointer_cast<const lc::geo::Arc>(entity1)) {
80  geovisit(v, *arc.get());
81  } else {
82  geovisit(v, *std::dynamic_pointer_cast<const lc::geo::Vector>(entity1).get());
83  }
84  }
85  return false;
86 }
87 
88 // Line
90  geovisit(lc::geo::Vector(l1.start(), l1.end()), v);
91  return false;
92 }
93 
95  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
96  return false;
97 }
98 
100  geovisit(lc::geo::Vector(l1.start(), l1.end()), lc::geo::Vector(l2.start(), l2.end()));
101  return false;
102 }
103 
105  geovisit(lc::geo::Vector(l.start(), l.end()), lc::geo::Arc(circle.center(), circle.radius(), -M_PI, M_PI));
106  return false;
107 }
108 
110  geovisit(lc::geo::Vector(l.start(), l.end()), arc);
111  return false;
112 }
113 
115  // TODO Check if point's are on path
116  auto &&coords = maths::Intersection::LineQuad(l.equation(), e.equation());
117  if (coords.size()>0) {
118  std::cerr << __PRETTY_FUNCTION__ << " TODO Check if point's are on path" << std::endl;
119  }
120  for (auto &i : coords) {
121  _intersectionPoints.push_back(i);
122  }
123  return false;
124 }
125 
127  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
128  return false;
129 }
130 
132  auto &&list1 = p.asEntities();
133  // Note: The dynamic_pointer_cast won't win a beauty contest, but the plan is to split
134  // the EntityVisitor into a GeoVisitor and EntityVisitor such that a application deciding
135  // to use double dispatch can decide to use a specific implementation.
136  // Once added, we can get rid of the dynamic_pointer_casts and simply
137  // call entity1.visit(entity2);
138  for (auto &entity1 : list1) {
139  if (auto arc = std::dynamic_pointer_cast<const lc::geo::Arc>(entity1)) {
140  geovisit(l, *arc.get());
141  } else {
142  geovisit(l, *std::dynamic_pointer_cast<const lc::geo::Vector>(entity1).get());
143  }
144  }
145  return false;
146 }
147 
148 
149 // Point
151  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
152  return false;
153 }
154 
156  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
157  return false;
158 }
159 
161  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
162  return false;
163 }
164 
166  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
167  return false;
168 }
169 
171  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
172  return false;
173 }
174 
176  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
177  return false;
178 }
179 
181  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
182  return false;
183 }
184 
186  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
187  return false;
188 }
189 
190 // Circle
192  geovisit(v, lc::geo::Arc(circle.center(), circle.radius(), -M_PI, M_PI));
193  return false;
194 }
195 
197  (*this)(p, c);
198  return false;
199 }
200 
202  geovisit(lc::geo::Vector(l.start(), l.end()), lc::geo::Arc(circle.center(), circle.radius(), -M_PI, M_PI));
203  return false;
204 }
205 
207  auto &&coords = maths::Intersection::QuadQuad(c1.equation(), c2.equation());
208  for (auto &i : coords) {
209  _intersectionPoints.push_back(i);
210  }
211  return false;
212 }
213 
214 bool Intersect::operator()(const lc::geo::Circle &circle, const lc::entity::Arc &arc) {
215  auto &&coords = maths::Intersection::QuadQuad(circle.equation(), arc.equation());
216  if (_method == Method::OnPath) {
217  _intersectionPoints.reserve(_intersectionPoints.size() + coords.size());
218  _intersectionPoints.insert(coords.end(), coords.begin(), coords.end());
219  } else {
220  for (auto &point : coords) {
221  double a = (point - arc.center()).angle();
222  if (Math::isAngleBetween(a, arc.startAngle(), arc.endAngle(), arc.CCW())) {
223  _intersectionPoints.push_back(point);
224  }
225  }
226  }
227  return false;
228 }
229 
231  // TODO: test if point is on path
232  auto &&coords = maths::Intersection::QuadQuad(c.equation(), e.equation());
233  if (coords.size()>0) {
234  std::cerr << __PRETTY_FUNCTION__ << " TODO Check if point's are on path" << std::endl;
235  }
236  for (auto &i : coords) {
237  _intersectionPoints.push_back(i);
238  }
239  return false;
240 }
241 
243  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
244  return false; //visit(c, s);
245 }
246 
248  auto &list1 = l.asEntities();
249  auto a = lc::geo::Arc(c.center(), c.radius(), -M_PI, M_PI);
250  // Note: The dynamic_pointer_cast won't win a beauty contest, but the plan is to split
251  // the EntityVisitor into a GeoVisitor and EntityVisitor such that a application deciding
252  // to use double dispatch can decide to use a specific implementation.
253  // Once added, we can get rid of the dynamic_pointer_casts and simply
254  // call entity1.visit(entity2);
255  for (auto &entity1 : list1) {
256  if (auto arc = std::dynamic_pointer_cast<const lc::geo::Arc>(entity1)) {
257  geovisit(a, *arc.get());
258  } else {
259  geovisit(*std::dynamic_pointer_cast<const lc::geo::Vector>(entity1).get(), a);
260  }
261  }
262  return false;
263 }
264 
265 
266 
267 // ARC
269  geovisit(v, a);
270  return false;
271 
272  /* Please do not delete this for the moment
273  const geo::Coordinate nearest = line.nearestPointOnPath(arc.center());
274  double dist = arc.center().distanceTo(nearest);
275 
276  // special case: arc touches line (tangent):
277  // TODO: We properly should add a tolorance here ??
278  if (fabs(dist - arc.radius()) < _tolerance) {
279  _intersectionPoints.push_back(nearest);
280  return;
281  }
282 
283  const geo::Coordinate d = line.end() - line.start();
284  const double r = arc.radius();
285  const geo:: Coordinate delta = line.start() - arc.center();
286  const double d2 = d.squared();
287 
288  //intersection
289  // solution = p + t d;
290  //| p -c+ t d|^2 = r^2
291  // |d|^2 t^2 + 2 (p-c).d t + |p-c|^2 -r^2 = 0
292  const double a1 = delta.dot(d);
293  const double discriminant = a1 * a1 - d2 * (delta.squared() - r * r);
294 
295  if (discriminant < - _tolerance) {
296  return;
297  } else {
298  const double t = sqrtf(fabs(discriminant));
299  //two intersections
300  const geo::Coordinate c1(line.start() + d * (t - a1) / d2);
301  const geo::Coordinate c2(line.start() - d * (t + a1) / d2);
302 
303  if (_method == Method::OnPath || (_method == Method::OnEntity && arc.isCoordinateOnPath(c1) && line.isCoordinateOnPath(c1))) {
304  _intersectionPoints.push_back(c1);
305  }
306 
307  if (_method == Method::OnPath || (_method == Method::OnEntity && arc.isCoordinateOnPath(c2) && line.isCoordinateOnPath(c2))) {
308  _intersectionPoints.push_back(c2);
309  }
310  } */
311 }
312 
314  (*this)(p, a);
315  return false;
316 }
317 
319  geovisit(lc::geo::Vector(line.start(), line.end()), arc);
320  return false;
321 }
322 
323 bool Intersect::operator()(const lc::entity::Arc &arc, const lc::geo::Circle &circle) {
324  (*this)(circle, arc);
325  return false;
326 }
327 
329  geovisit(a1, a2);
330  return false;
331 }
332 
334  // TODO Check if point's are on path
335  auto &&coords = maths::Intersection::QuadQuad(a.equation(), e.equation());
336  if (coords.size()>0) {
337  std::cerr << __PRETTY_FUNCTION__ << " TODO Check if point's are on path" << std::endl;
338  }
339  for (auto &i : coords) {
340  _intersectionPoints.push_back(i);
341  }
342  return false;
343 }
344 
346  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
347  return false;
348 }
349 
351  auto &list1 = l1.asEntities();
352  // Note: The dynamic_pointer_cast won't win a beauty contest, but the plan is to split
353  // the EntityVisitor into a GeoVisitor and EntityVisitor such that a application deciding
354  // to use double dispatch can decide to use a specific implementation.
355  // Once added, we can get rid of the dynamic_pointer_casts and simply
356  // call entity1.visit(entity2);
357  for (auto &entity1 : list1) {
358  if (auto arc = std::dynamic_pointer_cast<const lc::geo::Arc>(entity1)) {
359  geovisit(a1, *arc.get());
360  } else {
361  geovisit(*std::dynamic_pointer_cast<const lc::geo::Vector>(entity1).get(), a1);
362  }
363  }
364  return false; //visit(l1, a1);
365 }
366 
367 // Ellipse
369  (*this)(v, e);
370  return false;
371 }
372 
374  (*this)(p, e);
375  return false;
376 }
377 
379  (*this)(l, e);
380  return false;
381 }
382 
384  (*this)(c, e);
385  return false;
386 }
387 
389  (*this)(a, e);
390  return false;
391 }
392 
394  // TODO test if point's are on path for ellipse
395  auto &&coords = maths::Intersection::QuadQuad(e1.equation(), e2.equation());
396  if (coords.size()>0) {
397  std::cerr << __PRETTY_FUNCTION__ << " TODO Check if point's are on path" << std::endl;
398  }
399  for (auto &i : coords) {
400  _intersectionPoints.push_back(i);
401  }
402  return false;
403 }
404 
406  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
407  return false;
408 }
409 
411  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
412  return false;
413 }
414 
415 // Spline
417  (*this)(v, s);
418  return false;
419 }
420 
422  (*this)(p, s);
423  return false;
424 }
425 
427  (*this)(l, s);
428  return false;
429 }
430 
432  (*this)(c, s);
433  return false;
434 }
435 
437  (*this)(a, s);
438  return false;
439 }
440 
442  (*this)(e, s);
443  return false;
444 }
445 
447  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
448  return false;
449 }
450 
452  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
453  return false;
454 }
455 
456 // LWPolyline
458  (*this)(v, p);
459  return false;
460 }
461 
463  (*this)(po, lwp);
464  return false;
465 }
466 
468  (*this)(l, p);
469  return false;
470 }
471 
473  (*this)(c1, l1);
474  return false;
475 }
476 
478  (*this)(a, p);
479  return false;
480 }
481 
483  (*this)(e, p);
484  return false;
485 }
486 
488  (*this)(s, p);
489  return false;
490 }
491 
493  auto &list1 = l1.asEntities();
494  auto &list2 = l2.asEntities();
495 
496  // Note: The dynamic_pointer_cast won't win a beauty contest, but the plan is to split
497  // the EntityVisitor into a GeoVisitor and EntityVisitor such that a application deciding
498  // to use double dispatch can decide to use a specific implementation.
499  // Once added, we can get rid of the dynamic_pointer_casts and simply
500  // call entity1.visit(entity2);
501  for (auto &entity1 : list1) {
502  for (auto &entity2 : list2) {
503  if (auto vector = std::dynamic_pointer_cast<const lc::geo::Vector>(entity1)) {
504  if (auto arc = std::dynamic_pointer_cast<const lc::geo::Arc>(entity2)) {
505  geovisit(*vector.get(), *arc.get());
506  } else {
507  geovisit(*vector.get(), *std::dynamic_pointer_cast<const lc::geo::Vector>(entity2).get());
508  }
509  } else {
510  if (auto arc = std::dynamic_pointer_cast<const lc::geo::Arc>(entity2)) {
511  geovisit(*std::dynamic_pointer_cast<const lc::geo::Arc>(entity1).get(), *arc.get());
512  } else {
513  geovisit(*std::dynamic_pointer_cast<const lc::geo::Vector>(entity2).get(),
514  *std::dynamic_pointer_cast<const lc::geo::Arc>(entity1).get());
515  }
516  }
517  }
518  }
519  return false;
520 }
521 
522 
523 void Intersect::geovisit(const geo::Vector &v1, const geo::Vector &v2) {
524  const geo::Coordinate p1 = v1.start();
525  const geo::Coordinate p2 = v1.end();
526  const geo::Coordinate p3 = v2.start();
527  const geo::Coordinate p4 = v2.end();
528 
529  const double num = ((p4.x() - p3.x()) * (p1.y() - p3.y()) - (p4.y() - p3.y()) * (p1.x() - p3.x()));
530  const double div = ((p4.y() - p3.y()) * (p2.x() - p1.x()) - (p4.x() - p3.x()) * (p2.y() - p1.y()));
531 
532  // TODO: We properly should add a tolorance here ??
533  if (std::abs(div) > _tolerance) {
534  double u = num / div;
535  double xs = p1.x() + u * (p2.x() - p1.x());
536  double ys = p1.y() + u * (p2.y() - p1.y());
537  const geo::Coordinate coord(xs, ys);
538 
539  const geo::Area a1(p1, p2);
540  const geo::Area a2(p3, p4);
541 
542  const bool a1b = a1.inArea(coord);
543  const bool a2b = a2.inArea(coord);
544 
545  if (_method == Method::OnPath) {
546  _intersectionPoints.push_back(coord);
547  } else if (a1b && a2b) { // Test if it positivly fit's within a area
548  _intersectionPoints.push_back(coord);
549  } else if (
550  (p1.x() == p2.x() && ys >= a1.minP().y() && ys <= a1.maxP().y() && a2b) ||
551  // when we deal with horizontal or vertical lines, inArea might not
552  (p3.x() == p4.x() && ys >= a2.minP().y() && ys <= a2.maxP().y() && a1b) ||
553  // give a positive result, this conditions will confirm without using tolerance
554  (p1.y() == p2.y() && xs >= a1.minP().x() && xs <= a1.maxP().x() && a2b) ||
555  (p3.y() == p4.y() && xs >= a2.minP().x() && xs <= a2.maxP().x() && a1b)
556  ) {
557  _intersectionPoints.push_back(coord);
558  }
559  }
560 }
561 
562 void Intersect::geovisit(const geo::Vector &line, const geo::Arc &arc) {
563 
564  auto &&coords = maths::Intersection::LineQuad(line.equation(), arc.equation());
565  if (_method == Method::OnPath) {
566  _intersectionPoints.reserve(_intersectionPoints.size() + coords.size());
567  _intersectionPoints.insert(coords.end(), coords.begin(), coords.end());
568  } else {
569  for (auto &point : coords) {
570  double a = (point - arc.center()).angle();
571  if (arc.isAngleBetween(a) &&
572  line.nearestPointOnEntity(point).distanceTo(point) < LCTOLERANCE) {
573  _intersectionPoints.push_back(point);
574  }
575  }
576  }
577 }
578 
579 void Intersect::geovisit(const geo::Arc &arc1, const geo::Arc &arc2) {
580  auto &&coords = maths::Intersection::QuadQuad(arc1.equation(), arc2.equation());
581  if (_method == Method::OnPath) {
582  _intersectionPoints.reserve(_intersectionPoints.size() + coords.size());
583  _intersectionPoints.insert(coords.end(), coords.begin(), coords.end());
584  } else {
585  for (auto &point : coords) {
586  double a1 = (point - arc1.center()).angle();
587  double a2 = (point - arc2.center()).angle();
588  if (Math::isAngleBetween(a1, arc1.startAngle(), arc1.endAngle(), arc1.CCW()) &&
589  Math::isAngleBetween(a2, arc2.startAngle(), arc2.endAngle(), arc2.CCW())) {
590  _intersectionPoints.push_back(point);
591  }
592  }
593  }
594 }
595 
596 /***
597  * ~|~ _ _|_ _ _ _ _ __|_|\/| _ _
598  * _|_| | | (/_| _\(/_(_ | | |(_|| |\/
599  * /
600  */
601 IntersectMany::IntersectMany(std::vector<entity::CADEntity_CSPtr> entities, Intersect::Method method, double tolerance)
602  : _entities(entities), _method(method), _tolerance(tolerance) {
603 
604 }
605 
606 std::vector<geo::Coordinate> IntersectMany::result() const {
607  Intersect intersect(_method, _tolerance);
608  if (_entities.size() > 1) {
609  for (size_t outer = 0; outer < (_entities.size() - 1); outer++) {
610  for (size_t inner = outer + 1; inner < _entities.size(); inner++) {
611  visitorDispatcher<bool, lc::GeoEntityVisitor>(intersect, *_entities.at(outer).get(), *_entities.at(inner).get());
612  }
613  }
614  }
615  return intersect.result();
616 }
617 
618 /***
619  * ~|~ _ _|_ _ _ _ _ __|_/\ _ _ . _ __|_/~\_|_|_ _ _ _
620  * _|_| | | (/_| _\(/_(_ |/~~\(_|(_||| |_\ | \_/ | | |(/_| _\
621  * _|
622  */
623 IntersectAgainstOthers::IntersectAgainstOthers(std::vector<entity::CADEntity_CSPtr> entities,
624  std::vector<entity::CADEntity_CSPtr> others, Intersect::Method method,
625  double tolerance)
626  :
627  _entities(entities), _others(others), _method(method), _tolerance(tolerance) {
628 }
629 
630 std::vector<geo::Coordinate> IntersectAgainstOthers::result() const {
631 
632  Intersect intersect(_method, _tolerance);
633  /*FIXME unused
634  for (auto &other : _others) {
635  for (auto &entity : _entities) {
636  visitorDispatcher<bool, lc::GeoEntityVisitor>(intersect, *other.get(), *entity.get());
637  }
638  }
639  */
640  std::cerr << __PRETTY_FUNCTION__ << " requires implementation" << std::endl;
641 
642  return intersect.result();
643 }
644 
const Coordinate minP() const
Definition: geoarea.h:60
std::vector< geo::Coordinate > _intersectionPoints
Definition: intersect.h:175
const Coordinate end() const
Definition: geovector.h:23
IntersectAgainstOthers(std::vector< entity::CADEntity_CSPtr >, std::vector< entity::CADEntity_CSPtr >, Intersect::Method, double tolerance)
Definition: intersect.cpp:623
double x() const
Returns x of Coordinate.
Definition: geocoordinate.h:26
const double _tolerance
Definition: intersect.h:177
const Coordinate nearestPointOnEntity(const Coordinate &coord) const
Definition: geovector.h:49
calculate the intersection pojnts of 2 entities
Definition: intersect.h:35
The Spline class.
Definition: spline.h:27
void geovisit(const geo::Vector &, const geo::Vector &)
Definition: intersect.cpp:523
static std::vector< lc::geo::Coordinate > LineQuad(const Equation &l1, const Equation &q1)
double y() const
Returns y of Coordinate.
Definition: geocoordinate.h:34
std::vector< geo::Coordinate > result() const
Definition: intersect.cpp:630
double radius() const
returns the radius of the circle.
Definition: geocircle.cpp:17
bool CCW() const
Returns of the arc is in reversed direction.
Definition: geoarc.cpp:126
double endAngle() const
Returns the EndAngle.
Definition: geoarc.cpp:87
std::vector< geo::Coordinate > result() const
Definition: intersect.cpp:606
Definition: cadentity.h:12
const double _tolerance
Definition: intersect.h:194
const Intersect::Method _method
Definition: intersect.h:212
A ellipse that can be put in a drawing.
Definition: ellipse.h:27
const Intersect::Method _method
Definition: intersect.h:193
#define LCTOLERANCE
Definition: const.h:6
const maths::Equation equation() const
Returns equation of the circle.
Definition: geocircle.h:40
A line that can be put in a drawing.
Definition: line.h:28
bool operator()(const lc::geo::Vector &, const lc::geo::Vector &)
Definition: intersect.cpp:25
std::vector< geo::Coordinate > result() const
Definition: intersect.cpp:20
IntersectMany(std::vector< entity::CADEntity_CSPtr >, Intersect::Method=Intersect::OnEntity, double tolerance=LCTOLERANCE)
Definition: intersect.cpp:601
#define M_PI
Definition: const.h:16
const maths::Equation equation() const
Definition: geoarc.h:112
static bool isAngleBetween(double a, double start, double end, bool CCW)
isAngleBetween, checks if angle is between
Definition: lcmath.cpp:21
const maths::Equation equation() const
Returns the quadratic equation.
Definition: geoellipse.h:153
const Method _method
Definition: intersect.h:176
Intersect(Method method, double tolerance)
Definition: intersect.cpp:17
const Coordinate maxP() const
Definition: geoarea.h:67
const Coordinate center() const
Returns the Center of circle.
Definition: geocircle.cpp:14
static std::vector< geo::Coordinate > QuadQuad(const Equation &l1, const Equation &l2)
const Coordinate start() const
Definition: geovector.h:20
double distanceTo(const geo::Coordinate &c) const
bool inArea(const Coordinate &point, double tolerance=0.) const
Test of a specific point lies within area.
Definition: geoarea.h:94
bool isAngleBetween(double angle) const
Definition: geoarc.cpp:163
std::vector< entity::CADEntity_CSPtr > _entities
Definition: intersect.h:192
const maths::Equation equation() const
Definition: geovector.h:115
double startAngle() const
Returns the startAngle.
Definition: geoarc.cpp:83
const Coordinate center() const
Returns center of Arc.
Definition: geoarc.cpp:91
std::vector< CADEntity_CSPtr > const asEntities() const
Definition: lwpolyline.cpp:299