/** *find the tangential points from a given point, i.e., the tangent lines should pass * the given point and tangential points * *Author: Dongxu Li */ RS_VectorSolutions RS_Circle::getTangentPoint(const RS_Vector& point) const { RS_VectorSolutions ret; double r2(getRadius()*getRadius()); if(r2<RS_TOLERANCE*RS_TOLERANCE) return ret; //circle too small RS_Vector vp(point-getCenter()); double c2(vp.squared()); if(c2<r2-getRadius()*2.*RS_TOLERANCE) { //inside point, no tangential point return ret; } if(c2>r2+getRadius()*2.*RS_TOLERANCE) { //external point RS_Vector vp1(-vp.y,vp.x); vp1*=getRadius()*sqrt(c2-r2)/c2; vp *= r2/c2; vp += getCenter(); if(vp1.squared()>RS_TOLERANCE*RS_TOLERANCE) { ret.push_back(vp+vp1); ret.push_back(vp-vp1); return ret; } } ret.push_back(point); return ret; }
/** * @return One or two intersection points between given entities. */ RS_VectorSolutions RS_Information::getIntersectionEllipseLine(RS_Line* line, RS_Ellipse* ellipse) { RS_VectorSolutions ret; if (line==NULL || ellipse==NULL) { return ret; } // rotate into normal position: double rx = ellipse->getMajorRadius(); if(rx<RS_TOLERANCE) { //zero radius ellipse RS_Vector vp(line->getNearestPointOnEntity(ellipse->getCenter(), true)); if((vp-ellipse->getCenter()).squared() <RS_TOLERANCE2){ //center on line ret.push_back(vp); } return ret; } RS_Vector angleVector = ellipse->getMajorP().scale(RS_Vector(1./rx,-1./rx)); double ry = rx*ellipse->getRatio(); RS_Vector center = ellipse->getCenter(); RS_Vector a1 = line->getStartpoint().rotate(center, angleVector); RS_Vector a2 = line->getEndpoint().rotate(center, angleVector); // RS_Vector origin = a1; RS_Vector dir = a2-a1; RS_Vector diff = a1 - center; RS_Vector mDir = RS_Vector(dir.x/(rx*rx), dir.y/(ry*ry)); RS_Vector mDiff = RS_Vector(diff.x/(rx*rx), diff.y/(ry*ry)); double a = RS_Vector::dotP(dir, mDir); double b = RS_Vector::dotP(dir, mDiff); double c = RS_Vector::dotP(diff, mDiff) - 1.0; double d = b*b - a*c; // std::cout<<"RS_Information::getIntersectionEllipseLine(): d="<<d<<std::endl; if (d < - 1.e3*RS_TOLERANCE*sqrt(RS_TOLERANCE)) { RS_DEBUG->print("RS_Information::getIntersectionLineEllipse: outside 0"); return ret; } if( d < 0. ) d=0.; double root = sqrt(d); double t_a = -b/a; double t_b = root/a; // double t_b = (-b + root) / a; ret.push_back(a1.lerp(a2,t_a+t_b)); RS_Vector vp(a1.lerp(a2,t_a-t_b)); if ( (ret.get(0)-vp).squared()>RS_TOLERANCE2) { ret.push_back(vp); } angleVector.y *= -1.; ret.rotate(center, angleVector); // std::cout<<"found Ellipse-Line intersections: "<<ret.getNumber()<<std::endl; // std::cout<<ret<<std::endl; RS_DEBUG->print("RS_Information::getIntersectionEllipseLine(): done"); return ret; }
RS_VectorSolutions RS_Ellipse::getRefPoints() { RS_VectorSolutions ret; if(isArc()){ //no start/end point for whole ellipse ret.push_back(getStartpoint()); ret.push_back(getEndpoint()); } ret.push_back(data.center); ret.appendTo(getFoci()); return ret; }
/** switch x,y for all vectors */ RS_VectorSolutions RS_VectorSolutions::flipXY(void) const { RS_VectorSolutions ret; const int counts=vector.size(); for(int i=0;i<counts;i++) ret.push_back(vector[i].flipXY()); return ret; }
/** switch x,y for all vectors */ RS_VectorSolutions RS_VectorSolutions::flipXY(void) const { RS_VectorSolutions ret; for(const auto& vp: vector) ret.push_back(vp.flipXY()); return ret; }
void RS_Circle::moveRef(const RS_Vector& ref, const RS_Vector& offset) { if(ref.distanceTo(data.center)<1.0e-4){ data.center += offset; return; } RS_Vector v1(data.radius, 0.0); RS_VectorSolutions sol; sol.push_back(data.center + v1); sol.push_back(data.center - v1); v1.set(0., data.radius); sol.push_back(data.center + v1); sol.push_back(data.center - v1); double dist; v1=sol.getClosest(ref,&dist); if(dist>1.0e-4) return; data.radius = data.center.distanceTo(v1 + offset); }
RS_VectorSolutions RS_Polyline::getRefPoints() { RS_VectorSolutions ret; ret.push_back(data.startpoint); for (RS_Entity* e=firstEntity(RS2::ResolveNone); e!=NULL; e = nextEntity(RS2::ResolveNone)) { if (e->isAtomic()) { ret.push_back(((RS_AtomicEntity*)e)->getEndpoint()); } } ret.push_back( data.endpoint); return ret; }
RS_Vector RS_Image::getNearestCenter(const RS_Vector& coord, double* dist) const{ RS_VectorSolutions const& corners{getCorners()}; //bug#485, there's no clear reason to ignore snapping to center within an image // if(containsPoint(coord)){ // //if coord is within image // if(dist) *dist=0.; // return coord; // } RS_VectorSolutions points; for (size_t i=0; i < corners.size(); ++i) { size_t const j = (i+1)%corners.size(); points.push_back((corners.get(i) + corners.get(j))*0.5); } points.push_back((corners.get(0) + corners.get(2))*0.5); return points.getClosest(coord, dist); }
RS_Vector RS_Image::getNearestCenter(const RS_Vector& coord, double* dist) { RS_VectorSolutions points; RS_VectorSolutions corners = getCorners(); //bug#485, there's no clear reason to ignore snapping to center within an image // if(containsPoint(coord)){ // //if coord is within image // if(dist!=NULL) *dist=0.; // return coord; // } points.push_back((corners.get(0) + corners.get(1))/2.0); points.push_back((corners.get(1) + corners.get(2))/2.0); points.push_back((corners.get(2) + corners.get(3))/2.0); points.push_back((corners.get(3) + corners.get(0))/2.0); points.push_back((corners.get(0) + corners.get(2))/2.0); return points.getClosest(coord, dist); }
RS_Vector RS_Image::getNearestDist(double distance, const RS_Vector& coord, double* dist) const{ RS_VectorSolutions const& corners = getCorners(); RS_VectorSolutions points; for (size_t i = 0; i < corners.size(); ++i){ size_t const j = (i+1)%corners.size(); RS_Line const l{corners.get(i), corners.get(j)}; RS_Vector const& vp = l.getNearestDist(distance, coord, dist); points.push_back(vp); } return points.getClosest(coord, dist); }
RS_VectorSolutions LC_Quadratic::getIntersection(const LC_Quadratic& l1, const LC_Quadratic& l2) { RS_VectorSolutions ret; if( l1.isValid() && l2.isValid() == false ) return ret; auto p1=&l1; auto p2=&l2; if(p1->isQuadratic()==false){ std::swap(p1,p2); } if(p1->isQuadratic()==false){ //two lines QVector<QVector<double> > ce(2,QVector<double>(3,0.)); ce[0][0]=p1->m_vLinear(0); ce[0][1]=p1->m_vLinear(1); ce[0][2]=-p1->m_dConst; ce[1][0]=p2->m_vLinear(0); ce[1][1]=p2->m_vLinear(1); ce[1][2]=-p2->m_dConst; QVector<double> sn(2,0.); if(RS_Math::linearSolver(ce,sn)){ ret.push_back(RS_Vector(sn[0],sn[1])); } return ret; } if(p2->isQuadratic()==false){ //one line, one quadratic //avoid division by zero if(fabs(p2->m_vLinear(0))<fabs(p2->m_vLinear(1))){ return getIntersection(p1->flipXY(),p2->flipXY()).flipXY(); } } std::vector<std::vector<double> > ce(0); ce.push_back(p1->getCoefficients()); ce.push_back(p2->getCoefficients()); //DEBUG_HEADER(); //std::cout<<*p1<<std::endl; //std::cout<<*p2<<std::endl; return RS_Math::simultaneousQuadraticSolverFull(ce); }
/** * create a circle of radius r and tangential to two given entities */ RS_VectorSolutions RS_Circle::createTan2(const std::vector<RS_AtomicEntity*>& circles, const double& r) { if(circles.size()<2) return false; auto e0=circles[0]->offsetTwoSides(r); auto e1=circles[1]->offsetTwoSides(r); RS_VectorSolutions centers; if(e0.size() && e1.size()) { for(auto it0=e0.begin();it0!=e0.end();it0++){ for(auto it1=e1.begin();it1!=e1.end();it1++){ centers.push_back(RS_Information::getIntersection(*it0,*it1)); } } } for(auto it0=e0.begin();it0!=e0.end();it0++){ delete *it0; } for(auto it0=e1.begin();it0!=e1.end();it0++){ delete *it0; } return centers; }
RS_Vector RS_Image::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity) const{ if (entity) { *entity = const_cast<RS_Image*>(this); } RS_VectorSolutions const& corners =getCorners(); //allow selecting image by clicking within images, bug#3464626 if(containsPoint(coord)){ //if coord is within image if(dist) *dist=0.; return coord; } RS_VectorSolutions points; for (size_t i=0; i < corners.size(); ++i){ size_t const j = (i+1)%corners.size(); RS_Line const l{corners.at(i), corners.at(j)}; RS_Vector const vp = l.getNearestPointOnEntity(coord, onEntity); points.push_back(vp); } return points.getClosest(coord, dist); }
void RS_Line::draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) { if (! (painter && view)) { return; } //only draw the visible portion of line LC_Rect const viewportRect{view->toGraph(0, 0), view->toGraph(view->getWidth(), view->getHeight())}; RS_VectorSolutions endPoints(0); if (viewportRect.inArea(getStartpoint(), RS_TOLERANCE)) endPoints.push_back(getStartpoint()); if (viewportRect.inArea(getEndpoint(), RS_TOLERANCE)) endPoints.push_back(getEndpoint()); RS_EntityContainer ec(nullptr); ec.addRectangle(viewportRect.minP(), viewportRect.maxP()); if (endPoints.size()<2){ RS_VectorSolutions vpIts; for(auto p: ec) { auto const sol=RS_Information::getIntersection(this, p, true); for (auto const& vp: sol) { if (vpIts.getClosestDistance(vp) <= RS_TOLERANCE * 10.) continue; vpIts.push_back(vp); } } for (auto const& vp: vpIts) { if (endPoints.getClosestDistance(vp) <= RS_TOLERANCE * 10.) continue; endPoints.push_back(vp); } } if (endPoints.size()<2) return; if ((endPoints[0] - getStartpoint()).squared() > (endPoints[1] - getStartpoint()).squared() ) std::swap(endPoints[0],endPoints[1]); RS_Vector pStart{view->toGui(endPoints.at(0))}; RS_Vector pEnd{view->toGui(endPoints.at(1))}; // std::cout<<"draw line: "<<pStart<<" to "<<pEnd<<std::endl; RS_Vector direction = pEnd-pStart; if (isConstruction(true) && direction.squared() > RS_TOLERANCE){ //extend line on a construction layer to fill the whole view RS_VectorSolutions vpIts; for(auto p: ec) { auto const sol=RS_Information::getIntersection(this, p, false); for (auto const& vp: sol) { if (vpIts.getClosestDistance(vp) <= RS_TOLERANCE * 10.) continue; vpIts.push_back(vp); } } //draw construction lines up to viewport border switch (vpIts.size()) { case 2: // no need to sort intersections break; case 3: case 4: { // will use the inner two points size_t i{0}; for (size_t j = 2; j < vpIts.size(); ++j) { if (viewportRect.inArea(vpIts.at(j), RS_TOLERANCE * 10.)) std::swap(vpIts[j], vpIts[i++]); } } break; default: //should not happen return; } pStart=view->toGui(vpIts.get(0)); pEnd=view->toGui(vpIts.get(1)); direction=pEnd-pStart; } double length=direction.magnitude(); patternOffset -= length; if (( !isSelected() && ( getPen().getLineType()==RS2::SolidLine || view->getDrawingMode()==RS2::ModePreview)) ) { //if length is too small, attempt to draw the line, could be a potential bug painter->drawLine(pStart,pEnd); return; } // double styleFactor = getStyleFactor(view); // Pattern: const RS_LineTypePattern* pat; if (isSelected()) { // styleFactor=1.; pat = &RS_LineTypePattern::patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (!pat) { // patternOffset -= length; RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern"); painter->drawLine(pStart,pEnd); return; } // patternOffset = remainder(patternOffset - length-0.5*pat->totalLength,pat->totalLength)+0.5*pat->totalLength; if(length<=RS_TOLERANCE){ painter->drawLine(pStart,pEnd); return; //avoid division by zero } direction/=length; //cos(angle), sin(angle) // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); // index counter size_t i; // pattern segment length: double patternSegmentLength = pat->totalLength; // create pattern: size_t const patnum=pat->num > 0?pat->num:0; std::vector<RS_Vector> dp(patnum); std::vector<double> ds(patnum, 0.); if (pat->num >0 ){ double dpmm=static_cast<RS_PainterQt*>(painter)->getDpmm(); for (i=0; i<pat->num; ++i) { // ds[j]=pat->pattern[i] * styleFactor; //fixme, styleFactor support needed ds[i]=dpmm*pat->pattern[i]; if (fabs(ds[i]) < 1. ) ds[i] = (ds[i]>=0.)?1.:-1.; dp[i] = direction*fabs(ds[i]); } } else { RS_DEBUG->print(RS_Debug::D_WARNING,"invalid line pattern for line, draw solid line instread"); painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); return; } double total= remainder(patternOffset-0.5*patternSegmentLength,patternSegmentLength) -0.5*patternSegmentLength; // double total= patternOffset-patternSegmentLength; RS_Vector curP{pStart+direction*total}; for (int j=0; total<length; j=(j+1)%i) { // line segment (otherwise space segment) double const t2=total+fabs(ds[j]); RS_Vector const& p3=curP+dp[j]; if (ds[j]>0.0 && t2 > 0.0) { // drop the whole pattern segment line, for ds[i]<0: // trim end points of pattern segment line to line RS_Vector const& p1 =(total > -0.5)?curP:pStart; RS_Vector const& p2 =(t2<length+0.5)?p3:pEnd; painter->drawLine(p1,p2); } total=t2; curP=p3; } }
RS_VectorSolutions LC_Quadratic::getIntersection(const LC_Quadratic& l1, const LC_Quadratic& l2) { RS_VectorSolutions ret; if( l1.isValid()==false || l2.isValid()==false ) { // DEBUG_HEADER // std::cout<<l1<<std::endl; // std::cout<<l2<<std::endl; return ret; } auto p1=&l1; auto p2=&l2; if(p1->isQuadratic()==false){ std::swap(p1,p2); } if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){ DEBUG_HEADER std::cout<<*p1<<std::endl; std::cout<<*p2<<std::endl; } if(p1->isQuadratic()==false){ //two lines std::vector<std::vector<double> > ce(2,std::vector<double>(3,0.)); ce[0][0]=p1->m_vLinear(0); ce[0][1]=p1->m_vLinear(1); ce[0][2]=-p1->m_dConst; ce[1][0]=p2->m_vLinear(0); ce[1][1]=p2->m_vLinear(1); ce[1][2]=-p2->m_dConst; std::vector<double> sn(2,0.); if(RS_Math::linearSolver(ce,sn)){ ret.push_back(RS_Vector(sn[0],sn[1])); } return ret; } if(p2->isQuadratic()==false){ //one line, one quadratic //avoid division by zero if(fabs(p2->m_vLinear(0))+DBL_EPSILON<fabs(p2->m_vLinear(1))){ ret=getIntersection(p1->flipXY(),p2->flipXY()).flipXY(); // for(size_t j=0;j<ret.size();j++){ // DEBUG_HEADER // std::cout<<j<<": ("<<ret[j].x<<", "<< ret[j].y<<")"<<std::endl; // } return ret; } std::vector<std::vector<double> > ce(0); if(fabs(p2->m_vLinear(1))<RS_TOLERANCE){ const double angle=0.25*M_PI; LC_Quadratic p11(*p1); LC_Quadratic p22(*p2); ce.push_back(p11.rotate(angle).getCoefficients()); ce.push_back(p22.rotate(angle).getCoefficients()); ret=RS_Math::simultaneousQuadraticSolverMixed(ce); ret.rotate(-angle); // for(size_t j=0;j<ret.size();j++){ // DEBUG_HEADER // std::cout<<j<<": ("<<ret[j].x<<", "<< ret[j].y<<")"<<std::endl; // } return ret; } ce.push_back(p1->getCoefficients()); ce.push_back(p2->getCoefficients()); ret=RS_Math::simultaneousQuadraticSolverMixed(ce); // for(size_t j=0;j<ret.size();j++){ // DEBUG_HEADER // std::cout<<j<<": ("<<ret[j].x<<", "<< ret[j].y<<")"<<std::endl; // } return ret; } if( fabs(p1->m_mQuad(0,0))<RS_TOLERANCE && fabs(p1->m_mQuad(0,1))<RS_TOLERANCE && fabs(p2->m_mQuad(0,0))<RS_TOLERANCE && fabs(p2->m_mQuad(0,1))<RS_TOLERANCE ){ if(fabs(p1->m_mQuad(1,1))<RS_TOLERANCE && fabs(p2->m_mQuad(1,1))<RS_TOLERANCE){ //linear std::vector<double> ce(0); ce.push_back(p1->m_vLinear(0)); ce.push_back(p1->m_vLinear(1)); ce.push_back(p1->m_dConst); LC_Quadratic lc10(ce); ce.clear(); ce.push_back(p2->m_vLinear(0)); ce.push_back(p2->m_vLinear(1)); ce.push_back(p2->m_dConst); LC_Quadratic lc11(ce); return getIntersection(lc10,lc11); } return getIntersection(p1->flipXY(),p2->flipXY()).flipXY(); } std::vector<std::vector<double> > ce(0); ce.push_back(p1->getCoefficients()); ce.push_back(p2->getCoefficients()); if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){ DEBUG_HEADER std::cout<<*p1<<std::endl; std::cout<<*p2<<std::endl; } auto sol= RS_Math::simultaneousQuadraticSolverFull(ce); bool valid= sol.size()>0; for(auto & v: sol){ if(v.magnitude()>=RS_MAXDOUBLE){ valid=false; break; } } if(valid) return sol; ce.clear(); ce.push_back(p1->flipXY().getCoefficients()); ce.push_back(p2->flipXY().getCoefficients()); sol=RS_Math::simultaneousQuadraticSolverFull(ce); ret.clear(); for(auto const& v: sol){ if(v.magnitude()<=RS_MAXDOUBLE){ ret.push_back(v); if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){ DEBUG_HEADER std::cout<<v<<std::endl; } } } return ret; }
RS_VectorSolutions RS_Information::createQuadrilateral(const RS_EntityContainer& container) { RS_VectorSolutions ret; if(container.count()!=4) return ret; RS_EntityContainer c(container); std::vector<RS_Line*> lines; for(auto e: c){ if(e->rtti()!=RS2::EntityLine) return ret; lines.push_back(static_cast<RS_Line*>(e)); } if(lines.size()!=4) return ret; //find intersections std::vector<RS_Vector> vertices; for(auto it=lines.begin()+1; it != lines.end(); ++it){ for(auto jt=lines.begin(); jt != it; ++jt){ RS_VectorSolutions const& sol=RS_Information::getIntersectionLineLine(*it, *jt); if(sol.size()){ vertices.push_back(sol.at(0)); } } } // std::cout<<"vertices.size()="<<vertices.size()<<std::endl; switch (vertices.size()){ default: return ret; case 4: break; case 5: case 6: for(RS_Line* pl: lines){ const double a0=pl->getDirection1(); std::vector<std::vector<RS_Vector>::iterator> left; std::vector<std::vector<RS_Vector>::iterator> right; for(auto it=vertices.begin(); it != vertices.end(); ++it){ RS_Vector const& dir=*it - pl->getNearestPointOnEntity(*it, false); if(dir.squared()<RS_TOLERANCE15) continue; // std::cout<<"angle="<<remainder(dir.angle() - a0, 2.*M_PI)<<std::endl; if(remainder(dir.angle() - a0, 2.*M_PI) > 0.) left.push_back(it); else right.push_back(it); if(left.size()==2 && right.size()==1){ vertices.erase(right[0]); break; } else if(left.size()==1 && right.size()==2){ vertices.erase(left[0]); break; } } if(vertices.size()==4) break; } break; } //order vertices RS_Vector center{0., 0.}; for(const RS_Vector& vp: vertices) center += vp; center *= 0.25; std::sort(vertices.begin(), vertices.end(), [¢er](const RS_Vector& a, const RS_Vector&b)->bool{ return center.angleTo(a)<center.angleTo(b); } ); for(const RS_Vector& vp: vertices){ ret.push_back(vp); // std::cout<<"vp="<<vp<<std::endl; } return ret; }
RS_VectorSolutions RS_Information::getIntersectionEllipseEllipse( RS_Ellipse const* e1, RS_Ellipse const* e2) { RS_VectorSolutions ret; if (!(e1 && e2) ) { return ret; } if ( (e1->getCenter() - e2 ->getCenter()).squared() < RS_TOLERANCE2 && (e1->getMajorP() - e2 ->getMajorP()).squared() < RS_TOLERANCE2 && fabs(e1->getRatio() - e2 ->getRatio()) < RS_TOLERANCE ) { // overlapped ellipses, do not do overlap return ret; } RS_Ellipse ellipse01(nullptr,e1->getData()); RS_Ellipse *e01= & ellipse01; if( e01->getMajorRadius() < e01->getMinorRadius() ) e01->switchMajorMinor(); RS_Ellipse ellipse02(nullptr,e2->getData()); RS_Ellipse *e02= &ellipse02; if( e02->getMajorRadius() < e02->getMinorRadius() ) e02->switchMajorMinor(); //transform ellipse2 to ellipse1's coordinates RS_Vector shiftc1=- e01->getCenter(); double shifta1=-e01->getAngle(); e02->move(shiftc1); e02->rotate(shifta1); // RS_Vector majorP2=e02->getMajorP(); double a1=e01->getMajorRadius(); double b1=e01->getMinorRadius(); double x2=e02->getCenter().x, y2=e02->getCenter().y; double a2=e02->getMajorRadius(); double b2=e02->getMinorRadius(); if( e01->getMinorRadius() < RS_TOLERANCE || e01 -> getRatio()< RS_TOLERANCE) { // treate e01 as a line RS_Line *l0=new RS_Line{e1->getParent(), {{-a1,0.}, {a1,0.}}}; ret= getIntersectionEllipseLine(l0, e02); ret.rotate(-shifta1); ret.move(-shiftc1); return ret; } if( e02->getMinorRadius() < RS_TOLERANCE || e02 -> getRatio()< RS_TOLERANCE) { // treate e02 as a line RS_Line *l0=new RS_Line{e1->getParent(), {{-a2,0.}, {a2,0.}}}; l0->rotate({0.,0.}, e02->getAngle()); l0->move(e02->getCenter()); ret= getIntersectionEllipseLine(l0, e01); ret.rotate(-shifta1); ret.move(-shiftc1); return ret; } //ellipse01 equation: // x^2/(a1^2) + y^2/(b1^2) - 1 =0 double t2= - e02->getAngle(); //ellipse2 equation: // ( (x - u) cos(t) - (y - v) sin(t))^2/a^2 + ( (x - u) sin(t) + (y-v) cos(t))^2/b^2 =1 // ( cos^2/a^2 + sin^2/b^2) x^2 + // ( sin^2/a^2 + cos^2/b^2) y^2 + // 2 sin cos (1/b^2 - 1/a^2) x y + // ( ( 2 v sin cos - 2 u cos^2)/a^2 - ( 2v sin cos + 2 u sin^2)/b^2) x + // ( ( 2 u sin cos - 2 v sin^2)/a^2 - ( 2u sin cos + 2 v cos^2)/b^2) y + // (u cos - v sin)^2/a^2 + (u sin + v cos)^2/b^2 -1 =0 // detect whether any ellipse radius is zero double cs=cos(t2),si=sin(t2); double ucs=x2*cs,usi=x2*si, vcs=y2*cs,vsi=y2*si; double cs2=cs*cs,si2=1-cs2; double tcssi=2.*cs*si; double ia2=1./(a2*a2),ib2=1./(b2*b2); std::vector<double> m(0,0.); m.push_back( 1./(a1*a1)); //ma000 m.push_back( 1./(b1*b1)); //ma011 m.push_back(cs2*ia2 + si2*ib2); //ma100 m.push_back(cs*si*(ib2 - ia2)); //ma101 m.push_back(si2*ia2 + cs2*ib2); //ma111 m.push_back(( y2*tcssi - 2.*x2*cs2)*ia2 - ( y2*tcssi+2*x2*si2)*ib2); //mb10 m.push_back( ( x2*tcssi - 2.*y2*si2)*ia2 - ( x2*tcssi+2*y2*cs2)*ib2); //mb11 m.push_back((ucs - vsi)*(ucs-vsi)*ia2+(usi+vcs)*(usi+vcs)*ib2 -1.); //mc1 auto vs0=RS_Math::simultaneousQuadraticSolver(m); shifta1 = - shifta1; shiftc1 = - shiftc1; for(RS_Vector vp: vs0){ vp.rotate(shifta1); vp.move(shiftc1); ret.push_back(vp); } return ret; }
RS_VectorSolutions LC_Quadratic::getIntersection(const LC_Quadratic& l1, const LC_Quadratic& l2) { RS_VectorSolutions ret; if( l1.isValid()==false || l2.isValid()==false ) { // DEBUG_HEADER(); // std::cout<<l1<<std::endl; // std::cout<<l2<<std::endl; return ret; } auto p1=&l1; auto p2=&l2; if(p1->isQuadratic()==false){ std::swap(p1,p2); } // DEBUG_HEADER(); // std::cout<<*p1<<std::endl; // std::cout<<*p2<<std::endl; if(p1->isQuadratic()==false){ //two lines QVector<QVector<double> > ce(2,QVector<double>(3,0.)); ce[0][0]=p1->m_vLinear(0); ce[0][1]=p1->m_vLinear(1); ce[0][2]=-p1->m_dConst; ce[1][0]=p2->m_vLinear(0); ce[1][1]=p2->m_vLinear(1); ce[1][2]=-p2->m_dConst; QVector<double> sn(2,0.); if(RS_Math::linearSolver(ce,sn)){ ret.push_back(RS_Vector(sn[0],sn[1])); } return ret; } if(p2->isQuadratic()==false){ //one line, one quadratic //avoid division by zero if(fabs(p2->m_vLinear(0))<fabs(p2->m_vLinear(1))){ return getIntersection(p1->flipXY(),p2->flipXY()).flipXY(); } } if( fabs(p1->m_mQuad(0,0))<RS_TOLERANCE && fabs(p1->m_mQuad(0,1))<RS_TOLERANCE && fabs(p2->m_mQuad(0,0))<RS_TOLERANCE && fabs(p2->m_mQuad(0,1))<RS_TOLERANCE ){ if(fabs(p1->m_mQuad(1,1))<RS_TOLERANCE && fabs(p2->m_mQuad(1,1))<RS_TOLERANCE){ //linear std::vector<double> ce(0); ce.push_back(p1->m_vLinear(0)); ce.push_back(p1->m_vLinear(1)); ce.push_back(p1->m_dConst); LC_Quadratic lc10(ce); ce.clear(); ce.push_back(p2->m_vLinear(0)); ce.push_back(p2->m_vLinear(1)); ce.push_back(p2->m_dConst); LC_Quadratic lc11(ce); return getIntersection(lc10,lc11); } return getIntersection(p1->flipXY(),p2->flipXY()).flipXY(); } std::vector<std::vector<double> > ce(0); ce.push_back(p1->getCoefficients()); ce.push_back(p2->getCoefficients()); if(RS_DEBUG->getLevel()>=RS_Debug::D_INFORMATIONAL){ DEBUG_HEADER(); std::cout<<*p1<<std::endl; std::cout<<*p2<<std::endl; } return RS_Math::simultaneousQuadraticSolverFull(ce); }
void RS_Line::draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) { if (painter==NULL || view==NULL) { return; } //only draw the visible portion of line QVector<RS_Vector> endPoints(0); RS_Vector vpMin(view->toGraph(0,view->getHeight())); RS_Vector vpMax(view->toGraph(view->getWidth(),0)); QPolygonF visualBox(QRectF(vpMin.x,vpMin.y,vpMax.x-vpMin.x, vpMax.y-vpMin.y)); if( getStartpoint().isInWindowOrdered(vpMin, vpMax) ) endPoints<<getStartpoint(); if( getEndpoint().isInWindowOrdered(vpMin, vpMax) ) endPoints<<getEndpoint(); if(endPoints.size()<2){ QVector<RS_Vector> vertex; for(unsigned short i=0;i<4;i++){ const QPointF& vp(visualBox.at(i)); vertex<<RS_Vector(vp.x(),vp.y()); } for(unsigned short i=0;i<4;i++){ RS_Line line(NULL,RS_LineData(vertex.at(i),vertex.at((i+1)%4))); auto&& vpIts=RS_Information::getIntersection(static_cast<RS_Entity*>(this), &line, true); if( vpIts.size()==0) continue; endPoints<<vpIts.get(0); } } if(endPoints.size()<2) return; if( (endPoints[0] - getStartpoint()).squared() > (endPoints[1] - getStartpoint()).squared() ) std::swap(endPoints[0],endPoints[1]); RS_Vector pStart(view->toGui(endPoints.at(0))); RS_Vector pEnd(view->toGui(endPoints.at(1))); // std::cout<<"draw line: "<<pStart<<" to "<<pEnd<<std::endl; RS_Vector direction=pEnd-pStart; if(isHelpLayer(true) && direction.squared() > RS_TOLERANCE){ //extend line on a help layer to fill the whole view RS_Vector lb(0,0); RS_Vector rt(view->getWidth(),view->getHeight()); QList<RS_Vector> rect; rect<<lb<<RS_Vector(rt.x,lb.y); rect<<rt<<RS_Vector(lb.x,rt.y); rect<<lb; RS_VectorSolutions sol; RS_Line dLine(pStart,pEnd); for(int i=0;i<4;i++){ RS_Line bLine(rect.at(i),rect.at(i+1)); RS_VectorSolutions sol2=RS_Information::getIntersection(&bLine, &dLine); if( sol2.getNumber()>0 && bLine.isPointOnEntity(sol2.get(0),RS_TOLERANCE)) { sol.push_back(sol2.get(0)); } } switch(sol.getNumber()){ case 2: pStart=sol.get(0); pEnd=sol.get(1); break; case 3: case 4: pStart=sol.get(0); pEnd=sol.get(2); break; default: return; } direction=pEnd-pStart; } double length=direction.magnitude(); patternOffset -= length; if (( !isSelected() && ( getPen().getLineType()==RS2::SolidLine || view->getDrawingMode()==RS2::ModePreview)) ) { //if length is too small, attempt to draw the line, could be a potential bug painter->drawLine(pStart,pEnd); return; } // double styleFactor = getStyleFactor(view); // Pattern: RS_LineTypePattern* pat; if (isSelected()) { // styleFactor=1.; pat = &patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (pat==NULL) { // patternOffset -= length; RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern"); painter->drawLine(pStart,pEnd); return; } // patternOffset = remainder(patternOffset - length-0.5*pat->totalLength,pat->totalLength)+0.5*pat->totalLength; if(length<=RS_TOLERANCE){ painter->drawLine(pStart,pEnd); return; //avoid division by zero } direction/=length; //cos(angle), sin(angle) // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); // index counter int i; // pattern segment length: double patternSegmentLength = pat->totalLength; // create pattern: RS_Vector* dp=new RS_Vector[pat->num > 0?pat->num:0]; double* ds=new double[pat->num > 0?pat->num:0]; if (pat->num >0 ){ double dpmm=static_cast<RS_PainterQt*>(painter)->getDpmm(); for (i=0; i<pat->num; ++i) { // ds[j]=pat->pattern[i] * styleFactor; //fixme, styleFactor support needed ds[i]=dpmm*pat->pattern[i]; if( fabs(ds[i]) < 1. ) ds[i] = (ds[i]>=0.)?1.:-1.; dp[i] = direction*fabs(ds[i]); } }else { delete[] dp; delete[] ds; RS_DEBUG->print(RS_Debug::D_WARNING,"invalid line pattern for line, draw solid line instread"); painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); return; } double total= remainder(patternOffset-0.5*patternSegmentLength,patternSegmentLength) -0.5*patternSegmentLength; // double total= patternOffset-patternSegmentLength; RS_Vector p1,p2,p3; RS_Vector curP(pStart+direction*total); double t2; for(int j=0;total<length;j=(j+1)%i) { // line segment (otherwise space segment) t2=total+fabs(ds[j]); p3=curP+dp[j]; if (ds[j]>0.0 && t2 > 0.0) { // drop the whole pattern segment line, for ds[i]<0: // trim end points of pattern segment line to line p1 =(total > -0.5)? curP:pStart; p2 =(t2<length+0.5)?p3:pEnd; painter->drawLine(p1,p2); } total=t2; curP=p3; } delete[] dp; delete[] ds; }
void RS_Line::draw(RS_Painter* painter, RS_GraphicView* view, double& patternOffset) { if (painter==NULL || view==NULL) { return; } //visible in grahic view if(isVisibleInWindow(view)==false) return; RS_Vector pStart(view->toGui(getStartpoint())); RS_Vector pEnd(view->toGui(getEndpoint())); // std::cout<<"draw line: "<<pStart<<" to "<<pEnd<<std::endl; RS_Vector direction=pEnd-pStart; if(isHelpLayer(true) && direction.squared() > RS_TOLERANCE){ //extend line on a help layer to fill the whole view RS_Vector lb(0,0); RS_Vector rt(view->getWidth(),view->getHeight()); QList<RS_Vector> rect; rect<<lb<<RS_Vector(rt.x,lb.y); rect<<rt<<RS_Vector(lb.x,rt.y); rect<<lb; RS_VectorSolutions sol; RS_Line dLine(pStart,pEnd); for(int i=0;i<4;i++){ RS_Line bLine(rect.at(i),rect.at(i+1)); RS_VectorSolutions sol2=RS_Information::getIntersection(&bLine, &dLine); if( sol2.getNumber()>0 && bLine.isPointOnEntity(sol2.get(0),RS_TOLERANCE)) { sol.push_back(sol2.get(0)); } } switch(sol.getNumber()){ case 2: pStart=sol.get(0); pEnd=sol.get(1); break; case 3: case 4: pStart=sol.get(0); pEnd=sol.get(2); break; default: return; } direction=pEnd-pStart; } double length=direction.magnitude(); patternOffset -= length; if (( !isSelected() && ( getPen().getLineType()==RS2::SolidLine || view->getDrawingMode()==RS2::ModePreview)) ) { //if length is too small, attempt to draw the line, could be a potential bug painter->drawLine(pStart,pEnd); return; } // double styleFactor = getStyleFactor(view); // Pattern: RS_LineTypePattern* pat; if (isSelected()) { // styleFactor=1.; pat = &patternSelected; } else { pat = view->getPattern(getPen().getLineType()); } if (pat==NULL) { // patternOffset -= length; RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Line::draw: Invalid line pattern"); painter->drawLine(pStart,pEnd); return; } // patternOffset = remainder(patternOffset - length-0.5*pat->totalLength,pat->totalLength)+0.5*pat->totalLength; if(length<=RS_TOLERANCE){ painter->drawLine(pStart,pEnd); return; //avoid division by zero } direction/=length; //cos(angle), sin(angle) // Pen to draw pattern is always solid: RS_Pen pen = painter->getPen(); pen.setLineType(RS2::SolidLine); painter->setPen(pen); // index counter int i; // pattern segment length: double patternSegmentLength = pat->totalLength; // create pattern: RS_Vector* dp=new RS_Vector[pat->num > 0?pat->num:0]; double* ds=new double[pat->num > 0?pat->num:0]; if (pat->num >0 ){ for (i=0; i<pat->num; ++i) { // ds[j]=pat->pattern[i] * styleFactor; //fixme, styleFactor support needed ds[i]=pat->pattern[i] ; dp[i] = direction*fabs(ds[i]); } }else { delete[] dp; delete[] ds; RS_DEBUG->print(RS_Debug::D_WARNING,"invalid line pattern for line, draw solid line instread"); painter->drawLine(view->toGui(getStartpoint()), view->toGui(getEndpoint())); return; } double total= remainder(patternOffset-0.5*patternSegmentLength,patternSegmentLength) -0.5*patternSegmentLength; // double total= patternOffset-patternSegmentLength; RS_Vector p1,p2,p3; RS_Vector curP(pStart+direction*total); double t2; for(int j=0;total<length;j=(j+1)%i) { // line segment (otherwise space segment) t2=total+fabs(ds[j]); p3=curP+dp[j]; if (ds[j]>0.0 && t2 > 0.0) { // drop the whole pattern segment line, for ds[i]<0: // trim end points of pattern segment line to line p1 =(total > -0.5)? curP:pStart; p2 =(t2<length+0.5)?p3:pEnd; painter->drawLine(p1,p2); } total=t2; curP=p3; } delete[] dp; delete[] ds; }
RS_VectorSolutions RS_Information::getIntersectionEllipseEllipse(RS_Ellipse* e1, RS_Ellipse* e2) { RS_VectorSolutions ret; if (e1==NULL || e2==NULL ) { return ret; } if ( (e1->getCenter() - e2 -> getCenter() ).magnitude() < RS_TOLERANCE && ( e1->getMajorP() - e2 ->getMajorP()).magnitude() < RS_TOLERANCE && fabs(e1->getMajorRadius() - e2 ->getMajorRadius()) < RS_TOLERANCE && fabs(e1->getMinorRadius() - e2 ->getMinorRadius()) < RS_TOLERANCE ) { // overlapped ellipses, do not do overlap return ret; } RS_Ellipse *e01= ( RS_Ellipse *) e1->clone(); if( e01->getMajorRadius() < e01->getMinorRadius() ) e01->switchMajorMinor(); RS_Ellipse *e02= ( RS_Ellipse *) e2->clone(); if( e02->getMajorRadius() < e02->getMinorRadius() ) e02->switchMajorMinor(); //transform ellipse2 to ellipse1's coordinates RS_Vector shiftc1=- e01->getCenter(); double shifta1=-e01->getAngle(); e02->move(shiftc1); e02->rotate(shifta1); RS_Vector majorP2=e02->getMajorP(); double a1=e01->getMajorRadius(); double b1=e01->getMinorRadius(); double x2=e02->getCenter().x, y2=e02->getCenter().y; double a2=e02->getMajorRadius(); double b2=e02->getMinorRadius(); if( e01->getMinorRadius() < RS_TOLERANCE || e01 -> getRatio()< RS_TOLERANCE) { // treate e01 as a line RS_LineData ldata0(RS_Vector(-a1,0.),RS_Vector(a1,0.)); RS_Line *l0=new RS_Line(e1->getParent(),ldata0); ret= getIntersectionLineEllipse(l0, e02); ret.rotate(-shifta1); ret.move(-shiftc1); return ret; } if( e02->getMinorRadius() < RS_TOLERANCE || e02 -> getRatio()< RS_TOLERANCE) { // treate e02 as a line RS_LineData ldata0(RS_Vector(-a2,0.),RS_Vector(a2,0.)); RS_Line *l0=new RS_Line(e1->getParent(),ldata0); l0->rotate(RS_Vector(0.,0.),e02->getAngle()); l0->move(e02->getCenter()); ret= getIntersectionLineEllipse(l0, e01); ret.rotate(-shifta1); ret.move(-shiftc1); return ret; } //ellipse01 equation: // x^2/(a1^2) + y^2/(b1^2) - 1 =0 double t2= - e02->getAngle(); //ellipse2 equation: // ( (x - u) cos(t) - (y - v) sin(t))^2/a^2 + ( (x - u) sin(t) + (y-v) cos(t))^2/b^2 =1 // ( cos^2/a^2 + sin^2/b^2) x^2 + // ( sin^2/a^2 + cos^2/b^2) y^2 + // 2 sin cos (1/b^2 - 1/a^2) x y + // ( ( 2 v sin cos - 2 u cos^2)/a^2 - ( 2v sin cos + 2 u sin^2)/b^2) x + // ( ( 2 u sin cos - 2 v sin^2)/a^2 - ( 2u sin cos + 2 v cos^2)/b^2) y + // (u cos - v sin)^2/a^2 + (u sin + v cos)^2/b^2 -1 =0 // detect whether any ellipse radius is zero double cs=cos(t2),si=sin(t2); double ucs=x2*cs,usi=x2*si, vcs=y2*cs,vsi=y2*si; double cs2=cs*cs,si2=1-cs2; double tcssi=2.*cs*si; double ia2=1./(a2*a2),ib2=1./(b2*b2); // std::cout<<"e1: x^2/("<<a1<<")^2+y^2/("<<b1<<")^2-1 =0\n"; // std::cout<<"e2: ( (x-("<<x2<<"))*("<<cs<<")-(y-("<<y2<<"))*("<<si<<"))^2/"<<a2<<"^2+( ( x - ("<<x2<<"))*("<<si<<")+(y-("<<y2<<"))*("<<cs<<"))^2/"<<b2<<"^2 -1 =0\n"; double mc1=(ucs - vsi)*(ucs-vsi)*ia2+(usi+vcs)*(usi+vcs)*ib2 -1.; double mb10= ( y2*tcssi - 2.*x2*cs2)*ia2 - ( y2*tcssi+2*x2*si2)*ib2; //x double mb11= ( x2*tcssi - 2.*y2*si2)*ia2 - ( x2*tcssi+2*y2*cs2)*ib2; //y double ma100= cs2*ia2 + si2*ib2; // x^2 double ma101= cs*si*(ib2 - ia2); // xy term is 2*ma101*x*y double ma111= si2*ia2 + cs2*ib2; // y^2 double ma000= 1./(a1*a1),ma011=1./(b1*b1); // std::cout<<"simplified e1: "<<ma000<<"*x^2 + "<<ma011<<"*y^2 -1 =0\n"; // std::cout<<"simplified e2: "<<ma100<<"*x^2 + 2*("<<ma101<<")*x*y + "<<ma111<<"*y^2 "<<" + ("<<mb10<<")*x + ("<<mb11<<")*y + ("<<mc1<<") =0\n"; // construct the Bezout determinant double v0=2.*ma000*ma101; double v2=ma000*mb10; double v3=ma000*mb11; double v4=ma000*mc1+ma100; //double v5= 2.*ma101*ma011; //double v6= ma000*ma111; //double v7= 2.*ma101; double v8= 2.*ma011*mb10; //double v9= ma100*ma011; double v1=ma000*ma111-ma100*ma011; //double v1= v6 - v9; double u0 = v4*v4-v2*mb10; double u1 = 2.*(v3*v4-v0*mb10); double u2 = 2.*(v4*v1-ma101*v0)+v3*v3+0.5*v2*v8; double u3 = v0*v8+2.*v3*v1; double u4 = v1*v1+2.*ma101*ma011*v0; //std::cout<<"u0="<<u0<<"\tu1="<<u1<<"\tu2="<<u2<<"\tu3="<<u3<<"\tu4="<<u4<<std::endl; //std::cout<<"("<<u4<<")*x^4+("<<u3<<")*x^3+("<<u2<<")*x^2+("<<u1<<")*x+("<<u0<<")=0\n"; double ce[4]; double roots[4]; unsigned int counts=0; if ( fabs(u4) < 1.0e-75) { // this should not happen if ( fabs(u3) < 1.0e-75) { // this should not happen if ( fabs(u2) < 1.0e-75) { // this should not happen if( fabs(u1) > 1.0e-75) { counts=1; roots[0]=-u0/u1; } else { // can not determine y. this means overlapped, but overlap should have been detected before, therefore return empty set return ret; } } else { ce[0]=u1/u2; ce[1]=u0/u2; //std::cout<<"ce[2]={ "<<ce[0]<<' '<<ce[1]<<" }\n"; counts=RS_Math::quadraticSolver(ce,roots); } } else { ce[0]=u2/u3; ce[1]=u1/u3; ce[2]=u0/u3; //std::cout<<"ce[3]={ "<<ce[0]<<' '<<ce[1]<<' '<<ce[2]<<" }\n"; counts=RS_Math::cubicSolver(ce,roots); } } else { ce[0]=u3/u4; ce[1]=u2/u4; ce[2]=u1/u4; ce[3]=u0/u4; //std::cout<<"ce[4]={ "<<ce[0]<<' '<<ce[1]<<' '<<ce[2]<<' '<<ce[3]<<" }\n"; counts=RS_Math::quarticSolver(ce,roots); } // std::cout<<"Equation for y: y^4"; // for(int i=3; i>=0; i--) { // std::cout<<"+("<<ce[3-i]<<")"; // if ( i ) { // std::cout<<"*y^"<<i; // }else { // std::cout<<" ==0\n"; // } // } if (! counts ) { // no intersection found return ret; } // std::cout<<"counts="<<counts<<": "; // for(unsigned int i=0;i<counts;i++){ // std::cout<<roots[i]<<" "; // } // std::cout<<std::endl; RS_VectorSolutions vs0; unsigned int ivs0=0; for(unsigned int i=0; i<counts; i++) { double y=roots[i]; //double x=(ma100*(ma011*y*y-1.)-ma000*(ma111*y*y+mb11*y+mc1))/(ma000*(2.*ma101*y+mb11)); double x,d=v0*y+v2; // std::cout<<"d= "<<d<<std::endl; if( fabs(d)>RS_TOLERANCE*sqrt(RS_TOLERANCE)) {//whether there's x^1 term in bezout determinant x=-((v1*y+v3)*y+v4 )/d; if(vs0.getClosestDistance(RS_Vector(x,y),ivs0)>RS_TOLERANCE) vs0.push_back(RS_Vector(x,y)); } else { // no x^1 term, have to use x^2 term, then, have to check plus/minus sqrt x=a1*sqrt(1-y*y*ma011); if(vs0.getClosestDistance(RS_Vector(x,y),ivs0)>RS_TOLERANCE) vs0.push_back(RS_Vector(x,y)); x=-x; if(vs0.getClosestDistance(RS_Vector(x,y),ivs0)>RS_TOLERANCE) vs0.push_back(RS_Vector(x,y)); } //std::cout<<"eq1="<<ma000*x*x+ma011*y*y-1.<<std::endl; //std::cout<<"eq2="<<ma100*x*x + 2.*ma101*x*y+ma111*y*y+mb10*x+mb11*y+mc1<<std::endl; // if ( // fabs(ma100*x*x + 2.*ma101*x*y+ma111*y*y+mb10*x+mb11*y+mc1)< RS_TOLERANCE // ) {//found // vs0.set(ivs0++, RS_Vector(x,y)); // } } // for(unsigned int j=0; j<vs0.getNumber(); j++) { // std::cout<<" ( "<<vs0.get(j).x<<" , "<<vs0.get(j).y<<" ) "; // } // std::cout<<std::endl; // std::cout<<"counts= "<<counts<<"\tFound "<<ivs0<<" EllipseEllipse intersections\n"; //ret.alloc(ivs0); shifta1 = - shifta1; shiftc1 = - shiftc1; for(unsigned i=0; i<vs0.getNumber(); i++) { RS_Vector vp=vs0.get(i); vp.rotate(shifta1); vp.move(shiftc1); ret.push_back(vp); } return ret; }
/** * 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; }