RS_Vector RS_Line::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity)const { if (entity) { *entity = const_cast<RS_Line*>(this); } RS_Vector direction = data.endpoint-data.startpoint; RS_Vector vpc=coord-data.startpoint; double a=direction.squared(); if( a < RS_TOLERANCE2) { //line too short vpc=getMiddlePoint(); }else{ //find projection on line const double t=RS_Vector::dotP(vpc,direction)/a; if( !isConstruction() && onEntity && ( t<=-RS_TOLERANCE || t>=1.+RS_TOLERANCE ) ){ // !( vpc.x>= minV.x && vpc.x <= maxV.x && vpc.y>= minV.y && vpc.y<=maxV.y) ) { //projection point not within range, find the nearest endpoint // std::cout<<"not within window, returning endpoints\n"; return getNearestEndpoint(coord,dist); } vpc = data.startpoint + direction*t; } if (dist) { *dist = vpc.distanceTo(coord); } return vpc; }
RS_Vector RS_Line::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity)const { if (entity!=NULL) { *entity = const_cast<RS_Line*>(this); } //std::cout<<"RS_Line::getNearestPointOnEntity():"<<coord<<std::endl; RS_Vector direction = data.endpoint-data.startpoint; RS_Vector vpc=coord-data.startpoint; double a=direction.squared(); if( a < RS_TOLERANCE*RS_TOLERANCE) { //line too short vpc=getMiddlePoint(); }else{ //find projection on line vpc = data.startpoint + direction*RS_Vector::dotP(vpc,direction)/a; if( !isHelpLayer() && onEntity && ! vpc.isInWindowOrdered(minV,maxV) ){ // !( vpc.x>= minV.x && vpc.x <= maxV.x && vpc.y>= minV.y && vpc.y<=maxV.y) ) { //projection point not within range, find the nearest endpoint // std::cout<<"not within window, returning endpoints\n"; return getNearestEndpoint(coord,dist); } } if (dist!=NULL) { *dist = vpc.distanceTo(coord); } return vpc; }
RS_Vector RS_Line::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity) { if (entity!=NULL) { *entity = this; } RS_Vector ret; RS_Vector vpl = data.endpoint-data.startpoint; double angle=vpl.angle(); double r=vpl.magnitude(); RS_Vector vpc=coord-data.startpoint; vpc.rotate(-angle); // rotate to use the line direction as x-axis if ( (vpc.x >= 0. && vpc.x <= r) || ! onEntity ) { //use the projection ret=RS_Vector(vpc.x,0.); ret.rotate(angle); ret += data.startpoint; } else {// onEntity=true and projection not within range, only have to check the endpoints ret=getNearestEndpoint(coord,dist); } if (dist!=NULL) { *dist = ret.distanceTo(coord); } return ret; }
RS_Vector RS_Line::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity) { if (entity!=NULL) { *entity = this; } RS_Vector ae = data.endpoint-data.startpoint; RS_Vector ea = data.startpoint-data.endpoint; RS_Vector ap = coord-data.startpoint; RS_Vector ep = coord-data.endpoint; if (ae.magnitude()<1.0e-6 || ea.magnitude()<1.0e-6) { if (dist!=NULL) { *dist = RS_MAXDOUBLE; } return RS_Vector(false); } // Orthogonal projection from both sides: RS_Vector ba = ae * RS_Vector::dotP(ae, ap) / (ae.magnitude()*ae.magnitude()); RS_Vector be = ea * RS_Vector::dotP(ea, ep) / (ea.magnitude()*ea.magnitude()); // Check if the projection is within this line: if (onEntity==true && (ba.magnitude()>ae.magnitude() || be.magnitude()>ea.magnitude())) { return getNearestEndpoint(coord, dist); } else { if (dist!=NULL) { *dist = coord.distanceTo(data.startpoint+ba); } return data.startpoint+ba; } }
RS_Vector RS_Line::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity) const { if (entity) { *entity = const_cast<RS_Line*>(this); } RS_Vector direction {data.endpoint - data.startpoint}; RS_Vector vpc {coord - data.startpoint}; double a {direction.squared()}; if( a < RS_TOLERANCE2) { //line too short vpc = getMiddlePoint(); } else { //find projection on line const double t {RS_Vector::dotP( vpc, direction) / a}; if( !isConstruction() && onEntity && ( t <= -RS_TOLERANCE || t >= 1. + RS_TOLERANCE ) ) { //projection point not within range, find the nearest endpoint return getNearestEndpoint( coord, dist); } vpc = data.startpoint + direction * t; } if (dist) { *dist = vpc.distanceTo( coord); } return vpc; }
/** * Rearranges the atomic entities in this container in a way that connected * entities are stored in the right order and direction. * Non-recoursive. Only affects atomic entities in this container. * * @retval true all contours were closed * @retval false at least one contour is not closed * to do: find closed contour by flood-fill */ bool RS_EntityContainer::optimizeContours() { // std::cout<<"RS_EntityContainer::optimizeContours: begin"<<std::endl; // DEBUG_HEADER // std::cout<<"loop with count()="<<count()<<std::endl; RS_DEBUG->print("RS_EntityContainer::optimizeContours"); RS_EntityContainer tmp; tmp.setAutoUpdateBorders(false); bool closed=true; /** accept all full circles **/ QList<RS_Entity*> enList; for(auto e1: entities){ if (!e1->isEdge() || e1->isContainer() ) { enList<<e1; continue; } //detect circles and whole ellipses switch(e1->rtti()){ case RS2::EntityEllipse: if(static_cast<RS_Ellipse*>(e1)->isEllipticArc()) continue; case RS2::EntityCircle: //directly detect circles, bug#3443277 tmp.addEntity(e1->clone()); enList<<e1; default: continue; } } // std::cout<<"RS_EntityContainer::optimizeContours: 1"<<std::endl; /** remove unsupported entities */ for(RS_Entity* it: enList) removeEntity(it); /** check and form a closed contour **/ // std::cout<<"RS_EntityContainer::optimizeContours: 2"<<std::endl; /** the first entity **/ RS_Entity* current(nullptr); if(count()>0) { current=entityAt(0)->clone(); tmp.addEntity(current); removeEntity(entityAt(0)); }else { if(tmp.count()==0) return false; } // std::cout<<"RS_EntityContainer::optimizeContours: 3"<<std::endl; RS_Vector vpStart; RS_Vector vpEnd; if(current){ vpStart=current->getStartpoint(); vpEnd=current->getEndpoint(); } RS_Entity* next(nullptr); // std::cout<<"RS_EntityContainer::optimizeContours: 4"<<std::endl; /** connect entities **/ const QString errMsg=QObject::tr("Hatch failed due to a gap=%1 between (%2, %3) and (%4, %5)"); while(count()>0){ double dist(0.); RS_Vector&& vpTmp=getNearestEndpoint(vpEnd,&dist,&next); if(dist>1e-8) { if(vpEnd.squaredTo(vpStart)<1e-8){ RS_Entity* e2=entityAt(0); tmp.addEntity(e2->clone()); vpStart=e2->getStartpoint(); vpEnd=e2->getEndpoint(); removeEntity(e2); continue; } QG_DIALOGFACTORY->commandMessage(errMsg.arg(dist).arg(vpTmp.x).arg(vpTmp.y).arg(vpEnd.x).arg(vpEnd.y)); closed=false; } if(next && closed){ //workaround if next is nullptr next->setProcessed(true); RS_Entity* eTmp = next->clone(); if(vpEnd.squaredTo(eTmp->getStartpoint())>vpEnd.squaredTo(eTmp->getEndpoint())) eTmp->revertDirection(); vpEnd=eTmp->getEndpoint(); tmp.addEntity(eTmp); removeEntity(next); } else { //workaround if next is nullptr // std::cout<<"RS_EntityContainer::optimizeContours: next is nullptr" <<std::endl; closed=false; //workaround if next is nullptr break; //workaround if next is nullptr } //workaround if next is nullptr } // DEBUG_HEADER if(vpEnd.valid && vpEnd.squaredTo(vpStart)>1e-8) { if(closed) QG_DIALOGFACTORY->commandMessage(errMsg.arg(vpEnd.distanceTo(vpStart)) .arg(vpStart.x).arg(vpStart.y).arg(vpEnd.x).arg(vpEnd.y)); closed=false; } // std::cout<<"RS_EntityContainer::optimizeContours: 5"<<std::endl; // add new sorted entities: for(auto en: tmp){ en->setProcessed(false); addEntity(en->clone()); } // std::cout<<"RS_EntityContainer::optimizeContours: 6"<<std::endl; RS_DEBUG->print("RS_EntityContainer::optimizeContours: OK"); // std::cout<<"RS_EntityContainer::optimizeContours: end: count()="<<count()<<std::endl; // std::cout<<"RS_EntityContainer::optimizeContours: closed="<<closed<<std::endl; return closed; }
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; }
double RS_Line::getDistanceToPoint(const RS_Vector& coord, RS_Entity** entity, RS2::ResolveLevel /*level*/, double /*solidDist*/) { RS_DEBUG->print("RS_Line::getDistanceToPoint"); if (entity!=NULL) { *entity = this; } // check endpoints first: double dist = coord.distanceTo(getStartpoint()); if (dist<1.0e-4) { RS_DEBUG->print("RS_Line::getDistanceToPoint: OK1"); return dist; } dist = coord.distanceTo(getEndpoint()); if (dist<1.0e-4) { RS_DEBUG->print("RS_Line::getDistanceToPoint: OK2"); return dist; } dist = RS_MAXDOUBLE; RS_Vector ae = data.endpoint-data.startpoint; RS_Vector ea = data.startpoint-data.endpoint; RS_Vector ap = coord-data.startpoint; RS_Vector ep = coord-data.endpoint; if (ae.magnitude()<1.0e-6 || ea.magnitude()<1.0e-6) { RS_DEBUG->print("RS_Line::getDistanceToPoint: OK2a"); return dist; } // Orthogonal projection from both sides: RS_Vector ba = ae * RS_Vector::dotP(ae, ap) / RS_Math::pow(ae.magnitude(), 2); RS_Vector be = ea * RS_Vector::dotP(ea, ep) / RS_Math::pow(ea.magnitude(), 2); // Check if the projection is outside this line: if (ba.magnitude()>ae.magnitude() || be.magnitude()>ea.magnitude()) { // return distance to endpoint getNearestEndpoint(coord, &dist); RS_DEBUG->print("RS_Line::getDistanceToPoint: OK3"); return dist; } //RS_DEBUG->print("ba: %f", ba.magnitude()); //RS_DEBUG->print("ae: %f", ae.magnitude()); RS_Vector cp = RS_Vector::crossP(ap, ae); dist = cp.magnitude() / ae.magnitude(); RS_DEBUG->print("RS_Line::getDistanceToPoint: OK4"); return dist; }
/** * @todo Implement this. */ RS_Vector RS_Solid::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity, double* dist, RS_Entity** entity)const { //first check if point is inside solid bool s1 = sign(data.corner[0], data.corner[1], coord); bool s2 = sign(data.corner[1], data.corner[2], coord); bool s3 = sign(data.corner[2], data.corner[0], coord); if ( (s1 == s2) && (s2 == s3) ) { if (dist!=nullptr) *dist = 0.0; return coord; } if (data.corner[3].valid) { s1 = sign(data.corner[0], data.corner[2], coord); s2 = sign(data.corner[2], data.corner[3], coord); s3 = sign(data.corner[3], data.corner[0], coord); if ( (s1 == s2) && (s2 == s3) ) { if (dist!=nullptr) *dist = 0.0; return coord; } } //not inside of solid RS_Vector ret(false); double currDist = RS_MAXDOUBLE; double tmpDist; if (entity!=nullptr) { *entity = const_cast<RS_Solid*>(this); } //Find nearest distance from each edge int totalV = 3; if (data.corner[3].valid) totalV = 4; for (int i=0; i<=totalV; ++i) { int next =i+1; //closing edge if (next == totalV) next =0; RS_Vector direction = data.corner[next]-data.corner[i]; RS_Vector vpc=coord-data.corner[i]; double a=direction.squared(); if( a < RS_TOLERANCE2) { //line too short vpc=data.corner[i]; }else{ //find projection on line vpc = data.corner[i] + direction*RS_Vector::dotP(vpc,direction)/a; } tmpDist = vpc.distanceTo(coord); if (tmpDist < currDist) { currDist = tmpDist; ret = vpc; } } //verify this part if( onEntity && !ret.isInWindowOrdered(minV,maxV) ){ //projection point not within range, find the nearest endpoint ret = getNearestEndpoint(coord,dist); currDist = ret.distanceTo(coord); } if (dist!=nullptr) { *dist = currDist; } return ret; }
/** * @todo Implement this. */ RS_Vector RS_Solid::getNearestPointOnEntity(const RS_Vector& coord, bool onEntity /*= true*/, double* dist /*= nullptr*/, RS_Entity** entity /*= nullptr*/) const { //first check if point is inside solid bool s1 {sign(data.corner[0], data.corner[1], coord)}; bool s2 {sign(data.corner[1], data.corner[2], coord)}; bool s3 {sign(data.corner[2], data.corner[0], coord)}; if ((s1 == s2) && (s2 == s3)) { setDistPtr( dist, 0.0); return coord; } if (!isTriangle()) { s1 = sign(data.corner[0], data.corner[2], coord); s2 = sign(data.corner[2], data.corner[3], coord); s3 = sign(data.corner[3], data.corner[0], coord); if ((s1 == s2) && (s2 == s3)) { setDistPtr( dist, 0.0); return coord; } } // not inside of solid // Find nearest distance from each edge if (nullptr != entity) { *entity = const_cast<RS_Solid*>(this); } RS_Vector ret(false); double currDist {RS_MAXDOUBLE}; double tmpDist {0.0}; int totalV {isTriangle() ? RS_SolidData::Triangle : RS_SolidData::MaxCorners}; for (int i = RS_SolidData::FirstCorner, next = i + 1; i <= totalV; ++i, ++next) { //closing edge if (next == totalV) { next = RS_SolidData::FirstCorner; } RS_Vector direction {data.corner[next] - data.corner[i]}; RS_Vector vpc {coord-data.corner[i]}; double a {direction.squared()}; if( a < RS_TOLERANCE2) { //line too short vpc = data.corner[i]; } else{ //find projection on line vpc = data.corner[i] + direction * RS_Vector::dotP( vpc, direction) / a; } tmpDist = vpc.distanceTo( coord); if (tmpDist < currDist) { currDist = tmpDist; ret = vpc; } } //verify this part if (onEntity && !ret.isInWindowOrdered( minV, maxV)) { // projection point not within range, find the nearest endpoint ret = getNearestEndpoint( coord, dist); currDist = ret.distanceTo( coord); } setDistPtr( dist, currDist); return ret; }