/// <summary>
        /// Samples the current velocity and adds it to a rolling average.
        /// </summary>
        public void OnHold(InteractionBehaviour intObj,
                           ReadonlyList <InteractionController> controllers)
        {
            _velocityQueue.Enqueue(new VelocitySample(intObj.rigidbody.position,
                                                      intObj.rigidbody.rotation,
                                                      Time.fixedTime));

            while (true)
            {
                VelocitySample oldestVelocity = _velocityQueue.Peek();

                // Dequeue conservatively if the oldest velocity is more than 4 frames later
                // than the start of the window.
                if (oldestVelocity.time + (Time.fixedDeltaTime * 4) < Time.fixedTime
                    - _windowLength
                    - _windowDelay)
                {
                    _velocityQueue.Dequeue();
                }
                else
                {
                    break;
                }
            }
        }
        /** Transfers the averaged velocity to the released object. */
        public override void OnThrow(Hand throwingHand)
        {
            if (_velocityQueue.Count < 2)
            {
                _obj.rigidbody.velocity        = Vector3.zero;
                _obj.rigidbody.angularVelocity = Vector3.zero;
                return;
            }

            float windowEnd   = Time.fixedTime - _windowDelay;
            float windowStart = windowEnd - _windowLength;

            //0 occurs before 1
            //start occurs before end
            VelocitySample start0, start1;
            VelocitySample end0, end1;
            VelocitySample s0, s1;

            s0 = s1 = start0 = start1 = end0 = end1 = _velocityQueue.Dequeue();

            while (_velocityQueue.Count != 0)
            {
                s0 = s1;
                s1 = _velocityQueue.Dequeue();

                if (s0.time < windowStart && s1.time >= windowStart)
                {
                    start0 = s0;
                    start1 = s1;
                }

                if (s0.time < windowEnd && s1.time >= windowEnd)
                {
                    end0 = s0;
                    end1 = s1;

                    //We have assigned both start and end and can break out of loop
                    _velocityQueue.Clear();
                    break;
                }
            }

            VelocitySample start = VelocitySample.Interpolate(start0, start1, windowStart);
            VelocitySample end   = VelocitySample.Interpolate(end0, end1, windowEnd);

            Vector3 interpolatedVelocity = PhysicsUtility.ToLinearVelocity(start.position, end.position, _windowLength);

            //If trying to throw the object backwards into the hand
            Vector3 relativeVelocity = interpolatedVelocity - throwingHand.PalmVelocity.ToVector3();

            if (Vector3.Dot(relativeVelocity, throwingHand.PalmNormal.ToVector3()) < 0)
            {
                interpolatedVelocity -= Vector3.Project(relativeVelocity, throwingHand.PalmNormal.ToVector3());
            }

            _obj.rigidbody.velocity        = interpolatedVelocity;
            _obj.rigidbody.angularVelocity = PhysicsUtility.ToAngularVelocity(start.rotation, end.rotation, _windowLength);

            _obj.rigidbody.velocity *= _velocityMultiplierCurve.Evaluate(_obj.rigidbody.velocity.magnitude);
        }
            public static VelocitySample Interpolate(VelocitySample a, VelocitySample b, float time)
            {
                float alpha = Mathf.Clamp01(Mathf.InverseLerp(a.time, b.time, time));

                return(new VelocitySample(Vector3.Lerp(a.position, b.position, alpha),
                                          Quaternion.Slerp(a.rotation, b.rotation, alpha),
                                          time));
            }
        /// <summary>
        /// Transfers the averaged velocity to the released object.
        /// </summary>
        public void OnThrow(InteractionBehaviour intObj, InteractionController throwingController)
        {
            if (_velocityQueue.Count < 2)
            {
                intObj.rigidbody.velocity        = Vector3.zero;
                intObj.rigidbody.angularVelocity = Vector3.zero;
                return;
            }

            float windowEnd   = Time.fixedTime - _windowDelay;
            float windowStart = windowEnd - _windowLength;

            // 0 occurs before 1,
            // start occurs before end.
            VelocitySample start0, start1;
            VelocitySample end0, end1;
            VelocitySample s0, s1;

            s0 = s1 = start0 = start1 = end0 = end1 = _velocityQueue.Dequeue();

            while (_velocityQueue.Count != 0)
            {
                s0 = s1;
                s1 = _velocityQueue.Dequeue();

                if (s0.time < windowStart && s1.time >= windowStart)
                {
                    start0 = s0;
                    start1 = s1;
                }

                if (s0.time < windowEnd && s1.time >= windowEnd)
                {
                    end0 = s0;
                    end1 = s1;

                    // We have assigned both start and end and can break out of the loop.
                    _velocityQueue.Clear();
                    break;
                }
            }

            VelocitySample start = VelocitySample.Interpolate(start0, start1, windowStart);
            VelocitySample end   = VelocitySample.Interpolate(end0, end1, windowEnd);

            Vector3 interpolatedVelocity = PhysicsUtility.ToLinearVelocity(start.position,
                                                                           end.position,
                                                                           _windowLength);

            intObj.rigidbody.velocity        = interpolatedVelocity;
            intObj.rigidbody.angularVelocity = PhysicsUtility.ToAngularVelocity(start.rotation,
                                                                                end.rotation,
                                                                                _windowLength);

            intObj.rigidbody.velocity *= _velocityMultiplierCurve.Evaluate(intObj.rigidbody.velocity.magnitude);
        }
        /** Samples the current velocity and adds it to the rolling average. */
        public override void OnHold(ReadonlyList <Hand> hands)
        {
            _velocityQueue.Enqueue(new VelocitySample(_obj.warper.RigidbodyPosition, _obj.warper.RigidbodyRotation, Time.fixedTime));

            while (true)
            {
                VelocitySample oldest = _velocityQueue.Peek();

                //Dequeue conservatively if the oldest is more than 4 frames later than the start of the window
                if (oldest.time + Time.fixedDeltaTime * 4 < Time.fixedTime - _windowLength - _windowDelay)
                {
                    _velocityQueue.Dequeue();
                }
                else
                {
                    break;
                }
            }
        }
 public static VelocitySample Interpolate(VelocitySample a, VelocitySample b, float time) {
   float alpha = Mathf.Clamp01(Mathf.InverseLerp(a.time, b.time, time));
   return new VelocitySample(Vector3.Lerp(a.position, b.position, alpha),
                             Quaternion.Slerp(a.rotation, b.rotation, alpha),
                             time);
 }