/* 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(); }
RS2::Ending RS_Line::getTrimPoint(const RS_Vector& coord, const RS_Vector& trimPoint) { double angEl = getAngle1(); double angM = trimPoint.angleTo(coord); double angDif = angEl-angM; if (angDif<0.0) { angDif*=-1.0; } if (angDif>M_PI) { angDif=2*M_PI-angDif; } if (angDif<M_PI/2.0) { return RS2::EndingStart; } else { return RS2::EndingEnd; } }
RS_Vector RS_Line::getNearestDist(double distance, const RS_Vector& coord, double* dist) { double a1 = getAngle1(); RS_Vector dv; dv.setPolar(distance, a1); RS_Vector p1 = data.startpoint + dv; RS_Vector p2 = data.endpoint - dv; 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; }
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; }
bool RS_Ellipse::switchMajorMinor(void) //switch naming of major/minor, return true if success { if (fabs(data.ratio) < RS_TOLERANCE) return false; RS_Vector vp_start=getStartpoint(); RS_Vector vp_end=getStartpoint(); RS_Vector vp=getMajorP(); setMajorP(RS_Vector(- data.ratio*vp.y, data.ratio*vp.x)); //direction pi/2 relative to old MajorP; setRatio(1./data.ratio); 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(vp_start)); setAngle2(getEllipseAngle(vp_end)); } return true; }
RS_Vector RS_Line::getNearestDist(double distance, const RS_Vector& coord, double* dist) const{ RS_Vector dv = RS_Vector::polar(distance, getAngle1()); RS_Vector ret; //if(coord.distanceTo(getStartpoint()) < coord.distanceTo(getEndpoint())) { if( (coord-getStartpoint()).squared()< (coord-getEndpoint()).squared() ) { ret = getStartpoint() + dv; }else{ ret = getEndpoint() - dv; } if (dist) *dist=coord.distanceTo(ret); return ret; }
RS_Vector RS_Line::getNearestDist(double distance, bool startp) const{ double a1 = getAngle1(); RS_Vector dv = RS_Vector::polar(distance, a1); RS_Vector ret; if (startp) { ret = data.startpoint + dv; } else { ret = data.endpoint - dv; } return ret; }
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()); }
/** * mirror by the axis defined by axisPoint1 and axisPoint2 */ void RS_Ellipse::mirror(RS_Vector axisPoint1, RS_Vector axisPoint2) { RS_Vector center=getCenter(); RS_Vector mp = center + getMajorP(); RS_Vector startpoint = getStartpoint(); RS_Vector endpoint = getEndpoint(); center.mirror(axisPoint1, axisPoint2); mp.mirror(axisPoint1, axisPoint2); startpoint.mirror(axisPoint1, axisPoint2); endpoint.mirror(axisPoint1, axisPoint2); setCenter(center); setReversed(!isReversed()); setMajorP(mp - center); 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(startpoint)); setAngle2( getEllipseAngle(endpoint)); } /* old version data.majorP = mp - data.center; double a = axisPoint1.angleTo(axisPoint2); RS_Vector vec; vec.setPolar(1.0, data.angle1); vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1); data.angle1 = vec.angle() - 2*a; vec.setPolar(1.0, data.angle2); vec.mirror(RS_Vector(0.0,0.0), axisPoint2-axisPoint1); data.angle2 = vec.angle() - 2*a; data.reversed = (!data.reversed); */ //calculateEndpoints(); correctAngles();//avoid extra 2.*M_PI in angles calculateBorders(); }
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(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); }
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; 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; } }
/** * 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); }
/** * 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"); }
void RS_Line::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->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); } else { double styleFactor = getStyleFactor(view); if (styleFactor<0.0) { painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); return; } // Pattern: RS_LineTypePattern* pat; if (isSelected()) { pat = &patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (pat==NULL) { RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern"); return; } // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); // index counter int i; // line data: double length = getLength(); double angle = getAngle1(); // pattern segment length: double patternSegmentLength = 0.0; // create pattern: RS_Vector* dp = new RS_Vector[pat->num]; for (i=0; i<pat->num; ++i) { dp[i] = RS_Vector(cos(angle) * fabs(pat->pattern[i] * styleFactor), sin(angle) * fabs(pat->pattern[i] * styleFactor)); patternSegmentLength += fabs(pat->pattern[i] * styleFactor); } // handle pattern offset: int m; if (patternOffset<0.0) { m = (int)ceil(patternOffset / patternSegmentLength); } else { m = (int)floor(patternOffset / patternSegmentLength); } patternOffset -= (m*patternSegmentLength); //if (patternOffset<0.0) { // patternOffset+=patternSegmentLength; //} //RS_DEBUG->print("pattern. offset: %f", patternOffset); RS_Vector patternOffsetVec; patternOffsetVec.setPolar(patternOffset, angle); double tot=patternOffset; i=0; bool done = false; bool cutStartpoint, cutEndpoint, drop; RS_Vector curP=getStartpoint()+patternOffsetVec; do { cutStartpoint = false; cutEndpoint = false; drop = false; // line segment (otherwise space segment) if (pat->pattern[i]>0.0) { // drop the whole pattern segment line: if (tot+pat->pattern[i]*styleFactor < 0.0) { drop = true; } else { // trim startpoint of pattern segment line to line startpoint if (tot < 0.0) { cutStartpoint = true; } // trim endpoint of pattern segment line to line endpoint if (tot+pat->pattern[i]*styleFactor > length) { cutEndpoint = true; } } if (drop) { // do nothing } else { RS_Vector p1 = curP; RS_Vector p2 = curP + dp[i]; if (cutStartpoint) { p1 = getStartpoint(); } if (cutEndpoint) { p2 = getEndpoint(); } painter->drawLine(view->toGui(p1), view->toGui(p2)); } } curP+=dp[i]; tot+=fabs(pat->pattern[i]*styleFactor); //RS_DEBUG->print("pattern. tot: %f", tot); done=tot>length; i++; if (i>=pat->num) { i=0; } } while(!done); delete[] dp; } }
RS_Vector RS_Ellipse::prepareTrim(const RS_Vector& trimCoord, const RS_VectorSolutions& trimSol) { //special trimming for ellipse arc if( ! trimSol.hasValid() ) return (RS_Vector(false)); if( trimSol.getNumber() == 1 ) return (trimSol.get(0)); double am=getEllipseAngle(trimCoord); QList<double> ias; double ia(0.),ia2(0.); RS_Vector is,is2; for(int ii=0; ii<trimSol.getNumber(); ii++) { //find closest according ellipse angle ias.append(getEllipseAngle(trimSol.get(ii))); if( !ii || fabs( remainder( ias[ii] - am, 2*M_PI)) < fabs( remainder( ia -am, 2*M_PI)) ) { ia = ias[ii]; is = trimSol.get(ii); } } qSort(ias.begin(),ias.end()); for(int ii=0; ii<trimSol.getNumber(); ii++) { //find segment to enclude trimCoord if ( ! RS_Math::isSameDirection(ia,ias[ii],RS_TOLERANCE)) continue; if( RS_Math::isAngleBetween(am,ias[(ii+trimSol.getNumber()-1)% trimSol.getNumber()],ia,false)) { ia2=ias[(ii+trimSol.getNumber()-1)% trimSol.getNumber()]; } else { ia2=ias[(ii+1)% trimSol.getNumber()]; } break; } for(int ii=0; ii<trimSol.getNumber(); ii++) { //find segment to enclude trimCoord if ( ! RS_Math::isSameDirection(ia2,getEllipseAngle(trimSol.get(ii)),RS_TOLERANCE)) continue; is2=trimSol.get(ii); break; } if(RS_Math::isSameDirection(getAngle1(),getAngle2(),RS_TOLERANCE_ANGLE) || RS_Math::isSameDirection(ia2,ia,RS_TOLERANCE) ) { //whole ellipse if( !RS_Math::isAngleBetween(am,ia,ia2,isReversed())) { std::swap(ia,ia2); std::swap(is,is2); } setAngle1(ia); setAngle2(ia2); double da1=fabs(remainder(getAngle1()-am,2*M_PI)); double da2=fabs(remainder(getAngle2()-am,2*M_PI)); if(da2<da1) { std::swap(is,is2); } } else { double dia=fabs(remainder(ia-am,2*M_PI)); double dia2=fabs(remainder(ia2-am,2*M_PI)); double ai_min=std::min(dia,dia2); double da1=fabs(remainder(getAngle1()-am,2*M_PI)); double da2=fabs(remainder(getAngle2()-am,2*M_PI)); double da_min=std::min(da1,da2); if( da_min < ai_min ) { //trimming one end of arc bool irev= RS_Math::isAngleBetween(am,ia2,ia, isReversed()) ; if ( RS_Math::isAngleBetween(ia,getAngle1(),getAngle2(), isReversed()) && RS_Math::isAngleBetween(ia2,getAngle1(),getAngle2(), isReversed()) ) { // if(irev) { setAngle2(ia); setAngle1(ia2); } else { setAngle1(ia); setAngle2(ia2); } da1=fabs(remainder(getAngle1()-am,2*M_PI)); da2=fabs(remainder(getAngle2()-am,2*M_PI)); } if( ((da1 < da2) && (RS_Math::isAngleBetween(ia2,ia,getAngle1(),isReversed()))) || ((da1 > da2) && (RS_Math::isAngleBetween(ia2,getAngle2(),ia,isReversed()))) ) { std::swap(is,is2); //std::cout<<"reset: angle1="<<getAngle1()<<" angle2="<<getAngle2()<<" am="<< am<<" is="<<getEllipseAngle(is)<<" ia2="<<ia2<<std::endl; } } else { //choose intersection as new end if( dia > dia2) { std::swap(is,is2); std::swap(ia,ia2); } if(RS_Math::isAngleBetween(ia,getAngle1(),getAngle2(),isReversed())) { if(RS_Math::isAngleBetween(am,getAngle1(),ia,isReversed())) { setAngle2(ia); } else { setAngle1(ia); } } } } return is; }
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; }