/**************************************************************************** ** $Id: rs_arc.cpp 2399 2005-06-06 18:12:30Z andrew $ ** ** 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. */ 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. */ 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) { calculateEndpoints(); calculateBorders(); 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; } } /** * Creates an arc from its startpoint, endpoint, start direction (angle) * and radius. * * @retval true Successfully created arc * @retval false Cannot creats arc (radius to small or endpoint to far away) */ bool RS_Arc::createFrom2PDirectionRadius(const RS_Vector& startPoint, const RS_Vector& endPoint, double direction1, double radius) { RS_Vector ortho; ortho.setPolar(radius, direction1 + M_PI/2.0); RS_Vector center1 = startPoint + ortho; RS_Vector center2 = startPoint - ortho; if (center1.distanceTo(endPoint) < center2.distanceTo(endPoint)) { data.center = center1; } else { data.center = center2; } data.radius = radius; data.angle1 = data.center.angleTo(startPoint); data.angle2 = data.center.angleTo(endPoint); data.reversed = false; double diff = RS_Math::correctAngle(getDirection1()-direction1); if (fabs(diff-M_PI)<1.0e-1) { data.reversed = true; } calculateEndpoints(); calculateBorders(); return true; } /** * Creates an arc from its startpoint, endpoint and bulge. */ bool RS_Arc::createFrom2PBulge(const RS_Vector& startPoint, const RS_Vector& endPoint, double bulge) { data.reversed = (bulge<0.0); double alpha = atan(bulge)*4.0; RS_Vector middle = (startPoint+endPoint)/2.0; double dist = startPoint.distanceTo(endPoint)/2.0; // alpha can't be 0.0 at this point data.radius = fabs(dist / sin(alpha/2.0)); double wu = fabs(RS_Math::pow(data.radius, 2.0) - RS_Math::pow(dist, 2.0)); double h = sqrt(wu); double angle = startPoint.angleTo(endPoint); if (bulge>0.0) { angle+=M_PI/2.0; } else { angle-=M_PI/2.0; } if (fabs(alpha)>M_PI) { h*=-1.0; } data.center.setPolar(h, angle); data.center+=middle; data.angle1 = data.center.angleTo(startPoint); data.angle2 = data.center.angleTo(endPoint); calculateEndpoints(); calculateBorders(); return true; } /** * Recalculates the endpoints using the angles and the radius. */ 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); } 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 ((a1M_PI) || (a1>a2-1.0e-12 && a2>M_PI) || (a1>a2-1.0e-12 && 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 ((a1M_PI_2) || (a1>a2-1.0e-12 && a2>M_PI_2) || (a1>a2-1.0e-12 && a1rtti()==RS2::EntityPolyline) { double bulge = getBulge(); createFrom2PBulge(pos, getEndpoint(), bulge); //} // normal arc: move angle1 /*else { data.angle1 = data.center.angleTo(pos); calculateEndpoints(); calculateBorders(); }*/ } void RS_Arc::moveEndpoint(const RS_Vector& pos) { // polyline arcs: move point not angle: //if (parent!=NULL && parent->rtti()==RS2::EntityPolyline) { double bulge = getBulge(); createFrom2PBulge(getStartpoint(), pos, bulge); //} // normal arc: move angle1 /*else { data.angle2 = data.center.angleTo(pos); calculateEndpoints(); calculateBorders(); }*/ } void RS_Arc::trimStartpoint(const RS_Vector& pos) { data.angle1 = data.center.angleTo(pos); calculateEndpoints(); calculateBorders(); } void RS_Arc::trimEndpoint(const RS_Vector& pos) { data.angle2 = data.center.angleTo(pos); calculateEndpoints(); calculateBorders(); } 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(); } void RS_Arc::move(RS_Vector offset) { data.center.move(offset); calculateEndpoints(); calculateBorders(); } 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"); } 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(); } 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(); } 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); } } void RS_Arc::stretch(RS_Vector firstCorner, RS_Vector secondCorner, RS_Vector offset) { if (getMin().isInWindow(firstCorner, secondCorner) && getMax().isInWindow(firstCorner, secondCorner)) { move(offset); } else { if (getStartpoint().isInWindow(firstCorner, secondCorner)) { moveStartpoint(getStartpoint() + offset); } if (getEndpoint().isInWindow(firstCorner, secondCorner)) { moveEndpoint(getEndpoint() + offset); } } } 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(), isReversed()); } else { double styleFactor = getStyleFactor(view); if (styleFactor<0.0) { painter->drawArc(view->toGui(getCenter()), getRadius() * view->getFactor().x, getAngle1(), getAngle2(), isReversed()); return; } // 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; inum; ++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]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. */ 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. */ double RS_Arc::getAngleLength() const { double ret = 0.0; if (isReversed()) { if (data.angle1