Check if point exists in QPainterPath

I have an application where I put some curves in my scene. I was looking for an easy way to determine if a user is clicked on a line. boundingRect() and intersects() were too inaccurate when I was drawing multiple lines. So I made this function that works like a dream, except that the lines are vertical.

selectionMargin is a global variable set by the user (default = 0.5). It adjusts the margin for how accurate the selection check is. Names are based on a linear function for each subline, y = ax + b . Pos is a position from mousePressEvent.

 bool GraphApp::pointInPath(QPainterPath path, QPointF pos) { qreal posY = pos.y(); qreal posX = pos.x(); for (int i = 0; i < path.elementCount()-1; ++i) { if (posX < path.elementAt(i + 1).x && posX > path.elementAt(i).x) { qreal dy = path.elementAt(i + 1).y - path.elementAt(i).y; qreal dx = path.elementAt(i + 1).x - path.elementAt(i).x; qreal a = dy / dx; qreal b = path.elementAt(i).y - (path.elementAt(i).x * a); if (selectionMargin == 0.0) selectionMargin = 0.5; qreal lowerBound = (a * posX + b) + selectionMargin; qreal upperBound = (a * posX + b) - selectionMargin; if (posY < lowerBound && posY > upperBound) return true; } } return false; } 

It seems like this function returns false when I send a mousePressEvent from the area covered by vertical lines. My first thought is an if statement:

 if (posX < path.elementAt(i + 1).x && posX > path.elementAt(i).x) 

Any other ideas on how I can implement this without an if clause?

I also saw how other people tried their best to find a good way to check if QPainterPath contains a point without the boundingRect() and intersects() functions, so this can be used for other people as well :)

EDIT: As far as I know, contains() uses boundingRect() . Therefore, I would not see this as the right decision

+6
source share
1 answer

I once needed something similar than you. I needed to check two ways for similarities. Therefore, I created a path from a list of points (I hope you do not need a more complicated path, since this solution will become extremely difficult for general QPaintingPaths). This path is built using the specified "tolerance", this is your selectionMargin .

The function returns a QPainterPath, which "draws the area around the given polyline." This area can then be filled and lead to the same image as drawing the original polyline using tolerance pen width using round caps and round connection options.

You can also, and this is what you want to do, check if the given point is contained in this path. Note that QPainterPath::contains checks that the point lies in a closed area defined by the path. For example, this enclosed area is empty for one line segment and a triangle for two line segments, so this is not what you want if you use contains right in your path (as I mentioned in the third comment on your question).

 QPainterPath intersectionTestPath(QList<QPointF> input, qreal tolerance) { //will be the result QPainterPath path; //during the loop, p1 is the "previous" point, initially the first one QPointF p1 = input.takeFirst(); //begin with a circle around the start point path.addEllipse(p1, tolerance, tolerance); //input now starts with the 2nd point (there was a takeFirst) foreach(QPointF p2, input) { //note: during the algorithm, the pair of points (p1, p2) // describes the line segments defined by input. //offset = the distance vector from p1 to p2 QPointF offset = p2 - p1; //normalize offset to length of tolerance qreal length = sqrt(offset.x() * offset.x() + offset.y() * offset.y()); offset *= tolerance / length; //"rotate" the offset vector 90 degrees to the left and right QPointF leftOffset(-offset.y(), offset.x()); QPointF rightOffset(offset.y(), -offset.x()); //if (p1, p2) goes downwards, then left lies to the left and //right to the right of the source path segment QPointF left1 = p1 + leftOffset; QPointF left2 = p2 + leftOffset; QPointF right1 = p1 + rightOffset; QPointF right2 = p2 + rightOffset; //rectangular connection from p1 to p2 { QPainterPath p; p.moveTo(left1); p.lineTo(left2); p.lineTo(right2); p.lineTo(right1); p.lineTo(left1); path += p; //add this to the result path } //circle around p2 { QPainterPath p; p.addEllipse(p2, tolerance, tolerance); path += p; //add this to the result path } p1 = p2; } //This does some simplification; you should use this if you call //path.contains() multiple times on a pre-calculated path, but //you won't need this if you construct a new path for every call //to path.contains(). return path.simplified(); } 
+4
source

Source: https://habr.com/ru/post/921737/


All Articles