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; }
QVariantMap QgsShortestPathPointToLayerAlgorithm::processAlgorithm( const QVariantMap ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) { loadCommonParams( parameters, context, feedback ); QgsPointXY startPoint = parameterAsPoint( parameters, QStringLiteral( "START_POINT" ), context, mNetwork->sourceCrs() ); std::unique_ptr< QgsFeatureSource > endPoints( parameterAsSource( parameters, QStringLiteral( "END_POINTS" ), context ) ); if ( !endPoints ) throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "END_POINTS" ) ) ); QgsFields fields = endPoints->fields(); fields.append( QgsField( QStringLiteral( "start" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "end" ), QVariant::String ) ); fields.append( QgsField( QStringLiteral( "cost" ), QVariant::Double ) ); QString dest; std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, QgsWkbTypes::LineString, mNetwork->sourceCrs() ) ); if ( !sink ) throw QgsProcessingException( invalidSinkError( parameters, QStringLiteral( "OUTPUT" ) ) ); QVector< QgsPointXY > points; points.push_front( startPoint ); QHash< int, QgsAttributes > sourceAttributes; loadPoints( endPoints.get(), points, sourceAttributes, context, feedback ); feedback->pushInfo( QObject::tr( "Building graph…" ) ); QVector< QgsPointXY > snappedPoints; mDirector->makeGraph( mBuilder.get(), points, snappedPoints, feedback ); feedback->pushInfo( QObject::tr( "Calculating shortest paths…" ) ); QgsGraph *graph = mBuilder->graph(); int idxStart = graph->findVertex( snappedPoints[0] ); int idxEnd; QVector< int > tree; QVector< double > costs; QgsGraphAnalyzer::dijkstra( graph, idxStart, 0, &tree, &costs ); QVector<QgsPointXY> route; double cost; QgsFeature feat; feat.setFields( fields ); QgsAttributes attributes; int step = points.size() > 0 ? 100.0 / points.size() : 1; for ( int i = 1; i < points.size(); i++ ) { if ( feedback->isCanceled() ) { break; } idxEnd = graph->findVertex( snappedPoints[i] ); if ( tree.at( idxEnd ) == -1 ) { feedback->reportError( QObject::tr( "There is no route from start point (%1) to end point (%2)." ) .arg( startPoint.toString(), points[i].toString() ) ); feat.clearGeometry(); attributes = sourceAttributes.value( i ); attributes.append( QVariant() ); attributes.append( points[i].toString() ); feat.setAttributes( attributes ); sink->addFeature( feat, QgsFeatureSink::FastInsert ); continue; } route.clear(); route.push_front( graph->vertex( idxEnd ).point() ); cost = costs.at( idxEnd ); while ( idxEnd != idxStart ) { idxEnd = graph->edge( tree.at( idxEnd ) ).fromVertex(); route.push_front( graph->vertex( idxEnd ).point() ); } QgsGeometry geom = QgsGeometry::fromPolylineXY( route ); QgsFeature feat; feat.setFields( fields ); attributes = sourceAttributes.value( i ); attributes.append( startPoint.toString() ); attributes.append( points[i].toString() ); attributes.append( cost / mMultiplier ); feat.setAttributes( attributes ); feat.setGeometry( geom ); sink->addFeature( feat, QgsFeatureSink::FastInsert ); feedback->setProgress( i * step ); } QVariantMap outputs; outputs.insert( QStringLiteral( "OUTPUT" ), dest ); return outputs; }
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; } }