ruby-gr 0.0.15 → 0.0.20

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.
@@ -63,6 +63,7 @@ module GR
63
63
  try_extern 'void gr_settextcolorind(int)'
64
64
  try_extern 'void gr_inqtextcolorind(int *)'
65
65
  try_extern 'void gr_setcharheight(double)'
66
+ try_extern 'void gr_inqcharheight(double *)'
66
67
  try_extern 'void gr_setcharup(double, double)'
67
68
  try_extern 'void gr_settextpath(int)'
68
69
  try_extern 'void gr_settextalign(int, int)'
@@ -196,6 +197,10 @@ module GR
196
197
  try_extern 'void gr_inqwindow3d(double *, double *, double *, double *, double *, double *)'
197
198
  try_extern 'void gr_setscalefactors3d(double, double, double)'
198
199
  try_extern 'void gr_inqscalefactors3d(double *, double *, double *)'
199
- try_extern 'void gr_transformationinterfaceforrepl(double, double, double, double)'
200
+ try_extern 'void gr_setspace3d(double, double, double, double)'
201
+ try_extern 'void gr_text3d(double, double, double, char *, int axis)'
202
+ try_extern 'void gr_inqtext3d(double, double, double, char *, int axis, double *, double *)'
203
+ try_extern 'void gr_settextencoding(int)'
204
+ try_extern 'void gr_inqtextencoding(int *)'
200
205
  end
201
206
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module GR
4
- # This module automatically converts Ruby arrays and Numo::Narray into pointers.
4
+ # This module automatically converts Ruby arrays and Numo::Narray into
5
+ # Fiddley::MemoryPointer.
5
6
  module GRBase
6
7
  extend GRCommons::DefineMethods
7
8
  define_ffi_methods(FFI,
@@ -9,6 +10,4 @@ module GR
9
10
  default_type: :double)
10
11
  end
11
12
  private_constant :GRBase
12
-
13
- extend GRCommons::GRCommonUtils
14
13
  end
@@ -37,59 +37,59 @@ module GR
37
37
  volume].freeze # the name might be changed in the future.
38
38
 
39
39
  # Keyword options conform to GR.jl.
40
- KW_ARGS = %i[accelerate algorithm alpha backgroundcolor barwidth baseline
41
- clabels color colormap figsize horizontal isovalue label labels
42
- levels location nbins rotation size tilt title where xflip
43
- xform xlabel xlim xlog yflip ylabel ylim ylog zflip zlabel zlim
44
- zlog clim subplot].freeze
45
-
46
- @@last_plot = nil
47
- def self.last_plot
48
- @@last_plot
40
+ KW_ARGS = %i[accelerate algorithm alpha ax backgroundcolor barwidth baseline
41
+ clabels clear clim color colormap crange figsize grid horizontal
42
+ isovalue kind label labels levels location nbins ratio rotation
43
+ scale size spec subplot tilt title update xaxis xflip xform
44
+ xlabel xlim xlog xrange xticks yaxis yflip ylabel ylim ylog
45
+ zflip yrange yticks viewport vp where window zaxis zlabel zlim
46
+ zlog zrange zticks].freeze
47
+
48
+ @last_plot = nil
49
+ class << self
50
+ attr_accessor :last_plot
49
51
  end
50
52
 
51
- def initialize(*args)
52
- @kvs = if args[-1].is_a? Hash
53
- args.pop
54
- else
55
- {}
56
- end
53
+ attr_accessor :args, :kvs, :scheme
54
+
55
+ def initialize(*raw_args)
56
+ @kvs = raw_args.last.is_a?(Hash) ? raw_args.pop : {}
57
+ @args = plot_args(raw_args) # method name is the same as Julia/Python
58
+
57
59
  # Check keyword options.
58
- @kvs.each_key do |k|
59
- warn "Unknown keyword: #{k}" unless KW_ARGS.include? k
60
- end
60
+ kvs.each_key { |k| warn "Unknown keyword: #{k}" unless KW_ARGS.include? k }
61
61
 
62
62
  # label(singular form) is a original keyword arg which GR.jl does not have.
63
- @kvs[:labels] = [@kvs[:label]] if @kvs[:label] && @kvs[:labels].nil?
64
-
65
- @args = plot_args(args) # method name is the same as Julia/Python
66
- @kvs[:size] ||= [600, 450]
67
- @kvs[:ax] ||= false
68
- @kvs[:subplot] ||= [0, 1, 0, 1]
69
- @kvs[:clear] ||= true
70
- @kvs[:update] ||= true
71
- @scheme = 0
63
+ kvs[:labels] ||= [kvs[:label]] if kvs.has_key? :label
64
+
65
+ # Don't use || because we need to tell `false` from `nil`
66
+ kvs[:size] = [600, 450] unless kvs.has_key? :size
67
+ kvs[:ax] = false unless kvs.has_key? :ax
68
+ kvs[:subplot] = [0, 1, 0, 1] unless kvs.has_key? :subplot
69
+ kvs[:clear] = true unless kvs.has_key? :clear
70
+ kvs[:update] = true unless kvs.has_key? :update
71
+
72
+ @scheme = 0
72
73
  @background = 0xffffff
73
- @handle = nil
74
- @@last_plot = self
74
+ # @handle = nil # This variable will be used in gr_meta
75
+
76
+ self.class.last_plot = self
75
77
  end
76
- attr_accessor :args, :kvs, :scheme
77
78
 
78
79
  def set_viewport(kind, subplot)
79
80
  mwidth, mheight, width, height = GR.inqdspsize
80
- if kvs[:figsize]
81
- w = 0.0254 * width * kvs[:figsize][0] / mwidth
82
- h = 0.0254 * height * kvs[:figsize][1] / mheight
83
- else
84
- dpi = width / mwidth * 0.0254
85
- if dpi > 200
86
- w, h = kvs[:size].map { |x| x * dpi / 100 }
87
- else
88
- w, h = kvs[:size]
89
- end
90
- end
91
- viewport = [0, 0, 0, 0]
81
+ dpi = width / mwidth * 0.0254
82
+ w, h = if kvs[:figsize]
83
+ [(0.0254 * width * kvs[:figsize][0] / mwidth),
84
+ (0.0254 * height * kvs[:figsize][1] / mheight)]
85
+ elsif dpi > 200
86
+ kvs[:size].map { |i| i * dpi / 100 }
87
+ else
88
+ kvs[:size]
89
+ end
90
+
92
91
  vp = subplot.clone
92
+
93
93
  if w > h
94
94
  ratio = h / w.to_f
95
95
  msize = mwidth * w / width
@@ -105,6 +105,7 @@ module GR
105
105
  vp[0] *= ratio
106
106
  vp[1] *= ratio
107
107
  end
108
+
108
109
  if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
109
110
  extent = [vp[1] - vp[0], vp[3] - vp[2]].min
110
111
  vp1 = 0.5 * (vp[0] + vp[1] - extent)
@@ -114,10 +115,12 @@ module GR
114
115
  else
115
116
  vp1, vp2, vp3, vp4 = vp
116
117
  end
117
- viewport[0] = vp1 + 0.125 * (vp2 - vp1)
118
- viewport[1] = vp1 + 0.925 * (vp2 - vp1)
119
- viewport[2] = vp3 + 0.125 * (vp4 - vp3)
120
- viewport[3] = vp3 + 0.925 * (vp4 - vp3)
118
+
119
+ viewport = [vp1 + 0.125 * (vp2 - vp1),
120
+ vp1 + 0.925 * (vp2 - vp1),
121
+ vp3 + 0.125 * (vp4 - vp3),
122
+ vp3 + 0.925 * (vp4 - vp3)]
123
+
121
124
  if %i[contour contourf hexbin heatmap nonuniformheatmap polarheatmap
122
125
  surface trisurf volume].include?(kind)
123
126
  viewport[1] -= 0.1
@@ -131,11 +134,11 @@ module GR
131
134
  end
132
135
  end
133
136
 
134
- GR.setviewport(viewport[0], viewport[1], viewport[2], viewport[3])
137
+ GR.setviewport(*viewport)
135
138
 
136
139
  kvs[:viewport] = viewport
137
- kvs[:vp] = vp
138
- kvs[:ratio] = ratio
140
+ kvs[:vp] = vp
141
+ kvs[:ratio] = ratio
139
142
 
140
143
  if kvs[:backgroundcolor]
141
144
  GR.savestate
@@ -165,14 +168,15 @@ module GR
165
168
  def set_window(kind)
166
169
  scale = 0
167
170
  unless %i[polar polarhist polarheatmap].include?(kind)
168
- scale |= GR::OPTION_X_LOG if kvs[:xlog]
169
- scale |= GR::OPTION_Y_LOG if kvs[:ylog]
170
- scale |= GR::OPTION_Z_LOG if kvs[:zlog]
171
+ scale |= GR::OPTION_X_LOG if kvs[:xlog]
172
+ scale |= GR::OPTION_Y_LOG if kvs[:ylog]
173
+ scale |= GR::OPTION_Z_LOG if kvs[:zlog]
171
174
  scale |= GR::OPTION_FLIP_X if kvs[:xflip]
172
175
  scale |= GR::OPTION_FLIP_Y if kvs[:yflip]
173
176
  scale |= GR::OPTION_FLIP_Z if kvs[:zflip]
174
177
  end
175
178
  kvs[:scale] = scale
179
+
176
180
  if kvs.has_key?(:panzoom)
177
181
  xmin, xmax, ymin, ymax = GR.panzoom(*kvs[:panzoom])
178
182
  kvs[:xrange] = [xmin, xmax]
@@ -181,72 +185,77 @@ module GR
181
185
  minmax
182
186
  end
183
187
 
184
- major_count = if %i[wireframe surface plot3 scatter3 polar polarhist
185
- polarheatmap trisurf volume].include?(kind)
186
- 2
187
- else
188
- 5
189
- end
188
+ major_count = %i[wireframe surface plot3 scatter3 polar polarhist
189
+ polarheatmap trisurf volume].include?(kind) ? 2 : 5
190
+
191
+ kvs[:xticks] = [kvs[:xticks], major_count] if kvs[:xticks].is_a? Numeric
192
+ kvs[:yticks] = [kvs[:yticks], major_count] if kvs[:yticks].is_a? Numeric
193
+ kvs[:zticks] = [kvs[:zticks], major_count] if kvs[:zticks].is_a? Numeric
190
194
 
191
195
  xmin, xmax = kvs[:xrange]
192
- if (scale & GR::OPTION_X_LOG) == 0
193
- xmin, xmax = GR.adjustlimits(xmin, xmax) unless kvs.has_key?(:xlim) || kvs.has_key?(:panzoom)
194
- if kvs.has_key?(:xticks)
195
- xtick, majorx = kvs[:xticks]
196
- else
197
- majorx = major_count
198
- xtick = GR.tick(xmin, xmax) / majorx
199
- end
200
- else
201
- xtick = majorx = 1
196
+ if %i[heatmap polarheatmap].include?(kind) && kvs.has_key?(:xlim)
197
+ xmin -= 0.5
198
+ xmax += 0.5
202
199
  end
203
- xorg = if (scale & GR::OPTION_FLIP_X) == 0
204
- [xmin, xmax]
205
- else
206
- [xmax, xmin]
207
- end
200
+ xtick, majorx = if (scale & GR::OPTION_X_LOG) == 0
201
+ unless %i[heatmap polarheatmap].include?(kind)
202
+ unless kvs.has_key?(:xlim)
203
+ xmin, xmax = GR.adjustlimits(xmin, xmax) unless kvs[:panzoom]
204
+ end
205
+ end
206
+ if kvs.has_key?(:xticks)
207
+ kvs[:xticks]
208
+ else
209
+ [GR.tick(xmin, xmax) / major_count, major_count]
210
+ end
211
+ else
212
+ [1, 1]
213
+ end
214
+ xorg = (scale & GR::OPTION_FLIP_X) == 0 ? [xmin, xmax] : [xmax, xmin]
208
215
  kvs[:xaxis] = xtick, xorg, majorx
209
216
 
210
217
  ymin, ymax = kvs[:yrange]
211
- if kind == :hist && !kvs.has_key?(:ylim)
212
- ymin = (scale & GR::OPTION_Y_LOG) == 0 ? 0 : 1
218
+ if %i[heatmap polarheatmap].include?(kind) && kvs.has_key?(:ylim)
219
+ ymin -= 0.5
220
+ ymax += 0.5
213
221
  end
214
- if (scale & GR::OPTION_Y_LOG) == 0
215
- ymin, ymax = GR.adjustlimits(ymin, ymax) unless kvs.has_key?(:ylim) || kvs.has_key?(:panzoom)
216
- if kvs.has_key?(:yticks)
217
- ytick, majory = kvs[:yticks]
218
- else
219
- majory = major_count
220
- ytick = GR.tick(ymin, ymax) / majory
222
+ if kind == :hist
223
+ if kvs[:horizontal] && !kvs.has_key?(:xlim)
224
+ xmin = (scale & GR::OPTION_X_LOG) == 0 ? 0 : 1
225
+ elsif !kvs.has_key?(:ylim)
226
+ ymin = (scale & GR::OPTION_Y_LOG) == 0 ? 0 : 1
221
227
  end
222
- else
223
- ytick = majory = 1
224
228
  end
225
- yorg = if (scale & GR::OPTION_FLIP_Y) == 0
226
- [ymin, ymax]
227
- else
228
- [ymax, ymin]
229
- end
229
+ ytick, majory = if (scale & GR::OPTION_Y_LOG) == 0
230
+ unless %i[heatmap polarheatmap].include?(kind)
231
+ unless kvs.has_key?(:ylim)
232
+ ymin, ymax = GR.adjustlimits(ymin, ymax) unless kvs[:panzoom]
233
+ end
234
+ end
235
+ if kvs.has_key?(:yticks)
236
+ kvs[:yticks]
237
+ else
238
+ [GR.tick(ymin, ymax) / major_count, major_count]
239
+ end
240
+ else
241
+ [1, 1]
242
+ end
243
+ yorg = (scale & GR::OPTION_FLIP_Y) == 0 ? [ymin, ymax] : [ymax, ymin]
230
244
  kvs[:yaxis] = ytick, yorg, majory
231
245
 
232
246
  if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
233
247
  zmin, zmax = kvs[:zrange]
234
- if (scale & GR::OPTION_Z_LOG) == 0
235
- zmin, zmax = GR.adjustlimits(zmin, zmax) if kvs.has_key?(:zlim)
236
- if kvs.has_key?(:zticks)
237
- ztick, majorz = kvs[:zticks]
238
- else
239
- majorz = major_count
240
- ztick = GR.tick(zmin, zmax) / majorz
241
- end
242
- else
243
- ztick = majorz = 1
244
- end
245
- zorg = if (scale & GR::OPTION_FLIP_Z) == 0
246
- [zmin, zmax]
247
- else
248
- [zmax, zmin]
249
- end
248
+ ztick, majorz = if (scale & GR::OPTION_Z_LOG) == 0
249
+ zmin, zmax = GR.adjustlimits(zmin, zmax) if kvs.has_key?(:zlim)
250
+ if kvs.has_key?(:zticks)
251
+ kvs[:zticks]
252
+ else
253
+ [GR.tick(zmin, zmax) / major_count, major_count]
254
+ end
255
+ else
256
+ [1, 1]
257
+ end
258
+ zorg = (scale & GR::OPTION_FLIP_Z) == 0 ? [zmin, zmax] : [zmax, zmin]
250
259
  kvs[:zaxis] = ztick, zorg, majorz
251
260
  end
252
261
 
@@ -258,7 +267,7 @@ module GR
258
267
  end
259
268
  if %i[wireframe surface plot3 scatter3 trisurf volume].include?(kind)
260
269
  rotation = kvs[:rotation] || 40
261
- tilt = kvs[:tilt] || 70
270
+ tilt = kvs[:tilt] || 70
262
271
  GR.setspace(zmin, zmax, rotation, tilt)
263
272
  end
264
273
 
@@ -272,7 +281,7 @@ module GR
272
281
  ratio = kvs[:ratio]
273
282
  xtick, xorg, majorx = kvs[:xaxis]
274
283
  ytick, yorg, majory = kvs[:yaxis]
275
- drawgrid = kvs[:grid] || true
284
+ drawgrid = kvs.has_key?(:grid) ? kvs[:grid] : true
276
285
  xtick = 10 if kvs[:scale] & GR::OPTION_X_LOG != 0
277
286
  ytick = 10 if kvs[:scale] & GR::OPTION_Y_LOG != 0
278
287
  GR.setlinecolorind(1)
@@ -293,8 +302,8 @@ module GR
293
302
  else
294
303
  if %i[heatmap nonuniformheatmap shade].include?(kind)
295
304
  ticksize = -ticksize
296
- else
297
- drawgrid && GR.grid(xtick, ytick, 0, 0, majorx, majory)
305
+ elsif drawgrid
306
+ GR.grid(xtick, ytick, 0, 0, majorx, majory)
298
307
  end
299
308
  if kvs.has_key?(:xticklabels) || kvs.has_key?(:yticklabels)
300
309
  fx = if kvs.has_key?(:xticklabels)
@@ -332,7 +341,7 @@ module GR
332
341
  GR.axes(xtick, ytick, xorg[1], yorg[1], -majorx, -majory, -ticksize)
333
342
  end
334
343
 
335
- if kvs.has_key?(:title)
344
+ if kvs[:title]
336
345
  GR.savestate
337
346
  GR.settextalign(GR::TEXT_HALIGN_CENTER, GR::TEXT_VALIGN_TOP)
338
347
  text(0.5 * (viewport[0] + viewport[1]), vp[3], kvs[:title])
@@ -421,7 +430,7 @@ module GR
421
430
  if img.is_a? String
422
431
  width, height, data = GR.readimage(img)
423
432
  else
424
- width, height = img.shape
433
+ height, width = img.shape
425
434
  cmin, cmax = kvs[:crange]
426
435
  data = img.map { |i| normalize_color(i, cmin, cmax) }
427
436
  data = data.map { |i| (1000 + i * 255).round }
@@ -488,7 +497,9 @@ module GR
488
497
  end
489
498
 
490
499
  GR.selntran(0)
491
- values = ((v - v.min) / (v.max - v.min) * (2 ^ 16 - 1)).round
500
+ v = Numo::DFloat.cast(v) if v.is_a? Array
501
+ values = ((v - v.min) / (v.max - v.min) * (2**16 - 1)).round
502
+ values = Numo::UInt16.cast(values)
492
503
  nx, ny, nz = v.shape
493
504
  isovalue = ((kvs[:isovalue] || 0.5) - v.min) / (v.max - v.min)
494
505
  rotation = ((kvs[:rotation] || 40) * Math::PI / 180.0)
@@ -497,7 +508,7 @@ module GR
497
508
  GR3.clear
498
509
  mesh = GR3.createisosurfacemesh(values, [2.0 / (nx - 1), 2.0 / (ny - 1), 2.0 / (nz - 1)],
499
510
  [-1, -1, -1],
500
- (isovalue * (2 ^ 16 - 1)).round)
511
+ (isovalue * (2**16 - 1)).round)
501
512
  color = kvs[:color] || [0.0, 0.5, 0.8]
502
513
  GR3.setbackgroundcolor(1, 1, 1, 0)
503
514
  GR3.drawmesh(mesh, 1, [0, 0, 0], [0, 0, 1], [0, 1, 0], color, [1, 1, 1])
@@ -582,8 +593,7 @@ module GR
582
593
 
583
594
  GR.uselinespec(' ')
584
595
  args.each do |x, y, z, c, spec|
585
- # FIXME
586
- spec ||= ''
596
+ spec ||= kvs[:spec] ||= ''
587
597
  GR.savestate
588
598
  GR.settransparency(kvs[:alpha]) if kvs.has_key?(:alpha)
589
599
 
@@ -657,14 +667,26 @@ module GR
657
667
  GR.polymarker(x, y)
658
668
 
659
669
  when :hist
660
- ymin = kvs[:window][2]
661
- y.length.times do |i|
662
- GR.setfillcolorind(989)
663
- GR.setfillintstyle(GR::INTSTYLE_SOLID)
664
- GR.fillrect(x[i], x[i + 1], ymin, y[i])
665
- GR.setfillcolorind(1)
666
- GR.setfillintstyle(GR::INTSTYLE_HOLLOW)
667
- GR.fillrect(x[i], x[i + 1], ymin, y[i])
670
+ if kvs[:horizontal]
671
+ xmin = kvs[:window][0]
672
+ x.length.times do |i|
673
+ GR.setfillcolorind(989)
674
+ GR.setfillintstyle(GR::INTSTYLE_SOLID)
675
+ GR.fillrect(xmin, x[i], y[i], y[i + 1])
676
+ GR.setfillcolorind(1)
677
+ GR.setfillintstyle(GR::INTSTYLE_HOLLOW)
678
+ GR.fillrect(xmin, x[i], y[i], y[i + 1])
679
+ end
680
+ else
681
+ ymin = kvs[:window][2]
682
+ y.length.times do |i|
683
+ GR.setfillcolorind(989)
684
+ GR.setfillintstyle(GR::INTSTYLE_SOLID)
685
+ GR.fillrect(x[i], x[i + 1], ymin, y[i])
686
+ GR.setfillcolorind(1)
687
+ GR.setfillintstyle(GR::INTSTYLE_HOLLOW)
688
+ GR.fillrect(x[i], x[i + 1], ymin, y[i])
689
+ end
668
690
  end
669
691
 
670
692
  when :polarhist
@@ -685,9 +707,10 @@ module GR
685
707
  cmap = colormap
686
708
  cmin, cmax = kvs[:zrange]
687
709
  data = z.map { |i| normalize_color(i, cmin, cmax) }
688
- colors = data.map { |i| 1000 + i * 255 }
689
- # if kvs[:xflip]
690
- # if kvs[;yflip]
710
+ data.reverse(axis: 0) if kvs[:xflip]
711
+ data.reverse(axis: 1) if kvs[:yflip]
712
+ colors = data * 255 + 1000
713
+ colors = colors.transpose # Julia is column major
691
714
  GR.polarcellarray(0, 0, 0, 360, 0, 1, w, h, colors)
692
715
  draw_polar_axes
693
716
  kvs[:zrange] = [cmin, cmax]
@@ -699,11 +722,18 @@ module GR
699
722
  a, b = z.shape
700
723
  x = (1..b).to_a
701
724
  y = (1..a).to_a
702
- zmin, zmax = kvs[:zlim] || z.minmax
725
+ zmin, zmax = z.minmax
703
726
  elsif equal_length(x, y, z)
704
727
  x, y, z = GR.gridit(x, y, z, 200, 200)
705
- zmin, zmax = kvs[:zlim] || z.compact.minmax # compact : removed nil
728
+ zmin, zmax = z.compact.minmax # compact : removed nil
729
+ end
730
+
731
+ # kvs[:zlim] is supposed to be Array or Range
732
+ if kvs.has_key?(:zlim)
733
+ zmin = kvs[:zlim].first if kvs[:zlim].first
734
+ zmax = kvs[:zlim].last if kvs[:zlim].last
706
735
  end
736
+
707
737
  GR.setspace(zmin, zmax, 0, 90)
708
738
  levels = kvs[:levels] || 0
709
739
  clabels = kvs[:clabels] || false
@@ -795,7 +825,9 @@ module GR
795
825
  colorbar(0.05)
796
826
 
797
827
  when :plot3
798
- GR.polyline3d(x, y, z)
828
+ mask = GR.uselinespec(spec)
829
+ GR.polyline3d(x, y, z) if hasline(mask)
830
+ GR.polymarker3d(x, y, z) if hasmarker(mask)
799
831
  draw_axes(kind, 2)
800
832
 
801
833
  when :scatter3
@@ -858,7 +890,7 @@ module GR
858
890
 
859
891
  draw_legend if %i[line step scatter stem].include?(kind) && kvs.has_key?(:labels)
860
892
 
861
- if kvs.has_key?(:update)
893
+ if kvs[:update]
862
894
  GR.updatews
863
895
  # if GR.isinline()
864
896
  # restore_context()
@@ -978,24 +1010,31 @@ module GR
978
1010
  [*0..(num - 1)].collect { |i| low + i.to_f * (high - low) / (num - 1) }
979
1011
  end
980
1012
 
981
- def plot_args(args, _fmt = :xys)
1013
+ def plot_args(args)
982
1014
  # FIXME
983
1015
  args = [args] unless args.all? do |i|
984
1016
  i.is_a?(Array) && (i[0].is_a?(Array) || narray?(i[0]))
985
1017
  end
986
1018
  args.map do |xyzc|
1019
+ spec = nil
1020
+ case xyzc.last
1021
+ when String
1022
+ spec = xyzc.pop
1023
+ when Hash
1024
+ spec = xyzc.pop[:spec]
1025
+ end
1026
+
987
1027
  x, y, z, c = xyzc.map do |i|
988
- if i.is_a?(Array) || narray?(i)
1028
+ if i.is_a?(Array) || narray?(i) || i.nil?
989
1029
  i
990
1030
  elsif i.respond_to?(:to_a)
991
1031
  # Convert an Array-like class such as Daru::Vector to an Array
992
1032
  i.to_a
993
- else
994
- # String
1033
+ else # String
995
1034
  i
996
1035
  end
997
1036
  end
998
- [x, y, z, c]
1037
+ [x, y, z, c, spec]
999
1038
  end
1000
1039
  end
1001
1040
 
@@ -1042,7 +1081,8 @@ module GR
1042
1081
  args.each do |x, y, z, c|
1043
1082
  if x
1044
1083
  if scale & GR::OPTION_X_LOG != 0
1045
- x.map! { |v| v > 0 ? v : Float::NAN }
1084
+ # duck typing for NArray
1085
+ x = x.map { |v| v > 0 ? v : Float::NAN }
1046
1086
  end
1047
1087
  x0, x1 = x.minmax
1048
1088
  xmin = [x0, xmin].min
@@ -1053,7 +1093,7 @@ module GR
1053
1093
  end
1054
1094
  if y
1055
1095
  if scale & GR::OPTION_Y_LOG != 0
1056
- y.map! { |v| v > 0 ? v : Float::NAN }
1096
+ y = y.map { |v| v > 0 ? v : Float::NAN }
1057
1097
  end
1058
1098
  y0, y1 = y.minmax
1059
1099
  ymin = [y0, ymin].min
@@ -1064,7 +1104,7 @@ module GR
1064
1104
  end
1065
1105
  if z
1066
1106
  if scale & GR::OPTION_Z_LOG != 0
1067
- z.map! { |v| v > 0 ? v : Float::NAN }
1107
+ z = z.map { |v| v > 0 ? v : Float::NAN }
1068
1108
  end
1069
1109
  z0, z1 = z.minmax
1070
1110
  zmin = [z0, zmin].min
@@ -1083,30 +1123,12 @@ module GR
1083
1123
  xmin, xmax = fix_minmax(xmin, xmax)
1084
1124
  ymin, ymax = fix_minmax(ymin, ymax)
1085
1125
  zmin, zmax = fix_minmax(zmin, zmax)
1086
- if kvs.has_key?(:xlim)
1087
- x0, x1 = kvs[:xlim]
1088
- x0 ||= xmin
1089
- x1 ||= xmax
1090
- kvs[:xrange] = [x0, x1]
1091
- else
1092
- kvs[:xrange] = [xmin, xmax]
1093
- end
1094
- if kvs.has_key?(:ylim)
1095
- y0, y1 = kvs[:ylim]
1096
- y0 ||= ymin
1097
- y1 ||= ymax
1098
- kvs[:yrange] = [y0, y1]
1099
- else
1100
- kvs[:yrange] = [ymin, ymax]
1101
- end
1102
- if kvs.has_key?(:zlim)
1103
- z0, z1 = kvs[:zlim]
1104
- z0 ||= zmin
1105
- z1 ||= zmax
1106
- kvs[:zrange] = [z0, z1]
1107
- else
1108
- kvs[:zrange] = [zmin, zmax]
1109
- end
1126
+
1127
+ # kvs[:xlim], kvs[:ylim], kvs[:zlim] is supposed to be Array or Range
1128
+ kvs[:xrange] = [(kvs[:xlim]&.first || xmin), (kvs[:xlim]&.last || xmax)]
1129
+ kvs[:yrange] = [(kvs[:ylim]&.first || ymin), (kvs[:ylim]&.last || ymax)]
1130
+ kvs[:zrange] = [(kvs[:zlim]&.first || zmin), (kvs[:zlim]&.last || zmax)]
1131
+
1110
1132
  if kvs.has_key?(:clim)
1111
1133
  c0, c1 = kvs[:clim]
1112
1134
  c0 ||= cmin
@@ -1185,7 +1207,8 @@ module GR
1185
1207
  # (Plot) Draw a heatmap.
1186
1208
  def heatmap(*args)
1187
1209
  # FIXME
1188
- _x, _y, z, kv = parse_args(*args)
1210
+ args, kv = format_xyzc(*args)
1211
+ _x, _y, z = args
1189
1212
  ysize, xsize = z.shape
1190
1213
  z = z.reshape(xsize, ysize)
1191
1214
  create_plot(:heatmap, kv) do |plt|
@@ -1213,15 +1236,13 @@ module GR
1213
1236
  alias _contour_ contour
1214
1237
  # (Plot) Draw a contour plot.
1215
1238
  def contour(*args)
1216
- x, y, z, kv = parse_args(*args)
1217
- create_plot(:contour, x, y, z, kv)
1239
+ create_plot(:contour, *format_xyzc(*args))
1218
1240
  end
1219
1241
 
1220
1242
  alias _contourf_ contourf
1221
1243
  # (Plot) Draw a filled contour plot.
1222
1244
  def contourf(*args)
1223
- x, y, z, kv = parse_args(*args)
1224
- create_plot(:contourf, x, y, z, kv)
1245
+ create_plot(:contourf, *format_xyzc(*args))
1225
1246
  end
1226
1247
 
1227
1248
  alias _hexbin_ hexbin
@@ -1232,21 +1253,18 @@ module GR
1232
1253
 
1233
1254
  # (Plot) Draw a triangular contour plot.
1234
1255
  def tricont(*args)
1235
- x, y, z, kv = parse_args(*args)
1236
- create_plot(:tricont, x, y, z, kv)
1256
+ create_plot(:tricont, *format_xyzc(*args))
1237
1257
  end
1238
1258
 
1239
1259
  # (Plot) Draw a three-dimensional wireframe plot.
1240
1260
  def wireframe(*args)
1241
- x, y, z, kv = parse_args(*args)
1242
- create_plot(:wireframe, x, y, z, kv)
1261
+ create_plot(:wireframe, *format_xyzc(*args))
1243
1262
  end
1244
1263
 
1245
1264
  # (Plot) Draw a three-dimensional surface plot.
1246
1265
  alias _surface_ surface
1247
1266
  def surface(*args)
1248
- x, y, z, kv = parse_args(*args)
1249
- create_plot(:surface, x, y, z, kv)
1267
+ create_plot(:surface, *format_xyzc(*args))
1250
1268
  end
1251
1269
 
1252
1270
  # (Plot)
@@ -1256,8 +1274,7 @@ module GR
1256
1274
 
1257
1275
  # (Plot) Draw a triangular surface plot.
1258
1276
  def trisurf(*args)
1259
- x, y, z, kv = parse_args(*args)
1260
- create_plot(:trisurf, x, y, z, kv)
1277
+ create_plot(:trisurf, *format_xyzc(*args))
1261
1278
  end
1262
1279
 
1263
1280
  # (Plot) Draw one or more three-dimensional line plots.
@@ -1287,9 +1304,8 @@ module GR
1287
1304
  def barplot(labels, heights, kv = {})
1288
1305
  labels = labels.map(&:to_s)
1289
1306
  wc, hc = barcoordinates(heights)
1290
- horizontal = kv[:horizontal] || false
1291
1307
  create_plot(:bar, labels, heights, kv) do |plt|
1292
- if horizontal
1308
+ if kv[:horizontal]
1293
1309
  plt.args = [[hc, wc, nil, nil, '']]
1294
1310
  plt.kvs[:yticks] = [1, 1]
1295
1311
  plt.kvs[:yticklabels] = labels
@@ -1302,11 +1318,15 @@ module GR
1302
1318
  end
1303
1319
 
1304
1320
  # (Plot) Draw a histogram.
1305
- def histogram(x, kv = {})
1306
- create_plot(:hist, x, kv) do |plt|
1321
+ def histogram(series, kv = {})
1322
+ create_plot(:hist, series, kv) do |plt|
1307
1323
  nbins = plt.kvs[:nbins] || 0
1308
- x, y = hist(x, nbins)
1309
- plt.args = [[x, y, nil, nil, '']]
1324
+ x, y = hist(series, nbins)
1325
+ plt.args = if kv[:horizontal]
1326
+ [[y, x, nil, nil, '']]
1327
+ else
1328
+ [[x, y, nil, nil, '']]
1329
+ end
1310
1330
  end
1311
1331
  end
1312
1332
 
@@ -1326,6 +1346,34 @@ module GR
1326
1346
  end
1327
1347
  end
1328
1348
 
1349
+ def hold(flag = true)
1350
+ plt = GR::Plot.last_plot
1351
+ plt.kvs.slice(:window, :scale, :xaxis, :yaxis, :zaxis).merge({ ax: flag, clear: !flag })
1352
+ end
1353
+
1354
+ # Set current subplot index.
1355
+ def subplot(nr, nc, p, kv = {})
1356
+ xmin = 1
1357
+ xmax = 0
1358
+ ymin = 1
1359
+ ymax = 0
1360
+ p = [p] if p.is_a? Integer
1361
+ p.each do |i|
1362
+ r = (nr - (i - 1) / nc).to_f
1363
+ c = ((i - 1) % nc + 1).to_f
1364
+ xmin = [xmin, (c - 1) / nc].min
1365
+ xmax = [xmax, c / nc].max
1366
+ ymin = [ymin, (r - 1) / nr].min
1367
+ ymax = [ymax, r / nr].max
1368
+ end
1369
+ {
1370
+ subplot: [xmin, xmax, ymin, ymax],
1371
+ # The policy of clearing when p[0]==1 is controversial
1372
+ clear: p[0] == 1,
1373
+ update: p[-1] == nr * nc
1374
+ }.merge kv
1375
+ end
1376
+
1329
1377
  # (Plot) Save the current figure to a file.
1330
1378
  def savefig(filename, kv = {})
1331
1379
  GR.beginprint(filename)
@@ -1337,40 +1385,41 @@ module GR
1337
1385
 
1338
1386
  private
1339
1387
 
1340
- def create_plot(type, *args, &block)
1388
+ def create_plot(type, *args)
1341
1389
  plt = GR::Plot.new(*args)
1342
1390
  plt.kvs[:kind] = type
1343
- block.call(plt) if block_given?
1391
+ yield(plt) if block_given?
1344
1392
  plt.plot_data
1345
1393
  plt
1346
1394
  end
1347
1395
 
1348
- def parse_args(*args)
1396
+ def format_xyzc(*args)
1349
1397
  kv = if args[-1].is_a? Hash
1350
1398
  args.pop
1351
1399
  else
1352
1400
  {}
1353
1401
  end
1354
- if args.size == 1
1355
- if args[0].is_a? Array
1356
- z = Numo::DFloat.cast(args[0])
1357
- elsif narray?(args[0])
1358
- z = args[0]
1402
+
1403
+ args = [args] unless args.all? do |i|
1404
+ i.is_a?(Array) && (i[0].is_a?(Array) || narray?(i[0]))
1405
+ end
1406
+ args.map! do |xyzc|
1407
+ if xyzc.size == 1
1408
+ if xyzc[0].is_a? Array
1409
+ z = Numo::DFloat.cast(xyzc[0])
1410
+ elsif narray?(xyzc[0])
1411
+ z = xyzc[0]
1412
+ end
1413
+ xsize, ysize = z.shape
1414
+ x = (1..ysize).to_a * xsize
1415
+ y = (1..xsize).map { |i| Array.new(ysize, i) }.flatten
1416
+ [x, y, z]
1417
+ else
1418
+
1419
+ xyzc
1359
1420
  end
1360
- xsize, ysize = z.shape
1361
- # NOTE:
1362
- # See
1363
- # https://github.com/jheinen/GR.jl/pull/246
1364
- # https://github.com/jheinen/GR.jl/issues/241
1365
- x = (1..ysize).to_a * xsize
1366
- y = (1..xsize).map { |i| Array.new(ysize, i) }.flatten
1367
-
1368
- elsif args.size == 3
1369
- x, y, z = args
1370
- else
1371
- raise
1372
1421
  end
1373
- [x, y, z, kv]
1422
+ [*args, kv]
1374
1423
  end
1375
1424
 
1376
1425
  def hist(x, nbins = 0)