2013-07-07 12 views
6

के बीच छेड़छाड़ का बिंदु ढूँढना मैं उस बिंदु को निर्धारित करने की कोशिश कर रहा हूं जहां हिशकैन प्रोजेक्टाइल का पथ (मूल रूप से एक रेखा है, लेकिन मैंने इसे अपने उदाहरण में QPainterPath के रूप में दर्शाया है) में एक आइटम के साथ छेड़छाड़ करता है मेरा दृश्य। अगर वहाँ इस बिंदु QPainterPath, QLineF द्वारा प्रदान की काम करता है, का उपयोग कर खोजने के लिए एक तरीका है मुझे यकीन है कि नहीं कर रहा हूँ आदि नीचे कोड दिखाता है कि मैं क्या करने की कोशिश कर रहा हूँ:एक रेखा और एक QPainterPath

#include <QtWidgets> 

bool hit(const QPainterPath &projectilePath, QGraphicsScene *scene, QPointF &hitPos) 
{ 
    const QList<QGraphicsItem *> itemsInPath = scene->items(projectilePath, Qt::IntersectsItemBoundingRect); 
    if (!itemsInPath.isEmpty()) { 
     const QPointF projectileStartPos = projectilePath.elementAt(0); 
     float shortestDistance = std::numeric_limits<float>::max(); 
     QGraphicsItem *closest = 0; 
     foreach (QGraphicsItem *item, itemsInPath) { 
      QPointF distanceAsPoint = item->pos() - projectileStartPos; 
      float distance = abs(distanceAsPoint.x() + distanceAsPoint.y()); 
      if (distance < shortestDistance) { 
       shortestDistance = distance; 
       closest = item; 
      } 
     } 
     QPainterPath targetShape = closest->mapToScene(closest->shape()); 
     // hitPos = /* the point at which projectilePath hits targetShape */ 
     hitPos = closest->pos(); // incorrect; always gives top left 
     qDebug() << projectilePath.intersects(targetShape); // true 
     qDebug() << projectilePath.intersected(targetShape); // QPainterPath: Element count=0 
     // To show that they do actually intersect.. 
     QPen p1(Qt::green); 
     p1.setWidth(2); 
     QPen p2(Qt::blue); 
     p2.setWidth(2); 
     scene->addPath(projectilePath, p1); 
     scene->addPath(targetShape, p2); 
     return true; 
    } 
    return false; 
} 

int main(int argc, char *argv[]) 
{ 
    QApplication app(argc, argv); 

    QGraphicsView view; 
    view.setViewportUpdateMode(QGraphicsView::FullViewportUpdate); 
    QGraphicsScene *scene = new QGraphicsScene; 
    view.setScene(scene); 
    view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
    view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 

    QGraphicsItem *target = scene->addRect(0, 0, 25, 25); 
    target->setTransformOriginPoint(QPointF(12.5, 12.5)); 
    target->setRotation(35); 
    target->setPos(100, 100); 

    QPainterPath projectilePath; 
    projectilePath.moveTo(200, 200); 
    projectilePath.lineTo(0, 0); 
    projectilePath.lineTo(200, 200); 

    QPointF hitPos; 
    if (hit(projectilePath, scene, hitPos)) { 
     scene->addEllipse(hitPos.x() - 2, hitPos.y() - 2, 4, 4, QPen(Qt::red)); 
    } 

    scene->addPath(projectilePath, QPen(Qt::DashLine)); 
    scene->addText("start")->setPos(180, 150); 
    scene->addText("end")->setPos(20, 0); 

    view.show(); 

    return app.exec(); 
} 

projectilePath.intersects(targetShape) रिटर्न true, लेकिन projectilePath.intersected(targetShape) रिटर्न एक खाली रास्ता

क्या यह हासिल करने का कोई तरीका है?

+0

आप क्यूटी 4 या क्यूटी 5. का उपयोग कर यह अच्छा हो सकता है आपके सवाल का अधिक विशिष्ट टैग को जोड़ने के लिए कर रहे हैं:

यहाँ तय कार्य है। –

+0

का डुप्लिकेट: http://stackoverflow.com/questions/9393672/intersection-point-of-qpainterpath-and-line-find-qpainterpath-y-by-x – Mitch

उत्तर

5

Intersection point of QPainterPath and line (find QPainterPath y by x) के उत्तर के रूप में, QPainterPath::intersected() केवल उन क्षेत्रों के लिए खाते हैं जिनमें भरने वाले क्षेत्र हैं। आयताकार पथ चाल जो भी उल्लेख किया गया है वहाँ इस तरह लागू किया जा सकता:

#include <QtWidgets> 

/*! 
    Returns the closest element (position) in \a sourcePath to \a target, 
    using \l{QPoint::manhattanLength()} to determine the distances. 
*/ 
QPointF closestPointTo(const QPointF &target, const QPainterPath &sourcePath) 
{ 
    Q_ASSERT(!sourcePath.isEmpty()); 
    QPointF shortestDistance = sourcePath.elementAt(0) - target; 
    qreal shortestLength = shortestDistance.manhattanLength(); 
    for (int i = 1; i < sourcePath.elementCount(); ++i) { 
     const QPointF distance(sourcePath.elementAt(i) - target); 
     const qreal length = distance.manhattanLength(); 
     if (length < shortestLength) { 
      shortestDistance = sourcePath.elementAt(i); 
      shortestLength = length; 
     } 
    } 
    return shortestDistance; 
} 

/*! 
    Returns \c true if \a projectilePath intersects with any items in \a scene, 
    setting \a hitPos to the position of the intersection. 
*/ 
bool hit(const QPainterPath &projectilePath, QGraphicsScene *scene, QPointF &hitPos) 
{ 
    const QList<QGraphicsItem *> itemsInPath = scene->items(projectilePath, Qt::IntersectsItemBoundingRect); 
    if (!itemsInPath.isEmpty()) { 
     const QPointF projectileStartPos = projectilePath.elementAt(0); 
     float shortestDistance = std::numeric_limits<float>::max(); 
     QGraphicsItem *closest = 0; 
     foreach (QGraphicsItem *item, itemsInPath) { 
      QPointF distanceAsPoint = item->pos() - projectileStartPos; 
      float distance = abs(distanceAsPoint.x() + distanceAsPoint.y()); 
      if (distance < shortestDistance) { 
       shortestDistance = distance; 
       closest = item; 
      } 
     } 

     QPainterPath targetShape = closest->mapToScene(closest->shape()); 
     // QLineF has normalVector(), which is useful for extending our path to a rectangle. 
     // The path needs to be a rectangle, as QPainterPath::intersected() only accounts 
     // for intersections between fill areas, which projectilePath doesn't have. 
     QLineF pathAsLine(projectileStartPos, projectilePath.elementAt(1)); 
     // Extend the first point in the path out by 1 pixel. 
     QLineF startEdge = pathAsLine.normalVector(); 
     startEdge.setLength(1); 
     // Swap the points in the line so the normal vector is at the other end of the line. 
     pathAsLine.setPoints(pathAsLine.p2(), pathAsLine.p1()); 
     QLineF endEdge = pathAsLine.normalVector(); 
     // The end point is currently pointing the wrong way; move it to face the same 
     // direction as startEdge. 
     endEdge.setLength(-1); 
     // Now we can create a rectangle from our edges. 
     QPainterPath rectPath(startEdge.p1()); 
     rectPath.lineTo(startEdge.p2()); 
     rectPath.lineTo(endEdge.p2()); 
     rectPath.lineTo(endEdge.p1()); 
     rectPath.lineTo(startEdge.p1()); 
     // Visualize the rectangle that we created. 
     scene->addPath(rectPath, QPen(QBrush(Qt::blue), 2)); 
     // Visualize the intersection of the rectangle with the item. 
     scene->addPath(targetShape.intersected(rectPath), QPen(QBrush(Qt::cyan), 2)); 
     // The hit position will be the element (point) of the rectangle that is the 
     // closest to where the projectile was fired from. 
     hitPos = closestPointTo(projectileStartPos, targetShape.intersected(rectPath)); 

     return true; 
    } 
    return false; 
} 

int main(int argc, char *argv[]) 
{ 
    QApplication app(argc, argv); 

    QGraphicsView view; 
    QGraphicsScene *scene = new QGraphicsScene; 
    view.setScene(scene); 
    view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 
    view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); 

    QGraphicsItem *target = scene->addRect(0, 0, 25, 25); 
    target->setTransformOriginPoint(QPointF(12.5, 12.5)); 
    target->setRotation(35); 
    target->setPos(100, 100); 

    QPainterPath projectilePath; 
    projectilePath.moveTo(200, 200); 
    projectilePath.lineTo(0, 0); 
    projectilePath.lineTo(200, 200); 

    QPointF hitPos; 
    if (hit(projectilePath, scene, hitPos)) { 
     scene->addEllipse(hitPos.x() - 2, hitPos.y() - 2, 4, 4, QPen(Qt::red)); 
    } 

    scene->addPath(projectilePath, QPen(Qt::DashLine)); 
    scene->addText("start")->setPos(180, 150); 
    scene->addText("end")->setPos(20, 0); 

    view.show(); 

    return app.exec(); 
} 

यह बहुत अच्छी सटीक है (± 1 पिक्सेल, के बाद से QLineF::length() एक पूर्णांक है), लेकिन एक ही प्राप्त करने के लिए एक neater तरीका हो सकता है चीज़।

+0

मैंने समान कार्यक्षमता के लिए एक सुझाव जोड़ा है : https://bugreports.qt-project.org/browse/QTBUG-32313 – Mitch

+0

@AmusedToDeath - मैंने आपके परिवर्तन वापस ले लिए हैं - यह कई महीनों पहले एक स्वीकृत उत्तर में एक महत्वपूर्ण कोड परिवर्तन था - यदि कोई समस्या है उत्तर के साथ, मैं इसे पहले चर्चा करने या अपना उत्तर बनाने की सलाह देता हूं। – Krease

+0

मुझे यह जानकर भी उत्सुकता है कि यह गलत क्यों था। :) – Mitch

1

बस रिकॉर्ड के लिए (और यदि कोई और यहां कदम उठाता है)। उपर्युक्त उत्तर उत्कृष्ट है। निकटतम प्वाइंट फ़ंक्शन में बस एक छोटी सी बग है जो तब हो सकती है जब पहला बिंदु पहले से निकटतम हो। यह elementAt (0) - लक्ष्य के बजाय elementAt (0) को वापस करना चाहिए।

QPointF closestPointTo(const QPointF &target, const QPainterPath &sourcePath) 
{ 
    Q_ASSERT(!sourcePath.isEmpty()); 

    QPointF shortestDistance; 
    qreal shortestLength = std::numeric_limits<int>::max(); 

    for (int i = 0; i < sourcePath.elementCount(); ++i) { 
     const QPointF distance(sourcePath.elementAt(i) - target); 
     const qreal length = distance.manhattanLength(); 
     if (length < shortestLength) { 
      shortestDistance = sourcePath.elementAt(i); 
      shortestLength = length; 
     } 
    } 

    return shortestDistance; 
} 
+0

धन्यवाद! :) क्या आप कृपया मेरे उत्तर में उदाहरण के एक संशोधित संस्करण में पेस्टबिन/संपादित कर सकते हैं जो बग दिखाता है? फिर मैं फिक्स को सत्यापित कर सकता हूं और उत्तर अपडेट कर सकता हूं। – Mitch

संबंधित मुद्दे