xrvg 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/lib/fitting.rb DELETED
@@ -1,203 +0,0 @@
1
- #
2
- # Fitting file. See +Fitting+
3
- #
4
-
5
- require 'matrix'; # for matrix inversion
6
- require 'bezier.rb'; # for error computation
7
-
8
- module XRVG
9
- #
10
- # = Fitting computation class
11
- # == Intro
12
- # Used to compute cubic curve fitting on a list of points (that is sampling inverse operation). Only 2D.
13
- # == Example
14
- # Compute the most fitting single piece bezier curve given list of points
15
- # bezier = Fitting.compute( points )
16
- # Compute multipieces bezier curve given list of points
17
- # bezier = Fitting.adaptative_compute( points )
18
- class Fitting
19
-
20
- # compute first parameter t estimated values from length between consecutive points
21
- def Fitting.initparameters( pointlist, parameters=nil ) #:nodoc:
22
- lengths = [0.0]
23
- pointlist.pairs do |p1, p2|
24
- lengths.push( lengths[-1] + (p1-p2).r )
25
- end
26
- tlength = lengths[-1]
27
- if not parameters
28
- if not tlength == 0.0
29
- parameters = lengths.map {|length| length / tlength}
30
- else
31
- parameters = [0.0] * pointlist.length
32
- end
33
- end
34
- return [parameters, tlength]
35
- end
36
-
37
- # compute control points from polynomial bezier representation
38
- #
39
- # a, b, c, d are such as
40
- # piece( t ) = at3 + bt2 + ct + d
41
- def Fitting.bezierpiece( a, b, c, d )
42
- p0 = d
43
- p1 = p0 + c / 3.0
44
- p2 = p1 + c / 3.0 + b / 3.0
45
- p3 = p0 + c + b + a
46
- return [p0, p1, p2, p3]
47
- end
48
-
49
-
50
- # Base method
51
- #
52
- # Given a pointlist, compute the closest matching cubic bezier curve
53
- #
54
- # Result is in the form [p1, pc1, pc2, p2], with [p1, pc1, pc2, p2] V2D
55
- #
56
- # maxerror is normalized with curve length. In case of good match (that is pointlist can be modelized by cubic bezier curve),
57
- # result error will be under maxerror. If not, result may be above maxerror. In that case, computation is stopped because error
58
- # no longer decrease, or because iteration is too long.
59
- def Fitting.compute( pointlist, maxerror=0.01, maxiter=100 )
60
- parameters, tlength = Fitting.initparameters( pointlist )
61
- perror = 1.0
62
- niter = 0
63
- while true
64
- bezier, coeffs = Fitting.iterate( pointlist, parameters )
65
- error = Fitting.error( bezier, pointlist, parameters ) / tlength
66
- parameters = Fitting.renormalize( bezier, coeffs, pointlist, parameters )
67
- if (error < maxerror || (error-perror).abs < 0.00001 || niter > maxiter )
68
- break
69
- end
70
- niter += 1
71
- end
72
- return bezier
73
- end
74
-
75
- # adaptative computation with automatic splitting if error not low enough, or if convergence is not fast enough
76
- #
77
- #
78
- def Fitting.adaptative_compute( pointlist, maxerror=0.0001, maxiter=10, tlength=nil )
79
- parameters, tlengthtmp = Fitting.initparameters( pointlist )
80
- if parameters == [0] * pointlist.length
81
- return [Bezier.single(:vector, pointlist[0], V2D::O, pointlist[0], V2D::O),0.0]
82
- end
83
- tlength ||= tlengthtmp
84
- niter = 0
85
- bezier = nil
86
- while true
87
- bezier, coeffs = Fitting.iterate( pointlist, parameters )
88
- error = Fitting.error( bezier, pointlist, parameters ) / tlength
89
- parameters = Fitting.renormalize( bezier, coeffs, pointlist, parameters )
90
-
91
- # pointlist.length > 8 because matching with a bezier needs at least 4 points
92
- if (niter > maxiter and error > maxerror and pointlist.length > 8)
93
- pointlists = [pointlist[0..pointlist.length/2 - 1], pointlist[pointlist.length/2 - 1 ..-1]]
94
- beziers = []
95
- errors = []
96
- pointlists.each do |subpointlist|
97
- subbezier, suberror = Fitting.adaptative_compute( subpointlist, maxerror, maxiter, tlength )
98
- beziers << subbezier
99
- errors << suberror
100
- end
101
- bezier = beziers.sum
102
- error = errors.max
103
- break
104
- elsif (error < maxerror || niter > maxiter)
105
- break
106
- end
107
- perror = error
108
- niter += 1
109
- end
110
- return [bezier, error]
111
- end
112
-
113
- # algo comes from http://www.tinaja.com/glib/bezdist.pdf
114
- def Fitting.renormalize( bezier, coeffs, pointlist, parameters )
115
- a3, a2, a1, a0 = coeffs
116
- dxdu = Proc.new {|u| 3.0*a3.x*u**2 + 2.0*a2.x*u + a1.x}
117
- dydu = Proc.new {|u| 3.0*a3.y*u**2 + 2.0*a2.y*u + a1.y}
118
- container = V2D[]
119
- z = Proc.new {|u,p4| p = bezier.point( u, container, :parameter ); (p.x - p4.x) * dxdu.call( u ) + (p.y - p4.y) * dydu.call( u )}
120
- newparameters = []
121
- [pointlist, parameters].forzip do |point, parameter|
122
- u1 = parameter
123
- if parameter < 0.99
124
- u2 = parameter + 0.01
125
- else
126
- u2 = parameter - 0.01
127
- end
128
- z1 = z.call(u1,point)
129
- z2 = z.call(u2,point)
130
- if z1 == z2
131
- u2 += 0.01
132
- z2 = z.call(u2,point)
133
- end
134
- if z1 == z2
135
- u2 -= 0.01
136
- z2 = z.call(u2,point)
137
- end
138
- newparameters << (z2 * u1 - z1 * u2)/(z2-z1)
139
- end
140
- return newparameters
141
- end
142
-
143
- # error is max error between points in pointlist and points sampled from bezier with parameters
144
- def Fitting.error( bezier, pointlist, parameters )
145
- maxerror = 0.0
146
- container = V2D[]
147
- [pointlist, parameters].forzip do |point, parameter|
148
- # Trace("point #{point.inspect} parameter #{parameter}")
149
- error = (point - bezier.point( parameter, container, :parameter )).r
150
- if error > maxerror
151
- maxerror = error
152
- end
153
- end
154
- # Trace("Fitting.error #{maxerror}")
155
- return maxerror
156
- end
157
-
158
- # iterate method compute new bezier parameters from pointlist and previous bezier parameters
159
- #
160
- # Algo comes from http://www.tinaja.com/glib/bezdist.pdf
161
- #
162
- # TODO : optimized
163
- def Fitting.iterate( pointlist, parameters )
164
- p0 = pointlist[0]
165
- p1 = pointlist[-1]
166
-
167
- sumt0 = parameters.map{ |t| t**0.0 }.sum
168
- sumt1 = parameters.map{ |t| t**1.0 }.sum
169
- sumt2 = parameters.map{ |t| t**2.0 }.sum
170
- sumt3 = parameters.map{ |t| t**3.0 }.sum
171
- sumt4 = parameters.map{ |t| t**4.0 }.sum
172
- sumt5 = parameters.map{ |t| t**5.0 }.sum
173
- sumt6 = parameters.map{ |t| t**6.0 }.sum
174
-
175
- psumt1 = [pointlist, parameters].forzip.foreach(2).map {|point, t| point * (t**1.0) }.inject(V2D::O){|sum, item| sum + item}
176
- psumt2 = [pointlist, parameters].forzip.foreach(2).map {|point, t| point * (t**2.0) }.inject(V2D::O){|sum, item| sum + item}
177
- psumt3 = [pointlist, parameters].forzip.foreach(2).map {|point, t| point * (t**3.0) }.inject(V2D::O){|sum, item| sum + item}
178
-
179
- coeff11 = sumt6 - 2 * sumt4 + sumt2
180
- coeff12 = sumt5 - sumt4 - sumt3 + sumt2
181
-
182
- coeff21 = coeff12
183
- coeff22 = sumt4 - 2 * sumt3 + sumt2
184
-
185
- result1 = (p0 - p1) * (sumt4 - sumt2) - p0 * (sumt3 - sumt1) + psumt3 - psumt1
186
- result2 = (p0 - p1) * (sumt3 - sumt2) - p0 * (sumt2 - sumt1) + psumt2 - psumt1
187
-
188
- matrix = Matrix[ [coeff11, coeff12], [coeff21, coeff22] ]
189
- matrixinv = matrix.inverse
190
- ax, bx = (matrixinv * Vector[result1.x, result2.x])[0..-1]
191
- ay, by = (matrixinv * Vector[result1.y, result2.y])[0..-1]
192
-
193
- a = V2D[ax, ay]
194
- b = V2D[bx, by]
195
- d = p0
196
- c = p1- (a + b + p0)
197
-
198
- piece = Fitting.bezierpiece( a, b, c, d )
199
- return [Bezier.raw( *piece ), [a, b, c, d] ]
200
- end
201
- end
202
-
203
- end # end XRVG
data/lib/frame.rb DELETED
@@ -1,33 +0,0 @@
1
- # frame.rb file
2
- #
3
- # See +Frame+
4
- require 'attributable'
5
-
6
- module XRVG
7
- #
8
- # Frame class
9
- # = Intro
10
- # Defines a local geometry. Used by +Curve+ interface.
11
- # = Attributes
12
- # attribute :center
13
- # attribute :vector
14
- # attribute :rotation
15
- # attribute :scale
16
- class Frame
17
- include Attributable
18
- attribute :center
19
- attribute :vector
20
- attribute :rotation
21
- attribute :scale
22
-
23
- def ==(other)
24
- if self.center == other.center and
25
- self.vector == other.vector and
26
- self.rotation == other.rotation and
27
- self.scale == other.scale
28
- return true
29
- end
30
- return false
31
- end
32
- end
33
- end
data/lib/geovariety.rb DELETED
@@ -1,128 +0,0 @@
1
- # File for GeoVariety
2
- # See (also):
3
- # - InterBezier
4
- # - Offsetvariety
5
- # - FuseauVariety
6
-
7
- require 'interbezier'
8
-
9
- module XRVG
10
-
11
- # = GeoVariety abstract module
12
- # == Principle
13
- # Base module to define geometrical spaces or canvas different from simple euclidean one to draw curves on.
14
- # It provides three different services:
15
- # - point computation
16
- # - geodesic computation
17
- # - arbitrary bezier computation, this one by computing sampling of euclidean curve on the variety, and then fitting point
18
- # sequence with FitBezierBuilder
19
- module GeoVariety
20
-
21
- # must be overriden
22
- def point( point )
23
- raise NotImplementedError.new("#{self.class.name}#point is an abstract method.")
24
- end
25
-
26
- # must be overriden
27
- def line( x1, x2, y )
28
- raise NotImplementedError.new("#{self.class.name}#line is an abstract method.")
29
- end
30
-
31
- # see GeoVariety module description for algorithm
32
- def bezier( pointrange, bezier )
33
- bezier = bezier.similar( pointrange )
34
- points = bezier.samples( 20 )
35
- points = points.map {|point| self.point( point )}
36
- return FitBezierBuilder[ :points, points ]
37
- end
38
- end
39
-
40
- # = InterBezier GeoVariety implementation
41
- # == Principle
42
- # InterBezier defines a surface by the set of every possible curve sample from one interpolated curve to the other.
43
- # Geodesic corresponds then to one interpolated result, and point to a point of this curve
44
- class InterBezier
45
- include GeoVariety
46
-
47
- # Compute the geodesic curve by doing self.sample with y coord, and then compute point of this curve with length "x"
48
- def point( point )
49
- curve = self.sample( point.y )
50
- return curve.point( point.x )
51
- end
52
-
53
- # Compute the geodesic subcurve with y coord between x1 and x2
54
- def line( x1, x2, y )
55
- # Trace("interbezier line x1 #{x1} x2 #{x2} y #{y}")
56
- curve = self.sample( y )
57
- result = curve.apply_split( x1, x2 )
58
- # Trace("interbezier line result #{result.inspect}")
59
- return result
60
- end
61
-
62
- end
63
-
64
- # = OffsetVariety implementation
65
- # == Principle
66
- # Geovariety is defined by the set of offset curves from -ampl to +ampl
67
- # == Extension
68
- # Parameter could be a :samplable parameter : in that case, ampl will vary
69
- #
70
- # Another extension would be to parametrize range straightforwardly
71
- #
72
- # Finally, the two previous remarks must be synthetized :-)
73
- class OffsetVariety
74
- include Attributable
75
- attribute :support
76
- attribute :ampl, nil, Float
77
-
78
- include GeoVariety
79
-
80
- # builder: init static offset range with (-self.ampl..self.ampl)
81
- def initialize( *args )
82
- super( *args )
83
- @range = (-self.ampl..self.ampl)
84
- end
85
-
86
- # point computed by computing offset curve with ampl y coord mapped onto offset range, and then sampling the curve with x coord
87
- def point( point )
88
- curve = Offset[ :support, @support, :ampl, @range.sample( point.y ) ]
89
- return curve.point( point.x )
90
- end
91
-
92
- # subgeodesic computed by computing offset curve with ampl y coord
93
- def line( x1, x2, y )
94
- curve = Offset[ :support, @support, :ampl, @range.sample( y ) ]
95
- return curve.apply_split( x1, x2 )
96
- end
97
-
98
- end
99
-
100
- # = FuseauVariety implementation
101
- # == Principle
102
- # Same as OffsetVariety, with Fuseau shape, that is with linearly varying ampl range
103
- class FuseauVariety
104
- include Attributable
105
- attribute :support
106
- attribute :ampl, nil, Float
107
-
108
- include GeoVariety
109
-
110
- def initialize( *args )
111
- super( *args )
112
- @range = (-self.ampl..self.ampl)
113
- end
114
-
115
- def point( point )
116
- curve = Offset[ :support, @support, :ampl, (0.0..@range.sample( point.y ))]
117
- return curve.point( point.x )
118
- end
119
-
120
- def line( x1, x2, y )
121
- curve = Offset[ :support, @support, :ampl, (0.0..@range.sample( y ))]
122
- return curve.apply_split( x1, x2 )
123
- end
124
- end
125
-
126
- end # XRVG
127
-
128
- # see geovariety_test to see tests
data/lib/interbezier.rb DELETED
@@ -1,87 +0,0 @@
1
- require 'bezier'
2
-
3
- module XRVG
4
- class InterBezier
5
- include Attributable
6
- attribute :bezierlist
7
-
8
- include Interpolation
9
-
10
- def initialize( *args )
11
- super( *args )
12
- self.init_interpolation_structures
13
- end
14
-
15
- def init_interpolation_structures
16
- beziers = []
17
- indexes = []
18
- @bezierlist.foreach do |index, bezier|
19
- beziers.push( bezier )
20
- indexes.push( index )
21
- end
22
-
23
- lengthH = {}
24
- alllengths = []
25
- beziers.each do |bezier|
26
- lengths = bezier.piecelengths
27
- Trace("bezier lengths #{lengths.inspect}")
28
- lengthH[ bezier ] = lengths
29
- alllengths += lengths
30
- end
31
- alllengths = Float.sort_float_list( alllengths )
32
- Trace("alllengths #{alllengths.inspect}")
33
-
34
- newbezierlist = []
35
- beziers.each do |bezier|
36
- newpieces = []
37
- initlengths = lengthH[ bezier ]
38
- alllengths.pairs do |l1, l2|
39
- newpieces += bezier.subbezier( l1, l2 ).pieces
40
- end
41
- newbezier = Bezier[ :pieces, newpieces ]
42
- newbezierlist << newbezier
43
- end
44
-
45
- Trace("newbezierlist #{newbezierlist.length}")
46
- beziers = newbezierlist
47
- bezierpointlists = beziers.map {|bezier| bezier.pointlist(:vector) }
48
- Trace("bezierpointlists #{bezierpointlists.map {|list| list.length}.inspect}")
49
- pointsequencelist = bezierpointlists.forzip
50
- @interpolatorlist = []
51
- pointsequencelist.foreach(beziers.size) do |pointsequence|
52
- interlist = [indexes, pointsequence].forzip
53
- @interpolatorlist.push( Interpolator.new( :samplelist, interlist ) )
54
- end
55
- end
56
-
57
- def interpolate( abs, container=nil )
58
- pieces = []
59
- @interpolatorlist.foreach(4) do |interpiece|
60
- piece = interpiece.map {|inter| inter.interpolate( abs )}
61
- pieces.push( [:vector] + piece )
62
- end
63
- return Bezier.multi( pieces )
64
- end
65
-
66
- include Samplable
67
- alias apply_sample interpolate
68
-
69
- end
70
-
71
- class GradientBezier < InterBezier
72
-
73
- # TODO : does not work !!
74
- def samples( nsamples, &block )
75
- return super( nsamples + 1, &block )
76
- end
77
-
78
- def apply_samples( samples )
79
- samples = super( samples )
80
- result = []
81
- samples.pairs do |bezier1, bezier2|
82
- result.push( ClosureBezier.build( :bezierlist, [bezier1, bezier2.reverse]) )
83
- end
84
- return result
85
- end
86
- end
87
- end # XRVG
data/lib/render.rb DELETED
@@ -1,266 +0,0 @@
1
- #
2
- # Render file. See
3
- # - +Render+ for render "abstraction"
4
- # - +SVGRender+ for effective SVG render
5
-
6
- require 'color'
7
- require 'style'
8
-
9
- module XRVG
10
- # Render abstract class
11
- #
12
- # Is pretty useless for the moment
13
- class Render
14
- include Attributable
15
- attr_accessor :width
16
- attr_accessor :height
17
- end
18
-
19
- # SVG Render class
20
- #
21
- # In charge of generating a svg output file from different object passed to it
22
- # = Use
23
- # Canonical use of the class
24
- # render = SVGRender[ :filename, "essai.svg" ]
25
- # render.add( Circle[] )
26
- # render.end
27
- # = Improvements
28
- # Allows also the "with" syntax
29
- # = Attributes
30
- # attribute :filename, "", String
31
- # attribute :imagesize, "2cm", String
32
- # attribute :background, Color.white, [Color, String]
33
- class SVGRender < Render
34
- attribute :filename, "", String
35
- attribute :imagesize, "2cm", String
36
- attribute :background, "white", [Color, String]
37
- attr_reader :viewbox
38
-
39
- # SVGRender builder
40
- #
41
- # Allows to pass a block, to avoid using .end
42
- # SVGRender.[] do |render|
43
- # render.add( Circle[] )
44
- # end
45
- def SVGRender.[](*args,&block)
46
- result = self.new( *args )
47
- if block
48
- yield result
49
- result.end
50
- end
51
- return result
52
- end
53
-
54
-
55
- def initialize ( *args, &block ) #:nodoc:
56
- super( *args )
57
- @layers = {}
58
- @defs = ""
59
- @ngradients = 0
60
- if @filename.length == 0
61
- @filename = $0.split(".")[0..-2].join(".") + ".svg"
62
- Trace("filename is #{filename}")
63
- end
64
- end
65
-
66
- def layers=( backtofront ) #:nodoc:
67
- @sortlayers = backtofront
68
- @sortlayers.each do |key|
69
- @layers[ key ] = ""
70
- end
71
- end
72
-
73
- def add_content (string, layer) #:nodoc:
74
- if not @layers.key? layer
75
- @layers[ layer ] = ""
76
- end
77
- @layers[ layer ] += string
78
- end
79
-
80
- def svg_template #:nodoc:
81
- return '<?xml version="1.0" standalone="no"?>
82
- <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
83
- <svg width="%SIZE%" height="%SIZE%" %VIEWBOX% version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
84
- %DEFS%
85
- %BACKGROUND%
86
- %CONTENT%
87
- </svg>'
88
- end
89
-
90
- def add_def (object) #:nodoc:
91
- @defs += object
92
- end
93
-
94
- def add_gradient( gradient ) #:nodoc:
95
- id = "gradient#{@ngradients}"
96
- add_def( gradient.svgdef.subreplace( {"%ID%" => id} ) )
97
- @ngradients += 1
98
- return id
99
- end
100
-
101
- # render fundamental method
102
- #
103
- # used to render an object, with a particular style, on an optional layer.
104
- # render.add( Circle[], Style[ :fill, Color.black ], 1 )
105
- # If style is not provided, render asked to object its default_style, if any
106
- def add(object, style=nil, layer=0, type=:object)
107
- add_content( render( object, style ), layer)
108
- refresh_viewbox( object )
109
- end
110
-
111
- def render (object, style=nil) #:nodoc:
112
- owidth, oheight = object.size
113
-
114
- res = 0.0000001
115
- if owidth < res and oheight < res
116
- return ""
117
- end
118
-
119
- if not style
120
- style = object.default_style
121
- end
122
-
123
- result = "<g #{style.svgline}>\n"
124
- result += object.svg + "\n"
125
- result += "</g>\n"
126
-
127
- # puts "result #{result}"
128
-
129
- if style.fill.is_a? Gradient
130
- gradientID = add_gradient( style.fill )
131
- result = result.subreplace( {"%fillgradient%" => "url(##{gradientID})"} )
132
- end
133
-
134
- if style.stroke.is_a? Gradient
135
- gradientID = add_gradient( style.stroke )
136
- result = result.subreplace( {"%strokegradient%" => "url(##{gradientID})"} )
137
- end
138
- return result
139
- end
140
-
141
- def viewbox #:nodoc:
142
- return @viewbox
143
- end
144
-
145
- def size #:nodoc:
146
- xmin, ymin, xmax, ymax = viewbox
147
- return [xmax - xmin, ymax - ymin]
148
- end
149
-
150
- def refresh_viewbox (object) #:nodoc:
151
- newviewbox = object.viewbox
152
- if newviewbox.length > 0
153
- if @viewbox == nil
154
- @viewbox = newviewbox
155
- else
156
- newxmin, newymin, newxmax, newymax = newviewbox
157
- xmin, ymin, xmax, ymax = viewbox
158
-
159
- if newxmin < xmin
160
- xmin = newxmin
161
- end
162
- if newymin < ymin
163
- ymin = newymin
164
- end
165
- if newxmax > xmax
166
- xmax = newxmax
167
- end
168
- if newymax > ymax
169
- ymax = newymax
170
- end
171
-
172
- @viewbox = [xmin, ymin, xmax, ymax]
173
- end
174
- end
175
- end
176
-
177
- def get_background_svg #:nodoc:
178
- xmin, ymin, width, height = get_carre_viewbox( get_final_viewbox() )
179
- template = '<rect x="%x%" y="%y%" width="%width%" height="%height%" fill="%fill%"/>'
180
- bg = self.background
181
- if bg.respond_to? :svg
182
- bg = bg.svg
183
- end
184
- return template.subreplace( {"%x%" => xmin,
185
- "%y%" => ymin,
186
- "%width%" => width,
187
- "%height%" => height,
188
- "%fill%" => bg} )
189
- end
190
-
191
- def get_final_viewbox #:nodoc:
192
- marginfactor = 0.2
193
- xmin, ymin, xmax, ymax = viewbox()
194
- width, height = size()
195
-
196
- xcenter = (xmin + xmax)/2.0
197
- ycenter = (ymin + ymax)/2.0
198
-
199
- width *= 1.0 + marginfactor
200
- height *= 1.0 + marginfactor
201
-
202
- if width == 0.0
203
- width = 1.0
204
- end
205
- if height == 0.0
206
- height = 1.0
207
- end
208
-
209
- xmin = xcenter - width / 2.0
210
- ymin = ycenter - height / 2.0
211
-
212
- return xmin, ymin, width, height
213
- end
214
-
215
- def get_viewbox_svg #:nodoc:
216
- return viewbox_svg( get_final_viewbox() )
217
- end
218
-
219
- def get_carre_viewbox( viewbox ) #:nodoc:
220
- xmin, ymin, width, height = viewbox
221
- xcenter = xmin + width / 2.0
222
- ycenter = ymin + height / 2.0
223
- maxsize = width < height ? height : width
224
- return [xcenter - maxsize/2.0, ycenter - maxsize/2.0, maxsize, maxsize]
225
- end
226
-
227
- def viewbox_svg( viewbox ) #:nodoc:
228
- xmin, ymin, width, height = viewbox
229
- return "viewBox=\"#{xmin} #{ymin} #{width} #{height}\""
230
- end
231
-
232
- def content #:nodoc:
233
- keys = @sortlayers ? @sortlayers : @layers.keys.sort
234
- return keys.inject("") {|result,key| result += @layers[key]}
235
- end
236
-
237
- def svgdef #:nodoc:
238
- return "<defs>\n#{@defs}\n</defs>\n"
239
- end
240
-
241
- def end () #:nodoc:
242
- svgcontent = content()
243
- svgviewbox = get_viewbox_svg()
244
- svgbackground = get_background_svg()
245
-
246
- content = svg_template().subreplace( {"%VIEWBOX%" => svgviewbox,
247
- "%SIZE%" => @imagesize,
248
- "%DEFS%" => svgdef,
249
- "%BACKGROUND%" => svgbackground,
250
- "%CONTENT%" => svgcontent})
251
-
252
- File.open(filename(), "w") do |f|
253
- f << content
254
- end
255
-
256
- puts "render #{filename()} OK"; # necessary for Emacs to get output name !!!!
257
- end
258
-
259
- def raster () #:nodoc:
260
- # bg = background.format255
261
-
262
- # Kernel.system( "ruby", "svg2png.rb", filename(), "2.0" )
263
- # Kernel.system( "i_view32", filename().subreplace( ".svg" => ".png" ), "/fs" )
264
- end
265
- end
266
- end