65 Commits
Author SHA1 Message Date
Giorgio Calderone 0567acc60e Bump version to 1.4.0 (to meet automerge guidelines) 2021-05-05 23:48:11 +02:00
Giorgio Calderone 74e195f290 Updated 2021-05-05 12:19:04 +02:00
Giorgio Calderone 875773fd3d Docs updated 2021-05-05 12:03:13 +02:00
Giorgio Calderone 53159f89e1 Bugfix; Allow missing as input to hist(); Updated version() 2021-05-05 11:59:15 +02:00
Giorgio Calderone 54db30cc32 Bump version 2021-05-05 00:58:14 +02:00
Giorgio Calderone 364e5281bd Merge branch 'master' of https://github.com/gcalderone/Gnuplot.jl 2021-05-05 00:53:09 +02:00
Giorgio Calderone e8a6b21df6 Updated dependencies; Accept missing values on input arrays (if eltype <: AbstractFloat) 2021-05-05 00:52:56 +02:00
gcalderoneandGitHub 6c6b889df6 Merge pull request #43 from wentasah/vscode
VS Code support
2021-04-08 17:40:36 +02:00
Giorgio Calderone 5c46e35fea Updated DataStructures dependency 2021-04-01 14:04:40 +02:00
Michal Sojka 34af493921 Make the figures in VS Code bigger
Without this change, the figures in VS Code are too small.
2021-03-30 20:11:05 +02:00
Michal Sojka f2ef5373b0 Show plots in internal VS Code plot pane
With this change, when Gnuplot.jl is used within VSCode, the plot
shows in VSCode plot pane instead of in a separate Gnuplot window.

Note that when compared to IJulia and Juno, we do not test for active
VS Code connection. I don't think it is necessary, but if it turns out
to be needed later, it can be done with

    isopen(VSCodeServer.conn_endpoint[])
2021-03-30 19:43:27 +02:00
Giorgio Calderone bc74aa0db4 Accept AbstractArrays which can be converted to actual arrays 2021-01-02 00:15:39 +01:00
Giorgio Calderone b7a15290d3 Bugfix 2020-10-06 10:21:46 +02:00
Giorgio Calderone f62b569560 Fix #28 and similar issues 2020-10-05 01:14:18 +02:00
gcalderoneandGitHub 584144e2ec Merge pull request #30 from PallHaraldsson/patch-1
Juno and Jypyther in docs
2020-10-05 00:00:36 +02:00
gcalderoneandGitHub 03953c0ad6 Merge pull request #35 from jarvist/master
Add check for Pluto.jl interactive notebook.
2020-10-04 23:55:14 +02:00
Jarvist Moore Frost 084dd63059 Add check for Pluto.jl interactive notebook. 2020-09-21 21:02:08 +01:00
Páll HaraldssonandGitHub 50141d1b0e Juno and Jypyther in docs
[ci skip]
2020-07-13 11:38:23 +00:00
Giorgio Calderone 83c59ae682 Set utf8 encoding on gnuplot start 2020-05-05 11:44:08 +02:00
gcalderoneandGitHub 1877deef7f Merge pull request #26 from lyon-fnal/master
Fix the string indexing error and problems with multiplot on JupyterLab
2020-05-05 00:59:08 +02:00
Adam Lyon 67a5583c29 Fixes #25 2020-05-04 15:59:21 -05:00
Adam Lyon 69644f99ba Fix #24 2020-05-04 15:58:59 -05:00
Giorgio Calderone 2713f8517e Updated 2020-04-29 12:24:46 +02:00
Giorgio Calderone 2c2c74e448 Added dgrid3d function 2020-04-29 12:12:52 +02:00
Giorgio Calderone 6bf1b80058 Bump version (1.3.0) 2020-04-28 19:26:23 +02:00
Giorgio Calderone 671efccc17 Updated 2020-04-28 19:09:39 +02:00
Giorgio Calderone bdeb0e45ab Updated 2020-04-28 18:56:04 +02:00
Giorgio Calderone 3e03aa27f7 Updated 2020-04-28 18:50:40 +02:00
Giorgio Calderone dab424bfec Updated 2020-04-28 18:42:58 +02:00
Giorgio Calderone 92380bc468 Added smooth keyword to palette; Updated docstrings 2020-04-28 14:14:25 +02:00
Giorgio Calderone 4d1768e15e Added contourlines method accepting top fractions 2020-04-27 18:37:59 +02:00
Giorgio Calderone 8e140ee998 Updated 2020-04-25 20:52:19 +02:00
Giorgio Calderone 6df834bb88 Docs updated 2020-04-25 20:41:09 +02:00
Giorgio Calderone c9fa96c9eb Docs updated 2020-04-25 20:20:09 +02:00
Giorgio Calderone d74715f156 Fixed initialization in Juno 2020-04-25 19:28:57 +02:00
Giorgio Calderone 2fb581b3a3 Defined and implemented the display behaviour (based on options/gpviewer) 2020-04-25 18:37:17 +02:00
Giorgio Calderone 190562a322 Use AbstractVector and AbstractMatrix argument types in contourlines 2020-04-23 09:57:16 +02:00
Giorgio Calderone c5d64f5d76 Enable show mechanism, regardless of Juno/IJulia 2020-04-22 20:00:51 +02:00
Giorgio Calderone 184206946b Fix CI 2020-04-22 19:15:03 +02:00
Giorgio Calderone 4405daf088 Bugfix 2020-04-22 19:09:47 +02:00
Giorgio Calderone e3d9d6035e Fix CI 2020-04-22 18:26:47 +02:00
Giorgio Calderone e11d93d807 Fix CI 2020-04-22 18:17:23 +02:00
Giorgio Calderone 6996c98d40 Fix CI 2020-04-22 18:09:54 +02:00
Giorgio Calderone 02e15543cd Merge branch 'StdoutPassThrough' 2020-04-22 17:58:39 +02:00
Giorgio Calderone 8f737205a1 Improved readTask and capture protocol 2020-04-22 17:56:50 +02:00
Giorgio Calderone deee2693df Disable show mechanism 2020-04-22 14:46:29 +02:00
Giorgio Calderone a47705be6b Test updated 2020-04-22 14:46:11 +02:00
Giorgio Calderone 8a8ce1f533 Stdout is no longer filtered 2020-04-22 14:39:52 +02:00
Giorgio Calderone e687bb3716 Stdout is no longer filtered 2020-04-22 14:26:53 +02:00
Giorgio Calderone 00ef33a8a6 Stdout is no longer filtered 2020-04-22 14:26:06 +02:00
Giorgio Calderone 3238bcea80 Updated default terminals 2020-04-22 10:02:35 +02:00
Giorgio Calderone 993633886a Test 2020-04-22 09:52:09 +02:00
Giorgio Calderone e33b74bf6d Updated 2020-04-22 01:43:48 +02:00
Giorgio Calderone 7e3aa2b384 Merge branch 'testCanvasTerminal' 2020-04-22 01:42:56 +02:00
Giorgio Calderone c5890b6819 Implemented save() and show() based on Options.mime dictionary 2020-04-22 01:42:38 +02:00
Giorgio Calderone be273493eb Updated 2020-04-21 10:49:18 +02:00
Giorgio Calderone b79228d7b1 Implemented show(io::IO, ::MIME"text/html" method 2020-04-21 10:33:29 +02:00
Giorgio Calderone bad5e12681 Updated 2020-04-20 19:27:12 +02:00
Giorgio Calderone 703f0bf618 Updated for v1.2.0 2020-04-20 18:51:36 +02:00
Giorgio Calderone 96874bcdae Release v1.2.0 2020-04-20 18:43:27 +02:00
Giorgio Calderone f7bf0040fa Fix CI 2020-04-20 18:15:47 +02:00
Giorgio Calderone 739255330b Test coverage: 69% 2020-04-20 18:11:57 +02:00
Giorgio Calderone e264b27119 Comments updated 2020-04-19 16:49:19 +02:00
Giorgio Calderone 5b858670d6 Merge branch 'master' of github.com:gcalderone/Gnuplot.jl 2020-04-19 16:35:15 +02:00
gcalderoneandGitHub da0bd71007 Update README.md 2020-04-19 16:07:26 +02:00
17 changed files with 696 additions and 286 deletions
+5 -5
View File
@@ -6,15 +6,15 @@ os:
# - osx # - osx
julia: julia:
- 1.2 - 1.5
- nightly # - nightly
notifications: notifications:
email: false email: false
matrix: #matrix:
allow_failures: # allow_failures:
- julia: nightly # - julia: nightly
addons: addons:
apt: apt:
+59 -7
View File
@@ -1,4 +1,55 @@
# Version 1.2.0 (not yet released) # Version 1.4.0 (released on: May 5, 2021)
- New features:
* Missing values are accepted if the input arrays have `eltype <:
AbstractFloat`;
* Missing values are also accepted in calls to `hist`;
* VSCode and Pluto sessions are now properly handled (#35 and #43);
- Bugfix:
* Multiplot were not displayed in Jupyter (#25);
* `gpvars()` fails if gnuplot character encoding is utf8
(#24);
# Version 1.3.0 (released on: Apr. 29, 2020)
- New features:
* The new `dgrid3d()` allows to interpolate scattered 2D data on a
2D regular grid;
* The `Options` structure features a new `mime` field containing a
dictionary to map a MIME type to gnuplot terminals;
* The `Options` structure features a new `gpviewer` field allowing
to choose the display behaviour (using either gnuplot
interactive terminals or anexternal viewer such as Jupyter or
Juno);
* The `save()` function now accepts a `MIME` argument in place of
the `term=` keyword. The actual terminal is retrieved from the
`Options.mime` dictionary;
* The `contourlines()` function now accepts `AbstractVector` and
`AbstractMatrix` as arguments, rather than `Vector` and
`Matrix`;
* The `contourlines()` function now accepts a `fractions` input to
generate contours encompassing given fractions of the total
counts in a 2D histogram;
* The `palette()` function now accept a boolean `smooth` keyword,
allowing to interpolate a discrete palette into a continuous one.
- Breaking changes:
* The `Options` structure no longer provides the `term_svg` and
`term_png` fields. They have been replaced by the `mime`
dictionary.
# Version 1.2.0 (released on: Apr. 20, 2020)
- New features: - New features:
* REPL mode: a new `Gnuplot.repl_init()` function is available to * REPL mode: a new `Gnuplot.repl_init()` function is available to
@@ -16,20 +67,21 @@
line width and point size respectively), and the `dashed` (to line width and point size respectively), and the `dashed` (to
use dashed patterns in place of solid lines) keywords; use dashed patterns in place of solid lines) keywords;
* The new `Gnuplot.options.reset::Vector{String}` field allows to * The new `Gnuplot.options.term::String` field allows to set the
set initialization commands to be executed when a session is default terminal for interactive sessions;
reset. Unlike `Gnuplot.options.init`, these commands are saved
in the session and can be saved into a script;
* New functions: `gpvars()` to retrieve all gnuplot variables, * New functions: `gpvars()` to retrieve all gnuplot variables,
`gpmargins()` to retrieve current plot margins (in screen `gpmargins()` to retrieve current plot margins (in screen
coordinates, `gpranges()` to retrieve current plot axis ranges; coordinates, `gpranges()` to retrieve current plot axis ranges;
* New keywords for `@gp` and `@gsp`: `lmargin`, `rmargin`, * New keywords accepted by `@gp` and `@gsp`: `lmargin`, `rmargin`,
`bmargin`, `tmargin`, `margins`, to set plot margins; `bmargin`, `tmargin`, `margins`, to set plot margins;
* Implemented new implicit recipes to display histograms (as * Implemented new implicit recipes to display histograms (as
returned by `hist()`) and images; returned by `hist()`), contour lines (as returned by
`contourlines()`) and images;
* Implemented automatic display of plots in both Jupyter and Juno;
* Documentation updated; * Documentation updated;
+5 -5
View File
@@ -1,6 +1,6 @@
name = "Gnuplot" name = "Gnuplot"
uuid = "dc211083-a33a-5b79-959f-2ff34033469d" uuid = "dc211083-a33a-5b79-959f-2ff34033469d"
version = "1.2.0" version = "1.4.0"
[deps] [deps]
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
@@ -14,11 +14,11 @@ StructC14N = "d2514e9c-36c4-5b8e-97e2-51e7675c221c"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
[compat] [compat]
ColorSchemes = "^3.5" ColorSchemes = "^3.12"
ColorTypes = "^0.10" ColorTypes = "^0.11"
Colors = "^0.12" Colors = "^0.12"
DataStructures = "^0.17" DataStructures = "^0.18"
ReplMaker = "^0.2" ReplMaker = "^0.2"
StatsBase = "^0.33" StatsBase = "^0.33"
StructC14N = "^0.3" StructC14N = "^0.3"
julia = "^1.2" julia = "^1.5"
+8 -9
View File
@@ -1,13 +1,12 @@
# Gnuplot.jl # Gnuplot.jl
## A Julia interface to Gnuplot. ## A Julia interface to gnuplot.
[![Build Status](https://travis-ci.org/gcalderone/Gnuplot.jl.svg?branch=master)](https://travis-ci.org/gcalderone/Gnuplot.jl) [![Build Status](https://travis-ci.org/gcalderone/Gnuplot.jl.svg?branch=master)](https://travis-ci.org/gcalderone/Gnuplot.jl)
[![License](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](LICENSE.md) [![License](http://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](LICENSE.md)
[![DocumentationStatus](https://img.shields.io/badge/docs-stable-blue.svg?style=flat)](https://gcalderone.github.io/Gnuplot.jl/v1.1.0/) [![DocumentationStatus](https://img.shields.io/badge/docs-stable-blue.svg?style=flat)](https://gcalderone.github.io/Gnuplot.jl/v1.4.0/)
[![DocumentationStatus](https://img.shields.io/badge/docs-latest-blue.svg?style=flat)](https://gcalderone.github.io/Gnuplot.jl/v1.2.0/)
**Gnuplot.jl** is a simple package able to send both data and commands from Julia to an underlying [gnuplot](http://gnuplot.sourceforge.net/) process. Its main purpose it to provide a fast and powerful data visualization framework, using an extremely concise Julia syntax. **Gnuplot.jl** is a simple package able to send both data and commands from Julia to an underlying [gnuplot](http://gnuplot.sourceforge.net/) process. Its main purpose it to provide a fast and powerful data visualization framework, using an extremely concise Julia syntax. It also has automatic display of plots in Jupyter, Juno and VS Code.
## Installation ## Installation
@@ -22,9 +21,9 @@ You may check the installed **Gnuplot.jl** version with:
```julia ```julia
]st Gnuplot ]st Gnuplot
``` ```
If the displayed version is not v1.1.0 you are probably having a dependency conflict. In this case try forcing installation of the latest version with: If the displayed version is not v1.4.0 you are probably having a dependency conflict. In this case try forcing installation of the latest version with:
```julia ```julia
]add Gnuplot@1.1.0 ]add Gnuplot@1.4.0
``` ```
and check which package is causing the conflict. and check which package is causing the conflict.
@@ -38,14 +37,14 @@ test_terminal()
## Quick start ## Quick start
The following examples are supposed to be self-explaining. See [documentation](https://gcalderone.github.io/Gnuplot.jl/v1.1.0/) for further informations. The following examples are supposed to be self-explaining. See [documentation](https://gcalderone.github.io/Gnuplot.jl/v1.4.0/) for further informations.
### A simple parabola ### A simple parabola
```julia ```julia
x = 1.:20 x = 1.:20
@gp x x.^2 "with lines title 'Parabola'" @gp x x.^2 "with lines title 'Parabola'"
save(term="pngcairo size 480,360", output="examples/ex1.png") save(term="pngcairo size 480,360", output="examples/ex1.png")
save("parabola.gp") # => save a script file with both data and command to re-create the plot. save("parabola.gp") # => save a script file with both data and command to re-create the plot.
``` ```
![ex1.png](examples/ex1.png) ![ex1.png](examples/ex1.png)
@@ -74,7 +73,7 @@ y = randn(10_000)
h = hist(x, y, bs1=0.25, nbins2=20) h = hist(x, y, bs1=0.25, nbins2=20)
@gp "set multiplot layout 1,2" @gp "set multiplot layout 1,2"
@gp :- 1 key="outside top center box horizontal" "set size ratio -1" h @gp :- 1 key="outside top center box horizontal" "set size ratio -1" h
clines = contourlines(h.bins1, h.bins2, h.counts, cntrparam="levels discrete 10, 30, 60, 90"); clines = contourlines(h, "levels discrete 10, 30, 60, 90");
for i in 1:length(clines) for i in 1:length(clines)
@gp :- clines[i].data "w l t '$(clines[i].z)' lw $i lc rgb 'gray'" :- @gp :- clines[i].data "w l t '$(clines[i].z)' lw $i lc rgb 'gray'" :-
end end
+2 -1
View File
@@ -1,8 +1,9 @@
using Documenter, Gnuplot using Documenter, Gnuplot
empty!(Gnuplot.options.mime)
makedocs(sitename="Gnuplot.jl", makedocs(sitename="Gnuplot.jl",
authors = "Giorgio Calderone", authors = "Giorgio Calderone",
format = Documenter.HTML(prettyurls = false), # uncomment for local use, comment for deployment #format = Documenter.HTML(prettyurls = false), # uncomment for local use, comment for deployment
modules=[Gnuplot], modules=[Gnuplot],
pages = [ pages = [
"Home" => "index.md", "Home" => "index.md",
+65 -1
View File
@@ -198,7 +198,7 @@ saveas("advanced013b") # hide
``` ```
![](assets/advanced013b.png) ![](assets/advanced013b.png)
The [`hist()`](@ref) function compute also 2D histograms by passing two vectors (with the same lengths), e.g.: The [`hist()`](@ref) function compute also 2D histograms by passing two vectors (with the same lengths), e.g.:
```@example abc ```@example abc
x = randn(10_000) x = randn(10_000)
y = randn(10_000) y = randn(10_000)
@@ -255,6 +255,70 @@ saveas("advanced014e") # hide
![](assets/advanced014e.png) ![](assets/advanced014e.png)
The [`contourlines()`](@ref) function also allows to calculate the contour lines encompassing a given fraction of the total counts of a 2D histogram. E.g. to plot the contours corresponding to 1, 2, and 3 $\sigma$ of a 2D Gaussian distribution:
```@example abc
x = randn(10^5);
y = randn(10^5);
h = hist(x, y, nbins1=20, nbins2=20);
# Calculate probability within 0 < r < σ
p(σ) = round(1 - exp(-(σ^2) / 2), sigdigits=3)
# Draw contour lines at 1, 2 and 3 σ
clines = contourlines(h, p.(1:3));
@gp palette(:beach, smooth=true, rev=true) "set grid front" "set size ratio -1" h clines
saveas("advanced014f") # hide
```
![](assets/advanced014f.png)
## Interpolation of 2D scattered data
The `dgrid3d()` function allows to interpolate 2D scattered data onto a 2D regular grid, e.g.:
```@example abc
x = (rand(200) .- 0.5) .* 3;
y = (rand(200) .- 0.5) .* 3;
z = exp.(-(x.^2 .+ y.^2));
# Interpolate on a 20x30 regular grid with splines
gx, gy, gz = dgrid3d(x, y, z, "20,30 splines")
@gsp "set size ratio -1" "set xyplane at 0" xlab="X" ylab="Y" :-
@gsp :- x y z "w p t 'Scattered data' lc pal"
@gsp :- gx gy gz "w l t 'Interpolation on a grid' lc pal"
saveas("advanced015a") # hide
```
![](assets/advanced015a.png)
!!! warn
The `splines` algorithm may be very slow on large datasets. An alternative option is to use a smoothing kernel, such as `gauss`.
The interpolated data in scarcely sampled regions are poorly constrained, i.e. they are actually *extrapolated values*. By using the `extra=false` keyword all extrapolated values are set to `NaN`:
```@example abc
x = randn(2000) .* 0.5;
y = randn(2000) .* 0.5;
rsq = x.^2 + y.^2;
z = exp.(-rsq) .* sin.(y) .* cos.(2 * rsq);
@gsp "set size ratio -1" palette(:balance, smooth=true) "set view map" "set pm3d" :-
@gsp :- "set multiplot layout 1,3" xr=[-2,2] yr=[-2,2] :-
@gsp :- 1 tit="Scattered data" x y z "w p notit lc pal"
# Show extrapolated values
gx, gy, gz = dgrid3d(x, y, z, "40,40 gauss 0.1,0.1")
@gsp :- 2 tit="Interpolation on a grid\\n(extrapolated values are shown)" gx gy gz "w l notit lc pal"
# Hide exrapolated values
gx, gy, gz = dgrid3d(x, y, z, "40,40 gauss 0.1,0.1", extra=false)
@gsp :- 3 tit="Interpolation on a grid\\n(extrapolated values are hidden)" gx gy gz "w l notit lc pal"
save(term="pngcairo size 1000,400 fontscale 1.0", output="assets/advanced015b.png") # hide
```
![](assets/advanced015b.png)
## Animations ## Animations
The [Multiplot](@ref) capabilities can also be used to stack plots one above the other in order to create an animation, as in the following example: The [Multiplot](@ref) capabilities can also be used to stack plots one above the other in order to create an animation, as in the following example:
+1
View File
@@ -13,6 +13,7 @@ The list of **Gnuplot.jl** exported symbols is as follows:
boxxy boxxy
contourlines contourlines
dataset_names dataset_names
dgrid3d
gpexec gpexec
gpmargins gpmargins
gpranges gpranges
+1 -1
View File
@@ -21,7 +21,7 @@ saveas("basic000") # hide
``` ```
![](assets/basic000.png) ![](assets/basic000.png)
The plots are displayed either in an interactive window (if running in the Julia REPL), as an inline image (if running in Jupyter) or in the plot pane (if running in Juno). See [Options](@ref) and [Jupyter and Juno](@ref) for further informations. The plots are displayed either in an interactive window (if running in the Julia REPL), as an inline image (if running in Jupyter) or in the plot pane (if running in Juno). See [Display options](@ref) for further informations.
Both the [`@gp`](@ref) and [`@gsp`](@ref) macros accept any number of arguments, whose meaning is interpreted as follows: Both the [`@gp`](@ref) and [`@gsp`](@ref) macros accept any number of arguments, whose meaning is interpreted as follows:
+2
View File
@@ -21,6 +21,8 @@ The **Gnuplot.jl** package allows easy and fast use of [gnuplot](http://gnuplot.
- enhanced support for contour plots; - enhanced support for contour plots;
- 2D interpolation of scattered data on a regular grid;
- export to a huge number of formats such as `pdf`, `png`, `gif`, ``\LaTeX``, `svg`, etc. (actually all those supported by gnuplot); - export to a huge number of formats such as `pdf`, `png`, `gif`, ``\LaTeX``, `svg`, etc. (actually all those supported by gnuplot);
- compatibility with Jupyter and Juno; - compatibility with Jupyter and Juno;
+3 -3
View File
@@ -19,11 +19,11 @@ Check **Gnuplot.jl** version with:
```julia-repl ```julia-repl
julia> ]st Gnuplot julia> ]st Gnuplot
Status `~/.julia/environments/v1.4/Project.toml` Status `~/.julia/environments/v1.4/Project.toml`
[dc211083] Gnuplot v1.2.0 [dc211083] Gnuplot v1.4.0
``` ```
If the displayed version is not `v1.2.0` you are probably having a dependency conflict. In this case try forcing installation of the latest version with: If the displayed version is not `v1.4.0` you are probably having a dependency conflict. In this case try forcing installation of the latest version with:
```julia-repl ```julia-repl
julia> ]add Gnuplot@1.2.0 julia> ]add Gnuplot@1.4.0
``` ```
and check which package is causing the conflict. and check which package is causing the conflict.
+44 -18
View File
@@ -9,6 +9,19 @@ push!( Gnuplot.options.init, linetypes(:Set1_5, lw=1.5, ps=1.5))
saveas(file) = save(term="pngcairo size 550,350 fontscale 0.8", output="assets/$(file).png") saveas(file) = save(term="pngcairo size 550,350 fontscale 0.8", output="assets/$(file).png")
``` ```
# Display options
The display behaviour of **Gnuplot.jl** depends on the value of the `Gnuplot.options.gpviewer` flag:
- if `true` the plot is displayed in a gnuplot window, using one of the interactive terminals such as `wxt`, `qt` or `aqua`. This is the default setting when running a Julia REPL session; The terminal options can be customized using `Gnuplot.options.term`;
- if `false` the plot is displayed through the Julia [multimedia interface](https://docs.julialang.org/en/v1/base/io-network/#Multimedia-I/O-1), i.e. it is exported as either a `png`, `svg` or `html` file, and displayed in an external viewer. This is the default setting when running a Jupyter, JupyterLab or Juno session. The terminal options can be customized using the `Gnuplot.options.mime` dictionary.
The `Gnuplot.options.gpviewer` flag is automatically set when the package is first loaded according to the runtime environment, however the user can change its value at any time to fit specific needs. Further informations and examples for both options are available in this Jupyter [notebook](https://github.com/gcalderone/Gnuplot.jl/blob/gh-pages/v1.3.0/options/display.ipynb).
# Package options and initialization # Package options and initialization
## Options ## Options
@@ -18,14 +31,21 @@ The package options are stored in a global structure available in Julia as `Gnup
- `cmd::String`: command to start the gnuplot process, default value is `"gnuplot"`. Use this field to specify a custom path to the gnuplot executable; - `cmd::String`: command to start the gnuplot process, default value is `"gnuplot"`. Use this field to specify a custom path to the gnuplot executable;
- `gpviewer::Bool`: use a gnuplot terminal as main plotting device (if `true`) or an external viewer (if `false`);
- `term::String`: default terminal for interactive use (default is an empty string, i.e. use gnuplot settings). A custom terminal can be set with, e.g.: - `term::String`: default terminal for interactive use (default is an empty string, i.e. use gnuplot settings). A custom terminal can be set with, e.g.:
```@repl abc ```@repl abc
Gnuplot.options.term = "wxt size 700,400"; Gnuplot.options.term = "wxt size 700,400";
``` ```
- `term_svg::String`: terminal to save png files (default: `"svg background rgb 'white' dynamic"`); - `mime::Dict{MIME, String}`: dictionary of MIME types and corresponding gnuplot terminals. Used to export images with either [`save()`](@ref) or `show()` (see [Display options](@ref)). Default values are:
- `MIME"application/pdf" => "pdfcairo enhanced"`
- `MIME"image/jpeg" => "jpeg enhanced"`
- `MIME"image/png" => "pngcairo enhanced"`
- `MIME"image/svg+xml" => "svg enhanced mouse standalone dynamic background rgb 'white'"`
- `MIME"text/html" => "svg enhanced mouse standalone dynamic"`
- `MIME"text/plain" => "dumb enhanced ansi"`
- `term_png::String`: terminal to save png files (default: `"pngcairo"`);
- `init::Vector{String}`: commands to initialize the session when it is created or reset. It can be used to, e.g., set a custom linetypes or palette: - `init::Vector{String}`: commands to initialize the session when it is created or reset. It can be used to, e.g., set a custom linetypes or palette:
```@repl abc ```@repl abc
@@ -48,41 +68,47 @@ gpexec("set term unknown"); # hide
Each line reports the package name (`GNUPLOT`), the session name (`default`), the command or string being sent to gnuplot process, and the returned response (line starting with `->`). Default value is `false`; Each line reports the package name (`GNUPLOT`), the session name (`default`), the command or string being sent to gnuplot process, and the returned response (line starting with `->`). Default value is `false`;
## Jupyter and Juno
**Gnuplot.jl** can display plots in Jupyter and Juno by exporting images in the PNG and SVG formats. To customize the terminals used to export the images set the `term_png` or `term_svg` fields of the [`Gnuplot.Options`](@ref) structure, e.g.:
```@repl abc
Gnuplot.options.term_png = "pngcairo size 700,400 linewidth 2";
Gnuplot.options.term_svg = "svg dynamic";
```
## Package initialization ## Package initialization
If you use **Gnuplot.jl** frequently you may find convenient to collect all the package settings (see [Options](@ref)) in a single place, to quickly recall them in a Julia session. I suggest to put the following code in the `~/.julia/config/startup.jl` initialization file (further info [here](https://docs.julialang.org/en/v1/stdlib/REPL/)): If you use **Gnuplot.jl** frequently you may find convenient to collect all the package settings ([Options](@ref)) in a single place, to quickly recall them in a Julia session. A possibility is to put the following code in the `~/.julia/config/startup.jl` initialization file (further info [here](https://docs.julialang.org/en/v1/stdlib/REPL/)):
```julia ```julia
macro gnuplotrc() macro gnuplotrc()
return :( return :(
using Revise, Gnuplot; using Gnuplot;
# Uncomment following to true if you don't have the gnuplot # Uncomment the following if you don't have the gnuplot
# executable installed on your platform: # executable installed on your platform:
#Gnuplot.options.dry = true; #Gnuplot.options.dry = true;
# Uncomment the following and set the proper path if the # Set the proper path if the gnuplot executable is not
# gnuplot executable is not available in your $PATH # available in your $PATH
#Gnuplot.options.cmd = "/path/to/gnuplot"; #Gnuplot.options.cmd = "/path/to/gnuplot";
# Force a specific display behaviour (see documentation). If
# not given explicit Gnuplot.jl will choose the best option
# according to your runtime environment.
#Gnuplot.options.gpviewer = true
# Set the default terminal for interacitve use # Set the default terminal for interacitve use
Gnuplot.options.term = "wxt size 700,400"; Gnuplot.options.term = "wxt size 700,400";
# Set the terminal options for the exported MIME types:
#Gnuplot.options.mime[MIME"image/png"] = "";
#Gnuplot.options.mime[MIME"image/svg+xml"] = "svg enhanced standalone dynamic";
#Gnuplot.options.mime[MIME"text/html"] = "svg enhanced standalone mouse dynamic";
# Set the terminal to plot in a terminal emulator:
# (try with `save(MIME"text/plain")`):
#Gnuplot.options.mime[MIME"text/plain"] = "sixelgd enhanced"; # requires vt340 emulation
# Set the default linetypes # Set the default linetypes
empty!(Gnuplot.options.init); empty!(Gnuplot.options.init);
push!(Gnuplot.options.init, linetypes(:Set1_5, lw=1.5, ps=1.5)); push!(Gnuplot.options.init, linetypes(:Set1_5, lw=1.5, ps=1.5));
# Initialize the gnuplot REPL using the provided `start_key`. # Initialize the gnuplot REPL using the provided `start_key`.
# Comment the following to disable the REPL. if Gnuplot.options.gpviewer;
Gnuplot.repl_init(start_key='>'); Gnuplot.repl_init(start_key='>');
end;
) )
end end
``` ```
+2 -2
View File
@@ -18,7 +18,7 @@ There are two kinds of recipes:
- *explicit* recipe: a function which is explicitly invoked by the user. It can have any name and accept any number of arguments and keywords. It is typically used when the visualization of a data type requires some extra information, beside data itself (e.g. to plot data from a `DataFrame` object, see [Explicit recipe (example)](@ref)); - *explicit* recipe: a function which is explicitly invoked by the user. It can have any name and accept any number of arguments and keywords. It is typically used when the visualization of a data type requires some extra information, beside data itself (e.g. to plot data from a `DataFrame` object, see [Explicit recipe (example)](@ref));
- *implicit* recipe: a function which is automatically called by **Gnuplot.jl**. It must extend the p`recipe()`](@ref) function, and accept exactly one mandatory argument. It is typically used when the visualization is completely determined by the data type itself (e.g. the visualization of a `Matrix{ColorTypes.RGB}` object as an image, see [Image recipes](@ref)); - *implicit* recipe: a function which is automatically called by **Gnuplot.jl**. It must extend the [`recipe()`](@ref) function, and accept exactly one mandatory argument. It is typically used when the visualization is completely determined by the data type itself (e.g. the visualization of a `Matrix{ColorTypes.RGB}` object as an image, see [Image recipes](@ref));
An implicit recipe is invoked whenever the data type of an argument to `@gp` or `@gsp` is not among the allowed ones (see [`@gp()`](@ref) documentation). If a suitable recipe do not exists an error is raised. On the other hand, an explicit recipe needs to be invoked by the user, and the output passed directly to `@gp` or `@gsp`. An implicit recipe is invoked whenever the data type of an argument to `@gp` or `@gsp` is not among the allowed ones (see [`@gp()`](@ref) documentation). If a suitable recipe do not exists an error is raised. On the other hand, an explicit recipe needs to be invoked by the user, and the output passed directly to `@gp` or `@gsp`.
@@ -125,7 +125,7 @@ end
with only one mandatory argument. In order to exploit the optional keyword we can explicitly invoke the recipe as follows: with only one mandatory argument. In order to exploit the optional keyword we can explicitly invoke the recipe as follows:
```@example abc ```@example abc
img = testimage("walkbridge"); img = testimage("walkbridge");
@gp palette(:gray) recipe(img, "flipy rot=15deg") @gp palette(:gray1) recipe(img, "flipy rot=15deg")
saveas("recipes007c") # hide saveas("recipes007c") # hide
``` ```
![](assets/recipes007c.png) ![](assets/recipes007c.png)
+1 -1
View File
@@ -1,6 +1,6 @@
# Style Guide # Style Guide
The **Gnuplot.jl** loose syntax allows to create a plot using very different approaches. While this was one of the initial purposes for the package, it may lead to decreased code readability if not used judiciously. The **Gnuplot.jl** loose syntax allows to create a plot using very different approaches. While this was one of the initial purposes for the package, it may lead to a reduced code readability if not used judiciously.
Here I will summarize a few, non-mandatory, guidelines which allows to maintain a neat syntax and a high readability: Here I will summarize a few, non-mandatory, guidelines which allows to maintain a neat syntax and a high readability:
+7 -11
View File
@@ -4,11 +4,7 @@ Gnuplot provides dozens of terminals to display plots or export them into files
To use a specific terminal for interactive use you may either set it as initialization command for all new session with (see [Options](@ref)): To use a specific terminal for interactive use you may either set it as initialization command for all new session with (see [Options](@ref)):
```julia ```julia
Gnuplot.options.term = "wxt") Gnuplot.options.term = "wxt"
```
or directly send the command to a specific session (see [Direct command execution](@ref))
```julia
gpexec("set term wxt")
``` ```
See official [gnuplot documentation](http://gnuplot.sourceforge.net/documentation.html) for further info on terminals and their options. See official [gnuplot documentation](http://gnuplot.sourceforge.net/documentation.html) for further info on terminals and their options.
@@ -16,13 +12,13 @@ See official [gnuplot documentation](http://gnuplot.sourceforge.net/documentatio
## Interactive terminals (`wxt` and `qt`) ## Interactive terminals (`wxt` and `qt`)
The multiplatform `wxt` and `qt` terminals are among the most widely used ones for their nicely looking outputs on display and for their interactive capabilities. The multiplatform `wxt` and `qt` terminals are among the most widely used ones for their nicely looking outputs on display and for their interactive capabilities.
You may set them as terminal with: You may use such terminals with:
``` ```
"set term wxt size 800,600" Gnuplot.options.term = "wxt size 800,600"
``` ```
or or
``` ```
"set term qt size 800,600" Gnuplot.options.term = "qt size 800,600"
``` ```
(the `size 800,600` is optional and can be omitted). (the `size 800,600` is optional and can be omitted).
@@ -41,7 +37,7 @@ The above terminals are available if gnuplot has been compiled with the `--with-
## Export to image files ## Export to image files
Gnuplot provides dozens of terminals able to export on files. Examples are: Gnuplot provides dozens of terminals able to export on files. Examples are:
- `cairopng` to export PNG files; - `pngcairo` to export PNG files;
- `pdfcairo` for PDF; - `pdfcairo` for PDF;
- `jpeg` for JPG; - `jpeg` for JPG;
- `gif` for GIF (see [Animations](@ref)). - `gif` for GIF (see [Animations](@ref)).
@@ -58,7 +54,7 @@ Gnuplot is also able to export vector (i.e. non-raster) plots through the `svg`
The `cairolatex` terminal allows to produce high quality plots by splitting the output into a PDF file (containing a rasterized image of a plot) and a `.tex` file (containing all the text as ``\LaTeX`` code). The following example shows how to write plot tics and an equation in ``\LaTeX``: The `cairolatex` terminal allows to produce high quality plots by splitting the output into a PDF file (containing a rasterized image of a plot) and a `.tex` file (containing all the text as ``\LaTeX`` code). The following example shows how to write plot tics and an equation in ``\LaTeX``:
```julia ```julia
x = LinRange(-2pi, 2pi, 1000) x = LinRange(-2pi, 2pi, 1000)
@gp t="Polynomial approximation of sin(x)" "set style fill transparent solid 0.6 noborder" @gp tit="Polynomial approximation of sin(x)" "set style fill transparent solid 0.6 noborder"
@gp :- raw"""set xtics ('$-\pi$' -pi, '$-\pi/2$' -pi/2, 0, '$\pi/2$' pi/2, '$\pi$' pi)""" @gp :- raw"""set xtics ('$-\pi$' -pi, '$-\pi/2$' -pi/2, 0, '$\pi/2$' pi/2, '$\pi$' pi)"""
@gp :- xr=3.8.*[-1, 1] yr=[-1.5,1.5] key="box opaque left horiz" linetypes(:Blues_3) "set grid front" @gp :- xr=3.8.*[-1, 1] yr=[-1.5,1.5] key="box opaque left horiz" linetypes(:Blues_3) "set grid front"
latex = raw"""\begin{minipage}[c]{\textwidth}\begin{equation*}""" * latex = raw"""\begin{minipage}[c]{\textwidth}\begin{equation*}""" *
@@ -74,7 +70,7 @@ approx = fill(0., length(x));
save(term="cairolatex pdf input color dashed size 5in,3.3in", output="test.tex") save(term="cairolatex pdf input color dashed size 5in,3.3in", output="test.tex")
``` ```
!!! warning !!! warning
If you add a path in the `output=` keyword this will also be copied in the the `.tex` file. I suggest to use just filenames, with no path, in order to avoid possible errors when compiling ``\LaTeX`` code. If you add a path in the `output=` keyword this will also be copied in the the `.tex` file, and may generate errors when compiling ``\LaTeX`` code. The simplest way to solve this problem is to use just filenames, with no paths.
The two output files (`test.tex` and `test.pdf`) can then be included in a ``\LaTeX`` file as follows: The two output files (`test.tex` and `test.pdf`) can then be included in a ``\LaTeX`` file as follows:
```latex ```latex
+435 -212
View File
@@ -10,7 +10,7 @@ import Base.show
export session_names, dataset_names, palette_names, linetypes, palette, export session_names, dataset_names, palette_names, linetypes, palette,
terminal, terminals, test_terminal, terminal, terminals, test_terminal,
stats, @gp, @gsp, save, gpexec, stats, @gp, @gsp, save, gpexec,
boxxy, contourlines, hist, recipe, gpvars, gpmargins, gpranges boxxy, contourlines, dgrid3d, hist, recipe, gpvars, gpmargins, gpranges
# ╭───────────────────────────────────────────────────────────────────╮ # ╭───────────────────────────────────────────────────────────────────╮
@@ -184,7 +184,6 @@ mutable struct GPSession <: Session
plots::Vector{SinglePlot} # commands and plot commands (one entry for each plot of the multiplot) plots::Vector{SinglePlot} # commands and plot commands (one entry for each plot of the multiplot)
curmid::Int # current multiplot ID curmid::Int # current multiplot ID
pin::Base.Pipe; pin::Base.Pipe;
pout::Base.Pipe;
perr::Base.Pipe; perr::Base.Pipe;
proc::Base.Process; proc::Base.Process;
channel::Channel{String}; channel::Channel{String};
@@ -202,8 +201,8 @@ Structure containing the package global options, accessible through `Gnuplot.opt
- `cmd::String`: command to start the Gnuplot process (default: `"gnuplot"`) - `cmd::String`: command to start the Gnuplot process (default: `"gnuplot"`)
- `default::Symbol`: default session name (default: `:default`) - `default::Symbol`: default session name (default: `:default`)
- `term::String`: default terminal for interactive use (default: empty string, i.e. use gnuplot settings); - `term::String`: default terminal for interactive use (default: empty string, i.e. use gnuplot settings);
- `term_svg::String`: terminal to save png files (default `"svg background rgb 'white' dynamic"`); - `mime::Dict{DataType, String}`: dictionary of MIME types and corresponding gnuplot terminals. Used to export images with either [`save()`](@ref) or `show()` (see [Display options](@ref));
- `term_png::String`: terminal to save png files (default `"pngcairo"`); - `gpviewer::Bool`: use a gnuplot terminal as main plotting device (if `true`) or an external viewer (if `false`);
- `init::Vector{String}`: commands to initialize the session when it is created or reset (e.g., to set default palette); - `init::Vector{String}`: commands to initialize the session when it is created or reset (e.g., to set default palette);
- `verbose::Bool`: verbosity flag (default: `false`) - `verbose::Bool`: verbosity flag (default: `false`)
- `preferred_format::Symbol`: preferred format to send data to gnuplot. Value must be one of: - `preferred_format::Symbol`: preferred format to send data to gnuplot. Value must be one of:
@@ -216,8 +215,14 @@ Base.@kwdef mutable struct Options
cmd::String = "gnuplot" cmd::String = "gnuplot"
default::Symbol = :default default::Symbol = :default
term::String = "" term::String = ""
term_svg::String = "svg background rgb 'white' dynamic" mime::Dict{DataType, String} = Dict(
term_png::String = "pngcairo" MIME"application/pdf" => "pdfcairo enhanced",
MIME"image/jpeg" => "jpeg enhanced",
MIME"image/png" => "pngcairo enhanced",
MIME"image/svg+xml" => "svg enhanced mouse standalone dynamic background rgb 'white'",
MIME"text/html" => "svg enhanced mouse standalone dynamic", # canvas mousing
MIME"text/plain" => "dumb enhanced ansi")
gpviewer::Bool = false
init::Vector{String} = Vector{String}() init::Vector{String} = Vector{String}()
verbose::Bool = false verbose::Bool = false
preferred_format::Symbol = :auto preferred_format::Symbol = :auto
@@ -225,11 +230,26 @@ end
# ╭───────────────────────────────────────────────────────────────────╮ # ╭───────────────────────────────────────────────────────────────────╮
# │ GLOBAL VARIABLES # │ GLOBAL VARIABLES AND MODULE INITIALIZATION
# ╰───────────────────────────────────────────────────────────────────╯ # ╰───────────────────────────────────────────────────────────────────╯
const sessions = OrderedDict{Symbol, Session}() const sessions = OrderedDict{Symbol, Session}()
const options = Options() const options = Options()
function __init__()
# Check whether we are running in an IJulia, Juno, VSCode or Pluto session.
# (copied from Gaston.jl).
options.gpviewer = !(
((isdefined(Main, :IJulia) && Main.IJulia.inited) ||
(isdefined(Main, :Juno) && Main.Juno.isactive()) ||
(isdefined(Main, :VSCodeServer)) ||
(isdefined(Main, :PlutoRunner)) )
)
if isdefined(Main, :VSCodeServer)
# VS Code shows "dynamic" plots with fixed and small size :-(
options.mime[MIME"image/svg+xml"] = replace(options.mime[MIME"image/svg+xml"], "dynamic" => "")
end
end
# ╭───────────────────────────────────────────────────────────────────╮ # ╭───────────────────────────────────────────────────────────────────╮
# │ LOW LEVEL FUNCTIONS │ # │ LOW LEVEL FUNCTIONS │
@@ -439,60 +459,69 @@ end
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
pagerTokens() = ["Press return for more:"]
function GPSession(sid::Symbol) function readTask(gp::GPSession)
function readTask(sid, stream, channel) pagerTokens() = ["Press return for more:"]
function gpreadline(stream)
line = "" captureID = 0
while true function gpreadline()
c = read(stream, Char) line = ""
(c == '\r') && continue while true
(c == '\n') && break c = read(gp.perr, Char)
if c == Char(0x1b) # sixel (c == '\r') && continue
buf = Vector{UInt8}() (c == '\n') && break
push!(buf, UInt8(c)) line *= c
while true for token in pagerTokens() # handle pager interaction
c = read(stream, Char) if (length(line) == length(token)) && (line == token)
push!(buf, UInt8(c)) # GNUPLOT_CAPTURE_END maybe lost when pager is
(c == Char(0x1b)) && break # running: send it again.
end captureID += 1
c = read(stream, Char) write(gp.pin, "\nprint 'GNUPLOT_CAPTURE_END $(captureID)'\n")
push!(buf, UInt8(c)) line = ""
write(stdout, buf)
continue
end
line *= c
for token in pagerTokens() # handle pager interaction
if (length(line) == length(token)) && (line == token)
return line
end
end end
end end
return line
end end
return line
end
try
saveOutput = false saveOutput = false
while isopen(stream) while isopen(gp.perr)
line = gpreadline(stream) line = gpreadline()
if line == "GNUPLOT_CAPTURE_BEGIN" if line == "GNUPLOT_CAPTURE_BEGIN"
saveOutput = true saveOutput = true
elseif line == "GNUPLOT_CAPTURE_END" elseif line == "GNUPLOT_CAPTURE_END $(captureID)"
put!(channel, line)
saveOutput = false saveOutput = false
put!(gp.channel, "GNUPLOT_CAPTURE_END")
captureID = 0
elseif !isnothing(findfirst("GNUPLOT_CAPTURE_END", line))
;# old GNUPLOT_CAPTURE_END, ignore it
else else
if line != "" if line != ""
if options.verbose || !saveOutput if options.verbose || !saveOutput
printstyled(color=:cyan, "GNUPLOT ($sid) -> $line\n") printstyled(color=:cyan, "GNUPLOT ($(gp.sid)) -> $line\n")
end end
end end
(saveOutput) && (put!(channel, line)) (saveOutput) && (put!(gp.channel, line))
end end
end end
delete!(sessions, sid) catch err
return nothing if isopen(gp.perr)
@error "Error occurred in readTask for session $(gp.sid)"
@show(err)
else
put!(gp.channel, "GNUPLOT_CAPTURE_END")
end
end end
if options.verbose
printstyled(color=:red, "GNUPLOT ($(gp.sid)) Process terminated\n")
end
delete!(sessions, gp.sid)
end
function GPSession(sid::Symbol)
session = DrySession(sid) session = DrySession(sid)
if !options.dry if !options.dry
try try
@@ -506,26 +535,31 @@ function GPSession(sid::Symbol)
end end
pin = Base.Pipe() pin = Base.Pipe()
pout = Base.Pipe()
perr = Base.Pipe() perr = Base.Pipe()
proc = run(pipeline(`$(options.cmd)`, stdin=pin, stdout=pout, stderr=perr), wait=false) proc = run(pipeline(`$(options.cmd)`, stdin=pin, stdout=stdout, stderr=perr), wait=false)
chan = Channel{String}(32) chan = Channel{String}(32)
# Close unused sides of the pipes # Close unused sides of the pipes
Base.close(pout.in)
Base.close(perr.in) Base.close(perr.in)
Base.close(pin.out) Base.close(pin.out)
Base.start_reading(pout.out)
Base.start_reading(perr.out) Base.start_reading(perr.out)
# Start reading tasks
@async readTask(sid, pout, chan)
@async readTask(sid, perr, chan)
out = GPSession(getfield.(Ref(session), fieldnames(DrySession))..., out = GPSession(getfield.(Ref(session), fieldnames(DrySession))...,
pin, pout, perr, proc, chan) pin, perr, proc, chan)
sessions[sid] = out sessions[sid] = out
# Start reading tasks
@async readTask(out)
# Read gnuplot default terminal
if options.term == ""
options.term = terminal()
end
# The stderr of the gnuplot process goes to Julia which can parse
# UTF8 characters (regardless of the terminal).
gpexec("set encoding utf8")
return out return out
end end
@@ -611,32 +645,14 @@ function writeread(gp::GPSession, str::AbstractString)
write(gp, str) write(gp, str)
options.verbose = false options.verbose = false
write(gp, "print 'GNUPLOT_CAPTURE_END'") write(gp, "print 'GNUPLOT_CAPTURE_END 0'")
options.verbose = verbose options.verbose = verbose
out = Vector{String}() out = Vector{String}()
while true while true
l = take!(gp.channel) l = take!(gp.channel)
if l in pagerTokens() l == "GNUPLOT_CAPTURE_END" && break
# Consume all data from the pager push!(out, l)
while true
write(gp, "")
sleep(0.5)
if isready(gp.channel)
while isready(gp.channel)
push!(out, take!(gp.channel))
end
else
options.verbose = false
write(gp, "print 'GNUPLOT_CAPTURE_END'")
options.verbose = verbose
break
end
end
else
l == "GNUPLOT_CAPTURE_END" && break
push!(out, l)
end
end end
return out return out
end end
@@ -693,7 +709,7 @@ end
function DatasetBin(cols::Vararg{AbstractVector, N}) where N function DatasetBin(cols::Vararg{AbstractVector, N}) where N
source = "binary record=$(length(cols[1])) format='" source = "binary record=$(length(cols[1])) format='"
types = Vector{DataType}() types = Vector{DataType}()
(length(cols) == 1) && (source *= "%int") #(length(cols) == 1) && (source *= "%int")
for i in 1:length(cols) for i in 1:length(cols)
@assert length(cols[1]) == length(cols[i]) @assert length(cols[1]) == length(cols[i])
if isa(cols[i][1], Int32); push!(types, Int32); source *= "%int" if isa(cols[i][1], Int32); push!(types, Int32); source *= "%int"
@@ -710,7 +726,7 @@ function DatasetBin(cols::Vararg{AbstractVector, N}) where N
(path, io) = mktemp() (path, io) = mktemp()
source = " '$path' $source" source = " '$path' $source"
for row in 1:length(cols[1]) for row in 1:length(cols[1])
(length(cols) == 1) && (write(io, convert(Int32, row))) #(length(cols) == 1) && (write(io, convert(Int32, row)))
for col in 1:length(cols) for col in 1:length(cols)
write(io, convert(types[col], cols[col][row])) write(io, convert(types[col], cols[col][row]))
end end
@@ -741,12 +757,6 @@ end
# │ PRIVATE FUNCTIONS TO MANIPULATE SESSIONS │ # │ PRIVATE FUNCTIONS TO MANIPULATE SESSIONS │
# ╰───────────────────────────────────────────────────────────────────╯ # ╰───────────────────────────────────────────────────────────────────╯
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
function enableExportThroughShow()
return (isdefined(Main, :IJulia) && Main.IJulia.inited) ||
(isdefined(Main, :Juno) && Main.Juno.isactive())
end
function reset(gp::Session) function reset(gp::Session)
delete_binaries(gp) delete_binaries(gp)
gp.datas = OrderedDict{String, Dataset}() gp.datas = OrderedDict{String, Dataset}()
@@ -756,11 +766,8 @@ function reset(gp::Session)
gpexec(gp, "set output") gpexec(gp, "set output")
gpexec(gp, "reset session") gpexec(gp, "reset session")
# When running in IJulia or Juno set the unknown terminal if options.gpviewer
# (trick copied from Gaston.jl) # Use gnuplot viewer
if enableExportThroughShow()
gpexec(gp, "set term unknown")
else
(options.term != "") && gpexec(gp, "set term " * options.term) (options.term != "") && gpexec(gp, "set term " * options.term)
# Set window title (if not already set) # Set window title (if not already set)
@@ -771,7 +778,14 @@ function reset(gp::Session)
writeread(gp, "set term $term $opts title 'Gnuplot.jl: $(gp.sid)'") writeread(gp, "set term $term $opts title 'Gnuplot.jl: $(gp.sid)'")
end end
end end
else
# Use external viewer
gpexec(gp, "set term unknown")
end end
# Note: the reason to keep Options.term and .init separate are:
# - .term can be overriden by "unknown" (if options.gpviewer is false);
# - .init is dumped in scripts, while .term is not;
add_cmd.(Ref(gp), options.init) add_cmd.(Ref(gp), options.init)
return nothing return nothing
end end
@@ -800,7 +814,7 @@ function useBinaryMethod(args...)
elseif options.preferred_format == :auto elseif options.preferred_format == :auto
if (length(args) == 1) && isa(args[1], AbstractMatrix) if (length(args) == 1) && isa(args[1], AbstractMatrix)
binary = true binary = true
elseif all(ndims.(args) .== 1) elseif all(ndims.(args) .== 1) && all(Base.:<:.(eltype.(args), Real))
s = sum(length.(args)) s = sum(length.(args))
if s > 1e4 if s > 1e4
binary = true binary = true
@@ -857,7 +871,6 @@ end
function quit(gp::GPSession) function quit(gp::GPSession)
close(gp.pin) close(gp.pin)
close(gp.pout)
close(gp.perr) close(gp.perr)
wait( gp.proc) wait( gp.proc)
exitCode = gp.proc.exitcode exitCode = gp.proc.exitcode
@@ -909,10 +922,13 @@ function execall(gp::GPSession; term::AbstractString="", output::AbstractString=
if term != "" if term != ""
former_term = writeread(gp, "print GPVAL_TERM")[1] former_term = writeread(gp, "print GPVAL_TERM")[1]
former_opts = writeread(gp, "print GPVAL_TERMOPTIONS")[1] former_opts = writeread(gp, "print GPVAL_TERMOPTIONS")[1]
gpexec(gp, "unset multiplot")
gpexec(gp, "set term $term") gpexec(gp, "set term $term")
end end
(output != "") && gpexec(gp, "set output '$output'") (output != "") && gpexec(gp, "set output '$output'")
# printstyled("Plotting with terminal: " * terminal() * "\n", color=:blue, bold=true)
for i in 1:length(gp.plots) for i in 1:length(gp.plots)
d = gp.plots[i] d = gp.plots[i]
for j in 1:length(d.cmds) for j in 1:length(d.cmds)
@@ -924,7 +940,7 @@ function execall(gp::GPSession; term::AbstractString="", output::AbstractString=
gpexec(gp, s) gpexec(gp, s)
end end
end end
(length(gp.plots) > 1) && gpexec(gp, "unset multiplot") gpexec(gp, "unset multiplot")
(output != "") && gpexec(gp, "set output") (output != "") && gpexec(gp, "set output")
if term != "" if term != ""
gpexec(gp, "set term $former_term $former_opts") gpexec(gp, "set term $former_term $former_opts")
@@ -1033,35 +1049,49 @@ function parseArguments(_args...)
return (isplot, is3d, string(cmd)) return (isplot, is3d, string(cmd))
end end
# First pass: check for `:-` and session names # First pass: check for session names and `:-`
sid = options.default sid = nothing
doDump = true args = Vector{Any}([_args...])
doReset = true pos = 1
if length(_args) == 0 while pos <= length(args)
return (sid, doReset, doDump, Vector{PlotElement}()) arg = args[pos]
end if (typeof(arg) == Symbol) &&
for iarg in 1:length(_args) (arg != :-)
arg = _args[iarg] @assert isnothing(sid) "Only one session at a time can be addressed"
sid = arg
if typeof(arg) == Symbol deleteat!(args, pos)
if arg == :- continue
if iarg == 1
doReset = false
elseif iarg == length(_args)
doDump = false
else
@warn "Symbol `:-` at position $iarg in argument list has no meaning."
end
else
@assert (sid == options.default) "Only one session at a time can be addressed"
sid = arg
end
end end
pos += 1
end
isnothing(sid) && (sid = options.default)
if length(args) == 0
doReset = false
else
doReset = true
end
doDump = true
pos = 1
while pos <= length(args)
arg = args[pos]
if typeof(arg) == Symbol
@assert arg == :-
if pos == 1
doReset = false
elseif pos == length(args)
doDump = false
else
@warn "Symbol `:-` has a meaning only if it is at first or last position in argument list."
end
deleteat!(args, pos)
continue
end
pos += 1
end end
# Second pass: check data types, run implicit recipes and splat # Second pass: check data types, run implicit recipes and splat
# Vector{PlotElement} # Vector{PlotElement}
args = Vector{Any}([_args...])
pos = 1 pos = 1
while pos <= length(args) while pos <= length(args)
arg = args[pos] arg = args[pos]
@@ -1087,8 +1117,8 @@ function parseArguments(_args...)
end end
insert!(args, pos, string(strip(arg[1])) => nothing) insert!(args, pos, string(strip(arg[1])) => nothing)
elseif isa(arg, AbstractArray) && # ==> a dataset column elseif isa(arg, AbstractArray) && # ==> a dataset column
((valtype(arg) <: Real) || ((nonmissingtype(eltype(arg)) <: Real) ||
(valtype(arg) <: AbstractString)) ; (nonmissingtype(eltype(arg)) <: AbstractString)) ;
elseif isa(arg, Real) # ==> a dataset column with only one row elseif isa(arg, Real) # ==> a dataset column with only one row
args[pos] = [arg] args[pos] = [arg]
elseif isa(arg, Dataset) ; # ==> a Dataset object elseif isa(arg, Dataset) ; # ==> a Dataset object
@@ -1110,39 +1140,53 @@ function parseArguments(_args...)
pos += 1 pos += 1
end end
# Third pass: convert data into Dataset objetcs # Third pass: convert data into Dataset objects
pos = 1 pos = 1
accum = Vector{AbstractArray}()
while pos <= length(args) while pos <= length(args)
arg = args[pos] arg = args[pos]
if isa(arg, AbstractArray) && # ==> beginning of a dataset taken = false
((valtype(arg) <: Real) ||
(valtype(arg) <: AbstractString))
# Collect all data if isa(arg, AbstractArray)
accum = Vector{AbstractArray}() if nonmissingtype(eltype(arg)) != eltype(arg)
while isa(arg, AbstractArray) && @assert nonmissingtype(eltype(arg)) <: AbstractFloat "Missing values are supported only on arrays of floats"
((valtype(arg) <: Real) || arg = replace(arg, missing => NaN)
(valtype(arg) <: AbstractString)) end
tt = eltype(arg)
# Try to convert into Int, Float64 and String
if (tt <: Integer) && !(tt <: Int)
arg = convert(Array{Int}, arg)
elseif (tt <: AbstractFloat) && !(tt <: Float64)
arg = convert(Array{Float64}, arg)
elseif (tt <: AbstractString) && !(tt <: String)
arg = convert(Array{String}, arg)
end
tt = eltype(arg)
if (tt <: Real) ||
(tt <: AbstractString)
push!(accum, arg) push!(accum, arg)
deleteat!(args, pos) deleteat!(args, pos)
if pos <= length(args) taken = true
arg = args[pos] end
end
if !taken || (pos > length(args))
if length(accum) > 0
mm = extrema(length.(accum))
if mm[1] == 0 # empty Dataset
@assert mm[1] == mm[2] "At least one input array is empty, while other(s) are not"
d = DatasetEmpty()
else else
break d = Dataset(accum)
end end
insert!(args, pos, d)
empty!(accum)
end end
mm = extrema(length.(accum)) pos += 1
if mm[1] == 0
# empty Dataset
@assert mm[1] == mm[2] "At least one input array is empty, while other(s) are not"
d = DatasetEmpty()
else
d = Dataset(accum)
end
insert!(args, pos, d)
end end
pos += 1
end end
# Fourth pass: collect PlotElement objects # Fourth pass: collect PlotElement objects
@@ -1234,12 +1278,6 @@ function driver(_args...; is3d=false)
return source return source
end end
if length(_args) == 0
gp = getsession()
execall(gp)
return SessionID(gp.sid, true)
end
(sid, doReset, doDump, elems) = parseArguments(_args...) (sid, doReset, doDump, elems) = parseArguments(_args...)
gp = getsession(sid) gp = getsession(sid)
doReset && reset(gp) doReset && reset(gp)
@@ -1263,6 +1301,7 @@ function driver(_args...; is3d=false)
end end
end end
# Add commands and plot specifications
for elem in elems for elem in elems
(elem.mid > 0) && setmulti(gp, elem.mid) (elem.mid > 0) && setmulti(gp, elem.mid)
gp.plots[gp.curmid].is3d = (is3d | elem.is3d) gp.plots[gp.curmid].is3d = (is3d | elem.is3d)
@@ -1293,7 +1332,9 @@ function driver(_args...; is3d=false)
end end
end end
(doDump) && (execall(gp)) if options.gpviewer && doDump
execall(gp)
end
return SessionID(gp.sid, doDump) return SessionID(gp.sid, doDump)
end end
@@ -1306,7 +1347,7 @@ end
Return the **Gnuplot.jl** package version. Return the **Gnuplot.jl** package version.
""" """
version() = v"1.2.0-rc" version() = v"1.4.0"
# --------------------------------------------------------------------- # ---------------------------------------------------------------------
""" """
@@ -1470,66 +1511,82 @@ end
# -------------------------------------------------------------------- # --------------------------------------------------------------------
""" """
save(sid::Symbol; term="", output="") save([sid::Symbol]; term="", output="")
save(sid::Symbol, script_filename::String, ;term="", output="") save([sid::Symbol,] mime::Type{T}; output="") where T <: MIME
save(; term="", output="") save([sid::Symbol,] script_filename::String, ;term="", output="")
save(script_filename::String ;term="", output="")
Export a (multi-)plot into the external file name provided in the `output=` keyword. The gnuplot terminal to use is provided through the `term=` keyword. Export a (multi-)plot into the external file name provided in the `output=` keyword. The gnuplot terminal to use is provided through the `term=` keyword or the `mime` argument. In the latter case the proper terminal is set according to the `Gnuplot.options.mime` dictionary.
If the `script_filename` argument is provided a *gnuplot script* will be written in place of the output image. The latter can then be used in a pure gnuplot session (Julia is no longer needed) to generate exactly the same original plot. If the `script_filename` argument is provided a *gnuplot script* will be written in place of the output image. The latter can then be used in a pure gnuplot session (Julia is no longer needed) to generate exactly the same original plot.
If the `sid` argument is provided the operation applies to the corresponding session. If the `sid` argument is provided the operation applies to the corresponding session, otherwise the default session is considered.
Example:
```julia
@gp hist(randn(1000))
save(MIME"text/plain")
save(term="pngcairo", output="output.png")
save("script.gp")
```
""" """
save( ; kw...) = execall(getsession() ; kw...) save( ; kw...) = execall(getsession() ; kw...)
save(sid::Symbol; kw...) = execall(getsession(sid); kw...) save(sid::Symbol; kw...) = execall(getsession(sid); kw...)
save( file::AbstractString; kw...) = savescript(getsession() , file, kw...) save( file::AbstractString; kw...) = savescript(getsession() , file, kw...)
save(sid::Symbol, file::AbstractString; kw...) = savescript(getsession(sid), file, kw...) save(sid::Symbol, file::AbstractString; kw...) = savescript(getsession(sid), file, kw...)
save(mime::Type{T}; kw...) where T <: MIME = save(options.default, mime; kw...)
function save(sid::Symbol, mime::Type{T}; kw...) where T <: MIME
if mime in keys(options.mime)
term = strip(options.mime[mime])
if term != ""
return save(sid; term=term, kw...)
end
end
@error "No terminal is defined for $mime. Check `Gnuplot.options.mime` dictionary."
end
# ╭───────────────────────────────────────────────────────────────────╮ # ╭───────────────────────────────────────────────────────────────────╮
# │ Interfacing Julia's show │ # │ Interfacing Julia's show │
# ╰───────────────────────────────────────────────────────────────────╯ # ╰───────────────────────────────────────────────────────────────────╯
# -------------------------------------------------------------------- # --------------------------------------------------------------------
#= function show(obj::SessionID)
# Define a display that will be used when Gnuplot.jl is used gp = getsession(obj.sid)
# in the Julia REPL (see PGFPlotsX.jl). @info "Gnuplot session" sid=gp.sid datasets=length(gp.datas) plots=length(gp.plots)
struct GnuplotDisplay <: AbstractDisplay end nothing
function __init__() end
pushdisplay(GnuplotDisplay()) show(io::IO, gp::SessionID) = nothing
atreplinit(i -> begin
if PlotDisplay() in Base.Multimedia.displays
popdisplay(GnuplotDisplay()) function showable(mime::Type{T}, gp::SessionID) where T <: MIME
if gp.dump && !options.gpviewer
if mime in keys(options.mime)
term = strip(options.mime[mime])
if term != ""
return true
end
end end
pushdisplay(GnuplotDisplay()) end
end) return false
end end
function Base.display(d::GnuplotDisplay, gp::Session)
execall(gp) function internal_show(io::IO, mime::Type{T}, gp::SessionID) where T <: MIME
return if showable(mime, gp)
end term = strip(options.mime[mime])
=# file = tempname()
Base.show(gp::SessionID) = nothing save(gp.sid, term=term, output=file)
Base.show(io::IO, gp::SessionID) = nothing write(io, read(file))
function Base.show(io::IO, ::MIME"image/svg+xml", gp::SessionID) rm(file; force=true)
if gp.dump && enableExportThroughShow()
tmpfile = tempname()*".svg"
save(gp.sid; term=options.term_svg, output=tmpfile)
write(io, read(tmpfile))
rm(tmpfile; force=true)
end
nothing
end
function Base.show(io::IO, ::MIME"image/png", gp::SessionID)
if gp.dump && enableExportThroughShow()
tmpfile = tempname()*".png"
save(gp.sid; term=options.term_png, output=tmpfile)
write(io, read(tmpfile))
rm(tmpfile; force=true)
end end
nothing nothing
end end
show(io::IO, mime::MIME"image/svg+xml", gp::SessionID) = internal_show(io, typeof(mime), gp)
show(io::IO, mime::MIME"image/png" , gp::SessionID) = internal_show(io, typeof(mime), gp)
show(io::IO, mime::MIME"text/html" , gp::SessionID) = internal_show(io, typeof(mime), gp)
# ╭───────────────────────────────────────────────────────────────────╮ # ╭───────────────────────────────────────────────────────────────────╮
# │ HIGH LEVEL FACILITIES │ # │ HIGH LEVEL FACILITIES │
# ╰───────────────────────────────────────────────────────────────────╯ # ╰───────────────────────────────────────────────────────────────────╯
@@ -1642,17 +1699,19 @@ end
""" """
palette(cmap::ColorScheme; rev=false) palette(cmap::ColorScheme; rev=false, smooth=false)
palette(s::Symbol; rev=false) palette(s::Symbol; rev=false, smooth=false)
Convert a `ColorScheme` object into a string containing the gnuplot commands to set up the corresponding palette. Convert a `ColorScheme` object into a string containing the gnuplot commands to set up the corresponding palette.
If the argument is a `Symbol` it is interpreted as the name of one of the predefined schemes in [ColorSchemes](https://juliagraphics.github.io/ColorSchemes.jl/stable/basics/#Pre-defined-schemes-1). If `rev=true` the palette is reversed. If the argument is a `Symbol` it is interpreted as the name of one of the predefined schemes in [ColorSchemes](https://juliagraphics.github.io/ColorSchemes.jl/stable/basics/#Pre-defined-schemes-1).
If `rev=true` the palette is reversed. If `smooth=true` the palette is interpolated in 256 levels.
""" """
palette(s::Symbol; rev=false) = palette(colorschemes[s], rev=rev) palette(s::Symbol; kwargs...) = palette(colorschemes[s]; kwargs...)
function palette(cmap::ColorScheme; rev=false) function palette(cmap::ColorScheme; rev=false, smooth=false)
levels = Vector{String}() levels = Vector{String}()
for x in LinRange(0, 1, length(cmap.colors)) for x in LinRange(0, 1, (smooth ? 256 : length(cmap.colors)))
if rev if rev
color = get(cmap, 1-x) color = get(cmap, 1-x)
else else
@@ -1860,6 +1919,35 @@ function hist(v1::Vector{T1}, v2::Vector{T2};
return Histogram2D(x1, x2, hh.weights, binsize1, binsize2) return Histogram2D(x1, x2, hh.weights, binsize1, binsize2)
end end
# Allow missing values in input
function hist(v::Vector{Union{Missing,T}}; kw...) where T <: Real
ii = findall(.!ismissing.(v))
@info "Neglecting missing values ($(length(v) - length(ii)))"
hist(convert(Vector{T}, v[ii]); kw...)
end
function hist(v1::Vector{Union{Missing,T1}}, v2::Vector{T2}; kw...) where {T1 <: Real, T2 <: Real}
ii = findall(.!ismissing.(v1) .&
.!ismissing.(v2) )
@info "Neglecting missing values ($(length(v1) - length(ii)))"
hist(convert(Vector{T1}, v1[ii]), convert(Vector{T2}, v2[ii]), kw...)
end
function hist(v1::Vector{T1}, v2::Vector{Union{Missing, T2}}; kw...) where {T1 <: Real, T2 <: Real}
ii = findall(.!ismissing.(v1) .&
.!ismissing.(v2) )
@info "Neglecting missing values ($(length(v1) - length(ii)))"
hist(convert(Vector{T1}, v1[ii]), convert(Vector{T2}, v2[ii]), kw...)
end
function hist(v1::Vector{Union{Missing,T1}}, v2::Vector{Union{Missing,T2}}; kw...) where {T1 <: Real, T2 <: Real}
ii = findall(.!ismissing.(v1) .&
.!ismissing.(v2) )
@info "Neglecting missing values ($(length(v1) - length(ii)))"
hist(convert(Vector{T1}, v1[ii]), convert(Vector{T2}, v2[ii]), kw...)
end
# -------------------------------------------------------------------- # --------------------------------------------------------------------
""" """
@@ -1929,23 +2017,25 @@ struct IsoContourLines
paths::Vector{Path2d} paths::Vector{Path2d}
data::Dataset data::Dataset
z::Float64 z::Float64
function IsoContourLines(paths::Vector{Path2d}, z) prob::Float64
@assert length(z) == 1 end
# Prepare Dataset object function IsoContourLines(paths::Vector{Path2d}, z)
data = Vector{String}() @assert length(z) == 1
for i in 1:length(paths) # Prepare Dataset object
append!(data, arrays2datablock(paths[i].x, paths[i].y, z .* fill(1., length(paths[i].x)))) data = Vector{String}()
push!(data, "") for i in 1:length(paths)
push!(data, "") append!(data, arrays2datablock(paths[i].x, paths[i].y, z .* fill(1., length(paths[i].x))))
end push!(data, "")
return new(paths, DatasetText(data), z) push!(data, "")
end end
return IsoContourLines(paths, DatasetText(data), z, NaN)
end end
""" """
contourlines(x::Vector{Float64}, y::Vector{Float64}, z::Matrix{Float64}, cntrparam="level auto 10") contourlines(x, y, z, cntrparam="level auto 4")
contourlines(h::Histogram2D, cntrparam="level auto 10") contourlines(x, y, z, fractions)
contourlines(h::Histogram2D, ...)
Compute paths of contour lines for 2D data, and return a vector of [`IsoContourLines`](@ref) object. Compute paths of contour lines for 2D data, and return a vector of [`IsoContourLines`](@ref) object.
@@ -1953,16 +2043,19 @@ Compute paths of contour lines for 2D data, and return a vector of [`IsoContourL
This feature is not available in *dry* mode and will raise an error if used. This feature is not available in *dry* mode and will raise an error if used.
# Arguments: # Arguments:
- `x`, `y`: Coordinates; - `x`, `y` (as `AbstractVector{Float64}`): Coordinates;
- `z`: the levels on which iso contour lines are to be calculated - `z::AbstractMatrix{Float64}`: the levels on which iso-contour lines are to be calculated;
- `cntrparam`: settings to compute contour line paths (see gnuplot documentation for `cntrparam`). - `cntrparam::String`: settings to compute contour line paths (see gnuplot documentation for `cntrparam`);
- `fractions::Vector{Float64}`: compute contour lines encompassing these fractions of total counts;
- `h::Histogram2D`: use histogram bins and counts to compute contour lines.
# Example # Example
```julia ```julia
x = randn(5000); x = randn(10^5);
y = randn(5000); y = randn(10^5);
h = hist(x, y, nbins1=20, nbins2=20); h = hist(x, y, nbins1=20, nbins2=20);
clines = contourlines(h, "levels discrete 15, 30, 45"); clines = contourlines(h, "levels discrete 500, 1500, 2500");
# Use implicit recipe # Use implicit recipe
@gp clines @gp clines
@@ -1972,14 +2065,45 @@ clines = contourlines(h, "levels discrete 15, 30, 45");
for i in 1:length(clines) for i in 1:length(clines)
@gp :- clines[i].data "w l t '\$(clines[i].z)' lw \$i dt \$i" @gp :- clines[i].data "w l t '\$(clines[i].z)' lw \$i dt \$i"
end end
# Calculate probability within 0 < r < σ
p(σ) = round(1 - exp(-(σ^2) / 2), sigdigits=3)
# Draw contour lines at 1, 2 and 3 σ
clines = contourlines(h, p.(1:3));
@gp palette(:beach, smooth=true, rev=true) "set grid front" "set size ratio -1" h clines
``` ```
""" """
contourlines(h::Histogram2D, args...) = contourlines(h.bins1, h.bins2, h.counts, args...) contourlines(h::Histogram2D, args...) = contourlines(h.bins1, h.bins2, h.counts, args...)
function contourlines(x::Vector{Float64}, y::Vector{Float64}, z::Matrix{Float64}, function contourlines(x::AbstractVector{Float64}, y::AbstractVector{Float64}, z::AbstractMatrix{Float64},
cntrparam="level auto 10") fraction::Vector{Float64})
@assert minimum(fraction) > 0
@assert maximum(fraction) < 1
@assert length(fraction) >= 1
sorted_fraction = sort(fraction, rev=true)
i = sortperm(z[:], rev=true)
topfrac = cumsum(z[i]) ./ sum(z)
selection = Int[]
for f in sorted_fraction
push!(selection, minimum(findall(topfrac .>= f)))
end
levels = z[i[selection]]
clines = contourlines(x, y, z, "levels discrete " * join(string.(levels), ", "))
@assert issorted(getfield.(clines, :z))
if length(clines) == length(fraction)
out = [IsoContourLines(clines[i].paths, clines[i].data, clines[i].z,
sorted_fraction[i]) for i in 1:length(clines)]
return out
end
return clines
end
function contourlines(x::AbstractVector{Float64}, y::AbstractVector{Float64}, z::AbstractMatrix{Float64},
cntrparam="level auto 4")
lines = gp_write_table("set contour base", "unset surface", lines = gp_write_table("set contour base", "unset surface",
"set cntrparam $cntrparam", x, y, z, is3d=true) "set cntrparam $cntrparam", x, y, z, is3d=true)
level = NaN level = NaN
path = Path2d() path = Path2d()
paths = Vector{Path2d}() paths = Vector{Path2d}()
@@ -2025,6 +2149,105 @@ function contourlines(x::Vector{Float64}, y::Vector{Float64}, z::Matrix{Float64}
end end
# --------------------------------------------------------------------
"""
dgrid3d(x, y, z, opts=""; extra=true)
Interpolate non-uniformly spaced 2D data onto a regular grid.
!!! note
This feature is not available in *dry* mode and will raise an error if used.
# Arguments:
- `x`, `y`, `z` (as `AbstractVector{Float64}`): coordinates and values of the function to interpolate;
- `opts`: interpolation settings (see gnuplot documentation for `dgrid3d`);
- `extra`: if `true` (default) compute inerpolated values in all regions, even those which are poorly constrained by input data (namely, extrapolated values). If `false` set these values to `NaN`.
# Return values:
A tuple with `x` and `y` coordinates on the regular grid (as `Vector{Float64}`), and `z` containing interpolated values (as `Matrix{Float64}`).
# Example
```julia
x = (rand(200) .- 0.5) .* 3;
y = (rand(200) .- 0.5) .* 3;
z = exp.(-(x.^2 .+ y.^2));
# Interpolate on a 20x30 regular grid with splines
gx, gy, gz = dgrid3d(x, y, z, "20,30 splines")
@gsp "set size ratio -1" "set xyplane at 0" xlab="X" ylab="Y" :-
@gsp :- x y z "w p t 'Scattered data' lc pal"
@gsp :- gx gy gz "w l t 'Interpolation on a grid' lc pal"
```
!!! warn
The `splines` algorithm may be very slow on large datasets. An alternative option is to use a smoothing kernel, such as `gauss`:
```julia
x = randn(2000) .* 0.5;
y = randn(2000) .* 0.5;
rsq = x.^2 + y.^2;
z = exp.(-rsq) .* sin.(y) .* cos.(2 * rsq);
@gsp "set size ratio -1" palette(:balance, smooth=true) "set view map" "set pm3d" :-
@gsp :- "set multiplot layout 1,3" xr=[-2,2] yr=[-2,2] :-
@gsp :- 1 tit="Scattered data" x y z "w p notit lc pal"
# Show extrapolated values
gx, gy, gz = dgrid3d(x, y, z, "40,40 gauss 0.1,0.1")
@gsp :- 2 tit="Interpolation on a grid\\\\n(extrapolated values are shown)" gx gy gz "w l notit lc pal"
# Hide exrapolated values
gx, gy, gz = dgrid3d(x, y, z, "40,40 gauss 0.1,0.1", extra=false)
@gsp :- 3 tit="Interpolation on a grid\\\\n(extrapolated values are hidden)" gx gy gz "w l notit lc pal"
```
"""
function dgrid3d(x::AbstractVector{Float64},
y::AbstractVector{Float64},
z::AbstractVector{Float64},
opts::String="";
extra=true)
c = Gnuplot.gp_write_table("set dgrid3d $opts", x, y, z, is3d=true)
gx = Vector{Float64}()
gy = Vector{Float64}()
gz = Vector{Float64}()
ix = 0
iy = 0
for l in c
l = string(strip(l))
if l == "# x y z type"
ix += 1
iy = 1
else
(l == "" ) && continue
(l[1] == '#') && continue
n = Meta.parse.(split(l)[1:3])
(iy == 1) && push!(gx, n[1])
(ix == 1) && push!(gy, n[2])
if n[3] == :NaN
push!(gz, NaN)
else
push!(gz, n[3])
end
iy += 1
end
end
gz = collect(reshape(gz, length(gy), length(gx))')
if !extra
dx = abs(gx[2]-gx[1]) / 2
dy = abs(gy[2]-gy[1]) / 2
for ix in 1:length(gx)
for iy in 1:length(gy)
n = length(findall(((gx[ix] - dx) .< x .< (gx[ix] + dx)) .&
((gy[iy] - dy) .< y .< (gy[iy] + dy))))
(n == 0) && (gz[ix, iy] = NaN)
end
end
end
return (gx, gy, gz)
end
# ╭───────────────────────────────────────────────────────────────────╮ # ╭───────────────────────────────────────────────────────────────────╮
# │ GNUPLOT REPL │ # │ GNUPLOT REPL │
# ╰───────────────────────────────────────────────────────────────────╯ # ╰───────────────────────────────────────────────────────────────────╯
@@ -2086,7 +2309,7 @@ function gpvars(sid::Symbol)
if length(s) == 2 if length(s) == 2
key = Symbol(s[1]) key = Symbol(s[1])
if s[2][1] == '"' if s[2][1] == '"'
out[key] = s[2][2:end-1] out[key] = s[2][2:prevind(s[2], end, 1)]
else else
try try
out[key] = Meta.parse(s[2]) out[key] = Meta.parse(s[2])
+6 -1
View File
@@ -29,7 +29,12 @@ recipe(h::Histogram2D) =
Implicit recipes to visualize iso-contour lines. Implicit recipes to visualize iso-contour lines.
""" """
recipe(c::IsoContourLines) = PlotElement(data=c.data, plot="w l t '$(c.z)'") function recipe(c::IsoContourLines)
if isnan(c.prob)
return PlotElement(data=c.data, plot="w l t '$(c.z)'")
end
return PlotElement(data=c.data, plot="w l t '$(round(c.prob * 100, sigdigits=6))%'")
end
recipe(v::Vector{IsoContourLines}) = recipe.(v) recipe(v::Vector{IsoContourLines}) = recipe.(v)
+50 -9
View File
@@ -1,16 +1,15 @@
using Test, Gnuplot using Test, Gnuplot
try try
@info "Gnuplot version: " * string(Gnuplot.gpversion()) @info "Gnuplot.jl version: " * string(Gnuplot.version())
@info "gnuplot version: " * string(Gnuplot.gpversion())
catch catch
Gnuplot.options.dry = true Gnuplot.options.dry = true
end end
Gnuplot.options.term = "unknown" Gnuplot.options.gpviewer = true
x = [1, 2, 3] x = [1, 2, 3]
y = [4, 5, 6] y = [4, 5, 6]
s = Gnuplot.arrays2datablock(x) s = Gnuplot.arrays2datablock(x)
@test all(s .== [" 1" , @test all(s .== [" 1" ,
" 2" , " 2" ,
@@ -90,10 +89,30 @@ s = Gnuplot.arrays2datablock(1:3, 1:3, ["One", "Two", "Three"])
#----------------------------------------------------------------- #-----------------------------------------------------------------
dummy = palette_names()
pal = palette(:deepsea) pal = palette(:deepsea)
@test pal == "set palette defined (0.0 '#2B004D', 0.25 '#4E0F99', 0.5 '#3C54D4', 0.75 '#48A9F8', 1.0 '#C5ECFF')\nset palette maxcol 5\n" @test pal == "set palette defined (0.0 '#2B004D', 0.25 '#4E0F99', 0.5 '#3C54D4', 0.75 '#48A9F8', 1.0 '#C5ECFF')\nset palette maxcol 5\n"
ls = linetypes(:Set1_5) ls = linetypes(:Set1_5, lw=1.5, ps=2)
@test ls == "unset for [i=1:256] linetype i\nset linetype 1 lc rgb '#E41A1C' lw 1 dt solid pt 1 ps 1\nset linetype 2 lc rgb '#377EB8' lw 1 dt solid pt 2 ps 1\nset linetype 3 lc rgb '#4DAF4A' lw 1 dt solid pt 3 ps 1\nset linetype 4 lc rgb '#984EA3' lw 1 dt solid pt 4 ps 1\nset linetype 5 lc rgb '#FF7F00' lw 1 dt solid pt 5 ps 1\nset linetype cycle 5\n" @test ls == "unset for [i=1:256] linetype i\nset linetype 1 lc rgb '#E41A1C' lw 1.5 dt solid pt 1 ps 2\nset linetype 2 lc rgb '#377EB8' lw 1.5 dt solid pt 2 ps 2\nset linetype 3 lc rgb '#4DAF4A' lw 1.5 dt solid pt 3 ps 2\nset linetype 4 lc rgb '#984EA3' lw 1.5 dt solid pt 4 ps 2\nset linetype 5 lc rgb '#FF7F00' lw 1.5 dt solid pt 5 ps 2\nset linetype cycle 5\n"
dummy = terminals()
# if "sixelgd" in terminals()
# Gnuplot.options.term = "sixelgd enhanced"
# elseif "sixel" in terminals()
# Gnuplot.options.term = "sixel enhanced"
# elseif "dumb" in terminals()
# Gnuplot.options.term = "dumb"
# else
# Gnuplot.options.term = "unknown"
# end
# Gnuplot.quitall()
# Force unknown on Travis CI
Gnuplot.options.term = "unknown"
@gp 1:9
@info "using terminal: " terminal()
#test_terminal("unknown")
#----------------------------------------------------------------- #-----------------------------------------------------------------
# Test wth empty dataset # Test wth empty dataset
@@ -119,9 +138,9 @@ Gnuplot.quitall()
@gp "plot sin(x)" "pl cos(x)" @gp "plot sin(x)" "pl cos(x)"
@gp "plo sin(x)" "s cos(x)" @gp "plo sin(x)" "s cos(x)"
@gp "plot sin(x)" :- @gp mar="0,1,0,1" "plot sin(x)"
@gp :- "plot cos(x)" @gp :- mar=gpmargins() "plot cos(x)"
@gp :- 0. 0.
@gp "plot sin(x)" 2 xr=(-2pi,2pi) "pause 2" "plot cos(4*x)" @gp "plot sin(x)" 2 xr=(-2pi,2pi) "pause 2" "plot cos(4*x)"
@@ -245,4 +264,26 @@ Gnuplot.quitall()
"splot x7, v, (u<0.5) ? -1 : sinc(x7,v) notitle", "splot x7, v, (u<0.5) ? -1 : sinc(x7,v) notitle",
"splot x8, v, (u<0.5) ? -1 : sinc(x8,v) notitle", "splot x8, v, (u<0.5) ? -1 : sinc(x8,v) notitle",
"splot x9, v, (u<0.5) ? -1 : sinc(x9,v) notitle") "splot x9, v, (u<0.5) ? -1 : sinc(x9,v) notitle")
x = randn(5000);
y = randn(5000);
h = hist(x, y, nbins1=20, nbins2=20);
clines = contourlines(h, "levels discrete 15, 30, 45");
@gp clines
@gp "set size ratio -1"
for i in 1:length(clines)
@gp :- clines[i].data "w l t '$(clines[i].z)' lw $i dt $i"
end
Gnuplot.options.verbose = true
@gp randn(10^6) randn(10^6)
@gp :- 0. 0.
Gnuplot.quit(:default)
Gnuplot.options.dry = true
@gp hist(randn(1000))
Gnuplot.quitall() Gnuplot.quitall()