Class: Timed::Sequence
- Inherits:
-
Object
- Object
- Timed::Sequence
- Includes:
- Linked::List, Moment
- Defined in:
- lib/timed/sequence.rb
Overview
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.
Instance Method Summary collapse
-
#begin ⇒ Object
Returns the time at which the first item in the sequence, and therefore the sequence as a whole, begins.
-
#each_edge ⇒ Object
Iterate over all of the edges in the sequence.
-
#each_leading_edge ⇒ Object
Iterates over all the leading edges in the sequence.
-
#each_trailing_edge ⇒ Object
Iterates over all the trailing edges in the sequence.
-
#end ⇒ Object
Returns the time at which the last item in the sequence, and therefore the sequence as a whole, ends.
-
#first(n = 1, after: nil, &block) ⇒ Object
Extends the standard behaviour of Linked::List#first with the option of only returning items that begin after a specified time.
-
#intersect(other) ⇒ Object
(also: #&)
Returns a new sequence with items that make up the intersection between the two sequences.
-
#intersect_time(other, from: nil, to: nil) ⇒ Object
More efficient than first calling #intersect and then #time on the result.
-
#intersections(other, &block) ⇒ Object
This method takes a second sequence and iterates over each intersection between the two.
-
#last(n = 1, before: nil, &block) ⇒ Object
Extends the standard behaviour of Linked::List#last with the option of only returning items that end before a specified time.
-
#offset(time) ⇒ Object
Offset any time using the current offset settings of the sequence.
-
#offset_by(*c) ⇒ Object
(also: #offset=)
Offset the entire sequence by specifying the coefficients of a polynomial of up to degree 2.
-
#time ⇒ Object
Returns the total time made up by the items.
Methods included from Moment
#==, #after?, #before?, #duration, #during?, #inspect
Instance Method Details
#begin ⇒ Object
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.
33 34 35 |
# File 'lib/timed/sequence.rb', line 33 def begin empty? ? 0 : first.begin end |
#each_edge ⇒ Object
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.
143 144 145 146 147 148 149 150 |
# 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 |
#each_leading_edge ⇒ Object
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.
158 159 160 161 162 |
# 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 |
#each_trailing_edge ⇒ Object
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.
171 172 173 174 175 |
# 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 |
#end ⇒ Object
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.
41 42 43 |
# File 'lib/timed/sequence.rb', line 41 def end empty? ? 0 : last.end end |
#first(n = 1, after: nil, &block) ⇒ Object
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.
59 60 61 62 63 64 65 66 67 |
# 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 |
#intersect(other) ⇒ Object Also known as: &
Returns a new sequence with items that make up the intersection between the two sequences.
232 233 234 235 |
# 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 |
#intersect_time(other, from: nil, to: nil) ⇒ Object
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.
247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
# 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 |
#intersections(other, &block) ⇒ Object
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.
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# 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 |
#last(n = 1, before: nil, &block) ⇒ Object
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.
77 78 79 80 81 82 83 84 85 |
# 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(time) ⇒ Object
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.
132 133 134 |
# File 'lib/timed/sequence.rb', line 132 def offset(time) time end |
#offset_by(*c) ⇒ Object Also known as: offset=
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.
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 |
# 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 |
#time ⇒ Object
Returns the total time made up by the items
47 48 49 |
# File 'lib/timed/sequence.rb', line 47 def time each_item.reduce(0) { |a, e| a + e.duration } end |