/****************************************************************************** * 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.RuntimeGizmos; using System; using UnityEngine; namespace Leap.Unity.Animation { /// /// Represents a spline for poses -- positions and rotations -- that travel from one /// position and rotation in space to another over a specified time frame. The two /// endpoints are specified, as well as the instantaneous velocity and angular velocity /// at those two endpoints. /// /// You may ask for the position, rotation, velocity, or angular velocity at any time /// along the spline's duration. /// [Serializable] public struct HermitePoseSpline : ISpline, ISpline { public HermiteSpline3 pSpline; public HermiteQuaternionSpline qSpline; /// /// Constructs a spline by specifying the poses of the two endpoints. The velocity /// and angular velocity at each endpoint is zero, and the time range of the spline /// is 0 to 1. /// public HermitePoseSpline(Pose pose0, Pose pose1) { pSpline = new HermiteSpline3(pose0.position, pose1.position); qSpline = new HermiteQuaternionSpline(pose0.rotation, pose1.rotation); } /// /// Constructs a spline by specifying the poses and movements of the two /// endpoints. The time range of the spline is 0 to 1. /// public HermitePoseSpline(Pose pose0, Pose pose1, Movement move0, Movement move1) { pSpline = new HermiteSpline3(pose0.position, pose1.position, move0.velocity, move1.velocity); qSpline = new HermiteQuaternionSpline(pose0.rotation, pose1.rotation, move0.angularVelocity, move1.angularVelocity); } /// /// Constructs a spline by specifying the positions and velocities of the two /// endpoints. The time range of the spline is 0 to duration. /// public HermitePoseSpline(Pose pose0, Pose pose1, Movement move0, Movement move1, float duration) { pSpline = new HermiteSpline3(pose0.position, pose1.position, move0.velocity, move1.velocity, duration); qSpline = new HermiteQuaternionSpline(pose0.rotation, pose1.rotation, move0.angularVelocity, move1.angularVelocity, duration); } /// /// Constructs a spline by specifying the positions, velocities, and times of the /// endpoints. /// public HermitePoseSpline(float t0, float t1, Pose pose0, Pose pose1, Movement move0, Movement move1) { pSpline = new HermiteSpline3(t0, t1, pose0.position, pose1.position, move0.velocity, move1.velocity); qSpline = new HermiteQuaternionSpline(t0, t1, pose0.rotation, pose1.rotation, move0.angularVelocity, move1.angularVelocity); } /// /// Gets the position at time t along this spline. The time is clamped within the /// t0 - t1 range. /// public Vector3 PositionAt(float t) { return pSpline.PositionAt(t); } /// /// Gets the rotation at time t along this spline. The time is clamped within the /// t0 - t1 range. /// public Quaternion RotationAt(float t) { return qSpline.RotationAt(t); } /// /// Gets the pose at time t along this spline. The time is clamped within the t0 - t1 /// range. /// public Pose PoseAt(float t) { return new Pose(PositionAt(t), RotationAt(t)); } /// /// Gets the first derivative of position at time t. The time is clamped within the /// t0 - t1 range. /// public Vector3 VelocityAt(float t) { return pSpline.VelocityAt(t); } /// /// Gets the first derivative of rotation at time t. The time is clamped within the /// t0 - t1 range. Angular velocity is encoded as an angle-axis vector. /// public Vector3 AngularVelocityAt(float t) { return qSpline.AngularVelocityAt(t); } public Movement MovementAt(float t) { return new Movement(VelocityAt(t), AngularVelocityAt(t)); } /// /// Gets both the position and the first derivative of position at time t. The time /// is clamped within the t0 - t1 range. /// public void PositionAndVelAt(float t, out Vector3 position, out Vector3 velocity) { pSpline.PositionAndVelAt(t, out position, out velocity); } /// /// Gets both the rotation and the first derivative of rotation at time t. The time /// is clamped within the t0 - t1 range. Angular velocity is encoded as an angle-axis /// vector. /// public void RotationAndAngVelAt(float t, out Quaternion rotation, out Vector3 angularVelocity) { qSpline.RotationAndAngVelAt(t, out rotation, out angularVelocity); } /// /// Gets both the rotation and the first derivative of rotation at time t. The time /// is clamped within the t0 - t1 range. Angular velocity is encoded as an angle-axis /// vector. /// /// Gets both the pose and position/rotation first derivative at time t. The time is /// clamped within the t0 - t1 range. Angular velocity is encoded as an angle-axis /// vector. /// public void PoseAndMovementAt(float t, out Pose pose, out Movement movement) { Vector3 pos, vel, angVel; Quaternion rot; pSpline.PositionAndVelAt(t, out pos, out vel); qSpline.RotationAndAngVelAt(t, out rot, out angVel); pose = new Pose(pos, rot); movement = new Movement(vel, angVel); } #region ISpline public float minT { get { return pSpline.t0; } } public float maxT { get { return pSpline.t1; } } public Pose ValueAt(float t) { return PoseAt(t); } public Movement DerivativeAt(float t) { return MovementAt(t); } public void ValueAndDerivativeAt(float t, out Pose value, out Movement deltaValuePerSec) { PoseAndMovementAt(t, out value, out deltaValuePerSec); } #endregion #region ISpline float ISpline.minT { get { return pSpline.t0; } } float ISpline.maxT { get { return pSpline.t1; } } Vector3 ISpline.ValueAt(float t) { return PoseAt(t).position; } Vector3 ISpline.DerivativeAt(float t) { return MovementAt(t).velocity; } void ISpline.ValueAndDerivativeAt(float t, out Vector3 value, out Vector3 deltaValuePerT) { Pose pose; Movement movement; PoseAndMovementAt(t, out pose, out movement); value = pose.position; deltaValuePerT = movement.velocity; } #endregion } public static class HermitePoseSplineExtensions { public static void DrawPoseSpline(this RuntimeGizmos.RuntimeGizmoDrawer drawer, HermitePoseSpline spline, Color? color = null, float poseGizmoScale = 0.02f, int splineResolution = 32, int drawPosePeriod = 8, bool drawPoses = true, bool drawSegments = true) { if (!color.HasValue) { color = LeapColor.brown.WithAlpha(0.4f); } drawer.color = color.Value; var tWidth = spline.maxT - spline.minT; Vector3? prevPos = null; int counter = 0; float tStep = (1f / splineResolution) * tWidth; for (float t = spline.minT; t <= spline.minT + tWidth; t += tStep) { var pose = spline.PoseAt(t); if (counter % drawPosePeriod == 0 && drawPoses) { drawer.DrawPose(pose, 0.02f); } if (prevPos.HasValue && drawSegments) { drawer.DrawLine(prevPos.Value, pose.position); } prevPos = pose.position; counter++; } } } }