Class: Music::Performance::PiecewiseFunction

Inherits:
Object
  • Object
show all
Defined in:
lib/music-performance/util/piecewise_function.rb

Overview

Combine functions that are each applicable for a non-overlapping domain.

Author:

  • James Tunnell

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(points = []) ⇒ PiecewiseFunction

Take an array of points (each point is a two-element array pair) and create a piecewise function to calculate values in-between.



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 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

Instance Attribute Details

#piecesObject (readonly)

Returns the value of attribute pieces.



8
9
10
# File 'lib/music-performance/util/piecewise_function.rb', line 8

def pieces
  @pieces
end

Instance Method Details

#add_piece(domain, func) ⇒ Object

Add a function piece, which covers the given domain (includes domain start but not the end).

Parameters:

  • domain (Range)

    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.

Raises:

  • (ArgumentError)


44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 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) ⇒ Object



28
29
30
31
32
33
34
35
36
# 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) ⇒ Object

Evaluate the piecewise function by finding a function piece whose domain includes the given independent value.



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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