Forums

OverviewFelgo 2 Support (Qt 5) › Showing the path of an object using LineItem

Viewing 8 posts - 1 through 8 (of 8 total)
  • Author
    Posts
  • #18362

    Kool

    Hi All,

    I’m trying to create a ‘path’ that follows a point on screen. There’s usually other content on the screen scrolling past, so the x-pos of the path is modified to give the same effect. My first thought was to use a LineItem.

    Below is an example program, try dragging the black rectangle:

    import Felgo 3.0
    import QtQuick 2.0
    
    App {
        id: gameWindow
    
        NavigationStack {
    
            Page {
                id: page
    
                Rectangle {
                    id: rect
                    width: 25
                    height: 25
                    color: "black"
                    x: page.width / 2
                    y: page.height / 2
                    Drag.active: dragArea.drag.active
    
                    MouseArea {
                        id: dragArea
                        anchors.fill: parent
    
                        drag.axis: Drag.YAxis
                        drag.target: parent
                    }
                }
    
                LineItem {
                    id: path
                    color: "black"
                    lineWidth: 5
    
                    points: []
                    property int resolution: 50
    
                    Timer {
                        interval: 50; repeat: true; running: true; triggeredOnStart: true
                        onTriggered: {
                            if (path.points.length > path.resolution)
                            {
                                path.points.shift()
                            }
    
                            var xVelocity = 20 // pixels per sec
                            var pixelAdjust = xVelocity * (interval / 1000)
                            path.x -= pixelAdjust
    
                            var point = {"x": -path.x + rect.x + rect.width / 2, "y": rect.y + rect.height / 2}
                            path.points.push(point)
    
                            var newPoints = path.points
                            path.points = newPoints
                        }
                    }
                }
            }
        }
    }

     

    Notice how a line is rendered providing a view of the previous positions of the rectangle, in a sense the path of the rectangle.
    It works fine in the first instance, but I found that if you drag it up and down rapidly from top to bottom of the page everything slows down a lot but eventually catches up.

     

    I’ve tried lots of different variations to speed things up, including rolling my own Canvas/Context2D version but nothing gives the desired effect with performance in mind.
    Can anyone suggest what the problem might be or a better way to go about such an effect?

     

    Many thanks!

    #18365

    Marcin

    Hi Kool,
    yeah, it becomes very sluggish very quickly.
    I did short profile and, obviously, bottleneck is the LineItem.qml::onPaint().
    You said you tried canvas and still no luck?

    Could you please try this one, it’s from qt book(by Juergen Bocklage-Ryannel, Johan Thelin), chapter 07, about canvas.
    It is not mobile friendly or anything, but it’s a simple painting editor which is quite fast, maybe it can help:

    import QtQuick 2.0
    
    Rectangle {
        width: 400; height: 300
        color: "#333333"
    
        // M1>>
        Row {
            id: colorTools
            anchors {
                horizontalCenter: parent.horizontalCenter
                top: parent.top
                topMargin: 8
            }
            property variant activeSquare: red
            property color paintColor: "#33B5E5"
            spacing: 4
            Repeater {
                model: ["#33B5E5", "#99CC00", "#FFBB33", "#FF4444"]
                ColorSquare {
                    id: red
                    color: modelData
                    active: parent.paintColor == color
                    onClicked: {
                        parent.paintColor = color
                    }
                }
            }
        }
        // <<M1
    
        Rectangle {
            anchors.fill: canvas
            border.color: "#666666"
            border.width: 4
        }
    
        // M2>>
        Canvas {
            id: canvas
            anchors {
                left: parent.left
                right: parent.right
                top: colorTools.bottom
                bottom: parent.bottom
                margins: 8
            }
            property real lastX
            property real lastY
            property color color: colorTools.paintColor
    
            onPaint: {
                var ctx = getContext('2d')
                ctx.lineWidth = 1.5
                ctx.strokeStyle = canvas.color
                ctx.beginPath()
                ctx.moveTo(lastX, lastY)
                lastX = area.mouseX
                lastY = area.mouseY
                ctx.lineTo(lastX, lastY)
                ctx.stroke()
            }
            MouseArea {
                id: area
                anchors.fill: parent
                onPressed: {
                    canvas.lastX = mouseX
                    canvas.lastY = mouseY
                }
                onPositionChanged: {
                    canvas.requestPaint()
                }
            }
        }
        // <<M2
    }

    Ps. It has some small errors but in general works quite nice, at least for me.

    #18367

    Marcin

    Another option, since Qt 5.10, is using shapes
    http://blog.qt.io/blog/2017/07/07/let-there-be-shapes/
    it supports more complex scenarios but lines should be also fine.
    What is important that this should be accelerated by GPU, where possible, so there is a chance performance will be fine.

    #18371

    Kool

    senkal said:

    Hi Kool,
    yeah, it becomes very sluggish very quickly.
    I did short profile and, obviously, bottleneck is the LineItem.qml::onPaint().
    You said you tried canvas and still no luck?

    Could you please try this one, it’s from qt book(by Juergen Bocklage-Ryannel, Johan Thelin), chapter 07, about canvas.
    It is not mobile friendly or anything, but it’s a simple painting editor which is quite fast, maybe it can help:

    import QtQuick 2.0
    
    Rectangle {
        width: 400; height: 300
        color: "#333333"
    
        // M1>>
        Row {
            id: colorTools
            anchors {
                horizontalCenter: parent.horizontalCenter
                top: parent.top
                topMargin: 8
            }
            property variant activeSquare: red
            property color paintColor: "#33B5E5"
            spacing: 4
            Repeater {
                model: ["#33B5E5", "#99CC00", "#FFBB33", "#FF4444"]
                ColorSquare {
                    id: red
                    color: modelData
                    active: parent.paintColor == color
                    onClicked: {
                        parent.paintColor = color
                    }
                }
            }
        }
        // <<M1
    
        Rectangle {
            anchors.fill: canvas
            border.color: "#666666"
            border.width: 4
        }
    
        // M2>>
        Canvas {
            id: canvas
            anchors {
                left: parent.left
                right: parent.right
                top: colorTools.bottom
                bottom: parent.bottom
                margins: 8
            }
            property real lastX
            property real lastY
            property color color: colorTools.paintColor
    
            onPaint: {
                var ctx = getContext('2d')
                ctx.lineWidth = 1.5
                ctx.strokeStyle = canvas.color
                ctx.beginPath()
                ctx.moveTo(lastX, lastY)
                lastX = area.mouseX
                lastY = area.mouseY
                ctx.lineTo(lastX, lastY)
                ctx.stroke()
            }
            MouseArea {
                id: area
                anchors.fill: parent
                onPressed: {
                    canvas.lastX = mouseX
                    canvas.lastY = mouseY
                }
                onPositionChanged: {
                    canvas.requestPaint()
                }
            }
        }
        // <<M2
    }

    Ps. It has some small errors but in general works quite nice, at least for me.

    This one is quite interesting. One of the reasons the LineItem and my own Canvas approach were slow because of the iteration over 100 points in the paint method. The approach in your example is ‘store the last known point’ and draw from there in canvas coordinates.

     

    To get the ‘sliding’ effect one could just apply a translation via Context2D::translate.

     

    I’ll let you know how it goes, thanks again Marcin!

     

    #18394

    Kool

    senkal said:

    Another option, since Qt 5.10, is using shapes
    http://blog.qt.io/blog/2017/07/07/let-there-be-shapes/
    it supports more complex scenarios but lines should be also fine.
    What is important that this should be accelerated by GPU, where possible, so there is a chance performance will be fine.

    Hi Marcin,

    Just to say I managed to roll my own using a combination of your example and the following: https://stackoverflow.com/questions/19142773/how-to-generate-canvas-moving-waves-using-dynamic-x-y-values-in-html5

     

    I’d forgotten that QML’s Context2D is based on HTML5’s canvas, of which there’s wealth of examples to follow. I managed to get it running smoothly with minimal overhead.

     

    Thanks again for your help!

    #18395

    Marcin

    Hi Kool,
    Happy to hear that.

    #18396

    Alex
    Felgo Team

    Hi Kool,

    would you like to share your solution with a small code example like the ones above? Would be really nice to try.

    Cheers,
    Alex

    #18457

    Kool

    alex.huber said:

    Hi Kool,

    would you like to share your solution with a small code example like the ones above? Would be really nice to try.

    Cheers,
    Alex

     

    Enjoy:

    import Felgo 3.0
    import QtQuick 2.0
    
    App {
        id: app
    
        NavigationStack {
    
            Page {
                id: page
    
                Rectangle {
                    id: rect
                    width: 25
                    height: 25
                    color: "black"
                    anchors.right: parent.right
                    y: page.height / 2
                    Drag.active: dragArea.drag.active
    
                    MouseArea {
                        id: dragArea
                        anchors.fill: parent
    
                        drag.axis: Drag.YAxis
                        drag.target: parent
                    }
                }
    
                Canvas {
                    id: canvas
                    height: parent.height
                    anchors {left: parent.left; right: parent.right}
                    z: 2
                    antialiasing: true
    
                    property real now
                    property real xVelocity: 50 // px/s
                    property var points: []
    
                    onPaint: {
                        var ctx = getContext("2d")
                        ctx.lineWidth = 3;
                        ctx.strokeStyle = Theme.tintColor
                        ctx.clearRect(0, 0, canvas.width, canvas.height);
    
                        // Record a new point
                        var delta = -now + (now = Date.now());
                        var rectY = rect.y + rect.width / 2
                        points.push(points.length === 0 ? {delta: 0, y: rectY} : {delta: delta, y: rectY})
    
                        // Draw path
                        ctx.beginPath()
                        var currentX = canvas.width
                        for (var ii = points.length - 1; ii >= 0; --ii)
                        {
                            currentX -= xVelocity * (points[ii].delta / 1000.0)
                            ctx.lineTo(currentX, points[ii].y)
    
                            if (currentX < 0)
                            {
                                points.splice(0, ii)
                                break
                            }
                        }
    
                        ctx.stroke()
                    }
    
                    Timer {
                        id: canvasTimer
                        interval: 50; running: true; repeat: true
    
                        onTriggered: {
                           canvas.requestPaint()
                        }
                    }
                }
            }
        }
    }
    

    There’s a variety of novel things you could do with that. I’ve not yet had the chance to test it on Android though, which is where I noticed the major performance issues with other approaches.

Viewing 8 posts - 1 through 8 (of 8 total)

RSS feed for this thread

You must be logged in to reply to this topic.

Voted #1 for:

  • Easiest to learn
  • Most time saving
  • Best support

Develop Cross-Platform Apps and Games 50% Faster!

  • Voted the best supported, most time-saving and easiest to learn cross-platform development tool
  • Based on the Qt framework, with native performance and appearance on all platforms including iOS and Android
  • Offers a variety of plugins to monetize, analyze and engage users
FREE!
create apps
create games
cross platform
native performance
3rd party services
game network
multiplayer
level editor
easiest to learn
biggest time saving
best support