xrvg 0.0.1 → 0.0.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/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
|