gnuplotrb 0.2.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.
- checksums.yaml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.rdoc +34 -0
- data/Rakefile +17 -0
- data/gnuplotrb.gemspec +30 -0
- data/lib/gnuplotrb.rb +30 -0
- data/lib/gnuplotrb/external_classes/array.rb +14 -0
- data/lib/gnuplotrb/external_classes/daru.rb +25 -0
- data/lib/gnuplotrb/external_classes/iruby.rb +18 -0
- data/lib/gnuplotrb/external_classes/string.rb +5 -0
- data/lib/gnuplotrb/mixins/error_handling.rb +42 -0
- data/lib/gnuplotrb/mixins/option_handling.rb +157 -0
- data/lib/gnuplotrb/mixins/plottable.rb +102 -0
- data/lib/gnuplotrb/multiplot.rb +150 -0
- data/lib/gnuplotrb/plot.rb +152 -0
- data/lib/gnuplotrb/splot.rb +18 -0
- data/lib/gnuplotrb/staff/datablock.rb +70 -0
- data/lib/gnuplotrb/staff/dataset.rb +233 -0
- data/lib/gnuplotrb/staff/settings.rb +65 -0
- data/lib/gnuplotrb/staff/terminal.rb +171 -0
- data/lib/gnuplotrb/version.rb +6 -0
- metadata +191 -0
@@ -0,0 +1,150 @@
|
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# === Overview
|
4
|
+
# Multiplot allows to place several plots on one layout.
|
5
|
+
class Multiplot
|
6
|
+
include Plottable
|
7
|
+
##
|
8
|
+
# Array of plots contained by this object.
|
9
|
+
attr_reader :plots
|
10
|
+
|
11
|
+
##
|
12
|
+
# ====== Arguments
|
13
|
+
# * *plots* are Plot or Splot objects which should be placed
|
14
|
+
# on this multiplot
|
15
|
+
# * *options* will be considered as 'settable' options of gnuplot
|
16
|
+
# ('set xrange [1:10]' for { xrange: 1..10 },
|
17
|
+
# "set title 'plot'" for { title: 'plot' } etc) just as in Plot.
|
18
|
+
# Special options of Multiplot are :layout and :title.
|
19
|
+
def initialize(*plots, **options)
|
20
|
+
@plots = plots[0].is_a?(Hamster::Vector) ? plots[0] : Hamster::Vector.new(plots)
|
21
|
+
@options = Hamster.hash(options)
|
22
|
+
@terminal = Terminal.new
|
23
|
+
OptionHandling.validate_terminal_options(@options)
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# Create new Multiplot object with the same set of plots and
|
28
|
+
# given options.
|
29
|
+
def new_with_options(options)
|
30
|
+
self.class.new(@plots, options)
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Check if given options corresponds to multiplot.
|
35
|
+
# Multiplot special options are :title and :layout.
|
36
|
+
def mp_option?(key)
|
37
|
+
%w(title layout).include?(key.to_s)
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# ====== Overview
|
42
|
+
# This outputs all the plots to term (if given) or to this
|
43
|
+
# Multiplot's own terminal.
|
44
|
+
# ====== Arguments
|
45
|
+
# * *term* - Terminal to plot to
|
46
|
+
# * *options* - will be considered as 'settable' options of gnuplot
|
47
|
+
# ('set xrange [1:10]', 'set title 'plot'' etc)
|
48
|
+
# Options passed here have priority over already existing.
|
49
|
+
# Inner options of Plots have the highest priority (except
|
50
|
+
# :term and :output which are ignored).
|
51
|
+
def plot(term = nil, **options)
|
52
|
+
all_options = @options.merge(options)
|
53
|
+
mp_options, plot_options = all_options.partition { |key, _value| mp_option?(key) }
|
54
|
+
plot_options = plot_options.merge(multiplot: mp_options.to_h)
|
55
|
+
terminal = term || (plot_options[:output] ? Terminal.new : @terminal)
|
56
|
+
terminal.set(plot_options)
|
57
|
+
@plots.each { |graph| graph.plot(terminal, multiplot_part: true) }
|
58
|
+
terminal.unset(plot_options.keys)
|
59
|
+
if plot_options[:output]
|
60
|
+
# guaranteed wait for plotting to finish
|
61
|
+
terminal.close unless term
|
62
|
+
# not guaranteed wait for plotting to finish
|
63
|
+
# work bad with terminals like svg and html
|
64
|
+
sleep 0.01 until File.size?(plot_options[:output])
|
65
|
+
end
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
##
|
70
|
+
# ====== Overview
|
71
|
+
# Create new Multiplot object where plot (Plot or Splot object)
|
72
|
+
# at *position* will
|
73
|
+
# be replaced with the new one created from it by updating.
|
74
|
+
# To update a plot you can pass some options for it or a
|
75
|
+
# block, that should take existing plot (with new options if
|
76
|
+
# you gave them) and return a plot too.
|
77
|
+
# ====== Arguments
|
78
|
+
# * *position* - position of plot which you need to update
|
79
|
+
# (by default first plot is updated)
|
80
|
+
# * *options* - options to update plot with
|
81
|
+
# * method also may take a block which returns a plot
|
82
|
+
# ====== Example
|
83
|
+
# mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
|
84
|
+
# updated_mp = mp.update_plot(title: 'Sin(x) and Exp(x)') { |sinx| sinx.add_dataset('exp(x)') }
|
85
|
+
def update_plot(position = 0, **options)
|
86
|
+
return self unless block_given? if options.empty?
|
87
|
+
replacement = @plots[position].options(options)
|
88
|
+
replacement = yield(replacement) if block_given?
|
89
|
+
replace_plot(position, replacement)
|
90
|
+
end
|
91
|
+
|
92
|
+
alias_method :update, :update_plot
|
93
|
+
|
94
|
+
##
|
95
|
+
# ====== Overview
|
96
|
+
# Create new Multiplot object where plot (Plot or Splot object)
|
97
|
+
# at *position* will be replaced with the given one.
|
98
|
+
# ====== Arguments
|
99
|
+
# * *position* - position of plot which you need to update
|
100
|
+
# (by default first plot is updated)
|
101
|
+
# * *plot* - replacement for existing plot
|
102
|
+
# ====== Example
|
103
|
+
# mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
|
104
|
+
# mp_with_replaced_plot = mp.replace_plot(Plot.new('exp(x)', title: 'exp instead of sin'))
|
105
|
+
def replace_plot(position = 0, plot)
|
106
|
+
self.class.new(@plots.set(position, plot), @options)
|
107
|
+
end
|
108
|
+
|
109
|
+
alias_method :replace, :replace_plot
|
110
|
+
|
111
|
+
##
|
112
|
+
# ====== Overview
|
113
|
+
# Create new Multiplot with given *plot* added before plot at given *position*.
|
114
|
+
# (by default it adds plot at the front).
|
115
|
+
# ====== Arguments
|
116
|
+
# * *position* - position before which you want to add a plot
|
117
|
+
# * *plot* - plot you want to add
|
118
|
+
# ====== Example
|
119
|
+
# mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
|
120
|
+
# enlarged_mp = mp.add_plot(Plot.new('exp(x)')).layout([3,1])
|
121
|
+
def add_plot(position = 0, plot)
|
122
|
+
self.class.new(@plots.insert(position, plot), @options)
|
123
|
+
end
|
124
|
+
|
125
|
+
alias_method :<<, :add_plot
|
126
|
+
alias_method :add, :add_plot
|
127
|
+
|
128
|
+
##
|
129
|
+
# ====== Overview
|
130
|
+
# Create new Multiplot without plot at given position
|
131
|
+
# (by default last plot is removed).
|
132
|
+
# ====== Arguments
|
133
|
+
# * *position* - position of plot you want to remove
|
134
|
+
# ====== Example
|
135
|
+
# mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
|
136
|
+
# mp_with_only_cos = mp.remove_plot(0)
|
137
|
+
def remove_plot(position = -1)
|
138
|
+
self.class.new(@plots.delete_at(position), @options)
|
139
|
+
end
|
140
|
+
|
141
|
+
alias_method :remove, :remove_plot
|
142
|
+
|
143
|
+
##
|
144
|
+
# ====== Overview
|
145
|
+
# Equal to #plots[*args]
|
146
|
+
def [](*args)
|
147
|
+
@plots[*args]
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# === Overview
|
4
|
+
# Plot correspond to simple 2D visualisation
|
5
|
+
class Plot
|
6
|
+
include Plottable
|
7
|
+
##
|
8
|
+
# Array of datasets which are plotted by this object.
|
9
|
+
attr_reader :datasets
|
10
|
+
##
|
11
|
+
# ====== Arguments
|
12
|
+
# * *datasets* are either instances of Dataset class or
|
13
|
+
# [data, **dataset_options] arrays from which Dataset may be created
|
14
|
+
# * *options* will be considered as 'settable' options of gnuplot
|
15
|
+
# ('set xrange [1:10]' for { xrange: 1..10 },
|
16
|
+
# "set title 'plot'" for { title: 'plot' } etc)
|
17
|
+
def initialize(*datasets, **options)
|
18
|
+
@datasets = if datasets[0].is_a? Hamster::Vector
|
19
|
+
datasets[0]
|
20
|
+
else
|
21
|
+
Hamster::Vector.new(datasets).map { |ds| dataset_from_any(ds) }
|
22
|
+
end
|
23
|
+
@options = Hamster.hash(options)
|
24
|
+
@already_plotted = false
|
25
|
+
@cmd = 'plot '
|
26
|
+
@terminal = Terminal.new
|
27
|
+
OptionHandling.validate_terminal_options(@options)
|
28
|
+
yield(self) if block_given?
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# For inner use!
|
33
|
+
# Creates new Plot with existing data and given options.
|
34
|
+
def new_with_options(options)
|
35
|
+
self.class.new(@datasets, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# ====== Overview
|
40
|
+
# This outputs plot to term (if given) or to this plot's own terminal.
|
41
|
+
# ====== Arguments
|
42
|
+
# * *term* - Terminal to plot to
|
43
|
+
# * *multiplot_part* - part of a multiplot. Option for inner usage
|
44
|
+
# * *options* - will be considered as 'settable' options of gnuplot
|
45
|
+
# ('set xrange [1:10]', 'set title 'plot'' etc)
|
46
|
+
# Options passed here have priority over already existing.
|
47
|
+
def plot(term = nil, multiplot_part: false, **options)
|
48
|
+
opts = @options.merge(options)
|
49
|
+
opts = opts.reject { |key, _value| [:term, :output].include?(key) } if multiplot_part
|
50
|
+
terminal = term || (opts[:output] ? Terminal.new : @terminal)
|
51
|
+
full_command = @cmd + @datasets.map { |dataset| dataset.to_s(terminal) }.join(' , ')
|
52
|
+
terminal.set(opts)
|
53
|
+
.puts(full_command)
|
54
|
+
.unset(opts.keys)
|
55
|
+
if opts[:output]
|
56
|
+
# guaranteed wait for plotting to finish
|
57
|
+
terminal.close unless term
|
58
|
+
# not guaranteed wait for plotting to finish
|
59
|
+
# work bad with terminals like svg and html
|
60
|
+
sleep 0.01 until File.size?(opts[:output])
|
61
|
+
end
|
62
|
+
@already_plotted = true
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
alias_method :replot, :plot
|
67
|
+
|
68
|
+
##
|
69
|
+
# ====== Overview
|
70
|
+
# Create new Plot object where dataset at *position* will
|
71
|
+
# be replaced with the new one created from it by updating.
|
72
|
+
# ====== Arguments
|
73
|
+
# * *position* - position of dataset which you need to update
|
74
|
+
# (by default first dataset is updated)
|
75
|
+
# * *data* - data to update dataset with
|
76
|
+
# * *options* - options to update dataset with
|
77
|
+
# ====== Example
|
78
|
+
# updated_plot = plot.update_dataset(data: [x1,y1], title: 'After update')
|
79
|
+
def update_dataset(position = 0, data: nil, **options)
|
80
|
+
old_ds = @datasets[position]
|
81
|
+
new_ds = old_ds.update(data, options)
|
82
|
+
new_ds.equal?(old_ds) ? self : replace_dataset(position, new_ds)
|
83
|
+
end
|
84
|
+
|
85
|
+
##
|
86
|
+
# ====== Overview
|
87
|
+
# Create new Plot object where dataset at *position* will
|
88
|
+
# be replaced with the given one.
|
89
|
+
# ====== Arguments
|
90
|
+
# * *position* - position of dataset which you need to update
|
91
|
+
# (by default first dataset is replaced)
|
92
|
+
# * *dataset* - dataset to replace the old one. You can also
|
93
|
+
# give here [data, **dataset_options] array from
|
94
|
+
# which Dataset may be created.
|
95
|
+
# ====== Example
|
96
|
+
# sinx = Plot.new('sin(x)')
|
97
|
+
# cosx = sinx.replace_dataset(['cos(x)'])
|
98
|
+
def replace_dataset(position = 0, dataset)
|
99
|
+
self.class.new(@datasets.set(position, dataset_from_any(dataset)), @options)
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# ====== Overview
|
104
|
+
# Create new Plot object where given dataset will
|
105
|
+
# be appended to dataset list.
|
106
|
+
# ====== Arguments
|
107
|
+
# * *dataset* - dataset to add
|
108
|
+
# ====== Example
|
109
|
+
# sinx = Plot.new('sin(x)')
|
110
|
+
# sinx_and_cosx = sinx.add(['cos(x)'])
|
111
|
+
#
|
112
|
+
# cosx_and_sinx = sinx << ['cos(x)']
|
113
|
+
def add_dataset(dataset)
|
114
|
+
self.class.new(@datasets.add(dataset_from_any(dataset)), @options)
|
115
|
+
end
|
116
|
+
|
117
|
+
alias_method :<<, :add_dataset
|
118
|
+
|
119
|
+
##
|
120
|
+
# ====== Overview
|
121
|
+
# Create new Plot object where dataset at given position
|
122
|
+
# will be removed from dataset list.
|
123
|
+
# ====== Arguments
|
124
|
+
# * *position* - position of dataset that should be
|
125
|
+
# removed (by default last dataset is removed)
|
126
|
+
# ====== Example
|
127
|
+
# sinx_and_cosx = Plot.new('sin(x)', 'cos(x)')
|
128
|
+
# sinx = sinx_and_cosx.remove_dataset
|
129
|
+
# cosx = sinx_and_cosx.remove_dataset(0)
|
130
|
+
def remove_dataset(position = -1)
|
131
|
+
self.class.new(@datasets.delete_at(position), @options)
|
132
|
+
end
|
133
|
+
|
134
|
+
##
|
135
|
+
# ====== Overview
|
136
|
+
# The same as Plot#datasets[args]
|
137
|
+
def [](*args)
|
138
|
+
@datasets[*args]
|
139
|
+
end
|
140
|
+
|
141
|
+
##
|
142
|
+
# Method for inner use.
|
143
|
+
# Check if given args is a dataset and returns it. Creates
|
144
|
+
# new dataset from given args otherwise.
|
145
|
+
def dataset_from_any(source)
|
146
|
+
source.is_a?(Dataset) ? source.clone : Dataset.new(*source)
|
147
|
+
end
|
148
|
+
|
149
|
+
private :dataset_from_any,
|
150
|
+
:new_with_options
|
151
|
+
end
|
152
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# === Overview
|
4
|
+
# Splot class correspond to simple 3D visualisation
|
5
|
+
class Splot < Plot
|
6
|
+
##
|
7
|
+
# ==== Arguments
|
8
|
+
# * *datasets* are either instances of Dataset class or
|
9
|
+
# [data, **dataset_options] arrays
|
10
|
+
# * *options* will be considered as 'settable' options of gnuplot
|
11
|
+
# ('set xrange [1:10]' for { xrange: 1..10 }, "set title 'plot'"
|
12
|
+
# for { title: 'plot' } etc)
|
13
|
+
def initialize(*datasets, **options)
|
14
|
+
super
|
15
|
+
@cmd = 'splot '
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# === Overview
|
4
|
+
# This class corresponds to points we want to plot. It may be
|
5
|
+
# stored in temporary file (to allow fast update) or inside
|
6
|
+
# "$DATA << EOD ... EOD" construction. Datablock stores data passed
|
7
|
+
# to constructor and keeps datablock name or path to file where it is stored.
|
8
|
+
class Datablock
|
9
|
+
##
|
10
|
+
# ====== Parameters
|
11
|
+
# * *data* - sequence of anything with +#to_gnuplot_points+ method.
|
12
|
+
# * *stored_in_file* true here will force this datablock to store its data
|
13
|
+
# in temporary file.
|
14
|
+
def initialize(data, stored_in_file = false)
|
15
|
+
@stored_in_file = stored_in_file
|
16
|
+
data_str = data.to_gnuplot_points
|
17
|
+
if @stored_in_file
|
18
|
+
@file_name = Dir::Tmpname.make_tmpname('tmp_data', 0)
|
19
|
+
File.write(@file_name, data_str)
|
20
|
+
name = File.join(Dir.pwd, @file_name)
|
21
|
+
ObjectSpace.define_finalizer(self, proc { File.delete(name) })
|
22
|
+
else
|
23
|
+
@data = data_str
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
##
|
28
|
+
# ====== Overview
|
29
|
+
# Instantiate one more Datablock with updated data
|
30
|
+
# if data stored in here-doc. Append update to file
|
31
|
+
# if data stored there.
|
32
|
+
# ====== Parameters
|
33
|
+
# * *data* - anything with +#to_gnuplot_points+ method
|
34
|
+
def update(data)
|
35
|
+
data_str = data.to_gnuplot_points
|
36
|
+
if @stored_in_file
|
37
|
+
File.open(@file_name, 'a') { |f| f.puts "\n#{data_str}" }
|
38
|
+
self
|
39
|
+
else
|
40
|
+
Datablock.new("#{@data}\n#{data_str}", false)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
##
|
45
|
+
# ====== Overview
|
46
|
+
# Returns quoted filename if datablock stored in file or outputs
|
47
|
+
# datablock to gnuplot and returns its name otherwise.
|
48
|
+
# * *gnuplot_term* should be given if datablock not stored in file.
|
49
|
+
def name(gnuplot_term = nil)
|
50
|
+
if @stored_in_file
|
51
|
+
"'#{@file_name}'"
|
52
|
+
else
|
53
|
+
fail(ArgumentError, 'No terminal given to output datablock') unless gnuplot_term
|
54
|
+
gnuplot_term.store_datablock(@data)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
alias_method :to_s, :name
|
59
|
+
|
60
|
+
##
|
61
|
+
# ====== Overview
|
62
|
+
# Overridden #clone. Since datablock which store data
|
63
|
+
# in temporary files should not be cloned (otherwise it will cause
|
64
|
+
# double attempt to delete file), this #clone returns self for such
|
65
|
+
# cases. For other cases it just calls default #clone.
|
66
|
+
def clone
|
67
|
+
@stored_in_file ? self : super
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,233 @@
|
|
1
|
+
module GnuplotRB
|
2
|
+
##
|
3
|
+
# === Overview
|
4
|
+
# Dataset keeps control of Datablock or String (some math functions like
|
5
|
+
# this 'x*sin(x)' or filename) and options related to original dataset
|
6
|
+
# in gnuplot (with, title, using etc).
|
7
|
+
class Dataset
|
8
|
+
include OptionHandling
|
9
|
+
##
|
10
|
+
# Data represented by this dataset
|
11
|
+
attr_reader :data
|
12
|
+
|
13
|
+
##
|
14
|
+
# Order is significant for some options
|
15
|
+
OPTION_ORDER = %w(index using axes title)
|
16
|
+
|
17
|
+
##
|
18
|
+
# Hash of init handlers for data given in
|
19
|
+
# different containers.
|
20
|
+
INIT_HANDLERS = Hash.new(:init_default).merge(
|
21
|
+
String => :init_string,
|
22
|
+
Datablock => :init_dblock
|
23
|
+
)
|
24
|
+
INIT_HANDLERS.merge!(
|
25
|
+
Daru::DataFrame => :init_daru_frame,
|
26
|
+
Daru::Vector => :init_daru_vector
|
27
|
+
) if defined? Daru
|
28
|
+
|
29
|
+
##
|
30
|
+
# ====== Overview
|
31
|
+
# Creates new dataset out of given string with
|
32
|
+
# math function or filename. If *data* isn't a string
|
33
|
+
# it will create datablock to store data.
|
34
|
+
# ====== Parameters
|
35
|
+
# * *data* - String, Datablock or something acceptable by
|
36
|
+
# Datablock.new as data (e.g. [x,y] where x and y are arrays)
|
37
|
+
# * *options* - hash of options specific for gnuplot
|
38
|
+
# dataset, and some special options ('file: true' will
|
39
|
+
# make data to be stored inside temporary file).
|
40
|
+
# ====== Examples
|
41
|
+
# Math function:
|
42
|
+
# Dataset.new('x*sin(x)', with: 'lines', lw: 4)
|
43
|
+
# File with points:
|
44
|
+
# Dataset.new('points.data', with: 'lines', title: 'Points from file')
|
45
|
+
# Some data (creates datablock stored in memory):
|
46
|
+
# x = (0..5000).to_a
|
47
|
+
# y = x.map {|xx| xx*xx }
|
48
|
+
# points = [x, y]
|
49
|
+
# Dataset.new(points, with: 'points', title: 'Points')
|
50
|
+
# The same data but datablock stores it in temp file:
|
51
|
+
# Dataset.new(points, with: 'points', title: 'Points', file: true)
|
52
|
+
def initialize(data, **options)
|
53
|
+
self.send(INIT_HANDLERS[data.class], data, options)
|
54
|
+
end
|
55
|
+
|
56
|
+
##
|
57
|
+
# Method for inner use.
|
58
|
+
def init_string(data, options)
|
59
|
+
@type, @data= if File.exist?(data)
|
60
|
+
[:datafile, "'#{data}'"]
|
61
|
+
else
|
62
|
+
[:math_function, data.clone]
|
63
|
+
end
|
64
|
+
@options = Hamster.hash(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Method for inner use.
|
69
|
+
def init_dblock(data, options)
|
70
|
+
@type = :datablock
|
71
|
+
@data = data.clone
|
72
|
+
@options = Hamster.hash(options)
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Method for inner use.
|
77
|
+
# TODO: make it better
|
78
|
+
def init_daru_frame(data, options)
|
79
|
+
options[:title] ||= data.name
|
80
|
+
if options[:using]
|
81
|
+
options[:using] = " #{options[:using]} "
|
82
|
+
data.vectors.to_a.each_with_index do |daru_index, array_index|
|
83
|
+
options[:using].gsub!(/([\:\( ])#{daru_index}([\:\) ])/) { "#{$1}#{array_index + 2}#{$2}"}
|
84
|
+
end
|
85
|
+
options[:using].gsub!(/index/) { 1 }
|
86
|
+
options[:using].strip!
|
87
|
+
else
|
88
|
+
new_opt = (2...(2 + data.vectors.size)).to_a.join(':')
|
89
|
+
options[:using] = "#{new_opt}:xtic(1)"
|
90
|
+
end
|
91
|
+
init_default(data, options)
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Method for inner use.
|
96
|
+
def init_daru_vector(data, options)
|
97
|
+
options[:using] ||= '2:xtic(1)'
|
98
|
+
options[:title] ||= data.name
|
99
|
+
init_default(data, options)
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
# Method for inner use.
|
104
|
+
def init_default(data, file: false, **options)
|
105
|
+
@type = :datablock
|
106
|
+
@data = Datablock.new(data, file)
|
107
|
+
@options = Hamster.hash(options)
|
108
|
+
end
|
109
|
+
|
110
|
+
##
|
111
|
+
# ====== Overview
|
112
|
+
# Converts Dataset to string containing gnuplot dataset.
|
113
|
+
# ====== Parameters
|
114
|
+
# * *terminal* - must be given if data given as Datablock and
|
115
|
+
# it does not use temp file so data should be piped out
|
116
|
+
# to gnuplot via terminal before use.
|
117
|
+
# ====== Examples
|
118
|
+
# Dataset.new('points.data', with: 'lines', title: 'Points from file').to_s
|
119
|
+
# #=> "'points.data' with lines title 'Points form file'"
|
120
|
+
# Dataset.new(points, with: 'points', title: 'Points').to_s
|
121
|
+
# #=> "$DATA1 with points title 'Points'"
|
122
|
+
def to_s(terminal = nil)
|
123
|
+
"#{@type == :datablock ? @data.name(terminal) : @data } #{options_to_string}"
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# ====== Overview
|
128
|
+
# Create string from own options
|
129
|
+
def options_to_string
|
130
|
+
options.sort_by { |key, _| OPTION_ORDER.find_index(key.to_s) || 999 }
|
131
|
+
.map { |key, value| OptionHandling.option_to_string(key, value) }
|
132
|
+
.join(' ')
|
133
|
+
end
|
134
|
+
|
135
|
+
##
|
136
|
+
# ====== Overview
|
137
|
+
# Creates new dataset with updated data (given
|
138
|
+
# data is appended to existing) and merged options.
|
139
|
+
# Data is updated only if Dataset stores it in Datablock.
|
140
|
+
# Method does nothing if no options given and data isn't stored
|
141
|
+
# in in-memory Datablock.
|
142
|
+
# ====== Parameters
|
143
|
+
# * *data* - data to append to existing
|
144
|
+
# * *options* - hash to merge with existing options
|
145
|
+
# ====== Examples
|
146
|
+
# Updating dataset with Math formula or filename given:
|
147
|
+
# dataset = Dataset.new('file.data')
|
148
|
+
# dataset.update('asd')
|
149
|
+
# #=> nothing updated
|
150
|
+
# dataset.update('asd', title: 'File')
|
151
|
+
# #=> Dataset.new('file.data', title: 'File')
|
152
|
+
# Updating dataset with data stored in Datablock:
|
153
|
+
# in_memory_points = Dataset.new(points, title: 'Old one')
|
154
|
+
# in_memory_points.update(some_update, title: 'Updated')
|
155
|
+
# #=> Dataset.new(points + some_update, title: 'Updated')
|
156
|
+
# temp_file_points = Dataset.new(points, title: 'Old one', file: true)
|
157
|
+
# temp_file_points.update(some_update)
|
158
|
+
# #=> data updated but no new dataset created
|
159
|
+
# temp_file_points.update(some_update, title: 'Updated')
|
160
|
+
# #=> data updated and new dataset with title 'Updated' returned
|
161
|
+
def update(data = nil, **options)
|
162
|
+
if data && @type == :datablock
|
163
|
+
new_datablock = @data.update(data)
|
164
|
+
if new_datablock == @data
|
165
|
+
update_options(options)
|
166
|
+
else
|
167
|
+
Dataset.new(@data.update(data), @options.merge(options))
|
168
|
+
end
|
169
|
+
else
|
170
|
+
update_options(options)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# ====== Overview
|
176
|
+
# Own implementation of #clone. Creates new Dataset if
|
177
|
+
# data stored in datablock and calls super otherwise.
|
178
|
+
def clone
|
179
|
+
if @type == :datablock
|
180
|
+
Dataset.new(@data, **@options)
|
181
|
+
else
|
182
|
+
super
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
##
|
187
|
+
# ====== Overview
|
188
|
+
# Creates new dataset with existing options merged with
|
189
|
+
# the given ones. Does nothing if no options given.
|
190
|
+
# ====== Parameters
|
191
|
+
# * *options* - hash to merge with existing options
|
192
|
+
# ====== Examples
|
193
|
+
# Updating dataset with Math formula or filename given:
|
194
|
+
# dataset = Dataset.new('file.data')
|
195
|
+
# dataset.update_options(title: 'File')
|
196
|
+
# #=> Dataset.new('file.data', title: 'File')
|
197
|
+
def update_options(**options)
|
198
|
+
if options.empty?
|
199
|
+
return self
|
200
|
+
else
|
201
|
+
Dataset.new(@data, @options.merge(options))
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
##
|
206
|
+
# Method for inner use.
|
207
|
+
# Needed by OptionHandling to create new object when
|
208
|
+
# options are changed.
|
209
|
+
def new_with_options(options)
|
210
|
+
self.class.new(@data, options)
|
211
|
+
end
|
212
|
+
|
213
|
+
##
|
214
|
+
# ====== Overview
|
215
|
+
# You may set options using #option_name(option_value) method.
|
216
|
+
# A new object will be constructed with selected option set.
|
217
|
+
# And finally you can get current value of any option using
|
218
|
+
# #options_name without arguments.
|
219
|
+
# ====== Examples
|
220
|
+
# dataset = Dataset.new('file.data')
|
221
|
+
# dataset.title #=> nil
|
222
|
+
# new_dataset = dataset.title('Awesome plot')
|
223
|
+
# dataset.title #=> nil
|
224
|
+
# new_dataset.title #=> 'Awesome plot'
|
225
|
+
def method_missing(meth_id, *args)
|
226
|
+
option(meth_id, *args)
|
227
|
+
end
|
228
|
+
|
229
|
+
private :init_default,
|
230
|
+
*INIT_HANDLERS.values,
|
231
|
+
:new_with_options
|
232
|
+
end
|
233
|
+
end
|