xrvg 0.0.7 → 0.0.8
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 +10 -2
- data/examples/arcrecurse2.rb +1 -1
- data/examples/gradientgeo.rb +1 -1
- data/examples/hellocrown2.rb +1 -1
- data/examples/palette_circle.rb +1 -1
- data/examples/sample.rb +1 -1
- data/lib/bezier.rb +588 -0
- data/lib/bezierbuilders.rb +210 -0
- data/lib/beziermotifs.rb +121 -0
- data/lib/bezierspline.rb +235 -0
- data/lib/beziertools.rb +245 -0
- data/lib/color.rb +567 -0
- data/lib/fitting.rb +203 -0
- data/lib/frame.rb +33 -0
- data/lib/geovariety.rb +128 -0
- data/lib/interbezier.rb +87 -0
- data/lib/intersection.rb +89 -0
- data/lib/render.rb +266 -0
- data/lib/samplation.rb +44 -1
- data/lib/shape.rb +421 -0
- data/lib/spiral.rb +89 -0
- data/lib/style.rb +76 -0
- data/lib/utils.rb +1 -17
- data/lib/xrvg.rb +47 -0
- data/test/test_attributable.rb +19 -0
- data/test/test_bezier.rb +7 -0
- data/test/test_color.rb +36 -1
- data/test/test_intersection.rb +42 -0
- data/test/test_sample.rb +56 -5
- data/test/test_spiral.rb +8 -0
- data/test/test_utils.rb +1 -32
- metadata +19 -2
data/lib/color.rb
ADDED
@@ -0,0 +1,567 @@
|
|
1
|
+
# Color functionalities file. See
|
2
|
+
# - +Color+
|
3
|
+
# - +Palette+
|
4
|
+
# - +Gradient+
|
5
|
+
|
6
|
+
require 'utils'
|
7
|
+
require 'interpolation'
|
8
|
+
require 'shape'; # for gradient
|
9
|
+
|
10
|
+
module XRVG
|
11
|
+
#
|
12
|
+
# Color class
|
13
|
+
#
|
14
|
+
# = Basics
|
15
|
+
# Class Color consists in a 4D vector of (0.0..1.0) values, for red, blue, green, and opacity
|
16
|
+
# = Utilities
|
17
|
+
# Conversion from hsv and hsl color spaces available (see this link[http://en.wikipedia.org/wiki/HSV_color_space])
|
18
|
+
# = Future
|
19
|
+
# - Must use this library[https://rubyforge.org/projects/color/], to avoid effort duplication
|
20
|
+
# - Must add relative color operations as Nodebox wants to
|
21
|
+
# - Must optimize 4D vector operations (as C extension ?)
|
22
|
+
class Color
|
23
|
+
|
24
|
+
# Color builder
|
25
|
+
#
|
26
|
+
# only allows to build 4D vector, with composants between 0.0 and 1.0
|
27
|
+
def Color.[]( *args )
|
28
|
+
# TODO : check args number
|
29
|
+
Color.new( *args )
|
30
|
+
end
|
31
|
+
|
32
|
+
# builder
|
33
|
+
#
|
34
|
+
# r, g, b, a must be between 0.0 and 1.0
|
35
|
+
def initialize( r, g, b, a)
|
36
|
+
# cannot trim because otherwise color interpolation cannot be right !!
|
37
|
+
# r = Range.O.trim( r )
|
38
|
+
# g = Range.O.trim( g )
|
39
|
+
# b = Range.O.trim( b )
|
40
|
+
# a = Range.O.trim( a )
|
41
|
+
@items = [r,g,b,a]
|
42
|
+
end
|
43
|
+
|
44
|
+
# delegation componant indexation method
|
45
|
+
def [](index)
|
46
|
+
return @items[index]
|
47
|
+
end
|
48
|
+
|
49
|
+
# define addition operation, for interpolation
|
50
|
+
def +( other )
|
51
|
+
return Color[ self.r + other.r,
|
52
|
+
self.g + other.g,
|
53
|
+
self.b + other.b,
|
54
|
+
self.a + other.a ]
|
55
|
+
end
|
56
|
+
|
57
|
+
# define scalar multiplication, for interpolation
|
58
|
+
def *( scalar )
|
59
|
+
return Color[ self.r * scalar,
|
60
|
+
self.g * scalar,
|
61
|
+
self.b * scalar,
|
62
|
+
self.a * scalar ]
|
63
|
+
end
|
64
|
+
|
65
|
+
# return [r,g,b,a]
|
66
|
+
def rgba
|
67
|
+
return @items
|
68
|
+
end
|
69
|
+
|
70
|
+
# return hsva components
|
71
|
+
def hsva
|
72
|
+
return (Color.rgb2hsv(self.r, self.g, self.b) + [self.a])
|
73
|
+
end
|
74
|
+
|
75
|
+
# return hsla components
|
76
|
+
def hsla
|
77
|
+
return (Color.rgb2hsl(self.r, self.g, self.b) + [self.a])
|
78
|
+
end
|
79
|
+
|
80
|
+
# equality operator
|
81
|
+
def ==( other )
|
82
|
+
return (self.rgba == other.rgba)
|
83
|
+
end
|
84
|
+
|
85
|
+
# return the red composant
|
86
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.1
|
87
|
+
# Color[0.1,0.2,0.3,0.4].r( 0.3 ) => Color[0.3,0.2,0.3,0.4]
|
88
|
+
def r(val=nil)
|
89
|
+
if not val
|
90
|
+
return self[0]
|
91
|
+
else
|
92
|
+
return Color[ val, self.g, self.b, self.a ]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# return the green composant
|
97
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.2
|
98
|
+
def g(val=nil)
|
99
|
+
if not val
|
100
|
+
return self[1]
|
101
|
+
else
|
102
|
+
return Color[ self.r, val, self.b, self.a ]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# return the blue composant
|
107
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.3
|
108
|
+
def b(val=nil)
|
109
|
+
if not val
|
110
|
+
return self[2]
|
111
|
+
else
|
112
|
+
return Color[ self.r, self.g, val, self.a ]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# return the opacity composant
|
117
|
+
# Color[0.1,0.2,0.3,0.4].r => 0.4
|
118
|
+
def a(val=nil)
|
119
|
+
if not val
|
120
|
+
return self[3]
|
121
|
+
else
|
122
|
+
return Color[ self.r, self.g, self.b, val ]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
# get or set hue of color
|
127
|
+
# Color.red.hue => 0.0
|
128
|
+
# Color.red.hue( 0.1 ) => Color.hsva( 0.1, 1.0, 1.0, 1.0 )
|
129
|
+
def hue(newhue=nil)
|
130
|
+
if not newhue
|
131
|
+
return self.hsva[0]
|
132
|
+
else
|
133
|
+
hsva = self.hsva
|
134
|
+
hsva[0] = newhue
|
135
|
+
return Color.hsva( *hsva )
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# set saturation of color
|
140
|
+
# Color.red.saturation => 1.0
|
141
|
+
# Color.red.saturation( 0.1 ) => Color.hsva( 0.0, 0.1, 1.0, 1.0 )
|
142
|
+
def saturation(newsat=nil)
|
143
|
+
if not newsat
|
144
|
+
return self.hsva[1]
|
145
|
+
else
|
146
|
+
hsva = self.hsva
|
147
|
+
hsva[1] = newsat
|
148
|
+
return Color.hsva( *hsva )
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# set value (from hsv) of color
|
153
|
+
# Color.red.value => 1.0
|
154
|
+
# Color.red.value( 0.1 ) => Color.hsva( 0.0, 1.0, 0.1, 1.0 )
|
155
|
+
def value(newval=nil)
|
156
|
+
if not newval
|
157
|
+
return self.hsva[2]
|
158
|
+
else
|
159
|
+
hsva = self.hsva
|
160
|
+
hsva[2] = newval
|
161
|
+
return Color.hsva( *hsva )
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# set lightness (from hsl) of color
|
166
|
+
# Color.white.lightness => 1.0
|
167
|
+
# Color.white.lightness( 0.1 ) => Color.hsla( 1.0, 1.0, 0.1, 1.0 )
|
168
|
+
def lightness(newlight=nil)
|
169
|
+
if not newlight
|
170
|
+
return self.hsla[2]
|
171
|
+
else
|
172
|
+
hsla = self.hsla
|
173
|
+
hsla[2] = newlight
|
174
|
+
return Color.hsla( *hsla )
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# set the red composant
|
179
|
+
# Color[0.1,0.2,0.3,0.4].r = 0.5 => Color[0.5,0.2,0.3,0.4]
|
180
|
+
def r=(n)
|
181
|
+
@items[0]= n
|
182
|
+
end
|
183
|
+
|
184
|
+
# set the green composant
|
185
|
+
# Color[0.1,0.2,0.3,0.4].g = 0.5 => Color[0.1,0.5,0.3,0.4]
|
186
|
+
def g=(n)
|
187
|
+
@items[1] = n
|
188
|
+
end
|
189
|
+
|
190
|
+
# set the blue composant
|
191
|
+
# Color[0.1,0.2,0.3,0.4].b = 0.5 => Color[0.1,0.2,0.5,0.4]
|
192
|
+
def b=(n)
|
193
|
+
@items[2] = n
|
194
|
+
end
|
195
|
+
|
196
|
+
# set the opacity composant
|
197
|
+
# Color[0.1,0.2,0.3,0.4].a = 0.5 => Color[0.1,0.2,0.3,0.5]
|
198
|
+
def a=(n)
|
199
|
+
@items[3] = n
|
200
|
+
end
|
201
|
+
|
202
|
+
# compute complementary color
|
203
|
+
# Color.red.complement => Color.green
|
204
|
+
def complement
|
205
|
+
newvalues = self.rgba[0..-2].map {|v| Range.O.complement( v )}
|
206
|
+
newvalues += [self.a]
|
207
|
+
return Color[ *newvalues ]
|
208
|
+
end
|
209
|
+
|
210
|
+
# return an array containing colors on 255 integer format
|
211
|
+
# Color[0.0,1.0,0.0,1.0].format255 => [0,255,0,255]
|
212
|
+
def format255()
|
213
|
+
return @items.map {|v| (v * 255.0).to_i}
|
214
|
+
end
|
215
|
+
|
216
|
+
# return a random color vector, with 1.0 opacity !!
|
217
|
+
# Color.rand => Color[0.2345, 0.987623, 0.4123, 1.0]
|
218
|
+
def Color.rand( opacity=1.0 )
|
219
|
+
return Color[Kernel::rand,Kernel::rand,Kernel::rand,opacity]
|
220
|
+
end
|
221
|
+
|
222
|
+
# return a black color vector
|
223
|
+
def Color.black(opacity=1.0)
|
224
|
+
return Color[0.0, 0.0, 0.0, opacity]
|
225
|
+
end
|
226
|
+
|
227
|
+
# return a blue color vector
|
228
|
+
def Color.blue(opacity=1.0)
|
229
|
+
return Color[0.0, 0.0, 1.0, opacity]
|
230
|
+
end
|
231
|
+
|
232
|
+
# return a red color vector
|
233
|
+
def Color.red(opacity=1.0)
|
234
|
+
return Color[1.0, 0.0, 0.0, opacity]
|
235
|
+
end
|
236
|
+
|
237
|
+
# return a yellow color vector
|
238
|
+
def Color.yellow(opacity=1.0)
|
239
|
+
return Color[1.0, 1.0, 0.0, opacity]
|
240
|
+
end
|
241
|
+
|
242
|
+
# return a orange color vector
|
243
|
+
def Color.orange(opacity=1.0)
|
244
|
+
return Color[1.0, 0.5, 0.0, opacity]
|
245
|
+
end
|
246
|
+
|
247
|
+
# return a green color vector
|
248
|
+
def Color.green(opacity=1.0)
|
249
|
+
return Color[0.0, 1.0, 0.0, opacity]
|
250
|
+
end
|
251
|
+
|
252
|
+
# return a white color vector
|
253
|
+
def Color.white(opacity=1.0)
|
254
|
+
return Color[1.0, 1.0, 1.0, opacity]
|
255
|
+
end
|
256
|
+
|
257
|
+
# return a grey color vector
|
258
|
+
def Color.grey(light,opacity=1.0)
|
259
|
+
return Color[light, light, light, opacity]
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
# build a color vector from hsv parametrization (convert from hsv to rgb) h, s, v being between 0.0 and 1.0
|
264
|
+
#
|
265
|
+
# taken from wikipedia[http://en.wikipedia.org/wiki/HSV_color_space]
|
266
|
+
#
|
267
|
+
# error on algo with h = 1.0 => hi == 6 must be taken into account
|
268
|
+
def Color.hsva( h, s, v, a)
|
269
|
+
h = Range.O.trim( h )
|
270
|
+
s = Range.O.trim( s )
|
271
|
+
v = Range.O.trim( v )
|
272
|
+
a = Range.O.trim( a )
|
273
|
+
values = Color.hsv(h,s,v) + [a]
|
274
|
+
return Color[*values]
|
275
|
+
end
|
276
|
+
|
277
|
+
def Color.hsv( h, s, v)
|
278
|
+
if s == 0.0
|
279
|
+
return [v, v, v]
|
280
|
+
end
|
281
|
+
h *= 360.0
|
282
|
+
hi = (h/60.0).floor
|
283
|
+
if hi == 6
|
284
|
+
hi = 5
|
285
|
+
end
|
286
|
+
f = (h/60.0) - hi
|
287
|
+
p = v * ( 1 - s )
|
288
|
+
q = v * ( 1 - f * s )
|
289
|
+
t = v * ( 1 - ( 1 - f ) * s )
|
290
|
+
if hi == 0
|
291
|
+
return [ v, t, p]
|
292
|
+
end
|
293
|
+
if hi == 1
|
294
|
+
return [ q, v, p]
|
295
|
+
end
|
296
|
+
if hi == 2
|
297
|
+
return [ p, v, t]
|
298
|
+
end
|
299
|
+
if hi == 3
|
300
|
+
return [ p, q, v]
|
301
|
+
end
|
302
|
+
if hi == 4
|
303
|
+
return [ t, p, v]
|
304
|
+
end
|
305
|
+
if hi == 5
|
306
|
+
return [ v, p, q]
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
def Color.getHSLcomponent( tC, p, q ) #:nodoc:
|
312
|
+
while tC < 0.0
|
313
|
+
tC = tC + 1.0
|
314
|
+
end
|
315
|
+
while tC > 1.0
|
316
|
+
tC = tC - 1.0
|
317
|
+
end
|
318
|
+
|
319
|
+
if tC < (1.0 / 6.0)
|
320
|
+
tC = p + ( (q-p) * 6.0 * tC )
|
321
|
+
elsif tC >=(1.0 / 6.0) and tC < 0.5
|
322
|
+
tC = q
|
323
|
+
elsif tC >= 0.5 and tC < (2.0 / 3.0)
|
324
|
+
tC = p + ( (q-p) * 6.0 * ((2.0 / 3.0) - tC) )
|
325
|
+
else
|
326
|
+
tC = p
|
327
|
+
end
|
328
|
+
return tC
|
329
|
+
end
|
330
|
+
|
331
|
+
# build a color vector from hsl parametrization (convert from hsl to rgb) h, s, l being between 0.0 and 1.0
|
332
|
+
# taken from [[http://en.wikipedia.org/wiki/HSV_color_space]]
|
333
|
+
# h, s, l must be between 0.0 and 1.0
|
334
|
+
def Color.hsla( h, s, l, a)
|
335
|
+
Trace("Color hsla")
|
336
|
+
h = Range.O.trim( h )
|
337
|
+
s = Range.O.trim( s )
|
338
|
+
l = Range.O.trim( l )
|
339
|
+
a = Range.O.trim( a )
|
340
|
+
values = Color.hsl( h, s, l ) + [a]
|
341
|
+
return Color[*values]
|
342
|
+
end
|
343
|
+
|
344
|
+
def Color.hsl( h, s, l)
|
345
|
+
h *= 360.0
|
346
|
+
if l < 0.5
|
347
|
+
q = l * (1.0 + s)
|
348
|
+
else
|
349
|
+
q = l+ s - (l * s)
|
350
|
+
end
|
351
|
+
p = 2 * l - q
|
352
|
+
hk = h / 360.0
|
353
|
+
tR = hk + 1.0 / 3.0
|
354
|
+
tG = hk
|
355
|
+
tB = hk - 1.0 / 3.0
|
356
|
+
|
357
|
+
tR = self.getHSLcomponent( tR, p, q )
|
358
|
+
tG = self.getHSLcomponent( tG, p, q )
|
359
|
+
tB = self.getHSLcomponent( tB, p, q )
|
360
|
+
return [tR, tG, tB]
|
361
|
+
end
|
362
|
+
|
363
|
+
# from http://en.wikipedia.org/wiki/HSL_and_HSV#Conversion_from_RGB_to_HSL_or_HSV
|
364
|
+
def Color.rgb2h(r,g,b)
|
365
|
+
result = 0.0
|
366
|
+
range = [r,g,b].range
|
367
|
+
if range.begin == range.end
|
368
|
+
result = 0.0
|
369
|
+
elsif range.end == r
|
370
|
+
result = (60.0 * (g - b) / range.size + 0.0)
|
371
|
+
elsif range.end == g
|
372
|
+
result = (60.0 * (b - r) / range.size + 120.0)
|
373
|
+
else
|
374
|
+
result = (60.0 * (r - g) / range.size + 240.0)
|
375
|
+
end
|
376
|
+
return (result % 360.0) / 360.0
|
377
|
+
end
|
378
|
+
|
379
|
+
def Color.rgb2sl(r,g,b)
|
380
|
+
range = [r,g,b].range
|
381
|
+
l = range.middle
|
382
|
+
s = 0.0
|
383
|
+
if range.begin == range.end
|
384
|
+
s = 0.0
|
385
|
+
elsif l <= 0.5
|
386
|
+
s = range.size / (2.0 * l)
|
387
|
+
else
|
388
|
+
s = range.size / (2.0 - 2.0 * l)
|
389
|
+
end
|
390
|
+
return [s,l]
|
391
|
+
end
|
392
|
+
|
393
|
+
def Color.rgb2sv(r,g,b)
|
394
|
+
range = [r,g,b].range
|
395
|
+
v = range.end
|
396
|
+
if v == 0.0
|
397
|
+
s = 0.0
|
398
|
+
else
|
399
|
+
s = (1.0 - (range.begin/range.end))
|
400
|
+
end
|
401
|
+
return [s,v]
|
402
|
+
end
|
403
|
+
|
404
|
+
def Color.rgb2hsl(r,g,b)
|
405
|
+
h = Color.rgb2h(r,g,b)
|
406
|
+
s, l = Color.rgb2sl(r,g,b)
|
407
|
+
return [h,s,l]
|
408
|
+
end
|
409
|
+
|
410
|
+
def Color.rgb2hsv(r,g,b)
|
411
|
+
h = Color.rgb2h(r,g,b)
|
412
|
+
s, v = Color.rgb2sv(r,g,b)
|
413
|
+
return [h,s,v]
|
414
|
+
end
|
415
|
+
|
416
|
+
# get svg description of a color
|
417
|
+
def svg
|
418
|
+
values = self[0..2].map do |v|
|
419
|
+
v = Range.O.trim( v )
|
420
|
+
(255.0 * v).to_i
|
421
|
+
end
|
422
|
+
return "rgb(#{values.join(",")})"
|
423
|
+
end
|
424
|
+
|
425
|
+
end
|
426
|
+
|
427
|
+
# class Palette
|
428
|
+
# = Intro
|
429
|
+
# Palette defines color palettes, as interpolation between color points. As such, use Interpolation module, so uses for the moment only linear interpolation.
|
430
|
+
# But once built with interpolation, palette provides a continuous color "interval", and so is Samplable !
|
431
|
+
# = Use
|
432
|
+
# palette = Palette[ :colorlist, [ 0.0, Color.blue, 0.5, Color.orange, 1.0, Color.yellow ] ]
|
433
|
+
# palette.rand( 10 ) # => return 10 random colors in palette
|
434
|
+
# palette.color( 0.5 ) # => Color.orange
|
435
|
+
class Palette
|
436
|
+
include Attributable
|
437
|
+
attribute :colorlist, nil, Array
|
438
|
+
attribute :interpoltype, :linear
|
439
|
+
attribute :interpoldomain, :rgb # can also be :hsv or :hsl
|
440
|
+
|
441
|
+
include Samplable
|
442
|
+
include Interpolation
|
443
|
+
|
444
|
+
def initialize( *args )
|
445
|
+
super( *args )
|
446
|
+
build_interpolators
|
447
|
+
end
|
448
|
+
|
449
|
+
# build an interpolator by color componant
|
450
|
+
def build_interpolators()
|
451
|
+
vlists = [[],[],[],[]]
|
452
|
+
self.colorlist.foreach do |index, color|
|
453
|
+
values = []
|
454
|
+
if self.interpoldomain == :rgb
|
455
|
+
values = color.rgba
|
456
|
+
elsif self.interpoldomain == :hsv
|
457
|
+
values = color.hsva
|
458
|
+
elsif self.interpoldomain == :hsl
|
459
|
+
values = color.hsla
|
460
|
+
else
|
461
|
+
raise RuntimeError.new("#{self.interpoldomain} is an unknown color scheme.")
|
462
|
+
end
|
463
|
+
vlists[0] += [index, values[0] ]
|
464
|
+
vlists[1] += [index, values[1] ]
|
465
|
+
vlists[2] += [index, values[2] ]
|
466
|
+
vlists[3] += [index, values[3] ]
|
467
|
+
end
|
468
|
+
@interpolators = vlists.map {|samplelist| Interpolator[ :samplelist, samplelist, :interpoltype, self.interpoltype]}
|
469
|
+
end
|
470
|
+
|
471
|
+
# interpolators accessor (for debugging)
|
472
|
+
def interpolators()
|
473
|
+
return @interpolators
|
474
|
+
end
|
475
|
+
|
476
|
+
# overloading to reset interpolators if interpoltype changes
|
477
|
+
def interpoltype=(value)
|
478
|
+
@interpoltype = value
|
479
|
+
if @interpolators
|
480
|
+
self.build_interpolators
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
# method overloading to delegate computation to componant interpolators
|
485
|
+
def interpolate( dindex )
|
486
|
+
vs = self.interpolators.map {|inter| inter.interpolate( dindex )}
|
487
|
+
if self.interpoldomain == :rgb
|
488
|
+
return Color[ *vs ]
|
489
|
+
elsif self.interpoldomain == :hsv
|
490
|
+
return Color.hsva( *vs )
|
491
|
+
else
|
492
|
+
return Color.hsla( *vs )
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
# compute color given float pourcentage.
|
497
|
+
# Palette[ :colorlist, [ 0.0, Color.black, 1.0, Color.white ] ].sample( 0.5 ) => Color[0.5,0.5,0.5,1.O]
|
498
|
+
# "sample" method as defined in Samplable module
|
499
|
+
def color(dindex)
|
500
|
+
result = self.interpolate(dindex)
|
501
|
+
return result
|
502
|
+
end
|
503
|
+
|
504
|
+
# return a new palette by reversing the current one
|
505
|
+
def reverse()
|
506
|
+
newcolorlist = []
|
507
|
+
self.colorlist.reverse.foreach do |color, index|
|
508
|
+
newcolorlist += [(0.0..1.0).complement( index ), color]
|
509
|
+
end
|
510
|
+
return Palette[ :colorlist, newcolorlist ]
|
511
|
+
end
|
512
|
+
|
513
|
+
def apply_sample( abs ) #:nodoc:
|
514
|
+
Trace("Palette#apply_sample abs #{abs}")
|
515
|
+
return self.color( abs )
|
516
|
+
end
|
517
|
+
|
518
|
+
|
519
|
+
# alias apply_sample color
|
520
|
+
# alias apply_split ? => TODO
|
521
|
+
alias samplelist colorlist
|
522
|
+
alias colors samples
|
523
|
+
end
|
524
|
+
|
525
|
+
class Gradient < Palette #:nodoc:
|
526
|
+
def defsvg()
|
527
|
+
raise NotImplementedError.new("#{self.class.name}#defsvg is an abstract method.")
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
531
|
+
|
532
|
+
class LinearGradient < Gradient #:nodoc:
|
533
|
+
|
534
|
+
def svgdef
|
535
|
+
template = '<linearGradient id="%ID%" x1="0%" y1="0%" x2="0%" y2="100%">%stops%</linearGradient>'
|
536
|
+
stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
|
537
|
+
|
538
|
+
stops = "\n"
|
539
|
+
self.colorlist.foreach do |index, color|
|
540
|
+
stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
|
541
|
+
stops += "\n"
|
542
|
+
end
|
543
|
+
|
544
|
+
return template.subreplace( {"%stops%" => stops} )
|
545
|
+
end
|
546
|
+
end
|
547
|
+
|
548
|
+
class CircularGradient < Gradient #:nodoc:
|
549
|
+
attribute :circle, nil, Circle
|
550
|
+
|
551
|
+
def svgdef
|
552
|
+
template = '<radialGradient id="%ID%" gradientUnits="userSpaceOnUse" cx="%cx%" cy="%cy%" r="%r%">%stops%</radialGradient>'
|
553
|
+
stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
|
554
|
+
|
555
|
+
stops = "\n"
|
556
|
+
self.colorlist.foreach do |index, color|
|
557
|
+
stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
|
558
|
+
stops += "\n"
|
559
|
+
end
|
560
|
+
|
561
|
+
return template.subreplace( {"%stops%" => stops,
|
562
|
+
"%cx%" => circle.center.x,
|
563
|
+
"%cy%" => circle.center.y,
|
564
|
+
"%r%" => circle.radius} )
|
565
|
+
end
|
566
|
+
end
|
567
|
+
end
|