RS_Vector RS_Ellipse::getNearestMiddle(const RS_Vector& coord, double* dist, int middlePoints ) { if ( ! ( std::isnormal(getAngle1()) || std::isnormal(getAngle2()))) { //no middle point for whole ellipse, angle1=angle2=0 if (dist!=NULL) { *dist = RS_MAXDOUBLE; } return RS_Vector(false); } if ( getMajorRadius() < RS_TOLERANCE || getMinorRadius() < RS_TOLERANCE ) { //zero radius, return the center RS_Vector vp(getCenter()); if (dist!=NULL) { *dist = vp.distanceTo(coord); } return vp; } double angle=getAngle(); double amin=getCenter().angleTo(getStartpoint()); double amax=getCenter().angleTo(getEndpoint()); if(isReversed()) { std::swap(amin,amax); } int i=middlePoints + 1; double da=fmod(amax-amin+2.*M_PI, 2.*M_PI); if ( da < RS_TOLERANCE ) { da = 2.*M_PI; //whole ellipse } da /= i; int j=1; double curDist=RS_MAXDOUBLE; //double a=RS_Math::correctAngle(amin+da-angle); double a=amin-angle+da; RS_Vector curPoint; RS_Vector scaleFactor(RS_Vector(1./getMajorRadius(),1./getMinorRadius())); do { RS_Vector vp(a); RS_Vector vp2=vp; vp2.scale(scaleFactor); vp.scale(1./vp2.magnitude()); vp.rotate(angle); vp.move(getCenter()); double d=coord.distanceTo(vp); if(d<curDist) { curDist=d; curPoint=vp; } j++; a += da; } while (j<i); if (dist!=NULL) { *dist = curDist; } RS_DEBUG->print("RS_Ellipse::getNearestMiddle: angle1=%g, angle2=%g, middle=%g\n",amin,amax,a); return curPoint; }
/** * \return Point on ellipse at given ellipse angle. */ RVector REllipse::getPointAt(double angle) const { RVector v(cos(angle)*getMajorRadius(), sin(angle)*getMinorRadius()); v.rotate(getAngle()); v.move(center); return v; }
RBox REllipse::getBoundingBox() const { double radius1 = getMajorRadius(); double radius2 = getMinorRadius(); double angle = getAngle(); double a1 = ((!isReversed()) ? startParam : endParam); double a2 = ((!isReversed()) ? endParam : startParam); RVector startPoint = getStartPoint(); RVector endPoint = getEndPoint(); double minX = qMin(startPoint.x, endPoint.x); double minY = qMin(startPoint.y, endPoint.y); double maxX = qMax(startPoint.x, endPoint.x); double maxY = qMax(startPoint.y, endPoint.y); // kind of a brute force. TODO: exact calculation RVector vp; double a = a1; do { vp.set(center.x + radius1 * cos(a), center.y + radius2 * sin(a)); vp.rotate(angle, center); minX = qMin(minX, vp.x); minY = qMin(minY, vp.y); maxX = qMax(maxX, vp.x); maxY = qMax(maxY, vp.y); a += 0.03; } while (RMath::isAngleBetween(a, a1, a2, false) && a<4*M_PI); return RBox(RVector(minX,minY), RVector(maxX,maxY)); }
/* Dongxu Li's Version, 19 Aug 2011 * scale an ellipse * Find the eigen vactors and eigen values by optimization * original ellipse equation, * x= a cos t * y= b sin t * rotated by angle, * * x = a cos t cos (angle) - b sin t sin(angle) * y = a cos t sin (angle) + b sin t cos(angle) * scaled by ( kx, ky), * x *= kx * y *= ky * find the maximum and minimum of x^2 + y^2, */ void RS_Ellipse::scale(RS_Vector center, RS_Vector factor) { data.center.scale(center, factor); RS_Vector vpStart=getStartpoint().scale(getCenter(),factor); RS_Vector vpEnd=getEndpoint().scale(getCenter(),factor);; double ct=cos(getAngle()); double ct2 = ct*ct; // cos^2 angle double st=sin(getAngle()); double st2=1.0 - ct2; // sin^2 angle double kx2= factor.x * factor.x; double ky2= factor.y * factor.y; double a=getMajorRadius(); double b=getMinorRadius(); double cA=0.5*a*a*(kx2*ct2+ky2*st2); double cB=0.5*b*b*(kx2*st2+ky2*ct2); double cC=a*b*ct*st*(ky2-kx2); RS_Vector vp(cA-cB,cC); setMajorP(RS_Vector(a,b).scale(RS_Vector(vp.angle())).rotate(RS_Vector(ct,st)).scale(factor)); a=cA+cB; b=vp.magnitude(); setRatio( sqrt((a - b)/(a + b) )); if( std::isnormal(getAngle1()) || std::isnormal(getAngle2() ) ) { //only reset start/end points for ellipse arcs, i.e., angle1 angle2 are not both zero setAngle1(getEllipseAngle(vpStart)); setAngle2(getEllipseAngle(vpEnd)); } correctAngles();//avoid extra 2.*M_PI in angles //calculateEndpoints(); calculateBorders(); }
/** * \return Length of the ellipse segment from angle a1 to angle a2. */ double REllipse::getSimpsonLength(double a1, double a2) const { double df = (a2-a1) / 20; double R = getMajorRadius(); double r = getMinorRadius(); double sum = 0.0; double q = 1.0; for (int i=0; i<=20; ++i) { double y = sqrt(::pow(R * sin(a1 + i * df), 2) + ::pow(r * cos(a1 + i * df), 2)); if (i==0 || i==20) { q = 1.0; } else { if (i%2==0) { q = 2.0; } else { q = 4.0; } } sum += q * y; } return (df / 3.0) * sum; }
bool REllipseData::moveReferencePoint(const RVector& referencePoint, const RVector& targetPoint) { RVector startPoint = getStartPoint(); RVector endPoint = getEndPoint(); if (!isFullEllipse()) { if (referencePoint.equalsFuzzy(startPoint)) { moveStartPoint(targetPoint, true); return true; } if (referencePoint.equalsFuzzy(endPoint)) { moveEndPoint(targetPoint, true); return true; } } if (referencePoint.equalsFuzzy(center+majorPoint)) { double minorRadius = getMinorRadius(); majorPoint = targetPoint-center; setRatio(minorRadius / getMajorRadius()); return true; } if (referencePoint.equalsFuzzy(center-majorPoint)) { double minorRadius = getMinorRadius(); majorPoint = -(targetPoint-center); setRatio(minorRadius / getMajorRadius()); return true; } if (referencePoint.equalsFuzzy(center+getMinorPoint())) { setMinorPoint(targetPoint-center); return true; } if (referencePoint.equalsFuzzy(center-getMinorPoint())) { setMinorPoint(-(targetPoint-center)); return true; } if (referencePoint.equalsFuzzy(center)) { center = targetPoint; return true; } return false; }
RVector REllipse::getEndPoint() const { RVector p( center.x + cos(endParam) * getMajorRadius(), center.y + sin(endParam) * getMinorRadius() ); p.rotate(getAngle(), center); return p; }
bool REllipse::contains(const RVector& p) const { RVector pt = p; pt.move(-center); pt.rotate(-getAngle()); double rx = getMajorRadius(); double ry = getMinorRadius(); return (pt.x*pt.x) / (rx*rx) + (pt.y*pt.y) / (ry*ry) <= 1.0; }
/** * @param tolerance Tolerance. * * @retval true if the given point is on this entity. * @retval false otherwise */ bool RS_Ellipse::isPointOnEntity(const RS_Vector& coord, double tolerance) { if ( getCenter().distanceTo(coord) < tolerance ) { if (getMajorRadius() < tolerance || getMinorRadius() < tolerance ) { return true; } else { return false; } } double dist = getDistanceToPoint(coord, NULL, RS2::ResolveNone); return (dist<=tolerance); }
void REllipse::print(QDebug dbg) const { dbg.nospace() << "REllipse("; RShape::print(dbg); dbg.nospace() << ", startPoint: " << getStartPoint() << ", endPoint: " << getEndPoint() << ", center: " << getCenter() << ", majorPoint: " << getMajorPoint() << ", majorRadius: " << getMajorRadius() << ", minorRadius: " << getMinorRadius() << ", ratio: " << getRatio() << ", startAngle: " << RMath::rad2deg(getStartParam()) << ", endAngle: " << RMath::rad2deg(getEndParam()) << ", full: " << isFullEllipse() << ", clockwise: " << isReversed() << ")"; }
RS_Vector RS_Ellipse::getNearestOrthTan(const RS_Vector& coord, const RS_Line& normal, bool onEntity ) { if ( !coord.valid ) { return RS_Vector(false); } RS_Vector direction=normal.getEndpoint() - normal.getStartpoint(); if (RS_Vector::dotP(direction,direction)< RS_TOLERANCE*RS_TOLERANCE) { //undefined direction return RS_Vector(false); } //scale to ellipse angle direction.rotate(-getAngle()); double angle=direction.scale(RS_Vector(1.,getRatio())).angle(); direction.set(getMajorRadius()*cos(angle),getMinorRadius()*sin(angle));//relative to center QList<RS_Vector> sol; for(int i=0; i<2; i++) { if(!onEntity || RS_Math::isAngleBetween(angle,getAngle1(),getAngle2(),isReversed())) { if(i) { sol.append(- direction); } else { sol.append(direction); } } angle=RS_Math::correctAngle(angle+M_PI); } RS_Vector vp; switch(sol.count()) { case 0: return RS_Vector(false); case 2: if( RS_Vector::dotP(sol[1]-getCenter(),coord-getCenter())>0.) { vp=sol[1]; break; } default: vp=sol[0]; } return getCenter() + vp.rotate(getAngle()); }
/** * Calculates the boundary box of this ellipse. * * @todo Fix that - the algorithm used is really bad / slow. */ void RS_Ellipse::calculateBorders() { RS_DEBUG->print("RS_Ellipse::calculateBorders"); double radius1 = getMajorRadius(); double radius2 = getMinorRadius(); double angle = getAngle(); double a1 = ((!isReversed()) ? data.angle1 : data.angle2); double a2 = ((!isReversed()) ? data.angle2 : data.angle1); RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); 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); // kind of a brute force. TODO: calculation RS_Vector vp; double a = a1; do { vp.set(data.center.x + radius1 * cos(a), data.center.y + radius2 * sin(a)); vp.rotate(data.center, angle); minX = std::min(minX, vp.x); minY = std::min(minY, vp.y); maxX = std::max(maxX, vp.x); maxY = std::max(maxY, vp.y); a += 0.03; } while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) && a<4*M_PI); minV.set(minX, minY); maxV.set(maxX, maxY); RS_DEBUG->print("RS_Ellipse::calculateBorders: OK"); }
RS_Vector RS_Ellipse::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity) { RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity"); RS_Vector ret(false); if( ! coord.valid ) { if ( dist != NULL ) *dist=RS_MAXDOUBLE; return ret; } if (entity!=NULL) { *entity = this; } ret=coord; ret.move(-getCenter()); ret.rotate(-getAngle()); double x=ret.x,y=ret.y; double a=getMajorRadius(); double b=getMinorRadius(); //std::cout<<"(a= "<<a<<" b= "<<b<<" x= "<<x<<" y= "<<y<<" )\n"; //std::cout<<"finding minimum for ("<<x<<"-"<<a<<"*cos(t))^2+("<<y<<"-"<<b<<"*sin(t))^2\n"; double twoa2b2=2*(a*a-b*b); double twoax=2*a*x; double twoby=2*b*y; double a0=twoa2b2*twoa2b2; double ce[4]; double roots[4]; unsigned int counts=0; //need to handle a=b if(a0 > RS_TOLERANCE*RS_TOLERANCE ) { // a != b , ellipse ce[0]=-2.*twoax/twoa2b2; ce[1]= (twoax*twoax+twoby*twoby)/a0-1.; ce[2]= - ce[0]; ce[3]= -twoax*twoax/a0; //std::cout<<"1::find cosine, variable c, solve(c^4 +("<<ce[0]<<")*c^3+("<<ce[1]<<")*c^2+("<<ce[2]<<")*c+("<<ce[3]<<")=0,c)\n"; counts=RS_Math::quarticSolver(ce,roots); } else {//a=b, quadratic equation for circle counts=2; a0=twoby/twoax; roots[0]=sqrt(1./(1.+a0*a0)); roots[1]=-roots[0]; } if(!counts) { //this should not happen std::cout<<"(a= "<<a<<" b= "<<b<<" x= "<<x<<" y= "<<y<<" )\n"; std::cout<<"finding minimum for ("<<x<<"-"<<a<<"*cos(t))^2+("<<y<<"-"<<b<<"*sin(t))^2\n"; std::cout<<"2::find cosine, variable c, solve(c^4 +("<<ce[0]<<")*c^3+("<<ce[1]<<")*c^2+("<<ce[2]<<")*c+("<<ce[3]<<")=0,c)\n"; std::cout<<ce[0]<<' '<<ce[1]<<' '<<ce[2]<<' '<<ce[3]<<std::endl; std::cerr<<"RS_Math::RS_Ellipse::getNearestPointOnEntity() finds no root from quartic, this should not happen\n"; return RS_Vector(coord); // better not to return invalid: return RS_Vector(false); } RS_Vector vp2(false); double d(RS_MAXDOUBLE),d2,s,dDistance(RS_MAXDOUBLE); //double ea; for(unsigned int i=0; i<counts; i++) { //I don't understand the reason yet, but I can do without checking whether sine/cosine are valid //if ( fabs(roots[i])>1.) continue; s=twoby*roots[i]/(twoax-twoa2b2*roots[i]); //sine //if (fabs(s) > 1. ) continue; d2=twoa2b2+(twoax-2.*roots[i]*twoa2b2)*roots[i]+twoby*s; if (d2<0) continue; // fartherest RS_Vector vp3; vp3.set(a*roots[i],b*s); d=vp3.distanceTo(ret); // std::cout<<i<<" Checking: cos= "<<roots[i]<<" sin= "<<s<<" angle= "<<atan2(roots[i],s)<<" ds2= "<<d<<" d="<<d2<<std::endl; if( vp2.valid && d>dDistance) continue; vp2=vp3; dDistance=d; // ea=atan2(roots[i],s); } if( ! vp2.valid ) { //this should not happen std::cout<<ce[0]<<' '<<ce[1]<<' '<<ce[2]<<' '<<ce[3]<<std::endl; std::cout<<"(x,y)=( "<<x<<" , "<<y<<" ) a= "<<a<<" b= "<<b<<" sine= "<<s<<" d2= "<<d2<<" dist= "<<d<<std::endl; std::cout<<"RS_Ellipse::getNearestPointOnEntity() finds no minimum, this should not happen\n"; } if (dist!=NULL) { *dist = dDistance; } vp2.rotate(getAngle()); vp2.move(getCenter()); ret=vp2; if (onEntity) { if (!RS_Math::isAngleBetween(getEllipseAngle(ret), getAngle1(), getAngle2(), isReversed())) { // not on entity, use the nearest endpoint //std::cout<<"not on ellipse, ( "<<getAngle1()<<" "<<getEllipseAngle(ret)<<" "<<getAngle2()<<" ) reversed= "<<isReversed()<<"\n"; ret=getNearestEndpoint(coord,dist); } } if(! ret.valid) { std::cout<<"RS_Ellipse::getNearestOnEntity() returns invalid by mistake. This should not happen!"<<std::endl; } return ret; }
/** * @todo Implement this. */ RS_Vector RS_Ellipse::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity) { RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity"); RS_Vector ret(false); if (entity!=NULL) { *entity = this; } double step; double a1; double a2; double d; double minDist = RS_MAXDOUBLE; if (!onEntity) { a1 = 0.0; a2 = 2*M_PI; } else { if (!data.reversed) { a1 = data.angle1; a2 = data.angle2; } else { a1 = data.angle2; a2 = data.angle1; } } // find closest point (approximate) RS_Vector vp; double bestMatch = 0.0; step = 0.01; double a = a1+step; int eternal = 0; do { // point on elllipse (wanders from a1 to a2) vp.set(data.center.x+cos(a)*getMajorRadius(), data.center.y+sin(a)*getMinorRadius()); vp.rotate(data.center, getAngle()); d = vp.distanceTo(coord); if (d<minDist) { minDist = d; bestMatch = a; ret = vp; } a += step; eternal++; } while (eternal<10000 && RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) && (onEntity || a<2*M_PI)); // correct: /* a = bestMatch; step = 0.03; do { vp.set(data.center.x+cos(a)*getMajorRadius(), data.center.y+sin(a)*getMinorRadius()); vp.rotate(data.center, getAngle()); d = vp.distanceTo(coord); if (d<minDist) { minDist = d; bestMatch = a; ret = vp; } else { // change direction and decrease step amount step*=-0.3; } a += step; } while (fabs(step)>1.0e-6); */ // check endpoints: if (onEntity) { d = getStartpoint().distanceTo(coord); if (d<minDist) { ret = getStartpoint(); } d = getEndpoint().distanceTo(coord); if (d<minDist) { ret = getEndpoint(); } } if (dist!=NULL) { if (ret.valid) { *dist = ret.distanceTo(coord); } else { *dist = RS_MAXDOUBLE; } } RS_DEBUG->print("RS_Ellipse::getNearestPointOnEntity: OK"); return ret; }
void RS_Ellipse::draw(RS_Painter* painter, RS_GraphicView* view, double patternOffset) { if (painter==NULL || view==NULL) { return; } if (getPen().getLineType()==RS2::SolidLine || isSelected() || view->getDrawingMode()==RS2::ModePreview) { painter->drawEllipse(view->toGui(getCenter()), getMajorRadius() * view->getFactor().x, getMinorRadius() * view->getFactor().x, getAngle(), getAngle1(), getAngle2(), isReversed()); } else { double styleFactor = getStyleFactor(view); if (styleFactor<0.0) { painter->drawEllipse(view->toGui(getCenter()), getMajorRadius() * view->getFactor().x, getMinorRadius() * view->getFactor().x, getAngle(), getAngle1(), getAngle2(), isReversed()); return; } // Pattern: RS_LineTypePattern* pat; if (isSelected()) { pat = &patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (pat==NULL) { return; } // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); double* da; // array of distances in x. int i; // index counter double length = getAngleLength(); // create pattern: da = new double[pat->num]; double tot=0.0; i=0; bool done = false; double curA = getAngle1(); double curR; RS_Vector cp = view->toGui(getCenter()); double r1 = getMajorRadius() * view->getFactor().x; double r2 = getMinorRadius() * view->getFactor().x; do { curR = sqrt(RS_Math::pow(getMinorRadius()*cos(curA), 2.0) + RS_Math::pow(getMajorRadius()*sin(curA), 2.0)); if (curR>1.0e-6) { da[i] = fabs(pat->pattern[i] * styleFactor) / curR; if (pat->pattern[i] * styleFactor > 0.0) { if (tot+fabs(da[i])<length) { painter->drawEllipse(cp, r1, r2, getAngle(), curA, curA + da[i], false); } else { painter->drawEllipse(cp, r1, r2, getAngle(), curA, getAngle2(), false); } } } curA+=da[i]; tot+=fabs(da[i]); done=tot>length; i++; if (i>=pat->num) { i=0; } } while(!done); delete[] da; } }
/** * Overrides drawing of subentities. This is only ever called for solid fills. */ void RS_Hatch::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) { if (!data.solid) { for (RS_Entity* se=firstEntity(); se!=NULL; se = nextEntity()) { view->drawEntity(painter,se); } return; } QPainterPath path; QList<QPolygon> paClosed; QPolygon pa; // QPolygon jp; // jump points // loops: if (needOptimization==true) { for (RS_Entity* l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone)) { if (l->rtti()==RS2::EntityContainer) { RS_EntityContainer* loop = (RS_EntityContainer*)l; loop->optimizeContours(); } } needOptimization = false; } // loops: for (RS_Entity* l=firstEntity(RS2::ResolveNone); l!=NULL; l=nextEntity(RS2::ResolveNone)) { l->setLayer(getLayer()); if (l->rtti()==RS2::EntityContainer) { RS_EntityContainer* loop = (RS_EntityContainer*)l; // edges: for (RS_Entity* e=loop->firstEntity(RS2::ResolveNone); e!=NULL; e=loop->nextEntity(RS2::ResolveNone)) { e->setLayer(getLayer()); switch (e->rtti()) { case RS2::EntityLine: { QPoint pt1(RS_Math::round(view->toGuiX(e->getStartpoint().x)), RS_Math::round(view->toGuiY(e->getStartpoint().y))); QPoint pt2(RS_Math::round(view->toGuiX(e->getEndpoint().x)), RS_Math::round(view->toGuiY(e->getEndpoint().y))); // if (! (pa.size()>0 && (pa.last() - pt1).manhattanLength()<=2)) { // jp<<pt1; // } pa<<pt1<<pt2; } break; case RS2::EntityArc: { // QPoint pt1(RS_Math::round(view->toGuiX(e->getStartpoint().x)), // RS_Math::round(view->toGuiY(e->getStartpoint().y))); // if (! (pa.size()>0 && (pa.last() - pt1).manhattanLength()<=2)) { // jp<<pt1; // } QPolygon pa2; RS_Arc* arc=static_cast<RS_Arc*>(e); painter->createArc(pa2, view->toGui(arc->getCenter()), view->toGuiDX(arc->getRadius()), arc->getAngle1(), arc->getAngle2(), arc->isReversed()); pa<<pa2; } break; case RS2::EntityCircle: { RS_Circle* circle = static_cast<RS_Circle*>(e); // QPoint pt1(RS_Math::round(view->toGuiX(circle->getCenter().x+circle->getRadius())), // RS_Math::round(view->toGuiY(circle->getCenter().y))); // if (! (pa.size()>0 && (pa.last() - pt1).manhattanLength()<=2)) { // jp<<pt1; // } RS_Vector c=view->toGui(circle->getCenter()); double r=view->toGuiDX(circle->getRadius()); #if QT_VERSION >= 0x040400 path.addEllipse(QPoint(c.x,c.y),r,r); #else path.addEllipse(c.x - r, c.y + r, 2.*r, 2.*r); // QPolygon pa2; // painter->createArc(pa2, view->toGui(circle->getCenter()), // view->toGuiDX(circle->getRadius()), // 0.0, // 2*M_PI, // false); // pa<<pa2; #endif } break; case RS2::EntityEllipse: if(static_cast<RS_Ellipse*>(e)->isArc()) { QPolygon pa2; auto ellipse=static_cast<RS_Ellipse*>(e); painter->createEllipse(pa2, view->toGui(ellipse->getCenter()), view->toGuiDX(ellipse->getMajorRadius()), view->toGuiDX(ellipse->getMinorRadius()), ellipse->getAngle(), ellipse->getAngle1(), ellipse->getAngle2(), ellipse->isReversed() ); pa<<pa2; }else{ QPolygon pa2; auto ellipse=static_cast<RS_Ellipse*>(e); painter->createEllipse(pa2, view->toGui(ellipse->getCenter()), view->toGuiDX(ellipse->getMajorRadius()), view->toGuiDX(ellipse->getMinorRadius()), ellipse->getAngle(), ellipse->getAngle1(), ellipse->getAngle2(), ellipse->isReversed() ); path.addPolygon(pa2); } break; default: break; } if( pa.size()>2 && pa.first() == pa.last()) { paClosed<<pa; pa.clear(); } } } } if(pa.size()>2){ pa<<pa.first(); paClosed<<pa; } for(int i=0;i<paClosed.size();i++){ path.addPolygon(paClosed.at(i)); } painter->setBrush(painter->getPen().getColor()); painter->disablePen(); painter->drawPath(path); // pa<<jp; // painter->setBrush(painter->getPen().getColor()); // painter->disablePen(); // painter->drawPolygon(pa); }
/** * Overrides drawing of subentities. This is only ever called for solid fills. */ void RS_Hatch::draw(RS_Painter* painter, RS_GraphicView* view, double& /*patternOffset*/) { if (!data.solid) { for(auto se: entities){ view->drawEntity(painter,se); } return; } //area of solid fill. Use polygon approximation, except trivial cases QPainterPath path; QList<QPolygon> paClosed; QPolygon pa; // QPolygon jp; // jump points // loops: if (needOptimization==true) { for(auto l: entities){ if (l->rtti()==RS2::EntityContainer) { RS_EntityContainer* loop = (RS_EntityContainer*)l; loop->optimizeContours(); } } needOptimization = false; } // loops: for(auto l: entities){ l->setLayer(getLayer()); if (l->rtti()==RS2::EntityContainer) { RS_EntityContainer* loop = (RS_EntityContainer*)l; // edges: for(auto e: *loop){ e->setLayer(getLayer()); switch (e->rtti()) { case RS2::EntityLine: { QPoint pt1(RS_Math::round(view->toGuiX(e->getStartpoint().x)), RS_Math::round(view->toGuiY(e->getStartpoint().y))); QPoint pt2(RS_Math::round(view->toGuiX(e->getEndpoint().x)), RS_Math::round(view->toGuiY(e->getEndpoint().y))); // if (! (pa.size()>0 && (pa.last() - pt1).manhattanLength()<=2)) { // jp<<pt1; // } if(pa.size() && (pa.last()-pt1).manhattanLength()>=1) pa<<pt1; pa<<pt2; } break; case RS2::EntityArc: { // QPoint pt1(RS_Math::round(view->toGuiX(e->getStartpoint().x)), // RS_Math::round(view->toGuiY(e->getStartpoint().y))); // if (! (pa.size()>0 && (pa.last() - pt1).manhattanLength()<=2)) { // jp<<pt1; // } QPolygon pa2; RS_Arc* arc=static_cast<RS_Arc*>(e); painter->createArc(pa2, view->toGui(arc->getCenter()), view->toGuiDX(arc->getRadius()) ,arc->getAngle1(),arc->getAngle2(),arc->isReversed()); if(pa.size() &&pa2.size()&&(pa.last()-pa2.first()).manhattanLength()<1) pa2.remove(0,1); pa<<pa2; } break; case RS2::EntityCircle: { RS_Circle* circle = static_cast<RS_Circle*>(e); // QPoint pt1(RS_Math::round(view->toGuiX(circle->getCenter().x+circle->getRadius())), // RS_Math::round(view->toGuiY(circle->getCenter().y))); // if (! (pa.size()>0 && (pa.last() - pt1).manhattanLength()<=2)) { // jp<<pt1; // } RS_Vector c=view->toGui(circle->getCenter()); double r=view->toGuiDX(circle->getRadius()); #if QT_VERSION >= 0x040400 path.addEllipse(QPoint(c.x,c.y),r,r); #else path.addEllipse(c.x - r, c.y + r, 2.*r, 2.*r); // QPolygon pa2; // painter->createArc(pa2, view->toGui(circle->getCenter()), // view->toGuiDX(circle->getRadius()), // 0.0, // 2*M_PI, // false); // pa<<pa2; #endif } break; case RS2::EntityEllipse: if(static_cast<RS_Ellipse*>(e)->isArc()) { QPolygon pa2; auto ellipse=static_cast<RS_Ellipse*>(e); painter->createEllipse(pa2, view->toGui(ellipse->getCenter()), view->toGuiDX(ellipse->getMajorRadius()), view->toGuiDX(ellipse->getMinorRadius()), ellipse->getAngle() ,ellipse->getAngle1(),ellipse->getAngle2(),ellipse->isReversed() ); // qDebug()<<"ellipse: "<<ellipse->getCenter().x<<","<<ellipse->getCenter().y; // qDebug()<<"ellipse: pa2.size()="<<pa2.size(); // qDebug()<<"ellipse: pa2="<<pa2; if(pa.size() && pa2.size()&&(pa.last()-pa2.first()).manhattanLength()<1) pa2.remove(0,1); pa<<pa2; }else{ QPolygon pa2; auto ellipse=static_cast<RS_Ellipse*>(e); painter->createEllipse(pa2, view->toGui(ellipse->getCenter()), view->toGuiDX(ellipse->getMajorRadius()), view->toGuiDX(ellipse->getMinorRadius()), ellipse->getAngle(), ellipse->getAngle1(), ellipse->getAngle2(), ellipse->isReversed() ); path.addPolygon(pa2); } break; default: break; } // qDebug()<<"pa="<<pa; if( pa.size()>2 && pa.first() == pa.last()) { paClosed<<pa; pa.clear(); } } } } if(pa.size()>2){ pa<<pa.first(); paClosed<<pa; } for(auto& p:paClosed){ path.addPolygon(p); } //bug#474, restore brush after solid fill const QBrush brush(painter->brush()); const RS_Pen pen=painter->getPen(); painter->setBrush(pen.getColor()); painter->disablePen(); painter->drawPath(path); painter->setBrush(brush); painter->setPen(pen); }
/** * Calculates the boundary box of this ellipse. */ void RS_Ellipse::calculateBorders() { RS_DEBUG->print("RS_Ellipse::calculateBorders"); double radius1 = getMajorRadius(); double radius2 = getMinorRadius(); double angle = getAngle(); //double a1 = ((!isReversed()) ? data.angle1 : data.angle2); //double a2 = ((!isReversed()) ? data.angle2 : data.angle1); RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); 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); RS_Vector vp; // kind of a brute force. TODO: exact calculation // double a = a1; // do { // vp.set(data.center.x + radius1 * cos(a), // data.center.y + radius2 * sin(a)); // vp.rotate(data.center, angle); // // minX = std::min(minX, vp.x); // minY = std::min(minY, vp.y); // maxX = std::max(maxX, vp.x); // maxY = std::max(maxY, vp.y); // // a += 0.03; // } while (RS_Math::isAngleBetween(RS_Math::correctAngle(a), a1, a2, false) && // a<4*M_PI); // std::cout<<"a1="<<a1<<"\ta2="<<a2<<std::endl<<"Old algorithm:\nminX="<<minX<<"\tmaxX="<<maxX<<"\nminY="<<minY<<"\tmaxY="<<maxY<<std::endl; // Exact algorithm, based on rotation: // ( r1*cos(a), r2*sin(a)) rotated by angle to // (r1*cos(a)*cos(angle)-r2*sin(a)*sin(angle),r1*cos(a)*sin(angle)+r2*sin(a)*cos(angle)) // both coordinates can be further reorganized to the form rr*cos(a+ theta), // with rr and theta angle defined by the coordinates given above double amin,amax; // x range vp.set(radius1*cos(angle),radius2*sin(angle)); amin=RS_Math::correctAngle(getAngle1()+vp.angle()); // to the range of 0 to 2*M_PI amax=RS_Math::correctAngle(getAngle2()+vp.angle()); // to the range of 0 to 2*M_PI if( RS_Math::isAngleBetween(M_PI,amin,amax,isReversed()) ) { //if( (amin<=M_PI && delta_a >= M_PI - amin) || (amin > M_PI && delta_a >= 3*M_PI - amin)) { minX= data.center.x-vp.magnitude(); } // else // minX=data.center.x +vp.magnitude()*std::min(cos(amin),cos(amin+delta_a)); if( RS_Math::isAngleBetween(2.*M_PI,amin,amax,isReversed()) ) { //if( delta_a >= 2*M_PI - amin ) { maxX= data.center.x+vp.magnitude(); }// else // maxX= data.center.x+vp.magnitude()*std::max(cos(amin),cos(amin+delta_a)); // y range vp.set(radius1*sin(angle),-radius2*cos(angle)); amin=RS_Math::correctAngle(getAngle1()+vp.angle()); // to the range of 0 to 2*M_PI amax=RS_Math::correctAngle(getAngle2()+vp.angle()); // to the range of 0 to 2*M_PI if( RS_Math::isAngleBetween(M_PI,amin,amax,isReversed()) ) { //if( (amin<=M_PI &&delta_a >= M_PI - amin) || (amin > M_PI && delta_a >= 3*M_PI - amin)) { minY= data.center.y-vp.magnitude(); }// else // minY=data.center.y +vp.magnitude()*std::min(cos(amin),cos(amin+delta_a)); if( RS_Math::isAngleBetween(2.*M_PI,amin,amax,isReversed()) ) { //if( delta_a >= 2*M_PI - amin ) { maxY= data.center.y+vp.magnitude(); } // else // maxY= data.center.y+vp.magnitude()*std::max(cos(amin),cos(amin+delta_a)); //std::cout<<"New algorithm:\nminX="<<minX<<"\tmaxX="<<maxX<<"\nminY="<<minY<<"\tmaxY="<<maxY<<std::endl; minV.set(minX, minY); maxV.set(maxX, maxY); RS_DEBUG->print("RS_Ellipse::calculateBorders: OK"); }
QList<RLine> REllipse::getTangents(const RVector& point) const { QList<RLine> ret; if (getDistanceTo(point, false) < RS::PointTolerance) { // point is on ellipse: return ret; } // point is at center (prevents recursion when swapping ellipse minor / major): if (point.getDistanceTo(getCenter())<RS::PointTolerance) { return ret; } // swap ellipse minor / major if point is on minor axis // 20120928: and not also on major axis (prevent recursion): RLine minorAxis(getCenter(), getCenter() + getMinorPoint()); RLine majorAxis(getCenter(), getCenter() + getMajorPoint()); if (minorAxis.isOnShape(point, false) && !majorAxis.isOnShape(point, false)) { REllipse e2 =*this; e2.majorPoint = getMinorPoint(); e2.ratio = 1.0/ratio; return e2.getTangents(point); } double a = getMajorRadius(); // the length of the major axis / 2 double b = getMinorRadius(); // the length of the minor axis / 2 // rotate and move point: RVector point2 = point; point2.move(-getCenter()); point2.rotate(-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); if (RMath::isNaN(t)) { return ret; } yt1 = (t - bf) / (af * 2.0); xt1 = e - d * yt1; yt2 = (-t - bf) / (af * 2.0); xt2 = e - d * yt2; RVector s1(xt1, yt1); s1.rotate(getAngle()); s1.move(getCenter()); RVector s2(xt2, yt2); s2.rotate(getAngle()); s2.move(getCenter()); if (s1.isValid()) { ret.append(RLine(point, s1)); } if (s2.isValid()) { ret.append(RLine(point, s2)); } return ret; }
RVector REllipse::getVectorTo(const RVector& point, bool limited, double strictRange) const { Q_UNUSED(strictRange) RVector ret = RVector::invalid; double ang = getAngle(); //double dDistance = RMAXDOUBLE; bool swap = false; bool majorSwap = false; RVector normalized = (point - center).rotate(-ang); // special case: point in line with major axis: if (fabs(normalized.getAngle()) < RS::AngleTolerance || fabs(normalized.getAngle()) > 2*M_PI-RS::AngleTolerance) { ret = RVector(getMajorRadius(), 0.0); //dDistance = ret.distanceTo(normalized); } else if (fabs(normalized.getAngle()-M_PI) < RS::AngleTolerance) { ret = RVector(-getMajorRadius(), 0.0); //dDistance = ret.distanceTo(normalized); } else { double dU = normalized.x; double dV = normalized.y; double dA = getMajorRadius(); double dB = getMinorRadius(); double dEpsilon = 1.0e-8; // iteration maximum int iMax = 32; double rdX = 0.0; double rdY = 0.0; if (dA<dB) { double dum = dA; dA = dB; dB = dum; dum = dU; dU = dV; dV = dum; majorSwap = true; } if (dV<0.0) { dV*=-1.0; swap = true; } // initial guess: double dT = dB*(dV - dB); // newton's method: int i; for (i = 0; i < iMax; i++) { double dTpASqr = dT + dA*dA; double dTpBSqr = dT + dB*dB; double dInvTpASqr = 1.0/dTpASqr; double dInvTpBSqr = 1.0/dTpBSqr; double dXDivA = dA*dU*dInvTpASqr; double dYDivB = dB*dV*dInvTpBSqr; double dXDivASqr = dXDivA*dXDivA; double dYDivBSqr = dYDivB*dYDivB; double dF = dXDivASqr + dYDivBSqr - 1.0; if (fabs(dF) < dEpsilon) { // f(t0) is very close to zero: rdX = dXDivA*dA; rdY = dYDivB*dB; break; } double dFDer = 2.0*(dXDivASqr*dInvTpASqr + dYDivBSqr*dInvTpBSqr); double dRatio = dF/dFDer; if ( fabs(dRatio) < dEpsilon ) { // t1-t0 is very close to zero: rdX = dXDivA*dA; rdY = dYDivB*dB; break; } dT += dRatio; } if (i == iMax) { // failed to converge: //dDistance = RMAXDOUBLE; ret = RVector::invalid; } else { //double dDelta0 = rdX - dU; //double dDelta1 = rdY - dV; //dDistance = sqrt(dDelta0*dDelta0 + dDelta1*dDelta1); ret = RVector(rdX, rdY); } } if (ret.isValid()) { if (swap) { ret.y*=-1.0; } if (majorSwap) { double dum = ret.x; ret.x = ret.y; ret.y = dum; } ret = (ret.rotate(ang) + center); if (limited) { double a1 = center.getAngleTo(getStartPoint()); double a2 = center.getAngleTo(getEndPoint()); double a = center.getAngleTo(ret); if (!RMath::isAngleBetween(a, a1, a2, reversed)) { ret = RVector::invalid; } } } /* if (dist!=NULL) { if (ret.valid) { *dist = dDistance; } else { *dist = RS_MAXDOUBLE; } } if (entity!=NULL) { if (ret.valid) { *entity = this; } else { *entity = NULL; } } */ return point - ret; }
/** * \return Radius of ellipse at given ellipse angle. */ double REllipse::getRadiusAt(double angle) const { RVector v(cos(angle)*getMajorRadius(), sin(angle)*getMinorRadius()); return v.getMagnitude(); }
/** * \return Minor point relative to the center point. */ RVector REllipse::getMinorPoint() const{ double angle = RMath::getNormalizedAngle(getAngle() + M_PI/2.0); RVector ret; ret.setPolar(getMinorRadius(), angle); return ret; }