void RS_Grid::createIsometricGrid(LC_Rect const& rect, RS_Vector const& gridWidth) { double const left=rect.minP().x; double const right=rect.maxP().x; //top/bottom reversed double const top=rect.maxP().y; double const bottom=rect.minP().y; int numberY = (RS_Math::round((top-bottom) / gridWidth.y) + 1); double dx=sqrt(3.)*gridWidth.y; cellV.set(fabs(dx),fabs(gridWidth.y)); double hdx=0.5*dx; double hdy=0.5*gridWidth.y; int numberX = (RS_Math::round((right-left) / dx) + 1); int number = 2*numberX*numberY; baseGrid.set(left+remainder(-left,dx),bottom+remainder(-bottom,fabs(gridWidth.y))); if (number<=0 || number>maxGridPoints) return; pt.resize(number); int i=0; RS_Vector bp0(baseGrid),dbp1(hdx,hdy); for (int y=0; y<numberY; ++y) { RS_Vector bp1(bp0); for (int x=0; x<numberX; ++x) { pt[i++] = bp1; pt[i++] = bp1+dbp1; bp1.x += dx; } bp0.y += gridWidth.y; } //find metaGrid if (metaGridWidth.y>minimumGridWidth && graphicView->toGuiDY(metaGridWidth.y)>2) { metaGridWidth.x=(metaGridWidth.x<0.)?-sqrt(3.)*fabs(metaGridWidth.y):sqrt(3.)*fabs(metaGridWidth.y); RS_Vector baseMetaGrid(left+remainder(-left,metaGridWidth.x)-fabs(metaGridWidth.x),bottom+remainder(-bottom,metaGridWidth.y)-fabs(metaGridWidth.y)); // calculate number of visible meta grid lines: int numMetaX = (RS_Math::round((right-left) / metaGridWidth.x) + 1); int numMetaY = (RS_Math::round((top-bottom) / metaGridWidth.y) + 1); if (numMetaX<=0 || numMetaY<=0) return; // create meta grid arrays: metaX.resize(numMetaX); metaY.resize(numMetaY); double x0(baseMetaGrid.x); for (int i=0; i<numMetaX; x0 += metaGridWidth.x) { metaX[i++] = x0; } x0=baseMetaGrid.y; for (int i=0; i<numMetaY; x0 += metaGridWidth.y) { metaY[i++] = x0; } } }
/** * 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 const* e1, RS_Entity const* e2, bool onEntities) { RS_VectorSolutions ret; const double tol = 1.0e-4; if (!(e1 && e2) ) { RS_DEBUG->print("RS_Information::getIntersection() for nullptr 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; } if (onEntities && !(e1->isConstruction() || e2->isConstruction())) { // a little check to avoid doing unneeded intersections, an attempt to avoid O(N^2) increasing of checking two-entity information LC_Rect const rect1{e1->getMin(), e1->getMax()}; LC_Rect const rect2{e2->getMin(), e2->getMax()}; if (onEntities && !rect1.intersects(rect2, RS_TOLERANCE)) { 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() && 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; } } } if(e1->rtti() == RS2::EntitySplinePoints || e2->rtti() == RS2::EntitySplinePoints) { ret = LC_SplinePoints::getIntersection(e1, e2); } else { // issue #484 , quadratic intersection solver is not robust enough for quadratic-quadratic // TODO, implement a robust algorithm for quadratic based solvers, and detecting entity type // circles/arcs can be removed if(e1->rtti()==RS2::EntityCircle && e2->rtti()==RS2::EntityCircle){ //use specialized arc-arc intersection solver ret=getIntersectionArcArc(e1, e2); }else{ const auto qf1=e1->getQuadratic(); const auto qf2=e2->getQuadratic(); ret=LC_Quadratic::getIntersection(qf1,qf2); } } RS_VectorSolutions ret2; for(const RS_Vector& vp: ret){ if (!vp.valid) continue; if (onEntities) { //ignore intersections not on entity if (!( (e1->isConstruction(true) || e1->isPointOnEntity(vp, tol)) && (e2->isConstruction(true) || e2->isPointOnEntity(vp, tol)) ) ) { // std::cout<<"Ignored intersection "<<vp<<std::endl; // std::cout<<"because: e1->isPointOnEntity(ret.get(i), tol)="<<e1->isPointOnEntity(vp, tol) // <<"\t(e2->isPointOnEntity(ret.get(i), tol)="<<e2->isPointOnEntity(vp, 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 = nullptr, // *lpCircle = nullptr; // 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( nullptr != lpLine && nullptr != 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; }
void RS_Grid::createOrthogonalGrid(LC_Rect const& rect, RS_Vector const& gridWidth) { double const left=rect.minP().x; double const right=rect.maxP().x; //top/bottom reversed double const top=rect.maxP().y; double const bottom=rect.minP().y; cellV.set(fabs(gridWidth.x),fabs(gridWidth.y)); int numberX = (RS_Math::round((right-left) / gridWidth.x) + 1); int numberY = (RS_Math::round((top-bottom) / gridWidth.y) + 1); int number = numberX*numberY; //todo, fix baseGrid for orthogonal grid baseGrid.set(left,bottom); // create grid array: if (number<=0 || number>maxGridPoints) return; pt.resize(number); int i=0; RS_Vector bp0(baseGrid); for (int y=0; y<numberY; ++y) { RS_Vector bp1(bp0); for (int x=0; x<numberX; ++x) { pt[i++] = bp1; bp1.x += gridWidth.x; } bp0.y += gridWidth.y; } // find meta grid boundaries if (metaGridWidth.x>minimumGridWidth && metaGridWidth.y>minimumGridWidth && graphicView->toGuiDX(metaGridWidth.x)>2 && graphicView->toGuiDY(metaGridWidth.y)>2) { double mleft = (int)(graphicView->toGraphX(0) / metaGridWidth.x) * metaGridWidth.x; double mright = (int)(graphicView->toGraphX(graphicView->getWidth()) / metaGridWidth.x) * metaGridWidth.x; double mtop = (int)(graphicView->toGraphY(0) / metaGridWidth.y) * metaGridWidth.y; double mbottom = (int)(graphicView->toGraphY(graphicView->getHeight()) / metaGridWidth.y) * metaGridWidth.y; mleft -= metaGridWidth.x; mright += metaGridWidth.x; mtop += metaGridWidth.y; mbottom -= metaGridWidth.y; // calculate number of visible meta grid lines: int numMetaX = (RS_Math::round((mright-mleft) / metaGridWidth.x) + 1); int numMetaY = (RS_Math::round((mtop-mbottom) / metaGridWidth.y) + 1); if (numMetaX<=0 || numMetaY<=0) return; // create meta grid arrays: metaX.resize(numMetaX); metaY.resize(numMetaY); int i=0; for (int x=0; x<numMetaX; ++x) { metaX[i++] = mleft+x*metaGridWidth.x; } i=0; for (int y=0; y<numMetaY; ++y) { metaY[i++] = mbottom+y*metaGridWidth.y; } } }
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; } }