/****************************************************************************** * Copyright (C) Ultraleap, Inc. 2011-2021. * * * * Use subject to the terms of the Apache License 2.0 available at * * http://www.apache.org/licenses/LICENSE-2.0, or another agreement * * between Ultraleap and you, your company or other organization. * ******************************************************************************/ using Leap.Unity.Encoding; using Leap.Unity.Splines; using System; using UnityEngine; namespace Leap.Unity { public static class PoseExtensions { public static Pose inverse(this Pose ps) { Quaternion invQ = Quaternion.Inverse(ps.rotation); return new Pose(invQ * -ps.position, invQ.ToNormalized()); // Normalize } /// /// Returns a Matrix4x4 corresponding to this Pose's translation and /// rotation, with unit scale. /// public static Matrix4x4 matrix(this Pose ps) { ps.rotation = ps.rotation.ToNormalized(); // Normalize return Matrix4x4.TRS(ps.position, ps.rotation, Vector3.one); } /// /// Returns Pose B transformed by Pose A, like a transform hierarchy with A as the /// parent of B. /// public static Pose mul(this Pose A, Pose B) { return new Pose(A.position + (A.rotation * B.position), A.rotation * B.rotation); } /// /// Returns the accumulation of the two Poses: The positions summed, and with /// rotation A.rotation * B.rotation. Note that this accumulates the Poses without /// interpreting either Pose as a parent space of the other; but also beware that /// rotations are noncommutative, so this operation is also noncommutative. /// public static Pose add(this Pose A, Pose B) { return new Pose(A.position + B.position, A.rotation * B.rotation); } /// /// Transforms the right-hand-side Vector3 as a local-space position into /// world space as if this Pose were its reference frame or parent. /// public static Pose mul(this Pose pose, Vector3 localPosition) { return new Pose(pose.position + pose.rotation * localPosition, pose.rotation); } public static Pose mul(this Pose pose, Quaternion localRotation) { return pose.mul(new Pose(Vector3.zero, localRotation)); } public static Pose mul(this Quaternion parentRotation, Pose localPose) { return new Pose(Vector3.zero, parentRotation).mul(localPose); } /// Non-projective matrices only (MultiplyPoint3x4). public static Pose mul(this Matrix4x4 matrix, Pose localPose) { return new Pose(matrix.MultiplyPoint3x4(localPose.position), matrix.rotation * localPose.rotation); } public static bool ApproxEquals(this Pose pose, Pose other) { return pose.position.ApproxEquals(other.position) && pose.rotation.ApproxEquals(other.rotation); } /// /// Returns a Pose interpolated (Lerp for position, Slerp, NOT Lerp for rotation) /// between a and b by t from 0 to 1. This method clamps t between 0 and 1; if /// extrapolation is desired, see Extrapolate. /// public static Pose Lerp(this Pose a, Pose b, float t) { if (t >= 1f) return b; if (t <= 0f) return a; return new Pose(Vector3.Lerp(a.position, b.position, t), Quaternion.Lerp(Quaternion.Slerp(a.rotation, b.rotation, t), Quaternion.identity, 0f)); } /// /// As Lerp, but doesn't clamp t between 0 and 1. Values above one extrapolate /// forwards beyond b, while values less than zero extrapolate backwards past a. /// public static Pose LerpUnclamped(this Pose a, Pose b, float t) { return new Pose(Vector3.LerpUnclamped(a.position, b.position, t), Quaternion.SlerpUnclamped(a.rotation, b.rotation, t)); } /// /// As LerpUnclamped, but extrapolates using time values for a and b, and a target /// time at which to determine the extrapolated Pose. /// public static Pose LerpUnclampedTimed(this Pose a, float aTime, Pose b, float bTime, float extrapolateTime) { return LerpUnclamped(a, b, extrapolateTime.MapUnclamped(aTime, bTime, 0f, 1f)); } // IInterpolable Implementation public static Pose CopyFrom(this Pose orig, Pose h) { orig.position = h.position; orig.rotation = h.rotation; return orig; } public static bool FillLerped(this Pose orig, Pose a, Pose b, float t) { orig = LerpUnclamped(a, b, t); return true; } public static bool FillSplined(this Pose orig, Pose a, Pose b, Pose c, Pose d, float t) { orig.position = CatmullRom.ToCHS(a.position, b.position, c.position, d.position, centripetal: false).PositionAt(t); orig.rotation = Quaternion.SlerpUnclamped(b.rotation, c.rotation, t); return true; } /// /// Creates a Pose using the transform's localPosition and localRotation. /// public static Pose ToLocalPose(this Transform t) { return new Pose(t.localPosition, t.localRotation); } /// /// Creates a Pose using the transform's position and rotation. /// public static Pose ToPose(this Transform t) { return new Pose(t.position, t.rotation); } /// /// Creates a Pose using the transform's position and rotation. /// public static Pose GetPose(this Transform t) { return new Pose(t.position, t.rotation); } /// /// Creates a Pose using the transform's position and rotation. /// public static Pose ToWorldPose(this Transform t) { return t.ToPose(); } /// /// Sets the localPosition and localRotation of this transform to the argument Pose's /// position and rotation. /// public static void SetLocalPose(this Transform t, Pose localPose) { t.localPosition = localPose.position; t.localRotation = localPose.rotation; } /// /// Sets the position and rotation of this transform to the argument Pose's /// position and rotation. Identical to SetWorldPose. /// public static void SetPose(this Transform t, Pose worldPose) { t.position = worldPose.position; t.rotation = worldPose.rotation; } /// /// Sets the position and rotation of this transform to the argument Pose's /// position and rotation. Identical to SetPose. /// public static void SetWorldPose(this Transform t, Pose worldPose) { t.SetPose(worldPose); } /// /// Returns the Pose (position and rotation) described by a Matrix4x4. /// public static Pose GetPose(this Matrix4x4 m) { return new Pose(position: m.MultiplyPoint3x4(Vector3.zero), rotation: m.GetQuaternion()); // return new Pose(m.GetColumn(3), // m.GetColumn(2) == m.GetColumn(1) ? Quaternion.identity // : Quaternion.LookRotation( // m.GetColumn(2), // m.GetColumn(1))); } /// /// Returns a new Pose with the argument rotation instead of the Pose's current /// rotation. /// public static Pose WithRotation(this Pose pose, Quaternion newRotation) { return new Pose(pose.position, newRotation); } /// /// Returns a new Pose with the argument position instead of the Pose's current /// position. /// public static Pose WithPosition(this Pose pose, Vector3 newPosition) { return new Pose(newPosition, pose.rotation); } public const float EPSILON = 0.0001f; public static bool ApproxEquals(this Vector3 v0, Vector3 v1) { return (v0 - v1).magnitude < EPSILON; } public static bool ApproxEquals(this Quaternion q0, Quaternion q1) { return (q0.ToAngleAxisVector() - q1.ToAngleAxisVector()).magnitude < EPSILON; } } }