xrvg 0.0.3 → 0.0.4

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