lib/interpolate.rb in interpolate-0.2.1 vs lib/interpolate.rb in interpolate-0.2.2

- old
+ new

@@ -1,156 +1,3 @@ -# Library for generic interpolation objects. Useful for such things as generating -# linear motion between points (or arrays of points), multi-channel color -# gradients, piecewise functions, or even just placing values within intervals. -# -# The only requirement is that each interpolation point value must be able to -# figure out how to interpolate itself to its neighbor value(s). Numeric -# objects and uniformly sized arrays are automatically endowed with this -# ability by this gem, but other classes will require an implementation -# of +interpolate+. See the example color.rb in the examples directory for -# a brief demonstration using Color objects provided by the 'color' gem. -# -# Interpolation objects are constructed with a Hash object, wherein each key -# is a real number value and each value is can respond to +interpolate+ and -# determine the resulting value based on its neighbor value and the balance -# ratio between the two points. -# -# At or below the lower bounds of the interpolation, the result will be equal to -# the value of the lower bounds interpolation point. At or above the upper -# bounds of the graient, the result will be equal to the value of the upper -# bounds interpolation point. -# -# -# ==Author -# -# {Adam Collins}[mailto:adam.w.collins@gmail.com] -# -# -# ==License -# -# Licensed under the MIT license. -# - -class Interpolation - VERSION = '0.2.1' # :nodoc: - - # creates an Interpolation object with Hash object that specifies - # each point location (Numeric) and value (up to you) - def initialize(points = {}) - @points = {} - merge!(points) - end - - # creates an Interpolation object from the receiver object, - # merged with the interpolated points you specify - def merge(points = {}) - Interpolation.new(points.merge(@points)) - end - - # merges the interpolation points with the receiver object - def merge!(points = {}) - @points.merge!(points) - normalize_data - end - - # returns the interpolated value of the receiver object at the point specified - def at(point) - # deal with the two out-of-bounds cases first - if (point <= @min_point) - return @data.first.last - elsif (point >= @max_point) - return @data.last.last - end - - # go through the interpolation intervals, in order, to determine - # into which this point falls - 1.upto(@data.length - 1) do |zone| - left = @data.at(zone - 1) - right = @data.at(zone) - zone_range = left.first..right.first - - if (zone_range.include?(point)) - # what are the points in question? - left_point = left.first.to_f - right_point = right.first.to_f - - # what are the values in question? - left_value = left.last - right_value = right.last - - # span: difference between the left point and right point - # balance: ratio of right point to left point - span = right_point - left_point - balance = (point.to_f - left_point) / span - - # catch the cases where the point in quesion is - # on one of the zone's endpoints - return left_value if (balance == 0.0) - return right_value if (balance == 1.0) - - # otherwise, we need to interpolate - return left_value.interpolate(right_value, balance) - end - end - - # we shouldn't get to this point - raise "couldn't come up with a value for some reason!" - end - - private - - def normalize_data # :nodoc: - @data = @points.sort - @min_point = @data.first.first - @max_point = @data.last.first - - # make sure that all values respond_to? :interpolate - @data.each do |point| - value = point.last - unless value.respond_to?(:interpolate) - raise ArgumentError, "found an interpolation point that doesn't respond to :interpolate" - end - end - end - -end - - -# all numeric objects should be supported -class Numeric # :nodoc: - def interpolate(other, balance) - left = self.to_f - right = other.to_f - delta = (right - left).to_f - return left + (delta * balance) - end -end - - -# a little more complicated, but there's no reason why we can't -# interpolate between two equal length arrays as long as each element -# responds to +interpolate+ -class Array # :nodoc: - def interpolate(other, balance) - if (self.length < 1) then - raise ArgumentError, "cannot interpolate array with no values" - end - - if (self.length != other.length) then - raise ArgumentError, "cannot interpolate between arrays of different length" - end - - final = Array.new - - self.each_with_index do |left, index| - unless (left.respond_to? :interpolate) then - raise "array element does not respond to :interpolate" - end - - right = other[index] - - final[index] = left.interpolate(right, balance) - end - - return final - end -end +require 'interpolate/interpolation' +require 'interpolate/ruby_array' +require 'interpolate/ruby_numeric'