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; }
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; }
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; } }