4
votes

Below code allows the small red coloured rectangle to be dragged in an area which is a rectangle defined by minimum and maximum drag values.

I want it to go on only up till the boundary of the parent rectangle whose radius is 100 which means that it is now a circle.

How to make an item drag inside a circle in QML?

Window {
    width: 200; height: 200; visible: true

    Rectangle
    {
        x: 10; y: 10
        width: 200; height: 200
        radius: 100
        color: "blue"

        Rectangle {
            x: 10; y: 10
            width: 20; height: 20
            color: "red"

            MouseArea
            {
                id: dragArea
                anchors.fill: parent

                drag.target: parent
                drag.minimumX : 20
                drag.maximumX : 150

                drag.minimumY : 20
                drag.maximumY : 150
            }
        }
    }
}
2
Not sure if the drag/drop properties would accept a function. If so you easily setup sin/cos values.Sebastian Lange
You have to test all 4 vertex point of parent rectangle if they are inside the circle. The radius is max length from the center that they can go. Try this Math.pow((x - center_x),2) + Math.pow(y - center_y,2)) < Math.pow(r,2)folibis

2 Answers

5
votes

So I found some time to provide the aforementioned smoother solution.

import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    id: root
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    property int radius: 100

    Rectangle {
        id: circle
        width: 2 * radius
        height: 2 * radius
        radius: root.radius

        color: 'blue'
    }

    Rectangle {
        id: mark
        width: 20
        height: 20
        x: (dragObj.dragRadius <= root.radius ? dragObj.x : root.radius + ((dragObj.x - root.radius) * (root.radius / dragObj.dragRadius))) - 10
        y: (dragObj.dragRadius <= root.radius ? dragObj.y : root.radius + ((dragObj.y - root.radius) * (root.radius / dragObj.dragRadius))) - 10
        color: 'red'

        MouseArea {
            id: markArea
            anchors.fill: parent
            drag.target: dragObj
            onPressed: {
                dragObj.x = mark.x + 10
                dragObj.y = mark.y + 10
            }
        }
    }

    Item {
        id: dragObj
        readonly property real dragRadius: Math.sqrt(Math.pow(x - root.radius, 2) + Math.pow(y - root.radius, 2))
        x: root.radius
        y: root.radius

        onDragRadiusChanged: console.log(dragRadius)
    }
}

I use the dragObj to avoid the limitations of my dragging position. This spans a vector from the center of the circle. As long as the dragObj itself is contained in the circle, I will use it's position as the position of the marker.
But once it leaves the circle, I will project the vector on the circle, so it will stay within the limits.

To ensure, that every drag starts again on the mark, I will reset the position of the dragObj to the position of the mark, when ever the mouse is pressed again (precondition for a new drag-event)

Have fun with it.

1
votes

A little bit late, but I was up for some quiz.

Here is a imperfect solution, with some flaws.

import QtQuick 2.5
import QtQuick.Window 2.2

Window {
    width: 400; height: 400; visible: true

    Rectangle
    {
        x: 10; y: 10
        width: 200; height: 200
        radius: 100
        color: "blue"

        Rectangle {
            x: 10; y: 10
            width: 20; height: 20
            color: "red"

            MouseArea
            {
                id: dragArea
                anchors.fill: parent

                drag.target: parent
                drag.minimumX : Math.ceil(100 - Math.sqrt(200 * parent.y - Math.pow(parent.y, 2)))
                drag.maximumX : Math.floor(Math.sqrt(200 * parent.y - Math.pow(parent.y, 2)) + 100)

                drag.minimumY : Math.ceil(100 - Math.sqrt(200 * parent.x - Math.pow(parent.x, 2)))
                drag.maximumY : Math.floor(Math.sqrt(200 * parent.x - Math.pow(parent.x, 2)) + 100)
            }
        }
    }
}

The idea is, that you calculate the max- and min-values of x and y depending on the current value of y and x (so max-x is depending on current-y, and max-y is depending on current-x)

The function behind that is: (r - y)² + (r - x)² = r²

What I did not incorporate is the dimension of the small draggable Rectangle. So I only force the top-left corner to stay within the circles bounds. This however should be easy to adapt. To account for that, assume a circle, with half the rectangles width/height less radius, and shift it by half the rectangles width/height.

Another flaw is, that if I reach one of the limits (top, left, right, bottom) the Rectangle might flicker and does not follow the mouse smoothly.

To fix that, you can use an invisible object as helper, that can be freely drawn around, and you then use it's position to calculate the red Rectangles position that is shown.

Maybe I will add an implementation for that later as well. I am pretty sure, this should work pretty smoothly.