xrvg 0.0.1

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/lib/shape.rb ADDED
@@ -0,0 +1,338 @@
1
+ # shape.rb file
2
+ # See
3
+ # - Shape interface
4
+ # - Curve interface
5
+ # - Line class
6
+ # - Circle class
7
+ require 'frame'
8
+ require 'geometry2D'
9
+ require 'utils'
10
+ require 'attributable'
11
+
12
+ # Shape abstract interface
13
+ # = Intro
14
+ # To provide a set of services a shape class must provide
15
+ class Shape
16
+ include Attributable
17
+
18
+ # must return the contour of the shape, of Curve type
19
+ #
20
+ # abstract
21
+ #
22
+ # not yet used
23
+ def contour( *args )
24
+ Kernel::raise("Shape::contour must be redefined in subclasses")
25
+ end
26
+
27
+ # must return the svg description of the shape
28
+ #
29
+ # abstract
30
+ #
31
+ # must be defined
32
+ def svg()
33
+ Kernel::raise("Shape::svg must be redefined in subclasses")
34
+ end
35
+
36
+ # must return the enclosing box of the shape, that is [xmin, ymin, xmax, ymax]
37
+ #
38
+ # abstract
39
+ #
40
+ # must be defined
41
+ def viewbox()
42
+ Kernel::raise("Shape::viewbox must be redefined in subclasses")
43
+ end
44
+
45
+ # compute size of the shape, from viewbox
46
+ def size()
47
+ xmin, ymin, xmax, ymax = self.viewbox
48
+ return [xmax-xmin, ymax-ymin]
49
+ end
50
+
51
+ # must return the enclosing box of the shape, that is [xmin, ymin, xmax, ymax]
52
+ #
53
+ # abstract
54
+ def default_style()
55
+ return Style[:fill, Color.black ]
56
+ end
57
+
58
+ # compute the "surface" of the viewbox of the shape
59
+ #
60
+ # use size method
61
+ def surface
62
+ width, height = self.size
63
+ return width * height
64
+ end
65
+
66
+ end
67
+
68
+ # Curve abstract interface
69
+ # = Intro
70
+ # To provide a set of services a curve class must provide
71
+ class Curve < Shape
72
+ # must compute the point at curve abscissa
73
+ #
74
+ # abstract
75
+ #
76
+ # must be defined
77
+ def point( abscissa )
78
+ Kernel::raise("Curve::point must be redefined in subclasses")
79
+ end
80
+
81
+ # must compute the tangent at curve abscissa
82
+ #
83
+ # abstract
84
+ #
85
+ # must be defined
86
+ def tangent( abscissa )
87
+ Kernel::raise("Curve::tangent must be redefined in subclasses")
88
+ end
89
+
90
+ # must compute the acceleration at curve abscissa
91
+ #
92
+ # abstract
93
+ #
94
+ # must be defined
95
+ def acc( abscissa )
96
+ Kernel::raise("Curve::acc must be redefined in subclasses")
97
+ end
98
+
99
+ # must compute the rotation at curve abscissa
100
+ #
101
+ # abstract
102
+ #
103
+ # must be defined
104
+ def rotation( abscissa )
105
+ Kernel::raise("Curve::rotation must be redefined in subclasses")
106
+ end
107
+
108
+ # must compute the scale at curve abscissa
109
+ #
110
+ # abstract
111
+ #
112
+ # must be defined
113
+ def scale( abscissa )
114
+ Kernel::raise("Curve::scale must be redefined in subclasses")
115
+ end
116
+
117
+ # must return the length at abscissa, or total length if abscissa nil
118
+ #
119
+ # abstract
120
+ #
121
+ # must be defined
122
+ def length(abscissa=nil)
123
+ Kernel::raise("Curve::length must be redefined in subclasses")
124
+ end
125
+
126
+ # default style of a curve, as stroked with stroke width 1% of length
127
+ def default_style
128
+ return Style[ :stroke, Color.black, :strokewidth, self.length / 100.0 ]
129
+ end
130
+
131
+ # compute frame at abscissa t
132
+ def frame( t )
133
+ return Frame[ :center, self.point( t ), :vector, self.tangent( t ), :rotation, self.rotation( t ), :scale, self.scale( t ) ]
134
+ end
135
+
136
+ # compute normal at abscissa t
137
+ #
138
+ # do tangent.ortho
139
+ def normal( t )
140
+ return self.tangent( t ).ortho
141
+ end
142
+
143
+ # compute normal acceleration at abscissa t
144
+ def acc_normal( t )
145
+ normal = self.normal( t ).norm
146
+ result = self.acc( t ).inner_product( normal )
147
+ return result
148
+ end
149
+
150
+ # compute curvature at abscissa t
151
+ def curvature( t )
152
+ if self.acc_normal( t ) == 0.0
153
+ return 0.0
154
+ end
155
+ return 1.0 / (self.tangent( t ).r / self.acc_normal( t ))
156
+ end
157
+
158
+ # shortcut method to map frames from abscissas
159
+ def frames (abscissas)
160
+ return abscissas.map { |abscissa| self.frame( abscissa ) }
161
+ end
162
+
163
+ # shortcut method to map points from abscissas
164
+ def points (abscissas)
165
+ result = abscissas.map { |abscissa| self.point( abscissa ) }
166
+ return result
167
+ end
168
+
169
+ # shortcut method to map tangents from abscissas
170
+ def tangents (abscissas)
171
+ return abscissas.map { |abscissa| self.tangent( abscissa ) }
172
+ end
173
+
174
+ end
175
+
176
+ # Line class
177
+ # = Intro
178
+ # Used to draw polylines and polygons
179
+ # = Attributes
180
+ # attribute :exts, [Point[0.0, 0.0], Point[1.0, 1.0]]
181
+ # = Example
182
+ # line = Line[ :exts, [Point::O, Point::X] ]
183
+ class Line < Curve
184
+ attribute :exts, [Point[0.0, 0.0], Point[1.0, 1.0]]
185
+
186
+ def initialize (*args) #:nodoc:
187
+ super( *args )
188
+ self.init_tangents
189
+ end
190
+
191
+ def init_tangents #:nodoc:
192
+ index = 0
193
+ @tangents = Array.new
194
+ self.exts.pairs { |p1, p2|
195
+ @tangents[ index ] = (p2-p1).norm
196
+ index += 1
197
+ }
198
+ end
199
+
200
+ # return the total length of the polyline
201
+ def length
202
+ if not @length
203
+ @length = 0.0
204
+ self.exts.pairs do |p1, p2|
205
+ @length += (p1 - p2).r
206
+ end
207
+ end
208
+ return @length
209
+ end
210
+
211
+ # compute line point at abscissa
212
+ # Line[ :exts, [Point::O, Point::X] ].point( 0.3 ) => Vector[0.0,0.3]
213
+ def point (abscissa)
214
+ piece1 = abscissa.to_int
215
+ if piece1 == @exts.size - 1
216
+ return @exts[-1]
217
+ end
218
+ abscissa -= piece1
219
+ cexts = self.exts.slice( piece1, 2 )
220
+ return (cexts[0]..cexts[1]).sample( abscissa )
221
+ end
222
+
223
+ # compute line frame at abscissa
224
+ #
225
+ # for the moment, frame rotation is always 0.0, and scale always 1.0
226
+ def frame (abscissa)
227
+ return Frame[ :center, self.point( abscissa ), :vector, Vector[ 0.0, 0.0 ], :rotation, 0.0, :scale, 1.0 ]
228
+ end
229
+
230
+ # compute line tangent at abscissa
231
+ def tangent (abscissa)
232
+ return @tangents[abscissa.to_int].dup
233
+ end
234
+
235
+ # compute viewbox of the line
236
+ #
237
+ # simply call Point.viewbox on :exts
238
+ def viewbox
239
+ return Point.viewbox( self.exts )
240
+ end
241
+
242
+ # translate a line of v offset, v being a vector
243
+ #
244
+ # return a new line with every point of :exts translated
245
+ def translate( v )
246
+ return Line[ :exts, @exts.map {|ext| ext.translate( v )} ]
247
+ end
248
+
249
+ # reverse a line
250
+ #
251
+ # return a new line with :exts reversed
252
+ def reverse
253
+ return Line[ :exts, @exts.reverse ]
254
+ end
255
+
256
+ # return line svg description
257
+ def svg
258
+ path = "M #{exts[0].x} #{exts[0].y} "
259
+ exts[1..-1].each { |p|
260
+ path += "L #{p.x} #{p.y}"
261
+ }
262
+ return "<path d=\"" + path + "\"/>"
263
+ end
264
+
265
+ include Samplable
266
+ alias apply_sample point
267
+ end
268
+
269
+ # Circle class
270
+ # = Intro
271
+ # define a circle curve
272
+ # = Attributes
273
+ # attribute :center, Vector[0.0,0.0]
274
+ # attribute :radius, 1.0
275
+ # = Example
276
+ # c = Circle[ :center, Point::O, :radius, 1.0 ] # equiv Circle[]
277
+ class Circle < Curve
278
+ attribute :center, Vector[0.0,0.0]
279
+ attribute :radius, 1.0
280
+
281
+ # compute length of the circle
282
+ def length
283
+ if not @length
284
+ @length = 2.0 * Math::PI * self.radius
285
+ end
286
+ return @length
287
+ end
288
+
289
+ # shortcut method to retun center.x
290
+ def cx
291
+ return self.center.x
292
+ end
293
+
294
+ # shortcut method to retun center.y
295
+ def cy
296
+ return self.center.y
297
+ end
298
+
299
+ # viewbox of the circle
300
+ def viewbox
301
+ return [ self.cx - self.radius,
302
+ self.cy - self.radius,
303
+ self.cx + self.radius,
304
+ self.cy + self.radius ]
305
+ end
306
+
307
+ # size of the circle
308
+ def size
309
+ return [ self.radius, self.radius ]
310
+ end
311
+
312
+ # svg description of the circle
313
+ def svg
314
+ template = '<circle cx="%cx%" cy="%cy%" r="%r%"/>'
315
+ return template.subreplace( {"%cx%" => cx,
316
+ "%cy%" => cy,
317
+ "%r%" => radius} )
318
+ end
319
+
320
+ # compute point at abscissa
321
+ def point (abscissa)
322
+ angle = Range::Angle.sample( abscissa )
323
+ return Point[ self.cx + self.radius * Math.cos( angle ),
324
+ self.cy + self.radius * Math.sin( angle )]
325
+ end
326
+
327
+ # compute tangent at abscissa
328
+ #
329
+ # TODO
330
+ def tangent( abscissa )
331
+ TODO
332
+ end
333
+
334
+ include Samplable
335
+ alias apply_sample point
336
+
337
+ end
338
+
data/lib/style.rb ADDED
@@ -0,0 +1,74 @@
1
+ #
2
+ # See +Style+
3
+ #
4
+ require 'attributable'
5
+ require 'utils'
6
+ require 'color'
7
+
8
+ #
9
+ # Style class
10
+ #
11
+ # Used to define the way an object has to be rendered.
12
+ # For the moment, only the following style attributes are really useful :
13
+ # - attribute :fill, "none", [String, Color, Gradient]
14
+ # - attribute :stroke, "none", [String, Color, Gradient]
15
+ # - attribute :strokewidth, 1.0
16
+ #
17
+ # For example :
18
+ # render.add( Circle[], Style[ :fill, Color.red ] )
19
+ # render.add( Circle[], Style[ :stroke, Color.red ] )
20
+ class Style
21
+ include Attributable
22
+ attribute :opacity, 1.0
23
+ attribute :fill, "none", [String, Color, Gradient]
24
+ attribute :fillopacity, 1.0
25
+ attribute :stroke, "none", [String, Color, Gradient]
26
+ attribute :strokewidth, 1.0
27
+ attribute :strokeopacity, 1.0
28
+
29
+ def fill=( color )
30
+ if color.is_a? Color
31
+ self.fillopacity = color.a
32
+ end
33
+ @fill = color
34
+ end
35
+
36
+ def stroke=( color )
37
+ if color.is_a? Color
38
+ self.strokeopacity = color.a
39
+ end
40
+ @stroke = color
41
+ end
42
+
43
+ def svgfill
44
+ if fill.is_a? Color
45
+ return fill.svg
46
+ elsif fill.is_a? Gradient
47
+ return "%fillgradient%"
48
+ else
49
+ return fill
50
+ end
51
+ end
52
+
53
+ def svgstroke
54
+ if stroke.is_a? Color
55
+ return stroke.svg
56
+ elsif stroke.is_a? Gradient
57
+ return "%strokegradient%"
58
+ else
59
+ return stroke
60
+ end
61
+ end
62
+
63
+ def svgline
64
+ template = 'style="opacity:%opacity%;fill:%fill%;fill-opacity:%fillopacity%;stroke:%stroke%;stroke-width:%strokewidth%;stroke-opacity:%strokeopacity%"'
65
+
66
+ return template.subreplace( {"%opacity%" => opacity,
67
+ "%fill%" => svgfill,
68
+ "%fillopacity%" => fillopacity,
69
+ "%stroke%" => svgstroke,
70
+ "%strokewidth%" => strokewidth,
71
+ "%strokeopacity%" => strokeopacity} )
72
+ end
73
+ end
74
+
data/lib/trace.rb ADDED
@@ -0,0 +1,28 @@
1
+ # Trace utility
2
+ #
3
+ # Consists in a Trace class, and a Trace global method
4
+
5
+ # Trace "static" class, to be able to globally inhibit or activate traces
6
+ # Trace.inihibit
7
+ # Trace.activate
8
+ class Trace
9
+ @@active = true
10
+ def Trace.active?
11
+ return @@active
12
+ end
13
+ def Trace.inhibit
14
+ @@active = nil
15
+ end
16
+ def Trace.activate
17
+ @@active = true
18
+ end
19
+ end
20
+
21
+ # Standard trace method
22
+ # Trace("hello world")
23
+ # Check before printing the string if traces are active
24
+ def Trace(string)
25
+ if Trace.active?
26
+ puts string
27
+ end
28
+ end