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) { *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; }
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; } }