r2dsvg 0.4.0 → 0.5.2
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/r2dsvg.rb +128 -419
- data/lib/r2dsvg/r2dsvg_module.rb +586 -0
- metadata +9 -8
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 64d907b1e113033b25cb0f8f103f743c3994298a4bf66e14844c320dcb59c88a
|
4
|
+
data.tar.gz: 9ec11cbf12e85142093d766b56426d8cfe67c89cb57f3baf5991171514dfac61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 75fd5bd8348ae09903a12edb5a48b51bce69158b9a5d7630f52e9be2a8d1927669e7916255098aeb4eaf6260bcf39a9a7514f24176034f4dff0b36337b98e473
|
7
|
+
data.tar.gz: fdfd7077c881d6892ac2340e2bf10733ef041c3691f37e7a956e7710b09d6f082b16d5416f622dde272ec6d93b4530bff276aac901c87f397865fc83139d0c4a
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/r2dsvg.rb
CHANGED
@@ -5,487 +5,179 @@
|
|
5
5
|
# Description: Experimental gem to render SVG within a Ruby2D application.
|
6
6
|
|
7
7
|
|
8
|
-
require '
|
9
|
-
require 'onedrb'
|
10
|
-
require 'ruby2d' # experimental gem depends upon simple2d binaries
|
11
|
-
require 'dom_render'
|
8
|
+
require 'r2dsvg/r2dsvg_module'
|
12
9
|
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
rect {fill: yellow}
|
18
|
-
line, polyline {stroke: green; stroke-width: 3}
|
19
|
-
text {fill: red, size: 20}
|
20
|
-
|
21
|
-
CSS
|
22
|
-
|
23
|
-
module SvgleX
|
11
|
+
class Reader
|
12
|
+
include R2dEngine
|
13
|
+
using ColouredText
|
24
14
|
|
25
|
-
|
15
|
+
def initialize(s, model=Svgle, debug: false)
|
26
16
|
|
27
|
-
|
28
|
-
|
29
|
-
|
17
|
+
svg, _ = RXFHelper.read(s)
|
18
|
+
doc = model.new(svg, debug: debug)
|
19
|
+
@a = Render.new(doc, debug: debug).to_a
|
30
20
|
|
31
|
-
def obj()
|
32
|
-
@obj
|
33
|
-
end
|
34
21
|
|
35
22
|
end
|
36
23
|
|
24
|
+
def to_a()
|
25
|
+
@a
|
26
|
+
end
|
27
|
+
|
37
28
|
end
|
38
29
|
|
39
30
|
class R2dSvg
|
40
31
|
include Ruby2D
|
32
|
+
include R2dEngine
|
41
33
|
using ColouredText
|
42
34
|
using SvgleX
|
43
35
|
|
44
|
-
|
45
|
-
|
46
|
-
def audio(e, attributes, raw_style)
|
47
|
-
|
48
|
-
puts 'inside audio attributes: ' + attributes.inspect if @debug
|
49
|
-
style = style_filter(attributes).merge(raw_style)
|
50
|
-
h = attributes
|
51
|
-
|
52
|
-
sources = e.xpath('source').map do |x|
|
53
|
-
h = x.attributes.to_h
|
54
|
-
h.delete :style
|
55
|
-
h
|
56
|
-
end
|
57
|
-
|
58
|
-
[:embed_audio, sources, e]
|
59
|
-
end
|
60
|
-
|
61
|
-
def circle(e, attributes, raw_style)
|
62
|
-
|
63
|
-
style = style_filter(attributes).merge(raw_style)
|
64
|
-
|
65
|
-
h = attributes
|
66
|
-
|
67
|
-
x, y, radius= %i(cx cy r).map{|x| h[x].to_i }
|
68
|
-
fill = h[:fill]
|
69
|
-
|
70
|
-
|
71
|
-
[:draw_circle, [x, y], radius, fill, style, render_all(e)]
|
72
|
-
end
|
73
|
-
|
74
|
-
# not yet implemented
|
75
|
-
#
|
76
|
-
def ellipse(e, attributes, raw_style)
|
77
|
-
|
78
|
-
style = style_filter(attributes).merge(raw_style)
|
79
|
-
h = attributes
|
80
|
-
|
81
|
-
x, y= %i(cx cy).map{|x| h[x].to_i }
|
82
|
-
width = h[:rx].to_i * 2
|
83
|
-
height = h[:ry].to_i * 2
|
84
|
-
|
85
|
-
[:draw_arc, [x, y, width, height], style, render_all(e)]
|
86
|
-
end
|
87
|
-
|
88
|
-
def line(e, attributes, raw_style)
|
89
|
-
|
90
|
-
style = style_filter(attributes).merge(raw_style)
|
36
|
+
attr_reader :doc
|
91
37
|
|
92
|
-
|
38
|
+
def initialize(s, title: 'R2dSVG', debug: false, server: false)
|
93
39
|
|
94
|
-
|
95
|
-
end
|
96
|
-
|
40
|
+
@debug = debug
|
97
41
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
h = attributes
|
102
|
-
|
103
|
-
x, y, width, height = %i(x y width height).map{|x| h[x] ? h[x].to_i : nil }
|
104
|
-
src = h[:'xlink:href']
|
105
|
-
|
106
|
-
[:draw_image, [x, y, width, height], src, style, e, render_all(e)]
|
107
|
-
end
|
42
|
+
@window = window = Window.new
|
43
|
+
@loaded = false
|
44
|
+
@model = Svgle
|
108
45
|
|
109
|
-
|
110
|
-
|
111
|
-
style = style_filter(attributes).merge(raw_style)
|
112
|
-
points = attributes[:points].split(/\s+/). \
|
113
|
-
map {|x| x.split(/\s*,\s*/).map(&:to_i)}
|
46
|
+
read(s, title)
|
114
47
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
def polyline(e, attributes, raw_style)
|
119
|
-
|
120
|
-
style = style_filter(attributes).merge(raw_style)
|
121
|
-
points = attributes[:points].split(/\s+/). \
|
122
|
-
map {|x| x.split(/\s*,\s*/).map(&:to_i)}
|
123
|
-
|
124
|
-
[:draw_lines, points, style, render_all(e)]
|
125
|
-
end
|
126
|
-
|
127
|
-
def rect(e, attributes, raw_style)
|
128
|
-
|
129
|
-
puts 'inside rect attributes: ' + attributes.inspect if @debug
|
130
|
-
style = style_filter(attributes).merge(raw_style)
|
131
|
-
h = attributes
|
132
|
-
|
133
|
-
x1, y1, width, height = %i(x y width height).map{|x| h[x].to_i }
|
134
|
-
x2, y2, = x1 + width, y1 + height
|
135
|
-
|
136
|
-
[:draw_rectangle, [x1, y1, x2, y2], style, e, render_all(e)]
|
48
|
+
if server then
|
49
|
+
drb = OneDrb::Server.new(host: '127.0.0.1', port: '57844', obj: self)
|
50
|
+
Thread.new { drb.start }
|
137
51
|
end
|
138
52
|
|
139
|
-
|
140
|
-
[:script]
|
141
|
-
end
|
142
|
-
|
143
|
-
def svg(e, attributes, raw_style)
|
144
|
-
|
145
|
-
style = style_filter(attributes).merge(raw_style)
|
146
|
-
|
147
|
-
h = attributes
|
148
|
-
width, height = %i(width height).map{|x| h[x].to_i }
|
53
|
+
if @loaded then
|
149
54
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
55
|
+
sleep 0.05
|
56
|
+
|
57
|
+
window.on(:mouse_move) do |event|
|
58
|
+
mouse :mousemove, event
|
59
|
+
mouse :mouseenter, event
|
60
|
+
end
|
61
|
+
|
62
|
+
window.on(:mouse_down) do |event|
|
63
|
+
|
64
|
+
if event.button == :left then
|
65
|
+
|
66
|
+
# click and mousedown do the same thing
|
67
|
+
mouse :click, event
|
68
|
+
mouse :mousedown, event
|
69
|
+
end
|
162
70
|
|
163
|
-
|
71
|
+
end
|
164
72
|
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
attributes.has_key?(x) ? r.merge(x => attributes[x]) : r
|
73
|
+
window.on :key_down do |event|
|
74
|
+
# A key was pressed
|
75
|
+
keyboard :keydown, event
|
169
76
|
end
|
170
77
|
|
171
78
|
end
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
attr_accessor :area
|
179
|
-
|
180
|
-
|
181
|
-
def initialize(window, debug: false)
|
182
|
-
|
183
|
-
@window, @debug = window, debug
|
184
|
-
|
79
|
+
=begin
|
80
|
+
window.on :key_held do |event|
|
81
|
+
# A key is being held down
|
82
|
+
puts event.key
|
83
|
+
keyboard :onkeyheld, event
|
185
84
|
end
|
186
85
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
x, y, width, height = dimensions
|
192
|
-
|
193
|
-
#gc = gc_ini(fill: style[:fill] || :none)
|
194
|
-
#@area.window.draw_arc(gc, 1, x, y, width, height, 0, 64 * 360)
|
195
|
-
end
|
196
|
-
|
197
|
-
def draw_circle(args)
|
198
|
-
|
199
|
-
coords, radius, fill, style, e = args
|
200
|
-
|
201
|
-
x1, y1 = coords
|
202
|
-
|
203
|
-
if @debug then
|
204
|
-
puts 'inside draw_circle'.info
|
205
|
-
puts ('style: ' + style.inspect).debug
|
206
|
-
end
|
207
|
-
|
208
|
-
obj = Circle.new(
|
209
|
-
x: x1, y: y1,
|
210
|
-
radius: radius,
|
211
|
-
sectors: 32,
|
212
|
-
color: style[:fill],
|
213
|
-
z: style[:"z-index"].to_i
|
214
|
-
)
|
215
|
-
e.obj = obj if e.respond_to? :obj=
|
216
|
-
@window.add obj
|
217
|
-
|
218
|
-
end
|
219
|
-
|
220
|
-
def draw_image(args)
|
221
|
-
|
222
|
-
dimensions, src, style, e = args
|
223
|
-
|
224
|
-
x, y, width, height = dimensions
|
225
|
-
|
226
|
-
filepath = Tempfile.new('r2dsvg').path + File.basename(src)
|
227
|
-
puts 'filepath: ' + filepath.inspect if @debug
|
228
|
-
File.write filepath, RXFHelper.read(src).first
|
229
|
-
|
230
|
-
|
231
|
-
if File.exists? filepath then
|
232
|
-
|
233
|
-
obj = Image.new(
|
234
|
-
filepath,
|
235
|
-
x: x, y: y,
|
236
|
-
width: width, height: height,
|
237
|
-
color: style[:fill],
|
238
|
-
z: style[:"z-index"].to_i
|
239
|
-
)
|
240
|
-
|
241
|
-
e.obj = obj if e.respond_to? :obj=
|
242
|
-
@window.add obj
|
243
|
-
end
|
244
|
-
end
|
245
|
-
|
246
|
-
def draw_line(args)
|
247
|
-
|
248
|
-
coords, style, e = args
|
249
|
-
|
250
|
-
x1, y1, x2, y2 = coords
|
251
|
-
|
252
|
-
if @debug then
|
253
|
-
puts 'inside draw_rectangle'.info
|
254
|
-
puts ('style: ' + style.inspect).debug
|
255
|
-
end
|
256
|
-
|
257
|
-
obj = Line.new(
|
258
|
-
x1: x1, y1: y1,
|
259
|
-
x2: x2, y2: y2,
|
260
|
-
width: style[:"stroke-width"].to_f,
|
261
|
-
color: style[:"stroke"],
|
262
|
-
z: style[:"z-index"].to_i
|
263
|
-
)
|
264
|
-
|
265
|
-
e.obj = obj if e.respond_to? :obj=
|
266
|
-
@window.add obj
|
267
|
-
|
86
|
+
window.on :key_up do |event|
|
87
|
+
# A key was released
|
88
|
+
puts event.key
|
89
|
+
keyboard :onkeyup, event
|
268
90
|
end
|
91
|
+
=end
|
92
|
+
window.show
|
269
93
|
|
270
|
-
def draw_polygon(args)
|
271
|
-
|
272
|
-
vertices, style, e = args
|
273
|
-
|
274
|
-
puts ('vertices: ' + vertices.inspect).debug if @debug
|
275
|
-
|
276
|
-
if @debug then
|
277
|
-
puts 'inside draw_polygon'.info
|
278
|
-
puts ('style: ' + style.inspect).debug
|
279
|
-
end
|
280
|
-
|
281
|
-
puts ('vertices: ' + vertices.inspect).debug if @debug
|
282
|
-
|
283
|
-
h = vertices.map.with_index do |coords,i|
|
284
|
-
|
285
|
-
%w(x y).zip(coords).map {|key, c| [(key + (i+1).to_s).to_sym, c] }
|
286
|
-
|
287
|
-
end.flatten(1).to_h
|
288
|
-
puts ('triangle h: ' + h.inspect).debug if @debug
|
289
|
-
|
290
|
-
puts ('triangle h merged: ' + h.inspect).debug if @debug
|
291
|
-
obj = Triangle.new(h.merge({color: style[:fill], z: style[:"z-index"].to_i}))
|
292
|
-
e.obj = obj if e.respond_to? :obj=
|
293
|
-
@window.add obj
|
294
|
-
end
|
295
|
-
|
296
|
-
|
297
|
-
# not yet implemented
|
298
|
-
#
|
299
|
-
def draw_lines(args)
|
300
|
-
|
301
|
-
coords, width, style, e = args
|
302
|
-
|
303
|
-
x1, y1, x2, y2 = coords
|
304
|
-
|
305
|
-
|
306
|
-
end
|
307
|
-
|
308
|
-
def draw_rectangle(args)
|
309
|
-
|
310
|
-
coords, style, e = args
|
311
|
-
|
312
|
-
x1, y1, x2, y2 = coords
|
313
|
-
|
314
|
-
if @debug then
|
315
|
-
puts 'inside draw_rectangle'.info
|
316
|
-
puts ('style: ' + style.inspect).debug
|
317
|
-
end
|
318
|
-
|
319
|
-
obj = Rectangle.new(
|
320
|
-
x: x1, y: y1,
|
321
|
-
width: x2 - x1, height: y2 - y1,
|
322
|
-
color: style[:fill],
|
323
|
-
z: style[:"z-index"].to_i
|
324
|
-
)
|
325
|
-
e.obj = obj if e.respond_to? :obj=
|
326
|
-
@window.add obj
|
327
|
-
|
328
|
-
end
|
329
94
|
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
puts 'inside draw_text'.info
|
338
|
-
puts ('style: ' + style.inspect).debug
|
339
|
-
end
|
340
|
-
|
341
|
-
obj = Text.new(
|
342
|
-
text,
|
343
|
-
x: x, y: y,
|
344
|
-
#font: 'vera.ttf',
|
345
|
-
size: style[:font_size].to_i,
|
346
|
-
color: style[:color],
|
347
|
-
z: style[:"z-index"].to_i
|
348
|
-
)
|
349
|
-
e.obj = obj
|
350
|
-
@window.add obj
|
95
|
+
end
|
96
|
+
|
97
|
+
def clear()
|
98
|
+
@window.clear
|
99
|
+
end
|
100
|
+
|
101
|
+
def read(unknown, title=@title)
|
351
102
|
|
352
|
-
|
103
|
+
@loaded = false
|
104
|
+
@window.clear
|
105
|
+
doc = nil
|
353
106
|
|
354
|
-
|
355
|
-
# MP3, Ogg Vorbis, and FLAC.
|
356
|
-
|
357
|
-
def embed_audio(args)
|
107
|
+
if unknown.is_a? String
|
358
108
|
|
359
|
-
|
109
|
+
svg, _ = RXFHelper.read(unknown)
|
110
|
+
doc = @model.new(svg, callback: self, debug: @debug)
|
111
|
+
instructions = Render.new(doc, debug: @debug).to_a
|
360
112
|
|
361
|
-
|
362
|
-
puts 'sources: ' + sources.inspect if @debug
|
363
|
-
puts 'inside embed_audio'.info
|
364
|
-
end
|
365
|
-
|
366
|
-
|
367
|
-
audio_files = sources.map do |source|
|
368
|
-
|
369
|
-
filepath = Tempfile.new('r2dsvg').path + File.basename(source[:src])
|
370
|
-
File.write filepath, RXFHelper.read(source[:src]).first
|
371
|
-
filepath
|
372
|
-
end
|
373
|
-
|
374
|
-
audio = audio_files.find {|file| File.exists? file }
|
113
|
+
elsif unknown.is_a? Array
|
375
114
|
|
376
|
-
|
115
|
+
puts 'array found' if @debug
|
377
116
|
|
378
|
-
|
379
|
-
puts 'file: ' + file.inspect if @debug
|
380
|
-
|
381
|
-
obj = Sound.new(file)
|
382
|
-
e.obj = obj
|
117
|
+
instructions = unknown
|
383
118
|
|
384
|
-
def e.play() self.obj.play() end
|
385
|
-
|
386
|
-
end
|
387
|
-
|
388
|
-
def window(args)
|
389
119
|
end
|
120
|
+
|
121
|
+
drawing = DrawingInstructions.new @window, debug: @debug
|
390
122
|
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
123
|
+
if doc then
|
124
|
+
|
125
|
+
@width, @height = %i(width height).map{|x| doc.root.attributes[x].to_i }
|
126
|
+
@window.set title: title, width: @width, height: @height
|
395
127
|
|
396
|
-
def script(args)
|
397
128
|
|
398
|
-
end
|
399
|
-
|
400
|
-
private
|
401
|
-
|
402
|
-
def draw(a)
|
403
|
-
|
404
129
|
threads = []
|
405
130
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
x, *remaining = rawx
|
410
|
-
|
411
|
-
if x.is_a? Symbol then
|
412
|
-
method(x).call(args=remaining)
|
413
|
-
elsif x.is_a? String then
|
414
|
-
draw remaining
|
415
|
-
elsif x.is_a? Array
|
416
|
-
draw remaining
|
417
|
-
else
|
418
|
-
method(x).call(remaining.take 2)
|
419
|
-
end
|
420
|
-
end
|
421
|
-
|
131
|
+
threads << Thread.new do
|
132
|
+
doc.root.xpath('//script').each {|x| eval x.texts.join }
|
133
|
+
drawing.render instructions
|
422
134
|
end
|
423
135
|
|
424
136
|
threads.join
|
425
137
|
|
138
|
+
@loaded = true
|
139
|
+
@doc = doc
|
140
|
+
|
141
|
+
else
|
142
|
+
|
143
|
+
drawing.render instructions
|
144
|
+
h = instructions[2]
|
145
|
+
@width, @height = h[:width].to_i, h[:height].to_i
|
146
|
+
@window.set title: 'Untitled', width: @width, height: @height
|
426
147
|
end
|
427
|
-
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
def refresh()
|
152
|
+
puts 'do nothing' if @debug
|
428
153
|
end
|
429
154
|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
@debug = debug
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
def keyboard(action, event)
|
435
159
|
|
436
|
-
|
160
|
+
doc = @doc
|
437
161
|
|
438
|
-
@doc
|
162
|
+
@doc.root.xpath("//*[@on#{action}]").each do |x|
|
439
163
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
if event.button == :left then
|
451
|
-
|
452
|
-
# click and mousedown do the same thing
|
453
|
-
mouse :click, event
|
454
|
-
mouse :mousedown, event
|
164
|
+
if block_given? then
|
165
|
+
valid = yield(x)
|
166
|
+
statement = x.method(('on' + action.to_s).to_sym).call()
|
167
|
+
puts 'statement: ' + statement.inspect if @debug
|
168
|
+
eval statement if valid
|
169
|
+
else
|
170
|
+
statement = x.method(('on' + action.to_s).to_sym).call()
|
171
|
+
puts 'statement: ' + statement.inspect if @debug
|
172
|
+
eval statement
|
455
173
|
end
|
456
|
-
|
457
|
-
end
|
458
|
-
|
459
|
-
window.show
|
460
|
-
|
461
174
|
|
462
|
-
end
|
463
|
-
|
464
|
-
def read(s, title=@title)
|
465
|
-
|
466
|
-
svg, _ = RXFHelper.read(s)
|
467
|
-
doc = Svgle.new(svg, callback: self, debug: @debug)
|
468
|
-
instructions = Render.new(doc, debug: @debug).to_a
|
469
|
-
|
470
|
-
|
471
|
-
drawing = DrawingInstructions.new @window, debug: @debug
|
472
|
-
puts ('instructions: ' + instructions.inspect).debug if @debug
|
473
|
-
|
474
|
-
@width, @height = %i(width height).map{|x| doc.root.attributes[x].to_i }
|
475
|
-
@window.set title: title, width: @width, height: @height
|
476
|
-
|
477
|
-
Thread.new do
|
478
|
-
doc.root.xpath('//script').each {|x| eval x.text.unescape }
|
479
|
-
drawing.render instructions
|
480
175
|
end
|
481
176
|
|
482
|
-
doc
|
483
|
-
|
177
|
+
@doc.event[action].each {|name| method(name).call event }
|
178
|
+
|
484
179
|
end
|
485
180
|
|
486
|
-
|
487
|
-
private
|
488
|
-
|
489
181
|
def mouse(action, event)
|
490
182
|
|
491
183
|
doc = @doc
|
@@ -533,6 +225,23 @@ class R2dSvg
|
|
533
225
|
end
|
534
226
|
|
535
227
|
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
def timeout(duration=0.8)
|
232
|
+
|
233
|
+
@t = Time.now
|
234
|
+
|
235
|
+
Thread.new do
|
236
|
+
|
237
|
+
while @t + duration > Time.now do
|
238
|
+
sleep 0.1
|
239
|
+
end
|
240
|
+
|
241
|
+
yield
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
536
245
|
|
537
246
|
end
|
538
247
|
|
@@ -0,0 +1,586 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# file: r2dsvg_module.rb
|
4
|
+
|
5
|
+
# Description: Experimental gem to render SVG within a Ruby2D application.
|
6
|
+
|
7
|
+
|
8
|
+
#require 'svgle'
|
9
|
+
require 'onedrb'
|
10
|
+
require 'svgle'
|
11
|
+
require 'dom_render'
|
12
|
+
require 'ruby2d' # experimental gem depends upon simple2d binaries
|
13
|
+
|
14
|
+
|
15
|
+
DEFAULT_CSS = <<CSS
|
16
|
+
g {background-color: blue}
|
17
|
+
svg {background-color: white}
|
18
|
+
rect {fill: yellow}
|
19
|
+
line, polyline {stroke: green; stroke-width: 3}
|
20
|
+
text {fill: red, size: 20}
|
21
|
+
|
22
|
+
CSS
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
class Svgle::Shape
|
27
|
+
|
28
|
+
def initialize(*args)
|
29
|
+
super(*args)
|
30
|
+
@attr_map ||= {fill: :color}
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
module SvgleX
|
38
|
+
|
39
|
+
|
40
|
+
refine Svgle::Text do
|
41
|
+
|
42
|
+
def text=(raw_s)
|
43
|
+
|
44
|
+
oldval = @child_elements.first
|
45
|
+
|
46
|
+
r = super(raw_s)
|
47
|
+
self.obj.text = raw_s if oldval != raw_s
|
48
|
+
|
49
|
+
return r
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
module R2dEngine
|
58
|
+
|
59
|
+
|
60
|
+
|
61
|
+
class Render < DomRender
|
62
|
+
|
63
|
+
def audio(e, attributes)
|
64
|
+
|
65
|
+
puts 'inside audio attributes: ' + attributes.inspect if @debug
|
66
|
+
|
67
|
+
h = attributes
|
68
|
+
|
69
|
+
sources = e.xpath('source').map do |x|
|
70
|
+
h = x.attributes.to_h
|
71
|
+
h.delete :style
|
72
|
+
h
|
73
|
+
end
|
74
|
+
|
75
|
+
[:embed_audio, sources, e]
|
76
|
+
end
|
77
|
+
|
78
|
+
def circle(e, attributes)
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
h = attributes
|
83
|
+
|
84
|
+
x, y, radius= %i(cx cy r).map{|x| h[x].to_i }
|
85
|
+
fill = h[:fill]
|
86
|
+
|
87
|
+
|
88
|
+
[:draw_circle, [x, y], radius, fill, attributes, render_all(e)]
|
89
|
+
end
|
90
|
+
|
91
|
+
# not yet implemented
|
92
|
+
#
|
93
|
+
def ellipse(e, attributes)
|
94
|
+
|
95
|
+
h = attributes
|
96
|
+
|
97
|
+
x, y= %i(cx cy).map{|x| h[x].to_i }
|
98
|
+
width = h[:rx].to_i * 2
|
99
|
+
height = h[:ry].to_i * 2
|
100
|
+
|
101
|
+
[:draw_arc, [x, y, width, height], attributes, render_all(e)]
|
102
|
+
end
|
103
|
+
|
104
|
+
def g(e, attributes)
|
105
|
+
|
106
|
+
puts 'inside rect attributes: ' + attributes.inspect if @debug
|
107
|
+
|
108
|
+
h = attributes
|
109
|
+
|
110
|
+
x1, y1, width, height = %i(x y width height).map{|x| h[x].to_i }
|
111
|
+
x2, y2, = x1 + width, y1 + height
|
112
|
+
|
113
|
+
[:draw_rectangle, [x1, y1, x2, y2], attributes, e, render_all(e)]
|
114
|
+
end
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
def line(e, attributes)
|
119
|
+
|
120
|
+
|
121
|
+
x1, y1, x2, y2 = %i(x1 y1 x2 y2).map{|x| attributes[x].to_i }
|
122
|
+
|
123
|
+
[:draw_line, [x1, y1, x2, y2], attributes, render_all(e)]
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
def image(e, attributes)
|
128
|
+
|
129
|
+
h = attributes
|
130
|
+
|
131
|
+
x, y, width, height = %i(x y width height).map{|x| h[x] ? h[x].to_i : nil }
|
132
|
+
src = h[:'xlink:href']
|
133
|
+
|
134
|
+
[:draw_image, [x, y, width, height], src, attributes, e, render_all(e)]
|
135
|
+
end
|
136
|
+
|
137
|
+
def polygon(e, attributes)
|
138
|
+
|
139
|
+
|
140
|
+
points = attributes[:points].split(/\s+/). \
|
141
|
+
map {|x| x.split(/\s*,\s*/).map(&:to_i)}
|
142
|
+
|
143
|
+
[:draw_polygon, points, attributes, e, render_all(e)]
|
144
|
+
end
|
145
|
+
|
146
|
+
def polyline(e, attributes)
|
147
|
+
|
148
|
+
points = attributes[:points].split(/\s+/). \
|
149
|
+
map {|x| x.split(/\s*,\s*/).map(&:to_i)}
|
150
|
+
|
151
|
+
[:draw_lines, points, attributes, render_all(e)]
|
152
|
+
end
|
153
|
+
|
154
|
+
def rect(e, attributes)
|
155
|
+
|
156
|
+
puts 'inside rect attributes: ' + attributes.inspect if @debug
|
157
|
+
|
158
|
+
h = attributes
|
159
|
+
|
160
|
+
x1, y1, width, height = %i(x y width height).map{|x| h[x].to_i }
|
161
|
+
x2, y2, = x1 + width, y1 + height
|
162
|
+
|
163
|
+
[:draw_rectangle, [x1, y1, x2, y2], attributes, e, render_all(e)]
|
164
|
+
end
|
165
|
+
|
166
|
+
|
167
|
+
|
168
|
+
def svg(e, attributes)
|
169
|
+
|
170
|
+
h = attributes
|
171
|
+
width, height = %i(width height).map{|x| h[x].to_i }
|
172
|
+
|
173
|
+
[:draw_rectangle, [0, 0, width, height], attributes, render_all(e)]
|
174
|
+
end
|
175
|
+
|
176
|
+
def text(e, attributes)
|
177
|
+
|
178
|
+
|
179
|
+
attributes.merge!({font_size: '20'})
|
180
|
+
|
181
|
+
x, y = %i(x y).map{|x| attributes[x].to_i }
|
182
|
+
|
183
|
+
[:draw_text, [x, y], e.text, attributes, e, render_all(e)]
|
184
|
+
end
|
185
|
+
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
class DrawingInstructions
|
190
|
+
using ColouredText
|
191
|
+
|
192
|
+
attr_accessor :area
|
193
|
+
|
194
|
+
|
195
|
+
def initialize(window, debug: false)
|
196
|
+
|
197
|
+
@window, @debug = window, debug
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
def draw_arc(args)
|
202
|
+
|
203
|
+
dimensions, style = args
|
204
|
+
|
205
|
+
x, y, width, height = dimensions
|
206
|
+
|
207
|
+
#gc = gc_ini(fill: style[:fill] || :none)
|
208
|
+
#@area.window.draw_arc(gc, 1, x, y, width, height, 0, 64 * 360)
|
209
|
+
end
|
210
|
+
|
211
|
+
def draw_circle(args)
|
212
|
+
|
213
|
+
coords, radius, fill, style, e = args
|
214
|
+
|
215
|
+
x1, y1 = coords
|
216
|
+
|
217
|
+
if @debug then
|
218
|
+
puts 'inside draw_circle'.info
|
219
|
+
puts ('style: ' + style.inspect).debug
|
220
|
+
end
|
221
|
+
|
222
|
+
obj = Circle.new(
|
223
|
+
x: x1, y: y1,
|
224
|
+
radius: radius,
|
225
|
+
sectors: 32,
|
226
|
+
color: style[:fill],
|
227
|
+
z: style[:"z-index"].to_i
|
228
|
+
)
|
229
|
+
e.obj = obj if e.respond_to? :obj=
|
230
|
+
@window.add obj
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
def draw_image(args)
|
235
|
+
|
236
|
+
dimensions, src, style, e = args
|
237
|
+
|
238
|
+
x, y, width, height = dimensions
|
239
|
+
|
240
|
+
filepath = Tempfile.new('r2dsvg').path + File.basename(src)
|
241
|
+
puts 'filepath: ' + filepath.inspect if @debug
|
242
|
+
File.write filepath, RXFHelper.read(src).first
|
243
|
+
|
244
|
+
|
245
|
+
if File.exists? filepath then
|
246
|
+
|
247
|
+
obj = Image.new(
|
248
|
+
filepath,
|
249
|
+
x: x, y: y,
|
250
|
+
width: width, height: height,
|
251
|
+
color: style[:fill],
|
252
|
+
z: style[:"z-index"].to_i
|
253
|
+
)
|
254
|
+
|
255
|
+
e.obj = obj if e.respond_to? :obj=
|
256
|
+
@window.add obj
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def draw_line(args)
|
261
|
+
|
262
|
+
coords, style, e = args
|
263
|
+
|
264
|
+
x1, y1, x2, y2 = coords
|
265
|
+
|
266
|
+
if @debug then
|
267
|
+
puts 'inside draw_rectangle'.info
|
268
|
+
puts ('style: ' + style.inspect).debug
|
269
|
+
end
|
270
|
+
|
271
|
+
obj = Line.new(
|
272
|
+
x1: x1, y1: y1,
|
273
|
+
x2: x2, y2: y2,
|
274
|
+
width: style[:"stroke-width"].to_f,
|
275
|
+
color: style[:"stroke"],
|
276
|
+
z: style[:"z-index"].to_i
|
277
|
+
)
|
278
|
+
|
279
|
+
e.obj = obj if e.respond_to? :obj=
|
280
|
+
@window.add obj
|
281
|
+
|
282
|
+
end
|
283
|
+
|
284
|
+
def draw_polygon(args)
|
285
|
+
|
286
|
+
vertices, style, e = args
|
287
|
+
|
288
|
+
puts ('vertices: ' + vertices.inspect).debug if @debug
|
289
|
+
|
290
|
+
if @debug then
|
291
|
+
puts 'inside draw_polygon'.info
|
292
|
+
puts ('style: ' + style.inspect).debug
|
293
|
+
end
|
294
|
+
|
295
|
+
puts ('vertices: ' + vertices.inspect).debug if @debug
|
296
|
+
|
297
|
+
h = vertices.map.with_index do |coords,i|
|
298
|
+
|
299
|
+
%w(x y).zip(coords).map {|key, c| [(key + (i+1).to_s).to_sym, c] }
|
300
|
+
|
301
|
+
end.flatten(1).to_h
|
302
|
+
puts ('triangle h: ' + h.inspect).debug if @debug
|
303
|
+
|
304
|
+
puts ('triangle h merged: ' + h.inspect).debug if @debug
|
305
|
+
obj = Triangle.new(h.merge({color: style[:fill], z: style[:"z-index"].to_i}))
|
306
|
+
e.obj = obj if e.respond_to? :obj=
|
307
|
+
@window.add obj
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
# not yet implemented
|
312
|
+
#
|
313
|
+
def draw_lines(args)
|
314
|
+
|
315
|
+
coords, width, style, e = args
|
316
|
+
|
317
|
+
x1, y1, x2, y2 = coords
|
318
|
+
|
319
|
+
|
320
|
+
end
|
321
|
+
|
322
|
+
def draw_rectangle(args)
|
323
|
+
|
324
|
+
coords, style, e = args
|
325
|
+
|
326
|
+
x1, y1, x2, y2 = coords
|
327
|
+
|
328
|
+
if @debug then
|
329
|
+
puts 'inside draw_rectangle'.info
|
330
|
+
puts ('style: ' + style.inspect).debug
|
331
|
+
end
|
332
|
+
|
333
|
+
obj = Rectangle.new(
|
334
|
+
x: x1, y: y1,
|
335
|
+
width: x2 - x1, height: y2 - y1,
|
336
|
+
color: style[:fill],
|
337
|
+
z: style[:"z-index"].to_i
|
338
|
+
)
|
339
|
+
e.obj = obj if e.respond_to? :obj=
|
340
|
+
@window.add obj
|
341
|
+
|
342
|
+
end
|
343
|
+
|
344
|
+
def draw_text(args)
|
345
|
+
|
346
|
+
coords, text, style, e = args
|
347
|
+
|
348
|
+
x, y = coords
|
349
|
+
|
350
|
+
if @debug then
|
351
|
+
puts 'inside draw_text'.info
|
352
|
+
puts ('style: ' + style.inspect).debug
|
353
|
+
end
|
354
|
+
|
355
|
+
obj = Text.new(
|
356
|
+
text,
|
357
|
+
x: x, y: y,
|
358
|
+
#font: 'vera.ttf',
|
359
|
+
size: style[:font_size].to_i,
|
360
|
+
color: style[:color],
|
361
|
+
z: style[:"z-index"].to_i
|
362
|
+
)
|
363
|
+
puts 'e: ' + e.inspect
|
364
|
+
e.obj = obj
|
365
|
+
@window.add obj
|
366
|
+
|
367
|
+
end
|
368
|
+
|
369
|
+
# Ruby 2D supports a number of popular audio formats, including WAV,
|
370
|
+
# MP3, Ogg Vorbis, and FLAC.
|
371
|
+
|
372
|
+
def embed_audio(args)
|
373
|
+
|
374
|
+
sources, e = args
|
375
|
+
|
376
|
+
if @debug then
|
377
|
+
puts 'sources: ' + sources.inspect if @debug
|
378
|
+
puts 'inside embed_audio'.info
|
379
|
+
end
|
380
|
+
|
381
|
+
|
382
|
+
audio_files = sources.map do |source|
|
383
|
+
|
384
|
+
filepath = Tempfile.new('r2dsvg').path + File.basename(source[:src])
|
385
|
+
File.write filepath, RXFHelper.read(source[:src]).first
|
386
|
+
filepath
|
387
|
+
end
|
388
|
+
|
389
|
+
audio = audio_files.find {|file| File.exists? file }
|
390
|
+
|
391
|
+
return unless audio
|
392
|
+
|
393
|
+
file = File.exists?(audio) ? audio : nil
|
394
|
+
puts 'file: ' + file.inspect if @debug
|
395
|
+
|
396
|
+
obj = Sound.new(file)
|
397
|
+
e.obj = obj
|
398
|
+
|
399
|
+
def e.play() self.obj.play() end
|
400
|
+
|
401
|
+
end
|
402
|
+
|
403
|
+
def window(args)
|
404
|
+
end
|
405
|
+
|
406
|
+
def render(a)
|
407
|
+
|
408
|
+
stylex, e = a[-3..-2]
|
409
|
+
|
410
|
+
if e.respond_to? :attr_map then
|
411
|
+
|
412
|
+
h = e.attr_map.inject({}) do |r, x|
|
413
|
+
key, value = x
|
414
|
+
r.merge!({value => stylex[key]})
|
415
|
+
end
|
416
|
+
a[-3].merge!(h)
|
417
|
+
end
|
418
|
+
|
419
|
+
|
420
|
+
method(a[0]).call(args=a[1..2])
|
421
|
+
draw a[3]
|
422
|
+
end
|
423
|
+
|
424
|
+
def script(args)
|
425
|
+
|
426
|
+
end
|
427
|
+
|
428
|
+
def style(args)
|
429
|
+
end
|
430
|
+
|
431
|
+
private
|
432
|
+
|
433
|
+
def draw(a)
|
434
|
+
|
435
|
+
#threads = []
|
436
|
+
|
437
|
+
a.each do |rawx|
|
438
|
+
|
439
|
+
#threads << Thread.new do
|
440
|
+
puts 'rawx: ;' + rawx.inspect if @debug
|
441
|
+
x, *remaining = rawx
|
442
|
+
|
443
|
+
puts 'x: ;' + x.inspect if @debug
|
444
|
+
puts 'remaining: ' + remaining.inspect if @debug
|
445
|
+
|
446
|
+
if x.is_a? Symbol then
|
447
|
+
method(x).call(args=remaining)
|
448
|
+
draw(remaining[3])
|
449
|
+
elsif x.is_a? String then
|
450
|
+
draw remaining
|
451
|
+
elsif x.is_a? Array
|
452
|
+
draw remaining
|
453
|
+
else
|
454
|
+
method(x).call(remaining.take 2)
|
455
|
+
end
|
456
|
+
#end
|
457
|
+
|
458
|
+
end
|
459
|
+
|
460
|
+
#threads.join
|
461
|
+
|
462
|
+
end
|
463
|
+
|
464
|
+
end
|
465
|
+
|
466
|
+
|
467
|
+
|
468
|
+
class App
|
469
|
+
|
470
|
+
attr_reader :doc
|
471
|
+
|
472
|
+
|
473
|
+
def clear()
|
474
|
+
@window.clear
|
475
|
+
end
|
476
|
+
|
477
|
+
def read(s, title=@title)
|
478
|
+
@loaded = false
|
479
|
+
@window.clear
|
480
|
+
svg, _ = RXFHelper.read(s)
|
481
|
+
doc = Svgle.new(svg, callback: self, debug: @debug)
|
482
|
+
instructions = Render.new(doc, debug: @debug).to_a
|
483
|
+
|
484
|
+
|
485
|
+
drawing = DrawingInstructions.new @window, debug: @debug
|
486
|
+
puts ('instructions: ' + instructions.inspect).debug if @debug
|
487
|
+
|
488
|
+
@width, @height = %i(width height).map{|x| doc.root.attributes[x].to_i }
|
489
|
+
@window.set title: title, width: @width, height: @height
|
490
|
+
|
491
|
+
threads = []
|
492
|
+
|
493
|
+
threads << Thread.new do
|
494
|
+
doc.root.xpath('//script').each {|x| eval x.text.unescape }
|
495
|
+
drawing.render instructions
|
496
|
+
end
|
497
|
+
|
498
|
+
threads.join
|
499
|
+
|
500
|
+
@loaded = true
|
501
|
+
@doc = doc
|
502
|
+
|
503
|
+
end
|
504
|
+
|
505
|
+
def refresh()
|
506
|
+
puts 'do nothing' if @debug
|
507
|
+
end
|
508
|
+
|
509
|
+
|
510
|
+
private
|
511
|
+
|
512
|
+
def keyboard(action, event)
|
513
|
+
|
514
|
+
doc = @doc
|
515
|
+
|
516
|
+
@doc.root.xpath("//*[@on#{action}]").each do |x|
|
517
|
+
|
518
|
+
if block_given? then
|
519
|
+
valid = yield(x)
|
520
|
+
statement = x.method(('on' + action.to_s).to_sym).call()
|
521
|
+
puts 'statement: ' + statement.inspect if @debug
|
522
|
+
eval statement if valid
|
523
|
+
else
|
524
|
+
statement = x.method(('on' + action.to_s).to_sym).call()
|
525
|
+
puts 'statement: ' + statement.inspect if @debug
|
526
|
+
eval statement
|
527
|
+
end
|
528
|
+
|
529
|
+
end
|
530
|
+
|
531
|
+
@doc.event[action].each {|name| method(name).call event }
|
532
|
+
|
533
|
+
end
|
534
|
+
|
535
|
+
def mouse(action, event)
|
536
|
+
|
537
|
+
doc = @doc
|
538
|
+
|
539
|
+
@doc.root.xpath("//*[@on#{action}]").each do |x|
|
540
|
+
|
541
|
+
#puts 'x.class: ' + x.inspect if @debug
|
542
|
+
if x.obj and x.obj.contains? event.x, event.y then
|
543
|
+
|
544
|
+
|
545
|
+
if not x.active? then
|
546
|
+
x.active = true
|
547
|
+
elsif action == :mouseenter
|
548
|
+
next
|
549
|
+
end
|
550
|
+
|
551
|
+
if block_given? then
|
552
|
+
valid = yield(x)
|
553
|
+
statement = x.method(('on' + action.to_s).to_sym).call()
|
554
|
+
puts 'statement: ' + statement.inspect if @debug
|
555
|
+
eval statement if valid
|
556
|
+
else
|
557
|
+
statement = x.method(('on' + action.to_s).to_sym).call()
|
558
|
+
puts 'statement: ' + statement.inspect if @debug
|
559
|
+
eval statement
|
560
|
+
end
|
561
|
+
|
562
|
+
else
|
563
|
+
|
564
|
+
if x.active? then
|
565
|
+
x.active = false
|
566
|
+
onleave
|
567
|
+
end
|
568
|
+
end
|
569
|
+
|
570
|
+
end
|
571
|
+
|
572
|
+
end
|
573
|
+
|
574
|
+
def onleave()
|
575
|
+
|
576
|
+
@doc.root.xpath("//*[@onmouseleave]").each do |x|
|
577
|
+
puts 'onleave'.info if @debug
|
578
|
+
eval x.method(:onmouseleave).call()
|
579
|
+
end
|
580
|
+
|
581
|
+
end
|
582
|
+
|
583
|
+
end
|
584
|
+
|
585
|
+
end
|
586
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: r2dsvg
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Robertson
|
@@ -35,7 +35,7 @@ cert_chain:
|
|
35
35
|
2z9cm0kGX/0pBfn8cMKC0JgNNNONx3uqiCAhaBEN35C8dqBjua/CK4rMoKEB7XQq
|
36
36
|
OAk+mR2SqyWecmZhdnsnm86T
|
37
37
|
-----END CERTIFICATE-----
|
38
|
-
date: 2020-
|
38
|
+
date: 2020-09-19 00:00:00.000000000 Z
|
39
39
|
dependencies:
|
40
40
|
- !ruby/object:Gem::Dependency
|
41
41
|
name: svgle
|
@@ -46,7 +46,7 @@ dependencies:
|
|
46
46
|
version: '0.4'
|
47
47
|
- - ">="
|
48
48
|
- !ruby/object:Gem::Version
|
49
|
-
version: 0.4.
|
49
|
+
version: 0.4.7
|
50
50
|
type: :runtime
|
51
51
|
prerelease: false
|
52
52
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -56,7 +56,7 @@ dependencies:
|
|
56
56
|
version: '0.4'
|
57
57
|
- - ">="
|
58
58
|
- !ruby/object:Gem::Version
|
59
|
-
version: 0.4.
|
59
|
+
version: 0.4.7
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
61
|
name: onedrb
|
62
62
|
requirement: !ruby/object:Gem::Requirement
|
@@ -103,20 +103,20 @@ dependencies:
|
|
103
103
|
requirements:
|
104
104
|
- - "~>"
|
105
105
|
- !ruby/object:Gem::Version
|
106
|
-
version: '0.
|
106
|
+
version: '0.4'
|
107
107
|
- - ">="
|
108
108
|
- !ruby/object:Gem::Version
|
109
|
-
version: 0.
|
109
|
+
version: 0.4.1
|
110
110
|
type: :runtime
|
111
111
|
prerelease: false
|
112
112
|
version_requirements: !ruby/object:Gem::Requirement
|
113
113
|
requirements:
|
114
114
|
- - "~>"
|
115
115
|
- !ruby/object:Gem::Version
|
116
|
-
version: '0.
|
116
|
+
version: '0.4'
|
117
117
|
- - ">="
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version: 0.
|
119
|
+
version: 0.4.1
|
120
120
|
description:
|
121
121
|
email: james@jamesrobertson.eu
|
122
122
|
executables: []
|
@@ -124,6 +124,7 @@ extensions: []
|
|
124
124
|
extra_rdoc_files: []
|
125
125
|
files:
|
126
126
|
- lib/r2dsvg.rb
|
127
|
+
- lib/r2dsvg/r2dsvg_module.rb
|
127
128
|
homepage: https://github.com/jrobertson/r2dsvg
|
128
129
|
licenses:
|
129
130
|
- MIT
|
metadata.gz.sig
CHANGED
Binary file
|