xrvg 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,12 +1,20 @@
1
1
  = CHANGES
2
2
 
3
- == 0.0.8 (2008.08.26) (Future)
3
+ == 0.0.8 (2008.08.26)
4
+ === Content
5
+ - unitary tests completion
6
+ - new spiral builder from tangent (spiral refactoring to be committed soon...)
4
7
  - INCOMPATIBLE modifs:
5
8
  - Array is no longer SAMPLABLE => you must remplace "[].geo(5.0).samples" by "SyncS[].geo( 5.0 )"
6
9
  this has been done to avoid conflicts between Array and Samplation interface, namely .rand method,
7
10
  but also to clean semantic of the operation
11
+ and to provide shortcuts (as automatic Roller for arrays to be samples in a synchronized way...)
8
12
  and to be able to upgrade SyncS operations (with .split operator,...)
9
- == 0.0.7 (2008.08.24) (New)
13
+ === TODO
14
+ - tutorials need to be updated with the INCOMPATIBLE modif
15
+ - spiral class must be considered as curve, to redefine point and tangent computation, and to provide access to parameters
16
+
17
+ == 0.0.7 (2008.08.24)
10
18
  === Content
11
19
  - Add color services (hsv, hsl, ...)
12
20
  - Add intersection services (Beta for the moment, must be consolidated)
@@ -26,7 +26,7 @@ roots = [ArcBezier[ :support, [V2D::O, V2D::X]]]
26
26
  palette = Palette[ :colorlist, [ 0.0, Color.black,
27
27
  1.0, Color.blue]]
28
28
  niter = 6
29
- [(0.0..1.0),(0.01..0.001),palette].samples( niter ) do |time,width,color|
29
+ SyncS[(0.0..1.0),(0.01..0.001),palette].samples( niter ) do |time,width,color|
30
30
  time = (niter * time).to_i
31
31
  style.stroke = color
32
32
  style.strokewidth = width
@@ -13,7 +13,7 @@ palette = Palette[ :colorlist, [ 0.0, Color.black,
13
13
  0.5, Color.blue,
14
14
  1.0, Color.white]]
15
15
  style = Style.new( :strokewidth, 0.01 )
16
- [gbezier, palette].samples( 10 ) do |bezier, color|
16
+ SyncS[gbezier, palette].samples( 10 ) do |bezier, color|
17
17
  style.fill = color
18
18
  style.stroke = color
19
19
  render.add( bezier, style )
@@ -2,7 +2,7 @@ require 'xrvg'
2
2
  include XRVG
3
3
 
4
4
  render = SVGRender[ :filename, "hellocrown2.svg" ]
5
- [Circle[], (0.2..0.1)].samples( 10 ) do |point, radius|
5
+ SyncS[Circle[], (0.2..0.1)].samples( 10 ) do |point, radius|
6
6
  render.add( Circle[:center, point, :radius, radius ], Style[ :fill, Color.blue( 0.5 ) ] )
7
7
  end
8
8
  render.end
@@ -4,7 +4,7 @@ include XRVG
4
4
  render = SVGRender[ :filename, "palette_circle.svg", :background, "white" ]
5
5
 
6
6
  palette = Palette[ :colorlist, [ 0.0, Color.black, 1.0 , Color.blue] ]
7
- [Circle[], palette, (0.1..0.02).random()].samples(25) do |point, color, radius|
7
+ SyncS[Circle[], palette, (0.1..0.02).random()].samples(25) do |point, color, radius|
8
8
  render.add( Circle[ :center, point, :radius, radius ], Style[ :fill, color ])
9
9
  end
10
10
 
data/examples/sample.rb CHANGED
@@ -6,7 +6,7 @@ render = SVGRender[ :filename, "sample.svg" ]
6
6
  palette = Palette[ :colorlist, [ 0.0, Color.blue, 0.25, Color.orange,
7
7
  0.5, Color.yellow, 0.75, Color.green,
8
8
  1.0, Color.blue] ]
9
- [Circle[], palette, (0.1..0.02).random()].samples(25) do |point, color, radius|
9
+ SyncS[Circle[], palette, (0.1..0.02).random()].samples(25) do |point, color, radius|
10
10
  render.add( Circle[ :center, point, :radius, radius ], Style[ :fill, color ])
11
11
  end
12
12
 
data/lib/bezier.rb ADDED
@@ -0,0 +1,588 @@
1
+ # +Bezier+ source
2
+ #
3
+
4
+ require 'bezierspline'
5
+ require 'shape'
6
+
7
+ module XRVG
8
+ # = Base class for cubic bezier curves
9
+ # == Basics
10
+ # See http://en.wikipedia.org/wiki/B%C3%A9zier_curve
11
+ # == Examples
12
+ # Basically, a Bezier curve is a multi-pieces cubic bezier curve. As a first example, you can create bezier curves as follows :
13
+ # b = Bezier[ :pieces, [[:raw, p1, pc1, pc2, p2]] ]; # raw description, as SVG
14
+ # b = Bezier[ :pieces, [[:vector, p1, v1, p2, v2]] ]; # more "symetrical" description.
15
+ # For more extensive description, see http://xrvg.rubyforge.org/XRVGBezierCurve.html
16
+ # == Discussion
17
+ # In XRVG, bezier curves must be also viewed as a way to create smooth and non linear interpolation between values (see +Interpolation+)
18
+ #
19
+ # Other point : to run along a bezier curve, you can use two different parametrization :
20
+ # - the curve generic one, that is "curviligne" abscissa, that is length
21
+ # - the bezier parameter, as bezier curves are parametrized curves. For multi-pieces curve, by extension, parameter goes from one integer value to the next one
22
+ # As Bezier class provides several methods with a parameter input, it is necessary to specify with parameter type you want to use ! For example,
23
+ # to compute a point from a bezier curve, Bezier class defines the point method as follows :
24
+ # def point( t, parametertype=:length )
25
+ # This is a general declaration : every method with a parameter input will propose such a kind of interface :
26
+ # - t as Float parameter value
27
+ # - parametertype, by default :length, that can have two values, :length or :parameter. :parameter is kept because is far faster than other indexation.
28
+ # == Attributes
29
+ # attribute :pieces
30
+ class Bezier < Curve
31
+ attribute :pieces
32
+
33
+ # -------------------------------------------------------------
34
+ # builders
35
+ # -------------------------------------------------------------
36
+
37
+ # Initialize with the Attributable format
38
+ #
39
+ #
40
+ # Licit formats :
41
+ # b = Bezier.new( :pieces, [BezierSpline[:raw, p1, pc1, pc2, p2]] )
42
+ # b = Bezier[ :pieces, [BezierSpline[:vector, p1, v1, p2, v2]] ]
43
+ # However, prefer the use of the following builders
44
+ # b = Bezier.vector( p1, v1, p2, v2 )
45
+ # b = Bezier.raw( p1, pc1, pc2, p2 )
46
+ # b = Bezier.single( :raw, p1, pc1, pc2, p2 )
47
+ # b = Bezier.multi( [[:raw, p1, pc1, pc2, p2], [:raw, p1, pc1, pc2, p2]] )
48
+ # The two last syntaxes are provided as shortcuts, as used quite frequently, and must be used instead of :pieces attributable builder
49
+ def Bezier.[]( *args )
50
+ self.new( *args )
51
+ end
52
+
53
+ # Uni Bezier builder
54
+ #
55
+ # type can be :raw or :vector
56
+ def Bezier.single( type, p1, p2, p3, p4 )
57
+ return Bezier.new( :pieces, [BezierSpline[type, p1, p2, p3, p4]] )
58
+ end
59
+
60
+ # Uni Bezier builder in :raw format
61
+ def Bezier.raw( p1, pc1, pc2, p2 )
62
+ return Bezier.single( :raw, p1, pc1, pc2, p2 )
63
+ end
64
+
65
+ # Uni Bezier builder in :vector format
66
+ def Bezier.vector( p1, v1, p2, v2 )
67
+ return Bezier.single( :vector, p1, v1, p2, v2 )
68
+ end
69
+
70
+ # Uni Bezier builder in :vector format, with vector length normalized at 1/3 dist between points
71
+ def Bezier.vectorreg( p1, v1, p2, v2 )
72
+ d = (p1 - p2).r
73
+ v1 = v1.norm * d/3.0
74
+ v2 = v2.norm * d/3.0
75
+ return Bezier.vector( p1, v1, p2, v2 )
76
+ end
77
+
78
+
79
+ # Basic Multi Bezier builder
80
+ #
81
+ # raw pieces must be an array of arrays of the form [type, p1, p2, p3, p4] as defined for single
82
+ def Bezier.multi( rawpieces )
83
+ return Bezier.new( :pieces, rawpieces.map {|piece| BezierSpline[*piece]} )
84
+ end
85
+
86
+ # "regular" Multi Bezier :raw specification
87
+ #
88
+ # args is a list of points and control points as [p1, pc1, p2, pc2, p3, pc3]
89
+ #
90
+ # Beware that
91
+ # Bezier.raw( p1, pc1, pc2, p2 ) == Bezier.raws( p1, pc1, p2, p2 + (p2-pc2))
92
+ def Bezier.raws( *args )
93
+ rawpieces = []
94
+ args.foreach(2).pairs do |pair1, pair2|
95
+ p1, pc1 = pair1
96
+ p2, pc2 = pair2
97
+ rawpieces << [:raw, p1, pc1, p2+(p2-pc2), p2]
98
+ end
99
+ return Bezier.multi( rawpieces )
100
+ end
101
+
102
+ # "regular" Multi Bezier :vector specification
103
+ #
104
+ # args is a list of points and control points as [p1, v1, p2, v2, p3, v3]
105
+ #
106
+ # Beware that
107
+ # Bezier.vector( p1, v1, p2, v2 ) == Bezier.vectors( p1, v1, p2, -v2)
108
+ def Bezier.vectors( *args )
109
+ rawpieces = []
110
+ args.foreach(2).pairs do |pair1, pair2|
111
+ p1, v1 = pair1
112
+ p2, v2 = pair2
113
+ rawpieces << [:vector, p1, v1, p2, -v2]
114
+ end
115
+ return Bezier.multi( rawpieces )
116
+ end
117
+
118
+
119
+
120
+
121
+ # bezier point, as
122
+ # Bezier[:raw, V2D::O, V2D::O, V2D::O, V2D::O]
123
+ O = Bezier.raw( V2D::O, V2D::O, V2D::O, V2D::O )
124
+
125
+
126
+ # return piece of index "index",as BezierSpline object
127
+ #
128
+ # index can be
129
+ # - integer : in that case, simple return @pieces[index]
130
+ # - float : in that case, use second default argument
131
+ # this method must actually be very rarely called, as usually
132
+ # we want to compute something with index, and in that case we
133
+ # want to delegate computation to a BezierSpline, with parameter
134
+ # mapping parametermapping
135
+ def piece( index, parametertype=:length )
136
+ pieceindex = index
137
+ if index.is_a? Float
138
+ pieceindex, t = self.parametermapping( index, parametertype )
139
+ end
140
+ return @pieces[ pieceindex ]
141
+ end
142
+
143
+ # return number of pieces
144
+ def piecenumber
145
+ return @pieces.length
146
+ end
147
+
148
+ # -------------------------------------------------------------
149
+ # curve interface
150
+ # -------------------------------------------------------------
151
+
152
+ # overload Curve::viewbox
153
+ def viewbox
154
+ return V2D.viewbox( self.pointlist() )
155
+ end
156
+
157
+
158
+ # -------------------------------------------------------------
159
+ # piece shortcuts
160
+ # -------------------------------------------------------------
161
+
162
+ # generic method to return points list of a curve
163
+ # b = Bezier[ :pieces, [[:raw, p1, pc1, pc2, p2], [:raw, p2, pc2b, pc3, p3]] ]
164
+ # b.pointlist #=> equiv to b.pointlist(:raw)
165
+ # b.pointlist(:raw) #=> [p1, pc1, pc2, p2, p2, pc2b, pc3, p3]
166
+ # if you want to get a particular piece pointlist, do
167
+ # b.piece( t ).pointlist(nil|:raw|:vector)
168
+ # TODO : result must be cached by type
169
+ def pointlist( type=:raw )
170
+ result = []
171
+ @pieces.each {|piece| result = result + piece.pointlist(type)}
172
+ # Trace("Bezier.pointlist result #{result.inspect}")
173
+ return result
174
+ end
175
+
176
+ # shortcut method to get curve first point
177
+ def firstpoint
178
+ return self.pointlist()[0]
179
+ end
180
+
181
+ # shortcut method to get curve last point
182
+ def lastpoint
183
+ return self.pointlist()[-1]
184
+ end
185
+
186
+ # shortcut method to build Bezier objects for each piece
187
+ def beziers
188
+ return self.pieces.map{ |piece| Bezier.single( *piece.data ) }
189
+ end
190
+
191
+ # shortcut method to retrieve a piece as an Bezier object
192
+ def bezier( index )
193
+ return Bezier.single( *self.piece( index ).data )
194
+ end
195
+
196
+ # shortcut method to retrieve data list of a bezier
197
+ def data
198
+ return self.pieces.map{ |piece| piece.data }
199
+ end
200
+
201
+
202
+ # -------------------------------------------------------------
203
+ # piece delegation computation
204
+ # -------------------------------------------------------------
205
+
206
+ # with index (must be Float) and parametertype as inputs, must compute :
207
+ # - the index of the piece on which the computation must take place
208
+ # - the new parameter value corresponding to bezier computation input
209
+ def parametermapping( index, parametertype=:length, side=:right ) #:nodoc:
210
+ check_parametertype( parametertype )
211
+ result = []
212
+ if parametertype == :length
213
+ index = (0.0..1.0).trim( index )
214
+ result = length_parameter_mapping( index, side )
215
+ else # no need to test parametertype value, as check_parametertype already do it
216
+ index = (0.0..self.piecenumber.to_f).trim( index )
217
+ pieceindex = index < self.piecenumber ? index.to_i : (index-1).to_i
218
+ t = index - pieceindex
219
+ result = [pieceindex, t]
220
+ end
221
+
222
+ return result
223
+ end
224
+
225
+ # utilitary method to factorize abscissa parameter type value checking
226
+ def check_parametertype( parametertype ) #:nodoc:
227
+ if !(parametertype == :parameter or parametertype == :length )
228
+ Kernel::raise("Invalid parametertype value #{parametertype}")
229
+ end
230
+ end
231
+
232
+ # -------------------------------------------------------------
233
+ # bezier computations
234
+ # -------------------------------------------------------------
235
+
236
+ # compute a point at curviligne abscissa or parameter t
237
+ #
238
+ # curve method redefinition
239
+ def point( t, container=nil, parametertype=:length )
240
+ pieceindex, t = parametermapping( t, parametertype )
241
+ return self.piece( pieceindex ).point( t, container )
242
+ end
243
+
244
+ # compute tangent at curviligne abscissa or parameter t
245
+ #
246
+ # curve method redefinition
247
+ def tangent ( t, container=nil, parametertype=:length )
248
+ pieceindex, t = parametermapping( t, parametertype )
249
+ return self.piece( pieceindex ).tangent( t, container )
250
+ end
251
+
252
+ # compute acceleration at curviligne abscissa or parameter t
253
+ #
254
+ # curve method redefinition
255
+ def acc( t, container=nil, parametertype=:length )
256
+ pieceindex, t = parametermapping( t, parametertype )
257
+ return self.piece( pieceindex ).acc( t, container )
258
+ end
259
+
260
+ # curve method redefinition to factorize parametermapping
261
+ def frame( t, container=nil, parametertype=:length )
262
+ pieceindex, t = parametermapping( t, parametertype )
263
+ containerpoint = container ? container.center : nil
264
+ containertangent = container ? container.vector : nil
265
+ point = self.piece( pieceindex ).point( t, containerpoint )
266
+ tangent = self.piece( pieceindex ).tangent( t, containertangent )
267
+ rotation = self.rotation( nil, tangent )
268
+ scale = self.scale( nil, tangent )
269
+ result = container ? container : Frame[ :center, point, :vector, tangent, :rotation, rotation, :scale, scale ]
270
+ return result
271
+ end
272
+
273
+ # -------------------------------------------------------------
274
+ # subpiece computation
275
+ # -------------------------------------------------------------
276
+
277
+ # generalize Bezier method
278
+ def subpieces (t1, t2) #:nodoc:
279
+ # Trace("subpieces t1 #{t1} t2 #{t2}")
280
+ pieceindex1, t1 = parametermapping( t1, :length, :left )
281
+ pieceindex2, t2 = parametermapping( t2, :length, :right )
282
+ # Trace("after translation pieceindex1 #{pieceindex1} t1 #{t1} pieceindex2 #{pieceindex2} t2 #{t2}")
283
+ result = []
284
+
285
+ if pieceindex1 == pieceindex2
286
+ result = [self.piece( pieceindex1 ).subpiece( t1, t2 )]
287
+ else
288
+ result << self.piece( pieceindex1 ).subpiece( t1, 1.0 )
289
+ if pieceindex1 + 1 != pieceindex2
290
+ result += self.pieces[pieceindex1+1..pieceindex2-1]
291
+ end
292
+ result << self.piece( pieceindex2 ).subpiece( 0.0, t2 )
293
+ end
294
+ return result
295
+ end
296
+
297
+ # compute the sub curve between abscissa t1 and t2
298
+ #
299
+ # may result in a multi-pieces Bezier
300
+ #
301
+ # Note: special modulo effect to deal with closed bezier curve
302
+ #
303
+ # TODO: improve code (bas design)
304
+ def subbezier( t1, t2)
305
+ # return Bezier.new( :pieces, self.subpieces( t1, t2 ) )
306
+ ranges = (0.0..1.0).modulos( (t1..t2) )
307
+ # Trace("Bezier::subbezier t1 #{t1} t2 #{t2} ranges #{ranges.inspect}")
308
+ pieces = []
309
+ ranges.each do |range|
310
+ range = range.sort
311
+ pieces += self.subpieces( range.begin, range.end )
312
+ end
313
+
314
+ bezier = Bezier.new( :pieces, pieces )
315
+ if t1 > t2
316
+ bezier = bezier.reverse
317
+ end
318
+ return bezier
319
+ end
320
+
321
+ # -------------------------------------------------------------
322
+ # reverse
323
+ # -------------------------------------------------------------
324
+
325
+ # return a new Bezier curve reversed from current one
326
+ #
327
+ # simply reverse BezierSpline pieces, both internally and in the :pieces list
328
+ def reverse
329
+ newpieces = @pieces.map {|piece| piece.reverse()}
330
+ return Bezier.new( :pieces, newpieces.reverse )
331
+ end
332
+
333
+ # -------------------------------------------------------------
334
+ # translation
335
+ # -------------------------------------------------------------
336
+
337
+ # translate the Bezier curve, by translating its points
338
+ def translate( v )
339
+ return Bezier.new( :pieces, @pieces.map { |piece| piece.translate( v ) } )
340
+ end
341
+
342
+ # rotate the Bezier curve, by rotating its points
343
+ def rotate( angle, center=V2D::O )
344
+ return Bezier.new( :pieces, @pieces.map { |piece| piece.rotate( angle, center ) } )
345
+ end
346
+
347
+ # central symetry
348
+ def sym( center )
349
+ return Bezier.new( :pieces, @pieces.map { |piece| piece.sym( center ) } )
350
+ end
351
+
352
+ # axis symetry
353
+ def axesym( point, v )
354
+ return Bezier.new( :pieces, @pieces.map { |piece| piece.axesym( point, v ) } )
355
+ end
356
+
357
+ # -------------------------------------------------------------
358
+ # similar (transform is used for samplation)
359
+ # see XRVG#33
360
+ # -------------------------------------------------------------
361
+
362
+ # "Similitude" geometric transformation
363
+ #
364
+ # See http://en.wikipedia.org/wiki/Similitude_%28geometry%29
365
+ #
366
+ # Similtude geometric transformation is (firspoint..lastpoint) -> pointrange
367
+ #
368
+ # TODO : method must be put in Curve interface
369
+ def similar( pointrange )
370
+ oldRepere = [self.firstpoint, self.lastpoint - self.firstpoint]
371
+ newRepere = [pointrange.begin, pointrange.end - pointrange.begin]
372
+ rotation = V2D.angle( newRepere[1], oldRepere[1] )
373
+ if oldRepere[1].r == 0.0
374
+ Kernel::raise("similar error : bezier is length 0")
375
+ end
376
+ scale = newRepere[1].r / oldRepere[1].r
377
+ newpoints = []
378
+ self.pointlist.each do |oldpoint|
379
+ oldvector = oldpoint - oldRepere[0]
380
+ newvector = oldvector.rotate( rotation ) * scale
381
+ newpoints.push( newRepere[0] + newvector )
382
+ end
383
+ splines = []
384
+ newpoints.foreach do |p1, p2, p3, p4|
385
+ splines.push( BezierSpline[:raw, p1, p2, p3, p4] )
386
+ end
387
+ return Bezier[ :pieces, splines ]
388
+ end
389
+
390
+ # -------------------------------------------------------------
391
+ # concatenation
392
+ # -------------------------------------------------------------
393
+
394
+ # Bezier curve concatenation
395
+ def +( other )
396
+ return Bezier.new( :pieces, self.pieces + other.pieces )
397
+ end
398
+
399
+ # -------------------------------------------------------------
400
+ # svg
401
+ # -------------------------------------------------------------
402
+
403
+ # return the svg description of the curve
404
+ #
405
+ # if firstpoint == lastpoint, curve is considered as closed
406
+ def svg()
407
+ # Trace("Bezier::svg #{self.inspect}")
408
+ path = ""
409
+ previous = nil
410
+ self.pieces().each do |piece|
411
+ p1, pc1, pc2, p2 = piece.pointlist
412
+ # Trace("previous #{previous.inspect} p1 #{p1.inspect}")
413
+ if previous == nil or not (previous - p1).r <= 0.0000001
414
+ # Trace("svg bezier not equal => M")
415
+ path += "M #{p1.x},#{p1.y}"
416
+ end
417
+ previous = p2
418
+ path += "C #{pc1.x},#{pc1.y} #{pc2.x},#{pc2.y} #{p2.x},#{p2.y}"
419
+ end
420
+
421
+ if self.firstpoint == self.lastpoint
422
+ path += " z"
423
+ end
424
+
425
+ result = "<path d=\"#{path}\"/>"
426
+ return result
427
+ end
428
+
429
+ # -------------------------------------------------------------
430
+ # gdebug
431
+ # -------------------------------------------------------------
432
+
433
+ # Display Bezier curve decorated with points and control points
434
+ def gdebug(render)
435
+ self.pieces.each {|piece| piece.gdebug(render)}
436
+ end
437
+
438
+
439
+ # -------------------------------------------------------------
440
+ # length computation
441
+ # -------------------------------------------------------------
442
+
443
+ # return the length of the bezier curve
444
+ #
445
+ # simply add pieces lengths
446
+ def length
447
+ if not @length
448
+ @length = compute_length
449
+ end
450
+ return @length
451
+ end
452
+
453
+ # Note : lengthranges building should be more functional ...
454
+ # must use an interpolator ?
455
+ def compute_length #:nodoc:
456
+ lengths = self.pieces.map {|piece| piece.length}
457
+ # Trace("pieces #{self.pieces.inspect} lenghts #{lengths.inspect}")
458
+ result = lengths.sum
459
+ if result == 0.0
460
+ lengths = [1.0]
461
+ else
462
+ psum = 0.0
463
+ lengths = lengths.map {|v| psum += v/result}
464
+ end
465
+ lmin = 0.0
466
+ @lengthranges = []
467
+ lengths.each do |llength|
468
+ # Trace("lmin #{lmin} llength #{llength}")
469
+ @lengthranges << (lmin..llength)
470
+ lmin = llength
471
+ end
472
+ @lengthranges[-1] = @lengthranges[-1].begin..1.0
473
+ return result
474
+ end
475
+
476
+ # utilitary method for interbezier
477
+ #
478
+ # return list of piece lengths relatives to total bezier lengths, and cumulated
479
+ # Bezier.new( :pieces, [piece1, piece2] ).piecelengths; => [0.0, piece1.length / bezier.length, 1.0]
480
+ def piecelengths
481
+ result = self.lengthranges.map {|range| range.begin}
482
+ result << 1.0
483
+ return result
484
+ end
485
+
486
+ # private method, to compute lengthranges if not existing
487
+ def lengthranges #:nodoc:
488
+ if not @lengthranges
489
+ compute_length
490
+ end
491
+ return @lengthranges
492
+ end
493
+
494
+ def length_parameter_mapping( t, side ) #:nodoc:
495
+ pieceindex = -1
496
+ # Trace("self.lengthranges #{self.lengthranges.inspect}")
497
+ self.lengthranges.each_with_index do |lrange,i|
498
+ if lrange.include?( t )
499
+ pieceindex = i
500
+ t = lrange.abscissa( t )
501
+ t = self.piece( i ).parameterfromlength( t )
502
+ # Trace("pieceindex #{pieceindex} t #{t}")
503
+ break
504
+ end
505
+ end
506
+ if ((side == :left) and t.fequal?(1.0) and (pieceindex < self.piecenumber - 1))
507
+ pieceindex += 1
508
+ t = 0.0
509
+ end
510
+ if pieceindex == -1
511
+ Kernel::raise("length_parameter_mapping error : t #{t} is not in length range #{self.lengthranges.inspect}")
512
+ end
513
+ return [pieceindex, t]
514
+ end
515
+
516
+
517
+ # return a list of bezier params [a1,a2, b1, b2 ...] stating intersection points for each curve
518
+ # Algo:
519
+ # - compute segment approximations of 2 curves, by keeping parameter values
520
+ # - for the moment, brute algorithm: try to match every segment of one curve with every of the other
521
+ # - for every match, dychotomie to get precise parameter value
522
+ def Bezier.intersections( c1, c2 )
523
+ samples = Range.O.samples( 100 )
524
+
525
+ # sample curves
526
+ vs = []
527
+ [c1,c2].each do |c|
528
+ subvs = []
529
+ samples.each do |t|
530
+ subvs << [t, c.point( t )]
531
+ end
532
+ vs << subvs
533
+ end
534
+
535
+ # detect segment intersections and keep corresponding coords
536
+ rawresult = []
537
+ vs[0].pairs do |s01, s02|
538
+ vs[1].pairs do |s11, s12|
539
+ inter = V2DS[s01[1], s02[1]].intersection( V2DS[s11[1], s12[1]] )
540
+ if inter
541
+ # compute distances rapport
542
+ r0 = (inter - s01[1]).r / (s02[1] - s01[1]).r
543
+ r1 = (inter - s11[1]).r / (s12[1] - s11[1]).r
544
+ rawresult += [s01[0], s02[0], r0, s11[0], s12[0], r1]
545
+ end
546
+ end
547
+ end
548
+
549
+ Trace("rawresult #{rawresult.inspect}")
550
+
551
+ # compute accurately coords
552
+ # for the moment, just return mean
553
+ result = []
554
+ rawresult.foreach do |t1, t2, r12, t3, t4, r34|
555
+ result += [(t1..t2).sample( r12 ), (t3..t4).sample( r34 )]
556
+ end
557
+
558
+ return result
559
+ end
560
+
561
+ # -------------------------------------------------------------
562
+ # sampler computation
563
+ # -------------------------------------------------------------
564
+ include Samplable
565
+ include Splittable
566
+
567
+ # filter, sampler methods
568
+ #
569
+ # just a shortcut to define easily specific sampler on bezier curve
570
+ #
571
+ # TODO : must be defined on Curve interface !!
572
+ def filter(type=:point, &block)
573
+ return super(type, &block).addfilter( (0.0..1.0) )
574
+ end
575
+
576
+ def apply_split( t1, t2 ) #:nodoc:
577
+ return self.subbezier( t1, t2 )
578
+ end
579
+
580
+ alias apply_sample point
581
+ # alias apply_split subbezier
582
+ alias sampler filter
583
+
584
+ # TODO : add generic bezier builder from points : must be adaptative !! (use Fitting)
585
+ end
586
+ end
587
+
588
+