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 "information.h"
19 #include "constructionline.h"
23 * Default constructor.
25 * @param container The container to which we will add
26 * entities. Usually that's an Drawing entity but
27 * it can also be a polyline, text, ...
29 Information::Information(EntityContainer& container)
31 this->container = &container;
35 * @return true: if the entity is a dimensioning enity.
38 bool Information::isDimension(RS2::EntityType type)
40 if (type == RS2::EntityDimAligned || type == RS2::EntityDimLinear
41 || type == RS2::EntityDimRadial || type == RS2::EntityDimDiametric
42 || 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 Information::isTrimmable(Entity * e)
62 if (e->getParent()->rtti() == RS2::EntityPolyline)
66 else if (e->getParent()->rtti() == RS2::EntityContainer
67 || e->getParent()->rtti() == RS2::EntityGraphic
68 || e->getParent()->rtti() == RS2::EntityBlock)
80 * @retval true the two entities can be trimmed to each other;
81 * i.e. they are in a graphic or in the same polyline.
83 bool Information::isTrimmable(Entity * e1, Entity * e2)
87 if (e1->getParent() && e2->getParent())
89 if (e1->getParent()->rtti() == RS2::EntityPolyline
90 && e2->getParent()->rtti() == RS2::EntityPolyline
91 && e1->getParent()==e2->getParent())
93 // in the same polyline
94 EntityContainer * pl = e1->getParent();
95 int idx1 = pl->findEntity(e1);
96 int idx2 = pl->findEntity(e2);
97 DEBUG->print("Information::isTrimmable: idx1: %d, idx2: %d", idx1, idx2);
99 if (abs(idx1 - idx2) == 1 || abs(idx1 - idx2) == pl->count() - 1)
101 // directly following entities
106 // not directly following entities
110 else if ((e1->getParent()->rtti() == RS2::EntityContainer
111 || e1->getParent()->rtti() == RS2::EntityGraphic
112 || e1->getParent()->rtti() == RS2::EntityBlock)
113 && (e2->getParent()->rtti() == RS2::EntityContainer
114 || e2->getParent()->rtti() == RS2::EntityGraphic
115 || e2->getParent()->rtti() == RS2::EntityBlock))
123 // independent entities with the same parent:
124 return (e1->getParent() == e2->getParent());
132 * Gets the nearest end point to the given coordinate.
134 * @param coord Coordinate (typically a mouse coordinate)
136 * @return the coordinate found or an invalid vector
137 * if there are no elements at all in this graphics
140 Vector Information::getNearestEndpoint(const Vector & coord, double * dist) const
142 return container->getNearestEndpoint(coord, dist);
146 * Gets the nearest point to the given coordinate which is on an entity.
148 * @param coord Coordinate (typically a mouse coordinate)
149 * @param dist Pointer to a double which will contain the
150 * measured distance after return or NULL
151 * @param entity Pointer to a pointer which will point to the
152 * entity on which the point is or NULL
154 * @return the coordinate found or an invalid vector
155 * if there are no elements at all in this graphics
158 Vector Information::getNearestPointOnEntity(const Vector & coord,
159 bool onEntity, double * dist, Entity ** entity) const
161 return container->getNearestPointOnEntity(coord, onEntity, dist, entity);
165 * Gets the nearest entity to the given coordinate.
167 * @param coord Coordinate (typically a mouse coordinate)
168 * @param dist Pointer to a double which will contain the
169 * masured distance after return
170 * @param level Level of resolving entities.
172 * @return the entity found or NULL if there are no elements
173 * at all in this graphics container.
175 Entity * Information::getNearestEntity(const Vector & coord,
176 double * dist, RS2::ResolveLevel level) const
178 return container->getNearestEntity(coord, dist, level);
182 * Calculates the intersection point(s) between two entities.
184 * @param onEntities true: only return intersection points which are
186 * false: return all intersection points.
188 * @todo support more entities
190 * @return All intersections of the two entities. The tangent flag in
191 * VectorSolutions is set if one intersection is a tangent point.
193 VectorSolutions Information::getIntersection(Entity * e1,
194 Entity * e2, bool onEntities)
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)
213 if (e2->rtti() == RS2::EntityEllipse)
220 if (e2->rtti() == RS2::EntityLine)
222 Ellipse * ellipse = (Ellipse *)e1;
223 ret = getIntersectionLineEllipse((Line *)e2, ellipse);
226 // ellipse / arc, ellipse / ellipse: not supported:
237 // entity copies - so we only have to deal with lines and arcs
238 Line l1(NULL, LineData(Vector(0.0, 0.0), Vector(0.0, 0.0)));
239 Line l2(NULL, LineData(Vector(0.0, 0.0), Vector(0.0, 0.0)));
241 Arc a1(NULL, ArcData(Vector(0.0, 0.0), 1.0, 0.0, 2 * M_PI, false));
242 Arc a2(NULL, ArcData(Vector(0.0, 0.0), 1.0, 0.0, 2 * M_PI, false));
244 // convert construction lines to lines:
245 if (e1->rtti() == RS2::EntityConstructionLine)
247 ConstructionLine * cl = (ConstructionLine *)e1;
248 l1.setStartpoint(cl->getPoint1());
249 l1.setEndpoint(cl->getPoint2());
253 if (e2->rtti() == RS2::EntityConstructionLine)
255 ConstructionLine * cl = (ConstructionLine *)e2;
256 l2.setStartpoint(cl->getPoint1());
257 l2.setEndpoint(cl->getPoint2());
261 // convert circles to arcs:
262 if (e1->rtti() == RS2::EntityCircle)
264 Circle * c = (Circle *)e1;
265 ArcData data(c->getCenter(), c->getRadius(), 0.0, 2 * M_PI, false);
270 if (e2->rtti() == RS2::EntityCircle)
272 Circle * c = (Circle *)e2;
273 ArcData data(c->getCenter(), c->getRadius(), 0.0, 2 * M_PI, false);
281 if (te1->rtti() == RS2::EntityLine && te2->rtti() == RS2::EntityLine)
283 Line * line1 = (Line *)te1;
284 Line * line2 = (Line *)te2;
285 ret = getIntersectionLineLine(line1, line2);
289 else if (te1->rtti() == RS2::EntityLine && te2->rtti() == RS2::EntityArc)
291 Line * line = (Line *)te1;
292 Arc * arc = (Arc *)te2;
293 ret = getIntersectionLineArc(line, arc);
297 else if (te1->rtti() == RS2::EntityArc && te2->rtti() == RS2::EntityLine)
299 Arc * arc = (Arc *)te1;
300 Line * line = (Line *)te2;
301 ret = getIntersectionLineArc(line, arc);
305 else if (te1->rtti() == RS2::EntityArc && te2->rtti() == RS2::EntityArc)
307 Arc * arc1 = (Arc *)te1;
308 Arc * arc2 = (Arc *)te2;
309 ret = getIntersectionArcArc(arc1, arc2);
313 DEBUG->print("Information::getIntersection:: Unsupported entity type.");
317 // Check all intersection points for being on entities:
321 if (!e1->isPointOnEntity(ret.get(0), tol) || !e2->isPointOnEntity(ret.get(0), tol))
323 ret.set(0, Vector(false));
326 if (!e1->isPointOnEntity(ret.get(1), tol) || !e2->isPointOnEntity(ret.get(1), tol))
328 ret.set(1, Vector(false));
331 if (!e1->isPointOnEntity(ret.get(2), tol) || !e2->isPointOnEntity(ret.get(2), tol))
333 ret.set(2, Vector(false));
336 if (!e1->isPointOnEntity(ret.get(3), tol) || !e2->isPointOnEntity(ret.get(3), tol))
338 ret.set(3, Vector(false));
343 for(int i=0; i<4; ++i)
345 if (ret.get(i).valid)
347 ret.set(k, ret.get(i));
352 for (int i=k; i<4; ++i)
354 ret.set(i, Vector(false));
361 * @return Intersection between two lines.
363 VectorSolutions Information::getIntersectionLineLine(Line * e1, Line * e2)
370 Vector p1 = e1->getStartpoint();
371 Vector p2 = e1->getEndpoint();
372 Vector p3 = e2->getStartpoint();
373 Vector p4 = e2->getEndpoint();
375 double num = ((p4.x - p3.x) * (p1.y - p3.y) - (p4.y - p3.y) * (p1.x - p3.x));
376 double div = ((p4.y - p3.y) * (p2.x - p1.x) - (p4.x - p3.x) * (p2.y - p1.y));
378 if (fabs(div) > RS_TOLERANCE)
380 double u = num / div;
381 double xs = p1.x + u * (p2.x - p1.x);
382 double ys = p1.y + u * (p2.y - p1.y);
383 ret = VectorSolutions(Vector(xs, ys));
385 // lines are parallel
387 ret = VectorSolutions();
393 * @return One or two intersection points between given entities.
395 VectorSolutions Information::getIntersectionLineArc(Line * line, Arc * arc)
399 if (line==NULL || arc==NULL) {
405 nearest = line->getNearestPointOnEntity(arc->getCenter(), false, &dist);
407 // special case: arc touches line (tangent):
408 if (fabs(dist - arc->getRadius()) < 1.0e-4) {
409 ret = VectorSolutions(nearest);
410 ret.setTangent(true);
414 Vector p = line->getStartpoint();
415 Vector d = line->getEndpoint() - line->getStartpoint();
416 if (d.magnitude()<1.0e-6) {
420 Vector c = arc->getCenter();
421 double r = arc->getRadius();
422 Vector delta = p - c;
425 double term = Math::pow(Vector::dotP(d, delta), 2.0)
426 - Math::pow(d.magnitude(), 2.0)
427 * (Math::pow(delta.magnitude(), 2.0) - Math::pow(r, 2.0));
435 // one or two intersections:
437 double t1 = (- Vector::dotP(d, delta) + sqrt(term))
438 / Math::pow(d.magnitude(), 2.0);
440 bool tangent = false;
442 // only one intersection:
443 if (fabs(term)<RS_TOLERANCE) {
450 t2 = (-Vector::dotP(d, delta) - sqrt(term))
451 / Math::pow(d.magnitude(), 2.0);
463 ret = VectorSolutions(sol1, sol2);
464 ret.setTangent(tangent);
471 * @return One or two intersection points between given entities.
473 VectorSolutions Information::getIntersectionArcArc(Arc * e1, Arc * e2)
477 if (e1==NULL || e2==NULL) {
481 Vector c1 = e1->getCenter();
482 Vector c2 = e2->getCenter();
484 double r1 = e1->getRadius();
485 double r2 = e2->getRadius();
490 if (u.magnitude()<1.0e-6) {
494 Vector v = Vector(u.y, -u.x);
496 double s, t1, t2, term;
498 s = 1.0/2.0 * ((r1*r1 - r2*r2)/(Math::pow(u.magnitude(), 2.0)) + 1.0);
500 term = (r1*r1)/(Math::pow(u.magnitude(), 2.0)) - s*s;
504 ret = VectorSolutions();
507 // one or two intersections:
511 bool tangent = false;
513 Vector sol1 = c1 + u*s + v*t1;
514 Vector sol2 = c1 + u*s + v*t2;
516 if (sol1.distanceTo(sol2)<1.0e-4) {
517 sol2 = Vector(false);
518 ret = VectorSolutions(sol1);
521 ret = VectorSolutions(sol1, sol2);
524 ret.setTangent(tangent);
531 * @return One or two intersection points between given entities.
533 VectorSolutions Information::getIntersectionLineEllipse(Line * line, Ellipse * ellipse)
537 if (line==NULL || ellipse==NULL) {
541 // rotate into normal position:
542 double ang = ellipse->getAngle();
544 double rx = ellipse->getMajorRadius();
545 double ry = ellipse->getMinorRadius();
546 Vector center = ellipse->getCenter();
547 Vector a1 = line->getStartpoint().rotate(center, -ang);
548 Vector a2 = line->getEndpoint().rotate(center, -ang);
551 Vector diff = origin - center;
552 Vector mDir = Vector(dir.x/(rx*rx), dir.y/(ry*ry));
553 Vector mDiff = Vector(diff.x/(rx*rx), diff.y/(ry*ry));
555 double a = Vector::dotP(dir, mDir);
556 double b = Vector::dotP(dir, mDiff);
557 double c = Vector::dotP(diff, mDiff) - 1.0;
558 double d = b*b - a*c;
561 DEBUG->print("Information::getIntersectionLineEllipse: outside 0");
562 } else if ( d > 0 ) {
563 double root = sqrt(d);
564 double t_a = (-b - root) / a;
565 double t_b = (-b + root) / a;
567 /*if ( (t_a < 0 || 1 < t_a) && (t_b < 0 || 1 < t_b) ) {
568 if ( (t_a < 0 && t_b < 0) || (t_a > 1 && t_b > 1) ) {
569 DEBUG->print("Information::getIntersectionLineEllipse: outside 1");
572 DEBUG->print("Information::getIntersectionLineEllipse: inside 1");
575 DEBUG->print("Information::getIntersectionLineEllipse: intersection 1");
578 //if ( 0 <= t_a && t_a <= 1 ) {
579 //DEBUG->print("Information::getIntersectionLineEllipse: 0<=t_a<=1");
580 ret1 = a1.lerp(a2, t_a);
581 DEBUG->print("Information::getIntersectionLineEllipse: ret1: %f/%f", ret1.x, ret1.y);
583 //if ( 0 <= t_b && t_b <= 1 ) {
584 //DEBUG->print("Information::getIntersectionLineEllipse: 0<=t_b<=1");
585 ret2 = a1.lerp(a2, t_b);
586 DEBUG->print("Information::getIntersectionLineEllipse: ret2: %f/%f", ret2.x, ret2.y);
588 if (ret1.valid && ret2.valid) {
589 ret = VectorSolutions(ret1, ret2);
593 ret = VectorSolutions(ret1);
596 ret = VectorSolutions(ret2);
602 if ( 0 <= t && t <= 1 ) {
603 DEBUG->print("Information::getIntersectionLineEllipse: 0<=t<=1");
604 DEBUG->print("Information::getIntersectionLineEllipse: intersection 2");
605 ret = VectorSolutions(a1.lerp(a2, t));
606 DEBUG->print("Information::getIntersectionLineEllipse: ret1: %f/%f", ret.get(0).x, ret.get(0).y);
608 DEBUG->print("Information::getIntersectionLineEllipse: outside 2");
612 ret.rotate(center, ang);
618 Arc* arc = new Arc(NULL,
619 ArcData(ellipse->getCenter(),
620 ellipse->getMajorRadius(),
621 ellipse->getAngle1(),
622 ellipse->getAngle2(),
624 Line* other = (Line*)line->clone();
625 double angle = ellipse->getAngle();
626 //double ratio = ellipse->getRatio();
629 other->rotate(ellipse->getCenter(), -angle);
630 other->scale(ellipse->getCenter(), Vector(1.0, 1.0/ellipse->getRatio()));
632 ret = getIntersectionLineArc(other, arc);
634 ret.scale(ellipse->getCenter(), Vector(1.0, ellipse->getRatio()));
635 ret.rotate(ellipse->getCenter(), angle);
645 * Checks if the given coordinate is inside the given contour.
647 * @param point Coordinate to check.
648 * @param contour One or more entities which shape a contour.
649 * If the given contour is not closed, the result is undefined.
650 * The entities don't need to be in a specific order.
651 * @param onContour Will be set to true if the given point it exactly
654 bool Information::isPointInsideContour(const Vector & point,
655 EntityContainer * contour, bool * onContour)
658 DEBUG->print(Debug::D_WARNING,
659 "Information::isPointInsideContour: contour is NULL");
663 if (point.x < contour->getMin().x || point.x > contour->getMax().x ||
664 point.y < contour->getMin().y || point.y > contour->getMax().y)
667 double width = contour->getSize().x+1.0;
671 double rayAngle = 0.0;
679 v.setPolar(width*10.0, rayAngle);
680 Line ray(NULL, LineData(point, point+v));
687 for(Entity * e=contour->firstEntity(RS2::ResolveAll); e!=NULL;
688 e=contour->nextEntity(RS2::ResolveAll))
690 // intersection(s) from ray with contour entity:
691 sol = Information::getIntersection(&ray, e, true);
693 for(int i=0; i<=1; ++i)
695 Vector p = sol.get(i);
699 // point is on the contour itself
700 if (p.distanceTo(point) < 1.0e-5)
709 if (e->rtti() == RS2::EntityLine)
711 Line * line = (Line *)e;
713 // ray goes through startpoint of line:
714 if (p.distanceTo(line->getStartpoint()) < 1.0e-4)
716 if (Math::correctAngle(line->getAngle1()) < M_PI)
722 // ray goes through endpoint of line:
723 else if (p.distanceTo(line->getEndpoint()) < 1.0e-4)
725 if (Math::correctAngle(line->getAngle2()) < M_PI)
731 // ray goes through the line:
737 else if (e->rtti() == RS2::EntityArc)
739 Arc * arc = (Arc *)e;
741 if (p.distanceTo(arc->getStartpoint()) < 1.0e-4)
743 double dir = arc->getDirection1();
745 if ((dir < M_PI && dir >= 1.0e-5)
746 || ((dir > 2 * M_PI - 1.0e-5 || dir < 1.0e-5)
747 && arc->getCenter().y > p.y))
753 else if (p.distanceTo(arc->getEndpoint()) < 1.0e-4)
755 double dir = arc->getDirection2();
757 if ((dir < M_PI && dir >= 1.0e-5)
758 || ((dir > 2 * M_PI - 1.0e-5 || dir < 1.0e-5)
759 && arc->getCenter().y>p.y))
770 else if (e->rtti() == RS2::EntityCircle)
773 if (i == 0 && sol.get(1).valid == false)
775 if (!sol.isTangent())
784 else if (i == 1 || sol.get(1).valid)
797 while (!sure && rayAngle < 2 * M_PI && tries < 6);
799 // remove double intersections:
801 Q3PtrList<Vector> is2;
806 double minDist = RS_MAXDOUBLE;
809 for (Vector* v = is.first(); v!=NULL; v = is.next()) {
810 dist = point.distanceTo(*v);
818 if (!done && av!=NULL) {
824 return ((counter % 2) == 1);