/** * Helper function for makeContour * Calculate the intersection point of first and last entities * The first vertex is not added and the last is returned instead of added * * @retval RS_Vector nearest to startpoint of last and endpoint of first or RS_Vector(false) if not * * @author Rallaz */ RS_Vector RS_ActionPolylineEquidistant::calculateIntersection(RS_Entity* first,RS_Entity* last) { RS_VectorSolutions vsol; RS_Vector v(false); vsol = RS_Information::getIntersection(first, last, false); if (vsol.getNumber()==0) { //Parallel entities return RS_Vector(false); } else if (vsol.getNumber()>1 && vsol.get(0).distanceTo(last->getStartpoint()) > vsol.get(1).distanceTo(last->getStartpoint())) { return vsol.get(1); } return vsol.get(0); }
RS_Vector RS_Line::prepareTrim(const RS_Vector& trimCoord, const RS_VectorSolutions& trimSol) { //prepare trimming for multiple intersections if ( ! trimSol.hasValid()) return(RS_Vector(false)); if ( trimSol.getNumber() == 1 ) return(trimSol.get(0)); auto&& vp0=trimSol.getClosest(trimCoord,NULL,0); double dr2=trimCoord.squaredTo(vp0); //the trim point found is closer to mouse location (trimCoord) than both end points, return this trim point if(dr2 < trimCoord.squaredTo(getStartpoint()) && dr2 < trimCoord.squaredTo(getEndpoint())) return vp0; //the closer endpoint to trimCoord RS_Vector vp1=(trimCoord.squaredTo(getStartpoint()) <= trimCoord.squaredTo(getEndpoint()))?getStartpoint():getEndpoint(); //searching for intersection in the direction of the closer end point auto&& dvp1=vp1 - trimCoord; RS_VectorSolutions sol1; for(size_t i=0; i<trimSol.size(); i++){ auto&& dvp2=trimSol.at(i) - trimCoord; if( RS_Vector::dotP(dvp1, dvp2) > RS_TOLERANCE) sol1.push_back(trimSol.at(i)); } //if found intersection in direction, return the closest to trimCoord from it if(sol1.size()) return sol1.getClosest(trimCoord,NULL,0); //no intersection by direction, return previously found closest intersection return vp0; }
/** //create Ellipse with axes in x-/y- directions from 4 points * * *@Author Dongxu Li */ bool RS_Ellipse::createFrom4P(const RS_VectorSolutions& sol) { if (sol.getNumber() != 4 ) return (false); //only do 4 points QVector<QVector<double> > mt; QVector<double> dn; int mSize(4); mt.resize(mSize); for(int i=0;i<mSize;i++) {//form the linear equation, c0 x^2 + c1 x + c2 y^2 + c3 y = 1 mt[i].resize(mSize+1); mt[i][0]=sol.get(i).x * sol.get(i).x; mt[i][1]=sol.get(i).x ; mt[i][2]=sol.get(i).y * sol.get(i).y; mt[i][3]=sol.get(i).y ; mt[i][4]=1.; } if ( ! RS_Math::linearSolver(mt,dn)) return false; double d(1.+0.25*(dn[1]*dn[1]/dn[0]+dn[3]*dn[3]/dn[2])); if(fabs(dn[0])<RS_TOLERANCE*RS_TOLERANCE ||fabs(dn[2])<RS_TOLERANCE*RS_TOLERANCE ||d/dn[0]<RS_TOLERANCE*RS_TOLERANCE ||d/dn[2]<RS_TOLERANCE*RS_TOLERANCE ) { //ellipse not defined return false; } data.center.set(-0.5*dn[1]/dn[0],-0.5*dn[3]/dn[2]); // center d=sqrt(d/dn[0]); data.majorP.set(d,0.); data.ratio=sqrt(dn[0]/dn[2]); data.angle1=0.; data.angle2=0.; return true; }
RS_Vector RS_Line::prepareTrim(const RS_Vector& trimCoord, const RS_VectorSolutions& trimSol) { //prepare trimming for multiple intersections if ( ! trimSol.hasValid()) return(RS_Vector(false)); if ( trimSol.getNumber() == 1 ) return(trimSol.get(0)); return trimSol.getClosest(trimCoord,NULL,0); }
/** //create Ellipse with center and 3 points * * *@Author Dongxu Li */ bool RS_Ellipse::createFromCenter3Points(const RS_VectorSolutions& sol) { if(sol.getNumber()<3) return false; //need one center and 3 points on ellipse QVector<QVector<double> > mt; int mSize(sol.getNumber() -1); if( (sol.get(mSize) - sol.get(mSize-1)).squared() < RS_TOLERANCE*RS_TOLERANCE ) { //remove the last point mSize--; } mt.resize(mSize); QVector<double> dn(mSize); switch(mSize){ case 2: for(int i=0;i<mSize;i++){//form the linear equation mt[i].resize(mSize+1); RS_Vector vp(sol.get(i+1)-sol.get(0)); //the first vector is center mt[i][0]=vp.x*vp.x; mt[i][1]=vp.y*vp.y; mt[i][2]=1.; } if ( ! RS_Math::linearSolver(mt,dn) ) return false; if( dn[0]<RS_TOLERANCE*RS_TOLERANCE || dn[1]<RS_TOLERANCE*RS_TOLERANCE) return false; setMajorP(RS_Vector(1./sqrt(dn[0]),0.)); setRatio(sqrt(dn[0]/dn[1])); setAngle1(0.); setAngle2(0.); setCenter(sol.get(0)); return true; break; case 3: for(int i=0;i<mSize;i++){//form the linear equation mt[i].resize(mSize+1); RS_Vector vp(sol.get(i+1)-sol.get(0)); //the first vector is center mt[i][0]=vp.x*vp.x; mt[i][1]=vp.x*vp.y; mt[i][2]=vp.y*vp.y; mt[i][3]=1.; } if ( ! RS_Math::linearSolver(mt,dn) ) return false; setCenter(sol.get(0)); return createFromQuadratic(dn); default: return false; } }
/** * @return The intersection which is closest to 'coord' */ RS_Vector RS_EntityContainer::getNearestIntersection(const RS_Vector& coord, double* dist) { double minDist = RS_MAXDOUBLE; // minimum measured distance double curDist = RS_MAXDOUBLE; // currently measured distance RS_Vector closestPoint(false); // closest found endpoint RS_Vector point; // endpoint found RS_VectorSolutions sol; RS_Entity* closestEntity; closestEntity = getNearestEntity(coord, nullptr, RS2::ResolveAllButTextImage); if (closestEntity) { for (RS_Entity* en = firstEntity(RS2::ResolveAllButTextImage); en; en = nextEntity(RS2::ResolveAllButTextImage)) { if ( !en->isVisible() || en->getParent()->ignoredOnModification() ){ continue; } sol = RS_Information::getIntersection(closestEntity, en, true); point=sol.getClosest(coord,&curDist,nullptr); if(sol.getNumber()>0 && curDist<minDist){ closestPoint=point; minDist=curDist; } } } if(dist && closestPoint.valid) { *dist = minDist; } return closestPoint; }
//*create Circle from 3 points //Author: Dongxu Li bool RS_Circle::createFrom3P(const RS_VectorSolutions& sol) { if(sol.getNumber() < 2) return false; if(sol.getNumber() == 2) return createFrom2P(sol.get(0),sol.get(1)); if((sol.get(1)-sol.get(2)).squared() < RS_TOLERANCE*RS_TOLERANCE) return createFrom2P(sol.get(0),sol.get(1)); RS_Vector vra(sol.get(1) - sol.get(0)); RS_Vector vrb(sol.get(2) - sol.get(0)); double ra2=vra.squared()*0.5; double rb2=vrb.squared()*0.5; double crossp=vra.x * vrb.y - vra.y * vrb.x; if (fabs(crossp)< RS_TOLERANCE*RS_TOLERANCE) { RS_DEBUG->print(RS_Debug::D_WARNING, "RS_Circle::createFrom3P(): " "Cannot create a circle with radius 0.0."); return false; } crossp=1./crossp; data.center.set((ra2*vrb.y - rb2*vra.y)*crossp,(rb2*vra.x - ra2*vrb.x)*crossp); data.radius=data.center.magnitude(); data.center += sol.get(0); return true; }
bool RS_ActionPolylineEquidistant::makeContour() { if (container==NULL) { RS_DEBUG->print("RS_ActionPolylineEquidistant::makeContour: no valid container", RS_Debug::D_WARNING); return false; } RS_Vector offset(false); QList<RS_Entity*> addList; if (document!=NULL) { document->startUndoCycle(); } double neg = 1.0; if(bRightSide) neg = -1.0; // Create new entites RS_Line line1(NULL, RS_LineData(RS_Vector(true), RS_Vector(true))); RS_Line line2(NULL, RS_LineData(RS_Vector(true), RS_Vector(true))); for (int num=1; num<=number || (number==0 && num<=1); num++) { // std::cout<<"copy: "<<num<<" of "<<number<<std::endl; RS_Polyline* newPolyline = new RS_Polyline(container); newPolyline->setClosed(((RS_Polyline*)originalEntity)->isClosed()); // newPolyline->setSelected((RS_Polyline*)originalEntity)->isSelected()); newPolyline->setLayer(((RS_Polyline*)originalEntity)->getLayer()); newPolyline->setPen(((RS_Polyline*)originalEntity)->getPen()); bool first = true; RS_Entity* lastEntity = ((RS_Polyline*)originalEntity)->lastEntity(); for (RS_Entity* en=((RS_Polyline*)originalEntity)->firstEntity(); en!=NULL; en=((RS_Polyline*)originalEntity)->nextEntity()) { double bulge = 0.0; if (en->getLength() < 1.0e-15) continue; if (en->rtti()==RS2::EntityArc) { double r0 = ((RS_Arc*)en)->getRadius(); double r = r0 - dist*neg; if(r < 0) break; ((RS_Arc*)en)->setRadius(r); bulge = ((RS_Arc*)en)->getBulge(); ((RS_Arc*)en)->setRadius(r0); } else { bulge = 0.0; } RS_Vector v1 = ((RS_AtomicEntity*)en)->getStartpoint(); RS_Vector v2 = ((RS_AtomicEntity*)en)->getEndpoint(); offset.set(dist * cos(v1.angleTo(v2)+M_PI*0.5*neg), dist * sin(v1.angleTo(v2)+M_PI*0.5*neg)); v1.move(offset*num); v2.move(offset*num); if (first) { line1.setStartpoint(v1); line1.setEndpoint(v2); if(newPolyline->isClosed()){ RS_Vector v01 = ((RS_AtomicEntity*)lastEntity)->getStartpoint(); RS_Vector v02 = ((RS_AtomicEntity*)en)->getStartpoint(); offset.set(dist * cos(v01.angleTo(v02)+M_PI*0.5*neg), dist * sin(v01.angleTo(v02)+M_PI*0.5*neg)); v01.move(offset*num); v02.move(offset*num); line2.setStartpoint(v01); line2.setEndpoint(v02); RS_VectorSolutions vsol = RS_Information::getIntersection(&line1, &line2, false); v1 = vsol.get(0); } newPolyline->setStartpoint(v1); newPolyline->setNextBulge(bulge); if (en == lastEntity) { newPolyline->addVertex(v2, bulge); } first = false; }else{ line2.setStartpoint(v1); line2.setEndpoint(v2); RS_VectorSolutions vsol = RS_Information::getIntersection(&line1, &line2, false); RS_Vector v; if (vsol.getNumber()>0) { v= vsol.get(0); }else { //fixme, this is not correct v=(line1.getEndpoint()+v1)*0.5; } newPolyline->addVertex(v, bulge); newPolyline->setEndpoint(v); line1.setStartpoint(v1); line1.setEndpoint(v2); if (en==lastEntity/* && newPolyline->isClosed()==false*/){ newPolyline->addVertex(v2, bulge); } } } double bulge = lastEntity->rtti() == RS2::EntityArc? ((RS_Arc*)lastEntity)->getBulge():0.0; newPolyline->setNextBulge(bulge); newPolyline->endPolyline(); container->addEntity(newPolyline); document->addUndoable(newPolyline); } if (document!=NULL) { document->endUndoCycle(); } if (graphicView!=NULL) { graphicView->redraw(); } return true; }
RS_Vector RS_Ellipse::prepareTrim(const RS_Vector& trimCoord, const RS_VectorSolutions& trimSol) { //special trimming for ellipse arc RS_DEBUG->print("RS_Ellipse::prepareTrim()"); 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; }
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; }
/** * Calculates the intersection point(s) between two entities. * * @param onEntities true: only return intersection points which are * on both entities. * false: return all intersection points. * * @todo support more entities * * @return All intersections of the two entities. The tangent flag in * RS_VectorSolutions is set if one intersection is a tangent point. */ RS_VectorSolutions RS_Information::getIntersection(RS_Entity* e1, RS_Entity* e2, bool onEntities) { RS_VectorSolutions ret; const double tol = 1.0e-4; if (e1==NULL || e2==NULL ) { RS_DEBUG->print("RS_Information::getIntersection() for NULL entities"); return ret; } if (e1->getId() == e2->getId()) { RS_DEBUG->print("RS_Information::getIntersection() of the same entity"); return ret; } // unsupported entities / entity combinations: if ( e1->rtti()==RS2::EntityMText || e2->rtti()==RS2::EntityMText || e1->rtti()==RS2::EntityText || e2->rtti()==RS2::EntityText || isDimension(e1->rtti()) || isDimension(e2->rtti())) { return ret; } // a little check to avoid doing unneeded intersections, an attempt to avoid O(N^2) increasing of checking two-entity information if (onEntities && (! (e1 -> isConstructionLayer() || e2 -> isConstructionLayer() )) && ( e1 -> getMin().x > e2 -> getMax().x || e1 -> getMax().x < e2 -> getMin().x || e1 -> getMin().y > e2 -> getMax().y || e1 -> getMax().y < e2 -> getMin().y ) ) { return ret; } //avoid intersections between line segments the same spline /* ToDo: 24 Aug 2011, Dongxu Li, if rtti() is not defined for the parent, the following check for splines may still cause segfault */ if ( e1->getParent() != NULL && e1->getParent() == e2->getParent()) { if ( e1->getParent()->rtti()==RS2::EntitySpline ) { //do not calculate intersections from neighboring lines of a spline if ( abs(e1->getParent()->findEntity(e1) - e1->getParent()->findEntity(e2)) <= 1 ) { return ret; } } } const auto&& qf1=e1->getQuadratic(); const auto&& qf2=e2->getQuadratic(); ret=LC_Quadratic::getIntersection(qf1,qf2); RS_VectorSolutions ret2; for(int i=0;i<ret.getNumber();i++) { RS_Vector&& vp=ret.get(i); if ( ! ret.get(i).valid) continue; if (onEntities==true) { //ignore intersections not on entity if (!( (e1->isConstructionLayer(true) || e1->isPointOnEntity(vp, tol)) && (e2->isConstructionLayer(true) || e2->isPointOnEntity(vp, tol)) ) ) { // std::cout<<"Ignored intersection "<<ret.get(i)<<std::endl; // std::cout<<"because: e1->isPointOnEntity(ret.get(i), tol)="<<e1->isPointOnEntity(ret.get(i), tol) // <<"\t(e2->isPointOnEntity(ret.get(i), tol)="<<e2->isPointOnEntity(ret.get(i), tol)<<std::endl; continue; } } // need to test whether the intersection is tangential RS_Vector&& direction1=e1->getTangentDirection(vp); RS_Vector&& direction2=e2->getTangentDirection(vp); if( direction1.valid && direction2.valid && fabs(fabs(direction1.dotP(direction2)) - sqrt(direction1.squared()*direction2.squared())) < sqrt(tol)*tol ) ret2.setTangent(true); //TODO, make the following tangential test, nearest test work for all entity types // RS_Entity *lpLine = NULL, // *lpCircle = NULL; // if( RS2::EntityLine == e1->rtti() && RS2::EntityCircle == e2->rtti()) { // lpLine = e1; // lpCircle = e2; // } // else if( RS2::EntityCircle == e1->rtti() && RS2::EntityLine == e2->rtti()) { // lpLine = e2; // lpCircle = e1; // } // if( NULL != lpLine && NULL != lpCircle) { // double dist = 0.0; // RS_Vector nearest = lpLine->getNearestPointOnEntity( lpCircle->getCenter(), false, &dist); // // special case: line touches circle tangent // if( nearest.valid && fabs( dist - lpCircle->getRadius()) < tol) { // ret.set(i,nearest); // ret2.setTangent(true); // } // } ret2.push_back(vp); } return ret2; }
/** * Calculates the intersection point(s) between two entities. * * @param onEntities true: only return intersection points which are * on both entities. * false: return all intersection points. * * @todo support more entities * * @return All intersections of the two entities. The tangent flag in * RS_VectorSolutions is set if one intersection is a tangent point. */ RS_VectorSolutions RS_Information::getIntersection(RS_Entity* e1, RS_Entity* e2, bool onEntities) { RS_VectorSolutions ret; double tol = 1.0e-4; if (e1==NULL || e2==NULL ) { RS_DEBUG->print("RS_Information::getIntersection() for NULL entities"); return ret; } if (e1->getId() == e2->getId()) { RS_DEBUG->print("RS_Information::getIntersection() of the same entity"); return ret; } // unsupported entities / entity combinations: if ( e1->rtti()==RS2::EntityText || e2->rtti()==RS2::EntityText || isDimension(e1->rtti()) || isDimension(e2->rtti())) { return ret; } // a little check to avoid doing unneeded intersections, an attempt to avoid O(N^2) increasing of checking two-entity information if (onEntities && ( e1 -> getMin().x > e2 -> getMax().x || e1 -> getMax().x < e2 -> getMin().x || e1 -> getMin().y > e2 -> getMax().y || e1 -> getMax().y < e2 -> getMin().y ) ) { return ret; } // one entity is an ellipse: if (e1->rtti()==RS2::EntityEllipse || e2->rtti()==RS2::EntityEllipse) { if (e2->rtti()==RS2::EntityEllipse) std::swap( e1, e2); if (e2->rtti()==RS2::EntityEllipse) { ret = getIntersectionEllipseEllipse((RS_Ellipse*)e1, (RS_Ellipse *) e2); } if (e2->rtti()==RS2::EntityCircle) { ret = getIntersectionCircleEllipse((RS_Circle *)e2, (RS_Ellipse *) e1); } if (e2->rtti()==RS2::EntityArc) { ret = getIntersectionArcEllipse((RS_Arc *)e2, (RS_Ellipse *) e1); } if (e2->rtti()==RS2::EntityLine) { ret = getIntersectionLineEllipse((RS_Line*)e2, (RS_Ellipse*) e1); tol = 1.0e-1; } // not supported: else { return ret; } } else { RS_Entity* te1 = e1; RS_Entity* te2 = e2; // entity copies - so we only have to deal with lines and arcs RS_Line l1(NULL, RS_LineData(RS_Vector(0.0, 0.0), RS_Vector(0.0,0.0))); RS_Line l2(NULL, RS_LineData(RS_Vector(0.0, 0.0), RS_Vector(0.0,0.0))); RS_Arc a1(NULL, RS_ArcData(RS_Vector(0.0,0.0), 1.0, 0.0, 2*M_PI, false)); RS_Arc a2(NULL, RS_ArcData(RS_Vector(0.0,0.0), 1.0, 0.0, 2*M_PI, false)); // convert construction lines to lines: if (e1->rtti()==RS2::EntityConstructionLine) { RS_ConstructionLine* cl = (RS_ConstructionLine*)e1; l1.setStartpoint(cl->getPoint1()); l1.setEndpoint(cl->getPoint2()); te1 = &l1; } if (e2->rtti()==RS2::EntityConstructionLine) { RS_ConstructionLine* cl = (RS_ConstructionLine*)e2; l2.setStartpoint(cl->getPoint1()); l2.setEndpoint(cl->getPoint2()); te2 = &l2; } // convert circles to arcs: if (e1->rtti()==RS2::EntityCircle) { RS_Circle* c = (RS_Circle*)e1; RS_ArcData data(c->getCenter(), c->getRadius(), 0.0, 2*M_PI, false); a1.setData(data); te1 = &a1; } if (e2->rtti()==RS2::EntityCircle) { RS_Circle* c = (RS_Circle*)e2; RS_ArcData data(c->getCenter(), c->getRadius(), 0.0, 2*M_PI, false); a2.setData(data); te2 = &a2; } // line / line: // //else if (te1->rtti()==RS2::EntityLine && te2->rtti()==RS2::EntityLine) { RS_Line * line1=(RS_Line*) te1; RS_Line * line2=(RS_Line*) te2; /* ToDo: 24 Aug 2011, Dongxu Li, if rtti() is not defined for the parent, the following check for splines may still cause segfault */ if ( line1->getParent() != NULL && line1->getParent() == line2->getParent()) { if ( line1->getParent()->rtti()==RS2::EntitySpline ) { //do not calculate intersections from neighboring lines of a spline if ( abs(line1->getParent()->findEntity(line1) - line1->getParent()->findEntity(line2)) <= 1 ) { return ret; } } } ret = getIntersectionLineLine(line1, line2); } // line / arc: // else if (te1->rtti()==RS2::EntityLine && te2->rtti()==RS2::EntityArc) { RS_Line* line = (RS_Line*)te1; RS_Arc* arc = (RS_Arc*)te2; ret = getIntersectionLineArc(line, arc); } // arc / line: // else if (te1->rtti()==RS2::EntityArc && te2->rtti()==RS2::EntityLine) { RS_Arc* arc = (RS_Arc*)te1; RS_Line* line = (RS_Line*)te2; ret = getIntersectionLineArc(line, arc); } // arc / arc: // else if (te1->rtti()==RS2::EntityArc && te2->rtti()==RS2::EntityArc) { RS_Arc* arc1 = (RS_Arc*)te1; RS_Arc* arc2 = (RS_Arc*)te2; ret = getIntersectionArcArc(arc1, arc2); // ellipse / ellipse // } else { RS_DEBUG->print("RS_Information::getIntersection:: Unsupported entity type."); } } // Check all intersection points for being on entities: // RS_VectorSolutions ret2; for(int i=0;i<ret.getNumber();i++) { if ( ! ret.get(i).valid) continue; if (onEntities==true) { //ignore intersections not on entity if (!(e1->isPointOnEntity(ret.get(i), tol) && e2->isPointOnEntity(ret.get(i), tol))) { continue; } } ret2.push_back(ret.get(i)); } return ret2; }
/** * Calculates the intersection point(s) between two entities. * * @param onEntities true: only return intersection points which are * on both entities. * false: return all intersection points. * * @todo support more entities * * @return All intersections of the two entities. The tangent flag in * RS_VectorSolutions is set if one intersection is a tangent point. */ RS_VectorSolutions RS_Information::getIntersection(RS_Entity* e1, RS_Entity* e2, bool onEntities) { RS_VectorSolutions ret; double tol = 1.0e-4; if (e1==NULL || e2==NULL ) { RS_DEBUG->print("RS_Information::getIntersection() for NULL entities"); return ret; } if (e1->getId() == e2->getId()) { RS_DEBUG->print("RS_Information::getIntersection() of the same entity"); return ret; } // unsupported entities / entity combinations: if ( e1->rtti()==RS2::EntityMText || e2->rtti()==RS2::EntityMText || e1->rtti()==RS2::EntityText || e2->rtti()==RS2::EntityText || isDimension(e1->rtti()) || isDimension(e2->rtti())) { return ret; } // a little check to avoid doing unneeded intersections, an attempt to avoid O(N^2) increasing of checking two-entity information if (onEntities && (! (e1 -> isHelpLayer() || e2 -> isHelpLayer() )) && ( e1 -> getMin().x > e2 -> getMax().x || e1 -> getMax().x < e2 -> getMin().x || e1 -> getMin().y > e2 -> getMax().y || e1 -> getMax().y < e2 -> getMin().y ) ) { return ret; } //avoid intersections between line segments the same spline /* ToDo: 24 Aug 2011, Dongxu Li, if rtti() is not defined for the parent, the following check for splines may still cause segfault */ if ( e1->getParent() != NULL && e1->getParent() == e2->getParent()) { if ( e1->getParent()->rtti()==RS2::EntitySpline ) { //do not calculate intersections from neighboring lines of a spline if ( abs(e1->getParent()->findEntity(e1) - e1->getParent()->findEntity(e2)) <= 1 ) { return ret; } } } const auto&& qf1=e1->getQuadratic(); const auto&& qf2=e2->getQuadratic(); ret=LC_Quadratic::getIntersection(qf1,qf2); RS_VectorSolutions ret2; for(int i=0;i<ret.getNumber();i++) { if ( ! ret.get(i).valid) continue; if (onEntities==true) { //ignore intersections not on entity if (!( (e1->isHelpLayer(true) || e1->isPointOnEntity(ret.get(i), tol)) && (e2->isHelpLayer(true) || e2->isPointOnEntity(ret.get(i), tol)) ) ) { // std::cout<<"Ignored intersection "<<ret.get(i)<<std::endl; // std::cout<<"because: e1->isPointOnEntity(ret.get(i), tol)="<<e1->isPointOnEntity(ret.get(i), tol) // <<"\t(e2->isPointOnEntity(ret.get(i), tol)="<<e2->isPointOnEntity(ret.get(i), tol)<<std::endl; continue; } } ret2.push_back(ret.get(i)); } return ret2; }
void RS_GraphicView::drawEntity(RS_Painter *painter, RS_Entity* e, double& patternOffset) { // update is diabled: // given entity is NULL: if (e==NULL) { return; } // entity is not visible: if (!e->isVisible()) { return; } if( isPrintPreview() ) { //do not draw help layer on print preview if(e->isHelpLayer()) return; } // test if the entity is in the viewport /* temporary disabled so rs_overlaylien can be drawn if (!e->isContainer() && !isPrinting() && (painter==NULL || !painter->isPreviewMode()) && (toGuiX(e->getMax().x)<0 || toGuiX(e->getMin().x)>getWidth() || toGuiY(e->getMin().y)<0 || toGuiY(e->getMax().y)>getHeight())) { return; } */ // set pen (color): setPenForEntity(painter, e ); //RS_DEBUG->print("draw plain"); if (isDraftMode()) { // large mtexts as rectangles: if (e->rtti()==RS2::EntityMText) { if (toGuiDX(((RS_MText*)e)->getHeight())<4 || e->countDeep()>100) { painter->drawRect(toGui(e->getMin()), toGui(e->getMax())); } else { drawEntityPlain(painter, e, patternOffset); } } // large texts as rectangles: else if (e->rtti()==RS2::EntityText) { if (toGuiDX(((RS_Text*)e)->getHeight())<4 || e->countDeep()>100) { painter->drawRect(toGui(e->getMin()), toGui(e->getMax())); } else { drawEntityPlain(painter, e, patternOffset); } } // all images as rectangles: else if (e->rtti()==RS2::EntityImage) { painter->drawRect(toGui(e->getMin()), toGui(e->getMax())); } // hide hatches: else if (e->rtti()==RS2::EntityHatch) { // nothing } else { drawEntityPlain(painter, e, patternOffset); } } else { drawEntityPlain(painter, e, patternOffset); } // draw reference points: if (e->isSelected()) { if (!e->isParentSelected()) { RS_VectorSolutions s = e->getRefPoints(); for (int i=0; i<s.getNumber(); ++i) { int sz = -1; RS_Color col = RS_Color(0,0,255); if (e->rtti()==RS2::EntityPolyline) { if (i==0 || i==s.getNumber()-1) { if (i==0) { sz = 4; col = QColor(0,64,255); } else { sz = 3; col = QColor(0,0,128); } } } if (getDeleteMode()) { painter->drawHandle(toGui(s.get(i)), background, sz); } else { painter->drawHandle(toGui(s.get(i)), col, sz); } } } } //RS_DEBUG->print("draw plain OK"); //RS_DEBUG->print("RS_GraphicView::drawEntity() end"); }
/** * 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; }
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; }