xrvg 0.0.3 → 0.0.4

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.
Files changed (59) hide show
  1. data/CHANGES +21 -0
  2. data/README +3 -3
  3. data/Rakefile +4 -4
  4. data/examples/bezierbasic.rb +1 -0
  5. data/examples/bezierbasicvector.rb +1 -0
  6. data/examples/foreach.rb +1 -0
  7. data/examples/geodash.rb +1 -0
  8. data/examples/geodash2.rb +1 -0
  9. data/examples/hellocrown.rb +1 -0
  10. data/examples/hellocrown2.rb +1 -0
  11. data/examples/hellocrownrecurse.rb +1 -0
  12. data/examples/helloworldcompact.rb +1 -0
  13. data/examples/helloworldexpanded.rb +1 -0
  14. data/examples/multibezierbasic.rb +1 -0
  15. data/examples/palette_circle.rb +1 -0
  16. data/examples/randomdash.rb +1 -0
  17. data/examples/range_examples.rb +1 -0
  18. data/examples/range_examples2.rb +1 -0
  19. data/examples/sample.rb +1 -0
  20. data/examples/simpledash.rb +1 -0
  21. data/examples/uplets.rb +1 -0
  22. data/lib/bezier.rb +21 -55
  23. data/lib/bezierbuilders.rb +194 -0
  24. data/lib/beziermotifs.rb +114 -0
  25. data/lib/bezierspline.rb +20 -75
  26. data/lib/beziertools.rb +211 -0
  27. data/lib/color.rb +26 -7
  28. data/lib/fitting.rb +203 -0
  29. data/lib/frame.rb +2 -1
  30. data/lib/geometry2D.rb +6 -5
  31. data/lib/interbezier.rb +87 -0
  32. data/lib/interpolation.rb +6 -5
  33. data/lib/parametriclength.rb +87 -0
  34. data/lib/render.rb +4 -9
  35. data/lib/samplation.rb +71 -82
  36. data/lib/shape.rb +47 -25
  37. data/lib/style.rb +2 -1
  38. data/lib/trace.rb +2 -0
  39. data/lib/utils.rb +111 -17
  40. data/lib/xrvg.rb +16 -6
  41. data/test/test_attributable.rb +34 -2
  42. data/test/test_bezier.rb +93 -2
  43. data/test/test_bezierbuilders.rb +92 -0
  44. data/test/test_beziertools.rb +97 -0
  45. data/test/test_color.rb +65 -24
  46. data/test/test_fitting.rb +47 -0
  47. data/test/test_frame.rb +7 -2
  48. data/test/test_geometry2D.rb +26 -7
  49. data/test/test_interbezier.rb +29 -0
  50. data/test/test_interpolation.rb +16 -1
  51. data/test/test_parametric_length.rb +15 -0
  52. data/test/test_render.rb +54 -6
  53. data/test/test_shape.rb +103 -10
  54. data/test/test_trace.rb +13 -0
  55. data/test/test_utils.rb +114 -12
  56. data/test/test_xrvg.rb +3 -0
  57. metadata +16 -5
  58. data/lib/assertion.rb +0 -14
  59. data/lib/attributable.rb +0 -152
data/CHANGES CHANGED
@@ -1,5 +1,26 @@
1
1
  = CHANGES
2
2
 
3
+ == 0.0.4 (2008.03.16)
4
+ Really big functional release. Several additional releases are needed to add appropriate tests and docs.
5
+ === Content
6
+ - XRVG namespace creation: you must now add "include XRVG" after "require 'xrvg'" to make your previous work OK.
7
+ - bezier curve fitting algorithm implementation
8
+ - adaptative bezier curve fitting algorithm implementation
9
+ - bezier builder hierarchy introducing
10
+ - ArcBezier and PicBezier bezier motifs
11
+ - Offset, Ondulation, ClosureBezier bezier builders
12
+ - meta bezier builders: iterator builders, fitting builders
13
+ === Other little additions
14
+ - new FloatFunctor filter Alternate (must be consolidated though)
15
+ === Removed Bugs
16
+ - splits enumerator works again
17
+ - debug bezier filter method
18
+ === Refactoring
19
+ - make bezier frame with container
20
+ - ParametricLength module extraction from BezierSpline
21
+ === TODO
22
+ - complete Circle curve, with split interface (maybe add generic method in Curve, from bezier, as circle -> bezier method is available)
23
+
3
24
  == 0.0.3 (2008.02.17)
4
25
  === Content
5
26
  - just bug correction, optims and refactoring
data/README CHANGED
@@ -1,6 +1,6 @@
1
1
  = XRVG -- X Ruby Vector Graphics
2
2
 
3
- Supporting XRVG version: 0.0.3
3
+ Supporting XRVG version: 0.0.4
4
4
 
5
5
  This package contains XRVG, a Ruby vector graphic programming library.
6
6
 
@@ -64,7 +64,7 @@ new feature to be submitted in the form of new unit tests.
64
64
 
65
65
  For other information, feel free to ask on the ruby-talk mailing list
66
66
  (which is mirrored to comp.lang.ruby) or contact
67
- mailto:jblondinet@nospam@yahoo.com.
67
+ mailto:julien.leonard@nospam@ensta.org.
68
68
 
69
69
 
70
70
  = Other stuff
@@ -75,7 +75,7 @@ Thanks for Rake project, whose README file has been adapted to do this one.
75
75
 
76
76
  == Summary
77
77
 
78
- Author:: Julien L�onard <jblondinet@nospam@yahoo.com>
78
+ Author:: Julien L�onard <julien.leonard@nospam@ensta.org>
79
79
  Requires:: Ruby 1.8.0 or later (but not compatible with 1.9.0)
80
80
  License:: Copyright 2008 by Julien L�onard.
81
81
  Released under an MIT-style license.
data/Rakefile CHANGED
@@ -11,7 +11,7 @@ PROJECT = "XRVG"
11
11
  MY_NAME = "Julien L�onard"
12
12
 
13
13
  # Your email address, used in packaging.
14
- MY_EMAIL = "jblondinet@nospam@yahoo.com"
14
+ MY_EMAIL = "julien.leonard@nospam@ensta.org"
15
15
 
16
16
  # Short summary of your project, used in packaging.
17
17
  PROJECT_SUMMARY = "Ruby vector graphics library"
@@ -45,7 +45,7 @@ REQUIRE_PATHS << EXT_DIR if HAVE_EXT
45
45
  $LOAD_PATH.concat(REQUIRE_PATHS)
46
46
  # This library file defines the RAKEVERSION constant.
47
47
  require "#{UNIX_NAME}"
48
- PROJECT_VERSION = eval("\"#{XRVG_VERSION}\"") # e.g. "1.0.2"
48
+ PROJECT_VERSION = eval("\"#{XRVG_VERSION}\"")
49
49
 
50
50
  #---
51
51
  # Options common to RDocTask AND Gem::Specification.
@@ -63,7 +63,7 @@ RDOC_FILES = FileList["README", "CHANGES"]
63
63
 
64
64
  # Ruby library code.
65
65
  LIB_DIR = "lib"
66
- PRE_LIB_FILES = FileList["assertion.rb", "attributable.rb", "color.rb", "frame.rb", "geometry2D.rb", "interpolation.rb", "render.rb", "samplation.rb", "shape.rb", "style.rb", "trace.rb", "utils.rb", "bezier.rb", "bezierspline.rb", "xrvg.rb"]
66
+ PRE_LIB_FILES = FileList["trace.rb", "samplation.rb", "interpolation.rb", "parametriclength.rb", "utils.rb", "geometry2D.rb", "color.rb", "frame.rb", "shape.rb", "render.rb", "shape.rb", "style.rb", "bezier.rb", "bezierspline.rb", "fitting.rb", "bezierbuilders.rb", "beziermotifs.rb", "beziertools.rb", "interbezier.rb", "xrvg.rb"]
67
67
  LIB_FILES = FileList["#{LIB_DIR}/*.rb"]
68
68
 
69
69
  # Example code.
@@ -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", "test_interpolation.rb"]
75
+ PRE_TEST_FILES = FileList["test_*.rb"]
76
76
  TEST_FILES = FileList["test/*.rb"]
77
77
 
78
78
  # Executable scripts, all non-garbage files under bin/.
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "bezierbasic.svg" ]
4
5
  bezier = Bezier.raw( V2D[0.0, 1.0], V2D[1.0, 1.0], V2D[0.0, 0.0], V2D[1.0, 0.0] )
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "bezierbasicvector.svg" ]
4
5
  bezier = Bezier.vector( V2D[0.0, 1.0], V2D[1.0, 0.0], V2D[1.0, 0.0], V2D[-1.0, 0.0] )
data/examples/foreach.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "foreach.svg", :background, "white" ]
4
5
  Circle[].samples(10).foreach do |p1,p2|
data/examples/geodash.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "geodash.svg" ]
4
5
  bezier = Bezier.raw( V2D[0.0, 1.0], V2D[1.0, 1.0], V2D[0.0, 0.0], V2D[1.0, 0.0] )
data/examples/geodash2.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "geodash2.svg" ]
4
5
  bezier = Bezier.raw( V2D[0.0, 1.0], V2D[1.0, 1.0], V2D[0.0, 0.0], V2D[1.0, 0.0] )
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "hellocrown.svg" ]
4
5
  Circle[].samples( 8 ) do |point|
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "hellocrown2.svg" ]
4
5
  [Circle[], (0.2..0.1)].samples( 10 ) do |point, radius|
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "hellocrownrecurse.svg" ]
4
5
  Circle[].samples( 8 ) do |point|
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  SVGRender.[] do |render|
4
5
  render.add( Circle[] )
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender.new( :filename, "helloworldexpanded.svg" )
4
5
  render.add( Circle.new )
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "multibezierbasic.svg" ]
4
5
  bezier = Bezier.multi( [[:vector, V2D[0.0, 1.0], V2D[1.0, 0.0], V2D[1.0, 0.0], V2D[-1.0, 0.0]],
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "palette_circle.svg", :background, "white" ]
4
5
 
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "randomdash.svg" ]
4
5
  bezier = Bezier.raw( V2D[0.0, 1.0], V2D[1.0, 1.0], V2D[0.0, 0.0], V2D[1.0, 0.0] )
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  # Samplable module
4
5
  (1.0..2.0).sample(0.5); #=> 1.5
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  Range.O; #=> (0.0..1.0)
4
5
  Range.Angle; #=> (0.0..2*Math::PI)
data/examples/sample.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "sample.svg" ]
4
5
 
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "simpledash.svg" ]
4
5
  bezier = Bezier.raw( V2D[0.0, 1.0], V2D[1.0, 1.0], V2D[0.0, 0.0], V2D[1.0, 0.0] )
data/examples/uplets.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'xrvg'
2
+ include XRVG
2
3
 
3
4
  render = SVGRender[ :filename, "uplets.svg", :background, "white" ]
4
5
  Circle[].samples(10).uplets do |p1,p2|
data/lib/bezier.rb CHANGED
@@ -4,6 +4,7 @@
4
4
  require 'bezierspline'
5
5
  require 'shape'
6
6
 
7
+ module XRVG
7
8
  # = Base class for cubic bezier curves
8
9
  # == Basics
9
10
  # See http://en.wikipedia.org/wiki/B%C3%A9zier_curve
@@ -94,7 +95,7 @@ class Bezier < Curve
94
95
  # args is a list of points and control points as [p1, v1, p2, v2, p3, v3]
95
96
  #
96
97
  # Beware that
97
- # Bezier.vector( p1, v1, p2, v2 ) == Bezier.raws( p1, v1, p2, -v2)
98
+ # Bezier.vector( p1, v1, p2, v2 ) == Bezier.vectors( p1, v1, p2, -v2)
98
99
  def Bezier.vectors( *args )
99
100
  rawpieces = []
100
101
  args.foreach(2).pairs do |pair1, pair2|
@@ -182,6 +183,11 @@ class Bezier < Curve
182
183
  def bezier( index )
183
184
  return Bezier.single( *self.piece( index ).data )
184
185
  end
186
+
187
+ # shortcut method to retrieve data list of a bezier
188
+ def data
189
+ return self.pieces.map{ |piece| piece.data }
190
+ end
185
191
 
186
192
 
187
193
  # -------------------------------------------------------------
@@ -195,19 +201,10 @@ class Bezier < Curve
195
201
  check_parametertype( parametertype )
196
202
  result = []
197
203
  if parametertype == :length
198
- if index < 0.0
199
- index = 0.0
200
- elsif index > 1.0
201
- index = 1.0
202
- end
204
+ index = (0.0..1.0).trim( index )
203
205
  result = length_parameter_mapping( index, side )
204
206
  else # no need to test parametertype value, as check_parametertype already do it
205
- if index < 0.0
206
- index = 0.0
207
- elsif index > self.piecenumber
208
- index = self.piecenumber.to_f
209
- end
210
-
207
+ index = (0.0..self.piecenumber.to_f).trim( index )
211
208
  pieceindex = index < self.piecenumber ? index.to_i : (index-1).to_i
212
209
  t = index - pieceindex
213
210
  result = [pieceindex, t]
@@ -252,12 +249,16 @@ class Bezier < Curve
252
249
  end
253
250
 
254
251
  # curve method redefinition to factorize parametermapping
255
- def frame( t, parametertype=:length )
252
+ def frame( t, container=nil, parametertype=:length )
256
253
  pieceindex, t = parametermapping( t, parametertype )
257
- tangent = self.piece( pieceindex ).tangent( t )
254
+ containerpoint = container ? container.center : nil
255
+ containertangent = container ? container.vector : nil
256
+ point = self.piece( pieceindex ).point( t, containerpoint )
257
+ tangent = self.piece( pieceindex ).tangent( t, containertangent )
258
258
  rotation = self.rotation( nil, tangent )
259
259
  scale = self.scale( nil, tangent )
260
- return Frame[ :center, self.piece( pieceindex ).point( t ), :vector, tangent, :rotation, rotation, :scale, scale ]
260
+ result = container ? container : Frame[ :center, point, :vector, tangent, :rotation, rotation, :scale, scale ]
261
+ return result
261
262
  end
262
263
 
263
264
  # -------------------------------------------------------------
@@ -291,17 +292,6 @@ class Bezier < Curve
291
292
  return Bezier.new( :pieces, self.subpieces( t1, t2 ) )
292
293
  end
293
294
 
294
- # split method
295
- if nil
296
- def split(nchildren=2, type=:regular)
297
- return self.range.split(nchildren,type).map { |range| self.subbezier( range.begin, range.end ) }
298
- end
299
-
300
- def subdivise( samples )
301
- return samples.pairs.map { |t1, t2| self.subbezier( t1, t2 ) }
302
- end
303
- end
304
-
305
295
  # -------------------------------------------------------------
306
296
  # reverse
307
297
  # -------------------------------------------------------------
@@ -365,20 +355,6 @@ class Bezier < Curve
365
355
  return Bezier.new( :pieces, self.pieces + other.pieces )
366
356
  end
367
357
 
368
- # -------------------------------------------------------------
369
- # range
370
- # -------------------------------------------------------------
371
-
372
- # TODO
373
- def ranges #:nodoc:
374
- (0..self.piecenumber).to_a.pairs.map {|start,stop| Range.new( start, stop )}
375
- end
376
-
377
- # TODO
378
- def range #:nodoc:
379
- return (0..self.piecenumber)
380
- end
381
-
382
358
  # -------------------------------------------------------------
383
359
  # svg
384
360
  # -------------------------------------------------------------
@@ -437,7 +413,7 @@ class Bezier < Curve
437
413
  # must use an interpolator ?
438
414
  def compute_length #:nodoc:
439
415
  lengths = self.pieces.map {|piece| piece.length}
440
- Trace("pieces #{self.pieces.inspect} lenghts #{lengths.inspect}")
416
+ # Trace("pieces #{self.pieces.inspect} lenghts #{lengths.inspect}")
441
417
  result = lengths.sum
442
418
  if result == 0.0
443
419
  lengths = [1.0]
@@ -476,13 +452,13 @@ class Bezier < Curve
476
452
 
477
453
  def length_parameter_mapping( t, side ) #:nodoc:
478
454
  pieceindex = -1
479
- Trace("self.lengthranges #{self.lengthranges.inspect}")
455
+ # Trace("self.lengthranges #{self.lengthranges.inspect}")
480
456
  self.lengthranges.each_with_index do |lrange,i|
481
457
  if lrange.include?( t )
482
458
  pieceindex = i
483
459
  t = lrange.abscissa( t )
484
460
  t = self.piece( i ).parameterfromlength( t )
485
- Trace("pieceindex #{pieceindex} t #{t}")
461
+ # Trace("pieceindex #{pieceindex} t #{t}")
486
462
  break
487
463
  end
488
464
  end
@@ -510,11 +486,7 @@ class Bezier < Curve
510
486
  #
511
487
  # TODO : must be defined on Curve interface !!
512
488
  def filter(type=:point, &block)
513
- if type == :length
514
- return super(:pointbylength, &block )
515
- else
516
- return super(type, &block).addfilter( self.range )
517
- end
489
+ return super(type, &block).addfilter( (0.0..1.0) )
518
490
  end
519
491
 
520
492
  def apply_split( t1, t2 ) #:nodoc:
@@ -527,10 +499,4 @@ class Bezier < Curve
527
499
 
528
500
  # TODO : add generic bezier builder from points : must be adaptative !! (use Fitting)
529
501
  end
530
-
531
-
532
-
533
-
534
-
535
-
536
-
502
+ end
@@ -0,0 +1,194 @@
1
+ # BezierBuilder file
2
+ # See:
3
+ # - BezierBuilder
4
+ # - SimilarMotifIterator
5
+ # - AttributeMotifIterator
6
+ # - FitBezierBuilder
7
+
8
+ require 'bezier'
9
+ require 'fitting'
10
+
11
+ module XRVG
12
+
13
+ # = BezierBuilder class
14
+ # == Content
15
+ # Abstract class to define prototype of a bezier factory
16
+ #
17
+ # Provides the notation
18
+ # bezierresult = BezierBuilder[ :parameter1, arg1, :parameter2, arg2 ]
19
+ # with bezierresult the computed Bezier object from the BezierBuilder algorithm
20
+ class BezierBuilder
21
+ include Attributable
22
+
23
+ # Hook for subclassing : must return a list of raw pieces, to be provided to Bezier.multi
24
+ def compute()
25
+ raise NotImplementedError.new("#{self.class.name}#compute is an abstract method.")
26
+ end
27
+
28
+ # syntax sugar method to replace BezierBuilder.build notation with the simpler BezierBuilder[] one
29
+ #
30
+ # Note that BezierBuilder[] does not return a BezierBuilder object, but a Bezier one
31
+ def BezierBuilder.[](*args)
32
+ return self.build( *args )
33
+ end
34
+
35
+ # create the BezierBuilder, and build a Bezier.multi by calling BezierBuilder.compute
36
+ def BezierBuilder.build( *args )
37
+ builder = self.new( *args )
38
+ return Bezier.multi( builder.compute )
39
+ end
40
+
41
+ # multipiece bezier operator to smooth tangents of piece junctions
42
+ #
43
+ # Algo:
44
+ # - for each pair of pieces, take last and first vector
45
+ # - compute the mean of these vectors
46
+ # - for each vector, linearly interpolate given :factor between initial vector and mean
47
+ # As a consequence, default :factor value means that by default, we take vector means, and this operator
48
+ # do nothing if you set :factor to 0.0
49
+ def BezierBuilder.lissage( bezier, factor=1.0 )
50
+ result = [[:vector] + (bezier.pieces[0].pointlist(:vector))[0..1]]
51
+ bezier.pieces.pairs do |piece1, piece2|
52
+ p = piece1.lastpoint;# equal to piece2.firstpoint
53
+ v1, v2 = [-piece1.lastvector, piece2.firstvector]
54
+ mean = (v1..v2).mean
55
+ newv1 = (v1..mean).sample( factor )
56
+ newv2 = (v2..mean).sample( factor )
57
+ result[-1] += [p,-newv1]
58
+ result << [:vector, p, newv2]
59
+ end
60
+ result[-1] += (bezier.pieces[-1].pointlist(:vector))[-2..-1]
61
+ return Bezier.multi( result )
62
+ end
63
+ end
64
+
65
+
66
+ #-------------------------------------------------------------------------------
67
+ # We can now build on these atomic motifs some motif iterators
68
+ #-------------------------------------------------------------------------------
69
+
70
+ # = Similar Motif Iterator
71
+ # == Content
72
+ # Take a bezier curve as :motif, and a "curvesampler" as a support with sampling description, and iterate motif on each pair computed by sampling
73
+ # == Attributes
74
+ # attribute :curvesampler, nil, Samplable
75
+ # attribute :motif, nil, Bezier
76
+ # attribute :nmotifs, 10
77
+ # == Example
78
+ # motif = PicBezier[ :support, [V2D::O, V2D::X], :height, -1.0 ]
79
+ # curvesampler = bezier.geo( 3.0 )
80
+ # bezier = SimilarMotifIterator[ :curvesampler, curvesampler, :motif, motif, :nmotifs, 10 ]
81
+ # == TODO
82
+ # Represents the basic operator to compute bezier fractal curves !!
83
+ class SimilarMotifIterator < BezierBuilder
84
+ attribute :curvesampler, nil, Samplable
85
+ attribute :motif, nil, Bezier
86
+ attribute :nmotifs, 10
87
+
88
+ # BezierBuilder overloading
89
+ #
90
+ # Algo
91
+ # - sample @nmotif+1 times @curvesampler to get @nmotifs point pairs
92
+ # - foreach pair, compute new bezier by calling Bezier.similar on @motif
93
+ def compute
94
+ result = []
95
+ self.curvesampler.samples( self.nmotifs + 1).pairs do |p1,p2|
96
+ Trace("SimilarMotifIterator::compute p1 #{p1.inspect} p2 #{p2.inspect}")
97
+ newbezier = self.motif.similar( (p1..p2) )
98
+ result += newbezier.data
99
+ end
100
+ return result
101
+ end
102
+
103
+ end
104
+
105
+ # = Attribute Motif Iterator
106
+ # == Content
107
+ # More advanced motif iterator than SimilarMotifIterator, and also more expensive, AttributeMotifIterator samples
108
+ # a curvesampler and foreach pair build a new bezier motif, with varying attributes
109
+ # == Attributes
110
+ # attribute :curvesampler, nil, Splittable
111
+ # attribute :motifclass
112
+ # attribute :nmotifs, 10
113
+ # attribute :attributes, [], Array
114
+ # attribute :closed, false
115
+ # :motifclass is a BezierBuilder class, as ArcBezier, PicBezier, or even AttributeMotifIterator...
116
+ #
117
+ # :attributes is of the form [attribute1, specification1, :attribute2, specification, ...] with
118
+ # - attribute1, attribute2 attributes of the :motifclass
119
+ # - specification can be :
120
+ # - single value
121
+ # - sampler
122
+ #
123
+ # :closed attribute state if each subbezier computed for each point pair must be closed with the corresponding subbezier of the :curvesampler
124
+ # == Example
125
+ # motif = PicBezier[ :support, [V2D::O, V2D::X], :height, -1.0 ]
126
+ # curvesampler = bezier.geo( 3.0 )
127
+ # result = AttributeMotifIterator[ :curvesampler, curvesampler, :motifclass, ArcBezier, :attributes, [:height, (-2.0..0.0).random], :nmotifs, 30, :closed, true ]
128
+ # == WARNING
129
+ # Only works with BezierMotif defined by two points
130
+ class AttributeMotifIterator < BezierBuilder
131
+ attribute :curvesampler, nil, Splittable
132
+ attribute :motifclass
133
+ attribute :nmotifs, 10
134
+ attribute :attributes, [], Array
135
+ attribute :closed, false
136
+
137
+ # BezierBuilder overloading
138
+ #
139
+ # See AttributeMotifIterator class description for details
140
+ def compute
141
+ result = []
142
+ attrvalues = []
143
+ self.attributes.foreach do |name, spec|
144
+ attrvalues += [name, Samplable.build( spec ).samples( self.nmotifs )]
145
+ end
146
+ self.curvesampler.splits( self.nmotifs ).each_with_index do |subbezier,index|
147
+ pair = [subbezier.firstpoint, subbezier.lastpoint]
148
+ p1, p2 = pair
149
+ args = [:support, pair]
150
+ attrvalues.foreach do |name, values|
151
+ args += [name, values[index]]
152
+ end
153
+ newbezier = self.motifclass[ *args ]
154
+ if self.closed
155
+ newbezier = newbezier + subbezier.reverse
156
+ end
157
+ result += newbezier.data
158
+ end
159
+ return result
160
+ end
161
+
162
+ end
163
+
164
+ # = FitBezierBuilder class
165
+ # == Content
166
+ # Build a bezier from a point list defined by :points by computing adaptative multipiece bezier fitting.
167
+ #
168
+ # While this class is by itself quite usefull, it can also be subclassed by overloading "points" method to
169
+ # compute all sorts of curve (as Offset for example)
170
+ # == Attributes
171
+ # attribute :points, [], Array; # to be able to subclass FitBezierBuilder to compute points by diverse means
172
+ # attribute :maxerror, 0.001
173
+ # :maxerror attribute represents the bezier curve matching error (as explained in Fitting)
174
+ class FitBezierBuilder < BezierBuilder
175
+ attribute :points, [], Array; # to be able to subclass FitBezierBuilder to compute points by diverse means
176
+ attribute :maxerror, 0.001
177
+
178
+ def FitBezierBuilder.build( *args )
179
+ builder = self.new( *args )
180
+ return Fitting.adaptative_compute( builder.points, builder.maxerror )[0]
181
+ end
182
+
183
+ end
184
+
185
+ # Extend Circle class for subcurve definition
186
+ class Circle
187
+ # return approximating bezier curve
188
+ def bezier
189
+ return FitBezierBuilder[ :points, self.samples( 20 ) ]
190
+ end
191
+ end
192
+
193
+
194
+ end # end XRVG