Qt Quick 3D - Robot Arm Example

 // Copyright (C) 2022 The Qt Company Ltd.
 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause

 import QtQuick
 import QtQuick3D
 import QtQuick.Controls.Material
 import QtQuick.Controls
 import QtQuick.Layouts
 import RobotArm
 import Backend
 import QtQml

 Pane {
     id: root
     Material.theme: darkModeToggle.checked ? Material.Dark : Material.Light

     readonly property bool mobile: Qt.platform.os === "android"
     readonly property bool horizontal: width > height
     property real sliderWidth: width * 0.15
     property real buttonRowWidth: width * 0.12
     property real buttonMinWidth: 65

     leftPadding: 60
     rightPadding: 60
     topPadding: 50
     bottomPadding: 50

     width: 800
     height: 600
     state: "mobileHorizontal"

     Backend {
         id: backend
         rotation1Angle: rotation1Slider.value
         rotation2Angle: rotation2Slider.value
         rotation3Angle: rotation3Slider.value
         rotation4Angle: rotation4Slider.value
         clawsAngle: clawToggle.checked ? 0 : 90
     }

     Toggle {
         id: darkModeToggle
         text: qsTr("Dark mode")
         anchors.top: parent.top
     }

     ColumnLayout {
         id: slidersColumn
         spacing: 6
         anchors.bottom: parent.bottom

         LabeledSlider {
             id: rotation1Slider
             Layout.preferredWidth: root.sliderWidth
             Layout.minimumWidth: 160
             labelText: "Rotation 1"
             from: -90
             to: 90
             value: 60
         }

         LabeledSlider {
             id: rotation2Slider
             Layout.preferredWidth: root.sliderWidth
             Layout.minimumWidth: 160
             labelText: "Rotation 2"
             from: -135
             to: 135
             value: 45
         }

         LabeledSlider {
             id: rotation3Slider
             Layout.preferredWidth: root.sliderWidth
             Layout.minimumWidth: 160
             labelText: "Rotation 3"
             from: -90
             to: 90
             value: 45
         }

         LabeledSlider {
             id: rotation4Slider
             Layout.preferredWidth: root.sliderWidth
             Layout.minimumWidth: 160
             labelText: "Rotation 4"
             from: -180
             to: 180
         }
     }

     Toggle {
         id: clawToggle
         text: qsTr("Claw")
         anchors.bottom: slidersColumn.top
         anchors.bottomMargin: 30
     }

     GridLayout {
         id: buttonsRow
         columns: 2
         rows: 2
         columnSpacing: 16
         rowSpacing: 8
         anchors.bottom: clawToggle.top
         anchors.bottomMargin: 30

         Button {
             id: pose1
             text: qsTr("Pose 1")
             Layout.preferredWidth: root.buttonRowWidth / 2
             Layout.minimumWidth: root.buttonMinWidth
             Layout.preferredHeight: 45

             Connections {
                 target: pose1
                 onClicked: {
                     rotation1Slider.value = 30
                     rotation2Slider.value = 60
                     rotation3Slider.value = 90
                     rotation4Slider.value = 145
                 }
             }
         }

         Button {
             id: pose2
             text: qsTr("Pose 2")
             Layout.preferredWidth: root.buttonRowWidth / 2
             Layout.minimumWidth: root.buttonMinWidth
             Layout.preferredHeight: 45

             Connections {
                 target: pose2
                 onClicked: {
                     rotation1Slider.value = 60
                     rotation2Slider.value = 45
                     rotation3Slider.value = 45
                     rotation4Slider.value = 60
                 }
             }
         }

         Button {
             id: pose3
             text: qsTr("Pose 3")
             Layout.preferredWidth: root.buttonRowWidth / 2
             Layout.minimumWidth: root.buttonMinWidth
             Layout.preferredHeight: 45

             Connections {
                 target: pose3
                 onClicked: {
                     rotation1Slider.value = -90
                     rotation2Slider.value = -60
                     rotation3Slider.value = -45
                     rotation4Slider.value = -180
                 }
             }
         }

         Button {
             id: resetPose
             text: qsTr("Reset")
             Layout.preferredWidth: root.buttonRowWidth / 2
             Layout.minimumWidth: root.buttonMinWidth
             Layout.preferredHeight: 45

             Connections {
                 target: resetPose
                 onClicked: {
                     rotation1Slider.value = 0
                     rotation2Slider.value = 0
                     rotation3Slider.value = 0
                     rotation4Slider.value = 0
                     clawToggle.checked = false
                 }
             }
         }
     }

     View3D {
         anchors.fill: parent

         camera: camera
         Node {
             id: scene

             PointLight {
                 x: 760
                 z: 770
                 quadraticFade: 0
                 brightness: 1
             }

             DirectionalLight {
                 eulerRotation.z: 30
                 eulerRotation.y: -165
             }

             DirectionalLight {
                 y: 1000
                 brightness: 0.4
                 eulerRotation.z: -180
                 eulerRotation.y: 90
                 eulerRotation.x: -90
             }

             PerspectiveCamera {
                 id: camera
                 x: 1050
                 y: 375
                 z: -40
                 pivot.x: 200
                 eulerRotation.y: 85
             }
             RoboticArm {
                 id: roboArm
                 rotation1: backend.rotation1Angle
                 rotation2: backend.rotation2Angle
                 rotation3: backend.rotation3Angle
                 rotation4: backend.rotation4Angle
                 clawsAngle: backend.clawsAngle
             }
         }

         NodeIndicator {
             scenePosition: roboArm.hand_position
             isFocused: clawToggle.hasFocus
             label: clawToggle.text
             size: 30
         }

         NodeIndicator {
             scenePosition: roboArm.hand_hinge_position
             isFocused: rotation1Slider.activeFocus
             label: rotation1Slider.labelText
             size: 40
         }

         NodeIndicator {
             scenePosition: roboArm.arm_position
             isFocused: rotation2Slider.activeFocus
             label: rotation2Slider.labelText
             size: 50
         }

         NodeIndicator {
             scenePosition: roboArm.forearm_position
             isFocused: rotation3Slider.activeFocus
             label: rotation3Slider.labelText
             size: 60
         }

         NodeIndicator {
             scenePosition: roboArm.root_position
             isFocused: rotation4Slider.activeFocus
             label: rotation4Slider.labelText
             size: 60
         }

         environment: sceneEnvironment

         SceneEnvironment {
             id: sceneEnvironment
             antialiasingQuality: SceneEnvironment.VeryHigh
             antialiasingMode: SceneEnvironment.MSAA
         }
     }

     Label {
         id: robotStatus
         text: backend.status
         anchors.top: parent.top
         font.italic: true
         anchors.horizontalCenter: parent.horizontalCenter
         anchors.topMargin: 15
     }

     states: [
         State {
             name: "mobileHorizontal"
             when: root.mobile && root.horizontal

             PropertyChanges {
                 target: root
                 leftPadding: 45
                 topPadding: 15
                 bottomPadding: 0
                 sliderWidth: width * 0.4
                 buttonRowWidth: width * 0.2
                 buttonMinWidth: 75
             }

             PropertyChanges {
                 target: roboArm
                 z: -200
             }
         },
         State {
             name: "desktopVertical"
             when: !root.mobile && !root.horizontal

             PropertyChanges {
                 target: root
                 sliderWidth: width * 0.4
                 buttonRowWidth: width * 0.2
                 bottomPadding: 20
             }
             AnchorChanges {
                 target: slidersColumn
                 anchors.right: parent.right
             }
             PropertyChanges {
                 target: slidersColumn
                 anchors.rightMargin: 20
             }

             AnchorChanges {
                 target: buttonsRow
                 anchors.bottom: undefined
                 anchors.top: slidersColumn.top
             }

             AnchorChanges {
                 target: clawToggle
                 anchors.bottom: undefined
                 anchors.top: buttonsRow.bottom
             }

             PropertyChanges {
                 target: roboArm
                 scale.x: 0.7
                 scale.y: 0.7
                 scale.z: 0.7
                 y: 250
                 z: 150
             }
         },
         State {
             name: "mobileVertical"
             when: root.mobile && !root.horizontal

             PropertyChanges {
                 target: root
                 sliderWidth: width * 0.85
                 topPadding: 15
                 leftPadding: 45
                 bottomPadding: 0
                 buttonRowWidth: width * 0.2
                 buttonMinWidth: 75
             }

             AnchorChanges {
                 target: slidersColumn
                 anchors.left: undefined
                 anchors.horizontalCenter: parent.horizontalCenter
             }

             AnchorChanges {
                 target: clawToggle
                 anchors.left: undefined
                 anchors.right: slidersColumn.right
             }

             AnchorChanges {
                 target: buttonsRow
                 anchors.bottom: slidersColumn.top
                 anchors.left: slidersColumn.left
             }

             PropertyChanges {
                 target: roboArm
                 scale.x: 0.7
                 scale.y: 0.7
                 scale.z: 0.7
                 y: 280
                 z: 100
             }
         }
     ]

     transitions: Transition {
         PropertyAnimation {
             properties: "sliderWidth, scale.x, scale.y, scale.z, y, z"
         }
         AnchorAnimation {}
     }
 }