xrvg 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,5 +1,17 @@
1
1
  = CHANGES
2
- == => v 0.0.2
2
+
3
+ == 0.0.3 (2008.02.17)
4
+ === Content
5
+ - just bug correction, optims and refactoring
6
+ - quad tree implementation
7
+ === Testing
8
+ - test_interpolator
9
+ - some minors other additions
10
+ ==== Refactoring
11
+ - implementing bezier length interpolator as quad tree => +60% perf sur sample( 50000 ) !!!
12
+ - change order of list in interpolation ! now index before value (as nominal way of describing key,value pairs)
13
+
14
+ == 0.0.2 (2008.02.10)
3
15
  === Content
4
16
  ==== New features
5
17
  - bezier curve !!
@@ -14,13 +26,13 @@
14
26
  - range examples correction, and added to unitary tests
15
27
  - Range .complement correction
16
28
  - Range split correction
17
- === TODO
29
+ === TODO for 0.0.3
18
30
  - unitary tests to be completed (generate them from doc ?)
19
31
  - Shape Line and Circle interfaces must be completed to fill Curve abstract interface.
20
32
  - mabe bezier tutorial
21
33
 
22
34
 
23
- == => 0.0.1
35
+ == 0.0.1 (2008.02.02)
24
36
  === Content
25
37
  - First delivery. Contains utilitary classes (utils, samplable, interpolation, attributable), render, color, style, shape.
26
38
  - But no bezier yet.
data/README CHANGED
@@ -1,6 +1,6 @@
1
1
  = XRVG -- X Ruby Vector Graphics
2
2
 
3
- Supporting XRVG version: 0.0.2
3
+ Supporting XRVG version: 0.0.3
4
4
 
5
5
  This package contains XRVG, a Ruby vector graphic programming library.
6
6
 
data/Rakefile CHANGED
@@ -72,7 +72,7 @@ EXAMPLE_FILES = FileList["#{EXAMPLE_DIR}/*.rb"]
72
72
 
73
73
  # Filelist with Test::Unit test cases.
74
74
  TEST_DIR = "test"
75
- PRE_TEST_FILES = FileList["test_bezier.rb", "test_attributable.rb", "test_color.rb", "test_frame.rb", "test_geometry2D.rb", "test_render.rb", "test_style.rb", "test_utils.rb", "test_shape.rb"]
75
+ PRE_TEST_FILES = FileList["test_bezier.rb", "test_attributable.rb", "test_color.rb", "test_frame.rb", "test_geometry2D.rb", "test_render.rb", "test_style.rb", "test_utils.rb", "test_shape.rb", "test_interpolation.rb"]
76
76
  TEST_FILES = FileList["test/*.rb"]
77
77
 
78
78
  # Executable scripts, all non-garbage files under bin/.
@@ -2,7 +2,7 @@ require 'xrvg'
2
2
 
3
3
  render = SVGRender[ :filename, "palette_circle.svg", :background, "white" ]
4
4
 
5
- palette = Palette[ :colorlist, [ Color.black, 0.0, Color.blue, 1.0 ] ]
5
+ palette = Palette[ :colorlist, [ 0.0, Color.black, 1.0 , Color.blue] ]
6
6
  [Circle[], palette, (0.1..0.02).random()].samples(25) do |point, color, radius|
7
7
  render.add( Circle[ :center, point, :radius, radius ], Style[ :fill, color ])
8
8
  end
data/examples/sample.rb CHANGED
@@ -2,9 +2,9 @@ require 'xrvg'
2
2
 
3
3
  render = SVGRender[ :filename, "sample.svg" ]
4
4
 
5
- palette = Palette[ :colorlist, [ Color.blue, 0.0, Color.orange, 0.25,
6
- Color.yellow, 0.5, Color.green, 0.75,
7
- Color.blue, 1.0 ] ]
5
+ palette = Palette[ :colorlist, [ 0.0, Color.blue, 0.25, Color.orange,
6
+ 0.5, Color.yellow, 0.75, Color.green,
7
+ 1.0, Color.blue] ]
8
8
  [Circle[], palette, (0.1..0.02).random()].samples(25) do |point, color, radius|
9
9
  render.add( Circle[ :center, point, :radius, radius ], Style[ :fill, color ])
10
10
  end
data/lib/bezier.rb CHANGED
@@ -49,22 +49,64 @@ class Bezier < Curve
49
49
  self.new( *args )
50
50
  end
51
51
 
52
+ # Uni Bezier builder
53
+ #
54
+ # type can be :raw or :vector
52
55
  def Bezier.single( type, p1, p2, p3, p4 )
53
56
  return Bezier.new( :pieces, [BezierSpline[type, p1, p2, p3, p4]] )
54
57
  end
55
58
 
59
+ # Uni Bezier builder in :raw format
56
60
  def Bezier.raw( p1, pc1, pc2, p2 )
57
61
  return Bezier.single( :raw, p1, pc1, pc2, p2 )
58
62
  end
59
63
 
64
+ # Uni Bezier builder in :vector format
60
65
  def Bezier.vector( p1, v1, p2, v2 )
61
66
  return Bezier.single( :vector, p1, v1, p2, v2 )
62
67
  end
63
68
 
69
+ # Basic Multi Bezier builder
70
+ #
71
+ # raw pieces must be an array of arrays of the form [type, p1, p2, p3, p4] as defined for single
64
72
  def Bezier.multi( rawpieces )
65
73
  return Bezier.new( :pieces, rawpieces.map {|piece| BezierSpline[*piece]} )
66
74
  end
67
75
 
76
+ # "regular" Multi Bezier :raw specification
77
+ #
78
+ # args is a list of points and control points as [p1, pc1, p2, pc2, p3, pc3]
79
+ #
80
+ # Beware that
81
+ # Bezier.raw( p1, pc1, pc2, p2 ) == Bezier.raws( p1, pc1, p2, p2 + (p2-pc2))
82
+ def Bezier.raws( *args )
83
+ rawpieces = []
84
+ args.foreach(2).pairs do |pair1, pair2|
85
+ p1, pc1 = pair1
86
+ p2, pc2 = pair2
87
+ rawpieces << [:raw, p1, pc1, p2+(p2-pc2), p2]
88
+ end
89
+ return Bezier.multi( rawpieces )
90
+ end
91
+
92
+ # "regular" Multi Bezier :vector specification
93
+ #
94
+ # args is a list of points and control points as [p1, v1, p2, v2, p3, v3]
95
+ #
96
+ # Beware that
97
+ # Bezier.vector( p1, v1, p2, v2 ) == Bezier.raws( p1, v1, p2, -v2)
98
+ def Bezier.vectors( *args )
99
+ rawpieces = []
100
+ args.foreach(2).pairs do |pair1, pair2|
101
+ p1, v1 = pair1
102
+ p2, v2 = pair2
103
+ rawpieces << [:vector, p1, v1, p2, -v2]
104
+ end
105
+ return Bezier.multi( rawpieces )
106
+ end
107
+
108
+
109
+
68
110
 
69
111
  # bezier point, as
70
112
  # Bezier[:raw, V2D::O, V2D::O, V2D::O, V2D::O]
@@ -117,6 +159,7 @@ class Bezier < Curve
117
159
  def pointlist( type=:raw )
118
160
  result = []
119
161
  @pieces.each {|piece| result = result + piece.pointlist(type)}
162
+ # Trace("Bezier.pointlist result #{result.inspect}")
120
163
  return result
121
164
  end
122
165
 
@@ -135,6 +178,12 @@ class Bezier < Curve
135
178
  return self.pieces.map{ |piece| Bezier.single( *piece.data ) }
136
179
  end
137
180
 
181
+ # shortcut method to retrieve a piece as an Bezier object
182
+ def bezier( index )
183
+ return Bezier.single( *self.piece( index ).data )
184
+ end
185
+
186
+
138
187
  # -------------------------------------------------------------
139
188
  # piece delegation computation
140
189
  # -------------------------------------------------------------
@@ -142,7 +191,7 @@ class Bezier < Curve
142
191
  # with index (must be Float) and parametertype as inputs, must compute :
143
192
  # - the index of the piece on which the computation must take place
144
193
  # - the new parameter value corresponding to bezier computation input
145
- def parametermapping( index, parametertype=:length ) #:nodoc:
194
+ def parametermapping( index, parametertype=:length, side=:right ) #:nodoc:
146
195
  check_parametertype( parametertype )
147
196
  result = []
148
197
  if parametertype == :length
@@ -151,7 +200,7 @@ class Bezier < Curve
151
200
  elsif index > 1.0
152
201
  index = 1.0
153
202
  end
154
- result = length_parameter_mapping( index )
203
+ result = length_parameter_mapping( index, side )
155
204
  else # no need to test parametertype value, as check_parametertype already do it
156
205
  if index < 0.0
157
206
  index = 0.0
@@ -217,8 +266,10 @@ class Bezier < Curve
217
266
 
218
267
  # generalize Bezier method
219
268
  def subpieces (t1, t2) #:nodoc:
220
- pieceindex1, t1 = parametermapping( t1 )
221
- pieceindex2, t2 = parametermapping( t2 )
269
+ # Trace("subpieces t1 #{t1} t2 #{t2}")
270
+ pieceindex1, t1 = parametermapping( t1, :length, :left )
271
+ pieceindex2, t2 = parametermapping( t2, :length, :right )
272
+ # Trace("after translation pieceindex1 #{pieceindex1} t1 #{t1} pieceindex2 #{pieceindex2} t2 #{t2}")
222
273
  result = []
223
274
 
224
275
  if pieceindex1 == pieceindex2
@@ -228,7 +279,7 @@ class Bezier < Curve
228
279
  if pieceindex1 + 1 != pieceindex2
229
280
  result += self.pieces[pieceindex1+1..pieceindex2-1]
230
281
  end
231
- result << self.piece( pieceindex1 ).subpiece( 0, t2 )
282
+ result << self.piece( pieceindex2 ).subpiece( 0.0, t2 )
232
283
  end
233
284
  return result
234
285
  end
@@ -386,6 +437,7 @@ class Bezier < Curve
386
437
  # must use an interpolator ?
387
438
  def compute_length #:nodoc:
388
439
  lengths = self.pieces.map {|piece| piece.length}
440
+ Trace("pieces #{self.pieces.inspect} lenghts #{lengths.inspect}")
389
441
  result = lengths.sum
390
442
  if result == 0.0
391
443
  lengths = [1.0]
@@ -396,27 +448,50 @@ class Bezier < Curve
396
448
  lmin = 0.0
397
449
  @lengthranges = []
398
450
  lengths.each do |llength|
451
+ Trace("lmin #{lmin} llength #{llength}")
399
452
  @lengthranges << (lmin..llength)
400
453
  lmin = llength
401
454
  end
455
+ @lengthranges[-1] = @lengthranges[-1].begin..1.0
456
+ return result
457
+ end
458
+
459
+ # utilitary method for interbezier
460
+ #
461
+ # return list of piece lengths relatives to total bezier lengths, and cumulated
462
+ # Bezier.new( :pieces, [piece1, piece2] ).piecelengths; => [0.0, piece1.length / bezier.length, 1.0]
463
+ def piecelengths
464
+ result = self.lengthranges.map {|range| range.begin}
465
+ result << 1.0
402
466
  return result
403
467
  end
404
468
 
405
- def length_parameter_mapping( t ) #:nodoc:
469
+ # private method, to compute lengthranges if not existing
470
+ def lengthranges #:nodoc:
406
471
  if not @lengthranges
407
472
  compute_length
408
473
  end
474
+ return @lengthranges
475
+ end
476
+
477
+ def length_parameter_mapping( t, side ) #:nodoc:
409
478
  pieceindex = -1
410
- @lengthranges.each_with_index do |lrange,i|
479
+ Trace("self.lengthranges #{self.lengthranges.inspect}")
480
+ self.lengthranges.each_with_index do |lrange,i|
411
481
  if lrange.include?( t )
412
482
  pieceindex = i
413
483
  t = lrange.abscissa( t )
414
484
  t = self.piece( i ).parameterfromlength( t )
485
+ Trace("pieceindex #{pieceindex} t #{t}")
415
486
  break
416
487
  end
417
488
  end
489
+ if ((side == :left) and t.fequal?(1.0) and (pieceindex < self.piecenumber - 1))
490
+ pieceindex += 1
491
+ t = 0.0
492
+ end
418
493
  if pieceindex == -1
419
- Kernel::raise("length_parameter_mapping error : t #{t} is not in length range #{@lengthranges.inspect}")
494
+ Kernel::raise("length_parameter_mapping error : t #{t} is not in length range #{self.lengthranges.inspect}")
420
495
  end
421
496
  return [pieceindex, t]
422
497
  end
data/lib/bezierspline.rb CHANGED
@@ -1,5 +1,8 @@
1
1
  # +BezierSpline+ source
2
2
 
3
+ require 'interpolation'
4
+
5
+
3
6
  # BezierSpline class
4
7
  #
5
8
  # Internal class to represent a single-piece cubic bezier curve, defined by four points or two point + two vectors.
@@ -18,8 +21,8 @@ class BezierSpline #:nodoc:
18
21
 
19
22
  def checkvalues( v1, v2, v3, v4 )
20
23
  [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" )
24
+ if not (v.respond_to?(:x) || v.respond_to?(:y))
25
+ Kernel::raise( "BezierSpline : init value #{v.inspect} does not respond to :x or :y" )
23
26
  end
24
27
  end
25
28
  end
@@ -204,34 +207,34 @@ class BezierSpline #:nodoc:
204
207
  samplelist = [0.0, 0.0]
205
208
  new = V2D[0.0,0.0]
206
209
  previous = nil
207
- (0.0..1.0).samples( 100 ) do |abs|
210
+ (0.0..1.0).samples( 129 ) do |abs|
208
211
  self.point( abs, new )
209
212
  if previous
210
213
  sum+= (new - previous).r
211
- samplelist += [abs, sum]
214
+ samplelist += [sum, abs]
212
215
  else
213
216
  previous = V2D[0.0,0.0]
214
217
  end
215
218
  previous.x = new.x
216
219
  previous.y = new.y
217
220
  end
218
- @length = samplelist[-1]
221
+ @length = samplelist[-2]
219
222
 
220
223
  length_interpolator = nil
221
224
  if @length == 0.0
222
- newsamplelist = [0.0,0.0,1.0,0.0]
223
- invsamplelist = [0.0,0.0,0.0,1.0]
225
+ newsamplelist = [0.0,0.0,0.0,1.0]
226
+ invsamplelist = [0.0,0.0,1.0,0.0]
224
227
  else
225
228
  newsamplelist = []
226
229
  invsamplelist = []
227
- samplelist.foreach do |abs, sum|
228
- newsamplelist += [abs, sum / @length]
229
- invsamplelist += [sum / @length, abs]
230
+ samplelist.foreach do |sum, abs|
231
+ newsamplelist += [sum / @length, abs ]
232
+ invsamplelist += [abs, sum / @length ]
230
233
  end
231
234
  samplelist = newsamplelist
232
235
  end
233
- @abs_interpolator = Interpolator.new( :samplelist, invsamplelist )
234
- return Interpolator.new( :samplelist, samplelist )
236
+ @abs_interpolator = InterpolatorQuad.new( :samplelist, invsamplelist )
237
+ return InterpolatorQuad.new( :samplelist, samplelist )
235
238
  end
236
239
 
237
240
  def length_interpolator() #:nodoc:
@@ -257,10 +260,9 @@ class BezierSpline #:nodoc:
257
260
  return @length
258
261
  end
259
262
 
260
- def parameterfromlength( l ) #:nodoc:
261
- result = self.length_interpolator.interpolate( l )
263
+ def parameterfromlength( lvalue ) #:nodoc:
264
+ result = self.length_interpolator.interpolate( lvalue )
262
265
  return result
263
- end
264
-
265
-
266
+ end
266
267
  end
268
+
data/lib/color.rb CHANGED
@@ -251,7 +251,7 @@ end
251
251
  # Palette defines color palettes, as interpolation between color points. As such, use Interpolation module, so uses for the moment only linear interpolation.
252
252
  # But once built with interpolation, palette provides a continuous color "interval", and so is Samplable !
253
253
  # = Use
254
- # palette = Palette[ :colorlist, [ Color.blue, 0.0, Color.orange, 0.5, Color.yellow, 1.0 ] ]
254
+ # palette = Palette[ :colorlist, [ 0.0, Color.blue, 0.5, Color.orange, 1.0, Color.yellow ] ]
255
255
  # palette.rand( 10 ) # => return 10 random colors in palette
256
256
  # palette.color( 0.5 ) # => Color.orange
257
257
  class Palette
@@ -259,7 +259,7 @@ class Palette
259
259
  attribute :colorlist
260
260
 
261
261
  # compute color given float pourcentage.
262
- # Palette[ :colorlist, [ Color.black, 0.0, Color.white, 1.0 ] ].sample( 0.5 ) => Color[0.5,0.5,0.5,1.O]
262
+ # Palette[ :colorlist, [ 0.0, Color.black, 1.0, Color.white ] ].sample( 0.5 ) => Color[0.5,0.5,0.5,1.O]
263
263
  # "sample" method as defined in Samplable module
264
264
  def color(dindex)
265
265
  result = self.interpolate(dindex)
@@ -269,8 +269,8 @@ class Palette
269
269
  # return a new palette by reversing the current one
270
270
  def reverse()
271
271
  newcolorlist = []
272
- self.colorlist.reverse.foreach do |index,color|
273
- newcolorlist += [color, (0.0..1.0).complement( index )]
272
+ self.colorlist.reverse.foreach do |color, index|
273
+ newcolorlist += [(0.0..1.0).complement( index ), color]
274
274
  end
275
275
  return Palette[ :colorlist, newcolorlist ]
276
276
  end
@@ -304,7 +304,7 @@ class LinearGradient < Gradient #:nodoc:
304
304
  stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
305
305
 
306
306
  stops = "\n"
307
- self.colorlist.foreach do |color, index|
307
+ self.colorlist.foreach do |index, color|
308
308
  stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
309
309
  stops += "\n"
310
310
  end
@@ -321,14 +321,14 @@ class CircularGradient < Gradient #:nodoc:
321
321
  stoptemplate = '<stop offset="%offset%" stop-color="%color%" stop-opacity="%opacity%"/>'
322
322
 
323
323
  stops = "\n"
324
- self.colorlist.foreach do |color, index|
324
+ self.colorlist.foreach do |index, color|
325
325
  stops += stoptemplate.subreplace( {"%offset%" => index, "%color%" => color.svg, "%opacity%" => color.a} )
326
326
  stops += "\n"
327
327
  end
328
328
 
329
329
  return template.subreplace( {"%stops%" => stops,
330
- "%cx%" => circle.center.x,
331
- "%cy%" => circle.center.y,
332
- "%r%" => circle.radius} )
330
+ "%cx%" => circle.center.x,
331
+ "%cy%" => circle.center.y,
332
+ "%r%" => circle.radius} )
333
333
  end
334
334
  end
data/lib/geometry2D.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #
2
- # Contains Ruby geometric Vector 2D class.
2
+ # Contains Ruby geometric V2D 2D class.
3
3
  # See :
4
4
  # - +V2D+
5
5
 
@@ -10,7 +10,7 @@ require 'Attributable'
10
10
  #
11
11
  # V2D class definition, to provide several useful "geometrical" services
12
12
  #
13
- # Extends and somehow redefines Vector class, to speed up computation, as less generic
13
+ # Extends and somehow redefines V2D class, to speed up computation, as less generic
14
14
  #
15
15
  # For example :
16
16
  # V2D[0.0,2.0].norm => V2D[0.0,1.0]
@@ -103,7 +103,7 @@ class V2D
103
103
  return V2D[ self.x.succ, self.y.succ ]
104
104
  end
105
105
 
106
- # compute length of 2D vector (r notation to be compatible with Vector)
106
+ # compute length of 2D vector (r notation to be compatible with V2D)
107
107
  def r()
108
108
  return Math.hypot( self.x, self.y )
109
109
  end
@@ -118,7 +118,7 @@ class V2D
118
118
  if r != 0.0
119
119
  return self / r
120
120
  else
121
- return V::O
121
+ return V2D::O
122
122
  end
123
123
  end
124
124
 
@@ -210,7 +210,7 @@ class V2D
210
210
  end
211
211
 
212
212
  # compute the coord box enclosing every 2D elements considered as points
213
- # Vector.viewbox( [Vector[1.0,2.0], Vector[2.0,1.0]] ) => [1.0, 1.0, 2.0, 2.0]
213
+ # V2D.viewbox( [V2D[1.0,2.0], V2D[2.0,1.0]] ) => [1.0, 1.0, 2.0, 2.0]
214
214
  def V2D.viewbox (pointlist)
215
215
  if pointlist.size == 0
216
216
  return [0.0, 0.0, 0.0, 0.0]
@@ -223,7 +223,7 @@ class V2D
223
223
  end
224
224
 
225
225
  # compute dimension of the box enclosing 2D elemnts considered as points
226
- # Vector.viewbox( [Vector[1.0,2.0], Vector[10.0,1.0]] ) => [9.0, 1.0]
226
+ # V2D.viewbox( [V2D[1.0,2.0], V2D[10.0,1.0]] ) => [9.0, 1.0]
227
227
  # use +viewvox+
228
228
  def V2D.size (pointlist)
229
229
  xmin, ymin, xmax, ymax = V2D.viewbox( pointlist )
data/lib/interpolation.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  # See
4
4
  # - +Interpolation+
5
5
  # - +Interpolator+
6
+ # - +InterpolatorQuad+
6
7
 
7
8
  require 'utils'
8
9
  require 'attributable'
@@ -33,8 +34,8 @@ module Interpolation
33
34
  # interpolate uses + and * scalar operators on interpolation values
34
35
  def interpolate( dindex )
35
36
  result = nil
36
- pvalue, pindex = self.samplelist[0..1]
37
- self.samplelist.foreach do |value, index|
37
+ pindex, pvalue = self.samplelist[0..1]
38
+ self.samplelist.foreach do |index, value|
38
39
  if dindex <= index
39
40
  if dindex == index
40
41
  return value
@@ -45,7 +46,7 @@ module Interpolation
45
46
  pvalue, pindex = value, index
46
47
  end
47
48
  if not result
48
- result = self.samplelist[-2]
49
+ result = self.samplelist[-1]
49
50
  end
50
51
  return result
51
52
  end
@@ -59,3 +60,99 @@ class Interpolator
59
60
  attribute :samplelist
60
61
  include Interpolation
61
62
  end
63
+
64
+ class QuadRange
65
+
66
+ def initialize( limit, quadleft, quadright, range=nil )
67
+ @limit = limit
68
+ @quadleft = quadleft
69
+ @quadright = quadright
70
+ @range = range
71
+ end
72
+
73
+ def limit
74
+ return @limit
75
+ end
76
+
77
+ def range( index )
78
+ if @limit
79
+ if index < @limit
80
+ return @quadleft.range( index )
81
+ else
82
+ return @quadright.range( index )
83
+ end
84
+ else
85
+ return @range
86
+ end
87
+ end
88
+ end
89
+
90
+ # QuadTree class
91
+ # = Intro
92
+ # Optim class to look for predefine ranges for a value. Is actually a binary tree data structure, but used as unlinear space partitioner.
93
+ # = Example
94
+ # quad = QuadTree.new( [0.0,1.0, 0.2,0.0, 0.6,1.0, 0.8,0.0, 1.0,1.0] )
95
+ # quad.range( 0.5 ); #=> [0.2,0.0,0.6,1.0]
96
+ class QuadTree
97
+
98
+ def initialize( samplelist ) #:nodoc:
99
+ quads = []
100
+ ends = []
101
+ samplelist.foreach(2).pairs do |ppair, pair|
102
+ pindex, pvalue = ppair
103
+ index, value = pair
104
+ quads << QuadRange.new( nil, nil, nil, [pindex, pvalue, index, value] )
105
+ ends << index
106
+ end
107
+ @root = build_quads( quads, ends )
108
+ end
109
+
110
+ def build_quads( quads, ends ) #:nodoc:
111
+ newquads = []
112
+ newends = []
113
+ index = 0
114
+ quads.foreach do |quad1, quad2|
115
+ newquads << QuadRange.new( ends[2*index], quad1, quad2, nil)
116
+ newends << ends[2*index + 1]
117
+ index += 1
118
+ end
119
+ if newquads.size == 1
120
+ return newquads[0]
121
+ else
122
+ return build_quads( newquads, newends )
123
+ end
124
+ end
125
+
126
+ # utilitary method to retrieve range of index
127
+ # QuadTree.new( [0.0,1.0, 0.2,0.0, 0.6,1.0, 0.8,0.0, 1.0,1.0] ).range( 0.5 ); #=> [0.2,0.0,0.6,1.0]
128
+ def range( index )
129
+ return @root.range( index )
130
+ end
131
+ end
132
+
133
+ # Quad tree interpolator
134
+ #
135
+ # Use QuadTree to retrieve range of values between linear interpolation.
136
+ #
137
+ # Used in BezierSpline to compute parameter from length.
138
+ class InterpolatorQuad < Interpolator
139
+
140
+ def compute_quad #:nodoc:
141
+ @quad = QuadTree.new( self.samplelist )
142
+ end
143
+
144
+ # - first use QuadTree range to retrieve range of dindex,
145
+ # - then linearly interpolate
146
+ def interpolate( dindex )
147
+ if not @quad
148
+ compute_quad
149
+ end
150
+ pindex, pvalue, index, value = @quad.range( dindex )
151
+ if pindex.fequal?(index)
152
+ result = value
153
+ else
154
+ result = pvalue + ((value + pvalue * (-1.0) ) * ((dindex - pindex) / (index - pindex )))
155
+ end
156
+ return result
157
+ end
158
+ end
data/lib/render.rb CHANGED
@@ -65,6 +65,9 @@ class SVGRender < Render
65
65
 
66
66
  def layers=( backtofront ) #:nodoc:
67
67
  @sortlayers = backtofront
68
+ @sortlayers.each do |key|
69
+ @layers[ key ] = ""
70
+ end
68
71
  end
69
72
 
70
73
  def add_content (string, layer) #:nodoc:
@@ -181,7 +184,7 @@ class SVGRender < Render
181
184
  xmin, ymin, width, height = get_carre_viewbox( get_final_viewbox() )
182
185
  template = '<rect x="%x%" y="%y%" width="%width%" height="%height%" fill="%fill%"/>'
183
186
  bg = self.background
184
- if bg.is_a? Color
187
+ if bg.respond_to? :svg
185
188
  bg = bg.svg
186
189
  end
187
190
  return template.subreplace( {"%x%" => xmin,
data/lib/shape.rb CHANGED
@@ -318,11 +318,20 @@ end
318
318
  # = Attributes
319
319
  # attribute :center, V2D[0.0,0.0]
320
320
  # attribute :radius, 1.0
321
+ # attribute :initangle, 0.0
321
322
  # = Example
322
323
  # c = Circle[ :center, V2D::O, :radius, 1.0 ] # equiv Circle[]
323
324
  class Circle < Curve
324
325
  attribute :center, V2D[0.0,0.0]
325
326
  attribute :radius, 1.0
327
+ attribute :initangle, 0.0
328
+
329
+ # Circle builder from points diametraly opposed
330
+ # Circle.diameter( V2D[-1.0,0.0], V2D[1.0,0.0] ) == Circle[ :center, V2D::O, :radius, 1.0 ]
331
+ def Circle.diameter( p1, p2 )
332
+ initangle = ( p1 - p2 ).angle
333
+ return Circle[ :center, (p1 + p2)/2.0, :radius, (p1 - p2).r/2.0, :initangle, initangle ]
334
+ end
326
335
 
327
336
  # compute length of the circle
328
337
  def length
@@ -354,6 +363,10 @@ class Circle < Curve
354
363
  def size
355
364
  return [ self.radius, self.radius ]
356
365
  end
366
+
367
+ def rotate( angle )
368
+ @initangle += angle
369
+ end
357
370
 
358
371
  # svg description of the circle
359
372
  def svg
@@ -365,7 +378,7 @@ class Circle < Curve
365
378
 
366
379
  # compute point at abscissa
367
380
  def point (abscissa, container=nil)
368
- angle = Range::Angle.sample( abscissa )
381
+ angle = Range::Angle.sample( abscissa ) + @initangle
369
382
  container ||=V2D[]
370
383
  container.x = self.cx + self.radius * Math.cos( angle )
371
384
  container.y = self.cy + self.radius * Math.sin( angle )
data/lib/utils.rb CHANGED
@@ -168,7 +168,7 @@ class Array
168
168
  # return the sum of the elements of the Array
169
169
  # works for array whose content defines the + operator
170
170
  # [1.0, 2.0].sum => 3.0
171
- # [Vector[-1.0,-1.0], Vector[1.0,1.0]].sum => Vector[0.0,0.0]
171
+ # [V2D[-1.0,-1.0], V2D[1.0,1.0]].sum => V2D[0.0,0.0]
172
172
  # [curve1, curve2].sum => concatenation of curves
173
173
  def sum
174
174
  sum = self[0]
@@ -177,7 +177,7 @@ class Array
177
177
  end
178
178
 
179
179
  # returns the mean of the array content
180
- # [Vector[0.0,0.0], Vector[1.0,1.0]].mean => Vector[0.5,0.5]
180
+ # [V2D[0.0,0.0], V2D[1.0,1.0]].mean => V2D[0.5,0.5]
181
181
  def mean
182
182
  return self.sum / self.size
183
183
  end
@@ -342,6 +342,10 @@ class Float #:nodoc:
342
342
  return ( max - ( self.to_f - min ) )
343
343
  end
344
344
 
345
+ def fequal?( other )
346
+ return ((self - other).abs < 0.0000000001)
347
+ end
348
+
345
349
  def randsplit(minsize=0.0)
346
350
  v = self.to_f
347
351
  rand = minsize + Kernel::rand * (1.0 - minsize )
data/lib/xrvg.rb CHANGED
@@ -3,7 +3,7 @@
3
3
  # Please refer to README for XRVG introduction
4
4
 
5
5
  # XRVG version (used in rakefile)
6
- XRVG_VERSION = "0.0.2"
6
+ XRVG_VERSION = "0.0.3"
7
7
 
8
8
  # Standard Ruby extensions
9
9
  require 'enumerator'
data/test/test_bezier.rb CHANGED
@@ -30,8 +30,19 @@ class BezierTest < Test::Unit::TestCase
30
30
  assert_equal( V2D[0.0, 1.0], b.firstpoint )
31
31
  end
32
32
 
33
+ def test_builder2
34
+ b = Bezier.raws( V2D[0.0, 1.0], V2D[1.0, 1.0], V2D[1.0, 0.0], V2D[2.0, 0.0] )
35
+ assert_equal( V2D[0.0, 1.0], b.firstpoint )
36
+ assert( V2D.vequal?( @@bezier.point( 0.1, nil, :parameter ), b.point( 0.1, nil, :parameter ) ) )
37
+
38
+ b = Bezier.vectors( V2D[0.0, 1.0], V2D[1.0, 0.0], V2D[1.0, 0.0], V2D[1.0, 0.0] )
39
+ assert_equal( V2D[0.0, 1.0], b.firstpoint )
40
+ assert( V2D.vequal?( @@bezier.point( 0.1, nil, :parameter ), b.point( 0.1, nil, :parameter ) ) )
41
+ end
42
+
33
43
  def test_O
34
44
  assert_equal( [V2D::O, V2D::O, V2D::O, V2D::O], Bezier::O.pointlist )
45
+ assert_equal( V2D::O, Bezier::O.sample(0.5) )
35
46
  end
36
47
 
37
48
  def test_piece
@@ -144,6 +155,17 @@ class BezierTest < Test::Unit::TestCase
144
155
  assert_equal( [(0.0..1.0), (1.0..2.0)], @@multibezier.ranges )
145
156
  end
146
157
 
158
+ def test_split1
159
+ assert( V2D.vequal?( V2D[0.5, 0.5], @@beziersym.split(0.0,0.5).lastpoint ) )
160
+ assert( V2D.vequal?( V2D[0.5, 0.5], @@beziersym.split(0.5,1.0).firstpoint ) )
161
+ end
162
+
163
+ def test_split2
164
+ assert( V2D.vequal?( V2D::O, Bezier::O.split( 0.0,0.5 ).firstpoint ) )
165
+ assert( V2D.vequal?( V2D::O, Bezier::O.split( 0.5,0.8 ).lastpoint ) )
166
+ end
167
+
168
+
147
169
  end
148
170
 
149
171
 
data/test/test_color.rb CHANGED
@@ -38,7 +38,7 @@ end
38
38
  class PaletteTest < Test::Unit::TestCase
39
39
 
40
40
  def test_palette
41
- palette = Palette.new( :colorlist, [ Color.black, 0.0, Color.white, 1.0 ] )
41
+ palette = Palette.new( :colorlist, [ 0.0, Color.black, 1.0, Color.white ] )
42
42
  assert_equal( Color[0.5, 0.5, 0.5, 1.0], palette.color( 0.5 ) )
43
43
  end
44
44
  end
@@ -46,7 +46,7 @@ end
46
46
  class GradientTest < Test::Unit::TestCase
47
47
 
48
48
  def test_gradient
49
- gradient = LinearGradient.new( :colorlist, [Color.black, 0.0, Color.white, 1.0] )
49
+ gradient = LinearGradient.new( :colorlist, [0.0, Color.black, 1.0, Color.white] )
50
50
  assert_equal( gradient.svgdef,
51
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
52
 
@@ -57,7 +57,7 @@ class GradientTest < Test::Unit::TestCase
57
57
  require 'render'
58
58
  require 'shape'
59
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] ) ) )
60
+ render.add( Circle.new, Style.new( :stroke, "none", :fill, LinearGradient.new( :colorlist, [0.0, Color.black, 1.0, Color.white] ) ) )
61
61
  render.end
62
62
  assert( File.exist?( "gradient1.png" ) )
63
63
  end
@@ -69,7 +69,7 @@ class GradientTest < Test::Unit::TestCase
69
69
  require 'shape'
70
70
  render = SVGRender.new( :filename, "gradient2.svg" )
71
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 ) )
72
+ style = Style.new( :stroke, "none", :fill, CircularGradient.new( :colorlist, [0.0, Color.black(1.0), 1.0, Color.black(0.0)], :circle, circle ) )
73
73
  render.add( circle, style )
74
74
  render.end
75
75
  assert( File.exist?( "gradient2.png" ) )
data/test/test_frame.rb CHANGED
@@ -5,8 +5,8 @@ require 'geometry2D'
5
5
  class FrameTest < Test::Unit::TestCase
6
6
 
7
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] )
8
+ frame = Frame[ :center, V2D[0.0,0.0], :vector, V2D[1.0,0.0], :rotation, 0.0, :scale, 1.0 ]
9
+ assert_equal( 0.0, frame.center.x )
10
10
  end
11
11
 
12
12
  end
@@ -45,6 +45,11 @@ class V2DTest < Test::Unit::TestCase
45
45
 
46
46
  end
47
47
 
48
+ def test_norm
49
+ assert_equal( V2D[0.0,1.0], V2D[0.0,2.0].norm )
50
+ assert_equal( V2D[0.0,0.0], V2D[0.0,0.0].norm )
51
+ end
52
+
48
53
  def test_ortho
49
54
  assert_equal(V2D[-0.6,0.5],
50
55
  V2D[0.5,0.6].ortho)
@@ -0,0 +1,12 @@
1
+ require 'test/unit'
2
+ require 'interpolation'
3
+
4
+ class InterpolatorTest < Test::Unit::TestCase
5
+
6
+ def test_quadtree
7
+ quad = QuadTree.new( [0.0,1.0, 0.2,0.0, 0.6,1.0, 0.8,0.0, 1.0,1.0] )
8
+ assert_equal( [0.2,0.0,0.6,1.0], quad.range( 0.5 ) )
9
+ assert_equal( [0.0,1.0, 0.2,0.0], quad.range( 0.0 ) )
10
+ assert_equal( [0.8,0.0, 1.0,1.0], quad.range( 1.0 ) )
11
+ end
12
+ end
data/test/test_shape.rb CHANGED
@@ -64,4 +64,12 @@ class CircleTest < Test::Unit::TestCase
64
64
  assert( V2D.vequal?( v1, v2 ) )
65
65
  end
66
66
  end
67
+
68
+ def test_diameter
69
+ cd = Circle.diameter( V2D[0.0,1.0], V2D[0.0,-1.0] )
70
+ c2 = Circle[ :center, V2D::O, :radius, 1.0 ]
71
+ assert( V2D.vequal?( cd.center, c2.center ) )
72
+ assert_equal( cd.radius, c2.radius )
73
+ assert( V2D.vequal?( V2D[0.0,1.0], cd.point( 0.0 ) ) )
74
+ end
67
75
  end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: xrvg
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.2
7
- date: 2008-02-10 00:00:00 +01:00
6
+ version: 0.0.3
7
+ date: 2008-02-17 00:00:00 +01:00
8
8
  summary: Ruby vector graphics library
9
9
  require_paths:
10
10
  - lib
@@ -72,6 +72,7 @@ test_files:
72
72
  - test/test_color.rb
73
73
  - test/test_frame.rb
74
74
  - test/test_geometry2D.rb
75
+ - test/test_interpolation.rb
75
76
  - test/test_render.rb
76
77
  - test/test_shape.rb
77
78
  - test/test_style.rb