├── .gitignore
├── .rubocop.yml
├── .travis.yml
├── Gemfile
├── LICENSE
├── README.rdoc
├── Rakefile
├── examples
├── multiplot
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_3d_surface
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_fit_exp
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ ├── plot.rb
│ └── points.data
├── plot_fit_general
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ ├── plot.rb
│ └── points.data
├── plot_fit_poly
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ ├── plot.rb
│ └── points.data
├── plot_from_datafile
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ ├── plot.rb
│ └── points.data
├── plot_histogram
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_in_parametric
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_in_polar
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_math_function
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_points
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_several_datasets
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_test_term
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── plot_to_image_file
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ ├── plot.rb
│ ├── points.data
│ ├── real_result.html
│ ├── real_result.svg
│ └── real_result.txt
├── plot_to_string
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ ├── plot.rb
│ └── points.data
├── tons_of_data
│ ├── generate_data.rb
│ ├── plot.rb
│ └── result_screenshot.png
└── use_multi_word_option_keys
│ ├── gnuplot.png
│ ├── gnuplot_gem.png
│ ├── plot.gnuplot
│ └── plot.rb
├── gnuplotrb.gemspec
├── lib
├── gnuplotrb.rb
└── gnuplotrb
│ ├── animation.rb
│ ├── external_classes
│ ├── array.rb
│ ├── daru.rb
│ └── string.rb
│ ├── fit.rb
│ ├── mixins
│ ├── error_handling.rb
│ ├── option_handling.rb
│ └── plottable.rb
│ ├── multiplot.rb
│ ├── plot.rb
│ ├── splot.rb
│ ├── staff
│ ├── datablock.rb
│ ├── dataset.rb
│ ├── settings.rb
│ └── terminal.rb
│ └── version.rb
├── notebooks
├── .gitignore
├── 3d_plot.ipynb
├── Gemfile
├── README.rdoc
├── animated_plots.ipynb
├── basic_usage.ipynb
├── fitting_data.ipynb
├── heatmaps.ipynb
├── histogram.ipynb
├── math_plots.ipynb
├── multiplot_layout.ipynb
├── plotting_from_daru.ipynb
├── points_from_different_sources.ipynb
├── time_series_from_daru.ipynb
├── updating_data.ipynb
└── vector_field.ipynb
└── spec
├── animation_spec.rb
├── dataset_spec.rb
├── fit_spec.rb
├── gnuplot_spec.rb
├── multiplot_spec.rb
├── plot_spec.rb
├── points.data
├── spec_helper.rb
├── splot_spec.rb
└── terminal_spec.rb
/.gitignore:
--------------------------------------------------------------------------------
1 | /.bundle/
2 | /.yardoc
3 | /Gemfile.lock
4 | /_yardoc/
5 | /coverage/
6 | /doc/
7 | /pkg/
8 | /html/
9 | /spec/reports/
10 | /tmp/
11 | /.idea/
--------------------------------------------------------------------------------
/.rubocop.yml:
--------------------------------------------------------------------------------
1 | AllCops:
2 | Include:
3 | - 'lib/**/*'
4 | Exclude:
5 | - 'examples/**/*'
6 | DisplayCopNames: true
7 | DisplayStyleGuide: true
8 | Metrics/LineLength:
9 | Max: 100
10 | Style/GlobalVars:
11 | AllowedVariables: [$RSPEC_TEST]
12 | Style/MultilineOperationIndentation:
13 | Enabled: false
14 | Metrics/MethodLength:
15 | Max: 15
16 | Metrics/AbcSize:
17 | Max: 20
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | notifications:
2 | email: false
3 | language: ruby
4 | rvm:
5 | - ruby-2.0
6 | - ruby-2.1.5
7 | - ruby-2.2.2
8 | - jruby-9.0.0.0.pre2
9 | env:
10 | - JRUBY_OPTS="-Xcli.debug=true --debug"
11 | addons:
12 | code_climate:
13 | repo_token: bbd2a5e077e4b62bd8ba21a74a2ba901defc43506812f6192efc6b2a25c20d49
14 | before_install:
15 | - sudo add-apt-repository "deb http://cz.archive.ubuntu.com/ubuntu vivid main universe"
16 | - sudo apt-get update -q
17 | - sudo rm -rf /etc/dpkg/dpkg.cfg.d/multiarch
18 | - sudo apt-get install gnuplot5
19 | - bundle install
20 | install:
21 | - bundle exec rake install
22 | script:
23 | - bundle exec rake spec
24 |
--------------------------------------------------------------------------------
/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | # Specify your gem's dependencies in gnuplotrb.gemspec
4 | gemspec
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Ivan Evgrafov
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | = GnuplotRB
2 |
3 | GnuplotRB is a plot generator for Ruby based on {Gnuplot}[http://www.gnuplot.info].
4 |
5 | This software has been developed as a product in Google Summer of Code 2015 (GSoC2015). Its progress may be saw in {SciRuby mailing list}[https://groups.google.com/forum/?fromgroups#!topic/sciruby-dev/lhWvb5hWc3k] or in {project's blog}[http://www.evgrafov.work/gnuplotrb/].
6 |
7 | {
}[https://rubygems.org/gems/gnuplotrb]
8 |
9 | {
}[https://gemnasium.com/dilcom/gnuplotrb]
10 |
11 | {
}[https://travis-ci.org/dilcom/gnuplotrb]
12 | {
}[https://codeclimate.com/github/dilcom/gnuplotrb]
13 | {
}[https://codeclimate.com/github/dilcom/gnuplotrb]
14 |
15 | == Table of contents
16 | * {Installation}[https://github.com/dilcom/gnuplotrb#installation]
17 | * {Getting started}[https://github.com/dilcom/gnuplotrb#getting-started]
18 | * {Plottable classes}[https://github.com/dilcom/gnuplotrb#plottable-classes]
19 | * {Notebooks}[https://github.com/dilcom/gnuplotrb#notebooks]
20 | * {Plain examples}[https://github.com/dilcom/gnuplotrb#plain-examples]
21 | * {Contributing}[https://github.com/dilcom/gnuplotrb#contributing]
22 |
23 | == Installation
24 | === Dependencies
25 | * Ruby 2.0+
26 | * It is required to install {gnuplot 5.0}[http://www.gnuplot.info/download.html] to use that gem.
27 | === Gem installation
28 | ==== Install latest stable version from Rubygems
29 | gem install gnuplotrb
30 | ==== Install latest stable version using bundler
31 | * add
32 | gem 'gnuplotrb'
33 | to your Gemfile
34 | * run
35 | bundle install
36 | ==== Install latest version from source (may be unstable)
37 | git clone https://github.com/dilcom/gnuplotrb.git
38 | cd gnuplotrb
39 | bundle install
40 | rake install
41 |
42 | == Getting started
43 |
44 | GnuplotRB gem is based on {Gnuplot}[http://www.gnuplot.info/] so I would recommend to use {Gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] and {GnuplotRB doc at Rubydoc}[https://rubygems.org/gems/gnuplotrb] in cases when docs and examples (as notebooks and plain examples) present here are not enough to explain how to plot something.
45 |
46 | === Plottable classes
47 |
48 | Each of plottable classes may be outputted to image file using ```#to_png```, ```#to_svg```, ```#to_gif``` and so on methods. You can read more about it in {GnuplotRB doc}[https://rubygems.org/gems/gnuplotrb] related to Plottable module or see examples in {beginners notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb].
49 |
50 | ==== Dataset
51 | Single dataset may be created with math formula ('sin(x)') or some data. If your data is stored in a file you can just pass a filename (e.g. 'gnuplotrb.data'). Dataset may also be constructed out of data contained in Ruby classes (Array, Daru containers), see {example notebooks}[https://github.com/dilcom/gnuplotrb#possible-datasources].
52 |
53 | Dataset have several possible options which are explained in {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 80-102). Options are passed to Dataset.new as hash and are tranlated into gnuplot format before plotting:
54 | Dataset.new(data, with: 'lines', using: '1:2')
55 | Examples of option translation (nested containers allowed):
56 | * Hash:
57 | { key1: "value1", key2: { nested_key1: "nested_value1" } }
58 | # =>
59 | "key1 value1 key2 nested key1 nested_value1"
60 | * Hashes with underscored keys (see {#7}[https://github.com/dilcom/gnuplotrb/issues/7]):
61 | { style_data: 'histograms' }
62 | #=>
63 | "style data histograms"
64 | * Range:
65 | { xrange: 0..100 }
66 | # =>
67 | "xrange [0:100]"
68 | * Array (often used with nested hashes) and Array of Numeric
69 | ['png', { size: [400, 500] }]
70 | # =>
71 | "png size 400,500"
72 | * Others
73 | some_object
74 | # =>
75 | some_object.to_s
76 |
77 | Once Dataset created, it may be updated with new data or options. Methods related to updating are explained in {a notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb].
78 |
79 | Just as other Plottable object Dataset has several plotting methods which are desribed in {beginners notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb].
80 |
81 | ==== Plot
82 | Plot is a container for several datasets and layout options:
83 | Plot.new(ds1, ds2, ds2, xrange: 1..10)
84 |
85 | Datasets contained bu Plot are outputted on single xy plain.
86 |
87 | Plot's options are explained in {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 105-181). Plot options are translated into gnuplot format the same way as Dataset's (except adding 'set' before each option). Plot's datasets and Plot itself may be updated with almost the same methods as desribed in Dataset section above.
88 |
89 | ==== Splot
90 | Almost the same as Plot but for 3-D plots. See Plot section.
91 |
92 | ==== Multiplot
93 |
94 | Container for several Plot or Splot objects, each of them is plotted in its own xy(z) space. So Multiplot offers single layout (image file\window) for several plots. It's grid is tuned by :layout option, and you can also set layout's title:
95 | Multiplot.new(plot1, plot2, splot1, layout: [3, 1], title: 'Three plots on a layout')
96 |
97 | Updating methods for Multiplot are almost the same as Plot's and Dataset's and are covered in Multiplot's docs and {multiplot notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/multiplot_layout.ipynb]. See examples there.
98 |
99 | ==== Animation
100 |
101 | Animation is a container for several Plot, Splot or Multiplot objects. Each of contained items is considered as frame in gif animation which is creating by ```#plot``` call. Animation's frames and options may be modifyed or updated just as other classes above. Animation does not support methods like ```#to_png``` and may be plotted only with ```#plot``` method. Please see {related notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb] and docs at RubyDoc for examples.
102 |
103 | === Notebooks
104 |
105 | This notebooks are powered by {Ruby kernel}[https://github.com/SciRuby/iruby/] for {IPython/Jupyter}[https://jupyter.org/].
106 | I placed them here to show some GnuplotRB's capabilities and ways of using it together with iRuby.
107 |
108 | To use GnuplotRB gem with iRuby you need to install them both.
109 |
110 | * iRuby installation is covered in its {README}[https://github.com/SciRuby/iruby/blob/master/README.md].
111 | It also covers installation of iPython and other dependecies.
112 | * GnuplotRB gem installation covered in {README}[https://github.com/dilcom/gnuplotrb#installation] too.
113 |
114 | ==== Embedding plots into iRuby
115 | Using GnuplotRB inside iRuby notebooks is covered in:
116 |
117 | * {Basic usage notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb].
118 |
119 | ==== 2D and 3D plots
120 | GnuplotRB is capable to plot vast range of plots from histograms to 3D heatmaps. Gem's repository contains examples of several plot types:
121 |
122 | * {Heatmaps}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/heatmaps.ipynb]
123 | * {Vector field}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/vector_field.ipynb] (Thanks, {Alexej}[https://github.com/agisga])
124 | * {Math equations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/math_plots.ipynb]
125 | * {3D visualizations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/3d_plot.ipynb]
126 | * {Histogram}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/histogram.ipynb]
127 |
128 | ==== Possible datasources
129 | GnuplotRB may take data in Ruby container or in a file. Supported containers for now are Arrays, Daru::Vector and Daru::DataFrame.
130 | When data given in file, GnuplotRB pass filename to Gnuplot *without* reading the file into memory.
131 |
132 | Examples of using different datasources:
133 |
134 | * {Data given in file or Ruby Array}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/points_from_different_sources.ipynb]
135 | * {Data given in Daru containers}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/plotting_from_daru.ipynb]
136 | * {Data given in Daru containers (with timeseries)}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/time_series_from_daru.ipynb]
137 | * {Updating plots with new data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb]
138 |
139 | ==== Multiplot
140 | You can not only plot several datasets in single coordinate system but place several coordinate systems on a canvas.
141 |
142 | * {Multiplot example notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/multiplot_layout.ipynb].
143 |
144 | ==== Animation
145 | It's possible to use several plots (Plot, Splot or Multiplot objects) to create gif animation.
146 |
147 | * {Animation example notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb].
148 |
149 | ==== Fitting data with formula
150 | GnuplotRB also may be used to fit some data with given math formula.
151 |
152 | * {Fitting data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/fitting_data.ipynb]
153 |
154 | === Plain examples
155 | You may find several examples in {examples directory}[https://github.com/dilcom/gnuplotrb/tree/master/examples].
156 |
157 | == Contributing
158 |
159 | 1. {Fork repository}[https://github.com/dilcom/gnuplotrb/fork]
160 | 2. Create your feature branch (`git checkout -b my-new-feature`)
161 | 3. Commit your changes (`git commit -am 'Add some feature'`)
162 | 4. Push to the branch (`git push origin my-new-feature`)
163 | 5. Create a new Pull Request
164 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require 'bundler/gem_tasks'
2 | require 'rspec/core/rake_task'
3 | require 'rspec/core'
4 | require 'yard'
5 | require 'rubocop/rake_task'
6 |
7 | RSpec::Core::RakeTask.new(:spec) do |spec|
8 | spec.pattern = FileList['spec/**/*_spec.rb'].uniq
9 | spec.rspec_opts = '--format documentation'
10 | end
11 |
12 | YARD::Rake::YardocTask.new(:doc) do |t|
13 | t.files = %w(README.rdoc lib) # optional
14 | end
15 |
16 | RuboCop::RakeTask.new(:cop)
17 |
--------------------------------------------------------------------------------
/examples/multiplot/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/multiplot/gnuplot.png
--------------------------------------------------------------------------------
/examples/multiplot/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/multiplot/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/multiplot/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set style data histograms
2 | set output './gnuplot.png'
3 | set term png size 400,800
4 | set xtics nomirror rotate by -45
5 | $DATA << EOD
6 | "1891-1900" 234081 181288 18167
7 | "1901-1910" 668209 808511 41635
8 | "1911-1920" 453649 442693 33746
9 | "1921-1930" 32868 30680 15846
10 | "1931-1940" 3563 7861 4817
11 | "1941-1950" 24860 3469 12189
12 | "1951-1960" 67106 36637 18575
13 | "1961-1970" 20621 5401 9192
14 | EOD
15 | set multiplot layout 3,1 title 'Histograms'
16 | plot $DATA using 2:xtic(1) title 'Austria'
17 | plot $DATA using 3:xtic(1) title 'Hungary'
18 | plot $DATA using 4:xtic(1) title 'Belgium'
19 | unset multiplot
20 | unset output
21 |
--------------------------------------------------------------------------------
/examples/multiplot/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | titles = %w{decade Austria Hungary Belgium}
5 | data = [
6 | ['1891-1900', 234081, 181288, 18167],
7 | ['1901-1910', 668209, 808511, 41635],
8 | ['1911-1920', 453649, 442693, 33746],
9 | ['1921-1930', 32868, 30680, 15846],
10 | ['1931-1940', 3563, 7861, 4817],
11 | ['1941-1950', 24860, 3469, 12189],
12 | ['1951-1960', 67106, 36637, 18575],
13 | ['1961-1970', 20621, 5401, 9192],
14 | ]
15 | x = data.map(&:first)
16 | plots = (1..3).map do |col|
17 | y = data.map { |row| row[col] }
18 | Plot.new([[x, y], using: '2:xtic(1)', title: titles[col]])
19 | end
20 | mp = Multiplot.new(
21 | *plots,
22 | layout: [plots.size, 1],
23 | style_data: 'histograms',
24 | title: 'Histograms',
25 | xtics: 'nomirror rotate by -45'
26 | )
27 |
28 | mp.to_png('./gnuplot_gem.png', size: [400,800])
29 |
--------------------------------------------------------------------------------
/examples/plot_3d_surface/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_3d_surface/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_3d_surface/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_3d_surface/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_3d_surface/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set output './gnuplot.png'
2 | set term png size 600,600
3 | set hidden3d
4 | set xrange [-pi*2:pi*2]
5 | set yrange [-pi*2:pi*2]
6 | set style function lp
7 | splot cos(x)*cos(y)
8 | unset output
--------------------------------------------------------------------------------
/examples/plot_3d_surface/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | double_pi = Math::PI * 2
5 |
6 | plot3d = Splot.new('cos(x)*cos(y)', xrange: -double_pi..double_pi, yrange: -double_pi..double_pi, style: 'function lp', hidden3d: true)
7 |
8 | plot3d.to_png('./gnuplot_gem.png', size: [600, 600])
9 |
--------------------------------------------------------------------------------
/examples/plot_fit_exp/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_fit_exp/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_fit_exp/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_fit_exp/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_fit_exp/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | xoffset = 0.1
4 | xscale = 1
5 | yoffset = 0.1
6 | yscale = 1
7 | fit yscale * (yoffset + exp((x - xoffset) / xscale)) 'points.data' using 1:2:3 zerror via yscale,yoffset,xoffset,xscale
8 | plot 'points.data' with yerr, yscale * (yoffset + exp((x - xoffset) / xscale)) title 'Fit formula' lw 3
9 | unset output
10 |
--------------------------------------------------------------------------------
/examples/plot_fit_exp/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | fit_result = fit_exp('points.data', using: '1:2:3')
5 |
6 | plot = Plot.new(fit_result[:data].with('yerr'), fit_result[:formula_ds].lw(3))
7 |
8 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
9 |
--------------------------------------------------------------------------------
/examples/plot_fit_general/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_fit_general/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_fit_general/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_fit_general/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_fit_general/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | a = 1
4 | b = 1
5 | set xrange [0:15]
6 | fit a*x*x + b 'points.data' using 1:2:3 zerror via a,b
7 | unset xrange
8 | plot 'points.data' with yerr, a*x*x+b title 'Fit formula' lw 3
9 | unset output
10 |
--------------------------------------------------------------------------------
/examples/plot_fit_general/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | fit_result = fit('points.data', using: '1:2:3', function: 'a*x*x + b', initials: { a: 1, b: 1 }, term_options: { xrange: 0..15 })
5 |
6 | plot = Plot.new(fit_result[:data].with('yerr'), fit_result[:formula_ds].lw(3))
7 |
8 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
9 |
--------------------------------------------------------------------------------
/examples/plot_fit_poly/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_fit_poly/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_fit_poly/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_fit_poly/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_fit_poly/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | a3 = 1
4 | a2 = 1
5 | a1 = 1
6 | a0 = 1
7 | fit a3*x**3 + a2*x**2 + a1*x + a0 'points.data' using 1:2:3 zerror via a3,a2,a1,a0
8 | plot 'points.data' with yerr, a3*x**3 + a2*x**2 + a1*x + a0 title 'Fit formula' lw 3
9 | unset output
10 |
--------------------------------------------------------------------------------
/examples/plot_fit_poly/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | fit_result = fit_poly('points.data', degree: 3, using: '1:2:3')
5 |
6 | plot = Plot.new(fit_result[:data].with('yerr'), fit_result[:formula_ds].lw(3))
7 |
8 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
9 |
--------------------------------------------------------------------------------
/examples/plot_from_datafile/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_from_datafile/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_from_datafile/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_from_datafile/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_from_datafile/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | plot 'points.data' with lines title 'Points from file'
4 | unset output
--------------------------------------------------------------------------------
/examples/plot_from_datafile/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | plot = Plot.new(['points.data', with: 'lines', title: 'Points from file'])
5 |
6 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
--------------------------------------------------------------------------------
/examples/plot_from_datafile/points.data:
--------------------------------------------------------------------------------
1 | 0.0 1.0
2 | 0.1 0.9048374180359595
3 | 0.2 0.8187307530779818
4 | 0.3 0.7408182206817179
5 | 0.4 0.6703200460356393
6 | 0.5 0.6065306597126334
7 | 0.6 0.5488116360940264
8 | 0.7 0.4965853037914095
9 | 0.8 0.44932896411722156
10 | 0.9 0.4065696597405991
11 | 1.0 0.36787944117144233
12 | 1.1 0.33287108369807955
13 | 1.2 0.30119421191220214
14 | 1.3 0.2725317930340126
15 | 1.4 0.2465969639416065
16 | 1.5 0.22313016014842982
17 | 1.6 0.20189651799465538
18 | 1.7 0.18268352405273466
19 | 1.8 0.16529888822158653
20 | 1.9 0.14956861922263506
21 | 2.0 0.1353352832366127
22 | 2.1 0.1224564282529819
23 | 2.2 0.11080315836233387
24 | 2.3 0.10025884372280375
25 | 2.4 0.09071795328941251
26 | 2.5 0.0820849986238988
27 | 2.6 0.07427357821433388
28 | 2.7 0.06720551273974976
29 | 2.8 0.06081006262521797
30 | 2.9 0.05502322005640723
31 | 3.0 0.049787068367863944
32 | 3.1 0.0450492023935578
33 | 3.2 0.04076220397836621
34 | 3.3 0.036883167401240015
35 | 3.4 0.03337326996032608
36 | 3.5 0.0301973834223185
37 | 3.6 0.02732372244729256
38 | 3.7 0.024723526470339388
39 | 3.8 0.0223707718561656
40 | 3.9 0.02024191144580439
41 | 4.0 0.01831563888873418
42 | 4.1 0.016572675401761255
43 | 4.2 0.014995576820477703
44 | 4.3 0.013568559012200934
45 | 4.4 0.012277339903068436
46 | 4.5 0.011108996538242306
47 | 4.6 0.010051835744633586
48 | 4.7 0.009095277101695816
49 | 4.8 0.00822974704902003
50 | 4.9 0.007446583070924338
51 | 5.0 0.006737946999085467
52 | 5.1 0.006096746565515638
53 | 5.2 0.0055165644207607716
54 | 5.3 0.004991593906910217
55 | 5.4 0.004516580942612666
56 | 5.5 0.004086771438464067
57 | 5.6 0.003697863716482932
58 | 5.7 0.003345965457471272
59 | 5.8 0.0030275547453758153
60 | 5.9 0.0027394448187683684
61 | 6.0 0.0024787521766663585
62 | 6.1 0.0022428677194858034
63 | 6.2 0.002029430636295734
64 | 6.3 0.0018363047770289071
65 | 6.4 0.001661557273173934
66 | 6.5 0.0015034391929775724
67 | 6.6 0.0013603680375478939
68 | 6.7 0.001230911902673481
69 | 6.8 0.0011137751478448032
70 | 6.9 0.0010077854290485105
71 | 7.0 0.0009118819655545162
72 | 7.1 0.0008251049232659046
73 | 7.2 0.0007465858083766792
74 | 7.3 0.0006755387751938444
75 | 7.4 0.0006112527611295723
76 | 7.5 0.0005530843701478336
77 | 7.6 0.0005004514334406108
78 | 7.7 0.00045282718288679695
79 | 7.8 0.0004097349789797868
80 | 7.9 0.0003707435404590882
81 | 8.0 0.00033546262790251185
82 | 8.1 0.0003035391380788668
83 | 8.2 0.00027465356997214254
84 | 8.3 0.00024851682710795185
85 | 8.4 0.0002248673241788482
86 | 8.5 0.00020346836901064417
87 | 8.6 0.0001841057936675792
88 | 8.7 0.00016658581098763354
89 | 8.8 0.0001507330750954765
90 | 8.9 0.0001363889264820114
91 | 9.0 0.00012340980408667956
92 | 9.1 0.00011166580849011478
93 | 9.2 0.00010103940183709342
94 | 9.3 9.142423147817327e-05
95 | 9.4 8.272406555663223e-05
96 | 9.5 7.48518298877006e-05
97 | 9.6 6.77287364908539e-05
98 | 9.7 6.128349505322213e-05
99 | 9.8 5.5451599432176945e-05
100 | 9.9 5.017468205617528e-05
101 | 10.0 4.5399929762484854e-05
--------------------------------------------------------------------------------
/examples/plot_histogram/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_histogram/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_histogram/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_histogram/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_histogram/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | set title "Histogram example"
4 | set style data histograms
5 | set xtics nomirror rotate by -45
6 | $DATA << EOD
7 | "1891-1900" 234081 181288 18167
8 | "1901-1910" 668209 808511 41635
9 | "1911-1920" 453649 442693 33746
10 | "1921-1930" 32868 30680 15846
11 | "1931-1940" 3563 7861 4817
12 | "1941-1950" 24860 3469 12189
13 | "1951-1960" 67106 36637 18575
14 | "1961-1970" 20621 5401 9192
15 | EOD
16 | plot $DATA using 2:xtic(1) title 'Austria',\
17 | $DATA using 3:xtic(1) title 'Hungary',\
18 | $DATA using 4:xtic(1) title 'Belgium'
19 | unset output
--------------------------------------------------------------------------------
/examples/plot_histogram/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | titles = %w{decade Austria Hungary Belgium}
5 | data = [
6 | ['1891-1900', 234081, 181288, 18167],
7 | ['1901-1910', 668209, 808511, 41635],
8 | ['1911-1920', 453649, 442693, 33746],
9 | ['1921-1930', 32868, 30680, 15846],
10 | ['1931-1940', 3563, 7861, 4817],
11 | ['1941-1950', 24860, 3469, 12189],
12 | ['1951-1960', 67106, 36637, 18575],
13 | ['1961-1970', 20621, 5401, 9192],
14 | ]
15 | x = data.map(&:first)
16 | datasets = (1..3).map do |col|
17 | y = data.map { |row| row[col] }
18 | Dataset.new([x, y], using: '2:xtic(1)', title: titles[col], file: true)
19 | end
20 | plot = Plot.new(*datasets, title: 'Histogram example', style: 'data histograms', xtics: 'nomirror rotate by -45')
21 |
22 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
--------------------------------------------------------------------------------
/examples/plot_in_parametric/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_in_parametric/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_in_parametric/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_in_parametric/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_in_parametric/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set title 'Parametric plot example'
2 | set parametric
3 | set samples 3000
4 | set term png size 600,600
5 | set output './gnuplot.png'
6 | plot 1.5*cos(t) - cos(30*t), 1.5*sin(t) - sin(30*t) title 'Parametric curve'
7 | unset output
--------------------------------------------------------------------------------
/examples/plot_in_parametric/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | plot = Plot.new(['1.5*cos(t) - cos(30*t), 1.5*sin(t) - sin(30*t)', title: 'Parametric curve'], title: 'Parametric plot example', parametric: true, samples: 3000)
5 |
6 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
7 |
--------------------------------------------------------------------------------
/examples/plot_in_polar/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_in_polar/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_in_polar/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_in_polar/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_in_polar/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | set title 'Plot in polar example'
4 | set polar
5 | set samples 1000
6 | plot abs(sin(3*t)) with filledcurves
7 | unset output
--------------------------------------------------------------------------------
/examples/plot_in_polar/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | plot = Plot.new(['abs(sin(3*t))', with: 'filledcurves'], title: 'Plot in polar example', polar: true, samples: 1000)
5 |
6 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
7 |
--------------------------------------------------------------------------------
/examples/plot_math_function/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_math_function/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_math_function/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_math_function/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_math_function/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | set xrange [-10:10]
4 | set title "Math function example"
5 | set ylabel "x"
6 | set xlabel "x*sin(x)"
7 | plot x*sin(x) with lines lw 4
8 | unset output
--------------------------------------------------------------------------------
/examples/plot_math_function/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | plot = Plot.new(['x*sin(x)', with: 'lines', lw: 4], xrange: -10..10, title: 'Math function example', ylabel: 'x', xlabel: 'x*sin(x)')
5 |
6 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
--------------------------------------------------------------------------------
/examples/plot_points/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_points/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_points/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_points/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_points/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | $DATA << EOD
4 | 0 0
5 | 1 1
6 | 2 4
7 | 3 9
8 | 4 16
9 | 5 25
10 | EOD
11 | plot $DATA with points title 'Points'
12 | unset output
--------------------------------------------------------------------------------
/examples/plot_points/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | x = (0..5).to_a
5 | y = x.map {|xx| xx*xx }
6 | points = [x, y]
7 |
8 | plot = Plot.new([points, with: 'points', title: 'Points'])
9 |
10 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
11 |
--------------------------------------------------------------------------------
/examples/plot_several_datasets/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_several_datasets/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_several_datasets/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_several_datasets/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_several_datasets/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | $DATA << EOD
4 | 0 0
5 | 1 1
6 | 2 4
7 | 3 9
8 | 4 16
9 | 5 25
10 | EOD
11 | set xrange [0:5]
12 | plot x*x title 'True curve', $DATA title 'Points' with lines
13 | unset output
--------------------------------------------------------------------------------
/examples/plot_several_datasets/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | x = (0..5).to_a
5 | y = x.map {|xx| xx*xx }
6 | points = [x, y]
7 |
8 | plot = Plot.new(['x*x', title: 'True curve'], [points, with: 'lines', title: 'Points'])
9 |
10 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
11 |
--------------------------------------------------------------------------------
/examples/plot_test_term/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_test_term/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_test_term/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_test_term/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_test_term/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png
2 | set output './gnuplot.png'
3 | test
4 | unset output
--------------------------------------------------------------------------------
/examples/plot_test_term/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | Terminal::test('png', 'gnuplot_gem.png')
5 |
--------------------------------------------------------------------------------
/examples/plot_to_image_file/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_to_image_file/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_to_image_file/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_to_image_file/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_to_image_file/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | set title 'Plotting to png'
4 | plot 'points.data' with lines title 'Points from file'
5 | unset output
--------------------------------------------------------------------------------
/examples/plot_to_image_file/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | plot = Plot.new(['points.data', with: 'lines', title: 'Points from file'], title: 'Plotting to png')
5 |
6 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
7 |
8 | unless defined?(RSpec)
9 | plot.to_svg('./real_result.svg', size: [600, 600])
10 | plot.to_canvas('./real_result.html', size: [600, 600])
11 |
12 | # You can also just get contents of image (or not image) file
13 | contents = plot.to_dumb(size: [60, 40])
14 | File.write('./real_result.txt', contents)
15 | end
16 |
--------------------------------------------------------------------------------
/examples/plot_to_image_file/points.data:
--------------------------------------------------------------------------------
1 | 0.0 1.0
2 | 0.1 0.9048374180359595
3 | 0.2 0.8187307530779818
4 | 0.3 0.7408182206817179
5 | 0.4 0.6703200460356393
6 | 0.5 0.6065306597126334
7 | 0.6 0.5488116360940264
8 | 0.7 0.4965853037914095
9 | 0.8 0.44932896411722156
10 | 0.9 0.4065696597405991
11 | 1.0 0.36787944117144233
12 | 1.1 0.33287108369807955
13 | 1.2 0.30119421191220214
14 | 1.3 0.2725317930340126
15 | 1.4 0.2465969639416065
16 | 1.5 0.22313016014842982
17 | 1.6 0.20189651799465538
18 | 1.7 0.18268352405273466
19 | 1.8 0.16529888822158653
20 | 1.9 0.14956861922263506
21 | 2.0 0.1353352832366127
22 | 2.1 0.1224564282529819
23 | 2.2 0.11080315836233387
24 | 2.3 0.10025884372280375
25 | 2.4 0.09071795328941251
26 | 2.5 0.0820849986238988
27 | 2.6 0.07427357821433388
28 | 2.7 0.06720551273974976
29 | 2.8 0.06081006262521797
30 | 2.9 0.05502322005640723
31 | 3.0 0.049787068367863944
32 | 3.1 0.0450492023935578
33 | 3.2 0.04076220397836621
34 | 3.3 0.036883167401240015
35 | 3.4 0.03337326996032608
36 | 3.5 0.0301973834223185
37 | 3.6 0.02732372244729256
38 | 3.7 0.024723526470339388
39 | 3.8 0.0223707718561656
40 | 3.9 0.02024191144580439
41 | 4.0 0.01831563888873418
42 | 4.1 0.016572675401761255
43 | 4.2 0.014995576820477703
44 | 4.3 0.013568559012200934
45 | 4.4 0.012277339903068436
46 | 4.5 0.011108996538242306
47 | 4.6 0.010051835744633586
48 | 4.7 0.009095277101695816
49 | 4.8 0.00822974704902003
50 | 4.9 0.007446583070924338
51 | 5.0 0.006737946999085467
52 | 5.1 0.006096746565515638
53 | 5.2 0.0055165644207607716
54 | 5.3 0.004991593906910217
55 | 5.4 0.004516580942612666
56 | 5.5 0.004086771438464067
57 | 5.6 0.003697863716482932
58 | 5.7 0.003345965457471272
59 | 5.8 0.0030275547453758153
60 | 5.9 0.0027394448187683684
61 | 6.0 0.0024787521766663585
62 | 6.1 0.0022428677194858034
63 | 6.2 0.002029430636295734
64 | 6.3 0.0018363047770289071
65 | 6.4 0.001661557273173934
66 | 6.5 0.0015034391929775724
67 | 6.6 0.0013603680375478939
68 | 6.7 0.001230911902673481
69 | 6.8 0.0011137751478448032
70 | 6.9 0.0010077854290485105
71 | 7.0 0.0009118819655545162
72 | 7.1 0.0008251049232659046
73 | 7.2 0.0007465858083766792
74 | 7.3 0.0006755387751938444
75 | 7.4 0.0006112527611295723
76 | 7.5 0.0005530843701478336
77 | 7.6 0.0005004514334406108
78 | 7.7 0.00045282718288679695
79 | 7.8 0.0004097349789797868
80 | 7.9 0.0003707435404590882
81 | 8.0 0.00033546262790251185
82 | 8.1 0.0003035391380788668
83 | 8.2 0.00027465356997214254
84 | 8.3 0.00024851682710795185
85 | 8.4 0.0002248673241788482
86 | 8.5 0.00020346836901064417
87 | 8.6 0.0001841057936675792
88 | 8.7 0.00016658581098763354
89 | 8.8 0.0001507330750954765
90 | 8.9 0.0001363889264820114
91 | 9.0 0.00012340980408667956
92 | 9.1 0.00011166580849011478
93 | 9.2 0.00010103940183709342
94 | 9.3 9.142423147817327e-05
95 | 9.4 8.272406555663223e-05
96 | 9.5 7.48518298877006e-05
97 | 9.6 6.77287364908539e-05
98 | 9.7 6.128349505322213e-05
99 | 9.8 5.5451599432176945e-05
100 | 9.9 5.017468205617528e-05
101 | 10.0 4.5399929762484854e-05
--------------------------------------------------------------------------------
/examples/plot_to_image_file/real_result.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Gnuplot Canvas Graph
5 |
6 |
7 |
8 |
9 |
10 |
11 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
355 | |
356 |
357 |
358 |
359 |
360 |
361 |
--------------------------------------------------------------------------------
/examples/plot_to_image_file/real_result.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
173 |
174 |
--------------------------------------------------------------------------------
/examples/plot_to_image_file/real_result.txt:
--------------------------------------------------------------------------------
1 |
2 | Plotting to png
3 |
4 | 1 +-+-------+---------+---------+---------+-------+-+
5 | + + + + + +
6 | || Points from file +-----+ |
7 | 0.9 +-+ +-+
8 | || |
9 | || |
10 | |+ |
11 | 0.8 +-+ +-+
12 | | | |
13 | | + |
14 | 0.7 +-+ +-+
15 | | + |
16 | | | |
17 | | + |
18 | 0.6 +-+| +-+
19 | | + |
20 | | | |
21 | 0.5 +-+ + +-+
22 | | | |
23 | | + |
24 | 0.4 +-+ + +-+
25 | | + |
26 | | | |
27 | | + |
28 | 0.3 +-+ + +-+
29 | | + |
30 | | ++ |
31 | 0.2 +-+ + +-+
32 | | + |
33 | | + |
34 | | + |
35 | 0.1 +-+ ++ +-+
36 | | +++ |
37 | + + ++++++ + + +
38 | 0 +-+-------+---------+---------+---------+-------+-+
39 | 0 2 4 6 8 10
40 |
41 |
--------------------------------------------------------------------------------
/examples/plot_to_string/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_to_string/gnuplot.png
--------------------------------------------------------------------------------
/examples/plot_to_string/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/plot_to_string/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/plot_to_string/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | set title 'Plotting to png'
4 | plot 'points.data' with lines title 'Points from file'
5 | unset output
--------------------------------------------------------------------------------
/examples/plot_to_string/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | plot = Plot.new(['points.data', with: 'lines', title: 'Points from file'], title: 'Plotting to png')
5 |
6 | plot_contents = plot.to_png(size: [600, 600])
7 |
8 | File.binwrite('./gnuplot_gem.png', plot_contents)
9 |
--------------------------------------------------------------------------------
/examples/plot_to_string/points.data:
--------------------------------------------------------------------------------
1 | 0.0 1.0
2 | 0.1 0.9048374180359595
3 | 0.2 0.8187307530779818
4 | 0.3 0.7408182206817179
5 | 0.4 0.6703200460356393
6 | 0.5 0.6065306597126334
7 | 0.6 0.5488116360940264
8 | 0.7 0.4965853037914095
9 | 0.8 0.44932896411722156
10 | 0.9 0.4065696597405991
11 | 1.0 0.36787944117144233
12 | 1.1 0.33287108369807955
13 | 1.2 0.30119421191220214
14 | 1.3 0.2725317930340126
15 | 1.4 0.2465969639416065
16 | 1.5 0.22313016014842982
17 | 1.6 0.20189651799465538
18 | 1.7 0.18268352405273466
19 | 1.8 0.16529888822158653
20 | 1.9 0.14956861922263506
21 | 2.0 0.1353352832366127
22 | 2.1 0.1224564282529819
23 | 2.2 0.11080315836233387
24 | 2.3 0.10025884372280375
25 | 2.4 0.09071795328941251
26 | 2.5 0.0820849986238988
27 | 2.6 0.07427357821433388
28 | 2.7 0.06720551273974976
29 | 2.8 0.06081006262521797
30 | 2.9 0.05502322005640723
31 | 3.0 0.049787068367863944
32 | 3.1 0.0450492023935578
33 | 3.2 0.04076220397836621
34 | 3.3 0.036883167401240015
35 | 3.4 0.03337326996032608
36 | 3.5 0.0301973834223185
37 | 3.6 0.02732372244729256
38 | 3.7 0.024723526470339388
39 | 3.8 0.0223707718561656
40 | 3.9 0.02024191144580439
41 | 4.0 0.01831563888873418
42 | 4.1 0.016572675401761255
43 | 4.2 0.014995576820477703
44 | 4.3 0.013568559012200934
45 | 4.4 0.012277339903068436
46 | 4.5 0.011108996538242306
47 | 4.6 0.010051835744633586
48 | 4.7 0.009095277101695816
49 | 4.8 0.00822974704902003
50 | 4.9 0.007446583070924338
51 | 5.0 0.006737946999085467
52 | 5.1 0.006096746565515638
53 | 5.2 0.0055165644207607716
54 | 5.3 0.004991593906910217
55 | 5.4 0.004516580942612666
56 | 5.5 0.004086771438464067
57 | 5.6 0.003697863716482932
58 | 5.7 0.003345965457471272
59 | 5.8 0.0030275547453758153
60 | 5.9 0.0027394448187683684
61 | 6.0 0.0024787521766663585
62 | 6.1 0.0022428677194858034
63 | 6.2 0.002029430636295734
64 | 6.3 0.0018363047770289071
65 | 6.4 0.001661557273173934
66 | 6.5 0.0015034391929775724
67 | 6.6 0.0013603680375478939
68 | 6.7 0.001230911902673481
69 | 6.8 0.0011137751478448032
70 | 6.9 0.0010077854290485105
71 | 7.0 0.0009118819655545162
72 | 7.1 0.0008251049232659046
73 | 7.2 0.0007465858083766792
74 | 7.3 0.0006755387751938444
75 | 7.4 0.0006112527611295723
76 | 7.5 0.0005530843701478336
77 | 7.6 0.0005004514334406108
78 | 7.7 0.00045282718288679695
79 | 7.8 0.0004097349789797868
80 | 7.9 0.0003707435404590882
81 | 8.0 0.00033546262790251185
82 | 8.1 0.0003035391380788668
83 | 8.2 0.00027465356997214254
84 | 8.3 0.00024851682710795185
85 | 8.4 0.0002248673241788482
86 | 8.5 0.00020346836901064417
87 | 8.6 0.0001841057936675792
88 | 8.7 0.00016658581098763354
89 | 8.8 0.0001507330750954765
90 | 8.9 0.0001363889264820114
91 | 9.0 0.00012340980408667956
92 | 9.1 0.00011166580849011478
93 | 9.2 0.00010103940183709342
94 | 9.3 9.142423147817327e-05
95 | 9.4 8.272406555663223e-05
96 | 9.5 7.48518298877006e-05
97 | 9.6 6.77287364908539e-05
98 | 9.7 6.128349505322213e-05
99 | 9.8 5.5451599432176945e-05
100 | 9.9 5.017468205617528e-05
101 | 10.0 4.5399929762484854e-05
--------------------------------------------------------------------------------
/examples/tons_of_data/generate_data.rb:
--------------------------------------------------------------------------------
1 | File.open('tons_of_data', 'w') do |f|
2 | (1..10000000).each do |x|
3 | xx = x/100000.0
4 | yy = Math.exp(Math.sin(xx))
5 | f.puts "#{xx} #{yy}"
6 | end
7 | end
--------------------------------------------------------------------------------
/examples/tons_of_data/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 |
5 | graph = Plot.new(['tons_of_data', title: 'Tons of data', with: 'lines'], term: ['qt', persist: true])
6 | graph.plot
7 | #Need to change some dataset options? Ok:
8 | plot_with_points = graph.update_dataset(with: 'points', title: 'Plot with points')
9 | plot_with_points.plot
10 | #Need to change the whole plot options? Ok:
11 | plot_interval = graph.options(title: 'Plot on [1..3]', xrange: 1..3)
12 | plot_interval .plot
--------------------------------------------------------------------------------
/examples/tons_of_data/result_screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/tons_of_data/result_screenshot.png
--------------------------------------------------------------------------------
/examples/use_multi_word_option_keys/gnuplot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/use_multi_word_option_keys/gnuplot.png
--------------------------------------------------------------------------------
/examples/use_multi_word_option_keys/gnuplot_gem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ievgrafov/gnuplotrb/0a3146386ae28fcbe2c09cb6e266fe40ebb659f4/examples/use_multi_word_option_keys/gnuplot_gem.png
--------------------------------------------------------------------------------
/examples/use_multi_word_option_keys/plot.gnuplot:
--------------------------------------------------------------------------------
1 | set term png size 600,600
2 | set output './gnuplot.png'
3 | set title "Time spent to run deploy pipeline"
4 | set style data histograms
5 | set style fill pattern border
6 | set yrange [0:2200]
7 | set xlabel 'Number of test'
8 | set ylabel 'Time, s'
9 | $DATA << EOD
10 | 1 312 525 215 1052
11 | 2 630 1050 441 2121
12 | 3 315 701 370 1386
13 | 4 312 514 220 1046
14 | EOD
15 | plot $DATA using 2:xtic(1) title 'Build',\
16 | $DATA using 3:xtic(1) title 'Test',\
17 | $DATA using 4:xtic(1) title 'Deploy',\
18 | $DATA using 5:xtic(1) title 'Overall'
19 | unset output
--------------------------------------------------------------------------------
/examples/use_multi_word_option_keys/plot.rb:
--------------------------------------------------------------------------------
1 | require 'gnuplotrb'
2 | include GnuplotRB
3 |
4 | titles = %w{decade Build Test Deploy Overall}
5 | data = [
6 | [1, 312, 525, 215, 1052],
7 | [2, 630, 1050, 441, 2121],
8 | [3, 315, 701, 370, 1386],
9 | [4, 312, 514, 220, 1046]
10 | ]
11 | x = data.map(&:first)
12 | datasets = (1..4).map do |col|
13 | y = data.map { |row| row[col] }
14 | Dataset.new([x, y], using: '2:xtic(1)', title: titles[col], file: true)
15 | end
16 |
17 | plot = Plot.new(
18 | *datasets,
19 | style_data: 'histograms',
20 | style_fill: 'pattern border',
21 | yrange: 0..2200,
22 | xlabel: 'Number of test',
23 | ylabel: 'Time, s',
24 | title: 'Time spent to run deploy pipeline'
25 | )
26 |
27 | plot.to_png('./gnuplot_gem.png', size: [600, 600])
28 |
--------------------------------------------------------------------------------
/gnuplotrb.gemspec:
--------------------------------------------------------------------------------
1 | # coding: utf-8
2 | lib = File.expand_path('../lib', __FILE__)
3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4 | require 'gnuplotrb/version'
5 |
6 | Gem::Specification.new do |spec|
7 | spec.name = 'gnuplotrb'
8 | spec.version = GnuplotRB::VERSION
9 | spec.authors = ['Ivan Evgrafov']
10 | spec.email = ['dilcom3107@gmail.com']
11 |
12 | spec.summary = 'Ruby bindings for gnuplot'
13 | spec.description = 'Renewed ruby bindings for gnuplot. Started at GSoC 2015.'
14 | spec.homepage = 'https://github.com/dilcom/gnuplotrb'
15 | spec.license = 'MIT'
16 | spec.required_ruby_version = '>= 2.0'
17 |
18 | spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(/^(test|spec|unimplemented_features|examples|future_work|notebooks|\..+)/) }
19 | spec.require_paths = ['lib']
20 |
21 | spec.add_runtime_dependency 'hamster', '~> 1.0'
22 | spec.add_development_dependency 'bundler', '~> 1.7'
23 | spec.add_development_dependency 'rake', '~> 10.0'
24 | spec.add_development_dependency 'rspec', '~> 3.2'
25 | spec.add_development_dependency 'yard', '~> 0.8'
26 | spec.add_development_dependency 'rubocop', '~> 0.29'
27 | spec.add_development_dependency 'codeclimate-test-reporter'
28 | spec.add_development_dependency 'chunky_png'
29 | spec.add_development_dependency 'daru'
30 | end
31 |
--------------------------------------------------------------------------------
/lib/gnuplotrb.rb:
--------------------------------------------------------------------------------
1 | require 'tempfile'
2 | require 'hamster'
3 | require 'open3'
4 | require 'base64'
5 |
6 | ##
7 | # Require gem if it's available in current gemspace.
8 | #
9 | # @param name [String] gem name
10 | # @return [Boolean] true if gem was loaded, false otherwise
11 | def require_if_available(name)
12 | require name
13 | rescue LoadError
14 | false
15 | end
16 |
17 | require_if_available('daru')
18 |
19 | require 'gnuplotrb/external_classes/string'
20 | require 'gnuplotrb/external_classes/array'
21 | require 'gnuplotrb/external_classes/daru'
22 |
23 | require 'gnuplotrb/version'
24 | require 'gnuplotrb/staff/settings'
25 | require 'gnuplotrb/mixins/option_handling'
26 | require 'gnuplotrb/mixins/error_handling'
27 | require 'gnuplotrb/mixins/plottable'
28 | require 'gnuplotrb/staff/terminal'
29 | require 'gnuplotrb/staff/datablock'
30 | require 'gnuplotrb/staff/dataset'
31 | require 'gnuplotrb/fit'
32 | require 'gnuplotrb/plot'
33 | require 'gnuplotrb/splot'
34 | require 'gnuplotrb/multiplot'
35 | require 'gnuplotrb/animation'
36 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/animation.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Animation allows to create gif animation with given plots
4 | # as frames. Possible frames: Plot, Splot, Multiplot.
5 | # More about its usage in
6 | # {animation notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb].
7 | #
8 | # == Options
9 | # Animations has several specific options:
10 | # * animate - allows to get animated gif's. Possible values are true (just turn on animation),
11 | # ot hash with suboptions (:loop - count of loops, default 0 - infinity$;
12 | # :delay - delay between frames; :optimize - boolean, reduces file size).
13 | # * size - size of gif file in pixels (size: [500, 500]) or (size: 500)
14 | # * background - background color
15 | # * transparent
16 | # * enhanced
17 | # * font
18 | # * fontscale
19 | # * crop
20 | #
21 | # Animation ignores :term option and does not have methods like #to_png or #to_svg.
22 | # One can also set animation any options related to Plot and they will be considered
23 | # by all nested plots (if they does not override it with their own values).
24 | #
25 | # Animation inherits all plot array handling methods from Multiplot
26 | # and adds aliases for them (#plots -> #frames; #update_frame! -> #update_plot!; etc).
27 | class Animation < Multiplot
28 | ##
29 | # *Plot* here is also named as *frame*
30 | alias_method :frames, :plots
31 | alias_method :update_frame, :update_plot
32 | alias_method :replace_frame, :replace_plot
33 | alias_method :add_frame, :add_plot
34 | alias_method :add_frames, :add_plots
35 | alias_method :remove_frame, :remove_plot
36 | alias_method :update_frame!, :update_plot!
37 | alias_method :replace_frame!, :replace_plot!
38 | alias_method :add_frame!, :add_plot!
39 | alias_method :add_frames!, :add_plots!
40 | alias_method :remove_frame!, :remove_plot!
41 |
42 | ##
43 | # This method creates a gif animation where frames are plots
44 | # already contained by Animation object.
45 | #
46 | # Options passed in #plot have priority over those which were set before.
47 | #
48 | # Inner options of Plots have the highest priority (except
49 | # :term and :output which are ignored).
50 | #
51 | # @param path [String] path to new gif file that will be created as a result
52 | # @param options [Hash] see note about available options in top class documentation
53 | # @return [nil] if path to output file given
54 | # @return [String] gif file contents if no path to output file given
55 | def plot(path = nil, **options)
56 | options[:output] ||= path
57 | plot_options = mix_options(options) do |plot_opts, anim_opts|
58 | plot_opts.merge(term: ['gif', anim_opts])
59 | end.to_h
60 | need_output = plot_options[:output].nil?
61 | plot_options[:output] = Dir::Tmpname.make_tmpname('anim', 0) if need_output
62 | terminal = Terminal.new
63 | multiplot(terminal, plot_options)
64 | # guaranteed wait for plotting to finish
65 | terminal.close
66 | if need_output
67 | result = File.binread(plot_options[:output])
68 | File.delete(plot_options[:output])
69 | else
70 | result = nil
71 | end
72 | result
73 | end
74 |
75 | ##
76 | # #to_|term_name| methods are not supported by animation
77 | def to_specific_term(*_)
78 | fail 'Specific terminals are not supported by Animation'
79 | end
80 |
81 | ##
82 | # This method is used to embed gif animations
83 | # into iRuby notebooks.
84 | def to_iruby
85 | gif_base64 = Base64.encode64(plot)
86 | ['text/html', "
"]
87 | end
88 |
89 | private
90 |
91 | ##
92 | # Dafault options to be used for that plot
93 | def default_options
94 | {
95 | animate: {
96 | delay: 10,
97 | loop: 0,
98 | optimize: true
99 | }
100 | }
101 | end
102 |
103 | ##
104 | # This plot have some specific options which
105 | # should be handled different way than others.
106 | # Here are keys of this options.
107 | def specific_keys
108 | %w(
109 | animate
110 | size
111 | background
112 | transparent
113 | enhanced
114 | rounded
115 | butt
116 | linewidth
117 | dashlength
118 | tiny
119 | small
120 | medium
121 | large
122 | giant
123 | font
124 | fontscale
125 | crop
126 | )
127 | end
128 | end
129 | end
130 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/external_classes/array.rb:
--------------------------------------------------------------------------------
1 | ##
2 | # Methods to take data for GnuplotRB plots.
3 | class Array
4 | # taken for example from current gnuplot bindings
5 | # @return [String] array converted to Gnuplot format
6 | def to_gnuplot_points
7 | return '' if self.empty?
8 | case self[0]
9 | when Array
10 | self[0].zip(*self[1..-1]).map { |a| a.join(' ') }.join("\n")
11 | when Numeric
12 | join("\n")
13 | else
14 | self[0].zip(*self[1..-1]).to_gnuplot_points
15 | end
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/external_classes/daru.rb:
--------------------------------------------------------------------------------
1 | if defined? Daru
2 | ##
3 | # See {daru}[https://github.com/v0dro/daru] and
4 | # {plotting from daru}[https://github.com/dilcom/gnuplotrb/blob/master/notebooks/plotting_from_daru.ipynb]
5 | module Daru
6 | ##
7 | # Methods to take data for GnuplotRB plots.
8 | class DataFrame
9 | ##
10 | # Convert DataFrame to Gnuplot format.
11 | #
12 | # @return [String] data converted to Gnuplot format
13 | def to_gnuplot_points
14 | result = ''
15 | each_row_with_index do |row, index|
16 | quoted = (index.is_a?(String) || index.is_a?(Symbol)) && index.length > 0
17 | result += quoted ? "\"#{index}\" " : "#{index} "
18 | result += row.to_a.join(' ')
19 | result += "\n"
20 | end
21 | result
22 | end
23 | end
24 |
25 | ##
26 | # Methods to take data for GnuplotRB plots.
27 | class Vector
28 | ##
29 | # Convert Vector to Gnuplot format.
30 | #
31 | # @return [String] data converted to Gnuplot format
32 | def to_gnuplot_points
33 | result = ''
34 | each_with_index do |value, index|
35 | quoted = (index.is_a?(String) || index.is_a?(Symbol)) && index.length > 0
36 | result += quoted ? "\"#{index}\" " : "#{index} "
37 | result += "#{value}\n"
38 | end
39 | result
40 | end
41 | end
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/external_classes/string.rb:
--------------------------------------------------------------------------------
1 | ##
2 | # Methods to take data for GnuplotRB plots.
3 | class String
4 | # @return [String] data converted to Gnuplot format
5 | alias_method :to_gnuplot_points, :clone
6 | end
7 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/fit.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Contains methods relating to Gnuplot's fit function. Covered in
4 | # {fit notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/fitting_data.ipynb].
5 | #
6 | # You can also see original gnuplot's fit in
7 | # {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] p. 122.
8 | module Fit
9 | ##
10 | # Fit given data with function.
11 | #
12 | # Fit waits for output from gnuplot Settings.max_fit_delay and throw exception if gets nothing.
13 | # One can change this value in order to wait longer (if huge datasets is fitted).
14 | #
15 | # @param data [#to_gnuplot_points] method accepts the same sources as Dataset.new
16 | # and Dataset object
17 | # @param :function [String] function to fit data with
18 | # @param :initials [Hash] initial values for coefficients used in fitting
19 | # @param :term_options [Hash] terminal options that should be setted to terminal before fit.
20 | # You can see them in Plot's documentation (or even better in gnuplot doc)
21 | # Most useful here are ranges (xrange, yrange etc) and fit option which tunes fit parameters
22 | # (see {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] p. 122)
23 | # @param options [Hash] options passed to Gnuplot's fit such as *using*. They are covered in
24 | # {gnuplot doc}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 69-74)
25 | #
26 | # @return [Hash] hash with four elements:
27 | # - :formula_ds - dataset with best fit curve as data
28 | # - :coefficients - hash of calculated coefficients. So if you gave
29 | # ``{ initials: {a: 1, b: 1, c: 1} }`` it will return hash with keys :a, :b, :c and its values
30 | # - :deltas - Gnuplot calculates possible deltas for coefficients during fitting and
31 | # deltas hash contains this deltas
32 | # - :data - pointer to Datablock with given data
33 | # @example
34 | # fit(some_data, function: 'exp(a/x)', initials: {a: 10}, term_option: { xrange: 1..100 })
35 | # fit(some_dataset, using: '1:2:3')
36 | def fit(data, function: 'a2*x*x+a1*x+a0', initials: { a2: 1, a1: 1, a0: 1 }, term_options: {}, **options)
37 | dataset = data.is_a?(Dataset) ? Dataset.new(data.data) : Dataset.new(data)
38 | opts_str = OptionHandling.ruby_class_to_gnuplot(options)
39 | output = gnuplot_fit(function, dataset, opts_str, initials, term_options)
40 | res = parse_output(initials.keys, function, output)
41 | {
42 | formula_ds: Dataset.new(res[2], title: 'Fit formula'),
43 | coefficients: res[0],
44 | deltas: res[1],
45 | data: dataset
46 | }
47 | end
48 |
49 | ##
50 | # Shortcut for fit with polynomial. Degree here is max power of *x* in polynomial.
51 | #
52 | # @param data [#to_gnuplot_points] method accepts the same sources as Dataset.new
53 | # and Dataset object
54 | # @param :degree [Integer] degree of polynomial
55 | # @param options [Hash] all of this options will be passed to #fit so you
56 | # can set here any options listed in its docs. If you pass here :initials hash, it
57 | # will be merged into default initals hash. Formula by default is
58 | # "xn*x**n + ... + x0*x**0", initials by default "{ an: 1, ..., a0: 1 }"
59 | #
60 | # @return [Hash] hash with four elements:
61 | # - :formula_ds - dataset with best fit curve as data
62 | # - :coefficients - hash of calculated coefficients. So for degree = 3
63 | # it will return hash with keys :a3, :a2, :a1, :a0 and calculated values
64 | # - :deltas - Gnuplot calculates possible deltas for coefficients during fitting and
65 | # deltas hash contains this deltas
66 | # - :data - pointer to Datablock with given data
67 | # @example
68 | # fit_poly(some_data, degree: 5, initials: { a4: 10, a2: -1 }, term_option: { xrange: 1..100 })
69 | # #=> The same as:
70 | # #=> fit(
71 | # #=> some_data,
72 | # #=> function: 'a5*x**5 + a4*x**4 + ... + a0*x**0',
73 | # #=> initals: {a5: 1, a4: 10, a3: 1, a2: -1, a1: 1, a0: 1},
74 | # #=> term_option: { xrange: 1..100 }
75 | # #=> )
76 | def fit_poly(data, degree: 2, **options)
77 | sum_count = degree + 1
78 | initials = {}
79 | sum_count.times { |i| initials["a#{i}".to_sym] = 1 }
80 | options[:initials] = initials.merge(options[:initials] || {})
81 | function = sum_count.times.map { |i| "a#{i}*x**#{i}" }.join(' + ')
82 | fit(data, **options, function: function)
83 | end
84 |
85 | ##
86 | # @!method fit_exp(data, **options)
87 | # @!method fit_log(data, **options)
88 | # @!method fit_sin(data, **options)
89 | #
90 | # Shortcuts for fitting with several math functions (exp, log, sin).
91 | #
92 | # @param data [#to_gnuplot_points] method accepts the same sources as Dataset.new
93 | # and Dataset object
94 | # @param options [Hash] all of this options will be passed to #fit so you
95 | # can set here any options listed in its docs. If you pass here :initials hash, it
96 | # will be merged into default initals hash. Formula by default is
97 | # "yscale * (yoffset + #{function name}((x - xoffset) / xscale))", initials by default
98 | # "{ yoffset: 0.1, xoffset: 0.1, yscale: 1, xscale: 1 }"
99 | #
100 | # @return [Hash] hash with four elements:
101 | # - :formula_ds - dataset with best fit curve as data
102 | # - :coefficients - hash of calculated coefficients. So for this case
103 | # it will return hash with keys :yoffset, :xoffset, :yscale, :xscale and calculated values
104 | # - :deltas - Gnuplot calculates possible deltas for coefficients during fitting and
105 | # deltas hash contains this deltas
106 | # - :data - pointer to Datablock with given data
107 | #
108 | # @example
109 | # fit_exp(some_data, initials: { yoffset: -11 }, term_option: { xrange: 1..100 })
110 | # #=> The same as:
111 | # #=> fit(
112 | # #=> some_data,
113 | # #=> function: 'yscale * (yoffset + exp((x - xoffset) / xscale))',
114 | # #=> initals: { yoffset: -11, xoffset: 0.1, yscale: 1, xscale: 1 },
115 | # #=> term_option: { xrange: 1..100 }
116 | # #=> )
117 | # fit_log(...)
118 | # fit_sin(...)
119 | %w(exp log sin).map do |fname|
120 | define_method("fit_#{fname}".to_sym) do |data, **options|
121 | options[:initials] = {
122 | yoffset: 0.1,
123 | xoffset: 0.1,
124 | yscale: 1,
125 | xscale: 1
126 | }.merge(options[:initials] || {})
127 | function = "yscale * (yoffset + #{fname} ((x - xoffset) / xscale))"
128 | fit(data, **options, function: function)
129 | end
130 | end
131 |
132 | private
133 |
134 | ##
135 | # It takes some time to produce output so here we need
136 | # to wait for it.
137 | #
138 | # Max time to wait is stored in Settings.max_fit_delay, so one
139 | # can change it in order to wait longer.
140 | def wait_for_output(term, variables)
141 | # now we should catch 'error' from terminal: it will contain approximation data
142 | # but we can get a real error instead of output, so lets wait for limited time
143 | start = Time.now
144 | output = ''
145 | until output_ready?(output, variables)
146 | begin
147 | term.check_errors(raw: true)
148 | rescue GnuplotRB::GnuplotError => e
149 | output += e.message
150 | end
151 | if Time.now - start > Settings.max_fit_delay
152 | fail GnuplotError, "Seems like there is an error in gnuplotrb: #{output}"
153 | end
154 | end
155 | output
156 | end
157 |
158 | ##
159 | # Check if current output contains all the
160 | # variables given to fit.
161 | def output_ready?(output, variables)
162 | output =~ /Final set .*#{variables.join('.*')}/
163 | end
164 |
165 | ##
166 | # Parse Gnuplot's output to get coefficients and their deltas
167 | # from it. Also replaces coefficients in given function with
168 | # exact values.
169 | def parse_output(variables, function, output)
170 | plottable_function = " #{function.clone} "
171 | coefficients = {}
172 | deltas = {}
173 | variables.each do |var|
174 | value, error = output.scan(%r{#{var} *= ([^ ]+) *\+/\- ([^ ]+)})[0]
175 | plottable_function.gsub!(/#{var}([^0-9a-zA-Z])/) { value + Regexp.last_match(1) }
176 | coefficients[var] = value.to_f
177 | deltas[var] = error.to_f
178 | end
179 | [coefficients, deltas, plottable_function]
180 | end
181 |
182 | ##
183 | # Make fit command and send it to gnuplot
184 | def gnuplot_fit(function, data, options, initials, term_options)
185 | variables = initials.keys
186 | term = Terminal.new
187 | term.set(term_options)
188 | initials.each { |var_name, value| term.stream_puts "#{var_name} = #{value}" }
189 | command = "fit #{function} #{data.to_s(term, without_options: true)} " \
190 | "#{options} via #{variables.join(',')}"
191 | term.stream_puts(command)
192 | output = wait_for_output(term, variables)
193 | begin
194 | term.close
195 | rescue GnuplotError
196 | # Nothing interesting here.
197 | # If we had an error, we never reach this line.
198 | # Error here may be only additional information
199 | # such as correlation matrix.
200 | end
201 | output
202 | end
203 | end
204 | end
205 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/mixins/error_handling.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Just a new error name
4 | class GnuplotError < ArgumentError
5 | end
6 |
7 | ##
8 | # Mixin for classes that need to run subprocess and
9 | # handle errors from its stderr.
10 | module ErrorHandling
11 | ##
12 | # Check if there were errors in previous commands.
13 | # Throws GnuplotError in case of any errors.
14 | def check_errors(raw: false)
15 | return if @err_array.empty?
16 | command = ''
17 | rest = ''
18 | @semaphore.synchronize do
19 | command = @err_array.first
20 | rest = @err_array[1..-1].join('; ')
21 | @err_array.clear
22 | end
23 | message = if raw
24 | "#{command};#{rest}}"
25 | else
26 | "Error in previous command (\"#{command}\"): \"#{rest}\""
27 | end
28 | fail GnuplotError, message
29 | end
30 |
31 | private
32 |
33 | ##
34 | # Start new thread that will read stderr given as stream
35 | # and add errors into @err_array.
36 | def handle_stderr(stream)
37 | @err_array = []
38 | # synchronize access to @err_array
39 | @semaphore = Mutex.new
40 | Thread.new do
41 | until (line = stream.gets).nil?
42 | line.strip!
43 | @semaphore.synchronize { @err_array << line if line.size > 3 }
44 | end
45 | end
46 | end
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/mixins/option_handling.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # This module contains methods which are mixed into several classes
4 | # to set, get and convert their options.
5 | module OptionHandling
6 | class << self
7 | # Some values of options should be quoted to be read by gnuplot properly
8 | #
9 | # @todo update list with data from gnuplot documentation !!!
10 | QUOTED_OPTIONS = %w(
11 | title
12 | output
13 | xlabel
14 | x2label
15 | ylabel
16 | y2label
17 | clabel
18 | cblabel
19 | zlabel
20 | rgb
21 | font
22 | background
23 | format
24 | format_x
25 | format_y
26 | format_xy
27 | format_x2
28 | format_y2
29 | format_z
30 | format_cb
31 | timefmt
32 | dt
33 | dashtype
34 | )
35 |
36 | private_constant :QUOTED_OPTIONS
37 |
38 | ##
39 | # Replace '_' with ' ' is made to allow passing several options
40 | # with the same first word of key. See issue #7 for more info.
41 | # @param key [Symbol, String] key to modify
42 | # @return [String] given key with '_' replaced with ' '
43 | def string_key(key)
44 | key.to_s.gsub(/_/) { ' ' } + ' '
45 | end
46 |
47 | ##
48 | # Recursive function that converts Ruby option to gnuplot string
49 | #
50 | # @param key [Symbol] name of option in gnuplot
51 | # @param option an option that should be converted
52 | # @example
53 | # option_to_string(['png', size: [300, 300]])
54 | # #=> 'png size 300,300'
55 | # option_to_string(xrange: 0..100)
56 | # #=> 'xrange [0:100]'
57 | # option_to_string(multiplot: true)
58 | # #=> 'multiplot'
59 | def option_to_string(key = nil, option)
60 | return string_key(key) if !!option == option # check for boolean
61 | value = ruby_class_to_gnuplot(option)
62 | value = "\"#{value}\"" if QUOTED_OPTIONS.include?(key.to_s)
63 | ## :+ here is necessary, because using #{value} will remove quotes
64 | value = string_key(key) + value if key
65 | value
66 | end
67 |
68 | ##
69 | # @private
70 | # Method for inner use.
71 | # Needed to convert several ruby classes into
72 | # value that should be piped to gnuplot.
73 | def ruby_class_to_gnuplot(option_object)
74 | case option_object
75 | when Array
76 | option_object.map { |el| option_to_string(el) }
77 | .join(option_object[0].is_a?(Numeric) ? ',' : ' ')
78 | when Hash
79 | option_object.map { |i_key, i_val| option_to_string(i_key, i_val) }
80 | .join(' ')
81 | when Range
82 | "[#{option_object.begin}:#{option_object.end}]"
83 | else
84 | option_object.to_s
85 | end
86 | end
87 |
88 | ##
89 | # Check if given terminal available for use.
90 | #
91 | # @param terminal [String] terminal to check (e.g. 'png', 'qt', 'gif')
92 | # @return [Boolean] true or false
93 | def valid_terminal?(terminal)
94 | Settings.available_terminals.include?(terminal)
95 | end
96 |
97 | ##
98 | # Check if given options are valid for gnuplot.
99 | # Raises ArgumentError if invalid options found.
100 | # Now checks only terminal name.
101 | #
102 | # @param options [Hash] options to check (e.g. "{ term: 'qt', title: 'Plot title' }")
103 | def validate_terminal_options(options)
104 | terminal = options[:term]
105 | return unless terminal
106 | terminal = terminal[0] if terminal.is_a?(Array)
107 | message = 'Seems like your Gnuplot does not ' \
108 | "support that terminal (#{terminal}), please see " \
109 | 'supported terminals with Settings::available_terminals'
110 | fail(ArgumentError, message) unless valid_terminal?(terminal)
111 | end
112 | end
113 |
114 | ##
115 | # @private
116 | # You should implement #initialize in classes that use OptionsHelper
117 | def initialize(*_)
118 | fail NotImplementedError, 'You should implement #initialize' \
119 | ' in classes that use OptionsHelper!'
120 | end
121 |
122 | ##
123 | # @private
124 | # You should implement #new_with_options in classes that use OptionsHelper
125 | def new_with_options(*_)
126 | fail NotImplementedError, 'You should implement #new_with_options' \
127 | ' in classes that use OptionsHelper!'
128 | end
129 |
130 | ##
131 | # Create new Plot (or Dataset or Splot or Multiplot) object where current
132 | # options are merged with given. If no options
133 | # given it will just return existing set of options.
134 | #
135 | # @param options [Hash] options to add
136 | # @return [Dataset, Splot, Multiplot] new object created with given options
137 | # @return [Hamster::Hash] current options if given options empty
138 | # @example
139 | # sin_graph = Plot.new(['sin(x)', title: 'Sin'], title: 'Sin on [0:3]', xrange: 0..3)
140 | # sin_graph.plot
141 | # sin_graph_update = sin_graph.options(title: 'Sin on [-10:10]', xrange: -10..10)
142 | # sin_graph_update.plot
143 | # # sin_graph IS NOT affected
144 | def options(**options)
145 | @options ||= Hamster::Hash.new
146 | if options.empty?
147 | @options
148 | else
149 | new_with_options(@options.merge(options))
150 | end
151 | end
152 |
153 | ##
154 | # Update existing Plot (or Dataset or Splot or Multiplot) object with given options.
155 | #
156 | # @param options [Hash] options to add
157 | # @return [Dataset, Splot, Multiplot] self
158 | # @example
159 | # sin_graph = Plot.new(['sin(x)', title: 'Sin'], title: 'Sin on [0:3]', xrange: 0..3)
160 | # sin_graph.plot
161 | # sin_graph.options!(title: 'Sin on [-10:10]', xrange: -10..10)
162 | # sin_graph.plot
163 | # # second #plot call will plot not the same as first, sin_graph IS affected
164 | def options!(**options)
165 | @options = @options ? @options.merge(options) : Hamster::Hash.new(options)
166 | self
167 | end
168 |
169 | private
170 |
171 | ##
172 | # Return current option value if no value given. Create new
173 | # object with given option set if value given.
174 | def option(key, *value)
175 | if value.empty?
176 | value = options[key]
177 | value = value[0] if value && value.size == 1
178 | value
179 | else
180 | options(key => value)
181 | end
182 | end
183 |
184 | ##
185 | # Just set an option.
186 | def option!(key, *value)
187 | options!(key => value)
188 | end
189 | end
190 | end
191 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/mixins/plottable.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # This module contains methods that should be mixed into
4 | # plottable classes. It includes OptionHandling and
5 | # implements several plotting methods.
6 | module Plottable
7 | include OptionHandling
8 |
9 | ##
10 | # @private
11 | # You should implement #plot in classes that are Plottable
12 | def plot(*_)
13 | fail NotImplementedError, 'You should implement #plot in classes that are Plottable!'
14 | end
15 |
16 | ##
17 | # In this gem #method_missing is used both to handle
18 | # options and to handle plotting to specific terminal.
19 | #
20 | # == Options handling
21 | # === Overview
22 | # You may set options using #option_name(option_value) method.
23 | # A new object will be constructed with selected option set.
24 | # And finally you can get current value of any option using
25 | # #options_name without arguments.
26 | # === Arguments
27 | # * *option_value* - value to set an option. If none given
28 | # method will just return current option's value
29 | # === Examples
30 | # plot = Splot.new
31 | # new_plot = plot.title('Awesome plot')
32 | # plot.title #=> nil
33 | # new_plot.title #=> 'Awesome plot'
34 | #
35 | # == Plotting to specific term
36 | # === Overview
37 | # Gnuplot offers possibility to output graphics to many image formats.
38 | # The easiest way to to so is to use #to_ methods.
39 | # === Arguments
40 | # * *options* - set of options related to terminal (size, font etc).
41 | # Be careful, some terminals have their own specific options.
42 | # === Examples
43 | # # font options specific for png term
44 | # multiplot.to_png('./result.png', size: [300, 500], font: ['arial', 12])
45 | # # font options specific for svg term
46 | # content = multiplot.to_svg(size: [100, 100], fname: 'Arial', fsize: 12)
47 | def method_missing(meth_id, *args)
48 | meth = meth_id.id2name
49 | case
50 | when meth[0..2] == 'to_'
51 | term = meth[3..-1]
52 | super unless OptionHandling.valid_terminal?(term)
53 | to_specific_term(term, *args)
54 | when meth[-1] == '!'
55 | option!(meth[0..-2].to_sym, *args)
56 | when meth[-1] == '='
57 | option!(meth[0..-2].to_sym, *args)
58 | option(meth[0..-2].to_sym)
59 | else
60 | option(meth_id, *args)
61 | end
62 | end
63 |
64 | ##
65 | # @return [true] for existing methods and
66 | # #to_|term_name| when name is a valid terminal type.
67 | # @return [false] otherwise
68 | def respond_to?(meth_id)
69 | # Next line is here to force iRuby use #to_iruby
70 | # instead of #to_svg.
71 | return super if defined? IRuby
72 | meth = meth_id.id2name
73 | term = meth[0..2] == 'to_' && OptionHandling.valid_terminal?(meth[3..-1])
74 | term || super
75 | end
76 |
77 | ##
78 | # This method is used to embed plottable objects into iRuby notebooks. There is
79 | # {a notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb]
80 | # with examples of its usage.
81 | def to_iruby
82 | available_terminals = {
83 | 'png' => 'image/png',
84 | 'pngcairo' => 'image/png',
85 | 'jpeg' => 'image/jpeg',
86 | 'svg' => 'image/svg+xml',
87 | 'dumb' => 'text/plain'
88 | }
89 | terminal, options = term.is_a?(Array) ? [term[0], term[1]] : [term, {}]
90 | terminal = 'svg' unless available_terminals.keys.include?(terminal)
91 | [available_terminals[terminal], send("to_#{terminal}".to_sym, **options)]
92 | end
93 |
94 | ##
95 | # @private
96 | # Output plot to specific terminal (possibly some file).
97 | # Explicit use should be avoided. This method is called from #method_missing
98 | # when it handles method names like #to_png(options).
99 | #
100 | # @param trminal [String] terminal name ('png', 'svg' etc)
101 | # @param path [String] path to output file, if none given it will output to temp file
102 | # and then read it and return binary contents of file
103 | # @param options [Hash] used in #plot
104 | # @example
105 | # ## plot here may be Plot, Splot, Multiplot or any other plottable class
106 | # plot.to_png('./result.png', size: [300, 500])
107 | # contents = plot.to_svg(size: [100, 100])
108 | # plot.to_dumb('./result.txt', size: [30, 15])
109 | def to_specific_term(terminal, path = nil, **options)
110 | if path
111 | result = plot(term: [terminal, options], output: path)
112 | else
113 | path = Dir::Tmpname.make_tmpname(terminal, 0)
114 | plot(term: [terminal, options], output: path)
115 | result = File.binread(path)
116 | File.delete(path)
117 | end
118 | result
119 | end
120 |
121 | ##
122 | # @return [Terminal] terminal object linked with this Plottable object
123 | def own_terminal
124 | @terminal ||= Terminal.new
125 | end
126 |
127 | ##
128 | # @!method xrange(value = nil)
129 | # @!method yrange(value = nil)
130 | # @!method title(value = nil)
131 | # @!method option_name(value = nil)
132 | # Clone existing object and set new options value in created one or just return
133 | # existing value if nil given.
134 | #
135 | # Method is handled by #method_missing.
136 | #
137 | # You may set options using #option_name(option_value) method.
138 | # A new object will be constructed with selected option set.
139 | # And finally you can get current value of any option using
140 | # #options_name without arguments.
141 | #
142 | # Available options are listed in Plot, Splot, Multiplot etc class top level doc.
143 | #
144 | # @param value new value for option
145 | # @return new object with option_name set to *value* if value given
146 | # @return old option value if no value given
147 | #
148 | # @example
149 | # plot = Splot.new
150 | # new_plot = plot.title('Awesome plot')
151 | # plot.title #=> nil
152 | # new_plot.title #=> 'Awesome plot'
153 |
154 | ##
155 | # @!method xrange!(value)
156 | # @!method yrange!(value)
157 | # @!method title!(value)
158 | # @!method option_name!(value)
159 | # Set value for an option.
160 | #
161 | # Method is handled by #method_missing.
162 | #
163 | # You may set options using obj.option_name!(option_value) or
164 | # obj.option_name = option_value methods.
165 | #
166 | # Available options are listed in Plot, Splot, Multiplot etc class top level doc.
167 | #
168 | # @param value new value for option
169 | # @return self
170 | #
171 | # @example
172 | # plot = Splot.new
173 | # plot.title #=> nil
174 | # plot.title!('Awesome plot')
175 | # plot.title #=> 'Awesome plot'
176 | #
177 | # @example
178 | # plot = Splot.new
179 | # plot.title #=> nil
180 | # plot.title = 'Awesome plot'
181 | # plot.title #=> 'Awesome plot'
182 |
183 | ##
184 | # @!method to_png(path = nil, **options)
185 | # @!method to_svg(path = nil, **options)
186 | # @!method to_gif(path = nil, **options)
187 | # @!method to_canvas(path = nil, **options)
188 | # Output to plot to according image format.
189 | #
190 | # All of #to_|terminal_name| methods are handled with #method_missing.
191 | #
192 | # Gnuplot offers possibility to output graphics to many image formats.
193 | # The easiest way to to so is to use #to_ methods.
194 | #
195 | # @param path [String] path to save plot file to.
196 | # @param options [Hash] specific terminal options like 'size',
197 | # 'font' etc
198 | #
199 | # @return [String] contents of plotted file unless path given
200 | # @return self if path given
201 | #
202 | # @example
203 | # # font options specific for png term
204 | # multiplot.to_png('./result.png', size: [300, 500], font: ['arial', 12])
205 | # # font options specific for svg term
206 | # content = multiplot.to_svg(size: [100, 100], fname: 'Arial', fsize: 12)
207 | end
208 | end
209 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/multiplot.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Multiplot allows to place several plots on one layout.
4 | # It's usage is covered in
5 | # {multiplot notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/multiplot_layout.ipynb].
6 | #
7 | # == Options
8 | # Most of Multiplot options are the same as in Plot so one can also set any options related
9 | # to Plot and they will be considered by all nested plots
10 | # (if they does not override it with their own values).
11 | #
12 | # There are only 2 specific options:
13 | # * title - set title for the whole layout (above all the plots)
14 | # * layout - set layout size, examples:
15 | # { layout : [1, 3] } # 3 plots, 1 row, 3 columns
16 | # { layout : [2, 2] } # 4 plots, 2 rows, 2 columns
17 | class Multiplot
18 | include Plottable
19 | ##
20 | # @return [Array] Array of plots contained by this object
21 | attr_reader :plots
22 |
23 | ##
24 | # @param plots [Plot, Splot, Hamster::Vector] Hamster vector (or just sequence) with Plot
25 | # or Splot objects which should be placed on this multiplot layout
26 | # @param options [Hash] see options in top class docs
27 | def initialize(*plots, **options)
28 | @plots = plots[0].is_a?(Hamster::Vector) ? plots[0] : Hamster::Vector.new(plots)
29 | @options = Hamster.hash(options)
30 | OptionHandling.validate_terminal_options(@options)
31 | yield(self) if block_given?
32 | end
33 |
34 | ##
35 | # Output all the plots to term (if given) or to this Multiplot's own terminal.
36 | #
37 | # @param term [Terminal] Terminal to plot to
38 | # @param multiplot_part [Boolean] placeholder, does not really needed and should not be used
39 | # @param options [Hash] see options in top class docs.
40 | # Options passed here have priority over already set.
41 | # @return [Multiplot] self
42 | def plot(term = nil, multiplot_part: false, **options)
43 | plot_options = mix_options(options) do |plot_opts, mp_opts|
44 | plot_opts.merge(multiplot: mp_opts.to_h)
45 | end
46 | terminal = term || (plot_options[:output] ? Terminal.new : own_terminal)
47 | multiplot(terminal, plot_options)
48 | if plot_options[:output]
49 | # guaranteed wait for plotting to finish
50 | terminal.close unless term
51 | # not guaranteed wait for plotting to finish
52 | # work bad with terminals like svg and html
53 | sleep 0.01 until File.size?(plot_options[:output])
54 | end
55 | self
56 | end
57 |
58 | ##
59 | # Create new updated Multiplot object
60 | # where plot (Plot or Splot object) at *position* will
61 | # be replaced with the new one created from it by updating.
62 | # To update a plot you can pass some options for it or a
63 | # block, that should take existing plot (with new options if
64 | # you gave them) and return a plot too.
65 | #
66 | # Method yields new created Plot or Splot to allow you update it manually.
67 | #
68 | # @param position [Integer] position of plot which you need to update
69 | # (by default first plot is updated)
70 | # @param options [Hash] options to set into updated plot
71 | # @return [Multiplot] self
72 | # @yieldparam plot [Plot, Splot] a new plot
73 | # @yieldreturn [Plot, Splot] changed plot
74 | # @example
75 | # mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
76 | # updated_mp = mp.update_plot(title: 'Sin(x) and Exp(x)') { |sinx| sinx.add!('exp(x)') }
77 | # # mp IS NOT affected
78 | def update_plot(position = 0, **options)
79 | return self unless block_given? if options.empty?
80 | replacement = @plots[position].options(options)
81 | replacement = yield(replacement) if block_given?
82 | replace_plot(position, replacement)
83 | end
84 |
85 | alias_method :update, :update_plot
86 |
87 | ##
88 | # Destructive version of #update_plot.
89 | #
90 | # @return [Multiplot] self
91 | # @example
92 | # Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
93 | # mp.update_plot!(title: 'Sin(x) and Exp(x)') { |sinx| sinx.add!('exp(x)') }
94 | # # mp IS affected
95 | def update_plot!(position = 0, **options)
96 | return self unless block_given? if options.empty?
97 | replacement = @plots[position].options!(options)
98 | yield(replacement) if block_given?
99 | self
100 | end
101 |
102 | alias_method :update!, :update_plot!
103 |
104 | ##
105 | # Create new Multiplot object where plot (Plot or Splot object)
106 | # at *position* will be replaced with the given one.
107 | #
108 | # @param position [Integer] position of plot which you need to replace
109 | # (by default first plot is replace)
110 | # @param plot [Plot, Splot] replacement
111 | # @return [Multiplot] self
112 | # @example
113 | # mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
114 | # mp_with_replaced_plot = mp.replace_plot(Plot.new('exp(x)', title: 'exp instead of sin'))
115 | # # mp IS NOT affected
116 | def replace_plot(position = 0, plot)
117 | self.class.new(@plots.set(position, plot), @options)
118 | end
119 |
120 | alias_method :replace, :replace_plot
121 |
122 | ##
123 | # Destructive version of #replace_plot.
124 | #
125 | # @return [Multiplot] self
126 | # @example
127 | # mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
128 | # mp.replace_plot!(Plot.new('exp(x)', title: 'exp instead of sin'))
129 | # # mp IS affected
130 | def replace_plot!(position = 0, plot)
131 | @plots = @plots.set(position, plot)
132 | self
133 | end
134 |
135 | alias_method :replace!, :replace_plot!
136 | alias_method :[]=, :replace_plot!
137 |
138 | ##
139 | # Create new Multiplot with given *plots* added before plot at given *position*.
140 | # (by default it adds plot at the front).
141 | #
142 | # @param position [Integer] position of plot which you need to replace
143 | # (by default first plot is replace)
144 | # @param plots [Sequence of Plot or Splot] plots you want to add
145 | # @return [Multiplot] self
146 | # @example
147 | # mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
148 | # enlarged_mp = mp.add_plots(Plot.new('exp(x)')).layout([3,1])
149 | # # mp IS NOT affected
150 | def add_plots(*plots)
151 | plots.unshift(0) unless plots[0].is_a?(Numeric)
152 | self.class.new(@plots.insert(*plots), @options)
153 | end
154 |
155 | alias_method :add_plot, :add_plots
156 | alias_method :<<, :add_plots
157 | alias_method :add, :add_plots
158 |
159 | ##
160 | # Destructive version of #add_plots.
161 | #
162 | # @return [Multiplot] self
163 | # @example
164 | # mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
165 | # mp.add_plots!(Plot.new('exp(x)')).layout([3,1])
166 | # # mp IS affected
167 | def add_plots!(*plots)
168 | plots.unshift(0) unless plots[0].is_a?(Numeric)
169 | @plots = @plots.insert(*plots)
170 | self
171 | end
172 |
173 | alias_method :add_plot!, :add_plots!
174 | alias_method :add!, :add_plots!
175 |
176 | ##
177 | # Create new Multiplot without plot at given position
178 | # (by default last plot is removed).
179 | #
180 | # @param position [Integer] position of plot which you need to remove
181 | # (by default last plot is removed)
182 | # @return [Multiplot] self
183 | # @example
184 | # mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
185 | # mp_with_only_cos = mp.remove_plot(0)
186 | # # mp IS NOT affected
187 | def remove_plot(position = -1)
188 | self.class.new(@plots.delete_at(position), @options)
189 | end
190 |
191 | alias_method :remove, :remove_plot
192 |
193 | ##
194 | # Destructive version of #remove_plot.
195 | #
196 | # @return [Multiplot] self
197 | # @example
198 | # mp = Multiplot.new(Plot.new('sin(x)'), Plot.new('cos(x)'), layout: [2,1])
199 | # mp.remove_plot!(0)
200 | # # mp IS affected
201 | def remove_plot!(position = -1)
202 | @plots = @plots.delete_at(position)
203 | self
204 | end
205 |
206 | alias_method :remove!, :remove_plot!
207 |
208 | ##
209 | # Equal to #plots[*args]
210 | def [](*args)
211 | @plots[*args]
212 | end
213 |
214 | private
215 |
216 | ##
217 | # Default options to be used for that plot
218 | def default_options
219 | {
220 | layout: [2, 2],
221 | title: 'Multiplot'
222 | }
223 | end
224 |
225 | ##
226 | # This plot have some specific options which
227 | # should be handled different way than others.
228 | # Here are keys of this options.
229 | def specific_keys
230 | %w(
231 | title
232 | layout
233 | )
234 | end
235 |
236 | ##
237 | # Create new Multiplot object with the same set of plots and
238 | # given options.
239 | # Used in OptionHandling module.
240 | def new_with_options(options)
241 | self.class.new(@plots, options)
242 | end
243 |
244 | ##
245 | # Check if given options corresponds to multiplot.
246 | # Uses #specific_keys to check.
247 | def specific_option?(key)
248 | specific_keys.include?(key.to_s)
249 | end
250 |
251 | ##
252 | # Takes all options and splits them into specific and
253 | # others. Requires a block where this two classes should
254 | # be mixed.
255 | def mix_options(options)
256 | all_options = @options.merge(options)
257 | specific_options, plot_options = all_options.partition { |key, _value| specific_option?(key) }
258 | yield(plot_options, default_options.merge(specific_options))
259 | end
260 |
261 | ##
262 | # Just a part of #plot.
263 | def multiplot(terminal, options)
264 | terminal.set(options)
265 | @plots.each { |graph| graph.plot(terminal, multiplot_part: true) }
266 | terminal.unset(options.keys)
267 | end
268 | end
269 | end
270 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/plot.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Class corresponding to simple 2D visualisation.
4 | #
5 | # == Notebooks
6 | #
7 | # * {Heatmaps}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/heatmaps.ipynb]
8 | # * {Vector field}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/vector_field.ipynb]
9 | # * {Math equations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/math_plots.ipynb]
10 | # * {Histogram}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/histogram.ipynb]
11 | # * {Updating plots with new data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb]
12 | #
13 | # == Options
14 | # All possible options are exaplained in
15 | # {gnuplot docs}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 105-190).
16 | #
17 | # Several common ones:
18 | #
19 | # * xrange(yrange, zrange, urange, vrange) - set range for a variable. Takes
20 | # Range (xrange: 0..100), or String (yrange: '[0:100]').
21 | # * title - plot's title. Takes String (title: 'Some new plot').
22 | # * polar (parametric) - plot in polar or parametric space. Takes boolean (true).
23 | # * style_data - set style for plotting data. Takes string, possible values: histogram,
24 | # points, lines, linespoints, boxes etc. See gnuplot docs for more.
25 | # * term - select terminal used by gnuplot. Examples: { term: 'png' },
26 | # { term: ['svg', size: [600, 600]] }. Deprecated due to existance of #to_ methods.
27 | # One can use #to_png and #to_svg(size: [600, 600]) instead of passing previous options.
28 | # * output - select filename to output plot to. Should be used together with term. Deprecated
29 | # due to existance of #to_ methods. One should use #to_png('file.png') instead of
30 | # passing { term: 'png', output: 'file.png' }.
31 | # Every option may be passed to constructor in order to create plot with it.
32 | #
33 | # Methods #options(several: options, ...) and bunch of #option_name(only_an: option) such as
34 | # #xrange, #using, #polar etc create new Plot object based on existing but with a new options.
35 | #
36 | # Methods with the same names ending with '!' or '=' ('plot.xrange!(1..3)',
37 | # 'plot.title = "New title"') are destructive and modify state of existing object just as
38 | # "Array#sort!" do with Array object. See notebooks for examples.
39 | class Plot
40 | include Plottable
41 | ##
42 | # Array of datasets which are plotted by this object.
43 | attr_reader :datasets
44 | ##
45 | # @param *datasets [Sequence of Dataset or Array] either instances of Dataset class or
46 | # "[data, **dataset_options]"" arrays
47 | # @param options [Hash] see Plot top level doc for options examples
48 | def initialize(*datasets)
49 | # had to relace **options arg with this because in some cases
50 | # Daru::DataFrame was mentioned as hash and added to options
51 | # instead of plots
52 | @options = Hamster.hash
53 | if datasets[-1].is_a?(Hamster::Hash) || datasets[-1].is_a?(Hash)
54 | @options = Hamster.hash(datasets[-1])
55 | datasets = datasets[0..-2]
56 | end
57 | @datasets = parse_datasets_array(datasets)
58 | @cmd = 'plot '
59 | OptionHandling.validate_terminal_options(@options)
60 | yield(self) if block_given?
61 | end
62 |
63 | ##
64 | # Output plot to term (if given) or to this plot's own terminal.
65 | #
66 | # @param term [Terminal] Terminal object to plot to
67 | # @param :multiplot_part [Boolean] true if this plot is part of a multiplot. For inner use!
68 | # @param options [Hash] see options in Plot top level doc.
69 | # Options passed here have priority over already existing.
70 | # @return [Plot] self
71 | def plot(term = nil, multiplot_part: false, **options)
72 | fail ArgumentError, 'Empty plots are not supported!' if @datasets.empty?
73 | inner_opts = if multiplot_part
74 | @options.merge(options).reject { |key, _| [:term, :output].include?(key) }
75 | else
76 | @options.merge(options)
77 | end
78 | terminal = term || (inner_opts[:output] ? Terminal.new : own_terminal)
79 | ds_string = @datasets.map { |dataset| dataset.to_s(terminal) }.join(' , ')
80 | full_command = @cmd + ds_string
81 | terminal.set(inner_opts).stream_puts(full_command).unset(inner_opts.keys)
82 | if inner_opts[:output]
83 | # guaranteed wait for plotting to finish
84 | terminal.close unless term
85 | # not guaranteed wait for plotting to finish
86 | # work bad with terminals like svg and html
87 | sleep 0.01 until File.size?(inner_opts[:output])
88 | end
89 | self
90 | end
91 |
92 | alias_method :replot, :plot
93 |
94 | ##
95 | # Create new Plot object where dataset at *position* will
96 | # be replaced with the new one created from it by updating.
97 | #
98 | # @param position [Integer] position of dataset which you need to update
99 | # (by default first dataset is updated)
100 | # @param data [#to_gnuplot_points] data to update dataset with
101 | # @param options [Hash] options to update dataset with, see Dataset top level doc
102 | #
103 | # @example
104 | # updated_plot = plot.update_dataset(data: [x1,y1], title: 'After update')
105 | # # plot IS NOT affected (if dataset did not store data in a file)
106 | def update_dataset(position = 0, data: nil, **options)
107 | old_ds = @datasets[position]
108 | new_ds = old_ds.update(data, options)
109 | new_ds.equal?(old_ds) ? self : replace_dataset(position, new_ds)
110 | end
111 |
112 | ##
113 | # Updates existing Plot object by replacing dataset at *position*
114 | # with the new one created from it by updating.
115 | #
116 | # @param position [Integer] position of dataset which you need to update
117 | # (by default first dataset is updated)
118 | # @param data [#to_gnuplot_points] data to update dataset with
119 | # @param options [Hash] options to update dataset with, see Dataset top level doc
120 | #
121 | # @example
122 | # plot.update_dataset!(data: [x1,y1], title: 'After update')
123 | # # plot IS affected anyway
124 | def update_dataset!(position = 0, data: nil, **options)
125 | @datasets[position].update!(data, options)
126 | self
127 | end
128 |
129 | ##
130 | # Create new Plot object where dataset at *position* will
131 | # be replaced with the given one.
132 | #
133 | # @param position [Integer] position of dataset which you need to replace
134 | # (by default first dataset is replaced)
135 | # @param dataset [Dataset, Array] dataset to replace the old one. You can also
136 | # give here "[data, **dataset_options]"" array from which Dataset may be created.
137 | # @example
138 | # sinx = Plot.new('sin(x)')
139 | # cosx = sinx.replace_dataset(['cos(x)'])
140 | # # sinx IS NOT affected
141 | def replace_dataset(position = 0, dataset)
142 | self.class.new(@datasets.set(position, dataset_from_any(dataset)), @options)
143 | end
144 |
145 | ##
146 | # Updates existing Plot object by replacing dataset at *position*
147 | # with the given one.
148 | #
149 | # @param position [Integer] position of dataset which you need to replace
150 | # (by default first dataset is replaced)
151 | # @param dataset [Dataset, Array] dataset to replace the old one. You can also
152 | # give here "[data, **dataset_options]"" array from which Dataset may be created.
153 | # @example
154 | # sinx = Plot.new('sin(x)')
155 | # sinx.replace_dataset!(['cos(x)'])
156 | # # sinx IS affected
157 | def replace_dataset!(position = 0, dataset)
158 | @datasets = @datasets.set(position, dataset_from_any(dataset))
159 | self
160 | end
161 |
162 | alias_method :[]=, :replace_dataset!
163 |
164 | ##
165 | # Create new Plot object where given datasets will
166 | # be inserted into dataset list before given position
167 | # (position = 0 by default).
168 | #
169 | # @param position [Integer] position of dataset BEFORE which datasets will be placed.
170 | # 0 by default.
171 | # @param *datasets [ Sequence of Dataset or Array] datasets to insert
172 | # @example
173 | # sinx = Plot.new('sin(x)')
174 | # sinx_and_cosx_with_expx = sinx.add(['cos(x)'], ['exp(x)'])
175 | #
176 | # cosx_and_sinx = sinx << ['cos(x)']
177 | # # sinx IS NOT affected in both cases
178 | def add_datasets(*datasets)
179 | datasets.map! { |ds| ds.is_a?(Numeric) ? ds : dataset_from_any(ds) }
180 | # first element is position where to add datasets
181 | datasets.unshift(0) unless datasets[0].is_a?(Numeric)
182 | self.class.new(@datasets.insert(*datasets), @options)
183 | end
184 |
185 | alias_method :add_dataset, :add_datasets
186 | alias_method :<<, :add_datasets
187 |
188 | ##
189 | # Updates existing Plot object by inserting given datasets
190 | # into dataset list before given position (position = 0 by default).
191 | #
192 | # @param position [Integer] position of dataset BEFORE which datasets will be placed.
193 | # 0 by default.
194 | # @param *datasets [ Sequence of Dataset or Array] datasets to insert
195 | # @example
196 | # sinx = Plot.new('sin(x)')
197 | # sinx.add!(['cos(x)'], ['exp(x)'])
198 | # # sinx IS affected
199 | def add_datasets!(*datasets)
200 | datasets.map! { |ds| ds.is_a?(Numeric) ? ds : dataset_from_any(ds) }
201 | # first element is position where to add datasets
202 | datasets.unshift(0) unless datasets[0].is_a?(Numeric)
203 | @datasets = @datasets.insert(*datasets)
204 | self
205 | end
206 |
207 | alias_method :add_dataset!, :add_datasets!
208 |
209 | ##
210 | # Create new Plot object where dataset at given position
211 | # will be removed from dataset list.
212 | #
213 | # @param position [Integer] position of dataset that should be
214 | # removed (by default last dataset is removed)
215 | # @example
216 | # sinx_and_cosx = Plot.new('sin(x)', 'cos(x)')
217 | # sinx = sinx_and_cosx.remove_dataset
218 | # cosx = sinx_and_cosx.remove_dataset(0)
219 | # # sinx_and_cosx IS NOT affected in both cases
220 | def remove_dataset(position = -1)
221 | self.class.new(@datasets.delete_at(position), @options)
222 | end
223 |
224 | ##
225 | # Updates existing Plot object by removing dataset at given position.
226 | #
227 | # @param position [Integer] position of dataset that should be
228 | # removed (by default last dataset is removed)
229 | # @example
230 | # sinx_and_cosx = Plot.new('sin(x)', 'cos(x)')
231 | # sinx_and_cosx!.remove_dataset
232 | # sinx_and_cosx!.remove_dataset
233 | # # sinx_and_cosx IS affected and now is empty
234 | def remove_dataset!(position = -1)
235 | @datasets = @datasets.delete_at(position)
236 | self
237 | end
238 |
239 | ##
240 | # The same as #datasets[*args]
241 | def [](*args)
242 | @datasets[*args]
243 | end
244 |
245 | private
246 |
247 | ##
248 | # Checks several conditions and set options needed
249 | # to handle DateTime indexes properly.
250 | def provide_with_datetime_format(data, using)
251 | return unless defined?(Daru)
252 | return unless data.is_a?(Daru::DataFrame) || data.is_a?(Daru::Vector)
253 | return unless data.index.first.is_a?(DateTime)
254 | return if using[0..1] != '1:'
255 | @options = Hamster::Hash.new(
256 | xdata: 'time',
257 | timefmt: '%Y-%m-%dT%H:%M:%S',
258 | format_x: '%d\n%b\n%Y'
259 | ).merge(@options)
260 | end
261 |
262 | ##
263 | # Check if given args is a dataset and returns it. Creates
264 | # new dataset from given args otherwise.
265 | def dataset_from_any(source)
266 | ds = case source
267 | # when initialized with dataframe (it passes here several vectors)
268 | when (defined?(Daru) ? Daru::Vector : nil)
269 | Dataset.new(source)
270 | when Dataset
271 | source.clone
272 | else
273 | Dataset.new(*source)
274 | end
275 | data = source.is_a?(Array) ? source[0] : source
276 | provide_with_datetime_format(data, ds.using)
277 | ds
278 | end
279 |
280 | ##
281 | # Parses given array and returns Hamster::Vector of Datasets
282 | def parse_datasets_array(datasets)
283 | case datasets[0]
284 | when Hamster::Vector
285 | datasets[0]
286 | when (defined?(Daru) ? Daru::DataFrame : nil)
287 | Hamster::Vector.new(datasets[0].map { |ds| dataset_from_any(ds) })
288 | else
289 | Hamster::Vector.new(datasets.map { |ds| dataset_from_any(ds) })
290 | end
291 | end
292 |
293 | ##
294 | # Creates new Plot with existing data and given options.
295 | def new_with_options(options)
296 | self.class.new(@datasets, options)
297 | end
298 | end
299 | end
300 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/splot.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Splot class correspond to simple 3D visualisation.
4 | # Most of Plot's docs are right for Splot too.
5 | #
6 | # Examples of usage are in
7 | # {a notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/3d_plot.ipynb]
8 | class Splot < Plot
9 | ##
10 | # @param *datasets [Sequence of Dataset or Array] either instances of Dataset class or
11 | # "[data, **dataset_options]"" arrays
12 | # @param options [Hash] see Plot top level doc for options examples
13 | def initialize(*datasets, **options)
14 | super
15 | @cmd = 'splot '
16 | end
17 | end
18 | end
19 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/staff/datablock.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # This class corresponds to points we want to plot. It may be
4 | # stored in temporary file (to allow fast update) or inside
5 | # "$DATA << EOD ... EOD" construction. Datablock stores data passed
6 | # to constructor and keeps datablock name or path to file where it is stored.
7 | class Datablock
8 | ##
9 | # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
10 | # @param stored_in_file [Boolean] true here will force this datablock to store its data
11 | # in temporary file.
12 | def initialize(data, stored_in_file = false)
13 | @stored_in_file = stored_in_file
14 | data_str = data.to_gnuplot_points
15 | if @stored_in_file
16 | @file_name = Dir::Tmpname.make_tmpname('tmp_data', 0)
17 | File.write(@file_name, data_str)
18 | name = File.join(Dir.pwd, @file_name)
19 | ObjectSpace.define_finalizer(self, proc { File.delete(name) })
20 | else
21 | @data = data_str
22 | end
23 | end
24 |
25 | ##
26 | # Instantiate one more Datablock with updated data
27 | # if data stored in here-doc. Append update to file
28 | # if data stored there.
29 | #
30 | # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
31 | # @return [Datablock] self if data stored in file (see constructor)
32 | # @return [Datablock] new datablock with updated data otherwise
33 | #
34 | # @example
35 | # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
36 | # db = Datablock.new(data, false)
37 | # update = [[4, 5], [16, 25]]
38 | # updated_db = db.update(update)
39 | # # now db and updated_db contain DIFFERENT data
40 | # # db - points with x from 0 up to 3
41 | # # updated_db - points with x from 0 to 5
42 | #
43 | # @example
44 | # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
45 | # db = Datablock.new(data, true)
46 | # update = [[4, 5], [16, 25]]
47 | # updated_db = db.update(update)
48 | # # now db and updated_db contain THE SAME data
49 | # # because they linked with the same temporary file
50 | # # db - points with x from 0 up to 5
51 | # # updated_db - points with x from 0 to 5
52 | def update(data)
53 | data_str = data.to_gnuplot_points
54 | if @stored_in_file
55 | File.open(@file_name, 'a') { |f| f.puts "\n#{data_str}" }
56 | self
57 | else
58 | Datablock.new("#{@data}\n#{data_str}", false)
59 | end
60 | end
61 |
62 | ##
63 | # Update existing Datablock with new data.
64 | # Destructive version of #update.
65 | #
66 | # @param data [#to_gnuplot_points] anything with #to_gnuplot_points method
67 | # @return [Datablock] self
68 | #
69 | # @example
70 | # data = [[0, 1, 2, 3], [0, 1, 4, 9]] # y = x**2
71 | # db = Datablock.new(data, false)
72 | # update = [[4, 5], [16, 25]]
73 | # db.update!(update)
74 | # # now db contains points with x from 0 up to 5
75 | def update!(data)
76 | data_str = data.to_gnuplot_points
77 | if @stored_in_file
78 | File.open(@file_name, 'a') { |f| f.puts "\n#{data_str}" }
79 | else
80 | @data = "#{@data}\n#{data_str}"
81 | end
82 | self
83 | end
84 |
85 | ##
86 | # Get quoted filename if datablock stored in file or output
87 | # datablock to gnuplot and return its name otherwise.
88 | #
89 | # @param gnuplot_term [Terminal] should be given if datablock not stored in file
90 | # @return [String] quoted filename if data stored in file (see contructor)
91 | # @return [String] Gnuplot's datablock name otherwise
92 | def name(gnuplot_term = nil)
93 | if @stored_in_file
94 | "'#{@file_name}'"
95 | else
96 | fail(ArgumentError, 'No terminal given to output datablock') unless gnuplot_term
97 | gnuplot_term.store_datablock(@data)
98 | end
99 | end
100 |
101 | alias_method :to_s, :name
102 |
103 | ##
104 | # Overridden #clone. Since datablock which store data
105 | # in temporary files should not be cloned (otherwise it will cause
106 | # double attempt to delete file), this #clone returns self for such
107 | # cases. For other cases it just calls default #clone.
108 | def clone
109 | @stored_in_file ? self : super
110 | end
111 | end
112 | end
113 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/staff/dataset.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Dataset keeps control of Datablock or String (some math functions like
4 | # this 'x*sin(x)' or filename) and options related to original dataset
5 | # in gnuplot (with, title, using etc).
6 | #
7 | # == Options
8 | # Dataset options are explained in
9 | # {gnuplot docs}[http://www.gnuplot.info/docs_5.0/gnuplot.pdf] (pp. 80-101).
10 | # Several common options:
11 | # * with - set plot style for dataset ('lines', 'points', 'impulses' etc)
12 | # * using - choose which columns of input data gnuplot should use. Takes String
13 | # (using: 'xtic(1):2:3'). If Daru::Dataframe passed one can use column names
14 | # instead of numbers (using: 'index:value1:summ' - value1 and summ here are column names).
15 | # * linewidth (lw) - integer line width
16 | # * dashtype (dt) - takes pattern with dash style. Examples: '.. ', '-- ', '.- '.
17 | # * pointtype (pt) - takes integer number of point type (works only when :with option is set to
18 | # 'points'). One can call Terminal::test(term_name)
19 | # or Terminal#test in order to see which point types are supported by terminal.
20 | class Dataset
21 | include Plottable
22 | ##
23 | # Data represented by this dataset
24 | attr_reader :data
25 |
26 | ##
27 | # Order is significant for some options
28 | OPTION_ORDER = %w(index using axes title)
29 |
30 | private_constant :OPTION_ORDER
31 |
32 | ##
33 | # Hash of init handlers for data given in
34 | # different containers.
35 | INIT_HANDLERS = Hash.new(:init_default).merge(
36 | String => :init_string,
37 | Datablock => :init_dblock
38 | )
39 | INIT_HANDLERS.merge!(
40 | Daru::DataFrame => :init_daru_frame,
41 | Daru::Vector => :init_daru_vector
42 | ) if defined? Daru
43 |
44 | ##
45 | # Create new dataset out of given string with math function or filename.
46 | # If *data* isn't a string it will create datablock to store data.
47 | #
48 | # @param data [String, Datablock, #to_gnuplot_points] String, Datablock or something acceptable
49 | # by Datablock.new as data (e.g. [x,y] where x and y are arrays)
50 | # @param options [Hash] options specific for gnuplot
51 | # dataset (see Dataset top level doc), and some special options ('file: true' will
52 | # make data to be stored inside temporary file)
53 | #
54 | # @example Math function:
55 | # Dataset.new('x*sin(x)', with: 'lines', lw: 4)
56 | # @example File with points:
57 | # Dataset.new('points.data', with: 'lines', title: 'Points from file')
58 | # @example Some data (creates datablock stored in memory):
59 | # x = (0..5000).to_a
60 | # y = x.map {|xx| xx*xx }
61 | # points = [x, y]
62 | # Dataset.new(points, with: 'points', title: 'Points')
63 | # @example The same data but datablock stores it in temp file:
64 | # Dataset.new(points, with: 'points', title: 'Points', file: true)
65 | def initialize(data, **options)
66 | # run method by name
67 | send(INIT_HANDLERS[data.class], data, options)
68 | end
69 |
70 | ##
71 | # Convert Dataset to string containing gnuplot dataset.
72 | #
73 | # @param terminal [Terminal] must be given if data given as Datablock and
74 | # it does not use temp file so data should be piped out
75 | # to gnuplot via terminal before use
76 | # @param :without_options [Boolean] do not add options to dataset if set
77 | # to true. Used by Fit::fit
78 | # @return [String] gnuplot dataset
79 | # @example
80 | # Dataset.new('points.data', with: 'lines', title: 'Points from file').to_s
81 | # #=> "'points.data' with lines title 'Points from file'"
82 | # Dataset.new(points, with: 'points', title: 'Points').to_s
83 | # #=> "$DATA1 with points title 'Points'"
84 | def to_s(terminal = nil, without_options: false)
85 | result = "#{@type == :datablock ? @data.name(terminal) : @data} "
86 | result += options_to_string unless without_options
87 | result
88 | end
89 |
90 | ##
91 | # Create new dataset with updated data and merged options.
92 | #
93 | # Given data is appended to existing.
94 | # Data is updated only if Dataset stores it in Datablock.
95 | # Method does nothing if no options given and data isn't stored
96 | # in in-memory Datablock.
97 | #
98 | # @param data [#to_gnuplot_points] data to append to existing
99 | # @param options [Hash] new options to merge with existing options
100 | # @return self if dataset corresponds to math formula or file
101 | # (filename or temporary file if datablock)
102 | # @return [Dataset] new dataset if data is stored in 'in-memory' Datablock
103 | # @example Updating dataset with Math formula or filename given:
104 | # dataset = Dataset.new('file.data')
105 | # dataset.update(data: 'asd')
106 | # #=> nothing updated
107 | # dataset.update(data: 'asd', title: 'File')
108 | # #=> Dataset.new('file.data', title: 'File')
109 | # @example Updating dataset with data stored in Datablock (in-memory):
110 | # in_memory_points = Dataset.new(points, title: 'Old one')
111 | # in_memory_points.update(data: some_update, title: 'Updated')
112 | # #=> Dataset.new(points + some_update, title: 'Updated')
113 | # @example Updating dataset with data stored in Datablock (in-file):
114 | # temp_file_points = Dataset.new(points, title: 'Old one', file: true)
115 | # temp_file_points.update(data: some_update)
116 | # #=> data updated but no new dataset created
117 | # temp_file_points.update(data: some_update, title: 'Updated')
118 | # #=> data updated and new dataset with title 'Updated' returned
119 | def update(data = nil, **options)
120 | if data && @type == :datablock
121 | new_datablock = @data.update(data)
122 | if new_datablock == @data
123 | update_options(options)
124 | else
125 | self.class.new(new_datablock, options)
126 | end
127 | else
128 | update_options(options)
129 | end
130 | end
131 |
132 | ##
133 | # Update Dataset with new data and options.
134 | #
135 | # Given data is appended to existing.
136 | # Data is updated only if Dataset stores it in Datablock.
137 | # Method does nothing if no options given and data isn't stored
138 | # in in-memory Datablock.
139 | #
140 | # @param data [#to_gnuplot_points] data to append to existing
141 | # @param options [Hash] new options to merge with existing options
142 | # @return self
143 | # @example Updating dataset with Math formula or filename given:
144 | # dataset = Dataset.new('file.data')
145 | # dataset.update!(data: 'asd')
146 | # #=> nothing updated
147 | # dataset.update!(data: 'asd', title: 'File')
148 | # dataset.title
149 | # #=> 'File' # data isn't updated
150 | # @example Updating dataset with data stored in Datablock (in-memory):
151 | # in_memory_points = Dataset.new(points, title: 'Old one')
152 | # in_memory_points.update!(data: some_update, title: 'Updated')
153 | # in_memory_points.data
154 | # #=> points + some_update
155 | # in_memory_points.title
156 | # #=> 'Updated'
157 | # @example Updating dataset with data stored in Datablock (in-file):
158 | # temp_file_points = Dataset.new(points, title: 'Old one', file: true)
159 | # temp_file_points.update!(data: some_update)
160 | # #=> data updated but no new dataset created
161 | # temp_file_points.update!(data: some_update, title: 'Updated')
162 | # #=> data and options updated
163 | def update!(data = nil, **options)
164 | @data.update!(data) if data
165 | options!(options)
166 | self
167 | end
168 |
169 | ##
170 | # Own implementation of #clone. Creates new Dataset if
171 | # data stored in datablock and calls super otherwise.
172 | def clone
173 | if @type == :datablock
174 | new_with_options(@options)
175 | else
176 | super
177 | end
178 | end
179 |
180 | ##
181 | # Create new Plot object with only one Dataset given - self.
182 | # Calls #plot on created Plot. All arguments given to this #plot
183 | # will be sent to Plot#plot instead.
184 | # @param args sequence of arguments all of which will be passed to Plot#plot,
185 | # see docs there
186 | # @return [Plot] new Plot object with only one Dataset - self
187 | # @example
188 | # sin = Dataset.new('sin(x)')
189 | # sin.plot(term: [qt, size: [300, 300]])
190 | # #=> shows qt window 300x300 with sin(x)
191 | # sin.to_png('./plot.png')
192 | # #=> creates png file with sin(x) plotted
193 | def plot(*args)
194 | Plot.new(self).plot(*args)
195 | end
196 |
197 | private
198 |
199 | ##
200 | # Create new dataset with existing options merged with
201 | # the given ones. Does nothing if no options given.
202 | #
203 | # @param options [Hash] new options to merge with existing options
204 | # @return [Dataset] self if options empty
205 | # @return [Dataset] new Dataset with updated options otherwise
206 | # @example Updating dataset with Math formula or filename given:
207 | # dataset = Dataset.new('file.data')
208 | # dataset.update_options(title: 'File')
209 | # #=> Dataset.new('file.data', title: 'File')
210 | def update_options(**options)
211 | if options.empty?
212 | self
213 | else
214 | new_with_options(@options.merge(options))
215 | end
216 | end
217 |
218 | ##
219 | # Create string from own options
220 | # @return [String] options converted to Gnuplot format
221 | def options_to_string
222 | options.sort_by { |key, _| OPTION_ORDER.find_index(key.to_s) || 999 }
223 | .map { |key, value| OptionHandling.option_to_string(key, value) }
224 | .join(' ')
225 | end
226 |
227 | ##
228 | # Needed by OptionHandling to create new object when options are changed.
229 | def new_with_options(options)
230 | self.class.new(@data, options)
231 | end
232 |
233 | ##
234 | # Initialize Dataset from given String
235 | def init_string(data, options)
236 | @type, @data = File.exist?(data) ? [:datafile, "'#{data}'"] : [:math_function, data.clone]
237 | @options = Hamster.hash(options)
238 | end
239 |
240 | ##
241 | # Initialize Dataset from given Datablock
242 | def init_dblock(data, options)
243 | @type = :datablock
244 | @data = data.clone
245 | @options = Hamster.hash(options)
246 | end
247 |
248 | ##
249 | # Create new value for 'using' option based on column count
250 | def get_daru_columns(data, cnt)
251 | new_opt = (2..cnt).to_a.join(':')
252 | if data.index[0].is_a?(DateTime) || data.index[0].is_a?(Numeric)
253 | "1:#{new_opt}"
254 | else
255 | "#{new_opt}:xtic(1)"
256 | end
257 | end
258 |
259 | ##
260 | # Initialize Dataset from given Daru::DataFrame
261 | def init_daru_frame(data, options)
262 | options[:title] ||= data.name
263 | if options[:using]
264 | options[:using] = " #{options[:using]} "
265 | data.vectors.to_a.each_with_index do |daru_index, array_index|
266 | options[:using].gsub!(/([\:\(\$ ])#{daru_index}([\:\) ])/) do
267 | "#{Regexp.last_match(1)}#{array_index + 2}#{Regexp.last_match(2)}"
268 | end
269 | end
270 | options[:using].gsub!('index', '1')
271 | options[:using].strip!
272 | else
273 | options[:using] = get_daru_columns(data, data.vectors.size + 1)
274 | end
275 | init_default(data, options)
276 | end
277 |
278 | ##
279 | # Initialize Dataset from given Daru::Vector
280 | def init_daru_vector(data, options)
281 | options[:using] ||= get_daru_columns(data, 2)
282 | options[:title] ||= data.name
283 | init_default(data, options)
284 | end
285 |
286 | ##
287 | # Initialize Dataset from given data with #to_gnuplot_points method
288 | def init_default(data, file: false, **options)
289 | @type = :datablock
290 | @data = Datablock.new(data, file)
291 | @options = Hamster.hash(options)
292 | end
293 | end
294 | end
295 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/staff/settings.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # This module takes care of path to gnuplot executable and checking its version.
4 | module Settings
5 | ##
6 | # GnuplotRB can work with Gnuplot 5.0+
7 | MIN_GNUPLOT_VERSION = 5.0
8 |
9 | class << self
10 | ##
11 | # For heavy calculations max_fit_delay may be increased.
12 | attr_writer :max_fit_delay
13 | ##
14 | # Get max fit delay.
15 | #
16 | # Max fit delay (5s by default) is used inside Fit::fit function.
17 | # If it waits for output more than max_fit_delay seconds
18 | # this behaviour is considered as errorneus.
19 | # @return [Integer] seconds to wait for output
20 | def max_fit_delay
21 | @max_fit_delay ||= 5
22 | end
23 |
24 | ##
25 | # Get path that should be used to run gnuplot executable.
26 | # Default value: 'gnuplot'.
27 | # @return [String] path to gnuplot executable
28 | def gnuplot_path
29 | self.gnuplot_path = 'gnuplot' unless defined?(@gnuplot_path)
30 | @gnuplot_path
31 | end
32 |
33 | ##
34 | # Set path to gnuplot executable.
35 | # @param path [String] path to gnuplot executable
36 | # @return given path
37 | def gnuplot_path=(path)
38 | validate_version(path)
39 | opts = { stdin_data: "set term\n" }
40 | @available_terminals = Open3.capture2e(path, **opts)
41 | .first
42 | .scan(/[:\n] +([a-z][^ ]+)/)
43 | .map(&:first)
44 | @gnuplot_path = path
45 | end
46 |
47 | ##
48 | # Get list of terminals (png, html, qt, jpeg etc) available for this gnuplot.
49 | # @return [Array of String] array of terminals available for this gnuplot
50 | def available_terminals
51 | gnuplot_path
52 | @available_terminals
53 | end
54 |
55 | ##
56 | # Get gnuplot version. Uses gnuplot_path to find gnuplot executable.
57 | # @return [Numeric] gnuplot version
58 | def version
59 | gnuplot_path
60 | @version
61 | end
62 |
63 | ##
64 | # @private
65 | # Validate gnuplot version. Compares current gnuplot's
66 | # version with ::MIN_GNUPLOT_VERSION. Throws exception if version is
67 | # less than min.
68 | #
69 | # @param path [String] path to gnuplot executable
70 | def validate_version(path)
71 | @version = IO.popen("#{path} --version")
72 | .read
73 | .match(/gnuplot ([^ ]+)/)[1]
74 | .to_f
75 | message = "Your Gnuplot version is #{@version}, please update it to at least 5.0"
76 | fail(ArgumentError, message) if @version < MIN_GNUPLOT_VERSION
77 | end
78 | end
79 | end
80 | end
81 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/staff/terminal.rb:
--------------------------------------------------------------------------------
1 | module GnuplotRB
2 | ##
3 | # Terminal keeps open pipe to gnuplot process, cares about naming in-memory
4 | # datablocks (just indexing with sequential integers). All the output
5 | # to gnuplot handled by this class. Terminal also handles options passed
6 | # to gnuplot as 'set key value'.
7 | class Terminal
8 | include ErrorHandling
9 |
10 | # order is important for some options
11 | OPTION_ORDER = [:term, :output, :multiplot, :timefmt, :xrange]
12 |
13 | private_constant :OPTION_ORDER
14 |
15 | class << self
16 | ##
17 | # Close given gnuplot pipe
18 | # @param stream [IO] pipe to close
19 | def close_arg(stream)
20 | stream.puts
21 | stream.puts 'exit'
22 | Process.waitpid(stream.pid)
23 | end
24 |
25 | ##
26 | # Plot test page for given term_name into file
27 | # with file_name (optional).
28 | #
29 | # Test page contains possibilities of the term.
30 | # @param term_name [String] terminal name ('png', 'gif', 'svg' etc)
31 | # @param file_name [String] filename to output image if needed
32 | # and chosen terminal supports image output
33 | # @return nil
34 | def test(term_name, file_name = nil)
35 | Terminal.new.set(term: term_name).test(file_name)
36 | end
37 | end
38 |
39 | ##
40 | # Create new Terminal connected with gnuplot.
41 | # Uses Settings::gnuplot_path to find gnuplot
42 | # executable. Each time you create Terminal it starts new
43 | # gnuplot subprocess which is closed after GC deletes
44 | # linked Terminal object.
45 | #
46 | # @param :persist [Boolean] gnuplot's "-persist" option
47 | def initialize(persist: false)
48 | @cmd = Settings.gnuplot_path
49 | @current_datablock = 0
50 | @cmd += ' -persist' if persist
51 | @cmd += ' 2>&1'
52 | stream = IO.popen(@cmd, 'w+')
53 | handle_stderr(stream)
54 | ObjectSpace.define_finalizer(self, proc { Terminal.close_arg(stream) })
55 | @in = stream
56 | yield(self) if block_given?
57 | end
58 |
59 | ##
60 | # Output datablock to this gnuplot terminal.
61 | #
62 | # @param data [String] data stored in datablock
63 | # @example
64 | # data = "1 1\n2 4\n3 9"
65 | # Terminal.new.store_datablock(data)
66 | # #=> returns '$DATA1'
67 | # #=> outputs to gnuplot:
68 | # #=> $DATA1 << EOD
69 | # #=> 1 1
70 | # #=> 2 4
71 | # #=> 3 9
72 | # #=> EOD
73 | def store_datablock(data)
74 | name = "$DATA#{@current_datablock += 1}"
75 | stream_puts "#{name} << EOD"
76 | stream_puts data
77 | stream_puts 'EOD'
78 | name
79 | end
80 |
81 | ##
82 | # Convert given options to gnuplot format.
83 | #
84 | # For "{ opt1: val1, .. , optN: valN }" it returns
85 | # set opt1 val1
86 | # ..
87 | # set optN valN
88 | #
89 | # @param ptions [Hash] options to convert
90 | # @return [String] options in Gnuplot format
91 | def options_hash_to_string(options)
92 | result = ''
93 | options.sort_by { |key, _| OPTION_ORDER.find_index(key) || -1 }.each do |key, value|
94 | if value
95 | result += "set #{OptionHandling.option_to_string(key, value)}\n"
96 | else
97 | result += "unset #{key}\n"
98 | end
99 | end
100 | result
101 | end
102 |
103 | ##
104 | # Applie given options to current gnuplot instance.
105 | #
106 | # For "{ opt1: val1, .. , optN: valN }" it will output to gnuplot
107 | # set opt1 val1
108 | # ..
109 | # set optN valN
110 | #
111 | # @param options [Hash] options to set
112 | # @return [Terminal] self
113 | # @example
114 | # set({term: ['qt', size: [100, 100]]})
115 | # #=> outputs to gnuplot: "set term qt size 100,100\n"
116 | def set(options)
117 | OptionHandling.validate_terminal_options(options)
118 | stream_puts(options_hash_to_string(options))
119 | end
120 |
121 | ##
122 | # Unset options
123 | #
124 | # @param *options [Sequence of Symbol] each symbol considered as option key
125 | # @return [Terminal] self
126 | def unset(*options)
127 | options.flatten
128 | .sort_by { |key| OPTION_ORDER.find_index(key) || -1 }
129 | .each { |key| stream_puts "unset #{OptionHandling.string_key(key)}" }
130 | self
131 | end
132 |
133 | ##
134 | # Short way to plot Datablock, Plot or Splot object.
135 | # Other items will be just piped out to gnuplot.
136 | # @param item Object that should be outputted to Gnuplot
137 | # @return [Terminal] self
138 | def <<(item)
139 | if item.is_a? Plottable
140 | item.plot(self)
141 | else
142 | stream_print(item.to_s)
143 | end
144 | self
145 | end
146 |
147 | ##
148 | # Just put *command* + "\n" to gnuplot pipe.
149 | # @param command [String] command to send
150 | # @return [Terminal] self
151 | def stream_puts(command)
152 | stream_print("#{command}\n")
153 | end
154 |
155 | ##
156 | # Just print *command* to gnuplot pipe.
157 | # @param command [String] command to send
158 | # @return [Terminal] self
159 | def stream_print(command)
160 | check_errors
161 | @in.print(command)
162 | self
163 | end
164 |
165 | ##
166 | # @deprecated
167 | # Call replot on gnuplot. This will execute last plot once again
168 | # with rereading data.
169 | # @param options [Hash] options will be set before replotting
170 | # @return [Terminal] self
171 | def replot(**options)
172 | set(options)
173 | stream_puts('replot')
174 | unset(options.keys)
175 | sleep 0.01 until File.size?(options[:output]) if options[:output]
176 | self
177 | end
178 |
179 | ##
180 | # Send gnuplot command to turn it off and for its Process to quit.
181 | # Closes pipe so Terminal object should not be used after #close call.
182 | def close
183 | check_errors
184 | Terminal.close_arg(@in)
185 | end
186 |
187 |
188 | ##
189 | # Plot test page into file with file_name (optional).
190 | #
191 | # Test page contains possibilities of the term.
192 | # @param file_name [String] filename to output image if needed
193 | # and chosen terminal supports image output
194 | # @return nil
195 | def test(file_name = nil)
196 | set(output: file_name) if file_name
197 | stream_puts('test')
198 | unset(:output)
199 | nil
200 | end
201 | end
202 | end
203 |
--------------------------------------------------------------------------------
/lib/gnuplotrb/version.rb:
--------------------------------------------------------------------------------
1 | ##
2 | # === Overview
3 | # Ruby bindings for gnuplot.
4 | module GnuplotRB
5 | ##
6 | # Gem version
7 | VERSION = '0.3.1'
8 | end
9 |
--------------------------------------------------------------------------------
/notebooks/.gitignore:
--------------------------------------------------------------------------------
1 | .ipynb_checkpoints
2 |
--------------------------------------------------------------------------------
/notebooks/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 |
3 | gem 'iruby'
4 | # path option is used only for development stage
5 | gem 'gnuplotrb', :path => '../'
6 |
--------------------------------------------------------------------------------
/notebooks/README.rdoc:
--------------------------------------------------------------------------------
1 | == iRuby notebooks
2 |
3 | This notebooks are powered by {Ruby kernel}[https://github.com/SciRuby/iruby/] for {IPython/Jupyter}[https://jupyter.org/].
4 | I placed them here to show some GnuplotRB's capabilities and ways of using it together with iRuby.
5 |
6 | === Installation
7 | To use GnuplotRB gem with iRuby you need to install them both.
8 |
9 | * iRuby installation is covered in its {README}[https://github.com/SciRuby/iruby/blob/master/README.md].
10 | It also covers installation of iPython and other dependecies.
11 | * GnuplotRB gem installation covered in {README}[https://github.com/dilcom/gnuplotrb#installation] too.
12 |
13 | === List of notebooks
14 |
15 | ==== Embedding plots into iRuby
16 | Using GnuplotRB inside iRuby notebooks is covered in:
17 |
18 | * {basic usage notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/basic_usage.ipynb].
19 |
20 | ==== 2D and 3D plots
21 | GnuplotRB is capable to plot vast range of plots from histograms to 3D heatmaps. Gem's repository contains examples of several plot types:
22 |
23 | * {heatmaps}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/heatmaps.ipynb]
24 | * {vector field}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/vector_field.ipynb] (Thanks, {Alexej}[https://github.com/agisga])
25 | * {math equations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/math_plots.ipynb]
26 | * {3D visualizations}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/3d_plot.ipynb]
27 | * {histogram}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/histogram.ipynb]
28 |
29 | ==== Possible datasources
30 | GnuplotRB may take data in Ruby container or in a file. Supported containers for now are Arrays, Daru::Vector and Daru::DataFrame.
31 | When data given in file, GnuplotRB pass filename to Gnuplot *without* reading the file into memory.
32 |
33 | Examples of using different datasources:
34 |
35 | * {data given in file or Ruby Array}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/points_from_different_sources.ipynb]
36 | * {data given in Daru containers}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/plotting_from_daru.ipynb]
37 | * {data given in Daru containers (with timeseries)}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/time_series_from_daru.ipynb]
38 | * {updating plots with new data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/updating_data.ipynb]
39 |
40 | ==== Multiplot
41 | You can not only plot several datasets in single coordinate system but place several coordinate systems on a canvas.
42 |
43 | * {Multiplot example notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/multiplot_layout.ipynb].
44 |
45 | ==== Animation
46 | It's possible to use several plots (Plot, Splot or Multiplot objects) to create gif animation.
47 |
48 | * {Animation example notebook}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/animated_plots.ipynb].
49 |
50 | ==== Fitting data with formula
51 | GnuplotRB also may be used to fit some data with given math formula.
52 |
53 | * {Fitting data}[http://nbviewer.ipython.org/github/dilcom/gnuplotrb/blob/master/notebooks/fitting_data.ipynb]
54 |
--------------------------------------------------------------------------------
/spec/animation_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe Animation do
4 | before(:all) do
5 | @tmp_dir = File.join('spec', 'tmp')
6 | Dir.mkdir(@tmp_dir)
7 | @datafile_path = File.join('spec', 'points.data')
8 | end
9 |
10 | after(:all) do
11 | FileUtils.rm_r(@tmp_dir)
12 | end
13 |
14 | context 'creation' do
15 | before do
16 | @title = 'Animation spec'
17 | @formula = %w(sin(x) cos(x) exp(-x))
18 | @plots = @formula.map { |f| Plot.new(f) }
19 | @plots << Splot.new('sin(x) * cos(y)')
20 | @options = { title: @title, layout: [2, 2] }
21 | end
22 |
23 | it 'should be created out of sequence of plots' do
24 | expect(Animation.new(*@plots)).to be_an_instance_of(Animation)
25 | end
26 |
27 | it 'should set options passed to constructor' do
28 | anim = Animation.new(*@plots, **@options)
29 | expect(anim).to be_an_instance_of(Animation)
30 | expect(anim.title).to eql(@title)
31 | end
32 | end
33 |
34 | context 'option handling' do
35 | before do
36 | @options = Hamster.hash(title: 'GnuplotRB::Animation', yrange: 0..3)
37 | @anim = Animation.new(**@options)
38 | end
39 |
40 | it 'should allow to get option value by name' do
41 | expect(@anim.title).to eql(@options[:title])
42 | end
43 |
44 | it 'should know which options are Animation specific' do
45 | right = [:size, :tiny, :animate, :background]
46 | wrong = [:layout, :title, :xrange, :term]
47 | right.each { |opt| expect(@anim.send(:specific_option?, opt)).to be_truthy }
48 | wrong.each { |opt| expect(@anim.send(:specific_option?, opt)).to be_falsey }
49 | end
50 |
51 | it 'should return Animation object' do
52 | new_options = { title: 'Another title', xrange: 1..5 }
53 | new_plot = @anim.options(new_options)
54 | expect(new_plot).to_not equal(@anim)
55 | expect(new_plot).to be_an_instance_of(Animation)
56 | end
57 | end
58 |
59 | context 'handling plots as container' do
60 | before :each do
61 | @sinx = Plot.new('sin(x)')
62 | @plot3d = Splot.new('sin(x)*cos(y)')
63 | @exp = Plot.new('exp(x)')
64 | @paths = (0..2).map { |i| File.join(@tmp_dir, "#{i}plot.gif") }
65 | @anim = Animation.new(@sinx, @plot3d, @exp)
66 | end
67 |
68 | it 'should allow to remove plot from animation' do
69 | Animation.new(@sinx, @exp).plot(@paths[0])
70 | @anim.remove_plot(1).plot(@paths[1])
71 | @anim.remove_frame(1).plot(@paths[2])
72 | expect(same_files?(*@paths)).to be_truthy
73 | end
74 |
75 | it 'should allow to add plot to animation' do
76 | Animation.new(@plot3d, @sinx, @plot3d, @exp).plot(@paths[0])
77 | @anim.add_plot(@plot3d).plot(@paths[1])
78 | @anim.add_frame(@plot3d).plot(@paths[2])
79 | expect(same_files?(*@paths)).to be_truthy
80 | end
81 |
82 | it 'should allow to replace plot in animation' do
83 | Animation.new(@plot3d, @exp).plot(@paths[0])
84 | @anim.replace_plot(@plot3d).plot(@paths[1])
85 | @anim.replace_frame(@plot3d).plot(@paths[2])
86 | expect(same_files?(*@paths)).to be_truthy
87 | end
88 |
89 | it 'should allow to update options of any plot in animation' do
90 | ttl = "Wow, it's sin(x)!"
91 | Animation.new(@sinx.title(ttl), @plot3d, @exp).plot(@paths[0])
92 | @anim.update_plot(0, title: ttl).plot(@paths[1])
93 | @anim.update_frame(0, title: ttl).plot(@paths[2])
94 | expect(same_files?(*@paths)).to be_truthy
95 | end
96 |
97 | it 'should allow to update datasets of any plot in animation' do
98 | ds_ttl = 'Some dataset'
99 | ttl = "Wow, it's sin(x)!"
100 | Animation.new(@sinx.update_dataset(title: ds_ttl).title(ttl), @plot3d, @exp).plot(@paths[0])
101 | @anim.update_plot(0, title: ttl) do |new_plot|
102 | new_plot.update_dataset(title: ds_ttl)
103 | end.plot(@paths[1])
104 | @anim.update_frame(0, title: ttl) do |new_plot|
105 | new_plot.update_dataset(title: ds_ttl)
106 | end.plot(@paths[2])
107 | expect(same_files?(*@paths)).to be_truthy
108 | end
109 |
110 | it 'should allow to get plots using []' do
111 | (0..2).each { |i| expect(@anim[i]).to be_equal(@anim.plots[i]) }
112 | expect(@anim[0..-1]).to be_eql(@anim.plots)
113 | (0..2).each { |i| expect(@anim[i]).to be_equal(@anim.frames[i]) }
114 | expect(@anim[0..-1]).to be_eql(@anim.frames)
115 | end
116 | end
117 |
118 | it 'should not use terminal other than gif' do
119 | anim = Animation.new(Plot.new('sin(x)'))
120 | terminals = %w(png dumb svg)
121 | terminals.each { |term| expect { anim.send("to_#{term}".to_sym) }.to raise_error(RuntimeError) }
122 | end
123 |
124 | it 'should output html to iruby' do
125 | anim = Animation.new(Plot.new('sin(x)'))
126 | expect(anim.to_iruby[0]).to eql('text/html')
127 | end
128 | end
129 |
--------------------------------------------------------------------------------
/spec/dataset_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe Dataset do
4 | before(:all) do
5 | @tmp_dir = File.join('spec', 'tmp')
6 | Dir.mkdir(@tmp_dir)
7 | @datafile_path = File.join('spec', 'points.data')
8 | end
9 |
10 | after(:all) do
11 | FileUtils.rm_r(@tmp_dir)
12 | end
13 |
14 | context 'creation' do
15 | before do
16 | x = (0..10).to_a
17 | y = x.map { |xx| Math.exp(-xx) }
18 | @data = [x, y]
19 | end
20 |
21 | it 'may be created with math function as data' do
22 | dataset = Dataset.new('sin(x)')
23 | expect(dataset.to_s).to eql('sin(x) ')
24 | end
25 |
26 | it 'may be created with datafile as data' do
27 | dataset = Dataset.new('spec/points.data')
28 | expect(dataset.to_s).to eql("'spec/points.data' ")
29 | end
30 |
31 | it 'may be created with some class with #to_points' do
32 | dataset = Dataset.new(@data)
33 | expect(dataset.data).to be_an_instance_of(Datablock)
34 | expect(dataset.to_s(Terminal.new)).to eql('$DATA1 ')
35 | end
36 |
37 | it 'may be created with clone of existing datablock as data' do
38 | datablock = Datablock.new(@data)
39 | dataset = Dataset.new(datablock)
40 | expect(dataset.data).to be_an_instance_of(Datablock)
41 | expect(datablock).to_not equal(dataset.data) # given datablock should be cloned
42 | end
43 |
44 | it 'may be created with existing stored in file datablock' do
45 | datablock = Datablock.new(@data, true)
46 | dataset = Dataset.new(datablock)
47 | expect(dataset.data).to be_an_instance_of(Datablock)
48 | # since given datablock's data stored in file, it should not be cloned
49 | expect(datablock).to equal(dataset.data)
50 | expect(dataset.to_s(Terminal.new)).to eql("#{datablock.name} ")
51 | end
52 |
53 | it 'may be created with given gnuplot options' do
54 | dataset = Dataset.new(@data, title: 'Dataset title', with: 'linespoints')
55 | expect(dataset.to_s(Terminal.new)).to eql("$DATA1 title \"Dataset title\" with linespoints")
56 | end
57 |
58 | it 'may be created with special :file option' do
59 | # {:file => true} will force creation of stored in file datablock
60 | dataset = Dataset.new(@data, title: 'Dataset title', file: true)
61 | expect(dataset.data.name).to match(/tmp_data/)
62 | expect(dataset.to_s).to eql("#{dataset.data.name} title \"Dataset title\"")
63 | end
64 | end
65 |
66 | context 'creation with Daru' do
67 | before do
68 | @xtic = %w(1991 1993 1995 1997)
69 | @y = [2453, 2343, 2454, 2254]
70 | @yerr = [120, 133, 123, 113]
71 | @title = :plot_from_daru
72 | @data = (0..3).map { |i| "\"#{@xtic[i]}\" #{@y[i]} #{@yerr[i]}\n" }.join
73 | @df = Daru::DataFrame.new([@y, @yerr], order: [:y, :yerr], index: @xtic, name: @title)
74 | @alt_title = 'Some other given title'
75 | @vector = Daru::Vector.new(@y, index: @xtic, name: @title)
76 | end
77 |
78 | it 'may be created with Daru::Vector given' do
79 | ds = Dataset.new(@vector)
80 | expect(ds.title).to eql(@title)
81 | expect(ds.using).to eql('2:xtic(1)')
82 | data = (0..3).map { |i| "\"#{@xtic[i]}\" #{@y[i]}\n" }.join
83 | expect(ds.data.instance_variable_get(:@data)).to eql(data)
84 | end
85 |
86 | it 'may be created with Daru::DataFrame given' do
87 | ds = Dataset.new(@df)
88 | expect(ds.title).to eql(@title)
89 | expect(ds.using).to eql('2:3:xtic(1)')
90 | expect(ds.data.instance_variable_get(:@data)).to eql(@data)
91 | end
92 |
93 | it "should use given title instead of Daru's" do
94 | expect(Dataset.new(@vector, title: @alt_title).title).to eql(@alt_title)
95 | expect(Dataset.new(@df, title: @alt_title).title).to eql(@alt_title)
96 | end
97 |
98 | it 'may be created with Daru::DataFrame and *using* option given' do
99 | ds = Dataset.new(@df, using: 'index:yerr:xtic(y)')
100 | expect(ds.using).to eql('1:3:xtic(2)')
101 | expect(ds.data.instance_variable_get(:@data)).to eql(@data)
102 | end
103 | end
104 |
105 | context 'options handling' do
106 | before do
107 | @options = Hamster.hash(title: 'GnuplotRB::Dataset', with: 'lines')
108 | @dataset = Dataset.new('sin(x)', @options)
109 | end
110 |
111 | it 'should allow to get option value by name' do
112 | @options.each { |key, value| expect(@dataset.send(key)).to eql(value) }
113 | end
114 |
115 | it 'should allow to safely set option value by name' do
116 | new_with = 'points'
117 | new_dataset = @dataset.with(new_with)
118 | expect(@dataset.with).to equal(@options[:with])
119 | expect(new_dataset.with).to equal(new_with)
120 | end
121 |
122 | it 'should allow to get all the options' do
123 | expect(@dataset.options).to eql(@options)
124 | end
125 |
126 | it 'should allow to safely set several options at once' do
127 | new_options = Hamster.hash(title: 'Some new title', with: 'lines', lt: 3)
128 | new_dataset = @dataset.options(new_options)
129 | @options.each { |key, value| expect(@dataset.send(key)).to eql(value) }
130 | new_options.each { |key, value| expect(new_dataset.send(key)).to eql(value) }
131 | end
132 | end
133 |
134 | context 'safe update' do
135 | before do
136 | x = (0..10).to_a
137 | y = x.map { |xx| Math.exp(-xx) }
138 | @data = [x, y]
139 | @options = Hamster.hash(title: 'GnuplotRB::Dataset', with: 'lines')
140 | @sinx = Dataset.new('sin(x)', @options)
141 | @dataset = Dataset.new([x, y])
142 | @temp_file_dataset = Dataset.new([x, y], file: true)
143 | end
144 |
145 | it 'should update options' do
146 | # works just as Dataset#options(...)
147 | new_options = Hamster.hash(title: 'Some new title', with: 'lines', lt: 3)
148 | new_dataset = @sinx.update(new_options)
149 | @options.each { |key, value| expect(@sinx.send(key)).to eql(value) }
150 | new_options.each { |key, value| expect(new_dataset.send(key)).to eql(value) }
151 | end
152 |
153 | it 'should do nothing if no options given and no data update needed' do
154 | expect(@dataset.update).to equal(@dataset)
155 | expect(@sinx.update('some new data')).to equal(@sinx)
156 | end
157 |
158 | it 'should update if options are given and no data update needed' do
159 | updated = @dataset.update(@options)
160 | expect(updated).to_not equal(@dataset)
161 | @options.each { |key, value| expect(updated.send(key)).to eql(value) }
162 | end
163 |
164 | it 'should update datablock stored in here-doc' do
165 | updated = @dataset.update(@data)
166 | expect(updated.data).to_not equal(@dataset.data)
167 | end
168 |
169 | it 'should update datablock stored in temp file' do
170 | filename = @temp_file_dataset.data.name[1..-2] # avoid '' on ends
171 | size_before_update = File.size(filename)
172 | updated = @temp_file_dataset.update(@data)
173 | size_after_update = File.size(filename)
174 | expect(updated.data).to equal(@temp_file_dataset.data)
175 | expect(size_after_update).to be > size_before_update
176 | end
177 | end
178 |
179 | context 'destructive update' do
180 | before :each do
181 | x = (0..10).to_a
182 | y = x.map { |xx| Math.exp(-xx) }
183 | @data = [x, y]
184 | @ds = Dataset.new(@data)
185 | @ds_file = Dataset.new(@data, file: true)
186 | end
187 |
188 | it 'should update an option of existing object' do
189 | expect(@ds.lw!(3)).to equal(@ds)
190 | expect(@ds.lw).to eql(3)
191 | @ds.pt = 8
192 | expect(@ds.pt).to eql(8)
193 | end
194 |
195 | it 'should update several options of existing object at once via #options!' do
196 | expect(@ds.options!(lw: 3, pt: 8)).to equal(@ds)
197 | expect(@ds.lw).to eql(3)
198 | expect(@ds.pt).to eql(8)
199 | end
200 |
201 | it 'should update several options of existing object at once via #update!' do
202 | expect(@ds.update!(lw: 3, pt: 8)).to equal(@ds)
203 | expect(@ds.lw).to eql(3)
204 | expect(@ds.pt).to eql(8)
205 | end
206 |
207 | it 'should update data of existing in-memory datablock at once' do
208 | paths = (0..1).map { |i| File.join(@tmp_dir, "#{i}plot.png") }
209 | options0 = { term: ['png', size: [300, 300]], output: paths[0] }
210 | options1 = { term: ['png', size: [300, 300]], output: paths[1] }
211 | x1 = (11..15).to_a
212 | y1 = x1.map { |xx| Math.exp(-xx) }
213 | @ds.plot(options0)
214 | expect(@ds.update!([x1, y1])).to equal(@ds)
215 | @ds.plot(options1)
216 | expect(same_images?(*paths)).to be_falsey
217 | end
218 |
219 | it 'should update data of existing in-file datablock at once' do
220 | x1 = (11..15).to_a
221 | y1 = x1.map { |xx| Math.exp(-xx) }
222 | size_before = File.size(@ds_file.data.to_s[1..-2])
223 | expect(@ds_file.update!([x1, y1])).to equal(@ds_file)
224 | size_after = File.size(@ds_file.data.to_s[1..-2])
225 | expect(size_after).to be > size_before
226 | end
227 | end
228 | end
229 |
--------------------------------------------------------------------------------
/spec/fit_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe GnuplotRB::Fit do
4 | before(:all) do
5 | @tmp_dir = File.join('spec', 'tmp')
6 | Dir.mkdir(@tmp_dir)
7 | @datafile_path = File.join('spec', 'points.data')
8 | x = (1..100).map { |xx| xx / 10.0 }
9 | y = x.map { |xx| 3.0 * Math.exp(xx / 3) }
10 | @data = [x, y]
11 | end
12 |
13 | after(:all) do
14 | FileUtils.rm_r(@tmp_dir)
15 | end
16 |
17 | context 'input' do
18 | it 'should take Dataset as input' do
19 | ds = Dataset.new(@data)
20 | expect(fit(ds)).to be_instance_of(Hash)
21 | end
22 |
23 | it 'should take Datablock as input' do
24 | db = Datablock.new(@data)
25 | expect(fit(db)).to be_instance_of(Hash)
26 | db_in_file = Datablock.new(@data, true)
27 | expect(fit(db_in_file)).to be_instance_of(Hash)
28 | end
29 |
30 | it 'should take something out of which Dataset may be constructed as input' do
31 | expect(fit(@data)).to be_instance_of(Hash)
32 | expect(fit(@datafile_path)).to be_instance_of(Hash)
33 | end
34 | end
35 |
36 | context 'fitting data' do
37 | it 'should throw error in case of wrong input' do
38 | expect { fit(@data, formula: 'wrong_formula') }.to raise_error(GnuplotError)
39 | end
40 | end
41 | end
42 |
--------------------------------------------------------------------------------
/spec/gnuplot_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe GnuplotRB do
4 | it 'should be awesome' do
5 | expect(awesome?).to be_truthy
6 | end
7 |
8 | it 'should know its version' do
9 | expect(Settings.version).to be_a(Numeric)
10 | end
11 |
12 | context 'check examples' do
13 | samples = Dir.glob('./examples/*plot*')
14 | samples.each do |path|
15 | name = path[11..-1]
16 | it "should work with #{name} example" do
17 | expect(run_example_at path).to be_truthy
18 | end
19 | end
20 | end
21 | end
22 |
--------------------------------------------------------------------------------
/spec/multiplot_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe Multiplot do
4 | before(:all) do
5 | @tmp_dir = File.join('spec', 'tmp')
6 | Dir.mkdir(@tmp_dir)
7 | @datafile_path = File.join('spec', 'points.data')
8 | end
9 |
10 | after(:all) do
11 | FileUtils.rm_r(@tmp_dir)
12 | end
13 |
14 | context 'creation' do
15 | before do
16 | @title = 'Multiplot spec'
17 | @formula = %w(sin(x) cos(x) exp(-x))
18 | @plots = @formula.map { |f| Plot.new(f) }
19 | @plots << Splot.new('sin(x) * cos(y)')
20 | @options = { title: @title, layout: [2, 2] }
21 | end
22 |
23 | it 'should be created out of sequence of plots' do
24 | expect(Multiplot.new(*@plots)).to be_an_instance_of(Multiplot)
25 | end
26 |
27 | it 'should set options passed to constructor' do
28 | mp = Multiplot.new(*@plots, **@options)
29 | expect(mp).to be_an_instance_of(Multiplot)
30 | expect(mp.title).to eql(@title)
31 | end
32 | end
33 |
34 | context 'option handling' do
35 | before do
36 | @options = Hamster.hash(title: 'GnuplotRB::Multiplot', yrange: 0..3)
37 | @mp = Multiplot.new(**@options)
38 | end
39 |
40 | it 'should allow to get option value by name' do
41 | expect(@mp.title).to eql(@options[:title])
42 | end
43 |
44 | it 'should know which options are Multiplot special' do
45 | expect(@mp.send(:specific_option?, :layout)).to be_truthy
46 | expect(@mp.send(:specific_option?, :title)).to be_truthy
47 | expect(@mp.send(:specific_option?, :xrange)).to be_falsey
48 | end
49 |
50 | it 'should return Multiplot object' do
51 | new_options = { title: 'Another title', xrange: 1..5 }
52 | new_plot = @mp.options(new_options)
53 | expect(new_plot).to_not equal(@mp)
54 | expect(new_plot).to be_an_instance_of(Multiplot)
55 | end
56 | end
57 |
58 | context 'safe plot array update' do
59 | before :each do
60 | @sinx = Plot.new('sin(x)')
61 | @plot3d = Splot.new('sin(x)*cos(y)')
62 | @exp = Plot.new('exp(x)')
63 | @paths = (0..1).map { |i| File.join(@tmp_dir, "#{i}plot.png") }
64 | @options0 = { term: ['png', size: [300, 300]], output: @paths[0] }
65 | @options1 = { term: ['png', size: [300, 300]], output: @paths[1] }
66 | end
67 |
68 | it 'should allow to remove plot from Mp' do
69 | mp = Multiplot.new(@sinx, @plot3d, @exp)
70 | updated_mp = mp.remove_plot(1)
71 | expect(mp).to_not equal(updated_mp)
72 | updated_mp.plot(@options0)
73 | Multiplot.new(@sinx, @exp).plot(@options1)
74 | expect(same_images?(*@paths)).to be_truthy
75 | end
76 |
77 | it 'should allow to add plot to Mp' do
78 | mp = Multiplot.new(@sinx, @exp)
79 | updated_mp = mp.add_plot(@plot3d)
80 | expect(mp).to_not equal(updated_mp)
81 | updated_mp.plot(@options0)
82 | Multiplot.new(@plot3d, @sinx, @exp).plot(@options1)
83 | expect(same_images?(*@paths)).to be_truthy
84 | end
85 |
86 | it 'should allow to replace plot in Mp' do
87 | mp = Multiplot.new(@sinx, @exp)
88 | updated_mp = mp.replace_plot(@plot3d)
89 | expect(mp).to_not equal(updated_mp)
90 | updated_mp.plot(@options0)
91 | Multiplot.new(@plot3d, @exp).plot(@options1)
92 | expect(same_images?(*@paths)).to be_truthy
93 | end
94 |
95 | it 'should allow to update options of any plot in mp' do
96 | ttl = "Wow, it's sin(x)!"
97 | mp = Multiplot.new(@sinx, @exp)
98 | updated_mp = mp.update_plot(0, title: ttl)
99 | expect(mp).to_not equal(updated_mp)
100 | updated_mp.plot(@options0)
101 | Multiplot.new(@sinx.title(ttl), @exp).plot(@options1)
102 | expect(same_images?(*@paths)).to be_truthy
103 | end
104 |
105 | it 'should allow to update datasets of any plot in mp' do
106 | ds_ttl = 'Some dataset'
107 | ttl = "Wow, it's sin(x)!"
108 | mp = Multiplot.new(@sinx, @exp)
109 | updated_mp = mp.update_plot(0, title: ttl) do |new_plot|
110 | new_plot.update_dataset(title: ds_ttl)
111 | end
112 | expect(mp).to_not equal(updated_mp)
113 | updated_mp.plot(@options0)
114 | udpdated_sinx = @sinx.title(ttl).update_dataset(title: ds_ttl)
115 | Multiplot.new(udpdated_sinx, @exp).plot(@options1)
116 | expect(same_images?(*@paths)).to be_truthy
117 | end
118 |
119 | it 'should allow to get plots using []' do
120 | mp = Multiplot.new(@sinx, @exp, @plot3d)
121 | (0..2).each { |i| expect(mp[i]).to be_equal(mp.plots[i]) }
122 | expect(mp[0..-1]).to be_eql(mp.plots)
123 | end
124 | end
125 |
126 | context 'destructive plot array update' do
127 | before :each do
128 | plots = [
129 | Plot.new('sin(x)'),
130 | Splot.new('sin(x)*cos(y)'),
131 | Plot.new('exp(x)')
132 | ]
133 | @mp = Multiplot.new(*plots, layout: [1, 3])
134 | end
135 |
136 | it 'should update plots in the existing Plot' do
137 | @mp.update!(0) do |plot|
138 | plot.options!(title: 'Updated plot')
139 | plot.replace_dataset!('exp(x)')
140 | plot[0].lw!(3)
141 | end
142 | expect(@mp[0].title).to eql('Updated plot')
143 | expect(@mp[0][0].data).to eql('exp(x)')
144 | expect(@mp[0][0].lw).to eql(3)
145 | end
146 |
147 | it 'should replace plot in the existing Multiplot' do
148 | plot = Plot.new('cos(x)', title: 'Second plot')
149 | expect(@mp.replace!(1, plot)).to equal(@mp)
150 | expect(@mp[1].title).to eql('Second plot')
151 | @mp[0] = Plot.new('cos(x)', title: 'First plot')
152 | expect(@mp[0].title).to eql('First plot')
153 | end
154 |
155 | it 'should add plots to the existing Multiplot' do
156 | plot = Plot.new('cos(x)', title: 'First plot')
157 | expect(@mp.add!(plot)).to equal(@mp)
158 | expect(@mp[0].title).to eql('First plot')
159 | expect(@mp.plots.size).to eql(4)
160 | end
161 |
162 | it 'should remove plot from the existing Multiplot' do
163 | plot = Plot.new('cos(x)', title: 'Last plot')
164 | expect(@mp.add!(-1, plot)).to equal(@mp)
165 | expect(@mp.plots.size).to eql(4)
166 | expect(@mp.remove!).to equal(@mp)
167 | expect(@mp.plots.size).to eql(3)
168 | expect(@mp.plots.last.title).to eql('Last plot')
169 | end
170 | end
171 | end
172 |
--------------------------------------------------------------------------------
/spec/plot_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe Plot do
4 | before(:all) do
5 | @tmp_dir = File.join('spec', 'tmp')
6 | Dir.mkdir(@tmp_dir)
7 | @datafile_path = File.join('spec', 'points.data')
8 | end
9 |
10 | after(:all) do
11 | FileUtils.rm_r(@tmp_dir)
12 | end
13 |
14 | context 'creation' do
15 | before do
16 | @title = 'Awesome spec'
17 | @formula = %w(sin(x) cos(x) exp(-x))
18 | @options = { title: @title, term: 'dumb' }
19 | @df = Daru::DataFrame.new(
20 | Build: [312, 630, 315, 312],
21 | Test: [525, 1050, 701, 514],
22 | Deploy: [215, 441, 370, 220]
23 | )
24 | end
25 |
26 | it 'should be created out of sequence of datasets' do
27 | datasets = @formula.map { |formula| Dataset.new(formula) }
28 | expect(Plot.new(*datasets)).to be_an_instance_of(Plot)
29 | end
30 |
31 | it 'should be created out of sequence of arrays' do
32 | expect(Plot.new(*@formula)).to be_an_instance_of(Plot)
33 | end
34 |
35 | it 'should set options passed to constructor' do
36 | plot = Plot.new(*@formula, **@options)
37 | expect(plot).to be_an_instance_of(Plot)
38 | expect(plot.title).to eql(@title)
39 | end
40 |
41 | it 'should be created out of Daru::DataFrame' do
42 | p = Plot.new(@df)
43 | expect(p).to be_an_instance_of(Plot)
44 | expect(p.datasets.size).to be_eql(3)
45 | end
46 | end
47 |
48 | context 'options handling' do
49 | before do
50 | @options = Hamster.hash(title: 'GnuplotRB::Plot', yrange: 0..3)
51 | @plot = Plot.new(**@options)
52 | end
53 |
54 | it 'should allow to get option value by name' do
55 | expect(@plot.title).to eql(@options[:title])
56 | end
57 |
58 | it 'should allow to safely set option value by name' do
59 | another_title = 'Some new titile'
60 | new_plot = @plot.title(another_title)
61 | expect(@plot).not_to equal(new_plot)
62 | expect(new_plot.title).to eql(another_title)
63 | expect(@plot.title).to eql(@options[:title])
64 | end
65 |
66 | it 'should allow to get terminal' do
67 | expect(@plot.own_terminal).to be_an_instance_of(Terminal)
68 | end
69 |
70 | it 'should allow to get all the options' do
71 | expect(@plot.options).to eql(@options)
72 | end
73 |
74 | it 'should allow to safely set several options at once' do
75 | new_options = { title: 'Another title', xrange: 1..5 }
76 | new_plot = @plot.options(new_options)
77 | expect(new_plot).to_not equal(@plot)
78 | expect(new_plot).to be_an_instance_of(Plot)
79 | expect(new_plot.options).to eql(@options.merge(new_options))
80 | end
81 | end
82 |
83 | context 'safe datasets update' do
84 | before do
85 | @plot_math = Plot.new(['sin(x)', title: 'Just a sin'])
86 | @dataset = Dataset.new('exp(-x)')
87 | @plot_two_ds = Plot.new(['cos(x)'], ['x*x'])
88 | @options = { title: 'Example dataset' }
89 | @plot_datafile = Plot.new([@datafile_path])
90 | @data = [1, 2, 3, 4]
91 | @plot_data_inmemory = Plot.new([@data])
92 | @plot_data_tempfile = Plot.new([@data, file: true])
93 | end
94 |
95 | it 'should create new Plot when user adds a dataset' do
96 | new_plot = @plot_math.add_dataset(@dataset)
97 | expect(new_plot).to_not be_equal(@plot_math)
98 | end
99 |
100 | it 'should create new Plot when user adds a dataset using #<<' do
101 | new_plot = @plot_math << @dataset
102 | expect(new_plot).to_not be_equal(@plot_math)
103 | end
104 |
105 | it 'should create new Plot when user removes a dataset' do
106 | new_plot = @plot_two_ds.remove_dataset
107 | expect(new_plot).to_not be_equal(@plot_two_ds)
108 | end
109 |
110 | it 'should remove dataset exactly at given position' do
111 | (0..1).each do |i|
112 | j = i == 0 ? 1 : 0
113 | new_plot = @plot_two_ds.remove_dataset(i)
114 | expect(new_plot.datasets[0].data).to be_eql(@plot_two_ds.datasets[j].data)
115 | end
116 | end
117 |
118 | it 'should create new Plot when user replaces a dataset' do
119 | new_plot = @plot_two_ds.replace_dataset(@dataset)
120 | expect(new_plot).to_not be_equal(@plot_two_ds)
121 | end
122 |
123 | it 'should remplace dataset exactly at given position' do
124 | (0..1).each do |i|
125 | new_plot = @plot_two_ds.replace_dataset(i, @dataset)
126 | expect(new_plot.datasets[i].data).to be_eql(@dataset.data)
127 | end
128 | end
129 |
130 | it 'should allow to update dataset at given position with options' do
131 | (0..1).each do |i|
132 | new_plot = @plot_two_ds.update_dataset(i, @options)
133 | expect(new_plot.datasets[i].options.to_h).to be_eql(@options)
134 | expect(new_plot.datasets[i]).to_not equal(@plot_two_ds.datasets[i])
135 | end
136 | end
137 |
138 | it 'should not update Plot if neither data nor options update needed' do
139 | # data and options are empty so no update needed
140 | expect(@plot_math.update_dataset).to be_equal(@plot_math)
141 | # dataset with math formula could not to be updated
142 | expect(@plot_math.update_dataset(data: @data)).to be_equal(@plot_math)
143 | # dataset with data from existing file could not to be updated
144 | expect(@plot_datafile.update_dataset(data: @data)).to be_equal(@plot_datafile)
145 | end
146 |
147 | it 'should create new Plot (and new datablock) if you update data stored in memory' do
148 | current = File.join(@tmp_dir, 'plot.png')
149 | updated = File.join(@tmp_dir, 'updated_plot.png')
150 | new_plot = @plot_data_inmemory.update_dataset(data: @data)
151 | expect(new_plot).to_not be_equal(@plot_data_inmemory)
152 | @plot_data_inmemory.to_png(current, size: [200, 200])
153 | new_plot.to_png(updated, size: [200, 200])
154 | expect(same_images?(current, updated)).to be_falsy
155 | end
156 |
157 | it 'should not create new Plot (and new datablock) if you update data stored in temp file' do
158 | old = File.join(@tmp_dir, 'old_plot.png')
159 | current = File.join(@tmp_dir, 'plot.png')
160 | updated = File.join(@tmp_dir, 'updated_plot.png')
161 | @plot_data_tempfile.to_png(old, size: [200, 200])
162 | new_plot = @plot_data_tempfile.update_dataset(data: @data)
163 | expect(new_plot).to be_equal(@plot_data_tempfile)
164 | @plot_data_tempfile.to_png(current, size: [200, 200])
165 | new_plot.to_png(updated, size: [200, 200])
166 | expect(same_images?(current, updated)).to be_truthy
167 | expect(same_images?(current, old)).to be_falsy
168 | end
169 |
170 | it 'should allow to get datasets using []' do
171 | (0..1).each { |i| expect(@plot_two_ds[i]).to be_equal(@plot_two_ds.datasets[i]) }
172 | expect(@plot_two_ds[0..-1]).to be_eql(@plot_two_ds.datasets)
173 | end
174 | end
175 |
176 | context 'destructive datasets update' do
177 | before :each do
178 | @plot = Plot.new('sin(x)')
179 | end
180 |
181 | it 'should update datasets in the existing Plot' do
182 | expect(@plot.update_dataset!(lw: 3)).to equal(@plot)
183 | expect(@plot.datasets[0].lw).to eql(3)
184 | end
185 |
186 | it 'should replace dataset in the existing Plot' do
187 | expect(@plot.replace_dataset!('exp(x)')).to equal(@plot)
188 | expect(@plot.datasets[0].data).to eql('exp(x)')
189 | @plot[0] = 'cos(x)'
190 | expect(@plot.datasets[0].data).to eql('cos(x)')
191 | end
192 |
193 | it 'should add datasets to the existing Plot' do
194 | expect(@plot.add_dataset!('exp(x)')).to equal(@plot)
195 | expect(@plot.datasets[0].data).to eql('exp(x)')
196 | expect(@plot.datasets[1].data).to eql('sin(x)')
197 | end
198 |
199 | it 'should remove dataset from the existing Plot' do
200 | @plot.add_dataset!(1, 'exp(x)', 'cos(x)')
201 | expect(@plot.datasets.size).to eql(3)
202 | expect(@plot.remove_dataset!).to equal(@plot)
203 | expect(@plot.datasets.size).to eql(2)
204 | expect(@plot.datasets[0].data).to eql('sin(x)')
205 | expect(@plot.datasets[1].data).to eql('exp(x)')
206 | expect(@plot.datasets[2]).to be nil
207 | end
208 | end
209 |
210 | context '#to_iruby' do
211 | it 'should handle output to iRuby' do
212 | available_terminals = {
213 | 'png' => 'image/png',
214 | 'pngcairo' => 'image/png',
215 | 'jpeg' => 'image/jpeg',
216 | 'svg' => 'image/svg+xml',
217 | 'dumb' => 'text/plain'
218 | }
219 | available_terminals.each do |term, type|
220 | if OptionHandling::valid_terminal?(term)
221 | expect(Plot.new('sin(x)', term: term).to_iruby[0]).to eql(type)
222 | end
223 | end
224 | end
225 |
226 | it 'should use svg as default iruby terminal' do
227 | expect(Plot.new('sin(x)').to_iruby[0]).to eql('image/svg+xml')
228 | end
229 | end
230 | end
231 |
--------------------------------------------------------------------------------
/spec/points.data:
--------------------------------------------------------------------------------
1 | 0.0 1.0
2 | 0.1 0.9048374180359595
3 | 0.2 0.8187307530779818
4 | 0.3 0.7408182206817179
5 | 0.4 0.6703200460356393
6 | 0.5 0.6065306597126334
7 | 0.6 0.5488116360940264
8 | 0.7 0.4965853037914095
9 | 0.8 0.44932896411722156
10 | 0.9 0.4065696597405991
11 | 1.0 0.36787944117144233
12 | 1.1 0.33287108369807955
13 | 1.2 0.30119421191220214
14 | 1.3 0.2725317930340126
15 | 1.4 0.2465969639416065
16 | 1.5 0.22313016014842982
17 | 1.6 0.20189651799465538
18 | 1.7 0.18268352405273466
19 | 1.8 0.16529888822158653
20 | 1.9 0.14956861922263506
21 | 2.0 0.1353352832366127
22 | 2.1 0.1224564282529819
23 | 2.2 0.11080315836233387
24 | 2.3 0.10025884372280375
25 | 2.4 0.09071795328941251
26 | 2.5 0.0820849986238988
27 | 2.6 0.07427357821433388
28 | 2.7 0.06720551273974976
29 | 2.8 0.06081006262521797
30 | 2.9 0.05502322005640723
31 | 3.0 0.049787068367863944
32 | 3.1 0.0450492023935578
33 | 3.2 0.04076220397836621
34 | 3.3 0.036883167401240015
35 | 3.4 0.03337326996032608
36 | 3.5 0.0301973834223185
37 | 3.6 0.02732372244729256
38 | 3.7 0.024723526470339388
39 | 3.8 0.0223707718561656
40 | 3.9 0.02024191144580439
41 | 4.0 0.01831563888873418
42 | 4.1 0.016572675401761255
43 | 4.2 0.014995576820477703
44 | 4.3 0.013568559012200934
45 | 4.4 0.012277339903068436
46 | 4.5 0.011108996538242306
47 | 4.6 0.010051835744633586
48 | 4.7 0.009095277101695816
49 | 4.8 0.00822974704902003
50 | 4.9 0.007446583070924338
51 | 5.0 0.006737946999085467
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | require 'simplecov'
2 | require 'codeclimate-test-reporter'
3 | require 'digest'
4 | require 'chunky_png'
5 | require 'digest/md5'
6 | SimpleCov.add_filter 'vendor'
7 | SimpleCov.add_filter 'examples'
8 | SimpleCov.formatter = CodeClimate::TestReporter::Formatter
9 | SimpleCov.start CodeClimate::TestReporter.configuration.profile
10 |
11 | require 'gnuplotrb'
12 |
13 | include ChunkyPNG::Color
14 | include GnuplotRB
15 | include GnuplotRB::Fit
16 |
17 | $RSPEC_TEST = true
18 |
19 | def same_images?(*imgs)
20 | images = imgs.map { |img| Digest::MD5.digest(ChunkyPNG::Image.from_file(img).pixels.to_s) }
21 | images.all? { |img| img == images[0] }
22 | end
23 |
24 | # may be errorneuos in comparing images
25 | def same_files?(*files)
26 | images = files.map { |fname| Digest::MD5.digest(File.binread(fname)) }
27 | images.all? { |img| img == images[0] }
28 | end
29 |
30 | def awesome?
31 | # sure!
32 | true
33 | end
34 |
35 | def run_example_at(path)
36 | Dir.chdir(path) do
37 | FileUtils.rm(Dir["#{Dir.pwd}/*.png"])
38 | require "#{Dir.pwd}/plot.rb"
39 | # run gnuplot without output to current console
40 | `gnuplot plot.gnuplot 2>&1`
41 | same_images?('gnuplot.png', 'gnuplot_gem.png')
42 | end
43 | end
44 |
--------------------------------------------------------------------------------
/spec/splot_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe Splot do
4 | before(:all) do
5 | @tmp_dir = File.join('spec', 'tmp')
6 | Dir.mkdir(@tmp_dir)
7 | @datafile_path = File.join('spec', 'points.data')
8 | end
9 |
10 | after(:all) do
11 | FileUtils.rm_r(@tmp_dir)
12 | end
13 |
14 | context 'creation' do
15 | before do
16 | @title = 'Awesome spec'
17 | @formula = %w(sin(x) cos(x) exp(-x))
18 | @options = { title: @title, term: 'dumb' }
19 | end
20 |
21 | it 'should use *splot* command instead of *plot*' do
22 | plot = Splot.new(*@formula, **@options)
23 | expect(plot).to be_an_instance_of(Splot)
24 | expect(plot.instance_variable_get(:@cmd)).to eql('splot ')
25 | end
26 | end
27 |
28 | context 'options handling' do
29 | before do
30 | @options = Hamster.hash(title: 'GnuplotRB::Plot', yrange: 0..3)
31 | @plot = Splot.new(**@options)
32 | end
33 |
34 | it 'should return Splot object' do
35 | new_options = { title: 'Another title', xrange: 1..5 }
36 | new_plot = @plot.options(new_options)
37 | expect(new_plot).to_not equal(@plot)
38 | expect(new_plot).to be_an_instance_of(Splot)
39 | end
40 | end
41 |
42 | context 'modifying datasets' do
43 | before do
44 | @plot_math = Splot.new(['sin(x)', title: 'Just a sin'])
45 | @dataset = Dataset.new('exp(-x)')
46 | @plot_two_ds = Splot.new(['cos(x)'], ['x*x'])
47 | end
48 |
49 | it 'should create new *Splot* when user adds a dataset' do
50 | new_plot = @plot_math.add_dataset(@dataset)
51 | expect(new_plot).to be_instance_of(Splot)
52 | end
53 |
54 | it 'should create new *Splot* when user adds a dataset using #<<' do
55 | new_plot = @plot_math << @dataset
56 | expect(new_plot).to be_instance_of(Splot)
57 | end
58 |
59 | it 'should create new *Splot* when user removes a dataset' do
60 | new_plot = @plot_two_ds.remove_dataset
61 | expect(new_plot).to be_instance_of(Splot)
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/spec/terminal_spec.rb:
--------------------------------------------------------------------------------
1 | require 'spec_helper.rb'
2 |
3 | describe Terminal do
4 | before(:all) do
5 | @tmp_dir = File.join('spec', 'tmp')
6 | Dir.mkdir(@tmp_dir)
7 | @datafile_path = File.join('spec', 'points.data')
8 | end
9 |
10 | after(:all) do
11 | FileUtils.rm_r(@tmp_dir)
12 | end
13 |
14 | before(:each) do
15 | @terminal = Terminal.new
16 | @paths = (0..1).map { |i| File.join(@tmp_dir, "#{i}plot.png") }
17 | @options0 = { term: ['png', size: [300, 300]], output: @paths[0] }
18 | @options1 = { term: ['png', size: [300, 300]], output: @paths[1] }
19 | @dataset = Dataset.new('sin(x)')
20 | @plot = Plot.new(@dataset, @options0)
21 | end
22 |
23 | context 'options handling' do
24 | it 'should work with String as option value' do
25 | options = { term: 'qt' }
26 | string = @terminal.options_hash_to_string(options)
27 | expect(string.strip).to be_eql('set term qt')
28 | end
29 |
30 | it 'should work with Boolean and nil as option value' do
31 | [
32 | [{ multiplot: true }, 'set multiplot'],
33 | [{ multiplot: false }, 'unset multiplot'],
34 | [{ multiplot: nil }, 'unset multiplot']
35 | ].each do |variant|
36 | string = @terminal.options_hash_to_string(variant[0])
37 | expect(string.strip).to be_eql(variant[1])
38 | end
39 | end
40 |
41 | it 'should work with Array and Hash as option value' do
42 | # it works with arrays of numbers different way
43 | options = { term: ['qt', size: [100, 100]] }
44 | string = @terminal.options_hash_to_string(options)
45 | expect(string.strip).to be_eql('set term qt size 100,100')
46 | end
47 | end
48 |
49 | context '#<<' do
50 | it 'should output Plots' do
51 | @plot.plot
52 | FileUtils.mv(@paths[0], @paths[1])
53 | @terminal << @plot
54 | expect(same_images?(*@paths)).to be_truthy
55 | end
56 |
57 | it 'should output Datasets' do
58 | @plot.plot
59 | @terminal.set(@options1)
60 | @terminal << @dataset
61 | expect(same_images?(*@paths)).to be_truthy
62 | end
63 |
64 | it 'should output whatever you want just as string' do
65 | @plot.plot
66 | @terminal << @terminal.options_hash_to_string(@options1)
67 | @terminal << @dataset
68 | expect(same_images?(*@paths)).to be_truthy
69 | end
70 | end
71 |
72 | context '#stream_puts' do
73 | it 'should output whatever you want just as string' do
74 | @plot.plot
75 | @terminal.stream_puts(@terminal.options_hash_to_string(@options1))
76 | @terminal << @dataset
77 | expect(same_images?(*@paths)).to be_truthy
78 | end
79 | end
80 |
81 | context '#replot' do
82 | it 'should replot last plotted graph on #replot call' do
83 | @plot.plot(@terminal)
84 | @terminal.replot(@options1)
85 | expect(same_images?(*@paths)).to be_truthy
86 | end
87 | end
88 |
89 | context 'check correctness of a terminal' do
90 | it 'should raise an error when trying to use incorrect terminal' do
91 | expect { Plot.new(term: 'incorrect_term') }.to raise_error(ArgumentError)
92 | expect { Plot.new(term: ['incorrect_term', size: [300, 300]]) }.to raise_error(ArgumentError)
93 | expect { Plot.new.to_incorrect_term }.to raise_error(NoMethodError)
94 | expect { Plot.new.plot(term: 'incorrect_term') }.to raise_error(ArgumentError)
95 | expect { Plot.new.plot(term: ['incorrect_term']) }.to raise_error(ArgumentError)
96 | end
97 | end
98 |
99 | context 'gnuplot error handling' do
100 | it 'should raise exception only in next command after a delay' do
101 | @terminal.set(wrong_option: 'wrong_value')
102 | # it takes gnuplot some time to find error
103 | # and output it to stderr
104 | sleep 0.3
105 | expect { @terminal.set(polar: true) }.to raise_error(GnuplotError)
106 | expect { @terminal.set(polar: true) }.to_not raise_error
107 | end
108 | end
109 | end
110 |
--------------------------------------------------------------------------------