QPolygonF QwtSpline::polygonP( const QPolygonF &points, double delta, bool withNodes ) const { if ( delta <= 0.0 ) return QPolygonF(); const int n = points.size(); if ( n <= 1 ) return points; if ( n == 2 ) { // TODO return points; } QPolygonF path; const QVector<QLineF> controlPoints = bezierControlPointsP( points ); if ( controlPoints.size() < n - 1 ) return path; path += points.first(); double t = delta; for ( int i = 0; i < n - 1; i++ ) { #if 1 const double l = d_parameter->value( points[i], points[i+1] ); #endif while ( t < l ) { path += qwtBezierPoint( points[i], controlPoints[i].p1(), controlPoints[i].p2(), points[i+1], t / l ); t += delta; } if ( withNodes ) { if ( qFuzzyCompare( path.last().x(), points[i+1].x() ) ) path.last() = points[i+1]; else path += points[i+1]; t = delta; } else { t -= l; } } if ( controlPoints.size() >= n ) { const double l = d_parameter->value( points[n-1], points[0] ); while ( t < l ) { path += qwtBezierPoint( points[n-1], controlPoints[n-1].p1(), controlPoints[n-1].p2(), points[0], t / l ); t += delta; } if ( qFuzzyCompare( path.last().x(), points[0].x() ) ) path.last() = points[0]; else path += points[0]; } return path; }
void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement ) { if ( points.isEmpty() ) return; QgsRenderContext& rc = context.renderContext(); double origAngle = mMarker->angle(); double angle; int i, maxCount; bool isRing = false; if ( placement == FirstVertex ) { i = 0; maxCount = 1; } else if ( placement == LastVertex ) { i = points.count() - 1; maxCount = points.count(); } else { i = 0; maxCount = points.count(); if ( points.first() == points.last() ) isRing = true; } for ( ; i < maxCount; ++i ) { const QPointF& pt = points[i]; // rotate marker (if desired) if ( mRotateMarker ) { if ( i == 0 ) { if ( !isRing ) { // use first segment's angle const QPointF& nextPt = points[i+1]; if ( pt == nextPt ) continue; angle = MyLine( pt, nextPt ).angle(); } else { // closed ring: use average angle between first and last segment const QPointF& prevPt = points[points.count() - 2]; const QPointF& nextPt = points[1]; if ( prevPt == pt || nextPt == pt ) continue; angle = _averageAngle( prevPt, pt, nextPt ); } } else if ( i == points.count() - 1 ) { if ( !isRing ) { // use last segment's angle const QPointF& prevPt = points[i-1]; if ( pt == prevPt ) continue; angle = MyLine( prevPt, pt ).angle(); } else { // don't draw the last marker - it has been drawn already continue; } } else { // use average angle const QPointF& prevPt = points[i-1]; const QPointF& nextPt = points[i+1]; if ( prevPt == pt || nextPt == pt ) continue; angle = _averageAngle( prevPt, pt, nextPt ); } mMarker->setAngle( origAngle + angle * 180 / M_PI ); } mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() ); } // restore original rotation mMarker->setAngle( origAngle ); }
void CreateObjectTool::mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers) { AbstractObjectTool::mouseMoved(pos, modifiers); if (!mNewMapObjectItem) return; const MapRenderer *renderer = mapDocument()->renderer(); bool snapToGrid = Preferences::instance()->snapToGrid(); if (modifiers & Qt::ControlModifier) snapToGrid = !snapToGrid; switch (mMode) { case CreateRectangle: case CreateEllipse: { const QPointF tileCoords = renderer->pixelToTileCoords(pos); // Update the size of the new map object const QPointF objectPos = mNewMapObjectItem->mapObject()->position(); QSizeF newSize(qMax(qreal(0), tileCoords.x() - objectPos.x()), qMax(qreal(0), tileCoords.y() - objectPos.y())); if (snapToGrid) newSize = newSize.toSize(); // Holding shift creates circle or square if (modifiers & Qt::ShiftModifier) { qreal max = qMax(newSize.width(), newSize.height()); newSize.setWidth(max); newSize.setHeight(max); } mNewMapObjectItem->resize(newSize); break; } case CreateTile: { const QSize imgSize = mNewMapObjectItem->mapObject()->tile()->size(); const QPointF diff(-imgSize.width() / 2, imgSize.height() / 2); QPointF tileCoords = renderer->pixelToTileCoords(pos + diff); if (snapToGrid) tileCoords = tileCoords.toPoint(); mNewMapObjectItem->mapObject()->setPosition(tileCoords); mNewMapObjectItem->syncWithMapObject(); break; } case CreatePolygon: case CreatePolyline: { QPointF tileCoords = renderer->pixelToTileCoords(pos); if (snapToGrid) tileCoords = tileCoords.toPoint(); tileCoords -= mNewMapObjectItem->mapObject()->position(); QPolygonF polygon = mOverlayPolygonObject->polygon(); polygon.last() = tileCoords; mOverlayPolygonItem->setPolygon(polygon); break; } } }
void CreateObjectTool::mousePressed(QGraphicsSceneMouseEvent *event) { // Check if we are already creating a new map object if (mNewMapObjectItem) { switch (mMode) { case CreateRectangle: case CreateTile: case CreateEllipse: if (event->button() == Qt::RightButton) cancelNewMapObject(); break; case CreatePolygon: case CreatePolyline: if (event->button() == Qt::RightButton) { // The polygon needs to have at least three points and a // polyline needs at least two. int min = mMode == CreatePolygon ? 3 : 2; if (mNewMapObjectItem->mapObject()->polygon().size() >= min) finishNewMapObject(); else cancelNewMapObject(); } else if (event->button() == Qt::LeftButton) { QPolygonF current = mNewMapObjectItem->mapObject()->polygon(); QPolygonF next = mOverlayPolygonObject->polygon(); // If the last position is still the same, ignore the click if (next.last() == current.last()) return; // Assign current overlay polygon to the new object mNewMapObjectItem->setPolygon(next); // Add a new editable point to the overlay next.append(next.last()); mOverlayPolygonItem->setPolygon(next); } break; } return; } if (event->button() != Qt::LeftButton) { AbstractObjectTool::mousePressed(event); return; } ObjectGroup *objectGroup = currentObjectGroup(); if (!objectGroup || !objectGroup->isVisible()) return; const MapRenderer *renderer = mapDocument()->renderer(); QPointF tileCoords; if (mMode == CreateTile) { if (!mTile) return; const QPointF diff(-mTile->width() / 2, mTile->height() / 2); tileCoords = renderer->pixelToTileCoords(event->scenePos() + diff); } else { tileCoords = renderer->pixelToTileCoords(event->scenePos()); } bool snapToGrid = Preferences::instance()->snapToGrid(); if (event->modifiers() & Qt::ControlModifier) snapToGrid = !snapToGrid; if (snapToGrid) tileCoords = tileCoords.toPoint(); startNewMapObject(tileCoords, objectGroup); }
void MaGraphOverview::drawOverview(QPainter &p) { if (displaySettings->orientation == MaGraphOverviewDisplaySettings::FromTopToBottom) { // transform coordinate system p.translate( 0, height()); p.scale(1, -1); } p.fillRect(cachedConsensus.rect(), Qt::white); if (editor->getAlignmentLen() == 0) { return; } p.setPen(displaySettings->color); p.setBrush(displaySettings->color); if (graphCalculationTaskRunner.getResult().isEmpty() && !editor->isAlignmentEmpty() && !isBlocked) { sl_drawGraph(); return; } QPolygonF resultPolygon = graphCalculationTaskRunner.getResult(); if (!editor->isAlignmentEmpty() && resultPolygon.last().x() != width()) { sl_drawGraph(); return; } // area graph if (displaySettings->type == MaGraphOverviewDisplaySettings::Area) { p.drawPolygon( resultPolygon ); } // line graph if (displaySettings->type == MaGraphOverviewDisplaySettings::Line) { p.drawPolyline( resultPolygon ); } // hystogram if (displaySettings->type == MaGraphOverviewDisplaySettings::Hystogram) { int size = graphCalculationTaskRunner.getResult().size(); for (int i = 0; i < size; i++) { const QPointF point = resultPolygon.at(i); QPointF nextPoint; if (i != size - 1) { nextPoint = resultPolygon.at(i + 1); } else { nextPoint = QPointF(width(), point.y()); } p.drawRect( point.x(), point.y(), static_cast<int>(nextPoint.x() - point.x()) - 2 * (width() > 2 * size), height() - point.y()); } } // gray frame p.setPen(Qt::gray); p.setBrush(Qt::transparent); p.drawRect( rect().adjusted( 0, (displaySettings->orientation == MaGraphOverviewDisplaySettings::FromTopToBottom), -1, -1 * (displaySettings->orientation == MaGraphOverviewDisplaySettings::FromBottomToTop))); }
void ClipPainterPrivate::clipPolyObject ( const QPolygonF & polygon, QVector<QPolygonF> & clippedPolyObjects, bool isClosed ) { // mDebug() << "ClipPainter enabled." ; // Only create a new polyObject as soon as we know for sure that // the current point is on the screen. QPolygonF clippedPolyObject = QPolygonF(); const QVector<QPointF>::const_iterator itStartPoint = polygon.constBegin(); const QVector<QPointF>::const_iterator itEndPoint = polygon.constEnd(); QVector<QPointF>::const_iterator itPoint = itStartPoint; // We use a while loop to be able to cover linestrings as well as linear rings: // Linear rings require to tessellate the path from the last node to the first node // which isn't really convenient to achieve with a for loop ... bool processingLastNode = false; while ( itPoint != itEndPoint ) { m_currentPoint = (*itPoint); // mDebug() << "m_currentPoint.x()" << m_currentPoint.x() << "m_currentPOint.y()" << m_currentPoint.y(); // Figure out the sector of the current point. m_currentSector = sector( m_currentPoint ); // Initialize the variables related to the previous point. if ( itPoint == itStartPoint && processingLastNode == false ) { if ( isClosed ) { m_previousPoint = polygon.last(); // Figure out the sector of the previous point. m_previousSector = sector( m_previousPoint ); } else { m_previousSector = m_currentSector; } } // If the current point reaches a new sector, take care of clipping. if ( m_currentSector != m_previousSector ) { if ( m_currentSector == 4 || m_previousSector == 4 ) { // In this case the current or the previous point is visible on the // screen but not both. Hence we only need to clip once and require // only one interpolation for both cases. clipOnce( clippedPolyObject, clippedPolyObjects, isClosed ); } else { // This case mostly deals with lines that reach from one // sector that is located off screen to another one that // is located off screen. In this situation the line // can get clipped once, twice, or not at all. clipMultiple( clippedPolyObject, clippedPolyObjects, isClosed ); } m_previousSector = m_currentSector; } // If the current point is onscreen, just add it to our final polygon. if ( m_currentSector == 4 ) { clippedPolyObject << m_currentPoint; #ifdef MARBLE_DEBUG ++(m_debugNodeCount); #endif } m_previousPoint = m_currentPoint; // Now let's handle the case where we have a (closed) polygon and where the // last point of the polyline is outside the viewport and the start point // is inside the viewport. This needs special treatment if ( processingLastNode ) { break; } ++itPoint; if ( itPoint == itEndPoint && isClosed ) { itPoint = itStartPoint; processingLastNode = true; } } // Only add the pointer if there's node data available. if ( !clippedPolyObject.isEmpty() ) { clippedPolyObjects << clippedPolyObject; } }
void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement ) { if ( points.isEmpty() ) return; QgsRenderContext& rc = context.renderContext(); double origAngle = mMarker->angle(); int i, maxCount; bool isRing = false; double offsetAlongLine = mOffsetAlongLine; QgsExpression* offsetAlongLineExpression = expression( "offset_along_line" ); if ( offsetAlongLineExpression ) { offsetAlongLine = offsetAlongLineExpression->evaluate( const_cast<QgsFeature*>( context.feature() ) ).toDouble(); } if ( offsetAlongLine != 0 ) { //scale offset along line offsetAlongLine *= QgsSymbolLayerV2Utils::lineWidthScaleFactor( rc, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale ); } if ( placement == FirstVertex ) { i = 0; maxCount = 1; } else if ( placement == LastVertex ) { i = points.count() - 1; maxCount = points.count(); } else { i = 0; maxCount = points.count(); if ( points.first() == points.last() ) isRing = true; } if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) ) { double distance; distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine; renderOffsetVertexAlongLine( points, i, distance, context ); // restore original rotation mMarker->setAngle( origAngle ); return; } for ( ; i < maxCount; ++i ) { if ( isRing && placement == Vertex && i == points.count() - 1 ) { continue; // don't draw the last marker - it has been drawn already } // rotate marker (if desired) if ( mRotateMarker ) { double angle = markerAngle( points, isRing, i ); mMarker->setAngle( origAngle + angle * 180 / M_PI ); } mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() ); } // restore original rotation mMarker->setAngle( origAngle ); }
void QgsMarkerLineSymbolLayerV2::renderPolylineVertex( const QPolygonF& points, QgsSymbolV2RenderContext& context, Placement placement ) { if ( points.isEmpty() ) return; QgsRenderContext& rc = context.renderContext(); double origAngle = mMarker->angle(); int i, maxCount; bool isRing = false; double offsetAlongLine = mOffsetAlongLine; if ( hasDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE ) ) { context.setOriginalValueVariable( mOffsetAlongLine ); offsetAlongLine = evaluateDataDefinedProperty( QgsSymbolLayerV2::EXPR_OFFSET_ALONG_LINE, context, mOffsetAlongLine ).toDouble(); } if ( offsetAlongLine != 0 ) { //scale offset along line offsetAlongLine = QgsSymbolLayerV2Utils::convertToPainterUnits( rc, offsetAlongLine, mOffsetAlongLineUnit, mOffsetAlongLineMapUnitScale ); } if ( offsetAlongLine == 0 && context.renderContext().geometry() && context.renderContext().geometry()->hasCurvedSegments() && ( placement == Vertex || placement == CurvePoint ) ) { const QgsCoordinateTransform* ct = context.renderContext().coordinateTransform(); const QgsMapToPixel& mtp = context.renderContext().mapToPixel(); QgsVertexId vId; QgsPointV2 vPoint; double x, y, z; QPointF mapPoint; while ( context.renderContext().geometry()->nextVertex( vId, vPoint ) ) { if (( placement == Vertex && vId.type == QgsVertexId::SegmentVertex ) || ( placement == CurvePoint && vId.type == QgsVertexId::CurveVertex ) ) { //transform x = vPoint.x(), y = vPoint.y(); z = vPoint.z(); if ( ct ) { ct->transformInPlace( x, y, z ); } mapPoint.setX( x ); mapPoint.setY( y ); mtp.transformInPlace( mapPoint.rx(), mapPoint.ry() ); if ( mRotateMarker ) { double angle = context.renderContext().geometry()->vertexAngle( vId ); mMarker->setAngle( angle * 180 / M_PI ); } mMarker->renderPoint( mapPoint, context.feature(), rc, -1, context.selected() ); } } return; } if ( placement == FirstVertex ) { i = 0; maxCount = 1; } else if ( placement == LastVertex ) { i = points.count() - 1; maxCount = points.count(); } else if ( placement == Vertex ) { i = 0; maxCount = points.count(); if ( points.first() == points.last() ) isRing = true; } else { return; } if ( offsetAlongLine > 0 && ( placement == FirstVertex || placement == LastVertex ) ) { double distance; distance = placement == FirstVertex ? offsetAlongLine : -offsetAlongLine; renderOffsetVertexAlongLine( points, i, distance, context ); // restore original rotation mMarker->setAngle( origAngle ); return; } for ( ; i < maxCount; ++i ) { if ( isRing && placement == Vertex && i == points.count() - 1 ) { continue; // don't draw the last marker - it has been drawn already } // rotate marker (if desired) if ( mRotateMarker ) { double angle = markerAngle( points, isRing, i ); mMarker->setAngle( origAngle + angle * 180 / M_PI ); } mMarker->renderPoint( points.at( i ), context.feature(), rc, -1, context.selected() ); } // restore original rotation mMarker->setAngle( origAngle ); }
bool DataConverter::digitalPattern_Rotate(QPolygonF &points, qreal angle) { if (angle == 0.0) return true; if (angle < -180.0 || angle > 180.0) { qDebug("[DataConverter::digitalPattern_Rotate] Error: Angle: [%f]", angle); return false; } if (points.size() < 10) { qDebug("[DataConverter::digitalPattern_Rotate] Error: Polygon Point Count: [%d]", points.size()); return false; } // QTransform transform; transform.rotate(angle); points = transform.map(points); // int bottomIndex = 0; { qreal tmpX = -9999.99; for (int i = 0; i < points.size(); i++) { if (points.at(i).y() < 0.0) continue; if (points.at(i).x() > 0.0) continue; if (points.at(i).x() > tmpX) { tmpX = points.at(i).x(); bottomIndex = i; } } } qDebug("** bottomIndex: [%d]", bottomIndex); if (bottomIndex > 0) { QPointF tmpPoint; for (int i = points.size() - 1; i >= bottomIndex; i--) { tmpPoint = points.last(); points.pop_back(); points.push_front(tmpPoint); } } return true; }
void CreateObjectTool::mouseMoved(const QPointF &pos, Qt::KeyboardModifiers modifiers) { AbstractObjectTool::mouseMoved(pos, modifiers); if (!mNewMapObjectItem) return; const MapRenderer *renderer = mapDocument()->renderer(); bool snapToGrid = Preferences::instance()->snapToGrid(); bool snapToFineGrid = Preferences::instance()->snapToFineGrid(); if (modifiers & Qt::ControlModifier) { snapToGrid = !snapToGrid; snapToFineGrid = false; } switch (mMode) { case CreateRectangle: case CreateEllipse: { const QPointF pixelCoords = renderer->screenToPixelCoords(pos); // Update the size of the new map object const QPointF objectPos = mNewMapObjectItem->mapObject()->position(); QPointF newSize(qMax(qreal(0), pixelCoords.x() - objectPos.x()), qMax(qreal(0), pixelCoords.y() - objectPos.y())); QPointF newTileSize = renderer->pixelToTileCoords(newSize); if (snapToFineGrid) { int gridFine = Preferences::instance()->gridFine(); newTileSize = (newTileSize * gridFine).toPoint(); newTileSize /= gridFine; } else if (snapToGrid) newTileSize = newTileSize.toPoint(); // Holding shift creates circle or square if (modifiers & Qt::ShiftModifier) { qreal max = qMax(newTileSize.x(), newTileSize.y()); newTileSize.setX(max); newTileSize.setY(max); } newSize = renderer->tileToPixelCoords(newTileSize); mNewMapObjectItem->resizeObject(QSizeF(newSize.x(), newSize.y())); break; } case CreateTile: { const QSize imgSize = mNewMapObjectItem->mapObject()->cell().tile->size(); const QPointF diff(-imgSize.width() / 2, imgSize.height() / 2); QPointF tileCoords = renderer->screenToTileCoords(pos + diff); if (snapToFineGrid) { int gridFine = Preferences::instance()->gridFine(); tileCoords = (tileCoords * gridFine).toPoint(); tileCoords /= gridFine; } else if (snapToGrid) tileCoords = tileCoords.toPoint(); QPointF pixelCoords = renderer->tileToPixelCoords(tileCoords); mNewMapObjectItem->mapObject()->setPosition(pixelCoords); mNewMapObjectItem->syncWithMapObject(); mNewMapObjectItem->setZValue(10000); // sync may change it break; } case CreatePolygon: case CreatePolyline: { QPointF tileCoords = renderer->screenToTileCoords(pos); if (snapToFineGrid) { int gridFine = Preferences::instance()->gridFine(); tileCoords = (tileCoords * gridFine).toPoint(); tileCoords /= gridFine; } else if (snapToGrid) tileCoords = tileCoords.toPoint(); QPointF pixelCoords = renderer->tileToPixelCoords(tileCoords); pixelCoords -= mNewMapObjectItem->mapObject()->position(); QPolygonF polygon = mOverlayPolygonObject->polygon(); polygon.last() = pixelCoords; mOverlayPolygonItem->setPolygon(polygon); break; } } }
void _PolyClipper::clipPolyline(const QPolygonF& poly) { // exit if fewer than 2 points in polygon if ( poly.size() < 2 ) return; // output goes here QPolygonF pout; QPolygonF::const_iterator polyiter = poly.begin(); QPointF lastpt = *polyiter; polyiter++; for( ; polyiter != poly.end(); ++polyiter ) { QPointF p1 = lastpt; QPointF p2 = *polyiter; bool plotline = _clipper.clipLine(p1, p2); if( plotline ) { if( pout.isEmpty() ) { // add first line pout << p1; if( ! smallDelta(p1, p2) ) pout << p2; } else { if( p1 == pout.last() ) { if( ! smallDelta(p1, p2) ) // extend polyline pout << p2; } else { // paint existing line if( pout.size() >= 2 ) emitPolyline(pout); // start new line pout.clear(); pout << p1; if( ! smallDelta(p1, p2) ) pout << p2; } } } else { // line isn't in region, so ignore results from clip function // paint existing line if( pout.size() >= 2 ) emitPolyline(pout); // cleanup pout.clear(); } lastpt = *polyiter; } if( pout.size() >= 2 ) emitPolyline(pout); }
void assertPolygonIsClosed(QPolygonF polygon) { ASSERT_RETURN(polygon.isEmpty() || polygon.first() == polygon.last()); }
QPolygonF minigis::shiftPolygonSimple(const QPolygonF &origin, qreal delta, bool closed) { QPolygonF path = origin; QPolygonF norm; // ----------------- Поиск нормалей к оригинальному полигону int N = path.size(); for (int i = 1; i < N; ++i) { QPointF vect = path.at(i) - path.at(i-1); double len = lengthR2(vect); if (qFuzzyIsNull(len)) { path.remove(i); --N; --i; continue; } vect /= len; norm.append(QPointF(vect.y() * delta, -vect.x() * delta)); } // ---- if (closed) { QPointF vect = path.first() - path.last(); double len = lengthR2(vect); if (qFuzzyIsNull(len)) path.remove(path.size() - 1); else { vect /= len; norm.append(QPointF(vect.y() * delta, -vect.x() * delta)); } } // ------------------ QVector<QLineF> lines; // -------------------------- Построение смещенных линий for (int i = 1; i < path.size(); ++i) lines.append(QLineF(path.at(i) + norm.at(i-1), path.at(i-1) + norm.at(i-1))); // ---- if (closed) lines.append(QLineF(path.first() + norm.last(), path.last() + norm.last())); // ------------------ QPolygonF shell; if (lines.isEmpty()) return shell; // -------------------------- Построение смещенного полигона N = lines.size(); for (int i = 1; i < N; ++i) { QPointF tmp; QLineF::IntersectType type = lines.at(i-1).intersect(lines.at(i), &tmp); double ang = lines.at(i-1).angleTo(lines.at(i)); if (type != QLineF::NoIntersection) shell.append(tmp); else { if (qFuzzyCompare(ang, 180.)) shell.append(lines.at(i).p2() - 2 * norm.at(i)); shell.append(lines.at(i).p2()); } } // ---- if (closed) { QPointF tmp; QLineF::IntersectType type = lines.last().intersect(lines.first(), &tmp); double ang = lines.last().angleTo(lines.first()); if (type != QLineF::NoIntersection) shell.append(tmp); else { if (qFuzzyCompare(ang, 180.)) shell.append(lines.first().p2() - 2 * norm.first()); shell.append(lines.first().p2()); } shell.append(shell.first()); } else { shell.prepend(lines.first().p2()); shell.append(lines.last().p1()); } // ------------------ return shell; }
QPolygonF minigis::shiftPolygonDifficult(const QPolygonF &origin, qreal delta, bool closed) { if (qFuzzyIsNull(delta)) return origin; QPolygonF path = origin; QPolygonF norm; // ----------------- Поиск нормалей к оригинальному полигону int N = path.size(); for (int i = 1; i < N; ++i) { QPointF vect = path.at(i) - path.at(i-1); double len = lengthR2(vect); if (qFuzzyIsNull(len)) { path.remove(i); --N; --i; continue; } vect /= len; norm.append(QPointF(vect.y() * delta, -vect.x() * delta)); } // ---- if (closed) { QPointF vect = path.first() - path.last(); double len = lengthR2(vect); if (qFuzzyIsNull(len)) path.remove(path.size() - 1); else { vect /= len; norm.append(QPointF(vect.y() * delta, -vect.x() * delta)); } } // ------------------ QVector<QLineF> lines; // -------------------------- Построение смещенных линий for (int i = 1; i < path.size(); ++i) lines.append(QLineF(path.at(i) + norm.at(i-1), path.at(i-1) + norm.at(i-1))); // ---- if (closed) lines.append(QLineF(path.first() + norm.last(), path.last() + norm.last())); // ------------------ QPolygonF shell; if (lines.isEmpty()) return shell; // -------------------------- Построение смещенного полигона N = lines.size(); for (int i = 1; i < N; ++i) { QPointF tmp; QLineF::IntersectType type = lines.at(i-1).intersect(lines.at(i), &tmp); qreal ang = lines.at(i-1).angleTo(lines.at(i)); if (type != QLineF::NoIntersection) shell.append(tmp); else { if (qFuzzyCompare(ang, qreal(180))) shell.append(lines.at(i).p2() - 2 * norm.at(i)); shell.append(lines.at(i).p2()); } } // ---- if (closed) { QPointF tmp; QLineF::IntersectType type = lines.last().intersect(lines.first(), &tmp); qreal ang = lines.last().angleTo(lines.first()); if (type != QLineF::NoIntersection) shell.append(tmp); else { if (qFuzzyCompare(ang, qreal(180))) shell.append(lines.first().p2() - 2 * norm.first()); shell.append(lines.first().p2()); } shell.append(shell.first()); } else { shell.prepend(lines.first().p2()); shell.append(lines.last().p1()); } // ------------------ // -------------------------- обрезание острых углов int k = 0; N = lines.size(); for (int i = 1; i < N; ++i) { double ang = lines.at(i-1).angleTo(lines.at(i)); bool first = (120 < ang && ang < 180 && delta < 0) || (180 < ang && ang < 240 && delta > 0); bool second = (120 < ang && ang < 180 && delta > 0) || (180 < ang && ang < 240 && delta < 0); if (first) { int num = closed ? 1 : 0; QPointF v = shell.at(i + k - num) - path.at(i); v /= lengthR2(v); QPointF start = path.at(i) + v * qAbs(delta); QLineF tmp(start, start + QPointF(-v.y(), v.x())); QPointF a; if (tmp.intersect(lines.at(i ), &a) != QLineF::NoIntersection) shell.replace(i + k - num, a); if (tmp.intersect(lines.at(i-1), &a) != QLineF::NoIntersection) shell.insert(i + k - num, a); ++k; } else if (second) { // TODO: cut corner } } // ---- if (closed) { double ang = lines.last().angleTo(lines.first()); int num = lines.size(); int shellNum = (num + k - 1) % shell.size(); bool first = (120 < ang && ang < 180 && delta < 0) || (180 < ang && ang < 240 && delta > 0); bool second = (180 < ang && ang < 240 && delta < 0) || (120 < ang && ang < 180 && delta > 0); if (first) { QPointF v = shell.at(shellNum) - path.at(num % path.size()); v /= lengthR2(v); QPointF start = path.at(num % path.size()) + v * qAbs(delta); QLineF tmp(start, start + QPointF(-v.y(), v.x())); QPointF a; if (tmp.intersect(lines.first(), &a) != QLineF::NoIntersection) shell.replace(shellNum, a); if (tmp.intersect(lines.last() , &a) != QLineF::NoIntersection) shell.insert(shellNum, a); } else if (second) { // TODO: cut corner } } // ------------------ return shell; }
/** * Try to reduce the point size of a polyline. * * \param range Corridor beside the course line in meters. * * \param mwList list of polylines to be processed. * * \return list with reduced polylines. */ QList<QPolygonF> reducePoints( const double range, QList<QPolygonF>& mwList, const bool verbose ) { QPointF startP; QPointF lastP; double startBearing; QList<QPolygonF> retList; QList<QPointF> ignoreList; int sum = 0; for( int i = 0; i < mwList.size(); i++ ) { const QPolygonF& plIn = mwList.at(i); QPolygonF plOut; for( int j = 0; j < plIn.size(); j++ ) { const QPointF& p = plIn.at(j); if( j == 0 ) { // Set start point for filtering startP = p; plOut.append(p); continue; } if( j == 1 ) { startBearing = getBearingWgs( startP, p ); lastP = p; continue; } // calculate bearing from start point double bearing = getBearingWgs( startP, p ); // calculate distance from start point double dist = distP(startP.x(), startP.y(), p.x(), p.y()); // calculate sector angle from start point. const double delta = atan( range / dist ); if( fabs( startBearing - bearing ) < delta ) { // Ignore point, it is laying inside the corridor. ignoreList.append(p); lastP = p; continue; } #if 0 printf( "j=%d, Start=%.1f, Entf=%.0f, Kurs=%.1f, Winkel=%.1f, Diff=%.1f°\n", j, startBearing, dist, bearing, delta, fabs( startBearing - bearing) * 180.0 / M_PI ); #endif // Add points from the ignore list to get a better turn int start = ignoreList.size() - 2; if( start < 0 ) { start = 0; } if( start < ignoreList.size() ) { // qDebug() << "IgnoreList" << ignoreList.size() << "is used"; } for( int k=start; k < ignoreList.size(); k++ ) { // plOut.append( ignoreList.at(k) ); } ignoreList.clear(); startBearing = bearing; plOut.append(lastP); startP = lastP; lastP = p; } if( plOut.size() > 0 && lastP != plOut.last() ) { // Add last start point to the segment list. plOut.append(lastP); } if( plOut.size() < 2 ) { qWarning() << "Polyline" << i << "has only" << plOut.size() << "points!"; continue; } if( plIn.size() - plOut.size() ) { if( verbose ) { qDebug() << "Points=" << plIn.size() << "Taken=" << plOut.size() << "Reduced=" << (plIn.size() - plOut.size()); } sum += plIn.size() - plOut.size(); } retList.append( plOut ); } qDebug() << "Corridor=" << range << "m," << sum << "points are removed."; return retList; }