Class: Kamelopard::Functions::SplineFunction
- Inherits:
-
FunctionMultiDim
- Object
- Function
- FunctionMultiDim
- Kamelopard::Functions::SplineFunction
- Defined in:
- lib/kamelopard/spline.rb
Overview
Provides basic support for Catmul-Rom splines, or in other words, calculating a nice smooth path through a series of “control points”. You’ll create a spline, add a bunch of control points, and then call run_function for a series of values between 0 and 1 to get the calculated points on the path.
Mathematically, what this code calls a “point” is in fact a vector, a set of related numbers. Each number in the set represents one “dimension”, the spline supports any number of dimensions, but each control point must have the same number. The returned vectors will also have that number of dimensions. Dimensions can represent whatever the user wants. For instance, the first might be “latitude”, the second “longitude”, and the third “altitude” (though common cases like that are already taken care of with SplineFunction descendants like PointSplineFunction and ViewSplineFunction).
The tension value controls how tight the function’s curves are. The usual resources (Google, Wikipedia, etc.) should provide sufficient discussion of Catmul-Rom splines to answer any detailed questions.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#control_points ⇒ Object
readonly
Returns the value of attribute control_points.
-
#tension ⇒ Object
readonly
Returns the value of attribute tension.
-
#total_dur ⇒ Object
readonly
Returns the value of attribute total_dur.
Attributes inherited from FunctionMultiDim
Attributes inherited from Function
#append, #compose, #end, #max, #min, #start, #verbose
Instance Method Summary collapse
-
#add_control_point(point, dur) ⇒ Object
Adds a new control point.
-
#initialize(ndims, tension = 0.5) ⇒ SplineFunction
constructor
A new instance of SplineFunction.
-
#run_function(x) ⇒ Object
Evaluates the spline function at a given value, which should be between 0 and 1.
Methods inherited from FunctionMultiDim
Methods inherited from Function
Constructor Details
#initialize(ndims, tension = 0.5) ⇒ SplineFunction
Returns a new instance of SplineFunction.
30 31 32 33 34 35 36 |
# File 'lib/kamelopard/spline.rb', line 30 def initialize(ndims, tension = 0.5) @ndims = ndims @control_points = [] @total_dur = 0 @tension = tension super() end |
Instance Attribute Details
#control_points ⇒ Object (readonly)
Returns the value of attribute control_points.
28 29 30 |
# File 'lib/kamelopard/spline.rb', line 28 def control_points @control_points end |
#tension ⇒ Object (readonly)
Returns the value of attribute tension.
28 29 30 |
# File 'lib/kamelopard/spline.rb', line 28 def tension @tension end |
#total_dur ⇒ Object (readonly)
Returns the value of attribute total_dur.
28 29 30 |
# File 'lib/kamelopard/spline.rb', line 28 def total_dur @total_dur end |
Instance Method Details
#add_control_point(point, dur) ⇒ Object
Adds a new control point. :dur is a way of indicating the duration of the journey from the last point to this one, and is ignored for the first control point in the spline. Values for :dur are in whatever units the user wants; a spline with three control points with durations of 0, 10, and 20 will be identical to one with durations of 0, 1, and 2. – XXX: Raise an exception if the control point dimensionality doesn’t match @ndims ++
48 49 50 51 |
# File 'lib/kamelopard/spline.rb', line 48 def add_control_point(point, dur) @total_dur = @total_dur + dur if @control_points.size > 0 @control_points << [ point, dur ] end |
#run_function(x) ⇒ Object
Evaluates the spline function at a given value, which should be between 0 and 1. Returns an array of the same number of dimensions as each of the control points.
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 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/kamelopard/spline.rb', line 56 def run_function(x) # X will be between 0 and 1 # Find which control points I should am using for the point in # question dur = 0 last_dur = 0 cur_i = 0 u = 0 @control_points.each_index do |i| next if i == 0 cur_i = i last_dur = dur if 1.0 * (dur + @control_points[i][1]) / @total_dur >= x then # I've found the correct two control points: cp[i-1] and cp[i] # u is the point on the interval between the two control points # that we're interested in. 0 would be the first control point, # and 1 the second u = (x * @total_dur - dur) / @control_points[i][1] break end dur = dur + @control_points[i][1] end # http://www.cs.cmu.edu/~462/projects/assn2/assn2/catmullRom.pdf # cp = control points. cur_i will be at least 1 # I need two control points on either side of this part of the # spline. If they don't exist, duplicate the endpoints of the # control points. cp1 = @control_points[cur_i-1][0] cp2 = @control_points[cur_i][0] if cur_i == 1 then cpt1 = cp1 else cpt1 = @control_points[cur_i-2][0] end if cur_i >= @control_points.size - 1 then cpt2 = cp2 else cpt2 = @control_points[cur_i+1][0] end # Can't just say Matrix[cp], because that adds an extra # dimension to the matrix, somehow. cps = Matrix[cpt1, cp1, cp2, cpt2] t = @tension h = Matrix[ [ 0, 1, 0, 0 ], [ -t, 0, t, 0 ], [ 2*t, t-3, 3-2*t, -t ], [ -t, 2-t, t-2, t] ] p = Matrix[[1, u, u**2, u**3]] * h * cps return p.row(0) end |