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 Castan <fabcastan@gmail.com>2022-11-03 03:52:16 +0300
committerGitHub <noreply@github.com>2022-11-03 03:52:16 +0300
commit78e25709c0de4310230aec724085a9eeb99ce55c (patch)
tree3dc80b489fd0c3b2690928d1d81bfd7d2f516240
parent4911ec9ce94644f84529f068913316e94cad7573 (diff)
parent603b9df7c44f7fee2b0ec60bae9b5432feb8db4a (diff)
Merge pull request #1707 from alicevision/dev/manipulatorPanorama
Manipulator for panorama viewer
-rw-r--r--meshroom/ui/components/scene3D.py97
-rw-r--r--meshroom/ui/qml/Viewer/PanoramaViewer.qml57
2 files changed, 141 insertions, 13 deletions
diff --git a/meshroom/ui/components/scene3D.py b/meshroom/ui/components/scene3D.py
index ef91b853..d39ea6f6 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.
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: {