ruby-gr 0.58.1.0 → 0.64.0.1

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