1 /****************************************************************************
2 ** $Id: rs_information.cpp 2378 2005-05-16 17:05:15Z andrew $
4 ** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
6 ** This file is part of the qcadlib Library project.
8 ** This file may be distributed and/or modified under the terms of the
9 ** GNU General Public License version 2 as published by the Free Software
10 ** Foundation and appearing in the file LICENSE.GPL included in the
11 ** packaging of this file.
13 ** Licensees holding valid qcadlib Professional Edition licenses may use
14 ** this file in accordance with the qcadlib Commercial License
15 ** Agreement provided with the Software.
17 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 ** See http://www.ribbonsoft.com for further details.
22 ** Contact info@ribbonsoft.com if any conditions of this licensing are
25 **********************************************************************/
27 #include "rs_information.h"
29 #include "rs_constructionline.h"
33 * Default constructor.
35 * @param container The container to which we will add
36 * entities. Usually that's an RS_Graphic entity but
37 * it can also be a polyline, text, ...
39 RS_Information::RS_Information(RS_EntityContainer& container) {
40 this->container = &container;
46 * @return true: if the entity is a dimensioning enity.
49 bool RS_Information::isDimension(RS2::EntityType type) {
50 if (type==RS2::EntityDimAligned ||
51 type==RS2::EntityDimLinear ||
52 type==RS2::EntityDimRadial ||
53 type==RS2::EntityDimDiametric ||
54 type==RS2::EntityDimAngular) {
64 * @retval true the entity can be trimmed.
65 * i.e. it is in a graphic or in a polyline.
67 bool RS_Information::isTrimmable(RS_Entity* e) {
69 if (e->getParent()!=NULL) {
70 if (e->getParent()->rtti()==RS2::EntityPolyline) {
73 else if (e->getParent()->rtti()==RS2::EntityContainer ||
74 e->getParent()->rtti()==RS2::EntityGraphic ||
75 e->getParent()->rtti()==RS2::EntityBlock) {
88 * @retval true the two entities can be trimmed to each other;
89 * i.e. they are in a graphic or in the same polyline.
91 bool RS_Information::isTrimmable(RS_Entity* e1, RS_Entity* e2) {
92 if (e1!=NULL && e2!=NULL) {
93 if (e1->getParent()!=NULL && e2->getParent()!=NULL) {
94 if (e1->getParent()->rtti()==RS2::EntityPolyline &&
95 e2->getParent()->rtti()==RS2::EntityPolyline &&
96 e1->getParent()==e2->getParent()) {
98 // in the same polyline
99 RS_EntityContainer* pl = e1->getParent();
100 int idx1 = pl->findEntity(e1);
101 int idx2 = pl->findEntity(e2);
102 RS_DEBUG->print("RS_Information::isTrimmable: "
103 "idx1: %d, idx2: %d", idx1, idx2);
104 if (abs(idx1-idx2)==1 || abs(idx1-idx2)==pl->count()-1) {
105 // directly following entities
109 // not directly following entities
113 else if ((e1->getParent()->rtti()==RS2::EntityContainer ||
114 e1->getParent()->rtti()==RS2::EntityGraphic ||
115 e1->getParent()->rtti()==RS2::EntityBlock) &&
116 (e2->getParent()->rtti()==RS2::EntityContainer ||
117 e2->getParent()->rtti()==RS2::EntityGraphic ||
118 e2->getParent()->rtti()==RS2::EntityBlock)) {
125 // independent entities with the same parent:
126 return (e1->getParent()==e2->getParent());
135 * Gets the nearest end point to the given coordinate.
137 * @param coord Coordinate (typically a mouse coordinate)
139 * @return the coordinate found or an invalid vector
140 * if there are no elements at all in this graphics
143 Vector RS_Information::getNearestEndpoint(const Vector& coord,
144 double* dist) const {
145 return container->getNearestEndpoint(coord, dist);
150 * Gets the nearest point to the given coordinate which is on an entity.
152 * @param coord Coordinate (typically a mouse coordinate)
153 * @param dist Pointer to a double which will contain the
154 * measured distance after return or NULL
155 * @param entity Pointer to a pointer which will point to the
156 * entity on which the point is or NULL
158 * @return the coordinate found or an invalid vector
159 * if there are no elements at all in this graphics
162 Vector RS_Information::getNearestPointOnEntity(const Vector& coord,
165 RS_Entity** entity) const {
167 return container->getNearestPointOnEntity(coord, onEntity, dist, entity);
172 * Gets the nearest entity to the given coordinate.
174 * @param coord Coordinate (typically a mouse coordinate)
175 * @param dist Pointer to a double which will contain the
176 * masured distance after return
177 * @param level Level of resolving entities.
179 * @return the entity found or NULL if there are no elements
180 * at all in this graphics container.
182 RS_Entity* RS_Information::getNearestEntity(const Vector& coord,
184 RS2::ResolveLevel level) const {
186 return container->getNearestEntity(coord, dist, level);
192 * Calculates the intersection point(s) between two entities.
194 * @param onEntities true: only return intersection points which are
196 * false: return all intersection points.
198 * @todo support more entities
200 * @return All intersections of the two entities. The tangent flag in
201 * VectorSolutions is set if one intersection is a tangent point.
203 VectorSolutions RS_Information::getIntersection(RS_Entity* e1,
204 RS_Entity* e2, bool onEntities) {
209 if (e1==NULL || e2==NULL) {
213 // unsupported entities / entity combinations:
214 if ((e1->rtti()==RS2::EntityEllipse && e2->rtti()==RS2::EntityEllipse) ||
215 e1->rtti()==RS2::EntityText || e2->rtti()==RS2::EntityText ||
216 isDimension(e1->rtti()) || isDimension(e2->rtti())) {
221 // (only) one entity is an ellipse:
222 if (e1->rtti()==RS2::EntityEllipse || e2->rtti()==RS2::EntityEllipse) {
223 if (e2->rtti()==RS2::EntityEllipse) {
228 if (e2->rtti()==RS2::EntityLine) {
229 RS_Ellipse* ellipse = (RS_Ellipse*)e1;
230 ret = getIntersectionLineEllipse((RS_Line*)e2, ellipse);
234 // ellipse / arc, ellipse / ellipse: not supported:
243 // entity copies - so we only have to deal with lines and arcs
245 RS_LineData(Vector(0.0, 0.0), Vector(0.0,0.0)));
247 RS_LineData(Vector(0.0, 0.0), Vector(0.0,0.0)));
250 RS_ArcData(Vector(0.0,0.0), 1.0, 0.0, 2*M_PI, false));
252 RS_ArcData(Vector(0.0,0.0), 1.0, 0.0, 2*M_PI, false));
254 // convert construction lines to lines:
255 if (e1->rtti()==RS2::EntityConstructionLine) {
256 RS_ConstructionLine* cl = (RS_ConstructionLine*)e1;
258 l1.setStartpoint(cl->getPoint1());
259 l1.setEndpoint(cl->getPoint2());
263 if (e2->rtti()==RS2::EntityConstructionLine) {
264 RS_ConstructionLine* cl = (RS_ConstructionLine*)e2;
266 l2.setStartpoint(cl->getPoint1());
267 l2.setEndpoint(cl->getPoint2());
273 // convert circles to arcs:
274 if (e1->rtti()==RS2::EntityCircle) {
275 RS_Circle* c = (RS_Circle*)e1;
277 RS_ArcData data(c->getCenter(), c->getRadius(), 0.0, 2*M_PI, false);
282 if (e2->rtti()==RS2::EntityCircle) {
283 RS_Circle* c = (RS_Circle*)e2;
285 RS_ArcData data(c->getCenter(), c->getRadius(), 0.0, 2*M_PI, false);
295 if (te1->rtti()==RS2::EntityLine &&
296 te2->rtti()==RS2::EntityLine) {
298 RS_Line* line1 = (RS_Line*)te1;
299 RS_Line* line2 = (RS_Line*)te2;
301 ret = getIntersectionLineLine(line1, line2);
306 else if (te1->rtti()==RS2::EntityLine &&
307 te2->rtti()==RS2::EntityArc) {
309 RS_Line* line = (RS_Line*)te1;
310 RS_Arc* arc = (RS_Arc*)te2;
312 ret = getIntersectionLineArc(line, arc);
317 else if (te1->rtti()==RS2::EntityArc &&
318 te2->rtti()==RS2::EntityLine) {
320 RS_Arc* arc = (RS_Arc*)te1;
321 RS_Line* line = (RS_Line*)te2;
323 ret = getIntersectionLineArc(line, arc);
328 else if (te1->rtti()==RS2::EntityArc &&
329 te2->rtti()==RS2::EntityArc) {
331 RS_Arc* arc1 = (RS_Arc*)te1;
332 RS_Arc* arc2 = (RS_Arc*)te2;
334 ret = getIntersectionArcArc(arc1, arc2);
336 RS_DEBUG->print("RS_Information::getIntersection:: Unsupported entity type.");
341 // Check all intersection points for being on entities:
343 if (onEntities==true) {
344 if (!e1->isPointOnEntity(ret.get(0), tol) ||
345 !e2->isPointOnEntity(ret.get(0), tol)) {
346 ret.set(0, Vector(false));
348 if (!e1->isPointOnEntity(ret.get(1), tol) ||
349 !e2->isPointOnEntity(ret.get(1), tol)) {
350 ret.set(1, Vector(false));
352 if (!e1->isPointOnEntity(ret.get(2), tol) ||
353 !e2->isPointOnEntity(ret.get(2), tol)) {
354 ret.set(2, Vector(false));
356 if (!e1->isPointOnEntity(ret.get(3), tol) ||
357 !e2->isPointOnEntity(ret.get(3), tol)) {
358 ret.set(3, Vector(false));
363 for (int i=0; i<4; ++i) {
364 if (ret.get(i).valid) {
365 ret.set(k, ret.get(i));
369 for (int i=k; i<4; ++i) {
370 ret.set(i, Vector(false));
379 * @return Intersection between two lines.
381 VectorSolutions RS_Information::getIntersectionLineLine(RS_Line* e1,
386 if (e1==NULL || e2==NULL) {
390 Vector p1 = e1->getStartpoint();
391 Vector p2 = e1->getEndpoint();
392 Vector p3 = e2->getStartpoint();
393 Vector p4 = e2->getEndpoint();
395 double num = ((p4.x-p3.x)*(p1.y-p3.y) - (p4.y-p3.y)*(p1.x-p3.x));
396 double div = ((p4.y-p3.y)*(p2.x-p1.x) - (p4.x-p3.x)*(p2.y-p1.y));
398 if (fabs(div)>RS_TOLERANCE) {
399 double u = num / div;
401 double xs = p1.x + u * (p2.x-p1.x);
402 double ys = p1.y + u * (p2.y-p1.y);
403 ret = VectorSolutions(Vector(xs, ys));
406 // lines are parallel
408 ret = VectorSolutions();
417 * @return One or two intersection points between given entities.
419 VectorSolutions RS_Information::getIntersectionLineArc(RS_Line* line,
424 if (line==NULL || arc==NULL) {
430 nearest = line->getNearestPointOnEntity(arc->getCenter(), false, &dist);
432 // special case: arc touches line (tangent):
433 if (fabs(dist - arc->getRadius()) < 1.0e-4) {
434 ret = VectorSolutions(nearest);
435 ret.setTangent(true);
439 Vector p = line->getStartpoint();
440 Vector d = line->getEndpoint() - line->getStartpoint();
441 if (d.magnitude()<1.0e-6) {
445 Vector c = arc->getCenter();
446 double r = arc->getRadius();
447 Vector delta = p - c;
450 double term = RS_Math::pow(Vector::dotP(d, delta), 2.0)
451 - RS_Math::pow(d.magnitude(), 2.0)
452 * (RS_Math::pow(delta.magnitude(), 2.0) - RS_Math::pow(r, 2.0));
460 // one or two intersections:
462 double t1 = (- Vector::dotP(d, delta) + sqrt(term))
463 / RS_Math::pow(d.magnitude(), 2.0);
465 bool tangent = false;
467 // only one intersection:
468 if (fabs(term)<RS_TOLERANCE) {
475 t2 = (-Vector::dotP(d, delta) - sqrt(term))
476 / RS_Math::pow(d.magnitude(), 2.0);
488 ret = VectorSolutions(sol1, sol2);
489 ret.setTangent(tangent);
498 * @return One or two intersection points between given entities.
500 VectorSolutions RS_Information::getIntersectionArcArc(RS_Arc* e1,
505 if (e1==NULL || e2==NULL) {
509 Vector c1 = e1->getCenter();
510 Vector c2 = e2->getCenter();
512 double r1 = e1->getRadius();
513 double r2 = e2->getRadius();
518 if (u.magnitude()<1.0e-6) {
522 Vector v = Vector(u.y, -u.x);
524 double s, t1, t2, term;
526 s = 1.0/2.0 * ((r1*r1 - r2*r2)/(RS_Math::pow(u.magnitude(), 2.0)) + 1.0);
528 term = (r1*r1)/(RS_Math::pow(u.magnitude(), 2.0)) - s*s;
532 ret = VectorSolutions();
535 // one or two intersections:
539 bool tangent = false;
541 Vector sol1 = c1 + u*s + v*t1;
542 Vector sol2 = c1 + u*s + v*t2;
544 if (sol1.distanceTo(sol2)<1.0e-4) {
545 sol2 = Vector(false);
546 ret = VectorSolutions(sol1);
549 ret = VectorSolutions(sol1, sol2);
552 ret.setTangent(tangent);
561 * @return One or two intersection points between given entities.
563 VectorSolutions RS_Information::getIntersectionLineEllipse(RS_Line* line,
564 RS_Ellipse* ellipse) {
568 if (line==NULL || ellipse==NULL) {
572 // rotate into normal position:
573 double ang = ellipse->getAngle();
575 double rx = ellipse->getMajorRadius();
576 double ry = ellipse->getMinorRadius();
577 Vector center = ellipse->getCenter();
578 Vector a1 = line->getStartpoint().rotate(center, -ang);
579 Vector a2 = line->getEndpoint().rotate(center, -ang);
582 Vector diff = origin - center;
583 Vector mDir = Vector(dir.x/(rx*rx), dir.y/(ry*ry));
584 Vector mDiff = Vector(diff.x/(rx*rx), diff.y/(ry*ry));
586 double a = Vector::dotP(dir, mDir);
587 double b = Vector::dotP(dir, mDiff);
588 double c = Vector::dotP(diff, mDiff) - 1.0;
589 double d = b*b - a*c;
592 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 0");
593 } else if ( d > 0 ) {
594 double root = sqrt(d);
595 double t_a = (-b - root) / a;
596 double t_b = (-b + root) / a;
598 /*if ( (t_a < 0 || 1 < t_a) && (t_b < 0 || 1 < t_b) ) {
599 if ( (t_a < 0 && t_b < 0) || (t_a > 1 && t_b > 1) ) {
600 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 1");
603 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: inside 1");
606 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: intersection 1");
609 //if ( 0 <= t_a && t_a <= 1 ) {
610 //RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: 0<=t_a<=1");
611 ret1 = a1.lerp(a2, t_a);
612 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: ret1: %f/%f", ret1.x, ret1.y);
614 //if ( 0 <= t_b && t_b <= 1 ) {
615 //RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: 0<=t_b<=1");
616 ret2 = a1.lerp(a2, t_b);
617 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: ret2: %f/%f", ret2.x, ret2.y);
619 if (ret1.valid && ret2.valid) {
620 ret = VectorSolutions(ret1, ret2);
624 ret = VectorSolutions(ret1);
627 ret = VectorSolutions(ret2);
633 if ( 0 <= t && t <= 1 ) {
634 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: 0<=t<=1");
635 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: intersection 2");
636 ret = VectorSolutions(a1.lerp(a2, t));
637 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: ret1: %f/%f", ret.get(0).x, ret.get(0).y);
639 RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 2");
643 ret.rotate(center, ang);
649 RS_Arc* arc = new RS_Arc(NULL,
650 RS_ArcData(ellipse->getCenter(),
651 ellipse->getMajorRadius(),
652 ellipse->getAngle1(),
653 ellipse->getAngle2(),
655 RS_Line* other = (RS_Line*)line->clone();
656 double angle = ellipse->getAngle();
657 //double ratio = ellipse->getRatio();
660 other->rotate(ellipse->getCenter(), -angle);
661 other->scale(ellipse->getCenter(), Vector(1.0, 1.0/ellipse->getRatio()));
663 ret = getIntersectionLineArc(other, arc);
665 ret.scale(ellipse->getCenter(), Vector(1.0, ellipse->getRatio()));
666 ret.rotate(ellipse->getCenter(), angle);
677 * Checks if the given coordinate is inside the given contour.
679 * @param point Coordinate to check.
680 * @param contour One or more entities which shape a contour.
681 * If the given contour is not closed, the result is undefined.
682 * The entities don't need to be in a specific order.
683 * @param onContour Will be set to true if the given point it exactly
686 bool RS_Information::isPointInsideContour(const Vector& point,
687 RS_EntityContainer* contour, bool* onContour) {
690 RS_DEBUG->print(RS_Debug::D_WARNING,
691 "RS_Information::isPointInsideContour: contour is NULL");
695 if (point.x < contour->getMin().x || point.x > contour->getMax().x ||
696 point.y < contour->getMin().y || point.y > contour->getMax().y) {
700 double width = contour->getSize().x+1.0;
705 double rayAngle = 0.0;
711 v.setPolar(width*10.0, rayAngle);
712 RS_Line ray(NULL, RS_LineData(point, point+v));
716 if (onContour!=NULL) {
720 for (RS_Entity* e = contour->firstEntity(RS2::ResolveAll);
722 e = contour->nextEntity(RS2::ResolveAll)) {
724 // intersection(s) from ray with contour entity:
725 sol = RS_Information::getIntersection(&ray, e, true);
727 for (int i=0; i<=1; ++i) {
728 Vector p = sol.get(i);
731 // point is on the contour itself
732 if (p.distanceTo(point)<1.0e-5) {
733 if (onContour!=NULL) {
737 if (e->rtti()==RS2::EntityLine) {
738 RS_Line* line = (RS_Line*)e;
740 // ray goes through startpoint of line:
741 if (p.distanceTo(line->getStartpoint())<1.0e-4) {
742 if (RS_Math::correctAngle(line->getAngle1())<M_PI) {
748 // ray goes through endpoint of line:
749 else if (p.distanceTo(line->getEndpoint())<1.0e-4) {
750 if (RS_Math::correctAngle(line->getAngle2())<M_PI) {
755 // ray goes through the line:
761 } else if (e->rtti()==RS2::EntityArc) {
762 RS_Arc* arc = (RS_Arc*)e;
764 if (p.distanceTo(arc->getStartpoint())<1.0e-4) {
765 double dir = arc->getDirection1();
766 if ((dir<M_PI && dir>=1.0e-5) ||
767 ((dir>2*M_PI-1.0e-5 || dir<1.0e-5) &&
768 arc->getCenter().y>p.y)) {
773 else if (p.distanceTo(arc->getEndpoint())<1.0e-4) {
774 double dir = arc->getDirection2();
775 if ((dir<M_PI && dir>=1.0e-5) ||
776 ((dir>2*M_PI-1.0e-5 || dir<1.0e-5) &&
777 arc->getCenter().y>p.y)) {
784 } else if (e->rtti()==RS2::EntityCircle) {
786 if (i==0 && sol.get(1).valid==false) {
787 if (!sol.isTangent()) {
792 } else if (i==1 || sol.get(1).valid==true) {
804 while (!sure && rayAngle<2*M_PI && tries<6);
806 // remove double intersections:
808 RS_PtrList<Vector> is2;
813 double minDist = RS_MAXDOUBLE;
816 for (Vector* v = is.first(); v!=NULL; v = is.next()) {
817 dist = point.distanceTo(*v);
825 if (!done && av!=NULL) {
831 return ((counter%2)==1);