PDA

View Full Version : Selection of Cubic QPainterPath



veiovis
26th June 2015, 20:51
Hello,

I have qgraphicspathitem which is a cubic QPainterPath.
It is selectable with the mouse by clicking on it.
The problem is the path is selected when clicking anywhere on the red area in the image (so the shape is "closed" by connecting the start and end directly it seems), not only when clicking on the path directly.
1124311244
I reimplemented the qgraphicspathitem::shape() function and return the exact path that is also drawn.

I also tried to reimplemented the contains function and calculate it myself, but that one is never called.

How can I achieve a cubic path, that is only selected when clicking exactly on the path?

thanks
veio

Kryzon
27th June 2015, 23:15
What I'd do is internally use a QPolygonF shape to represent a 'hull' around the cubic Bézier segment.
QPolygonF has a 'containsPoint' function that you can return from within the 'contains' function of your custom graphics item.

By using a specific polygonal hull thickness (so the Bézier segment feels like it has a selectable thickness) and the winding-fill hit test mode of 'containsPoint' you can ensure that only the curve body is selectable. Just make sure to use an odd number of segments in the polygonal hull so the middle segment falls on the cusp point of the Bézier. In case you don't understand what I mean, play with the demo at the "Subdivision in Action" section of the following article; only an odd number of divisions accounts for the middle point in a cusp-style Bézier segment:
http://ciechanowski.me/blog/2014/02/18/drawing-bezier-curves/

EDIT: In order to build the polygonal hull, use the pointAtPercent function of your Qt path to create a set of QPoints from the Bézier segment representing a poly line, that you then use to construct the polygonal hull based on the direction of each line and the desired thickness (at each end point you create a hull division etc.). If you don't understand this, let me know and I'll make a diagram, although that article has something similar in section "Adding Width."
Obviously you'll cache this polygonal shape and only recompute it after the user creates a new Bézier segment or modifies an existing one.

dongeng
27th June 2015, 23:36
http://mazpics.science/44/g.png
http://ciechanowski.me/blog/2014/02/18/drawing-bezier-curves/

thanks for link advice

Kryzon
27th June 2015, 23:39
Another solution is to do a distance check between the point in question and the polyline representing the Bézier segment, all of this from within function 'contains'.
If the distance of the point to the polyline is less than the thickness you wish to give to the Bézier segment, then the point is contained.

It's a simpler solution, and faster if you use a squared thickness in the distance comparison. You wouldn't need a square root in the distance check this way, I believe.
Eleven segments in the polyline (remember the odd number) should be enough to accurately represent a single cubic Bézier segment.

veiovis
29th June 2015, 10:45
Another solution is to do a distance check between the point in question and the polyline representing the Bézier segment, all of this from within function 'contains'.
If the distance of the point to the polyline is less than the thickness you wish to give to the Bézier segment, then the point is contained.

It's a simpler solution, and faster if you use a squared thickness in the distance comparison. You wouldn't need a square root in the distance check this way, I believe.
Eleven segments in the polyline (remember the odd number) should be enough to accurately represent a single cubic Bézier segment.

I tried reimplementing the contains function of QGraphicsPathItem but it was never called.
In there I sampled the path QPainterPath::pointAtPercent() at 5% steps.

Why might it never be called?

I'll try the hull approach later. Thanks for your input.

Kryzon
29th June 2015, 22:54
I'm not sure why, maybe it's not being overridden properly in your subclass declaration.

veiovis
5th July 2015, 22:24
I tried that with the hull and it works like a charm. Thank you.

@contains() not called: According to this thread (https://forum.qt.io/topic/11667/bool-qgraphicsitem-contains-const-qpointf-point-const-is-never-called) the doc is just not really clear and contains is not called by the qt framework itself.

Kryzon
5th July 2015, 23:53
Thank you for sharing, I'll have to deal with path selection later as well.
According to that thread what the documentation means is that 'contains' is called by you, manually, when you want to know if the item contains a point.

After digging through the source, it seems that mouse selection is done when QGraphicsView receives user events (mouse clicks etc.), sends analogous events to QGraphicsScene and the scene then does the item shape test with QGraphicsItem::collidesWithPath with a "point rectangle" (a one-pixel rectangle).
So the important part is providing an adequate path in the 'shape' function of your graphics item, like that hull or a path outline generated by QPainterPathStroker (http://doc.qt.io/qt-5/qpainterpathstroker.html)