xrvg 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +34 -0
- data/README +2 -2
- data/Rakefile +111 -29
- data/examples/bezierbasic.rb +7 -0
- data/examples/bezierbasicvector.rb +7 -0
- data/examples/foreach.rb +1 -1
- data/examples/geodash.rb +8 -0
- data/examples/geodash2.rb +8 -0
- data/examples/hellocrown.rb +1 -1
- data/examples/hellocrown2.rb +1 -1
- data/examples/hellocrownrecurse.rb +1 -1
- data/examples/multibezierbasic.rb +8 -0
- data/examples/randomdash.rb +8 -0
- data/examples/range_examples.rb +16 -0
- data/examples/range_examples2.rb +10 -0
- data/examples/sample.rb +4 -2
- data/examples/simpledash.rb +8 -0
- data/examples/uplets.rb +1 -1
- data/lib/bezier.rb +461 -0
- data/lib/bezierspline.rb +266 -0
- data/lib/color.rb +44 -5
- data/lib/geometry2D.rb +116 -90
- data/lib/interpolation.rb +4 -2
- data/lib/samplation.rb +16 -10
- data/lib/shape.rb +101 -53
- data/lib/style.rb +13 -12
- data/lib/utils.rb +4 -3
- data/lib/xrvg.rb +2 -3
- data/test/test_bezier.rb +151 -0
- data/test/test_geometry2D.rb +38 -25
- data/test/test_shape.rb +67 -0
- data/test/test_utils.rb +45 -0
- metadata +28 -14
- data/examples/helloworld.rb +0 -5
data/lib/bezierspline.rb
ADDED
@@ -0,0 +1,266 @@
|
|
1
|
+
# +BezierSpline+ source
|
2
|
+
|
3
|
+
# BezierSpline class
|
4
|
+
#
|
5
|
+
# Internal class to represent a single-piece cubic bezier curve, defined by four points or two point + two vectors.
|
6
|
+
# You may never have to use this class. Prefer the use of +Bezier+ class
|
7
|
+
class BezierSpline #:nodoc:
|
8
|
+
|
9
|
+
def BezierSpline.[](*args)
|
10
|
+
return BezierSpline.new( *args )
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize( type, v1, v2, v3, v4 )
|
14
|
+
self.checktype( type )
|
15
|
+
self.checkvalues( v1, v2, v3, v4 )
|
16
|
+
self.initdata( type, v1, v2, v3, v4 )
|
17
|
+
end
|
18
|
+
|
19
|
+
def checkvalues( v1, v2, v3, v4 )
|
20
|
+
[v1, v2, v3, v4].each do |v|
|
21
|
+
if not v.is_a? V2D
|
22
|
+
Kernel::raise( "BezierSpline : init value #{v.inspect} is not a V2D" )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def checktype( type )
|
28
|
+
if not type == :raw || type == :vector
|
29
|
+
Kernel::raise( "BezierSpline : type #{type.inspect} is not :raw or :vector" )
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def data()
|
34
|
+
return [:raw] + self.pointlist
|
35
|
+
end
|
36
|
+
|
37
|
+
def initdata( type, v1, v2, v3, v4 )
|
38
|
+
@rawpointlist = nil
|
39
|
+
@vectorpointlist = nil
|
40
|
+
if type == :raw
|
41
|
+
@rawpointlist = [v1, v2, v3, v4]
|
42
|
+
elsif
|
43
|
+
@vectorpointlist = [v1, v2, v3, v4]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def compute_rawpointlist
|
48
|
+
# Assert{ @vectorpointlist }
|
49
|
+
@rawpointlist = [@vectorpointlist[0], @vectorpointlist[0] + @vectorpointlist[1], @vectorpointlist[2] + @vectorpointlist[3], @vectorpointlist[2]]
|
50
|
+
end
|
51
|
+
|
52
|
+
def compute_vectorpointlist
|
53
|
+
# Assert{ @rawpointlist }
|
54
|
+
@vectorpointlist = [@rawpointlist[0], @rawpointlist[1] - @rawpointlist[0], @rawpointlist[3], @rawpointlist[2] - @rawpointlist[3]]
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
def pointlist(type=:raw)
|
59
|
+
self.checktype( type )
|
60
|
+
if type == :raw
|
61
|
+
if not @rawpointlist
|
62
|
+
self.compute_rawpointlist
|
63
|
+
end
|
64
|
+
return @rawpointlist
|
65
|
+
elsif type == :vector
|
66
|
+
if not @vectorpointlist
|
67
|
+
self.compute_vectorpointlist
|
68
|
+
end
|
69
|
+
return @vectorpointlist
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# shortcut method to get curve first point
|
74
|
+
def firstpoint
|
75
|
+
return self.pointlist()[0]
|
76
|
+
end
|
77
|
+
|
78
|
+
# shortcut method to get curve last point
|
79
|
+
def lastpoint
|
80
|
+
return self.pointlist()[-1]
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# -------------------------------------------------------------
|
85
|
+
# bezier formula
|
86
|
+
# -------------------------------------------------------------
|
87
|
+
|
88
|
+
def compute_factors
|
89
|
+
p1, p2, p3, p4 = self.pointlist
|
90
|
+
@factors = [-(p1 - p2 * 3.0 + p3 * 3.0 - p4), (p1 - p2 * 2.0 + p3) * 3.0, (p2 - p1) * 3.0, p1]
|
91
|
+
@tfactors = [@factors[0], @factors[1] * 2.0 / 3.0, @factors[2] / 3.0]
|
92
|
+
@afactors = [@tfactors[0] * 2.0, @tfactors[1]]
|
93
|
+
end
|
94
|
+
|
95
|
+
# get point from the bezier curve
|
96
|
+
# this method use the definition of the bezier curve
|
97
|
+
def point( t, result=nil )
|
98
|
+
t2 = t * t
|
99
|
+
t3 = t2 * t
|
100
|
+
if not result
|
101
|
+
result = V2D[0.0,0.0]
|
102
|
+
end
|
103
|
+
|
104
|
+
if not @factors
|
105
|
+
compute_factors
|
106
|
+
end
|
107
|
+
|
108
|
+
# decomposed because avoid to build useless V2D
|
109
|
+
result.x = @factors[3].x + @factors[2].x * t + @factors[1].x * t2 + @factors[0].x * t3
|
110
|
+
result.y = @factors[3].y + @factors[2].y * t + @factors[1].y * t2 + @factors[0].y * t3
|
111
|
+
|
112
|
+
return result
|
113
|
+
end
|
114
|
+
|
115
|
+
# compute the bezier tangent vector
|
116
|
+
#
|
117
|
+
# Beware that what is actually computed here is 1/3 tangent !!
|
118
|
+
def tangent( t, result=nil )
|
119
|
+
t2 = t * t
|
120
|
+
if not result
|
121
|
+
result = V2D[0.0,0.0]
|
122
|
+
end
|
123
|
+
|
124
|
+
if not @factors
|
125
|
+
compute_factors
|
126
|
+
end
|
127
|
+
|
128
|
+
# decomposed because avoid to build useless V2D
|
129
|
+
result.x = @tfactors[2].x + @tfactors[1].x * t + @tfactors[0].x * t2
|
130
|
+
result.y = @tfactors[2].y + @tfactors[1].y * t + @tfactors[0].y * t2
|
131
|
+
|
132
|
+
return result
|
133
|
+
end
|
134
|
+
|
135
|
+
# compute the acc of bezier curve at t abscissa
|
136
|
+
def acc( t, result=nil )
|
137
|
+
if not result
|
138
|
+
result = V2D[0.0,0.0]
|
139
|
+
end
|
140
|
+
|
141
|
+
if not @factors
|
142
|
+
compute_factors
|
143
|
+
end
|
144
|
+
|
145
|
+
result.x = @afactors[1].x + @afactors[0].x * t
|
146
|
+
result.y = @afactors[1].y + @afactors[0].y * t
|
147
|
+
|
148
|
+
return result
|
149
|
+
end
|
150
|
+
|
151
|
+
# compute tangent vectors corresponding to the new subbezier
|
152
|
+
# very strange method, but effective
|
153
|
+
def subtangents (t1, t2 )
|
154
|
+
v1 = self.tangent( t1 ) * (t2 - t1)
|
155
|
+
v2 = self.tangent( t2 ) * (t1 - t2)
|
156
|
+
return [v1, v2]
|
157
|
+
end
|
158
|
+
|
159
|
+
# compute a subpiece of the current bezier
|
160
|
+
# t1 and t2 must correspond to the same atomic bezier
|
161
|
+
def subpiece (t1, t2)
|
162
|
+
tan1, tan2 = self.subtangents( t1, t2 )
|
163
|
+
return BezierSpline.new( :vector, self.point( t1 ), tan1, self.point( t2 ), tan2 )
|
164
|
+
end
|
165
|
+
|
166
|
+
def reverse()
|
167
|
+
return BezierSpline[ :raw, *self.pointlist().reverse ]
|
168
|
+
end
|
169
|
+
|
170
|
+
# simple translation operation : translate every point of the piece
|
171
|
+
# return a new Bezier
|
172
|
+
def translate( v )
|
173
|
+
newpoints = self.pointlist.map {|point| point + v}
|
174
|
+
return BezierSpline[ :raw, *newpoints ]
|
175
|
+
end
|
176
|
+
|
177
|
+
def gdebug(render)
|
178
|
+
p1, pc1, pc2, p2 = self.pointlist()
|
179
|
+
v1 = pc1 - p1
|
180
|
+
r1 = v1.r / 30.0
|
181
|
+
v2 = pc2 - p2
|
182
|
+
r2 = v2.r / 30.0
|
183
|
+
render.add( Circle[ :center, p1, :radius, r1 ], Style[ :fill, "red" ])
|
184
|
+
render.add( Circle[ :center, pc1, :radius, r1 ], Style[ :fill, "red" ])
|
185
|
+
render.add( Line[ :points, [p1, pc1] ], Style[ :stroke, "red", :strokewidth, (r1 / 10.0) ])
|
186
|
+
render.add( Circle[ :center, p2, :radius, r2 ], Style[ :fill, "red" ])
|
187
|
+
render.add( Circle[ :center, pc2, :radius, r1 ], Style[ :fill, "red" ])
|
188
|
+
render.add( Line[ :points, [p2, pc2] ], Style[ :stroke, "red", :strokewidth, (r2 / 10.0) ])
|
189
|
+
end
|
190
|
+
|
191
|
+
# -------------------------------------------------------------
|
192
|
+
# length computation
|
193
|
+
# -------------------------------------------------------------
|
194
|
+
|
195
|
+
|
196
|
+
# compute the length of the bezier curve defined by the points
|
197
|
+
#
|
198
|
+
# Algo : recursively approximate curve by lines, until length variation is under some epsilon
|
199
|
+
#
|
200
|
+
# for the moment, just take a fix number of samples, and some it
|
201
|
+
def compute_length_interpolator() #:nodoc:
|
202
|
+
sum = 0.0
|
203
|
+
previous = nil
|
204
|
+
samplelist = [0.0, 0.0]
|
205
|
+
new = V2D[0.0,0.0]
|
206
|
+
previous = nil
|
207
|
+
(0.0..1.0).samples( 100 ) do |abs|
|
208
|
+
self.point( abs, new )
|
209
|
+
if previous
|
210
|
+
sum+= (new - previous).r
|
211
|
+
samplelist += [abs, sum]
|
212
|
+
else
|
213
|
+
previous = V2D[0.0,0.0]
|
214
|
+
end
|
215
|
+
previous.x = new.x
|
216
|
+
previous.y = new.y
|
217
|
+
end
|
218
|
+
@length = samplelist[-1]
|
219
|
+
|
220
|
+
length_interpolator = nil
|
221
|
+
if @length == 0.0
|
222
|
+
newsamplelist = [0.0,0.0,1.0,0.0]
|
223
|
+
invsamplelist = [0.0,0.0,0.0,1.0]
|
224
|
+
else
|
225
|
+
newsamplelist = []
|
226
|
+
invsamplelist = []
|
227
|
+
samplelist.foreach do |abs, sum|
|
228
|
+
newsamplelist += [abs, sum / @length]
|
229
|
+
invsamplelist += [sum / @length, abs]
|
230
|
+
end
|
231
|
+
samplelist = newsamplelist
|
232
|
+
end
|
233
|
+
@abs_interpolator = Interpolator.new( :samplelist, invsamplelist )
|
234
|
+
return Interpolator.new( :samplelist, samplelist )
|
235
|
+
end
|
236
|
+
|
237
|
+
def length_interpolator() #:nodoc:
|
238
|
+
if @length_interpolator == nil
|
239
|
+
@length_interpolator = self.compute_length_interpolator()
|
240
|
+
end
|
241
|
+
return @length_interpolator
|
242
|
+
end
|
243
|
+
|
244
|
+
def length(t=nil)
|
245
|
+
if @length == nil
|
246
|
+
self.compute_length()
|
247
|
+
end
|
248
|
+
if not t
|
249
|
+
return @length
|
250
|
+
else
|
251
|
+
return @abs_interpolator.interpolate( t )
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def compute_length() #:nodoc:
|
256
|
+
self.length_interpolator()
|
257
|
+
return @length
|
258
|
+
end
|
259
|
+
|
260
|
+
def parameterfromlength( l ) #:nodoc:
|
261
|
+
result = self.length_interpolator.interpolate( l )
|
262
|
+
return result
|
263
|
+
end
|
264
|
+
|
265
|
+
|
266
|
+
end
|
data/lib/color.rb
CHANGED
@@ -8,26 +8,65 @@ require 'interpolation'
|
|
8
8
|
require 'attributable'
|
9
9
|
require 'utils'
|
10
10
|
require 'shape'; # for gradient
|
11
|
+
require 'matrix'
|
11
12
|
|
12
13
|
#
|
13
14
|
# Color class
|
14
15
|
#
|
15
16
|
# = Basics
|
16
|
-
# Class Color
|
17
|
+
# Class Color consists in a 4D vector of (0.0..1.0) values, for red, blue, green, and opacity
|
17
18
|
# = Utilities
|
18
19
|
# Conversion from hsv and hsl color spaces available (see this link[http://en.wikipedia.org/wiki/HSV_color_space])
|
19
20
|
# = Future
|
20
21
|
# - Must use this library[https://rubyforge.org/projects/color/], to avoid effort duplication
|
21
22
|
# - Must add relative color operations as Nodebox wants to
|
22
23
|
# - Must optimize 4D vector operations (as C extension ?)
|
23
|
-
class Color
|
24
|
+
class Color
|
24
25
|
|
25
26
|
# Color builder
|
26
27
|
#
|
27
28
|
# only allows to build 4D vector, with composants between 0.0 and 1.0
|
28
|
-
def
|
29
|
+
def Color.[]( *args )
|
29
30
|
# TODO : check args number
|
30
|
-
|
31
|
+
Color.new( *args )
|
32
|
+
end
|
33
|
+
|
34
|
+
# builder
|
35
|
+
#
|
36
|
+
# r, g, b, a must be between 0.0 and 1.0
|
37
|
+
def initialize( r, g, b, a)
|
38
|
+
@items = [r,g,b,a]
|
39
|
+
end
|
40
|
+
|
41
|
+
# delegation componant indexation method
|
42
|
+
def [](index)
|
43
|
+
return @items[index]
|
44
|
+
end
|
45
|
+
|
46
|
+
# define addition operation, for interpolation
|
47
|
+
def +( other )
|
48
|
+
return Color[ self.r + other.r,
|
49
|
+
self.g + other.g,
|
50
|
+
self.b + other.b,
|
51
|
+
self.a + other.a ]
|
52
|
+
end
|
53
|
+
|
54
|
+
# define scalar multiplication, for interpolation
|
55
|
+
def *( scalar )
|
56
|
+
return Color[ self.r * scalar,
|
57
|
+
self.g * scalar,
|
58
|
+
self.b * scalar,
|
59
|
+
self.a * scalar ]
|
60
|
+
end
|
61
|
+
|
62
|
+
# return [r,g,b,a]
|
63
|
+
def composants
|
64
|
+
return @items
|
65
|
+
end
|
66
|
+
|
67
|
+
# equality operator
|
68
|
+
def ==( other )
|
69
|
+
return (self.composants == other.composants)
|
31
70
|
end
|
32
71
|
|
33
72
|
# return the red composant
|
@@ -224,7 +263,7 @@ class Palette
|
|
224
263
|
# "sample" method as defined in Samplable module
|
225
264
|
def color(dindex)
|
226
265
|
result = self.interpolate(dindex)
|
227
|
-
return
|
266
|
+
return result
|
228
267
|
end
|
229
268
|
|
230
269
|
# return a new palette by reversing the current one
|
data/lib/geometry2D.rb
CHANGED
@@ -1,45 +1,90 @@
|
|
1
1
|
#
|
2
|
-
# Contains Ruby Vector class
|
2
|
+
# Contains Ruby geometric Vector 2D class.
|
3
3
|
# See :
|
4
|
-
# - +
|
5
|
-
# - +Point+
|
4
|
+
# - +V2D+
|
6
5
|
|
7
|
-
require '
|
6
|
+
require 'Attributable'
|
8
7
|
|
9
|
-
#
|
10
|
-
# Vector class extension, to provide several useful "geometrical" services
|
11
|
-
#
|
12
|
-
# For example :
|
13
|
-
# Vector[0.0,2.0].norm => Vector[0.0,1.0]
|
14
|
-
# Vector[0.0,2.0].angle => 0.0
|
15
|
-
# Vector[0.0,2.0].ortho => Vector[-2.0,0.0]
|
16
|
-
# Vector[0.0,2.0].rotate( Math::PI ) => Vector[0.0,-2.0]
|
17
8
|
#
|
18
|
-
#
|
19
|
-
# It is however not compatible with Color definition, as Vector 4D, and prevent from using this lib in 3D or 4D (with time).
|
20
|
-
# Must be checked though.
|
9
|
+
# 2D vector
|
21
10
|
#
|
22
|
-
#
|
23
|
-
|
11
|
+
# V2D class definition, to provide several useful "geometrical" services
|
12
|
+
#
|
13
|
+
# Extends and somehow redefines Vector class, to speed up computation, as less generic
|
14
|
+
#
|
15
|
+
# For example :
|
16
|
+
# V2D[0.0,2.0].norm => V2D[0.0,1.0]
|
17
|
+
# V2D[0.0,2.0].angle => 0.0
|
18
|
+
# V2D[0.0,2.0].ortho => V2D[-2.0,0.0]
|
19
|
+
# V2D[0.0,2.0].rotate( Math::PI ) => V2D[0.0,-2.0]
|
20
|
+
class V2D
|
24
21
|
include Comparable
|
22
|
+
include Attributable
|
23
|
+
attribute :x, 0.0, Float
|
24
|
+
attribute :y, 0.0, Float
|
25
|
+
|
26
|
+
# create a new 2D vector
|
27
|
+
# v = V2D[0.0,0.0]
|
28
|
+
def V2D.[](x=0.0,y=0.0)
|
29
|
+
return V2D.new(x,y)
|
30
|
+
end
|
31
|
+
|
32
|
+
# set coords
|
33
|
+
def xy=(other)
|
34
|
+
if other.is_a? Array
|
35
|
+
self.x = other[0]
|
36
|
+
self.y = other[1]
|
37
|
+
else
|
38
|
+
self.x = other.x
|
39
|
+
self.y = other.y
|
40
|
+
end
|
41
|
+
end
|
25
42
|
|
26
|
-
|
27
|
-
|
28
|
-
|
43
|
+
# initialize overloading on Attributable to speed up
|
44
|
+
def initialize(x,y) #:nodoc:
|
45
|
+
self.x = x
|
46
|
+
self.y = y
|
47
|
+
end
|
48
|
+
|
49
|
+
X = V2D[1.0, 0.0]
|
50
|
+
Y = V2D[0.0, 1.0]
|
51
|
+
O = V2D[0.0, 0.0]
|
29
52
|
|
30
|
-
#
|
31
|
-
|
53
|
+
# scalar multiplication
|
54
|
+
# V2D[1.0,2.0] * 2.0; #=> V2D[2.0,4.0]
|
32
55
|
def *( scalar )
|
33
|
-
return
|
56
|
+
return V2D[ self.x * scalar, self.y * scalar ]
|
57
|
+
end
|
58
|
+
|
59
|
+
# scalar division
|
60
|
+
# V2D[1.0,2.0] / 2.0; #=> V2D[0.5,1.0]
|
61
|
+
# ruby checks for dividing by zero
|
62
|
+
def /( scalar )
|
63
|
+
return V2D[ self.x / scalar, self.y / scalar ]
|
34
64
|
end
|
35
65
|
|
66
|
+
# 2D vector addition
|
67
|
+
# V2D[1.0,2.0] + V2D[2.0,1.0]; #=> V2D[3.0,3.0]
|
36
68
|
def +(other)
|
37
|
-
return
|
69
|
+
return V2D[ self.x + other.x, self.y + other.y ]
|
70
|
+
end
|
71
|
+
|
72
|
+
# 2D vector substraction
|
73
|
+
# V2D[1.0,2.0] - V2D[2.0,1.0]; #=> V2D[-1.0,1.0]
|
74
|
+
def -(other)
|
75
|
+
return V2D[ self.x - other.x, self.y - other.y ]
|
38
76
|
end
|
77
|
+
|
78
|
+
# 2D vector negative
|
79
|
+
# -V2D[1.0,2.0] #=> V2D[-1.0,2.0]
|
80
|
+
def -@ ()
|
81
|
+
return V2D[ -self.x, -self.y ]
|
39
82
|
end
|
40
83
|
|
84
|
+
# alias for 2D vector negative
|
85
|
+
alias reverse -@
|
41
86
|
|
42
|
-
# method necessary to make
|
87
|
+
# method necessary to make V2D Ranges
|
43
88
|
#
|
44
89
|
# make <=> on x, then on y
|
45
90
|
def <=>( other )
|
@@ -51,34 +96,41 @@ class Vector
|
|
51
96
|
end
|
52
97
|
end
|
53
98
|
|
54
|
-
# method necessary to make
|
99
|
+
# method necessary to make V2D Ranges
|
55
100
|
#
|
56
101
|
# simply call succ on each coord
|
57
102
|
def succ()
|
58
|
-
return
|
103
|
+
return V2D[ self.x.succ, self.y.succ ]
|
59
104
|
end
|
60
105
|
|
106
|
+
# compute length of 2D vector (r notation to be compatible with Vector)
|
107
|
+
def r()
|
108
|
+
return Math.hypot( self.x, self.y )
|
109
|
+
end
|
110
|
+
|
111
|
+
# alias for computing 2D vector length
|
112
|
+
alias length r
|
113
|
+
|
61
114
|
# compute the normalized vector given the current vector
|
62
|
-
#
|
115
|
+
# V2D[0.0,2.0].norm => V2D[0.0,1.0]
|
63
116
|
def norm
|
64
117
|
r = r()
|
65
118
|
if r != 0.0
|
66
119
|
return self / r
|
67
120
|
else
|
68
|
-
return
|
121
|
+
return V::O
|
69
122
|
end
|
70
123
|
end
|
71
124
|
|
72
125
|
# compute the angle of a vector considering x axis.
|
73
|
-
#
|
74
|
-
# must be made in C extension, as expensive
|
126
|
+
# V2D[0.0,2.0].angle => 0.0
|
75
127
|
def angle
|
76
128
|
r = self.r()
|
77
129
|
if r == 0
|
78
130
|
return 0
|
79
131
|
else
|
80
132
|
unitary = self/r
|
81
|
-
cos, sin = unitary
|
133
|
+
cos, sin = unitary.x, unitary.y
|
82
134
|
angle = Math.acos( cos )
|
83
135
|
if sin < 0.0
|
84
136
|
angle = -angle
|
@@ -88,66 +140,44 @@ class Vector
|
|
88
140
|
end
|
89
141
|
|
90
142
|
# compute the angle between two vectors
|
91
|
-
#
|
92
|
-
def
|
143
|
+
# V2D.angle( V2D[1.0,0.0], V2D[0.0,1.0] ) => Math::PI/2.0
|
144
|
+
def V2D.angle( v1, v2 )
|
93
145
|
return v1.angle - v2.angle
|
94
146
|
end
|
95
147
|
|
148
|
+
# 2D translation, simply defines as addition
|
149
|
+
alias translate +
|
150
|
+
|
96
151
|
# scale a vector with another one
|
97
|
-
#
|
98
|
-
# v.scale(
|
152
|
+
# V2D[1.0,2.0].scale( V2D[3.0,4.0] ) => V2D[3.0,8.0]
|
153
|
+
# v.scale( V2D[a,a] ) <=> v * a
|
99
154
|
def scale( scaler )
|
100
|
-
return
|
155
|
+
return V2D[self.x * scaler.x, self.y * scaler.y ]
|
101
156
|
end
|
102
157
|
|
103
158
|
# rotate a 2D vector
|
104
|
-
#
|
159
|
+
# V2D[1.0,0.0].rotate( Math::PI/2.0 ) => V2D[0.0,1.0]
|
105
160
|
def rotate( angle )
|
106
161
|
newx = self.x * Math.cos( angle ) - self.y * Math.sin( angle )
|
107
162
|
newy = self.x * Math.sin( angle ) + self.y * Math.cos( angle )
|
108
|
-
return
|
163
|
+
return V2D[ newx, newy ]
|
109
164
|
end
|
110
165
|
|
111
166
|
# compute the orthogonal vector. Equiv to .rotate( Math::PI/2.0 )
|
112
|
-
#
|
167
|
+
# V2D[1.0,0.0].ortho => V2D[0.0,1.0]
|
113
168
|
def ortho
|
114
|
-
return
|
115
|
-
end
|
116
|
-
|
117
|
-
# return first coord of a vector
|
118
|
-
# Vector[1.0,2.0].x => 1.0
|
119
|
-
def x
|
120
|
-
return self[0]
|
169
|
+
return V2D[ -self.y, self.x ]
|
121
170
|
end
|
122
171
|
|
123
|
-
#
|
124
|
-
#
|
125
|
-
def y
|
126
|
-
return self[1]
|
127
|
-
end
|
128
|
-
|
129
|
-
# compute the symetric of vector considered as point, with center of symetrie being other considered as point
|
130
|
-
# Vector[1.0,2.0].sym( Vector[0.0,0.0] ) => Vector[-1.0,-2.0]
|
172
|
+
# compute the symetric of vector considered as point, with center of symetrie being "other" considered as point
|
173
|
+
# V2D[1.0,2.0].sym( V2D[0.0,0.0] ) => V2D[-1.0,-2.0]
|
131
174
|
def sym( other )
|
132
175
|
return self * 2.0 - other
|
133
176
|
end
|
134
177
|
|
135
|
-
# shortcut method to do
|
136
|
-
# vector * (1.0 / scalar)
|
137
|
-
def /( scalar )
|
138
|
-
return self * (1.0 / scalar)
|
139
|
-
end
|
140
|
-
|
141
|
-
# more efficient that self * -1.0
|
142
|
-
def -@ ()
|
143
|
-
return Vector[ -self[0], -self[1] ]
|
144
|
-
end
|
145
|
-
|
146
|
-
alias reverse -@
|
147
|
-
|
148
178
|
# coords management between different coord systems (for the moment only euclidian and polar)
|
149
|
-
#
|
150
|
-
#
|
179
|
+
# V2D[0.0,1.0].coords => [0.0,1.0]
|
180
|
+
# V2D[0.0,1.0].coords(:polar) => [1.0,Math::PI/2.0]
|
151
181
|
def coords(type=:cartesien)
|
152
182
|
if type == :cartesien
|
153
183
|
return [self.x, self.y]
|
@@ -162,26 +192,26 @@ class Vector
|
|
162
192
|
end
|
163
193
|
|
164
194
|
# build a vector from polar coords
|
165
|
-
def
|
195
|
+
def V2D.polar( r, angle )
|
166
196
|
x = r * Math.cos( angle )
|
167
197
|
y = r * Math.sin( angle )
|
168
|
-
return
|
198
|
+
return V2D[x,y]
|
169
199
|
end
|
170
200
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
# Point class just to have some more meaningful notation, as Point::O
|
177
|
-
#
|
178
|
-
class Point < Vector
|
201
|
+
# compute 2D inner_product as v1.x * v2.x + v1.y * v2.y
|
202
|
+
# V2D[ 1.0, 2.0 ].inner_product( V2D[3.0,4.0] ); #=> 11.0
|
203
|
+
def inner_product( other )
|
204
|
+
return self.x * other.x + self.y * other.y
|
205
|
+
end
|
179
206
|
|
180
|
-
|
207
|
+
# specific method to test vector equality with specified precision
|
208
|
+
def V2D.vequal?( v1, v2, epsilon=0.000000001 )
|
209
|
+
return ((v2-v1).r < epsilon)
|
210
|
+
end
|
181
211
|
|
182
212
|
# compute the coord box enclosing every 2D elements considered as points
|
183
|
-
#
|
184
|
-
def
|
213
|
+
# Vector.viewbox( [Vector[1.0,2.0], Vector[2.0,1.0]] ) => [1.0, 1.0, 2.0, 2.0]
|
214
|
+
def V2D.viewbox (pointlist)
|
185
215
|
if pointlist.size == 0
|
186
216
|
return [0.0, 0.0, 0.0, 0.0]
|
187
217
|
end
|
@@ -193,15 +223,11 @@ class Point < Vector
|
|
193
223
|
end
|
194
224
|
|
195
225
|
# compute dimension of the box enclosing 2D elemnts considered as points
|
196
|
-
#
|
226
|
+
# Vector.viewbox( [Vector[1.0,2.0], Vector[10.0,1.0]] ) => [9.0, 1.0]
|
197
227
|
# use +viewvox+
|
198
|
-
def
|
199
|
-
xmin, ymin, xmax, ymax = viewbox( pointlist )
|
228
|
+
def V2D.size (pointlist)
|
229
|
+
xmin, ymin, xmax, ymax = V2D.viewbox( pointlist )
|
200
230
|
return [xmax - xmin, ymax - ymin]
|
201
231
|
end
|
202
|
-
|
203
232
|
end
|
204
233
|
|
205
|
-
|
206
|
-
|
207
|
-
|
data/lib/interpolation.rb
CHANGED
@@ -23,12 +23,14 @@ module Interpolation
|
|
23
23
|
# for example, Palette redefines samplelist as
|
24
24
|
# alias samplelist colorlist
|
25
25
|
def samplelist()
|
26
|
-
|
26
|
+
Kernel::raise("Interpolation::samplelist method must be redefined in subclasses")
|
27
27
|
end
|
28
28
|
|
29
29
|
# computing method
|
30
30
|
#
|
31
31
|
# from an input between 0.0 and 1.0, returns linear interpolated value
|
32
|
+
#
|
33
|
+
# interpolate uses + and * scalar operators on interpolation values
|
32
34
|
def interpolate( dindex )
|
33
35
|
result = nil
|
34
36
|
pvalue, pindex = self.samplelist[0..1]
|
@@ -37,7 +39,7 @@ module Interpolation
|
|
37
39
|
if dindex == index
|
38
40
|
return value
|
39
41
|
end
|
40
|
-
result = pvalue + ((value
|
42
|
+
result = pvalue + ((value + pvalue * (-1.0) ) * ((dindex - pindex) / (index - pindex )))
|
41
43
|
break
|
42
44
|
end
|
43
45
|
pvalue, pindex = value, index
|