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