xrvg 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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