Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/alicevision/meshroom.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorfabien servant <fabien.servant@technicolor.com>2022-06-02 21:40:39 +0300
committerFabien Castan <fabcastan@gmail.com>2022-11-03 03:40:42 +0300
commit603b9df7c44f7fee2b0ec60bae9b5432feb8db4a (patch)
tree06b343cc09f50ea7d63c41eefb7a0b49386886f7
parent3822bc847e1f1bb5a98d525dc0631aec035d6d8d (diff)
[ui] PanoramaViewer: change the way the user interact with the panorama widget
-rw-r--r--meshroom/ui/components/scene3D.py101
-rw-r--r--meshroom/ui/qml/Viewer/PanoramaViewer.qml57
2 files changed, 143 insertions, 15 deletions
diff --git a/meshroom/ui/components/scene3D.py b/meshroom/ui/components/scene3D.py
index b4569c19..ac1cc1e1 100644
--- a/meshroom/ui/components/scene3D.py
+++ b/meshroom/ui/components/scene3D.py
@@ -1,4 +1,4 @@
-from math import acos, pi, sqrt
+from math import acos, pi, sqrt, atan2, cos, sin, asin
from PySide2.QtCore import QObject, Slot, QSize, Signal, QPointF
from PySide2.Qt3DCore import Qt3DCore
@@ -109,6 +109,101 @@ class Transformations3DHelper(QObject):
# ---------- Exposed to QML ---------- #
+ @Slot(QVector3D, QVector3D, result=QQuaternion)
+ def rotationBetweenAandB(self, A, B):
+
+ A = A/A.length()
+ B = B/B.length()
+
+ # Get rotation matrix between 2 vectors
+ v = QVector3D.crossProduct(A, B)
+ s = v.length()
+ c = QVector3D.dotProduct(A, B)
+ return QQuaternion.fromAxisAndAngle(v / s, atan2(s, c) * 180 / pi)
+
+ @Slot(QVector3D, result=QVector3D)
+ def fromEquirectangular(self, vector):
+ return QVector3D(cos(vector.x()) * sin(vector.y()), sin(vector.x()), cos(vector.x()) * cos(vector.y()))
+
+ @Slot(QVector3D, result=QVector3D)
+ def toEquirectangular(self, vector):
+ return QVector3D(asin(vector.y()), atan2(vector.x(), vector.z()), 0)
+
+ @Slot(QVector3D, QVector2D, QVector2D, result=QVector3D)
+ def updatePanorama(self, euler, ptStart, ptEnd):
+
+ delta = 1e-3
+
+ #Get initial rotation
+ qStart = QQuaternion.fromEulerAngles(euler.y(), euler.x(), euler.z())
+
+ #Convert input to points on unit sphere
+ vStart = self.fromEquirectangular(QVector3D(ptStart))
+ vStartdY = self.fromEquirectangular(QVector3D(ptStart.x(), ptStart.y() + delta, 0))
+ vEnd = self.fromEquirectangular(QVector3D(ptEnd))
+
+ qAdd = QQuaternion.rotationTo(vStart, vEnd)
+
+
+ #Get the 3D point on unit sphere which would correspond to the no rotation +X
+ vCurrent = qAdd.rotatedVector(vStartdY)
+ vIdeal = self.fromEquirectangular(QVector3D(ptEnd.x(), ptEnd.y() + delta, 0))
+
+ #project on rotation plane
+ lambdaEnd = 1 / QVector3D.dotProduct(vEnd, vCurrent)
+ lambdaIdeal = 1 / QVector3D.dotProduct(vEnd, vIdeal)
+ vPlaneCurrent = lambdaEnd * vCurrent
+ vPlaneIdeal = lambdaIdeal * vIdeal
+
+ #Get the directions
+ rotStart = (vPlaneCurrent - vEnd).normalized()
+ rotEnd = (vPlaneIdeal - vEnd).normalized()
+
+ # Get rotation matrix between 2 vectors
+ v = QVector3D.crossProduct(rotEnd, rotStart)
+ s = QVector3D.dotProduct(v, vEnd)
+ c = QVector3D.dotProduct(rotStart, rotEnd)
+ angle = atan2(s, c) * 180 / pi
+
+ qImage = QQuaternion.fromAxisAndAngle(vEnd, -angle)
+
+ return (qImage * qAdd * qStart).toEulerAngles()
+
+ @Slot(QVector3D, QVector2D, QVector2D, result=QVector3D)
+ def updatePanoramaInPlane(self, euler, ptStart, ptEnd):
+
+ delta = 1e-3
+
+ #Get initial rotation
+ qStart = QQuaternion.fromEulerAngles(euler.y(), euler.x(), euler.z())
+
+ #Convert input to points on unit sphere
+ vStart = self.fromEquirectangular(QVector3D(ptStart))
+ vEnd = self.fromEquirectangular(QVector3D(ptEnd))
+
+ #Get the 3D point on unit sphere which would correspond to the no rotation +X
+ vIdeal = self.fromEquirectangular(QVector3D(ptStart.x(), ptStart.y() + delta, 0))
+
+ #project on rotation plane
+ lambdaEnd = 1 / QVector3D.dotProduct(vStart, vEnd)
+ lambdaIdeal = 1 / QVector3D.dotProduct(vStart, vIdeal)
+ vPlaneEnd = lambdaEnd * vEnd
+ vPlaneIdeal = lambdaIdeal * vIdeal
+
+ #Get the directions
+ rotStart = (vPlaneEnd - vStart).normalized()
+ rotEnd = (vPlaneIdeal - vStart).normalized()
+
+ # Get rotation matrix between 2 vectors
+ v = QVector3D.crossProduct(rotEnd, rotStart)
+ s = QVector3D.dotProduct(v, vStart)
+ c = QVector3D.dotProduct(rotStart, rotEnd)
+ angle = atan2(s, c) * 180 / pi
+
+ qAdd = QQuaternion.fromAxisAndAngle(vStart, angle)
+
+ return (qAdd * qStart).toEulerAngles()
+
@Slot(QVector4D, Qt3DRender.QCamera, QSize, result=QVector2D)
def pointFromWorldToScreen(self, point, camera, windowSize):
""" Compute the Screen point corresponding to a World Point.
@@ -123,7 +218,7 @@ class Transformations3DHelper(QObject):
viewMatrix = camera.transform().matrix().inverted()
projectedPoint = (camera.projectionMatrix() * viewMatrix[0]).map(point)
projectedPoint2D = QVector2D(
- projectedPoint.x()/projectedPoint.w(),
+ projectedPoint.x()/projectedPoint.w(),
projectedPoint.y()/projectedPoint.w()
)
@@ -145,7 +240,7 @@ class Transformations3DHelper(QObject):
initialScaleMat (QMatrix4x4): initial scale matrix
translateVec (QVector3D): vector used for the local translation
"""
- # Compute the translation transformation matrix
+ # Compute the translation transformation matrix
translationMat = QMatrix4x4()
translationMat.translate(translateVec)
diff --git a/meshroom/ui/qml/Viewer/PanoramaViewer.qml b/meshroom/ui/qml/Viewer/PanoramaViewer.qml
index 70f641f8..5ca2e46d 100644
--- a/meshroom/ui/qml/Viewer/PanoramaViewer.qml
+++ b/meshroom/ui/qml/Viewer/PanoramaViewer.qml
@@ -59,6 +59,10 @@ AliceVision.PanoramaViewer {
property var xStart : 0
property var yStart : 0
+ property var previous_yaw: 0;
+ property var previous_pitch: 0;
+ property var previous_roll: 0;
+
property double yaw: 0;
property double pitch: 0;
property double roll: 0;
@@ -132,18 +136,43 @@ AliceVision.PanoramaViewer {
// Rotate Panorama
if (isRotating && isEditable) {
- var xoffset = mouse.x - lastX;
- var yoffset = mouse.y - lastY;
- lastX = mouse.x;
- lastY = mouse.y;
-
- // Update Euler Angles
- if (mouse.modifiers & Qt.AltModifier) {
- root.roll = limitAngle(root.roll + toDegrees((xoffset / width) * mouseMultiplier))
- }
- else {
- root.yaw = limitAngle(root.yaw + toDegrees((xoffset / width) * mouseMultiplier))
- root.pitch = limitPitch(root.pitch + toDegrees(-(yoffset / height) * mouseMultiplier))
+
+ var nx = Math.max(0, mouse.x)
+ var nx = Math.min(width - 1, mouse.x)
+ var ny = Math.max(0, mouse.y)
+ var ny = Math.min(height - 1, mouse.y)
+
+ var xoffset = nx - lastX;
+ var yoffset = ny - lastY;
+
+ if (xoffset != 0 || yoffset !=0)
+ {
+ var latitude_start = (yStart / height) * Math.PI - (Math.PI / 2);
+ var longitude_start = ((xStart / width) * 2 * Math.PI) - Math.PI;
+ var latitude_end = (ny / height) * Math.PI - ( Math.PI / 2);
+ var longitude_end = ((nx / width) * 2 * Math.PI) - Math.PI;
+
+ var start_pt = Qt.vector2d(latitude_start, longitude_start)
+ var end_pt = Qt.vector2d(latitude_end, longitude_end)
+
+ var previous_euler = Qt.vector3d(previous_yaw, previous_pitch, previous_roll)
+
+ if (mouse.modifiers & Qt.ControlModifier)
+ {
+ var result = Transformations3DHelper.updatePanoramaInPlane(previous_euler, start_pt, end_pt)
+ root.pitch = result.x
+ root.yaw = result.y
+ root.roll = result.z
+ }
+ else
+ {
+ var result = Transformations3DHelper.updatePanorama(previous_euler, start_pt, end_pt)
+ root.pitch = result.x
+ root.yaw = result.y
+ root.roll = result.z
+ }
+
+
}
_reconstruction.setAttribute(activeNode.attribute("manualTransform.manualRotation.x"), Math.round(root.pitch));
@@ -160,6 +189,10 @@ AliceVision.PanoramaViewer {
xStart = mouse.x;
yStart = mouse.y;
+
+ previous_yaw = yaw;
+ previous_pitch = pitch;
+ previous_roll = roll;
}
onReleased: {