svg-lib 0.0.5 → 0.0.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0958d94cf32f41cfab9959fb9a0b1334feb3fdad141179787c16f61b632211fe'
4
- data.tar.gz: 7d03990d325e1cfd66df81a112eee1b39dbdc9ee4f90701bd22a6d2e75722f65
3
+ metadata.gz: 1838ec9557c2332e4ca7a8daa07b3d3d64ae0aebccd8d373f88bcbec7a2ee5fd
4
+ data.tar.gz: f5b7bdf0371fd15426349d87e9cf80204aae66d4c719f26088830c400f22bf27
5
5
  SHA512:
6
- metadata.gz: 75509788882b34a492f5192a0ed2fbba9ad021cb0a90ac5ae5f5df9a66cc34cddbbb77dd423dea3fcaced8bb003281a745efd702d0769ff015b35156df002b6f
7
- data.tar.gz: b79c3d399c12bac44f10460a090c89ef6264ca0dd59078ebf73406c98210b1ca443ccd2858673d8cf8ee4eee753ce54b7dfa05a77b4836008baf99a77dab955a
6
+ metadata.gz: 1ec3399c2bb0cdf2a8da8acd9c83513d2e7563a4c0db4a81a3c74b9d587417edec65a34fec9671b18a07818e1e34766fb01bffff7a74c5768ab624cebe27e5b2
7
+ data.tar.gz: fea5449f4ccf671806db77cc29cfc9e48c53063324e6f20d5be2799fd9b627bb788cfee0564e3b94a76b5d5cd2398dbbdb009ea8db3c238b9b90f776b9079faa
data/bin/eplot CHANGED
@@ -11,6 +11,8 @@ opts[:ylog] = false
11
11
  opts[:xlog] = false
12
12
  opts[:legend] = false
13
13
  opts[:scatterplot] = false
14
+ opts[:lineplot] = false
15
+ opts[:boxplot] = false
14
16
  opts[:minx] = nil
15
17
  opts[:maxx] = nil
16
18
  opts[:miny] = nil
@@ -22,8 +24,9 @@ opts[:linestyles] = []
22
24
  opts[:title] = ""
23
25
  opts[:xlabel] = ""
24
26
 
25
- opts[:width] = 500
26
- opts[:height] = 500
27
+ opts[:width] = 800
28
+ opts[:height] = 800
29
+ opts[:noaxis] = false
27
30
 
28
31
  progname = File.basename($PROGRAM_NAME,File.extname($PROGRAM_NAME))
29
32
  parser = OptionParser.new do |parser|
@@ -47,6 +50,10 @@ parser = OptionParser.new do |parser|
47
50
  opts[:scatterplot] = true
48
51
  end
49
52
 
53
+ parser.on("--boxplot","boxplot mode") do
54
+ opts[:boxplot] = true
55
+ end
56
+
50
57
  parser.on("--ylog","show logarithmic axis") do
51
58
  opts[:ylog] = true
52
59
  end
@@ -122,15 +129,15 @@ end
122
129
 
123
130
  # MAIN PROGRAM ================================================================
124
131
 
125
- SVG.new(x: 0.0, y: 0.0, width: opts[:width], height: opts[:height],font_family: "monospace") do |svg|
132
+ SVG.new(x: 0.0, y: 0.0, width: opts[:width], height: opts[:height],font_family: "monospaced") do |svg|
126
133
  plt = SVG::Plot.new(svg)
127
134
  plt.title = opts[:title];
128
135
  plt.xlabel = opts[:xlabel];
129
136
  plt.ylabel = opts[:ylabel];
130
- plt.minx = opts[:minx];
131
- plt.maxx = opts[:maxx];
132
- plt.miny = opts[:miny];
133
- plt.maxy = opts[:maxy];
137
+ plt.minx = opts[:minx] || plt.minx
138
+ plt.maxx = opts[:maxx] || plt.maxx
139
+ plt.miny = opts[:miny] || plt.miny
140
+ plt.maxy = opts[:maxy] || plt.maxy
134
141
 
135
142
 
136
143
  datavar = []
@@ -157,30 +164,42 @@ SVG.new(x: 0.0, y: 0.0, width: opts[:width], height: opts[:height],font_family:
157
164
  x = (1 .. data.size).to_a.map{|e| e.to_f}
158
165
  data = data.map{|e| e[0]}
159
166
  end
167
+
160
168
  rawx.push(x)
161
169
  rawdata.push(data)
162
- end
170
+ end
163
171
 
164
172
  plt.minmax(rawx,rawdata,xlog: opts[:xlog], ylog: opts[:ylog])
165
173
 
166
- rawdata.each_with_index do |y,i|
167
- if opts[:scatterplot]
168
- plt.scatter(rawx[i],y,
169
- ylog: opts[:ylog],
170
- label: opts[:labels][i],
171
- marker: opts[:markers][i])
172
- else
173
- plt.plot(rawx[i],y,
174
- ylog: opts[:ylog],
175
- label: opts[:labels][i],
176
- marker: opts[:markers][i],
177
- linestyle: opts[:linestyles][i])
174
+
175
+ yticks,ylabels = plt.yticks(log: opts[:ylog])
176
+ if opts[:boxplot]
177
+ plt.boxplot(rawdata,ylog: opts[:ylog],labels: opts[:labels])
178
+ xticks,xlabels = plt.boxplotticks(rawdata.size)
179
+ xlabels = ["",*opts[:labels],""]
180
+ plt.axis(xticks,xlabels,stroke: "black",fill: "black",flip: true,rotang: -15) unless opts[:noaxis]
181
+ else
182
+ xticks,xlabels = plt.xticks(log: opts[:xlog])
183
+ rawdata.each_with_index do |y,i|
184
+ if opts[:scatterplot]
185
+ plt.scatter(rawx[i],y,
186
+ ylog: opts[:ylog],
187
+ label: opts[:labels][i],
188
+ marker: opts[:markers][i])
189
+ else
190
+ plt.plot(rawx[i],y,
191
+ ylog: opts[:ylog],
192
+ label: opts[:labels][i],
193
+ marker: opts[:markers][i],
194
+ linestyle: opts[:linestyles][i])
195
+ end
178
196
  end
179
197
 
198
+ plt.axis(xticks,xlabels,flip: true,rotang: 0) unless opts[:noaxis]
180
199
  end
181
200
 
201
+ plt.axis(yticks,ylabels) unless opts[:noaxis]
182
202
  plt.legend if opts[:legend]
183
- plt.axes(xlog: opts[:xlog],ylog: opts[:ylog])
184
203
  plt.title
185
204
  end
186
205
 
@@ -26,10 +26,6 @@ class SVG
26
26
  end
27
27
 
28
28
  attr_accessor :minx,:maxx,:miny,:maxy
29
- @minx = -1.0/0.0
30
- @maxx = 1.0/0.0
31
- @miny = -1.0/0.0
32
- @maxy = 1.0/0.0
33
29
 
34
30
  def minmax(x,y,xlog: false,ylog: false)
35
31
  miny,maxy = y.flatten.minmax
@@ -61,14 +57,14 @@ class SVG
61
57
  attr_accessor :legendfontsize,:legendbgcolor,:legendbgopacity,:legendfgcolor,
62
58
  :legendlinelen,:legendheight,:legendpad
63
59
  attr_accessor :title, :titlefontsize
64
-
60
+
65
61
  def initialize(svg)
66
62
  @svg = svg
67
63
 
68
64
  @padleft = 100
69
65
  @padright = 50
70
66
  @padtop = 50
71
- @padbottom = 80
67
+ @padbottom = 90
72
68
  @colors = []
73
69
  @labels = []
74
70
  @markers = []
@@ -84,34 +80,48 @@ class SVG
84
80
  @axcolor = "black"
85
81
  @axlinewidth = 2
86
82
  @axlabelstrokewidth = 0.5
87
- @axlabelfontsize = 20
83
+ @axlabelfontsize = 25
84
+ @xlabelformat = "%-0.4f"
85
+ @ylabelformat = "%-0.4f"
88
86
 
89
87
 
90
88
  @ticksnum = 5
91
89
  @tickslen = 10
92
90
  @tickstextlen = 60
93
- @ticksfontsize = 12
91
+ @ticksfontsize = 18
94
92
  @ticksstrokewidth = 0.1
95
93
 
96
- @legendfontsize = 10
94
+ @legendfontsize = 20
97
95
  @legendbgcolor = "white"
98
- @legendbgopacity = 0.5
96
+ @legendbgopacity = 0.8
99
97
  @legendfgcolor = "black"
100
98
  @legendlinelen = 50
101
99
  @legendheight = 100
102
100
  @legendpad = 10
103
101
 
104
102
  @title = ""
105
- @titlefontsize = 20
103
+ @titlefontsize = 40
104
+
105
+ @minx = -1.0/0.0
106
+ @maxx = 1.0/0.0
107
+ @miny = -1.0/0.0
108
+ @maxy = 1.0/0.0
109
+
110
+ end
111
+
112
+ def dark_mode
113
+ @axcolor = "white"
114
+ @legendbgcolor = "black"
115
+ @legendfgcolor = "white"
106
116
  end
107
117
 
108
118
  def self.mapxcoords(x,minx,maxx,w,padleft,padright,log)
109
- x = log ? Math.log(x,10) : x
119
+ begin x = log ? Math.log(x,10) : x rescue x = 1e-12 end
110
120
  xnew = padleft + ((x - minx)/((maxx-minx) + 1e-100))*(w-(padleft + padright))
111
121
  end
112
122
 
113
123
  def self.mapycoords(y,miny,maxy,h,padtop,padbottom,log)
114
- y = log ? Math.log(y,10) : y
124
+ begin y = log ? Math.log(y,10) : y rescue y = 1e-12 end
115
125
  ynew = padtop + ((maxy - y)/((maxy-miny) + 1e-100))*(h-(padtop + padbottom))
116
126
  end
117
127
 
@@ -136,71 +146,6 @@ class SVG
136
146
  @opacities.push(opacity)
137
147
  end
138
148
 
139
- def scatter(
140
- xraw, yraw,
141
- minx: @minx, maxx: @maxx, miny: @miny, maxy: @maxy,
142
- label: nil,
143
- color: nil,
144
- marker: nil,
145
- opacity: nil,
146
- xlog: false, ylog: false,**args)
147
-
148
- self.set_plot_style(label,color,marker,"",opacity)
149
- points = self.map_and_zip(xraw,yraw,minx,maxx,miny,maxy,xlog,ylog)
150
-
151
- @svg.g(id: @labels.last, stroke: "none",fill: @colors.last,opacity: @opacities.last) do |g|
152
- points.each_with_index do |(x,y),i|
153
- self.marker(@markers.last,x,y,"%g" % yraw[i])
154
- end
155
- end
156
-
157
- end
158
-
159
- def plot(
160
- xraw, yraw,
161
- minx: @minx,maxx: @maxx,miny: @miny,maxy: @maxy,
162
- label: nil,
163
- color: nil,
164
- marker: nil,
165
- linestyle: nil,
166
- opacity: nil,
167
- xlog: false,ylog: false)
168
-
169
- self.set_plot_style(label,color,marker,linestyle,opacity)
170
- points = self.map_and_zip(xraw,yraw,minx,maxx,miny,maxy,xlog,ylog)
171
-
172
- @svg.g(
173
- id: @labels.last,
174
- stroke: @colors.last,
175
- fill: @colors.last,
176
- stroke_width: @linewidth,
177
- opacity: @opacities.last) do |g|
178
-
179
- g.path(title: @labels.last,fill: "none",stroke_dasharray: self.linestyle(linestyle)) do |path|
180
- missing = true
181
- points.each do |(x,y)|
182
- if !x.finite? || !y.finite?
183
- missing = true
184
- next
185
- end
186
- if missing
187
- path.m(x,y)
188
- missing = false
189
- end
190
- path.l(x,y)
191
- end
192
- end
193
-
194
- g.g(id: @labels.last+"-markers", fill: @colors.last) do |mg|
195
- points.each_with_index do |(x,y),i|
196
- next if ((i % (points.size.to_i / @markernum.to_i)) != 0) || !x.finite? || !y.finite?
197
- self.marker(@markers.last,x,y,yraw[i].round(2))
198
- end
199
- end
200
- end
201
-
202
- end
203
-
204
149
  def title
205
150
  @svg.g(id: "title",text_anchor: "middle") do |g|
206
151
  g.text(
@@ -211,7 +156,8 @@ class SVG
211
156
  end
212
157
 
213
158
  def legend
214
- legendwidth = @legendlinelen + 2*@legendpad + @legendfontsize*@labels.map{|e| e.size}.max
159
+ legendtextwidth = @legendfontsize*@labels.map{|e| e.size}.max / 2
160
+ legendwidth = @legendlinelen + 3*@legendpad + legendtextwidth
215
161
  legendheight = 2*@legendpad + @labels.size*2*@legendfontsize
216
162
 
217
163
  t = SVG.transform do |t|
@@ -229,7 +175,7 @@ class SVG
229
175
  opacity: @legendbgopacity)
230
176
 
231
177
  @labels.size.times do |i|
232
- y = (i+1)*(2*@legendfontsize)
178
+ y = @legendpad + @legendfontsize + (i*2*@legendfontsize)
233
179
  g.g(id: "legend-marker-line-"+@labels[i],fill: @colors[i],stroke: @colors[i]) do |g|
234
180
  g.line(
235
181
  x1: @legendpad,
@@ -244,292 +190,134 @@ class SVG
244
190
  "",
245
191
  @legendfontsize)
246
192
  end
247
- g.text(x: @legendpad+@legendfontsize+@legendlinelen,y: y ,text: @labels[i])
193
+ g.text(x: @legendpad+@legendfontsize+@legendlinelen,y: y ,
194
+ stroke: "none" ,
195
+ fill: @legendfgcolor,
196
+ text: @labels[i],
197
+ font_size: @legendfontsize)
248
198
  end
249
199
  end
250
200
  end
251
201
 
252
- def xaxis(min= @minx,max= @maxx,log= false)
253
- @svg.g(id: "xaxis",text_anchor: "middle") do |xaxis|
254
- xaxis.path(fill: "none") do |path|
255
- path.m(@padleft,@svg.height-@padbottom)
256
- path.l(@svg.width-@padright,@svg.height - @padbottom)
202
+ def axis(ticks,labels, flip: false, rotang: 0,**args)
203
+ min,max = ticks[0], ticks[-1]
204
+ # Dibujar el eje
205
+ @svg.g(id: "axis#{flip ? @xlabel : @ylabel}", text_anchor: "middle",stroke: @axcolor, fill: @axcolor,**args) do |axis|
206
+ axis.path() do |path|
207
+ path.m(min[0],min[1])
208
+ path.l(max[0],max[1])
257
209
  end
258
210
 
211
+ # Mostrar nombre
259
212
  t = SVG.transform do |t|
260
- t.translate(@svg.width / 2,@svg.height-@axlabelfontsize)
261
- end
262
- xaxis.g(id: "xlabel",transform: t) do |g|
263
- g.text(text: @xlabel ,x: 0,y: 0,stroke_width: @axlabelstrokewidth)
264
- end
265
-
266
-
267
- tickypos = @svg.height - @padbottom - (@tickslen / 2)
268
- ticks = [[min,Plot.mapxcoords(min,min,max,@svg.width,@padleft,@padright,false)]]
269
- @ticksnum.times do |i|
270
- v = min + ((max - min)/(@ticksnum+1))*(i+1)
271
- ticks.push([v,Plot.mapxcoords(v,min,max,@svg.width,@padleft,@padright,false)])
272
- end
273
- ticks.push([max,Plot.mapxcoords(max,min,max,@svg.width,@padleft,@padright,false)])
274
-
275
- # Dibujar los ticks
276
- xaxis.path do |tickspath|
277
- ticks.each do |tick|
278
- tickspath.m(tick.last,tickypos)
279
- tickspath.l(0,@tickslen,relative: true)
213
+ if flip
214
+ t.translate(@svg.width/2,@svg.height - @axlabelfontsize/2)
215
+ else
216
+ t.translate(@axlabelfontsize/2,@svg.height/2)
217
+ t.rotate(-90,0,0)
280
218
  end
281
219
  end
282
-
283
- tickypos += @tickstextlen/2
284
- ticks.each do |tick|
285
- txt = tick.first.round(2).to_s
286
- txt = log ? "10^"+txt : txt
287
- xaxis.text(
288
- x: tick.last,
289
- y: tickypos,
290
- text: txt,
291
- font_size: @ticksfontsize,
292
- stroke_width: @ticksstrokewidth)
293
-
294
- end
295
-
296
- end
297
- end
298
-
299
- def yaxis(min = @miny,max = @maxy ,log = false)
300
- @svg.g(id: "yaxis") do |yaxis|
301
- yaxis.path(fill: "none") do |path|
302
- path.m(@padleft,@padtop)
303
- path.l(@padleft,@svg.height-@padbottom)
304
- end
305
220
 
306
- # Mostrar el nombre
307
- t = SVG.transform do |t|
308
- t.translate(@axlabelfontsize,@svg.height/2)
309
- t.rotate(-90,0,0)
310
- end
311
- yaxis.g(id: "yaxislabel",transform: t) do |g|
312
- g.text(text: @ylabel ,x: 0,y: 0,stroke_width: @axlabelstrokewidth)
313
- end
314
-
315
-
316
- # Obtener la posición de los ticks
317
- tickxpos = @padleft-@tickslen/2
318
- ticks = [[min,Plot.mapycoords(min,min,max,@svg.height,@padtop,@padbottom,false)]]
319
- @ticksnum.times do |i|
320
- v = min + ((max - min) / (@ticksnum+1)) * (i+1)
321
- ticks.push([v,Plot.mapycoords(v,min,max,@svg.height,@padtop,@padbottom,false)])
322
- end
323
- ticks.push([max,Plot.mapycoords(max,min,max,@svg.height,@padtop,@padbottom,false)])
221
+ axis.text(text: flip ? @xlabel : @ylabel, x: 0, y: 0, stroke_width: @axlabelstrokewidth,transform: t,
222
+ font_size: @axlabelfontsize)
324
223
 
325
- # Dibujar los ticks
326
- yaxis.path do |tickspath|
327
- ticks.each do |tick|
328
- tickspath.m(tickxpos,tick.last)
224
+ # Colocar los ticks
225
+ axis.path do |tickspath|
226
+ (0 ... ticks.size).each do |i|
227
+ x,y = ticks[i]
228
+ tickspath.m(x-@tickslen/2,y)
329
229
  tickspath.l(@tickslen,0,relative: true)
230
+ tickspath.m(x,y-@tickslen/2)
231
+ tickspath.l(0,@tickslen,relative: true)
330
232
  end
331
233
  end
332
234
 
333
235
  # Colocar los valores
334
- tickxpos -= @tickstextlen/2
335
- ticks.each do |tick|
336
- txt = tick.first.round(2).to_s
337
- txt = log ? "10^"+txt : txt
338
- yaxis.text(
339
- x: tickxpos, y: tick.last,
340
- text: txt,
341
- text_anchor: "middle",
236
+ (0 ... ticks.size).each do |i|
237
+ x,y = ticks[i]
238
+ x += flip ? 0 : -@tickslen
239
+ y += !flip ? 0 : @tickslen + @ticksfontsize*1.5
240
+ text = labels[i]
241
+ axis.text(
242
+ x: x,
243
+ y: y ,
244
+ text: text,
245
+ text_anchor: flip ? "middle" : "end",
342
246
  stroke_width: @ticksstrokewidth,
343
- font_size: @ticksfontsize)
247
+ font_size: @ticksfontsize,
248
+ transform: SVG.transform{|t| t.rotate(rotang,x,y)})
344
249
  end
345
250
  end
346
- end
347
-
348
- def axes(minx: @minx,maxx: @maxx,miny: @miny,maxy: @maxy,xlog: false,ylog: false)
349
- @svg.g(id: "axis" , stroke: @axcolor,fill: @axcolor,stroke_width: @axlinewidth) do |g|
350
- self.xaxis(minx,maxx,xlog)
351
- self.yaxis(miny,maxy,ylog)
352
- end
353
- end
251
+ end
354
252
 
355
- def linestyle(style)
356
- case style
357
- when "-" then "5 0"
358
- when "--","dashed" then "5"
359
- when "-.","dashdot" then "6 3 3 3"
360
- when ":","dotted" then "2"
361
- when " ","","none" then "0 1"
362
- else style
363
- end
364
- end
253
+ def yticks(min: @miny,max: @maxy,frmt: @ylabelformat, ticksnum: @ticksnum,log: false)
254
+ ticks = []
255
+ labels = []
365
256
 
366
- def marker(marker,x,y,val,ms = @markersize)
367
- t = SVG.transform do |t|
368
- t.translate(x,y)
369
- t.scale(ms)
370
- case marker
371
- when "D","x","X" then t.rotate(45)
372
- when ">","2","H" then t.rotate(90)
373
- when "v","3" then t.rotate(180)
374
- when "<","4" then t.rotate(270)
375
- else nil
257
+ ticks.push([@padleft,Plot.mapycoords(min,min,max,@svg.height,@padtop,@padbottom,log)])
258
+ labels.push(frmt % min)
259
+ ticksnum.times do |i|
260
+ v = min + ((max - min) / (ticksnum+1)) * (i+1)
261
+ ticks.push([@padleft,Plot.mapycoords(v,min,max,@svg.height,@padtop,@padbottom,log)])
262
+ labels.push(frmt % v)
376
263
  end
377
- end
378
-
379
- case marker
380
- when "." then self.marker_point(t,val)
381
- when "," then self.marker_pixel(t,val)
382
- when "o" then self.marker_circle(t,val)
383
- when "^",">","v","<" then self.marker_triangle(t,val)
384
- when "s","D" then self.marker_square(t,val)
385
- when "d" then self.marker_diamond(t,val)
386
- when "+","x" then self.marker_plus(t,0.1,val)
387
- when "P","X" then self.marker_plus(t,0.2,val)
388
- when "1","2","3","4" then self.marker_tri(t,val)
389
- when "h","H" then self.marker_hexagon(t,val)
390
- when "p" then self.marker_pentagon(t,val)
391
- when "8" then self.marker_octagon(t,val)
392
- when "*" then self.marker_star(t,val)
393
- else raise "unkown marker #{marker}"
394
- end
395
- end
396
-
397
- def marker_point(t,val="")
398
- @svg.circle(cx: 0, cy: 0, r: 0.3, transform: t,title: val,stroke_width: 0)
264
+ ticks.push([@padleft,Plot.mapycoords(max,min,max,@svg.height,@padtop,@padbottom,log)])
265
+ labels.push(frmt % max)
266
+ return ticks,labels
399
267
  end
400
268
 
401
- def marker_pixel(t,val="")
402
- @svg.circle(cx: 0, cy: 0, r: 0.2, transform: t,title: val,stroke_width: 0)
403
- end
269
+ def boxplotticks(nboxes,boxscale = 0.5)
270
+ ticks = []
271
+ labels = []
404
272
 
405
- def marker_circle(t,val="")
406
- @svg.circle(cx: 0, cy: 0, r: 0.45, transform: t,title: val,stroke_width: 0)
407
- end
408
-
409
- def marker_triangle(t,val="")
410
- @svg.path(transform: t, title: val,stroke_width: 0) do |path|
411
- path.m(0,-0.5)
412
- path.l(0.5,0.5)
413
- path.l(-0.5,0.5)
414
- path.z
415
- end
416
- end
273
+ w = (@svg.width - (@padleft + @padright)) / nboxes
274
+ boxwidth = boxscale * w
417
275
 
418
- def marker_square(t,val="")
419
- @svg.path(transform: t, title: val,stroke_width: 0) do |path|
420
- path.m(-0.4,-0.4)
421
- path.h(0.4)
422
- path.v(0.4)
423
- path.h(-0.4)
424
- path.z
276
+ ticks.push([@padleft,@svg.height - @padbottom])
277
+ labels.push("")
278
+ nboxes.times do |i|
279
+ x = @padleft + w*(i.to_f+0.5)
280
+ ticks.push([x,@svg.height - @padbottom])
281
+ labels.push("%d" % i)
425
282
  end
426
- end
427
283
 
428
- def marker_diamond(t,val="")
429
- @svg.path(transform: t, title: val,stroke_width: 0) do |path|
430
- path.m(0,-0.5)
431
- path.l(0.3,0)
432
- path.l(0,0.5)
433
- path.l(-0.3,0.0)
434
- path.z
435
- end
284
+ ticks.push([@svg.width - @padright,@svg.height - @padbottom])
285
+ labels.push("")
286
+ return ticks,labels
436
287
  end
437
288
 
438
- def marker_plus(t,width,val="")
439
- @svg.path(transform: t, title: val,stroke_width: 0 ) do |path|
440
- path.m(-width,-0.5)
441
- path.h(width)
442
- path.v(0.5)
443
- path.h(-width)
444
- path.z
445
- end
446
- @svg.path(transform: t, title: val,stroke_width: 0 ) do |path|
447
- path.m(-0.5,-width)
448
- path.v(width)
449
- path.h(0.5)
450
- path.v(-width)
451
- path.z
452
- end
453
- end
289
+ def xticks(min: @minx, max: @maxy, ticksnum: @ticksnum, frmt: @xlabelformat,log: false)
290
+ ticks = []
291
+ labels = []
454
292
 
455
- def marker_tri(t,val="")
456
- @svg.g(title: val, transform: t,stroke_width: 0) do |g|
457
- width = 0.1
458
- @svg.path(title: val) do |path|
459
- path.m(-width,0.5)
460
- path.h(width)
461
- path.v(0.0)
462
- path.h(-width)
463
- path.z
464
- end
465
- @svg.path(
466
- title: val,
467
- transform: SVG.transform{|t| t.rotate(135)}) do |path|
468
- path.m(-width,0.5)
469
- path.h(width)
470
- path.v(0.0)
471
- path.h(-width)
472
- path.z
473
- end
474
- @svg.path(
475
- title: val,
476
- transform: SVG.transform{|t| t.rotate(225)}) do |path|
477
- path.m(-width,0.5)
478
- path.h(width)
479
- path.v(0.0)
480
- path.h(-width)
481
- path.z
482
- end
293
+ ticks.push([Plot.mapxcoords(min,min,max,@svg.height,@padleft,@padright,log),@svg.height-@padbottom])
294
+ labels.push(frmt % min)
295
+ ticksnum.times do |i|
296
+ v = min + ((max - min) / (ticksnum+1)) * (i+1)
297
+ ticks.push([Plot.mapxcoords(v,min,max,@svg.height,@padleft,@padright,log),@svg.height-@padbottom])
298
+ labels.push(frmt % v)
483
299
  end
484
- end
485
-
486
- def marker_hexagon(t,val="")
487
- @svg.path(title: val, transform: t,stroke_width: 0) do |path|
488
- path.m( 0.00,-0.50)
489
- path.l( 0.40,-0.20)
490
- path.l( 0.40, 0.30)
491
- path.l( 0.00, 0.50)
492
- path.l(-0.40, 0.30)
493
- path.l(-0.40,-0.20)
494
- path.z
495
- end
496
- end
300
+ ticks.push([Plot.mapxcoords(max,min,max,@svg.height,@padleft,@padright,log),@svg.height-@padbottom])
301
+ labels.push(frmt % max)
302
+ return ticks,labels
497
303
 
498
- def marker_pentagon(t,val="")
499
- @svg.path(title: val, transform: t,stroke_width: 0) do |path|
500
- path.m( 0.00,-0.50)
501
- path.l( 0.45,-0.10)
502
- path.l( 0.30, 0.50)
503
- path.l(-0.30, 0.50)
504
- path.l(-0.45,-0.10)
505
- path.z
506
- end
507
304
  end
508
305
 
509
- def marker_octagon(t,val="")
510
- @svg.path(title: val, transform: t,stroke_width: 0) do |path|
511
- path.m(-0.25,-0.50)
512
- path.l( 0.18,-0.50)
513
- path.l( 0.50,-0.25)
514
- path.l( 0.50, 0.18)
515
- path.l( 0.18, 0.50)
516
- path.l(-0.25, 0.50)
517
- path.l(-0.50, 0.18)
518
- path.l(-0.50,-0.25)
519
- path.z
306
+ def linestyle(style)
307
+ case style
308
+ when "-" then "5 0"
309
+ when "--","dashed" then "5"
310
+ when "-.","dashdot" then "6 3 3 3"
311
+ when ":","dotted" then "2"
312
+ when " ","","none" then "0 1"
313
+ else style
520
314
  end
521
315
  end
522
316
 
523
- def marker_star(t,val="")
524
- @svg.path(title: val, transform: t,stroke_width: 0) do |path|
525
- path.m(0,-0.5)
526
- path.l(-0.3,0.5)
527
- path.l(0.5,-0.1)
528
- path.l(-0.5,-0.1)
529
- path.l(0.3,0.5)
530
- path.z
531
- end
532
- end
533
317
 
534
318
  end
535
319
  end
320
+ require "plot2d/markers.rb"
321
+ require "plot2d/scatter.rb"
322
+ require "plot2d/lineplot.rb"
323
+ require "plot2d/boxplot.rb"
@@ -0,0 +1,142 @@
1
+ class SVG
2
+ class Plot
3
+
4
+ def boxplot(
5
+ boxesdata,
6
+ colors: @@colors,
7
+ markers: @@markers,
8
+ linewidths: nil,
9
+ labels: nil,
10
+ opacity: nil,
11
+ ylog: false, **args)
12
+
13
+ @colors = colors.dup
14
+ @markers = markers.dup
15
+ @labels = labels.dup
16
+ linewidths = linewidths || @colors.size
17
+
18
+ #
19
+ median = lambda do |arr|
20
+ len = arr.size
21
+ if arr.size.even?
22
+ return (arr[len/2] + arr[(len+1)/2])/2.0
23
+ else
24
+ return arr[len/2]
25
+ end
26
+ end
27
+
28
+ mean = lambda do |arr|
29
+ return arr.reduce(0.0){|acc,d| acc + d} / arr.size
30
+ end
31
+
32
+ std = lambda do |arr,mean|
33
+ return (arr.reduce(0.0){|acc,d| acc + (d - mean)**2}/(arr.size-1))**0.5
34
+ end
35
+
36
+
37
+ # Cantidad de cajas
38
+ nboxes = boxesdata.size
39
+ min,max = boxesdata.flatten.minmax
40
+
41
+ # Obtener los estadísticos necesarios para el boxplot
42
+ boxes = [ ]
43
+ nboxes.times do |i|
44
+ box = {}
45
+ data = boxesdata[i]
46
+
47
+ sz = data.size;
48
+ data = data.sort
49
+ box[:mean] = mean.call(data)
50
+ box[:q1] = median.call(data[0..data.size/2])
51
+ box[:q2] = median.call(data)
52
+ box[:q3] = median.call(data[(data.size/2)+1..-1])
53
+ box[:iqr] = box[:q3] - box[:q1]
54
+ box[:median] = box[:q2]
55
+ box[:std] = std.call(data,box[:mean])
56
+
57
+ box[:w1] = data[0]
58
+ (0 ... data.size).each do |i|
59
+ if data[i] >= (box[:q1] - (1.5*box[:iqr]))
60
+ box[:w1] = data[i]
61
+ break
62
+ end
63
+ end
64
+ box[:w2] = data[-1]
65
+ (0 ... data.size).reverse_each do |i|
66
+ if data[i] <= (box[:q3] + (1.5*box[:iqr]))
67
+ box[:w2] = data[i]
68
+ break
69
+ end
70
+ end
71
+
72
+ boxes.push(box)
73
+ end
74
+
75
+ # Modificar el máximo y mínimo para mostrar mejor los boxplots
76
+ @maxy = boxes.map{|b| b[:w2]*1.01}.max
77
+ # @miny = boxes.map{|b| b[:w1]*0.1}.max
78
+
79
+ boxscale = 0.5
80
+ w = (@svg.width - (@padleft + @padright)) / nboxes
81
+ boxes.each_with_index do |box,i|
82
+ @svg.g(id: @labels.last,
83
+ stroke: @colors[i],
84
+ fill: @colors[i],
85
+ stroke_width: @linewidth,
86
+ opacity: @opacities.last) do |g|
87
+
88
+ boxwidth = boxscale * w
89
+ x = @padleft + w*(i.to_f+0.5)
90
+
91
+ ymedian = Plot.mapycoords(box[:median],@miny,@maxy,@svg.height,@padtop,@padbottom,ylog)
92
+
93
+ # median line
94
+ g.line(x1: x - boxwidth/2, y1: ymedian, x2: x + boxwidth/2, y2: ymedian, stroke: "black")
95
+
96
+ # mean line
97
+ ymean = Plot.mapycoords(box[:mean],@miny,@maxy,@svg.height,@padtop,@padbottom,ylog)
98
+ g.line(
99
+ x1: x - boxwidth/2, y1: ymean,
100
+ x2: x + boxwidth/2, y2: ymean,
101
+ stroke_dasharray: self.linestyle("--"),
102
+ stroke: "black")
103
+
104
+ # box
105
+ yq3 = Plot.mapycoords(box[:q3],@miny,@maxy,@svg.height,@padtop,@padbottom,ylog)
106
+ yq1 = Plot.mapycoords(box[:q1],@miny,@maxy,@svg.height,@padtop,@padbottom,ylog)
107
+
108
+ g.rect(x: x-(boxwidth/2), y: yq3, width: boxwidth, height: (yq3-yq1).abs,opacity: 0.2)
109
+ g.rect(x: x-(boxwidth/2), y: yq3, width: boxwidth, height: (yq3-yq1).abs,fill: "none", stroke: "black")
110
+
111
+ # whiskers
112
+ wisker_width = boxwidth/2
113
+ w1 = Plot.mapycoords(box[:w1],@miny,@maxy,@svg.height,@padtop,@padbottom,ylog)
114
+ w2 = Plot.mapycoords(box[:w2],@miny,@maxy,@svg.height,@padtop,@padbottom,ylog)
115
+
116
+ g.path(stroke_dasharray: self.linestyle("-"),stroke: "black") do |wp|
117
+ wp.m(x,w2)
118
+ wp.m(-wisker_width/2,0,relative: true)
119
+ wp.l(wisker_width,0,relative: true)
120
+ wp.m(x,w2)
121
+ wp.l(x,yq3)
122
+
123
+ wp.m(x,w1)
124
+ wp.m(-wisker_width/2,0,relative: true)
125
+ wp.l(wisker_width,0,relative: true)
126
+ wp.m(x,w1)
127
+ wp.l(x,yq1)
128
+ end
129
+ # Draw outliers
130
+ boxesdata[i].each do |y|
131
+ next if (y >= box[:w1] && y <= box[:w2])
132
+ y = Plot.mapycoords(y,@miny,@maxy,@svg.height,@padtop,@padbottom,ylog)
133
+ self.marker(@markers[i],x,y,"%g" % y,8)
134
+ end
135
+
136
+ end
137
+ end
138
+
139
+ end
140
+
141
+ end
142
+ end
@@ -0,0 +1,48 @@
1
+ class SVG
2
+ class Plot
3
+ def plot(
4
+ xraw, yraw,
5
+ minx: @minx,maxx: @maxx,miny: @miny,maxy: @maxy,
6
+ label: nil,
7
+ color: nil,
8
+ marker: nil,
9
+ linestyle: nil,
10
+ opacity: nil,
11
+ xlog: false,ylog: false)
12
+
13
+ self.set_plot_style(label,color,marker,linestyle,opacity)
14
+ points = self.map_and_zip(xraw,yraw,minx,maxx,miny,maxy,xlog,ylog)
15
+
16
+ @svg.g(
17
+ id: @labels.last,
18
+ stroke: @colors.last,
19
+ fill: @colors.last,
20
+ stroke_width: @linewidth,
21
+ opacity: @opacities.last) do |g|
22
+
23
+ g.path(title: @labels.last,fill: "none",stroke_dasharray: self.linestyle(linestyle)) do |path|
24
+ missing = true
25
+ points.each do |(x,y)|
26
+ if !x.finite? || !y.finite?
27
+ missing = true
28
+ next
29
+ end
30
+ if missing
31
+ path.m(x,y)
32
+ missing = false
33
+ end
34
+ path.l(x,y)
35
+ end
36
+ end
37
+
38
+ g.g(id: @labels.last+"-markers", fill: @colors.last) do |mg|
39
+ points.each_with_index do |(x,y),i|
40
+ next if ((i % (points.size.to_i / @markernum.to_i)) != 0) || !x.finite? || !y.finite?
41
+ self.marker(@markers.last,x,y,yraw[i].round(2))
42
+ end
43
+ end
44
+ end
45
+
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,171 @@
1
+ class SVG
2
+ class Plot
3
+ def marker(marker,x,y,val,ms = @markersize)
4
+ t = SVG.transform do |t|
5
+ t.translate(x,y)
6
+ t.scale(ms)
7
+ case marker
8
+ when "D","x","X" then t.rotate(45)
9
+ when ">","2","H" then t.rotate(90)
10
+ when "v","3" then t.rotate(180)
11
+ when "<","4" then t.rotate(270)
12
+ else nil
13
+ end
14
+ end
15
+
16
+ case marker
17
+ when "." then self.marker_point(t,val)
18
+ when "," then self.marker_pixel(t,val)
19
+ when "o" then self.marker_circle(t,val)
20
+ when "^",">","v","<" then self.marker_triangle(t,val)
21
+ when "s","D" then self.marker_square(t,val)
22
+ when "d" then self.marker_diamond(t,val)
23
+ when "+","x" then self.marker_plus(t,0.1,val)
24
+ when "P","X" then self.marker_plus(t,0.2,val)
25
+ when "1","2","3","4" then self.marker_tri(t,val)
26
+ when "h","H" then self.marker_hexagon(t,val)
27
+ when "p" then self.marker_pentagon(t,val)
28
+ when "8" then self.marker_octagon(t,val)
29
+ when "*" then self.marker_star(t,val)
30
+ else raise "unkown marker #{marker}"
31
+ end
32
+ end
33
+
34
+ def marker_point(t,val="")
35
+ @svg.circle(cx: 0, cy: 0, r: 0.3, transform: t,title: val,stroke_width: 0)
36
+ end
37
+
38
+ def marker_pixel(t,val="")
39
+ @svg.circle(cx: 0, cy: 0, r: 0.2, transform: t,title: val,stroke_width: 0)
40
+ end
41
+
42
+ def marker_circle(t,val="")
43
+ @svg.circle(cx: 0, cy: 0, r: 0.45, transform: t,title: val,stroke_width: 0)
44
+ end
45
+
46
+ def marker_triangle(t,val="")
47
+ @svg.path(transform: t, title: val,stroke_width: 0) do |path|
48
+ path.m(0,-0.5)
49
+ path.l(0.5,0.5)
50
+ path.l(-0.5,0.5)
51
+ path.z
52
+ end
53
+ end
54
+
55
+ def marker_square(t,val="")
56
+ @svg.path(transform: t, title: val,stroke_width: 0) do |path|
57
+ path.m(-0.4,-0.4)
58
+ path.h(0.4)
59
+ path.v(0.4)
60
+ path.h(-0.4)
61
+ path.z
62
+ end
63
+ end
64
+
65
+ def marker_diamond(t,val="")
66
+ @svg.path(transform: t, title: val,stroke_width: 0) do |path|
67
+ path.m(0,-0.5)
68
+ path.l(0.3,0)
69
+ path.l(0,0.5)
70
+ path.l(-0.3,0.0)
71
+ path.z
72
+ end
73
+ end
74
+
75
+ def marker_plus(t,width,val="")
76
+ @svg.path(transform: t, title: val,stroke_width: 0 ) do |path|
77
+ path.m(-width,-0.5)
78
+ path.h(width)
79
+ path.v(0.5)
80
+ path.h(-width)
81
+ path.z
82
+ end
83
+ @svg.path(transform: t, title: val,stroke_width: 0 ) do |path|
84
+ path.m(-0.5,-width)
85
+ path.v(width)
86
+ path.h(0.5)
87
+ path.v(-width)
88
+ path.z
89
+ end
90
+ end
91
+
92
+ def marker_tri(t,val="")
93
+ @svg.g(title: val, transform: t,stroke_width: 0) do |g|
94
+ width = 0.1
95
+ @svg.path(title: val) do |path|
96
+ path.m(-width,0.5)
97
+ path.h(width)
98
+ path.v(0.0)
99
+ path.h(-width)
100
+ path.z
101
+ end
102
+ @svg.path(
103
+ title: val,
104
+ transform: SVG.transform{|t| t.rotate(135)}) do |path|
105
+ path.m(-width,0.5)
106
+ path.h(width)
107
+ path.v(0.0)
108
+ path.h(-width)
109
+ path.z
110
+ end
111
+ @svg.path(
112
+ title: val,
113
+ transform: SVG.transform{|t| t.rotate(225)}) do |path|
114
+ path.m(-width,0.5)
115
+ path.h(width)
116
+ path.v(0.0)
117
+ path.h(-width)
118
+ path.z
119
+ end
120
+ end
121
+ end
122
+
123
+ def marker_hexagon(t,val="")
124
+ @svg.path(title: val, transform: t,stroke_width: 0) do |path|
125
+ path.m( 0.00,-0.50)
126
+ path.l( 0.40,-0.20)
127
+ path.l( 0.40, 0.30)
128
+ path.l( 0.00, 0.50)
129
+ path.l(-0.40, 0.30)
130
+ path.l(-0.40,-0.20)
131
+ path.z
132
+ end
133
+ end
134
+
135
+ def marker_pentagon(t,val="")
136
+ @svg.path(title: val, transform: t,stroke_width: 0) do |path|
137
+ path.m( 0.00,-0.50)
138
+ path.l( 0.45,-0.10)
139
+ path.l( 0.30, 0.50)
140
+ path.l(-0.30, 0.50)
141
+ path.l(-0.45,-0.10)
142
+ path.z
143
+ end
144
+ end
145
+
146
+ def marker_octagon(t,val="")
147
+ @svg.path(title: val, transform: t,stroke_width: 0) do |path|
148
+ path.m(-0.25,-0.50)
149
+ path.l( 0.18,-0.50)
150
+ path.l( 0.50,-0.25)
151
+ path.l( 0.50, 0.18)
152
+ path.l( 0.18, 0.50)
153
+ path.l(-0.25, 0.50)
154
+ path.l(-0.50, 0.18)
155
+ path.l(-0.50,-0.25)
156
+ path.z
157
+ end
158
+ end
159
+
160
+ def marker_star(t,val="")
161
+ @svg.path(title: val, transform: t,stroke_width: 0) do |path|
162
+ path.m(0,-0.5)
163
+ path.l(-0.3,0.5)
164
+ path.l(0.5,-0.1)
165
+ path.l(-0.5,-0.1)
166
+ path.l(0.3,0.5)
167
+ path.z
168
+ end
169
+ end
170
+ end
171
+ end
@@ -0,0 +1,24 @@
1
+ class SVG
2
+ class Plot
3
+ def scatter(
4
+ xraw, yraw,
5
+ minx: @minx, maxx: @maxx, miny: @miny, maxy: @maxy,
6
+ label: nil,
7
+ color: nil,
8
+ marker: nil,
9
+ opacity: nil,
10
+ xlog: false, ylog: false,**args)
11
+
12
+ self.set_plot_style(label,color,marker,"",opacity)
13
+ points = self.map_and_zip(xraw,yraw,minx,maxx,miny,maxy,xlog,ylog)
14
+
15
+ @svg.g(id: @labels.last, stroke: "none",fill: @colors.last,opacity: @opacities.last) do |g|
16
+ points.each_with_index do |(x,y),i|
17
+ self.marker(@markers.last,x,y,"%g" % yraw[i])
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: svg-lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ricardo Nieto
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-28 00:00:00.000000000 Z
11
+ date: 2020-10-17 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: basic svg-lib ruby implementation
14
14
  email: nifr91@gmail.com
@@ -20,6 +20,10 @@ files:
20
20
  - bin/eplot
21
21
  - lib/containers.rb
22
22
  - lib/plot2d.rb
23
+ - lib/plot2d/boxplot.rb
24
+ - lib/plot2d/lineplot.rb
25
+ - lib/plot2d/markers.rb
26
+ - lib/plot2d/scatter.rb
23
27
  - lib/shapes.rb
24
28
  - lib/svg-lib.rb
25
29
  - lib/text.rb