Draw a dashed and dotted bezier curve in QML

Wojciech Danilo picture Wojciech Danilo · Aug 20, 2013 · Viewed 9.3k times · Source

I've seen there is an example implementation of a Bezier curve in QML, but I'm looking for a hint how to implement dashed or dotted bezier curve line. As far as I see, tha authors of Bezier curve example are using QSGGeometryNode to store inside QSGGeometry with a QSGFlatColorMaterial material applied on it. Then they simply create list of points and draw segments between them.

Is it possible to write a shader and apply it to QSGFlatColorMaterial (to display line as dashed, dotted, etc)?

Eventually, is it possible to store more than one QSGGeometry inside QSGGeometryNode?

UPDATE

I would like to implement this in "pure QtQuick" - not in "old" interfaces (like QPainter etc) - because I do not want to use something, which switches context (openGL and CPU). I prefer the solution with custom shader (if is it doable) - because I'll have more possibilities in implementing custom look and feel (dashed, doted, colored, maybe animated etc).

If it is not possible, I'll use QPainter.

Answer

dtech picture dtech · Aug 26, 2013

I don't think this task is a good candidate for implementing using QSGGeometryNode, it would be much easier to implement it using QPainter based drawing and a QQuickPaintedItem. You will still get the benefits of OpenGL, since QPainter supports GL drawing as well and it is still faster than software. You can use the stock QPen with stock dotted or dashed patterns or make your own with a simple QVector.

Alternatively, you can go for a custom GL drawing approach instead of using the classes provided by Qt, which are pretty limited when it comes to representing advanced compound geometry. You can even use instancing (if available) to improve performance even further, and just position dashes or dot geometry along the path curve.

Last but not least, you can use a QML Canvas element, which supports pretty much the same operations as QPainter and probably offers the same performance.

EDIT: As your update suggests, you missed the part where I said QPainter can draw in both software and GL, with GL drawing being often significantly faster. Also, by drawing to a GL context, you don't have to move the framebuffer from CPU to GPU memory, it is kept in GPU memory. So no overhead. As for animations and the other stuff, sure, it is not possible with QPainter you are limited to whatever QPen provides - different joins, caps and so on can be used to modify the shape to some extent, but no miracles... It won't be possible with shaders too, it will only be possible with custom geometry. And if you use a QObject based object for each dash/dot element in order to independently animate them, it will end up quite expenssive, QObject is very heavy and should not be used with such light hand. So custom GL rendering to a FBO is pretty much the way to go if you want that kind of flexibility, but you will have to move completely out of the QtQuick API and into GL land.

At any rate, a dashed line shader should not be all that complex, basically you color the fragment based on the distance from the curve and the "period" along its length. I found this example, haven't tried it myself. You could animate the thresholds, even use a sine function to get funky looking styling.

As for a "pure" QtQuick implementation, the API has not really been designed to handle such type of drawing tasks, that is why the Canvas element was provided to fill the gap and get advanced paining functionality from QML/JS. The Canvas is effectively a wrapper around QPainter that draws onto a FBO.

In the end it doesn't boil down to what is possible/impossible but which approach makes the most sense and is most efficient at getting the job done. Try the QQuickPaintedItem approach first, since it is the easiest, if you are not happy with performance, you can implement another more complex solution and profile against the first. After all, that is why QQuickPaintedItem was introduced in the first place - to handle legacy painting that is not convenient to do with the QQuickItem class.