Пример #1
0
        /// <summary>
        /// Builder for quadratic extrapolation function
        /// </summary>
        public static TimelineExtrapolator <T> BuildQuadraticExtrapolator <T>
            (Func <T, T, T> add, Func <T, float, T> mul)
        {
            return((timeline, context) =>
            {
                // first need to get the 2 previous values
                TimelineEntry <T> pp = context._prev._prev;

                // either there is no previous value or it has the same time
                if (pp == null || pp._time == context.Prev._time)
                {
                    return context._prev._value;
                }

                TimelineEntry <T> ppp = pp._prev;
                double t = context._time;
                double tp = context._prev._time;
                double tpp = pp._time;

                if (ppp == null || ppp._time == pp._time)                 // only 2 values so do linear extrapolation
                {
                    float factorPrevPrev = (float)((tp - t) / (tp - tpp));
                    float factorPrev = 1 - factorPrevPrev;
                    return (add(mul(pp._value, factorPrevPrev), mul(context._prev._value, factorPrev)));
                }
                else                  // we have three values so can do quadratic
                {
                    double tppp = ppp._time;
                    float L0 = (float)(((t - tpp) * (t - tp)) / ((tppp - tpp) * (tppp - tp)));                      //PPP
                    float L1 = (float)(((t - tppp) * (t - tp)) / ((tpp - tppp) * (tpp - tp)));                      //PP
                    float L2 = (float)(((t - tppp) * (t - tpp)) / ((tp - tppp) * (tp - tpp)));                      //P
                    return (add(mul(ppp._value, L0), add(mul(pp._value, L1), mul(context._prev._value, L2))));
                }
            });
        }
Пример #2
0
        /// <summary>
        /// Delete entries from the timeline starting at the first entry so
        /// that the number of entries in the timeline does not exceed MaxEntries
        /// Also update _prevEntry and _nextEntry if the current values are removed
        /// from the timeline
        /// </summary>
        internal override void GuaranteeSize()
        {
            lock (_entryLock)
            {
                while (_numEntries > _maxEntries && _numEntries > 0)
                {
                    _firstEntry = _firstEntry._next;
                    _numEntries--;
                }

                if (_firstEntry != null)
                {
                    _firstEntry._prev = null;
                    if (_prevEntry != null && _firstEntry._time > _prevEntry._time)
                    {
                        _prevEntry = null;
                    }
                    if (_nextEntry != null && _firstEntry._time > _nextEntry._time)
                    {
                        _nextEntry = _firstEntry;
                    }
                }
                else                 // the timeline has 0 entries
                {
                    _prevEntry = null;
                    _nextEntry = null;
                    _lastEntry = null;
                }
            }
        }
Пример #3
0
        /// <summary>
        /// Adds a value to the timeline.
        /// </summary>
        /// <param name="time">The time at which to insert the value.</param>
        /// <param name="value">The value to insert.</param>
        /// <param name="isTimeAbsolute">Whether or not te specified time is absolute.</param>
        /// <remarks>
        /// Entry will be processed. Insertion is not guaranteed. Insertion may be propagated, depending on settings.
        /// </remarks>
        public void Set(double time, T value, bool isTimeAbsolute = false)
        {
            double absoluteTime = isTimeAbsolute ? time : _now + time;
            var    currentEntry = new TimelineEntry <T>(absoluteTime, value);

            Insert(currentEntry, false);

            // message format:
            //     2 bytes (ushort) timelineIndex
            //     8 bytes (double) time
            //     n bytes (byte[])

            if (_numGuaranteedSends <= 0)
            {
                foreach (TimelineSendFilter <T> sendFilter in _sendFilters)
                {
                    if (sendFilter != null && !sendFilter(this, currentEntry))
                    {
                        return;
                    }
                }
            }

            lock (_entryLock)
            {
                _lastSendTime      = DateTime.Now;
                currentEntry._sent = true;

                _lastLastSentEntry = _lastSentEntry;
                _lastSentEntry     = currentEntry;

                if (_numGuaranteedSends > 0)
                {
                    _numGuaranteedSends--;
                }
            }

            byte[] timelineData = Encode(value);

            byte[]       data   = new byte[sizeof(ushort) + sizeof(double) + timelineData.Length * sizeof(byte)];
            BinaryWriter writer = new BinaryWriter(new MemoryStream(data));

            writer.Write(_index);
            writer.Write(absoluteTime);
            writer.Write(timelineData);
            writer.Close();

            if (_manager != null)
            {
                _manager.EnqueueOutgoingMessage(new TimelineMessage(TimelineMessageType.RelayAbsolute, data, _deliveryMode));
            }
        }
Пример #4
0
        /// <summary>
        /// Adds a remote value.
        /// </summary>
        /// <param name="time">The time at which to insert the value.</param>
        /// <param name="valueBytes">The encoded bytes of the value to insert.</param>
        /// <param name="isTimeAbsolute">Whether or not the specified time is absolute.</param>
        /// <param name="isCached">Indicates whether the value comes from a remote peer or is a cached value from the synchronizer</param>
        internal override void RemoteSet(double time, byte[] valueBytes, bool isTimeAbsolute, bool isCached)
        {
            double absoluteTime = isTimeAbsolute ? time : _now + time;
            T      value        = Decode(valueBytes);

            var entry = new TimelineEntry <T>(absoluteTime, value);

            Insert(entry, isCached);

            if (!(_ignoreCachedEvents && isCached))
            {
                _delayedRemoteInsertionEvents.Enqueue(entry);
            }
        }
Пример #5
0
        /// <summary>
        /// Gets the list of entries which fall within a time range.
        /// </summary>
        /// <param name="startTime">The lower time bound.</param>
        /// <param name="isStartTimeInclusive">Whether or not the start time is inclusive.</param>
        /// <param name="endTime">The upper time bound.</param>
        /// <param name="isEndTimeInclusive">Whether or not the end time is inclusive.</param>
        /// <param name="isTimeAbsolute">Whether or not te specified time is absolute.</param>
        /// <returns>The list of entries which fall within the specified time range.</returns>
        public TimelineEntry <T>[] GetRange(double startTime, bool isStartTimeInclusive,
                                            double endTime, bool isEndTimeInclusive, bool isTimeAbsolute = false)
        {
            double absStartTime = isTimeAbsolute ? startTime : _now + startTime;
            double absEndTime   = isTimeAbsolute ? endTime : _now + endTime;

            Func <double, bool> isTimeInRange;

            if (isStartTimeInclusive)
            {
                if (isEndTimeInclusive)
                {
                    isTimeInRange = time => time >= absStartTime && time <= absEndTime;
                }
                else
                {
                    isTimeInRange = time => time >= absStartTime && time < absEndTime;
                }
            }
            else
            {
                if (isEndTimeInclusive)
                {
                    isTimeInRange = time => time > absStartTime && time <= absEndTime;
                }
                else
                {
                    isTimeInRange = time => time > absStartTime && time < absEndTime;
                }
            }

            List <TimelineEntry <T> > entriesInRange = new List <TimelineEntry <T> >();

            TimelineEntry <T> currentEntry = _firstEntry;

            lock (_entryLock)
            {
                while (currentEntry != null)
                {
                    if (isTimeInRange(currentEntry._time))
                    {
                        entriesInRange.Add(currentEntry);
                    }

                    currentEntry = currentEntry._next;
                }
            }

            return(entriesInRange.ToArray());
        }
Пример #6
0
 /// <summary>
 /// Builder for linear extrapolation function
 /// </summary>
 public static TimelineExtrapolator <T> BuildLinearExtrapolator <T>
     (Func <T, T, T> add, Func <T, float, T> mul, float maxTimeJump = float.PositiveInfinity)
 {
     return((timeline, context) =>
     {
         TimelineEntry <T> prevPrev = context._prevPrev;
         TimelineEntry <T> prev = context._prev;
         if (prevPrev == null || prevPrev._time == prev._time)
         {
             return prev._value;                                                                   // either there is no previous value or it has the same time
         }
         double tPrevPrev = prevPrev._time;
         double tPrev = prev._time;
         double t = context._time;
         float timeJump = Math.Min(maxTimeJump, (float)(t - tPrev));
         t = tPrev + timeJump;
         float factorPrevPrev = (float)((tPrev - t) / (tPrev - tPrevPrev));
         float factorPrev = 1 - factorPrevPrev;
         return (add(mul(prevPrev._value, factorPrevPrev), mul(prev._value, factorPrev)));
     });
 }
Пример #7
0
        /// <summary>
        /// Builder for quadratic interpolation function
        /// </summary>
        public static TimelineInterpolator <T> BuildQuadraticInterpolator <T>
            (Func <T, T, T> add, Func <T, float, T> mul)
        {
            return((timeline, context) =>
            {
                // Lagrange basis functions
                // L0(t) = ((t-t1)(t-t2))/((t0-t1)(t0-t2))
                // L1(t) = ((t-t0)(t-t2))/((t1-t0)(t1-t2))
                // L2(t) = ((t-t0)(t-t1))/((t2-t0)(t2-t1))
                // P(t) = y0*L0(t) + y1*L1(t) + y2*L2(t)

                double tPrev = context._prev._time;
                double tNext = context._next._time;
                double t = context._time;

                // need a third value for interpolation
                // look for a value before tPrev - if there isn't one we
                // will just do linear interplation
                TimelineEntry <T> prevPrev = context._prev;
                while (prevPrev._prev != null && prevPrev._time >= context._prev._time)                 // need loop to make sure that there are not 2 entries with the same time
                {
                    prevPrev = context._prev._prev;
                }
                if (prevPrev != null)                 // we have three values so do quadratic
                {
                    double tPrevPrev = prevPrev._time;
                    float L0 = (float)(((t - tPrev) * (t - tNext)) / ((tPrevPrev - tPrev) * (tPrevPrev - tNext)));
                    float L1 = (float)(((t - tPrevPrev) * (t - tNext)) / ((tPrev - tPrevPrev) * (tPrev - tNext)));
                    float L2 = (float)(((t - tPrevPrev) * (t - tPrev)) / ((tNext - tPrevPrev) * (tNext - tPrev)));
                    return (add(mul(prevPrev._value, L0), add(mul(context._prev._value, L1), mul(context._next._value, L2))));
                }
                else                 // no third value so do linear interpolation
                {
                    float factorNext = (float)((t - tPrev) / (tNext - tPrev));
                    float factorPrev = 1 - factorNext;
                    return (add(mul(context._prev._value, factorPrev), mul(context._next._value, factorNext)));
                }
            });
        }
Пример #8
0
        /// <summary>
        /// Adds an entry locally. For internal use only.
        /// </summary>
        /// <param name="entry">The entry to insert.</param>
        /// <param name="isCached">Whether or not the entry is a cached value.</param>
        /// <remarks>
        /// Entry is not processed. Insertion is guaranteed, and will not be propagated.
        /// </remarks>
        void Insert(TimelineEntry <T> entry, bool isCached)
        {
            lock (_entryLock)
            {
                entry._isCached = isCached;

                // Start with last entry in timeline and look for where the new Entry
                // should be inserted
                TimelineEntry <T> entryBefore = _lastEntry;
                TimelineEntry <T> entryAfter  = null;

                while (entryBefore != null && entryBefore._time > entry._time)
                {
                    entryAfter  = entryBefore;
                    entryBefore = entryBefore._prev;
                }

                if (entryBefore != null)
                {
                    entryBefore._next = entry;
                }
                if (entryAfter != null)
                {
                    entryAfter._prev = entry;
                }

                entry._prev = entryBefore;
                entry._next = entryAfter;

                _numEntries++;

                // update _prevEntry and _nextEntry if inserting between them
                if ((_prevEntry == null || _prevEntry._time <= entry._time) && entry._time <= _now)
                {
                    _prevEntry = entry;
                }
                if ((_nextEntry == null || _nextEntry._time > entry._time) && entry._time > _now)
                {
                    _nextEntry = entry;
                }

                // update _firstEntry and _lastEntry if inserting before or after them
                if ((_firstEntry == null || _firstEntry._time > entry._time))
                {
                    _firstEntry = entry;
                }
                if ((_lastEntry == null || _lastEntry._time <= entry._time))
                {
                    _lastEntry = entry;
                }

                GuaranteeSize();

                if (!(_ignoreCachedEvents && isCached))
                {
                    _delayedInsertionEvents.Enqueue(entry);

                    if (entry._time <= _now)
                    {
                        _delayedPassingEvents.Enqueue(entry);
                    }
                }
            }
        }
Пример #9
0
        /// <summary>
        /// Removes all entries which fall within a time range.
        /// </summary>
        /// <param name="startTime">The lower time bound.</param>
        /// <param name="isStartTimeInclusive">Whether or not the start time is inclusive.</param>
        /// <param name="endTime">The upper time bound.</param>
        /// <param name="isEndTimeInclusive">Whether or not the end time is inclusive.</param>
        /// <param name="isTimeAbsolute">Whether or not te specified time is absolute.</param>
        /// <returns>The number of entries removed.</returns>
        public int RemoveRange(double startTime, bool isStartTimeInclusive,
                               double endTime, bool isEndTimeInclusive, bool isTimeAbsolute = false)
        {
            double absStartTime = isTimeAbsolute ? startTime : _now + startTime;
            double absEndTime   = isTimeAbsolute ? endTime : _now + endTime;
            int    numRemoved   = 0;

            Func <double, bool> isTimeInRange;

            if (isStartTimeInclusive)
            {
                if (isEndTimeInclusive)
                {
                    isTimeInRange = time => time >= absStartTime && time <= absEndTime;
                }
                else
                {
                    isTimeInRange = time => time >= absStartTime && time < absEndTime;
                }
            }
            else
            {
                if (isEndTimeInclusive)
                {
                    isTimeInRange = time => time > absStartTime && time <= absEndTime;
                }
                else
                {
                    isTimeInRange = time => time > absStartTime && time < absEndTime;
                }
            }

            TimelineEntry <T> currentEntry      = _firstEntry;
            TimelineEntry <T> beforeRemoveEntry = null;
            TimelineEntry <T> afterRemoveEntry  = null;

            lock (_entryLock)
            {
                // find the entry before the first entry to be removed
                while (currentEntry != null && (currentEntry._time < absStartTime ||
                                                (isStartTimeInclusive && currentEntry._time == absStartTime)))
                {
                    beforeRemoveEntry = currentEntry;
                    currentEntry      = currentEntry._next;
                }

                // find the entry after the last entry to be removed
                while (currentEntry != null && (currentEntry._time < absEndTime ||
                                                (isEndTimeInclusive && currentEntry._time == absEndTime)))
                {
                    currentEntry     = currentEntry._next;
                    afterRemoveEntry = currentEntry;
                    numRemoved++;
                }
                // join together the two entries: beforeRemoveEntry and afterRemoveEntry

                if (beforeRemoveEntry != null)
                {
                    beforeRemoveEntry._next = afterRemoveEntry;
                }
                if (afterRemoveEntry != null)
                {
                    afterRemoveEntry._prev = beforeRemoveEntry;
                }

                // fix up _firstEntry, _lastEntry, _prevEntry and _next Entry if they have been
                // affected by the removal
                if (_prevEntry != null && isTimeInRange(_prevEntry._time))
                {
                    _prevEntry = beforeRemoveEntry;
                }
                if (_nextEntry != null && isTimeInRange(_nextEntry._time))
                {
                    _nextEntry = afterRemoveEntry;
                }
                if (beforeRemoveEntry == null)
                {
                    _firstEntry = afterRemoveEntry;
                }
                if (afterRemoveEntry == null)
                {
                    _lastEntry = beforeRemoveEntry;
                }
                _numEntries -= numRemoved;
            }
            return(numRemoved);
        }
Пример #10
0
        /// <summary>
        /// Perform updates for the current frame. For internal use only.
        /// </summary>
        internal override void Step()
        {
            lock (_entryLock)
            {
                if (EntryInserted != null)
                {
                    while (_delayedInsertionEvents.Count != 0)
                    {
                        EntryInserted(this, _delayedInsertionEvents.Dequeue());
                    }
                }
                else
                {
                    _delayedInsertionEvents.Clear();
                }

                if (RemoteEntryInserted != null)
                {
                    while (_delayedRemoteInsertionEvents.Count != 0)
                    {
                        RemoteEntryInserted(this, _delayedRemoteInsertionEvents.Dequeue());
                    }
                }
                else
                {
                    _delayedRemoteInsertionEvents.Clear();
                }

                if (EntryPassed != null)
                {
                    while (_delayedPassingEvents.Count != 0)
                    {
                        EntryPassed(this, _delayedPassingEvents.Dequeue());
                    }
                }
                else
                {
                    _delayedPassingEvents.Clear();
                }

                // Update prev and next entry references.
                while (_nextEntry != null && _now >= _nextEntry._time)
                {
                    if (EntryPassed != null)
                    {
                        EntryPassed(this, _nextEntry);
                    }

                    if (EntryMet != null)
                    {
                        EntryMet(this, _nextEntry);
                    }

                    _nextEntry = _nextEntry._next;
                }

                if (_nextEntry != null)
                {
                    _prevEntry = _nextEntry._prev;
                }
                else
                {
                    _prevEntry = _lastEntry;
                }
            }
        }