C
Qt Quick Ultralite perspective_transforms Example
// Copyright (C) 2024 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial
import QtQuick 2.12
import Constants 1.0
Rectangle {
id: cover
property string texture
property int coverIndex
property CoverFlowState state
property matrix4x4 coverTransform: calcCoverTransform()
property matrix4x4 postMatrix
property matrix4x4 preMatrix
property matrix4x4 reflectionTransform
z: calcFinalZ()
Image {
id: coverImageBase
source: cover.texture
transform: Matrix4x4 { matrix: coverTransform }
opacity: 1.0
}
Image {
id: mirrorImageBase
visible: cover.state.showReflection
source: cover.texture
transform: [
Matrix4x4 { matrix: reflectionTransform },
Matrix4x4 { matrix: coverTransform }
]
opacity: 0.1
}
function mtxTranslate(x : real, y : real, z : real) : matrix4x4 {
return Qt.matrix4x4(1, 0, 0, x,
0, 1, 0, y,
0, 0, 1, z,
0, 0, 0, 1)
}
function mtxScale(x : real, y : real, z : real) : matrix4x4 {
return Qt.matrix4x4(x, 0, 0, 0,
0, y, 0, 0,
0, 0, z, 0,
0, 0, 0, 1)
}
function mtxRotX(angle : real) : matrix4x4 {
var radians = angle * (Math.PI / 180)
var sinAngle = Math.sin(radians)
var cosAngle = Math.cos(radians)
return Qt.matrix4x4(1, 0, 0, 0,
0, cosAngle, -sinAngle, 0,
0, sinAngle, cosAngle, 0,
0, 0, 0, 1)
}
function mtxRotY(angle : real) : matrix4x4 {
var radians = angle * (Math.PI / 180)
var sinAngle = Math.sin(radians)
var cosAngle = Math.cos(radians)
return Qt.matrix4x4( cosAngle, 0, sinAngle, 0,
0, 1, 0, 0,
-sinAngle, 0, cosAngle, 0,
0, 0, 0, 1)
}
property matrix4x4 carouselMatrixPost: {
var r1 = mtxRotX(state.viewAngle)
var t2 = mtxTranslate(0, state.cameraHeight, 0)
return postMatrix.times(r1.times(t2))
}
property matrix4x4 carouselMatrixPre: {
var t4 = mtxTranslate(0, 0, state.carouselZ)
var r5 = mtxRotX(-state.viewAngle/4)
var s6 = mtxScale(state.coverScaling, state.coverScaling, 1)
return t4.times(r5.times(s6)).times(preMatrix)
}
// Calculate transforms for individual transform types
function calcPosMatrixCarousel() : matrix4x4 {
var angle = (coverIndex - state.selectedCover) / state.numberOfCovers * 360;
var r3 = mtxRotY(angle)
return carouselMatrixPost.times(r3).times(carouselMatrixPre)
}
property matrix4x4 circlePost: {
var r1 = mtxRotX(state.viewAngle)
var t2 = mtxTranslate(0, state.cameraHeight, 0)
return postMatrix.times(r1.times(t2))
}
function calcPosMatrixCircle() : matrix4x4 {
var angle = (coverIndex - state.selectedCover) / state.numberOfCovers * 360;
var radians = angle * (Math.PI / 180)
var sinAngle = Math.sin(radians)
var cosAngle = Math.cos(radians)
// var t3 = mtxTranslate(state.circleRadius * sinAngle, 0, state.circleRadius * cosAngle)
// var s4 = mtxScale(state.coverScaling, state.coverScaling, 1)
// the below is equivalent to t3.times(s4)
var cs = state.coverScaling
var dx = state.circleRadius * sinAngle
var dz = state.circleRadius * cosAngle
var m3 = Qt.matrix4x4(cs, 0, 0, dx,
0, cs, 0, 0,
0, 0, 1, dz,
0, 0, 0, 1)
return circlePost.times(m3).times(preMatrix)
}
property matrix4x4 circle2DPost: {
var r1 = mtxRotX(state.viewAngle)
var t2 = mtxTranslate(0, state.cameraHeight, 0)
return postMatrix.times(r1.times(t2))
}
property matrix4x4 circle2DPre: {
var r4 = mtxRotX(-state.viewAngle)
var s5 = mtxScale(state.coverScaling, state.coverScaling, 1)
return r4.times(s5).times(preMatrix)
}
function calcPosMatrixCircle2D() : matrix4x4 {
var angle = (coverIndex - state.selectedCover) / state.numberOfCovers * 360;
var radians = angle * (Math.PI / 180)
var sinAngle = Math.sin(radians)
var cosAngle = Math.cos(radians)
var t3 = mtxTranslate(state.circleRadius * sinAngle, 0, state.circleRadius * cosAngle)
return circle2DPost.times(t3.times(circle2DPre))
}
function calcPosMatrixPerspective() : matrix4x4 {
var fract = Math.min(Math.abs(coverIndex - state.selectedCover), 1)
var dir = coverIndex <= state.selectedCover ? 1 : -1
var ta1 = mtxTranslate(0, 0, (1 - fract) * state.perspectiveViewCenterZ)
var ta2 = mtxTranslate(fract * ((coverIndex - state.selectedCover) * state.perspectiveViewCoverDist - dir * state.perspectiveViewCenterDist), 0, 0)
var ra3 = mtxRotY(dir * fract * state.perspectiveViewMaxRot)
return postMatrix.times(ta1.times(ta2.times(ra3))).times(preMatrix)
}
// Calculates transform matrix for a given cover flow type
function calcMatrixForType(type : int) : matrix4x4 {
switch (type)
{
case CoverFlowType.Carousel:
return calcPosMatrixCarousel()
case CoverFlowType.Circle:
return calcPosMatrixCircle()
case CoverFlowType.Circle2D:
return calcPosMatrixCircle2D()
case CoverFlowType.Perspective:
default:
return calcPosMatrixPerspective()
}
}
// Calculate final cover transformation considering morphRatio
function calcCoverTransform() : matrix4x4 {
var previousViewType = state.previousViewType
var currentViewType = state.currentViewType
if (state.morphRatio == 0) {
currentViewType = previousViewType
} else if (state.morphRatio == 1) {
previousViewType = currentViewType
}
if (currentViewType == previousViewType) {
return calcMatrixForType(currentViewType)
} else {
var currentShape = calcMatrixForType(currentViewType)
var oldShape = calcMatrixForType(previousViewType)
return currentShape.times(state.morphRatio).plus(oldShape.times(1 - state.morphRatio))
}
}
// Calculate cover depth along z-axis
function calcFinalZ() : real {
var coverTransform = cover.coverTransform
var x = state.coverSize/2
var y = state.coverSize/2
var inv_d = 1.0 / (coverTransform.m41 * x + coverTransform.m42 * y + coverTransform.m44)
var fX = (coverTransform.m11 * x + coverTransform.m12 * y + coverTransform.m14) * inv_d
var fZ = (coverTransform.m31 * x + coverTransform.m32 * y + coverTransform.m34) * inv_d
var littleX = Math.abs(fX - state.coverFlowX - state.coverFlowW * 0.5)
return -fZ * 100000 - littleX
}
}