Logo Search packages:      
Sourcecode: qcad version File versions  Download package

rs_arc.cpp

/****************************************************************************
** $Id: rs_arc.cpp,v 1.24 2004/09/11 20:05:47 andrew Exp $
**
** Copyright (C) 2001-2003 RibbonSoft. All rights reserved.
**
** This file is part of the qcadlib Library project.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid qcadlib Professional Edition licenses may use 
** this file in accordance with the qcadlib Commercial License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.ribbonsoft.com for further details.
**
** Contact info@ribbonsoft.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "rs_arc.h"

#include "rs_constructionline.h"
#include "rs_linetypepattern.h"
#include "rs_information.h"
#include "rs_math.h"
#include "rs_graphicview.h"
#include "rs_painter.h"


/**
 * Default constructor.
 */
00040 RS_Arc::RS_Arc(RS_EntityContainer* parent,
               const RS_ArcData& d)
        : RS_AtomicEntity(parent), data(d) {
    calculateEndpoints();
    calculateBorders();
}



/**
 * Creates this arc from 3 given points which define the arc line.
 *
 * @param p1 1st point.
 * @param p2 2nd point.
 * @param p3 3rd point.
 */
00056 bool RS_Arc::createFrom3P(const RS_Vector& p1, const RS_Vector& p2,
                          const RS_Vector& p3) {
    if (p1.distanceTo(p2)>RS_TOLERANCE &&
            p2.distanceTo(p3)>RS_TOLERANCE &&
            p3.distanceTo(p1)>RS_TOLERANCE) {

        // middle points between 3 points:
        RS_Vector mp1, mp2;
        RS_Vector dir1, dir2;
        double a1, a2;

        // intersection of two middle lines
        mp1 = (p1 + p2)/2.0;
        a1 = p1.angleTo(p2) + M_PI/2.0;
        dir1.setPolar(100.0, a1);
        mp2 = (p2 + p3)/2.0;
        a2 = p2.angleTo(p3) + M_PI/2.0;
        dir2.setPolar(100.0, a2);

        RS_ConstructionLineData d1(mp1, mp1 + dir1);
        RS_ConstructionLineData d2(mp2, mp2 + dir2);
        RS_ConstructionLine midLine1(NULL, d1);
        RS_ConstructionLine midLine2(NULL, d2);

        RS_VectorSolutions sol =
            RS_Information::getIntersection(&midLine1, &midLine2);

        data.center = sol.get(0);
        data.radius = data.center.distanceTo(p3);
        data.angle1 = data.center.angleTo(p1);
        data.angle2 = data.center.angleTo(p3);
        data.reversed = RS_Math::isAngleBetween(data.center.angleTo(p2),
                                                data.angle1, data.angle2, true);

        if (sol.get(0).valid && data.radius<1.0e14 &&
                data.radius>RS_TOLERANCE) {
            return true;
        } else {
            RS_DEBUG->print("RS_Arc::createFrom3P(): "
                            "Cannot create an arc with inf radius.");
            return false;
        }
    } else {
        RS_DEBUG->print("RS_Arc::createFrom3P(): "
                        "Cannot create an arc with radius 0.0.");
        return false;
    }
}



/**
 * Recalculates the endpoints using the angles and the radius.
 */
00110 void RS_Arc::calculateEndpoints() {
    startpoint.set(data.center.x + cos(data.angle1) * data.radius,
                   data.center.y + sin(data.angle1) * data.radius);
    endpoint.set(data.center.x + cos(data.angle2) * data.radius,
                 data.center.y + sin(data.angle2) * data.radius);
}


00118 void RS_Arc::calculateBorders() {
    double minX = std::min(startpoint.x, endpoint.x);
    double minY = std::min(startpoint.y, endpoint.y);
    double maxX = std::max(startpoint.x, endpoint.x);
    double maxY = std::max(startpoint.y, endpoint.y);

    double a1 = !isReversed() ? data.angle1 : data.angle2;
    double a2 = !isReversed() ? data.angle2 : data.angle1;

      // check for left limit:
    if ((a1<M_PI && a2>M_PI) ||
            (a1>a2-1.0e-12 && a2>M_PI) ||
            (a1>a2-1.0e-12 && a1<M_PI) ) {

        minX = std::min(data.center.x - data.radius, minX);
    }

      // check for right limit:
    if (a1 > a2-1.0e-12) {
        maxX = std::max(data.center.x + data.radius, maxX);
    }

      // check for bottom limit:
    if ((a1<(M_PI_2*3) && a2>(M_PI_2*3)) ||
            (a1>a2-1.0e-12    && a2>(M_PI_2*3)) ||
            (a1>a2-1.0e-12    && a1<(M_PI_2*3)) ) {

        minY = std::min(data.center.y - data.radius, minY);
    }

      // check for top limit:
    if ((a1<M_PI_2 && a2>M_PI_2) ||
            (a1>a2-1.0e-12   && a2>M_PI_2) ||
            (a1>a2-1.0e-12   && a1<M_PI_2) ) {

        maxY = std::max(data.center.y + data.radius, maxY);
    }

    minV.set(minX, minY);
    maxV.set(maxX, maxY);
}



00162 RS_VectorSolutions RS_Arc::getRefPoints() {
      RS_VectorSolutions ret(startpoint, endpoint, data.center);
      return ret;
}


00168 RS_Vector RS_Arc::getNearestEndpoint(const RS_Vector& coord, double* dist) {
    double dist1, dist2;
    RS_Vector* nearerPoint;

    dist1 = startpoint.distanceTo(coord);
    dist2 = endpoint.distanceTo(coord);

    if (dist2<dist1) {
        if (dist!=NULL) {
            *dist = dist2;
        }
        nearerPoint = &endpoint;
    } else {
        if (dist!=NULL) {
            *dist = dist1;
        }
        nearerPoint = &startpoint;
    }

    return *nearerPoint;
}



00192 RS_Vector RS_Arc::getNearestPointOnEntity(const RS_Vector& coord,
        bool onEntity, double* dist, RS_Entity** entity) {

    RS_Vector vec(false);
    if (entity!=NULL) {
        *entity = this;
    }

    double angle = (coord-data.center).angle();
    if (onEntity==false || RS_Math::isAngleBetween(angle, 
                              data.angle1, data.angle2, isReversed())) {
        vec.setPolar(data.radius, angle);
        vec+=data.center;
    }
    if (dist!=NULL) {
        *dist = fabs((vec-data.center).magnitude()-data.radius);
    }

    return vec;
}



00215 RS_Vector RS_Arc::getNearestCenter(const RS_Vector& coord,
                                   double* dist) {
    if (dist!=NULL) {
        *dist = coord.distanceTo(data.center);
    }
    return data.center;
}



00225 RS_Vector RS_Arc::getNearestMiddle(const RS_Vector& coord,
                                   double* dist) {

    RS_Vector ret = getMiddlepoint();

    if (dist!=NULL) {
        *dist = coord.distanceTo(ret);
    }
    return ret;
}



00238 RS_Vector RS_Arc::getNearestDist(double distance,
                                 const RS_Vector& coord,
                                 double* dist) {

      if (data.radius<1.0e-6) {
            if (dist!=NULL) {
                  *dist = RS_MAXDOUBLE;
            }
            return RS_Vector(false);
      }
      
    double a1, a2;
    RS_Vector p1, p2;
    double aDist = distance / data.radius;

    if (isReversed()) {
        a1 = data.angle1 - aDist;
        a2 = data.angle2 + aDist;
    } else {
        a1 = data.angle1 + aDist;
        a2 = data.angle2 - aDist;
    }

    p1.setPolar(data.radius, a1);
      p1 += data.center;
    p2.setPolar(data.radius, a2);
      p2 += data.center;

    double dist1, dist2;
    RS_Vector* nearerPoint;

    dist1 = p1.distanceTo(coord);
    dist2 = p2.distanceTo(coord);

    if (dist2<dist1) {
        if (dist!=NULL) {
            *dist = dist2;
        }
        nearerPoint = &p2;
    } else {
        if (dist!=NULL) {
            *dist = dist1;
        }
        nearerPoint = &p1;
    }

    return *nearerPoint;
}




00290 RS_Vector RS_Arc::getNearestDist(double distance,
                                  bool startp) {

      if (data.radius<1.0e-6) {
            return RS_Vector(false);
      }
      
    double a;
    RS_Vector p;
    double aDist = distance / data.radius;

    if (isReversed()) {
            if (startp) {
            a = data.angle1 - aDist;
            } else {
            a = data.angle2 + aDist;
            }
    } else {
            if (startp) {
              a = data.angle1 + aDist;
            } else {
                a = data.angle2 - aDist;
            }
    }

    p.setPolar(data.radius, a);
      p += data.center;

    return p;
}



00323 double RS_Arc::getDistanceToPoint(const RS_Vector& coord,
                                  RS_Entity** entity,
                                  RS2::ResolveLevel,
                                                  double) {
    if (entity!=NULL) {
        *entity = this;
    }

      // check endpoints first:
      double dist = coord.distanceTo(getStartpoint());
      if (dist<1.0e-4) {
            return dist;
      }
      dist = coord.distanceTo(getEndpoint());
      if (dist<1.0e-4) {
            return dist;
      }

    if (RS_Math::isAngleBetween(data.center.angleTo(coord),
                                data.angle1, data.angle2,
                                isReversed())) {

        return fabs((coord-data.center).magnitude() - data.radius);
    } else {
        return RS_MAXDOUBLE;
    }
}



00353 void RS_Arc::moveStartpoint(const RS_Vector& pos) {
      data.angle1 = data.center.angleTo(pos);
      calculateEndpoints();
      calculateBorders();
}



00361 void RS_Arc::moveEndpoint(const RS_Vector& pos) {
      data.angle2 = data.center.angleTo(pos);
      calculateEndpoints();
      calculateBorders();
}



00369 RS2::Ending RS_Arc::getTrimPoint(const RS_Vector& coord, 
            const RS_Vector& trimPoint) {
      
      double angEl = data.center.angleTo(trimPoint);
      double angM = data.center.angleTo(coord);

      if (RS_Math::getAngleDifference(angM, angEl)>M_PI) {
            if (data.reversed) {
                  return RS2::EndingEnd;
            }
            else {
                  return RS2::EndingStart;
            }
      }
      else {
            if (data.reversed) {
                  return RS2::EndingStart;
            }
            else {
                  return RS2::EndingEnd;
            }
      }
}

      
void RS_Arc::reverse() {
      double a = data.angle1;
      data.angle1 = data.angle2;
      data.angle2 = a;
      data.reversed = !data.reversed;
    calculateEndpoints();
      calculateBorders();
}


00404 void RS_Arc::move(RS_Vector offset) {
    data.center.move(offset);
    calculateEndpoints();
    calculateBorders();
}



00412 void RS_Arc::rotate(RS_Vector center, double angle) {
      RS_DEBUG->print("RS_Arc::rotate");
    data.center.rotate(center, angle);
    data.angle1 = RS_Math::correctAngle(data.angle1+angle);
    data.angle2 = RS_Math::correctAngle(data.angle2+angle);
    calculateEndpoints();
    calculateBorders();
      RS_DEBUG->print("RS_Arc::rotate: OK");
}



00424 void RS_Arc::scale(RS_Vector center, RS_Vector factor) {
      // negative scaling: mirroring
      if (factor.x<0.0) {
            mirror(data.center, data.center + RS_Vector(0.0, 1.0));
            //factor.x*=-1;
      }
      if (factor.y<0.0) {
            mirror(data.center, data.center + RS_Vector(1.0, 0.0));
            //factor.y*=-1;
      }

    data.center.scale(center, factor);
    data.radius *= factor.x;
      if (data.radius<0.0) {
            data.radius*=-1.0;
      }
    calculateEndpoints();
    calculateBorders();
}



00446 void RS_Arc::mirror(RS_Vector axisPoint1, RS_Vector axisPoint2) {
      data.center.mirror(axisPoint1, axisPoint2);
      data.reversed = (!data.reversed);
      /*
      startpoint.mirror(axisPoint1, axisPoint2);
      endpoint.mirror(axisPoint1, axisPoint2);
      
      data.angle1 = data.center.angleTo(startpoint);
      data.angle2 = data.center.angleTo(endpoint);
      */
      
      RS_Vector vec;
      vec.setPolar(1.0, data.angle1);
      vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
      data.angle1 = vec.angle();
      
      vec.setPolar(1.0, data.angle2);
      vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1);
      data.angle2 = vec.angle();

      calculateEndpoints();
    calculateBorders();
}



00472 void RS_Arc::moveRef(const RS_Vector& ref, const RS_Vector& offset) {
    if (ref.distanceTo(startpoint)<1.0e-4) {
        moveStartpoint(startpoint+offset);
    }
    if (ref.distanceTo(endpoint)<1.0e-4) {
        moveEndpoint(endpoint+offset);
    }
}



00483 void RS_Arc::draw(RS_Painter* painter, RS_GraphicView* view, 
      double /*patternOffset*/) {

    if (painter==NULL || view==NULL) {
        return;
    }

      //double styleFactor = getStyleFactor();

    // simple style-less lines
    if (getPen().getLineType()==RS2::SolidLine ||
            isSelected() ||
            view->getDrawingMode()==RS2::ModePreview) {

        painter->drawArc(view->toGui(getCenter()),
                         getRadius() * view->getFactor().x,
                         getAngle1(), getAngle2(),
                         //toGuiX(a->getStartpoint().x),
                         //toGuiY(a->getStartpoint().y),
                         //toGuiX(a->getEndpoint().x),
                         //toGuiY(a->getEndpoint().y),
                         isReversed());
    } else {
        double styleFactor = getStyleFactor(view);
            
            /*if (view->isDraftMode()) {
                  styleFactor = 1.0/view->getFactor().x;
            }
            else {
                  styleFactor = getStyleFactor();
            }

        if (view->isPrinting() || view->isPrintPreview() || view->isDraftMode()==false) {
            RS_Graphic* graphic = getGraphic();
            if (graphic!=NULL && graphic->getPaperScale()>1.0e-6) {
                styleFactor /= graphic->getPaperScale();
            }
        }*/
            
        // Pattern:
        RS_LineTypePattern* pat;
        if (isSelected()) {
            pat = &patternSelected;
        } else {
            pat = view->getPattern(getPen().getLineType());
        }

        if (pat==NULL) {
            return;
        }

            if (getRadius()<1.0e-6) {
                  return;
            }

        // Pen to draw pattern is always solid:
        RS_Pen pen = painter->getPen();
        pen.setLineType(RS2::SolidLine);
        painter->setPen(pen);

            double a1;
            double a2;
            if (data.reversed) {
                  a2 = getAngle1();
                  a1 = getAngle2();
            }
            else {
                  a1 = getAngle1();
                  a2 = getAngle2();
            }

        double* da;     // array of distances in x.
        int i;          // index counter

        double length = getAngleLength();

        // create scaled pattern:
        da = new double[pat->num];

        for (i=0; i<pat->num; ++i) {
            da[i] = fabs(pat->pattern[i] * styleFactor) / getRadius();
        }

        double tot=0.0;
        i=0;
        bool done = false;
        double curA = a1;
        //double cx = getCenter().x * factor.x + offsetX;
        //double cy = - a->getCenter().y * factor.y + getHeight() - offsetY;
        RS_Vector cp = view->toGui(getCenter());
        double r = getRadius() * view->getFactor().x;

        do {
            if (pat->pattern[i] > 0.0) {
                if (tot+da[i]<length) {
                    painter->drawArc(cp, r,
                                     curA,
                                     curA + da[i],
                                     false);
                } else {
                    painter->drawArc(cp, r,
                                     curA,
                                     a2,
                                     false);
                }
            }
            curA+=da[i];
            tot+=da[i];
            done=tot>length;

            i++;
            if (i>=pat->num) {
                i=0;
            }
        } while(!done);

        delete[] da;
    }
}



/**
 * @return Middle point of the entity. 
 */
00608 RS_Vector RS_Arc::getMiddlepoint() const {
    double a;
    RS_Vector ret;

    if (isReversed()) {
        a = data.angle1 - getAngleLength()/2.0;
    } else {
        a = data.angle1 + getAngleLength()/2.0;
    }
    ret.setPolar(data.radius, a);
    ret+=data.center;

    return ret;
}



/**
 * @return Angle length in rad.
 */
00628 double RS_Arc::getAngleLength() const {
    if (isReversed()) {
        if (data.angle1<data.angle2) {
            return data.angle1+2*M_PI-data.angle2;
        } else {
            return data.angle1-data.angle2;
        }
    } else {
        if (data.angle2<data.angle1) {
            return data.angle2+2*M_PI-data.angle1;
        } else {
            return data.angle2-data.angle1;
        }
    }
}



/**
 * @return Length of the arc.
 */
00649 double RS_Arc::getLength() {
      return getAngleLength()*data.radius;
}



/**
 * Gets the arc's bulge (tangens of angle length divided by 4).
 */
00658 double RS_Arc::getBulge() const {
    double bulge = tan(fabs(getAngleLength())/4.0);
    if (isReversed()) {
        bulge*=-1;
    }
    return bulge;
}



/**
 * Dumps the point's data to stdout.
 */
00671 std::ostream& operator << (std::ostream& os, const RS_Arc& a) {
    os << " Arc: " << a.data << "\n";
    return os;
}


// EOF

Generated by  Doxygen 1.6.0   Back to index