xrvg 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENCE +21 -0
- data/README +88 -0
- data/Rakefile +263 -0
- data/examples/foreach.rb +9 -0
- data/examples/hellocrown.rb +7 -0
- data/examples/hellocrown2.rb +7 -0
- data/examples/hellocrownrecurse.rb +9 -0
- data/examples/helloworld.rb +5 -0
- data/examples/helloworldcompact.rb +5 -0
- data/examples/helloworldexpanded.rb +5 -0
- data/examples/palette_circle.rb +10 -0
- data/examples/sample.rb +10 -0
- data/examples/uplets.rb +9 -0
- data/lib/assertion.rb +14 -0
- data/lib/attributable.rb +152 -0
- data/lib/color.rb +295 -0
- data/lib/frame.rb +32 -0
- data/lib/geometry2D.rb +207 -0
- data/lib/interpolation.rb +59 -0
- data/lib/render.rb +269 -0
- data/lib/samplation.rb +416 -0
- data/lib/shape.rb +338 -0
- data/lib/style.rb +74 -0
- data/lib/trace.rb +28 -0
- data/lib/utils.rb +404 -0
- data/lib/xrvg.rb +37 -0
- data/test/test_attributable.rb +24 -0
- data/test/test_color.rb +79 -0
- data/test/test_frame.rb +12 -0
- data/test/test_geometry2D.rb +76 -0
- data/test/test_render.rb +18 -0
- data/test/test_style.rb +16 -0
- data/test/test_utils.rb +207 -0
- metadata +80 -0
data/lib/color.rb
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
# Color functionalities file. See
|
2
|
+
# - +Color+
|
3
|
+
# - +Palette+
|
4
|
+
# - +Gradient+
|
5
|
+
|
6
|
+
require 'geometry2D'; # for vector extension
|
7
|
+
require 'interpolation'
|
8
|
+
require 'attributable'
|
9
|
+
require 'utils'
|
10
|
+
require 'shape'; # for gradient
|
11
|
+
|
12
|
+
#
|
13
|
+
# Color class
|
14
|
+
#
|
15
|
+
# = Basics
|
16
|
+
# Class Color derives from Vector, and consists in a 4D vector of (0.0..1.0) values, for red, blue, green, and opacity
|
17
|
+
# = Utilities
|
18
|
+
# Conversion from hsv and hsl color spaces available (see this link[http://en.wikipedia.org/wiki/HSV_color_space])
|
19
|
+
# = Future
|
20
|
+
# - Must use this library[https://rubyforge.org/projects/color/], to avoid effort duplication
|
21
|
+
# - Must add relative color operations as Nodebox wants to
|
22
|
+
# - Must optimize 4D vector operations (as C extension ?)
|
23
|
+
class Color < Vector
|
24
|
+
|
25
|
+
# Color builder
|
26
|
+
#
|
27
|
+
# only allows to build 4D vector, with composants between 0.0 and 1.0
|
28
|
+
def initialize( *args )
|
29
|
+
# TODO : check args number
|
30
|
+
super( *args )
|
31
|
+
end
|
32
|
+
|
33
|
+
# return the red composant
|
34
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.1
|
35
|
+
def r
|
36
|
+
return self[0]
|
37
|
+
end
|
38
|
+
|
39
|
+
# return the green composant
|
40
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.2
|
41
|
+
def g
|
42
|
+
return self[1]
|
43
|
+
end
|
44
|
+
|
45
|
+
# return the blue composant
|
46
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.3
|
47
|
+
def b
|
48
|
+
return self[2]
|
49
|
+
end
|
50
|
+
|
51
|
+
# return the opacity composant
|
52
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.4
|
53
|
+
def a
|
54
|
+
return self[3]
|
55
|
+
end
|
56
|
+
|
57
|
+
# set the red composant
|
58
|
+
# Color[0.1,0.2,0.3,0.4].r = 0.5 => Color[0.5,0.2,0.3,0.4]
|
59
|
+
def r=(n)
|
60
|
+
self[0]= n
|
61
|
+
end
|
62
|
+
|
63
|
+
# set the green composant
|
64
|
+
# Color[0.1,0.2,0.3,0.4].g = 0.5 => Color[0.1,0.5,0.3,0.4]
|
65
|
+
def g=(n)
|
66
|
+
self[1] = n
|
67
|
+
end
|
68
|
+
|
69
|
+
# set the blue composant
|
70
|
+
# Color[0.1,0.2,0.3,0.4].b = 0.5 => Color[0.1,0.2,0.5,0.4]
|
71
|
+
def b=(n)
|
72
|
+
self[2] = n
|
73
|
+
end
|
74
|
+
|
75
|
+
# set the opacity composant
|
76
|
+
# Color[0.1,0.2,0.3,0.4].a = 0.5 => Color[0.1,0.2,0.3,0.5]
|
77
|
+
def a=(n)
|
78
|
+
self[3] = n
|
79
|
+
end
|
80
|
+
|
81
|
+
# return an array containing colors on 255 integer format
|
82
|
+
# Color[0.0,1.0,0.0,1.0].format255 => [0,255,0,255]
|
83
|
+
def format255()
|
84
|
+
return self.map {|v| (v * 255.0).to_i}
|
85
|
+
end
|
86
|
+
|
87
|
+
# return a random color vector, with 1.0 opacity !!
|
88
|
+
# Color.rand => Color[0.2345, 0.987623, 0.4123, 1.0]
|
89
|
+
def Color.rand
|
90
|
+
return Color[Kernel::rand,Kernel::rand,Kernel::rand,1.0]
|
91
|
+
end
|
92
|
+
|
93
|
+
# return a black color vector
|
94
|
+
def Color.black(opacity=1.0)
|
95
|
+
return Color[0.0, 0.0, 0.0, opacity]
|
96
|
+
end
|
97
|
+
|
98
|
+
# return a blue color vector
|
99
|
+
def Color.blue(opacity=1.0)
|
100
|
+
return Color[0.0, 0.0, 1.0, opacity]
|
101
|
+
end
|
102
|
+
|
103
|
+
# return a red color vector
|
104
|
+
def Color.red(opacity=1.0)
|
105
|
+
return Color[1.0, 0.0, 0.0, opacity]
|
106
|
+
end
|
107
|
+
|
108
|
+
# return a yellow color vector
|
109
|
+
def Color.yellow(opacity=1.0)
|
110
|
+
return Color[1.0, 1.0, 0.0, opacity]
|
111
|
+
end
|
112
|
+
|
113
|
+
# return a orange color vector
|
114
|
+
def Color.orange(opacity=1.0)
|
115
|
+
return Color[1.0, 0.5, 0.0, opacity]
|
116
|
+
end
|
117
|
+
|
118
|
+
# return a green color vector
|
119
|
+
def Color.green(opacity=1.0)
|
120
|
+
return Color[0.0, 1.0, 0.0, opacity]
|
121
|
+
end
|
122
|
+
|
123
|
+
# return a white color vector
|
124
|
+
def Color.white(opacity=1.0)
|
125
|
+
return Color[1.0, 1.0, 1.0, opacity]
|
126
|
+
end
|
127
|
+
|
128
|
+
# build a color vector from hsv parametrization (convert from hsv to rgb) h, s, v being between 0.0 and 1.0
|
129
|
+
# taken from wikipedia[http://en.wikipedia.org/wiki/HSV_color_space]
|
130
|
+
def Color.hsv( h, s, v, a)
|
131
|
+
if s == 0.0
|
132
|
+
return Color[v, v, v, a]
|
133
|
+
end
|
134
|
+
h *= 360.0
|
135
|
+
hi = (h/60.0).floor
|
136
|
+
f = (h/60.0) - hi
|
137
|
+
p = v * ( 1 - s )
|
138
|
+
q = v * ( 1 - f * s )
|
139
|
+
t = v * ( 1 - ( 1 - f ) * s )
|
140
|
+
if hi == 0
|
141
|
+
return Color[ v, t, p, a]
|
142
|
+
end
|
143
|
+
if hi == 1
|
144
|
+
return Color[ q, v, p, a]
|
145
|
+
end
|
146
|
+
if hi == 2
|
147
|
+
return Color[ p, v, t, a]
|
148
|
+
end
|
149
|
+
if hi == 3
|
150
|
+
return Color[ p, q, v, a]
|
151
|
+
end
|
152
|
+
if hi == 4
|
153
|
+
return Color[ t, p, v, a]
|
154
|
+
end
|
155
|
+
if hi == 5
|
156
|
+
return Color[ v, p, q, a]
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
def Color.getHSLcomponent( tC, p, q ) #:nodoc:
|
161
|
+
while tC < 0.0
|
162
|
+
tC = tC + 1.0
|
163
|
+
end
|
164
|
+
while tC > 1.0
|
165
|
+
tC = tC - 1.0
|
166
|
+
end
|
167
|
+
|
168
|
+
if tC < (1.0 / 6.0)
|
169
|
+
tC = p + ( (q-p) * 6.0 * tC )
|
170
|
+
elsif tC >=(1.0 / 6.0) and tC < 0.5
|
171
|
+
tC = q
|
172
|
+
elsif tC >= 0.5 and tC < (2.0 / 3.0)
|
173
|
+
tC = p + ( (q-p) * 6.0 * ((2.0 / 3.0) - tC) )
|
174
|
+
else
|
175
|
+
tC = p
|
176
|
+
end
|
177
|
+
return tC
|
178
|
+
end
|
179
|
+
|
180
|
+
# build a color vector from hsl parametrization (convert from hsl to rgb) h, s, l being between 0.0 and 1.0
|
181
|
+
# taken from [[http://en.wikipedia.org/wiki/HSV_color_space]]
|
182
|
+
# h, s, l must be between 0.0 and 1.0
|
183
|
+
def Color.hsl( h, s, l, a)
|
184
|
+
h *= 360.0
|
185
|
+
if l < 0.5
|
186
|
+
q = l * (1.0 + s)
|
187
|
+
else
|
188
|
+
q = l+ s - (l * s)
|
189
|
+
end
|
190
|
+
p = 2 * l - q
|
191
|
+
hk = h / 360.0
|
192
|
+
tR = hk + 1.0 / 3.0
|
193
|
+
tG = hk
|
194
|
+
tB = hk - 1.0 / 3.0
|
195
|
+
|
196
|
+
tR = self.getHSLcomponent( tR, p, q )
|
197
|
+
tG = self.getHSLcomponent( tG, p, q )
|
198
|
+
tB = self.getHSLcomponent( tB, p, q )
|
199
|
+
return Color[tR, tG, tB, a]
|
200
|
+
end
|
201
|
+
|
202
|
+
# get svg description of a color
|
203
|
+
def svg
|
204
|
+
values = self[0..2].map {|v| (255.0 * v).to_i }
|
205
|
+
return "rgb(#{values.join(",")})"
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
# class Palette
|
211
|
+
# = Intro
|
212
|
+
# Palette defines color palettes, as interpolation between color points. As such, use Interpolation module, so uses for the moment only linear interpolation.
|
213
|
+
# But once built with interpolation, palette provides a continuous color "interval", and so is Samplable !
|
214
|
+
# = Use
|
215
|
+
# palette = Palette[ :colorlist, [ Color.blue, 0.0, Color.orange, 0.5, Color.yellow, 1.0 ] ]
|
216
|
+
# palette.rand( 10 ) # => return 10 random colors in palette
|
217
|
+
# palette.color( 0.5 ) # => Color.orange
|
218
|
+
class Palette
|
219
|
+
include Attributable
|
220
|
+
attribute :colorlist
|
221
|
+
|
222
|
+
# compute color given float pourcentage.
|
223
|
+
# Palette[ :colorlist, [ Color.black, 0.0, Color.white, 1.0 ] ].sample( 0.5 ) => Color[0.5,0.5,0.5,1.O]
|
224
|
+
# "sample" method as defined in Samplable module
|
225
|
+
def color(dindex)
|
226
|
+
result = self.interpolate(dindex)
|
227
|
+
return Color.elements(result[0..-1],false)
|
228
|
+
end
|
229
|
+
|
230
|
+
# return a new palette by reversing the current one
|
231
|
+
def reverse()
|
232
|
+
newcolorlist = []
|
233
|
+
self.colorlist.reverse.foreach do |index,color|
|
234
|
+
newcolorlist += [color, (0.0..1.0).complement( index )]
|
235
|
+
end
|
236
|
+
return Palette[ :colorlist, newcolorlist ]
|
237
|
+
end
|
238
|
+
|
239
|
+
|
240
|
+
include Samplable
|
241
|
+
include Interpolation
|
242
|
+
|
243
|
+
def apply_sample( abs ) #:nodoc:
|
244
|
+
# Trace("Palette#apply_sample abs #{abs}")
|
245
|
+
return self.color( abs )
|
246
|
+
end
|
247
|
+
|
248
|
+
# alias apply_sample color
|
249
|
+
# alias apply_split ? => TODO
|
250
|
+
alias samplelist colorlist
|
251
|
+
alias colors samples
|
252
|
+
end
|
253
|
+
|
254
|
+
class Gradient < Palette #:nodoc:
|
255
|
+
def defsvg()
|
256
|
+
Kernel::raise("Gradient::defsvg must be redefined in subclasses")
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
class LinearGradient < Gradient #:nodoc:
|
262
|
+
|
263
|
+
def svgdef
|
264
|
+
template = '<linearGradient id="%ID%" x1="0%" y1="0%" x2="0%" y2="100%">%stops%</linearGradient>'
|
265
|
+
stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
|
266
|
+
|
267
|
+
stops = "\n"
|
268
|
+
self.colorlist.foreach do |color, index|
|
269
|
+
stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
|
270
|
+
stops += "\n"
|
271
|
+
end
|
272
|
+
|
273
|
+
return template.subreplace( {"%stops%" => stops} )
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
class CircularGradient < Gradient #:nodoc:
|
278
|
+
attribute :circle, nil, Circle
|
279
|
+
|
280
|
+
def svgdef
|
281
|
+
template = '<radialGradient id="%ID%" gradientUnits="userSpaceOnUse" cx="%cx%" cy="%cy%" r="%r%">%stops%</radialGradient>'
|
282
|
+
stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
|
283
|
+
|
284
|
+
stops = "\n"
|
285
|
+
self.colorlist.foreach do |color, index|
|
286
|
+
stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
|
287
|
+
stops += "\n"
|
288
|
+
end
|
289
|
+
|
290
|
+
return template.subreplace( {"%stops%" => stops,
|
291
|
+
"%cx%" => circle.center.x,
|
292
|
+
"%cy%" => circle.center.y,
|
293
|
+
"%r%" => circle.radius} )
|
294
|
+
end
|
295
|
+
end
|
data/lib/frame.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# frame.rb file
|
2
|
+
#
|
3
|
+
# See +Frame+
|
4
|
+
require 'attributable'
|
5
|
+
|
6
|
+
#
|
7
|
+
# Frame class
|
8
|
+
# = Intro
|
9
|
+
# Defines a local geometry. Used by +Curve+ interface.
|
10
|
+
# = Attributes
|
11
|
+
# attribute :center
|
12
|
+
# attribute :vector
|
13
|
+
# attribute :rotation
|
14
|
+
# attribute :scale
|
15
|
+
class Frame
|
16
|
+
include Attributable
|
17
|
+
attribute :center
|
18
|
+
attribute :vector
|
19
|
+
attribute :rotation
|
20
|
+
attribute :scale
|
21
|
+
|
22
|
+
def ==(other)
|
23
|
+
if self.center == other.center and
|
24
|
+
self.vector == other.vector and
|
25
|
+
self.rotation == other.rotation and
|
26
|
+
self.scale == other.scale
|
27
|
+
return true
|
28
|
+
end
|
29
|
+
return false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
data/lib/geometry2D.rb
ADDED
@@ -0,0 +1,207 @@
|
|
1
|
+
#
|
2
|
+
# Contains Ruby Vector class extension.
|
3
|
+
# See :
|
4
|
+
# - +Vector+
|
5
|
+
# - +Point+
|
6
|
+
|
7
|
+
require 'matrix'
|
8
|
+
|
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
|
+
#
|
18
|
+
# It could be efficient to define specific 2D vector, to speed up computation.
|
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.
|
21
|
+
#
|
22
|
+
# Another thing is to make this class C extension, to speed up computation.
|
23
|
+
class Vector
|
24
|
+
include Comparable
|
25
|
+
|
26
|
+
X = Vector[1.0, 0.0]
|
27
|
+
Y = Vector[0.0, 1.0]
|
28
|
+
O = Vector[0.0, 0.0]
|
29
|
+
|
30
|
+
# to optimize for 2D
|
31
|
+
if nil
|
32
|
+
def *( scalar )
|
33
|
+
return Vector[ self[0] * scalar, self[1] * scalar ]
|
34
|
+
end
|
35
|
+
|
36
|
+
def +(other)
|
37
|
+
return Vector[ self[0] + other[0], self[1] + other[1] ]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
# method necessary to make Vector Ranges
|
43
|
+
#
|
44
|
+
# make <=> on x, then on y
|
45
|
+
def <=>( other )
|
46
|
+
first = self.x <=> other.x
|
47
|
+
if first != 0
|
48
|
+
return first
|
49
|
+
else
|
50
|
+
return self.y <=> other.y
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# method necessary to make Vector Ranges
|
55
|
+
#
|
56
|
+
# simply call succ on each coord
|
57
|
+
def succ()
|
58
|
+
return Vector[ self.x.succ, self.y.succ ]
|
59
|
+
end
|
60
|
+
|
61
|
+
# compute the normalized vector given the current vector
|
62
|
+
# Vector[0.0,2.0].norm => Vector[0.0,1.0]
|
63
|
+
def norm
|
64
|
+
r = r()
|
65
|
+
if r != 0.0
|
66
|
+
return self / r
|
67
|
+
else
|
68
|
+
return Vector::O
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# compute the angle of a vector considering x axis.
|
73
|
+
# Vector[0.0,2.0].angle => 0.0
|
74
|
+
# must be made in C extension, as expensive
|
75
|
+
def angle
|
76
|
+
r = self.r()
|
77
|
+
if r == 0
|
78
|
+
return 0
|
79
|
+
else
|
80
|
+
unitary = self/r
|
81
|
+
cos, sin = unitary[0], unitary[1]
|
82
|
+
angle = Math.acos( cos )
|
83
|
+
if sin < 0.0
|
84
|
+
angle = -angle
|
85
|
+
end
|
86
|
+
return angle
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# compute the angle between two vectors
|
91
|
+
# Vector.angle( Vector[1.0,0.0], Vector[0.0,1.0] ) => Math::PI/2.0
|
92
|
+
def Vector.angle( v1, v2 )
|
93
|
+
return v1.angle - v2.angle
|
94
|
+
end
|
95
|
+
|
96
|
+
# scale a vector with another one
|
97
|
+
# Vector[1.0,2.0].scale( Vector[3.0,4.0] ) => Vector[3.0,8.0]
|
98
|
+
# v.scale( Vector[a,a] ) <=> v * a
|
99
|
+
def scale( scaler )
|
100
|
+
return Vector[self.x * scaler.x, self.y * scaler.y ]
|
101
|
+
end
|
102
|
+
|
103
|
+
# rotate a 2D vector
|
104
|
+
# Vector[1.0,0.0].rotate( Math::PI/2.0 ) => Vector[0.0,1.0]
|
105
|
+
def rotate( angle )
|
106
|
+
newx = self.x * Math.cos( angle ) - self.y * Math.sin( angle )
|
107
|
+
newy = self.x * Math.sin( angle ) + self.y * Math.cos( angle )
|
108
|
+
return Vector[ newx, newy ]
|
109
|
+
end
|
110
|
+
|
111
|
+
# compute the orthogonal vector. Equiv to .rotate( Math::PI/2.0 )
|
112
|
+
# Vector[1.0,0.0].ortho => Vector[0.0,1.0]
|
113
|
+
def ortho
|
114
|
+
return Vector[ -self[1], self[0] ]
|
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]
|
121
|
+
end
|
122
|
+
|
123
|
+
# return second coord of a vector
|
124
|
+
# Vector[1.0,2.0].y => 2.0
|
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]
|
131
|
+
def sym( other )
|
132
|
+
return self * 2.0 - other
|
133
|
+
end
|
134
|
+
|
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
|
+
# coords management between different coord systems (for the moment only euclidian and polar)
|
149
|
+
# Vector[0.0,1.0].coords => [0.0,1.0]
|
150
|
+
# Vector[0.0,1.0].coords(:polar) => [1.0,Math::PI/2.0]
|
151
|
+
def coords(type=:cartesien)
|
152
|
+
if type == :cartesien
|
153
|
+
return [self.x, self.y]
|
154
|
+
elsif type == :polar
|
155
|
+
if @polarcoords == nil
|
156
|
+
@polarcoords = [self.r, self.angle]
|
157
|
+
end
|
158
|
+
return @polarcoords
|
159
|
+
else
|
160
|
+
Kernel.raise( "Unknown coord type #{type}" )
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# build a vector from polar coords
|
165
|
+
def Vector.polar( r, angle )
|
166
|
+
x = r * Math.cos( angle )
|
167
|
+
y = r * Math.sin( angle )
|
168
|
+
return Vector[x,y]
|
169
|
+
end
|
170
|
+
|
171
|
+
alias length r
|
172
|
+
alias translate +
|
173
|
+
end
|
174
|
+
|
175
|
+
#
|
176
|
+
# Point class just to have some more meaningful notation, as Point::O
|
177
|
+
#
|
178
|
+
class Point < Vector
|
179
|
+
|
180
|
+
O = Point[0.0, 0.0]
|
181
|
+
|
182
|
+
# compute the coord box enclosing every 2D elements considered as points
|
183
|
+
# Point.viewbox( [Vector[1.0,2.0], Vector[2.0,1.0]] ) => [1.0, 1.0, 2.0, 2.0]
|
184
|
+
def Point.viewbox (pointlist)
|
185
|
+
if pointlist.size == 0
|
186
|
+
return [0.0, 0.0, 0.0, 0.0]
|
187
|
+
end
|
188
|
+
|
189
|
+
xs = pointlist.map {|p| p.x}
|
190
|
+
ys = pointlist.map {|p| p.y}
|
191
|
+
|
192
|
+
return [xs.min, ys.min, xs.max, ys.max]
|
193
|
+
end
|
194
|
+
|
195
|
+
# compute dimension of the box enclosing 2D elemnts considered as points
|
196
|
+
# Point.viewbox( [Vector[1.0,2.0], Vector[10.0,1.0]] ) => [9.0, 1.0]
|
197
|
+
# use +viewvox+
|
198
|
+
def Point.size (pointlist)
|
199
|
+
xmin, ymin, xmax, ymax = viewbox( pointlist )
|
200
|
+
return [xmax - xmin, ymax - ymin]
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
|
207
|
+
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Interpolation file.
|
2
|
+
#
|
3
|
+
# See
|
4
|
+
# - +Interpolation+
|
5
|
+
# - +Interpolator+
|
6
|
+
|
7
|
+
require 'utils'
|
8
|
+
require 'attributable'
|
9
|
+
|
10
|
+
# Interpolation module
|
11
|
+
# = Intro
|
12
|
+
# Defines an interpolation service from a samplelist that must be a list [value1, index1, value2, index2, ..., valueN, indexN],
|
13
|
+
# with index between 0.0 and 1.0 and in increasing order.
|
14
|
+
# value must be an object with + and * scalar operators defined
|
15
|
+
# = Uses
|
16
|
+
# Used for example by Palette
|
17
|
+
# = Future
|
18
|
+
# Must be extended for all kinds of interpolation (bezier, ...)
|
19
|
+
module Interpolation
|
20
|
+
|
21
|
+
# must be the overloaded method to adapt Interpolation
|
22
|
+
#
|
23
|
+
# for example, Palette redefines samplelist as
|
24
|
+
# alias samplelist colorlist
|
25
|
+
def samplelist()
|
26
|
+
return [0.0, 0.0, 1.0, 1.0]
|
27
|
+
end
|
28
|
+
|
29
|
+
# computing method
|
30
|
+
#
|
31
|
+
# from an input between 0.0 and 1.0, returns linear interpolated value
|
32
|
+
def interpolate( dindex )
|
33
|
+
result = nil
|
34
|
+
pvalue, pindex = self.samplelist[0..1]
|
35
|
+
self.samplelist.foreach do |value, index|
|
36
|
+
if dindex <= index
|
37
|
+
if dindex == index
|
38
|
+
return value
|
39
|
+
end
|
40
|
+
result = pvalue + ((value - pvalue ) * ((dindex - pindex) / (index - pindex )))
|
41
|
+
break
|
42
|
+
end
|
43
|
+
pvalue, pindex = value, index
|
44
|
+
end
|
45
|
+
if not result
|
46
|
+
result = self.samplelist[-2]
|
47
|
+
end
|
48
|
+
return result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Buildable interpolator
|
53
|
+
#
|
54
|
+
# Simply instanciated module in a class
|
55
|
+
class Interpolator
|
56
|
+
include Attributable
|
57
|
+
attribute :samplelist
|
58
|
+
include Interpolation
|
59
|
+
end
|