class Timed::Sequence
This class implements a sequence of Timed
Items. Any object that implements the methods begin
and end
can be added to the sequence. Note that the items must be inserted in chronological order, or the sequence will raise an exception.
Example
sequence = Timed::Sequence.new sequence << 2..3 sequence.prepend Timed::Item.new 1..2 # Same result as above
A sequence can also be treated like a Moment
and be compared, in time, with other compatible objects.
Sequences also provide a mechanism to offset the items in it, in time by providing the offset
method. Items can use it to offset their begin and end times on the fly.
Public Instance Methods
Returns the time at which the first item in the sequence, and therefore the sequence as a whole, begins. If the sequence is empty, by convention, it both begins and ends at time 0, giving it a 0 length.
# File lib/timed/sequence.rb, line 33 def begin empty? ? 0 : first.begin end
Iterate over all of the edges in the sequence. An edge is the point in time when an item either begins or ends. That time, a numeric value, will be yielded to the block. If a block is not given and enumerator is returned.
Returns an enumerator if a block was not given.
# File lib/timed/sequence.rb, line 143 def each_edge return to_enum(__method__) { 2 * count } unless block_given? each_item do |item| yield item.begin yield item.end end end
Iterates over all the leading edges in the sequence. A leading edge is the point in time where an item begins. That time, a numeric value, will be yielded to the block. If a block is not given and enumerator is returned.
Returns an enumerator if a block was not given.
# File lib/timed/sequence.rb, line 158 def each_leading_edge return to_enum(__method__) { count } unless block_given? each_item { |item| yield item.begin } end
Iterates over all the trailing edges in the sequence. A trailing edge is the point in time where an item begins. That time, a numeric value, will be yielded to the block. If a block is not given and enumerator is returned.
Returns an enumerator if a block was not given.
# File lib/timed/sequence.rb, line 171 def each_trailing_edge return to_enum(__method__) { count } unless block_given? each_item { |item| yield item.end } end
Returns the time at which the last item in the sequence, and therefore the sequence as a whole, ends. If the sequence is empty, by convention, it both begins and ends at time 0, giving it a 0 length.
# File lib/timed/sequence.rb, line 41 def end empty? ? 0 : last.end end
Extends the standard behaviour of Linked::List#first with the option of only returning items that begin after a specified time.
after - a time after which the returned item(s) must occur.
Returns one or more items, or nil if there are no items after the given time.
# File lib/timed/sequence.rb, line 59 def first(n = 1, after: nil, &block) return super(n, &block) unless after if include? after first_item_after after, n else super(n) { |item| item.after? after } end end
Returns a new sequence with items that make up the intersection between the two sequences.
# File lib/timed/sequence.rb, line 232 def intersect(other) intersections(other) .with_object(self.class.new) { |(b, e), a| a << create_item(b, e) } end
More efficient than first calling intersect
and then time
on the result.
from - a point in time to start from. to - a point in time to stop at.
Returns the total time of the intersection between this sequence and the other one.
# File lib/timed/sequence.rb, line 247 def intersect_time(other, from: nil, to: nil) enum = intersections(other) total = 0 if from # Reuse the variable total. It's perhaps a bit messy # and confusing but it works. _, total = enum.next until total > from total -= from end if to loop do b, e = enum.next if e > to total += to - b unless b >= to break end total += e - b end else loop do b, e = enum.next total += e - b end end total rescue StopIteration return 0 end
This method takes a second sequence and iterates over each intersection between the two. If a block is given, the beginning and end of each intersecting period will be yielded to it. Otherwise an enumerator is returned.
other - a sequence, or object that responds to begin
and end
and returns
a Timed::Item in response to #first
Returns an enumerator unless a block is given.
# File lib/timed/sequence.rb, line 187 def intersections(other, &block) return to_enum __callee__, other unless block_given? return unless during? other # Sort the first items from each sequence into leading # and trailing by whichever begins first if self.begin <= other.begin item_l, item_t = self.item, other.item else item_l, item_t = other.item, self.item end loop do # Now there are three posibilities: # 1: The leading item ends before the trailing one # begins. In this case the items do not intersect # at all and we do nothing. if item_l.end <= item_t.begin # 2: The leading item ends before the trailing one # ends elsif item_l.end <= item_t.end yield item_t.begin, item_l.end # 3: The leading item ends after the trailing one else yield item_t.begin, item_t.end # Swap leading and trailing item_l, item_t = item_t, item_l end # Advance the leading item item_l = item_l.next # Swap leading and trailing if needed item_l, item_t = item_t, item_l if item_l.begin > item_t.begin end end
Extends the standard behaviour of Linked::List#last with the option of only returning items that end before a specified time.
after - a time after which the returned item(s) must occur.
Returns one or more items, or nil if there are no items before the given time.
# File lib/timed/sequence.rb, line 77 def last(n = 1, before: nil, &block) return super(n, &block) unless before if include? before last_item_before before, n else super(n) { |item| item.before? before } end end
Offset any time using the current offset settings of the sequence. Note that this method is overridden everytime offset_by
is called.
time - the time to be offset.
Returns the offset time.
# File lib/timed/sequence.rb, line 132 def offset(time) time end
Offset the entire sequence by specifying the coefficients of a polynomial of up to degree 2. This is then used to recalculate the begin and end times of each item in the set. The operation does not change the items but is instead performed on the fly every time either begin
or end
is called.
Example
sequence.offset_by 10, 1.1, 0.01 ^ ^ ^ Quadratic term | + Linear term + Constant term sequence.offset 42.0 # => 73.84
c - list of coefficients, starting with the constant term and ending with,
at most, the quadratic.
# File lib/timed/sequence.rb, line 103 def offset_by(*c) body = case c.length when 0 then proc { |t| t } when 1 if c[0] == 0 || !c[0] proc { |t| t } else proc { |t| c[0] + t } end when 2 then proc { |t| c[0] + c[1] * t } when 3 then proc { |t| c[0] + c[1] * t + c[2] * t**2 } else raise ArgumentError, 'Only polynomilas of order 2 or lower are supported' end redefine_method :offset, body end
Returns the total time made up by the items
# File lib/timed/sequence.rb, line 47 def time each_item.reduce(0) { |a, e| a + e.duration } end
Protected Instance Methods
Protected factory method for creating items compatible with the sequence. This method is called whenever an arbitrary object is pushed or unshifted onto the list and need to be wraped inside an Item
.
args - any arguments will be passed on to Item.new
.
Returns a new Item
.
# File lib/timed/sequence.rb, line 289 def create_item(*args) Item.new(*args) end
Private Instance Methods
Private helper method for (re)defining method on the singleton class.
name - symbol name of the method. body - proc that will be used as method body.
# File lib/timed/sequence.rb, line 298 def redefine_method name, body singleton_class.send :remove_method, name rescue NameError ensure singleton_class.send :define_method, name, body end