Esempio n. 1
0
    /**
     * Creates a tangent between a given point and a circle or arc.
     * Out of the 2 possible tangents, the one closest to
     * the given coordinate is returned.
     *
     * @param coord Coordinate to define which tangent we want (typically a
     *              mouse coordinate).
     * @param point Point.
     * @param circle Circle, arc or ellipse entity.
     */
    RS_Line* RS_Creation::createTangent1(const RS_Vector& coord,
                                         const RS_Vector& point,
                                         RS_Entity* circle) {
        RS_Line* ret = NULL;
        RS_Vector circleCenter;

        // check given entities:
        if (circle==NULL || !point.valid ||
                (circle->rtti()!=RS2::EntityArc && circle->rtti()!=RS2::EntityCircle
                 && circle->rtti()!=RS2::EntityEllipse)) {

            return NULL;
        }

        if (circle->rtti()==RS2::EntityCircle) {
            circleCenter = ((RS_Circle*)circle)->getCenter();
        } else if (circle->rtti()==RS2::EntityArc) {
            circleCenter = ((RS_Arc*)circle)->getCenter();
        } else if (circle->rtti()==RS2::EntityEllipse) {
            circleCenter = ((RS_Ellipse*)circle)->getCenter();
        }

        // the two tangent points:
        RS_VectorSolutions sol;

        // calculate tangent points for arcs / circles:
        if (circle->rtti()!=RS2::EntityEllipse) {
            // create temp. thales circle:
            RS_Vector tCenter = (point + circleCenter)/2.0;
            double tRadius = point.distanceTo(tCenter);

            RS_Circle tmp(NULL, RS_CircleData(tCenter, tRadius));

            // get the two intersection points which are the tangent points:
            sol = RS_Information::getIntersection(&tmp, circle, false);
        }

        // calculate tangent points for ellipses:
        else {
            RS_Ellipse* el = (RS_Ellipse*)circle;
            //sol.alloc(2);
            //sol.set(0, circleCenter);
            //sol.set(1, circleCenter);


            double a = el->getMajorRadius();     // the length of the major axis / 2
            double b = el->getMinorRadius();     // the length of the minor axis / 2

            // rotate and move point:
            RS_Vector point2 = point;
            point2.move(-el->getCenter());
            point2.rotate(-el->getAngle());

            double xp = point2.x;             // coordinates of the given point
            double yp = point2.y;

            double xt1;                      // Tangent point 1
            double yt1;
            double xt2;                      // Tangent point 2
            double yt2;

            double a2 = a * a;
            double b2 = b * b;
            double d = a2 / b2 * yp / xp;
            double e = a2 / xp;
            double af = b2 * d * d + a2;
            double bf = -b2 * d * e * 2.0;
            double cf = b2 * e * e - a2 * b2;
            double t = sqrt(bf * bf - af * cf * 4.0);
            yt1 = (t - bf) / (af * 2.0);
            xt1 = e - d * yt1;
            yt2 = (-t - bf) / (af * 2.0);
            xt2 = e - d * yt2;

            RS_Vector s1 = RS_Vector(xt1, yt1);
            RS_Vector s2 = RS_Vector(xt2, yt2);

            s1.rotate(el->getAngle());
            s1.move(el->getCenter());

            s2.rotate(el->getAngle());
            s2.move(el->getCenter());

            sol.push_back(s1);
            sol.push_back(s2);


        }

        if (sol.getNumber() < 2 ) {
                return NULL;
        }
        if (!sol.get(0).valid || !sol.get(1).valid) {
            return NULL;
        }

        // create all possible tangents:
        RS_Line* poss[2];

        RS_LineData d;

        d = RS_LineData(sol.get(0), point);
        poss[0] = new RS_Line(NULL, d);
        d = RS_LineData(sol.get(1), point);
        poss[1] = new RS_Line(NULL, d);

        // find closest tangent:
        double minDist = RS_MAXDOUBLE;
        double dist;
        int idx = -1;
        for (int i=0; i<2; ++i) {
            dist = poss[i]->getDistanceToPoint(coord);
            if (dist<minDist) {
                minDist = dist;
                idx = i;
            }
        }

        // create the closest tangent:
        if (idx!=-1) {
            RS_LineData d = poss[idx]->getData();

            for (int i=0; i<2; ++i) {
                delete poss[i];
            }

            if (document!=NULL && handleUndo) {
                document->startUndoCycle();
            }

            ret = new RS_Line(container, d);
            ret->setLayerToActive();
            ret->setPenToActive();
            if (container!=NULL) {
                container->addEntity(ret);
            }
            if (document!=NULL && handleUndo) {
                document->addUndoable(ret);
                document->endUndoCycle();
            }
            if (graphicView!=NULL) {
                graphicView->drawEntity(ret);
            }
        } else {
            ret = NULL;
        }

        return ret;
    }