PlanimetryCalculus.cc 11.6 KB
Newer Older
1
#include "PlanimetryCalculus.h"
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
#include "Circle.h"


namespace PlanimetryCalculus {
    namespace  {
        /*!
            \fn IntersectType intersects(const Circle &circle, const QLineF &line, QList<QPointF> &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
        */
        IntersectType intersects(const Circle &circle, const QLineF &line, QList<QPointF> &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 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 Tangent;
                    }

                    return 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 ? Secant : NoIntersection;
                }
            }
70

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
            return Error;
        }
    } // end anonymous namespace

    /*!
        \fn void rotatePoint(QPointF &point, double alpha)
        Rotates the \a point counter clockwisely by the angle \a alpha (in radiants).
    */
    void rotatePoint(QPointF &point, double alpha)
    {
        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));
        }
88 89
    }

90 91 92 93 94
    void rotatePoint(QList<QPointF> &points, double alpha)
    {
        for (int i = 0; i < points.size(); i++) {
            rotatePoint(points[i], alpha);
        }
95
    }
96

97 98 99 100 101 102 103
    /*!
        \fn void rotatePointDegree(QPointF &point, double alpha)
        Rotates the \a point counter clockwisely by the angle \a alpha (in degrees).
    */
    void rotatePointDegree(QPointF &point, double alpha)
    {
        rotatePoint(point, alpha/180*M_PI);
104
    }
105

106 107 108 109
    void rotatePointDegree(QList<QPointF> &points, double alpha)
    {
        for (int i = 0; i < points.size(); i++) {
            rotatePointDegree(points[i], alpha);
110
        }
111
    }
112

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155
    /*!
        \fn IntersectType 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
    */
    IntersectType intersects(const Circle &circle1, const Circle &circle2)
    {
        // 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 InsideNoIntersection;
            } else if (qFuzzyCompare(r + d, R)) {
                if    (qFuzzyIsNull(d))
                    return CirclesEqual;
                else
                    return InsideTouching;
            } else if (d < R) {
                return InsideIntersection;
            } else if (d - r < R) {
                return OutsideIntersection;
            } else if (qFuzzyCompare(d - r, R)) {
                return OutsideTouching;
            } else {
                return OutsideNoIntersection;
            }
156
        }
157
        return Error;
158 159
    }

160 161 162 163
    /*!
        \fn IntersectType intersects(const Circle &circle1, const Circle &circle2, QList<QPointF> 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.
164

165
        The function assumes that the list \a intersectionPoints is empty.
166

167
        \note Returns Error if circle.isNull() returns true;
168

169 170 171 172 173 174 175 176 177 178 179
        \sa Circle
    */
    IntersectType intersects(const Circle &circle1, const Circle &circle2, QList<QPointF> &intersectionPoints)
    {
        IntersectType returnValue = intersects(circle1, circle2);

        if (   returnValue == InsideNoIntersection
            || returnValue == OutsideNoIntersection
            || returnValue == CirclesEqual
            || returnValue == Error  ) {
            return returnValue; // No intersection Points, or infinitly many (in case of CirclesEqual).
180
        } else {
181 182 183 184 185 186 187 188 189 190 191 192 193 194
            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;
            }
195

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
            if        (   returnValue == InsideTouching
                       || returnValue == 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 == InsideIntersection
                     //              || returnValue == 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);
215

216 217
            return returnValue;
        }
218 219 220
    }


221 222 223 224
    /*!
        \fn IntersectType 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}.
225

226 227 228 229 230 231 232
        \sa QPointF, Circle
    */
    IntersectType intersects(const Circle &circle, const QLineF &line)
    {
        QList<QPointF> dummyList;
        return intersects(circle, line, dummyList, false /* calculate intersection points*/);
    }
233

234 235 236 237
    IntersectType intersects(const Circle &circle, const QLineF &line, QList<QPointF> &intersectionPoints)
    {
        return intersects(circle, line, intersectionPoints, true /* calculate intersection points*/);
    }
238

239 240 241 242 243 244 245 246 247 248 249 250
    /*!
        \fn double distance(const QPointF &p1, const QPointF p2)
        Calculates the distance (2-norm) between \a p1 and \a p2.
        \sa QPointF
    */
    double distance(const QPointF &p1, const QPointF p2)
    {
        double dx = p2.x()-p1.x();
        double dy = p2.y()-p1.y();

        return qSqrt(dx*dx+dy*dy);
    }
251

252 253 254 255
    /*!
        \fn double 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().
256

257 258 259 260 261 262 263
        \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 angle(const QPointF &p1, const QPointF p2)
    {
        double dx = p2.x()-p1.x();
        double dy = p2.y()-p1.y();
264

265 266
        return qAtan2(dy, dx);
    }
267

268 269 270 271 272 273 274 275 276 277 278 279
    /*!
        \fn double 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 angleDegree(const QPointF &p1, const QPointF p2)
    {
        return angle(p1, p2)*180/M_PI;
    }
280

281 282 283 284
    double truncateAngle(double angle)
    {
        while (angle < 0     ) { angle += 2*M_PI;}
        while (angle > 2*M_PI) { angle -= 2*M_PI;}
285

286 287
        return angle;
    }
288

289 290 291 292
    double truncateAngleDegree(double angle)
    {
        return truncateAngle(angle/180*M_PI);
    }
293 294


295
} // end namespace PlanimetryCalculus
296 297 298



299 300 301 302 303 304
/*!
    \class PlanimetryCalculus
    \inmodule Wima

    \brief The \c PlanimetryCalculus class provides routines handy for planimetrical (2D) calculations.
*/