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.
- 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
|