ruby-gr 0.61.0.0 → 0.64.0.0

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/gr/plot.rb DELETED
@@ -1,1553 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'gr'
4
- autoload :GR3, 'gr3'
5
-
6
- # FIXME: Plot should not depend on Numo::Narrray unless the GR3 module is required.
7
- # Note: The Plot class has a linspace function that is independent of Numo..
8
- require 'numo/narray'
9
-
10
- module GR
11
- # This class offers a simple, matlab-style API built on top of the GR package.
12
- # The class name Plot may be changed in the future.
13
- class Plot
14
- # Why is the Plot class NOT object-oriented?
15
- #
16
- # Because the code here is mainly ported from GR.jl.
17
- # https://github.com/jheinen/GR.jl/blob/master/src/jlgr.jl
18
- #
19
- # The Python implementation is also Julia compliant.
20
- # https://github.com/sciapp/python-gr
21
- #
22
- # Julia is not an object-oriented language (at least in 2019).
23
- # So, you will see many if branches here.
24
- # This is not the Ruby code style. But it WORKS.
25
- #
26
- # I want to thank Josef Heinen(@jheinen), the creator of GR.jl
27
- # and Florian Rhiem(@FlorianRhiem), the creator of python-gr.
28
- #
29
- # If you are interested in an object-oriented implementation,
30
- # See rubyplot.
31
- # https://github.com/SciRuby/rubyplot
32
-
33
- # Plot kinds conform to GR.jl
34
- PLOT_KIND = %i[line step scatter stem hist contour contourf hexbin heatmap
35
- nonuniformheatmap wireframe surface plot3 scatter3 imshow
36
- isosurface polar polarhist polarheatmap nonuniformpolarheatmap
37
- trisurf tricont shade volume].freeze
38
-
39
- # Keyword options conform to GR.jl.
40
- KW_ARGS = %i[accelerate algorithm alpha ax backgroundcolor barwidth baseline
41
- clabels clear clim color colormap crange figsize font grid
42
- horizontal isovalue kind label labels levels linewidth location
43
- nbins ratio rotation scale size spec subplot tilt title update
44
- xaxis xflip xform xlabel xlim xlog xrange xticks yaxis yflip
45
- ylabel ylim ylog zflip yrange yticks viewport vp where window
46
- zaxis zlabel zlim zlog zrange zticks].freeze
47
-
48
- FONTS = {
49
- times_roman: 101,
50
- times_italic: 102,
51
- times_bold: 103,
52
- times_bolditalic: 104,
53
- helvetica_regular: 105,
54
- helvetica_oblique: 106,
55
- helvetica_bold: 107,
56
- helvetica_boldoblique: 108,
57
- courier_regular: 109,
58
- courier_oblique: 110,
59
- courier_bold: 111,
60
- courier_boldoblique: 112,
61
- symbol: 113,
62
- bookman_light: 114,
63
- bookman_lightitalic: 115,
64
- bookman_demi: 116,
65
- bookman_demiitalic: 117,
66
- newcenturyschlbk_roman: 118,
67
- newcenturyschlbk_italic: 119,
68
- newcenturyschlbk_bold: 120,
69
- newcenturyschlbk_bolditalic: 121,
70
- avantgarde_book: 122,
71
- avantgarde_bookoblique: 123,
72
- avantgarde_demi: 124,
73
- avantgarde_demioblique: 125,
74
- palatino_roman: 126,
75
- palatino_italic: 127,
76
- palatino_bold: 128,
77
- palatino_bolditalic: 129,
78
- zapfchancery_mediumitalic: 130,
79
- zapfdingbats: 131,
80
- cmuserif_math: 232, # original: cmuserif-math
81
- dejavusans: 233
82
- }.freeze
83
-
84
- @last_plot = nil
85
- class << self
86
- attr_accessor :last_plot
87
- end
88
-
89
- attr_accessor :args, :kvs, :scheme
90
-
91
- def initialize(*raw_args)
92
- # Keywords are cloned to avoid disruptive changes
93
- @kvs = raw_args.last.is_a?(Hash) ? raw_args.pop.clone : {}
94
- @args = plot_args(raw_args) # method name is the same as Julia/Python
95
-
96
- # Check keyword options.
97
- kvs.each_key { |k| warn "Unknown keyword: #{k}" unless KW_ARGS.include? k }
98
-
99
- # label(singular form) is a original keyword arg which GR.jl does not have.
100
- kvs[:labels] ||= [kvs[:label]] if kvs.has_key? :label
101
-
102
- # Don't use ||= here, because we need to tell `false` from `nil`
103
- kvs[:size] = [600, 450] unless kvs.has_key? :size
104
- kvs[:ax] = false unless kvs.has_key? :ax
105
- kvs[:subplot] = [0, 1, 0, 1] unless kvs.has_key? :subplot
106
- kvs[:clear] = true unless kvs.has_key? :clear
107
- kvs[:update] = true unless kvs.has_key? :update
108
-
109
- @scheme = 0
110
- @background = 0xffffff
111
- # @handle = nil # This variable will be used in gr_meta
112
-
113
- self.class.last_plot = self
114
- end
115
-
116
- def set_viewport(kind, subplot)
117
- mwidth, mheight, width, height = GR.inqdspsize
118
- dpi = width / mwidth * 0.0254
119
- w, h = if kvs[:figsize]
120
- [(0.0254 * width * kvs[:figsize][0] / mwidth),
121
- (0.0254 * height * kvs[:figsize][1] / mheight)]
122
- elsif dpi > 200
123
- kvs[:size].map { |i| i * dpi / 100 }
124
- else
125
- kvs[:size]
126
- end
127
-
128
- vp = subplot.clone
129
-
130
- if w > h
131
- ratio = h / w.to_f
132
- msize = mwidth * w / width
133
- GR.setwsviewport(0, msize, 0, msize * ratio)
134
- GR.setwswindow(0, 1, 0, ratio)
135
- vp[2] *= ratio
136
- vp[3] *= ratio
137
- else
138
- ratio = w / h.to_f
139
- msize = mheight * h / height
140
- GR.setwsviewport(0, msize * ratio, 0, msize)
141
- GR.setwswindow(0, ratio, 0, 1)
142
- vp[0] *= ratio
143
- vp[1] *= ratio
144
- end
145
-
146
- if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
147
- extent = [vp[1] - vp[0], vp[3] - vp[2]].min
148
- vp1 = 0.5 * (vp[0] + vp[1] - extent)
149
- vp2 = 0.5 * (vp[0] + vp[1] + extent)
150
- vp3 = 0.5 * (vp[2] + vp[3] - extent)
151
- vp4 = 0.5 * (vp[2] + vp[3] + extent)
152
- else
153
- vp1, vp2, vp3, vp4 = vp
154
- end
155
-
156
- left_margin = kvs.has_key?(:ylabel) ? 0.05 : 0
157
- right_margin = if %i[contour contourf hexbin heatmap nonuniformheatmap polarheatmap
158
- nonuniformpolarheatmap surface trisurf volume].include?(kind)
159
- (vp2 - vp1) * 0.1
160
- else
161
- 0
162
- end
163
- bottom_margin = kvs.has_key?(:xlabel) ? 0.05 : 0
164
- top_margin = kvs.has_key?(:title) ? 0.075 : 0
165
-
166
- viewport = [vp1 + (0.075 + left_margin) * (vp2 - vp1),
167
- vp1 + (0.95 - right_margin) * (vp2 - vp1),
168
- vp3 + (0.075 + bottom_margin) * (vp4 - vp3),
169
- vp3 + (0.975 - top_margin) * (vp4 - vp3)]
170
-
171
- if %i[line step scatter stem].include?(kind) && kvs[:labels]
172
- location = kvs[:location] || 1
173
- if [11, 12, 13].include?(location)
174
- w, h = legend_size
175
- viewport[1] -= w + 0.1
176
- end
177
- end
178
-
179
- GR.setviewport(*viewport)
180
-
181
- kvs[:viewport] = viewport
182
- kvs[:vp] = vp
183
- kvs[:ratio] = ratio
184
-
185
- if kvs[:backgroundcolor]
186
- GR.savestate
187
- GR.selntran(0)
188
- GR.setfillintstyle(GR::INTSTYLE_SOLID)
189
- GR.setfillcolorind(kvs[:backgroundcolor])
190
- if w > h
191
- GR.fillrect(subplot[0], subplot[1],
192
- ratio * subplot[2], ratio * subplot[3])
193
- else
194
- GR.fillrect(ratio * subplot[0], ratio * subplot[1],
195
- subplot[2], subplot[3])
196
- end
197
- GR.selntran(1)
198
- GR.restorestate
199
- end
200
-
201
- if %i[polar polarhist polarheatmap nonuniformpolarheatmap].include? kind
202
- xmin, xmax, ymin, ymax = viewport
203
- xcenter = 0.5 * (xmin + xmax)
204
- ycenter = 0.5 * (ymin + ymax)
205
- r = 0.5 * [xmax - xmin, ymax - ymin].min
206
- GR.setviewport(xcenter - r, xcenter + r, ycenter - r, ycenter + r)
207
- end
208
- end
209
-
210
- def set_window(kind)
211
- scale = 0
212
- unless %i[polar polarhist polarheatmap nonuniformpolarheatmap].include?(kind)
213
- scale |= GR::OPTION_X_LOG if kvs[:xlog]
214
- scale |= GR::OPTION_Y_LOG if kvs[:ylog]
215
- scale |= GR::OPTION_Z_LOG if kvs[:zlog]
216
- scale |= GR::OPTION_FLIP_X if kvs[:xflip]
217
- scale |= GR::OPTION_FLIP_Y if kvs[:yflip]
218
- scale |= GR::OPTION_FLIP_Z if kvs[:zflip]
219
- end
220
- kvs[:scale] = scale
221
-
222
- if kvs.has_key?(:panzoom)
223
- xmin, xmax, ymin, ymax = GR.panzoom(*kvs[:panzoom])
224
- kvs[:xrange] = [xmin, xmax]
225
- kvs[:yrange] = [ymin, ymax]
226
- else
227
- minmax(kind)
228
- end
229
-
230
- major_count = if %i[wireframe surface plot3 scatter3 polar polarhist
231
- polarheatmap nonuniformpolarheatmap trisurf volume].include?(kind)
232
- 2
233
- else
234
- 5
235
- end
236
-
237
- kvs[:xticks] = [kvs[:xticks], major_count] if kvs[:xticks].is_a? Numeric
238
- kvs[:yticks] = [kvs[:yticks], major_count] if kvs[:yticks].is_a? Numeric
239
- kvs[:zticks] = [kvs[:zticks], major_count] if kvs[:zticks].is_a? Numeric
240
-
241
- xmin, xmax = kvs[:xrange]
242
- if %i[heatmap polarheatmap].include?(kind) && kvs.has_key?(:xlim)
243
- xmin -= 0.5
244
- xmax += 0.5
245
- end
246
- xtick, majorx = if (scale & GR::OPTION_X_LOG) == 0
247
- if !%i[heatmap polarheatmap].include?(kind) &&
248
- !kvs.has_key?(:xlim) &&
249
- !kvs[:panzoom]
250
- xmin, xmax = GR.adjustlimits(xmin, xmax)
251
- end
252
- if kvs.has_key?(:xticks)
253
- kvs[:xticks]
254
- else
255
- [auto_tick(xmin, xmax) / major_count, major_count]
256
- end
257
- else
258
- [1, 1]
259
- end
260
- xorg = (scale & GR::OPTION_FLIP_X) == 0 ? [xmin, xmax] : [xmax, xmin]
261
- kvs[:xaxis] = xtick, xorg, majorx
262
-
263
- ymin, ymax = kvs[:yrange]
264
- if %i[heatmap polarheatmap].include?(kind) && kvs.has_key?(:ylim)
265
- ymin -= 0.5
266
- ymax += 0.5
267
- end
268
- if kind == :hist
269
- if kvs[:horizontal] && !kvs.has_key?(:xlim)
270
- xmin = (scale & GR::OPTION_X_LOG) == 0 ? 0 : 1
271
- elsif !kvs.has_key?(:ylim)
272
- ymin = (scale & GR::OPTION_Y_LOG) == 0 ? 0 : 1
273
- end
274
- end
275
- ytick, majory = if (scale & GR::OPTION_Y_LOG) == 0
276
- if !%i[heatmap polarheatmap].include?(kind) &&
277
- !kvs.has_key?(:ylim) &&
278
- !kvs[:panzoom]
279
- ymin, ymax = GR.adjustlimits(ymin, ymax)
280
- end
281
- if kvs.has_key?(:yticks)
282
- kvs[:yticks]
283
- else
284
- [auto_tick(ymin, ymax) / major_count, major_count]
285
- end
286
- else
287
- [1, 1]
288
- end
289
- yorg = (scale & GR::OPTION_FLIP_Y) == 0 ? [ymin, ymax] : [ymax, ymin]
290
- kvs[:yaxis] = ytick, yorg, majory
291
-
292
- if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
293
- zmin, zmax = kvs[:zrange]
294
- ztick, majorz = if (scale & GR::OPTION_Z_LOG) == 0
295
- zmin, zmax = GR.adjustlimits(zmin, zmax) if kvs.has_key?(:zlim)
296
- if kvs.has_key?(:zticks)
297
- kvs[:zticks]
298
- else
299
- [auto_tick(zmin, zmax) / major_count, major_count]
300
- end
301
- else
302
- [1, 1]
303
- end
304
- zorg = (scale & GR::OPTION_FLIP_Z) == 0 ? [zmin, zmax] : [zmax, zmin]
305
- kvs[:zaxis] = ztick, zorg, majorz
306
- end
307
-
308
- kvs[:window] = xmin, xmax, ymin, ymax
309
- if %i[polar polarhist polarheatmap nonuniformpolarheatmap trisurf].include?(kind)
310
- GR.setwindow(-1, 1, -1, 1)
311
- else
312
- GR.setwindow(xmin, xmax, ymin, ymax)
313
- end
314
- if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
315
- rotation = kvs[:rotation] || 40
316
- tilt = kvs[:tilt] || 60
317
- GR.setwindow3d(xmin, xmax, ymin, ymax, zmin, zmax)
318
- GR.setspace3d(-rotation, tilt, 30, 0)
319
- end
320
-
321
- kvs[:scale] = scale
322
- GR.setscale(scale)
323
- end
324
-
325
- def draw_axes(kind, pass = 1)
326
- viewport = kvs[:viewport]
327
- vp = kvs[:vp]
328
- _ratio = kvs[:ratio]
329
- xtick, xorg, majorx = kvs[:xaxis]
330
- ytick, yorg, majory = kvs[:yaxis]
331
- drawgrid = kvs.has_key?(:grid) ? kvs[:grid] : true
332
- xtick = 10 if kvs[:scale] & GR::OPTION_X_LOG != 0
333
- ytick = 10 if kvs[:scale] & GR::OPTION_Y_LOG != 0
334
- GR.setlinecolorind(1)
335
- diag = Math.sqrt((viewport[1] - viewport[0])**2 + (viewport[3] - viewport[2])**2)
336
- GR.setlinewidth(1)
337
- ticksize = 0.0075 * diag
338
- if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
339
- charheight = [0.024 * diag, 0.012].max
340
- GR.setcharheight(charheight)
341
- ztick, zorg, majorz = kvs[:zaxis]
342
- if pass == 1 && drawgrid
343
- GR.grid3d(xtick, 0, ztick, xorg[0], yorg[1], zorg[0], 2, 0, 2)
344
- GR.grid3d(0, ytick, 0, xorg[0], yorg[1], zorg[0], 0, 2, 0)
345
- else
346
- GR.axes3d(xtick, 0, ztick, xorg[0], yorg[0], zorg[0], majorx, 0, majorz, -ticksize)
347
- GR.axes3d(0, ytick, 0, xorg[1], yorg[0], zorg[0], 0, majory, 0, ticksize)
348
- end
349
- else
350
- charheight = [0.018 * diag, 0.012].max
351
- GR.setcharheight(charheight)
352
- if %i[heatmap nonuniformheatmap shade].include?(kind)
353
- ticksize = -ticksize
354
- elsif drawgrid
355
- GR.grid(xtick, ytick, 0, 0, majorx, majory)
356
- end
357
- if kvs.has_key?(:xticklabels) || kvs.has_key?(:yticklabels)
358
- fx = if kvs.has_key?(:xticklabels)
359
- GRCommons::Fiddley::Function.new(
360
- :void, %i[double double string double]
361
- ) do |x, y, _svalue, value|
362
- label = value < 0 ? '' : kvs[:xticklabels][value] || ''
363
- GR.textext(x, y, label)
364
- end
365
- else
366
- GRCommons::Fiddley::Function.new(
367
- :void, %i[double double string double]
368
- ) do |x, y, _svalue, value|
369
- GR.textext(x, y, value.to_s)
370
- end
371
- end
372
- fy = if kvs.has_key?(:yticklabels)
373
- GRCommons::Fiddley::Function.new(
374
- :void, %i[double double string double]
375
- ) do |x, y, _svalue, value|
376
- label = value < 0 ? '' : kvs[:yticklabels][value] || ''
377
- GR.textext(x, y, label)
378
- end
379
- else
380
- GRCommons::Fiddley::Function.new(
381
- :void, %i[double double string double]
382
- ) do |x, y, _svalue, value|
383
- GR.textext(x, y, value.to_s)
384
- end
385
- end
386
- GR.axeslbl(xtick, ytick, xorg[0], yorg[0], majorx, majory, ticksize, fx, fy)
387
- else
388
- GR.axes(xtick, ytick, xorg[0], yorg[0], majorx, majory, ticksize)
389
- end
390
- GR.axes(xtick, ytick, xorg[1], yorg[1], -majorx, -majory, -ticksize)
391
- end
392
-
393
- if kvs.has_key?(:title)
394
- GR.savestate
395
- GR.settextalign(GR::TEXT_HALIGN_CENTER, GR::TEXT_VALIGN_TOP)
396
- text(0.5 * (viewport[0] + viewport[1]), vp[3], kvs[:title].to_s)
397
- GR.restorestate
398
- end
399
- if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
400
- xlabel = (kvs[:xlabel] || '').to_s
401
- ylabel = (kvs[:ylabel] || '').to_s
402
- zlabel = (kvs[:zlabel] || '').to_s
403
- GR.titles3d(xlabel, ylabel, zlabel)
404
- else
405
- if kvs.has_key?(:xlabel)
406
- GR.savestate
407
- GR.settextalign(GR::TEXT_HALIGN_CENTER, GR::TEXT_VALIGN_BOTTOM)
408
- text(0.5 * (viewport[0] + viewport[1]), vp[2] + 0.5 * charheight, kvs[:xlabel].to_s)
409
- GR.restorestate
410
- end
411
- if kvs.has_key?(:ylabel)
412
- GR.savestate
413
- GR.settextalign(GR::TEXT_HALIGN_CENTER, GR::TEXT_VALIGN_TOP)
414
- GR.setcharup(-1, 0)
415
- text(vp[0] + 0.5 * charheight, 0.5 * (viewport[2] + viewport[3]), kvs[:ylabel].to_s)
416
- GR.restorestate
417
- end
418
- end
419
- end
420
-
421
- def draw_polar_axes
422
- viewport = kvs[:viewport]
423
- diag = Math.sqrt((viewport[1] - viewport[0])**2 + (viewport[3] - viewport[2])**2)
424
- charheight = [0.018 * diag, 0.012].max
425
-
426
- window = kvs[:window]
427
- rmin = window[2]
428
- rmax = window[3]
429
-
430
- GR.savestate
431
- GR.setcharheight(charheight)
432
- GR.setlinetype(GR::LINETYPE_SOLID)
433
-
434
- tick = auto_tick(rmin, rmax)
435
- n = ((rmax - rmin) / tick + 0.5).round
436
- (n + 1).times do |i|
437
- r = i.to_f / n
438
- if i.even?
439
- GR.setlinecolorind(88)
440
- GR.drawarc(-r, r, -r, r, 0, 359) if i > 0
441
- GR.settextalign(GR::TEXT_HALIGN_LEFT, GR::TEXT_VALIGN_HALF)
442
- x, y = GR.wctondc(0.05, r)
443
- GR.text(x, y, (rmin + i * tick).to_s) # FIXME: round. significant digits.
444
- else
445
- GR.setlinecolorind(90)
446
- GR.drawarc(-r, r, -r, r, 0, 359)
447
- end
448
- end
449
- 0.step(by: 45, to: 315) do |alpha|
450
- sinf = Math.sin(alpha * Math::PI / 180)
451
- cosf = Math.cos(alpha * Math::PI / 180)
452
- GR.polyline([cosf, 0], [sinf, 0])
453
- GR.settextalign(GR::TEXT_HALIGN_CENTER, GR::TEXT_VALIGN_HALF)
454
- x, y = GR.wctondc(1.1 * cosf, 1.1 * sinf)
455
- GR.textext(x, y, "#{alpha}^o")
456
- end
457
- GR.restorestate
458
- end
459
-
460
- def plot_polar(θ, ρ)
461
- window = kvs[:window]
462
- rmax = window[3].to_f
463
- ρ = ρ.map { |i| i / rmax }
464
- n = ρ.length
465
- x = []
466
- y = []
467
- n.times do |i|
468
- x << ρ[i] * Math.cos(θ[i])
469
- y << ρ[i] * Math.sin(θ[i])
470
- end
471
- GR.polyline(x, y)
472
- end
473
-
474
- def plot_img(img)
475
- viewport = kvs[:vp].clone
476
- viewport[3] -= 0.05 if kvs.has_key?(:title)
477
- vp = kvs[:vp]
478
-
479
- if img.is_a? String
480
- width, height, data = GR.readimage(img)
481
- else
482
- height, width = img.shape
483
- cmin, cmax = kvs[:crange]
484
- data = img.map { |i| normalize_color(i, cmin, cmax) }
485
- data = data.map { |i| (1000 + i * 255).round }
486
- end
487
-
488
- if width * (viewport[3] - viewport[2]) < height * (viewport[1] - viewport[0])
489
- w = width.to_f / height * (viewport[3] - viewport[2])
490
- xmin = [0.5 * (viewport[0] + viewport[1] - w), viewport[0]].max
491
- xmax = [0.5 * (viewport[0] + viewport[1] + w), viewport[1]].min
492
- ymin = viewport[2]
493
- ymax = viewport[3]
494
- else
495
- h = height.to_f / width * (viewport[1] - viewport[0])
496
- xmin = viewport[0]
497
- xmax = viewport[1]
498
- ymin = [0.5 * (viewport[3] + viewport[2] - h), viewport[2]].max
499
- ymax = [0.5 * (viewport[3] + viewport[2] + h), viewport[3]].min
500
- end
501
-
502
- GR.selntran(0)
503
- GR.setscale(0)
504
- if kvs.has_key?(:xflip)
505
- tmp = xmax
506
- xmax = xmin
507
- xmin = tmp
508
- end
509
- if kvs.has_key?(:yflip)
510
- tmp = ymax
511
- ymax = ymin
512
- ymin = tmp
513
- end
514
- if img.is_a? String
515
- GR.drawimage(xmin, xmax, ymin, ymax, width, height, data)
516
- else
517
- GR.cellarray(xmin, xmax, ymin, ymax, width, height, data)
518
- end
519
-
520
- if kvs.has_key?(:title)
521
- GR.savestate
522
- GR.settextalign(GR::TEXT_HALIGN_CENTER, GR::TEXT_VALIGN_TOP)
523
- text(0.5 * (viewport[0] + viewport[1]), vp[3], kvs[:title].to_s)
524
- GR.restorestate
525
- end
526
- GR.selntran(1)
527
- end
528
-
529
- def plot_iso(v)
530
- viewport = kvs[:viewport]
531
-
532
- if viewport[3] - viewport[2] < viewport[1] - viewport[0]
533
- width = viewport[3] - viewport[2]
534
- centerx = 0.5 * (viewport[0] + viewport[1])
535
- xmin = [centerx - 0.5 * width, viewport[0]].max
536
- xmax = [centerx + 0.5 * width, viewport[1]].min
537
- ymin = viewport[2]
538
- ymax = viewport[3]
539
- else
540
- height = viewport[1] - viewport[0]
541
- centery = 0.5 * (viewport[2] + viewport[3])
542
- xmin = viewport[0]
543
- xmax = viewport[1]
544
- ymin = [centery - 0.5 * height, viewport[2]].max
545
- ymax = [centery + 0.5 * height, viewport[3]].min
546
- end
547
-
548
- GR.selntran(0)
549
- v = Numo::DFloat.cast(v) if v.is_a? Array
550
- values = ((v - v.min) / (v.max - v.min) * (2**16 - 1)).round
551
- values = Numo::UInt16.cast(values)
552
- nx, ny, nz = v.shape
553
- isovalue = ((kvs[:isovalue] || 0.5) - v.min) / (v.max - v.min)
554
- rotation = ((kvs[:rotation] || 40) * Math::PI / 180.0)
555
- tilt = ((kvs[:tilt] || 70) * Math::PI / 180.0)
556
- r = 2.5
557
- GR3.clear
558
- mesh = GR3.createisosurfacemesh(values, [2.0 / (nx - 1), 2.0 / (ny - 1), 2.0 / (nz - 1)],
559
- [-1, -1, -1],
560
- (isovalue * (2**16 - 1)).round)
561
- color = kvs[:color] || [0.0, 0.5, 0.8]
562
- GR3.setbackgroundcolor(1, 1, 1, 0)
563
- GR3.drawmesh(mesh, 1, [0, 0, 0], [0, 0, 1], [0, 1, 0], color, [1, 1, 1])
564
- GR3.cameralookat(r * Math.sin(tilt) * Math.sin(rotation),
565
- r * Math.cos(tilt), r * Math.sin(tilt) * Math.cos(rotation),
566
- 0, 0, 0, 0, 1, 0)
567
- GR3.drawimage(xmin, xmax, ymin, ymax, 500, 500, GR3::DRAWABLE_GKS)
568
- GR3.deletemesh(mesh)
569
- GR.selntran(1)
570
- end
571
-
572
- def colorbar(off = 0, colors = 256)
573
- GR.savestate
574
- viewport = kvs[:viewport]
575
- zmin, zmax = kvs[:zrange]
576
- mask = (GR::OPTION_Z_LOG | GR::OPTION_FLIP_Y | GR::OPTION_FLIP_Z)
577
- options = if kvs.has_key?(:zflip)
578
- (GR.inqscale | GR::OPTION_FLIP_Y)
579
- elsif kvs.has_key?(:yflip)
580
- GR.inqscale & ~GR::OPTION_FLIP_Y
581
- else
582
- GR.inqscale
583
- end
584
- GR.setscale(options & mask)
585
- h = 0.5 * (zmax - zmin) / (colors - 1)
586
- GR.setwindow(0, 1, zmin, zmax)
587
- GR.setviewport(viewport[1] + 0.02 + off, viewport[1] + 0.05 + off,
588
- viewport[2], viewport[3])
589
- l = linspace(1000, 1255, colors).map(&:round)
590
- GR.cellarray(0, 1, zmax + h, zmin - h, 1, colors, l)
591
- GR.setlinecolorind(1)
592
- diag = Math.sqrt((viewport[1] - viewport[0])**2 + (viewport[3] - viewport[2])**2)
593
- charheight = [0.016 * diag, 0.012].max
594
- GR.setcharheight(charheight)
595
- if kvs[:scale] & GR::OPTION_Z_LOG == 0
596
- ztick = auto_tick(zmin, zmax)
597
- GR.axes(0, ztick, 1, zmin, 0, 1, 0.005)
598
- else
599
- GR.setscale(GR::OPTION_Y_LOG)
600
- GR.axes(0, 2, 1, zmin, 0, 1, 0.005)
601
- end
602
- GR.restorestate
603
- end
604
-
605
- def plot_data(_figure = true)
606
- # GR.init
607
-
608
- # target = GR.displayname
609
- # if flag && target != None
610
- # if target == "js" || target == "meta"
611
- # send_meta(0)
612
- # else
613
- # send_serialized(target)
614
- # end
615
- # return
616
- # end
617
-
618
- kind = kvs[:kind] || :line
619
- GR.clearws if kvs[:clear]
620
-
621
- if scheme != 0
622
- # Not yet.
623
- end
624
-
625
- if kvs.has_key?(:font)
626
- name = kvs[:font]
627
- # 'Cmuserif-Math' => :cmuserif_math
628
- sym_name = name.to_s.gsub('-', '_').downcase.to_sym
629
- if FONTS.include?(sym_name)
630
- font = FONTS[sym_name]
631
- GR.settextfontprec(font, font > 200 ? 3 : 0)
632
- else
633
- font = GR.loadfont(name)
634
- if font >= 0
635
- GR.settextfontprec(font, 3)
636
- else
637
- warn "Unknown font name: #{name}"
638
- end
639
- end
640
- else
641
- # The following fonts are the default in GR.jl
642
- # Japanese, Chinese, Korean, etc. cannot be displayed.
643
-
644
- # GR.settextfontprec(232, 3) # CM Serif Roman
645
- end
646
-
647
- set_viewport(kind, kvs[:subplot])
648
- unless kvs[:ax]
649
- set_window(kind)
650
- if %i[polar polarhist].include?(kind)
651
- draw_polar_axes
652
- elsif !%i[imshow isosurface polarheatmap nonuniformpolarheatmap].include?(kind)
653
- draw_axes(kind)
654
- end
655
- end
656
-
657
- if kvs.has_key?(:colormap)
658
- GR.setcolormap(kvs[:colormap])
659
- else
660
- GR.setcolormap(GR::COLORMAP_VIRIDIS)
661
- end
662
-
663
- GR.uselinespec(' ')
664
- args.each do |x, y, z, c, spec|
665
- spec ||= kvs[:spec] ||= ''
666
- GR.savestate
667
- GR.settransparency(kvs[:alpha]) if kvs.has_key?(:alpha)
668
-
669
- case kind
670
-
671
- when :line
672
- mask = GR.uselinespec(spec)
673
- linewidth = kvs[:linewidth]
674
- # Slightly different from Julia,
675
- # Because the implementation of polyline and polymarker is different.
676
- z ||= linewidth # FIXME
677
- GR.polyline(x, y, z, c) if hasline(mask)
678
- GR.polymarker(x, y, z, c) if hasmarker(mask)
679
-
680
- when :step
681
- mask = GR.uselinespec(spec)
682
- if hasline(mask)
683
- where = kvs[:where] || 'mid'
684
- n = x.length
685
- xs = [x[0]]
686
- case where
687
- when 'pre'
688
- ys = [y[0]]
689
- (n - 1).times do |i|
690
- xs << x[i] << x[i + 1]
691
- ys << y[i + 1] << y[i + 1]
692
- end
693
- when 'post'
694
- ys = [y[0]]
695
- (n - 1).times do |i|
696
- xs << x[i + 1] << x[i + 1]
697
- ys << y[i] << y[i + 1]
698
- end
699
- else
700
- ys = []
701
- (n - 1).times do |i|
702
- xs << 0.5 * (x[i] + x[i + 1]) << 0.5 * (x[i] + x[i + 1])
703
- ys << y[i] << y[i]
704
- end
705
- xs << x[n - 1]
706
- ys << y[n - 1] << y[n - 1]
707
- end
708
- GR.polyline(xs, ys)
709
- end
710
- GR.polymarker(x, y) if hasmarker(mask)
711
-
712
- when :scatter
713
- GR.setmarkertype(GR::MARKERTYPE_SOLID_CIRCLE)
714
- z = z&.map { |i| i * 0.01 }
715
- c = c&.map { |i| normalize_color(i, *kvs[:crange]) }
716
- GR.polymarker(x, y, z, c)
717
-
718
- when :stem
719
- GR.setlinecolorind(1)
720
- GR.polyline(kvs[:window][0..1], [0, 0])
721
- GR.setmarkertype(GR::MARKERTYPE_SOLID_CIRCLE)
722
- GR.uselinespec(spec)
723
- x = x.to_a if narray?(x)
724
- y = y.to_a if narray?(y)
725
- x.zip(y).each do |xi, yi|
726
- GR.polyline([xi, xi], [0, yi])
727
- end
728
- GR.polymarker(x, y)
729
-
730
- when :hist
731
- if kvs[:horizontal]
732
- xmin = kvs[:window][0]
733
- x.length.times do |i|
734
- GR.setfillcolorind(989)
735
- GR.setfillintstyle(GR::INTSTYLE_SOLID)
736
- GR.fillrect(xmin, x[i], y[i], y[i + 1])
737
- GR.setfillcolorind(1)
738
- GR.setfillintstyle(GR::INTSTYLE_HOLLOW)
739
- GR.fillrect(xmin, x[i], y[i], y[i + 1])
740
- end
741
- else
742
- ymin = kvs[:window][2]
743
- y.length.times do |i|
744
- GR.setfillcolorind(989)
745
- GR.setfillintstyle(GR::INTSTYLE_SOLID)
746
- GR.fillrect(x[i], x[i + 1], ymin, y[i])
747
- GR.setfillcolorind(1)
748
- GR.setfillintstyle(GR::INTSTYLE_HOLLOW)
749
- GR.fillrect(x[i], x[i + 1], ymin, y[i])
750
- end
751
- end
752
-
753
- when :polarhist
754
- ymax = kvs[:window][3].to_f
755
- ρ = y.map { |i| i / ymax }
756
- θ = x.map { |i| i * 180 / Math::PI }
757
- (1...ρ.length).each do |i|
758
- GR.setfillcolorind(989)
759
- GR.setfillintstyle(GR::INTSTYLE_SOLID)
760
- GR.fillarc(-ρ[i], ρ[i], -ρ[i], ρ[i], θ[i - 1], θ[i])
761
- GR.setfillcolorind(1)
762
- GR.setfillintstyle(GR::INTSTYLE_HOLLOW)
763
- GR.fillarc(-ρ[i], ρ[i], -ρ[i], ρ[i], θ[i - 1], θ[i])
764
- end
765
-
766
- when :polarheatmap, :nonuniformpolarheatmap
767
- w, h = z.shape
768
- cmap = colormap
769
- cmin, cmax = kvs[:zrange]
770
- data = z.map { |i| normalize_color(i, cmin, cmax) }
771
- data.reverse(axis: 0) if kvs[:xflip]
772
- data.reverse(axis: 1) if kvs[:yflip]
773
- colors = data * 255 + 1000
774
- colors = colors.transpose # Julia is column major
775
- case kind
776
- when :polarheatmap
777
- GR.polarcellarray(0, 0, 0, 360, 0, 1, w, h, colors)
778
- when :nonuniformpolarheatmap
779
- ymax = y.max.to_f
780
- ρ = y.map { |i| i / ymax }
781
- θ = x.map { |i| i * 180 / Math::PI }
782
- GR.nonuniformpolarcellarray(θ, ρ, w, h, colors)
783
- end
784
- draw_polar_axes
785
- kvs[:zrange] = [cmin, cmax]
786
- colorbar
787
-
788
- when :contour, :contourf
789
- zmin, zmax = kvs[:zrange]
790
- if narray?(z) && z.ndim == 2
791
- a, b = z.shape
792
- x = (1..b).to_a
793
- y = (1..a).to_a
794
- zmin, zmax = z.minmax
795
- elsif equal_length(x, y, z)
796
- x, y, z = GR.gridit(x, y, z, 200, 200)
797
- zmin, zmax = z.compact.minmax # compact : removed nil
798
- end
799
-
800
- # kvs[:zlim] is supposed to be Array or Range
801
- if kvs.has_key?(:zlim)
802
- zmin = kvs[:zlim].first if kvs[:zlim].first
803
- zmax = kvs[:zlim].last if kvs[:zlim].last
804
- end
805
- GR.setprojectiontype(0)
806
- GR.setspace(zmin, zmax, 0, 90)
807
- levels = kvs[:levels] || 0
808
- clabels = kvs[:clabels] || false
809
- if levels.is_a? Integer
810
- hmin, hmax = GR.adjustrange(zmin, zmax)
811
- h = linspace(hmin, hmax, levels == 0 ? 21 : levels + 1)
812
- else
813
- h = levels
814
- end
815
- case kind
816
- when :contour
817
- GR._contour_(x, y, h, z, clabels ? 1 : 1000)
818
- when :contourf
819
- GR._contourf_(x, y, h, z, clabels ? 1 : 0)
820
- end
821
- colorbar(0, h.length)
822
-
823
- when :hexbin
824
- nbins = kvs[:nbins] || 40
825
- cntmax = GR._hexbin_(x, y, nbins)
826
- if cntmax > 0
827
- kvs[:zrange] = [0, cntmax]
828
- colorbar
829
- end
830
-
831
- when :heatmap, :nonuniformheatmap
832
- case z
833
- when Array
834
- if z.all? { |zi| zi.size = z[0].size }
835
- w = z.size
836
- h = z[0].size
837
- else
838
- raise
839
- end
840
- when ->(obj) { narray?(obj) }
841
- w, h = z.shape
842
- else
843
- raise
844
- end
845
- cmap = colormap
846
- cmin, cmax = kvs[:crange]
847
- levels = kvs[:levels] || 256
848
- data = z.flatten.to_a.map { |i| normalize_color(i, cmin, cmax) } # NArray -> Array
849
- if kind == :heatmap
850
- rgba = data.map { |v| to_rgba(v, cmap) }
851
- GR.drawimage(0.5, w + 0.5, h + 0.5, 0.5, w, h, rgba)
852
- else
853
- colors = data.map { |i| (1000 + i * 255).round }
854
- GR.nonuniformcellarray(x, y, w, h, colors)
855
- end
856
- colorbar(0, levels)
857
-
858
- when :wireframe
859
- if narray?(z) && z.ndim == 2
860
- a, b = z.shape
861
- x = (1..b).to_a
862
- y = (1..a).to_a
863
- elsif equal_length(x, y, z)
864
- x, y, z = GR.gridit(x, y, z, 50, 50)
865
- end
866
- GR.setfillcolorind(0)
867
- GR._surface_(x, y, z, GR::OPTION_FILLED_MESH)
868
- draw_axes(kind, 2)
869
-
870
- when :surface
871
- if narray?(z) && z.ndim == 2
872
- a, b = z.shape
873
- x = (1..b).to_a
874
- y = (1..a).to_a
875
- elsif equal_length(x, y, z)
876
- x, y, z = GR.gridit(x, y, z, 200, 200)
877
- end
878
- if kvs[:accelerate] == false
879
- GR._surface_(x, y, z, GR::OPTION_COLORED_MESH)
880
- else
881
- require 'gr3'
882
- GR3.clear
883
- GR3.surface(x, y, z, GR::OPTION_COLORED_MESH)
884
- end
885
- draw_axes(kind, 2)
886
- colorbar(0.05)
887
-
888
- when :volume
889
- algorithm = kvs[:algorithm] || 0
890
- require 'gr3'
891
- GR3.clear
892
- dmin, dmax = GR3.volume(z, algorithm)
893
- draw_axes(kind, 2)
894
- kvs[:zrange] = [dmin, dmax]
895
- colorbar(0.05)
896
-
897
- when :plot3
898
- mask = GR.uselinespec(spec)
899
- GR.polyline3d(x, y, z) if hasline(mask)
900
- GR.polymarker3d(x, y, z) if hasmarker(mask)
901
- draw_axes(kind, 2)
902
-
903
- when :scatter3
904
- GR.setmarkertype(GR::MARKERTYPE_SOLID_CIRCLE)
905
- if c
906
- cmin, cmax = kvs[:crange]
907
- c = c.map { |i| normalize_color(i, cmin, cmax) }
908
- cind = c.map { |i| (1000 + i * 255).round }
909
- x.length.times do |i|
910
- GR.setmarkercolorind(cind[i])
911
- GR.polymarker3d([x[i]], [y[i]], [z[i]])
912
- end
913
- else
914
- GR.polymarker3d(x, y, z)
915
- end
916
- draw_axes(kind, 2)
917
-
918
- when :imshow
919
- plot_img(z)
920
-
921
- when :isosurface
922
- plot_iso(z)
923
-
924
- when :polar
925
- GR.uselinespec(spec)
926
- plot_polar(x, y)
927
-
928
- when :trisurf
929
- GR.trisurface(x, y, z)
930
- draw_axes(kind, 2)
931
- colorbar(0.05)
932
-
933
- when :tricont
934
- zmin, zmax = kvs[:zrange]
935
- levels = linspace(zmin, zmax, 20)
936
- GR.tricontour(x, y, z, levels)
937
-
938
- when :shade
939
- xform = kvs[:xform] || 5
940
- if x.to_a.include? Float::NAN # FIXME: Ruby is different from Julia?
941
- # How to check NArray?
942
- GR.shadelines(x, y, xform: xform)
943
- else
944
- GR.shadepoints(x, y, xform: xform)
945
- end
946
-
947
- when :bar
948
- 0.step(x.length - 1, 2) do |i|
949
- GR.setfillcolorind(989)
950
- GR.setfillintstyle(GR::INTSTYLE_SOLID)
951
- GR.fillrect(x[i], x[i + 1], y[i], y[i + 1])
952
- GR.setfillcolorind(1)
953
- GR.setfillintstyle(GR::INTSTYLE_HOLLOW)
954
- GR.fillrect(x[i], x[i + 1], y[i], y[i + 1])
955
- end
956
- end
957
-
958
- GR.restorestate
959
- end
960
-
961
- draw_legend if %i[line step scatter stem].include?(kind) && kvs.has_key?(:labels)
962
-
963
- if kvs[:update]
964
- GR.updatews
965
- # if GR.isinline()
966
- # restore_context()
967
- # return GR.show()
968
- # end
969
- end
970
-
971
- # flag && restore_context()
972
- end
973
-
974
- def draw_legend
975
- w, h = legend_size
976
- viewport = kvs[:viewport]
977
- location = kvs[:location] || 1
978
- num_labels = kvs[:labels].length
979
- GR.savestate
980
- GR.selntran 0
981
- GR.setscale 0
982
- px = case location
983
- when 11, 12, 13
984
- viewport[1] + 0.11
985
- when 8, 9, 10
986
- 0.5 * (viewport[0] + viewport[1] - w + 0.05)
987
- when 2, 3, 6
988
- viewport[0] + 0.11
989
- else
990
- viewport[1] - 0.05 - w
991
- end
992
- py = case location
993
- when 5, 6, 7, 10, 12
994
- 0.5 * (viewport[2] + viewport[3] + h - 0.03)
995
- when 13
996
- viewport[2] + h
997
- when 3, 4, 8
998
- viewport[2] + h + 0.03
999
- when 11
1000
- viewport[3] - 0.03
1001
- else
1002
- viewport[3] - 0.06
1003
- end
1004
- GR.setfillintstyle(GR::INTSTYLE_SOLID)
1005
- GR.setfillcolorind(0)
1006
- GR.fillrect(px - 0.08, px + w + 0.02, py + 0.03, py - h)
1007
- GR.setlinetype(GR::LINETYPE_SOLID)
1008
- GR.setlinecolorind(1)
1009
- GR.setlinewidth(1)
1010
- GR.drawrect(px - 0.08, px + w + 0.02, py + 0.03, py - h)
1011
- i = 0
1012
- GR.uselinespec(' ')
1013
- args.each do |_x, _y, _z, _c, spec|
1014
- if i < num_labels
1015
- label = kvs[:labels][i]
1016
- label = label.to_s
1017
- _tbx, tby = inqtext(0, 0, label)
1018
- dy = [(tby[2] - tby[0]) - 0.03, 0].max
1019
- py -= 0.5 * dy
1020
- end
1021
- GR.savestate
1022
- mask = GR.uselinespec(spec || '')
1023
- GR.polyline([px - 0.07, px - 0.01], [py, py]) if hasline(mask)
1024
- GR.polymarker([px - 0.06, px - 0.02], [py, py]) if hasmarker(mask)
1025
- GR.restorestate
1026
- GR.settextalign(GR::TEXT_HALIGN_LEFT, GR::TEXT_VALIGN_HALF)
1027
- if i < num_labels
1028
- text(px, py, label)
1029
- py -= 0.5 * dy
1030
- i += 1
1031
- end
1032
- py -= 0.03
1033
- end
1034
- GR.selntran(1)
1035
- GR.restorestate
1036
- end
1037
-
1038
- def to_svg
1039
- ## Need IRuby improvemend.
1040
- GR.show(false) if ENV['GKS_WSTYPE'] == 'svg'
1041
- end
1042
-
1043
- private
1044
-
1045
- def hasline(mask)
1046
- mask == 0x00 || (mask & 0x01 != 0)
1047
- end
1048
-
1049
- def hasmarker(mask)
1050
- mask & 0x02 != 0
1051
- end
1052
-
1053
- def colormap
1054
- # rgb
1055
- Array.new(256) do |colorind|
1056
- color = GR.inqcolor(1000 + colorind)
1057
- [(color & 0xff) / 255.0,
1058
- ((color >> 8) & 0xff) / 255.0,
1059
- ((color >> 16) & 0xff) / 255.0]
1060
- end
1061
- end
1062
-
1063
- def to_rgba(value, cmap)
1064
- begin
1065
- r, g, b = cmap[(value * 255).round]
1066
- a = 1.0
1067
- rescue StandardError # nil
1068
- r = 0
1069
- g = 0
1070
- b = 0
1071
- a = 0
1072
- end
1073
-
1074
- ((a * 255).round << 24) + ((b * 255).round << 16) +
1075
- ((g * 255).round << 8) + (r * 255).round
1076
- end
1077
-
1078
- # https://gist.github.com/rysk-t/8d1aef0fb67abde1d259#gistcomment-1925021
1079
- def linspace(low, high, num)
1080
- [*0..(num - 1)].collect { |i| low + i.to_f * (high - low) / (num - 1) }
1081
- end
1082
-
1083
- def plot_args(args)
1084
- # FIXME
1085
- args = [args] unless args.all? do |i|
1086
- i.is_a?(Array) && (i[0].is_a?(Array) || narray?(i[0]))
1087
- end
1088
- args.map do |xyzc|
1089
- spec = nil
1090
- case xyzc.last
1091
- when String
1092
- spec = xyzc.pop
1093
- when Hash
1094
- spec = xyzc.pop[:spec]
1095
- end
1096
-
1097
- x, y, z, c = xyzc.map do |i|
1098
- if i.is_a?(Array) || narray?(i) || i.nil?
1099
- i
1100
- elsif i.respond_to?(:to_a)
1101
- # Convert an Array-like class such as Daru::Vector to an Array
1102
- i.to_a
1103
- else # String
1104
- i
1105
- end
1106
- end
1107
- [x, y, z, c, spec]
1108
- end
1109
- end
1110
-
1111
- # Normalize a color c with the range [cmin, cmax]
1112
- # 0 <= normalize_color(c, cmin, cmax) <= 1
1113
- def normalize_color(c, cmin, cmax)
1114
- # NOTE: narray.map{|i| normalize_color(i)} There's room for speedup.
1115
- c = c.to_f # if c is Integer
1116
- c = c.clamp(cmin, cmax) - cmin
1117
- c /= (cmax - cmin) if cmin != cmax
1118
- c
1119
- end
1120
-
1121
- def inqtext(x, y, s)
1122
- s = s.to_s
1123
- if s.length >= 2 && s[0] == '$' && s[-1] == '$'
1124
- GR.inqmathtex(x, y, s[1..-2])
1125
- elsif s.include?('\\') || s.include?('_') || s.include?('^')
1126
- GR.inqtextext(x, y, s)
1127
- else
1128
- GR.inqtext(x, y, s)
1129
- end
1130
- end
1131
-
1132
- def text(x, y, s)
1133
- s = s.to_s
1134
- if s.length >= 2 && s[0] == '$' && s[-1] == '$'
1135
- GR.mathtex(x, y, s[1..-2])
1136
- elsif s.include?('\\') || s.include?('_') || s.include?('^')
1137
- GR.textext(x, y, s)
1138
- else
1139
- GR.text(x, y, s)
1140
- end
1141
- end
1142
-
1143
- def fix_minmax(a, b)
1144
- if a == b
1145
- a -= a != 0 ? 0.1 * a : 0.1
1146
- b += b != 0 ? 0.1 * b : 0.1
1147
- end
1148
- [a, b]
1149
- end
1150
-
1151
- def minmax(kind)
1152
- xmin = ymin = zmin = cmin = Float::INFINITY
1153
- xmax = ymax = zmax = cmax = -Float::INFINITY
1154
- scale = kvs[:scale]
1155
- args.each do |x, y, z, c|
1156
- if x
1157
- if scale & GR::OPTION_X_LOG != 0
1158
- # duck typing for NArray
1159
- x = x.map { |v| v > 0 ? v : Float::NAN }
1160
- end
1161
- x0, x1 = x.minmax
1162
- xmin = [x0, xmin].min
1163
- xmax = [x1, xmax].max
1164
- elsif kind == :volume
1165
- xmin = -1
1166
- xmax = 1
1167
- else
1168
- xmin = 0
1169
- xmax = 1
1170
- end
1171
- if y
1172
- if scale & GR::OPTION_Y_LOG != 0
1173
- y = y.map { |v| v > 0 ? v : Float::NAN }
1174
- end
1175
- y0, y1 = y.minmax
1176
- ymin = [y0, ymin].min
1177
- ymax = [y1, ymax].max
1178
- elsif kind == :volume
1179
- ymin = -1
1180
- ymax = 1
1181
- else
1182
- ymin = 0
1183
- ymax = 1
1184
- end
1185
- if z
1186
- if scale & GR::OPTION_Z_LOG != 0
1187
- z = z.map { |v| v > 0 ? v : Float::NAN }
1188
- end
1189
- z0, z1 = z.minmax
1190
- zmin = [z0, zmin].min
1191
- zmax = [z1, zmax].max
1192
- end
1193
- if c
1194
- c0, c1 = c.minmax
1195
- cmin = [c0, cmin].min
1196
- cmax = [c1, cmax].max
1197
- elsif z
1198
- z0, z1 = z.minmax
1199
- cmin = [z0, zmin].min
1200
- cmax = [z1, zmax].max
1201
- end
1202
- end
1203
- xmin, xmax = fix_minmax(xmin, xmax)
1204
- ymin, ymax = fix_minmax(ymin, ymax)
1205
- zmin, zmax = fix_minmax(zmin, zmax)
1206
-
1207
- # kvs[:xlim], kvs[:ylim], kvs[:zlim] is supposed to be Array or Range
1208
- kvs[:xrange] = [(kvs[:xlim]&.first || xmin), (kvs[:xlim]&.last || xmax)]
1209
- kvs[:yrange] = [(kvs[:ylim]&.first || ymin), (kvs[:ylim]&.last || ymax)]
1210
- kvs[:zrange] = [(kvs[:zlim]&.first || zmin), (kvs[:zlim]&.last || zmax)]
1211
-
1212
- if kvs.has_key?(:clim)
1213
- c0, c1 = kvs[:clim]
1214
- c0 ||= cmin
1215
- c1 ||= cmax
1216
- kvs[:crange] = [c0, c1]
1217
- else
1218
- kvs[:crange] = [cmin, cmax]
1219
- end
1220
- end
1221
-
1222
- def to_wc(wn)
1223
- xmin, ymin = GR.ndctowc(wn[0], wn[2])
1224
- xmax, ymax = GR.ndctowc(wn[1], wn[3])
1225
- [xmin, xmax, ymin, ymax]
1226
- end
1227
-
1228
- def auto_tick(amin, amax)
1229
- scale = 10.0**Math.log10(amax - amin).truncate
1230
- tick_size = [5.0, 2.0, 1.0, 0.5, 0.2, 0.1, 0.05, 0.02, 0.01]
1231
- i = tick_size.find_index do |tsize|
1232
- ((amax - amin) / scale / tsize) > 7 # maximum number of tick marks
1233
- end
1234
- tick_size[i - 1] * scale
1235
- end
1236
-
1237
- def legend_size
1238
- scale = GR.inqscale
1239
- GR.selntran(0)
1240
- GR.setscale(0)
1241
- w = 0
1242
- h = 0
1243
- kvs[:labels].each do |label|
1244
- label = label.to_s
1245
- tbx, tby = inqtext(0, 0, label)
1246
- w = [w, tbx[2] - tbx[0]].max
1247
- h += [tby[2] - tby[0], 0.03].max
1248
- end
1249
- GR.setscale(scale)
1250
- GR.selntran(1)
1251
- [w, h]
1252
- end
1253
-
1254
- def equal_length(*args)
1255
- GRCommons::GRCommonUtils.equal_length(*args)
1256
- end
1257
-
1258
- def narray?(data)
1259
- GRCommons::GRCommonUtils.narray?(data)
1260
- end
1261
- end
1262
-
1263
- class << self
1264
- # (Plot) Draw one or more line plots.
1265
- def plot(*args)
1266
- create_plot(:line, *args)
1267
- end
1268
-
1269
- # (Plot) Draw one or more step or staircase plots.
1270
- def step(*args)
1271
- create_plot(:step, *args)
1272
- end
1273
-
1274
- # (Plot) Draw one or more scatter plots.
1275
- def scatter(*args)
1276
- create_plot(:scatter, *args)
1277
- end
1278
-
1279
- # (Plot) Draw a stem plot.
1280
- def stem(*args)
1281
- create_plot(:stem, *args)
1282
- end
1283
-
1284
- # (Plot)
1285
- def polarhistogram(x, kv = {})
1286
- plt = GR::Plot.new(x, kv)
1287
- plt.kvs[:kind] = :polarhist
1288
- nbins = plt.kvs[:nbins] || 0
1289
- x, y = hist(x, nbins)
1290
- plt.args = [[x, y, nil, nil, '']]
1291
- plt.plot_data
1292
- end
1293
-
1294
- # (Plot) Draw a heatmap.
1295
- def heatmap(*args)
1296
- # FIXME
1297
- args, kv = format_xyzc(*args)
1298
- _x, _y, z = args
1299
- ysize, xsize = z.shape
1300
- z = z.reshape(xsize, ysize)
1301
- create_plot(:heatmap, kv) do |plt|
1302
- plt.kvs[:xlim] ||= [0.5, xsize + 0.5]
1303
- plt.kvs[:ylim] ||= [0.5, ysize + 0.5]
1304
- plt.args = [[(1..xsize).to_a, (1..ysize).to_a, z, nil, '']]
1305
- end
1306
- end
1307
-
1308
- # (Plot) Draw a polarheatmap.
1309
- def polarheatmap(*args)
1310
- d = args.shift
1311
- # FIXME
1312
- z = Numo::DFloat.cast(d)
1313
- raise 'expected 2-D array' unless z.ndim == 2
1314
-
1315
- create_plot(:polarheatmap, z, *args) do |plt|
1316
- width, height = z.shape
1317
- plt.kvs[:xlim] ||= [0.5, width + 0.5]
1318
- plt.kvs[:ylim] ||= [0.5, height + 0.5]
1319
- plt.args = [[(1..width).to_a, (1..height).to_a, z, nil, '']]
1320
- end
1321
- end
1322
-
1323
- # (Plot) Draw a nonuniformpolarheatmap.
1324
- def nonuniformpolarheatmap(*args)
1325
- # FIXME
1326
- args, kv = format_xyzc(*args)
1327
- _x, _y, z = args
1328
- ysize, xsize = z.shape
1329
- z = z.reshape(xsize, ysize)
1330
- create_plot(:nonuniformpolarheatmap, kv) do |plt|
1331
- plt.kvs[:xlim] ||= [0.5, xsize + 0.5]
1332
- plt.kvs[:ylim] ||= [0.5, ysize + 0.5]
1333
- plt.args = [[(1..xsize).to_a, (1..ysize).to_a, z, nil, '']]
1334
- end
1335
- end
1336
-
1337
- alias _contour_ contour
1338
- # (Plot) Draw a contour plot.
1339
- def contour(*args)
1340
- create_plot(:contour, *format_xyzc(*args))
1341
- end
1342
-
1343
- alias _contourf_ contourf
1344
- # (Plot) Draw a filled contour plot.
1345
- def contourf(*args)
1346
- create_plot(:contourf, *format_xyzc(*args))
1347
- end
1348
-
1349
- alias _hexbin_ hexbin
1350
- # (Plot) Draw a hexagon binning plot.
1351
- def hexbin(*args)
1352
- create_plot(:hexbin, *args)
1353
- end
1354
-
1355
- # (Plot) Draw a triangular contour plot.
1356
- def tricont(*args)
1357
- create_plot(:tricont, *format_xyzc(*args))
1358
- end
1359
-
1360
- # (Plot) Draw a three-dimensional wireframe plot.
1361
- def wireframe(*args)
1362
- create_plot(:wireframe, *format_xyzc(*args))
1363
- end
1364
-
1365
- # (Plot) Draw a three-dimensional surface plot.
1366
- alias _surface_ surface
1367
- def surface(*args)
1368
- create_plot(:surface, *format_xyzc(*args))
1369
- end
1370
-
1371
- # (Plot)
1372
- def polar(*args)
1373
- create_plot(:polar, *args)
1374
- end
1375
-
1376
- # (Plot) Draw a triangular surface plot.
1377
- def trisurf(*args)
1378
- create_plot(:trisurf, *format_xyzc(*args))
1379
- end
1380
-
1381
- # (Plot) Draw one or more three-dimensional line plots.
1382
- def plot3(*args)
1383
- create_plot(:plot3, *args)
1384
- end
1385
-
1386
- # (Plot) Draw one or more three-dimensional scatter plots.
1387
- def scatter3(*args)
1388
- create_plot(:scatter3, *args)
1389
- end
1390
-
1391
- alias _shade_ shade
1392
- # (Plot)
1393
- def shade(*args)
1394
- create_plot(:shade, *args)
1395
- end
1396
-
1397
- # (Plot)
1398
- def volume(v, kv = {})
1399
- create_plot(:volume, v, kv) do |plt|
1400
- plt.args = [[nil, nil, v, nil, '']]
1401
- end
1402
- end
1403
-
1404
- # (Plot) Draw a bar plot.
1405
- def barplot(labels, heights, kv = {})
1406
- labels = labels.map(&:to_s)
1407
- wc, hc = barcoordinates(heights)
1408
- create_plot(:bar, labels, heights, kv) do |plt|
1409
- if kv[:horizontal]
1410
- plt.args = [[hc, wc, nil, nil, '']]
1411
- plt.kvs[:yticks] = [1, 1]
1412
- plt.kvs[:yticklabels] = labels
1413
- else
1414
- plt.args = [[wc, hc, nil, nil, '']]
1415
- plt.kvs[:xticks] = [1, 1]
1416
- plt.kvs[:xticklabels] = labels
1417
- end
1418
- end
1419
- end
1420
-
1421
- # (Plot) Draw a histogram.
1422
- def histogram(series, kv = {})
1423
- create_plot(:hist, series, kv) do |plt|
1424
- nbins = plt.kvs[:nbins] || 0
1425
- x, y = hist(series, nbins)
1426
- plt.args = if kv[:horizontal]
1427
- [[y, x, nil, nil, '']]
1428
- else
1429
- [[x, y, nil, nil, '']]
1430
- end
1431
- end
1432
- end
1433
-
1434
- # (Plot) Draw an image.
1435
- def imshow(img, kv = {})
1436
- img = Numo::DFloat.cast(img) # Umm...
1437
- create_plot(:imshow, img, kv) do |plt|
1438
- plt.args = [[nil, nil, img, nil, '']]
1439
- end
1440
- end
1441
-
1442
- # (Plot) Draw an isosurface.
1443
- def isosurface(v, kv = {})
1444
- v = Numo::DFloat.cast(v) # Umm...
1445
- create_plot(:isosurface, v, kv) do |plt|
1446
- plt.args = [[nil, nil, v, nil, '']]
1447
- end
1448
- end
1449
-
1450
- def hold(flag = true)
1451
- plt = GR::Plot.last_plot
1452
- plt.kvs.slice(:window, :scale, :xaxis, :yaxis, :zaxis).merge({ ax: flag, clear: !flag })
1453
- end
1454
-
1455
- # Set current subplot index.
1456
- def subplot(nr, nc, p, kv = {})
1457
- xmin = 1
1458
- xmax = 0
1459
- ymin = 1
1460
- ymax = 0
1461
- p = [p] if p.is_a? Integer
1462
- p.each do |i|
1463
- r = (nr - (i - 1) / nc).to_f
1464
- c = ((i - 1) % nc + 1).to_f
1465
- xmin = [xmin, (c - 1) / nc].min
1466
- xmax = [xmax, c / nc].max
1467
- ymin = [ymin, (r - 1) / nr].min
1468
- ymax = [ymax, r / nr].max
1469
- end
1470
- {
1471
- subplot: [xmin, xmax, ymin, ymax],
1472
- # The policy of clearing when p[0]==1 is controversial
1473
- clear: p[0] == 1,
1474
- update: p[-1] == nr * nc
1475
- }.merge kv
1476
- end
1477
-
1478
- # (Plot) Save the current figure to a file.
1479
- def savefig(filename, kv = {})
1480
- GR.beginprint(filename)
1481
- plt = GR::Plot.last_plot
1482
- plt.kvs.merge!(kv)
1483
- plt.plot_data(false)
1484
- GR.endprint
1485
- end
1486
-
1487
- private
1488
-
1489
- def create_plot(type, *args)
1490
- plt = GR::Plot.new(*args)
1491
- plt.kvs[:kind] = type
1492
- yield(plt) if block_given?
1493
- plt.plot_data
1494
- plt
1495
- end
1496
-
1497
- def format_xyzc(*args)
1498
- kv = if args[-1].is_a? Hash
1499
- args.pop
1500
- else
1501
- {}
1502
- end
1503
-
1504
- args = [args] unless args.all? do |i|
1505
- i.is_a?(Array) && (i[0].is_a?(Array) || narray?(i[0]))
1506
- end
1507
- args.map! do |xyzc|
1508
- if xyzc.size == 1
1509
- if xyzc[0].is_a? Array
1510
- z = Numo::DFloat.cast(xyzc[0])
1511
- elsif narray?(xyzc[0])
1512
- z = xyzc[0]
1513
- end
1514
- xsize, ysize = z.shape
1515
- x = (1..ysize).to_a * xsize
1516
- y = (1..xsize).map { |i| Array.new(ysize, i) }.flatten
1517
- [x, y, z]
1518
- else
1519
-
1520
- xyzc
1521
- end
1522
- end
1523
- [*args, kv]
1524
- end
1525
-
1526
- def hist(x, nbins = 0)
1527
- nbins = (3.3 * Math.log10(x.length)).round + 1 if nbins <= 1
1528
- begin
1529
- require 'histogram/array'
1530
- rescue LoadError => e
1531
- e.message << " Please add gem 'histogram' to your project's Gemfile."
1532
- raise e
1533
- end
1534
- x = x.to_a if narray?(x)
1535
- x, y = x.histogram(nbins, bin_boundary: :min)
1536
- x.push(x[-1] + x[1] - x[0])
1537
- [x, y]
1538
- end
1539
-
1540
- def barcoordinates(heights, barwidth = 0.8, baseline = 0.0)
1541
- halfw = barwidth / 2.0
1542
- wc = []
1543
- hc = []
1544
- heights.each_with_index do |value, i|
1545
- wc << i - halfw
1546
- wc << i + halfw
1547
- hc << baseline
1548
- hc << value
1549
- end
1550
- [wc, hc]
1551
- end
1552
- end
1553
- end