2013-07-07 22 views
6

ヒットスキャンの発射経路(基本的に行ですが、私の例ではQPainterPathとして表現しています)がどこにあるかを判断しようとしています。私のシーン。 QPainterPathQLineFによって提供される機能を使用して、このポイントを見つけるための方法がある場合、私は以下のコードは、私が何をしようとしている示しなど、わからない:行と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

あなたはあなたの質問に多くの特定のタグを追加するために良いことができQtの4かのQt 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()は整数であるため)が、同じことを達成するためにすっきり方法があるかもしれませんもの。

+0

類似の機能を追加するための提案を作成しました:https://bugreports.qt-project.org/browse/QTBUG-32313 – Mitch

+0

@AmusedToDeath - 変更をロールバックしました。問題が発生した場合は、数か月前から受け入れられた回答に重大なコード変更が加えられました答えは、最初に議論するか、自分の答えを作成することをお勧めします。 – Krease

+0

私はまたそれがなぜ間違っていたのか知​​りたいです。 :) – Mitch

1

レコードのためだけに(そして他の誰かがここに踏み込んだ場合)。上記の答えは優れています。 closestPoint関数には、最初のポイントが既に最も近いポイントである場合に発生するかもしれない小さなバグがあります。 elementAt(0) - targetの代わりに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

ありがとう! :)私はバグを示す私の答えの例の変更されたバージョンでpastebin/editをしていただけますか?それで私は修正を検証し、答えを更新することができます。 – Mitch

関連する問題