From 38ec24c51e4eb45efd390c66536f8f5cf3ed7b7f Mon Sep 17 00:00:00 2001 From: Valentin Platzgummer Date: Mon, 15 Jul 2019 15:08:20 +0200 Subject: [PATCH] before changeing planiCalc form class to ordinary header file --- src/Wima/Circle.cc | 13 +- src/Wima/Circle.h | 3 +- src/Wima/PlanimetryCalculus.cc | 243 +++++++++++++++++++++++++++++++-- src/Wima/PlanimetryCalculus.h | 18 ++- 4 files changed, 258 insertions(+), 19 deletions(-) diff --git a/src/Wima/Circle.cc b/src/Wima/Circle.cc index 552c4bd55..f755a0016 100644 --- a/src/Wima/Circle.cc +++ b/src/Wima/Circle.cc @@ -12,18 +12,16 @@ Circle::Circle(QObject *parent) Circle::Circle(double radius, QObject *parent) : QObject (parent) - , _circleRadius(radius) + , _circleRadius(radius >= 0 ? radius : 0) , _circleOrigin(QPointF(0,0)) { - } Circle::Circle(double radius, QPointF origin, QObject *parent) : QObject (parent) - , _circleRadius(radius) + , _circleRadius(radius >= 0 ? radius : 0) , _circleOrigin(origin) { - } Circle::Circle(const Circle &other, QObject *parent) @@ -95,7 +93,7 @@ QPointF Circle::origin() const * * \sa QPointF */ -QPolygonF Circle::approximate(int numberOfCorners) +QPolygonF Circle::approximate(int numberOfCorners) const { if ( numberOfCorners < 3) return QPolygonF(); @@ -114,6 +112,11 @@ QPolygonF Circle::approximate(int numberOfCorners) return polygon; } +bool Circle::isNull() const +{ + return _circleRadius <= 0 ? true : false; +} + /*! * \class Circle * \brief Provies a circle with radius and origin. diff --git a/src/Wima/Circle.h b/src/Wima/Circle.h index 3b5e10bb8..0b6e3a54a 100644 --- a/src/Wima/Circle.h +++ b/src/Wima/Circle.h @@ -25,7 +25,8 @@ public: QPointF origin() const; // Member methodes - QPolygonF approximate(int numberOfCorners); + QPolygonF approximate(int numberOfCorners) const; + bool isNull() const; signals: void radiusChanged(double radius); diff --git a/src/Wima/PlanimetryCalculus.cc b/src/Wima/PlanimetryCalculus.cc index ca3a9eab7..3f2275064 100644 --- a/src/Wima/PlanimetryCalculus.cc +++ b/src/Wima/PlanimetryCalculus.cc @@ -12,11 +12,20 @@ PlanimetryCalculus::PlanimetryCalculus() */ void PlanimetryCalculus::rotatePoint(QPointF &point, double alpha) { - double x = point.x(); - double y = point.y(); + if (!point.isNull()) { + double x = point.x(); + double y = point.y(); - point.setX(x*qCos(alpha) - y*qSin(alpha)); - point.setY(x*qSin(alpha) + y*qCos(alpha)); + point.setX(x*qCos(alpha) - y*qSin(alpha)); + point.setY(x*qSin(alpha) + y*qCos(alpha)); + } +} + +void PlanimetryCalculus::rotatePoint(QList &points, double alpha) +{ + for (int i = 0; i < points.size(); i++) { + rotatePoint(points[i], alpha); + } } /*! @@ -28,19 +37,137 @@ void PlanimetryCalculus::rotatePointDegree(QPointF &point, double alpha) rotatePoint(point, alpha/180*M_PI); } +void PlanimetryCalculus::rotatePointDegree(QList &points, double alpha) +{ + for (int i = 0; i < points.size(); i++) { + rotatePointDegree(points[i], alpha); + } +} + /*! \fn PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle1, const Circle &circle2) Returns the intersection type of the two cirles \a circle1 and \a circle2. + \note Returns Error if circle.isNull() returns true; + \sa Circle */ PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle1, const Circle &circle2) { - double r1 = circle1.radius(); - double r2 = circle2.radius(); - double d = distance(circle1.origin(), circle2.origin()); + // r1 == 0 || r2 == 0 results in indefined behavior + if (!circle1.isNull() && !circle2.isNull()) { + double r1 = circle1.radius(); + double r2 = circle2.radius(); + double d = distance(circle1.origin(), circle2.origin()); + double r = 0; + double R = 0; + if (r1 > r2) { + R = r1; // large + r = r2; // small + } else { + // this branch is also choosen if r1 == r2 + R = r2; + r = r1; + } + + if (r + d < R) { + // this branch is also reached if d < rLarge && rSmall == 0 + return PlanimetryCalculus::InsideNoIntersection; + } else if (qFuzzyCompare(r + d, R)) { + if (qFuzzyIsNull(d)) + return PlanimetryCalculus::CirclesEqual; + else + return PlanimetryCalculus::InsideTouching; + } else if (d < R) { + return PlanimetryCalculus::InsideIntersection; + } else if (d - r < R) { + return PlanimetryCalculus::OutsideIntersection; + } else if (qFuzzyCompare(d - r, R)) { + return PlanimetryCalculus::OutsideTouching; + } else { + return PlanimetryCalculus::OutsideNoIntersection; + } + } + return PlanimetryCalculus::Error; +} + +/*! + \fn PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle1, const Circle &circle2, QList intersectionPoints) + Calculates the intersection points of two circles if present and stores the result in \a intersectionPoints. + Returns the intersection type of the two cirles \a circle1 and \a circle2. + + The function assumes that the list \a intersectionPoints is empty. + + \note Returns Error if circle.isNull() returns true; + + \sa Circle +*/ +PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle1, const Circle &circle2, QList &intersectionPoints) +{ + PlanimetryCalculus::IntersectType returnValue = intersects(circle1, circle2); + + if ( returnValue == PlanimetryCalculus::InsideNoIntersection + || returnValue == PlanimetryCalculus::OutsideNoIntersection + || returnValue == PlanimetryCalculus::CirclesEqual + || returnValue == PlanimetryCalculus::Error ) { + return returnValue; // No intersection Points, or infinitly many (in case of CirclesEqual). + } else { + double r1 = circle1.radius(); + double r2 = circle2.radius(); + double d = distance(circle1.origin(), circle2.origin()); + double alpha = angle(circle1.origin(), circle2.origin()); + double r = 0; + double R = 0; + if (r1 > r2) { + R = r1; + r = r2; + } else { + // this branch is also choosen if r1 == r2 + R = r2; + r = r1; + } + + if ( returnValue == PlanimetryCalculus::InsideTouching + || returnValue == PlanimetryCalculus::OutsideTouching) { + // Intersection point in coordinate system of circle 1. + // Coordinate system circle1: origin = circle1.origin(), x-axis towars circle2.origin() y-axis such that the + // coordinate system is dextrorse with z-axis outward faceing with respect to the drawing plane. + intersectionPoints.append(QPointF(0, r1)); + } else { //triggered if ( returnValue == PlanimetryCalculus::InsideIntersection + // || returnValue == PlanimetryCalculus::OutsideIntersection) + // See fist branch for explanation + + // this equations are obtained by solving x^2+y^2=R^2 and (x - d)^2+y^2=r^2 + double x = (qPow(d, 2) - qPow(r, 2) + qPow(R, 2))/2/d; + double y = 1/2/d*qSqrt(4*qPow(d*R, 2) - qPow(qPow(d, 2) - qPow(r, 2) + qPow(R, 2), 2)); + + intersectionPoints.append(QPointF(x, y)); + intersectionPoints.append(QPointF(x, -y)); + } + // Transform the coordinate to the world coordinate system. Alpha is the angle between world and circle1 coordinate system. + rotatePoint(intersectionPoints, alpha); + + return returnValue; + } +} + + +/*! + \fn PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle, const QLineF &line) + Returns the Intersection type of \a circle and \a line. + Returns \c Error if either line or circe \c {isNull() == true}. + + \sa QPointF, Circle +*/ +PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle, const QLineF &line) +{ + QList dummyList; + return intersects(circle, line, dummyList, false /* calculate intersection points*/); +} - //go on here +PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle, const QLineF &line, QList &intersectionPoints) +{ + return intersects(circle, line, intersectionPoints, true /* calculate intersection points*/); } /*! @@ -50,12 +177,108 @@ PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &c */ double PlanimetryCalculus::distance(const QPointF &p1, const QPointF p2) { - double dx = p1.x()-p2.x(); - double dy = p1.y()-p2.y(); + double dx = p2.x()-p1.x(); + double dy = p2.y()-p1.y(); return qSqrt(dx*dx+dy*dy); } +/*! + \fn double PlanimetryCalculus::distance(const QPointF &p1, const QPointF p2) + Calculates the angle (in radiants) between the line defined by \a p1 and \a p2 and the x-axis according to the following rule. + Angle = qAtan2(dy, dx), where dx = p2.x()-p1.x() and dy = p2.y()-p1.y(). + + \note The order of \a p1 and \a p2 matters. Swapping \a p1 and \a p2 will result in a angle of oposite sign. + \sa QPointF +*/ +double PlanimetryCalculus::angle(const QPointF &p1, const QPointF p2) +{ + double dx = p2.x()-p1.x(); + double dy = p2.y()-p1.y(); + + return qAtan2(dy, dx); +} + +/*! + \fn double PlanimetryCalculus::distance(const QPointF &p1, const QPointF p2) + Calculates the angle (in degrees) between the line defined by \a p1 and \a p2 and the x-axis according to the following rule. + Angle = qAtan2(dy, dx)*180/pi, where dx = p2.x()-p1.x() and dy = p2.y()-p1.y(). + + \note The order of \a p1 and \a p2 matters. Swapping \a p1 and \a p2 will result in a angle of oposite sign. + \sa QPointF +*/ +double PlanimetryCalculus::angleDegree(const QPointF &p1, const QPointF p2) +{ + return angle(p1, p2)*180/M_PI; +} + +/*! + \fn PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle, const QLineF &line, QList &intersectionPoints, bool calcInstersect) + Returns the Intersection type of \a circle and \a line. + Stores the intersection points in \a intersectionPoints if \a calcIntersect is \c true. + Returns \c Error if either line or circe \c {isNull() == true}. + + \sa QPointF, Circle +*/ +PlanimetryCalculus::IntersectType PlanimetryCalculus::intersects(const Circle &circle, const QLineF &line, QList &intersectionPoints, bool calcInstersect) +{ + if (!circle.isNull() && ! line.isNull()) { + QPointF translationVector = line.p1(); + double angleWLDegree = line.angle(); // angle between wold and line coordinate system + + QPointF originCircleL = circle.origin() - translationVector; + rotatePoint(originCircleL, -angleWLDegree); // circle origin in line corrdinate system + + double y = originCircleL.y(); + double r = circle.radius(); + if (qAbs(y) > r) + return PlanimetryCalculus::NoIntersection; + else if ( qFuzzyCompare(qFabs(y), r) ) { // tangent + double x_ori = originCircleL.x(); + + if (x_ori >= 0 && x_ori <= line.length()) { + if (calcInstersect) { + QPointF intersectionPt = QPointF(x_ori, 0); + rotatePoint(intersectionPt, angleWLDegree); + intersectionPoints.append(intersectionPt + translationVector); + } + + return PlanimetryCalculus::Tangent; + } + + return PlanimetryCalculus::NoIntersection; + } else { // sekant + double x_ori = originCircleL.x(); + double y_ori = originCircleL.y(); + double delta = qSqrt(qPow(r, 2)-qPow(y_ori, 2)); + double x1 = x_ori + delta; // x coordinate (line system) of fist intersection point + double x2 = x_ori - delta;// x coordinate (line system) of second intersection point + bool doesIntersect = false; // remember if actual intersection was on the line + + if (x1 >= 0 && x1 <= line.length()) { // check if intersection point is on the line + if (calcInstersect) { + QPointF intersectionPt = QPointF(x1, 0); // first intersection point (line system) + rotatePoint(intersectionPt, angleWLDegree); + intersectionPoints.append(intersectionPt + translationVector); // transform (to world system) and append first intersection point + } + doesIntersect = true; + } + if (x2 >= 0 && x2 <= line.length()) { // check if intersection point is on the line + if (calcInstersect) { + QPointF intersectionPt = QPointF(x2, 0); // second intersection point (line system) + rotatePoint(intersectionPt, angleWLDegree); + intersectionPoints.append(intersectionPt + translationVector); // transform (to world system) and append second intersection point + } + doesIntersect = true; + } + + return doesIntersect ? PlanimetryCalculus::Secant : PlanimetryCalculus::NoIntersection; + } + } + + return PlanimetryCalculus::Error; +} + /*! \class PlanimetryCalculus \inmodule Wima diff --git a/src/Wima/PlanimetryCalculus.h b/src/Wima/PlanimetryCalculus.h index 110b2ddd9..9dccf5d42 100644 --- a/src/Wima/PlanimetryCalculus.h +++ b/src/Wima/PlanimetryCalculus.h @@ -11,16 +11,28 @@ class PlanimetryCalculus public: PlanimetryCalculus(); - enum IntersectType{CircleInsideNoIntersection, CircleInsideTouching, CircleInsideIntersection, - CircleOutsideIntersection, CircleOutsideTouching, CircleOutsideNoIntersection, //Circle Circle intersection + enum IntersectType{InsideNoIntersection, InsideTouching, InsideIntersection, + OutsideIntersection, OutsideTouching, OutsideNoIntersection, + CirclesEqual, //Circle Circle intersection - NoIntersection, Tangent, Secant // Circle Line Intersetion + NoIntersection, Tangent, Secant, // Circle Line Intersetion + + Error // general }; void rotatePoint(QPointF &point, double alpha); + void rotatePoint(QList &point, double alpha); void rotatePointDegree(QPointF &point, double alpha); + void rotatePointDegree(QList &points, double alpha); IntersectType intersects(const Circle &circle1, const Circle &circle2); + IntersectType intersects(const Circle &circle1, const Circle &circle2, QList &intersectionPoints); IntersectType intersects(const Circle &circle, const QLineF &line); + IntersectType intersects(const Circle &circle, const QLineF &line, QList &intersectionPoints); double distance(const QPointF &p1, const QPointF p2); + double angle(const QPointF &p1, const QPointF p2); + double angleDegree(const QPointF &p1, const QPointF p2); + +private: + IntersectType intersects(const Circle &circle, const QLineF &line, QList &intersectionPoints, bool calcIntersect); }; -- 2.22.0