xrvg 0.0.1

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/lib/utils.rb ADDED
@@ -0,0 +1,404 @@
1
+ #
2
+ # Several Ruby base class extensions
3
+ #
4
+ # Please also refer to the following link[http://xrvg.rubyforge.org/], and more particularly this[http://xrvg.rubyforge.org/RubyXRVGExtension.html], for
5
+ # further details.
6
+ #
7
+ # This contains :
8
+ # - +Range+ extension
9
+ # - +Array+ extension
10
+
11
+ require 'enumerator'
12
+ require 'trace'
13
+ require 'samplation'
14
+ require 'assertion'
15
+
16
+ #
17
+ # Extend class Range with module Samplable and other utilitary methods
18
+ # = Intro
19
+ # Range class is used in XRVG as a continuous 1D float interval.
20
+ # See this[http://xrvg.rubyforge.org/RubyXRVGExtension.html] for presentation
21
+ # = Use
22
+ # (0.0..1.0).rand
23
+ # (0.0..1.0).middle # => 0.5
24
+ # (0.0..2.0).sample(0.3) # => 0.6
25
+ # (0.0..1.0).samples( 3 ) {|v| puts v} # => "0.0" "0.5" "1.0"
26
+ #
27
+ class Range
28
+
29
+ # constant for (0.0..1.0) range
30
+ #
31
+ # must be used only as const (else use Range.O)
32
+ O = (0.0..1.0)
33
+
34
+ # constant for Angle range
35
+ #
36
+ # must be used only as const (else use Range.Angle)
37
+ Angle = (0.0..2.0*Math::PI)
38
+
39
+ # return unitary range, that is
40
+ # (0.0..1.0)
41
+ def Range.O
42
+ return Range::O.clone
43
+ end
44
+
45
+ # return angle range, that is
46
+ # (0.0..2.0*Math::PI)
47
+ def Range.Angle
48
+ return Range::Angle.clone
49
+ end
50
+
51
+
52
+ # compute the symetric of value in context of range
53
+ # (0.0..1.0).complement( 0.3 ) => 0.7
54
+ def complement( value )
55
+ diff = value - self.begin
56
+ return (self.end - value)
57
+ end
58
+
59
+
60
+ # -------------------------------------------------------------
61
+ # Samplable interface include and overriding
62
+ # -------------------------------------------------------------
63
+ include Samplable
64
+ include Splittable
65
+
66
+ # Range base FloatFunctor overloading to do
67
+ # (1.0..2.0).sample( 0.3 ) => 1.3
68
+ # (1.0..2.0).samples( 3 ) => [1.0, 1.5, 2.0]
69
+ def transform( value )
70
+ return (self.begin + ( self.end - self.begin ) * value)
71
+ end
72
+
73
+ # to speed up samplation on ranges, as used everywhere
74
+ def generate( nsamples ) #:nodoc:
75
+ result = []
76
+ if nsamples == 1
77
+ result = [0.0]
78
+ else
79
+ (nsamples).times {|i| result.push( i.to_f / (nsamples-1) )}
80
+ end
81
+ return result
82
+ end
83
+
84
+ # apply_sample is good by default
85
+ # apply_split is good by default
86
+
87
+ # size of the range
88
+ # (0.0..2.0).size => 2.0
89
+ def size
90
+ return self.end - self.begin
91
+ end
92
+
93
+ # returns a reversed range
94
+ # (1.0..2.0).reverse => (2.0..1.0)
95
+ def reverse
96
+ return Range.new( self.end, self.begin )
97
+ end
98
+
99
+ # resize range by factor, with fixed point center of the range
100
+ # (1.0..2.0).resize( 0.5 ) => (1.25..1.75)
101
+ def resize( factor )
102
+ center = self.sample( 0.5 )
103
+ halfsize = self.size / 2.0
104
+ newhalfsize = halfsize * factor
105
+ return (center - newhalfsize .. center + newhalfsize)
106
+ end
107
+
108
+ # mean value of the range (equiv to sample( 0.5 ))
109
+ # -> define in Samplable
110
+ # def mean()
111
+ # return self.sample( 0.5 )
112
+ # end
113
+
114
+ # alias mean as middle, as sample( 0.5 )
115
+ # -> define in Samplable
116
+ # alias middle mean
117
+
118
+ # return range with previous begin as new middle
119
+ # (1.0..2.0).sym => (0.0..2.0)
120
+ def sym()
121
+ return (self.begin - (self.end - self.begin ) .. self.end )
122
+ end
123
+
124
+ # return range with previous end as new middle
125
+ # (1.0..2.0).symend => (1.0..3.0)
126
+ def symend()
127
+ return (self.begin .. self.end + (self.end - self.begin ) )
128
+ end
129
+
130
+ # inverse function of .sample
131
+ # (1.0..2.0).abscissa( 0.3 ) => 1.3
132
+ def abscissa( value )
133
+ return (value - self.begin) / (self.end - self.begin)
134
+ end
135
+
136
+ # return a new range with boundaries translated of "value"
137
+ # (1.0..2.0).translate( -1.0 ) => (0.0..1.0)
138
+ def translate( value )
139
+ return (self.begin + value..self.end + value)
140
+ end
141
+
142
+ end
143
+
144
+ #
145
+ # Array extension to synchronize enumerations, and also provide other recurrent services
146
+ # See this[http://xrvg.rubyforge.org/RubyXRVGExtension.html] for presentation
147
+ #
148
+ class Array
149
+
150
+ # take only the nieme elements
151
+ #
152
+ # Experimental
153
+ # [1, 2, 3, 4].sub(2) => [1, 3]
154
+ def sub(period, &block)
155
+ result = []
156
+ self.foreach(period) do |slice|
157
+ item = slice[0]
158
+ if block
159
+ yield item
160
+ else
161
+ result.push( item )
162
+ end
163
+ end
164
+ return result
165
+ end
166
+
167
+ # return the sum of the elements of the Array
168
+ # works for array whose content defines the + operator
169
+ # [1.0, 2.0].sum => 3.0
170
+ # [Vector[-1.0,-1.0], Vector[1.0,1.0]].sum => Vector[0.0,0.0]
171
+ # [curve1, curve2].sum => concatenation of curves
172
+ def sum
173
+ sum = self[0]
174
+ self[1..-1].each {|v| sum += v}
175
+ return sum
176
+ end
177
+
178
+ # returns the mean of the array content
179
+ # [Vector[0.0,0.0], Vector[1.0,1.0]].mean => Vector[0.5,0.5]
180
+ def mean
181
+ return self.sum / self.size
182
+ end
183
+
184
+ # compute range of an array
185
+ # - if proc nil, returns (min..max)
186
+ # - else, first compute new array with proc, then (min..max) on this array
187
+ # [1.0, 3.0, 2.0].range => (1.0..3.0)
188
+ def range( proc=nil )
189
+ if not proc
190
+ return (self.min..self.max)
191
+ else
192
+ arraytmp = self.map {|item| item.send( proc )}
193
+ return arraytmp.range
194
+ end
195
+ end
196
+
197
+ # alias for sub(2)
198
+ # [1,2,3,4].half => [1,3]
199
+ def half( &block )
200
+ return sub(2,&block)
201
+ end
202
+
203
+ # flatten an array of arrays
204
+ # [[1,1], [2,2]].flattenonce => [1,1,2,2]
205
+ def flattenonce
206
+ result = []
207
+ self.each do |subarray|
208
+ result += subarray
209
+ end
210
+ return result
211
+ end
212
+
213
+ # same as Enumerator.each_slice with implicit size given by block.arity, or explicit if no blocks
214
+ # (in that case, return array of array)
215
+ # same enumeration model as for Tcl foreach command (see Array.zip method for further compatibility)
216
+ # [1,2,3,4].foreach {|v1,v2| puts "#{v1} #{v2}"} => "1 2" "3 4"
217
+ def foreach( arity=nil, &block )
218
+ if not arity
219
+ arity = block.arity
220
+ end
221
+ if block
222
+ if arity == 1
223
+ return self.each( &block )
224
+ else
225
+ return self.each_slice(arity, &block)
226
+ end
227
+ else
228
+ return self.enum_slice(arity).to_a
229
+ end
230
+ end
231
+
232
+ # same as Enumerator.each_cons with implicit size given by block.arity, or explicit if no blocks
233
+ # (in that case, return array of array)
234
+ # [1,2,3,4].uplets {|v1,v2| puts "#{v1} #{v2}"} => "1 2" "2 3" "3 4"
235
+ def uplets(arity=nil, &block )
236
+ if not arity
237
+ arity = block.arity
238
+ end
239
+ if block
240
+ return self.each_cons(arity, &block)
241
+ else
242
+ return self.enum_cons(arity).to_a
243
+ end
244
+ end
245
+
246
+ # alias for uplets(2, &block)
247
+ # [1,2,3,4].pairs {|v| puts "#{v[0]} #{v[1]}"} => "1 2" "2 3" "3 4"
248
+ def pairs( &block )
249
+ return self.uplets(2, &block)
250
+ end
251
+
252
+ # alias for uplets(3, &block)
253
+ # [1,2,3,4].pairs {|v| puts "#{v[0]} #{v[1]} #{v[2]}"} => "1 2 3" "2 3 4"
254
+ def triplets( &block )
255
+ return self.uplets(3, &block)
256
+ end
257
+
258
+ # aarity = array of arity
259
+ # if nil, default value is Array.new( self.size, 1 )
260
+ # size of aarity must be inferior or equal to self.size. If inferior, is completed with 1
261
+ # Rke : with array size 1, is equivalent to foreach
262
+ # [ [1,2,3,4], [a,b] ].forzip( [2,1] ) => [1,2,a,3,4,b]
263
+ # [ [1,2,3,4], [a,b] ].forzip => [[1,a], [2,b], [3,nil], [4,nil]]
264
+ # [ [a,b], [1,2,3,4] ].forzip => [[a,1], [b,2], [nil,3], [nil,4]]
265
+ def forzip(aarity=nil, &block)
266
+ if not aarity
267
+ aarity = Array.new( self.size, 1 )
268
+ end
269
+ if aarity.size < self.size
270
+ aarity = aarity.concat( Array.new( self.size - aarity.size, 1 ) )
271
+ end
272
+ tozip = Array.new
273
+ self.zip( aarity ) do |subarray, arity|
274
+ tozip.push( subarray.foreach(arity) )
275
+ end
276
+ result = tozip[0].zip( *tozip[1..-1] )
277
+ result = result.flattenonce.flattenonce
278
+ if block
279
+ return result.foreach( nil, &block )
280
+ end
281
+ return result
282
+ end
283
+
284
+ # in same idea as forzip, but with explicit array index
285
+ # if pattern is nil, is equivalent to [0,1,..., self.size-1]
286
+ # [ [1,2,3,4], [a,b] ].forpattern( [0,0,1] ) => [1,2,a,3,4,b]
287
+ # [ [1,2,3,4], [a,b] ].forpattern( [0,1,0] ) => [1,a,2,3,b,4]
288
+ #
289
+ # Rke : an interesting application is to use this method to filter some item periodically
290
+ # for example [[array]].forpattern( [0,0] ) {|i,j| result.push( i )} take only first item on a pair (to be tested)
291
+ # Rke2 : not so usefull for the moment (since compared with forzip, the only added value is to allow permutations of values between different subarrays)
292
+ def forpattern(pattern, &block)
293
+ cindexes = Array.new( self.size, 0 )
294
+ result = []
295
+ while true
296
+ newitem = []
297
+ pattern.each do |arrayindex|
298
+ newitem.push( self.[]( arrayindex )[ cindexes[ arrayindex] ] )
299
+ cindexes[ arrayindex] += 1
300
+ end
301
+ if newitem.compact.size == 0
302
+ break
303
+ end
304
+ result += newitem
305
+ end
306
+ # result = result.flatten
307
+ if block
308
+ return result.foreach( nil, &block )
309
+ end
310
+ return result
311
+ end
312
+
313
+ # -------------------------------------------------------------
314
+ # Samplable interface include and overriding
315
+ # -------------------------------------------------------------
316
+ include Samplable
317
+ include Splittable
318
+
319
+ # FloatFunctor overloading to synchronize content sampling and splitting
320
+ def compute( inputs, type )
321
+ return self.map {|v| v.compute( inputs, type )}.forzip
322
+ end
323
+
324
+ end
325
+
326
+ # -------------------------------------------------------------
327
+ # String class
328
+ # -------------------------------------------------------------
329
+
330
+ class String #:nodoc:
331
+
332
+ def subreplace (tokens)
333
+ gsub(/#{tokens.keys.join("|")}/) { tokens[$&] }
334
+ end
335
+
336
+ end
337
+
338
+ class Float #:nodoc:
339
+
340
+ def complement( max = 1.0, min = 0.0 )
341
+ return ( max - ( self.to_f - min ) )
342
+ end
343
+
344
+ def randsplit(minsize=0.0)
345
+ v = self.to_f
346
+ rand = minsize + Kernel::rand * (1.0 - minsize )
347
+ min = v * rand
348
+ max = v - min
349
+ return [min, max]
350
+ end
351
+
352
+ end
353
+
354
+
355
+ class Integer #:nodoc:
356
+
357
+
358
+ def randsplit!(minsize=0.0)
359
+ size = 1.0
360
+ nsplit = self.to_i
361
+ rsize = size - nsplit * minsize
362
+ if rsize < 0.0
363
+ return []
364
+ end
365
+
366
+ minsizes = Array.new( nsplit, minsize )
367
+ # puts "minsizes #{minsizes.join(" ")}"
368
+
369
+ randarray = [0.0]
370
+ subarray = (0.0..rsize).rand( nsplit - 1 )
371
+ Trace("subarray #{subarray.inspect}")
372
+ randarray += subarray
373
+ randarray.push( rsize )
374
+ randarray.sort!
375
+
376
+ # puts "randarray #{randarray.join(" ")}"
377
+
378
+ rsizes = Array.new
379
+ randarray.each_cons(2) { |min, max| rsizes.push( max - min ) }
380
+
381
+ # puts "rsizes #{rsizes.join(" ")}"
382
+
383
+ result = Array.new
384
+ minsizes.zip( rsizes ) {|minsize, rsize| result.push( minsize + rsize )}
385
+
386
+ # puts "result randsplit! #{result.join(" ")}"
387
+
388
+ return result
389
+ end
390
+
391
+ def randsplitsum!(minsize=0.0)
392
+ preresult = self.randsplit!(minsize)
393
+
394
+ result = Array.new
395
+ sum = 0
396
+ preresult.each {|v| sum += v; result.push( sum ) }
397
+
398
+ # puts "result randsplitsum! #{result.join(" ")}"
399
+ return result
400
+ end
401
+
402
+
403
+ end
404
+
data/lib/xrvg.rb ADDED
@@ -0,0 +1,37 @@
1
+ # This file is to be included if you want to start a XRVG script
2
+ #
3
+ # Please refer to README for XRVG introduction
4
+
5
+ # XRVG version (used in rakefile)
6
+ XRVG_VERSION = "0.0.1"
7
+
8
+ # Standard Ruby extensions
9
+ require 'enumerator'
10
+
11
+ # XRVG Infrastructure
12
+ require 'trace'
13
+ require 'assertion'
14
+
15
+ # XRVG new mixins
16
+ require 'samplation'
17
+ require 'attributable'
18
+ require 'interpolation'
19
+
20
+ # XRVG base class extensions
21
+ require 'utils'
22
+ require 'geometry2D'
23
+
24
+ # XRVG base classes
25
+ require 'color'
26
+ require 'frame'
27
+ require 'shape'
28
+ require 'render'
29
+ # require 'bezier'
30
+ # require 'bezierspline'
31
+
32
+ # XRVG extensions
33
+ # require 'bezierbuilders'
34
+ # require 'beziertools'
35
+ # require 'interbezier'
36
+ # require 'ondulation'
37
+
@@ -0,0 +1,24 @@
1
+ require 'test/unit'
2
+ require 'attributable'
3
+ require 'utils'
4
+
5
+ class AttrClass
6
+ include Attributable
7
+
8
+ attribute :a, 1.0
9
+ attribute :b, nil, Float
10
+ attribute :c
11
+ end
12
+
13
+
14
+ # Test class
15
+ class AttributableTest < Test::Unit::TestCase
16
+
17
+ def test_nominal
18
+ obj = AttrClass[ :b, 2.0, :c, 3.0 ]
19
+ assert_equal( 1.0, obj.a)
20
+ assert_equal( 2.0, obj.b)
21
+ assert_equal( 3.0, obj.c)
22
+ end
23
+
24
+ end
@@ -0,0 +1,79 @@
1
+ require 'test/unit'
2
+ require 'color'
3
+
4
+
5
+ class ColorTest < Test::Unit::TestCase
6
+
7
+ def test_color
8
+ color = Color[0.1, 0.2, 0.3, 0.4]
9
+ assert_equal( 0.1, color.r )
10
+ assert_equal( 0.2, color.g )
11
+ assert_equal( 0.3, color.b )
12
+ assert_equal( 0.4, color.a )
13
+ end
14
+
15
+ def test_colors
16
+ black = Color[0.0, 0.0, 0.0, 1.0]
17
+ white = Color[1.0, 1.0, 1.0, 1.0]
18
+ assert_equal( black, Color.black )
19
+ assert_equal( white, Color.white )
20
+ end
21
+
22
+ def test_svg
23
+ assert_equal( "rgb(0,0,0)", Color.black.svg )
24
+ end
25
+
26
+ def test_hsv
27
+ black = Color[0.0, 0.0, 0.0, 1.0]
28
+ white = Color[1.0, 1.0, 1.0, 1.0]
29
+ red = Color[1.0, 0.0, 0.0, 1.0]
30
+ assert_equal( black, Color.hsv( 0.0, 0.0, 0.0, 1.0 ) )
31
+ assert_equal( white, Color.hsv( 0.0, 0.0, 1.0, 1.0 ) )
32
+ assert_equal( red, Color.hsv( 0.0, 1.0, 1.0, 1.0 ) )
33
+ end
34
+
35
+ end
36
+
37
+
38
+ class PaletteTest < Test::Unit::TestCase
39
+
40
+ def test_palette
41
+ palette = Palette.new( :colorlist, [ Color.black, 0.0, Color.white, 1.0 ] )
42
+ assert_equal( Color[0.5, 0.5, 0.5, 1.0], palette.color( 0.5 ) )
43
+ end
44
+ end
45
+
46
+ class GradientTest < Test::Unit::TestCase
47
+
48
+ def test_gradient
49
+ gradient = LinearGradient.new( :colorlist, [Color.black, 0.0, Color.white, 1.0] )
50
+ assert_equal( gradient.svgdef,
51
+ "<linearGradient id=\"%ID%\" x1=\"0%\" y1=\"0%\" x2=\"0%\" y2=\"100%\">\n<stop offset=\"0.0\" stop-color=\"rgb(0,0,0)\" stop-opacity=\"1.0\"/>\n<stop offset=\"1.0\" stop-color=\"rgb(255,255,255)\" stop-opacity=\"1.0\"/>\n</linearGradient>")
52
+
53
+ end
54
+
55
+ if nil
56
+ def test_gradient1
57
+ require 'render'
58
+ require 'shape'
59
+ render = SVGRender.new( :filename, "gradient1.svg" )
60
+ render.add( Circle.new, Style.new( :stroke, "none", :fill, LinearGradient.new( :colorlist, [Color.black, 0.0, Color.white, 1.0] ) ) )
61
+ render.end
62
+ assert( File.exist?( "gradient1.png" ) )
63
+ end
64
+ end
65
+
66
+ if nil
67
+ def test_gradient2
68
+ require 'render'
69
+ require 'shape'
70
+ render = SVGRender.new( :filename, "gradient2.svg" )
71
+ circle = Circle.new
72
+ style = Style.new( :stroke, "none", :fill, CircularGradient.new( :colorlist, [Color.black(1.0), 0.0, Color.black(0.0), 1.0], :circle, circle ) )
73
+ render.add( circle, style )
74
+ render.end
75
+ assert( File.exist?( "gradient2.png" ) )
76
+ end
77
+ end
78
+
79
+ end
@@ -0,0 +1,12 @@
1
+ require 'test/unit'
2
+ require 'frame'
3
+ require 'geometry2D'
4
+
5
+ class FrameTest < Test::Unit::TestCase
6
+
7
+ def test_frame1
8
+ frame = Frame[ :center, Vector[0.0,0.0], :vector, Vector[1.0,0.0], :rotation, 0.0, :scale, 1.0 ]
9
+ assert_equal( 0.0, frame.center[0] )
10
+ end
11
+
12
+ end
@@ -0,0 +1,76 @@
1
+ require 'test/unit'
2
+ require 'geometry2D.rb'
3
+
4
+ # Test class
5
+ class VectorTest < Test::Unit::TestCase
6
+
7
+ def test_indice
8
+ s = Vector[0,1]
9
+ assert_equal(1, s[1])
10
+ assert_equal(0, s[0])
11
+ end
12
+
13
+ def test_add
14
+ s = Vector[0,1]
15
+ t = Vector[2,3]
16
+ assert_equal(Vector[2,4], s + t)
17
+ end
18
+
19
+ def test_norm
20
+ assert_equal(Vector[0,1], Vector[0,2].norm)
21
+ end
22
+
23
+ def test_angle
24
+ assert_equal(0.0, Vector[0,0].angle)
25
+ assert_equal(0.0, Vector[1,0].angle)
26
+ assert_equal(Math::PI * 0.5, Vector[0,1].angle)
27
+ assert_equal(-Math::PI * 0.5, Vector[0,-1].angle)
28
+ end
29
+
30
+ def test_mean
31
+ assert_equal(Vector[0,1], (Vector[0,0]..Vector[0,2]).middle)
32
+ end
33
+
34
+ def test_length
35
+ assert_equal(1,Vector[0,1].length)
36
+ end
37
+
38
+ def test_interpol
39
+ assert_equal(Vector[0.2,1.4], (Vector[0.0, 1.0]..Vector[1.0, 3.0]).sample( 0.2 ))
40
+ end
41
+
42
+ def test_createwithpoints
43
+
44
+ assert_equal(Vector[1,1], Point[1,1] - Point[0,0])
45
+
46
+ end
47
+
48
+ def test_ortho
49
+ assert_equal(Vector[-0.6,0.5],
50
+ Vector[0.5,0.6].ortho)
51
+ end
52
+
53
+ def test_reverse
54
+ assert_equal(Vector[-0.6,-0.5],
55
+ Vector[0.6,0.5].reverse)
56
+ end
57
+
58
+ def test_viewbox
59
+ assert_equal( [1.0, 1.0, 2.0, 2.0],
60
+ Point.viewbox( [Point[1.0, 2.0], Point[2.0, 1.0]] ))
61
+ end
62
+
63
+ def test_size
64
+ assert_equal( [1.0, 1.0],
65
+ Point.size( [Point[1.0, 2.0], Point[2.0, 1.0]] ))
66
+ end
67
+
68
+ def test_operation_sequences
69
+ assert_equal(Vector[0.0,1.0],
70
+ Vector[1.0,0.0].norm.ortho)
71
+ end
72
+
73
+ def test_inner_product
74
+ assert_equal( 1.0, Vector[1.0, 0.0].inner_product( Vector[1.0,1.0] ) )
75
+ end
76
+ end
@@ -0,0 +1,18 @@
1
+ require 'test/unit'
2
+ require 'render'
3
+
4
+ class SVGRenderTest < Test::Unit::TestCase
5
+
6
+
7
+
8
+ def test_raster
9
+ if nil
10
+ require 'shape'
11
+ render = SVGRender.new( :filename, "output/svgrender.svg" )
12
+ render.add( Circle[] )
13
+ render.end
14
+ assert( File.exist?( "svgrender.jpg" ) )
15
+ end
16
+ end
17
+
18
+ end
@@ -0,0 +1,16 @@
1
+ require 'test/unit'
2
+ require 'style'
3
+
4
+ class StyleTest < Test::Unit::TestCase
5
+
6
+ def test_svgline
7
+ style = Style.new(:fill, "red", :stroke, "none")
8
+ assert_equal( 'style="opacity:1.0;fill:red;fill-opacity:1.0;stroke:none;stroke-width:1.0;stroke-opacity:1.0"', style.svgline )
9
+ end
10
+
11
+ def test_default
12
+ style = Style.new
13
+ assert_equal( 'style="opacity:1.0;fill:none;fill-opacity:1.0;stroke:none;stroke-width:1.0;stroke-opacity:1.0"', style.svgline )
14
+ end
15
+
16
+ end