bool RS_ActionDrawCircleTan3::preparePreview(){ if(getStatus() != SetCenter || valid==false) { valid=false; return false; } //find the nearest circle size_t index=candidates.size(); double dist=RS_MAXDOUBLE*RS_MAXDOUBLE; for(size_t i=0;i<candidates.size();++i){ preview->addEntity(new RS_Point(preview.get(), RS_PointData(candidates.at(i)->center))); double d; RS_Circle(nullptr, *candidates.at(i)).getNearestPointOnEntity(coord,false,&d); double dCenter=coord.distanceTo(candidates.at(i)->center); d=std::min(d,dCenter); if(d<dist){ dist=d; index=i; } } if( index<candidates.size()){ cData=candidates.at(index); valid=true; }else{ valid=false; } return valid; }
QList<RS_Circle> RS_Circle::createTan3(const QVector<RS_AtomicEntity*>& circles) { QList<RS_Circle> ret; if(circles.size()!=3) return ret; QList<RS_Circle> cs; for(unsigned short i=0;i<3;i++){ cs<<RS_Circle(NULL,RS_CircleData(circles.at(i)->getCenter(),circles.at(i)->getRadius())); } unsigned short flags=0; do{ ret.append(solveAppolloniusSingle(cs)); flags++; unsigned short j=0; for(unsigned short i=1u;i<=4u;i<<=1){ if(flags & i) { cs[j].setRadius( - fabs(cs[j].getRadius())); }else{ cs[j].setRadius( fabs(cs[j].getRadius())); } j++; } }while(flags<8u); // std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl; // std::cout<<"before testing, ret.size()="<<ret.size()<<std::endl; for(int i=0;i<ret.size();){ if(ret[i].testTan3(circles) == false) { ret.erase(ret.begin()+i); }else{ i++; } } // std::cout<<"after testing, ret.size()="<<ret.size()<<std::endl; return ret; }
std::vector<RS_Circle> RS_Circle::createTan3(const std::vector<RS_AtomicEntity*>& circles) { std::vector<RS_Circle> ret; if(circles.size()!=3) return ret; std::vector<RS_Circle> cs; for(auto c: circles){ cs.emplace_back(RS_Circle(nullptr, {c->getCenter(),c->getRadius()})); } unsigned short flags=0; do{ for(unsigned short j=0u;j<3u;++j){ if(flags & (1u<<j)) { cs[j].setRadius( - fabs(cs[j].getRadius())); }else{ cs[j].setRadius( fabs(cs[j].getRadius())); } } // RS_DEBUG->print(RS_Debug::D_ERROR, "flags=%d\n",flags); auto list=solveAppolloniusSingle(cs); if(list.size()>=1){ for(RS_Circle& c0: list){ bool addNew=true; for(RS_Circle& c: ret){ if((c0.getCenter()-c.getCenter()).squared()<RS_TOLERANCE15 && fabs(c0.getRadius() - c.getRadius())<RS_TOLERANCE){ addNew=false; break; } } if(addNew) ret.push_back(c0); } } }while(++flags<8u); // std::cout<<__FILE__<<" : "<<__func__<<" : line "<<__LINE__<<std::endl; // std::cout<<"before testing, ret.size()="<<ret.size()<<std::endl; for(size_t i=0;i<ret.size();){ if(ret[i].testTan3(circles) == false) { ret.erase(ret.begin()+i); }else{ ++i; } } // DEBUG_HEADER // std::cout<<"after testing, ret.size()="<<ret.size()<<std::endl; return ret; }
bool RS_ActionDrawCircleTan3::getData(){ if(getStatus() != SetCircle3) return false; //find the nearest circle int i=0; for(i=0;i<circles.size();i++) if(circles[i]->rtti() == RS2::EntityLine) break; candidates.clear(); if(i<circles.size() && circles[i]->rtti() == RS2::EntityLine){ LC_Quadratic lc0(circles[i],circles[(i+1)%3]); LC_Quadratic lc1(circles[i],circles[(i+2)%3]); auto&& sol=LC_Quadratic::getIntersection(lc0,lc1); double d; //line passes circle center, need a second parabola as the image of the line for(int j=1;j<=2;j++){ if(circles[(i+j)%3]->rtti() == RS2::EntityCircle){ circles[i]->getNearestPointOnEntity(circles[(i+j)%3]->getCenter(), false,&d); if(d<RS_TOLERANCE) { LC_Quadratic lc2(circles[i],circles[(i+j)%3], true); sol.appendTo(LC_Quadratic::getIntersection(lc2,lc1)); } } } for(size_t j=0;j<sol.size();j++){ circles[i]->getNearestPointOnEntity(sol[j],false,&d); RS_CircleData data(sol[j],d); // DEBUG_HEADER(); // std::cout<<sol[j]<<" r="<<d<<std::endl; if(circles[(i+1)%3]->isTangent(data)==false) continue; if(circles[(i+2)%3]->isTangent(data)==false) continue; candidates<<RS_Circle(NULL,data); } }else{ RS_Circle c(NULL,cData); candidates=c.createTan3(circles); } valid = ( candidates.size() >0); return valid; }
bool RS_ActionDrawCircleTan3::getData(){ if(getStatus() != SetCircle3) return false; //find the nearest circle int i=0; for(;i<circles.size();++i) if(circles[i]->rtti() == RS2::EntityLine) break; candidates.clear(); const int i1=(i+1)%3; const int i2=(i+2)%3; if(i<circles.size() && circles[i]->rtti() == RS2::EntityLine){ LC_Quadratic lc0(circles[i],circles[i1],false); LC_Quadratic lc01(circles[i],circles[i1],true); LC_Quadratic lc1; RS_VectorSolutions sol; //detect degenerate case two circles with the same radius if(circles[i1]->rtti()== RS2::EntityCircle && circles[i2]->rtti()== RS2::EntityCircle ){ RS_Circle* c1=static_cast<RS_Circle*>(circles[i1]); RS_Circle* c2=static_cast<RS_Circle*>(circles[i2]); if(fabs(fabs(c1->getRadius())-fabs(c2->getRadius()))<RS_TOLERANCE){ //degenerate const RS_Vector p0=(c1->getCenter()+c2->getCenter())*0.5; const RS_Vector p1=p0 + (c1->getCenter() - p0).rotate(0.5*M_PI); lc1=RS_Line(NULL, RS_LineData(p0,p1 )).getQuadratic(); sol=LC_Quadratic::getIntersection(lc0,lc1); sol.appendTo(LC_Quadratic::getIntersection(lc01,lc1)); lc1=RS_Line(NULL, RS_LineData(c1->getCenter(),c1->getCenter())).getQuadratic(); sol.appendTo(LC_Quadratic::getIntersection(lc0,lc1)); sol.appendTo(LC_Quadratic::getIntersection(lc01,lc1)); } } if(sol.size()==0) { switch(circles[i2]->rtti()){ case RS2::EntityCircle: lc1=LC_Quadratic(circles[i],circles[i2], true); sol.appendTo(LC_Quadratic::getIntersection(lc01,lc1)); if(circles[i1]->rtti()== RS2::EntityCircle ) sol.appendTo(LC_Quadratic::getIntersection(lc01,lc1)); //there's no break, because the default part would be run for circles as well default: lc1=LC_Quadratic(circles[i],circles[i2]); sol.appendTo(LC_Quadratic::getIntersection(lc01,lc1)); if(circles[i1]->rtti()== RS2::EntityCircle ) sol.appendTo(LC_Quadratic::getIntersection(lc01,lc1)); } } double d; //line passes circle center, need a second parabola as the image of the line for(int j=1;j<=2;j++){ if(circles[(i+j)%3]->rtti() == RS2::EntityCircle){ circles[i]->getNearestPointOnEntity(circles[(i+j)%3]->getCenter(), false,&d); if(d<RS_TOLERANCE) { LC_Quadratic lc2(circles[i],circles[(i+j)%3], true); sol.appendTo(LC_Quadratic::getIntersection(lc2,lc1)); } } } //clean up duplicate and invalid RS_VectorSolutions sol1; for(size_t j=0; j<sol.size(); ++j){ const RS_Vector&& vp=sol.at(j); if(vp.magnitude()>RS_MAXDOUBLE) continue; if(sol1.size()) if(sol1.getClosestDistance(vp)<RS_TOLERANCE) continue; sol1.push_back(vp); } for(size_t j=0;j<sol1.size();j++){ circles[i]->getNearestPointOnEntity(sol1[j],false,&d); RS_CircleData data(sol1[j],d); if(circles[(i+1)%3]->isTangent(data)==false) continue; if(circles[(i+2)%3]->isTangent(data)==false) continue; candidates<<RS_Circle(NULL,data); } }else{ RS_Circle c(NULL,cData); candidates=c.createTan3(circles); } valid = ( candidates.size() >0); return valid; }
/** solve one of the eight Appollonius Equations | Cx - Ci|^2=(Rx+Ri)^2 with Cx the center of the common tangent circle, Rx the radius. Ci and Ri are the Center and radius of the i-th existing circle **/ QList<RS_Circle> RS_Circle::solveAppolloniusSingle(const QList<RS_Circle>& circles) { // std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl; // for(int i=0;i<circles.size();i++){ //std::cout<<"i="<<i<<"\t center="<<circles[i].getCenter()<<"\tr="<<circles[i].getRadius()<<std::endl; // } QList<RS_Circle> ret; QList<RS_Vector> centers; QList<double> radii; for(size_t i=0;i<3;i++){ if(circles[i].getCenter().valid==false) return ret; centers.push_back(circles[i].getCenter()); radii.push_back(fabs(circles[i].getRadius())); } /** form the linear equation to solve center in radius **/ QVector<QVector<double> > mat(2,QVector<double>(3,0.)); mat[0][0]=centers[2].x - centers[0].x; mat[0][1]=centers[2].y - centers[0].y; mat[1][0]=centers[2].x - centers[1].x; mat[1][1]=centers[2].y - centers[1].y; if(fabs(mat[0][0]*mat[1][1] - mat[0][1]*mat[1][0])<RS_TOLERANCE*RS_TOLERANCE){ // DEBUG_HEADER(); // std::cout<<"The provided circles are in a line, not common tangent circle"<<std::endl; size_t i0=0; if( centers[0].distanceTo(centers[1]) <= RS_TOLERANCE || centers[0].distanceTo(centers[2]) <= RS_TOLERANCE) i0 = 1; LC_Quadratic lc0(& (circles[i0]), & (circles[(i0+1)%3])); LC_Quadratic lc1(& (circles[i0]), & (circles[(i0+2)%3])); auto&& c0 = LC_Quadratic::getIntersection(lc0, lc1); // qDebug()<<"c0.size()="<<c0.size(); for(size_t i=0; i<c0.size(); i++){ const double dc = c0[i].distanceTo(centers[i0]); ret<<RS_Circle(NULL, RS_CircleData(c0[i], fabs(dc - radii[i0]))); if( dc > radii[i0]) { ret<<RS_Circle(NULL, RS_CircleData(c0[i], dc + radii[i0])); } } return ret; } // r^0 term mat[0][2]=0.5*(centers[2].squared()-centers[0].squared()+radii[0]*radii[0]-radii[2]*radii[2]); mat[1][2]=0.5*(centers[2].squared()-centers[1].squared()+radii[1]*radii[1]-radii[2]*radii[2]); std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl; for(unsigned short i=0;i<=1;i++){ std::cout<<"eqs P:"<<i<<" : "<<mat[i][0]<<"*x + "<<mat[i][1]<<"*y = "<<mat[i][2]<<std::endl; } // QVector<QVector<double> > sm(2,QVector<double>(2,0.)); QVector<double> sm(2,0.); if(RS_Math::linearSolver(mat,sm)==false){ return ret; } RS_Vector vp(sm[0],sm[1]); // std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl; // std::cout<<"vp="<<vp<<std::endl; // r term mat[0][2]= radii[0]-radii[2]; mat[1][2]= radii[1]-radii[2]; // for(unsigned short i=0;i<=1;i++){ // std::cout<<"eqs Q:"<<i<<" : "<<mat[i][0]<<"*x + "<<mat[i][1]<<"*y = "<<mat[i][2]<<std::endl; // } if(RS_Math::linearSolver(mat,sm)==false){ return ret; } RS_Vector vq(sm[0],sm[1]); // std::cout<<"vq="<<vq<<std::endl; //form quadratic equation for r RS_Vector dcp=vp-centers[0]; double a=vq.squared()-1.; if(fabs(a)<RS_TOLERANCE*1e-4) { return ret; } std::vector<double> ce(0,0.); ce.push_back(2.*(dcp.dotP(vq)-radii[0])/a); ce.push_back((dcp.squared()-radii[0]*radii[0])/a); std::vector<double>&& vr=RS_Math::quadraticSolver(ce); for(size_t i=0; i < vr.size();i++){ if(vr.at(i)<RS_TOLERANCE) continue; ret<<RS_Circle(NULL,RS_CircleData(vp+vq*vr.at(i),vr.at(i))); } // std::cout<<__FILE__<<" : "<<__FUNCTION__<<" : line "<<__LINE__<<std::endl; // std::cout<<"Found "<<ret.size()<<" solutions"<<std::endl; return ret; }