rsvgr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rsvgr.rb +405 -0
  3. metadata +44 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cd4d756ecd876ced14a6866dd9594a1c93666f6e
4
+ data.tar.gz: d3e3f0d0e9da5624f44ebe704132cf6adfb2a17e
5
+ SHA512:
6
+ metadata.gz: f508fc6214a96e9411ef2830e5e6bd79ae9476425be762854812b0edbf4f79f2e08928e99376f465d718483e9232b27e4d63919fb285f7e2993a7654e90e4098
7
+ data.tar.gz: 37c542277ac4c4f7184458b83820067259ae155c056f24c0dc8d7dfa63794beef0a406ded045f5473972d52f81af941730d0bda0da6fe009b39a5ba25897235e
data/lib/rsvgr.rb ADDED
@@ -0,0 +1,405 @@
1
+ # I don't give a fuck about namespaces, xslt, etc.
2
+ # I don't use any xml library, because I just don't really need it here
3
+ # I don't give a fuck about xslt and other shit
4
+ # I JUST A PSYCHO REDING THIS AND DOING THE EASIEST WAY
5
+
6
+ # http://www.w3.org/TR/SVGTiny12/single-page.html
7
+
8
+
9
+ module RSVGR
10
+
11
+ DEFAULT_SIZE = 100.0
12
+ UNSUPPORTED = ->{ raise "not supported currently" }
13
+
14
+ class Node
15
+ def initialize args = {}
16
+ args[:x1] ||= 0
17
+ args[:y1] ||= 0
18
+ args[:x2] ||= DEFAULT_SIZE
19
+ args[:y2] ||= DEFAULT_SIZE
20
+ args.each{ |k,v| instance_variable_set "@#{k}",v }
21
+ end
22
+ def to_s
23
+ raise "you had to override me, while ascending from #{self.class.name} class"
24
+ end
25
+ end
26
+
27
+ class CanIHazChildren < Node
28
+ def initialize args = {}
29
+ @children = []
30
+ super
31
+ end
32
+ def << x
33
+ @children << x
34
+ self
35
+ end
36
+ def to_s_children additional_child = []
37
+ [*@children, *additional_child].map{ |child|
38
+ child.to_s.gsub(/.+/, ' \0')
39
+ }.join
40
+ end
41
+ def save_as what, where
42
+ File.open(where, "w"){ |f| f.puts what }
43
+ self
44
+ end
45
+ end
46
+
47
+ class Group < CanIHazChildren
48
+ def to_s additional_child = []
49
+ "<g transform=\"" \
50
+ "#{"translate(#{@x || 0},#{@y || 0}) " if @x || @y}" \
51
+ "#{"scale(#{@scale_x || @scale || 1},#{@scale_y || @scale || 1}) " if @scale_x || @scale_y || @scale}" \
52
+ "#{"rotate(#{@rotate})" if @rotate}\"" \
53
+ "#{" id=\"#{@id}\"" if @id}" \
54
+ "#{" fill=\"#{@fill_color}\"" if @fill_color}" \
55
+ "#{" stroke-width=\"#{@stroke_width}\"" if @stroke_width}" \
56
+ "#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
57
+ ">\n" + to_s_children(additional_child) + "</g>\n"
58
+ end
59
+ end
60
+
61
+ class Root < CanIHazChildren
62
+ def initialize args = {}
63
+ @fill_color = "silver"
64
+ @stroke_color = "black"
65
+ @stroke_width = DEFAULT_SIZE*0.01
66
+ @stroke_linecap = "round"
67
+ super
68
+ end
69
+ def to_s
70
+ "<?xml version=\"1.0\"?>\n" \
71
+ "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.2\" baseProfile=\"tiny\"" \
72
+ " viewBox=\"#{[@x1, @y1, @x2, @y2].join " "}\"" \
73
+ " fill=\"#{@fill_color}\"" \
74
+ " stroke=\"#{@stroke_color}\"" \
75
+ " stroke-width=\"#{@stroke_width}\"" \
76
+ " stroke-linecap=\"#{@stroke_linecap}\"" \
77
+ " preserveAspectRatio=\"xMidYMid meet\"" \
78
+ ">\n <title>Generated by RubySVGmakeR v0.1 (c) Nakilon</title>\n" +
79
+ to_s_children + "</svg>\n"
80
+ end
81
+ def to_html
82
+ "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" \
83
+ " <head>\n" \
84
+ " <title>#{Time.now}</title>\n" \
85
+ " <style type=\"text/css\" media=\"screen\">\n" \
86
+ " body {\n" \
87
+ " background:lightgrey;\n" \
88
+ " margin:0;\n" \
89
+ " }\n" \
90
+ " svg {\n" \
91
+ " display:block;\n" \
92
+ " border:2px solid grey;\n" \
93
+ " position:absolute;\n" \
94
+ " top:3%;\n" \
95
+ " left:3%;\n" \
96
+ " width:94%;\n" \
97
+ " height:94%;\n" \
98
+ " background:white;\n" \
99
+ " }\n" \
100
+ " </style>\n" \
101
+ " </head>\n" \
102
+ " <body>\n" + to_s + " </body>\n</html>\n"
103
+ end
104
+ def cat
105
+ puts self.to_s
106
+ self
107
+ end
108
+ def save
109
+ save_as to_s, "temp.svg"
110
+ end
111
+ def save_html
112
+ UNSUPPORTED
113
+ save_as to_html, "temp.html"
114
+ end
115
+ def save_as what, where
116
+ UNSUPPORTED
117
+ File.open(where, "w"){ |f| f.puts what }
118
+ self
119
+ end
120
+ def open_with app
121
+ RUBY_PLATFORM["mingw"] ? `#{app} temp.svg` : `open -a #{app} temp.svg`
122
+ end
123
+ end
124
+
125
+
126
+ class Script < CanIHazChildren
127
+ def to_s
128
+ "<script" \
129
+ " type=\"text/ecmascript\"" \
130
+ "#{" id=\"#{@id}\"" if @id}" \
131
+ "><![CDATA[\n" + to_s_children + "]]></script>\n"
132
+ end
133
+ end
134
+
135
+ class Text < CanIHazChildren
136
+ def initialize args = {}
137
+ args[:x] ||= DEFAULT_SIZE/2
138
+ args[:y] ||= DEFAULT_SIZE/2
139
+ args[:size] ||= DEFAULT_SIZE*0.8
140
+ args[:anchor] ||= "middle"
141
+ args[:baseline] ||= "central"
142
+ args[:fill_color] ||= "black"
143
+ super
144
+ end
145
+ def to_s
146
+ "<text" \
147
+ "#{" id=\"#{@id}\"" if @id}" \
148
+ "#{" visibility=\"#{@visibility}\"" if @visibility}" \
149
+ " x=\"#{@x}\"" \
150
+ " y=\"#{@y}\"" \
151
+ " font-size=\"#{@size}\"" \
152
+ " text-anchor=\"#{@anchor}\"" \
153
+ " dominant-baseline=\"#{@baseline}\"" \
154
+ " fill=\"#{@fill_color}\"" \
155
+ "#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
156
+ "#{" style=\"#{@style}\"" if @style}" \
157
+ ">\n" + to_s_children + "</text>\n"
158
+ end
159
+ end
160
+
161
+ class Line < Node
162
+ def to_s
163
+ "<line " \
164
+ "x1=\"#{@x1}\" " \
165
+ "x2=\"#{@x2}\" " \
166
+ "y1=\"#{@y1}\" " \
167
+ "y2=\"#{@y2}\" " \
168
+ "#{"stroke=\"#{@stroke_color}\" " if @stroke_color}" \
169
+ "/>\n"
170
+ end
171
+ end
172
+
173
+ class Path < Node
174
+ def initialize args = {}
175
+ args[:d] = "M " + args[:points].map{ |x, y| "#{x} #{y}" }.join(" L ")
176
+ args[:fill_color] ||= "none"
177
+ super
178
+ end
179
+ def to_s
180
+ "<path" \
181
+ " d=\"#{@d}\"" \
182
+ "#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
183
+ " fill=\"#{@fill_color}\"" \
184
+ "/>\n"
185
+ end
186
+ end
187
+
188
+ class Circle < Node
189
+ def initialize args = {}
190
+ args[:r] ||= DEFAULT_SIZE*0.5
191
+ super
192
+ end
193
+ def to_s
194
+ "<circle" \
195
+ "#{" cx=\"#{@x}\"" if @x}" \
196
+ "#{" cy=\"#{@y}\"" if @y}" \
197
+ " r=\"#{@r}\"" \
198
+ "#{" fill=\"#{@fill_color}\"" if @fill_color}" \
199
+ "#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
200
+ "#{" onmousemove=\"#{@onmousemove}\"" if @onmousemove}" \
201
+ "#{" onmouseout=\"#{@onmouseout}\"" if @onmouseout}" \
202
+ "/>\n"
203
+ end
204
+ end
205
+
206
+ class Rect < Node
207
+ def initialize args = {}
208
+ args[:width] ||= DEFAULT_SIZE
209
+ args[:height] ||= DEFAULT_SIZE
210
+ super
211
+ end
212
+ def to_s
213
+ "<rect" \
214
+ "#{" x=\"#{@x1}\"" if @x1}" \
215
+ "#{" y=\"#{@y1}\"" if @y1}" \
216
+ " width=\"#{@width}\"" \
217
+ " height=\"#{@height}\"" \
218
+ "#{" stroke=\"#{@stroke_color}\"" if @stroke_color}" \
219
+ "#{" stroke-width=\"#{@stroke_width}\"" if @stroke_width}" \
220
+ "#{" fill=\"#{@fill_color}\"" if @fill_color}" \
221
+ "/>\n"
222
+ end
223
+ end
224
+
225
+
226
+ module Samples
227
+ def self.face args = {}
228
+ args[:scale_x] = args[:scale_y] = args[:size]/DEFAULT_SIZE if args[:size]
229
+ args[:fill_color] = "yellow"
230
+ args[:x] = DEFAULT_SIZE/2
231
+ args[:y] = DEFAULT_SIZE/2
232
+ args[:stroke_width] = DEFAULT_SIZE/10
233
+ args[:stroke_color] = :black
234
+ Group.new(args) <<
235
+ Circle.new <<
236
+ Circle.new(x:-0.2*DEFAULT_SIZE, y:-0.2*DEFAULT_SIZE, r:0.1*DEFAULT_SIZE) <<
237
+ Circle.new(x:+0.2*DEFAULT_SIZE, y:-0.2*DEFAULT_SIZE, r:0.1*DEFAULT_SIZE) <<
238
+ Line.new(x1:-0.2*DEFAULT_SIZE, y1:+0.2*DEFAULT_SIZE, x2:+0.2*DEFAULT_SIZE, y2:+0.2*DEFAULT_SIZE)
239
+ end
240
+ class Grid < Group
241
+ def initialize args = {}
242
+ #args[:x2] ||= args[:xn] + args[:x1] ||= 0
243
+ #args[:y2] ||= args[:yn] + args[:y1] ||= 0
244
+ args[:fill_color] = "white"
245
+ super
246
+ if @xn*(@y2-@y1) > (@x2-@x1)*@yn
247
+ y = (@x2-@x1)*@yn/@xn
248
+ @y1 = (DEFAULT_SIZE-y)/2
249
+ @y2 = (DEFAULT_SIZE+y)/2
250
+ else
251
+ x = (@y2-@y1)*@xn/@yn
252
+ @x1 = (DEFAULT_SIZE-x)/2
253
+ @x2 = (DEFAULT_SIZE+x)/2
254
+ end
255
+ w = (@x2 - @x1)/@xn
256
+ h = (@y2 - @y1)/@yn
257
+ [*0...@yn].product([*0...@xn]) do |i,j|
258
+ self << cell = Group.new(
259
+ x: @x1+j*w,
260
+ y: @y1+i*h,
261
+ scale_x: w/DEFAULT_SIZE,
262
+ scale_y: h/DEFAULT_SIZE,
263
+ )
264
+ cell << Rect.new(stroke_color:"silver", stroke_width:DEFAULT_SIZE/10) << inside = Group.new(
265
+ x: DEFAULT_SIZE*0.1,
266
+ y: DEFAULT_SIZE*0.1,
267
+ scale_x: 0.8,
268
+ scale_y: 0.8,
269
+ )
270
+ yield inside,i,j if block_given?
271
+ end
272
+ end
273
+ end
274
+ class Plot < Group
275
+ def initialize args = {}
276
+ args[:stroke_width] = DEFAULT_SIZE * 0.002
277
+ args[:fill_color] = "white"
278
+ super
279
+ @lines = []
280
+ end
281
+ def add_line args = {}
282
+ args[:data_pairs] = Hash[args[:data_pairs].to_a]
283
+ @lines << args
284
+ self
285
+ end
286
+ def to_s
287
+ return @to_s_cached if @to_s_cached
288
+ ticks = lambda do |basics|
289
+ if basics.any?{ |i| ! i.is_a? Integer }
290
+ [
291
+ basics.uniq,
292
+ -> x { DEFAULT_SIZE * basics.index(x) / (basics.size-1) }
293
+ ]
294
+ else
295
+ min, max = basics.minmax
296
+ [
297
+ [min, *(1...8).map{ |i| (max-min)*i/8 }, max],
298
+ -> x { DEFAULT_SIZE * (x - min) / (max - min) }
299
+ ]
300
+ end
301
+ end
302
+ ticks_x, f_x = ticks[ @ticks_x || @lines.flat_map{ |line| line[:data_pairs].keys } ]
303
+ ticks_y, f_y = ticks[ @lines.flat_map{ |line| line[:data_pairs].values } ]
304
+ top, left, scale = 0.05, 0.1
305
+ size = DEFAULT_SIZE * 0.03
306
+ self << field = Group.new(id:"TODOfield", x: DEFAULT_SIZE * left, y: DEFAULT_SIZE * top, scale: 0.7)
307
+ hsv2rgb = lambda do |h,s,v|
308
+ vmin = (100-s)*v/100
309
+ a = (v - vmin)*(h % 60)/60.0
310
+ vinc = vmin + a
311
+ vdec = v - a
312
+ case h/60
313
+ when 0 ; [v, vinc, vmin]
314
+ when 1 ; [vdec, v, vmin]
315
+ when 2 ; [vmin, v, vinc]
316
+ when 3 ; [vmin, vdec, v]
317
+ when 4 ; [vinc, vmin, v]
318
+ when 5 ; [v, vmin, vdec]
319
+ end.map{ |i| (2.55 * i).floor }
320
+ end
321
+ colors = (0...@lines.size).map{ |i| ?# + hsv2rgb[i*360/@lines.size, 100, 100].pack("C*").unpack("H*").first }
322
+ @lines.zip(colors).each_with_index do |(line, color), i|
323
+ #@@id = defined?(@@id) ? @@id + 1 : 0
324
+ field << (Text.new(
325
+ #id: @@id,
326
+ fill_color: "black",# color.tr("0123456789abcdef", "fedcba9876543210"),
327
+ stroke_color: "none",
328
+ size: DEFAULT_SIZE * 0.03,
329
+ x: DEFAULT_SIZE * 0.12,
330
+ y: DEFAULT_SIZE * (0.05 + i * 0.03),
331
+ anchor: "begin",
332
+ baseline: "text-before-edge",
333
+ ) << line[:title])
334
+ field << Rect.new(
335
+ x1: DEFAULT_SIZE * 0.05,
336
+ y1: DEFAULT_SIZE * (0.05 + i * 0.03),
337
+ width: DEFAULT_SIZE * 0.05,
338
+ height: DEFAULT_SIZE * 0.03,
339
+ fill_color: color,
340
+ )
341
+ end
342
+ @lines.zip(colors).each_with_index do |(line, color), i|
343
+ field << Path.new(
344
+ stroke_color: color,
345
+ points: line[:data_pairs].map{ |k,v| [f_x[k], DEFAULT_SIZE-f_y[v]] }
346
+ )
347
+ end
348
+ field << Text.new(
349
+ id: "tooltip",
350
+ visibility: "hidden",
351
+ baseline: "before-edge",
352
+ size: DEFAULT_SIZE * 0.03,
353
+ stroke_color: "none",
354
+ )
355
+ field << (Script.new(id: "TODOscript") << "
356
+ var svg = document.getElementById(\"TODOscript\").ownerSVGElement;
357
+ svg.tooltip = svg.getElementById(\"tooltip\");
358
+ function show_tooltip(evt, text, x, y) {
359
+ var tooltip = evt.target.ownerSVGElement.tooltip;
360
+ tooltip.setAttributeNS(null, \"x\", x);
361
+ tooltip.setAttributeNS(null, \"y\", y);
362
+ tooltip.textContent = text;
363
+ tooltip.setAttributeNS(null, \"visibility\", \"visible\");
364
+ }
365
+ function hide_tooltip(evt) { evt.target.ownerSVGElement.tooltip.setAttributeNS(null, \"visibility\", \"hidden\"); }
366
+ ")
367
+ @lines.zip(colors) do |line, color| line[:data_pairs].each do |k,v|
368
+ x = f_x[k]
369
+ y = DEFAULT_SIZE - f_y[v]
370
+ field << Circle.new(
371
+ fill_color: color,
372
+ stroke_color: color,
373
+ x: x,
374
+ y: y,
375
+ r: DEFAULT_SIZE * 0.005,
376
+ onmousemove: "show_tooltip(evt, '#{k}/#{v} - #{line[:title]}', #{x}, #{y - DEFAULT_SIZE*0.025})",
377
+ onmouseout: "hide_tooltip(evt)",
378
+ )
379
+ end end
380
+ field << Rect.new(fill_color:"none")
381
+ ticks_x.each_with_index{ |tick, i| _ = f_x[tick]
382
+ field << Line.new(y1: DEFAULT_SIZE * 1.025, x1:_, x2:_)
383
+ field << tick_group = Group.new(x: _, y: DEFAULT_SIZE * 1.025, rotate: 90)
384
+ tick_group << (Text.new(x: 0, y: 0, size: size,
385
+ #baseline: "text-before-edge",
386
+ anchor: "begin",
387
+ #style: "writing-mode: tb;",
388
+ ) << tick.to_s)
389
+ }
390
+ ticks_y.each{ |tick| _ = f_y[tick]
391
+ field << Line.new(x2: -DEFAULT_SIZE * 0.025, y1:_, y2:_) <<
392
+ (Text.new(y: DEFAULT_SIZE - _, x: -DEFAULT_SIZE * left * 0.3, size: size, anchor: "end") << tick.to_s)
393
+ }
394
+ @to_s_cached = super
395
+ end
396
+ end
397
+ end
398
+
399
+ end
400
+
401
+
402
+ if __FILE__ == $0
403
+ puts `ruby -Cexamples chart.rb`
404
+ end
405
+
metadata ADDED
@@ -0,0 +1,44 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rsvgr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Victor Maslov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-07-10 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: not ready for public usage -- uploaded for personal usage
14
+ email: nakilon@gmail.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/rsvgr.rb
20
+ homepage: http://rubygems.org/gems/rsvgr
21
+ licenses:
22
+ - MIT
23
+ metadata: {}
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 2.0.0
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ requirements: []
39
+ rubyforge_project:
40
+ rubygems_version: 2.0.14
41
+ signing_key:
42
+ specification_version: 4
43
+ summary: Ruby SVG Renderer.
44
+ test_files: []