Class: Interpolate::Points

Inherits:
Object
  • Object
show all
Defined in:
lib/interpolate/base.rb

Constant Summary collapse

DEFAULT_BLEND =

default blending function, which delegates to the :interpolate method on each given value object

lambda { |value, other, balance|
  unless value.respond_to? :interpolate
    raise "found an object (#{value.inspect}) that doesn't respond to :interpolate"
  end
  value.interpolate(other, balance)
}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(points = {}, &block) ⇒ Points

creates an Interpolate::Points object with an optional Hash object that specifies key points (Numeric) and associated value objects

the blend_with Proc can also be given when creating a new Interpolate::Points object



59
60
61
62
63
# File 'lib/interpolate/base.rb', line 59

def initialize(points = {}, &block)
  @points = {}
  @blend_with = block
  merge!(points)
end

Instance Attribute Details

#pointsObject (readonly)

Returns the value of attribute points.



43
44
45
# File 'lib/interpolate/base.rb', line 43

def points
  @points
end

Instance Method Details

#at(point, &block) ⇒ Object Also known as: []

returns the interpolated value at the Numeric point specified, optionally using a given block as the blending function

if no key points have been specified, the return value is nil

if one key point has been specified, the return value is the value of that key point

if the given point falls outside the interpolation key range (lower than the lowest key point or higher than the highest key point), the nearest point value is used; in other words, no extrapolation is performed

otherwise, the interpolated value is calculated in accordance with the first of:

* the given block
* the stored blending function, :blend_with
* a call to :interpolate on a key value object


112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/interpolate/base.rb', line 112

def at(point, &block)
  # obvious cases first
  if @sorted.empty?
    # no key points
    return nil
  elsif @sorted.size == 1
    # one key point
    return @sorted.first.last
  end

  # out-of-bounds cases next
  if point <= @min_point
    # lower than lowest key point
    return @sorted.first.last
  elsif point >= @max_point
    # higher than highest key point
    return @sorted.last.last
  end

  # binary search to find the right interpolation key point/value interval
  left = 0
  right = @sorted.length - 2 # highest point will be included
  low_point = nil
  low_value = nil
  high_point = nil
  high_value = nil

  while left <= right
    middle = (right - left) / 2 + left

    (low_point, low_value) = @sorted[middle]
    (high_point, high_value) = @sorted[middle + 1]

    break if low_point <= point and point <= high_point

    if point < low_point
      right = middle - 1
    else
      left = middle + 1
    end
  end

  # determine the balance ratio
  span = high_point - low_point
  balance = (point.to_f - low_point) / span

  # choose and call the blending function
  blend = block || @blend_with || DEFAULT_BLEND
  blend.call(low_value, high_value, balance)
end

#blend_with(&block) ⇒ Object

define the blending function to use with this Interpolate::Points object

setting this to nil will reset the blending behavior back to calling the default blending function



69
70
71
# File 'lib/interpolate/base.rb', line 69

def blend_with(&block)
  @blend_with = block
end

#merge(points = {}) ⇒ Object

returns a new Interpolate::Points object with the given key points merged with the original points



75
76
77
# File 'lib/interpolate/base.rb', line 75

def merge(points = {})
  Points.new(points.merge(@points))
end

#merge!(points = {}) ⇒ Object

merges the given key points with the original points

Raises:

  • (ArgumentError)


80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/interpolate/base.rb', line 80

def merge!(points = {})
  # points must be a Hash
  raise ArgumentError, "key points must be a Hash object" unless points.is_a? Hash
  # ensure the points are all keyed Numeric-ally
  points.each do |key, value|
    raise ArgumentError, "found a point key that is not a Numeric object: #{key.inspect}" unless key.is_a? Numeric
  end

  @points.merge!(points)
  normalize_data
  self
end