QList<QPointF> Geometry::intersection(QLineF const &line, QPainterPath const &path, qreal eps) { QList<QPointF> result; QPointF startPoint; QPointF endPoint; for (int i = 0; i < path.elementCount(); ++i) { QPainterPath::Element const element = path.elementAt(i); // Checking that element belongs to the wall path if (element.isMoveTo()) { endPoint = QPointF(element.x, element.y); continue; } startPoint = endPoint; endPoint = QPointF(element.x, element.y); QLineF currentLine(startPoint, endPoint); QPointF intersectionPoint; // TODO: consider curve cases if (line.intersect(currentLine, &intersectionPoint) != QLineF::NoIntersection && belongs(intersectionPoint, currentLine, eps)) { result << intersectionPoint; } } return result; }
void QGIViewPart::dumpPath(const char* text,QPainterPath path) { QPainterPath::Element elem; Base::Console().Message(">>>%s has %d elements\n",text,path.elementCount()); char* typeName; for(int iElem = 0; iElem < path.elementCount(); iElem++) { elem = path.elementAt(iElem); if(elem.isMoveTo()) { typeName = "MoveTo"; } else if (elem.isLineTo()) { typeName = "LineTo"; } else if (elem.isCurveTo()) { typeName = "CurveTo"; } else { typeName = "Unknown"; } Base::Console().Message(">>>>> element %d: type:%d/%s pos(%.3f,%.3f) M:%d L:%d C:%d\n",iElem, elem.type,typeName,elem.x,elem.y,elem.isMoveTo(),elem.isLineTo(),elem.isCurveTo()); } }
GlyphToSVGHelper::GlyphToSVGHelper(QPainterPath path, QTransform tf) :m_path(path), m_transform(tf) { QStringList data; QPointF curPos; for (int i = 0; i < path.elementCount(); ++i) { QPainterPath::Element cur = path.elementAt(i); QPointF curPoint(tf.map(cur)); if(cur.isMoveTo()) { curPos = curPoint; data << QString("M %1 %2").arg(curPos.x()).arg(curPos.y()); } else if(cur.isLineTo()) { curPos = curPoint; data << QString("L %1 %2").arg(curPos.x()).arg(curPos.y()); } else if(cur.isCurveTo()) { QPointF c1 = tf.map(path.elementAt(i + 1)); QPointF c2 = tf.map(path.elementAt(i + 2)); data << QString("C %1 %2 %3 %4 %5 %6") .arg(curPoint.x()).arg(curPoint.y()) .arg(c1.x()).arg(c1.y()) .arg(c2.x()).arg(c2.y()); // qDebug(data.last().toUtf8()); i += 2; curPos = c2; } else qDebug("Unknown point type"); } m_svg += QString("<path d=\"%1\" fill=\"%2\" />").arg(data.join(" ")).arg("black"); }
/*! \internal */ void QGeoMapPolygonGeometry::updateScreenPoints(const QGeoMap &map) { if (!screenDirty_) return; if (map.width() == 0 || map.height() == 0) { clear(); return; } QPointF origin = map.coordinateToScreenPosition(srcOrigin_, false); // Create the viewport rect in the same coordinate system // as the actual points QRectF viewport(0, 0, map.width(), map.height()); viewport.translate(-1 * origin); QPainterPath vpPath; vpPath.addRect(viewport); QPainterPath ppi; if (clipToViewport_) ppi = srcPath_.intersected(vpPath); // get the clipped version of the path else ppi = srcPath_; clear(); // a polygon requires at least 3 points; if (ppi.elementCount() < 3) return; // Intersection between the viewport and a concave polygon can create multiple polygons // joined by a line at the viewport border, and poly2tri does not triangulate this very well // so use the full src path if the resulting polygon is concave. if (clipToViewport_) { int changeInX = 0; int changeInY = 0; QPainterPath::Element e1 = ppi.elementAt(1); QPainterPath::Element e = ppi.elementAt(0); QVector2D edgeA(e1.x - e.x ,e1.y - e.y); for (int i = 2; i <= ppi.elementCount(); ++i) { e = ppi.elementAt(i % ppi.elementCount()); if (e.x == e1.x && e.y == e1.y) continue; QVector2D edgeB(e.x - e1.x, e.y - e1.y); if ((edgeA.x() < 0) == (edgeB.x() >= 0)) changeInX++; if ((edgeA.y() < 0) == (edgeB.y() >= 0)) changeInY++; edgeA = edgeB; e1 = e; } if (changeInX > 2 || changeInY > 2) // polygon is concave ppi = srcPath_; } // translate the path into top-left-centric coordinates QRectF bb = ppi.boundingRect(); ppi.translate(-bb.left(), -bb.top()); firstPointOffset_ = -1 * bb.topLeft(); ppi.closeSubpath(); screenOutline_ = ppi; std::vector<p2t::Point*> curPts; curPts.reserve(ppi.elementCount()); for (int i = 0; i < ppi.elementCount(); ++i) { const QPainterPath::Element e = ppi.elementAt(i); if (e.isMoveTo() || i == ppi.elementCount() - 1 || (qAbs(e.x - curPts.front()->x) < 0.1 && qAbs(e.y - curPts.front()->y) < 0.1)) { if (curPts.size() > 2) { p2t::CDT *cdt = new p2t::CDT(curPts); cdt->Triangulate(); std::vector<p2t::Triangle*> tris = cdt->GetTriangles(); screenVertices_.reserve(screenVertices_.size() + int(tris.size())); for (size_t i = 0; i < tris.size(); ++i) { p2t::Triangle *t = tris.at(i); for (int j = 0; j < 3; ++j) { p2t::Point *p = t->GetPoint(j); screenVertices_ << Point(p->x, p->y); } } delete cdt; } curPts.clear(); curPts.reserve(ppi.elementCount() - i); curPts.push_back(new p2t::Point(e.x, e.y)); } else if (e.isLineTo()) { curPts.push_back(new p2t::Point(e.x, e.y)); } else { qWarning("Unhandled element type in polygon painterpath"); } } if (curPts.size() > 0) { qDeleteAll(curPts.begin(), curPts.end()); curPts.clear(); } screenBounds_ = ppi.boundingRect(); }
void LineRenderable::render(QPainter &painter, const RenderConfig &config) const { QPen pen(painter.pen()); pen.setCapStyle(cap_style); pen.setJoinStyle(join_style); if (join_style == Qt::MiterJoin) { pen.setMiterLimit(LineSymbol::miterLimit()); fixPenForPdf(pen, painter); } painter.setPen(pen); // One-time adjustment for line width QRectF bounding_box = config.bounding_box.adjusted(-line_width, -line_width, line_width, line_width); const int count = path.elementCount(); if (count <= 2 || bounding_box.contains(path.controlPointRect())) { // path fully contained painter.drawPath(path); } else { // Manually clip the path with bounding_box, this seems to be faster. // The code splits up the painter path into new paths which intersect // the view rect and renders these only. // NOTE: this does not work correctly with miter joins, but this // should be a minor issue. QPainterPath::Element element = path.elementAt(0); QPainterPath::Element last_element = path.elementAt(count-1); bool path_closed = (element.x == last_element.x) && (element.y == last_element.y); QPainterPath part_path; QPainterPath first_path; bool path_started = false; bool part_finished = false; bool current_part_is_first = bounding_box.contains(element); QPainterPath::Element prev_element = element; for (int i = 1; i < count; ++i) { element = path.elementAt(i); if (element.isLineTo()) { qreal min_x, min_y, max_x, max_y; if (prev_element.x < element.x) { min_x = prev_element.x; max_x = element.x; } else { min_x = element.x; max_x = prev_element.x; } if (prev_element.y < element.y) { min_y = prev_element.y; max_y = element.y; } else { min_y = element.y; max_y = prev_element.y; } if ( min_x <= bounding_box.right() && max_x >= bounding_box.left() && min_y <= bounding_box.bottom() && max_y >= bounding_box.top() ) { if (!path_started) { part_path = QPainterPath(); part_path.moveTo(prev_element.x, prev_element.y); path_started = true; } part_path.lineTo(element.x, element.y); } else if (path_started) { part_finished = true; } else { current_part_is_first = false; } } else if (element.isCurveTo()) { Q_ASSERT(i < count - 2); QPainterPath::Element next_element = path.elementAt(i + 1); QPainterPath::Element end_element = path.elementAt(i + 2); qreal min_x = qMin(prev_element.x, qMin(element.x, qMin(next_element.x, end_element.x))); qreal min_y = qMin(prev_element.y, qMin(element.y, qMin(next_element.y, end_element.y))); qreal max_x = qMax(prev_element.x, qMax(element.x, qMax(next_element.x, end_element.x))); qreal max_y = qMax(prev_element.y, qMax(element.y, qMax(next_element.y, end_element.y))); if ( min_x <= bounding_box.right() && max_x >= bounding_box.left() && min_y <= bounding_box.bottom() && max_y >= bounding_box.top() ) { if (!path_started) { part_path = QPainterPath(); part_path.moveTo(prev_element.x, prev_element.y); path_started = true; } part_path.cubicTo(element.x, element.y, next_element.x, next_element.y, end_element.x, end_element.y); } else if (path_started) { part_finished = true; } else { current_part_is_first = false; } } else if (element.isMoveTo() && path_started) { part_path.moveTo(element.x, element.y); } if (part_finished) { if (current_part_is_first && path_closed) { current_part_is_first = false; first_path = part_path; } else { painter.drawPath(part_path); } path_started = false; part_finished = false; } prev_element = element; } if (path_started) { if (path_closed && !first_path.isEmpty()) part_path.connectPath(first_path); painter.drawPath(part_path); } } // DEBUG: show all control points /*QPen debugPen(QColor(Qt::red)); painter.setPen(debugPen); for (int i = 0; i < path.elementCount(); ++i) { const QPainterPath::Element& e = path.elementAt(i); painter.drawEllipse(QPointF(e.x, e.y), 0.2f, 0.2f); } painter.setPen(pen);*/ }
/*! \internal */ void QGeoMapCircleGeometry::updateScreenPointsInvert(const QGeoMap &map) { if (!screenDirty_) return; if (map.width() == 0 || map.height() == 0) { clear(); return; } QPointF origin = map.coordinateToItemPosition(srcOrigin_, false).toPointF(); QPainterPath ppi = srcPath_; clear(); // a circle requires at least 3 points; if (ppi.elementCount() < 3) return; // translate the path into top-left-centric coordinates QRectF bb = ppi.boundingRect(); ppi.translate(-bb.left(), -bb.top()); firstPointOffset_ = -1 * bb.topLeft(); ppi.closeSubpath(); // calculate actual width of map on screen in pixels QGeoCoordinate mapCenter(0, map.cameraData().center().longitude()); QDoubleVector2D midPoint = map.coordinateToItemPosition(mapCenter, false); QDoubleVector2D midPointPlusOne = QDoubleVector2D(midPoint.x() + 1.0, midPoint.y()); QGeoCoordinate coord1 = map.itemPositionToCoordinate(midPointPlusOne, false); double geoDistance = coord1.longitude() - map.cameraData().center().longitude(); if ( geoDistance < 0 ) geoDistance += 360.0; double mapWidth = 360.0 / geoDistance; qreal leftOffset = origin.x() - (map.width()/2.0 - mapWidth/2.0) - firstPointOffset_.x(); qreal topOffset = origin.y() - (midPoint.y() - mapWidth/2.0) - firstPointOffset_.y(); QPainterPath ppiBorder; ppiBorder.moveTo(QPointF(-leftOffset, -topOffset)); ppiBorder.lineTo(QPointF(mapWidth - leftOffset, -topOffset)); ppiBorder.lineTo(QPointF(mapWidth - leftOffset, mapWidth - topOffset)); ppiBorder.lineTo(QPointF(-leftOffset, mapWidth - topOffset)); screenOutline_ = ppiBorder; std::vector<p2t::Point*> borderPts; borderPts.reserve(4); std::vector<p2t::Point*> curPts; curPts.reserve(ppi.elementCount()); for (int i = 0; i < ppi.elementCount(); ++i) { const QPainterPath::Element e = ppi.elementAt(i); if (e.isMoveTo() || i == ppi.elementCount() - 1 || (qAbs(e.x - curPts.front()->x) < 0.1 && qAbs(e.y - curPts.front()->y) < 0.1)) { if (curPts.size() > 2) { for (int j = 0; j < 4; ++j) { const QPainterPath::Element e2 = ppiBorder.elementAt(j); borderPts.push_back(new p2t::Point(e2.x, e2.y)); } p2t::CDT *cdt = new p2t::CDT(borderPts); cdt->AddHole(curPts); cdt->Triangulate(); std::vector<p2t::Triangle*> tris = cdt->GetTriangles(); screenVertices_.reserve(screenVertices_.size() + int(tris.size())); for (size_t i = 0; i < tris.size(); ++i) { p2t::Triangle *t = tris.at(i); for (int j = 0; j < 3; ++j) { p2t::Point *p = t->GetPoint(j); screenVertices_ << QPointF(p->x, p->y); } } delete cdt; } curPts.clear(); curPts.reserve(ppi.elementCount() - i); curPts.push_back(new p2t::Point(e.x, e.y)); } else if (e.isLineTo()) { curPts.push_back(new p2t::Point(e.x, e.y)); } else { qWarning("Unhandled element type in circle painterpath"); } } if (curPts.size() > 0) { qDeleteAll(curPts.begin(), curPts.end()); curPts.clear(); } if (borderPts.size() > 0) { qDeleteAll(borderPts.begin(), borderPts.end()); borderPts.clear(); } screenBounds_ = ppiBorder.boundingRect(); }