3 // Part of the Architektonas Project
4 // Originally part of QCad Community Edition by Andrew Mustun
5 // Extensively rewritten and refactored by James L. Hammons
6 // Portions copyright (C) 2001-2003 RibbonSoft
7 // Copyright (C) 2010 Underground Software
8 // See the README and GPLv2 files for licensing and warranty information
10 // JLH = James L. Hammons <jlhamm@acm.org>
13 // --- ---------- -----------------------------------------------------------
14 // JLH 06/01/2010 Added this text. :-)
17 #include "rs_information.h"
19 #include "rs_constructionline.h"
22 * Default constructor.
24 * @param container The container to which we will add
25 * entities. Usually that's an Drawing entity but
26 * it can also be a polyline, text, ...
28 RS_Information::RS_Information(RS_EntityContainer& container)
30 this->container = &container;
34 * @return true: if the entity is a dimensioning enity.
37 bool RS_Information::isDimension(RS2::EntityType type)
39 if (type==RS2::EntityDimAligned ||
40 type==RS2::EntityDimLinear ||
41 type==RS2::EntityDimRadial ||
42 type==RS2::EntityDimDiametric ||
43 type==RS2::EntityDimAngular) {
53 * @retval true the entity can be trimmed.
54 * i.e. it is in a graphic or in a polyline.
56 bool RS_Information::isTrimmable(RS_Entity* e) {
58 if (e->getParent()!=NULL) {
59 if (e->getParent()->rtti()==RS2::EntityPolyline) {
62 else if (e->getParent()->rtti()==RS2::EntityContainer ||
63 e->getParent()->rtti()==RS2::EntityGraphic ||
64 e->getParent()->rtti()==RS2::EntityBlock) {
77 * @retval true the two entities can be trimmed to each other;
78 * i.e. they are in a graphic or in the same polyline.
80 bool RS_Information::isTrimmable(RS_Entity* e1, RS_Entity* e2) {
81 if (e1!=NULL && e2!=NULL) {
82 if (e1->getParent()!=NULL && e2->getParent()!=NULL) {
83 if (e1->getParent()->rtti()==RS2::EntityPolyline &&
84 e2->getParent()->rtti()==RS2::EntityPolyline &&
85 e1->getParent()==e2->getParent()) {
87 // in the same polyline
88 RS_EntityContainer* pl = e1->getParent();
89 int idx1 = pl->findEntity(e1);
90 int idx2 = pl->findEntity(e2);
91 RS_DEBUG->print("RS_Information::isTrimmable: "
92 "idx1: %d, idx2: %d", idx1, idx2);
93 if (abs(idx1-idx2)==1 || abs(idx1-idx2)==pl->count()-1) {
94 // directly following entities
98 // not directly following entities
102 else if ((e1->getParent()->rtti()==RS2::EntityContainer ||
103 e1->getParent()->rtti()==RS2::EntityGraphic ||
104 e1->getParent()->rtti()==RS2::EntityBlock) &&
105 (e2->getParent()->rtti()==RS2::EntityContainer ||
106 e2->getParent()->rtti()==RS2::EntityGraphic ||
107 e2->getParent()->rtti()==RS2::EntityBlock)) {
114 // independent entities with the same parent:
115 return (e1->getParent()==e2->getParent());
124 * Gets the nearest end point to the given coordinate.
126 * @param coord Coordinate (typically a mouse coordinate)
128 * @return the coordinate found or an invalid vector
129 * if there are no elements at all in this graphics
132 Vector RS_Information::getNearestEndpoint(const Vector& coord,
133 double* dist) const {
134 return container->getNearestEndpoint(coord, dist);
139 * Gets the nearest point to the given coordinate which is on an entity.
141 * @param coord Coordinate (typically a mouse coordinate)
142 * @param dist Pointer to a double which will contain the
143 * measured distance after return or NULL
144 * @param entity Pointer to a pointer which will point to the
145 * entity on which the point is or NULL
147 * @return the coordinate found or an invalid vector
148 * if there are no elements at all in this graphics
151 Vector RS_Information::getNearestPointOnEntity(const Vector& coord,
154 RS_Entity** entity) const {
156 return container->getNearestPointOnEntity(coord, onEntity, dist, entity);
161 * Gets the nearest entity to the given coordinate.
163 * @param coord Coordinate (typically a mouse coordinate)
164 * @param dist Pointer to a double which will contain the
165 * masured distance after return
166 * @param level Level of resolving entities.
168 * @return the entity found or NULL if there are no elements
169 * at all in this graphics container.
171 RS_Entity* RS_Information::getNearestEntity(const Vector& coord,
173 RS2::ResolveLevel level) const {
175 return container->getNearestEntity(coord, dist, level);
181 * Calculates the intersection point(s) between two entities.
183 * @param onEntities true: only return intersection points which are
185 * false: return all intersection points.
187 * @todo support more entities
189 * @return All intersections of the two entities. The tangent flag in
190 * VectorSolutions is set if one intersection is a tangent point.
192 VectorSolutions RS_Information::getIntersection(RS_Entity* e1,
193 RS_Entity* e2, bool onEntities) {
198 if (e1==NULL || e2==NULL) {
202 // unsupported entities / entity combinations:
203 if ((e1->rtti()==RS2::EntityEllipse && e2->rtti()==RS2::EntityEllipse) ||
204 e1->rtti()==RS2::EntityText || e2->rtti()==RS2::EntityText ||
205 isDimension(e1->rtti()) || isDimension(e2->rtti())) {
210 // (only) one entity is an ellipse:
211 if (e1->rtti()==RS2::EntityEllipse || e2->rtti()==RS2::EntityEllipse) {
212 if (e2->rtti()==RS2::EntityEllipse) {
217 if (e2->rtti()==RS2::EntityLine) {
218 RS_Ellipse* ellipse = (RS_Ellipse*)e1;
219 ret = getIntersectionLineEllipse((RS_Line*)e2, ellipse);
223 // ellipse / arc, ellipse / ellipse: not supported:
232 // entity copies - so we only have to deal with lines and arcs
234 RS_LineData(Vector(0.0, 0.0), Vector(0.0,0.0)));
236 RS_LineData(Vector(0.0, 0.0), Vector(0.0,0.0)));
239 RS_ArcData(Vector(0.0,0.0), 1.0, 0.0, 2*M_PI, false));
241 RS_ArcData(Vector(0.0,0.0), 1.0, 0.0, 2*M_PI, false));
243 // convert construction lines to lines:
244 if (e1->rtti()==RS2::EntityConstructionLine) {
245 RS_ConstructionLine* cl = (RS_ConstructionLine*)e1;
247 l1.setStartpoint(cl->getPoint1());
248 l1.setEndpoint(cl->getPoint2());
252 if (e2->rtti()==RS2::EntityConstructionLine) {
253 RS_ConstructionLine* cl = (RS_ConstructionLine*)e2;
255 l2.setStartpoint(cl->getPoint1());
256 l2.setEndpoint(cl->getPoint2());
262 // convert circles to arcs:
263 if (e1->rtti()==RS2::EntityCircle) {
264 RS_Circle* c = (RS_Circle*)e1;
266 RS_ArcData data(c->getCenter(), c->getRadius(), 0.0, 2*M_PI, false);
271 if (e2->rtti()==RS2::EntityCircle) {
272 RS_Circle* c = (RS_Circle*)e2;
274 RS_ArcData data(c->getCenter(), c->getRadius(), 0.0, 2*M_PI, false);
284 if (te1->rtti()==RS2::EntityLine &&
285 te2->rtti()==RS2::EntityLine) {
287 RS_Line* line1 = (RS_Line*)te1;
288 RS_Line* line2 = (RS_Line*)te2;
290 ret = getIntersectionLineLine(line1, line2);
295 else if (te1->rtti()==RS2::EntityLine &&
296 te2->rtti()==RS2::EntityArc) {
298 RS_Line* line = (RS_Line*)te1;
299 RS_Arc* arc = (RS_Arc*)te2;
301 ret = getIntersectionLineArc(line, arc);
306 else if (te1->rtti()==RS2::EntityArc &&
307 te2->rtti()==RS2::EntityLine) {
309 RS_Arc* arc = (RS_Arc*)te1;
310 RS_Line* line = (RS_Line*)te2;
312 ret = getIntersectionLineArc(line, arc);
317 else if (te1->rtti()==RS2::EntityArc &&
318 te2->rtti()==RS2::EntityArc) {
320 RS_Arc* arc1 = (RS_Arc*)te1;
321 RS_Arc* arc2 = (RS_Arc*)te2;
323 ret = getIntersectionArcArc(arc1, arc2);
325 RS_DEBUG->print("RS_Information::getIntersection:: Unsupported entity type.");
330 // Check all intersection points for being on entities:
332 if (onEntities==true) {
333 if (!e1->isPointOnEntity(ret.get(0), tol) ||
334 !e2->isPointOnEntity(ret.get(0), tol)) {
335 ret.set(0, Vector(false));
337 if (!e1->isPointOnEntity(ret.get(1), tol) ||
338 !e2->isPointOnEntity(ret.get(1), tol)) {
339 ret.set(1, Vector(false));
341 if (!e1->isPointOnEntity(ret.get(2), tol) ||
342 !e2->isPointOnEntity(ret.get(2), tol)) {
343 ret.set(2, Vector(false));
345 if (!e1->isPointOnEntity(ret.get(3), tol) ||
346 !e2->isPointOnEntity(ret.get(3), tol)) {
347 ret.set(3, Vector(false));
352 for (int i=0; i<4; ++i) {
353 if (ret.get(i).valid) {
354 ret.set(k, ret.get(i));
358 for (int i=k; i<4; ++i) {
359 ret.set(i, Vector(false));
368 * @return Intersection between two lines.
370 VectorSolutions RS_Information::getIntersectionLineLine(RS_Line* e1,
375 if (e1==NULL || e2==NULL) {
379 Vector p1 = e1->getStartpoint();
380 Vector p2 = e1->getEndpoint();
381 Vector p3 = e2->getStartpoint();
382 Vector p4 = e2->getEndpoint();
384 double num = ((p4.x-p3.x)*(p1.y-p3.y) - (p4.y-p3.y)*(p1.x-p3.x));
385 double div = ((p4.y-p3.y)*(p2.x-p1.x) - (p4.x-p3.x)*(p2.y-p1.y));
387 if (fabs(div)>RS_TOLERANCE) {
388 double u = num / div;
390 double xs = p1.x + u * (p2.x-p1.x);
391 double ys = p1.y + u * (p2.y-p1.y);
392 ret = VectorSolutions(Vector(xs, ys));
395 // lines are parallel
397 ret = VectorSolutions();
406 * @return One or two intersection points between given entities.
408 VectorSolutions RS_Information::getIntersectionLineArc(RS_Line* line,
413 if (line==NULL || arc==NULL) {
419 nearest = line->getNearestPointOnEntity(arc->getCenter(), false, &dist);
421 // special case: arc touches line (tangent):
422 if (fabs(dist - arc->getRadius()) < 1.0e-4) {
423 ret = VectorSolutions(nearest);
424 ret.setTangent(true);
428 Vector p = line->getStartpoint();
429 Vector d = line->getEndpoint() - line->getStartpoint();
430 if (d.magnitude()<1.0e-6) {
434 Vector c = arc->getCenter();
435 double r = arc->getRadius();
436 Vector delta = p - c;
439 double term = RS_Math::pow(Vector::dotP(d, delta), 2.0)
440 - RS_Math::pow(d.magnitude(), 2.0)
441 * (RS_Math::pow(delta.magnitude(), 2.0) - RS_Math::pow(r, 2.0));
449 // one or two intersections:
451 double t1 = (- Vector::dotP(d, delta) + sqrt(term))
452 / RS_Math::pow(d.magnitude(), 2.0);
454 bool tangent = false;
456 // only one intersection:
457 if (fabs(term)<RS_TOLERANCE) {
464 t2 = (-Vector::dotP(d, delta) - sqrt(term))
465 / RS_Math::pow(d.magnitude(), 2.0);
477 ret = VectorSolutions(sol1, sol2);
478 ret.setTangent(tangent);
487 * @return One or two intersection points between given entities.
489 VectorSolutions RS_Information::getIntersectionArcArc(RS_Arc* e1,
494 if (e1==NULL || e2==NULL) {
498 Vector c1 = e1->getCenter();
499 Vector c2 = e2->getCenter();
501 double r1 = e1->getRadius();
502 double r2 = e2->getRadius();
507 if (u.magnitude()<1.0e-6) {
511 Vector v = Vector(u.y, -u.x);
513 double s, t1, t2, term;
515 s = 1.0/2.0 * ((r1*r1 - r2*r2)/(RS_Math::pow(u.magnitude(), 2.0)) + 1.0);
517 term = (r1*r1)/(RS_Math::pow(u.magnitude(), 2.0)) - s*s;
521 ret = VectorSolutions();
524 // one or two intersections:
528 bool tangent = false;
530 Vector sol1 = c1 + u*s + v*t1;
531 Vector sol2 = c1 + u*s + v*t2;
533 if (sol1.distanceTo(sol2)<1.0e-4) {
534 sol2 = Vector(false);
535 ret = VectorSolutions(sol1);
538 ret = VectorSolutions(sol1, sol2);
541 ret.setTangent(tangent);
550 * @return One or two intersection points between given entities.
552 VectorSolutions RS_Information::getIntersectionLineEllipse(RS_Line* line,
553 RS_Ellipse* ellipse) {
557 if (line==NULL || ellipse==NULL) {
561 // rotate into normal position:
562 double ang = ellipse->getAngle();
564 double rx = ellipse->getMajorRadius();
565 double ry = ellipse->getMinorRadius();
566 Vector center = ellipse->getCenter();
567 Vector a1 = line->getStartpoint().rotate(center, -ang);
568 Vector a2 = line->getEndpoint().rotate(center, -ang);
571 Vector diff = origin - center;
572 Vector mDir = Vector(dir.x/(rx*rx), dir.y/(ry*ry));
573 Vector mDiff = Vector(diff.x/(rx*rx), diff.y/(ry*ry));
575 double a = Vector::dotP(dir, mDir);
576 double b = Vector::dotP(dir, mDiff);
577 double c = Vector::dotP(diff, mDiff) - 1.0;
578 double d = b*b - a*c;
581 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 0");
582 } else if ( d > 0 ) {
583 double root = sqrt(d);
584 double t_a = (-b - root) / a;
585 double t_b = (-b + root) / a;
587 /*if ( (t_a < 0 || 1 < t_a) && (t_b < 0 || 1 < t_b) ) {
588 if ( (t_a < 0 && t_b < 0) || (t_a > 1 && t_b > 1) ) {
589 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 1");
592 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: inside 1");
595 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: intersection 1");
598 //if ( 0 <= t_a && t_a <= 1 ) {
599 //RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: 0<=t_a<=1");
600 ret1 = a1.lerp(a2, t_a);
601 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: ret1: %f/%f", ret1.x, ret1.y);
603 //if ( 0 <= t_b && t_b <= 1 ) {
604 //RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: 0<=t_b<=1");
605 ret2 = a1.lerp(a2, t_b);
606 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: ret2: %f/%f", ret2.x, ret2.y);
608 if (ret1.valid && ret2.valid) {
609 ret = VectorSolutions(ret1, ret2);
613 ret = VectorSolutions(ret1);
616 ret = VectorSolutions(ret2);
622 if ( 0 <= t && t <= 1 ) {
623 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: 0<=t<=1");
624 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: intersection 2");
625 ret = VectorSolutions(a1.lerp(a2, t));
626 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: ret1: %f/%f", ret.get(0).x, ret.get(0).y);
628 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 2");
632 ret.rotate(center, ang);
638 RS_Arc* arc = new RS_Arc(NULL,
639 RS_ArcData(ellipse->getCenter(),
640 ellipse->getMajorRadius(),
641 ellipse->getAngle1(),
642 ellipse->getAngle2(),
644 RS_Line* other = (RS_Line*)line->clone();
645 double angle = ellipse->getAngle();
646 //double ratio = ellipse->getRatio();
649 other->rotate(ellipse->getCenter(), -angle);
650 other->scale(ellipse->getCenter(), Vector(1.0, 1.0/ellipse->getRatio()));
652 ret = getIntersectionLineArc(other, arc);
654 ret.scale(ellipse->getCenter(), Vector(1.0, ellipse->getRatio()));
655 ret.rotate(ellipse->getCenter(), angle);
666 * Checks if the given coordinate is inside the given contour.
668 * @param point Coordinate to check.
669 * @param contour One or more entities which shape a contour.
670 * If the given contour is not closed, the result is undefined.
671 * The entities don't need to be in a specific order.
672 * @param onContour Will be set to true if the given point it exactly
675 bool RS_Information::isPointInsideContour(const Vector& point,
676 RS_EntityContainer* contour, bool* onContour) {
679 RS_DEBUG->print(RS_Debug::D_WARNING,
680 "RS_Information::isPointInsideContour: contour is NULL");
684 if (point.x < contour->getMin().x || point.x > contour->getMax().x ||
685 point.y < contour->getMin().y || point.y > contour->getMax().y) {
689 double width = contour->getSize().x+1.0;
694 double rayAngle = 0.0;
700 v.setPolar(width*10.0, rayAngle);
701 RS_Line ray(NULL, RS_LineData(point, point+v));
705 if (onContour!=NULL) {
709 for (RS_Entity* e = contour->firstEntity(RS2::ResolveAll);
711 e = contour->nextEntity(RS2::ResolveAll)) {
713 // intersection(s) from ray with contour entity:
714 sol = RS_Information::getIntersection(&ray, e, true);
716 for (int i=0; i<=1; ++i) {
717 Vector p = sol.get(i);
720 // point is on the contour itself
721 if (p.distanceTo(point)<1.0e-5) {
722 if (onContour!=NULL) {
726 if (e->rtti()==RS2::EntityLine) {
727 RS_Line* line = (RS_Line*)e;
729 // ray goes through startpoint of line:
730 if (p.distanceTo(line->getStartpoint())<1.0e-4) {
731 if (RS_Math::correctAngle(line->getAngle1())<M_PI) {
737 // ray goes through endpoint of line:
738 else if (p.distanceTo(line->getEndpoint())<1.0e-4) {
739 if (RS_Math::correctAngle(line->getAngle2())<M_PI) {
744 // ray goes through the line:
750 } else if (e->rtti()==RS2::EntityArc) {
751 RS_Arc* arc = (RS_Arc*)e;
753 if (p.distanceTo(arc->getStartpoint())<1.0e-4) {
754 double dir = arc->getDirection1();
755 if ((dir<M_PI && dir>=1.0e-5) ||
756 ((dir>2*M_PI-1.0e-5 || dir<1.0e-5) &&
757 arc->getCenter().y>p.y)) {
762 else if (p.distanceTo(arc->getEndpoint())<1.0e-4) {
763 double dir = arc->getDirection2();
764 if ((dir<M_PI && dir>=1.0e-5) ||
765 ((dir>2*M_PI-1.0e-5 || dir<1.0e-5) &&
766 arc->getCenter().y>p.y)) {
773 } else if (e->rtti()==RS2::EntityCircle) {
775 if (i==0 && sol.get(1).valid==false) {
776 if (!sol.isTangent()) {
781 } else if (i==1 || sol.get(1).valid==true) {
793 while (!sure && rayAngle<2*M_PI && tries<6);
795 // remove double intersections:
797 Q3PtrList<Vector> is2;
802 double minDist = RS_MAXDOUBLE;
805 for (Vector* v = is.first(); v!=NULL; v = is.next()) {
806 dist = point.distanceTo(*v);
814 if (!done && av!=NULL) {
820 return ((counter%2)==1);