Use separate MouseArea3D to calculate drag vector

Using the MouseArea3D linked to gizmo to manage the drag leads to
slight unwanted drift on the drag plane, as it moves with the gizmo.
Fixed by using a helper MouseArea3D to calculate drag vector.
Helper area's transform is set at mouse press time and is not changed
during the drag.

Change-Id: I50d3f738277bd34005c977423d114abc8dbed520
Fixes: QDS-1312
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Miikka Heikkinen
2019-12-10 13:14:30 +02:00
parent 128c7a34af
commit 783e5d0a8d
12 changed files with 92 additions and 20 deletions

View File

@@ -35,6 +35,7 @@ Model {
property Node targetNode: null
property bool dragging: mouseAreaYZ.dragging || mouseAreaXZ.dragging
property bool active: false
property MouseArea3D dragHelper: null
readonly property bool hovering: mouseAreaYZ.hovering || mouseAreaXZ.hovering
@@ -57,7 +58,7 @@ Model {
return;
var maskedPosition = Qt.vector3d(scenePos.x, 0, 0);
_pointerPosPressed = mouseArea.mapPositionToScene(maskedPosition);
_pointerPosPressed = mouseArea.dragHelper.mapPositionToScene(maskedPosition);
if (targetNode.orientation === Node.RightHanded)
_pointerPosPressed.z = -_pointerPosPressed.z;
var sp = targetNode.scenePosition;
@@ -68,7 +69,7 @@ Model {
function calcRelativeDistance(mouseArea, scenePos)
{
var maskedPosition = Qt.vector3d(scenePos.x, 0, 0);
var scenePointerPos = mouseArea.mapPositionToScene(maskedPosition);
var scenePointerPos = mouseArea.dragHelper.mapPositionToScene(maskedPosition);
if (targetNode.orientation === Node.RightHanded)
scenePointerPos.z = -scenePointerPos.z;
return scenePointerPos.minus(_pointerPosPressed);
@@ -100,6 +101,8 @@ Model {
rotation: Qt.vector3d(0, 0, 90)
grabsMouse: targetNode
active: rootModel.active
dragHelper: rootModel.dragHelper
onPressed: rootModel.handlePressed(mouseAreaYZ, scenePos)
onDragged: rootModel.handleDragged(mouseAreaYZ, scenePos)
onReleased: rootModel.handleReleased(mouseAreaYZ, scenePos)
@@ -115,6 +118,8 @@ Model {
rotation: Qt.vector3d(0, 90, 90)
grabsMouse: targetNode
active: rootModel.active
dragHelper: rootModel.dragHelper
onPressed: rootModel.handlePressed(mouseAreaXZ, scenePos)
onDragged: rootModel.handleDragged(mouseAreaXZ, scenePos)
onReleased: rootModel.handleReleased(mouseAreaXZ, scenePos)

View File

@@ -28,6 +28,7 @@ import QtQuick.Window 2.12
import QtQuick3D 1.0
import QtQuick.Controls 2.0
import QtGraphicalEffects 1.0
import MouseArea3D 1.0
Window {
id: viewWindow
@@ -183,6 +184,11 @@ Window {
scale: editOrthoCamera.scale
}
MouseArea3D {
id: gizmoDragHelper
view3D: overlayView
}
MoveGizmo {
id: moveGizmo
scale: autoScale.getScale(Qt.vector3d(5, 5, 5))
@@ -191,6 +197,7 @@ Window {
globalOrientation: btnLocalGlobal.toggled
visible: selectedNode && btnMove.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onPositionCommit: viewWindow.commitObjectProperty(selectedNode, "position")
onPositionMove: viewWindow.changeObjectProperty(selectedNode, "position")
@@ -204,6 +211,7 @@ Window {
globalOrientation: false
visible: selectedNode && btnScale.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onScaleCommit: viewWindow.commitObjectProperty(selectedNode, "scale")
onScaleChange: viewWindow.changeObjectProperty(selectedNode, "scale")
@@ -217,6 +225,7 @@ Window {
globalOrientation: btnLocalGlobal.toggled
visible: selectedNode && btnRotate.selected
view3D: overlayView
dragHelper: gizmoDragHelper
onRotateCommit: viewWindow.commitObjectProperty(selectedNode, "rotation")
onRotateChange: viewWindow.changeObjectProperty(selectedNode, "rotation")

View File

@@ -33,7 +33,7 @@ Node {
property View3D view3D
property bool highlightOnHover: true
property Node targetNode: null
property var selectedNodes: null
property var selectedNodes: []
readonly property bool selected: {
for (var i = 0; i < selectedNodes.length; ++i) {
if (selectedNodes[i] === targetNode)

View File

@@ -37,6 +37,8 @@ Node {
readonly property bool dragging: arrowX.dragging || arrowY.dragging || arrowZ.dragging
|| planeX.dragging || planeY.dragging || planeZ.dragging
|| centerBall.dragging
property MouseArea3D dragHelper: null
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
orientation: targetNode ? targetNode.orientation : Node.LeftHanded
@@ -57,6 +59,7 @@ Node {
: Qt.rgba(1, 0, 0, 1)
view3D: moveGizmo.view3D
active: moveGizmo.visible
dragHelper: moveGizmo.dragHelper
onPositionCommit: moveGizmo.positionCommit()
onPositionMove: moveGizmo.positionMove()
@@ -70,6 +73,7 @@ Node {
: Qt.rgba(0, 0.6, 0, 1)
view3D: moveGizmo.view3D
active: moveGizmo.visible
dragHelper: moveGizmo.dragHelper
onPositionCommit: moveGizmo.positionCommit()
onPositionMove: moveGizmo.positionMove()
@@ -83,6 +87,7 @@ Node {
: Qt.rgba(0, 0, 1, 1)
view3D: moveGizmo.view3D
active: moveGizmo.visible
dragHelper: moveGizmo.dragHelper
onPositionCommit: moveGizmo.positionCommit()
onPositionMove: moveGizmo.positionMove()
@@ -100,6 +105,7 @@ Node {
: Qt.rgba(1, 0, 0, 1)
view3D: moveGizmo.view3D
active: moveGizmo.visible
dragHelper: moveGizmo.dragHelper
onPositionCommit: moveGizmo.positionCommit()
onPositionMove: moveGizmo.positionMove()
@@ -117,6 +123,7 @@ Node {
: Qt.rgba(0, 0.6, 0, 1)
view3D: moveGizmo.view3D
active: moveGizmo.visible
dragHelper: moveGizmo.dragHelper
onPositionCommit: moveGizmo.positionCommit()
onPositionMove: moveGizmo.positionMove()
@@ -134,6 +141,7 @@ Node {
: Qt.rgba(0, 0, 1, 1)
view3D: moveGizmo.view3D
active: moveGizmo.visible
dragHelper: moveGizmo.dragHelper
onPositionCommit: moveGizmo.positionCommit()
onPositionMove: moveGizmo.positionMove()
@@ -152,6 +160,7 @@ Node {
view3D: moveGizmo.view3D
active: moveGizmo.visible
dragHelper: moveGizmo.dragHelper
onPositionCommit: moveGizmo.positionCommit()
onPositionMove: moveGizmo.positionMove()

View File

@@ -36,11 +36,12 @@ Model {
property Node targetNode: null
property bool dragging: mouseArea.dragging
property bool active: false
property MouseArea3D dragHelper: null
readonly property bool hovering: mouseArea.hovering
property var _pointerPosPressed
property var _targetStartPos
property vector3d _pointerPosPressed
property vector3d _targetStartPos
signal pressed(var mouseArea)
signal dragged(var mouseArea, vector3d sceneRelativeDistance)
@@ -62,7 +63,7 @@ Model {
if (!targetNode)
return;
_pointerPosPressed = mouseArea.mapPositionToScene(scenePos);
_pointerPosPressed = mouseArea.dragHelper.mapPositionToScene(scenePos);
if (targetNode.orientation === Node.RightHanded)
_pointerPosPressed.z = -_pointerPosPressed.z;
var sp = targetNode.scenePosition;
@@ -72,7 +73,7 @@ Model {
function calcRelativeDistance(mouseArea, scenePos)
{
var scenePointerPos = mouseArea.mapPositionToScene(scenePos);
var scenePointerPos = mouseArea.dragHelper.mapPositionToScene(scenePos);
if (targetNode.orientation === Node.RightHanded)
scenePointerPos.z = -scenePointerPos.z;
return scenePointerPos.minus(_pointerPosPressed);
@@ -103,6 +104,8 @@ Model {
height: 120
grabsMouse: targetNode
active: rootModel.active
dragHelper: rootModel.dragHelper
onPressed: rootModel.handlePressed(mouseArea, scenePos)
onDragged: rootModel.handleDragged(mouseArea, scenePos)
onReleased: rootModel.handleReleased(mouseArea, scenePos)

View File

@@ -36,7 +36,7 @@ PlanarDraggable {
signal scaleCommit()
signal scaleChange()
property var _startScale
property vector3d _startScale
onPressed: {
// Recreate vector so we don't follow the changes in targetNode.sceneScale

View File

@@ -36,6 +36,7 @@ Node {
property bool globalOrientation: true
readonly property bool dragging: cameraRing.dragging
|| rotRingX.dragging || rotRingY.dragging || rotRingZ.dragging
property MouseArea3D dragHelper: null
property real currentAngle
property point currentMousePos
@@ -88,6 +89,7 @@ Node {
priority: 40
view3D: rotateGizmo.view3D
active: rotateGizmo.visible
dragHelper: rotateGizmo.dragHelper
onRotateCommit: rotateGizmo.rotateCommit()
onRotateChange: rotateGizmo.rotateChange()
@@ -107,6 +109,7 @@ Node {
priority: 30
view3D: rotateGizmo.view3D
active: rotateGizmo.visible
dragHelper: rotateGizmo.dragHelper
onRotateCommit: rotateGizmo.rotateCommit()
onRotateChange: rotateGizmo.rotateChange()
@@ -126,6 +129,7 @@ Node {
priority: 20
view3D: rotateGizmo.view3D
active: rotateGizmo.visible
dragHelper: rotateGizmo.dragHelper
onRotateCommit: rotateGizmo.rotateCommit()
onRotateChange: rotateGizmo.rotateChange()
@@ -145,6 +149,7 @@ Node {
priority: 10
view3D: rotateGizmo.view3D
active: rotateGizmo.visible
dragHelper: rotateGizmo.dragHelper
onRotateCommit: rotateGizmo.rotateCommit()
onRotateChange: rotateGizmo.rotateChange()
@@ -226,6 +231,8 @@ Node {
circlePickArea: Qt.point(25, 50)
grabsMouse: rotateGizmo.targetNode
active: rotateGizmo.visible
dragHelper: rotateGizmo.dragHelper
onPressed: freeRotator.handlePressed(screenPos)
onDragged: freeRotator.handleDragged(screenPos)
onReleased: freeRotator.handleReleased(screenPos)

View File

@@ -39,6 +39,7 @@ Model {
property alias priority: mouseAreaMain.priority
property real currentAngle
property point currentMousePos
property MouseArea3D dragHelper: null
property vector3d _pointerPosPressed
property vector3d _targetPosOnScreen
@@ -132,6 +133,8 @@ Model {
active: rotateRing.active
pickNode: pickModel
minAngle: 0.05
dragHelper: rotateRing.dragHelper
onPressed: rotateRing.handlePressed(screenPos, angle)
onDragged: rotateRing.handleDragged(screenPos)
onReleased: rotateRing.handleReleased(screenPos)

View File

@@ -37,6 +37,7 @@ Node {
readonly property bool dragging: scaleRodX.dragging || scaleRodY.dragging || scaleRodZ.dragging
|| planeX.dragging || planeY.dragging || planeZ.dragging
|| centerMouseArea.dragging
property MouseArea3D dragHelper: null
position: targetNode ? targetNode.scenePosition : Qt.vector3d(0, 0, 0)
orientation: targetNode ? targetNode.orientation : Node.LeftHanded
@@ -58,6 +59,7 @@ Node {
view3D: scaleGizmo.view3D
active: scaleGizmo.visible
globalOrientation: scaleGizmo.globalOrientation
dragHelper: scaleGizmo.dragHelper
onScaleCommit: scaleGizmo.scaleCommit()
onScaleChange: scaleGizmo.scaleChange()
@@ -72,6 +74,7 @@ Node {
view3D: scaleGizmo.view3D
active: scaleGizmo.visible
globalOrientation: scaleGizmo.globalOrientation
dragHelper: scaleGizmo.dragHelper
onScaleCommit: scaleGizmo.scaleCommit()
onScaleChange: scaleGizmo.scaleChange()
@@ -86,6 +89,7 @@ Node {
view3D: scaleGizmo.view3D
active: scaleGizmo.visible
globalOrientation: scaleGizmo.globalOrientation
dragHelper: scaleGizmo.dragHelper
onScaleCommit: scaleGizmo.scaleCommit()
onScaleChange: scaleGizmo.scaleChange()
@@ -104,6 +108,7 @@ Node {
view3D: scaleGizmo.view3D
active: scaleGizmo.visible
globalOrientation: scaleGizmo.globalOrientation
dragHelper: scaleGizmo.dragHelper
onScaleCommit: scaleGizmo.scaleCommit()
onScaleChange: scaleGizmo.scaleChange()
@@ -122,6 +127,7 @@ Node {
view3D: scaleGizmo.view3D
active: scaleGizmo.visible
globalOrientation: scaleGizmo.globalOrientation
dragHelper: scaleGizmo.dragHelper
onScaleCommit: scaleGizmo.scaleCommit()
onScaleChange: scaleGizmo.scaleChange()
@@ -140,6 +146,7 @@ Node {
view3D: scaleGizmo.view3D
active: scaleGizmo.visible
globalOrientation: scaleGizmo.globalOrientation
dragHelper: scaleGizmo.dragHelper
onScaleCommit: scaleGizmo.scaleCommit()
onScaleChange: scaleGizmo.scaleChange()
@@ -171,15 +178,16 @@ Node {
grabsMouse: scaleGizmo.targetNode
priority: 1
active: scaleGizmo.visible
dragHelper: scaleGizmo.dragHelper
property var _startScale
property var _startScreenPos
property vector3d _startScale
property point _startScreenPos
function localScale(screenPos)
{
var yDelta = screenPos.y - _startScreenPos.y;
if (yDelta === 0)
return;
return _startScale;
var scaler = 1.0 + (yDelta * 0.025);
if (scaler === 0)
scaler = 0.0001;
@@ -203,7 +211,6 @@ Node {
onDragged: {
if (!scaleGizmo.targetNode)
return;
scaleGizmo.targetNode.scale = localScale(screenPos);
scaleGizmo.scaleChange();
}

View File

@@ -36,7 +36,7 @@ DirectionalDraggable {
signal scaleCommit()
signal scaleChange()
property var _startScale
property vector3d _startScale
Model {
source: "#Cube"

View File

@@ -83,6 +83,11 @@ QQuick3DNode *MouseArea3D::pickNode() const
return m_pickNode;
}
MouseArea3D *MouseArea3D::dragHelper() const
{
return m_dragHelper;
}
qreal MouseArea3D::x() const
{
return m_x;
@@ -179,6 +184,15 @@ void MouseArea3D::setPickNode(QQuick3DNode *node)
emit pickNodeChanged();
}
void MouseArea3D::setDragHelper(MouseArea3D *dragHelper)
{
if (m_dragHelper == dragHelper)
return;
m_dragHelper = dragHelper;
emit dragHelperChanged();
}
void MouseArea3D::setX(qreal x)
{
if (qFuzzyCompare(m_x, x))
@@ -433,19 +447,22 @@ void MouseArea3D::applyFreeRotation(QQuick3DNode *node, const QVector3D &startRo
node->rotate(degrees, finalAxis, QQuick3DNode::SceneSpace);
}
QVector3D MouseArea3D::getMousePosInPlane(const QPointF &mousePosInView) const
QVector3D MouseArea3D::getMousePosInPlane(const MouseArea3D *helper,
const QPointF &mousePosInView) const
{
if (!helper)
helper = this;
const QVector3D mousePos1(float(mousePosInView.x()), float(mousePosInView.y()), 0);
const QVector3D mousePos2(float(mousePosInView.x()), float(mousePosInView.y()), 1);
const QVector3D rayPos0 = m_view3D->mapTo3DScene(mousePos1);
const QVector3D rayPos1 = m_view3D->mapTo3DScene(mousePos2);
const QVector3D globalPlanePosition = mapPositionToScene(QVector3D(0, 0, 0));
const QVector3D globalPlanePosition = helper->mapPositionToScene(QVector3D(0, 0, 0));
const QVector3D intersectGlobalPos = rayIntersectsPlane(rayPos0, rayPos1,
globalPlanePosition, forward());
if (qFuzzyCompare(intersectGlobalPos.z(), -1))
return intersectGlobalPos;
return mapPositionFromScene(intersectGlobalPos);
return helper->mapPositionFromScene(intersectGlobalPos);
}
bool MouseArea3D::eventFilter(QObject *, QEvent *event)
@@ -505,7 +522,13 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event)
case QEvent::MouseButtonPress: {
auto const mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton) {
m_mousePosInPlane = getMousePosInPlane(mouseEvent->pos());
// Reset drag helper area to global transform of this area
if (m_dragHelper) {
m_dragHelper->setPosition(scenePosition());
m_dragHelper->setRotation(sceneRotation());
m_dragHelper->setScale(sceneScale());
}
m_mousePosInPlane = getMousePosInPlane(m_dragHelper, mouseEvent->pos());
if (mouseOnTopOfMouseArea(m_mousePosInPlane, mouseEvent->pos())) {
setDragging(true);
emit pressed(m_mousePosInPlane, mouseEvent->pos(), pickAngle);
@@ -527,7 +550,7 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event)
auto const mouseEvent = static_cast<QMouseEvent *>(event);
if (mouseEvent->button() == Qt::LeftButton) {
if (m_dragging) {
QVector3D mousePosInPlane = getMousePosInPlane(mouseEvent->pos());
QVector3D mousePosInPlane = getMousePosInPlane(m_dragHelper, mouseEvent->pos());
if (qFuzzyCompare(mousePosInPlane.z(), -1))
mousePosInPlane = m_mousePosInPlane;
setDragging(false);
@@ -554,7 +577,8 @@ bool MouseArea3D::eventFilter(QObject *, QEvent *event)
case QEvent::MouseMove:
case QEvent::HoverMove: {
auto const mouseEvent = static_cast<QMouseEvent *>(event);
const QVector3D mousePosInPlane = getMousePosInPlane(mouseEvent->pos());
const QVector3D mousePosInPlane = getMousePosInPlane(m_dragging ? m_dragHelper : this,
mouseEvent->pos());
const bool hasMouse = mouseOnTopOfMouseArea(mousePosInPlane, mouseEvent->pos());
setHovering(hasMouse);

View File

@@ -55,6 +55,7 @@ class MouseArea3D : public QQuick3DNode
Q_PROPERTY(QPointF circlePickArea READ circlePickArea WRITE setCirclePickArea NOTIFY circlePickAreaChanged)
Q_PROPERTY(qreal minAngle READ minAngle WRITE setMinAngle NOTIFY minAngleChanged)
Q_PROPERTY(QQuick3DNode *pickNode READ pickNode WRITE setPickNode NOTIFY pickNodeChanged)
Q_PROPERTY(MouseArea3D *dragHelper READ dragHelper WRITE setDragHelper NOTIFY dragHelperChanged)
Q_INTERFACES(QQmlParserStatus)
@@ -76,6 +77,7 @@ public:
QPointF circlePickArea() const;
qreal minAngle() const;
QQuick3DNode *pickNode() const;
MouseArea3D *dragHelper() const;
static qreal mouseDragMultiplier() { return .02; }
@@ -86,6 +88,7 @@ public slots:
void setCirclePickArea(const QPointF &pickArea);
void setMinAngle(qreal angle);
void setPickNode(QQuick3DNode *node);
void setDragHelper(MouseArea3D *dragHelper);
void setX(qreal x);
void setY(qreal y);
@@ -126,6 +129,7 @@ signals:
void circlePickAreaChanged();
void minAngleChanged();
void pickNodeChanged();
void dragHelperChanged();
// angle parameter is only set if circlePickArea is specified
void pressed(const QVector3D &scenePos, const QPoint &screenPos, qreal angle);
@@ -156,7 +160,7 @@ private:
bool m_dragging = false;
bool m_active = false;
QVector3D getMousePosInPlane(const QPointF &mousePosInView) const;
QVector3D getMousePosInPlane(const MouseArea3D *helper, const QPointF &mousePosInView) const;
static MouseArea3D *s_mouseGrab;
bool m_grabsMouse = false;
@@ -164,6 +168,7 @@ private:
QPointF m_circlePickArea;
qreal m_minAngle = 0.;
QQuick3DNode *m_pickNode = nullptr;
MouseArea3D *m_dragHelper = nullptr;
};
}