class Music::Performance::PiecewiseFunction
Combine functions that are each applicable for a non-overlapping domain.
@author James Tunnell
Attributes
pieces[R]
Public Class Methods
new(points = [])
click to toggle source
Take an array of points (each point is a two-element array pair) and create a piecewise function to calculate values in-between.
# File lib/music-performance/util/piecewise_function.rb, line 12 def initialize points = [] @pieces = { } points = points.sort_by {|p| p[0]} if points.count > 1 if points.is_a?(Hash) points = points.to_a end for i in 1...points.count add_points points[i-1], points[i] end end end
Public Instance Methods
add_piece(domain, func)
click to toggle source
Add a function piece, which covers the given domain (includes domain start but not the end). @param [Range] domain The function domain. If this overlaps an existing domain,
the existing domain will be split with the non- overlapping pieces kept and the overlapping old piece discarded.
# File lib/music-performance/util/piecewise_function.rb, line 44 def add_piece domain, func raise ArgumentError, "domain is not a Range" if !domain.is_a? Range raise ArgumentError, "func is not a Proc" if !func.is_a? Proc contains_domain_completely = @pieces.select { |d,f| d.include?(domain.begin) && d.include?(domain.end) } if contains_domain_completely.any? contains_domain_completely.each do |d,f| l = d.begin...domain.begin if d.exclude_end? r = domain.end...d.end else r = domain.end..d.end end @pieces.delete d if domain.begin != d.begin @pieces[l] = f end if domain.end == d.end @pieces[domain.begin..domain.end] = func else @pieces[domain.begin...domain.end] = func @pieces[r] = f end end else delete_completely = @pieces.select { |d,f| domain.include?(d.begin) && domain.include?(d.end) } delete_completely.each do |d,f| @pieces.delete d end # should only be one move_end = @pieces.select { |d,f| domain.include?(d.end) } move_end.each do |d,f| @pieces.delete d @pieces[d.begin...domain.begin] = f end # should only be one move_begin = @pieces.select { |d,f| domain.include?(d.begin) } move_begin.each do |d,f| @pieces.delete d if d.exclude_end? @pieces[domain.end...d.end] = f else @pieces[domain.end..d.end] = f end end if move_begin.any? @pieces[domain.begin...domain.end] = func else @pieces[domain] = func end end end
add_points(prev_point, point)
click to toggle source
# File lib/music-performance/util/piecewise_function.rb, line 28 def add_points prev_point, point domain = prev_point[0]..point[0] func = lambda do |x| perc = (x - domain.min).to_f / (domain.max - domain.min) y = Interpolation.linear prev_point[1], point[1], perc return y end add_piece(domain, func) end
eval(x)
click to toggle source
Evaluate the piecewise function by finding a function piece whose domain includes the given independent value.
# File lib/music-performance/util/piecewise_function.rb, line 105 def eval x y = nil @pieces.each do |domain, func| if domain.include? x y = func.call x break end end if y.nil? raise ArgumentError, "The input #{x} is not in the domain." end return y end