interpolate 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data.tar.gz.sig +0 -0
- data/{CHANGELOG.txt → History.txt} +6 -0
- data/Manifest.txt +4 -1
- data/Rakefile +1 -1
- data/lib/interpolate.rb +3 -156
- data/lib/interpolate/interpolation.rb +116 -0
- data/lib/interpolate/ruby_array.rb +59 -0
- data/lib/interpolate/ruby_numeric.rb +34 -0
- data/test/test_all.rb +13 -4
- metadata +8 -5
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/Manifest.txt
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
History.txt
|
2
2
|
LICENSE.txt
|
3
3
|
Manifest.txt
|
4
4
|
README.txt
|
@@ -8,4 +8,7 @@ examples/colors.rb
|
|
8
8
|
examples/nested.rb
|
9
9
|
examples/zones.rb
|
10
10
|
lib/interpolate.rb
|
11
|
+
lib/interpolate/interpolation.rb
|
12
|
+
lib/interpolate/ruby_array.rb
|
13
|
+
lib/interpolate/ruby_numeric.rb
|
11
14
|
test/test_all.rb
|
data/Rakefile
CHANGED
@@ -10,7 +10,7 @@ Hoe.new('Interpolate', Interpolation::VERSION) do |p|
|
|
10
10
|
p.url = "http://interpolate.rubyforge.org"
|
11
11
|
p.description = File.read('README.txt').delete("\r").split(/^== /)[2].chomp.chomp
|
12
12
|
p.summary = p.description
|
13
|
-
p.changes = File.read('
|
13
|
+
p.changes = File.read('History.txt').delete("\r").split(/^== /)[1].chomp
|
14
14
|
p.remote_rdoc_dir = '' # Release to root
|
15
15
|
end
|
16
16
|
|
data/lib/interpolate.rb
CHANGED
@@ -1,156 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
#
|
5
|
-
# The only requirement is that each interpolation point value must be able to
|
6
|
-
# figure out how to interpolate itself to its neighbor value(s). Numeric
|
7
|
-
# objects and uniformly sized arrays are automatically endowed with this
|
8
|
-
# ability by this gem, but other classes will require an implementation
|
9
|
-
# of +interpolate+. See the example color.rb in the examples directory for
|
10
|
-
# a brief demonstration using Color objects provided by the 'color' gem.
|
11
|
-
#
|
12
|
-
# Interpolation objects are constructed with a Hash object, wherein each key
|
13
|
-
# is a real number value and each value is can respond to +interpolate+ and
|
14
|
-
# determine the resulting value based on its neighbor value and the balance
|
15
|
-
# ratio between the two points.
|
16
|
-
#
|
17
|
-
# At or below the lower bounds of the interpolation, the result will be equal to
|
18
|
-
# the value of the lower bounds interpolation point. At or above the upper
|
19
|
-
# bounds of the graient, the result will be equal to the value of the upper
|
20
|
-
# bounds interpolation point.
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# ==Author
|
24
|
-
#
|
25
|
-
# {Adam Collins}[mailto:adam.w.collins@gmail.com]
|
26
|
-
#
|
27
|
-
#
|
28
|
-
# ==License
|
29
|
-
#
|
30
|
-
# Licensed under the MIT license.
|
31
|
-
#
|
32
|
-
|
33
|
-
class Interpolation
|
34
|
-
VERSION = '0.2.1' # :nodoc:
|
35
|
-
|
36
|
-
# creates an Interpolation object with Hash object that specifies
|
37
|
-
# each point location (Numeric) and value (up to you)
|
38
|
-
def initialize(points = {})
|
39
|
-
@points = {}
|
40
|
-
merge!(points)
|
41
|
-
end
|
42
|
-
|
43
|
-
# creates an Interpolation object from the receiver object,
|
44
|
-
# merged with the interpolated points you specify
|
45
|
-
def merge(points = {})
|
46
|
-
Interpolation.new(points.merge(@points))
|
47
|
-
end
|
48
|
-
|
49
|
-
# merges the interpolation points with the receiver object
|
50
|
-
def merge!(points = {})
|
51
|
-
@points.merge!(points)
|
52
|
-
normalize_data
|
53
|
-
end
|
54
|
-
|
55
|
-
# returns the interpolated value of the receiver object at the point specified
|
56
|
-
def at(point)
|
57
|
-
# deal with the two out-of-bounds cases first
|
58
|
-
if (point <= @min_point)
|
59
|
-
return @data.first.last
|
60
|
-
elsif (point >= @max_point)
|
61
|
-
return @data.last.last
|
62
|
-
end
|
63
|
-
|
64
|
-
# go through the interpolation intervals, in order, to determine
|
65
|
-
# into which this point falls
|
66
|
-
1.upto(@data.length - 1) do |zone|
|
67
|
-
left = @data.at(zone - 1)
|
68
|
-
right = @data.at(zone)
|
69
|
-
zone_range = left.first..right.first
|
70
|
-
|
71
|
-
if (zone_range.include?(point))
|
72
|
-
# what are the points in question?
|
73
|
-
left_point = left.first.to_f
|
74
|
-
right_point = right.first.to_f
|
75
|
-
|
76
|
-
# what are the values in question?
|
77
|
-
left_value = left.last
|
78
|
-
right_value = right.last
|
79
|
-
|
80
|
-
# span: difference between the left point and right point
|
81
|
-
# balance: ratio of right point to left point
|
82
|
-
span = right_point - left_point
|
83
|
-
balance = (point.to_f - left_point) / span
|
84
|
-
|
85
|
-
# catch the cases where the point in quesion is
|
86
|
-
# on one of the zone's endpoints
|
87
|
-
return left_value if (balance == 0.0)
|
88
|
-
return right_value if (balance == 1.0)
|
89
|
-
|
90
|
-
# otherwise, we need to interpolate
|
91
|
-
return left_value.interpolate(right_value, balance)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# we shouldn't get to this point
|
96
|
-
raise "couldn't come up with a value for some reason!"
|
97
|
-
end
|
98
|
-
|
99
|
-
private
|
100
|
-
|
101
|
-
def normalize_data # :nodoc:
|
102
|
-
@data = @points.sort
|
103
|
-
@min_point = @data.first.first
|
104
|
-
@max_point = @data.last.first
|
105
|
-
|
106
|
-
# make sure that all values respond_to? :interpolate
|
107
|
-
@data.each do |point|
|
108
|
-
value = point.last
|
109
|
-
unless value.respond_to?(:interpolate)
|
110
|
-
raise ArgumentError, "found an interpolation point that doesn't respond to :interpolate"
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
end
|
116
|
-
|
117
|
-
|
118
|
-
# all numeric objects should be supported
|
119
|
-
class Numeric # :nodoc:
|
120
|
-
def interpolate(other, balance)
|
121
|
-
left = self.to_f
|
122
|
-
right = other.to_f
|
123
|
-
delta = (right - left).to_f
|
124
|
-
return left + (delta * balance)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
|
129
|
-
# a little more complicated, but there's no reason why we can't
|
130
|
-
# interpolate between two equal length arrays as long as each element
|
131
|
-
# responds to +interpolate+
|
132
|
-
class Array # :nodoc:
|
133
|
-
def interpolate(other, balance)
|
134
|
-
if (self.length < 1) then
|
135
|
-
raise ArgumentError, "cannot interpolate array with no values"
|
136
|
-
end
|
137
|
-
|
138
|
-
if (self.length != other.length) then
|
139
|
-
raise ArgumentError, "cannot interpolate between arrays of different length"
|
140
|
-
end
|
141
|
-
|
142
|
-
final = Array.new
|
143
|
-
|
144
|
-
self.each_with_index do |left, index|
|
145
|
-
unless (left.respond_to? :interpolate) then
|
146
|
-
raise "array element does not respond to :interpolate"
|
147
|
-
end
|
148
|
-
|
149
|
-
right = other[index]
|
150
|
-
|
151
|
-
final[index] = left.interpolate(right, balance)
|
152
|
-
end
|
153
|
-
|
154
|
-
return final
|
155
|
-
end
|
156
|
-
end
|
1
|
+
require 'interpolate/interpolation'
|
2
|
+
require 'interpolate/ruby_array'
|
3
|
+
require 'interpolate/ruby_numeric'
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Library for generic interpolation objects. Useful for such things as generating
|
2
|
+
# linear motion between points (or arrays of points), multi-channel color
|
3
|
+
# gradients, piecewise functions, or even just placing values within intervals.
|
4
|
+
#
|
5
|
+
# The only requirement is that each interpolation point value must be able to
|
6
|
+
# figure out how to interpolate itself to its neighbor value(s). Numeric
|
7
|
+
# objects and uniformly sized arrays are automatically endowed with this
|
8
|
+
# ability by this gem, but other classes will require an implementation
|
9
|
+
# of +interpolate+. See the example color.rb in the examples directory for
|
10
|
+
# a brief demonstration using Color objects provided by the 'color' gem.
|
11
|
+
#
|
12
|
+
# Interpolation objects are constructed with a Hash object, wherein each key
|
13
|
+
# is a real number value and each value is can respond to +interpolate+ and
|
14
|
+
# determine the resulting value based on its neighbor value and the balance
|
15
|
+
# ratio between the two points.
|
16
|
+
#
|
17
|
+
# At or below the lower bounds of the interpolation, the result will be equal to
|
18
|
+
# the value of the lower bounds interpolation point. At or above the upper
|
19
|
+
# bounds of the graient, the result will be equal to the value of the upper
|
20
|
+
# bounds interpolation point.
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# ==Author
|
24
|
+
#
|
25
|
+
# {Adam Collins}[mailto:adam.w.collins@gmail.com]
|
26
|
+
#
|
27
|
+
#
|
28
|
+
# ==License
|
29
|
+
#
|
30
|
+
# Licensed under the MIT license.
|
31
|
+
#
|
32
|
+
|
33
|
+
class Interpolation
|
34
|
+
VERSION = '0.2.2'
|
35
|
+
|
36
|
+
# creates an Interpolation object with Hash object that specifies
|
37
|
+
# each point location (Numeric) and value (up to you)
|
38
|
+
def initialize(points = {})
|
39
|
+
@points = {}
|
40
|
+
merge!(points)
|
41
|
+
end
|
42
|
+
|
43
|
+
# creates an Interpolation object from the receiver object,
|
44
|
+
# merged with the interpolated points you specify
|
45
|
+
def merge(points = {})
|
46
|
+
Interpolation.new(points.merge(@points))
|
47
|
+
end
|
48
|
+
|
49
|
+
# merges the interpolation points with the receiver object
|
50
|
+
def merge!(points = {})
|
51
|
+
@points.merge!(points)
|
52
|
+
normalize_data
|
53
|
+
end
|
54
|
+
|
55
|
+
# returns the interpolated value of the receiver object at the point specified
|
56
|
+
def at(point)
|
57
|
+
# deal with the two out-of-bounds cases first
|
58
|
+
if (point <= @min_point)
|
59
|
+
return @data.first.last
|
60
|
+
elsif (point >= @max_point)
|
61
|
+
return @data.last.last
|
62
|
+
end
|
63
|
+
|
64
|
+
# go through the interpolation intervals, in order, to determine
|
65
|
+
# into which this point falls
|
66
|
+
1.upto(@data.length - 1) do |zone|
|
67
|
+
left = @data.at(zone - 1)
|
68
|
+
right = @data.at(zone)
|
69
|
+
zone_range = left.first..right.first
|
70
|
+
|
71
|
+
if (zone_range.include?(point))
|
72
|
+
# what are the points in question?
|
73
|
+
left_point = left.first.to_f
|
74
|
+
right_point = right.first.to_f
|
75
|
+
|
76
|
+
# what are the values in question?
|
77
|
+
left_value = left.last
|
78
|
+
right_value = right.last
|
79
|
+
|
80
|
+
# span: difference between the left point and right point
|
81
|
+
# balance: ratio of right point to left point
|
82
|
+
span = right_point - left_point
|
83
|
+
balance = (point.to_f - left_point) / span
|
84
|
+
|
85
|
+
# catch the cases where the point in quesion is
|
86
|
+
# on one of the zone's endpoints
|
87
|
+
return left_value if (balance == 0.0)
|
88
|
+
return right_value if (balance == 1.0)
|
89
|
+
|
90
|
+
# otherwise, we need to interpolate
|
91
|
+
return left_value.interpolate(right_value, balance)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# we shouldn't get to this point
|
96
|
+
raise "couldn't come up with a value for some reason!"
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def normalize_data # :nodoc:
|
102
|
+
@data = @points.sort
|
103
|
+
@min_point = @data.first.first
|
104
|
+
@max_point = @data.last.first
|
105
|
+
|
106
|
+
# make sure that all values respond_to? :interpolate
|
107
|
+
@data.each do |point|
|
108
|
+
value = point.last
|
109
|
+
unless value.respond_to?(:interpolate)
|
110
|
+
raise ArgumentError, "found an interpolation point that doesn't respond to :interpolate"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Extension(s) for the Ruby Array class.
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# ==Author
|
5
|
+
#
|
6
|
+
# {Adam Collins}[mailto:adam.w.collins@gmail.com]
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# ==License
|
10
|
+
#
|
11
|
+
# Licensed under the MIT license.
|
12
|
+
#
|
13
|
+
|
14
|
+
class Array
|
15
|
+
# Returns a new Array in which each element is the interpolated value
|
16
|
+
# between +self+ and +other+. +balance+ should be a Float from 0.0
|
17
|
+
# to 1.0 where the value is a ratio between +self+ and +other+. +self+
|
18
|
+
# and +other+ should be arrays of equal, non-zero length.
|
19
|
+
#
|
20
|
+
# Between two interpolation points, let's say +a+ and +b+, the final result
|
21
|
+
# will be +c+ where <tt>c[0]</tt> is the interpolation of <tt>a[0]</tt> and
|
22
|
+
# <tt>b[0]</tt> and <tt>c[1]</tt> is interpolated between <tt>a[1]</tt> and
|
23
|
+
# <tt>b[1]</tt> and so on, up to <tt>c[c.length - 1]</tt>.
|
24
|
+
#
|
25
|
+
# This method is intentionally abstract to allow for the interpolation
|
26
|
+
# of nested arrays. In this case, both arrays need to have the same array
|
27
|
+
# structure (same number of dimensions, equal length in each dimension),
|
28
|
+
# but the contents can, of course, be different.
|
29
|
+
#
|
30
|
+
# A balance greater than or equal to 0.0 returns +self+, while a
|
31
|
+
# balance less than or equal to 1.0 returns +other+.
|
32
|
+
def interpolate(other, balance)
|
33
|
+
if (self.length < 1) then
|
34
|
+
raise ArgumentError, "cannot interpolate array with no values"
|
35
|
+
end
|
36
|
+
|
37
|
+
if (self.length != other.length) then
|
38
|
+
raise ArgumentError, "cannot interpolate between arrays of different length"
|
39
|
+
end
|
40
|
+
|
41
|
+
# catch the easy cases
|
42
|
+
return self.dup if (balance <= 0.0)
|
43
|
+
return other.dup if (balance >= 1.0)
|
44
|
+
|
45
|
+
final = Array.new
|
46
|
+
|
47
|
+
self.each_with_index do |left, index|
|
48
|
+
unless (left.respond_to? :interpolate) then
|
49
|
+
raise "array element does not respond to :interpolate"
|
50
|
+
end
|
51
|
+
|
52
|
+
right = other[index]
|
53
|
+
|
54
|
+
final[index] = left.interpolate(right, balance)
|
55
|
+
end
|
56
|
+
|
57
|
+
return final
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Extension(s) for the Ruby Numeric class.
|
2
|
+
#
|
3
|
+
#
|
4
|
+
# ==Author
|
5
|
+
#
|
6
|
+
# {Adam Collins}[mailto:adam.w.collins@gmail.com]
|
7
|
+
#
|
8
|
+
#
|
9
|
+
# ==License
|
10
|
+
#
|
11
|
+
# Licensed under the MIT license.
|
12
|
+
#
|
13
|
+
|
14
|
+
class Numeric
|
15
|
+
# Returns a Float that is equal to the interpolated value between
|
16
|
+
# +self+ and +other+. +balance+ should be a Float from 0.0 to 1.0,
|
17
|
+
# where the value is a ratio between +self+ and +other+.
|
18
|
+
#
|
19
|
+
# A balance greater than or equal to 0.0 returns +self+, while a
|
20
|
+
# balance less than or equal to 1.0 returns +other+.
|
21
|
+
def interpolate(other, balance)
|
22
|
+
balance = balance.to_f
|
23
|
+
left = self.to_f
|
24
|
+
right = other.to_f
|
25
|
+
|
26
|
+
# catch the easy cases
|
27
|
+
return left if (balance <= 0.0)
|
28
|
+
return right if (balance >= 1.0)
|
29
|
+
|
30
|
+
delta = (right - left).to_f
|
31
|
+
return left + (delta * balance)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/test/test_all.rb
CHANGED
@@ -21,16 +21,16 @@ class InterpolationTest < Test::Unit::TestCase
|
|
21
21
|
8 => 0.8,
|
22
22
|
9 => 0.9,
|
23
23
|
10 => 1
|
24
|
-
}
|
24
|
+
}.freeze
|
25
25
|
|
26
26
|
array_points = {
|
27
27
|
100 => [1, 10, 100],
|
28
28
|
200 => [5, 50, 500],
|
29
29
|
500 => [10, 100, 1000]
|
30
|
-
}
|
30
|
+
}.freeze
|
31
31
|
|
32
|
-
@dec_gradient = Interpolation.new(decimal_points)
|
33
|
-
@array_gradient = Interpolation.new(array_points)
|
32
|
+
@dec_gradient = Interpolation.new(decimal_points).freeze
|
33
|
+
@array_gradient = Interpolation.new(array_points).freeze
|
34
34
|
end
|
35
35
|
|
36
36
|
|
@@ -133,6 +133,15 @@ class InterpolationTest < Test::Unit::TestCase
|
|
133
133
|
assert_equal(@array_gradient.at(200), [5, 50, 500])
|
134
134
|
assert_equal(@array_gradient.at(350), [7.5, 75, 750])
|
135
135
|
end
|
136
|
+
|
137
|
+
def test_frozen_points
|
138
|
+
a = @array_gradient.at(200)
|
139
|
+
assert_nothing_raised RuntimeError do
|
140
|
+
a[0] = 10
|
141
|
+
a[1] = 70
|
142
|
+
a[2] = 100
|
143
|
+
end
|
144
|
+
end
|
136
145
|
|
137
146
|
end
|
138
147
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: interpolate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Collins
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
xJc09X9KG2jBdxa4tp+uy7KZ
|
31
31
|
-----END CERTIFICATE-----
|
32
32
|
|
33
|
-
date: 2008-
|
33
|
+
date: 2008-02-05 00:00:00 -08:00
|
34
34
|
default_executable:
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
@@ -40,7 +40,7 @@ dependencies:
|
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 1.
|
43
|
+
version: 1.5.0
|
44
44
|
version:
|
45
45
|
description: Description 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.
|
46
46
|
email: adam.w.collins@gmail.com
|
@@ -49,12 +49,12 @@ executables: []
|
|
49
49
|
extensions: []
|
50
50
|
|
51
51
|
extra_rdoc_files:
|
52
|
-
-
|
52
|
+
- History.txt
|
53
53
|
- LICENSE.txt
|
54
54
|
- Manifest.txt
|
55
55
|
- README.txt
|
56
56
|
files:
|
57
|
-
-
|
57
|
+
- History.txt
|
58
58
|
- LICENSE.txt
|
59
59
|
- Manifest.txt
|
60
60
|
- README.txt
|
@@ -64,6 +64,9 @@ files:
|
|
64
64
|
- examples/nested.rb
|
65
65
|
- examples/zones.rb
|
66
66
|
- lib/interpolate.rb
|
67
|
+
- lib/interpolate/interpolation.rb
|
68
|
+
- lib/interpolate/ruby_array.rb
|
69
|
+
- lib/interpolate/ruby_numeric.rb
|
67
70
|
- test/test_all.rb
|
68
71
|
has_rdoc: true
|
69
72
|
homepage: http://interpolate.rubyforge.org
|
metadata.gz.sig
CHANGED
Binary file
|