1

I have following challenge

enter image description here

I have to simulate a rotary knob. Both green cicles are fix, the yellow one should be rotated around the dark green one on mouse drag.

To keep this simple, I calculate the angle by which the outer rectangle (the light green one) must be rotated depending on the mouse position.

This almost works, but it flickers massive with every mouse movement and it looks like the yellow circle always jumps back to the starting position.

Here my code

import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Rectangle {
    id: encoder
    color: "lightgreen";
    height: 150;
    width: height;
    radius: (height / 2);
    property real centerX : (width / 2);
    property real centerY : (height / 2);
    antialiasing: true;

    Rectangle {
        id: center
        color: "darkgreen";
        height: parent.height * 0.4;
        width: height;
        radius: (height / 2);
        border.color: "black"
        border.width: 0
        anchors {
            horizontalCenter: parent.horizontalCenter
            verticalCenter: parent.verticalCenter
        }
        MouseArea {
            anchors.fill: parent
            onPressed: center.border.width = 2
            onReleased: center.border.width = 0
        }
    }
    
    Rectangle {
        id: wheel
        color: "yellow";
        height: parent.height * 0.2
        width: height
        radius: (height / 2)
        anchors {
            top: parent.top;
            margins: 10;
            horizontalCenter: parent.horizontalCenter;
        }
        MouseArea{
            id: mouseArea
            anchors.fill: parent;
            onPositionChanged: {
                var point =  mapToItem (encoder, mouse.x, mouse.y);
                var diffX = (point.x - encoder.centerX)
                var diffY = -1 * (point.y - encoder.centerY)
                var rad = Math.atan (diffY / diffX)
                var deg = (rad * 180 / Math.PI)
                if (diffX > 0 && diffY > 0) {
                    encoder.rotation = 90 - Math.abs (deg);
                }
                else if (diffX > 0 && diffY < 0) {
                    encoder.rotation = 90 + Math.abs (deg);
                }
                else if (diffX < 0 && diffY > 0) {
                    encoder.rotation = 270 + Math.abs (deg);
                }
                else if (diffX < 0 && diffY < 0) {
                    encoder.rotation = 270 - Math.abs (deg);
                }
                else 
                    console.log ("nix");
            }
        }
    }
}

Thanks and best regards

Arne

user2377283
  • 365
  • 1
  • 2
  • 12
  • I once increased the angle of the right-hand rotation by only 1, and the flickering disappeared. The calculated values seem, at least according to log output but also correct – user2377283 Jan 11 '23 at 10:40

1 Answers1

4

The issue with your approach is that the encoder item is rotating and this will be part of the mouse position mapping as well. You can see that clearly when you rotate half way and then release and grab again. Each following rotation will toggle between two completely different rotation angles hence the flickering.

I solved the issue by giving your component a base Item which is static (not affected by rotation) and used for the mapping.

Edit: I've improved the rotation calculation by utilizing Qt.vector2d and the steps described here to calculate the angle between 2d vectors.

import QtQuick

Window {
    id: root
    width: 240
    height: 240
    visible: true

    Item {
        id: base

        property real centerX: base.width / 2
        property real centerY: base.height / 2

        height: 150
        width: base.height
        anchors.centerIn: parent

        Rectangle {
            id: encoder
            anchors.fill: parent
            radius: encoder.height / 2
            color: "lightgreen"
            antialiasing: true

            Rectangle {
                id: center
                height: parent.height * 0.4
                width: center.height
                radius: center.height / 2
                color: "darkgreen"
                border.color: "black"
                border.width: centerMouseArea.pressed ? 2 : 0
                anchors {
                    horizontalCenter: parent.horizontalCenter
                    verticalCenter: parent.verticalCenter
                }

                MouseArea {
                    id: centerMouseArea
                    anchors.fill: parent
                }
            }

            Rectangle {
                id: knob
                height: parent.height * 0.2
                width: knob.height
                radius: knob.height / 2
                color: "yellow"
                anchors {
                    top: parent.top
                    margins: 10
                    horizontalCenter: parent.horizontalCenter
                }

                MouseArea {
                    id: mouseArea
                    anchors.fill: parent
                    onPositionChanged: function(mouse) {
                        var point = mapToItem(base, mouse.x, mouse.y)

                        var a = Qt.vector2d(0, -base.centerY)
                        var b = Qt.vector2d(point.x - base.centerX, point.y - base.centerY)

                        var dot = a.dotProduct(b) // dot product
                        var det = a.x * b.y - a.y * b.x // determinant

                        encoder.rotation = Math.atan2(det, dot) * 180 / Math.PI
                    }
                }
            }
        }
    }
}

enter image description here

iam_peter
  • 3,205
  • 1
  • 19
  • 31
  • Hello Peter, what can I say I am really speechless. Thanks for your detailed explaining what I did wrong and thanks again for the provided solution - the calculation via the vector is of course even more charming. Works perfect. Thanks – user2377283 Jan 11 '23 at 12:43
  • 1
    Glad to hear that it solved your issue. Consider up voting answers that helped you or accepting them if it solved your issue. This way you give credit to the person that helped you and also shows other people that your issue got solved. – iam_peter Jan 11 '23 at 13:01
  • Shure I like to do that - but I'am sorry to ask - how will I do that? – user2377283 Jan 11 '23 at 13:22