xrvg 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +21 -0
- data/README +3 -3
- data/Rakefile +4 -4
- data/examples/bezierbasic.rb +1 -0
- data/examples/bezierbasicvector.rb +1 -0
- data/examples/foreach.rb +1 -0
- data/examples/geodash.rb +1 -0
- data/examples/geodash2.rb +1 -0
- data/examples/hellocrown.rb +1 -0
- data/examples/hellocrown2.rb +1 -0
- data/examples/hellocrownrecurse.rb +1 -0
- data/examples/helloworldcompact.rb +1 -0
- data/examples/helloworldexpanded.rb +1 -0
- data/examples/multibezierbasic.rb +1 -0
- data/examples/palette_circle.rb +1 -0
- data/examples/randomdash.rb +1 -0
- data/examples/range_examples.rb +1 -0
- data/examples/range_examples2.rb +1 -0
- data/examples/sample.rb +1 -0
- data/examples/simpledash.rb +1 -0
- data/examples/uplets.rb +1 -0
- data/lib/bezier.rb +21 -55
- data/lib/bezierbuilders.rb +194 -0
- data/lib/beziermotifs.rb +114 -0
- data/lib/bezierspline.rb +20 -75
- data/lib/beziertools.rb +211 -0
- data/lib/color.rb +26 -7
- data/lib/fitting.rb +203 -0
- data/lib/frame.rb +2 -1
- data/lib/geometry2D.rb +6 -5
- data/lib/interbezier.rb +87 -0
- data/lib/interpolation.rb +6 -5
- data/lib/parametriclength.rb +87 -0
- data/lib/render.rb +4 -9
- data/lib/samplation.rb +71 -82
- data/lib/shape.rb +47 -25
- data/lib/style.rb +2 -1
- data/lib/trace.rb +2 -0
- data/lib/utils.rb +111 -17
- data/lib/xrvg.rb +16 -6
- data/test/test_attributable.rb +34 -2
- data/test/test_bezier.rb +93 -2
- data/test/test_bezierbuilders.rb +92 -0
- data/test/test_beziertools.rb +97 -0
- data/test/test_color.rb +65 -24
- data/test/test_fitting.rb +47 -0
- data/test/test_frame.rb +7 -2
- data/test/test_geometry2D.rb +26 -7
- data/test/test_interbezier.rb +29 -0
- data/test/test_interpolation.rb +16 -1
- data/test/test_parametric_length.rb +15 -0
- data/test/test_render.rb +54 -6
- data/test/test_shape.rb +103 -10
- data/test/test_trace.rb +13 -0
- data/test/test_utils.rb +114 -12
- data/test/test_xrvg.rb +3 -0
- metadata +16 -5
- data/lib/assertion.rb +0 -14
- data/lib/attributable.rb +0 -152
@@ -0,0 +1,87 @@
|
|
1
|
+
# File for ParametricLength module
|
2
|
+
|
3
|
+
require 'geometry2D'
|
4
|
+
|
5
|
+
module XRVG
|
6
|
+
# Utilitary module to provide length sampling frrom parameter one
|
7
|
+
#
|
8
|
+
# Use pointfromparam method to compute points
|
9
|
+
module ParametricLength
|
10
|
+
|
11
|
+
# abstract method to provide range to be sampled to compute samples for length interpolation
|
12
|
+
def parameter_range
|
13
|
+
raise NotImplementedError.new("#{self.class.name}#parameter_range is an abstract method.")
|
14
|
+
end
|
15
|
+
|
16
|
+
def pointfromparameter( parameter, container )
|
17
|
+
raise NotImplementedError.new("#{self.class.name}#pointfromparameter is an abstract method.")
|
18
|
+
end
|
19
|
+
|
20
|
+
ParametricLength::NSAMPLES = 129
|
21
|
+
|
22
|
+
# compute the length of the bezier curve defined by the points
|
23
|
+
#
|
24
|
+
# Algo :
|
25
|
+
#
|
26
|
+
# for the moment, just take a fix number of samples, and some it
|
27
|
+
def compute_length_interpolator() #:nodoc:
|
28
|
+
sum = 0.0
|
29
|
+
previous = nil
|
30
|
+
samplelist = [0.0, 0.0]
|
31
|
+
new = V2D[0.0,0.0]
|
32
|
+
previous = nil
|
33
|
+
self.parameter_range.samples( ParametricLength::NSAMPLES ) do |abs|
|
34
|
+
self.pointfromparameter( abs, new )
|
35
|
+
if previous
|
36
|
+
sum+= (new - previous).r
|
37
|
+
samplelist += [sum, abs]
|
38
|
+
else
|
39
|
+
previous = V2D[0.0,0.0]
|
40
|
+
end
|
41
|
+
previous.x = new.x
|
42
|
+
previous.y = new.y
|
43
|
+
end
|
44
|
+
@length = samplelist[-2]
|
45
|
+
|
46
|
+
length_interpolator = nil
|
47
|
+
if @length == 0.0
|
48
|
+
newsamplelist = [0.0,0.0,0.0,1.0]
|
49
|
+
invsamplelist = [0.0,0.0,1.0,0.0]
|
50
|
+
else
|
51
|
+
newsamplelist = []
|
52
|
+
invsamplelist = []
|
53
|
+
samplelist.foreach do |sum, abs|
|
54
|
+
newsamplelist += [sum / @length, abs ]
|
55
|
+
invsamplelist += [abs, sum / @length ]
|
56
|
+
end
|
57
|
+
samplelist = newsamplelist
|
58
|
+
end
|
59
|
+
@abs_interpolator = InterpolatorQuad.new( :samplelist, invsamplelist )
|
60
|
+
return InterpolatorQuad.new( :samplelist, samplelist )
|
61
|
+
end
|
62
|
+
|
63
|
+
def length_interpolator() #:nodoc:
|
64
|
+
if @length_interpolator == nil
|
65
|
+
@length_interpolator = self.compute_length_interpolator()
|
66
|
+
end
|
67
|
+
return @length_interpolator
|
68
|
+
end
|
69
|
+
|
70
|
+
def length
|
71
|
+
if @length == nil
|
72
|
+
self.compute_length()
|
73
|
+
end
|
74
|
+
return @length
|
75
|
+
end
|
76
|
+
|
77
|
+
def compute_length() #:nodoc:
|
78
|
+
self.length_interpolator()
|
79
|
+
return @length
|
80
|
+
end
|
81
|
+
|
82
|
+
def parameterfromlength( lvalue ) #:nodoc:
|
83
|
+
result = self.length_interpolator.interpolate( lvalue )
|
84
|
+
return result
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/render.rb
CHANGED
@@ -7,6 +7,7 @@ require 'color'
|
|
7
7
|
require 'attributable'
|
8
8
|
require 'style'
|
9
9
|
|
10
|
+
module XRVG
|
10
11
|
# Render abstract class
|
11
12
|
#
|
12
13
|
# Is pretty useless for the moment
|
@@ -79,10 +80,8 @@ class SVGRender < Render
|
|
79
80
|
|
80
81
|
def svg_template #:nodoc:
|
81
82
|
return '<?xml version="1.0" standalone="no"?>
|
82
|
-
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
83
|
-
|
84
|
-
<svg width="%SIZE%" height="%SIZE%" %VIEWBOX% version="1.1"
|
85
|
-
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
83
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
84
|
+
<svg width="%SIZE%" height="%SIZE%" %VIEWBOX% version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
86
85
|
%DEFS%
|
87
86
|
%BACKGROUND%
|
88
87
|
%CONTENT%
|
@@ -110,10 +109,6 @@ class SVGRender < Render
|
|
110
109
|
refresh_viewbox( object )
|
111
110
|
end
|
112
111
|
|
113
|
-
def adds (objects) #:nodoc:
|
114
|
-
objects.each { |object| add( object )}
|
115
|
-
end
|
116
|
-
|
117
112
|
def render (object, style=nil) #:nodoc:
|
118
113
|
owidth, oheight = object.size
|
119
114
|
|
@@ -268,5 +263,5 @@ class SVGRender < Render
|
|
268
263
|
# Kernel.system( "ruby", "svg2png.rb", filename(), "2.0" )
|
269
264
|
# Kernel.system( "i_view32", filename().subreplace( ".svg" => ".png" ), "/fs" )
|
270
265
|
end
|
271
|
-
|
266
|
+
end
|
272
267
|
end
|
data/lib/samplation.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
# The base module is FloatFunctor, used by Samplable and Splittable
|
4
4
|
#
|
5
5
|
|
6
|
+
module XRVG
|
6
7
|
#
|
7
8
|
# Base module to define float lists processing and computation.
|
8
9
|
# = Design principle
|
@@ -26,11 +27,9 @@ module FloatFunctor
|
|
26
27
|
#
|
27
28
|
# is private ?
|
28
29
|
def addfilter( newfilter )
|
29
|
-
# Trace("Sampler addfilter method self #{self.inspect}")
|
30
30
|
if not @subfilter
|
31
31
|
@subfilter = newfilter
|
32
32
|
else
|
33
|
-
# Trace("Sampler addfilter recurse on subfilter #{@subfilter.inspect}")
|
34
33
|
@subfilter.addfilter( newfilter )
|
35
34
|
end
|
36
35
|
return self
|
@@ -52,15 +51,13 @@ module FloatFunctor
|
|
52
51
|
|
53
52
|
# hook for Array
|
54
53
|
def compute( indata, type, &block )
|
55
|
-
return self.apply( self.modify( indata ), type, &block )
|
54
|
+
return self.apply( self.modify( indata, type ), type, &block )
|
56
55
|
end
|
57
56
|
|
58
57
|
# hook for rand()
|
59
58
|
def process( indata, type, &block )
|
60
59
|
if not block
|
61
|
-
|
62
|
-
# Trace("Samplable#trigger object #{self.inspect} indata #{indata.inspect} outdata #{outdata.inspect}")
|
63
|
-
return outdata
|
60
|
+
return self.compute( indata, type )
|
64
61
|
else
|
65
62
|
self.compute( indata, type, &block )
|
66
63
|
end
|
@@ -69,18 +66,16 @@ module FloatFunctor
|
|
69
66
|
# recursive method to compose modifications.
|
70
67
|
#
|
71
68
|
# must not be overloaded
|
72
|
-
def modify( inputs )
|
73
|
-
# Trace("Samplable#modify object #{self.inspect} inputs #{inputs.inspect}")
|
69
|
+
def modify( inputs, type )
|
74
70
|
if @subfilter
|
75
|
-
inputs = @subfilter.modify( inputs )
|
71
|
+
inputs = @subfilter.modify( inputs, type )
|
76
72
|
end
|
77
|
-
result = self.transforms( inputs )
|
78
|
-
# Trace("Samplable#filter object #{self.inspect} inputs #{inputs.inspect} result #{result.inspect}")
|
73
|
+
result = self.transforms( inputs, type )
|
79
74
|
return result
|
80
75
|
end
|
81
76
|
|
82
77
|
# to be overloaded if needed
|
83
|
-
def transforms( inputs )
|
78
|
+
def transforms( inputs, type )
|
84
79
|
return inputs.map {|abs| self.transform( abs )}
|
85
80
|
end
|
86
81
|
|
@@ -91,7 +86,6 @@ module FloatFunctor
|
|
91
86
|
|
92
87
|
# default generator method
|
93
88
|
def generate( nsamples )
|
94
|
-
# Trace("Samplable#generate object #{self.inspect} nsamples #{nsamples}")
|
95
89
|
return (0.0..1.0).generate( nsamples )
|
96
90
|
end
|
97
91
|
|
@@ -114,7 +108,7 @@ module FloatFunctor
|
|
114
108
|
def apply( data, type, &block )
|
115
109
|
@applyhash = self.applyhash
|
116
110
|
if not @applyhash.key? type
|
117
|
-
Kernel::
|
111
|
+
Kernel::raise("FloatFunctor::apply no regsitration for type #{type} and object #{self.inspect}")
|
118
112
|
else
|
119
113
|
return self.send(@applyhash[type], data, &block )
|
120
114
|
end
|
@@ -139,60 +133,15 @@ module FloatFunctor
|
|
139
133
|
return self.addfilter( SortFilter.new )
|
140
134
|
end
|
141
135
|
|
136
|
+
# alternate filter
|
137
|
+
def alternate()
|
138
|
+
return self.addfilter( AlternateFilter.new )
|
139
|
+
end
|
140
|
+
|
142
141
|
# shortcut method to build a sampler from self and a block
|
143
142
|
def filter(samplemethod=:sample,&block)
|
144
143
|
return Filter.new( self, samplemethod, &block )
|
145
144
|
end
|
146
|
-
|
147
|
-
|
148
|
-
# -------------------------------------------------------------
|
149
|
-
# old methods to be refactored
|
150
|
-
# -------------------------------------------------------------
|
151
|
-
|
152
|
-
# deprecated
|
153
|
-
#
|
154
|
-
# ratios sum must be equal to 1.0
|
155
|
-
def multisamples( nsamples, ratios )
|
156
|
-
ratiosum = ratios.sum
|
157
|
-
samplesum = nsamples.sum
|
158
|
-
ratios = ratios.map {|ratio| ratio / ratiosum}
|
159
|
-
|
160
|
-
rratios = ratios
|
161
|
-
index = 0
|
162
|
-
ratios.each do |ratio|
|
163
|
-
rratios[index] = ratio / nsamples[index]
|
164
|
-
index += 1
|
165
|
-
end
|
166
|
-
|
167
|
-
sum = 0.0
|
168
|
-
samples = [0.0]
|
169
|
-
periodindex = 0
|
170
|
-
while sum <= 1.0
|
171
|
-
sum += rratios[ periodindex ]
|
172
|
-
if sum > 1.0
|
173
|
-
break
|
174
|
-
end
|
175
|
-
samples += [sum]
|
176
|
-
periodindex += 1
|
177
|
-
if periodindex >= rratios.length
|
178
|
-
periodindex = 0.0
|
179
|
-
end
|
180
|
-
end
|
181
|
-
return self.samples( samples )
|
182
|
-
end
|
183
|
-
|
184
|
-
# deprecated
|
185
|
-
#
|
186
|
-
# TODO : must add gauss parameters
|
187
|
-
def randgauss()
|
188
|
-
begin
|
189
|
-
x1 = 2.0 * Kernel::rand - 1.0
|
190
|
-
x2 = 2.0 * Kernel::rand - 1.0
|
191
|
-
w = x1 * x1 + x2 * x2
|
192
|
-
end while w >= 1.0
|
193
|
-
w = Math.sqrt( ( -2.0 * Math.log( w ) ) / w )
|
194
|
-
return 1.0/2.0 * ( 1.0 + x1 * w )
|
195
|
-
end
|
196
145
|
|
197
146
|
end
|
198
147
|
|
@@ -229,7 +178,8 @@ module Samplable
|
|
229
178
|
#
|
230
179
|
# is basically .samples([abs]).pop
|
231
180
|
def sample( abs )
|
232
|
-
|
181
|
+
type = :sample
|
182
|
+
return self.apply( self.modify( [abs], type ), type ).pop
|
233
183
|
end
|
234
184
|
|
235
185
|
# alias for sample( 0.5 )
|
@@ -255,8 +205,10 @@ module Samplable
|
|
255
205
|
if not block
|
256
206
|
return inputs.map {|abs| self.apply_sample( abs ) }
|
257
207
|
else
|
258
|
-
|
259
|
-
|
208
|
+
if inputs.length > 0
|
209
|
+
container = self.apply_sample( inputs[0] ); yield container;
|
210
|
+
inputs[1..-1].each {|abs| yield self.apply_sample( abs, container ) }
|
211
|
+
end
|
260
212
|
end
|
261
213
|
end
|
262
214
|
|
@@ -265,6 +217,19 @@ module Samplable
|
|
265
217
|
return abs
|
266
218
|
end
|
267
219
|
|
220
|
+
# method to transform any object into samplable object
|
221
|
+
#
|
222
|
+
# used to add Attribute type :samplable
|
223
|
+
def Samplable.build( value )
|
224
|
+
if value.is_a? Array
|
225
|
+
return Roller[*value]
|
226
|
+
elsif value.is_a? Samplable
|
227
|
+
return value
|
228
|
+
else
|
229
|
+
return Roller[value]
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
268
233
|
# apply_register( :sample, :apply_samples )
|
269
234
|
|
270
235
|
end
|
@@ -290,11 +255,11 @@ module Splittable
|
|
290
255
|
if nsamples.is_a? Integer
|
291
256
|
nsamples += 1
|
292
257
|
if nsamples < 2
|
293
|
-
Kernel::
|
258
|
+
Kernel::raise("Samplable#split method needs at least two data, instead of #{nsamples}")
|
294
259
|
end
|
295
260
|
else
|
296
261
|
if nsamples.size < 2
|
297
|
-
Kernel::
|
262
|
+
Kernel::raise("Samplable#split method needs at least two data, instead of #{nsamples.inspect}")
|
298
263
|
end
|
299
264
|
end
|
300
265
|
return self.trigger( nsamples, :split, &block )
|
@@ -302,12 +267,18 @@ module Splittable
|
|
302
267
|
|
303
268
|
# must not be overriden
|
304
269
|
def split( abs1, abs2 )
|
305
|
-
|
270
|
+
type = :split
|
271
|
+
return self.apply( self.modify( [abs1,abs2], type ), type ).pop
|
306
272
|
end
|
307
273
|
|
308
274
|
# to be overloaded if needed
|
309
|
-
def apply_splits( inputs )
|
310
|
-
|
275
|
+
def apply_splits( inputs, &block )
|
276
|
+
result = inputs.pairs.map {|t1,t2| self.apply_split( t1, t2 )}
|
277
|
+
if not block
|
278
|
+
return result
|
279
|
+
else
|
280
|
+
result.each {|v| yield v}
|
281
|
+
end
|
311
282
|
end
|
312
283
|
|
313
284
|
# to be overloaded if needed
|
@@ -328,11 +299,6 @@ end
|
|
328
299
|
class Filter
|
329
300
|
|
330
301
|
include Samplable
|
331
|
-
|
332
|
-
# identity functor
|
333
|
-
def Filter.identity
|
334
|
-
return Filter.new(nil,nil) {|x| x}
|
335
|
-
end
|
336
302
|
|
337
303
|
# to define a filter on "object" with method "samplemethod", or with a block (exclusive)
|
338
304
|
#
|
@@ -362,6 +328,7 @@ class Filter
|
|
362
328
|
|
363
329
|
end
|
364
330
|
|
331
|
+
|
365
332
|
# RandomFilter class, to resample randomly
|
366
333
|
class RandomFilter < Filter
|
367
334
|
def initialize(*args) #:nodoc:
|
@@ -370,11 +337,13 @@ class RandomFilter < Filter
|
|
370
337
|
def transform(abs) #:nodoc:
|
371
338
|
return (0.0..1.0).rand
|
372
339
|
end
|
373
|
-
|
374
|
-
def transforms( inputs ) #:nodoc:
|
340
|
+
|
341
|
+
def transforms( inputs, type ) #:nodoc:
|
375
342
|
result = inputs.map { |v| self.transform( v ) }
|
376
|
-
|
377
|
-
|
343
|
+
if type == :split
|
344
|
+
result[0] = 0.0
|
345
|
+
result[-1] = 1.0
|
346
|
+
end
|
378
347
|
return result
|
379
348
|
end
|
380
349
|
end
|
@@ -384,12 +353,29 @@ class SortFilter < Filter
|
|
384
353
|
def initialize(*args) #:nodoc:
|
385
354
|
end
|
386
355
|
|
387
|
-
def transforms( inputs ) #:nodoc:
|
356
|
+
def transforms( inputs, type ) #:nodoc:
|
388
357
|
return inputs.sort
|
389
358
|
end
|
390
359
|
|
391
360
|
end
|
392
361
|
|
362
|
+
# AlternateFilter, to inverse one value per two
|
363
|
+
class AlternateFilter < Filter
|
364
|
+
def initialize(range=nil) #:nodoc:
|
365
|
+
if range
|
366
|
+
self.addfilter( range )
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def transforms( inputs, type ) #:nodoc:
|
371
|
+
result = []
|
372
|
+
inputs.foreach do |v1, v2|
|
373
|
+
result += [v1, -v2]
|
374
|
+
end
|
375
|
+
return result
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
393
379
|
# Experimental class to add discrete processing in float processing chains
|
394
380
|
# Roller["white","black"].samples(3) => ["white","black","white"]
|
395
381
|
class Roller
|
@@ -418,5 +404,8 @@ class Roller
|
|
418
404
|
index = (0.0..@items.size).rand.to_i
|
419
405
|
return @items[index]
|
420
406
|
end
|
407
|
+
end
|
421
408
|
|
409
|
+
require 'attributable'
|
410
|
+
Attribute.addtype( :samplable, Samplable.method("build") )
|
422
411
|
end
|
data/lib/shape.rb
CHANGED
@@ -9,6 +9,7 @@ require 'geometry2D'
|
|
9
9
|
require 'utils'
|
10
10
|
require 'attributable'
|
11
11
|
|
12
|
+
module XRVG
|
12
13
|
# Shape abstract interface
|
13
14
|
# = Intro
|
14
15
|
# To provide a set of services a shape class must provide
|
@@ -21,7 +22,7 @@ class Shape
|
|
21
22
|
#
|
22
23
|
# not yet used
|
23
24
|
def contour( *args )
|
24
|
-
|
25
|
+
raise NotImplementedError.new("#{self.class.name}#contour is an abstract method.")
|
25
26
|
end
|
26
27
|
|
27
28
|
# must return the svg description of the shape
|
@@ -30,7 +31,7 @@ class Shape
|
|
30
31
|
#
|
31
32
|
# must be defined
|
32
33
|
def svg()
|
33
|
-
|
34
|
+
raise NotImplementedError.new("#{self.class.name}#svg is an abstract method.")
|
34
35
|
end
|
35
36
|
|
36
37
|
# must return the enclosing box of the shape, that is [xmin, ymin, xmax, ymax]
|
@@ -39,7 +40,7 @@ class Shape
|
|
39
40
|
#
|
40
41
|
# must be defined
|
41
42
|
def viewbox()
|
42
|
-
|
43
|
+
raise NotImplementedError.new("#{self.class.name}#viewbox is an abstract method.")
|
43
44
|
end
|
44
45
|
|
45
46
|
# compute size of the shape, from viewbox
|
@@ -48,9 +49,9 @@ class Shape
|
|
48
49
|
return [xmax-xmin, ymax-ymin]
|
49
50
|
end
|
50
51
|
|
51
|
-
#
|
52
|
+
# return the default style for a Shape instance
|
52
53
|
#
|
53
|
-
#
|
54
|
+
# is done on instance, because for Curve for example, strokewidth is proportional to length
|
54
55
|
def default_style()
|
55
56
|
return Style[:fill, Color.black ]
|
56
57
|
end
|
@@ -67,7 +68,7 @@ end
|
|
67
68
|
|
68
69
|
# Curve abstract interface
|
69
70
|
# = Intro
|
70
|
-
# To
|
71
|
+
# To define a set of services a curve class must provide
|
71
72
|
class Curve < Shape
|
72
73
|
# must compute the point at curve abscissa
|
73
74
|
#
|
@@ -75,7 +76,7 @@ class Curve < Shape
|
|
75
76
|
#
|
76
77
|
# must be defined
|
77
78
|
def point( abscissa, container=nil )
|
78
|
-
|
79
|
+
raise NotImplementedError.new("#{self.class.name}#curve is an abstract method.")
|
79
80
|
end
|
80
81
|
|
81
82
|
# must compute the tangent at curve abscissa
|
@@ -84,7 +85,7 @@ class Curve < Shape
|
|
84
85
|
#
|
85
86
|
# must be defined
|
86
87
|
def tangent( abscissa, container=nil )
|
87
|
-
|
88
|
+
raise NotImplementedError.new("#{self.class.name}#tangent is an abstract method.")
|
88
89
|
end
|
89
90
|
|
90
91
|
# must compute the acceleration at curve abscissa
|
@@ -93,7 +94,7 @@ class Curve < Shape
|
|
93
94
|
#
|
94
95
|
# must be defined
|
95
96
|
def acc( abscissa, container=nil )
|
96
|
-
|
97
|
+
raise NotImplementedError.new("#{self.class.name}#acc is an abstract method.")
|
97
98
|
end
|
98
99
|
|
99
100
|
# must return the length at abscissa, or total length if abscissa nil
|
@@ -102,7 +103,7 @@ class Curve < Shape
|
|
102
103
|
#
|
103
104
|
# must be defined
|
104
105
|
def length(abscissa=nil)
|
105
|
-
|
106
|
+
raise NotImplementedError.new("#{self.class.name}#length is an abstract method.")
|
106
107
|
end
|
107
108
|
|
108
109
|
# default style of a curve, as stroked with stroke width 1% of length
|
@@ -127,7 +128,7 @@ class Curve < Shape
|
|
127
128
|
end
|
128
129
|
result = 0.0
|
129
130
|
if not self.tangent0_length == 0.0
|
130
|
-
result = (tangent.r
|
131
|
+
result = (tangent.r / self.tangent0_length)
|
131
132
|
end
|
132
133
|
return result
|
133
134
|
end
|
@@ -157,7 +158,7 @@ class Curve < Shape
|
|
157
158
|
|
158
159
|
# compute frame vector at abscissa t, that is [curve.point( t ), curve.tangent( t ) ]
|
159
160
|
def framev( t )
|
160
|
-
return [
|
161
|
+
return [self.point( t ), self.tangent( t ) ]
|
161
162
|
end
|
162
163
|
|
163
164
|
# compute frame at abscissa t
|
@@ -212,6 +213,7 @@ class Curve < Shape
|
|
212
213
|
|
213
214
|
end
|
214
215
|
|
216
|
+
|
215
217
|
# Line class
|
216
218
|
# = Intro
|
217
219
|
# Used to draw polylines and polygons
|
@@ -264,11 +266,13 @@ class Line < Curve
|
|
264
266
|
return container
|
265
267
|
end
|
266
268
|
|
267
|
-
#
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
269
|
+
# redefining to discriminate between @points and map.point
|
270
|
+
def points(arg=nil)
|
271
|
+
if not arg
|
272
|
+
return @points
|
273
|
+
else
|
274
|
+
super(arg)
|
275
|
+
end
|
272
276
|
end
|
273
277
|
|
274
278
|
# compute line tangent at abscissa
|
@@ -278,6 +282,13 @@ class Line < Curve
|
|
278
282
|
return container
|
279
283
|
end
|
280
284
|
|
285
|
+
# acc V2D.O
|
286
|
+
def acc( abscissa, container=nil )
|
287
|
+
container ||= V2D[]
|
288
|
+
container.xy = [0.0,0.0]
|
289
|
+
return container
|
290
|
+
end
|
291
|
+
|
281
292
|
# compute viewbox of the line
|
282
293
|
#
|
283
294
|
# simply call V2D.viewbox on :points
|
@@ -289,7 +300,7 @@ class Line < Curve
|
|
289
300
|
#
|
290
301
|
# return a new line with every point of :points translated
|
291
302
|
def translate( v )
|
292
|
-
return Line[ :points, @points.map {|ext| ext
|
303
|
+
return Line[ :points, @points.map {|ext| ext + v } ]
|
293
304
|
end
|
294
305
|
|
295
306
|
# reverse a line
|
@@ -365,7 +376,7 @@ class Circle < Curve
|
|
365
376
|
end
|
366
377
|
|
367
378
|
def rotate( angle )
|
368
|
-
|
379
|
+
return Circle[:center, self.center, :radius, self.radius, :initangle, self.initangle + angle]
|
369
380
|
end
|
370
381
|
|
371
382
|
# svg description of the circle
|
@@ -386,14 +397,25 @@ class Circle < Curve
|
|
386
397
|
end
|
387
398
|
|
388
399
|
# compute tangent at abscissa
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
400
|
+
def tangent( abscissa, container=nil )
|
401
|
+
angle = Range::Angle.sample( abscissa ) + @initangle
|
402
|
+
container ||=V2D[]
|
403
|
+
container.x = -self.radius * Math.sin( angle )
|
404
|
+
container.y = self.radius * Math.cos( angle )
|
405
|
+
return container
|
406
|
+
end
|
407
|
+
|
408
|
+
# compute acc at abscissa
|
409
|
+
def acc( abscissa, container=nil )
|
410
|
+
angle = Range::Angle.sample( abscissa ) + @initangle
|
411
|
+
container ||=V2D[]
|
412
|
+
container.x = -self.radius * Math.cos( angle )
|
413
|
+
container.y = -self.radius * Math.sin( angle )
|
414
|
+
return container
|
393
415
|
end
|
394
416
|
|
417
|
+
|
395
418
|
include Samplable
|
396
419
|
alias apply_sample point
|
397
|
-
|
398
420
|
end
|
399
|
-
|
421
|
+
end
|