Skip to content

Commit

Permalink
[ui] PanoramaViewer: change the way the user interact with the panora…
Browse files Browse the repository at this point in the history
…ma widget
  • Loading branch information
servantftechnicolor authored and fabiencastan committed Nov 3, 2022
1 parent 3822bc8 commit 603b9df
Show file tree
Hide file tree
Showing 2 changed files with 143 additions and 15 deletions.
101 changes: 98 additions & 3 deletions meshroom/ui/components/scene3D.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -123,7 +218,7 @@ def pointFromWorldToScreen(self, point, camera, windowSize):
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()
)

Expand All @@ -145,7 +240,7 @@ def relativeLocalTranslate(self, transformQtInstance, initialPosMat, initialRotM
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)

Expand Down
57 changes: 45 additions & 12 deletions meshroom/ui/qml/Viewer/PanoramaViewer.qml
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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));
Expand All @@ -160,6 +189,10 @@ AliceVision.PanoramaViewer {

xStart = mouse.x;
yStart = mouse.y;

previous_yaw = yaw;
previous_pitch = pitch;
previous_roll = roll;
}

onReleased: {
Expand Down

0 comments on commit 603b9df

Please sign in to comment.