Skip to content

Commit

Permalink
Create a Quaternion class.
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=260891
<rdar://114675971>

Reviewed by Don Olmstead.

The Quaternion code is currently within TransformationMatrix and is getting fairly complicated. Moving it out into a standalone class should make it easier to work on

* Source/WebCore/Headers.cmake:
* Source/WebCore/Modules/webxr/WebXRFrame.cpp:
(WebCore::WebXRFrame::matrixFromPose):
* Source/WebCore/Modules/webxr/WebXRRigidTransform.cpp:
(WebCore::WebXRRigidTransform::WebXRRigidTransform):
* Source/WebCore/Sources.txt:
* Source/WebCore/WebCore.xcodeproj/project.pbxproj:
* Source/WebCore/platform/graphics/transforms/Quaternion.cpp: Added.
(WebCore::Quaternion::slerp):
(WebCore::Quaternion::accumulate):
(WebCore::Quaternion::interpolate):
* Source/WebCore/platform/graphics/transforms/Quaternion.h: Added.
(WebCore::Quaternion::operator== const):
* Source/WebCore/platform/graphics/transforms/RotateTransformOperation.cpp:
(WebCore::RotateTransformOperation::blend):
* Source/WebCore/platform/graphics/transforms/TransformationMatrix.cpp:
(WebCore::decompose4):
(WebCore::TransformationMatrix::fromQuaternion):
(WebCore::TransformationMatrix::blend4):
(WebCore::TransformationMatrix::recompose4):
(WebCore::slerp): Deleted.
(WebCore::accumulateQuaternion): Deleted.
(WebCore::interpolateQuaternion): Deleted.
* Source/WebCore/platform/graphics/transforms/TransformationMatrix.h:
(WebCore::TransformationMatrix::Decomposed4Type::operator== const):

Canonical link: https://commits.webkit.org/267465@main
  • Loading branch information
mattwoodrow committed Aug 30, 2023
1 parent fb885f3 commit 968ffb0
Show file tree
Hide file tree
Showing 10 changed files with 163 additions and 92 deletions.
1 change: 1 addition & 0 deletions Source/WebCore/Headers.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -2101,6 +2101,7 @@ set(WebCore_PRIVATE_FRAMEWORK_HEADERS
platform/graphics/transforms/Matrix3DTransformOperation.h
platform/graphics/transforms/MatrixTransformOperation.h
platform/graphics/transforms/PerspectiveTransformOperation.h
platform/graphics/transforms/Quaternion.h
platform/graphics/transforms/RotateTransformOperation.h
platform/graphics/transforms/ScaleTransformOperation.h
platform/graphics/transforms/SkewTransformOperation.h
Expand Down
2 changes: 1 addition & 1 deletion Source/WebCore/Modules/webxr/WebXRFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ TransformationMatrix WebXRFrame::matrixFromPose(const PlatformXR::Device::FrameD
{
TransformationMatrix matrix;
matrix.translate3d(pose.position.x(), pose.position.y(), pose.position.z());
matrix.multiply(TransformationMatrix::fromQuaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w));
matrix.multiply(TransformationMatrix::fromQuaternion({ pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w }));
return matrix;
}

Expand Down
4 changes: 2 additions & 2 deletions Source/WebCore/Modules/webxr/WebXRRigidTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ WebXRRigidTransform::WebXRRigidTransform(const DOMPointInit& position, const DOM
{
TransformationMatrix translation;
translation.translate3d(position.x, position.y, position.z);
auto rotation = TransformationMatrix::fromQuaternion(orientation.x, orientation.y, orientation.z, orientation.w);
auto rotation = TransformationMatrix::fromQuaternion({ orientation.x, orientation.y, orientation.z, orientation.w });
m_rawTransform = translation * rotation;
}

Expand All @@ -110,7 +110,7 @@ WebXRRigidTransform::WebXRRigidTransform(const TransformationMatrix& transform)

m_position = DOMPointReadOnly::create(decomp.translateX, decomp.translateY, decomp.translateZ, 1.0f);

DOMPointInit orientationInit { -decomp.quaternionX, -decomp.quaternionY, -decomp.quaternionZ, decomp.quaternionW };
DOMPointInit orientationInit { decomp.quaternion.x, decomp.quaternion.y, decomp.quaternion.z, decomp.quaternion.w };
normalizeQuaternion(orientationInit);
m_orientation = DOMPointReadOnly::create(orientationInit);
}
Expand Down
1 change: 1 addition & 0 deletions Source/WebCore/Sources.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2397,6 +2397,7 @@ platform/graphics/transforms/TransformOperations.cpp
platform/graphics/transforms/TransformState.cpp
platform/graphics/transforms/TransformationMatrix.cpp
platform/graphics/transforms/TranslateTransformOperation.cpp
platform/graphics/transforms/Quaternion.cpp
platform/mediacapabilities/MediaCapabilitiesLogging.cpp
platform/mediacapabilities/MediaEngineConfigurationFactory.cpp
platform/mediarecorder/MediaRecorderPrivate.cpp
Expand Down
6 changes: 6 additions & 0 deletions Source/WebCore/WebCore.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -4059,6 +4059,7 @@
A5F36D3B18F758720054C024 /* PageDebugger.h in Headers */ = {isa = PBXBuildFile; fileRef = A5F36D3918F758720054C024 /* PageDebugger.h */; settings = {ATTRIBUTES = (Private, ); }; };
A6D169641346B4C1000EB770 /* ShadowRoot.h in Headers */ = {isa = PBXBuildFile; fileRef = A6D169631346B4C1000EB770 /* ShadowRoot.h */; settings = {ATTRIBUTES = (Private, ); }; };
A6D5A99D1629D70000297330 /* ScrollingTreeScrollingNodeDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = A6D5A99B1629D70000297330 /* ScrollingTreeScrollingNodeDelegate.h */; settings = {ATTRIBUTES = (Private, ); }; };
A70B77D62A9EE40B003D8566 /* Quaternion.h in Headers */ = {isa = PBXBuildFile; fileRef = A70B77D52A9EE40B003D8566 /* Quaternion.h */; settings = {ATTRIBUTES = (Private, ); }; };
A7151BD812F1558F005A0F64 /* TextCheckerClient.h in Headers */ = {isa = PBXBuildFile; fileRef = A7151BD712F1558F005A0F64 /* TextCheckerClient.h */; settings = {ATTRIBUTES = (Private, ); }; };
A715E653134BBBEC00D8E713 /* ProgressShadowElement.h in Headers */ = {isa = PBXBuildFile; fileRef = A715E651134BBBEC00D8E713 /* ProgressShadowElement.h */; };
A718760E0B2A120100A16ECE /* DragActions.h in Headers */ = {isa = PBXBuildFile; fileRef = A718760D0B2A120100A16ECE /* DragActions.h */; settings = {ATTRIBUTES = (Private, ); }; };
Expand Down Expand Up @@ -15666,6 +15667,8 @@
A6D169631346B4C1000EB770 /* ShadowRoot.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ShadowRoot.h; sourceTree = "<group>"; };
A6D5A99A1629D6FF00297330 /* ScrollingTreeScrollingNodeDelegate.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ScrollingTreeScrollingNodeDelegate.cpp; sourceTree = "<group>"; };
A6D5A99B1629D70000297330 /* ScrollingTreeScrollingNodeDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ScrollingTreeScrollingNodeDelegate.h; sourceTree = "<group>"; };
A70B77D52A9EE40B003D8566 /* Quaternion.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Quaternion.h; sourceTree = "<group>"; };
A70B77D72A9EE50C003D8566 /* Quaternion.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Quaternion.cpp; sourceTree = "<group>"; };
A7151BD712F1558F005A0F64 /* TextCheckerClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TextCheckerClient.h; sourceTree = "<group>"; };
A715E650134BBBEC00D8E713 /* ProgressShadowElement.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ProgressShadowElement.cpp; sourceTree = "<group>"; };
A715E651134BBBEC00D8E713 /* ProgressShadowElement.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProgressShadowElement.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -24463,6 +24466,8 @@
49E911B70EF86D47009D0CAF /* MatrixTransformOperation.h */,
49D5DC290F423A73008F20FD /* PerspectiveTransformOperation.cpp */,
49D5DC2A0F423A73008F20FD /* PerspectiveTransformOperation.h */,
A70B77D72A9EE50C003D8566 /* Quaternion.cpp */,
A70B77D52A9EE40B003D8566 /* Quaternion.h */,
49E911B80EF86D47009D0CAF /* RotateTransformOperation.cpp */,
49E911B90EF86D47009D0CAF /* RotateTransformOperation.h */,
49E911BA0EF86D47009D0CAF /* ScaleTransformOperation.cpp */,
Expand Down Expand Up @@ -40443,6 +40448,7 @@
93B2489A2990CD6000B769E4 /* Quad.h in Headers */,
550A0BCA085F6039007353D6 /* QualifiedName.h in Headers */,
83C1F5941EDF69D300410D27 /* QualifiedNameCache.h in Headers */,
A70B77D62A9EE40B003D8566 /* Quaternion.h in Headers */,
CD20ED3C27878FFB0038BE44 /* QueuedVideoOutput.h in Headers */,
A15E31F41E0CB0B5004B371C /* QuickLook.h in Headers */,
9BAEE92C22388A7D004157A9 /* Quirks.h in Headers */,
Expand Down
83 changes: 83 additions & 0 deletions Source/WebCore/platform/graphics/transforms/Quaternion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include "config.h"
#include "Quaternion.h"

#include <cmath>

namespace WebCore {

// Perform a spherical linear interpolation between the two
// passed quaternions with 0 <= t <= 1.

Quaternion Quaternion::slerp(const Quaternion& other, double t)
{
const double kEpsilon = 1e-5;
Quaternion copy = *this;

double cosHalfAngle = copy.x * other.x + copy.y * other.y + copy.z * other.z + copy.w * other.w;

if (cosHalfAngle < 0.0) {
copy.x = -copy.x;
copy.y = -copy.y;
copy.z = -copy.z;
copy.w = -copy.w;
cosHalfAngle = -cosHalfAngle;
}

if (cosHalfAngle > 1)
cosHalfAngle = 1;

double sinHalfAngle = std::sqrt(1.0 - cosHalfAngle * cosHalfAngle);
if (sinHalfAngle < kEpsilon) {
// Quaternions share common axis and angle.
return *this;
}

double halfAngle = std::acos(cosHalfAngle);
double scale = std::sin((1 - t) * halfAngle) / sinHalfAngle;
double invscale = std::sin(t * halfAngle) / sinHalfAngle;

return { copy.x * scale + other.x * invscale, copy.y * scale + other.y * invscale, copy.z * scale + other.z * invscale, copy.w * scale + other.w * invscale };
}

// Compute quaternion multiplication
Quaternion Quaternion::accumulate(const Quaternion& other)
{
return { w * other.x + x * other.w + y * other.z - z * other.y,
w * other.y - x * other.z + y * other.w + z * other.x,
w * other.z + x * other.y - y * other.x + z * other.w,
w * other.w - x * other.x - y * other.y - z * other.z };
}

Quaternion Quaternion::interpolate(const Quaternion& other, double progress, CompositeOperation compositeOperation)
{
if (compositeOperation == CompositeOperation::Accumulate)
return accumulate(other);
return slerp(other, progress);
}

} // namespace WebCore
45 changes: 45 additions & 0 deletions Source/WebCore/platform/graphics/transforms/Quaternion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright (C) 2023 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#pragma once

#include "CompositeOperation.h"

namespace WebCore {

struct Quaternion {
double x, y, z, w;

bool operator==(const Quaternion& other) const
{
return x == other.x && y == other.y && z == other.z && w == other.w;
}

Quaternion slerp(const Quaternion& other, double progress);
Quaternion accumulate(const Quaternion& other);
Quaternion interpolate(const Quaternion& other, double progress, CompositeOperation);
};

} // namespace WebCore
Original file line number Diff line number Diff line change
Expand Up @@ -114,17 +114,17 @@ Ref<TransformOperation> RotateTransformOperation::blend(const TransformOperation
}

// Convert that to Axis/Angle form
double x = decomp.quaternionX;
double y = decomp.quaternionY;
double z = decomp.quaternionZ;
double x = decomp.quaternion.x;
double y = decomp.quaternion.y;
double z = decomp.quaternion.z;
double length = std::hypot(x, y, z);
double angle = 0;

if (length > 0.00001) {
x /= length;
y /= length;
z /= length;
angle = rad2deg(acos(decomp.quaternionW) * 2);
angle = rad2deg(acos(decomp.quaternion.w) * 2);
} else {
x = 0;
y = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -546,74 +546,32 @@ static bool decompose4(const TransformationMatrix::Matrix4& mat, TransformationM
w = (column[0][1] - column[1][0]) * s;
}

result.quaternionX = x;
result.quaternionY = y;
result.quaternionZ = z;
result.quaternionW = w;
result.quaternion.x = x;
result.quaternion.y = y;
result.quaternion.z = z;
result.quaternion.w = w;

return true;
}

// Perform a spherical linear interpolation between the two
// passed quaternions with 0 <= t <= 1.

static void slerp(double qa[4], const double qb[4], double t)
{
const double kEpsilon = 1e-5;
double ax, ay, az, aw;
double bx, by, bz, bw;
double cx, cy, cz, cw;

ax = qa[0]; ay = qa[1]; az = qa[2]; aw = qa[3];
bx = qb[0]; by = qb[1]; bz = qb[2]; bw = qb[3];

double cosHalfAngle = ax * bx + ay * by + az * bz + aw * bw;

if (cosHalfAngle < 0.0) {
ax = -ax; ay = -ay;
az = -az; aw = -aw;
cosHalfAngle = -cosHalfAngle;
}

if (cosHalfAngle > 1)
cosHalfAngle = 1;

double sinHalfAngle = std::sqrt(1.0 - cosHalfAngle * cosHalfAngle);
if (sinHalfAngle < kEpsilon) {
// Quaternions share common axis and angle.
return;
}

double halfAngle = std::acos(cosHalfAngle);
double scale = std::sin((1 - t) * halfAngle) / sinHalfAngle;
double invscale = std::sin(t * halfAngle) / sinHalfAngle;

cx = ax * scale + bx * invscale;
cy = ay * scale + by * invscale;
cz = az * scale + bz * invscale;
cw = aw * scale + bw * invscale;

qa[0] = cx; qa[1] = cy; qa[2] = cz; qa[3] = cw;
}

// End of Supporting Math Functions

TransformationMatrix::TransformationMatrix(const AffineTransform& t)
{
setMatrix(t.a(), t.b(), t.c(), t.d(), t.e(), t.f());
}

TransformationMatrix TransformationMatrix::fromQuaternion(double qx, double qy, double qz, double qw)
TransformationMatrix TransformationMatrix::fromQuaternion(const Quaternion& q)
{
double xx = qx * qx;
double yy = qy * qy;
double zz = qz * qz;
double xz = qx * qz;
double xy = qx * qy;
double yz = qy * qz;
double xw = qw * qx;
double yw = qw * qy;
double zw = qw * qz;
double xx = q.x * q.x;
double yy = q.y * q.y;
double zz = q.z * q.z;
double xz = q.x * q.z;
double xy = q.x * q.y;
double yz = q.y * q.z;
double xw = q.w * q.x;
double yw = q.w * q.y;
double zw = q.w * q.z;

return TransformationMatrix(1 - 2 * (yy + zz), 2 * (xy + zw), 2 * (xz - yw), 0,
2 * (xy - zw), 1 - 2 * (xx + zz), 2 * (yz + xw), 0,
Expand Down Expand Up @@ -1748,30 +1706,6 @@ void TransformationMatrix::blend2(const TransformationMatrix& from, double progr
recompose2(fromDecomp);
}

// Compute quaternion multiplication
static void accumulateQuaternion(double qa[4], const double qb[4])
{
auto qx = qa[3] * qb[0] + qa[0] * qb[3] + qa[1] * qb[2] - qa[2] * qb[1];
auto qy = qa[3] * qb[1] - qa[0] * qb[2] + qa[1] * qb[3] + qa[2] * qb[0];
auto qz = qa[3] * qb[2] + qa[0] * qb[1] - qa[1] * qb[0] + qa[2] * qb[3];
auto qw = qa[3] * qb[3] - qa[0] * qb[0] - qa[1] * qb[1] - qa[2] * qb[2];
qa[0] = qx; qa[1] = qy; qa[2] = qz; qa[3] = qw;
}

static void interpolateQuaternion(TransformationMatrix::Decomposed4Type& fromDecomp, TransformationMatrix::Decomposed4Type& toDecomp, double progress, CompositeOperation compositeOperation)
{
double qa[4] = { fromDecomp.quaternionX, fromDecomp.quaternionY, fromDecomp.quaternionZ, fromDecomp.quaternionW };
double qb[4] = { toDecomp.quaternionX, toDecomp.quaternionY, toDecomp.quaternionZ, toDecomp.quaternionW };
if (compositeOperation == CompositeOperation::Accumulate)
accumulateQuaternion(qa, qb);
else
slerp(qa, qb, progress);
fromDecomp.quaternionX = qa[0];
fromDecomp.quaternionY = qa[1];
fromDecomp.quaternionZ = qa[2];
fromDecomp.quaternionW = qa[3];

}
void TransformationMatrix::blend4(const TransformationMatrix& from, double progress, CompositeOperation compositeOperation)
{
Decomposed4Type fromDecomp;
Expand Down Expand Up @@ -1799,7 +1733,7 @@ void TransformationMatrix::blend4(const TransformationMatrix& from, double progr
blendFloat(fromDecomp.perspectiveY, toDecomp.perspectiveY, progress, operationForNonOneBasedValues);
blendFloat(fromDecomp.perspectiveZ, toDecomp.perspectiveZ, progress, operationForNonOneBasedValues);
blendFloat(fromDecomp.perspectiveW, toDecomp.perspectiveW, progress, compositeOperation);
interpolateQuaternion(fromDecomp, toDecomp, progress, compositeOperation);
fromDecomp.quaternion = fromDecomp.quaternion.interpolate(toDecomp.quaternion, progress, compositeOperation);

recompose4(fromDecomp);
}
Expand Down Expand Up @@ -1880,7 +1814,7 @@ void TransformationMatrix::recompose4(const Decomposed4Type& decomp)
translate3d(decomp.translateX, decomp.translateY, decomp.translateZ);

// Apply rotation.
TransformationMatrix rotationMatrix = TransformationMatrix::fromQuaternion(decomp.quaternionX, decomp.quaternionY, decomp.quaternionZ, decomp.quaternionW);
TransformationMatrix rotationMatrix = TransformationMatrix::fromQuaternion(decomp.quaternion);

multiply(rotationMatrix);

Expand Down
Loading

0 comments on commit 968ffb0

Please sign in to comment.