public TrackingData CloneDataWithConstantInterval(double interval, InterpolationMethod interpolationMethod = InterpolationMethod.Cubic)
        {
            if (interval <= 0.0)
            {
                throw new ArgumentException("Interval must be a positive value.");
            }
            int          capacity = (int)(Duration / interval);
            TrackingData newData  = new TrackingData(capacity);
            double       time     = 0.0;
            int          index    = 0;

            while (time < Duration)
            {
                // update index to the correct entry we should work off of at the given time
                while (index < NumberOfEntries && time >= m_entries[index].TimeStamp)
                {
                    index++;
                }

                // build entry and add it to new data
                TrackingEntry entry = GetDataAtTimeAndIndex(time, index, interpolationMethod);
                newData.PushEntry(interval, entry.Position, entry.Rotation);

                // advance time to next interval
                time += interval;
            }
            return(newData);
        }
        private int FindIndexAtTimeRec(double time, int start, int end)
        {
            // base case - we've narrowed down the range
            if (end - start <= 1)
            {
                return(start);
            }

            // get middle index
            int           middle = start + (end - start) / 2;
            TrackingEntry entry  = m_entries[middle];

            // which way should we go?
            if (time == entry.TimeStamp)
            {
                return(middle);
            }
            else if (time < entry.TimeStamp)
            {
                return(FindIndexAtTimeRec(time, start, middle));
            }
            else
            {
                return(FindIndexAtTimeRec(time, middle, end));
            }
        }
        private TrackingEntry GetDataLinearAtIndex(int index, double time)
        {
            int[]         pair   = GetIndexPairFromIndex(index);
            TrackingEntry entry1 = m_entries[pair[0]];
            TrackingEntry entry2 = m_entries[pair[1]];
            float         t      = NormalizeTimeAtRange(time, entry1.TimeStamp, entry2.TimeStamp);

            return(new TrackingEntry(
                       time,
                       Interpolations.Linear(entry1.Position, entry2.Position, t),
                       Interpolations.Linear(entry1.Rotation, entry2.Rotation, t)
                       ));
        }
        public void PushEntry(double deltaTime, Point3 position, Point3 rotation)
        {
            // add entry
            TrackingEntry entry = new TrackingEntry(
                m_duration,
                position,
                rotation
                );

            m_entries.Add(entry);

            // update duration
            m_duration += deltaTime;
        }
        private TrackingEntry GetDataCubicAtTime(int index, double time)
        {
            int[]         quad   = GetIndexQuadFromIndex(index);
            TrackingEntry entry1 = m_entries[quad[0]];
            TrackingEntry entry2 = m_entries[quad[1]];
            TrackingEntry entry3 = m_entries[quad[2]];
            TrackingEntry entry4 = m_entries[quad[3]];
            float         t      = NormalizeTimeAtRange(time, entry2.TimeStamp, entry3.TimeStamp);

            return(new TrackingEntry(
                       time,
                       Interpolations.Cubic(entry1.Position, entry2.Position, entry3.Position, entry4.Position, t),
                       Interpolations.Cubic(entry1.Rotation, entry2.Rotation, entry3.Rotation, entry4.Rotation, t)
                       ));
        }
        public TrackingEntry GetDataAtIndex(int index)
        {
            TrackingEntry entry = m_entries[index];

            return(new TrackingEntry(entry.TimeStamp, entry.Position, entry.Rotation));
        }