[
  {
    "path": ".github/dependabot.yml",
    "content": "# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates\nversion: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n  - package-ecosystem: \"julia\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: ci\n\non:\n  pull_request:\n  workflow_dispatch:\n  push:\n    branches: [master]\n\nconcurrency: \n  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}\n  cancel-in-progress: true\n\npermissions:\n  actions: write\n  contents: read\n\njobs:\n  test:\n    name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }}\n    continue-on-error: ${{ matrix.experimental }}\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        version:\n          - 'lts'\n          - '1'\n        experimental:\n          - false\n        os: [ubuntu-latest, windows-latest, macos-latest]\n        arch: [x64]\n        include:\n          - os: ubuntu-latest\n            experimental: true\n            version: 'pre'\n            arch: x64\n\n    steps:\n      - uses: actions/checkout@v6\n      - uses: julia-actions/setup-julia@v3\n        with:\n          version: ${{ matrix.version }}\n          arch: ${{ matrix.arch }}\n      - uses: julia-actions/cache@v3\n      - uses: julia-actions/julia-buildpkg@latest\n      - uses: julia-actions/julia-runtest@latest\n      - uses: julia-actions/julia-processcoverage@latest\n      - uses: codecov/codecov-action@v6\n        with:\n          token: ${{ secrets.CODECOV_TOKEN }}\n          fail_ci_if_error: false\n          files: lcov.info\n"
  },
  {
    "path": ".github/workflows/tagbot.yml",
    "content": "name: tagbot\n\non:\n  issue_comment:\n    types: ['created']\n  workflow_dispatch:\n    inputs:\n      lookback:\n        default: 10\n\njobs:\n  tagbot:\n    if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: JuliaRegistries/TagBot@v1\n        with:\n          token: ${{ secrets.GITHUB_TOKEN }}\n          ssh: ${{ secrets.DOCUMENTER_KEY }}\n"
  },
  {
    "path": ".gitignore",
    "content": "docs/build\n\nManifest-v*.toml\nManifest.toml\n\ndocs/v2/.quarto/\ndocs/v2/_site/\ndocs/v1/_site/\ndocs/v1/assets\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "New in version 2\n================\n\nGaston v2 is a breaking release. A\n[migration guide](https://mbaz.github.io/Gaston.jl/v2/migrate.html) is available.\n\n* A recipe system for plotting arbitrary Julia types.\n  * Recipes can be written without depending on Gaston using a new package,\n    GastonRecipes, which is very small, has only one dependency (`MacroTools`),\n    and no exports.\n* New, simpler but more flexible syntax.\n  * Axis settings and curve appearance are distinguished by their position in the\n    `plot` command (before and after data, respectively). Example:\n\n    `plot(\"set grid\", x, y, \"linecolor 'blue'\")`\n\n  * `@plot` and `@splot` macros that take key-value arguments, as in:\n\n    `@plot {title = Q\"A Plot\"} x y {with = \"lp\"}`\n\n  * The string macro `Q_str` converts string `Q\"A b\"` to `\"'A b'\"`\n  * The macro `@gpkw` allows any plot command to take key-value arguments:\n    `@gpkw surf({grid}, x, y, z)`\n  * Support for gnuplot datasets, including reading back data after plotting\n    `with table`.\n* Overhauled multiplot support.\n  * Axes are placed by indexing a figure: `plot(f[2], x, y)` will place the plot\n    as the second axis in figure `f`.\n  * Curves can be pushed into axes and axes into figures arbitrarily.\n  * `p1 = plot(...); p2 = plot(...); plot(p1, p2)` is also supported (creates a\n    multiplot with figures `p1` and `p2`.\n  * Automatic layout keeps a square aspect ratio, or user may take complete\n    control over layout.\n* Every figure is backed up by a separate gnuplot process.\n* Simpler interace for saving plots: `save([figure,], filename, terminal)`.\n* Better support for animations in notebooks.\n* Support for themes.\n  * More than 20 pre-defined plotting styles (`surf`, `heatmap`, `histogram`, etc)\n    based on built-in themes.\n* Simpler configuration, by changing values of a few `Gaston.config` fields.\n* Re-written [documentation](https://mbaz.github.io/Gaston.jl/v2/).\n\n# version 1.1.2\n\n* Bug fixes\n\n# version 1.1.1\n\n* Bug fixes\n\n# version 1.1\n\n* Require Julia 1.6\n\n# version 1.0.6\n\n* Bug fixes\n\n# version 1.0.5\n\n* Bug fixes\n\n# version 1.0.4\n\n* Bug fixes\n\n# version 1.0.3\n\n* Bug fixes\n\n# version 1.0.2\n\n* Bug fixes\n\n# version 1.0.1\n\n* Bug fixes\n\n# version 1.0\n\n* New plot syntax, using key-value pairs for line configuration and\n  Axes() for axis settings.\n* Parse key-value pairs to extend/simplify gnuplot's syntax\n* Use palettes from colorschemes\n* Generate linetypes from colorschemes\n* Gaston now does not validate that commands/data sent to gnuplot is valid.\n  This opens the door to a much simplified and more flexible design.\n* Extended support for multiplot.\n* Bug fixes\n* Debug mode\n* New plot styles\n* Support for more terminals\n\n# version 0.10\n\n* Bug fixes\n* Introduce precompilation\n* Refactor exceptions to use correct types\n* Improve terminal configuration\n* Extended support for gnuplot terminals\n* More plot styles\n\n# version 0.9\n\n* Bug fixes\n* Add ranges to imagesc\n* Default to svg in notebooks\n\n\n# version 0.7.4\n\n* Add support for `dumb` text terminal\n* Add a `null` terminal that does not display anything\n* Tests for 0.7 pass\n\n# version 0.7.3\n\n* fix default size for pdf-like terminals.\n\n# version 0.7.2\n\n* add `linestyle` option (corresponds to gnuplot's `dashtype`)\n* update docs\n* fix indexing bug in image plotting\n\n# version 0.7.1\n\n* add `palette` option\n* Use empty string as defaults for axis labels and title\n* Add missing `plot!()` commands\n* Update .travis.yml\n* Fix tempdir problem in Windows\n* Update changelog\n\n# version 0.7\n\n* Require Julia 0.6\n* New tutorial\n* New syntax for plotting\n* New `set` command to set defaults\n* Add support for plotting complex vectors\n* Improve and add tests\n* Many internal fixes and code optimization\n\n# version 0.6\n\n* Add support for grids\n* Fix deprecations\n* Restore histogram functionality broken by Julia update\n* Remove support for Julia 0.3\n\n# version 0.5.7\n\n* Update tests to use Julia's infrastructure\n\n# version 0.5.6\n\n* Require Julia 0.5.\n\n# version 0.5.5\n\n* Update syntax again. Convert into a Julia package.\n\n# version 0.5.4\n\n* Update syntax to keep up with Julia.\n\n# version 0.5.3:\n\nUser visible:\n\n* New terminals: aqua (OS X only) and EPS.\n* Improved documentation.\n* Compatibility with latest Julia syntax changes.\n\nUnder the hood:\n\n* A few bug fixes and performance improvements.\n\n# version 0.5:\n\nUser visible:\n\n* New high-level command imagesc.\n* New high-level command surf.\n* Support for printing to file; SVG, PNG, GIF and PDF formats supported.\n* Add support for 'dots' plotstyle.\n* Add a test framework and 93 tests.\n* Remove artificial restrictions on mixing many images and curves on the\n  same figure.\n* Images support explicit x, y coordinates.\n* Updated and improved documentation.\n\nUnder the hood:\n\n* A few small bug fixes.\n* Code has been simplified in many places.\n\n# version 0.4:\n\nUser visible:\n\n* Add support for x11 terminal\n* Add support for printing to files (gif and svg)\n* Add support for setting default values for all plot properties\n* Improved documentation\n\nUnder the hood:\n\n* Improved detection of invalid configurations\n* No longer require 'figs' global variable\n* Add new 'config' type to store configuration: this will allow the user\n  to configure many aspects of Gaston's behavior\n* File organization has been completely revamped\n\n# version 0.3:\n\n* Add 'high-level' plot() and histogram() functions\n* Add error checking of arguments and types, to minimise risk of gnuplot\n\tbarfing on us on misconfigured plots\n* Change type names to conform to Julia conventions (no underscores)\n* Improved PDF documentation\n* Fixed a few bugs\n\n# version 0.2:\n\n* Add support for histograms, via the \"boxes\" plot style.\n* Add example histogram to demos.\n* Add support for rgbimage plot style\n* Add rgbimage example to demos.\n* Fix bug (issue #1 on bitbucket) in the way figure handles were used.\n"
  },
  {
    "path": "COPYING",
    "content": "Copyright (c) 2012 Miguel Bazdresch\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "Copyright (c) 2013 Miguel Bazdresch\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of\nthis software and associated documentation files (the \"Software\"), to deal in\nthe Software without restriction, including without limitation the rights to\nuse, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of\nthe Software, and to permit persons to whom the Software is furnished to do so,\nsubject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS\nFOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR\nCOPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\nIN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\nCONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n"
  },
  {
    "path": "Project.toml",
    "content": "name = \"Gaston\"\nuuid = \"4b11ee91-296f-5714-9832-002c20994614\"\nversion = \"2.0.2\"\n\n[deps]\nColorSchemes = \"35d6a980-a343-548e-a6ea-1d62b119f2f4\"\nDelimitedFiles = \"8bb1440f-4735-579b-a4ab-409b98df4dab\"\nGastonRecipes = \"39356fd2-1f9e-4efe-8abf-5745c7d9f608\"\nGnuplot_jll = \"e5af9688-3aeb-5ed5-8c7e-253e55323d3e\"\nMacroTools = \"1914dd2f-81c6-5fcd-8719-6d5c9610ff09\"\nPrecompileTools = \"aea7be01-6a6a-4083-8856-8a6e6704d82a\"\nPreferences = \"21216c6a-2e73-6563-6e65-726566657250\"\nStatsBase = \"2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91\"\n\n[compat]\nGastonRecipes = \"1\"\nGnuplot_jll = \"60.0.4000\"\nColorSchemes = \"3.27\"\nDelimitedFiles = \"1\"\nMacroTools = \"0.5\"\nPrecompileTools = \"1\"\nPreferences = \"1\"\nStatsBase = \"0.34\"\njulia = \"1.10\"\n"
  },
  {
    "path": "README.md",
    "content": "[![Docs stable](https://img.shields.io/badge/docs-stable-blue.svg)](\n  https://mbaz.github.io/Gaston.jl/v2/\n)\n[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat)](\n  LICENSE.txt\n)\n<br>\n[![CI](https://github.com/mbaz/Gaston.jl/actions/workflows/ci.yml/badge.svg)](\n  https://github.com/mbaz/Gaston.jl/actions/workflows/ci.yml\n)\n[![Coverage Status](https://codecov.io/gh/mbaz/Gaston.jl/branch/master/graphs/badge.svg?branch=master)](\n  https://app.codecov.io/gh/mbaz/Gaston.jl\n)\n[![PkgEval](https://juliaci.github.io/NanosoldierReports/pkgeval_badges/G/Gaston.named.svg)](\n  https://juliaci.github.io/NanosoldierReports/pkgeval_badges/G/Gaston.html\n)\n<br>\n[![JuliaHub deps](https://juliahub.com/docs/General/Gaston/stable/deps.svg)](\n  https://juliahub.com/ui/Packages/General/Gaston?t=2\n)\n[![Downloads](https://img.shields.io/badge/dynamic/json?url=http%3A%2F%2Fjuliapkgstats.com%2Fapi%2Fv1%2Fmonthly_downloads%2FGaston&query=total_requests&suffix=%2Fmonth&label=Downloads)](\n  https://juliapkgstats.com/pkg/Gaston\n)\n\nGaston: Julia plotting using gnuplot\n==================================== \n\nGaston is a [Julia](https://julialang.org) package for plotting. It provides an interface to [gnuplot](http://gnuplot.info), a powerful plotting package available on all major platforms.\n\nCurrent stable release is v2.0, and it has been tested with Julia LTS (1.10) and stable (1.11), on\nLinux. Gaston _should_ work on any platform that runs julia and gnuplot.\n\nVersion 1.1.2 runs with Julia 1.6 and later, but it is no longer maintained. All\nusers are encouraged to move to version 2.\n\nDocumentation\n-------------\n\n`Gaston.jl`'s documentation can be found [here](https://mbaz.github.io/Gaston.jl/v2/).\n\nThe documentation for the older v1.x releases is [here](https://mbaz.github.io/Gaston.jl/v1/).\n\nWhy use Gaston?\n--------------\n\nWhy use Gaston, when there are powerful alternatives such as Plots.jl and Makie.jl? These are some Gaston features that may be attractive to you:\n\n* Gaston can plot:\n    * Using graphical windows, and keeping multiple plots active at a time, with mouse interaction\n    * Arbitrary Julia types, using recipes.\n    * Directly to the REPL, using text (ASCII) or sixels\n    * In Jupyter, Pluto and other IDEs\n* Supports popular 2-D plots: regular function plots, stem, step, histograms, images, etc.\n* Supports surface, contour and heatmap 3-D plots.\n* Can save plots to multiple formats, including pdf, png and svg.\n* Provides a simple interface for knowledgeable users to access gnuplot features.\n* It is fast.\n\nInstallation\n------------\n\nGaston requires gnuplot to be installed in your system. It has been tested with versions 5.4 and above, but it should work with any recent version. Gnuplot version\n6 is recommended.\n\nTo install Gaston using Julia's packaging system, enter Julia's package manager prompt with `]`, and run\n\n    pkg> add Gaston\n\nContributing\n------------\n\nContributions are encouraged, in the form of issue reports, pull requests, new\ntests, and new recipes.\n\n"
  },
  {
    "path": "docs/notebook/how-to-plot-a-torus.jl",
    "content": "### A Pluto.jl notebook ###\n# v0.20.10\n\nusing Markdown\nusing InteractiveUtils\n\n# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).\nmacro bind(def, element)\n    #! format: off\n    return quote\n        local iv = try Base.loaded_modules[Base.PkgId(Base.UUID(\"6e696c72-6542-2067-7265-42206c756150\"), \"AbstractPlutoDingetjes\")].Bonds.initial_value catch; b -> missing; end\n        local el = $(esc(element))\n        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)\n        el\n    end\n    #! format: on\nend\n\n# ╔═╡ 4bd21a1f-900c-4837-b121-6e708ed9e178\nimport Pkg\n\n# ╔═╡ 4a857a54-37f5-45d5-a715-95614ee569fe\n# ╠═╡ show_logs = false\nPkg.add(\"PlutoUI\")\n\n# ╔═╡ fd4dbf2c-3e9d-4a9e-8d45-0976c6fbd46c\n# ╠═╡ show_logs = false\nPkg.develop(path=\"/home/miguel/rcs/jdev/Gaston\")\n\n# ╔═╡ 2fd048bc-2e87-490a-afc2-5c216d260e77\nusing Gaston\n\n# ╔═╡ 4ccbcb36-2fb7-495a-ac80-3d4eab0f4471\nusing PlutoUI\n\n# ╔═╡ 05614214-fb38-4170-acfe-66b90b88c283\nmd\"# Gaston demo/tutorial\n\n## How to plot one (or two) torus\n\nThis demo/tutorial shows how to use Gaston in a Pluto notebook, including interactive plots. Some auxiliary functions used throughout the tutorial are defined at the end of the notebook. \n\nLet's start by loading Gaston and PlutoUI.\"\n\n# ╔═╡ 8fa8505a-98c8-46bf-a716-dba45626b98a\nPlutoUI.TableOfContents(title = \"Contents\")\n\n# ╔═╡ a87cb7e3-18e4-49d3-a8db-31b7bbeacae0\nimport LinearAlgebra: norm, cross, dot\n\n# ╔═╡ 65a53ffa-c8c0-448b-957a-ea9c55928057\nmd\"\"\"### Step 1: plotting a circle in three dimensions\n\nOur first task is to draw circles of arbitrary size at arbitrary postions in space.\n\nConsider a circle of radius $r$ centered at $p \\in \\mathbb{R}^3$ on the plane defined by the orthonormal vectors $\\mathbf{v}_1$ and $\\mathbf{v}_2$. The parametric equation of this circle is\n\n$[x, y, z] = p + r\\cos(t)\\mathbf{v}_1 + r\\sin(t)\\mathbf{v}_2.$\n\nLet's test this out:\n\"\"\"\n\n# ╔═╡ 053a7600-c582-4498-a848-e704fc1dd927\nmd\"\"\"#### Parameters\ncenter x: $(@bind cx Slider(-5:0.1:5, default = 0, show_value = true))\n\ncenter y: $(@bind cy Slider(-5:0.1:5, default = 0, show_value = true))\n\ncenter z: $(@bind cz Slider(-5:0.1:5, default = 0, show_value = true))\n\nradius: $(@bind r Slider(0.1:0.1:3, default = 1, show_value = true))\n\norientation θ: $(@bind θ Slider(0:0.05:π, default = 0, show_value = true))\n\norientation ρ: $(@bind ρ Slider(0:0.05:2π, default = 0, show_value = true))\n\"\"\"\n\n# ╔═╡ 3480c76e-a643-4856-a51a-a4b621c7b545\nmd\"\"\"### Step 2: Plotting a donut frame\n\nNow that we know how to plot arbitrary circles, we'll draw a bunch of small circles making a ring around a larger one. These will form the frame of \"skeleton\" of the torus. \n\nLet's draw the frame of a torus of major radius $r_M$ and minor radius $r_m$.\"\"\"\n\n# ╔═╡ e7906ae5-e460-4d28-af8b-948415310d3d\nmd\"#### Radii\n\nrM $(@bind rM Slider(0.1:0.1:2, default = 1, show_value = true))\nrm $(@bind rm Slider(0.1:0.1:1, default = 0.1, show_value = true))\"\n\n# ╔═╡ 036ae8fa-e6ba-4d06-94f9-c64c7b87507f\nmd\"\"\"### Step 3: Plotting the torus' surface\n\nIn order to \"fill\" the torus' frame, we need to stack the coordinates of all the small circles and pass them to `splot`. Gnuplot will automatically create a wireframe connecting the vertices of neighboring circles. Using the `pm3d` plotstyle, gnuplot will apply surface and lighting effects to the torus frame. To see the wireframe, plot using `with lines`.\n\n\"\"\"\n\n# ╔═╡ 1de39361-2264-4bdd-8694-462d41436a81\nmd\"\"\"#### Center\ncx: $(@bind c2x Slider(-5:0.1:5, default = 0, show_value = true))\n\ncy: $(@bind c2y Slider(-5:0.1:5, default = 0, show_value = true))\n\ncz: $(@bind c2z Slider(-5:0.1:5, default = 0, show_value = true))\n\"\"\"\n\n# ╔═╡ 4a0cdeda-1d93-4be9-8e79-e1510a9fe1e7\nmd\"\"\"### Step 4: Rotate the torus\n\nNow, we will apply a rotation to the torus using angles along the x, y and z axes.\"\"\"\n\n# ╔═╡ 07294d8b-b01c-4528-8217-0572466f9eaa\nmd\"\"\"#### Center\ncx: $(@bind c3x Slider(-5:0.1:5, default = 0, show_value = true))\n\ncy: $(@bind c3y Slider(-5:0.1:5, default = 0, show_value = true))\n\ncz: $(@bind c3z Slider(-5:0.1:5, default = 0, show_value = true))\n\n#### Angle\n\nθx: $(@bind θx Slider(0:0.05:3.15, default = 0, show_value = true))\n\nθy: $(@bind θy Slider(0:0.05:3.15, default = 0, show_value = true))\n\nθz: $(@bind θz Slider(0:0.05:6.3, default = 0, show_value = true))\n\"\"\"\n\n# ╔═╡ 7e89c619-c951-45eb-adfc-8e91f42d7e60\nmd\"\"\"### Conclusion\n\nNow that we know how to plot arbitrary torii, let's draw two of them with high resolution.\"\"\"\n\n# ╔═╡ e47bb743-75ba-427d-9301-a15cea857be3\nmd\"Support code\"\n\n# ╔═╡ 22941298-cc85-45d1-9fc7-96b2a1e6d863\nbegin\n\tft = Figure(\"torus\")\n\tfti = Figure(\"torii\")\n\tnothing\nend\n\n# ╔═╡ 9d71d2ee-c194-49cf-ab41-c99500d36298\n\"Circle parametric equation\"\nfunction paramcircle(p, r, v1, v2, t)\n\tx = p[1] + r*cos(t)*v1[1] + r*sin(t)*v2[1]\n\ty = p[2] + r*cos(t)*v1[2] + r*sin(t)*v2[2]\n\tz = p[3] + r*cos(t)*v1[3] + r*sin(t)*v2[3]\n\treturn (x, y, z)\nend\n\n# ╔═╡ f55bf207-fad4-44b1-b5d9-d7efe686bf42\n\"\"\"Given 3-D orientation vector `o`, return orthonormal vectors\n   `v1`, `v2` that span the plane to which `o` is normal.\"\"\"\nfunction normals(o)\n\tex = [1., 0., 0.]; ey = [0., 1., 0.]; ez = [0., 0., 1.]\n\tox = o[1]; oy = o[2]; oz = o[3]\n\tif all(iszero, o)\n\t\tv1 = ey\n\telseif (ox == 0) && (oy == 0)\n\t\tv1 = sign(oz)*ey\n\telseif (ox == 0) && (oz == 0)\n\t\tv1 = -sign(oy)*ex\n\telseif (oy == 0) && (oz == 0)\n\t\tv1 = sign(ox)*ey\n\telse\n#\t\tif (oz > 0)\n#\t    \tv1 = [-sign(ox)*oy/ox, sign(ox), 0]\n#\t\telse\n#\t\t\tv1 = [oy/ox, -1, 0]\n#\t\tend\n\t\tv1 = [ox == 0 ? 0 : -sign(ox)*oy/ox, sign(ox), 0]\n\tend\n\tv2 = cross(collect(o), v1)\n\treturn (v1./norm(v1), v2./norm(v2))\nend\n\n# ╔═╡ 5cdd317a-de26-4093-8651-615bcb46ffa9\nlet\n\tc = [cx, cy, cz]; ox, oy, oz = sin(θ)*cos(ρ), sin(θ)*sin(ρ), cos(θ)\n\to_ = [ox, oy, oz]\n\to = 0.35 .* o_ ./ norm(o_)\n\tv1, v2 = normals(o)\n\tv1p = 0.35 .* v1; v2p = 0.35 .* v2;\n\tcir = stack(t -> paramcircle(c, r, v1, v2, t), range(0, 2π, 20), dims=1)\n\t@splot(:unitranges, {xyplane = \"at 0\"},\n\t       cx, cy, cz, ox, oy, oz, \"w vectors lc 'black'\")\n\tsplot!(cx, cy, cz, v1p[1], v1p[2], v1p[3], \"w vectors lc 'blue'\")\n\tsplot!(cx, cy, cz, v2p[1], v2p[2], v2p[3], \"w vectors lc 'dark-green'\")\n\tsplot!(cir[:,1], cir[:,2], cir[:,3], \"lc 'black'\")\n\tcr = cross(v1, v2)\n\tsplot!(cx, cy, cz, cr[1], cr[2], cr[3], \"w vectors lc 'red'\")\nend\n\n# ╔═╡ f4334764-5ada-424d-a0eb-8a9f7ee55c32\n\"\"\"Central diff of `f` at `t` with step `h = 1e-6`\"\"\"\nfunction cendiff(f, t, h = 1e-6)\n\t(f(t.+h/2) .- f(t.-h/2))./h\nend\n\n# ╔═╡ 398a6f8b-b783-4a38-82c9-b01012b347d4\nlet ft = ft\n\tN = 16 # number of circles to draw\n\t#c = [c2x, c2y, c2z] # torus center\n\t#ox, oy, oz = sin(θt)*cos(ρt), sin(θt)*sin(ρt), cos(θt)\n\t#o_ = [ox, oy, oz]\n\t#o = 0.2 .* o_ ./ norm(o_)\n\t#v1, v2 = normals(o)\n\tc = [0,0,0]; v1 = [1,0,0]; v2 = [0,1,0]\n\tpc(t) = paramcircle(c, rM, v1, v2, t)  # torus core\n\tcir = stack(pc, range(0, 2π, 20), dims=1)\n\t@splot(ft, :notics, :labels, {view=(60,30), view = \"equal xy\", ranges = (-3,3), xyplane = \"at 0\"}, cir[:,1], cir[:,2], cir[:,3])\n\t# center of each circle in the frame\n\tpcenters = range(0, 2π-2π/N, N)\n\tcenters = [paramcircle(c, rM, v1, v2, t) for t in pcenters]\n\tfor n in 1:N\n\t\t# orientation of each circle -- tangent to torus core\n\t\toc = cendiff(pc, pcenters[n])\n\t\tn1, n2 = normals(oc); #display((oc, n1,n2))\n\t\td = stack(t -> paramcircle(centers[n], rm, n1, n2, t), range(0,2π,10), dims=1)\n\t\t# plots\n\t\tsplot!(ft, d[:,1], d[:,2], d[:,3], \"lc 'black'\")\n\t\t#splot!(ft, d[1,1], d[1,2], d[1,3], \"w p pt '0'\")\n\t\t#splot!(ft, d[3,1], d[3,2], d[3,3], \"w p pt '3'\")\n\t\t#splot!(ft, d[:,1], d[:,2], d[:,3], \"lc 'black'\")\n\t\t#splot!(ft, centers[n][1], centers[n][2], centers[n][3], centers[n][1]+oc[1], centers[n][2]+oc[2], centers[n][3]+oc[3], \"w vectors lc 'green'\")\n\tend\n\tft\nend\n\n# ╔═╡ d25d04f9-c8c9-4b25-af01-1b67b4c1999f\nlet ft = ft\n\tres = 64 # points per circle\n\tN = 64 # number of circles to draw\n\tc = [c2x, c2y, c2z] # torus center\n\tox, oy, oz = sin(θ)*cos(ρ), sin(θ)*sin(ρ), cos(θ) # torus orientation\n\to_ = [ox, oy, oz]\n\to = 0.2 .* o_ ./ norm(o_)\n\tv1, v2 = normals(o)\n\tpc(t) = paramcircle(c, rM, v1, v2, t)  # torus core\n\tcir = stack(pc, range(0, 2π, res), dims=1)\n\t# center of each circle in the frame\n\tpcenters = range(0, 2π, N)\n\tcenters = [paramcircle(c, rM, v1, v2, t) for t in pcenters]\n\tx = zeros(res, N); y = zeros(res, N); z = zeros(res, N)\n\tfor n in 1:N\n\t\t# orientation of each circle -- tangent to torus core\n\t\toc = cendiff(pc, pcenters[n])\n\t\tn1, n2 = normals(oc)\n\t\td = stack(t -> paramcircle(centers[n], rm, n1, n2, t), range(0,2π,res), dims=1)\n\t\tx[:,n] .= d[:,1]; y[:,n] .= d[:,2]; z[:,n] .= d[:,3]\n\tend\n\t@splot(ft, {pm3d = \"depthorder\", pm3d = \"lighting\", ranges=(-3,3),\n\t            hidden3d, cbrange = (-rm,rm),\n\t            palette = :plasma,\n\t            xyplane = \"at 0\",\n\t            colorbox = false}, :notics,\n\t            x, y, z, \"w pm3d fillcolor 'dark-turquoise'\")\nend\n\n# ╔═╡ e6c5661f-c864-4834-9ac5-1fc1b799bed0\n\"\"\" torus\n\nCalculate torus coordinates.\"\"\"\nfunction torus(c, θx, θy, θz, rM, rm, N, n)\n\t# step 1: calculate a torus in the origin\n\tx, y, z = torus(rM, rm, N, n)\n\t# step 2: calculate rotation matrices\n\tRx = [1 0      0      ;\n\t      0 cos(θx) -sin(θx);\n\t      0 sin(θx)  cos(θx)]\n\tRy = [ cos(θy) 0 sin(θy);\n\t       0      1 0     ;\n\t      -sin(θy) 0 cos(θy)]\n\tRz = [cos(θz) -sin(θz) 0;\n\t      sin(θz)  cos(θz) 0;\n\t      0       0      1]\n\tR = Rz*Ry*Rx\n\t# step 3: rotate and translate each torus point\n\tp = zeros(3); q = zeros(3)\n\tfor col in 1:N\n\t\tfor row = 1:n\n\t\t\tp[1] = x[row, col]; p[2] = y[row, col]; p[3] = z[row, col]\n\t\t\tq = R*p;\n\t\t\tx[row, col] = q[1] + c[1]\n\t\t\ty[row, col] = q[2] + c[2]\n\t\t\tz[row, col] = q[3] + c[3]\n\t\tend\n\tend\n\tx, y, z\nend\n\n# ╔═╡ 1a407189-602e-4f79-9c68-8c0ec9527336\nfunction torus(rM, rm, N, n)\n\tc = (0, 0, 0)\n\tv1, v2 = normals([0, 0, 1])\n\t# calculate torus core\n\tcore(t) = paramcircle(c, rM, v1, v2, t)\n\t# find center of each torus slice\n\tpcenters = range(0, 2π, N)\n\tcenters = [paramcircle(c, rM, v1, v2, t) for t in range(0, 2π, N)]\n\tx = zeros(n, N); y = zeros(n, N); z = zeros(n, N)\n\td = zeros(n, 3)\n\tfor k in 1:N\n\t\t# orientation of each circle -- tangent to torus core\n\t\toc = cendiff(core, pcenters[k])\n\t\tn1, n2 = normals(oc)\n\t\td .= stack(t -> paramcircle(centers[k], rm, n1, n2, t), range(0, 2π, n), dims = 1)\n\t\tx[:,k] .= d[:,1]; y[:,k] .= d[:,2]; z[:,k] .= d[:,3]\n\tend\n\tx, y, z\nend\n\n# ╔═╡ 45904d7b-3b2e-411d-8715-372220b5cd00\nlet\n\tall(iszero, (θx, θy, θz)) && (θz = 1.0)\n\tc = [c3x, c3y, c3z] # torus center\n\tx, y, z = torus(c, θx, θy, θz, rM, rm, 32, 32)\n\tsplot(\"set xrange [-3:3]\", \"set yrange [-3:3]\", \"set zrange [-2:2]\", :labels, \"set pm3d depthorder\", \"set view 60,30\", \"set view equal xyz\", \"unset colorbox\", \"set pm3d lighting\", x, y, z, \"w pm3d fillcolor 'dark-turquoise'\")\nend\n\n# ╔═╡ 6b476e91-aa7d-48ad-8cee-00630818238a\nlet\n\tc1 = [0,0,0]; x1 = 0; y1 = 0; z1 = 1; rM1 = 1; rm1 = 0.4;\n\tc2 = [1,0,0]; x2 = π/2; y2 = 0; z2 = 0; rM2 = 1; rm2 = 0.4;\n\tt1x, t1y, t1z = torus(c1, x1, y1, z1, rM1, rm1, 128, 128)\n\tt2x, t2y, t2z = torus(c2, x2, y2, z2, rM2, rm2, 128, 128)\n\t@splot(fti, {pm3d = \"depthorder\",\n\t             pm3d = \"lighting\",\n\t             tics = false,\n\t             colorbox = false,\n\t             view = \"equal xyz\",\n\t             view = (40, 20, 2)},\n\t            t1x, t1y, t1z, \"w pm3d fillcolor 'dark-turquoise'\")\n\tsplot!(fti, t2x, t2y, t2z, \"w pm3d fillcolor 'salmon'\")\nend\n\n# ╔═╡ Cell order:\n# ╟─05614214-fb38-4170-acfe-66b90b88c283\n# ╠═4bd21a1f-900c-4837-b121-6e708ed9e178\n# ╠═4a857a54-37f5-45d5-a715-95614ee569fe\n# ╠═fd4dbf2c-3e9d-4a9e-8d45-0976c6fbd46c\n# ╠═2fd048bc-2e87-490a-afc2-5c216d260e77\n# ╠═4ccbcb36-2fb7-495a-ac80-3d4eab0f4471\n# ╠═8fa8505a-98c8-46bf-a716-dba45626b98a\n# ╠═a87cb7e3-18e4-49d3-a8db-31b7bbeacae0\n# ╟─65a53ffa-c8c0-448b-957a-ea9c55928057\n# ╟─053a7600-c582-4498-a848-e704fc1dd927\n# ╠═5cdd317a-de26-4093-8651-615bcb46ffa9\n# ╟─3480c76e-a643-4856-a51a-a4b621c7b545\n# ╟─e7906ae5-e460-4d28-af8b-948415310d3d\n# ╠═398a6f8b-b783-4a38-82c9-b01012b347d4\n# ╟─036ae8fa-e6ba-4d06-94f9-c64c7b87507f\n# ╟─1de39361-2264-4bdd-8694-462d41436a81\n# ╠═d25d04f9-c8c9-4b25-af01-1b67b4c1999f\n# ╟─4a0cdeda-1d93-4be9-8e79-e1510a9fe1e7\n# ╟─07294d8b-b01c-4528-8217-0572466f9eaa\n# ╠═45904d7b-3b2e-411d-8715-372220b5cd00\n# ╟─7e89c619-c951-45eb-adfc-8e91f42d7e60\n# ╠═6b476e91-aa7d-48ad-8cee-00630818238a\n# ╟─e47bb743-75ba-427d-9301-a15cea857be3\n# ╟─22941298-cc85-45d1-9fc7-96b2a1e6d863\n# ╟─9d71d2ee-c194-49cf-ab41-c99500d36298\n# ╟─f55bf207-fad4-44b1-b5d9-d7efe686bf42\n# ╟─f4334764-5ada-424d-a0eb-8a9f7ee55c32\n# ╟─e6c5661f-c864-4834-9ac5-1fc1b799bed0\n# ╟─1a407189-602e-4f79-9c68-8c0ec9527336\n"
  },
  {
    "path": "docs/notebook/tutorial-3d.jl",
    "content": "### A Pluto.jl notebook ###\n# v0.20.10\n\nusing Markdown\nusing InteractiveUtils\n\n# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).\nmacro bind(def, element)\n    #! format: off\n    return quote\n        local iv = try Base.loaded_modules[Base.PkgId(Base.UUID(\"6e696c72-6542-2067-7265-42206c756150\"), \"AbstractPlutoDingetjes\")].Bonds.initial_value catch; b -> missing; end\n        local el = $(esc(element))\n        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)\n        el\n    end\n    #! format: on\nend\n\n# ╔═╡ 93b3b71e-0a4e-4165-9f92-b770c06a5964\n# ╠═╡ show_logs = false\nbegin\n\timport Pkg\n\tPkg.add(\"PlutoUI\")\n\tPkg.develop(path=\"/home/miguel/rcs/jdev/Gaston\")\n\tusing Revise\n\tusing Gaston\n\tusing PlutoUI\n\tpkgversion(Gaston)\nend\n\n# ╔═╡ a86a096a-f66b-11ed-3c0d-f3dce992f2d7\nmd\"# Gaston demo/tutorial: 3-D Plots\n\nLet's start by loading needed packages: Gaston.\"\n\n# ╔═╡ 198a01e0-fa6c-426f-b485-ae2922da121f\nPlutoUI.TableOfContents(title = \"Contents\")\n\n# ╔═╡ af9f5b7f-84ec-4f53-a4ba-df4dd73933b6\nGaston.config.term = \"pngcairo font ',10' size 700,400\"\n\n# ╔═╡ 1851bc4f-c625-4c14-96b3-c8fce30b2182\nmd\"## 3-D plots\n\nTo create a 3-D plot, use the `splot` function or the `@splot` macro. The following examples illustrate their use.\"\n\n# ╔═╡ 9a92ee79-8db5-4532-ad7b-046700d1cd84\nmd\"##### z values as an array\"\n\n# ╔═╡ 5e3d28f4-9500-44ec-82fb-dc6ee8f8d239\nlet\n    z = [10 10 10 10 ;\n\t\t 10  5  1  0 ;\n\t\t 10 10 10 10 ]\n\tsplot(\"set title 'A Valley'\n\t\t   set hidden3d\n\t       set view 75, 38\",\n\t\t  z,\n\t\t  \"lc 'dark-green'\") \nend\n\n# ╔═╡ 97dc9773-310d-44bf-b518-baead81cd252\nmd\"##### x, y vectors or ranges; z an array\"\n\n# ╔═╡ 4586bc09-8556-4627-b473-8f168f62bfb5\nlet\n\tx = -2:1\n    y = [2, 3, 4]\n    z = [10 10 10 10 ;\n\t\t 10  5  1  0 ;\n\t\t 10 10 10 10 ]\n\tsplot(\"set title 'A Valley'\n\t\t   set hidden3d\n\t       set view 75, 38\n\t       set xlabel 'x' offset -1.5,-1.5 \n\t       set ylabel 'y' offset 1,1\n\t       set zlabel 'z'\",\n\t\t  x, y, z,\n\t\t  \"lc 'dark-green'\") \nend\n\n# ╔═╡ 6d34cafc-164e-4731-b0fc-0f586bf7e994\nmd\"##### x and y vectors or ranges; z a function\"\n\n# ╔═╡ e7697a24-f587-4215-9cfa-5416e0b2627f\nlet\n\tx = y = -15:0.4:15\n\tf1(x,y) = @. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\n\tsplot(\"set title 'Sombrero'\n\t\t   set hidden3d\",\n\t\t  x, y, f1,\n\t\t  \"lc 'turquoise'\") \nend\n\n# ╔═╡ 3dc0145f-49df-4c29-a5e1-12d158f901bc\nmd\"If `x` and `y` are not provided, then `range(-10, 10, 100)` is used by default.\"\n\n# ╔═╡ b09cfaf6-b56b-4818-bda8-83a9a98e7b6d\nmd\"##### x and/or y tuples; z a function\"\n\n# ╔═╡ a73fbdfa-452a-4fbc-821a-1e00046c602d\nlet\n\tsplot(\"set hidden3d\",\n\t\t  (-6, 6, 20), (-3, 3, 20), (x,y) -> cos.(x./2).*sin.(y./2))\nend\n\n# ╔═╡ 470f890e-9010-4ee4-abc3-f5c5c73c233b\nmd\"The format is either `(min, max)` or `(min, max, samples)`. The default number of samples is 100. If only one tuple is provided, it's assumed to specify values for both `x` and `y`. The default is `(-10, 10, 100)`.\"\n\n# ╔═╡ 1ceb96e8-fecb-4f1e-9457-3717532766d9\nmd\"## Plot styles\n\nThe following plot styles are provided:\n\n* `surf` and `surf!` to plot surfaces.\n* `contour` for contour plots.\n* `surfcontour` combines the previous two styles.\n* `scatter3` and `scatter3!` for scatter plots.\n* `wireframe` and `wireframe!` for wireframe plots.\n* `wiresurf` and `wiresurf!` combine a surface and a wireframe.\n* `heatmap` is a projection of a surface or 3-D histogram to a plane.\n\nThe following examples illustrate these styles.\"\n\n# ╔═╡ b67cf328-1659-46f4-afcd-d393836035bc\nmd\"##### Surface plots\"\n\n# ╔═╡ ebe88c25-eded-4643-aac5-175013a8bd3d\nlet\n\tf1(x,y) = sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\n\t@gpkw surf({title = \"'Sombrero Surface'\",\n\t            hidden3d,\n\t            palette = :matter},\n\t\t        (-15, 15, 200), f1)\nend\n\n# ╔═╡ 24893b90-a180-4df2-a5cb-c252920f74d6\nmd\"##### Contour plots\"\n\n# ╔═╡ de4c0f15-e0b9-406f-b777-a8eea53491dc\nlet\n\tf1(x,y) = cos(x/2)*sin(y/2)\n\tcontour(\"set title  'Sombrero Contours'\", (-10, 10, 50), f1)\nend\n\n# ╔═╡ 3ba30e9c-3ce5-4e02-8225-b611f9631675\nmd\"Labels can be removed with the `labels = false` argument:\"\n\n# ╔═╡ 426d3c69-d7c2-4bfa-a63d-79da7d0f4c8f\nlet\n\tf1(x,y) = cos(x/2)*sin(y/2)\n\tcontour(\"set title  'Sombrero Contours; no labels'\",\n\t\t    (-10, 10, 50), f1, labels = false)\nend\n\n# ╔═╡ 57e27251-9723-429c-976e-61208dcd61f0\nlet\n\tf1(x,y) = @. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\n\tsurfcontour(\"set title 'Sombrero Wireframe and Contours'\",\n\t\t        (-15, 15, 40), f1, \"lc 'orange'\")\nend\n\n# ╔═╡ 59efd1da-b92d-4edf-b8e1-4ac375cf9485\nmd\"##### Scatter plots\"\n\n# ╔═╡ 015d489d-a51d-47ae-8899-4ee04a064ec0\nmd\"Scatter plots use the `points` pointstyle.\"\n\n# ╔═╡ dbeeaaa0-d0da-455d-b3c5-e1a3405014e0\nlet\n\tscatter3(\"set title 'A 3-D scatter plot\",\n\t\t     randn(10), randn(10), randn(10))\nend\n\n# ╔═╡ 070eb594-80f9-48ed-a416-ef7eeb53a97e\nlet\n\tx = 0:0.1:6π\n\t@splot({title = \"'Trigonometric spiral'\",\n\t        colorbox = false,\n\t        palette = :matter},\n\t\t   x, x.*cos.(x), x.*sin.(x), x./10,\n\t       \"with p pt 7 lc palette ps variable\")\nend\n\n# ╔═╡ 09901ab7-298f-456b-aa92-db1d642c36ce\nmd\"##### Wireframe plots\"\n\n# ╔═╡ ebd8dc6f-3d5b-4ce6-992e-41be7a19767d\nmd\"Wireframe plots use the `lines` plotstyle, which is `gnuplot`'s default and is illustraded above. The following example shows how to plot a surface and a wireframe:\"\n\n# ╔═╡ c76118f4-9e8f-4c8c-8cce-df23126551c9\nlet\n\tf1(x, y) = cos(x/2) * sin(y/2)\n\ttheme = @gpkw {palette = :matter, title = Q\"Wiresurf plot\"}\n\twiresurf(theme, :notics, :labels, (-10, 10, 30), f1)\nend\n\n# ╔═╡ d10136b4-b397-4388-9bc6-b3194341a918\nmd\"##### Heatmap plots\"\n\n# ╔═╡ 729f87a6-e861-425a-a405-cad1ec4fb320\nlet\n\tf1(x, y) = cos(x/2) * sin(y/2)\n\ttheme = @gpkw {palette = :matter, title = Q\"Heatmap\"}\n\theatmap(theme, :notics, :labels, (-10, 10, 70), f1)\nend\n\n# ╔═╡ 5f3c4090-417f-4616-b268-735be74fc3ae\nmd\"## Interactivity\"\n\n# ╔═╡ 7bc6cd91-ce7c-4800-a36a-ec12dedf5a9d\nmd\"Interaction with notebook sliders and other widgets works in a similar way to regular 2-D plots.\"\n\n# ╔═╡ dca8473a-dac9-438b-9d86-987bdc1631ba\nmd\"Azimuth: $(@bind az Slider(0:180, default = 115, show_value = true))\"\n\n# ╔═╡ 094b2a61-30b8-4739-a125-cb1e1a37dff7\nmd\"Altitude: $(@bind al Slider(0:90, default = 55, show_value = true))\"\n\n# ╔═╡ 77843582-1942-471d-987b-4b4a03adfecf\nmd\"\"\"Palette: $(@bind p Select([:viridis => \"viridis\", :matter => \"matter\", :ice => \"ice\", :thermal => \"thermal\"]))\"\"\"\n\n# ╔═╡ 5a5a8dae-526d-4122-856a-f712cfaaf858\nlet al = al, az = az\n\tf1(x, y) = cos(x/2) * sin(y/2)\n\ttheme = @gpkw {view = (al, az),\n\t               palette = p,\n\t\t\t\t   title = Q\"Wiresurf plot\"}\n\twiresurf(theme, :notics, :labels, (-8, 8, 30), f1)\nend\n\n# ╔═╡ 28b107c9-4f20-415e-b68e-d204798c94c8\nmd\"## Animation\n\nAnimation works in a similar way to 1-D plots.\"\n\n# ╔═╡ 40b076e3-92ba-48fe-90bf-f94078370b20\nlet\n\tf = Figure()\n\tfunction z(x, y, i)\n\t\tif 1.8 < atan(y, x)+π < 2.7\n\t\t\treturn NaN\n\t\tend\n\t\td = sqrt(x*x+y*y)\n\t\ti*sin(2d) / d\n\tend\n\tx = y = range(-10, 10, 35)\n\ttheme = @gpkw {zrange = (-1.5, 1.5),\n\t               cbrange = (-0.5, 1),\n\t\t\t\t   colorbox = false,\n\t               palette = :matter}\n\tframes = 20\n\tfor (idx, i) in enumerate(range(-1, 1, frames))\n\t\twiresurf(f[idx], theme, :notics, x, y, (x, y) -> z(x, y, i))\n\tend\n\tfor (idx, i) in enumerate(range(1, -1, frames))\n\t\twiresurf(f[idx+frames], theme, :notics, x, y, (x, y) -> z(x, y, i))\n\tend\n\tanimate(f, \"gif animate loop 0 size 700,400\")\nend\n\n# ╔═╡ Cell order:\n# ╟─a86a096a-f66b-11ed-3c0d-f3dce992f2d7\n# ╠═93b3b71e-0a4e-4165-9f92-b770c06a5964\n# ╠═198a01e0-fa6c-426f-b485-ae2922da121f\n# ╠═af9f5b7f-84ec-4f53-a4ba-df4dd73933b6\n# ╟─1851bc4f-c625-4c14-96b3-c8fce30b2182\n# ╟─9a92ee79-8db5-4532-ad7b-046700d1cd84\n# ╠═5e3d28f4-9500-44ec-82fb-dc6ee8f8d239\n# ╟─97dc9773-310d-44bf-b518-baead81cd252\n# ╠═4586bc09-8556-4627-b473-8f168f62bfb5\n# ╟─6d34cafc-164e-4731-b0fc-0f586bf7e994\n# ╠═e7697a24-f587-4215-9cfa-5416e0b2627f\n# ╟─3dc0145f-49df-4c29-a5e1-12d158f901bc\n# ╟─b09cfaf6-b56b-4818-bda8-83a9a98e7b6d\n# ╠═a73fbdfa-452a-4fbc-821a-1e00046c602d\n# ╟─470f890e-9010-4ee4-abc3-f5c5c73c233b\n# ╟─1ceb96e8-fecb-4f1e-9457-3717532766d9\n# ╟─b67cf328-1659-46f4-afcd-d393836035bc\n# ╠═ebe88c25-eded-4643-aac5-175013a8bd3d\n# ╟─24893b90-a180-4df2-a5cb-c252920f74d6\n# ╠═de4c0f15-e0b9-406f-b777-a8eea53491dc\n# ╟─3ba30e9c-3ce5-4e02-8225-b611f9631675\n# ╠═426d3c69-d7c2-4bfa-a63d-79da7d0f4c8f\n# ╠═57e27251-9723-429c-976e-61208dcd61f0\n# ╟─59efd1da-b92d-4edf-b8e1-4ac375cf9485\n# ╟─015d489d-a51d-47ae-8899-4ee04a064ec0\n# ╠═dbeeaaa0-d0da-455d-b3c5-e1a3405014e0\n# ╠═070eb594-80f9-48ed-a416-ef7eeb53a97e\n# ╟─09901ab7-298f-456b-aa92-db1d642c36ce\n# ╟─ebd8dc6f-3d5b-4ce6-992e-41be7a19767d\n# ╠═c76118f4-9e8f-4c8c-8cce-df23126551c9\n# ╟─d10136b4-b397-4388-9bc6-b3194341a918\n# ╠═729f87a6-e861-425a-a405-cad1ec4fb320\n# ╟─5f3c4090-417f-4616-b268-735be74fc3ae\n# ╟─7bc6cd91-ce7c-4800-a36a-ec12dedf5a9d\n# ╟─dca8473a-dac9-438b-9d86-987bdc1631ba\n# ╟─094b2a61-30b8-4739-a125-cb1e1a37dff7\n# ╟─77843582-1942-471d-987b-4b4a03adfecf\n# ╠═5a5a8dae-526d-4122-856a-f712cfaaf858\n# ╟─28b107c9-4f20-415e-b68e-d204798c94c8\n# ╠═40b076e3-92ba-48fe-90bf-f94078370b20\n"
  },
  {
    "path": "docs/notebook/tutorial-essentials.jl",
    "content": "### A Pluto.jl notebook ###\n# v0.20.10\n\nusing Markdown\nusing InteractiveUtils\n\n# This Pluto notebook uses @bind for interactivity. When running this notebook outside of Pluto, the following 'mock version' of @bind gives bound variables a default value (instead of an error).\nmacro bind(def, element)\n    #! format: off\n    return quote\n        local iv = try Base.loaded_modules[Base.PkgId(Base.UUID(\"6e696c72-6542-2067-7265-42206c756150\"), \"AbstractPlutoDingetjes\")].Bonds.initial_value catch; b -> missing; end\n        local el = $(esc(element))\n        global $(esc(def)) = Core.applicable(Base.get, el) ? Base.get(el) : iv(el)\n        el\n    end\n    #! format: on\nend\n\n# ╔═╡ ccdce6f8-f1d8-11ed-19e9-fddf2407113b\nimport Pkg\n\n# ╔═╡ 7076fc20-8f0c-449c-9cfc-edf9801e85cd\n# ╠═╡ show_logs = false\nPkg.add(\"PlutoUI\")\n\n# ╔═╡ c55fca39-9251-4593-b7f4-4342119e76e1\n# ╠═╡ show_logs = false\nPkg.develop(path=\"/home/miguel/rcs/jdev/Gaston\")\n\n# ╔═╡ a64f470c-a8d3-4c28-99f4-8149ac65ff58\nusing Revise\n\n# ╔═╡ 93d7c222-6755-4b71-b4cb-3ad82f4515f1\nusing Gaston\n\n# ╔═╡ dab403c8-f4ae-4029-a8c8-ef5ae161142c\nusing PlutoUI\n\n# ╔═╡ d4e94a70-866a-46e2-98c2-6babc7745fd2\nmd\"# Gaston demo/tutorial: Essentials\n\nLet's start by loading Gaston and PlutoUI.\"\n\n# ╔═╡ ff57c40b-615d-4902-9810-8d874220f626\npkgversion(Gaston)\n\n# ╔═╡ a66e7e1d-b3a4-4504-be15-2324808607be\nPlutoUI.TableOfContents(title = \"Contents\")\n\n# ╔═╡ 1a521882-7ee0-413d-9738-3a025499883e\nmd\"## Introduction to Gaston\n\nGaston is a plotting package for Julia. It provides an interface between Julia and [gnuplot](https://gnuplot.info). Gaston has two main functions:\n* Convert Julia data variables to gnuplot's format.\n* Provide plot commands with familiar syntax, e.g. `plot(x, y)`.\nIt also provides some additional functionality:\n* Manage multiple figures simultaneously.\n* Work seamlessly in notebooks.\n* Convenient syntax for configuring gnuplot.\n* Recipes (styles) for common plot types.\n* Lightweight themes.\n* Can be extended to plot user-defined data types.\nFamiliarity with both Julia and gnuplot is assumed throughout the documentation.\n\"\n\n# ╔═╡ 8f50fd19-5ce4-447e-a7f9-7b16d59af6c0\nmd\"\"\"## Basic syntax\n\nThe `plot` function is Gaston's workhorse for 1-D plotting. It takes three different kinds of arguments, in order: axis settings, data, and curve settings. Axis settings correspond to gnuplot's `set` commands, while curve settings determine how a curve is plotted (its color, width, markers, etc.)\n\nAxis settings and curve settings are strings; any number of them can be used. The signature of `plot` is:\n\n    plot(axis_settings..., data..., curve_settings...)\n\nFor example, the command\n\n    plot(\"set title 'a plot'\", \"set grid\", x, y, \"w p lc 'green'\")\n\nis converted to the following sequence of gnuplot commands:\n\n    set title 'a plot'\n    set grid\n    plot '/tmp/data' w p lc 'green'\n\nwhere `x` and `y` are written to the temporary file `data` in the format gnuplot expects.\n\"\"\"\n\n# ╔═╡ ab0aba7c-d841-44bb-8e6a-2e5515ef9aa5\nmd\"## Configuring the terminal\n\nWhen working in a notebook (whether Juno, IJulia, Pluto or VS Code), the terminal is automatically set to `pngcairo`. This terminal is very good for plotting on a notebook, since it's very fast and produces small files. We still want to configure the terminal ourselves, though, to set a good plot size (in pixels) and font size. This is done as follows:\"\n\n# ╔═╡ 0e8236b4-6d1f-46ae-9c0e-ee1d4f605c0d\nGaston.config.term = \"pngcairo font ',10' size 700,400\"\n\n# ╔═╡ fc31bafd-eb7b-4204-b781-6e06cf95fd25\nmd\"## 2-D plots\n\n### Plotting coordinates\n\nFollowing are some examples of basic plots, with different data arguments.\n\n##### Only `y` is given\n\nThe `x` coordinates are implicitly defined as the range `firstindex(y):lastindex(y)`.\"\n\n# ╔═╡ 804b8b60-8976-43a3-a5b7-86cedf9118e4\nplot(rand(10))\n\n# ╔═╡ e467671a-62a2-406d-99e4-9ecfa7f55b5e\nmd\"##### Both `x` and `y` are given\"\n\n# ╔═╡ a25f483f-f5c7-4949-9690-3d126a12e86a\nplot(range(0, 1, length=10), rand(10))\n\n# ╔═╡ 26fe4805-3b08-4750-b0f8-f210a72a502b\nmd\"##### Multiple columns\n\nMany `gnuplot` plot styles take multiple columns. Each data argument to `plot` is interpreted as a column, and you can use as many as you need:\"\n\n# ╔═╡ 21e1f17f-4fb2-490e-ade7-77725fb24f61\nlet\n\tx = 1:10\n\tboxmin = 0.1*rand(10) .+ 0.2\n\twhiskmin = boxmin .- 0.1*rand(10)\n\tboxmax = boxmin .+ 0.5*rand(10)\n\twhiskmax = boxmax .+ 0.2*rand(10)\n\tym = minimum(whiskmin)-0.1\n\tyM = maximum(whiskmax)+0.1\n\tplot(\"set yrange [$ym:$yM]\n\t      set boxwidth 0.3 relative\n\t      set style fill solid 0.25\n\t      set title 'A random candlesticks plot'\",\n\t      x, boxmin, whiskmin, whiskmax, boxmax,\n\t      \"w candlesticks whiskerbars 0.75\")\nend\n\n# ╔═╡ cedb6fa7-e8c7-4a48-8935-1ce6385f1185\nmd\"### Plotting functions\n\n##### Only a function is given\n\nWhen a function is passed to `plot`, the function is first evaluated in the range from -10 to 10, with 100 samples.\"\n\n# ╔═╡ 8a50be7a-bcfa-468c-914a-9b1aa2c970ad\nfun(x) = exp(-abs(x/8))*cos(x)\n\n# ╔═╡ 6d72504e-fede-41a6-bfef-0aef52936e1b\nplot(fun)\n\n# ╔═╡ e15e224f-834c-459e-b355-15d1fb8975fb\nmd\"##### A function and a range\n\nThe range can be given explicitly as `(start, stop)`; the function is still sampled 100 times.\"\n\n# ╔═╡ 162ccb40-92b0-43fe-a89e-676864ac9883\nplot((-1, 5), fun)\n\n# ╔═╡ 0b229ed3-868a-42d3-b669-6cacaf213c90\nmd\"The number of samples can be specified with `(start, stop, samples)`.\"\n\n# ╔═╡ fbce3eb9-6691-4945-ac6b-4b0bcf31e003\nplot((-1, 5, 10), fun)\n\n# ╔═╡ 262faf7b-ac62-45d9-bc18-3860e6e10fb7\nmd\"### Plotting multiple curves\n\nTo plot multiple curves on the same axis, use `plot!`.\n\n(Note that `plot!` ignores any provided axis settings, which must be specified with `plot`).\"\n\n# ╔═╡ 57f66b17-8c2d-4737-84e1-c456a7dacc89\nbegin\n\tplot(\"set key box top left\", fun, \"lc 'dark-green' title 'fun'\")\n\tplot!(cos, \"lc 'orange' title 'cos'\")\nend\n\n# ╔═╡ a0762a24-26be-40a6-a333-92bd8999a5a3\nmd\"### Plot styles\n\nThe following commands plot data with a specific plot style:\n* `scatter`, `scatter!`\n* `stem`, `stem!`\n* `bar`, `bar!`\n* `barerror`, `barerror!`\n* `histogram`\n* `imagesc`\nThe following examples illustrate these styles.\"\n\n# ╔═╡ d66d3ef3-130f-45a7-81d6-0cac24e9b1d2\nmd\"##### Scatter plot\"\n\n# ╔═╡ 4f145f29-f99d-4f36-9ebf-5cc632f956c1\nlet\n\txg = randn(20)\n\tyg = randn(20)\n\tscatter(\"set title 'Scatter plot'\n\t         set key outside\",\n\t\t    xg, yg,\n\t\t    \"title 'gaussian'\")\n\txu = rand(20)\n\tyu = rand(20)\n\tscatter!(xu, yu, \"title 'uniform'\")\nend\n\n# ╔═╡ 970fd5da-91e2-44bb-a427-6823b03c79e9\nmd\"##### Stem plot\"\n\n# ╔═╡ 881e794d-bc5e-49d0-b240-2cb58abe82ce\nstem(\"set title 'stem plot'\", fun)\n\n# ╔═╡ 996ef179-f5fd-4345-a6af-a74709bd2a6b\nmd\"Avoid printing the circular marks with the `onlyimpulses` argument.\"\n\n# ╔═╡ f395e87b-39f6-4f79-83b1-1b44bcb37feb\nstem(\"set title 'only impulses'\", fun, onlyimpulses = true)\n\n# ╔═╡ 9db5812e-47a3-4b17-b3f0-9e74a38e3f54\nmd\"##### Bar plots\n\n`bar` uses gnuplot's `with boxes` style.\"\n\n# ╔═╡ 061a881c-220c-4183-8f5b-12fd91a8f358\nbar(\"set title 'bar plot'\", rand(10), \"lc 'turquoise'\")\n\n# ╔═╡ 8c14838e-fcb6-4b49-83cd-a3ef5f267b24\nlet\n\tbar(\"set title 'Two bar plots'\",\n        rand(10),\n\t\t\"lc 'dark-violet'\")\n\tbar!(1.5:10.5, 0.5*rand(10), \"lc 'plum' fill pattern 4\")\nend\n\n# ╔═╡ be09ba73-cabb-40ce-aa06-6bfe0705f6e2\nmd\"`barerror` uses gnuplot's `boxerrorbars` style.\"\n\n# ╔═╡ 33731005-bb8c-48a9-863a-93e25d579930\nbarerror(\"set title 'Error bars'\",\n         1:10, rand(10), 0.1*rand(10).+0.1,\n         \"lc 'sandybrown'\")\n\n# ╔═╡ 906770c9-07bd-4447-af21-de487c7a1e02\nmd\"##### Histograms\n\nThe `histogram` function takes these keyword arguments:\n* `nbins`: specifies the number of bins. Defaults to 10.\n* `norm::Bool`: if `true`, the area of the histogram is normalized.\n* `mode::Symbol`: Controls histogram normalization mode; passed to [`StatsBase.normalize`](https://juliastats.org/StatsBase.jl/stable/empirical/#LinearAlgebra.normalize). Defaults to `:pdf`, which makes the histogram area equal to 1.\n* `edges`: a vector or a range specifying the bin edges; takes precedence over `nbins`.\n* `horizontal::Bool`: if `true`, the histogram is drawn horizontally.\n2-D histograms are supported, by passing two datasets.\n\"\n\n# ╔═╡ 38a71681-dc2d-4af5-be14-1682924abf51\nhistogram(\"set title 'histogram (nbins)'\",\n\t      randn(10_000), nbins = 20, norm = true)\n\n# ╔═╡ 5c25b0e3-9a45-4ad5-92ae-314cfce5c117\nhistogram(\"set title 'histogram (edges)'\",\n\t      randn(10_000), edges = -5:5,\n          \"lc 'dark-khaki'\")\n\n# ╔═╡ 4f4fb009-cf62-4e64-a519-77f340f83f69\nhistogram(\"set title 'horizontal histogram'\",\n\t      rand(1000), nbins = 10, horizontal = true,\n          \"lc 'orchid'\")\n\n# ╔═╡ 84ccf2ff-472b-4b34-9472-2006f70684e6\nmd\"In the case of 2-D histograms, `nbins` or `egdes` may be a tuple; otherwise, both axes use the same configuration.\"\n\n# ╔═╡ b42213a1-f27a-4c96-a6a7-b5a8a200ef19\nlet\n\tx = randn(100_000)\n\ty = randn(100_000)\n\thistogram(\"set palette gray\n\t           unset colorbox\n\t           set title '2-D histogram'\",\n\t\t      x, y, nbins = 50, norm = true)\nend\n\n# ╔═╡ e1e93d06-4565-4625-8fcf-fe8dbc9fe55e\nmd\"##### Images\"\n\n# ╔═╡ 8ab2a66a-df03-4aeb-a736-1336b75dadaa\nmd\"Arrays may be plotted as images using `imagesc`. Note that, in contrast to other plotting packages, the array columns are plotted with the first row at the top.\"\n\n# ╔═╡ ca7554bc-6f9c-4574-bf78-20afd42dc647\nlet\n\tX = [0 1 2 3;\n\t     0 3 2 1;\n\t     0 2 2 0;\n\t     3 0 0 0]\n\timagesc(\"unset tics\", X)\nend\n\n# ╔═╡ 5acd956b-e22d-4638-be19-1ad090cac7d2\nmd\"\"\"## Plot options\n\nGnuplot provides many possibilities to fine-tune a plot. There are two main kinds of configuration options:\n* Axes-wide options, corresponding to `set` commands; for example, `set grid` turns on the grid.\n* Specific curve settings, such as line width and color; for example, `with points linecolor 'red'`.\n\nGaston provides a few different ways to specify these plot options. In all cases, axes-wide options come before the data to plot, and curve-specific options come afterward.\n\nOne way to specify the options is with strings enclosing `gnuplot` commands:\n\"\"\"\n\n# ╔═╡ 20a9dc74-0324-4d76-88fa-1c55ac281e5e\nplot(\"set title 'A nice sinusoid'\n\t  set key box left\n      unset grid\n      set xlabel 'time'\n      set ylabel 'voltage'\",\n\t  sin,\n      \"w points lc 'orange' title 'sin'\")\n\n# ╔═╡ a0720ae4-8da5-4ed6-acd9-0471b840fa1d\nmd\"Options may also be given using the following syntax:\"\n\n# ╔═╡ dc989e43-20ad-4f03-86a3-655c00c02a4e\n@plot({title = \"'Another sinusoid'\",\n        key = \"box left\",\n        grid = false,\n        xlabel = \"'time'\",\n        ylabel = \"'voltage'\"\n\t   },\n       sin,\n       {w = \"points\",\n        lc = \"'orange'\",\n        title = \"'sin'\"\n       })\n\n# ╔═╡ 564caa1c-222f-4655-9505-162f5e23b234\nmd\"\"\"##### Axis-wide options\n\nFor axis-wide options (first argument to `@plot` above), `{option = value}` is converted to `\"set option value\"`. To unset an option, set it to false: `{tics = false}` is converted to `unset tics`.\n\nCurve-specific options (last argument to `@plot` above) are handled similary, but they are used to \"build\" the plot command given to `gnuplot`.\n\nThis syntax offers some convenience features:\n* Specify a range as a tuple: `{xrange = (1, 2)}` is converted to `set xrange [1:2]`, while `{yrange = (-Inf, 5)}` is converted to `set yrange [*:5]`.\n* To set all ranges (`x`, `y`, `z` and `cb`) simultaneously, use `{ranges = (min, max)}`.\n* Specify tics conveniently:\n    * `{xtics = 1:2}` is equivalent to `set xtics 1,1,2`\n    * `{ztics = 1:2:7})` to `set ztics 1,2,7`\n    * `{tics = (0,5)}` to `set tics (0, 5)`\n    * `{tics = (labels = (\"one\", \"two\"), positions = (0, 5))}` to `set tics ('one' 0, 'two' 5, )`\n* Specify a color palette from [Colorschemes.jl](https://juliapackages.com/p/colorschemes). For example, `{palette = :viridis}` is converted to a `set palette` command with the appropriate colors. The palette name must be given as a symbol.\n    * To reverse the palette order, specify it as `{palette = (:viridis, :reverse)}`.\n* You may also use any custom palette (for example, one created with `ColorSchemes.resample)`\n* A line type may be specified similary: `{lt = :viridis}`.\n* In 3D plots, specify the view as a tuple: `{view = (50, 60)}` is converted to `set view 50, 60`.\n* Specify margins, useful for multiplot with arbitrary layouts: `{margins = (0.33, 0.66, 0.2, 0.8)}` is converted to `set lmargin at screen 0.33...`. The margins are specified in the order left, right, bottom, top.\n\nAn option may be specified more than one time: `{tics, tics = 1:2}` is converted to\n\n    set tics\n    set tics 1,1,2\n\nAny number of option arguments may be given before the data to plot, and they will be combined.\n\"\"\"\n\n# ╔═╡ 52a654cf-9aac-47ba-82d4-6d7b4320d79e\nmd\"Options can also be given as `gnuplot` commands in strings:\"\n\n# ╔═╡ 44930880-7dd2-4120-b097-6c19f8e7022a\n@plot({title = \"'A nice sinusoid'\"}, \"set key box left\", {grid = false},\n       \"set xlabel 'time'\\nset ylabel 'voltage'\",\n\t   sin,\n\t   {w = \"points\"}, \"lc 'orange' title 'sin'\")\n\n# ╔═╡ 5809cde8-fa4d-49a5-bc4f-4baa0133b21f\nmd\"\"\"##### Quoted strings\n\nString arguments given to gnuplot must be quoted. To simplify this, the `@Q_str` string macro might come in handy. The string `Q\"a title\"` is converted to `\"'a title'\"`.\"\"\"\n\n# ╔═╡ 1b7dfb67-6047-40fa-9b91-c48aa8e8aa9c\n@plot({title = Q\"A nice sinusoid\"}, \"set key box left\", {grid = false},\n       {xlabel = Q\"time\", ylabel = Q\"voltage\"},\n\t   sin,\n\t   {w = \"points\"}, \"lc 'orange' title 'sin'\")\n\n# ╔═╡ beb2b6cd-ee6b-4bd8-bf8e-5d071adbd857\nmd\"\"\"##### Curve-specific options\n\nThese options affect the way a curve is plotted (line style, color, etcetera), and may be specified as a string (which is passed directly to `gnuplot`) and/or as a set of options inside `{}` brackets. These options are passed to `gnuplot` as part of the `plot` command. For example, `@plot sin {with = \"points\", lc = Q\"red\"}` is converted to a `gnuplot` command `plot <datafile> with points lc 'red'`.\n\nWhen using the bracketed options format, the following convenient syntax is available:\n* `marker` is available as synonym of `pointtype`\n* `markersize` and `ms` are available as synonyms of `pointsize`.\n* `legend` is available as a synonym of `title`.\n* `plotstyle` is a synonym of `with`.\n* A point type may be specified by name (instead of just by number as in native `gnuplot`):\n| name | gnuplot pt number |\n|-----:|:------------------|\n| dot | 0 |\n| ⋅ | 0 |\n| + | 1 |\n|  plus     | 1 |\n|  x        | 2 |\n|  *        | 3 |\n|  star     | 3 |\n|  esquare  | 4 |\n|  fsquare  | 5 |\n|  ecircle  | 6 |\n|  fcircle  | 7 |\n|  etrianup | 8 |\n|  ftrianup | 9 |\n|  etriandn | 10 |\n|  ftriandn | 11 |\n|  edmd     | 12 |\n|  fdmd     | 13 |\nHere a prefix \"e\" stands for \"empty\", \"f\" for \"full\"; \"up\" and \"dn\" stand for \"pointing up\" and \"pointing down\"; \"trian\" is a triangle and \"dmd\" is a diamond (rhombus). Note that this mapping of marker shapes to numbers is compatible with the most popular `gnuplot` terminals, but the specific mapping may be different for a given terminal.\n\"\"\"\n\n# ╔═╡ ea4e84ab-4820-4f91-8782-3bfc3ef4ba6a\nmd\"\"\"##### Themes\n\nIt is possible to create themes with the `@gpkw` macro (stands for \"gnuplot keywords\"):\"\"\"\n\n# ╔═╡ 1c0d72b4-992a-4a0e-b9bc-24a62a26da02\nlet\n\ttheme = @gpkw {grid, xlabel = \"'x'\", ylabel = Q\"voltage\"}\n\t@plot(theme, sin)\nend\n\n# ╔═╡ 59fd1827-6592-473a-9baf-e17c4c93adb4\nmd\"\"\"A couple of lightweight themes are included:\n\n|Axis themes | Description |\n|-----------:|:------------|\n| :notics | Removes all tics |\n| :labels | Generic axis labels |\n| :nocb | Removes the colorbox |\n| :unitranges | Set all ranges to `[-1:1]` |\n\nNote that `plot` accepts any number of option arguments. Arguments before data are assumed to correspond to `gnuplot` `set` commands; arguments after the data affect the curve attributes.\n\"\"\"\n\n# ╔═╡ 83d697a8-3a33-4d37-a173-c31309c112e3\nmd\"## Interactivity\"\n\n# ╔═╡ afc4dfba-eb72-42de-aec0-d68cf20514be\nmd\"In a notebook, it is easy to tie plot variables to sliders or other UI elements.\"\n\n# ╔═╡ 8c124c3d-d444-41d6-8187-6058f4b5808f\nmd\"\"\"Frequency: $(@bind freq Slider(1:10, default = 5, show_value = true))\"\"\"\n\n# ╔═╡ c2bc72a9-e96d-4216-92dd-71a0e51a647d\nmd\"\"\"Line color: $(@bind color Select(\n                   [\"'red'\" => \"red\",\n                    \"'blue'\" => \"blue\",\n                    \"'orange'\" => \"orange\",\n                    \"'dark-green'\" => \"dark green\",\n                    \"'chartreuse'\" => \"chartreuse\"]))\"\"\"\n\n# ╔═╡ d253f4ea-b633-4255-8308-e1182c680df2\nplot((-1, 1, 200), t -> sin(2π*freq*t), \"lw 2 lc $(color)\")\n\n# ╔═╡ 6245bfd6-a945-4e84-825d-57fbaa63ffd1\nmd\"## Multiplot\"\n\n# ╔═╡ 80e46def-fb34-426a-9883-18a8e99e72ce\nmd\"A multiplot can be easily generated and automatically laid out by adding more axes to an existing figure:\"\n\n# ╔═╡ 52e760d6-3848-4705-a9d2-5968e2ad9b03\nbegin\n\tf = Figure() # create an empty figure\n\tplot(sin)\n\tplot(f[2], cos) # figures are row-major\n\tplot(f[3], (1:10).^2) # Gaston adjusts the layout as the number of\n\tplot(f[4], rand(10))  #  suplots grows.\nend\n\n# ╔═╡ 91cbc979-b191-45cd-8aba-2ecc31f09a38\nmd\"Add another plot to a subplot using indexing:\"\n\n# ╔═╡ 379428f6-1007-457c-95ad-7f090947f72a\nplot!(f[4], randn(10))\n\n# ╔═╡ 4779cbbb-9b02-4ffa-b393-c19d0423a967\nmd\"Full control over gnuplot multiplot options can be obtained using margins, as follows:\"\n\n# ╔═╡ 6ae49346-1bc5-46dc-9fb1-7530f6606474\nlet\n\tf = Figure(multiplot = \"title 'Arbitrary layout demo'\", autolayout = false)\n\tx = randn(100)\n\ty = randn(100)\n\t@plot({margins = (0.1, 0.65, 0.1, 0.65)},\n\t      x, y,\n\t      \"w p pt '+' lc 'dark-green'\")\n\t@gpkw histogram(f[2],\n\t                {margins = (0.7, 0.9, 0.1, 0.65), tics = false},\n\t                y,\n\t                {lc = Q\"dark-green\"},\n\t                nbins = 10, horizontal = true)\n\t@gpkw histogram(f[3],\n\t                {margins = (0.1, 0.65, 0.7, 0.9),\n\t                boxwidth = \"1 relative\"},\n\t                x,\n\t                {lc = \"'dark-green'\"},\n\t\t\t\t\tnbins = 10)\nend\n\n# ╔═╡ c860dea5-1a8a-4516-98d6-97122962345d\nmd\"## Animations\n\nAnimations require use of a terminal that supports it (such as `gif` or `webp`). Make sure your notebook supports the `webp` file format before using it. Gif is supported almost everywhere.\n\nCreating an animation is similar to multiplotting: multiple axes are drawn on the same figure. When using the `animate` option of the `gif` or `webp` terminals, however, the plot is rendered as an animation.\n\nNote that `gnuplot` will output a message to STDERR indicating how many frames were recorded; this message is purely informative and not actually an error.\n\nA difficulty arises when mixing plot formats in a notbook (say, `png` and `gif`): the terminal is specified in the global variable `Gaston.config.term`; however, Pluto executes cells in arbitrary order. This means that changing the terminal in one cell may affect other cells.\n\nTo solve this problem, `Gaston` provides a way to ignore the global terminal configuration when rendering a plot. A figure `f` can be rendered with a given terminal by calling `animate(f, term)`. The default value of `term` is stored in `Gaston.config.altterm` and defaults to `gif animate loop 0`.\n\nThe following examples illustrate how to create and display animations in a notebook:\"\n\n# ╔═╡ 5895f542-8a5b-42a4-a1d6-6e4103a71308\nlet\n\tf = Figure()\n\tframes = 20\n\tx = range(-1, 1, 200)\n\tϕ = range(0, 2π-1/frames, frames)\n\tfor i = 1:frames\n\t\tplot(f[i], x, sin.(5π*x .+ ϕ[i]), \"lw 2\")\n\tend\n\tanimate(f, \"gif animate loop 0 size 700,400\")\nend\n\n# ╔═╡ 4f0391b9-fdf3-48f1-9cb5-9c722ff89c6c\nmd\"A background can be included in an animation as follows:\"\n\n# ╔═╡ 42da1eb0-23f6-4f13-8107-a8a6063e87ef\nlet\n\tf = Figure()\n\tframes = 75\n\tx_bckgnd = range(-1, 1, 200)\n\tbckgnd = Gaston.Plot(x_bckgnd, sin.(2π*2*x_bckgnd), \"lc 'black'\")\n\tx = range(-1, 1, frames)\n\tfor i in 1:frames\n\t\tplot(f[i], x[i], sin(2π*2*x[i]), \"w p lc 'orange' pt 7 ps 7\")\n\t\tpush!(f[i], bckgnd)\n\tend\n\tfor i in frames:-1:1\n\t\tplot(f[2frames-i+1], x[i], sin(2π*2*x[i]),\n\t\t\t \"w p lc 'orange' pt 7 ps 7\")\n\t\tpush!(f[2frames-i+1], bckgnd)\n\tend\n\tanimate(f, \"gif animate loop 0 size 700,400\")\nend\n\n# ╔═╡ Cell order:\n# ╟─d4e94a70-866a-46e2-98c2-6babc7745fd2\n# ╠═ccdce6f8-f1d8-11ed-19e9-fddf2407113b\n# ╠═7076fc20-8f0c-449c-9cfc-edf9801e85cd\n# ╠═c55fca39-9251-4593-b7f4-4342119e76e1\n# ╠═a64f470c-a8d3-4c28-99f4-8149ac65ff58\n# ╠═93d7c222-6755-4b71-b4cb-3ad82f4515f1\n# ╠═ff57c40b-615d-4902-9810-8d874220f626\n# ╠═dab403c8-f4ae-4029-a8c8-ef5ae161142c\n# ╠═a66e7e1d-b3a4-4504-be15-2324808607be\n# ╟─1a521882-7ee0-413d-9738-3a025499883e\n# ╟─8f50fd19-5ce4-447e-a7f9-7b16d59af6c0\n# ╟─ab0aba7c-d841-44bb-8e6a-2e5515ef9aa5\n# ╠═0e8236b4-6d1f-46ae-9c0e-ee1d4f605c0d\n# ╟─fc31bafd-eb7b-4204-b781-6e06cf95fd25\n# ╠═804b8b60-8976-43a3-a5b7-86cedf9118e4\n# ╟─e467671a-62a2-406d-99e4-9ecfa7f55b5e\n# ╠═a25f483f-f5c7-4949-9690-3d126a12e86a\n# ╟─26fe4805-3b08-4750-b0f8-f210a72a502b\n# ╠═21e1f17f-4fb2-490e-ade7-77725fb24f61\n# ╟─cedb6fa7-e8c7-4a48-8935-1ce6385f1185\n# ╠═8a50be7a-bcfa-468c-914a-9b1aa2c970ad\n# ╠═6d72504e-fede-41a6-bfef-0aef52936e1b\n# ╟─e15e224f-834c-459e-b355-15d1fb8975fb\n# ╠═162ccb40-92b0-43fe-a89e-676864ac9883\n# ╟─0b229ed3-868a-42d3-b669-6cacaf213c90\n# ╠═fbce3eb9-6691-4945-ac6b-4b0bcf31e003\n# ╟─262faf7b-ac62-45d9-bc18-3860e6e10fb7\n# ╠═57f66b17-8c2d-4737-84e1-c456a7dacc89\n# ╟─a0762a24-26be-40a6-a333-92bd8999a5a3\n# ╟─d66d3ef3-130f-45a7-81d6-0cac24e9b1d2\n# ╠═4f145f29-f99d-4f36-9ebf-5cc632f956c1\n# ╟─970fd5da-91e2-44bb-a427-6823b03c79e9\n# ╠═881e794d-bc5e-49d0-b240-2cb58abe82ce\n# ╟─996ef179-f5fd-4345-a6af-a74709bd2a6b\n# ╠═f395e87b-39f6-4f79-83b1-1b44bcb37feb\n# ╟─9db5812e-47a3-4b17-b3f0-9e74a38e3f54\n# ╠═061a881c-220c-4183-8f5b-12fd91a8f358\n# ╠═8c14838e-fcb6-4b49-83cd-a3ef5f267b24\n# ╟─be09ba73-cabb-40ce-aa06-6bfe0705f6e2\n# ╠═33731005-bb8c-48a9-863a-93e25d579930\n# ╟─906770c9-07bd-4447-af21-de487c7a1e02\n# ╠═38a71681-dc2d-4af5-be14-1682924abf51\n# ╠═5c25b0e3-9a45-4ad5-92ae-314cfce5c117\n# ╠═4f4fb009-cf62-4e64-a519-77f340f83f69\n# ╟─84ccf2ff-472b-4b34-9472-2006f70684e6\n# ╠═b42213a1-f27a-4c96-a6a7-b5a8a200ef19\n# ╟─e1e93d06-4565-4625-8fcf-fe8dbc9fe55e\n# ╟─8ab2a66a-df03-4aeb-a736-1336b75dadaa\n# ╠═ca7554bc-6f9c-4574-bf78-20afd42dc647\n# ╟─5acd956b-e22d-4638-be19-1ad090cac7d2\n# ╠═20a9dc74-0324-4d76-88fa-1c55ac281e5e\n# ╟─a0720ae4-8da5-4ed6-acd9-0471b840fa1d\n# ╟─dc989e43-20ad-4f03-86a3-655c00c02a4e\n# ╟─564caa1c-222f-4655-9505-162f5e23b234\n# ╟─52a654cf-9aac-47ba-82d4-6d7b4320d79e\n# ╠═44930880-7dd2-4120-b097-6c19f8e7022a\n# ╟─5809cde8-fa4d-49a5-bc4f-4baa0133b21f\n# ╠═1b7dfb67-6047-40fa-9b91-c48aa8e8aa9c\n# ╟─beb2b6cd-ee6b-4bd8-bf8e-5d071adbd857\n# ╟─ea4e84ab-4820-4f91-8782-3bfc3ef4ba6a\n# ╟─1c0d72b4-992a-4a0e-b9bc-24a62a26da02\n# ╟─59fd1827-6592-473a-9baf-e17c4c93adb4\n# ╟─83d697a8-3a33-4d37-a173-c31309c112e3\n# ╟─afc4dfba-eb72-42de-aec0-d68cf20514be\n# ╟─8c124c3d-d444-41d6-8187-6058f4b5808f\n# ╟─c2bc72a9-e96d-4216-92dd-71a0e51a647d\n# ╠═d253f4ea-b633-4255-8308-e1182c680df2\n# ╟─6245bfd6-a945-4e84-825d-57fbaa63ffd1\n# ╟─80e46def-fb34-426a-9883-18a8e99e72ce\n# ╠═52e760d6-3848-4705-a9d2-5968e2ad9b03\n# ╟─91cbc979-b191-45cd-8aba-2ecc31f09a38\n# ╠═379428f6-1007-457c-95ad-7f090947f72a\n# ╟─4779cbbb-9b02-4ffa-b393-c19d0423a967\n# ╠═6ae49346-1bc5-46dc-9fb1-7530f6606474\n# ╟─c860dea5-1a8a-4516-98d6-97122962345d\n# ╠═5895f542-8a5b-42a4-a1d6-6e4103a71308\n# ╟─4f0391b9-fdf3-48f1-9cb5-9c722ff89c6c\n# ╠═42da1eb0-23f6-4f13-8107-a8a6063e87ef\n"
  },
  {
    "path": "docs/v1/2d-gallery.md",
    "content": "# [2-D Gallery](@id twodeegal)\n\n(Many of these examples taken from, or inspired by, [@lazarusa's amazing gallery](https://lazarusa.github.io/gnuplot-examples/gallery/))\n\n# Glowing curves\n```@example 2dgal\nusing Gaston # hide\nset(reset=true) # hide\nset(termopts=\"size 550,325 font 'Consolas,11'\") # hide\nx = 0:0.3:4\na = exp.(- x)\nb =  exp.(- x.^2)\nplot(x, a, curveconf = \"w lp lw 1 lc '#08F7FE' pt 7 t 'e^{-x}'\",\n     Axes(object=\"rectangle from screen 0,0 to screen 1,1 behind fc 'black' fs solid noborder\",\n          border=\"lw 1 lc 'white'\",\n          xtics=\"textcolor rgb 'white'\",\n          ytics=\"textcolor rgb 'white'\",\n          ylabel=\"'y' textcolor 'white'\",\n          xlabel=\"'x' textcolor 'white'\",\n          grid=\"ls 1 lc '#2A3459' dt 4\",\n          key=\"t r textcolor 'white'\",\n          style=\"fill transparent solid 0.08 noborder\")\n    )\nplot!(x, b, curveconf = \"w lp lw 1 lc '#FFE64D' pt 7 t 'e^{-x^2}'\")\nfor i in 1:10\n       plot!(x,a,w=\"l lw $(1 + 1.05*i) lc '#F508F7FE' t ''\")\n       plot!(x,b,w=\"l lw $(1 + 1.05*i) lc '#F5FFE64D' t ''\")\nend\nplot!(x, a, curveconf = \"w filledcu y=0 lw 1 lc '#08F7FE' t ''\")\nplot!(x, a, supp= b , curveconf = \"w filledcu lw 1 lc '#FFE64D' t ''\")\n```\n\n# Volcano data\n\n```@example 2dgal\nusing RDatasets\nvolcano = Matrix{Float64}(dataset(\"datasets\", \"volcano\"))\nimagesc(volcano,\n        Axes(palette = :inferno,\n             auto=\"fix\",\n             size=\"ratio -1\",\n             title = \"'Aukland s Maunga Whau Volcano'\")\n       )\n```\n\n# Animation\n\nAn animation can be produced by pushing new plots into an existing plot, and then saving the result as a GIF the the `animate` option.\n\n```julia\ncloseall()  #hide\nt = 0:0.01:2π\nf(t,i) = sin.(t .+ i/10)\nac = Axes(title = :Animation, xlabel = :x, ylabel = :y);  # axes configuration\ncc = \"w l lc 'black' notitle\"  # curve configuration\nF = plot(t, f(t,1), curveconf = cc, ac);  # create the first frame, with handle 1\nfor i = 2:50\n    pi = plot(t, f(t,i), curveconf = cc, ac, handle=2) # frames, with handle 2\n    push!(F, pi)  # push the frame to F\nend\nsave(term = \"gif\", saveopts = \"animate size 600,400 delay 1\",\n     output=\"anim.gif\", handle=1)\n```\n\n![](assets/anim.gif)\n\n# Color from palette\n\n```@example 2dgal\nx = -2π:0.05:2π\nplot(x, sin.(3x), supp = x, curveconf = \"w l notitle lw 3 lc palette\",\n     Axes(palette = :ice)\n    )\n```\n\n# Categorical data\n\n```@example 2dgal\nusing RDatasets\ndset = dataset(\"datasets\", \"iris\")\nbyCat = dset.Species\ncateg = unique(byCat)\nac = Axes(linetype = :tab10,\n          xlabel = \"'Sepal length'\",\n          ylabel = \"'Sepal width'\",\n          auto = \"fix\",\n          title = \"'Iris dataset'\",\n          key = \"b r font ',9' tit 'Species' box\")\nc = categ[1]\nindc = findall(x -> x == c, byCat)\np = plot(dset.SepalLength[indc], dset.SepalWidth[indc],\n         ac, curveconf = \"w p tit '$(c)' pt 7 ps 1.4 \")\nc = categ[1]\nindc = findall(x -> x == c, byCat)\nP = plot(dset.SepalLength[indc], dset.SepalWidth[indc],\n         ac, curveconf = \"w p tit '$(c)' pt 7 ps 1.4 \");\nc = categ[2]\nindc = findall(x -> x == c, byCat)\nplot!(dset.SepalLength[indc], dset.SepalWidth[indc],\n      curveconf = \"w p tit '$(c)' pt 7 ps 1.4 \");\nc = categ[3]\nindc = findall(x -> x == c, byCat)\nplot!(dset.SepalLength[indc], dset.SepalWidth[indc],\n      curveconf = \"w p tit '$(c)' pt 7 ps 1.4 \");\nP\n```\n\n# Vector fields (arrow plots)\n\nVector fields can be plotted with the `vectors` plot style. The arrows' `x` and `y` coordinates need to be specified in supplementary columns.\n\n```@example 2dgal\nx = range(0, 6π, length = 50)\nA = range(0, 2, length = 50)\nxdelta = A./7.0.*rand(50)\nydelta = A./3.0.*rand(50)\nplot(A.*cos.(x), A.*sin.(x), supp = [xdelta ydelta], w = :vectors, lc = \"'dark-turquoise'\")\n```\n\n# Violin plots\n\nA violin plot is created by plotting `(y, x)` instead of `(x, y)`, using the `filledcurves` style, and mirroring the plot.\n\n```@example 2dgal\nx = range(0, 5, length=100)\ny = 2cos.(x) + sin.(2x) + 0.5cos.(3x) - sin.(4x) .+ 3\nM = maximum(y)\nplot(y, x, w = \"filledcurves x = $M\", lc = :turquoise,\n     Axes(title = \"'Violin plot'\"))\nplot!(-y .+ 2M, x, w = \"filledcurves x = $M\", lc = :turquoise)\n```\n\nViolin plot with a superimposed boxplot:\n\n```@example 2dgal\nplot(y, x, w = \"filledcurves x = $M\", lc = :turquoise,\n     Axes(title     = \"'Violin plot'\",\n          style     = \"fill solid bo -1\",\n          boxwidth  = 0.075,\n          errorbars = \"lt black lw 1\"))\nplot!(-y .+ 2M, x, w=\"filledcurves x = $M\", lc=:turquoise)\nplot!(x, y, w = \"boxplot\", u = \"($M):2\", fc = :white, lw = 2)\n```\n\n# Plotting times/dates\n\nThe key to plotting dates and times is converting them to strings, and then telling gnuplot what the format is, using `timefmt`. In Julia, dates and times are defined in module `Dates`. This example is inspired by [this gnuplot demo](http://gnuplot.sourceforge.net/demo_5.2/timedat.html).\n\n```@example 2dgal\nusing Dates\ndates = [DateTime(2013,6,1,0,0),\n                DateTime(2013,6,10,9,30),\n                DateTime(2013,6,18,13,05),\n                DateTime(2013,7,4,20,35),\n                DateTime(2013,7,13,17,18)]\nconcentrations = [0.2, 0.3, 0.5, 0.38, 0.18]\nx = Dates.format.(dates, \"dd/mm/yy HHMM\")\nplot(x, values, u = \"1:3\",\n     Axes(xdata   = \"time\",\n          timefmt = \"'%d/%m/%y %H%M'\",\n          style   = \"data fsteps\",\n          format  = \"x \\\"%d/%m\\\\n%H:%M\\\"\",\n          xlabel  = \"\\\"Date\\\\nTime\\\"\",\n          ylabel  = \"\\\"Concentration\\\\nmg/l\\\"\",\n          title   = \"'Plot with date and time as x-values'\",\n          key     = \"right\"))\nplot!(x, values, u=\"1:3\", w=:p, marker=\"esquare\", legend = \"'Total P'\")\n```\n\n# Displaying a flag with bars\n\nThis example shows how one can use `set palette defined` to associate particular colors with numerical values.\n\n```@example 2dgal\nimagesc([0 0.5 1 ; 0 0.5 1],\n        Axes(palette=\"defined (0 'blue', 0.5 'white', 1 'red')\",\n             colorbox=:off))\n```\n"
  },
  {
    "path": "docs/v1/2dplots.md",
    "content": "# [2-D plotting tutorial](@id twodeetut)\n\nThis section provides a brief tutorial on 2-D plotting, with examples on how to obtain common plot types. For full details, we refer the reader to gnuplot's documentation.\n\n## Basics of plotting\n\nA call to `plot` looks like this:\n\n    plot(x, y, z, supp, curvekwargs..., Axes(axeskwargs...))\n\n`x`, `y`, `z` and `supp` are the data to plot. Only `y` is mandatory for 2-D plots. For most plots, vectors are plotted, but plotting images requires a matrix or 3-D array. `supp` is a keyword argument used for supplementary data, which are additional columns that gnuplot can use, such as the errorbar length, or the marker size. Gaston translates the provided data to the format that gnuplot requires, and writes it to a temporary file.\n\n`curvekwargs` is a set of keyword arguments that are related to the appearance of the plotted data. These typically specify the plot style, the line color, the marker type, etcetera. These arguments are used to build a `plot` command for gnuplot. Note that, instead of using a bunch of individual keyword arguments, you can pass gnuplot a complete plot command using the keyword `curveconf`.\n\n`axeskwargs` is a set of keyword arguments wrapped in `Axes()`, which specify the look of the axes, or figure; this refers to things like the plot title, tics, ranges, grid, etcetera. Essentially, anything that can be `set` in gnuplot, can be configured from Gaston by wrapping it in `Axes()`. The special keyword `axesconf` is used to provide a string with commands that are passed literally to gnuplot.\n\nTo add a new curve to an existing figure, use `plot!`. It accepts the same arguments as `plot`, except for `Axes()` arguments, which can only be set from `plot`.\n\nThe `plot` command has enough flexibility to plot everything that Gaston is capable of. However, Gaston provides a few specialized commands that make certain plots easier. These are illustrated below.\n\n| Command     | Purpose                          |\n|-------------|:---------------------------------|\n| `scatter`, `scatter!`   | Plot point clouds    |\n| `stem`      | Plot discrete (sampled) signals  |\n| `bar`       | Plot bar charts                  |\n| `histogram` | Plot histograms                  |\n| `imagesc`   | Plot images                      |\n\n(Some of the examples below are taken from lazarusa's [excellent gallery](https://lazarusa.github.io/gnuplot-examples/gallery/)).\n\n## Debug mode\n\nIf you want to see exactly what commands Gaston is sending to gnuplot, you can turn on debug mode:\n\n    set(debug = true)\n\nUse `set(debug = false)` to turn this mode off.\n\n## Set the plot style, line color, line pattern, line width, and markers\n\nThe plot style is set with the keys `w`, `with`, or `plotstyle`. Gnuplot supports many different plot styles; for example, `lines` means plotting a line, `points` is just the markers, and `linespoints` is a line with markers. See all the details in gnuplot's documentation.\n\nThe line color is set with `lc` or `linecolor`; while the line width is specified with `linewidth` or `lw`. The marker type is configured with `pointtype`, `pt` or `marker`. Usually gnuplot identifies each marker type by a number, but Gaston provides some equivalent names (see [Introduction to plotting](@ref). The marker size is configured with `pointsize`, `ps` or `ms`. The number of markers may be configured with `pointnumber` or `pn`.\n\nThe line style can be configured in multiple ways; one is to specify `linestyle` or `ls` followed by a pattern of dashes and points such as `'-.-'`.\n\nThe plotted curve can be given a legend with `title` or `legend`.\n\nThe following examples use all these options.\n\n```@example 2dtut\nusing Gaston # hide\nset(reset=true) # hide\nset(termopts=\"size 550,325 font 'Consolas,11'\") # hide\n# plot with lines and markers\nt = -5:0.05:5\nplot(t, sin,\n     # linespoints plot style\n     w  = :lp,\n     # line color\n     lc = :turquoise,\n     # line width\n     lw = 3,\n     # empty circles\n     marker = \"ecircle\",\n     # marker size\n     ms = 1.5,\n     # plot only ten markers\n     pn = 10,\n     # legend\n     legend = :A_sine_wave\n    )\n```\n\n```@example 2dtut\n# plot with dashed line\nplot(t, sin,\n     # lines plot style\n     w  = :l,\n     # line width\n     lw = 3,\n     # dashed line\n     ls = \"'-.-'\"\n    )\n```\n\n## Set the plot title, axis labels, tics, legends and grid\n\nSince these are attributes of the entire figure, they must be wrapped by `Axes()`. The title is set with `title`, the axis labels with `xlabel` and `ylabel`.\n\nThe tics are configured with `xtics` and `ytics`. The grid can be turned on with `grid`. The position and shape of the legend box is configured with `key`.\n\nThe following example shows how to use these attributes.\n\n```@example 2dtut\nplot(t, sin,\n    w  = :lp, lc = :turquoise, lw = 3,\n    marker = \"ecircle\", ms = 1.5,\n    pn = 10, legend = :A_sine_wave,\n    Axes(# set the title\n         title = \"'Example plot'\",\n         # turn on the grid\n         grid = :on,\n         # specify tics\n         xtics = -5:2:5,\n         ytics = ([-1 0 1], [\"- one\", \"zero\", \"+ one\"]),\n         # configure legend box\n         key = \"outside center bottom\")\n    )\n```\n\n## Logarithmic plots\n\nThe axes can be configured to have a logarithmic scale, using `axis = semilogy`, `semilogx`, or `loglog`.\n\n```@example 2dtut\nusing SpecialFunctions\nQ(x) = 0.5erfc(x/sqrt(2))\nSNR = 1:15\nplot(10log10.(SNR), Q.(sqrt.(SNR)),\n     Axes(axis = \"semilogy\",\n          xlabel = \"'Signal to Noise Ratio (dB)'\",\n          ylabel = \"'Bit Error Rate'\",\n          ytics  = \"out format '10^{%T}'\",\n          grid   = \"xtics mytics\",\n          title  = \"'BPSK Bit Error Rate'\")\n    )\n```\n\n## Step plots\n\nIn step plots, data points are joined with a horizontal line. To obtain a step plot, set the plot style to `steps`, `fsteps`, or `fillsteps`.\n\n```@example 2dtut\nt = -2:0.06:2\nplot(t, sin.(2π*t),\n     plotstyle = :steps,\n     Axes(title = \"'Steps plot'\")\n    )\n```\n\n```@example 2dtut\nplot(t, sin.(2π*t),\n     w = :fillsteps,\n     Axes(style = \"fill solid 0.5\",\n          title = \"'Fillsteps plot'\")\n    )\n```\n\nThe color can be specified with `fillcolor`:\n\n```@example 2dtut\nplot(t, sin.(2π*t),\n     w = :fillsteps,\n     fc = :plum,\n     Axes(style = \"fill solid 0.5\",\n          title = \"'Fillsteps plot'\")\n    )\n```\n\n## Plotting with financial and error bars\n\nGaston supports plotting using financial and error bars, by setting the plot style appropriately. Supplementary data is passed to gnuplot using the argument `supp`.\n\n```@example 2dtut\nx = 1:0.5:8\nopen = 3*(0.5 .- rand(length(x)))\nclose = open .+ 1;\nlow = open .- 1;\nhigh = open .+ 1.5;\nfin = [low high close]\nplot(x, open, supp = fin, plotstyle = \"financebars\",\n     Axes(title = \"'Example of financial bars'\")\n    )\n```\n\n```@example 2dtut\nx = 0:2:50\ny = @. 2.5x/(5.67+x)^2\nerr = 0.05*rand(length(x))\nplot(x, y, supp = err, plotstyle = :errorlines,\n     Axes(title = \"'Example of error lines'\")\n    )\n```\n\n## Plotting filled curves\n\nTo \"fill\" the area below a curve, use the plot style \"filledcurves\". In the example below, we use `curveconf` to pass a full plot command to gnuplot. The `style` is set to `transparent`, so one plot will not obscure those behind it.\n\n```@example 2dtut\nx = LinRange(-10,10,200)\nfg(x,μ,σ) = exp.(.-(x.-μ).^2 ./(2σ^2))./(σ*√(2π))\nplot(x, fg(x, 0.25, 1.5),\n     curveconf = \"w filledcu lc '#E69F00' dt 1 t '0.25,1.5'\",\n     Axes(style = \"fill transparent solid 0.3 noborder\",\n          key = \"title 'μ,σ' box 3\",\n          xlabel = \"'x'\", ylabel=\"'P(x)'\",\n          title = \"'Example of filled curves'\"))\nplot!(x, fg(x, 2, 1), curveconf = \"w filledcu lc '#56B4E9' dt 1 t '2,1'\")\nplot!(x, fg(x, -1, 2), curveconf =\"w filledcu lc '#009E73' dt 1 t '-1,2'\")\n```\n\n## Filling the space between two curves\n\nIt is possible to fill the space between two curves by providing the second curve as a supplementary column. In this example, gnuplot will fill the space between `sin.(x)` and `sin.(x) .+ 1`.\n\n```@example 2dtut\nx = LinRange(-10,10,200)\nplot(x, sin.(x) .- 0.2, supp = sin.(x) .+ 0.2,\n     curveconf = \"w filledcu lc '#56B4E9' fs transparent solid 0.3\",\n     Axes(title = :Filling_the_space_between_two_curves))\nplot!(x, sin.(x), lc = :blue)\n```\n\n## Box plots\n\nThis example shows the use of supplementary data with the \"boxerrorbars\" style. the vector `yerr` controls the length of the error bar for each box, while `lcval` assigns each box a color (since `lc palette` is given in `curveconf`). Finally, a color palette is specified using a symbol (`:summer`), which refers to a color scheme from ColorSchemes.jl.\n\n```@example 2dtut\nusing Random\nx = 1:2:20\ny = 5*rand(10)\nyerr = 0.4*abs.(randn(10))\nlcval = 1:10\nplot(x, y, supp=[yerr lcval],\n     curveconf = \"w boxerrorbars notit lc palette fs solid 0.5\",\n     Axes(palette = :summer,\n          xrange=(0,22),\n          yrange=(0,6))\n    )\n```\n\n## Scatter plots (point clouds)\n\nA scatter plot can be generated with the `scatter` command:\n\n```@example 2dtut\nc = 2rand(1000).-1 .+ im*(2rand(1000).-1)\np = filter(x->abs(x)<1, c)\nscatter(p,\n        marker = \"fsquare\",\n        pointsize = 0.25,\n        Axes(object = \"ellipse at 0,0 size 2,2\",\n             title = \"'Random points within the unit circle'\")\n       )\n```\n\nNote that, when the data to plot is complex, the real part is interpreted as the `x` coordinate and the imaginary part as the `y` coordinate.\n\nBesides the standard markers, any UTF-8 character may be used:\n\n```@example 2dtut\nscatter(randn(30), randn(30), marker = \"λ\")\n```\n\n### Bubble plots\n\nThis example shows how to generate a scatter plot where the color and size of each point is specified with supplementary data. This example also shows how to turn off the colorbox.\n\n```@example 2dtut\nn = 40\nx, y, z = randn(n), randn(n), randn(n)\nplot(x, y, supp = [5z z],\n     curveconf = \"w p notit pt 7 ps var lc palette\",\n     Axes(palette = :ice,\n          xrange = (-2.2, 2.5),\n          yrange = (-2.2, 2.2),\n          colorbox = :off)\n    )\n```\n\n!!! info \"`scatter` with gnuplot\"\n    Behind the scenes, `scatter` calls `plot` with the `points` plotstyle.\n\n## Stem plots\n\nStem plots make it obvious one is plotting a discrete-time signal. The `stem` command replicates the behavior of `stem` in Matlab, Octave, et al:\n\n```@example 2dtut\nt = -2:0.06:2\nstem(t, sin.(2π*t))\n```\n\nBy default, the line color is blue and the lines are made sligthly thicker. If only the vertical lines (\"impulses\") are desired, pass the option `onlyimpulses=true` to `stem`:\n\n```@example 2dtut\nstem(t, sin.(2π*t), onlyimpulses = true)\n```\n\n!!! info \"`stem` with gnuplot\"\n    Behind the scenes, `stem` calls `plot` with the `impulses` plotstyle, followed (if `onlyimpulses == true`) by a call to `plot!` with the `points` plotstyle and the pointtype set to `\"ecircle\"`.\n\n## Bar plots\n\nBar plots can be generated with the `bar` command:\n\n```@example 2dtut\nyear = range(1985, length=20);\ndata = 0.5 .- rand(20)\nbar(year, data,\n    fc = \"'dark-goldenrod'\",\n    legend = \"'Random number'\",\n    Axes(xtics = \"rotate\",\n         key = \"box under\",\n         boxwidth = 0.66,\n         style = \"fill pattern 2\")\n   )\n```\n\n!!! info \"`bar` with gnuplot\"\n   Behind the scenes, `bar` uses gnuplot's `boxes` plotstyle, with a default box width of 0.8 and solid fill.\n\n## Histograms\n\nTo plot histograms, use the `histogram` command. This command takes the same properties as `bar`. In addition, `histogram` accepts a `bins` parameter, used to specify the number of bins, and a `norm` parameter that can be used to normalize the area under the histogram.\n\n```@example 2dtut\nhistogram(rand(10000),\n          bins = 15,\n          norm = 1,\n          Axes(title = :Histogram,\n               yrange = \"[0:1.8]\")\n         )\n```\n\nIt is of course possible to use `histogram` (or any other plot command) along with `plot!` to produce different kinds of plots in the same figure:\n\n```@example 2dtut\nx = -5:0.05:5\ndata = randn(10000)\ngaussian = @. exp(-x^2/2)/sqrt(2π)\nhistogram(data,\n          bins = 25,\n          norm = 1,\n          legend = \"'Experimental'\",\n          linecolor = :turquoise,\n          Axes(boxwidth = \"0.8 relative\",\n               title = \"'Experimental and Theoretical Gaussian distributions'\",\n               key = \"box top left\"))\nplot!(x, gaussian,\n      linecolor = :black,\n      legend = \"'Theoretical'\")\n```\n\n## Images\n\nThe command to plot an image is `imagesc`. It can plot a scaled or RGB image, depending on whether the provided coordinates are an array with two or with three dimensions.\n\nNote that `imagesc` interprets the `x` axis as the columns of the matrix. In other words, element `[1,1]` is located in the top-left corner of the plot, and element `[end:1]` is in the bottom-left corner.\n\n### Scaled image\n\nA scaled image is a plot of a matrix whose elements are interpreted as grayscale values (which may be displayed in color with a given palette).\n\n```@example 2dtut\nZ = [5 4 3 1 0 ;\n     2 2 0 0 1 ;\n     0 0 0 1 0 ;\n     0 1 2 4 3]\nimagesc(Z, Axes(title = \"'Simple scaled image'\", palette = :summer))\n```\n\nTo display the image as grayscale, use the `gray` palette.\n\n```@example 2dtut\nusing Images\nusing TestImages\nimg = testimage(\"lake_gray\");\nii = channelview(img)[1,:,:].*255;\nimagesc(ii, Axes(palette = :gray))\n```\n\n### RGB image\n\nAn RGB image is a plot of a 3-D array whose elements are interpreted as the red, green, and blue components of each image pixel. The array's `[1,;,:]` elements are a matrix representing the red channel, while `[2,:,:]` and `[3,:,:]` are the green and blue channels respectively.\n\n```@example 2dtut\nimg = testimage(\"lake_color\")\nimagesc(channelview(img).*255,\n        Axes(size = \"square\", autoscale = \"fix\"))\n```\n"
  },
  {
    "path": "docs/v1/3d-gallery.md",
    "content": "# [3-D Gallery](@id threedeegal)\n\n(Many of these examples taken from, or inspired by, [@lazarusa's amazing gallery](https://lazarusa.github.io/gnuplot-examples/gallery/))\n\n# Interlocking Tori\n\n```@example 3dgal\nusing Gaston # hide\nset(reset=true) # hide\nset(termopts=\"size 500,500 font 'Consolas,11'\") # hide\nU = LinRange(-pi, pi, 100)\nV = LinRange(-pi, pi, 20)\nx = [cos(u) + .5 * cos(u) * cos(v)      for u in U, v in V]\ny = [sin(u) + .5 * sin(u) * cos(v)      for u in U, v in V]\nz = [.5 * sin(v)                        for u in U, v in V]\nsurf(x', y', z',\n     w = :pm3d,\n     Axes(palette = :dense,\n          pm3d = \"depthorder\",\n          colorbox = :off,\n          key = :false,\n          tics = :false,\n          border = 0,\n          view = \"60, 30, 1.5, 0.9\",\n          style = \"fill transparent solid 0.7\"))\nx = [1 + cos(u) + .5 * cos(u) * cos(v)  for u in U, v in V]\ny = [.5 * sin(v)                        for u in U, v in V]\nz = [sin(u) + .5 * sin(u) * cos(v)      for u in U, v in V]\nsurf!(x', y' ,z' , w = :pm3d)\n```\n\n# Fill a curve in 3-D\n\n```@example 3dgal\nset(saveopts=\"size 550,325 font 'Consolas,11'\") # hide\nx = 0.:0.05:3;\ny = 0.:0.05:3;\nz = @. sin(x) * exp(-(x+y))\nsurf(x, y, z, supp = [z.*0 z], curveconf = \"w zerror t 'Data'\", lw = 3,\n     Axes(xlabel = :X, ylabel = :Y,\n          linetype = :Set1_5,\n          style = \"fill transparent solid 0.3\",\n          xyplane = \"at 0\",\n          grid = :on)\n    )\nsurf!(x.*0, y, z, w = :l, lw = 3)\nsurf!(x, y.*0, z, w = :l, lw = 3)\n```\n\n# Variable marker size and color\n\n```@example 3dgal\nx = 0:0.1:6π;\nscatter3(x, cos.(x), sin.(x), supp = x./10,\n         ps = \"variable\", pt = \"fcircle\", lc = \"palette\",\n         Axes(colorbox = :off))\n```\n\n# Surface with contours\n\n```@example 3dgal\nx = y = -10:0.5:10\nf1 = (x,y) -> cos.(x./2).*sin.(y./2)\nsurf(x, y, f1,\n     lc = :turquoise,\n     Axes(hidden3d = :on,\n          contour = \"base\",\n          cntrparam = \"levels 10\",\n          key = :off))\n```\n\n# Egg-shaped contours\n\n```@example 3dgal\nx = -1:0.05:1\ny = -1.5:0.05:2\negg(x,y) = x^2 + y^2/(1.4 + y/5)^2\nsegg = [egg(x,y) for x in x, y in y]\ncontour(x, y, segg', labels = false,\n        curveconf = \"w l lc palette\",\n        Axes(palette = :cool,\n             cntrparam = \"levels incremental 0,0.01,1\",\n             auto = \"fix\",\n             xrange = (-1.2, 1.2),\n             yrange = (-1.5, 2),\n             cbrange = (0, 1),\n             xlabel = :x,\n             ylabel = :y,\n             size=\"ratio -1\"))\n```\n\n# Tubes\n\n```@example 3dgal\nU  = LinRange(0,10π, 80)\nV = LinRange(0,2π, 20)\nx = [(1-0.1*cos(v))*cos(u) for u in U, v in V]\ny = [(1-0.1*cos(v))*sin(u) for u in U, v in V]\nz = [0.1*(sin(v) + u/1.7 - 10) for u in U, v in V]\nsurf(x, y, z, w=\"pm3d\",\n     Axes(pm3d = \"depthorder\",\n     style = \"fill transparent solid 0.7\",\n     view = \"equal xyz\",\n     xyplane = -0.05,\n     palette = :ice,\n     xrange = (-1.2, 1.2),\n     yrange = (-1.2, 1.2),\n     colorbox = :off))\n```\n\n# Spheres\n\n```@example 3dgal\nΘ = LinRange(0, 2π, 100) # 50\nΦ = LinRange(0, π, 20)\nr = 0.8\nx = [r * cos(θ) * sin(ϕ)      for θ in Θ, ϕ in Φ]\ny = [r * sin(θ) * sin(ϕ)      for θ in Θ, ϕ in Φ]\nz = [r * cos(ϕ) for θ in Θ, ϕ in Φ]\nsurf(x, y, z, w = :l, lc = :turquoise,\n     Axes(view = \"equal xyz\",\n          pm3d = \"depthorder\",\n          hidden3d = :on))\n```\n\n```@example 3dgal\nsurf(x, y, z, w = :pm3d,\n     Axes(style = \"fill transparent solid 0.5\",\n     xyplane = 0,\n     palette = :summer,\n     view = \"equal xyz\",\n     pm3d = \"depthorder\"))\n```\n\n# Torus\n\n```@example 3dgal\nU  = LinRange(-π,π, 50)\nV = LinRange(-π,π, 100)\nr = 0.5\nx = [1 + cos(u) + r * cos(u) * cos(v)  for u in U, v in V]\ny = [r * sin(v)                        for u in U, v in V]\nz = [sin(u) + r * sin(u) * cos(v)      for u in U, v in V]\naxesconf = \"\"\"set object rectangle from screen 0,0 to screen 1,1 behind fillcolor 'black' fillstyle solid noborder\n              set pm3d depthorder\n              set style fill transparent solid 0.5\n              set pm3d lighting primary 0.05 specular 0.2\n              set view 108,2\n              unset border\n              set xyplane 0\n              unset tics\n              unset colorbox\"\"\"\nsurf(x, y, z, w = :pm3d, Axes(palette = :cool, axesconf = axesconf))\n```\n\n# Animation\n\n```julia\ncloseall()  # hide\nz=0:0.1:10pi;\nstep = 5;\ncc = \"w l lc 'turquoise' lw 3 notitle\"\nac = Axes(zrange = (0,30), xrange = (-1.2, 1.2), yrange = (-1.2, 1.2),\n          tics = :off,\n          xlabel = :x, ylabel = :y, zlabel = :z)\nF = scatter3(cos.(z[1:step]), sin.(z[1:step]), z[1:step], curveconf = cc, ac);\nfor i = 2:60\n    pi = scatter3(cos.(z[1:i*step]), sin.(z[1:i*step]), z[1:i*step],\n                  curveconf = cc, ac, handle = 2);\n    push!(F, pi)\nend\nfor i = 60:-1:1\n    pi = scatter3(cos.(z[1:i*step]), sin.(z[1:i*step]), z[1:i*step],\n                  curveconf = cc, ac, handle = 2);\n    push!(F, pi)\nend\nsave(term=\"gif\", saveopts = \"animate size 600,400 delay 1\", output=\"anim3d.gif\", handle=1)\n```\n\n![](assets/anim3d.gif)\n\n```julia\ncloseall() # hide\nx = y = -15:0.4:15\nac = Axes(title = :Sombrero_Surface,\n          palette  = :cool,\n          cbrange  = (-0.2, 1),\n          zrange   = (-0.3, 1),\n          hidden3d = :on)\nF = surf(x, y, (x,y) -> (@. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)),\n         ac, w = :pm3d);\nfor i = 1:-0.1:-1\n    pi = surf(x, y, (x,y) -> (@. i*sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)),\n              ac, w = :pm3d, handle = 2);\n    push!(F, pi)\nend\nfor i = -0.9:0.1:1\n    pi = surf(x, y, (x,y) -> (@. i*sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)),\n              ac, w = :pm3d, handle = 2);\n    push!(F, pi)\nend\nsave(term = \"gif\", saveopts = \"animate size 600,400 delay 1\",\n     output = \"anim3db.gif\", handle=1)\n```\n\n![](assets/anim3db.gif)\n"
  },
  {
    "path": "docs/v1/3dplots.md",
    "content": "# [3-D plotting tutorial](@id threedeetut)\n\nThree dimensional plots can be created with the commands `surf` and `surf!`. These are very similar to [plot](@ref twodeetut), except for the `z` coordinate, which is a matrix that specifies the z-coordinate of the points specified in `x` and `y`.\n\nIn addition, the following commands are specialized for different types of 3-D plots:\n\n| Command     | Purpose                          |\n|-------------|:---------------------------------|\n| `scatter3`, `scatter3!`   | 3-D point clouds   |\n| `contour`      | Contour plots                 |\n| `heatmap`      | Heatmap plots                 |\n\n## How to plot a set of 3-D coordinates\n\nA set of 3-D coordinates may be explicitly given. Plotting them is just a matter of providing the data to gnuplot. Note that the data may be plotted as a wireframe (with plot style `lines`, which is the default), or as a surface (`pm3d`) with an optional palette.\n\n```@example 3dtut\nusing Gaston # hide\nset(reset=true) # hide\nset(termopts=\"size 550,325 font 'Consolas,11'\") # hide\n# plot a wireframe\nx = [0,1,2,3]\ny = [0,1,2]\nz = [10 10 10 10 ; 10 5 1 0 ; 10 10 10 10]\nsurf(x, y, z,\n     Axes(title = \"'3D: Valley of the Gnu from gnuplot manual'\"))\n```\n\nNote that the matrix of `z` coordinates is defined so that the columns are indexed by the `x` coordinates. In other words, `z[1,1]` is the surface at `x = 0, y = 0` and `z[3,1]` is the coordinate at `x = 0, y = 2`.\n\n```@example 3dtut\nsurf(x, y, z, w = :pm3d,\n     Axes(title = \"'Surface with palette'\",\n          palette = :summer))\n```\n\n## How to plot a 3-D function\n\nA 3-D function defines a surface for a given set of `x` and `y` samples. Consider the function `(x,y) -> @. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)`. It may be plotted with\n\n```@example 3dtut\nx = y = -15:0.4:15\nf1 = (x,y) -> @. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\nsurf(x, y, f1, lc = :turquoise,\n     Axes(title    = :Sombrero_Wireframe,\n          hidden3d = :on))\n```\n\n```@example 3dtut\nsurf(x, y, f1, w = :pm3d,\n     Axes(title    = :Sombrero_Surface,\n          palette  = :cool,\n          cbrange  = (-0.2, 1),\n          hidden3d = :on))\n```\n\n## Plotting multiple surfaces with `surf!`\n\nThe equivalent to `plot!` is `surf!`:\n\n```@example 3dtut\nsurf(x,y,f1,w=:pm3d,Axes(title=:Sombrero_Surface,palette=:cool,cbrange=(-0.2,1),hidden3d=:on)) # hide\nsurf!(x, y , (x,y) -> cos.(x./2).*sin.(y./2)-3, lc = :orange, w = :l)\n```\n\n## Plotting contours\n\nGnuplot's contour support is quite flexible. The `contour` command sets up pretty generic contours, which hopefully are useful in many cases. For much more detail, see gnuplot's documentation.\n\nThe contours of a surface can be plotted using:\n\n```@example 3dtut\nx = y = -5:0.1:5\ncontour(x, y, (x,y) -> 5cos.(x/2).*sin.(y/2))\n```\n\nThe labels can be disabled if `labels=false` is passed as an argument:\n\n```@example 3dtut\ncontour(x, y, (x,y) -> 5cos.(x/2).*sin.(y/2), labels=false)\n```\n\n## Plotting heatmaps\n\nHeatmaps can be plotted using `heatmap`. Just like `contour`, this command sets up a pretty basic heatmap; for more control, see gnuplot's documentation.\n\n```@example 3dtut\nheatmap(x, y, (x,y)->cos.(x/2).*sin.(y/2))\n```\n"
  },
  {
    "path": "docs/v1/api.md",
    "content": "# API Reference\n"
  },
  {
    "path": "docs/v1/examples.md",
    "content": "# Examples\n\nThe plots below have been rendered in a png terminal with the following configuration:\n```julia\nGaston.config.term = \"pngcairo font ',10' size 640,480\"\n```\nIn addition, gnuplot's start up file is as described in the Introduction: [Gnuplot startup file](@ref).\n\n## 2-D Plots\n\n```@setup 2dt\nusing Gaston\nGaston.config.output = :echo\nGaston.config.term = \"pngcairo font ',10' size 640,480\"\ncloseall() # hide\n```\n\nLet us start with a simple sine wave plot:\n\n```@example 2dt\nx = range(0, 0.5, length = 100)\ny = sin.(2*pi*10*x)\nplot(x, y)\n```\nNow, let us add a grid and some annotations:\n```@example 2dt\n@plot {grid, title = Q\"{/:Bold A sine wave}\", xlabel = Q\"Time\", ylabel = Q\"Volts\"} x y\n```\nHere we have used `@plot` instead of `plot`, which allows us to specify the plot settings\nas a list of keyword arguments. These arguments can be stored in a \"theme\" using `@gpkw`:\n```julia\nsettings = @gpkw {grid, title = Q\"{/:Bold A sine wave}\", xlabel = Q\"Time\", ylabel = Q\"Volts\"}\n```\nIn addition, we have used the `Q` string macro to avoid typing single quotes; `Q\"Time\"` is\nconverted to `\"'Time'\"`.\n\nNow let us change the line color and markers:\n```@example 2dt\nsettings = @gpkw {grid, title = Q\"A sine wave\", xlabel = Q\"Time\", ylabel = Q\"Volts\"}; # hide\n@plot settings x y {with = \"lp\", lc = Q\"sea-green\", pt = :fcircle, ps = 1.5}\n```\nParameters that affect how the curve is plotted are specified *after* the data. These\ncan also be stored and reused, so that\n```julia\nplotline = @gpkw {with = \"lp\", lc = Q\"sea-green\", pt = :fcircle, ps = 2}\n@plot settings x y plotline\n```\nwould produce the same plot. Settings and plotline parameters can also be specified as strings;\nsee the [Manual](@ref) for all the details. Gaston also has a number of built-in [Themes](@ref).\n\nUse `plot!` or `@plot!` to plot a second curve:\n```@example 2dt\nplotline = @gpkw {with = \"lp\", lc = Q\"sea-green\", pt = :fcircle, ps = 2} # hide\n@plot(settings,\n      {title = Q\"Two sinusoids\", key = \"columns 1\", key = \"box outside right top\"},\n      x, y,\n      plotline, {title = \"'sin'\"})\ny2 = cos.(2*pi*10*x)\n@plot! x y2 {dashtype = Q\".-.\", title = Q\"cos\"}\n```\nHere we see how multiple settings and plotline arguments can be combined. \nNote that new settings cannot be declared in `plot!` commands; only the plotline for the new curve can be\nspecified.\n\n#### Plotting functions\n\nIn the examples above, the data given to `plot` is stored in vectors. Functions can be plotted\ndirectly, with a given range and number of samples, as follows:\n```@example 2dt\ng(x) = exp(-abs(x/5))*cos(x)\ntt = \"set title 'g = x -> exp(-abs(x/5))*cos(x))'\"\nplot(tt, (-10, 10, 200), g) # plot from x = -10 to 10, using 200 samples\n```\nRanges can be specified in the following alternative ways:\n```julia\nplot(g)            # 100 samples, from -10 to 9.99\nplot((a, b), g)    # 100 samples, from a to b\nplot((a, b, c), g) # c samples, from a to b\nplot(x, g)         # plot g.(x)\n```\n\n#### Multiplots\n\nTo plot multiple sets of axes in a single figure, we use indexing into the figure as follows:\n```@example 2dt\nf = plot(x, y) # f is of type Gaston.Figure\nplot(f[2], x, sinc.(10x))\n```\nIt is possible to have empty \"slots\":\n```@example 2dt\nplot(f[4], x, sinc.(20x), \"w lp pn 12\")\n```\nGaston tries to keep a square figure aspect ratio as more and more axes are included. \nAdd another plot to a subplot using indexing:\n```@example 2dt\nplot!(f[2], x, 0.3randn(length(x)))\n```\nTo gain full control of gnuplot's multiplot options, instantiate a\nnew `Gaston.Figure` with the string keyword argument `multiplot`; the string is passed to\ngnuplot's `set multiplot`:\n```@example 2dt\ncloseall() # hide\nf = Figure(multiplot = \"title 'Arbitrary multiplot layout demo'\")\nx = randn(100)\ny = randn(100)\n@plot({margins = (0.1, 0.65, 0.1, 0.65)},\n      x, y,\n      \"w p pt '+' lc 'dark-green'\")\n@gpkw histogram(f[2],\n                {margins = (0.7, 0.95, 0.1, 0.65), tics = false},\n                y,\n                {lc = Q\"dark-green\"}, nbins = 10, horizontal = true)\n@gpkw histogram(f[3],\n                {margins = (0.1, 0.65, 0.7, 0.9), boxwidth = \"1 relative\"},\n                x,\n                {lc = Q\"dark-green\"}, nbins = 10)\n```\nNote that margins can be specified as a tuple. The macro `@gpkw` allows us to use keyword\nsettings in the `histogram` plot command (described below).\n\nThe figure's `multiplot` field can be modified *post-hoc*:\n```julia\nf.multiplot = \"title 'New title' layout 2,2\"\n```\n!!! info\n    Gaston takes care of the multiplot layout automatically **only** if the figure's `multiplot`\n    setting is an empty string (this is the default value). If it's not empty, then the user\n    is in charge of handling the layout.\n\n!!! info\n    Gaston never clears a figure's `multiplot` setting. If re-using a figure for subsequent\n    plots, this setting must be adjusted manually.\n\n## 3-D Plots\nPlotting in 3-D is similar to 2-D, except that `splot` (and `@splot`, `splot!`, `@splot!`) are used\ninstead of `plot`. This example shows how to plot the surface defined by function `s`:\n```@example 2dt\nx = y = -15:0.2:15\ns = (x,y) -> @. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\n@splot(\"set title 'Sombrero'\\nset hidden3d\", {palette = :cool}, x, y, s, \"w pm3d\")\n```\nThe palette `cool` is defined in [ColorSchemes](https://github.com/JuliaGraphics/ColorSchemes.jl).\n\n## Animations\n\nAnimations require use of the `gif` or `webp` terminals (make sure your\nnotebook supports the `image/webp` MIME type before using it).\n\nCreating an animation is similar to multiplotting: multiple axes are drawn on\nthe same figure. When using the `animate` option of the `gif` or `webp`\nterminals, however, the plot is rendered as an animation.\n\nNote that gnuplot will output a message to `STDERR` indicating how many frames\nwere recorded; this message is purely informative and not actually an error.\n\nA difficulty arises when mixing plot formats in a notbook (say, `png` and\n`gif`): the terminal is specified in the configuration variable `Gaston.config.term`.\nHowever, some notebook programs (such as Pluto) execute cells in arbitrary\norder. This means that changing the terminal in one cell may affect other\ncells.\n\nTo solve this problem, Gaston provides a way to ignore the global terminal\nconfiguration when rendering a plot. A figure `f` can be rendered with a given\nterminal by calling `animate(f, term)`. The default value of `term` is stored\nin `Gaston.config.altterm` and defaults to `gif animate loop 0`.\n\nThe following examples illustrate how to create and display animations, in this case with a\nbackground image:\n```@example 2dt\nf = Figure()  # new, empty figure\nframes = 75\nx_bckgnd = range(-1, 1, 200)  # x values for the background image\nbckgnd = Plot(x_bckgnd, sin.(2π*2*x_bckgnd), \"lc 'black'\")  # background image\nx = range(-1, 1, frames)\nfor i in 1:frames\n    plot(f[i], x[i], sin(2π*2*x[i]), \"w p lc 'orange' pt 7 ps 7\") # first plot the function...\n    push!(f[i], bckgnd)  # ... then add the background\nend\nfor i in frames:-1:1  # in reverse\n    plot(f[2frames-i+1], x[i], sin(2π*2*x[i]), \"w p lc 'orange' pt 7 ps 7\")\n    push!(f[2frames-i+1], bckgnd)\nend\nsave(f, output = \"2DAnim.webp\", term = \"webp animate loop 0 size 640,480\")\n```\n![An animation](2DAnim.webp)\n\n## Themes\n\nGaston includes several themes for common plot styles. The easiest way to use\nthem is through the specialized plot commands described below. For more\ndetails, see the [Manual](@ref).\n\nThemes are divided into _settings themes_, which specify gnuplot `set` commands,\nand _plotline themes_, which specify how a particular curve is displayed\n(color, thickness, etc.) Settings themes are stored in the dictionary\n`Gaston.sthemes`, and plotline themes are stored in `Gaston.pthemes`. The\nthemed commands described below use combinations of these themes to create a\nspecific type of plot.\n\nIn gnuplot, plotlines (as in `plot with lines`) are especially difficult to\ntheme, because repeated options are errors, and options given in the wrong\norder may also cause errors. As an example, consider using `scatter` to plot\nsome points; we want to use `pointtype` number 4:\n```julia\nscatter(rand(10), rand(10), \"pointtype = 4\")\n```\nThis command causes an error because the plotline theme `:scatter` already\nspecifies the pointtype! To plot a scatter plot using the desired point type,\nuse plain `plot` with the appropriate settings, create your own theme, or\nmodify the built-in theme. Here is an example where the theme is modified.\nFirst find out how the theme is set up:\n```@example 2dt\nGaston.pthemes[:scatter]\n```\nThen, modify the entry for the pointtype:\n```@example 2dt\nGaston.pthemes[:scatter][2] = \"pointtype\" => 4\nscatter(\"set title 'Scatter plot with modified theme\", rand(10), rand(10), \"lc 'dark-green'\")\n```\nNote how the linecolor was specified without causing an error, since it is not included in the theme.\n\n#### Scatter plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`scatter` | none | `:scatter` |\n|`scatter3` | `:scatter3` | `:scatter` |\n\n```@example 2dt\n# reset theme # hide\n@gpkw Gaston.pthemes[:scatter] = {with = \"points\", pointtype = :fcircle, pointsize = 1.5} # hide\nxg = randn(20)\nyg = randn(20)\nscatter(\"set title 'Scatter plot'\n         set key outside\",\n        xg, yg,\n        \"title 'gaussian'\")\nxu = rand(20)\nyu = rand(20)\nscatter!(xu, yu, \"title 'uniform'\")\n```\nA 3-D scatter plot (the default settings theme (`:scatter3`) draws all the borders):\n```@example 2dt\nscatter3(\"set title 'A 3-D scatter plot\", randn(10), randn(10), randn(10))\n```\n\n#### Stem plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`stem` | none | `:stem`, `:impulses` |\n\nStem plots are often used in digital signal processing applications to represent\na discrete-time (sampled) signal.\n```@example 2dt\nstem(\"set title 'Stem plot'\", g)\n```\nTo generate a stem plot, gnuplot actually plots twice: once with style `impulses` and once with\n`points` (set to empty circles). Normally, each of these plots would have a different color. To\nuse the same color for both, use the `color` keyword argument:\n```@example 2dt\nstem(\"set title 'Stem plot'\", g, color = \"'goldenrod'\")\n```\nThe circular marks can be omitted with the `onlyimpulses` keyword argument:\n```@example 2dt\nstem(\"set title 'Stem plot with onlyimpulses'\", g, onlyimpulses = true)\n```\n\n#### Bar plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`bar` | `:boxplot` | `:box` |\n|`barerror` | `:boxerror` | `:box` |\n\n```@example 2dt\nbar(\"set title 'Bar plot'\", rand(10), \"lc 'turquoise'\")\n```\nThis example shows how to plot two sets of bars, using `bar!`:\n```@example 2dt\nbar(\"set title 'Two bar plots'\", rand(10), \"lc 'dark-violet'\")\nbar!(1.5:10.5, 0.5*rand(10), \"lc 'plum' fill pattern 4\")\n```\nError bars are handled by `barerror`; there is also `barerror!`.\n```@example 2dt\nbarerror(\"set title 'Error bars plot'\", 1:10, rand(10), 0.1*rand(10).+0.1, \"lc 'sandybrown'\")\n```\n\n#### Histograms\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`histogram` | `:histplot` | `:box`, `:horhist` (1-D); `:image`  (2-D) |\n\nThe `histogram` function takes these optional keyword arguments:\n* `nbins`: specifies the number of bins. Defaults to 10.\n* `mode::Symbol`: Controls histogram normalization mode; passed to\n  [`StatsBase.normalize`](https://juliastats.org/StatsBase.jl/stable/empirical/#LinearAlgebra.normalize).\n  Defaults to `:none`.\n* `edges`: a vector or a range specifying the bin edges; if specified, takes\n  precedence over `nbins`. Defaults to `nothing`.\n* `horizontal::Bool`: if `true`, the histogram is drawn horizontally. Defaults\n  to `false`.\n`histogram` uses the settings theme `:histplot`, and plotline themes `:box` or `:horhist`.\n2-D histograms are supported, by passing two datasets.\n\nUsing `nbins`:\n```@example 2dt\nhistogram(\"set title 'Histogram (nbins)'\",\n          randn(10_000),\n          nbins = 20, mode = :pdf)\n```\n\nUsing `edges`:\n```@example 2dt\nhistogram(\"set title 'Histogram (edges)'\",\n          0.75*randn(10_000),\n          edges = -2:0.75:3, \"lc 'dark-khaki'\")\n```\n\nA horizontal histogram:\n```@example 2dt\nhistogram(\"set title 'horizontal histogram'\",\n          rand(1000),\n          nbins = 15, horizontal = true, \"lc 'orchid'\")\n```\n\nIn the case of 2-D histograms, `nbins` or `egdes` may be a tuple; otherwise, both axes use the\nsame settings. The plotline theme is `:image`.\n```@example 2dt\nx = 2.5*randn(100_000)\ny = 2.5*randn(100_000)\nth = @gpkw {palette = :matter, colorbox = false, title = Q\"2-D histogram\",\n            xrange = (-10, 10), yrange = (-10, 10)}\nhistogram(th, x, y, nbins = 50, mode = :pdf)\n```\n\n#### Images\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`imagesc` | `:imagesc` | `:image`, `:rgbimage` |\n\nArrays may be plotted as images using `imagesc`. Note that, in contrast to other plotting packages,\nthe first row is plotted at the top.\n```@example 2dt\nX = [0 1 2 3;\n     0 3 2 1;\n     0 2 2 0;\n     3 0 0 0]\nimagesc(\"unset xtics\\nunset ytics\", X)\n```\nTo display the image as grayscale, use the `gray` palette.\n```@example 2dt\nusing Images, TestImages\nimg = testimage(\"lake_gray\");\nii = channelview(img)[1,:,:].*255;\n@gpkw imagesc({palette = :gray}, ii)\n```\nAn RGB image is a plot of a 3-D array, where  `[1,;,:]`\nis the red channel, `[2,:,:]` is the green channel, and\n`[3,:,:]` is the blue channels.\n```@example 2dt\nimg = testimage(\"lake_color\")\n@gpkw imagesc({size = \"square\", autoscale = \"fix\"}, channelview(img).*255)\n```\n\n#### Surfaces\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`wireframe` | `:hidden3d` | none |\n|`surf` | `:hidden3d` | `:pm3d` |\n\nA surface can be plotted as a \"wireframe\" (or a \"mesh\") with the `wireframe`\ncommand. By default, `hidden3d` is active, so that elements behind the surface\nare not plotted.\n```@example 2dt\nf1(x,y) = sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\nth = @gpkw {title = Q\"Sombrero Wireframe\", palette = :matter}\n@gpkw wireframe(th, (-15, 15, 30), f1)\n```\nSolid surfaces can be plot with `surf`:\n```@example 2dt\nth = @gpkw {title = Q\"Sombrero Surface\", palette = :matter}\n@gpkw surf(th, (-15, 15, 200), f1)\n```\nWhen plotting a function and a single range (such as `(-15, 15, 200)` above) is given, it is used for\nboth `x` and `y` coordinates. Two ranges may be given as well to control the `x` and `y` ranges\nseparately:\n```@example 2dt\n@gpkw surf(th, (-15, 15, 200), (-25, 5, 200), f1)\n```\n\n#### Contour plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`contour` | `:contour` | `:labels` |\n| `surfcountour` | `:contourproj` | `:labels` |\n\nBy default, contour plots include numerical labels:\n```@example 2dt\nf2(x,y) = cos(x/2)*sin(y/2)\ncontour(\"set title 'Contour Plot'\", (-10, 10, 50), f2)\n```\nTo plot contours without labels, use the keyword argument `labels = false`:\n```@example 2dt\ncontour(\"set title 'Contour Plot Without Labels'\", (-10, 10, 50), f2, labels = false)\n```\nIt's possible to plot a wireframe surface and a contour projected on the base of the plot\nusing `surfcountour`:\n```@example 2dt\nsurfcontour(\"set title 'Surface With Projected Contours'\", (-5, 5, 40), f2, \"lc 'orange'\")\n```\nThe same plot without contour labels:\n```@example 2dt\nsurfcontour(\"set title 'Surface With Contours, No Labels'\", (-5, 5, 40), f2, \"lc 'orange'\", labels = false)\n```\n\n#### Heatmap plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`heatmap` | `:heatmap` | `:pm3d` |\n\n```@example 2dt\ntheme = @gpkw {palette = :matter, title = Q\"Heatmap\"}\nheatmap(theme, :notics, :nocb, :labels, (-10, 10, 70), f2)\n```\n\n##### Contour lines on heatmap\n\nIt is possible to include contour lines in a heatmap plot. The following example is\ntaken from [this gnuplot blog post]\n(https://gnuplot-tricks.blogspot.com/2009/07/maps-contour-plots-with-labels.html).\nThe function `Gaston.plotwithtable` returns a `Gaston.DataTable`, which wraps\n`IOBuffer`. It can be used as an argument to `plot`.\n```@example 2dt\n# define function to plot\nx = y = range(-5, 5, 100)\nf4(x,y) = sin(1.3*x)*cos(0.9*y)+cos(.8*x)*sin(1.9*y)+cos(y*.2*x)\n\n# obtain function contours using 'plot with table'\nsettings = \"\"\"set contour base\n              set cntrparam level incremental -3, 0.5, 3\n              unset surface\"\"\"\ncontours = Gaston.plotwithtable(settings, x, y, f4)\n\n# calculate meshgrid for heatmap plot\nz = Gaston.meshgrid(x, y, f4)\n\n# plot heatmap and contours\nplot(\"\"\"unset key\n        unset colorbox\n        set palette rgbformulae 33,13,10\"\"\",\n        x, y, z, \"with image\")\nplot!(contours, \"w l lw 1.5 lc 'slategray'\")\n```\n\n## Other examples\n\n#### [3-D Euler spiral (Clothoid)](https://en.wikipedia.org/wiki/Euler_spiral)\n\n```@example 2dt\nusing QuadGK\nz = range(-5, 5, 200)\nfx(z) = sin(z^2)\nfy(z) = cos(z^2)\nx = [quadgk(fx, 0, t)[1] for t in z]\ny = [quadgk(fy, 0, t)[1] for t in z]\nsplot(\"\"\"unset zeroaxis\n         set tics border\n         set xyplane at -5 \n         set view 65,35\n         set border 4095\"\"\",\n         x, y, z, \"w l lc 'black' lw 1.5\")\n```\n\n#### Waterfall\n\nInspired by this [Julia Discourse discussion](https://discourse.julialang.org/t/how-to-produce-a-waterfall-plot-in-julia/93441).\n```@example 2dt\nx = -15:0.1:15\ny = 0:30\nu1data = [exp(-(x-0.5*(y-15))^2) for x in x, y in y]\nZf = fill(0.0, length(x))\nf = Figure()\nGaston.set!(f(1), \"\"\"set zrange [0:2]\n               set tics out\n               set ytics border\n               set xyplane at 0\n               set view 45,5\n               set zrange [0:3]\n               set xlabel 'ξ' offset -0,-2\n               set ylabel 't'\n               set zlabel '|u|'\n               set border 21\"\"\")\nfor i in reverse(eachindex(y))\n    Y = fill(y[i], length(x))\n    Z = u1data[:,i]\n    splot!(x, Y, Z, Zf, Z, \"w zerrorfill lc 'black' fillstyle solid 1.0 fc 'white'\")\nend\nf\n```\n\n## Plot recipes\n\nThere are two ways to extend Gaston to plot arbitrary types. The first is to define a new\nfunction that takes the required type, and returns a `Gaston.Figure`. For example, we may wish to plot\ncomplex data as two subplots, with the magnitude and phase of the data. This can be done as follows:\n```@example 2dt\nfunction myplot(data::Vector{<:Complex}; kwargs...)\n                    x = 1:length(data)\n                    y1 = abs2.(data)\n                    y2 = angle.(data)\n                    Gaston.sthemes[:myplot1] = @gpkw {grid, ylabel = Q\"Magnitude\"}\n                    Gaston.sthemes[:myplot2] = @gpkw {grid, ylabel = Q\"Angle\"}\n                    Gaston.pthemes[:myplot1] = @gpkw {w = \"lp\"}\n                    Gaston.pthemes[:myplot2] = @gpkw {w = \"p\", lc = \"'black'\"}\n                    f = Figure(multiplot = \"layout 2,1\")\n                    plot(f[1], x, y1, stheme = :myplot1, ptheme = :myplot1)\n                    plot(f[2], x, y2, stheme = :myplot2, ptheme = :myplot2)\n                    return f\n                end\nt = range(0, 1, 20)\nmyplot(exp.(t) .* cis.(2*pi*7.3*t))\n```\nThe use of themes allows the user to modify the default properties of the plot, by modifying the\nthemes (such as `Gaston.sthemes[:myplot1]`) instead of having to re-define `myplot`.\n\nThe second way to plot an arbitrary type is to define a new method of `Gaston.convert_args` for that\ntype (or `Gaston.convert_args3` for 3-D plots). Here's an example:\n\n```@example 2dt\nusing Gaston: PlotObject, TimeSeries, TSBundle\nimport Gaston: convert_args\n\nstruct MyType end\n\nfunction convert_args(x::MyType)\n    t1 = range(0, 1, 40)\n    t2 = range(-5, 5, 50)\n    z = Gaston.meshgrid(t2, t2, (x,y) -> cos(x)*cos(y))\n    @gpkw PlotObject(\n        TSBundle(\n            TimeSeries(1:10, rand(10)),\n            settings = {title = Q\"First Axis\"}\n        ),\n        TSBundle(\n            TimeSeries(t1, sin.(5t1), pl = {lc = Q\"black\"}),\n            TimeSeries(t1, cos.(5t1), pl = {w = \"p\", pt = 16}),\n            settings = {title = Q\"Trig\"}\n        ),\n        TSBundle(\n            TimeSeries(t2, t2, z, pl = {w = \"pm3d\"}, is3d = true),\n            settings = {title = Q\"3D\",\n                        tics = false,\n                        palette = (:matter, :reverse)}\n        ),\n        TSBundle(\n            TimeSeries(1:10, 1:10, rand(10,10), pl = \"w image\"),\n            settings = {tics, title = false}\n        ),\n        mp_settings = \"title 'A Four-Axes Recipe' layout 2,2\"\n    )\nend\n\nfigure().multiplot = \"\" # hide\nplot(MyType())\n```\n"
  },
  {
    "path": "docs/v1/extending.md",
    "content": "# Extending Gaston\n\nGaston offers multiple plotting commands that cover most cases of general data plotting. However, it is sometimes convenient to extend its capabilities to cover more specific use cases. One simple case is the generation of consistent plots of a certain type. A more complex example is plotting of new types, not just numerical data. We illustrate these two cases with examples.\n\n## Consistent plots in a specific application\n\nConsider this situation: we are designing and simulating a new communications algorithms and we need to plot the resulting bit error rate (BER). BER plots have certain characteristics:\n\n* The x label defaults to `E_b/N_0 (dB)`.\n* The y label defaults to `Bit Error Rate`, but could also be `Symbol Error Rate`.\n* The y axis is logarithmic, and we want the tics to be negative powers of ten.\n* The grid should be visible.\n* The plot title defaults to `BER as a function of SNR`.\n* We wish to have markers at each SNR value, defaulting to full diamonds.\n* We like to use a thick line (width 2) that defaults to color blue.\n\nLet us define a new plot command, called `berplot`, that allows us to do this.\n\n```@example ext\nusing Gaston # hide\nset(reset=true) # hide\nset(termopts=\"size 550,325 font 'Consolas,11'\") # hide\nfunction berplot(snr, ber, axes::Axes = Axes() ; ser = false, args...)\n\n    # support an optional Boolean argument to control the y label\n    ylab = \"'Bit Error Rate'\"\n    if ser\n        ylab = \"'Symbol Error Rate'\"\n    end\n\n    # Build the default axes configuration\n    a = Axes(title  = \"'BER as a function of SNR'\",\n             xlabel = \"'E_b/N_0 (dB)'\",\n             ylabel = ylab,  # use the label specified by ser\n             axis   = \"semilogy\",\n             grid   = \"on\",\n             ytics  = \"out format '10^{%T}'\"\n            )\n\n    # Execute the plot command with the default curve configuration. Note that\n    # the default axes configuration is merged with the one provided by the\n    # user, giving preference to the latter.\n    plot(snr, ber, Gaston.merge(a, axes) ;\n         w = :lp,\n         lc = :blue,\n         lw = 2,\n         marker = \"fdmd\",\n         args...\n        )\nend\n```\n\nLet us try it out:\n\n```@example ext\nusing SpecialFunctions\nQ(x) = 0.5erfc(x/sqrt(2))\nsnr = 3:15\nsnr_dB = 10log10.(snr)\nber = 4Q.(sqrt.(snr))\nberplot(snr_dB, ber)\n```\n\nLet us verify that we can control the y label:\n\n```@example ext\nberplot(snr_dB, ber, ser = true)\n```\n\nAnd verify that we can override the defaults:\n\n```@example ext\nberplot(snr_dB, ber, Axes(grid = \"xtics mytics\"), lc = :orange)\n```\n\n## Plotting a new type\n\nAs an example, let us extend `plot` to display the frequency and phase response of a filter designed with [DSP.jl](https://docs.juliadsp.org/stable/filters/). The idea is to plot magnitude and phase responses in two subplots, Matlab-style.\n\n```@example ext\nusing DSP, FFTW\n\nfs = 200.\ndf = digitalfilter(Lowpass(50, fs=fs), Chebyshev1(21, 0.5))\ntypeof(df)\n```\n\nWe cannot run `plot(df)` directly, since neither Gaston nor gnuplot know what to do with data of this type.. We need to extend `plot` to type `ZeroPoleGain`, wee can also define a default plot configuration.\n\n```@example ext\n# We need to explicitly import plot, since we're extending it.\nimport Gaston.plot\n\nfunction plot(x::ZeroPoleGain, axes::Axes = Axes() ; fs = π, n = 250, args...)\n    # The filter's frequency response is obtained with freqz.\n    f = range(0, fs/2, length = n)\n    fz = freqz(x, f, fs)\n    mg = abs.(fz)\n    ph = angle.(fz)\n\n    # magnitude plot\n    a = Axes(title = \"'Magnitude response'\",\n             grid = :on,\n             xlabel = \"'Frequency'\",\n             ylabel = \"'Magnitude'\")\n    p1 = plot(f, mg, merge(a, axes) ; handle = Gaston.nexthandle(), args...)\n\n    # phase plot\n    a = Axes(title = \"'Phase response'\",\n             grid = :on,\n             xlabel = \"'Frequency'\",\n             ylabel = \"'Phase'\")\n    p2 = plot(f, ph, merge(a, axes) ; handle = Gaston.nexthandle(), args...)\n\n    plot([p1 ; p2])\n\nend\n```\n\nNote that, when creating plots for the magnitude and phase response, the function `Gaston.nexthandle()` is used to select a new, unused handle. The reason is that `plot` overwrites the last created plot. So, when defining `p1` we run the risk of overwriting the last plot the user created, and when defining `p2` we run the risk of overwriting `p1`. These are avoided by choosing handles that are not shared with any other plots.\n\nLet us test it:\n\n```@example ext\nset(termopts = \"size 550, 600 font 'Consolas,11'\")  # hide\nplot(df, fs=fs)\n```\n"
  },
  {
    "path": "docs/v1/extguide.md",
    "content": "# Extension Guide\n"
  },
  {
    "path": "docs/v1/faq.md",
    "content": "# Usage notes and FAQ\n\n## How to set the terminal\n\nGnuplot supports a huge amount of terminals. Most modern gnuplot installations should support the `qt` terminal. A different terminal can be selected with\n\n    set(term = t::String)\n\nwhere `newterm` is the desired terminal. Most terminals accept a configuration string, which can be set with\n\n    set(termopts = opt::String)\n\nFor example, to set the font on the qt terminal to Consolas size 11, one could do\n\n    set(term = \"qt\")  # not needed in most installations\n    set(termopts = \"font 'Consolas,11')\n\n!!! info \"Choosing a terminal on Windows\"\n    On Windows, Gaston selects the `windows` terminal by default. Changing the terminal is not recommended, since they tend to be very slow and have other issues (for example, see [here](https://github.com/mbaz/Gaston.jl/issues/136) and [here](https://sourceforge.net/p/gnuplot/bugs/2279/)).\n\n## What settings are available in Gaston?\n\nThe `set` command can be used to configure Gaston's behavior. The following settings are available:\n\n| Setting | Purpose |\n|:--------|:--------|\n| term | Sets gnuplot's terminal. |\n| termopts | Sets the terminal's options. |\n| mode    | If set to \"null\", plots are not shown. |\n| preamble | A string that is sent to gnuplot for every plot. |\n| debug | If set to `true`, all data sent to gnuplot is printed on the screen. |\n| saveopts | A string that specifies options when saving a plot. |\n| showable | For IJulia, Documenter.jl, Juno and similar uses. Defaults to `\"png\"`; all plots are generated in PNG format only. Set to `\"svg\"` to enable SVG plots, or `\"png+svg\"` to enable both.\n| timeout | How long Gaston waits for gnuplot to complete a plot. Defaults to 10 seconds (20 on Windows and Mac); you may need to set it higher if your computer is slow or you're plotting lots of data.\n\n## How to plot text or sixels?\n\nPlots can be rendered using text on the console by setting the terminal to `dumb`:\n\n```julia\nusing Gaston  # hide\nset(reset=true)  # hide\nset(term = \"dumb\", termopts = \"size 80,25 ansirgb\")\nt = -5:0.1:5\nplot(t, sin);\nplot!(t, cos)\n```\n\n![](assets/dumb.png)\n\nTo plot with sixels, use\n\n    set(term = \"sixelgd\")\n\nA terminal that supports sixels is required (for example, xterm in mode vt340 (invoked as `xterm -ti vt340`).\n\n![](assets/sixels.png)\n\n# How to configure plot size in Documenter.jl, IJulia, etc?\n\nIn these environments, the front-end chooses among all supported MIME types. Gaston supports PNG and SVG images. Some of these front-ends, though, ask Gaston to produce plots in both formats, and then choose SVG. This is a waste of resources, and combined with the fact that plots in SVG format can grow very large, it is recommeded to configure Gaston to produce only PNG files. This is achieved with\n\n    set(ijulia = \"png\")\n\nThe `png` terminal can be configured with\n\n    set(termopts=...)\n\nFor example, the plots in this document are created with these settings:\n\n    set(termopts=\"size 550,325 font 'Consolas,11'\")\n\n## I run `plot` inside a `for` loop and no plots are produced! (or: Julia's display system)\n\nJulia separates the calculation of a result from the display of the result (see [custom pretty-printing](https://docs.julialang.org/en/v1/manual/types/#man-custom-pretty-printing-1) and [multimedia I/O](https://docs.julialang.org/en/v1/base/io-network/#Multimedia-I/O-1) in Julia's documentation). This mechanism is very powerful; in Gaston, it enables plotting to the REPL, Jupyter, Juno, or in Documenter.jl with just a few lines of code. In other words, plotting is not a side effect of running `plot`, the way it is in, say, Matlab; rather, a plot is produced when a result of type `Gaston.Figure` is returned by some code.\n\nWhile elegant and powerful, this mechanism can also be surprising if you're used to side-effect plotting. None of the following code samples display any plots:\n\n```julia\ny = rand(20)\nplot(y);  # note the final ; suppreses displaying the result\n```\n\n```julia\n# nothing is returned by the for loop\nfor k = 1:5\n    plot(k*y)\nend\n```\n\n```julia\n# function that does not return a figure\nfunction f(y)\n    plot(sin.(y))\n    println(\"Sum = $(sum(y))\")\nend\n```\n\nThe common problem in the code samples above is that a figure is never returned; in consequence, no figure is displayed. This can be fixed by making sure your code returns a figure; or alternatively, save the figure in a variable and display it when it is convenient. For example:\n\n```julia\np = Gaston.Figure[]\nfor k = 1:5\n    push!(p, plot(k*y))\nend\n```\n\nNow, `p[3]` returns the third plot (for example). Another way to force the figure to be rendered is to call `display()`:\n\n```julia\n# all five figures are displayed\ncloseall()\nfor k = 1:5\n    figure()\n    display(plot(k*y))\nend\n```\n## How does gnuplot report errors?\n\nGnuplot's main drawback, from a usability standpoint, is that it is not a library; it is designed to be used interactively. Gaston simulates a user typing interactive commands in a gnuplot session. Gaston tries to catch any errors reported back by gnuplot.\n\nAn example of an error returned by gnuplot and caught by Gaston:\n\n```julia\nusing Gaston # hide\ny = rand(20)\nplot(y, plotstyle=\"linepoints\")  # missing an 's'\n```\n\nresults in an error message like:\n\n```julia\n┌ Warning: Gnuplot returned an error message:\n│\n│ gnuplot> plot '/tmp/jl_d8yIs9' i 0  with linepoints\n│                                                     ^\n│          line 0: unrecognized plot type\n│\n└ @ Gaston ~/.julia/dev/Gaston/src/gaston_llplot.jl:172\n```\n\nGaston does its best effort to read and display any warnings or errors produced by gnuplot, and to recover gracefully. In some corner cases, it might happen that the communication link enters an unforeseen state and a restart is required. Please file a Gaston issue if you experience this.\n\n## Support\n\nPlease post support questions to [Julia's discuss forum](https://discourse.julialang.org/tag/plotting).\n\n## Contributing\n\nBug reports, suggestions and pull requests are welcome at [Gaston's github page](https://github.com/mbaz/Gaston.jl)\n"
  },
  {
    "path": "docs/v1/figures.md",
    "content": "# Managing multiple figures\n\nWhen using a graphical terminal such as `qt` or `wxt`, it is possible to have multiple figures on the screen at one time. Gaston provides a few commands to help manage them.\n\nEach figure is identified by its handle, which must be an integer larger than zero. Handles don't have to be consecutive. Plotting commands accept an optional handle argument, which directs the plot to the specified figure. For example, in this code:\n\n```julia\nt = -5:0.01:5\nplot(t, sin)\nfigure()  # creates a new figure\nplot(t, cos)\nplot!(t, sin.(t).^2, handle = 1)\n```\nthe cosine will be plotted in a second figure, while the squared sine will be appended to the first figure.\n\n```@docs\nfigure\nclosefigure\ncloseall\n```\n"
  },
  {
    "path": "docs/v1/index.md",
    "content": "```@meta\nAuthor = \"Miguel Bazdresch\"\n```\n\n# Gaston.jl\n\nGaston (source code [here](https://github.com/mbaz/Gaston.jl)) is a Julia package for plotting. It provides an interface to [gnuplot](http://www.gnuplot.info), a mature, powerful, and actively developed plotting package available on all major platforms.\n\nGaston emphasizes easy and fast plotting on the screen, notebook or IDE. Knowledge of gnuplot is not required, but some familiarity is beneficial. Gaston also exposes the full power of gnuplot, for more expert users.\n\n```@example t2\nusing Gaston, SpecialFunctions\nset(reset=true) # hide\nset(termopts=\"size 550,325 font 'Consolas,11'\") # hide\nx = y = 0:0.075:10\nsurf(x, y, (x,y) -> besselj0(y)*x^2, with = \"pm3d\",\n     Axes(view = (45, 45),\n          pm3d = \"lighting primary 0.5 specular 0.4\",\n          key = :off)\n     )\n```\n\n(Image inspired by [What's new in gnuplot 5.2?](https://lwn.net/Articles/723818/))\n\n## Gaston features\n\n* Plot using graphical windows, and keeping multiple plots active at a time, with mouse interaction. A browser is not required to show plots.\n* Plot also directly to the REPL, using text (ASCII) or [sixels](https://en.wikipedia.org/wiki/Sixel).\n* Plot in Jupyter, Juno or VS Code.\n* \"Recipes\" to generate common 2-D and 3-D plots, such as stem plots, histograms, images, surfaces, contour and heatmaps.\n* Easy definition of custom plotting commands for specific types, or with specific defaults.\n* Save plots to multiple formats, including pdf, png and svg.\n* Color palettes from [ColorSchemes.jl](https://github.com/JuliaGraphics/ColorSchemes.jl).\n* Export plots for integration into Latex documents.\n* A simple interface to almost the full power of gnuplot, for users who have more experience with it.\n* Fast first plot: load package, plot, and save to pdf in less than six seconds. Subsequent plots take a few hundreds of milliseconds.\n* A simple interface to manage multiple plots, using commands such as `figure()`, `closeall()`, etc.\n\n### Gaston and Gnuplot.jl: two philosophies\n\n[Gnuplot.jl](https://github.com/gcalderone/Gnuplot.jl) is another front-end for gnuplot, with comparable capabilities to Gaston. An example serves to illustrate the differences in how the two packages approach the interface problem. Consider [this example plot](https://gcalderone.github.io/Gnuplot.jl/v1.3.0/basic/#Multiple-datasets,-logarithmic-axis,-labels-and-colors,-etc.-1):\n\n```julia\nx = 1:0.1:10\n@gp    \"set grid\" \"set key left\" \"set logscale y\"\n@gp :- \"set title 'Plot title'\" \"set label 'X label'\" \"set xrange [0:*]\"\n@gp :- x x.^0.5 \"w l tit 'Pow 0.5' dt 2 lw 2 lc rgb 'red'\"\n@gp :- x x      \"w l tit 'Pow 1'   dt 1 lw 3 lc rgb 'blue'\"\n@gp :- x x.^2   \"w l tit 'Pow 2'   dt 3 lw 2 lc rgb 'purple'\"\n```\n\nThis shows that Gnuplot.jl essentially allows one to write gnuplot commands directly in Julia. The same plot in Gaston would be:\n\n```@example t2\nx = 1:0.1:10\nplot(x, x.^0.5,\n     w = \"l\",\n     legend = \"'Pow 0.5'\",\n     dt = 2,\n     lw = 2,\n     lc = :red,\n     Axes(grid = :on,\n          key = \"left\",\n          axis = \"semilogy\"))\nplot!(x, x,\n      w = :l,\n      leg = :Pow_1,\n      dt = 1,\n      lw = 3,\n      lc = :blue)\nplot!(x, x.^2,\n      curveconf = \"w l tit 'Pow 2' dt 3 lw 2 lc 'purple'\")\n```\n\nIn summary, Gaston offers a function-based interface, and gnuplot commands can be specified in a few different ways, with convenient notation, such as the optional use of \"legend\" instead of gnuplot's \"title\", symbols to avoid typing quote marks (\") all the time, and others that are described later in this document.\n\n## Installation\n\nGaston requires Julia version 1.3.0 or above, and requires Gnuplot version 5.0 or above (version 5.2.8 is recommended). You should install gnuplot on your system prior to using Gaston. On Linux, it is highly recommended that you select a version with support for Qt: on Debian and Ubuntu, you will need `gnuplot-qt`.\n\nTo install Gaston from the Julia REPL, run\n\n```julia\njulia> ]add Gaston\n```\n\nTyping `]` switches the Julia REPL to the package manager, and the `add` command installs the package. To exit the package manager, hit the backspace key.\n\n## Gnuplot configuration\n\nGaston respects user configuration settings in gnuplot's startup file. Left un-configured, gnuplot's plots are less than attractive. The following minimum configuration is suggested (and was used to generate the plots in this document):\n\n    set linetype 1 lc rgb \"blue\" pt 3\n    set linetype 2 lc rgb \"red\" pt 4\n    set linetype 3 lc rgb \"green\" pt 6\n    set linetype 4 lc rgb \"black\" pt 12\n    set linetype 5 lc rgb \"blue\" pt 5\n    set linetype 6 lc rgb \"red\" pt 1\n    set linetype 7 lc rgb \"green\" pt 2\n    set linetype 8 lc rgb \"black\" pt 7\n    set linetype cycle 8\n    set style data lines\n    set key noautotitle\n    set auto fix\n    set offsets graph .05, graph .05, graph .05, graph .05\n\nThe configuration file is `~/.gnuplot` in Unix-like systems, and `%APPDATA%\\GNUPLOT.INI` in Windows.\n\n## Next steps\n\nLoad Gaston into your Julia session with\n\n```julia\nusing Gaston\n```\n\nThe [Introduction to plotting](@ref) has more information about basic use and configuration.\n\nThere is a [2-D plotting tutorial](@ref twodeetut) and a [3-D plotting tutorial](@ref threedeetut).\n\nThe [Extending Gaston](@ref) section explains how to extend Gaston by creating your own \"recipes\", both for specific kinds of plots, and for plotting data of specific types.\n\nThere is a section on  [Managing multiple figures](@ref) and all related commands.\n\nThe [2-D Gallery](@ref twodeegal) and [3-D Gallery](@ref threedeegal) show many plotting examples.\n\nThe [Usage notes and FAQ](@ref) section includes additional usage examples and answers frequent questions.\n\n## Gnuplot resources\n\nThese websites have more information on gnuplot and how to use it:\n\n* [Official website](http://www.gnuplot.info/)\n* [Official demo gallery](http://gnuplot.sourceforge.net/demo_5.2/)\n* [PDF documentation for 5.2](http://www.gnuplot.info/docs_5.2/Gnuplot_5.2.pdf)\n* [A good blog on gnuplot](http://www.gnuplotting.org/)\n\n## Running tests\n\nGaston includes an extensive test suite, which can executed with:\n\n```julia\njulia> ]test Gaston\n```\n\nAll tests should pass (but a few may be skipped).\n\n## Support\n\nPlease post support questions to [Julia's discuss forum](https://discourse.julialang.org/tag/plotting).\n\n## Contributing\n\nBug reports, suggestions and pull requests are welcome at [Gaston's github page](https://github.com/mbaz/Gaston.jl)\n"
  },
  {
    "path": "docs/v1/introduction.md",
    "content": "```@meta\nAuthor = \"Miguel Bazdresch\"\n```\n\n# Introduction to plotting\n\nGaston supports essentially all 2-D plots styles that gnuplot is capable of, including regular function plots, plots with logarithmic axes, scatter, stem and step plots, bar plots and histograms, images, etcetera.\n\nIt can also create 3-D plots, including wireframe, surface, scatter and contour plots.\n\nThis section presents the basic usage of `plot` and `plot!`. Examples of specific plot types, such as cloud points, stem plots, and images, are presented in [2-D plotting tutorial](@ref twodeetut). For 3-D plots, see [3-D plotting tutorial](@ref threedeetut).\n\n## `plot` and `plot!` commands\n\nThe main 2-D plotting commands are `plot` and `plot!`. To plot a vector `y` against a vector `x`, use `plot(x,y)`:\n\n```@example intro\nusing Gaston # hide\nset(reset=true) # hide\nset(termopts=\"size 550,325 font 'Consolas,11'\") # hide\nt = 0:0.01:1\nplot(t, sin.(2π*5*t),\n     linecolor  = :coral,\n     Axes(title = :First_Plot)\n    )\n```\n\nTo add a second curve, use `plot!`:\n\n```@example intro\nplot!(t, cos.(2π*5*t),\n      plotstyle = \"linespoints\",\n      pointtype = \"ecircle\",\n      linecolor = \"'blue'\"\n     )\n```\n\nCurves are added to a figure one by one; the first curve is plotted with `plot`, and the rest with succesive `plot!` commands.\n\nThe commands `plot` and `plot!` take three kinds of arguments:\n* Data, in the form of vectors `x`, `y`, etcetera.\n* Configuration related to the data's appearance: line color, line width, markers, line style, etcetera. These are passed to `plot` as regular arguments (for example, `linecolor = :coral` above).\n* Configuration related to the entire figure: title, tics, ranges, grid, etcetera. These must be wrapped in `Axes()`; for example, `Axes(title = :First_Plot)`. Only `plot` accepts these arguments.\n\n## Figure and curve configuration\n\nAn example can be worth a thousand words. The following commands are all exactly equivalent:\n\n```julia\nplot(t, sin.(2π*5*t),\n     with       = \"linespoints\",\n     linecolor  = :coral,\n     Axes(title = :First_Plot,\n          xtics = \"(0.25, 0.5, 0.75)\")\n    )\n```\n\n```julia\nplot(t, sin.(2π*5*t),\n     curveconf = \"with linespoints linecolor 'coral'\",\n     Axes(title = :First_Plot,\n          xtics = \"(0.25, 0.5, 0.75)\")\n    )\n```\n\n```julia\nA = Axes(title = :First_Plot, xtics = \"(0.25, 0.5, 0.75)\")\nplot(t, sin.(2π*5*t), A,\n     plotstyle = :linespoints,\n     lc  = \"'coral'\"\n    )\n```\n\n```julia\nplot(t, sin.(2π*5*t),\n     curveconf = \"w lp lc 'coral'\",\n     Axes(axesconf = \"\"\"set title 'First Plot'\n                        set xtics (0.25, 0.5, 0.75)\"\"\")\n    )\n```\n\n```julia\nplot(t, sin.(2π*5*t),\n     curveconf = \"w lp lc 'coral'\",\n     Axes(axesconf = \"set title 'First Plot'\",\n          xtics = \"(0.25, 0.5, 0.75)\")\n    )\n```\n\n## How arguments are handled\n\nGaston has a few rules to handle arguments, and supports special syntax to make passing commands to gnuplot more convenient. All configuration commands are given as key-value arguments.\n\n* Values can be given in quotes (`\"'red'\"`) or as symbols (`:red`).\n* Values in quotes are passed directly to gnuplot.\n* Symbol values are passed to gnuplot wrapped in single quotes. For example, `linecolor = :blue` in the example above is translated as `linecolor 'blue'`.\n* In symbols, underscores are converted to spaces. For example, `title = :First_Plot` is translated as `set title 'First Plot'`.\n* When an argument is a vector, each element is handled as a separate argument. For example, `xtics = [1:2:5, \"reverse\"]` is translated to two separate gnuplot commands, `set xtics 1, 2, 5` and `set xtics reverse`.\n* To send a `set` command without options, like `set grid`, use (for example) `grid = :on` (or `\"on\"`, or `true`).\n* To send an `unset` command, use (for example) `tics = :off` (or `\"off\"`, or `false`).\n\n!!! info \"Interaction with gnuplot\"\n    Keyword arguments wrapped in `Axes()` are converted to gnuplot `set` commands. For example,\n\n        Axes(pm3d = \"lighting primary 0.5\")\n\n    is sent to gnuplot as\n\n        set pm3d lighting primary 0.5\n\n    Other keyword arguments are used as plot elements; for example,\n\n        w = :lp, u = \"1:3\"\n\n    is sent to gnuplot as\n\n        plot 'filename' w lp u 1:3\n\n## Configuring a figure's appearance\n\nAs explained above, a figure's configuration is given as key-value pairs wrapped in `Axes()`. Some arguments have special syntax for convenience:\n\n* The `axis` argument sets the axis type:\n    * `axis = \"semilogx\"` →  `set logscale x`\n    * `axis = \"semilogy\"` →  `set logscale y`\n    * `axis = \"semilogz\"` →  `set logscale z`\n    * `axis = \"loglog\"` →  `set logscale xyz`\n\n* Tics are set with `xtics`, `ytics`, `ztics` or `tics`:\n    * `tics = a:b:c` →  `set tics a, b, c`\n    * `tics = (a:b:c, [\"l1\" \"l2\" ... \"lN\"])`  →  `set tics (\"l1\", a, ..., \"lN\", c)`\n    * `tics = ([t1 t2 ... tN], [\"l1\" \"l2\" ... \"lN\"])`  →  `set tics (\"l1\", t1, ..., \"lN\", tN)`\n\nIn the last two cases, the first element in the tuple represents the numerical tics, and the second element is the set of labels.\n\nExample:\n```@example intro\nplot(t, sin.(2π*5*t),\n     linecolor  = :coral,\n     Axes(title = \"'Tics Example'\",\n          xtics = [(0.25:0.5:1, [\"1/4\" \"3/4\"]), \"rotate\"])\n    )\n```\n\n* Ranges are specified with `xrange`, `yrange`, `zrange` or `cbrange`:\n    * `{x,y,z,cb}range = (low, high)` → `set {x,y,z,cb}range [low|high]`\n    * `{x,y,z,cb}range = (-Inf, high)` → `set {x,y,z,cb}range [*|high]`\n    * `{x,y,z,cb}range = (low, Inf)` → `set {x,y,z,cb}range [low|*]`\n\nExample:\n```@example intro\nplot(t, sin.(2π*5*t),\n     linecolor  = :coral,\n     Axes(title = :Range_Example,\n          yrange = (-Inf, 2))\n    )\n```\n\n* A set of linetypes with the colors specified by a palette from [ColorSchemes.jl](https://github.com/JuliaGraphics/ColorSchemes.jl). The palette name must be specified as a symbol. For example,\n\n```@example intro\nt = range(-2, 2, length = 100)\nf(t, σ) = exp.(-σ*abs.(t))\nA = Axes(title = :Linetypes_Example, linetype = :sunset)\nplot(t, f(t,0.5), lw = 3, A)\nplot!(t, f(t, 1), lw = 3)\nplot!(t, f(t, 1.5), lw = 3)\nplot!(t, f(t, 2), lw = 3)\nplot!(t, f(t, 2.5), lw = 3)\n```\n\n* A string containing gnuplot commands may be passed as argument `axesconf`. This string is sent to gnuplot without modification.\n\n* In addition, a string of gnuplot commands may be specified using Gaston's configuration setting `preamble`. This string will be used in all subsequent plots, before the commands specified in `Axes()`. This may be useful to configure gnuplot in environments where it is not feasible to have a permanent gnuplot configuration file. For example,\n\n```julia\nset(preamble = \"set offsets graph .05, graph .05, graph .05, graph .05\")\n```\n\n## Configuring a curve's appearance\n\nAll key-value arguments provided to `plot` and not wrapped in `Axes()` are interpreted as a curve configuration. Some of them have offer some convenient syntax:\n\n* The plot style can be specified with the `with` key. The keys `with`, `w` and `plotstyle` are synonyms.\n\n* The point type is specified with the key `pointtype`. This key is synonym with `pt` and `marker`. Gnuplot accepts markers specified as numbers. In addition, Gaston accepts the following descriptive strings:\n| Value | Meaning |\n|-------|---------|\n| `\"dot\"` | Single pixel |\n| `\"+\"` | A plus sign|\n| `\"x\"` | A cross |\n| `\"*\"` | An asterisk|\n| `\"ecircle\"` | Empty circle |\n| `\"fcircle\"` | Full circle |\n| `\"esquare\"` | Empty square |\n| `\"fsquare\"` | Full square |\n| `\"etrianup\"` | Empty up triangle |\n| `\"ftrianup\"` | Full up triangle |\n| `\"etriandn\"` | Empty down triangle |\n| `\"ftriandn\"` | Full down triangle |\n| `\"edmd\"` | Empty diamond |\n| `\"fdmd\"` | Full diamond |\nOther strings are passed to gnuplot wrapped in single quotes; for example, `pt = \"λ\"` is translated as `pointtype 'λ'`.\n\n* A legend can be specified with the keys `legend`, `leg`, `title` or `t`.\n\n* A full plot specification can be provided with the key `curveconf`. This overrides all other provided arguments. For example, this plot\n\n```julia\nt = 0:0.05:10pi\nplot(t, cos, w=:lp, leg = :A_sine_wave, marker = \"fdmd\", pi = -20)\n```\n\ncan equivalently be specified as:\n```julia\ncc = \"w lp t 'A sine wave' pt 13 pi -20\"\nplot(t, cos, curveconf = cc)\n```\n\n## Data arguments\n\nMost plotting commands accept data in a few different formats:\n\n* `plot(y, args...)` assumes that `x = 1:length(y)`\n* `plot(x, f::Function, args...)` applies function `f` to `x`.\n* `plot(c, args...)` where `c` is complex, plots `real(c)` vs `imag(c)`.\n\nIn addition, gnuplot may use additional data to, for example, set a marker's size or color. These are called \"supplementary data\" by Gaston, and are provided to `plot` using the `supp` keyword argument. For example,\n\n```@example intro\nc = rand(30) .+ im*rand(30)\nplot(c, supp = 3abs.(c), w = :p, marker = \"ecircle\", markersize = \"variable\")\n```\n\n## 3-D plotting\n\nGaston and gnuplot are fully capable of plotting surfaces and other kinds of 3-D plots such as contours and heatmaps. See the [3-D plotting tutorial](@ref threedeetut).\n\n## Multiplot\n\nMultiple plots can be included in the same figure. This is accomplished by calling `plot` with a matrix made up of other figures. If a matrix elements is `nothing`, then the corresponding subplot is left empty. An example:\n\n```@example intro\nt = 0.01:0.01:10pi\np1 = plot(t, cos, Axes(title = :Plot_1), handle = 1)\np2 = plot(t, t.^2, Axes(title = :Plot_2), handle = 2)\np4 = plot(t, exp.(-t), Axes(title = :Plot_4), handle = 4)\nplot([p1 p2 ; nothing p4])\n```\n\nThe `handle` argument is necessary because a `plot` command, by default, overwrites the previous plot. See the section on [Managing multiple figures](@ref) for more details on how handles work.\n\n## Saving plots\n\nTo save a plot (or \"print\" it, in gnuplot's parlance), use the `save` command, which requires `term` and `output` arguments. Optionally, arguments specifying the `font`, `size`, `linewidth`, and `background` color may be given. These may be specified in a gnuplot command string with `saveopts`, which may also be specified in advance using `set(saveopts = \"...\")`. The following two examples are equivalent:\n\n```julia\nsave(term = \"png\",\n     output= \"myfigure.png\",\n     font = \"Consolas,10\",\n     size = \"1280,900\",\n     linewidth = 1,\n     background = \"blue\")\n```\n\n```julia\nsave(term = \"png\", output = \"myfigure.png\",\n     saveopts = \"font 'Consolas,10' size 1280,900 lw 1 background 'blue'\")\n```\n"
  },
  {
    "path": "docs/v1/plotguide.md",
    "content": "# Manual\n\nThis manual covers all aspects of using Gaston.\n\n## Gaston Settings\n\n### The terminal\n\nBy default, gnuplot chooses an appropriate terminal: `qt` or `wxt` on Linux, `windows` on Windows,\nand `aqua` on MacOS.\nThe terminal can be set by changing the value of `Gaston.config.term`; for example:\n```julia\nGaston.config.term = \"pngcairo font ',10' size 700,400\"\n```\nThe terminals supported by gnuplot can be listed by running:\n```julia\nGaston.terminals()\n```\n\n### Other settings\n\n* `Gaston.config.output`: controls how plots are displayed. Possible values are:\n    * `:external`: plots are displayed in GUI windows. This is the default value.\n    * `:echo`: sends text-based plots (like `png` and `sixelgd`) back to the terminal. Useful for notebooks and IDEs, and for plotting on the terminal.\n    * `:null`: execute all plot commands but do not actually produce a plot.\n\n    If Gaston detects it is running in a notebook environment, it automatically sets the terminal\n    to `pngcairo` and `config.output` to `:echo`.\n* `Gaston.config.embedhtml`: `Bool`, defaults to `false`. Enables embedding plots in HTML; useful to enable interactivity in Pluto and Jupyter notebooks. See examples in the included Pluto notebooks.\n\n## Plotting\n\nA `plot` command takes three different kinds of arguments: settings, data, and plotline, in that\norder.\n```julia\nplot([settings...], data..., [plotline...])\n```\nFurther curves may be added using `plot!`. (For 3-D plots, use `splot` instead.)\n\nMore specifically, a `plot` command takes:\n* Zero or more **settings** arguments, which get converted to gnuplot `set` commands.\n* One or more **data** arguments, which are written to a file in the format gnuplot expects.\n* Zero or more **plotline** arguments, which are appended to gnuplot's `plot` or `splot` commands.\n\nGaston provides several alternative ways to specify these.\n\n### Settings and Plotlines\n\nAll the following are equivalent.\n\n* One single string\n```julia\nplot(\"set grid\n      unset key\n      set title 'A Sinusoid'\",\n     x, y,\n     \"with linespoints lc 'green'\")\n```\n* One string per setting\n```julia\nplot(\"set grid\", \"unset key\", \"set title 'A Sinusoid'\",\n     x, y,\n     \"with linespoints\", \"lc 'green'\")\n```\n* Keywords with `@plot`\n```julia\n@plot({grid = true, key = false, title = \"'A Sinusoid'\"},\n      x, y,\n      {with = \"linespoints\", lc = \"'green'\"})\n```\n\nKeyword options are enclosed in curly brackets `{}`. To set an option without arguments,\nsuch as `set grid`, use either a lone `grid`, or `grid = true`. To unset an option, such as in\n`unset grid`, use ` grid = false`.  Options can be repeated; each one will be converted to a\nseparate `set` line.\n\n`@plot` also accepts strings, and in fact strings and keywords may be combined:\n```julia\n@plot({grid, key = false}, \"set title 'A Sinusoid'\",\n      x, y,\n      \"with linespoints\", {lc = \"'green'\"})\n```\nIt is possible to omit the parenthesis, but in this case the command must fit in a single line.\n```julia\n@plot {grid, key = false, title = \"'A Sinusoid'\"} x y {with = \"lp\", lc = \"'green'\"}\n```\nFor 3-D plots, use the macro `@splot`.\n\n#### Quoted strings\n\nAll strings passed to gnuplot must be enclosed in single quotes, such as in `lc = \"'green'\"` in the\nexample above. The `Q` string macro can help reduce the number of quotes needed:\n```julia\n@plot {grid = true, key = false, title = sqs\"A Sinusoid\"} x y {with = \"lp\", lc = Q\"green\"}\n```\nThis macro turns `\"abc\"` into `\"'abc'\"`.\n\n### Data\n\nData to be plotted can be provided as vectors and/or matrices. Gaston converts the data to a\nformat compatible with gnuplot. Three cases are supported:\n* All data arguments are vectors.\n* The first two arguments are vectors of length `n` and `m`, and the third argument is a matrix\nof size `n x m`; further arguments are optional.\n* All provided arguments are matrices of size `n x m`.\n\n#### Functions\n\nFunctions can be plotted directly, with a given range and number of samples, which\ncan be specified in the following alternative ways:\n```julia\n# g is a function\nplot(g)            # plots `g` evaluated at 100 samples, from -10 to 9.99\nplot((a, b), g)    # plots `g` evaluated at 100 samples, from a to b\nplot((a, b, c), g) # plots `g` evaluated at c samples, from a to b\nplot(x, g)         # plots g.(x)\n```\n\n#### Plot with table\n\nIn some cases, it is useful to have gnuplot produce plot data in a \"table\" format, which can then\nbe plotted. See an example in [Contour lines on heatmap](@ref). The function `Gaston.plotwithtable`\nreturns a `Gaston.DataTable` storing the table. All plot commands accept this type.\n\n### Simple themes\n\nFrequently-used settings or plotlines may be stored in a theme; the `@gpkw` macro processes\nkeyword arguments wrapped in curly brackets.\n```julia\ntheme = @gpkw {grid, key = false}\nplot(theme, x, y)\n```\nThemes may be combined with other themes and/or with strings:\n```julia\ntheme2 = @gpkw {xlabel = Q\"X\"}\nplot(theme, \"set title 'A Sinusoid'\", theme2, x, y)\n```\nThemes can also be used for plotlines, and these may also be combined with other themes and/or\nstrings.\n```julia\npltheme = @gpkw {w = \"lp\", pt = \"'o'\", ps = 3}\nplot(theme, \"set title 'A Sinusoid'\", theme2, x, y, pltheme)\n```\nGaston includes a few generic themes:\n\n|Axis themes | Description |\n|-----------:|:------------|\n| :notics | Removes all tics |\n| :labels | Generic axis labels (`x`, `y`, `z`) |\n| :nocb   | Removes colorbox |\n| :unitranges | Set all ranges to `[-1:1]` |\n\nFor example, the following command plots a sine wave with no tics and generic `x` and `y` axis\nlabels:\n```julia\nplot(:notics, :labels, \"set title 'Example'\", (-1, 1), sin)\n```\nThemes are also used to provide common plot types (illustrated in the [Themes](@ref) section). These\nspecialized plot commands and the themes they use are:\n\n| Commands | Settings theme | Plotline theme |\n|----------|----------------|----------------|\n| `scatter`, `scatter!` | `:scatter`, `:scatter3` | `:scatter` |\n| `stem`, `stem!` | None | `:stem`, `:impulses` |\n| `bar`, `bar!` | `:boxplot` | `:box` |\n| `barerror`, `barerror!` | `:boxerror` | `:box` |\n| `histogram` | `:histplot` | `:box`, `:horhist` (1-D); `:image`  (2-D) |\n| `imagesc` | `:imagesc` | `:image`, `:rgbimage` |\n| `surf`, `surf!` |\n| `contour` |\n| `surfcontour` |\n| `wireframe`, `wireframe!` |\n| `wiresurf`, `wiresurf!` |\n| `heatmap` | \n\n!!! note \"Plotline themes\"\n    Plotline themes must be handled with care: gnuplot requires plotline options\n    to be specified in a certain order, may not be repeated, and some combinations are invalid.\n    It is very easy to create erroneous plotlines.\n\n!!! note \"Gaston lacks a parser\"\n    Gaston does not validate that the settings and plotline given to gnuplot are valid. When\n    gnuplot returns an error or warning, it is echoed to the terminal.\n\n## Multiplot\n\n## Managing multiple figures\n\nGaston has the ability to create and manage multiple GUI plot windows simultaneously. Each window\nis backed up by its own gnuplot process. The following commands can be used to create and control\nmultiple windows.\n\n#### Creating and selecting figures\n\n```\nFigure()\n```\nCreates a new, empty figure. All figures are of type `Gaston.Figure`. Gaston keeps an internal\npointer to the figure, so that its not removed by the garbage collector.\n\nWhen creating a figure intended for multiplot, a setting string can be included, with the\n`multiplot` keyword.\n```\nFigure(multiplot = \"title 'A Multiplot'\")\n```\n\nWhen a figure is created, it becomes active, meaning that subsequent plot\ncommands will go to this figure. Of course, it is possible to keep figures in different variables:\n```\nfig1 = Figure()\nfig2 = Figure()\n```\nand then redirect plot commands to the desired figure:\n```\nplot(fig1, ...)  # plot goes to fig1\nplot!(fig2, ...) # new plot added to fig2\n```\nIt is also possible to select figures using _handles_:\n```\nFigure(\"density\") # figure with handle \"density\"\nFigure(:volume)   # figure with handle :volume\nFigure(33)        # figure with handle 33\n```\nHandles can be of any type. If not specified, handles are integers assigned in increasing order\nstarting from 1.\n\nTo plot in a specific figure, specify its handle using the keyword\nargument `handle`:\n```\nplot(..., handle = :volume)\nplot!(..., handle = 33)\nscatter(..., handle = \"density\")\n```\n\nTo activate a figure given its handle, use:\n```\nfigure(handle)\n```\nor, given its index number `i`, use:\n```\nfigure(index = i)\n```\nWith no arguments, `figure()` returns the current figure.\n\nTo obtain the list of all current figures and their handles, and to identify the active figure,\nuse the unexported function `Gaston.listfigures()`.\n\n#### Closing figures\n\nTo close the active figure, run\n```\nclosefigure()\n```\nThe figure with handle `h` can be closed with `closefigure(h)`. Likewise, to close figure `f` use `closefigure(f)`. Closing a figure quits the underlying gnuplot process. \n\nTo close all figures, use `closeall()`.\n\n## Saving plots\n\nA plot can be saved to a file in any format supported by gnuplot, with the function\n```\nsave(f ; output, term)\n```\nwhere the arguments are:\n* `f`, which can be either a `Figure`, or an arbitrary value that is taken to be the handle of the figure to save. Defaults to the active figure.\n* `output`, a string that specifies the filename. If empty, it defaults to `figure-` followed by the figure's handle; the filename extension is set to the first three characters of the gnuplot terminal (see next argument).\n* `term`, specifies the gnuplot terminal used to save the plot; defaults to `\"pngcairo font ',7'\"`.\n\n## Interacting with gnuplot\n\n\n"
  },
  {
    "path": "docs/v2/.gitignore",
    "content": "/.quarto/\n"
  },
  {
    "path": "docs/v2/Project.toml",
    "content": "[deps]\nColorSchemes = \"35d6a980-a343-548e-a6ea-1d62b119f2f4\"\nGaston = \"4b11ee91-296f-5714-9832-002c20994614\"\nGastonRecipes = \"39356fd2-1f9e-4efe-8abf-5745c7d9f608\"\nImages = \"916415d5-f1e6-5110-898d-aaa5f9f070e0\"\nQuadGK = \"1fd47b50-473d-5c70-9696-f719f8f3bcdc\"\nQuartoNotebookRunner = \"4c0109c6-14e9-4c88-93f0-2b974d3468f4\"\nQuartoTools = \"5fded309-f5a0-485a-9129-b3749510da85\"\nTestImages = \"5e47fb64-e119-507b-a336-dd2b206d9990\"\n"
  },
  {
    "path": "docs/v2/_extensions/jjallaire/code-visibility/_extension.yml",
    "content": "title: Code Visibility\nauthor: fast.ai\nversion: 1.0.0\ncontributes:\n  filters:\n    - code-visibility.lua\n"
  },
  {
    "path": "docs/v2/_extensions/jjallaire/code-visibility/code-visibility.lua",
    "content": "\n\n\n-- remove any lines with the hide_line directive.\nfunction CodeBlock(el)\n  if el.classes:includes('cell-code') then\n    el.text = filter_lines(el.text, function(line)\n      return not line:match(\"#| ?hide_line%s*$\")\n    end)\n    return el\n  end\nend\n\n-- apply filter_stream directive to cells\nfunction Div(el)\n  if el.classes:includes(\"cell\") then\n    local filters = el.attributes[\"filter_stream\"]\n    if filters then\n      -- process cell-code\n      return pandoc.walk_block(el, {\n        CodeBlock = function(el)\n          -- CodeBlock that isn't `cell-code` is output\n          if not el.classes:includes(\"cell-code\") then\n            for filter in filters:gmatch(\"[^%s,]+\") do\n              el.text = filter_lines(el.text, function(line)\n                return not line:find(filter, 1, true)\n              end)\n            end\n            return el\n          end\n        end\n      })\n      \n    end\n\n  end\n  \nend\n\nfunction filter_lines(text, filter)\n  local lines = pandoc.List()\n  local code = text .. \"\\n\"\n  for line in code:gmatch(\"([^\\r\\n]*)[\\r\\n]\") do\n    if filter(line) then\n      lines:insert(line)\n    end\n  end\n  return table.concat(lines, \"\\n\")\nend\n\n\n"
  },
  {
    "path": "docs/v2/_quarto.yml",
    "content": "project:\n  type: website\n\nwebsite:\n  title: \"Gaston.jl\"\n  navbar:\n    left:\n      - href: index.qmd\n        text: Introduction\n      - href: tutorial.qmd\n        text: Tutorial\n      - href: examples.qmd\n        text: Examples\n      - href: recipes.qmd\n        text: Recipes\n      - href: manual.qmd\n        text: Manual\n      - href: migrate.qmd\n        text: Migration Guide\n      - href: reference.qmd\n        text: API Reference\n    tools:\n      - icon: github\n        href: https://github.com/mbaz/Gaston.jl\n    search: true\n    pinned: true\n    reader-mode: true\n    back-to-top-navigation: true\n\nformat:\n  html:\n    theme:\n      - minty\n    css: styles.css\n    toc: true\n    embed-resources: true\n\nengines: ['julia']\n\nexecute:\n  daemon: 600\n\nfilters:\n  - code-visibility\n\n\n"
  },
  {
    "path": "docs/v2/assets/cairolatex.tex",
    "content": "\\documentclass{article}\n\\usepackage{amsmath}\n\\usepackage{graphicx}\n\\usepackage{color}\n\n\\begin{document}\n\\begin{figure}\n  \\input{test.tex}\n\\end{figure}\n\\end{document}\n"
  },
  {
    "path": "docs/v2/assets/lorenz.jl",
    "content": "using Gaston\nusing ColorSchemes\n\nBase.@kwdef mutable struct Lorenz\n    dt::Float64 = 0.01\n    σ::Float64 = 10\n    ρ::Float64 = 28\n    β::Float64 = 8/3\n    x::Float64 = 1\n    y::Float64 = 1\n    z::Float64 = 1\nend\n\nfunction step!(l::Lorenz)\n    dx = l.σ * (l.y - l.x)\n    dy = l.x * (l.ρ - l.z) - l.y\n    dz = l.x * l.y - l.β * l.z\n    l.x += l.dt * dx\n    l.y += l.dt * dy\n    l.z += l.dt * dz\n    return (l.x, l.y, l.z)\nend\n\nNframes = 120\nNpoints = 50\nattractor = Lorenz()\nx = Float64[];\ny = Float64[];\nz = Float64[];\n\ns = @gpkw {xrange = (-30, 30),\n           yrange = (-30, 30),\n           zrange = (0, 60),\n           xtics = \"offset -1.2,0\",\n           xtics = \"add ('' -30, '' 30)\",\n           ytics = \"offset 1.2,0\",\n           ytics = \"add ('' -30, '' 30)\",\n           origin = \"-0.1, -0.1\",\n           size = \"1.2, 1.2\",\n           object = \"rectangle from screen 0,0 to screen 1,1 fillcolor 'black' behind\",\n           border = \"back lc rgb '#eeeeee' lt 1 lw 1.5\",\n           view = \"equal xyz\",\n           xyplane = \"at 0\"}\n\nf = splot(s, 1, 1, 1)\n\nfor i = 1:Nframes\n    for j = 1:Npoints\n        step!(attractor)\n        push!(x, attractor.x);\n        push!(y, attractor.y);\n        push!(z, attractor.z)\n    end\n    cs = resample(ColorSchemes.inferno, length(x))\n    splot(f[i],\n          s, \"set view 70, $(45 + 17 * sin(2pi * i / Nframes))\",\n          x, y, z, Gaston.cs2dec(cs),\n          \"w l notitle lc rgb variable\")\nend\nsave(f, filename = \"lorenz.webp\", term = \"webp animate loop 0 size 640,480\")\n"
  },
  {
    "path": "docs/v2/assets/test.tex",
    "content": "% GNUPLOT: LaTeX picture with Postscript\n\\begingroup\n  \\makeatletter\n  \\providecommand\\color[2][]{%\n    \\GenericError{(gnuplot) \\space\\space\\space\\@spaces}{%\n      Package color not loaded in conjunction with\n      terminal option `colourtext'%\n    }{See the gnuplot documentation for explanation.%\n    }{Either use 'blacktext' in gnuplot or load the package\n      color.sty in LaTeX.}%\n    \\renewcommand\\color[2][]{}%\n  }%\n  \\providecommand\\includegraphics[2][]{%\n    \\GenericError{(gnuplot) \\space\\space\\space\\@spaces}{%\n      Package graphicx or graphics not loaded%\n    }{See the gnuplot documentation for explanation.%\n    }{The gnuplot epslatex terminal needs graphicx.sty or graphics.sty.}%\n    \\renewcommand\\includegraphics[2][]{}%\n  }%\n  \\providecommand\\rotatebox[2]{#2}%\n  \\@ifundefined{ifGPcolor}{%\n    \\newif\\ifGPcolor\n    \\GPcolortrue\n  }{}%\n  \\@ifundefined{ifGPblacktext}{%\n    \\newif\\ifGPblacktext\n    \\GPblacktexttrue\n  }{}%\n  % define a \\g@addto@macro without @ in the name:\n  \\let\\gplgaddtomacro\\g@addto@macro\n  % define empty templates for all commands taking text:\n  \\gdef\\gplbacktext{}%\n  \\gdef\\gplfronttext{}%\n  \\makeatother\n  \\ifGPblacktext\n    % no textcolor at all\n    \\def\\colorrgb#1{}%\n    \\def\\colorgray#1{}%\n  \\else\n    % gray or color?\n    \\ifGPcolor\n      \\def\\colorrgb#1{\\color[rgb]{#1}}%\n      \\def\\colorgray#1{\\color[gray]{#1}}%\n      \\expandafter\\def\\csname LTw\\endcsname{\\color{white}}%\n      \\expandafter\\def\\csname LTb\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LTa\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT0\\endcsname{\\color[rgb]{1,0,0}}%\n      \\expandafter\\def\\csname LT1\\endcsname{\\color[rgb]{0,1,0}}%\n      \\expandafter\\def\\csname LT2\\endcsname{\\color[rgb]{0,0,1}}%\n      \\expandafter\\def\\csname LT3\\endcsname{\\color[rgb]{1,0,1}}%\n      \\expandafter\\def\\csname LT4\\endcsname{\\color[rgb]{0,1,1}}%\n      \\expandafter\\def\\csname LT5\\endcsname{\\color[rgb]{1,1,0}}%\n      \\expandafter\\def\\csname LT6\\endcsname{\\color[rgb]{0,0,0}}%\n      \\expandafter\\def\\csname LT7\\endcsname{\\color[rgb]{1,0.3,0}}%\n      \\expandafter\\def\\csname LT8\\endcsname{\\color[rgb]{0.5,0.5,0.5}}%\n    \\else\n      % gray\n      \\def\\colorrgb#1{\\color{black}}%\n      \\def\\colorgray#1{\\color[gray]{#1}}%\n      \\expandafter\\def\\csname LTw\\endcsname{\\color{white}}%\n      \\expandafter\\def\\csname LTb\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LTa\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT0\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT1\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT2\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT3\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT4\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT5\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT6\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT7\\endcsname{\\color{black}}%\n      \\expandafter\\def\\csname LT8\\endcsname{\\color{black}}%\n    \\fi\n  \\fi\n    \\setlength{\\unitlength}{0.0500bp}%\n    \\ifx\\gptboxheight\\undefined%\n      \\newlength{\\gptboxheight}%\n      \\newlength{\\gptboxwidth}%\n      \\newsavebox{\\gptboxtext}%\n    \\fi%\n    \\setlength{\\fboxrule}{0.5pt}%\n    \\setlength{\\fboxsep}{1pt}%\n    \\definecolor{tbcol}{rgb}{1,1,1}%\n\\begin{picture}(7200.00,4740.00)%\n    \\gplgaddtomacro\\gplbacktext{%\n    }%\n    \\gplgaddtomacro\\gplfronttext{%\n      \\csname LTb\\endcsname%%\n      \\put(1512,3921){\\makebox(0,0)[r]{\\strut{}n=0}}%\n      \\csname LTb\\endcsname%%\n      \\put(1512,3716){\\makebox(0,0)[r]{\\strut{}n=1}}%\n      \\csname LTb\\endcsname%%\n      \\put(2937,3921){\\makebox(0,0)[r]{\\strut{}n=2}}%\n      \\csname LTb\\endcsname%%\n      \\put(2937,3716){\\makebox(0,0)[r]{\\strut{}n=3}}%\n      \\csname LTb\\endcsname%%\n      \\put(4363,3921){\\makebox(0,0)[r]{\\strut{}sin(x)}}%\n      \\csname LTb\\endcsname%%\n      \\put(616,409){\\makebox(0,0)[r]{\\strut{}$-1.5$}}%\n      \\csname LTb\\endcsname%%\n      \\put(616,1025){\\makebox(0,0)[r]{\\strut{}$-1$}}%\n      \\csname LTb\\endcsname%%\n      \\put(616,1641){\\makebox(0,0)[r]{\\strut{}$-0.5$}}%\n      \\csname LTb\\endcsname%%\n      \\put(616,2257){\\makebox(0,0)[r]{\\strut{}$0$}}%\n      \\csname LTb\\endcsname%%\n      \\put(616,2873){\\makebox(0,0)[r]{\\strut{}$0.5$}}%\n      \\csname LTb\\endcsname%%\n      \\put(616,3489){\\makebox(0,0)[r]{\\strut{}$1$}}%\n      \\csname LTb\\endcsname%%\n      \\put(616,4105){\\makebox(0,0)[r]{\\strut{}$1.5$}}%\n      \\csname LTb\\endcsname%%\n      \\put(1257,204){\\makebox(0,0){\\strut{}$-\\pi$}}%\n      \\csname LTb\\endcsname%%\n      \\put(2521,204){\\makebox(0,0){\\strut{}$-\\pi/2$}}%\n      \\csname LTb\\endcsname%%\n      \\put(3786,204){\\makebox(0,0){\\strut{}0}}%\n      \\csname LTb\\endcsname%%\n      \\put(5050,204){\\makebox(0,0){\\strut{}$\\pi/2$}}%\n      \\csname LTb\\endcsname%%\n      \\put(6314,204){\\makebox(0,0){\\strut{}$\\pi$}}%\n      \\csname LTb\\endcsname%%\n      \\put(4519,1148){\\makebox(0,0){\\strut{}\\begin{minipage}[c]{\\textwidth}\\begin{equation*}\\sin(x) = \\sum_0^{+\\infty} \\frac{(-1)^n}{(2n + 1)!} x^{2n+1}\\end{equation*} \\end{minipage}}}%\n      \\csname LTb\\endcsname%%\n      \\put(3785,4412){\\makebox(0,0){\\strut{}Polynomial approximation of sin(x)}}%\n    }%\n    \\gplbacktext\n    \\put(0,0){\\includegraphics[width={360.00bp},height={237.00bp}]{test}}%\n    \\gplfronttext\n  \\end{picture}%\n\\endgroup\n"
  },
  {
    "path": "docs/v2/examples.qmd",
    "content": "---\ntitle: \"Examples\"\n---\n\n```{julia}\n#| echo: false\n#| output: false\nusing Gaston\nGaston.config.term = \"pngcairo font ',10' size 640,480\"\nGaston.config.output = :echo\n```\n\n### 3-D Euler spiral [(Clothoid)](https://en.wikipedia.org/wiki/Euler_spiral)\n\n```{julia}\nusing QuadGK\nz = range(-5, 5, 200)\nfx(z) = sin(z^2)\nfy(z) = cos(z^2)\nx = [quadgk(fx, 0, t)[1] for t in z]\ny = [quadgk(fy, 0, t)[1] for t in z]\nsplot(\"\"\"unset zeroaxis\n         set tics border\n         set xyplane at -5 \n         set view 65,35\n         set border 4095\n         set xtics offset 0, -0.5\"\"\",\n         x, y, z, \"w l lc 'black' lw 1.5\")\n```\n\n### Waterfall\n\nInspired by this [Julia Discourse discussion](https://discourse.julialang.org/t/how-to-produce-a-waterfall-plot-in-julia/93441).\n```{julia}\nx = -15:0.1:15\ny = 0:30\nu1data = [exp(-(x-0.5*(y-15))^2) for x in x, y in y]\nZf = fill(0.0, length(x))\nf = Figure()\nGaston.set!(f(1), \"\"\"set zrange [0:1.5]\n               set tics out\n               set ytics border\n               set xyplane at 0\n               set view 45,17\n               set xlabel 'ξ'\n               set ylabel 't' offset -2.5\n               set zlabel '|u|' offset -0.85\n               set border 21\n               set size 1, 1.3\"\"\")\nfor i in reverse(eachindex(y))\n    Y = fill(y[i], length(x))\n    Z = u1data[:,i]\n    splot!(x, Y, Z, Zf, Z, \"w zerrorfill lc 'black' fillstyle solid 1.0 fc 'white'\")\nend\nf\n```\n\n### Vector field\n\nInspired by this [post in gnuplotting.org](https://gnuplotting.org/vector-field-from-function/index.html).\n\n```{julia}\nxr = 15  # samples in x direction\nyr = 15  # samples in y direction\n\n# parameters\nx01 = 1\ny01 = 0\nq1  = 1\nx02 = -1\ny02 = 0\nq2  = -1\nscaling = 0.22\n\n# equations\nr(x,y)     = sqrt(x*x+y*y)\nv1(x,y)    = q1/(r(x-x01,y-y01))\nv2(x,y)    = q2/(r(x-x02,y-y02))\nv(x,y)     = v1(x,y)+v2(x,y)\ne1x(x,y)   = q1*x/r(x,y)^3\ne1y(x,y)   = q1*y/r(x,y)^3\ne2x(x,y)   = q2*x/r(x,y)^3\ne2y(x,y)   = q2*y/r(x,y)^3\nex(x,y)    = e1x(x-x01,y-y01)+e2x(x-x02,y-y02)\ney(x,y)    = e1y(x-x01,y-y01)+e2y(x-x02,y-y02)\nenorm(x,y) = sqrt(ex(x,y)^2 + ey(x,y)^2)\ndx(x,y)    = scaling*ex(x,y)/enorm(x,y)\ndy(x,y)    = scaling*ey(x,y)/enorm(x,y)\n\n# initialize data vectors\nd1 = zeros(xr*yr)\nd2 = zeros(xr*yr)\nd3 = zeros(xr*yr)\nd4 = zeros(xr*yr)\nd5 = zeros(xr*yr)\n\n# calculations\nfor X in range(-2, 2, length=xr)\n    for Y in range(-1.8, 1.8, length=yr)\n         push!(d1, X-dx(X,Y)/2)\n         push!(d2, Y-dy(X,Y)/2)\n         push!(d3, dx(X,Y))\n         push!(d4, dy(X,Y))\n         push!(d5, v(X,Y))\n    end\nend\n\n@plot({palette = :linear_kry_5_95_c72_n256}, :nocb,\n      d1, d2, d3, d4, d5,\n      \"with vectors head size 0.08,20,60 filled lc palette\")\n```\n\n### Line color from palette\n\n```{julia}\nx = -2π:0.05:2π\n@plot {palette = :ice} x sin.(3x) x \"w l notitle lw 3 lc palette\"\n```\n\n### Variable marker size and color\n\n```{julia}\nx = 0:0.1:6π\nsplot(\"unset colorbox\",\n      x, cos.(x), sin.(x), x./10,\n      \"w p\", \"ps variable\", \"pt 7\", \"lc palette\")\n```\n\n### Filled curves\n\n#### Filled transparent curves in 2-D\n\n```{julia}\npois(λ, k) = (λ^k)*exp(-λ)/factorial(k)\ns = \"set style fill transparent solid 0.4 noborder \\nset title 'Poisson PMF'\"\nplot(s, 0:15, k -> pois(4, k), \"w filledcu x1 lc 'cyan' t 'λ = 4'\")\nplot!(0:15, k -> pois(6, k), \"w filledcu x1 lc 'blue' t 'λ = 6'\")\nplot!(0:15, k -> pois(8, k), \"w filledcu x1 lc 'pink' t 'λ = 8'\")\n```\n\n#### Fill between two curves\n\n```{julia}\nx = range(-10, 10, 100)\ny1 = sin.(x) .- 0.5\ny2 = sin.(x) .+ 0.5\nplot(x, y1, y2, \"w filledcu lc 'turquoise'\")\n```\n\n#### Filled curve in 3-D\n\n```{julia}\nx = 0.:0.05:3;\ny = 0.:0.05:3;\nz = @. sin(x) * exp(-(x+y))\n@gpkw splot(:labels, {style = \"fill transparent solid 0.3\", xyplane = \"at 0\", grid, lt = :Set1_5},\n            x, y, z, z.*0, z,\n            \"w zerror t 'Data'\")\nsplot!(x.*0, y, z, \"w l lw 3\")\nsplot!(x, y.*0, z, \"w l lw 3\")\n```\n\nHere, `Set1_5` is a color scheme from [ColorSchemes.jl](https://github.com/JuliaGraphics/ColorSchemes.jl).\n\n### Spheres\n\n#### Wireframe\n\n```{julia}\nΘ = range(0, 2π, length = 100)\nΦ = range(0, π, length = 20)\nrd = 0.8\nx = [rd*cos(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]\ny = [rd*sin(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]\nz = [rd*cos(ϕ)        for θ in Θ, ϕ in Φ]\n@gpkw splot({view = \"equal xyz\", pm3d = \"depthorder\", hidden3d},\n            x, y, z,\n            {w = \"l\", lc = Q\"turquoise\"})\n```\n\n#### Surface\n\n```{julia}\nΘ = range(0, 2π, length = 100)\nΦ = range(0, π, length = 100)\nrd = 0.8\nx = [rd*cos(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]\ny = [rd*sin(θ)*sin(ϕ) for θ in Θ, ϕ in Φ]\nz = [rd*cos(ϕ)        for θ in Θ, ϕ in Φ]\n@splot({style = \"fill transparent solid 1\",\n        palette = :summer,\n        view = \"equal xyz\",\n        pm3d = \"depthorder\"},\n       x, y, z,\n       \"w pm3d\")\n```\n\n### Torus\n\n```{julia}\nU = range(-π, π, length = 50)\nV = range(-π, π, length = 100)\nrd = 0.5\nx = [1+cos(u)+rd*cos(u)*cos(v) for u in U, v in V]\ny = [rd*sin(v)                 for u in U, v in V]\nz = [sin(u)+rd*sin(u)*cos(v)   for u in U, v in V]\nsettings = \"\"\"set object rectangle from screen 0,0 to screen 1,1 behind fillcolor 'black' fillstyle solid noborder\n              set pm3d depthorder\n              set style fill transparent solid 0.5\n              set pm3d lighting primary 0.05 specular 0.2\n              set view 108,2\n              unset border\n              set xyplane 0\n              unset tics\n              unset colorbox\"\"\"\n@splot(settings, {palette = :cool}, x, y, z, \"w pm3d\")\n```\n\n#### Interlocking torii\n\n```{julia}\nU = LinRange(-π, pi, 100)\nV = LinRange(-π, pi, 20)\nx = [cos(u) + .5 * cos(u) * cos(v) for u in U, v in V]\ny = [sin(u) + .5 * sin(u) * cos(v) for u in U, v in V]\nz = [.5 * sin(v)                   for u in U, v in V]\n@gpkw surf({palette = :dense,\n        pm3d = \"depthorder\",\n        colorbox = false,\n        key = :false,\n        tics = :false,\n        border = 0,\n        view = \"60, 30, 1.5, 0.9\",\n        style = \"fill transparent solid 0.7\"},\n       x', y', z')\nx = [1 + cos(u) + .5 * cos(u) * cos(v) for u in U, v in V]\ny = [.5 * sin(v)                       for u in U, v in V]\nz = [sin(u) + .5 * sin(u) * cos(v)     for u in U, v in V]\nsurf!(x', y', z')\n```\n\nSee more torus examples in the included Pluto notebook.\n\n### Contours\n\n#### Surface with contours\n\n```{julia}\nx = y = -10:0.5:10\nf1(x, y) = cos.(x./2).*sin.(y./2)\nsurf(\"\"\"set hidden3d\n        set contour base\n        set cntrparam levels 10\n        unset key\"\"\",\n     x, y, f1,\n     \"lc 'turquoise'\")\n```\n\n#### Egg-shaped contours\n\n```{julia}\nx = -1:0.05:1\ny = -1.5:0.05:2\negg(x,y) = x^2 + y^2/(1.4 + y/5)^2\nsegg = [egg(x,y) for x in x, y in y]\n@gpkw contour({palette = :cool,\n               cntrparam = \"levels incremental 0,0.01,1\",\n               auto = \"fix\",\n               xrange = (-1.2, 1.2),\n               yrange = (-1.5, 2),\n               cbrange = (0, 1),\n               xlabel = \"'x'\",\n               ylabel = \"'y'\",\n               size = \"ratio -1\"},\n              x, y, segg',\n              \"w l lc palette\",\n              labels = false)\n```\n\n### 3D Tubes\n\n#### Wireframe\n\n```{julia}\nU = range(0, 10π, length = 80)\nV = range(0, 2π, length = 10)\nx = [(1-0.1*cos(v))*cos(u)     for u in U, v in V]\ny = [(1-0.1*cos(v))*sin(u)     for u in U, v in V]\nz = [0.2*(sin(v) + u/1.7 - 10) for u in U, v in V]\nsettings = @gpkw {pm3d = \"depthorder\",\n                  style = \"fill transparent solid 1\",\n                  view = \"equal xyz\",\n                  xyplane = -0.05,\n                  palette = :ice,\n                  xrange = (-1.2, 1.2),\n                  yrange = (-1.2, 1.2),\n                  colorbox = false,\n                  hidden3d,\n                  view = (70, 79)}\n@splot(settings, x, y, z, \"w l lc 'turquoise'\")\n```\n\n#### Surface\n\n```{julia}\n@splot(settings, x, y, z, \"w pm3d\")\n```\n\n### Animations\n\n#### Lorenz attractor\n\nThis example is adapted from [https://docs.makie.org/stable/#example](Makie's documentation).\nA few notes on the adaptation to Gaston:\n\n* The camera animation is achieved by changing the `view` setting every frame.\n* Each frame, `Npoints` coordinates are added to the plot. The animation consists of `Nframes`\n  frames.\n* The coloring of the attractor is different than in most 3-D plots. Normally, the color of\n  a point depends on its `z` coordinate. In this case, the `z` coordinate is not an amplitude,\n  since the curve lives in a state space. Therefore, the color palette will be applied along\n  the length of the curve, with points nearer the start given colors at the start of the palette.\n  To achieve this, we use `ColorSchemes.resample` to create a new palette with the same number\n  of colors as there are points in the curve. Then, `Gaston.hex` is used to convert these\n  colors to decimal numbers. Finally, the colors are used as a fourth column of data and the\n  line color is set to `lc rgb variable`.\n\n```{.julia}\nusing ColorSchemes\n\nBase.@kwdef mutable struct Lorenz\n    dt::Float64 = 0.01\n    σ::Float64 = 10\n    ρ::Float64 = 28\n    β::Float64 = 8/3\n    x::Float64 = 1\n    y::Float64 = 1\n    z::Float64 = 1\nend\n\nfunction step!(l::Lorenz)\n    dx = l.σ * (l.y - l.x)\n    dy = l.x * (l.ρ - l.z) - l.y\n    dz = l.x * l.y - l.β * l.z\n    l.x += l.dt * dx\n    l.y += l.dt * dy\n    l.z += l.dt * dz\n    return (l.x, l.y, l.z)\nend\n\nNframes = 120\nNpoints = 50\nattractor = Lorenz()\nx = Float64[];\ny = Float64[];\nz = Float64[];\n\ns = @gpkw {xrange = (-30, 30),\n           yrange = (-30, 30),\n           zrange = (0, 60),\n           xtics = \"offset -1.2,0\",\n           xtics = \"add ('' -30, '' 30)\",\n           ytics = \"offset 1.2,0\",\n           ytics = \"add ('' -30, '' 30)\",\n           origin = \"-0.1, -0.1\",\n           size = \"1.2, 1.2\",\n           object = \"rectangle from screen 0,0 to screen 1,1 fillcolor 'black' behind\",\n           border = \"back lc rgb '#eeeeee' lt 1 lw 1.5\",\n           view = \"equal xyz\",\n           xyplane = \"at 0\"}\n\nf = splot(s, 1, 1, 1)\n\nfor i = 1:Nframes\n    for j = 1:Npoints\n        step!(attractor)\n        push!(x, attractor.x);\n        push!(y, attractor.y);\n        push!(z, attractor.z)\n    end\n    cs = resample(ColorSchemes.inferno, length(x))\n    splot(f[i],\n          s, \"set view 70, $(45 + 17 * sin(2pi * i / Nframes))\",\n          x, y, z, Gaston.hex(cs),\n          \"w l notitle lc rgb variable\")\nend\nsave(f, filename = \"lorenz.webp\", term = \"webp animate loop 0 size 640,480\")\n```\n\n![](assets/lorenz.webp)\n\n\n#### 3-D spiral\n\n```{.julia}\nz = 0:0.1:10pi\nstep = 5\ncc = \"lc 'turquoise' lw 3 notitle\"\nac = @gpkw {zrange = (0,30), xrange = (-1.2, 1.2), yrange = (-1.2, 1.2)}\nF = scatter3(ac, :notics, :labels, cos.(z[1:step]), sin.(z[1:step]), z[1:step], cc)\nframe = Figure()\nfor i = 2:60\n    frame = scatter3(ac, :notics, :labels, cos.(z[1:i*step]), sin.(z[1:i*step]), z[1:i*step], cc)\n    push!(F, frame)\nend\nfor i = 60:-1:1\n    frame = scatter3(ac, :notics, :labels, cos.(z[1:i*step]), sin.(z[1:i*step]), z[1:i*step], cc)\n    push!(F, frame)\nend\nsave(F, filename = \"3dspiral.webp\", term = \"webp animate loop 0 size 640,480\")\n```\n![](assets/3dspiral.webp)\n\n#### Splash\n\n```{.julia}\nx = y = -15:0.4:15\nac = @gpkw {title = Q\"Splash\",\n            palette  = :cool,\n            cbrange  = (-0.2, 1),\n            zrange   = (-0.3, 1),\n            hidden3d = true}\nF = splot(ac, x, y, (x, y) -> sin(sqrt(x*x+y*y))/sqrt(x*x+y*y), \"w pm3d\")\nframe = Figure()\nfor i = 1:-0.1:-1\n    frame = splot(ac, x, y, (x,y) -> i*sin(sqrt(x*x+y*y))/sqrt(x*x+y*y), \"w pm3d\");\n    push!(F, frame)\nend\nfor i = -0.9:0.1:1\n    frame = splot(ac, x, y, (x,y) -> i*sin(sqrt(x*x+y*y))/sqrt(x*x+y*y), \"w pm3d\");\n    push!(F, frame)\nend\nsave(F, filename = \"3dsplash.webp\", term = \"webp animate loop 0 size 640,480\")\n```\n![](assets/3dsplash.webp)\n\n"
  },
  {
    "path": "docs/v2/index.qmd",
    "content": "---\ntitle: \"Introduction\"\n---\n\n[Gnuplot](http://www.gnuplot.info/) is a venerable (but actively\ndeveloped), very powerful and fast program for plotting.\n[Julia](https://julialang.org) is a very powerful language for numerical\ncomputing.  [Gaston](https://github.com/mbaz/Gaston.jl) is a package for\nplotting Julia data and functions using gnuplot.\n\nThe following diagram illustrates how Gaston works. On the left there is a\ngnuplot script; the equivalent Gaston function call is on the right.\n\n![](assets/comparison.png)\n\nThe data to plot is in green color. In gnuplot, the data can be provided\ninline as a _data block_ (as in this example), or it can be provided in a\nseparate file. Gaston can plot data stored in arrays (`x` and `y` in this\nexample). Gaston also supports recipes to plot arbitrary Julia types.\n\nThe lines in red specify the _axes settings_, affecting things like the\npresence of a grid, the title, the legend box, tics positions/labels, etc.\nFinally, in blue color, the _plot settings_ or _plotline_ specify the\nproperties of one specific curve within the axes (for example, the line\ncolor, thickness and style, which marker to use, etc.)\n\nThere is a close correspondence between gnuplot and Gaston commands; in\nfact, the main purpose of Gaston is to translate the Julia code on the\nright to the gnuplot commands on the left. Gaston has two main features:\n\n* It provides convenient, flexible syntax for plotting, along with common\n  2-D and 3-D plot styles.\n* It provides a simple mechanism to add user-defined recipes for plotting\n  arbitrary Julia types.\n\nOther features are:\n\n* Support for plotting in separate GUI windows, in the terminal (with text\n  or [sixels](https://en.wikipedia.org/wiki/Sixel)), in VS Code, or in\n  notebooks (such as Jupyter and Pluto).\n* Handling multiple co-existing interactive GUI plot windows.\n* Support for (user-defined) themes.\n* Convenient commands for common types of plots (like histograms, contour\n  plots, surface plots, heatmaps, etcetera).\n* Convenient syntax for creating multiplots and animations.\n* Saving plots to multiple formats, including pdf, png and svg.\n* Integrated color palettes from\n  [ColorSchemes.jl](https://github.com/JuliaGraphics/ColorSchemes.jl).\n\n::: {.callout-note}\n\nThis manual is for Gaston version 2. The manual for Gaston v1 (no longer\nmaintained) is [here](https://mbaz.github.io/Gaston.jl/v1/index.html).\n\n:::\n\n## Learning gnuplot\n\nThis documentation assumes at least a basic understanding of gnuplot. Some\npointers to get started with gnuplot are:\n\n* The [official documentation](http://www.gnuplot.info/documentation.html).\n* The [official list of tutorials](http://www.gnuplot.info/help.html).\n* [Plotting data](http://www.gnuplotting.org/plotting-data/) article on\n  [gnuplotting.org](http://www.gnuplotting.org/).\n* [A blog post about Gaston by Julia\n  Frank](https://juliaifrank.com/gnuplot-with-julia-for-beautiful-graphics/).\n* Stackoverflow has a [gnuplot\n  tag](https://stackoverflow.com/questions/tagged/gnuplot) with answers to\n  more than 6,500 questions.\n\nThe following are interesting plot galleries:\n\n* [Official gnuplot demo\n  gallery](http://www.gnuplot.info/screenshots/index.html#demos).\n* [Wikimedia commons\n  gallery](https://commons.wikimedia.org/wiki/Category:Gnuplot_diagrams).\n* [Nice collection of volumetric\n  plots](https://ayapin-film.sakura.ne.jp/Gnuplot/pm3d.html).\n\n## Installation\n\nGaston v2.x requires Julia version 1.8.0 or above (Gaston v1.x supports\nJulia 1.6 and above), and has been tested with gnuplot versions 5 and 6.\nYou should manually install gnuplot on your system prior to using Gaston.\nOn Linux, it is highly recommended that you select a version with Qt\nsupport: on Debian and Ubuntu, you will need `gnuplot-qt`. On Arch and its\nderivatives, a simple `pacman -S gnuplot` suffices.\n\nGnuplot also supports Windows and Mac. This author does not use these systems\nmuch, but it is said that gnuplot Windows executables are available at\n[Sourceforge's gnuplot repository](https://sourceforge.net/projects/gnuplot/files/gnuplot/).\nOn Mac, gnuplot is supposed to be available using Homebrew.\n\nTo install Gaston from the Julia REPL, run\n```julia\njulia> ]add Gaston\n```\nTyping `]` switches the Julia REPL to the package manager, and the `add`\ncommand installs the package. To exit the package manager, hit the backspace\nkey.\nLoad Gaston into your Julia session with\n```julia\nusing Gaston\n```\n\n::: {.callout-note}\n## Specifying the location of gnuplot\n\n    The location of the gnuplot executable can be specified with the environmental variable\n    `JULIA_GNUPLOT_EXE`. If gnuplot is in the system's path, setting this variable is not\n    necessary.\n\n:::\n\n## Support\n\nHere are some ideas on what to do if you need help with Gaston:\n\n* Post a question in [Julia's discuss forum](https://discourse.julialang.org/tag/plotting)\n  in the \"plotting\" category.\n* Chat with the author (@mbaz) on [Julia's Zulip chat forum](https://julialang.zulipchat.com/),\n  in the \"plotting\" or \"helpdesk\" channels.\n* Bug reports, suggestions and pull requests are welcome at\n  [Gaston's github page](https://github.com/mbaz/Gaston.jl).\n\n## Contributing\n\nContributions are welcome! Examples of things you can do are bug reports,\nimprovements to the documentation, new examples and tutorials, and new features or\nsuggestions.\n\n## Gnuplot startup file\n\nGnuplot reads and executes a startup file, if it exists, before every plot.\nSince an un-configured gnuplot produces plots that are less than attractive,\nthe following minimum configuration is suggested (and was used to generate the\nplots in this document):\n\n    set linetype 1 lc rgb \"blue\" pt 3 ps 1.2\n    set linetype 2 lc rgb \"red\" pt 4 ps 1.2\n    set linetype 3 lc rgb \"dark-green\" pt 6 ps 1.2\n    set linetype 4 lc rgb \"orange-red\" pt 12 ps 1.2\n    set linetype 5 lc rgb \"gold\" pt 5 ps 1.2\n    set linetype 6 lc rgb \"dark-violet\" pt 1 ps 1.2\n    set linetype 7 lc rgb \"gray50\" pt 2 ps 1.2\n    set linetype 8 lc rgb \"black\" pt 7 ps 1.2\n    set linetype cycle 8\n    set style data lines\n    set key noautotitle\n    set auto fix\n    set offsets graph .05, graph .05, graph .05, graph .05\n\nThe configuration file is `~/.gnuplot` on Unix-like systems, and\n`%APPDATA%\\GNUPLOT.INI` on Windows.\n\n## Next steps\n\n* Read the [Tutorial](tutorial.qmd).\n* See plot examples in the [Examples](examples.qmd) section.\n* Learn how to extend Gaston to plot arbitrary Julia types in the [Recipes](recipes.qmd) section.\n* Learn all the details about how to plot with Gaston in the [Manual](manual.qmd).\n* For specific information about migrating from Gaston v1 to v2, see the\n  [Migration Guide](migrate.qmd).\n* Consult the full [API Reference](reference.qmd).\n\nGaston's documentation includes three [Pluto](https://plutojl.org/)\nnotebooks:\n\n* An overview of the [essential plotting concepts](https://github.com/mbaz/Gaston.jl/tree/master/docs/v2/tutorial-essentials.jl).\n* An overview of [3-D plotting](https://github.com/mbaz/Gaston.jl/tree/master/docs/v2/tutorial-3d.jl).\n* A tutorial on how to [plot a torus](https://github.com/mbaz/Gaston.jl/tree/master/docs/how-to-plot-a-torus.jl) (which aims to showcase Gaston in interactive notebooks).\n\n## Acknowledgments\n\nWhen developing Gaston, I've been inspired by the excellent features and ideas\nbehind other Julia plotting packages, including\n[Plots.jl](https://github.com/JuliaPlots/Plots.jl),\n[Gnuplot.jl](https://github.com/gcalderone/Gnuplot.jl),\n[PGFPlotsX.jl](https://github.com/KristofferC/PGFPlotsX.jl),\nand [Makie.jl](https://github.com/MakieOrg/Makie.jl).\nMany thanks to their multiple authors for freely sharing their code and their APIs!\n"
  },
  {
    "path": "docs/v2/manual.qmd",
    "content": "---\ntitle: \"Manual\"\n---\n\nThis manual covers all aspects of using Gaston.\n\n## Configuration\n\n### The terminal\n\nBy default, gnuplot chooses an appropriate terminal: `qt` or `wxt` on Linux,\n`windows` on Windows, and `aqua` on MacOS.  The terminal can be set by changing\nthe value of `Gaston.config.term`; for example:\n\n```julia\nGaston.config.term = \"pngcairo font ',10' size 700,400\";\n```\n\nTo show the terminals supported by gnuplot, run:\n\n```julia\nGaston.terminals()\n```\n\n### Other settings\n\n* `Gaston.config.output`: controls how plots are displayed. Possible values are:\n    * `:external`: plots are displayed in GUI windows. This is the default value.\n    * `:echo`: sends text-based plots (like `png` and `sixelgd`) back to the terminal. Useful for notebooks and IDEs, and for plotting on the terminal.\n    * `:null`: execute all plot commands but do not actually produce a plot.\n\n    If Gaston detects it is running in a notebook environment, it automatically sets the terminal\n    to `pngcairo` and `config.output` to `:echo`. When the automatic detection does\n    not work, these setting have to be set manually.\n* `Gaston.config.embedhtml`: `Bool`, defaults to `false`. Enables embedding plots in HTML; useful to enable interactivity in Pluto and Jupyter notebooks.\n\n### Location of gnuplot executable\n\nIf gnuplot is not in the system's path, its location can be set using the environmental\nvariable `JULIA_GNUPLOT_EXE`. This variable must be set before Gaston is loaded.\n\n## Plotting\n\nThe `plot` function is used to plot one curve, while `plot!` is used to add a\ncurve to the same plot. 3-D plots are created with `splot` and `splot!`. `plot`\nreturns a value of type `Figure`, which contains a vector of `Axis` (each\ncontaining one or more `Plot`s, or curves.\n\nA `plot` command takes four different kinds of arguments: a figure (possibly\nindexed), settings, data, and plotline, in that order.\n\n```julia\nplot([figure,] [settings...,] data..., [plotline...])\n```\n\nFurther curves may be added using `plot!`. (For 3-D plots, use `splot` and `splot!` instead.)\n\nMore specifically, a `plot` command takes:\n\n* Optionally, a figure where the plot is to be produced.\n  * If the figure is indexed, then the plot will be produced in the specified axis (if it\n    doesn't exist, it will be created).\n* Zero or more **settings** arguments, which get converted to gnuplot `set` commands.\n* One or more **data** arguments, which are written to a file in the format gnuplot expects.\n* Zero or more **plotline** arguments, which are appended to gnuplot's `plot` or `splot` commands.\n\nGaston provides several alternative ways to specify settings and plotlines.\n\n### Settings and Plotlines\n\nAll the following are equivalent.\n\n* One single string\n\n```julia\nplot(\"set grid\n      unset key\n      set title 'A Sinusoid'\",\n     x, y,\n     \"with linespoints lc 'green'\")\n```\n\n* Multiple strings\n\n```julia\nplot(\"set grid\", \"unset key \\n set title 'A Sinusoid'\",\n     x, y,\n     \"with linespoints\", \"lc 'green'\")\n```\n\n* Keywords with `@plot`\n\n```julia\n@plot({grid = true, key = false, title = \"'A Sinusoid'\"},\n      x, y,\n      {with = \"linespoints\", lc = \"'green'\"})\n```\n\n* Keywords with `@gpkw`:\n\n```julia\n@gpkw plot({grid = true, key = false, title = \"'A Sinusoid'\"},\n      x, y,\n      {with = \"linespoints\", lc = \"'green'\"})\n```\n\nKeyword options are enclosed in curly brackets `{}`. To set an option without arguments,\nsuch as `set grid`, use either a lone `grid`, or `grid = true`. To unset an option, such as in\n`unset grid`, use ` grid = false`.  Options can be repeated; each one will be converted to a\nseparate `set` line.\n\n`@plot` also accepts strings, and in fact strings and keywords may be combined:\n\n```julia\n@plot({grid, key = false}, \"set title 'A Sinusoid'\",\n      x, y,\n      \"with linespoints\", {lc = \"'green'\"})\n```\n\nIt is possible to omit the parenthesis, but in this case the command must fit in a single line.\n\n```julia\n@plot {grid, key = false, title = \"'A Sinusoid'\"} x y {with = \"lp\", lc = \"'green'\"}\n```\n\nFor 3-D plots, use the macro `@splot`.\n\n#### Quoted strings\n\nAll strings passed to gnuplot must be enclosed in single quotes, such as in `lc = \"'green'\"` in the\nexample above. The `@Q_str` string macro can help reduce the number of quotes needed:\n\n```julia\n@plot {grid = true, key = false, title = Q\"A Sinusoid\"} x y {with = \"lp\", lc = Q\"green\"}\n```\n\nThis macro turns `\"abc\"` into `\"'abc'\"`.\n\n#### Keyword parsing\n\nSome `@plot` (or `@gpkw`) keyword arguments are parsed by Gaston, providing syntax that may be more\nconvenient than gnuplot's. The following list is for keywords that specify axis settings:\n\n* For any keyword argument, `{arg}` or `{arg = true}` is parsed to `set arg`, while `{arg = false}` is parsed to `unset arg`.\n* For `xtics`, `ytics`, `ztics` or `tics`:\n  * `{tics = R}` where `R` is an `AbstractRange` is parsed as `set tics $(first(R)), $(step(R)), $(last(R))`.\n  * `{tics = T}` where `T` is a `Tuple` is parsed as `set tics $T`.\n  * `{tics = NT}` where `NT` is a `NamedTuple` is parsed as in this example:\n    `{tics = (labels = (\"one\", \"two\"), positions = (0, 2))}` is equivalent to `set tics ('one' 0, 'two' 2, )`\n* For `xrange`, `yrange`, `zrange`, `cbrange`:\n  * `{xrange = R}` where `R` is a vector or tuple is parsed as `set xrange [$R[1]:$R[2]]`\n  * If `R` contains an `Inf`, then it is replaced with `*`.\n* If the keyword is `ranges`, then all four ranges listed above are set.\n* For `palette`, if the value is a symbol, then the corresponding color scheme from `ColorSchemes.jl` is converted to gnuplot's format. If a tuple of two symbols is provided and the second one is `:reverse`, then the order of the palette is reversed.\n* For `{view = V}`, if `V` is a `Tuple`, then it is parsed as `set view $V[1], $V[2]`.\n* For `{linetype = S}` where `S` is a `Symbol`, then the corresponding color scheme is converted to a set of line types, one per color in the scheme.\n* For `{margins = T}` where `T` is a `Tuple` is parsed as in this example:\n  `{margins = (1, 2, 3, 4)}` is equivalent to `set lmargin at screen 1`, `set rmargin at screen 2`, `set bmargin at screen 3`, `set tmargin at screen 4`.\n\nThe following list is for keywords that specify plotline elements:\n\n* `plotstyle` is equivalent to `with`.\n* `markersize` and `ms` are equivalent to `pointsize`.\n* `legend` is equivalent to `title`.\n* `marker` is equivalent `pointtype`.\n\nIn addition, `marker`, `pointtype` and `pt` accept symbolic names for the\nmarker types, according to the following table:\n\n| name | gnuplot pointtype |\n|------|-------------------|\n| :dot      | 0 |\n| :⋅        | 0 |\n| :+        | 1 |\n| :plus     | 1 |\n| :x        | 2 |\n| :*        | 3 |\n| :star     | 3 |\n| :esquare  | 4 |\n| :fsquare  | 5 |\n| :ecircle  | 6 |\n| :fcircle  | 7 |\n| :etrianup | 8 |\n| :ftrianup | 9 |\n| :etriandn | 10 |\n| :ftriandn | 11 |\n| :edmd     | 12 |\n| :fdmd     | 13 |\n\n### Data\n\nData to be plotted can be provided as vectors and/or matrices. Gaston converts the data to a\nformat compatible with gnuplot. Three cases are supported:\n* All data arguments are vectors.\n* The first two arguments are vectors of length `n` and `m`, and the third argument is a matrix\n  of size `n x m`; further arguments are optional.\n* All provided arguments are matrices of size `n x m`.\n\n#### Recipes\n\n[Recipes](manual.qmd#defining-new-plot-types-and-recipes) can be provided to\nconvert arbitrary types to data that gnuplot understands.\n\n#### Functions\n\nFunctions can be plotted directly, with a given range and number of samples, which\ncan be specified in the following alternative ways:\n\n```julia\n# g is a function\nplot(g)            # plots `g` evaluated at 100 samples, from -10 to 9.99\nplot((a, b), g)    # plots `g` evaluated at 100 samples, from a to b\nplot((a, b, c), g) # plots `g` evaluated at c samples, from a to b\nplot(x, g)         # plots g.(x)\n```\n\n#### Plot with table\n\nIn some cases, it is useful to have gnuplot produce plot data in a \"table\"\nformat, which can then be plotted. See an example in [contour lines on\nheatmap](tutorial.qmd#gnuplot-datasets-and-tables). The (non-exported) function\n`Gaston.plotwithtable` returns a `Gaston.DataTable` storing the table. All plot\ncommands accept this type.\n\nThe following `DataTable` constructors are provided:\n\n* `DataTable(vs::Vector{<:AbstractString}...)`\n* `DataTable(ts::T) where T <: Tuple`; the tuple is assumed to contain strings.\n* `DataTable(args::Matrix...)`; each matrix is a datablock.\n\n### Simple themes\n\nFrequently-used settings or plotlines may be stored in a \"theme\"; the `@gpkw` macro processes\nkeyword arguments wrapped in curly brackets.\n\n```julia\ntheme = @gpkw {grid, key = false}\nplot(theme, x, y)\n```\n\nThemes may be combined with other themes and/or with strings:\n\n```julia\ntheme2 = @gpkw {xlabel = Q\"X\"}\nplot(theme, \"set title 'A Sinusoid'\", theme2, x, y)\n```\n\nThemes can also be used for plotlines, and these may also be combined with other themes and/or\nstrings.\n\n```julia\npltheme = @gpkw {w = \"lp\", pt = \"'o'\", ps = 3}\nplot(theme, \"set title 'A Sinusoid'\", theme2, x, y, pltheme)\n```\n\nGaston includes a few generic themes:\n\n|Axis themes | Description |\n|-----------:|:------------|\n| :notics | Removes all tics |\n| :labels | Generic axis labels (`x`, `y`, `z`) |\n| :nocb   | Removes colorbox |\n| :unitranges | Set all ranges to `[-1:1]` |\n\nFor example, the following command plots a sine wave with no tics and generic `x` and `y` axis\nlabels:\n\n```julia\nplot(:notics, :labels, \"set title 'Example'\", (-1, 1), sin)\n```\n\nThemes are also used to provide common plot types (illustrated in\n[Themes](examples.qmd#themes)). The following are the provided specialized plot\ncommands and the themes they use:\n\n| Command | Settings theme | Plotline theme |\n|----------|----------------|----------------|\n| `scatter`, `scatter!` | `:scatter`, `:scatter3` | `:scatter` |\n| `stem`, `stem!` | None | `:stem`, `:impulses` (optional) |\n| `bar`, `bar!` | `:boxplot` | `:box` |\n| `barerror`, `barerror!` | `:boxerror` | `:box` |\n| `histogram` | `:histplot` | `:box`, `:horhist` (1-D); `:image`  (2-D) |\n| `imagesc` | `:imagesc` | `:image`, `:rgbimage` |\n| `surf`, `surf!` | `:hidden3d` | `:pm3d` |\n| `contour` | `:contour` | `:labels` (optional) |\n| `surfcontour` | `:contourproj` | `:labels` (optional) |\n| `wireframe`, `wireframe!` | `:hidden3d` | None |\n| `wiresurf`, `wiresurf!` | `:wiresurf` | None |\n| `heatmap` | `:heatmap` | `:pm3d` |\n\n::: {.callout-warning}\n\n# Plotline themes\n\n    Plotline themes must be handled with care: gnuplot requires plotline\n    options to be specified in a certain order, which may not be repeated, and\n    some combinations are invalid.  It is very easy to create erroneous\n    plotlines.\n\n:::\n\n::: {.callout-note}\n\n# Gaston is not a gnuplot parser\n\nGaston does not validate that the settings and plotline given to gnuplot are valid. When\ngnuplot returns an error or warning, it is echoed to the terminal.\n\n:::\n\n## Multiplot\n\nAs mentioned above, a `Figure` contains a vector of `Axis`. Any figure with more than one axis is plotted using gnuplot's `multiplot` feature (except in the case where the terminal configuration contains `animate`).\n\nThere are several ways to insert axes into a figure. The first is to index into the figure:\n\n```julia\nf = Figure()\nplot(f[2], sin)\nplot(f[4], cos)\n```\n\nNote that:\n\n* Indexing into a non-existing axis creates an empty axis at that index.\n* It's possible to have empty axes (`f[1]` and `f[3]` above are empty).\n* By default, Gaston will manage the figure's layout, trying to keep a square aspect ratio. In the\n  example above, the figure will have dimensions 2x2.\n\nThe second method is to `push!` a figure into another:\n\n```julia\nf1 = plot(sin)\nf2 = plot(cos)\npush!(f1, f2)\n```\n\nHere, the axis at `f[2]` will be inserted into the axes vector of `f1`. It is possible to index\ninto a figure to obtain a specific axis:\n\n```julia\nf1 = plot(sin)\nplot(f1[2], cos)  # f1 now contains two axes\nf2 = Figure()\nplot(tan)         # plot goes into f2 since it is the active figure\npush!(f2, f1[2])  # the axis with a plot of cos is inserted into f2\n```\n\nThe third and final method is to plot multiple figures together:\n\n```julia\nplot(f1, f2, multiplot = \"...\", autolayout = ...)\n```\n\nThis will return a new figure with all axes from `f1` and `f2`. Any number of figures\nmay be provided as arguments.\n\nThe `Figure` constructor takes a couple of options to control how multiplot behaves:\n\n* `multiplot`: a string that is appended to `set multiplot`, such as `\"title 'A multiplot'\"`.\n  Defaults to `\"\"`.\n* `autolayout`: a boolean that controls whether Gaston should manage the figure's layout. Defaults\n  to `true`.\n\n## Managing multiple figures\n\nGaston has the ability to create and manage multiple GUI plot windows simultaneously. Each window\nis backed up by its own gnuplot process. The following commands can be used to create and control\nmultiple windows.\n\n#### Creating and selecting figures\n\n```julia\nFigure()\n```\n\nCreates a new, empty figure. All figures are of type `Gaston.Figure`. Gaston keeps internal\nreferences to all figures, to prevent them from being garbage collected. As described\nabove, `Figure` takes two optional arguments, `multiplot` and `autolayout`.\n\nWhen a figure is created, it becomes the active figure, meaning that subsequent\nplot commands will go to this figure by default. It is possible to keep figures\nin different variables:\n\n```julia\nfig1 = Figure()\nfig2 = Figure()\n```\n\nand then redirect plot commands to the desired figure:\n\n```julia\nplot(fig1, ...)  # plot goes to fig1\nplot!(fig2, ...) # new curve added to fig2\n```\n\nBy default, `plot` resets the contents of a figure.\n\nUsually it is more convenient to keep figures in variables, but it is also\npossible to manage figures using _handles_:\n\n```julia\nFigure(\"density\") # figure with handle \"density\"\nFigure(:volume)   # figure with handle :volume\nFigure(33)        # figure with handle 33\n```\n\nHandles can be of any type. All figures have a handle. By default, handles are\nintegers in increasing order starting from 1.\n\nThe keyword argument `handle` allows specifying the destination of a `plot` command:\n\n```julia\nplot(..., handle = :volume)\nplot!(..., handle = 33)\nscatter(..., handle = \"density\")\n```\n\nTo activate a figure given its handle, use:\n\n```julia\nfigure(handle)\n```\n\nIt is possible to make an existing figure `f` the active figure with:\n\n```julia\nfigure(f)\n```\n\nWith no arguments, `figure()` returns the current figure.\n\nTo obtain the list of all current figures and their handles, and to identify the active figure,\nuse the unexported function `Gaston.listfigures()`.\n\n#### Closing figures\n\nTo close the active figure, run\n\n```julia\nclosefigure()\n```\n\nThe figure with handle `h` can be closed with `closefigure(h)`. Likewise, to close figure `f` use `closefigure(f)`. Closing a figure quits the underlying gnuplot process. \n\nTo close all figures, use `closeall()`.\n\n## Saving plots\n\nA plot can be saved to a file in any format supported by gnuplot, with the function\n\n```julia\nsave([f] ; filename, term)\n```\n\nwhere the arguments are:\n\n* `f`, which can be either a `Figure`, or an arbitrary value that is taken to be the handle of the figure to save. Defaults to the active figure.\n* `filename`, a string that specifies the filename. If empty, it defaults to `figure-` followed by the figure's handle; the filename extension is set to the first three characters of the gnuplot terminal (see next argument).\n* `term`, specifies the gnuplot terminal used to save the plot; defaults to `\"pngcairo font ',7'\"`.\n\n## Defining new plot types and recipes\n\nThere are several ways to extend Gaston to create new plot types or to plot\narbitrary types. One is to define a new function that returns a\n`Gaston.Figure`. The rest involve extending `Gaston.convert_args` in various\nways.\n\n### Functions that return a `Gaston.Figure`\n\nThe first way to extend Gaston to handle arbitrary types is to define a new\nfunction (and optionally new themes) that returns a `Gaston.Figure`. See an\nexample [here](recipes.qmd#functions-that-return-a-gaston.figure).  For 3-D\nplot commands such as `splot`, the function `convert_args3` should be used\ninstead.\n\nThe recommended way to proceed is to:\n\n0. Define new themes if necessary, by adding key-value pairs to `Gaston.sthemes` and/or\n   `Gaston.pthemes`.\n2. Process the function arguments as required.\n1. Create a new figure inside the function, using either `Figure` or `MultiFigure`.\n3. Use `plot` to add new axes and curves to the figure, possibly using the new themes.\n4. Return the figure.\n\n### Adding new methods to `Gaston.convert_args`\n\nWhen the data provided to `plot` is not of a type that gnuplot directly understands,\nGaston calls the function `convert_args`, defined in the package `GastonRecipes`.\nThis function returns a value of one of three different types:\n\n* `GastonRecipes.PlotRecipe`, used to define a single curve (coordinates and a plotline). See\n  and example [here](recipes.qmd#plotrecipe).\n* `GastonRecipes.AxisRecipe`, used to define an axis (settings, a vector of `PlotRecipe`s,\n  and a boolean to define if the axis is three dimensional). See an example\n  [here](recipes.qmd#axisrecipe).\n* `GastonRecipes.FigureRecipe`, used to define a full figure (a vector of `AxisRecipes`,\n  plus multiplot and autolayout settings). See an example [here](recipes.qmd#figurerecipe).\n\nThe function `convert_args` is called with all data and all keyword arguments\ngiven to the `plot` command. Keyword arguments can be used to control the recipe's\nbehavior.\n\nNote that these functions and types are not exported by Gaston.\n\n## Internals\n\nIn Gaston, the basic building block is the `Plot` type. This type has two fields: the\nplotline, a string; and the name of a file where the coordinates are stored. When a `Plot`\nis constructed, the data provided is immediately written to a file; a `Plot` does not\nstore any coordinates.\n\nOn top of `Plot` we have the `Axis` type, which contains a vector of `Plot`s, a string\nwith the axis settings, and a boolean that indicates whether the axis should be\nrendered with `plot` (2-D) or `splot` (3-D).\n\nFinally, on top of `Axis` we have the `Figure` type, which contains a vector of `Axis`\nplus multiplot configuration. Besides, a `Figure` contains a figure handle, and\nmost importantly, a gnuplot process with which it communicates. Each figure is\nassociated with a different gnuplot process.\n\nWhen a `Figure` is displayed by Julia, the `show` function builds a set of commands\nthat are sent to gnuplot. Gnuplot is instructed to send a sentinel string back to\nthe figure, which indicates that gnuplot is done displaying the figure (and also\nprevents race conditions).\n\nEach `Figure` is associated with a finalizer that makes sure its associated gnuplot\nprocess exits gracefully.\n\nGaston uses the following functions to set up and communicate with gnuplot:\n\n* `gp_start` initializes a new gnuplot process and connects to its stdin, stdout and\n  stderr streams.\n* `gp_quit` terminates a gnuplot process.\n* `gp_send` sends a string of commands to an existing gnuplot process.\n* `gp_exec` starts a new gnuplot process, sends it commands, and quits the process.\n\nGaston also keeps some internal state:\n\n* `Gaston.state.figures` stores pointers to all existing figures.\n* `Gaston.state.enabled` is `true` if gnuplot is runnable.\n* `Gaston.state.activefig` stores the handle of the currently active figure.\n"
  },
  {
    "path": "docs/v2/migrate.qmd",
    "content": "---\ntitle: \"Migration guide\"\n---\n\n```{julia}\n#| echo: false\n#| output: false\nusing Gaston\nGaston.config.term = \"pngcairo font ',10' size 640,480\"\nGaston.config.output = :echo\n```\n\nThis guide provides hints on how to migrate from Gaston v1 to v2. In\nside-by-side code comparisons, Gaston v1 is always on the left, and v2 on the\nright.\n\n### Axis settings\n\nIn v1, axes settings are wrapped in a type called `Axes`, and given as key-value pairs.\nIn v2, settings always come before data, and may be given as strings and/or key-value\npairs enclosed in curly brackets (which require either `@plot` or `@gpkw`).\n\n::: {layout-ncol=2}\n\n```julia\nusing SpecialFunctions\nx = y = 0:0.075:10\nsurf(x, y, (x,y) -> besselj0(y)*x^2, with = \"pm3d\",\n     Axes(view = (45, 45),\n          pm3d = \"lighting primary 0.5 specular 0.4\",\n          key = :off)\n     )\n```\n\n```julia\nusing SpecialFunctions\nx = y = 0:0.075:10\n@gpkw surf({view = (45, 45),\n            pm3d = \"lighting primary 0.5 specular 0.4\",\n            key = :off},\n           x, y, (x,y) -> besselj0(y)*x^2)\n```\n\n:::\n\nTwo other differences:\n\n* Using `key = false` instead of `key = :off` is valid syntax to produce `unset key`.\n* In v2, `surf` is a plot style that includes `with pm3d`. The generic 3-D plot\n  command is `splot`, so `splot(..., x, y, z, \"with pm3d\")` in v2 is equivalent\n  to `surf` in v1.\n\n### Plotline (or curve appearance settings)\n\nIn v1, a curve's appearance is configured with key-value arguments that are\nnot data and not `Axes`. Values can be symbols or strings, which are interpreted\ndifferently, and is some cases spaces had to be written as underscores.\n\nIn v2, all curve settings (or _plotline_) are given after the data.\nJust like axis settings, they may be strings and/or key-value pairs enclosed in\ncurly brackets.\n\n::: {layout-ncol=2}\n\n```julia\nt = 0:0.01:1\nplot(t, sin.(2π*5*t),\n     linecolor  = :coral,\n     plotstyle = \"linespoints\",\n     pointtype = \"ecircle\"\n     Axes(title = :First_Plot))\n```\n\n```julia\nt = 0:0.01:1\nplot(\"title = 'First Plot'\",\n     t, sin.(2π*5*t),\n     \"w lp lc 'coral' pt 6\")\n```\n\n:::\n\nIn v2, the following plot commands (and variations thereof) can also be used:\n\n* `plot(..., \"w lp\", \"lc 'coral\", \"pt 6\")`\n* `@plot ... {w = \"lp\", lc = \"'coral'\", pt = :ecircle}`\n* `@gpkw plot(..., {w = \"lp\", lc = \"'coral'\", pt = :ecircle})`\n* `@plot ... {w = \"lp\"} \"lc 'coral'\" {pt = :ecircle}`\n\nFinally, `lc = \"'coral'\"` may be written as `lc = Q\"coral\"`. The `Q_str` string\nmacro inserts the single quotes.\n\n### Multiplot\n\nMultiplot support was completely overhauled in v2.\n\n::: {layout-ncol=2}\n\n```julia\nt = 0.01:0.01:10pi\np1 = plot(t, cos, Axes(title = :Plot_1), handle = 1)\np2 = plot(t, t.^2, Axes(title = :Plot_2), handle = 2)\np4 = plot(t, exp.(-t), Axes(title = :Plot_4), handle = 4)\nplot([p1 p2 ; nothing p4])\n```\n\n```julia\nt = 0.01:0.01:10pi\nf = plot(\"set title 'Plot 1'\", t, cos)\nplot(f[2], \"set title 'Plot 2'\", t, t.^2)\nplot(f[4], \"set title 'Plot 4'\", t, exp.(-t))\n```\n\n:::\n\nIn v2, `f` is a value of type `Figure`, and it can be indexed inside a `plot` command\nto create a multiplot. Indices without a plot create an empty \"slot\".\n\nMore details on multiplots and their settings and layout are given in the\n[tutorial](tutorial.qmd).\n\n### Saving plots\n\nThe command to save plots has been streamlined.\n\n::: {layout-ncol=2}\n\n```julia\nsave(term = \"png\",\n     output= \"myfigure.png\",\n     font = \"Consolas,10\",\n     size = \"1280,900\",\n     linewidth = 1,\n     background = \"blue\")\n```\n\n```julia\nsave(filename = \"myfigure.png\",\n     term = \"png font 'Consolas,10' size 1280,900 lw 1 background 'blue'\")\n```\n\n:::\n\nTo save a specific figure `f`, just run `save(f, ...)`.\n\n### Other differences\n\n* The `set` command is no longer available.\n  * To set the terminal, use `Gaston.config.term`, for example `Gaston.config.term = \"gif\"`.\n  * To enable debug mode, run `Gaston.debug(true)` (use `false` to disable).\n  * To prevent plots from being produced, run `Gaston.config.output = :echo`.\n  * To enable notebook mode (for Jupyter, Pluto, VS Code, etc), use\n    `Gaston.config.output = :echo` (Gaston should detect when running in a notebook,\n    but sometimes this needs to be manually configured, for example when generating\n    Quarto documents).\n* The `axis` key inside `Axes()` is no longer supported.\n"
  },
  {
    "path": "docs/v2/recipes.qmd",
    "content": "---\ntitle: \"Recipes\"\n---\n\n```{julia}\n#| echo: false\n#| output: false\nusing Gaston\nGaston.config.term = \"pngcairo font ',10' size 640,480\"\nGaston.config.output = :echo\n```\n\nThere are several ways to extend Gaston to plot data of arbitrary types.\n\n## Functions that return a `Gaston.Figure`\n\nA straightforward way to extend (or customize) Gaston's functionality is by defining\nfunctions that return a value of type `Figure`.\n\nThe example below shows how to plot complex vectors as two subplots, one of the\nmagnitude and the other of phase of the data. This example defines new themes.\n\n```{julia}\n# define new themes\nGaston.sthemes[:myplot_mag] = @gpkw {grid, ylabel = Q\"Magnitude\"}\nGaston.sthemes[:myplot_ph] = @gpkw {grid, ylabel = Q\"Angle\"}\nGaston.pthemes[:myplot_mag] = @gpkw {w = \"lp\", marker = :ecircle}\nGaston.pthemes[:myplot_ph] = @gpkw {w = \"l\", lc = \"'black'\"}\n\n# define new function\nfunction myplot(f::Figure, data::AbstractVector{<:Complex}; kwargs...)::Figure\n     # convert data to a format gnuplot understands\n     x = 1:length(data)\n     magnitude = abs.(data)\n     phase = angle.(data)\n\n     # make sure figure f is empty\n     Gaston.reset!(f)\n\n     # fixed layout (two rows, one col)\n     f.multiplot = \"layout 2,1\"\n     f.autolayout = false\n\n     # add two plots to f, using the themes defined above\n     plot(f[1], x, magnitude, stheme = :myplot_mag, ptheme = :myplot_mag)\n     plot(f[2], x, phase, stheme = :myplot_ph, ptheme = :myplot_ph)\n\n     return f\nend\n\n# plot on active figure if none specified, or new figure if none exist\nmyplot(data::AbstractVector{<:Complex}; kwargs...) = myplot(figure(), data; kwargs...)\n\n# plot example: complex damped sinusoid\nt = range(0, 1, 20)\ny = exp.(-t) .* cis.(2*pi*7.3*t)\nmyplot(y)  # plot\n```\n\nNote that the function `myplot` has two methods:\n\n* The main method takes an existing figure as first argument, and then the data.\n* A second method handles the case where only data is provided by selecting\n  a new figure and then forwarding execution to the main method.\n\nHere, `myplot` takes a base Julia type. A simple modification handles the case where\nthe data is of a user-defined type:\n\n```{.julia}\n#define new type\nstruct ComplexData{T <: Complex}\n    samples :: Vector{T}\nend\n\n# define new function\nfunction myplot(data::ComplexData; kwargs...)::Figure\n                # convert data to a format gnuplot understands\n                x = 1:length(data.samples)\n                magnitude = abs.(data.samples)\n                phase = angle.(data.samples)\n                [...]\nend\n\n# create new Figure if not provided\nmyplot(data::ComplexData; kwargs...) = myplot(Figure(), data; kwargs...)\n\n# plot example: complex damped sinusoid\nt = range(0, 1, 20)\ny = ComplexData(exp.(-t) .* cis.(2*pi*7.3*t))\nmyplot(y)  # plot\n```\n\nThe use of themes allows the user to modify the default properties of\nthe plot, by modifying the themes (such as `Gaston.sthemes[:myplot_mag]`) instead\nof having to re-define `myplot`. Of course, similar functionality can be\nachieved with the use of keyword arguments.\n\nThe main drawback of this method of extending Gaston is that it requires an\nenvironement where Gaston has been installed This may be undesirable when\nsharing code with others, which may prefer to use a different plotting package,\nor when developing a package, which would burden all users with a relatively\nlarge, unneeded dependency. The solution to this problem is to use \"recipes\".\n\n## Adding new methods to `Gaston.convert_args`\n\nThe package `GastonRecipes` is a tiny package that allows extending Gaston to\nplot arbitrary types. It provides three types of recipes:\n\n* `PlotRecipe`, which return a single curve that can inserted into an axis.\n* `AxisRecipe`, which return a single axis that can be inserted into a figure.\n* `FigureRecipe`, which consists of one or more axes, mostly useful for multiplots or\n  for animations.\n\nRecipes work as follows: Gaston's `plot` function calls function `Gaston.convert_args`\n(or `convert_args3` for 3-D plots), if an appropriate method exists. This function\ntakes the data provided to `plot` and returns an appropriate object\n(of type `PlotRecipe`, `AxisRecipe` or `FigureRecipe`) containing data of a type\nand format that gnuplot understands. The idea then is to add methods to\n`convert_args` to handle any arbitrary type we wish to plot.\n\n### `PlotRecipe`\n\nThe following example shows how to extend `Gaston.convert_args` to plot a\ncustom type `Data1`. This simple example returns a `PlotRecipe` object\n(essentially a curve), which contains data and a plotline.\n\n```{julia}\nusing GastonRecipes: PlotRecipe\nimport GastonRecipes: convert_args\n\n# define custom type\nstruct Type1\n    samples\nend\n\n# add method to convert_args\nfunction convert_args(d::Type1)\n    x = 1:length(d.samples)\n    y = d.samples\n    PlotRecipe((x, y), \"\") # all coordinates are in a Tuple\nend\n\n# create some data\ndata = Type1(rand(20))\n\n# plot\nplot(\"set title 'Simple data conversion recipe'\", \"set grid\", data, \"w lp pt 7 lc 'olive'\")\n```\n\nNote that this kind of recipe will also seamlessly work with `plot!`, which\nadds the curve to the current axis.\n\n```{julia}\ndata2 = Type1(rand(20))\nplot!(data2, \"lc 'red'\")\n```\n\nFinally, note that `Gaston` calls `convert_args` with all data and keyword\narguments given to `plot`. This may be used to control the recipe's behavior,\nas illustrated in the next example.\n\n### `AxisRecipe`\n\nA recipe may also return an entire `AxisRecipe` object, with its own settings and\ncurves. The following example returns an axis with two curves. Keyword arguments are\nused to control the linecolor of each curve.\n\n```{julia}\nusing GastonRecipes: AxisRecipe\n\nstruct Type2 end\n\nfunction convert_args(::Type2, args... ; lc_first = \"'blue'\", lc_second = \"'red'\", kwargs...)\n    x = range(0, 1, 100)\n\n    # build first curve\n    p1 = @gpkw PlotRecipe((x, cos.(4x)), {dt = \"'-'\", lc = lc_first, t =  \"'cosine'\"})\n\n    # build second curve\n    p2 = PlotRecipe((x, sin.(5x)), \"dt '.' lc $(lc_second) t 'sine'\")\n\n    # build AxisRecipe, using a vector of PlotRecipes\n    AxisRecipe(\"set grid\\nset title 'Full axis recipe'\", [p1, p2])\nend\n\nplot(Type2())\n```\n\nBy default, the line colors will be blue and red for the first and second curve,\nrespectively, but these can be specified by the user with the keyword arguments\n`lc_first` and `lc_second`.\n\nNote that the axis returned by a recipe can be inserted directly into a multiplot:\n\n```{julia}\nf = Figure(multiplot = \"title 'Recipe example'\")\nplot(f[1], randn(100), \"w p\")\nplot(f[2], Type2())\n```\n\nFinally, the following example shows how to create a recipe for `splot`, using\n`convert_args3`. Note that now `AxisRecipe` takes a third argument, which\nindicates a 3-D plot when set to `true` (it is `false` by default).\n\n```{julia}\nimport Gaston: convert_args3\n\nfunction convert_args3(::Type2)\n    p1 = PlotRecipe((1:20, 1:20, randn(20,20)), \"w pm3d\")\n    @gpkw s = {palette = :matter, title = Q\"A Surface\"}\n    AxisRecipe(s, [p1], true)\nend\n\nsplot(Type2())\n```\n\n### `FigureRecipe`\n\nFinally, a recipe can also generate a full multiplot, with multiple axes, as\nillustrated in the example below:\n\n```{julia}\nusing GastonRecipes: FigureRecipe\n\nstruct Type3 end\n\nfunction convert_args(::Type3)\n    # first axis\n    p1 = PlotRecipe((1:10, rand(10)), \"\")\n    @gpkw a1 = AxisRecipe({title = Q\"First Axis\"}, [p1])\n\n    # axis #2\n    t1 = range(0, 1, 40)\n    p2 = @gpkw PlotRecipe((t1, sin.(5t1)), {lc = Q\"black\"})\n    p3 = @gpkw PlotRecipe((t1, cos.(5t1)), {w = \"p\", pt = 16})\n    a2 = @gpkw AxisRecipe({title = Q\"Trig\"}, [p2, p3])\n\n    # axis #3\n    t2 = range(-5, 5, 50)\n    z = Gaston.meshgrid(t2, t2, (x,y) -> cos(x)*cos(y))\n    p4 = @gpkw PlotRecipe((t2, t2, z), {w = \"pm3d\"})\n    @gpkw a3 = AxisRecipe({title = Q\"Surface\", tics = false, palette = (:matter, :reverse)},\n                          [p4], true)\n\n    # axis the fourth\n    @gpkw a4 = AxisRecipe({tics, title = false, title = Q\"Last Axis\"},\n                          [PlotRecipe((1:10, 1:10, rand(10,10)), \"w image\")])\n\n    # return named tuple with four axes\n    FigureRecipe([a1, a2, a3, a4],\n                 \"title 'A Four-Axes Recipe' layout 2,2\", false)\nend\n\nplot(Type3())\n```\n\n## Recipes for types owned by other packages\n\nLet's say we want to create a recipe for `Base.Vector`. We don't own either\n`convert_args` nor `Base.Vector`, so creating a recipe would be [type\npiracy](https://docs.julialang.org/en/v1.11/manual/style-guide/#avoid-type-piracy).\nThe solution is to define a new type and dispatch on it. Here's an example: we want\nto plot the elements of a matrix as lines from the coordinates specified in the\nfirst row to to each of the other row.\n\n```{julia}\nstruct StarPlot end\n\nimport GastonRecipes: DataBlock\n\nfunction convert_args(::Type{StarPlot}, x::Matrix)\n    center = x[1,:]\n    points = DataBlock([stack((center, x[n,:]), dims=1) for n in 2:size(x,1)]...)\n    return PlotRecipe((points,), \"w l\")\nend\n\nx = rand(10, 2)\nplot(StarPlot, x)\n```\n\n(Of course, the same functionality could be achieved by wrapping `x` in `StarPlot`. The\nchoice of approach is a matter of taste).\n\nThis recipe also illustrates the use of `DataBlock`. In this case, the data that\nis given to gnuplot has the following form:\n\n```\nx[1,:]\nx[2,:]\n\nx[1,:]\nx[3,:]\n\nx[1,:]\nx[4,:]\n\n....\n```\n"
  },
  {
    "path": "docs/v2/reference.qmd",
    "content": "---\ntitle: \"API Reference\"\n---\n\n```{julia}\n#| echo: false\n#| output: false\nusing Gaston\nusing QuartoTools: Cell, MarkdownCell\n\nfunction quartodoc(title, doc)\n    docs = string(doc)\n    docs = replace(docs, \"\\n# Examples\\n\" => \"\\n**Examples**\\n\")\n    docs = replace(docs, \"\\n# Example\\n\" => \"\\n**Example**\\n\")\n    q = MarkdownCell(\"\"\"\n        ::: {.callout-note icon=false title=$(title)}\n\n        $docs\n\n        :::\n\n        \"\"\")\nend\n```\n\n## Types and constructors\n\n```{julia}\n#| echo: false\nquartodoc(\"Figure\", @doc Figure)\n```\n\n## Plot commands\n\n```{julia}\n#| echo: false\nquartodoc(\"plot\", @doc plot)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"plot!\", @doc plot!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"splot\", @doc splot)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"splot!\", @doc splot!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"@plot\", @doc @plot)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"@plot!\", @doc @plot!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"@splot\", @doc @splot)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"@splot!\", @doc @splot!)\n```\n\n### Plotting with built-in themes\n\nThese functions call `plot` behind the scenes, with settings and plotline taken\nfrom a built-in theme.\n\n```{julia}\n#| echo: false\nquartodoc(\"scatter\", @doc scatter)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"scatter!\", @doc scatter!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"stem\", @doc stem)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"stem!\", @doc stem!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"bar\", @doc bar)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"bar!\", @doc bar!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"barerror\", @doc barerror)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"barerror!\", @doc barerror!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"histogram\", @doc histogram)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"imagesc\", @doc imagesc)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"surf\", @doc surf)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"surf!\", @doc surf!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"scatter3\", @doc scatter3)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"scatter3\", @doc scatter3!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"wireframe\", @doc wireframe)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"wireframe!\", @doc wireframe!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"wiresurf\", @doc wiresurf)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"wiresurf!\", @doc wiresurf!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"surfcontour\", @doc surfcontour)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"contour\", @doc contour)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"heatmap\", @doc heatmap)\n```\n\n## Recipes\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.convert_args\", @doc Gaston.convert_args)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.convert_args3\", @doc Gaston.convert_args3)\n```\n\n## Figure management\n\n```{julia}\n#| echo: false\nquartodoc(\"figure\", @doc figure)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"closefigure\", @doc closefigure)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"closeall\", @doc closeall)\n```\n\n## Utility functions and macros\n\n```{julia}\n#| echo: false\nquartodoc(\"@Q_str\", @doc @Q_str)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"@gpkw\", @doc @gpkw)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"push!\", @doc push!(::Figure, ::Figure))\nquartodoc(\"push!\", @doc push!(::Gaston.Axis, ::Gaston.Plot))\nquartodoc(\"push!\", @doc push!(::Figure, ::Gaston.Axis))\nquartodoc(\"push!\", @doc push!(::Gaston.FigureAxis, ::Gaston.Plot))\nquartodoc(\"push!\", @doc push!(::Figure, ::Gaston.FigureAxis))\n```\n\n## Saving plots\n\n```{julia}\n#| echo: false\nquartodoc(\"save\", @doc save)\n```\n\n## Animations\n\n```{julia}\n#| echo: false\nquartodoc(\"animate\", @doc animate)\n```\n\n## Non-exported functions and types\n\nThe following may be useful when extending or developing Gaston. These functions\nare not part of the official API and may be modified or removed in future versions.\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.terminals\", @doc Gaston.terminals)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.listfigures\", @doc Gaston.listfigures)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.gp_start\", @doc Gaston.gp_start)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.gp_quit\", @doc Gaston.gp_quit)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.gp_send\", @doc Gaston.gp_send)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.gp_exec\", @doc Gaston.gp_exec)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.Plot\", @doc Gaston.Plot)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.Axis\", @doc Gaston.Axis)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.cs2dec\", @doc Gaston.cs2dec)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.set!\", @doc Gaston.set!)\n```\n\n```{julia}\n#| echo: false\nquartodoc(\"Gaston.meshgrid\", @doc Gaston.meshgrid)\n```\n\n"
  },
  {
    "path": "docs/v2/styles.css",
    "content": "/* css styles */\n"
  },
  {
    "path": "docs/v2/tutorial.qmd",
    "content": "---\ntitle: \"Tutorial\"\n---\n\nIf you have not installed Gaston yet, then run the following commands in the Julia REPL:\n\n```{.julia}\nimport Pkg.add\nPkg.add(\"Gaston\")\n```\n\nLoad Gaston with:\n```{julia}\n#| output: false\nusing Gaston\n```\n\nThe plots below have been rendered in a `png` terminal with the following configuration:\n\n```{julia}\n#| output: false\nGaston.config.term = \"pngcairo font ',10' size 640,480\"\n```\n\nWe need to specify that the plot examples below will be rendered to a document and not\na GUI window:\n\n```{julia}\n#| output: false\nGaston.config.output = :echo\n```\n\nIn addition, gnuplot's startup file is as\n[described in the Introduction](index.qmd#gnuplot-startup-file).\n\n## Getting started\n\nLet's start with a simple sine wave plot:\n\n```{julia}\nx = range(0, 0.5, length = 100)\ny = sin.(2*pi*10*x)\nplot(x, y)\n```\nNow, let us add a grid and some annotations:\n```{julia}\n@plot {grid, title = Q\"{/:Bold A sine wave}\", xlabel = Q\"Time\", ylabel = Q\"Volts\"} x y\n```\nHere we have used `@plot` instead of `plot`, which allows us to specify the plot settings\nas a list of keyword arguments. These arguments can be stored in a \"theme\" using the\n`@gpkw` macro:\n```{julia}\n#| output: false\nsettings = @gpkw {grid, title = Q\"{/:Bold A sine wave}\", xlabel = Q\"Time\", ylabel = Q\"Volts\"}\n```\nIn addition, we have used the `Q` string macro to avoid typing single quotes; `Q\"Time\"` is\nconverted to `\"'Time'\"`.\n\nNow let us change the line color and markers:\n\n```{julia}\n@plot settings x y {with = \"lp\", lc = Q\"sea-green\", pt = :ecircle, pn = -16, ps = 1.5}\n```\n\nParameters that affect how the curve is plotted are specified *after* the data. These\ncan also be stored and reused, so that\n\n```{julia}\n#| output: false\nplotline = @gpkw {with = \"lp\", lc = Q\"sea-green\", pt = :ecircle, pn = -16, ps = 1.5}\n@plot settings x y plotline\n```\n\nwould produce the exact same plot. Settings and plotline parameters can also be\nspecified as strings; see the [Manual](manual.qmd#settings-and-plotlines) for\nall the details. Gaston also has a number of built-in [plot\nstyles](tutorial.qmd#included-plot-styles), showcased below.\n\nOne of the keyword arguments used above is a little peculiar: `pt = :ecircle`.\nGnuplot wants point types to be specified as integers, so `:ecircle` (stands\nfor \"empty circle\") should not be valid. The explanation is that Gaston parses\nsome keyword arguments to provide a more comfortable syntax. In this case,\nGaston converts point types specified as symbols according to the table\n[provided here](manual.qmd#keyword-parsing). It is easier to remember that an\nempty circle is specified as `:ecircle` (or a full square as `:fsquare`) than\nas \"6\" (or \"5\").\n\nA `plot` command can only generate a single curve. Use `plot!` or `@plot!` to append a curve:\n\n```{julia}\n@plot(settings,\n      {title = Q\"Two sinusoids\", key = \"columns 1\", key = \"box outside right top\"},\n      x, y,\n      plotline, {title = \"'sin'\"})\ny2 = cos.(2*pi*10*x)\n@plot! x y2 {dashtype = Q\".-.\", lw = 2, lc = Q\"orchid\", title = Q\"cos\"}\n```\n\nHere we see how multiple settings and plotline arguments can be combined.  Note\nthat any axis settings used in `plot!` are ignored.\n\n## Plotting functions\n\nIn the examples above, the data given to `plot` is stored in vectors. Functions\ncan be plotted directly, with a given range and number of samples, as follows:\n```{julia}\ng(x) = exp(-abs(x/5))*cos(x)  # function to plot\ntt = \"set title 'g = x -> exp(-abs(x/5))*cos(x))'\"\nplot(tt, (-10, 10, 200), g) # plot of g from x = -10 to 10, using 200 samples\n```\nRanges can be specified in the following alternative ways:\n```julia\nplot(g)            # 101 samples, from -10 to 10\nplot((a, b), g)    # 101 samples, from a to b\nplot((a, b, c), g) # c samples, from a to b\nplot(x, g)         # g.(x)\n```\n\n## A note on side effects\n\nPlot commands return a value of type `Gaston.Figure`. When values of this type\nare displayed, Julia's display system calls gnuplot behind the scenes to\ngenerate the actual plot. Plots are never generated as side effects the way they\nare, for example, in Matlab.\n\nThis means that, for example, the following code does not display any plots:\n\n```{.julia}\ni = 1\nwhile (i < 10)\n    plot(x, i.*y)\n    i += 1\nend\n```\n\nCalling the function `g` below does not produce any plots either:\n\n```{.julia}\nfunction g(x, y)\n    plot(x, y)\n    println(\"Done.\")\nend\n```\n\nThe easiest way to \"force\" a piece of code to generate a plot is to call `display` explicitly:\n\n```{.julia}\ni = 1\nwhile (i < 10)\n    plot(x, i.*y) |> display\n    i += 1\nend\n```\n\n## Variables that store plots\n\nNaturally, values of type `Figure` can be stored in variables and manipulated as any other\nJulia value:\n\n```{.julia}\nfunction g(x, y)\n    f = plot(x, y)\n    println(\"Done.\")\n    return f\nend\n```\n\nInternally, Gaston keeps references to all `Figure` values it has produced. If there are one\nor more, one of them is _active_ in the sense that all subsequent `plot` and `plot!` commands\nwill target that figure. The active figure can be obtained with:\n\n```{.julia}\nfigure()\n```\n\nand the figure `f` can be made active with:\n\n```{.julia}\nfigure(f)\n```\n\nA new empty figure can be instantiated with the `Figure` constructor, as in `f =\nFigure()`. The new figure is automatically made the active figure.\n\n## Multiplots\n\nA convenient, automatic method to create multiplot figures is provided. First, instantiate\na new figure like this:\n\n```{julia}\nf = Figure(multiplot = \"title 'Auto Layout'\");\n```\nWhen a figure contains more than one axis, it is rendered using `set multiplot`. The `multiplot`\nkeyword argument provides a flexible way to specify additional settings specific to multiplots.\nThe figure `f` will be rendered by gnuplot using\n\n    set multiplot title 'Auto Layout'\n\nAxes can be added by indexing into the figure:\n```{julia}\nplot(f[1], x, y)           # plot x vs y in the first axis\nplot(f[2], x, sinc.(10x))  # plot sinc(10x) in the second axis\n```\nIt is possible to have empty \"slots\":\n```{julia}\nplot(f[4], x, sinc.(20x), \"w lp pn 12\")  # the third axis is empty\n```\nNote that Gaston tries to keep a square figure aspect ratio as more and more axes are included.\n\nAdd another curve to an axis using indexing:\n```{julia}\nplot!(f[2], x, 0.3randn(length(x)))  # add noise curve to second axis\n```\n\nA different way to generate a multiplot is to call `plot` on two or more figures, creating\na new figure that contains the axes of all arguments, as in this example:\n\n```{julia}\nf1 = Figure()\nf2 = Figure()\nplot(f1, \"set title 'sin'\", sin)\nplot(f2, \"set title 'cos'\", cos)\nplot(f1, f2)\n```\n\nA trick that may be useful in some cases is pushing an axis from one figure to a different\nfigure:\n\n```{julia}\nplot(f1, \"set title 'sin'\", sin)\nplot(f2, \"set title 'cos'\", cos)\nplot(f2[2], \"set title 'tan'\", tan)\npush!(f1, f2[2]) # inserts plot of tan into f1\n```\n\nTo get full control of the layout, pass the argument `autolayout = false` to `Figure`:\n```{julia}\nf = Figure(\"title 'Arbitrary multiplot layout demo'\", autolayout = false)\nx = randn(100)\ny = randn(100)\n@plot(f[1], {margins = (0.1, 0.65, 0.1, 0.65)},\n      x, y,\n      \"w p pt '+' lc 'dark-green'\")\n@gpkw histogram(f[2],\n                {margins = (0.7, 0.95, 0.1, 0.65), tics = false},\n                y,\n                {lc = Q\"dark-green\"}, nbins = 10, horizontal = true)\n@gpkw histogram(f[3],\n                {margins = (0.1, 0.65, 0.7, 0.9), boxwidth = \"1 relative\"},\n                x,\n                {lc = Q\"dark-green\"}, nbins = 10)\n```\n\nNote that the `margins` keyword followed by a tuple is parsed as ([see\nhere](manual.qmd#keyword-parsing)):\n\n```julia\n\"\"\"set lmargin at screen 0.7\n   set rmargin at screen 0.95\n   set bmargin at screen 0.1\n   set tmargin at screen 0.65\n\"\"\"\n```\n\n## Closing figures\n\nWe can close all figures created so far with\n```{julia}\ncloseall()\n```\nThis command closes all gnuplot processes started by Gaston and closes all figures. Close\nfigure `f` with\n\n```{.julia}\nclose(f)\n```\n\n## 3-D Plots\nPlotting in 3-D is similar to 2-D, except that `splot` (and `@splot`, `splot!`, `@splot!`) are used\ninstead of `plot`. This example shows how to plot the surface defined by function `s`:\n\n```{julia}\nx = y = -15:0.2:15\ns = (x,y) -> @. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\n@splot \"set title 'Sombrero'\" \"set hidden3d\" {palette = :cool} x y s \"w pm3d\"\n```\n\nThe palette `cool` is defined in\n[ColorSchemes](https://github.com/JuliaGraphics/ColorSchemes.jl). Together with\nthe `palette` keywork, any color scheme from that package can be used simply by\nprepending its name with `:`.\n\n## Plotting in text terminals\n\nIt is often convenient to generate plots directly in the terminal. Gnuplot supports a few\ndifferent ways to do this:\n\n* `sixelgd` uses [sixels](https://en.wikipedia.org/wiki/Sixel) to generate plots almost\n  identical to those produced by regular GUI terms:\n  ![sixel](assets/sixelgd.png)\n* `block` uses Unicode characters to draw a plot in the terminal:\n  ![block](assets/block.png)\n\nNote that, in all cases, Gaston must be configured for terminal output with\n\n```{.julia}\nGaston.config.output = :echo\n```\n\nThere are other text terms, such as `dumb`, but in general those produce output\nof worse quality than `sixelgd` and `block`.\n\n## Plotting with strings and dates\n\nBesides numerical arrays and ranges, Gaston can also plot arrays of strings, as\nshown in the following example:\n\n```{julia}\nx = 10*rand(10)\ny = 10*rand(10)\nw = [\"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"ten\"]\nplot(x, y, w, \"w labels\")\n```\n\nJulia's `Date` type can be plotted by converting it to strings, and using\ngnuplot's date support.  The following code is inspired in\n[this example](https://lazarusa.github.io/gnuplot-examples/examples/2d/lines/dates).\n\n```{julia}\nusing Dates\n\ndates = Date(2018, 1, 1):Day(1):Date(2019, 12, 31)\nta = 0.5*rand(length(dates))\ntimefmt = \"%Y-%m-%d\"\npfmt = \"%Y-%m-%d\"\ntempo = string.(dates) # convert dates to strings\nxmin1 = \"2018-02-01\"\nxmax1 = \"2019-04-01\"\n\n@gpkw settings = {xdata = \"time\",\n                  timefmt = \"'$(timefmt)'\",\n                  grid,\n                  format = \"x '$(pfmt)'\",\n                  xtics = \"rotate by -35\",\n                  xrange = \"['$(xmin1)':'$(xmax1)']\",\n                  yrange = (-0.25, 0.75)}\nplot(settings, tempo, ta, \"u 1:2 w l t 'Activity per day'\")\n```\n\n## Plotting using Latex\n\nGnuplot provides a few different ways to interface with Latex. This example\nshows how to do it using the `cairolatex` terminal. The process is as follows:\nfirst, generate a plot and save it using `term = cairolatex`. This generates\ntwo files: the plot without any text, tics, or markings, in pdf format, and a\n`.tex` file that can be included in a Latex document. This file will add text\nto the pdf image using Latex, resulting in a plot that integrates well with the\nrest of the document.\n\nFirst, generate the plot. Note that the Latex string `equation` is added as a label\nto the plot, causing Latex to render it.\n\n```{.julia}\nequation = raw\"\"\"\\begin{minipage}[c]{\\textwidth}\\begin{equation*}\"\"\" *\n           raw\"\"\"\\sin(x) = \\sum_0^{+\\infty} \\frac{(-1)^n}{(2n + 1)!} x^{2n+1}\"\"\" * \n           raw\"\"\"\\end{equation*} \\end{minipage}\"\"\"\ns = @gpkw { title = Q\"Polynomial approximation of sin(x)\",\n            style = \"fill transparent solid 0.6 noborder\",\n            xtics = (positions = [-pi, -pi/2, 0, pi/2, pi],\n                     labels = [raw\"$-\\pi$\", raw\"$-\\pi/2$\", \"0\", raw\"$\\pi/2$\", raw\"$\\pi$\"]),\n            xrange = (-3.8, 3.8),\n            yrange = (-1.5, 1.5),\n            key = \"box opaque left horiz\",\n            linetype = :YlOrBr_7,\n            grid = \"front\",\n            label = \"at graph 0.62,0.2 front center '$equation'\" }\nx = range(-2pi, 2pi, 1000)\ny = sin.(x)\n@plot s x y x \"w filledcurve t 'n=0' lt 1\"\n@plot! x y x .- x.^3/6 \"w filledcurve t 'n=1' lt 2\"\n@plot! x y x .- x.^3/6 .+ x.^5/120 \"w filledcurve t 'n=2' lt 3\"\n@plot! x y x .- x.^3/6 .+ x.^5/120 .- x.^7/5040 \"w filledcurve t 'n=3' lt 4\"\n@plot! x y \"w l t 'sin(x)' lw 2 lc rgb 'black'\"\nsave(term=\"cairolatex pdf input color dashed size 5in,3.3in\", filename = \"test.tex\")\n```\n\nThis code creates two files, `test.tex` and `test.pdf`, which can be used to\ngenerate a final pdf plot by compiling the following script with `pdflatex`:\n\n```{.latex}\n\\documentclass{article}\n\\usepackage{amsmath}\n\\usepackage{graphicx}\n\\usepackage{color}\n\n\\begin{document}\n\\begin{figure}\n    \\input{test.tex}\n\\end{figure}\n\\end{document}\n```\n\nThe finished plot looks like this:\n\n![](assets/cairolatex.png)\n\nOther gnuplot terminals, such as `tikz`, `epslatex`, etc, can be used with similar workflows. This\nexample is inspired in [this code](https://gcalderone.github.io/Gnuplot.jl/stable/terminals/#The-cairolatex-terminal).\n\n## Gnuplot datasets and tables\n\nSome kinds of plots require a way to specify exactly what points to plot in a\nspecific format (instead of relying on Gaston to format the data the right\nway). This can be accomplished with `Gaston.Datatable`, which wraps `IOBuffer`.\nThe contents of `Datatable` are provided to gnuplot without any further\nprocessing.\n\nOne example is drawing contour lines on a heatmap (taken from [this gnuplot\nblog post](https://gnuplot-tricks.blogspot.com/2009/07/maps-contour-plots-with-labels.html)).\nThe function `Gaston.plotwithtable` returns a `Gaston.DataBlock`, which can be\nused as an argument to `plot`.\n\n```{julia}\n# define function to plot\nx = y = range(-5, 5, 100)\nf4(x,y) = sin(1.3x) * cos(0.9y) + cos(0.8x) * sin(1.9y) + cos(0.2x*y)\n\n# obtain function contours using 'plot with table'\nsettings = \"\"\"set contour base\n              set cntrparam level incremental -3, 0.5, 3\n              unset surface\"\"\"\ncontours = Gaston.plotwithtable(settings, x, y, f4)\n\n# calculate meshgrid for heatmap plot\nz = Gaston.meshgrid(x, y, f4)\n\n# plot heatmap and contours\nplot(\"\"\"unset key\n        unset colorbox\n        set palette rgbformulae 33,13,10\"\"\",\n        x, y, z, \"with image\")\nplot!(contours, \"w l lw 1.5 lc 'slategray'\")\n```\n\nAnother example is drawing polygons or polytopes, where the data provided to\ngnuplot are the coordinates of the vertices of each face, and faces are\nseparated by newlines. The following example (adapted from the demos in\ngnuplot's official site) draws an icosahedron using the plotline `with\npolygon`.\n\n```{julia}\n# The icosahedron has twelve vertices, given by these coordinates\nphi = (1 + sqrt(5)) / 2\nico = ( ( 0,    1,    phi), #1\n        ( 0,    1,   -phi), #2\n        ( 0,   -1,    phi), #3\n        ( 0,   -1,   -phi), #4\n        ( 1,    phi,  0),   #5\n        ( 1,   -phi,  0),   #6\n        (-1,    phi,  0),   #7\n        (-1,   -phi,  0),   #8\n        ( phi,  0,    1),   #9\n        (-phi,  0,    1),   #10\n        ( phi,  0,   -1),   #11\n        (-phi,  0,   -1)    #12\n)\n# plot settings\ns = \"\"\"unset border\n       unset key\n       set view equal xyz\n       unset xtics\n       unset ytics\n       unset ztics\n       set pm3d depthorder\n       set pm3d interpolate 1,1 flush begin noftriangles border linecolor rgb \"black\" linewidth 2 dashtype solid corners2color mean\n       set title \"An icosahedron drawn as 20 individual faces\"\n       set style fill transparent solid 0.8\n       set view 69, 33\n    \"\"\"\n# write the vertices that make up each face in a Datatable\nfaces = Gaston.DataBlock(stack([ico[i] for i in (1, 5, 7)], dims=1),\n    stack([ico[i] for i in (1, 7, 10)], dims=1),\n    stack([ico[i] for i in (1, 10, 3)], dims=1),\n    stack([ico[i] for i in (1, 3, 9)], dims=1),\n    stack([ico[i] for i in (1, 9, 5)], dims=1),\n    stack([ico[i] for i in (2, 5, 11)], dims=1),\n    stack([ico[i] for i in (2, 11, 4)], dims=1),\n    stack([ico[i] for i in (2, 4, 12)], dims=1),\n    stack([ico[i] for i in (2, 12, 7)], dims=1),\n    stack([ico[i] for i in (2, 7, 5)], dims=1),\n    stack([ico[i] for i in (8, 3, 10)], dims=1),\n    stack([ico[i] for i in (8, 10, 12)], dims=1),\n    stack([ico[i] for i in (8, 12, 4)], dims=1),\n    stack([ico[i] for i in (8, 4, 6)], dims=1),\n    stack([ico[i] for i in (8, 6, 3)], dims=1),\n    stack([ico[i] for i in (7, 12, 10)], dims=1),\n    stack([ico[i] for i in (6, 4, 11)], dims=1),\n    stack([ico[i] for i in (5, 9, 11)], dims=1),\n    stack([ico[i] for i in (9, 6, 11)], dims=1),\n    stack([ico[i] for i in (9, 3, 6)], dims=1))\n# plotline\nwp = \"with polygons fc rgb 'gray'\"\n# splot is used since the icosahedron is 3-D\nsplot(s, faces, wp)\n```\n\nGaston provides a few `Datatable` constructors, described in the [Manual](manual.qmd#plot-with-table).\n\n## Animations\n\nAnimations require using a terminal that support them; the `gif` or `webp`\nterminals are the most popular ones (make sure your notebook supports the\n`image/webp` MIME type before using it).\n\nCreating an animation is similar to multiplotting: multiple axes are drawn on\nthe same figure. When the gnuplot terminal contains the `animate` option,\nhowever, the plot is rendered as an animation.\n\nNote that gnuplot will output a message to `STDERR` indicating how many frames\nwere recorded; this message is purely informative and not actually an error.\n\nThe following examples illustrate how to create and display animations, in this case with a\nbackground image:\n\n```{.julia}\nframes = 75 # number of animation frames\n# new, empty figure\nf = Figure()\n# create a background curve that is shown in all frames\nx_bckgnd = range(-1, 1, 200)  # x values for the background image\ny_bckgnd = sin.(2π*2*x_bckgnd)\nbckgnd = Gaston.Plot(x_bckgnd, y_bckgnd, \"lc 'black'\")  # background curve\n# generate all frames\nx = range(-1, 1, frames)\nfor i in 1:frames\n    # first plot the function...\n    plot(f[i], x[i], sin(2π*2*x[i]), \"w p lc 'orange' pt 7 ps 7\")\n    # ... then add the background\n    push!(f[i], bckgnd)\nend\nfor i in frames:-1:1  # in reverse\n    plot(f[2frames-i+1], x[i], sin(2π*2*x[i]), \"w p lc 'orange' pt 7 ps 7\")\n    push!(f[2frames-i+1], bckgnd)\nend\nsave(f, filename = \"2DAnim.webp\", term = \"webp animate loop 0 size 640,480\")\n```\n![](assets/2DAnim.webp)\n\nFirst,  an empty figure `f` is created with `Figure()`. Then,\n[`Gaston.Plot`](reference.qmd) is used to create an object, `bckgnd`, which\ncontains a curve (a black sine wave) and which can be inserted into an axis.\nEvery loop iteration, a new axis is inserted into `f` with `plot(f[i], ...)`\nwith a curve consisting of an orange circle somewhere along the sine wave.\nThen, the background curve is inserted into the same axis with `push!(f[i],\nbckgnd)`. Finally, the plot is saved in a format that supports animation\n(`webp` in this case).\n\nA difficulty arises when mixing plot formats in a notbook (say, `png` and\n`webp`): the terminal is specified in the configuration variable `Gaston.config.term`.\nHowever, some notebook programs (such as Pluto) execute cells in arbitrary\norder. This means that changing the terminal in one cell may affect other\ncells.\n\nTo solve this problem, Gaston provides a way to ignore the global terminal\nconfiguration when rendering a plot. A figure `f` can be rendered with a given\nterminal by calling `animate(f, term)`. The default value of `term` is stored\nin `Gaston.config.altterm` and defaults to `gif animate loop 0`. Examples are\nprovided in these [interactive Pluto\nnotebooks](https://github.com/mbaz/Gaston.jl/tree/master/notebooks).\n\n## Themes\n\nGaston supports _themes_, which are pre-configured plot styles. There are two\nkinds of themes: _settings themes_, which specify gnuplot `set` commands, and\n_plotline themes_, which specify how a particular curve is displayed (color,\nthickness, etc.) Settings themes are stored in the dictionary `Gaston.sthemes`,\nand plotline themes are stored in `Gaston.pthemes`. The themes in these\ndictionaries can be modified, and new themes can be stored in them. Built-in\nthemes used to create common plot styles (such as `scatter` and `stem`) are\ndescribed in [the next section](tutorial.qmd#included-plot-styles).\n\nGaston also includes a few settings themes that conveniently define common\nconfigurations:\n\n| theme | purpose |\n|-------|---------|\n| `:notics` | Remove all tics from the plot. |\n| `:labels` | Label axes using `x`, `y`, and `z` (for `splot`) |\n| `:unitranges` | Set `x`, `y`, and `z` ranges to `[-1:1]` |\n| `:nocb` | Disable the colorbox |\n\nThis example shows how to use these themes:\n\n```{julia}\nplot(:notics, :labels, \"set grid\", sin)\n```\n\n## Included plot styles\n\nGaston includes several themes for common plot styles. The easiest way to use\nthem is through the specialized plot commands described below. For more\ndetails, see the [manual](manual.qmd#simple-themes).\n\nThe themed commands described below use combinations of these themes to create\na specific type of plot.\n\nNote that, in gnuplot, plotlines (as in `with lines linecolor 'green'`) are\nespecially difficult to theme, because repeated options (and options given in\nthe wrong order) are errors. It is recommended to keep plotline themes very\nsimple, and specify the plotline manually as part of the plot command.\n\nThe following subsections cover all included plot styles, along with the list\nof built-in themes they rely on, as well as some examples.\n\n### Scatter plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`scatter` | none | `:scatter` |\n|`scatter3` | `:scatter3` | `:scatter` |\n\n```{julia}\n# reset theme #| hide_line\n@gpkw Gaston.pthemes[:scatter] = {with = \"points\", pointtype = :fcircle, pointsize = 1.5} #| hide_line\nxg = randn(20)\nyg = randn(20)\nscatter(\"set title 'Scatter plot'\n         set key outside\",\n        xg, yg,\n        \"title 'gaussian'\")\nxu = rand(20)\nyu = rand(20)\nscatter!(xu, yu, \"title 'uniform'\")\n```\nA 3-D scatter plot (the default settings theme (`:scatter3`) draws all the borders):\n```{julia}\nscatter3(\"set title 'A 3-D scatter plot\", randn(10), randn(10), randn(10))\n```\n\n### Stem plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`stem` | none | `:stem`, `:impulses` |\n\nStem plots are often used in digital signal processing applications to represent\na discrete-time (sampled) signal.\n```{julia}\nstem(\"set title 'Stem plot'\", g)\n```\nTo generate a stem plot, gnuplot actually plots twice: once with style `impulses` and once with\n`points` (set to empty circles). Normally, each of these plots would have a different color. To\nuse the same color for both, use the `color` keyword argument:\n```{julia}\nstem(\"set title 'Stem plot'\", g, color = \"'goldenrod'\")\n```\nThe circular marks can be omitted with the `onlyimpulses` keyword argument:\n```{julia}\nstem(\"set title 'Stem plot with onlyimpulses'\", g, onlyimpulses = true)\n```\n\n### Bar plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`bar` | `:boxplot` | `:box` |\n|`barerror` | `:boxerror` | `:box` |\n\n```{julia}\nbar(\"set title 'Bar plot'\", rand(10), \"lc 'turquoise'\")\n```\nThis example shows how to plot two sets of bars, using `bar!`:\n```{julia}\nbar(\"set title 'Two bar plots'\", rand(10), \"lc 'dark-violet'\")\nbar!(1.5:10.5, 0.5*rand(10), \"lc 'plum' fill pattern 4\")\n```\nError bars are handled by `barerror`; there is also `barerror!`.\n```{julia}\nbarerror(\"set title 'Error bars plot'\", 1:10, rand(10), 0.1*rand(10).+0.1, \"lc 'sandybrown'\")\n```\n\n### Histograms\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`histogram` | `:histplot` | `:box`, `:horhist` (1-D); `:image`  (2-D) |\n\nThe `histogram` function takes these optional keyword arguments:\n\n* `nbins`: specifies the number of bins. Defaults to 10.\n* `mode::Symbol`: Controls histogram normalization mode; passed to\n  [`StatsBase.normalize`](https://juliastats.org/StatsBase.jl/stable/empirical/#LinearAlgebra.normalize).\n  Defaults to `:none`.\n* `edges`: a vector or a range specifying the bin edges; if specified, takes\n  precedence over `nbins`. Defaults to `nothing`.\n* `horizontal::Bool`: if `true`, the histogram is drawn horizontally. Defaults\n  to `false`.\n\n`histogram` uses the settings theme `:histplot`, and plotline themes `:box` or `:horhist`.\n2-D histograms are supported, by passing two datasets.\n\nUsing `nbins`:\n```{julia}\nhistogram(\"set title 'Histogram (nbins)'\",\n          randn(10_000),\n          nbins = 20, mode = :pdf)\n```\n\nUsing `edges`:\n```{julia}\nhistogram(\"set title 'Histogram (edges)'\",\n          0.75*randn(10_000),\n          edges = -2:0.75:3, \"lc 'dark-khaki'\")\n```\n\nA horizontal histogram:\n```{julia}\nhistogram(\"set title 'horizontal histogram'\",\n          rand(1000),\n          nbins = 15, horizontal = true, \"lc 'orchid'\")\n```\n\nIn the case of 2-D histograms, `nbins` or `egdes` may be a tuple; otherwise, both axes use the\nsame settings. The plotline theme is `:image`.\n```{julia}\nx = 2.5*randn(100_000)\ny = 2.5*randn(100_000)\nth = @gpkw {palette = :matter, colorbox = false, title = Q\"2-D histogram\",\n            xrange = (-10, 10), yrange = (-10, 10)}\nhistogram(th, x, y, nbins = 50, mode = :pdf)\n```\n\n### Images\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`imagesc` | `:imagesc` | `:image`, `:rgbimage` |\n\nArrays may be plotted as images using `imagesc`. Note that, in contrast to other plotting packages,\nthe first data row is plotted horizontally and at the top.\n```{julia}\nX = [0 1 2 3;\n     0 3 2 1;\n     0 2 2 0;\n     3 0 0 0]\nimagesc(\"unset xtics\", \"unset ytics\", X)\n```\nTo display the image as grayscale, use the `gray` palette.\n```{julia}\nusing Images, TestImages\nimg = testimage(\"lake_gray\");\nii = channelview(img)[1,:,:].*255;\n@gpkw imagesc({palette = :gray}, ii)\n```\n\nAn RGB image is a plot of a 3-D array, where  `[1,;,:]`\nis the red channel, `[2,:,:]` is the green channel, and\n`[3,:,:]` is the blue channels.\n```{julia}\nimg = testimage(\"lake_color\")\n@gpkw imagesc({size = \"square\", autoscale = \"fix\"}, channelview(img).*255)\n```\n\n### Surfaces\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`wireframe` | `:hidden3d` | none |\n|`surf` | `:hidden3d` | `:pm3d` |\n\nA surface can be plotted as a \"wireframe\" (or a \"mesh\") with the `wireframe`\ncommand. By default, `hidden3d` is active, so that elements behind the surface\nare not plotted.\n```{julia}\ng(x,y) = sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\nth = @gpkw {title = Q\"Sombrero Wireframe\", palette = :matter}\nwireframe(th, (-15, 15, 30), g)\n```\nSolid surfaces are plotted with `surf`:\n```{julia}\nth = @gpkw {title = Q\"Sombrero Surface\", palette = :matter}\nsurf(th, (-15, 15, 200), g)\n```\n\nWhen plotting a function and a single range (such as `(-15, 15, 200)` above) is\ngiven, it is used for both `x` and `y` coordinates. Two ranges may be given as\nwell to control the `x` and `y` ranges separately:\n\n```{julia}\nsurf(th, (-15, 15, 200), (-25, 5, 200), g)\n```\n\n### Contour plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`contour` | `:contour` | `:labels` |\n| `surfcountour` | `:contourproj` | `:labels` |\n\nBy default, contour plots include numerical labels:\n```{julia}\nh(x,y) = cos(x/2)*sin(y/2)\ncontour(\"set title 'Contour Plot'\", (-10, 10, 50), h)\n```\nTo plot contours without labels, use the keyword argument `labels = false`:\n```{julia}\ncontour(\"set title 'Contour Plot Without Labels'\", (-10, 10, 50), h, labels = false)\n```\nIt's possible to plot a wireframe surface and a contour projected on the base of the plot\nusing `surfcountour`:\n```{julia}\nsurfcontour(\"set title 'Surface With Projected Contours'\", (-5, 5, 40), h, \"lc 'orange'\")\n```\nThe same plot without contour labels:\n```{julia}\nsurfcontour(\"set title 'Surface With Contours, No Labels'\",\n            (-5, 5, 40), h, \"lc 'orange'\", labels = false)\n```\n\n### Heatmap plots\n\n| command | settings theme | plotline theme |\n|:--------|:---------------|:---------------|\n|`heatmap` | `:heatmap` | `:pm3d` |\n\n```{julia}\ntheme = @gpkw {palette = :matter, title = Q\"Heatmap\"}\nheatmap(theme, :notics, :nocb, :labels, (-10, 10, 70), h)\n```\n"
  },
  {
    "path": "src/Gaston.jl",
    "content": "## Copyright (c) 2013 Miguel Bazdresch\n##\n## This file is distributed under the 2-clause BSD License.\n\nmodule Gaston\n\n# Basic commands\nexport @Q_str#, @gpkw\nexport @plot, @plot!, @splot, @splot!\nexport plot, plot!, splot, splot!\nexport figure, closefigure, closeall\n\n# Types\nexport Figure\n\n# 2-D styled plots\nexport scatter,  stem,  bar,  barerror, histogram, imagesc\nexport scatter!, stem!, bar!, barerror!\n\n# 3-D styled plots\nexport surf,  scatter3,  wireframe,  wiresurf, surfcontour, contour, heatmap\nexport surf!, scatter3!, wireframe!, wiresurf!\n\n# Saving and animations\nexport save, animate\n\n# New methods are added to these functions\nimport Base: show, showable, getindex, isempty, push!, setindex!, length\n\n# Methods for recipes\nusing GastonRecipes\nimport GastonRecipes: PlotRecipe, AxisRecipe, FigureRecipe, AbstractFigure, DataBlock,\n                      convert_args, convert_args3,\n                      @gpkw, expand, prockey, procopts\nexport @gpkw\n\n# Methods from other packages\n#using MacroTools: postwalk, @capture\n\nusing Base: keys\n\nusing StatsBase: fit, Histogram, normalize\n\nusing DelimitedFiles: writedlm\n\nusing ColorSchemes: colorschemes, get, resample, ColorScheme, RGB, RGBA\n\nusing PrecompileTools\n\nimport Preferences\nimport Gnuplot_jll\n\nconst GNUPLOT_VERSION = Ref(v\"0.0.0\")\n\n# URL for web-hosted javascript files, for svg and canvas interactivity\nconst JSDIR = \"'https://cdn.jsdelivr.net/gh/mbaz/gnuplot-js@1.0/'\"\n\n# load files\ninclude(\"gaston_options.jl\")\ninclude(\"gaston_figures.jl\")\ninclude(\"gaston_aux.jl\")\ninclude(\"gaston_plot.jl\")\ninclude(\"gaston_builtinthemes.jl\")\ninclude(\"gaston_recipes.jl\")\ninclude(\"gaston_llplot.jl\")\n\n# Figure storage\nstruct FigureStore\n    figs::Vector{Figure}\nend\n\ngetindex(fs::FigureStore, args...) = getindex(fs.figs, args...)\nlength(fs::FigureStore) = length(fs.figs)\n\n# State\nBase.@kwdef mutable struct State\n    figures   = FigureStore(Figure[])   # figure storage\n    enabled   = false      # is gnuplot installed on this system?\n    activefig = nothing    # handle of active figure\nend\nstate = State()\n\ngetindex(s::State, args...) = getindex(s.figures, args...)\nlength(s::State) = length(s.figures)\n\nactivefig() = state.activefig\n\n# Configuration\n# embedhtml::Bool controls whether a figure is embedded in html when displayed in a notebook\n# output::Symbol\n#   :external -> display figures in separate gnuplot windows\n#   :null ->  do not display anything\n#   :echo -> display plot as text back to terminal (for notebooks, sixel and text)\n# term::String -> terminal to use\n# exec::Cmd -> gnuplot executable\nBase.@kwdef mutable struct Config\n    embedhtml :: Bool   = false\n    output    :: Symbol = :external\n    term      :: String = \"\"\n    altterm   :: String = \"gif animate loop 0\"\n    alttoggle :: Bool   = false\n    exec      :: Union{Nothing,Cmd} = gnuplot_path()\nend\nconfig = Config()\n\n# Determine if running in a notebook environment\nfunction isnotebook()\n    # Comment the following two lines, and uncomment the two below, to run JET tests\n    if (isdefined(Main, :IJulia) && Main.IJulia.inited) ||\n       (isdefined(Main, :Juno) && Main.Juno.isactive()) ||\n#    if isdefined(Main, :IJulia) ||\n#       isdefined(Main, :Juno) ||\n       isdefined(Main, :VSCodeServer) ||\n       isdefined(Main, :PlutoRunner)\n       return true\n    end\n    return false\nend\n\n# initialize gnuplot\nfunction __init__()\n    global config, state\n\n    if \"JULIA_GNUPLOT_EXE\" in keys(ENV)\n        config.exec = Cmd([ENV[\"JULIA_GNUPLOT_EXE\"]])\n    end\n\n    try\n        ver_str = replace(read(`$(config.exec) --version`, String), \"gnuplot\" => \"\", \"patchlevel\" => \"\", \".\" => \" \") |> strip |> split\n        GNUPLOT_VERSION[] = VersionNumber(parse.(Int, ver_str)...)\n        state.enabled = true\n    catch\n        @warn \"Gnuplot is not available on this system. Gaston will be unable to produce any plots.\"\n    end\n\n    # This configuration depends on run-time state\n    config.output = isnotebook() ? :echo : :external\n    config.term = isnotebook() ? \"pngcairo\" : \"\"\n\n   return nothing\nend\n\n@compile_workload begin\n    if state.enabled\n        config.output = :null\n        f = Figure()\n        y = 1.1:0.5:10.6\n        plot(y)\n        plot!(y)\n        plot(f[2], y)\n        @plot({grid}, y, {w = \"l\", lc = Q\"red\"})\n        f1 = (x,y) -> sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\n        splot(f, (-5,5), f1)\n        save(f, term = \"png\", filename = \"test.png\")\n        rm(\"test.png\")\n        closeall()\n        gp_quit(f)\n    end\nend\n\nend\n"
  },
  {
    "path": "src/gaston_aux.jl",
    "content": "# Copyright (c) 2013 Miguel Bazdresch\n##\n## This file is distributed under the 2-clause BSD License.\n\n# Auxiliary, non-exported functions are declared here.\n\n# from github.com/JuliaPackaging/Preferences.jl/blob/master/README.md:\n# \"Preferences that are accessed during compilation are automatically marked as compile-time preferences\"\n# ==> this must always be done during precompilation, otherwise\n# the cache will not invalidate when preferences change\nconst gnuplot_binary = Preferences.load_preference(Gaston, \"gnuplot_binary\", \"artifact\")\n\nconst max_lines = string(typemax(Int32) ÷ 2)\nconst magic = \"Gaston-4b11ee91-296f-5714-9832-002c20994614\"\n\nfunction gnuplot_path()\n    return if gnuplot_binary in (\"artifact\", \"jll\")\n        addenv(\n            Gnuplot_jll.gnuplot(),\n            \"LINES\" => max_lines,\n            \"PAGER\" => nothing,\n            \"GNUPLOT_DRIVER_DIR\" => dirname(Gnuplot_jll.gnuplot_fake_path)\n        )\n    elseif Sys.isexecutable(gnuplot_binary)\n        addenv(Cmd([gnuplot_binary]), \"LINES\" => max_lines)\n    else\n        @debug gnuplot_binary\n        nothing\n    end\nend\n\"\"\"\n    Gaston.gp_start()::Base.Process\n\nReturns a new gnuplot process.\n\"\"\"\nfunction gp_start()\n    if state.enabled\n        inp = Base.PipeEndpoint()\n        out = Base.PipeEndpoint()\n        err = Base.PipeEndpoint()\n        process = run(config.exec, inp, out, err, wait=false)\n        return process\n    else\n        @warn \"gnuplot is not available on this system.\"\n    end\nend\n\n\"\"\"\n    Gaston.gp_quit(process::Base.Process)\n\nEnd gnuplot process `process`. Returns the exit code.\"\n\"\"\"\nfunction gp_quit(process::Base.Process)\n    if state.enabled\n        if process_running(process)\n            write(process, \"exit gnuplot\\n\")\n            close(process.in)\n            wait(process)\n        end\n        return process.exitcode\n    else\n        @warn \"gnuplot is not available on this system.\"\n    end\nend\n\n\"\"\"\n    Gaston.gp_quit(f::Figure)\n\nEnd the gnuplot process associated with `f`. Returns the process exit code.\n\"\"\"\ngp_quit(f::Figure) = gp_quit(f.gp_proc)\n\n\"\"\"\n    gp_send(process::Base.Process, message::String)\n\nSend string `message` to `process` and handle its response.\n\"\"\"\nfunction gp_send(process::Base.Process, message::String)\n    if state.enabled\n        if process_running(process)\n            message *= '\\n'\n            write(process, message) # send user input to gnuplot\n\n            @debug \"String sent to gnuplot:\" message\n\n            # ask gnuplot to return sigils when it is done\n            write(process, \"\"\"\\\n                set print '-'\n                print '$magic'\n                printerr '$magic'\n                \"\"\"\n            )\n            gpout = readuntil(process, magic) |> rstrip\n            gperr = readuntil(process.err, magic) |> rstrip\n\n            # handle errors\n            process_running(process) || @warn \"gnuplot crashed.\"\n            isempty(gperr) || @info \"gnuplot returned a message in STDERR:$gperr\"\n\n            return gpout * '\\n', gperr * '\\n'\n        else\n            @warn \"Tried to send a message to a process that is not running\"\n            return nothing\n        end\n    else\n        @warn \"gnuplot is not available on this system.\"\n    end\nend\n\n\"\"\"\n    gp_send(f::Figure, message)\n\nSend string `message` to the gnuplot process associated with `f` and handle its response.\n\"\"\"\ngp_send(f::Figure, message) = gp_send(f.gp_proc, message)\n\n\"\"\"\n    gp_exec(message::AbstractString)\n\nRun an arbitrary gnuplot command and return gnuplot's stdout.\n\"\"\"\nfunction gp_exec(message::AbstractString)\n    if state.enabled\n        p = gp_start()\n        (gpout, gperr) = gp_send(p, message)\n        gp_quit(p)\n        return gpout[1:end-11]\n    else\n        @warn \"gnuplot is not available on this system.\"\n    end\nend\n\n\"\"\"\n    save(f::Figure = figure(); filename = nothing, term = \"pngcairo font ',7'\")::Nothing\n\nSave figure `f` using the specified terminal `term` and `filename`.\n\nIf `filename` is not provided, the filename used is `\"figure-handle.ext`, where\n`handle = f.handle` and `ext` is given by the first three characters of the\ncurrent terminal.\n\"\"\"\nfunction save(f::Figure = figure() ; filename = nothing, term = \"pngcairo font ',7'\")\n    # determine file name\n    if isnothing(filename)\n        # determine extension\n        ext = split(term)[1]\n        if length(ext) > 2\n            ext = ext[1:3]\n        end\n        filename = \"figure-$(f.handle).\"*ext\n    end\n    producefigure(f ; filename, term)\n    return nothing\nend\n\n\"\"\"\n    save(handle; filename = nothing, term = \"pngcairo font ',7'\")::Nothing\n\nSave the figure with the given handle.\n\"\"\"\nfunction save(handle ; kwargs...)\n    f = figure(handle)\n    save(f::Figure ; kwargs...)\nend\n\n\"\"\"\n    Gaston.terminals()\n\nReturn a list of available gnuplot terminals\"\"\"\nfunction terminals()\n    t = gp_exec(\"set term\")\n    if !(Gaston.config.output == :null)\n        print(t)\n    end\nend\n\n\"\"\"\n    Gaston.debug(d::Bool)\n\nEnable or disable debug mode.\"\"\"\nfunction debug(flag)\n    if flag\n        ENV[\"JULIA_DEBUG\"] = Gaston\n    else\n        ENV[\"JULIA_DEBUG\"] = \"\"\n    end\n    return nothing\nend\n\n\"Restore default configuration.\"\nfunction reset()\n    global config\n    config.embedhtml = false\n    config.output = :external\n    config.term = \"\"\n    config.exec = gnuplot_path()\nend\n\n\"\"\"\n    meshgrid(x, y, z)\n\nReturn a z-coordinate matrix from `x`, `y` coordinates and a function `f`,\nsuch that `z[row,col] = f(x[row], y[col])`\n\"\"\"\nmeshgrid(x, y, f::F) where {F<:Function} = [f(xx,yy) for yy in y, xx in x]\n\n\"\"\"\n    hist(s::Vector{T}, nbins=10, norm=false) where {T<:Real}\n\nReturn the histogram of values in `s` using `nbins` bins. If `normalize` is true,\nthe values of `s` are scaled so that `sum(s) == 1.0`.\n\"\"\"\nfunction hist(s ;\n              edges        = nothing,\n              nbins        = 10,\n              norm::Bool   = false,\n              mode::Symbol = :pdf)\n    if edges === nothing\n        # Unfortunately, `StatsBase.fit` regards `nbins` as a mere suggestion.\n        # Therefore, we need to give it the actual edges.\n        (ms, Ms) = extrema(s)\n        if Ms == ms\n            # compute a \"natural\" scale\n            g = (10.0^floor(log10(abs(ms)+eps()))) / 2\n            ms, Ms = ms - g, ms + g\n        end\n        edges = range(ms, Ms, length=nbins+1)\n    end\n\n    h = fit(Histogram, s, edges)\n    norm && (h = normalize(h, mode=mode))\n\n    @debug \"hist():\" nbins collect(edges) h.weights\n\n    return h\nend\n\n# 2D histogram\nfunction hist(s1, s2 ;\n              edges          = nothing,\n              nbins          = (10, 10),\n              mode :: Symbol = :none)\n    edg = edges\n    if edges === nothing\n        (ms1, Ms1) = extrema(s1)\n        if Ms1 == ms1\n            g = (10.0^floor(log10(abs(ms1)+eps()))) / 2\n            ms1, Ms1 = ms1 - g, ms1 + g\n        end\n        edges1 = range(ms1, Ms1, length=nbins[1]+1)\n\n        (ms2, Ms2) = extrema(s2)\n        if Ms2 == ms2\n            g = (10.0^floor(log10(abs(ms2)+eps()))) / 2\n            ms2, Ms2 = ms2 - g, ms2 + g\n        end\n        edges2 = range(ms2, Ms2, length=nbins[2]+1)\n        edg = (edges1, edges2)\n    elseif !(edges isa Tuple)\n        edg = (edges, edges)\n    end\n\n    h = fit(Histogram, (s1, s2), edg)\n    h = normalize(h, mode = mode)\n\n    @debug \"hist():\" nbins collect(edges) h.weights\n\n    return h\nend\n\nfunction showable(::MIME{mime}, f::Figure) where {mime}\n    rv = false\n    if config.alttoggle\n        t = split(config.altterm)[1]\n    else\n        t = split(config.term)[1]\n    end\n    h = config.embedhtml\n    if (mime == Symbol(\"image/gif\")) && (t == \"gif\")\n        rv = true\n    end\n    if (mime == Symbol(\"image/webp\")) && (t == \"webp\")\n        rv = true\n    end\n    if (mime == Symbol(\"image/png\")) && (t == \"png\" || t == \"pngcairo\")\n        rv = true\n    end\n    if (mime == Symbol(\"image/svg+xml\")) && (t == \"svg\")\n        rv = true\n    end\n    if (mime == Symbol(\"text/html\")) && (t == \"svg\" || t == \"canvas\") && h\n        rv = true\n    end\n    @debug \"showable():\" mime config.term config.embedhtml t rv\n    return rv\nend\n\nfunction show(io::IO, figax::FigureAxis)\n    println(io, \"Gaston.Axis with $(length(figax.f.axes)) ax(es),\")\n    println(io, \"    stored in figure with handle \", figax.f.handle)\nend\n\nfunction show(io::IO, a::Axis)\n    p = length(a) == 1 ? \"plot\" : \"plots\"\n    println(io, \"Gaston.Axis ($(a.is3d ? \"3D\" : \"2D\")) with $(length(a.plots)) $p\")\nend\n\nfunction show(io::IO, p::Plot)\n    println(io, \"Gaston.Plot with plotline: \\\"$(p.plotline)\\\"\")\nend\n\nfunction internal_show(io::IO, f::Figure)\n\n    @debug \"internal_show()\" config.term config.output state.enabled\n\n    # handle cases where no output is produced\n    state.enabled || return nothing\n    config.output == :null && return nothing\n\n    # verify figure's gnuplot process is running\n    if !process_running(f.gp_proc)\n        error(\"gnuplot process associated with figure handle $(f.handle) has exited.\")\n    end\n\n    if isempty(f)\n        println(io, \"Empty Gaston.Figure with handle \", f.handle)\n        return nothing\n    end\n\n    # echo mode: save plot, read it and write to io\n    if config.output == :echo\n        @debug \"Notebook plotting\" config.term config.embedhtml\n        tmpfile = tempname()\n        producefigure(f, filename = tmpfile, term = config.term)\n        while !isfile(tmpfile) end  # avoid race condition with read in next line\n        if config.embedhtml\n            println(io, \"<html><body>\")\n        end\n        write(io, read(tmpfile))\n        if config.embedhtml\n            println(io, \"</body></html>\")\n        end\n        rm(tmpfile, force=true)\n    # external mode: create graphical window\n    elseif config.output == :external\n        producefigure(f)\n    end\n\n    return nothing\nend\n\nfunction producefigure(f::Figure ; filename::String = \"\", term = config.term)\n    iob = IOBuffer()\n    # Determine which terminal to use. Precedence order is:\n    # * `config.altterm` if `config.alttoggle` (highest)\n    # * function kw argument `term`\n    # * default global value `config.term` (lowest)\n    if config.alttoggle\n        term = config.altterm\n        config.alttoggle = false\n    end\n    if term isa Symbol\n        term = String(term)\n    end\n    animterm = contains(term, \"animate\")  # this is an animation, not a multiplot\n\n    # auto-calculate multiplot layout if mp_auto is true\n    # note: here I'm trying to be clever, there may be undiscovered edge cases\n    autolayout = \"\"\n    if f.autolayout\n        if length(f) <= 2\n            rows = 1\n            cols = length(f)\n        elseif length(f) <= 4\n            rows = 2\n            cols = 2\n        else\n            cols = ceil(Int, sqrt(length(f)))\n            rows = ceil(Int, length(f)/cols)\n        end\n        autolayout = \" layout $rows, $cols \"\n    end\n\n    write(iob, \"reset session\\n\")\n\n    # if term is different than config.term, push it, and pop it after plotting is done\n    term != config.term && write(iob, \"set term push\\n\")\n    term != \"\" && write(iob, \"set term $(term)\\n\")\n\n    # if saving the plot\n    filename != \"\" && write(iob, \"set output '$(filename)'\\n\")\n\n    # handle multiplot\n    (length(f) > 1 && !animterm) && write(iob, \"set multiplot \" * autolayout * f.multiplot * \"\\n\")\n    for axis in f.axes\n        if isempty(axis)\n            if f.autolayout\n                write(iob, \"set multiplot next\\n\")\n            end\n            continue\n        else\n            write(iob, axis.settings*\"\\n\")\n            write(iob, plotstring(axis)*\"\\n\") # send plotline\n        end\n    end\n    (length(f) > 1 && !animterm) && write(iob, \"unset multiplot\\n\")\n    filename != \"\" && write(iob, \"set output\\n\")\n    term != config.term && write(iob, \"set term pop\\n\")\n    seekstart(iob)\n    gp_send(f, String(read(iob)))\nend\n\nBase.show(io::IO, ::MIME\"text/plain\", x::Figure) = internal_show(io, x)\nBase.show(io::IO, ::MIME\"text/html\", x::Figure) = internal_show(io, x)\nBase.show(io::IO, ::MIME\"image/png\", x::Figure) = internal_show(io, x)\nBase.show(io::IO, ::MIME\"image/gif\", x::Figure) = internal_show(io, x)\nBase.show(io::IO, ::MIME\"image/webp\", x::Figure) = internal_show(io, x)\nBase.show(io::IO, ::MIME\"image/svg+xml\", x::Figure) = internal_show(io, x)\nBase.show(io::IO, x::Figure) = internal_show(io, x)\n\n# build a string with plot commands according to configuration\nfunction plotstring(a::Axis)\n    # We have to insert \",\" between plot commands.\n    pstring = Vector{String}()\n    for p in a.plots\n        push!(pstring, \" '$(p.datafile)' \" * p.plotline)\n    end\n    command = \"plot \"\n    a.is3d && (command = \"splot \")\n    command * join(pstring, \", \")\nend\n\n# Calculating palettes is expensive, so store them in a cache. The cache is\n# pre-populated with gnuplot's gray palette. The key is `(name, rev)`, where\n# `name` is the palette name and `rev` is true if the palette is reversed.\nPalette_cache = Dict{Tuple{Symbol, Bool}, String}((:gray, false) => \"set palette gray\")\n\n# Calculating linetypes from a colorscheme is expensive, so we use a cache.\nLinetypes_cache = Dict{Symbol, String}()\n\n# Convert a symbol to string, converting all '_' to spaces and surrounding it with ' '\nfunction symtostr(s)\n    s isa Symbol || return s\n    s = string(s)\n    return \"'$(join(split(s,\"_\"),\" \"))'\"\nend\n\nfunction strstr(s)\n    s isa String || return s\n    return \"'$s'\"\nend\n\nparse_settings(x) = x\n\nfunction parse_settings(s::Vector{<:Pair})::String\n    @debug \"parse_settings\" s\n    # Initialize string that will be returned\n    settings = String[]\n    for (key::String, v) in s\n        if v isa Bool\n            v && push!(settings, \"set $key\")\n            v || push!(settings, \"unset $key\")\n        elseif key ∈ (\"xtics\", \"ytics\", \"ztics\", \"tics\")\n            # tics\n            if v isa AbstractRange\n                push!(settings,\"set $key $(first(v)),$(step(v)),$(last(v))\")\n            elseif v isa Tuple\n                push!(settings, \"set $key $v\")\n            elseif v isa NamedTuple\n                ticstr = \"(\"\n                for i in eachindex(v.positions)\n                    ticstr *= string(\"'\", v.labels[i], \"' \", v.positions[i], \", \")\n                end\n                ticstr *= \")\"\n                push!(settings,\"set $key $ticstr\")\n            else\n                push!(settings,\"set $key $v\")\n            end\n        elseif key ∈ (\"xrange\", \"yrange\", \"zrange\", \"cbrange\")\n            # ranges\n            if v isa Vector || v isa Tuple\n                push!(settings, \"set $key [$(ifelse(isinf(v[1]),*,v[1])):$(ifelse(isinf(v[2]),*,v[2]))]\")\n            else\n                push!(settings, \"set $key $v\")\n            end\n        elseif key == \"ranges\"\n            if v isa Vector || v isa Tuple\n                r = \"[$(ifelse(isinf(v[1]),*,v[1])):$(ifelse(isinf(v[2]),*,v[2]))]\"\n                push!(settings, \"set xrange $r\\nset yrange $r\\nset zrange $r\\nset cbrange $r\")\n            else\n                push!(settings, \"set xrange $v\\nset yrange $v\\nset zrange $v\\nset cbrange $v\")\n            end\n        elseif key ∈ (\"pal\", \"palette\")\n            # palette; code inspired by @gcalderone's Gnuplot.jl\n            rev = false\n            if v isa Tuple\n                if v[2] == :reverse\n                    rev = true\n                end\n                v = v[1]\n            end\n            if v isa ColorScheme || v isa Symbol\n                if v isa Symbol\n                    if haskey(Palette_cache, (v, rev))\n                        push!(settings, Palette_cache[(v, rev)])\n                        continue\n                    else\n                        cm = colorschemes[v]\n                    end\n                else\n                    cm = v\n                end\n                colors = String[]\n                if rev\n                    r = range(1, 0, length(cm))\n                else\n                    r = range(0, 1, length(cm))\n                end\n                for i in 1:length(cm)\n                    c = get(cm, r[i])\n                    push!(colors, \"$i $(c.r) $(c.g) $(c.b)\")\n                end\n                cols = join(colors, \", \")\n                pal = \"set palette defined (\" * cols * \")\\nset palette maxcolors $(length(cm))\"\n                if v isa Symbol\n                    push!(Palette_cache, ((v, rev) => pal))\n                end\n                push!(settings, pal)\n            else\n                push!(settings, \"set palette $v\")\n            end\n        elseif key == \"view\"\n            # view\n            if v isa Tuple\n                push!(settings, \"set view $(join(v, \", \"))\")\n            else\n                push!(settings, \"set view $v\")\n            end\n        elseif key ∈ (\"lt\", \"linetype\")\n            # linetype definitions; code inspired by @gcalderone's Gnuplot.jl\n            if v isa Symbol\n                if haskey(Linetypes_cache, v)\n                    push!(settings, Linetypes_cache[v])\n                else\n                    cm = colorschemes[v]\n                    linetypes = String[]\n                    for i in 1:length(cm)\n                        c = cm[i]\n                        s = join(string.( round.(Int, 255 .*(c.r, c.g, c.b)), base=16, pad=2))\n                        push!(linetypes, \"set lt $i lc rgb '#$s'\")\n                    end\n                    s = join(linetypes,\"\\n\")*\"\\nset linetype cycle $(length(cm))\"\n                    push!(Linetypes_cache, (v => s))\n                    push!(settings, s)\n                end\n            else\n                push!(settings, \"set linetype $v\")\n            end\n        elseif key == \"margins\" && v isa Tuple\n            # margin definitions using at screen: left, right, bottom top\n            push!(settings, \"\"\"set lmargin at screen $(v[1])\n                  set rmargin at screen $(v[2])\n                  set bmargin at screen $(v[3])\n                  set tmargin at screen $(v[4])\"\"\")\n        else\n            push!(settings, \"set $key $v\")\n        end\n    end\n    return join(settings, \"\\n\")\nend\n\n# parse plot configuration\nparse_plotline(x) = x\n\nfunction parse_plotline(pl::Vector{<:Pair})::String\n    @debug \"parse_plotline()\" pl\n    # Initialize string that will be returned\n    plotline = String[]\n    count::Int = 0\n    for (k, v) in pl\n        # ensure that the same key does not appear later\n        flag = true\n        count += 1\n        for z in pl[(count+1):end]\n            if k == z[1]\n                flag = false\n                break\n            end\n        end\n        if flag\n            if v == true\n                push!(plotline, k)\n            else\n                if k == \"marker\" || k == \"pointtype\" || k == \"pt\"\n                    k = \"pointtype\"\n                    if v isa Symbol\n                        v = get(pointtypes, v, \"'$v'\")\n                    end\n                    @debug k v\n                elseif k == \"plotstyle\"\n                    k = \"with\"\n                elseif k == \"markersize\" || k == \"ms\"\n                    k = \"pointsize\"\n                elseif k == \"legend\"\n                    k = \"title\"\n                end\n                push!(plotline, k*\" \"*string(v))\n            end\n        end\n    end\n    return join(plotline, \" \")\nend\n\n\"\"\"\n    cs2dec(cs::ColorScheme)\n\nConvert a colorsheme to a vector of integers, where each number corresponds to\neach color in `cs`, expressed in base 10. Meant to be used with `lc rgb variable`.\n\nThis function accepts color schemes made up of `RGB` or `RGBA` values.\n\"\"\"\nfunction cs2dec(cs::ColorScheme)\n    colors = cs.colors\n    s = Int[]\n    str(c) = string(c, base = 16, pad = 2)\n    if colors[1] isa RGB\n        # no alpha\n        for c in colors\n            push!(s, parse(Int, string(str(round(Int, 255*c.r)) *\n                                       str(round(Int, 255*c.g)) *\n                                       str(round(Int, 255*c.b))), base = 16))\n        end\n    else\n        # alpha\n        for c in colors\n            push!(s, parse(Int, string(str(round(Int, 255*c.alpha)) *\n                                       str(round(Int, 255*c.r)) *\n                                       str(round(Int, 255*c.g)) *\n                                       str(round(Int, 255*c.b))), base = 16))\n        end\n    end\n    s\nend\n\n# Define pointtype synonyms\npointtypes = (dot      = 0,\n              ⋅        = 0,\n              +        = 1,\n              plus     = 1,\n              x        = 2,\n              *        = 3,\n              star     = 3,\n              esquare  = 4,\n              fsquare  = 5,\n              ecircle  = 6,\n              fcircle  = 7,\n              etrianup = 8,\n              ftrianup = 9,\n              etriandn = 10,\n              ftriandn = 11,\n              edmd     = 12,\n              fdmd     = 13,\n             )\n"
  },
  {
    "path": "src/gaston_builtinthemes.jl",
    "content": "# Themes in use are stored in this dictionary.\n# Each theme is identified by a name.\n\n# settings themes\nsthemes = @gpkw Dict(\n    :none =>       Pair[],\n\n    :notics =>      {tics = false},\n    :labels =>      {xlabel = \"'x'\", ylabel = \"'y'\", zlabel = \"'z'\"},\n    :unitranges =>  {xrange = (-1,1), yrange = (-1,1), zrange = (-1,1)},\n    :nocb =>        {colorbox = false},\n    :boxplot =>     {boxwidth = \"0.8 relative\", style = \"fill solid 0.5\"},\n    :histplot =>    {boxwidth = \"0.8 relative\", style = \"fill solid 0.5\", yrange = \"[0:*]\"},\n    :imagesc =>     {yrange = \"reverse\"},\n    :hidden3d =>    {hidden3d},\n    :wiresurf =>    {hidden3d, pm3d = \"implicit depthorder border lc 'black' lw 0.3\"},\n    :heatmap =>     {view = \"map\"},\n    :contour =>     {key       =  false,\n                     view      = \"map\",\n                     contour   = \"base\",\n                     surface   = false,\n                     cntrlabel = \"font ',7'\",\n                     cntrparam = \"levels auto 10\",\n                    },\n    :contourproj => {hidden3d,\n                     key       = false,\n                     contour   = \"base\",\n                     cntrlabel = \"font ',7'\",\n                     cntrparam = \"levels auto 10\"\n                    },\n    :scatter3 =>    {border = \"4095\",\n                     grid = \"xtics ytics ztics vertical\",\n                     xyplane = \"relative 0.05\"\n                    },\n)\n\n# plotline themes\npthemes = @gpkw Dict(\n    :none =>        Pair[],\n    :scatter =>     {with = \"points\"},\n    :impulses =>    {with = \"impulses\"},\n    :stem =>        {with = \"points\", pointtype = :ecircle},\n    :box =>         {with = \"boxes\"},\n    :boxerror =>    {with = \"boxerrorbars\"},\n    :boxxyerror =>  {with = \"boxxyerror\"},\n    :image =>       {with = \"image\"},\n    :rgbimage =>    {with = \"rgbimage\"},\n    :horhist =>     {u = \"2:1:(0):2:(\\$0-0.5):(\\$0+0.5)\", with = \"boxxyerror\"},\n    :pm3d =>        {with = \"pm3d\"},\n    :labels =>      {with = \"labels\"},\n)\n"
  },
  {
    "path": "src/gaston_figures.jl",
    "content": "## Copyright (c) 2013-2021 Miguel Bazdresch\n##\n## This file is distributed under the 2-clause BSD License.\n\n# Code related to figures.\n\n\"\"\"\n    Gaston.Plot(datafile::String, plotline::String)\n\nType that stores the data needed to plot a curve.\n\n    Gaston.Plot(args..., plotline = \"\")\n\nConstruct a new Plot. The curve data (for instance, `x` and `y` coordinates) are\nprovided first. The curve plotline is the last argument.\n\"\"\"\nmutable struct Plot\n    const datafile :: String\n    plotline       :: String\n\n    function Plot(args...)\n        if isempty(args) || all(i -> isa(i, Union{String, Vector{<:Pair}}), args)\n            throw(ArgumentError(\"No plot data provided.\"))\n        end\n        if args[end] isa String\n            plotline = args[end]\n            args = args[1:end-1]\n        elseif args[end] isa Vector{<:Pair}\n            plotline = parse_plotline(args[end])\n            args = args[1:end-1]\n        else\n            plotline = \"\"\n        end\n        datafile = tempname()\n        writedata(datafile, args...)\n        new(datafile, plotline)\n    end\nend\n\n\"\"\"\n    Gaston.Plot!(P, args...)::Nothing\n\nUpdate existing plot `P` with provided `args`.\n\nThe existing data file is overwritten; no new data file is created. A quick\nbenchmark shows that this has no advantage over creating new Plots; however,\nin some cases avoiding the creation of lots of small files might be desired.\n\"\"\"\nfunction Plot!(P::Plot, args...)\n    if isempty(args)\n        return P\n    end\n    if args[end] isa String\n        plotline = args[end]\n        args = args[1:end-1]\n    elseif args[end] isa Vector{<:Pair}\n        plotline = parse_plotline(args[end])\n        args = args[1:end-1]\n    else\n        plotline = P.plotline\n    end\n    datafile = P.datafile\n    writedata(datafile, args...)\n    P.plotline = plotline\n    return nothing\nend\n\n\"\"\"\n    Axis(settings::String           = \"\",\n         plots::Vector{Gaston.Plot} = Gaston.Plot[],\n         is3d::Bool                 = false)\n\nType that stores the data required to create a 2-D or 3-D axis.\n\nThe constructor takes the following arguments:\n\n* `settings`: stores the axis settings.\n* `plots`: a vector of Plot, one per curve.\n* `is3d`: determines if axis is generated with `plot` or `splot`.\n\"\"\"\nmutable struct Axis\n    settings :: String         # axis settings\n    plots    :: Vector{Plot}   # one Plot per curve in the axis\n    is3d     :: Bool           # if true, 'splot' is used; otherwise, 'plot' is used\nend\n\n# Axis constructors\nAxis(x, y) = Axis(x, y, false)\nAxis3(x, y) = Axis(x, y, true)\n\nAxis() = Axis(\"\", Plot[])\nAxis3() = Axis3(\"\", Plot[])\n\nAxis(p::Plot) = Axis(\"\", [p])\nAxis3(p::Plot) = Axis3(\"\", [p])\n\nAxis(s::String) = Axis(s, Plot[])\nAxis3(s::String) = Axis3(s, Plot[])\n\nAxis(s::String, p::Plot) = Axis(s, [p])\nAxis3(s::String, p::Plot) = Axis3(s, [p])\n\nAxis(s::Vector{T}) where T <: Pair = Axis(parse_settings(s), Plot[])\nAxis3(s::Vector{T}) where T <: Pair = Axis3(parse_settings(s), Plot[])\n\nAxis(s::Vector{T}, p::Plot) where T <: Pair = Axis(parse_settings(s), [p])\nAxis3(s::Vector{T}, p::Plot) where T <: Pair = Axis3(parse_settings(s), [p])\n\nAxis(s::Vector{T}, p::Vector{Plot}) where T <: Pair = Axis(parse_settings(s), p)\nAxis3(s::Vector{T}, p::Vector{Plot}) where T <: Pair = Axis3(parse_settings(s), p)\n\n# Figures\n\n\"\"\"\n    Figure\n\nType that stores a figure. It has the following fields:\n\n* `handle`: the figure's unique identifier (it may be of any type).\n* `gp_proc`: the gnuplot process associated with the figure (type `Base.Process`).\n* `axes`: a vector of `Gaston.Axis`.\n* `multiplot`: a string with arguments to `set multiplot`.\n* `autolayout`: `true` if Gaston should handle the figure's layout (`Bool`).\n\"\"\"\nmutable struct Figure <: AbstractFigure\n    handle\n    gp_proc    :: Base.Process\n    axes       :: Vector{Axis}\n    multiplot  :: String\n    autolayout :: Bool\nend\n\n\"\"\"\n    Figure(h = nothing, autolayout = true, multiplot = \"\")::Figure\n\nReturn an empty a figure with given handle. If `h === nothing`, automatically\nassign the next available numerical handle. A new gnuplot process is started\nand associated with the new figure, which becomes the active figure.\n\nIf the handle provided already exists, an error is thrown.\n\n# Examples\n\n```{.julia}\nfig = Figure()   # new figure with next available numerical handle\nfig = Figure(5)  # new figure with handle 5 (if it was available)\nfig = Figure(multiplot = \"'title 'test'\") # new figure with multiplot settings\n```\n\"\"\"\nfunction Figure(handle = nothing ; autolayout = true, multiplot = \"\")\n    global state\n    handle === nothing && (handle = nexthandle())\n    if handle ∈ gethandles()\n        error(\"Figure with given handle already exists. Handle: \", handle)\n    else\n        f = finalizer(finalize_figure, Figure(handle, gp_start(), Axis[], multiplot, autolayout))\n        push!(state.figures.figs, f)\n        state.activefig = handle\n    end\n    @debug \"Returning figure with handle: \" handle\n    return f\nend\n\n\"\"\"\n    Gaston.FigureAxis\n\nWhen indexing a figure, a FigureAxis is returned. It contains the figure\nitself along with the index.\n\nThis is required because `plot(figure[index])` modifies the axis at\n`figure.axis[index]`, but it must return `figure`.\n\"\"\"\nstruct FigureAxis\n    f   :: Figure\n    idx :: Int\nend\n\n\"\"\"\n    (f::Figure)(index)::Gaston.Axis\n\nReturn the axis stored at the specified index. If the axis does not exist, an\nempty axis is created.\n\"\"\"\nfunction (f::Figure)(idx)::Axis\n    ensure(f.axes, idx)\n    return f.axes[idx]\nend\n\nfunction finalize_figure(f::Figure)\n    #@async @info \"Finalizing figure with handle $(f.handle).\"\n    for i in eachindex(f.axes)\n        empty!(f(i))\n    end\n    @async gp_quit(f)\nend\n\n# functions to push stuff into figures/axis/plots\n\n\"\"\"\n    push!(a::Axis, p::Plot)\n\nPush plot (curve) `p` into axis `a`.\n\"\"\"\nfunction push!(a::Axis, p::Plot)\n    push!(a.plots, p)\n    return a\nend\n\n\"\"\"\n    push!(f::Figure, a::Axis)\n\nPush axis `a` into figure `f`.\n\"\"\"\nfunction push!(f::Figure, a::Axis)\n    push!(f.axes, a)\n    return f\nend\n\n\"\"\"\n    push!(f::FigureAxis, p::Plot)\n\nPush plot (curve) `p` into the indexed axis of figure `f`.\n\"\"\"\nfunction push!(fa::FigureAxis, p::Plot)\n    a = fa.f.axes[fa.idx]\n    push!(a, p)\n    return fa.f\nend\n\n\"\"\"\n    push!(f1::Figure, f2::Figure)::Figure\n\nInsert the first axis of f2 into f1.\n\n# Example\n```julia\nf1 = plot(sin)\nf2 = Figure()\nhistogram(randn(100), bins = 10)  # plots on f2\npush!(f1, f2)  # insert the histogram as second axis of f1\n```\n\"\"\"\nfunction push!(f1::Figure, f2::Figure)::Figure\n    push!(f1, f2(1))\nend\n\n\"\"\"\n    push!(f1::Figure, an::FigureAxis)::Figure\n\nInsert the axis in `an` into `f`.\n\n# Example\n\n``` julia\nf1 = plot(sin)\nf2 = Figure()\nplot(f2, cos)\nplot(f2[2], tan)\npush!(f1, f2[2]) # insert the plot of tan into f1\n\"\"\"\nfunction push!(f1::Figure, f2::FigureAxis)::Figure\n    push!(f1.axes, f2.f.axes[f2.idx])\n    return f1\nend\n\n\"\"\"\n    set!(a::Gaston.Axis, s)\n    set!(f::Gaston.FigureAxis, s)\n\nSet the settings of the axis or indexed figure. `s` can be a string or a\nvector of pairs.\n\"\"\"\nfunction set!(a::Axis, s::S)::Axis where {S <: AbstractString}\n    a.settings = s\n    return a\nend\n\nfunction set!(fa::FigureAxis, s::S)::Figure where {S <: AbstractString}\n    set!(fa.f.axes[fa.idx], s)\n    return fa.f\nend\n\nfunction set!(a::Axis, s::Vector{<:Pair})::Axis\n    a.settings = parse_settings(s)\n    return a\nend\n\nfunction set!(fa::FigureAxis, s::Vector{<:Pair})::Figure\n    set!(fa.f.axes[fa.idx], s)\n    return fa.f\nend\n\n## Indexing into figures/axes\n# It is possible to set/get plots and axes using indexing.\n# Important: accessing a non-assigned location in a figure will create that location (like in Makie).\n# Notation, assuming `f::Figure`, `a::Axis`, and `p::Plot`.\n# `f[3]`        # `f.axes[3]`, creating it (and `f[1]` and `f[2]` as well) if necessary\n# `f[3] = a`    # `f.axes[3] = a`, setting `f[1]` and `f[2]` to `Axis()` if necessary\n# `f[i,j] = p`  # Replace `f.axes[i].plots[j]` with `p`. `f.axes[i]` will be created if necessary.\n# `f[i] = p`    # Replace `f.axes[1].plots[i]` with `p`.\n# `f[] = p`     # Replace `f.axes[1].plots[1]` with `p`.\n\n\"\"\"\n    getindex(f::Figure, index)::Gaston.FigureAxis\n\nReturn `Gaston.FigureAxis(f, index)`. If the axis at the specified index\ndoes not exist, one is created.\n\nTo obtain (or create) an axis, use `f(index)`.\n\n# Example\n```julia\nf1 = Figure()\nplot(f1[3], sin) # Figure f1 contains three axes: the first two are empty,\n                 # and the third one contains a sine wave.\n```\n\"\"\"\nfunction getindex(f::Figure, idx)::FigureAxis\n    ensure(f.axes, idx)\n    return FigureAxis(f, idx)\nend\n\ngetindex(a::Axis, idx)::Plot = a.plots[idx]\n\n# Index into a Plot\nfunction (f::Figure)(idx1, idx2)::Plot\n    return f.axes[idx1][idx2]\nend\n\n# Replace an axis\nfunction setindex!(f::Figure, a::Axis, idx)\n    ensure(f.axes, idx)\n    f.axes[idx] = a\nend\n\n# Replace a plot. Plot must already exist. (see `push!` to insert plots).\nfunction setindex!(f::Figure, p::Plot, idx...)\n    if isempty(idx)\n        plotidx, axisidx = 1, 1\n    elseif length(idx) == 1\n        plotidx, axisidx = idx[1], 1\n    else\n        plotidx, axisidx = idx\n    end\n    f[axisidx].a[plotidx] = p\nend\n\n# Replace a plot in an axis.\nfunction setindex!(a::Axis, p::Plot, idx)\n    a.plots[idx] = p\n    return a\nend\n\n# utility functions\n\nisempty(f::Figure) = all(isempty(a) for a in f.axes)\nisempty(a::Axis) = isempty(a.plots)\n\nlength(f::Figure) = length(f.axes)\nlength(a::Axis) = length(a.plots)\n\nfunction empty!(a::Axis)\n    a.settings = \"\"\n    a.plots = Plot[]\n    a\nend\n\nfunction ensure(v::Vector{T}, idx) where T <: Union{Axis, Plot}\n    if !isassigned(v, idx)\n        for i in (length(v)+1):idx\n            push!(v, T())\n        end\n    end\nend\n\n\"\"\"\n    figure(handle = <active figure handle> ; index = nothing)::Figure\n\nReturn specified figure (by handle or index) and make it the active\nfigure. If no figures exist, then a new figure is returned.\n\nIf no arguments are given, the current active figure is returned.\n\"\"\"\nfunction figure(handle = state.activefig ; index = nothing)::Figure\n    global state\n    if isnothing(handle) && isnothing(index)\n        return Figure()\n    end\n    if isnothing(index)\n        for fig in state.figures.figs\n            if fig.handle == handle\n                state.activefig = handle\n                return fig\n            end\n        end\n        error(\"No figure with handle: \", handle)\n    end\n    if isassigned(state.figures.figs, index)\n        fig = state.figures.figs[index]\n        state.activefig = fig.handle\n        return fig\n    else\n        error(\"No figure stored in index: \" , index)\n    end\nend\n\n\"\"\"\n    Gaston.listfigures()\n\nDisplay a list of all existing figures.\n\"\"\"\nfunction listfigures(io::IO = stdin)\n    L = length(state.figures.figs)\n    if L == 0\n        println(io, \"Currently managing no figures.\")\n    elseif L == 1\n        println(io, \"Currently managing 1 figure:\")\n    else\n        println(io, \"Currently managing $L figures:\")\n    end\n    for idx in 1:length(state.figures.figs)\n        h = state.figures.figs[idx].handle\n        s = \"Figure with index: $idx and handle: $h\"\n        if h == state.activefig\n            s = \"  (Active) \"*s\n        else\n            s = \"  \"*s\n        end\n        println(io, s)\n    end\nend\n\n\"\"\"\n    reset!(f::Figure)\n\nReset figure `f` to its initial state, without restarting its associated\ngnuplot process.\n\"\"\"\nfunction reset!(f::Figure)\n    f.axes = Axis[]\n    f.multiplot = \"\"\n    f.autolayout = true\nend\n\n\"\"\"\n    closefigure(h = nothing)::Nothing\n\nClose figure with handle `h`. If no arguments are given, the active figure is\nclosed. The most recent remaining figure (if any) is made active.\n\nThe associated gnuplot process is also terminated.\n\n# Examples\n\n```{.julia}\nplot(sin, handle = :first);\nplot(cos, handle = :second);\nplot(tan, handle = :third);\nclosefigure()        # close figure with handle `:third`\nclosefigure(:first)  # close figure with handle `:first`\nclosefigure()        # close figure with handle `:second`\n```\n\"\"\"\nfunction closefigure(handle = nothing)\n    handle === nothing && (handle = state.activefig)\n    if handle ∉ gethandles()\n        error(\"Attempted to close figure with non-existing handle \", handle)\n    end\n    closefigure(figure(handle))\n    nothing\nend\n\n\"\"\"\n    closefigure(fig::Figure)::Nothing\n\nCloses the specified figure. The associated gnuplot process is also terminated.\n\n# Example\n\n```{.julia}\np = plot(1:10);\nclosefigure(p)\n```\n\"\"\"\nfunction closefigure(fig::Figure)\n    @debug \"closefigure(): closing figure with handle: \" fig.handle\n    #gp_quit(fig)\n    finalize(fig)\n    deleteat!(state.figures.figs, getidx(fig))\n    state.activefig = isempty(state.figures.figs) ? nothing : state.figures.figs[end].handle\n    nothing\nend\n\n\"\"\"\n    closeall()::Nothing\n\nClose all existing figures.\n\"\"\"\nfunction closeall()\n    while true\n        if isempty(state.figures.figs)\n            break\n        end\n        closefigure(state.figures.figs[end])\n    end\n    nothing\nend\n\n\"\"\"\n    Gaston.nexthandle()::Int\n\nReturn the next available handle (smallest not-yet-used positive integer).\n\"\"\"\nfunction nexthandle()\n    isempty(state.figures.figs) && return 1\n    handles = filter(t->isa(t, Int), gethandles())  # remove non-integer handles\n    mh = maximum(handles, init=1)  # largest handle, or 1 if handles is empty\n    if mh <= 0\n        return 1\n    else\n        for i = 1:mh+1\n            i ∉ handles && return i\n        end\n    end\nend\n\n\"\"\"\n    Gaston.gethandles()::Vector{Any}\n\nReturn a vector with the handles of all existing figures.\n\"\"\"\ngethandles() = [figure.handle for figure in state.figures.figs]\n\n\"\"\"\n    Gaston.getidx(fig::Figure)\n\nReturn the index (in Gaston's internal state) of given figure\n\"\"\"\nfunction getidx(fig::Figure)\n    h = fig.handle\n    idx = 1\n    for f in state.figures.figs\n        h == f.handle && return idx\n        idx += 1\n    end\nend\n"
  },
  {
    "path": "src/gaston_llplot.jl",
    "content": "## Copyright (c) 2013 Miguel Bazdresch\n##\n## This file is distributed under the 2-clause BSD License.\n\n# Write plot data to a file.\n# \n# Valid arguments:\n#\n# Case 1: n×1...\n# In this format, there are N n×1 vectors. Data is written as one block,\n# with N coordinates per line.\n#\n# Case 2: n×1 m×1 n×m...\n# In this format, there are N n×m matrices. Data is written as n blocks,\n# each block made up of triplets, with the first coordinate constant in\n# each block.\n#\nfunction writedata(file, args... ; append=false)\n    @debug \"writedata()\" size.(args)\n    @debug \"writedata()\" args\n    mode = \"w\"\n    append && (mode = \"a\")\n    nl = \"\\n\"\n    iob = IOBuffer()\n    # Case 1: all args are 1-D: mx1 mx1 mx1...\n    if all(ndims.(args) .== 1)\n        if minimum(length.(args)) == maximum(length.(args))\n            d = hcat(args...)\n            writedlm(iob, d)\n            write(iob, nl*nl)\n        else\n            error(\"Incompatible vector lengths\")\n        end\n    # Case 2: n×1 m×1 n×m...\n    elseif (length(args) >= 3) && (ndims(args[1]) == 1) && (ndims(args[2]) == 1) && (ndims(args[3]) == 2)\n        n::Int = size(args[2])[1]\n        m::Int = size(args[1])[1]\n        al::Int = length(args)\n        if size(args[3]) == (n, m)\n            block = zeros(n, al)\n            for xi in eachindex(args[1])\n                block[:,1] .= args[1][xi]\n                block[:,2] .= args[2]\n                for k in 3:al\n                    block[:,k] .= args[k][:,xi]\n                end\n                writedlm(iob, block)\n                write(iob, nl)\n            end\n            write(iob, nl*nl)\n        else\n            @debug \"writedata()\" n m size(args[3])\n            error(\"Incompatible array lengths\")\n        end\n    # Case 3: n×m n×m n×m...\n    elseif (ndims(args[1]) == 2) && (ndims(args[2]) == 2) && (ndims(args[3]) == 2)\n        (n, m) = size(args[1])\n        al = length(args)\n        if (size(args[2]) == (n, m)) || size(args[3] == (n, m))\n            block = zeros(n, al)\n            for xi in axes(args[1], 2)\n                block[:,1] .= args[1][:,xi]\n                block[:,2] .= args[2][:,xi]\n                block[:,3] .= args[3][:,xi]\n                for k in 4:al\n                    block[:,k] .= args[k][:,xi]\n                end\n                writedlm(iob, block)\n                write(iob, nl)\n            end\n        else\n            @debug \"writedata()\" n m size(args[3])\n            error(\"Incompatible array lengths\")\n        end\n    end\n    open(file, mode, lock = false) do io\n        seekstart(iob)\n        write(io, iob)\n    end\n    nothing\nend\n\n\"Handle data stored in a DataBlock\"\nfunction writedata(file, table::DataBlock)\n    seekstart(table.data)\n    write(file, table.data)\nend\n"
  },
  {
    "path": "src/gaston_options.jl",
    "content": "# Macro and functions to handle options in brackets\n\n\"\"\"\n    @Q_str\n\nInserts single quotation marks around a string.\n\nWhen passing options to gnuplot, some arguments should be quoted and some should\nnot. For example:\n\n* `set title Example    # gnuplot errors`\n* `set title 'Example'  # the title must be quoted`\n* `set pointtype 7      # the point type is not quoted`\n\nGaston allows setting options using keyword arguments:\n\n```julia\n@plot {title = \"Example\"} x y  # converted to \"set title Example\"\n```\n\nHere, the keyword argument should be `{title = \"'Example'\"}`, which is correctly\nconverted to `set title 'Example'`. To avoid having to type the single quotes,\nthis macro allows us to write:\n\n```julia\n@plot {title = Q\"Example\"} x y  # converted to \"set title 'Example'\"\n```\n\"\"\"\nmacro Q_str(s)\n    \"'$s'\"\nend\n\n\"\"\"\n    @plot args...\n\n@plot provides an alternative syntax for plotting. The arguments are interpreted\nsimilarly to `plot`: first, a figure or axis may be specified; then, data is\nprovided, and finally a plotline may be given. This macro allows specifying\ngnuplot settings as `setting = value`, which is converted to `set setting value`\nbefore passing it to gnuplot. These key, value pairs must be surrounded by\ncurly brackets.\n\n# Examples\n\n```{.julia}\n# Plot a sine wave with title `example` and with a grid, with a red line\n@plot {title = \"'example'\", grid = true} sin {lc = 'red'}\n```\n\nIn this example, `grid = true` is converted to `set grid`. To disable a\nsetting, use (for example) `grid = false` (converted to `unset grid`).\n\"\"\"\nmacro plot(ex...)\n    args = []\n    kwargs = Pair{Symbol,Any}[]\n    for el in ex\n        Meta.isexpr(el, :(=)) ? push!(kwargs, Pair(el.args...)) : push!(args, el)\n    end\n    args2 = (esc(procopts(v)) for v in args)\n    :( plot($(args2...) ; $kwargs...) )\nend\n\n\"\"\"\n    @plot! args...\n\nAlternative Convenient syntax for `plot!`. See the documentation for `@plot`.\n\"\"\"\nmacro plot!(ex...)\n    args = []\n    kwargs = Pair{Symbol,Any}[]\n    for el in ex\n        Meta.isexpr(el, :(=)) ? push!(kwargs, Pair(el.args...)) : push!(args, el)\n    end\n    args2 = (esc(procopts(v)) for v in args)\n    :( plot!($(args2...) ; $kwargs...) )\nend\n\n\"\"\"\n    @splot args...\n\nAlternative Convenient syntax for `splot`. See the documentation for `@plot`.\n\"\"\"\nmacro splot(ex...)\n    args = []\n    kwargs = Pair{Symbol,Any}[]\n    for el in ex\n        Meta.isexpr(el, :(=)) ? push!(kwargs, Pair(el.args...)) : push!(args, el)\n    end\n    args2 = (esc(procopts(v)) for v in args)\n    :( splot($(args2...) ; $kwargs...) )\nend\n\n\"\"\"\n    @splot! args...\n\nAlternative Convenient syntax for `splot!`. See the documentation for `@plot`.\n\"\"\"\nmacro splot!(ex...)\n    args = []\n    kwargs = Pair{Symbol,Any}[]\n    for el in ex\n        Meta.isexpr(el, :(=)) ? push!(kwargs, Pair(el.args...)) : push!(args, el)\n    end\n    args2 = (esc(procopts(v)) for v in args)\n    :( splot!($(args2...) ; $kwargs...) )\nend\n"
  },
  {
    "path": "src/gaston_plot.jl",
    "content": "## Copyright (c) 2013 Miguel Bazdresch\n##\n## This file is distributed under the 2-clause BSD License.\n\n\"\"\"\n    plot([f::Figure,] [indexed figure,], [settings...,] data..., [plotline...,] [kwargs...])::Figure\n\nPlot the provided data, returning a figure.\n\nArguments (in order from left to right):\n* `f::Figure` (optional). If a figure `f` is given as argument, the figure is reset\n  (all previous axes are removed), and the new plot is created in the first axis of `f`.\n* An indexed figure (e.g. `f[3]`) (optional). The axis at the given index is cleared\n  (or created if it does not exist), and the plot is added to it.\n* Axis settings (optional, default `\"\"`). See documentation for details on how to\n  specify these settings.\n* The data to be plotted. Data may be provided as vectors, ranges, matrices,\n  functions, etcetera (see documentation).\n* A plotline (optional, default `\"\"`) specifiying the plot formatting. See\n  documentation for details on how to specify these settings.\n\nThe figure to use for plotting may also be specified using the keyword argument\n`handle`.  Other keyword arguments are passed to `convert_args`, documented\nunder [Recipes](reference.qmd#recipes).\n\n# Examples\n\n```{.julia}\nplot(1:10) # A simple plot\nplot(\"set title 'test'}, 1:10)  # Configure the axes\nplot(\"set title 'test'}, 1:10, \"w p\")  # Configure the axes and plotline\nplot(sin)  # plot the sine function from -10 to 10\nplot(0:0.01:1, sin) # plot the sine function at the given time instants\n```\n\nSee also `plot!` and `splot`.\n\"\"\"\nfunction plot(args... ;\n              handle           = state.activefig,\n              splot  :: Bool   = false,  # true if called by splot\n              stheme :: Symbol = :none,\n              ptheme :: Symbol = :none,\n              kwargs...\n             ) :: Figure\n    @debug args\n\n    ### 1. Determine figure and axis to use, and reset them as appropriate\n    # if no index is provided, the whole figure is reset and the first axis is used\n    (f, idx, args) = whichfigaxis(handle, args...)\n    if ismissing(idx)\n        reset!(f)\n        idx = 1\n    end\n    @debug args\n\n    ### 2. settings -- loop over all strings, symbols or Vector{Pairs} and join them\n    settings = Union{Vector{<:Pair},AbstractString}[sthemes[stheme]]\n    while true\n        if args[1] isa AbstractString || args[1] isa Vector{<:Pair}\n            push!(settings, args[1])\n            args = Base.tail(args)\n        elseif args[1] isa Symbol\n            push!(settings, sthemes[args[1]])\n            args = Base.tail(args)\n        else\n            break\n        end\n    end\n    @debug args\n\n    ### 3. plotline -- check arguments from last to first\n    plotline = Union{Vector{<:Pair}, AbstractString}[pthemes[ptheme]]\n    while true\n        if args[end] isa AbstractString || args[end] isa Vector{<:Pair}\n            insert!(plotline, 2, args[end])\n            args = Base.front(args)\n        elseif args[end] isa Symbol\n            insert!(plotline, 2, pthemes[args[end]])\n            args = Base.front(args)\n        else\n            break\n        end\n    end\n    @debug args\n\n    ### 4. Apply recipe to arguments, if one exists\n    if splot && applicable(convert_args3, args...)\n        po = convert_args3(args... ; kwargs...)\n    elseif !splot && applicable(convert_args, args...)\n        po = convert_args(args...; kwargs...)\n    else\n        try\n            # if there is no conversion function, try to parse data directly\n            po = Plot(args...)\n        catch\n            err = \"Gaston does not know how to plot this.\\n\" *\n                  \"The data provided has the following type(s):\\n\"\n            for i in eachindex(args)\n                err *= \"    argument $i of type $(typeof(args[i]))\\n\"\n            end\n            error(err)\n        end\n    end\n\n    ### 5. Build axis and place it in figure\n    if po isa Plot\n        ensure(f.axes, idx)\n        push!(plotline, po.plotline)\n        po.plotline = merge_plotline(plotline)\n        setts = merge_settings(settings)\n        f.axes[idx] = Axis(setts, [po], splot)\n    elseif po isa PlotRecipe\n        ensure(f.axes, idx)\n        push!(plotline, po.plotline)\n        pl = merge_plotline(plotline)\n        setts = merge_settings(settings)\n        f.axes[idx] = Axis(setts, [Plot(po.data..., pl)], splot)\n    elseif po isa AxisRecipe\n        push!(settings, po.settings)\n        setts = merge_settings(settings)\n        P = Plot[]\n        for p in po.plots\n            push!(P, Plot(p.data..., parse_plotline(p.plotline)))\n        end\n        a = Axis(setts, P, po.is3d)\n        if isempty(f)\n            push!(f, a)\n        else\n            f.axes[idx] = a\n        end\n    elseif po isa FigureRecipe\n        A = Axis[]\n        for a in po.axes\n            P = Plot[]\n            for p in a.plots\n                push!(P, Plot(p.data..., parse_plotline(p.plotline)))\n            end\n            push!(A, Axis(parse_settings(a.settings), P, a.is3d))\n        end\n        f.axes = A\n        f.multiplot = po.multiplot\n        f.autolayout = po.autolayout\n    end\n    return f\nend\n\n\"\"\"\n    plot(f1::Figure, f2::Figure,... ; multiplot = \"\", autolayout = false, kwargs...)::Figure\n\nReturn a new figure whose axes come from the figures provided in the arguments.\n\"\"\"\nfunction plot(fs::Figure...; multiplot = \"\", autolayout = true, kwargs...)\n    f = Figure(;multiplot, autolayout)\n    for fig in fs\n        for ax in fig.axes\n            push!(f, ax)\n        end\n    end\n    f\nend\n\n\"\"\"\n    plot!(...)::Figure\n\nSimilar to `plot`, but adds a new curve to an axis. If the axis does not exist, it\nis created. However, `plot!` does not support specification of the axis settings.\n\n# Examples\n\n```{.julia}\nplot(1:10)        # plot a curve\nplot!((1:10.^2))  # add a second curve\nf = plot(sin)     # store new plot in f\nplot!(f, cos)     # add second curve to plot\n```\n\nSee documentation to `plot` for more details.\n\"\"\"\nfunction plot!(args... ; splot = false, handle = state.activefig, ptheme = :none, kwargs...)::Figure\n    # determine figure and axis to use\n    (f, idx, args) = whichfigaxis(handle, args...)\n    if ismissing(idx)\n        idx = 1\n    end\n\n    # remove stray settings\n    while true\n        if args[1] isa AbstractString || args[1] isa Vector{<:Pair} || args[1] isa Symbol\n            args = Base.tail(args)\n        else\n            break\n        end\n    end\n\n    # parse plotline\n    plotline = Union{Vector{<:Pair}, AbstractString}[pthemes[ptheme]]\n    while true\n        if args[end] isa AbstractString || args[end] isa Vector{<:Pair}\n            insert!(plotline, 2, args[end])\n            args = Base.front(args)\n        elseif args[end] isa Symbol\n            insert!(plotline, 2, pthemes[args[end]])\n            args = Base.front(args)\n        else\n            break\n        end\n    end\n\n    # apply recipe if one exists\n    if splot && applicable(convert_args3, args...)\n        po = convert_args3(args... ; kwargs...)\n    elseif !splot && applicable(convert_args, args...)\n        po = convert_args(args...; kwargs...)\n    else\n        try\n            # if there is no conversion function, try to parse data directly\n            po = Plot(args...)\n        catch\n            err = \"Gaston does not know how to plot this.\\n\" *\n                  \"The data provided has the following type(s):\\n\"\n            for i in eachindex(args)\n                err *= \"    argument $i of type $(typeof(args[i]))\\n\"\n            end\n            error(err)\n        end\n    end\n\n    if po isa Plot\n        ensure(f.axes, idx)\n        # For flexibility, we want to allow splot! before any splot commands. We want to make sure\n        # that, in this case, the axis is set to 3D.\n        if splot\n            f.axes[idx].is3d = true\n        end\n        push!(plotline, po.plotline)\n        po.plotline = merge_plotline(plotline)\n        push!(f.axes[idx], po)\n    elseif po isa PlotRecipe\n        ensure(f.axes, idx)\n        splot && (f.axes[idx].is3d = true)\n        push!(plotline, po.plotline)\n        pl = merge_plotline(plotline)\n        push!(f.axes[idx], Plot(po.data..., pl))\n    else\n        error(\"Argument to plot! must be a single curve.\")\n    end\n\n    return f\nend\n\n\"\"\"\n    splot(...)::Figure\n\nSimilar to plot, but creates a 3D plot.\n\n# Example\n\n```{.julia}\nsplot(-1:0.1:1, -1:0.1:1, (x,y)->sin(x)*cos(y)) # Plot an equation in the specified range\n```\n\nSee documentation to `plot` for more details.\n\"\"\"\nsplot(args... ; kwargs...) = plot(args... ; splot = true, handle = state.activefig, kwargs...)\n\n\"\"\"\n    splot!(...) -> Figure\n\nSimilar to `splot`, but adds a new surface to an existing plot.\n\nSee documentation to `plot!` for more details.\n\"\"\"\nsplot!(args... ; kwargs...) = plot!(args... ; splot = true, handle = state.activefig, kwargs...)\n\n\"\"\"\n    plotwithtable(settings, args... ; splot = true)\n\nCreate and generate a table. 3D is assumed, so `splot` defaults to `true`.\n\"\"\"\nfunction plotwithtable(settings::AbstractString, args... ; splot = true)\n    if applicable(convert_args3, args...)\n        po = convert_args3(args...)\n        tmpf = tempname()\n        writedata(tmpf, po.data...)\n    elseif applicable(convert_args, args...)\n        po = convert_args(args...)\n        tmpf = tempname()\n        writedata(tmpf, po.data...)\n    else\n        tmpf = tempname()\n        writedata(tmpf, args...)\n    end\n    tblf = tempname()\n    cmd = splot ? \"splot\" : \"plot\"\n    s = \"set term unknown\\n\" * settings * \"\\nset table '$tblf'\\n\" * \"$cmd '$tmpf'\\n\" * \"unset table\\n\"\n    gp_exec(s)\n    table = readlines(tblf)\n    rm(tmpf)\n    rm(tblf)\n    return DataBlock(table)\nend\n\n\"\"\"\n    whichfigaxis(handle, args...)\n\nReturn a figure and an index into its axes.\n\nProvided arguments and return values may be:\n* f::Figure. Returns (f, missing, remaining args)\n* f::FigureAxis. Returns (f, index, remaining args)\n* else, returns:\n  * (f(handle), missing, remaining args) if f(handle) exists\n  * (Figure(handle), missing, remaining args) if it does not\n\"\"\"\nfunction whichfigaxis(handle, args...)\n    index_provided = false\n    if args[1] isa FigureAxis\n        # plot(fig[1], ...)\n        (; f, idx) = args[1]\n        args = Base.tail(args)\n    elseif args[1] isa Figure\n        # plot(fig, ...)\n        f = args[1]\n        idx = missing\n        args = Base.tail(args)\n    else\n        # neither a figure nor an axis were given as first argument\n        if handle ∈ gethandles()\n            f = figure(handle)\n        else\n            f = Figure(handle)\n        end\n        idx = missing\n    end\n    (f, idx, args)\nend\n\nfunction merge_settings(s)\n    ans = \"\"\n    for a in s\n        if !isempty(a)\n            if a isa AbstractString\n                !isempty(ans) && (ans *= '\\n')\n                ans *= a\n            else\n                !isempty(ans) && (ans *= '\\n')\n                ans *= parse_settings(a)\n            end\n        end\n    end\n    return ans\nend\n\nfunction merge_plotline(p)\n    ans = \"\"\n    for a in p\n        if !isempty(a)\n            if a isa AbstractString\n                !isempty(ans) && (ans *= ' ')\n                ans *= a\n            else\n                !isempty(ans) && (ans *= ' ')\n                ans *= parse_plotline(a)\n            end\n        end\n    end\n    return ans\nend\n\n\"\"\"\n    animate(f::Figure, term = config.altterm)\n\nRender an animated plot in notebooks such as Pluto and Jupyter.\n\nThis function is meant to be used to render an animation within a notebook environment.\nNormally, all plots are rendered in a terminal such as `png`. However, rendering an\nanimation requires switching to `gif`, `webp` or other terminal that supports animations.\nChanging the global terminal configuration wil cause all other plots in the notebook to\nbe re-rendered with the wrong terminal. This function allows changing the terminal\non a plot-by-plot basis, without changing the global terminal configuration.\n\"\"\"\nfunction animate(f::Figure, term = config.altterm)\n    global config.alttoggle = true\n    global config.altterm = term\n    return f\nend\n"
  },
  {
    "path": "src/gaston_recipes.jl",
    "content": "## Copyright (c) 2013 Miguel Bazdresch\n##\n## This file is distributed under the 2-clause BSD License.\n\n# Recipes: argument conversion and specialized plot commands\n\n\"\"\"\n    convert_args(args...)\n\nConvert values of specific types to data that gnuplot can plot.\n\nUsers should add methods to this function for their own types. The returned value\nmust be one of the following types:\n\n* A `Gaston.PlotRecipe`, which describes a curve (i.e. it contains\n  coordinates and a plotline).\n* A `Gaston.AxisRecipe`, which contains multiple `PlotRecipe`s and axis settings.\n* A `Gaston.FigureRecipe`, which contains multiple `AxisRecipe`s and multiplot settings.\n\nSee the Gaston documentation for full details and examples.\n\nTo add a recipe for 3-D plotting, use `convert_args3`.\n\"\"\"\nconvert_args\n\n\"\"\"\n    convert_args3(args...)\n\nConvert values of specific types to data that gnuplot can plot using `splot`.\n\nSee documentation for `convert_args` for more details.\n\"\"\"\nconvert_args3\n\n# 2-D conversions\n\n### 1-argument\n# one number\nfunction convert_args(r::R, args... ;\n                      pl = \"\", kwargs...)::PlotRecipe where R <: Real\n    PlotRecipe(([1], [r], args...), pl)\nend\n\nfunction convert_args(c::C, args... ;\n                      pl = \"\", kwargs...)::PlotRecipe where C <: Complex\n    PlotRecipe(([real(c)], [imag(c)], args...), pl)\nend\n\n# complex vector\nfunction convert_args(c::AbstractVector{<:Complex}, args... ;\n                      pl = \"\", kwargs...)::PlotRecipe\n    PlotRecipe((collect(real(c)), collect(imag(c)), args...), pl)\nend\n\n# functions\nfunction convert_args(f::F, args... ;\n                      pl = \"\", kwargs...)::PlotRecipe where {F <: Function}\n    r = range(-10, stop = 10, length = 101)\n    PlotRecipe((r, f.(r), args...), pl)\nend\n\n### 2-argument\nfunction convert_args(x::R1, y::R2, args... ;\n                      pl = \"\", kwargs...)::PlotRecipe where {R1 <: Real, R2 <: Real}\n    PlotRecipe(([x], [y], args...), pl)\nend\n\nfunction convert_args(x::Tuple, f::F, args... ;\n                      pl = \"\", kwargs...)::PlotRecipe where {F<:Function}\n    samples = length(x) == 3 ? x[3] : 101\n    r = range(x[1], x[2], length=samples)\n    PlotRecipe((r, f.(r), args...), pl)\nend\n\nfunction convert_args(r::AbstractVector, f::F, args... ;\n                      pl = \"\", kwargs...)::PlotRecipe where {F<:Function}\n    PlotRecipe((r, f.(r), args...), pl)\nend\n\n# for use with \"w image\"\nfunction convert_args(a::Matrix{<:Real}, args... ; pl = \"\", kwargs...)::PlotRecipe\n    x = collect(axes(a,2))\n    y = collect(axes(a,1))\n    PlotRecipe((x, y, a, args...), pl)\nend\n\nfunction convert_args(a::Array{<:Real, 3}, args... ; pl = \"\", kwargs...)::PlotRecipe\n    x = collect(axes(a, 3))\n    y = collect(axes(a, 2))\n    PlotRecipe((x, y, a[1,:,:], a[2,:,:], a[3,:,:], args...), pl)\nend\n\n# histogram\nfunction convert_args(h::Histogram, args... ; pl = \"\", kwargs...)::PlotRecipe\n    # convert from StatsBase histogram to gnuplot x, y values\n    if h.weights isa Vector\n        xx = collect(h.edges[1])\n        x = (xx[1:end-1]+xx[2:end])./2\n        y = h.weights\n        return PlotRecipe((x, y, args...), pl)\n    else\n        xx = collect(h.edges[1])\n        x = (xx[1:end-1]+xx[2:end])./2\n        yy = collect(h.edges[2])\n        y = (yy[1:end-1]+yy[2:end])./2\n        z = permutedims(h.weights)\n        return PlotRecipe((x, y, z, args...), pl)\n    end\nend\n\n### 3-D conversions\n\nfunction convert_args3(x::R1, y::R2, z::R3, args... ;\n                       pl = \"\", kwargs...)::PlotRecipe where {R1 <: Real, R2 <: Real, R3 <: Real}\n    PlotRecipe(([x], [y], [z], args...), pl)\nend\n\nfunction convert_args3(a::Matrix{<:Real} ; pl = \"\", kwargs...)::PlotRecipe\n    x = axes(a, 2)\n    y = axes(a, 1)\n    PlotRecipe((x, y, a), pl)\nend\n\nfunction convert_args3(x::AbstractVector{<:Real}, y::AbstractVector{<:Real}, f::F, args... ;\n                       pl = \"\", kwargs...)::PlotRecipe where {F <: Function}\n    PlotRecipe((x, y, meshgrid(x, y, f), args...), pl)\nend\n\nfunction convert_args3(f::F, args... ; pl = \"\", kwargs...)::PlotRecipe where {F <: Function}\n    x = y = range(-10, 10, length = 100)\n    PlotRecipe((x, y, meshgrid(x, y, f), args...), pl)\nend\n\nfunction convert_args3(xy::Tuple, f::F, args... ;\n                       pl = \"\", kwargs...)::PlotRecipe where {F <: Function}\n    convert_args3(xy, xy, f, args... ; pl, kwargs...)\nend\n\nfunction convert_args3(xr::Tuple, yr::Tuple, f::F, args... ;\n                       pl = \"\", kwargs...)::PlotRecipe where {F <: Function}\n    samples_x = samples_y = 100\n    length(xr) == 3 && (samples_x = xr[3])\n    length(yr) == 3 && (samples_y = yr[3])\n    xx = range(xr[1], xr[2], length = samples_x)\n    yy = range(yr[1], yr[2], length = samples_y)\n    PlotRecipe((xx, yy, meshgrid(xx, yy, f), args...), pl)\nend\n\n### Plot recipes\n\n\"\"\"\n    scatter(args...; kwargs...)::Figure\n\nGenerate a scatter plot with built-in plotline theme `scatter`.\n\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nscatter(args... ; kwargs...) = plot(args... ; kwargs..., ptheme = :scatter)\n\n\"\"\"\n    scatter!(args...; kwargs...)::Figure\n\nInsert a scatter plot. See the `scatter` documentation for more details.\n\"\"\"\nscatter!(args... ; kwargs...) = plot!(args... ; kwargs..., ptheme = :scatter)\n\n\"\"\"\n    stem(args...; onlyimpulses::Bool = false, color = \"'blue'\", kwargs...)::Figure\n\nGenerate a stem plot with built-in plotline themes `impulses` and `stem`.\n\nThis function takes the following keyword arguments:\n\n* `onlyimpulses`: if `true`, plot using only impulses and omit the dots.\n* `color`: specify line color to use. If not specified, the impulses and\n  the dots may be plotted with different colors.\n\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nfunction stem(args... ; onlyimpulses = false, color = \"'blue'\", kwargs...)\n    clr = color != \"\" ? \"linecolor $(color)\" : \"\"\n    plot(args..., clr; kwargs..., ptheme = :impulses)\n    if !onlyimpulses\n        plot!(args..., clr ; kwargs..., ptheme = :stem)\n    end\n    figure()\nend\n\n\"\"\"\n    stem!(args... ; onlyimpulses = false, color = \"'blue'\", kwargs...)::Figure\n\nInsert a stem plot. See the `stem` documentation for more details.\n\"\"\"\nfunction stem!(args... ; onlyimpulses = false, color = \"'blue'\", kwargs...)\n    clr = color != \"\" ? \"linecolor $(color)\" : \"\"\n    plot!(args..., clr; kwargs..., ptheme = :impulses)\n    if !onlyimpulses\n        plot!(args..., clr ; kwargs..., ptheme = :stem)\n    end\nend\n\n\"\"\"\n    bar(args...; kwargs...)::Figure\n\nGenerate a bar plot with built-in settings theme `boxplot` and plotline theme `box`.\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nbar(args... ; kwargs...) = plot(args... ; kwargs..., stheme = :boxplot, ptheme = :box)\n\n\"\"\"\n    bar!(args...; kwargs...)::Figure\n\nInsert a bar plot. See the `bar` documentation for more details.\n\"\"\"\nbar!(args... ; kwargs...) = plot!(args... ; kwargs..., ptheme = :box)\n\n\"\"\"\n    barerror(args...; kwargs...)::Figure\n\nGenerate a barerror plot with built-in settings theme `boxplot` and plotline theme\n`boxerror`. See the `plot` documentation for more information on the arguments.\n\"\"\"\nbarerror(args... ; kwargs...) = plot(args... ; kwargs..., stheme = :boxplot, ptheme = :boxerror)\n\n\"\"\"\n    barerror!(args...; kwargs...)::Figure\n\nInsert a barerror plot. See the `barerror` documentation for more details.\n\"\"\"\nbarerror!(args... ; kwargs...) = plot!(args... ; kwargs..., ptheme = :boxerror)\n\n## Histograms\n\"\"\"\n    histogram(args...,[bins = 10,] [mode = :pdf,] [edges = nothing,] [horizontal = false]; kwargs...)::Figure\n\nPlot a histogram of the provided data, using `StatsBase.fit`. This function takes\nthe following keyword arguments:\n\n* `bins` specifies the number of bins (default 10)\n* `mode` specifies how the histogram area is normalized (see `StatsBase.fit`)\n\"\"\"\nfunction histogram(args... ;\n                   edges                = nothing,\n                   nbins                = 10,\n                   mode       :: Symbol = :pdf,\n                   horizontal :: Bool   = false,\n                   kwargs...)\n    # Extract data and non-data from args...\n    data = []\n    front = []\n    back = []\n    i = 1\n    while i <= length(args)\n        a = args[i]\n        if typeof(a) in (Axis, Figure, FigureAxis, String, Symbol) || a isa Vector{T} where T<:Pair\n            push!(front, a)\n            i = i + 1\n        else\n            break\n        end\n    end\n    while i <= length(args)\n        a = args[i]\n        if !(typeof(a) in (Axis, Figure, FigureAxis, String, Symbol) || a isa Vector{T} where T<:Pair)\n            push!(data, a)\n            i = i + 1\n        else\n            break\n        end\n    end\n    while i <= length(args)\n        a = args[i]\n        if typeof(a) in (Axis, Figure, FigureAxis, String, Symbol) || a isa Vector{T} where T<:Pair\n            push!(back, a)\n            i = i + 1\n        else\n            break\n        end\n    end\n    if length(data) == 1\n        h = edges === nothing ? hist(data[1] ; nbins, mode) : hist(data[1] ; edges, mode)\n        if horizontal\n            return plot(front..., h, back... ; kwargs..., stheme = :histplot, ptheme = :horhist)\n        else\n            return plot(front..., h, back... ; kwargs..., stheme = :histplot, ptheme = :box)\n        end\n    else\n        nbins isa Number && (nbins = (nbins, nbins))\n        h = edges === nothing ? hist(data[1], data[2] ; nbins, mode) :\n                                hist(data[1], data[2] ; edges, mode)\n        return plot(front..., h, back... ; kwargs..., ptheme = :image)\n    end\nend\n\n\"\"\"\n    imagesc(args...; kwargs...)::Figure\n\nPlot an array as an image. If the array is a matrix, a grayscale image is\nassumed. If the given array `z` is three-dimensional, an rgbimage is assumed,\nwith `z[1,:,:]` the red channel, `z[2,:,:]` the blue channel, and `z[3,:,:]`\nthe blue channel.\n\nSee the documentation to `plot` for more details.\n\"\"\"\nfunction imagesc(args... ; kwargs...)\n    rgb = false\n    for a in args\n        if a isa AbstractArray && ndims(a) == 3\n            rgb = true\n            break\n        end\n    end\n    if rgb\n        plot(args... ; kwargs..., stheme = :imagesc, ptheme = :rgbimage)\n    else\n        plot(args... ; kwargs..., stheme = :imagesc, ptheme = :image)\n    end\nend\n\n### 3-D recipes\n\n## Wireframes\n\n\"\"\"\n    wireframe(args...; kwargs...)::Figure\n\nPlot the provided data using a wireframe, using the settings theme `hidden3d`.\n\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nwireframe(args... ; kwargs...) = splot(args... ; kwargs..., stheme = :hidden3d)\n\n\"\"\"\n    wireframe!(args...; kwargs...)::Figure\n\nInsert a wireframe plot. See the `wireframe` documentation for more details.\n\"\"\"\nwireframe!(args... ; kwargs...) = splot!(args... ; kwargs...)\n\n## Surfaces\n\n\"\"\"\n    surf(args...; kwargs...)::Figure\n\nPlot the provided data as a surface, using the settings theme `hidden3d` and the\nplotline theme `pm3d`.\n\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nsurf(args... ; kwargs...) = splot(args... ; kwargs..., stheme = :hidden3d, ptheme = :pm3d)\n\n\"\"\"\n    surf!(args...; kwargs...)::Figure\n\nInsert a surface plot. See the `surf` documentation for more details.\n\"\"\"\nsurf!(args... ; kwargs...) = splot!(args... ; kwargs..., ptheme = :pm3d)\n\n# Surface with contours on the base\n\n\"\"\"\n    surfcontour(args...; [labels::Bool = true,] kwargs...)::Figure\n\nPlot the provided data as a surface with contour lines at the base, using the\nsettings theme `contourproj` and the plotline theme `labels`.\n\nIf the keyword argument `labels` is `true`, then numerical labels are added to\nthe contour lines.\n\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nfunction surfcontour(args... ; labels = true, kwargs...)\n    splot(args... ; kwargs..., stheme = :contourproj)\n    if labels\n        splot!(args... ; kwargs..., ptheme = :labels)\n    end\n    figure()\nend\n\n# surface with superimposed wireframe\n\n\"\"\"\n    wiresurf(args...; kwargs...)::Figure\n\nPlot the provided data as a surface with a superimposed wireframe, using the\nsettings theme `wiresurf`.\n\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nwiresurf(args... ; kwargs...) = splot(args... ; kwargs..., stheme = :wiresurf)\n\n\"\"\"\n    wiresurf!(args...; kwargs...)::Figure\n\nInsert a wiresurf plot. See the `wiresurf` documentation for more details.\n\"\"\"\nwiresurf!(args... ; kwargs...) = splot!(args... ; kwargs...)\n\n# 3D scatter plots\n\n\"\"\"\n    scatter3(args...; kwargs...)::Figure\n\nGenerate a scatter plot of the provided data, using the settings theme `scatter3` and the\nplotline theme `scatter`.\n\nSee the `plot` documentation for more information on the arguments.\n\"\"\"\nscatter3(args... ; kwargs...) = splot(args... ; kwargs..., stheme = :scatter3, ptheme = :scatter)\n\n\"\"\"\n    scatter3!(args...; kwargs...)::Figure\n\nInsert a scatter plot. See the `scatter3` documentation for more details.\n\"\"\"\nscatter3!(args... ; kwargs...) = splot!(args... ; kwargs..., ptheme = :scatter)\n\n\"\"\"\n    contour(args...; [labels::Bool = true,] kwargs...)::Figure\n\nPlot the provided data using contour lines, with settings themes `countour` and `labels`.\n\nIf the keyword argument `labels` is `true`, then the contour lines are labeled.\nSee the documentation to `plot` for more details.\n\"\"\"\nfunction contour(args... ; labels = true, kwargs...)\n    splot(args... ; kwargs..., stheme = :contour)\n    if labels\n        splot!(args... ; kwargs..., ptheme = :labels)\n    end\n    figure()\nend\n\n\"\"\"\n    heatmap(args...; kwargs...)\n\nPlot the data provided as a heatmap, using the settings theme `heatmap` and the\nplotline theme `pm3d`.\n\nSee the documentation to `plot` for more details.\n\"\"\"\nheatmap(args... ; kwargs...) = splot(args... ; kwargs..., stheme = :heatmap, ptheme = :pm3d)\n"
  },
  {
    "path": "test/Project.toml",
    "content": "[deps]\nAqua = \"4c88cf16-eb10-579e-8560-4a9242c79595\"\nDownloads = \"f43a241f-c20a-4ad4-852c-f6b1247861c6\"\nJET = \"c3a54625-cd67-489e-a8e7-0a5a0ff4e31b\"\nJSON = \"682c06a0-de6a-54ab-a142-c8b1cf79cde6\"\nPkg = \"44cfe95a-1eb2-52ea-b672-e2afdf69b78f\"\nPreferences = \"21216c6a-2e73-6563-6e65-726566657250\"\nTest = \"8dfed614-e22c-5e08-85e1-65c5234f0b40\"\n\n[compat]\nAqua = \"0.8\"\nDownloads = \"1\"\nJET = \"0.9 - 0.10\"\nJSON = \"1\"\nPkg = \"1\"\nPreferences = \"1\"\nTest = \"1\"\n"
  },
  {
    "path": "test/downstream.jl",
    "content": "using Downloads, JSON, Test\n\nfunction available_channels()\n    juliaup = \"https://julialang-s3.julialang.org/juliaup\"\n    for i ∈ 1:6\n        buf = PipeBuffer()\n        Downloads.download(\"$juliaup/DBVERSION\", buf)\n        dbversion = VersionNumber(readline(buf))\n        dbversion.major == 1 || continue\n        buf = PipeBuffer()\n        Downloads.download(\n            \"$juliaup/versiondb/versiondb-$dbversion-x86_64-unknown-linux-gnu.json\",\n            buf,\n        )\n        json = JSON.parse(buf)\n        haskey(json, \"AvailableChannels\") || continue\n        return json[\"AvailableChannels\"]\n        sleep(10i)\n    end\n    return\nend\n\n\"\"\"\njulia> is_latest(:lts)\njulia> is_latest(:release)\n\"\"\"\nfunction is_latest(variant)\n    channels = available_channels()\n    ver = let var::String = (\n        release = \"release\",\n        rel = \"release\",\n        lts = \"lts\",\n        release_candidate = \"rc\",\n        alpha = \"alpha\",\n        beta = \"beta\",\n        rc = \"rc\",\n    )[variant]\n        VersionNumber(split(channels[var][\"Version\"], '+') |> first)\n    end\n    dev = occursin(\"DEV\", string(VERSION))  # or length(VERSION.prerelease) < 2\n    return !dev && (\n        VersionNumber(ver.major, ver.minor, 0, (\"\",)) ≤ VERSION < VersionNumber(ver.major, ver.minor + 1)\n    )\nend\n\n(is_ci() && Sys.islinux() && is_latest(:release)) && @testset \"downstream\" begin\n    tmpd = mktempdir()\n    Plots_jl = joinpath(tmpd, \"Plots.jl\")\n    @test Cmd(`$(Base.julia_cmd()) $(joinpath(@__DIR__, \"downstream_dev.jl\")) $tmpd`) |> run |> success\n    script = tempname()\n    write(\n        script,\n        \"\"\"\n        using Pkg\n\n        Pkg.activate(joinpath(\"$Plots_jl\", \"PlotsBase\"))\n        Pkg.develop(path=\"$(joinpath(@__DIR__, \"..\"))\")\n\n        import Gaston  # trigger `PlotsBase` extension\n        Pkg.status([\"Gaston\", \"PlotsBase\"])\n\n        # test basic plots creation and bitmap or vector exports\n        using PlotsBase, Test\n\n        prefix = tempname()\n        @time for i ∈ 1:length(PlotsBase._examples)\n            i ∈ PlotsBase._backend_skips[:gaston] && continue  # skip unsupported examples\n            PlotsBase._examples[i].imports ≡ nothing || continue  # skip examples requiring optional test deps\n            pl = PlotsBase.test_examples(:gaston, i; disp = false)\n            for ext in (\".png\", \".pdf\")  # TODO: maybe more ?\n                fn = string(prefix, i, ext)\n                PlotsBase.savefig(pl, fn)\n                @test filesize(fn) > 1_000\n            end\n        end\n        \"\"\"\n    )\n    @test Cmd(`$(Base.julia_cmd()) --project=@. $script`; dir = Plots_jl) |> run |> success\nend\n"
  },
  {
    "path": "test/downstream_dev.jl",
    "content": "using Pkg\n\nLibGit2 = Pkg.GitTools.LibGit2\nTOML = Pkg.TOML\n\nfailsafe_clone_checkout(path, url, pkg = nothing; stable = true) = begin\n    local repo\n    for i in 1:6\n        try\n            repo = Pkg.GitTools.ensure_clone(stdout, path, url)\n            break\n        catch err\n            @warn err\n            sleep(20i)\n        end\n    end\n\n    name, _ = splitext(basename(url))\n    registries = joinpath(first(DEPOT_PATH), \"registries\")\n    general = joinpath(registries, \"General\")\n    versions = joinpath(general, name[1:1], name, \"Versions.toml\")\n    if !isfile(versions)\n        mkpath(general)\n        run(setenv(`tar xf $general.tar.gz`; dir = general))\n    end\n    @assert isfile(versions)\n\n    if stable\n        v_stable = maximum(VersionNumber.(keys(TOML.parse(read(versions, String)))))\n        obj = LibGit2.GitObject(repo, \"v$v_stable\")\n        hash = if isa(obj, LibGit2.GitTag)\n            LibGit2.target(obj)\n        else\n            LibGit2.GitHash(obj)\n        end |> string\n        LibGit2.checkout!(repo, hash)\n    end\n\n    toml = if pkg ≢ nothing && (fn = joinpath(path, pkg, \"Project.toml\")) |> isfile  # monorepo layout\n        fn\n    elseif (fn = joinpath(path, \"Project.toml\")) |> isfile  # single package toplevel\n        fn\n    end\n    @assert isfile(toml) \"$toml does not exist, bailing out !\"\n    toml\nend\n\nfake_supported_version!(toml) = begin\n    # fake the supported Gaston version for testing (for `Pkg.develop`)\n    Gaston_version = Pkg.Types.read_package(normpath(@__DIR__, \"..\", \"Project.toml\")).version\n    parsed_toml = TOML.parse(read(toml, String))\n    parsed_toml[\"compat\"][\"Gaston\"] = string(Gaston_version)\n    open(toml, \"w\") do io\n        TOML.print(io, parsed_toml)\n    end\n    nothing\nend\n\ndn = joinpath(ARGS[1], \"Plots.jl\")\ntoml = failsafe_clone_checkout(dn, \"https://github.com/JuliaPlots/Plots.jl\", \"PlotsBase\"; stable = false)\ndocs = joinpath(dn, \"docs\")\nisdir(docs) && rm(docs; recursive=true)  # ERROR: LoadError: empty intersection between Gaston@2.0.1 and project compatibility ∅\nfake_supported_version!(toml)\n"
  },
  {
    "path": "test/manualtests.txt",
    "content": "Tests to be run manually.\n\n* Produce a sixelgd plot, using `xterm -ti vt340`, `wezterm` or other\n  sixelgd-capable terminal.\n\nusing Gaston\nGaston.config.term = \"sixelgd size 300,200\"\nGaston.config.output = :echo\nplot(1:10)\n\n--\n\n* In the repl, produce an ascii plot\n\nGaston.config.term = \"dumb\"\nGaston.config.output = :echo\nplot(1:10)\n\n--\n\n* In the repl, produce an svg script\n\nGaston.config.term = \"svg\"\nGaston.config.output = :echo\nplot(1:10)\n\n--\n\n* In the repl, produce an html page\n\nGaston.config.term = \"canvas\"\nGaston.config.output = :echo\nplot(1:10)\n\n--\n\n* In the repl, produce no output\n\nGaston.config.output = :null\nplot(1:10)\n\n--\n\n* In the repl, produce a qt plot\n\nGaston.config.output = :external\nGaston.config.term = \"qt\"\nplot(1:10)\n\n--\n\n* In the repl, save a plot\n\nplot(1:10);\nsave(\"test.gif\", term = \"gif size 300,200\")\nsave(\"test.pdf\", term = \"pdfcairo\")\nsave(\"test.png\", term = \"png enhanced background 'blue' size 300,200\")\n\n--\n\n* In Pluto, load the notebooks under notebeook/*\n\n"
  },
  {
    "path": "test/preferences.jl",
    "content": "using Preferences, Gaston\n\nconst PREVIOUS_DEFAULT_GNUPLOT = load_preference(Gaston, \"gnuplot_binary\")\n\ninvalidate_compiled_cache!(m::Module) =  # make sure the compiled cache is removed\n    rm.(Base.find_all_in_cache_path(Base.PkgId(m, string(nameof(m)))))\n\n@testset \"Invalid path\" begin\n    script = tempname()\n    set_preferences!(Gaston, \"gnuplot_binary\" => \"/_some_non_existent_invalid_path_\"; force = true)\n    invalidate_compiled_cache!(Gaston)\n    write(\n        script,\n        \"\"\"\n        using Gaston, Test\n        res = @testset \"[subtest] invalid gnuplot_binary path\" begin\n            @test Gaston.config.exec ≡ nothing\n        end\n        exit(res.n_passed == 1 ? 0 : 123)\n        \"\"\"\n    )\n    @test run(`$(Base.julia_cmd()) $script`) |> success\nend\n\nconst sys_gnuplot = Sys.which(\"gnuplot\")\n\n(Sys.islinux() && sys_gnuplot ≢ nothing) && @testset \"System gnuplot\" begin\n    script = tempname()\n    set_preferences!(Gaston, \"gnuplot_binary\" => sys_gnuplot; force = true)\n    invalidate_compiled_cache!(Gaston)\n    write(\n        script,\n        \"\"\"\n        using Gaston, Test\n        res = @testset \"[subtest] system gnuplot path\" begin\n            @test Gaston.config.exec.exec == [\"$sys_gnuplot\"]\n        end\n        exit(res.n_passed == 1 ? 0 : 123)\n        \"\"\"\n    )\n    @test run(`$(Base.julia_cmd()) $script`) |> success\nend\n\nif PREVIOUS_DEFAULT_GNUPLOT ≡ nothing\n    # restore the absence of a preference\n    delete_preferences!(Gaston, \"gnuplot_binary\"; force = true)\nelse\n    # reset to previous state\n    set_preferences!(Gaston, \"gnuplot_binary\" => PREVIOUS_DEFAULT_GNUPLOT; force = true)\nend\n"
  },
  {
    "path": "test/runtests.jl",
    "content": "## Copyright (c) 2013 Miguel Bazdresch\n##\n## This file is distributed under the 2-clause BSD License.\n\nusing Test, Gaston, Aqua, JET\nusing Gaston: Axis, Axis3, Plot\nimport Gaston: convert_args, convert_args3, PlotRecipe, AxisRecipe, FigureRecipe\n\ngh = Gaston.gethandles\nreset = Gaston.reset\nnull() = Gaston.config.output = :null\n\nis_pkgeval() = Base.get_bool_env(\"JULIA_PKGEVAL\", false)\nis_ci() = Base.get_bool_env(\"CI\", false)\n\n@testset \"Gnuplot version\" begin\n    @test Gaston.GNUPLOT_VERSION[] ≥ v\"6\"\nend\n\n@testset \"Available terminals\" begin\n    @test Gaston.terminals() ≡ nothing\nend\n\n@testset \"JULIA_GNUPLOT_EXE\" begin\n    withenv(\"JULIA_GNUPLOT_EXE\" => \"gnuplot\") do\n        @test read(`$(Base.julia_cmd()) -e 'using Gaston; print(Gaston.config.exec.exec[1])'`, String) == \"gnuplot\"\n    end\nend\n\n@testset \"AQUA\" begin\n    # Aqua.test_all(Gaston)\n    # Aqua.test_ambiguities(Gaston) # disabled -- fails with ambiguities from StatsBase\n    Aqua.test_unbound_args(Gaston)\n    Aqua.test_undefined_exports(Gaston)\n    Aqua.test_project_extras(Gaston)\n    Aqua.test_stale_deps(Gaston)\n    Aqua.test_deps_compat(Gaston)\n    Aqua.test_piracies(Gaston, treat_as_own = [convert_args, convert_args3])\n    Aqua.test_persistent_tasks(Gaston)\nend\n\n@testset \"JET\" begin\n    if Gaston.state.enabled\n        null()\n        #JET.test_package(\"Gaston\"; toplevel_logger=nothing)\n        JET.@test_call target_modules=(Gaston,) Figure(1)\n        JET.@test_call target_modules=(Gaston,) plot(rand(10), rand(10))\n        JET.@test_call target_modules=(Gaston,) @gpkw plot({grid, title = Q\"test\"}, rand(10), rand(10), {lc = \"'red'\"})\n        f = Figure()\n        x = 1:10; y = rand(10); z = rand(10,10)\n        JET.@test_call target_modules=(Gaston,) plot(f, x, y, z)\n        JET.@test_call target_modules=(Gaston,) plot(f[2], x, y, z)\n        JET.@test_call target_modules=(Gaston,) plot!(f, x, y, z)\n        closeall()\n    end\nend\n\n@testset \"Options\" begin\n    if Gaston.state.enabled\n        w = 5.3\n        x = @gpkw {a = :summer, b, c = 3, d = Q\"long title\", e = w}\n        @test x[1] == Pair(\"a\", :summer)\n        @test x[2] == Pair(\"b\", true)\n        @test x[3] == Pair(\"c\", 3)\n        @test x[4] == Pair(\"d\", \"'long title'\")\n        @test x[5] == Pair(\"e\", 5.3)\n    end\nend\n\n@testset \"Handles\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        f1 = Figure(1);\n        f2 = Figure(\"a\");\n        f3 = Figure(3.14);\n        f4 = Figure(:a);\n        @test Gaston.nexthandle() == 2\n        @test Gaston.getidx(f1) == 1\n        @test Gaston.getidx(f2) == 2\n        @test Gaston.getidx(f3) == 3\n        @test Gaston.getidx(f4) == 4\n        closefigure(\"a\")\n        @test Gaston.getidx(f3) == 2\n        closefigure(f4)\n        @test Gaston.nexthandle() == 2\n        closeall()\n        @test Gaston.nexthandle() == 1\n        # negative handles\n        f1 = Figure(-5)\n        f2 = Figure(\"a\")\n        f3 = Figure(0)\n        @test Gaston.nexthandle() == 1\n        closefigure(-5)\n        @test Gaston.nexthandle() == 1\n        f4 = Figure()\n        @test f4.handle == 1\n        closeall()\n        p1 = plot(1:10, handle = :a)\n        p2 = plot(1:10, handle = :b)\n        p3 = plot(1:10, handle = :c)\n        @test begin\n            closefigure(:b)\n            gh()\n        end == [:a, :c]\n        @test begin\n            closefigure()\n            gh()\n        end == [:a]\n        @test begin\n            closefigure(:a)\n            gh()\n        end == Any[]\n        Figure()\n        Figure()\n        @test Gaston.activefig() == 2\n        @test Gaston.state.figures.figs[1].handle == 1\n        @test Gaston.state.figures.figs[2].handle == 2\n        closefigure(1)\n        Figure()\n        @test Gaston.state.figures.figs[1].handle == 2\n        @test Gaston.state.figures.figs[2].handle == 1\n        closeall()\n        @test gh() == Any[]\n        closeall()\n        Figure(1)\n        Figure(4)\n        @test length(Gaston.state.figures.figs) == 2\n        @test closefigure(1) == nothing\n        @test length(Gaston.state.figures.figs) == 1\n        @test Gaston.activefig() == 4\n        closefigure(4)\n        @test isempty(Gaston.state.figures.figs)\n        @test Gaston.activefig() === nothing\n        closeall()\n        Figure(1)\n        Figure(10)\n        f = figure()\n        @test f.handle == 10\n        f = figure(1)\n        @test f.handle == 1\n        f = figure(index = 1)\n        @test f.handle == 1\n        f = figure(index = 2)\n        @test f.handle == 10\n        @test_throws ErrorException figure(3)\n        @test_throws ErrorException figure(index = 3)\n    end\nend\n\n@testset \"Configuration commands\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        @test Gaston.config.term == \"\"\n        @test Gaston.config.embedhtml == false\n        @test Gaston.config.output == :external\n        @test run(`$(Gaston.config.exec) --version`) |> success\n        null()\n        @test Gaston.config.output == :null\n    end\nend\n\n@testset \"Plot\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        @test_throws ArgumentError Plot()\n        @test_throws ArgumentError Plot(\"w l\")\n        p = Plot(1:10, \"w l\")\n        @test p.plotline == \"w l\"\n        p = Plot(1:10, 1:10, 1:10, \"w l\")\n        @test p.plotline == \"w l\"\n        p = Plot(1:10)\n        @test p.plotline == \"\"\n        p = @gpkw Plot(1:10, {with=\"lines\"})\n        @test p.plotline == \"with lines\"\n        #test Plot!\n        pp = Gaston.Plot!(p)\n        @test p.plotline == \"with lines\"\n        pp = Gaston.Plot!(p, 1:10, 1:10, \"w p\")\n        @test p.plotline == \"w p\"\n        # test plot(figs...)\n        f1 = Figure()\n        f2 = Figure()\n        plot(f1,sin)\n        plot(f2,cos)\n        f3 = plot(f1,f2)\n        @test f3 isa Figure\n        @test length(f3) == 2\n        # test that an existing f.multiplot is not overwritten\n        f1 = Figure(multiplot = \"title '1'\")\n        plot(1:10)\n        @test f1.multiplot == \"\"\n        f2 = Figure(multiplot = \"title '1'\")\n        plot(f2, 1:10)\n        @test f2.multiplot == \"\"\n        f3 = Figure(multiplot = \"title '1'\")\n        plot(f3[1], 1:10)\n        @test f3.multiplot == \"title '1'\"\n        f4 = Figure(multiplot = \"title '1'\")\n        plot(f4[2], 1:10)\n        @test f4.multiplot == \"title '1'\"\n        f5 = Figure()\n        @test f5.multiplot == \"\"\n        closeall()\n    end\nend\n\n@testset \"Axis\" begin\n    if Gaston.state.enabled\n        a = Axis()\n        @test a.settings == \"\"\n        @test a.plots == Plot[]\n        @test isempty(a)\n        p = Plot(1:10)\n        push!(a, p)\n        @test !isempty(a)\n        @test length(a.plots) == 1\n        push!(a, p)\n        @test length(a.plots) == 2\n        Gaston.set!(a, \"s\")\n        @test a.settings == \"s\"\n        Gaston.set!(a, [\"title\" => \"1\"])\n        @test a.settings == \"set title 1\"\n        Gaston.empty!(a)\n        @test isempty(a)\n        a = Axis(p)\n        @test !isempty(a)\n        @test length(a.plots) == 1\n        Gaston.empty!(a)\n        a = Axis(\"s\")\n        @test a.settings == \"s\"\n        @test a.plots == Plot[]\n        a = Axis(\"s\", p)\n        @test a.settings == \"s\"\n        @test length(a.plots) == 1\n        a = Axis([\"title\" => \"1\"], p)\n        @test a.settings == \"set title 1\"\n        @test a.plots == [p]\n        a = Axis([\"title\" => \"1\"], p)\n        @test a.settings == \"set title 1\"\n        @test length(a.plots) == 1\n        @gpkw a = Axis({title = \"1\", grid})\n        @test a.settings == \"set title 1\\nset grid\"\n        a = Axis()\n        @test a.is3d == false\n        a = Axis3()\n        @test a.is3d == true\n    end\nend\n\n@testset \"push! and set! with FigureAxis\" begin\n    if Gaston.state.enabled\n        closeall()\n        f1 = plot(sin)\n        f2 = Figure()\n        histogram(randn(100), bins = 10)  # plots on f2\n        push!(f1, f2)\n        @test f1 isa Figure\n        plot(f2[2], cos)\n        push!(f1, f2[2])\n        @test f1 isa Figure\n        push!(f1, f2)\n        @test f1 isa Figure\n        push!(f1, f2[2])\n        @test f1 isa Figure\n        Gaston.set!(f2[2], \"testing\")\n        @test f2(2).settings == \"testing\"\n        Gaston.set!(f2[2], [\"grid\" => true])\n        @test f2(2).settings == \"set grid\"\n        p = Plot(1:10, 1:10)\n        push!(f2[2], p)\n        @test length(f2(2)) == 2\n    end\nend\n\n@testset \"Figure and figure\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        @test Figure() isa Figure\n        @test length(Gaston.state.figures) == 1\n        @test Gaston.state.figures.figs[1].handle == 1\n        @test_throws ErrorException Figure(1)\n        @test_throws ErrorException figure(2)\n        closeall()\n        f = Figure(π)\n        @test f.handle == π\n        @test f.gp_proc isa Base.Process\n        @test f.multiplot == \"\"\n        @test f.axes == Axis[]\n        @test isempty(f)\n        @test length(f) == 0\n        Figure(\"a\")\n        @test Gaston.activefig() == \"a\"\n        f = figure(index = 2)\n        @test f.handle == \"a\"\n        @test f[1] isa Gaston.FigureAxis\n        f = figure(π)\n        @test f.handle == π\n        @test length(f) == 0\n        # test pushes\n        f = Figure(1)\n        p = Plot([1])\n        push!(f(2), p)\n        @test f[2] isa Gaston.FigureAxis\n        @test f(2, 1) == p\n    end\nend\n\n@testset \"Parsing settings\" begin\n    if Gaston.state.enabled\n        ps = Gaston.parse_settings\n        @test ps(\"x\") == \"x\"\n        # booleans\n        @test @gpkw ps({g}) == \"set g\"\n        @test @gpkw ps({g=true}) == \"set g\"\n        @test @gpkw ps({g=false}) == \"unset g\"\n        @test @gpkw ps({g,g=false}) == \"set g\\nunset g\"\n        # tics\n        @test @gpkw ps({tics=\"axis border\"}) == \"set tics axis border\"\n        @test @gpkw ps({xtics=1:2}) == \"set xtics 1,1,2\"\n        @test @gpkw ps({ytics=1:2}) == \"set ytics 1,1,2\"\n        @test @gpkw ps({ztics=1:2:7}) == \"set ztics 1,2,7\"\n        @test @gpkw ps({tics=1:2}) == \"set tics 1,1,2\"\n        @test @gpkw ps({tics,tics=1:5}) == \"set tics\\nset tics 1,1,5\"\n        @test @gpkw ps({tics=(0,5)}) == \"set tics (0, 5)\"\n        @test @gpkw ps({tics=(labels=(\"one\", \"two\"), positions=(0, 5))}) == \"set tics ('one' 0, 'two' 5, )\"\n        # ranges\n        #@test @gpkw ps({})\n        @test @gpkw ps({xrange=(-5,5)}) == \"set xrange [-5:5]\"\n        @test @gpkw ps({xrange=(-5.1,5.6)}) == \"set xrange [-5.1:5.6]\"\n        @test @gpkw ps({xrange=[-Inf,0]}) == \"set xrange [*:0.0]\"\n        @test @gpkw ps({yrange=[0,Inf]}) == \"set yrange [0.0:*]\"\n        @test @gpkw ps({zrange=[-Inf,Inf]}) == \"set zrange [*:*]\"\n        @test @gpkw ps({cbrange=(1,2)}) == \"set cbrange [1:2]\"\n        @test @gpkw ps({cbrange=(0,Inf)}) == \"set cbrange [0:*]\"\n        @test @gpkw ps({zrange=(-Inf,Inf)}) == \"set zrange [*:*]\"\n        @test @gpkw ps({ranges=(-3,3)}) == \"set xrange [-3:3]\\nset yrange [-3:3]\\nset zrange [-3:3]\\nset cbrange [-3:3]\"\n        # palette\n        @test @gpkw ps({palette=:jet}) == \"set palette defined (1 0.0 0.0 0.498, 2 0.0 0.0 1.0, 3 0.0 0.498 1.0, 4 0.0 1.0 1.0, 5 0.498 1.0 0.498, 6 1.0 1.0 0.0, 7 1.0 0.498 0.0, 8 1.0 0.0 0.0, 9 0.498 0.0 0.0)\\nset palette maxcolors 9\"\n        @test @gpkw ps({palette=(:jet,:reverse)}) == \"set palette defined (1 0.498 0.0 0.0, 2 1.0 0.0 0.0, 3 1.0 0.498 0.0, 4 1.0 1.0 0.0, 5 0.498 1.0 0.498, 6 0.0 1.0 1.0, 7 0.0 0.498 1.0, 8 0.0 0.0 1.0, 9 0.0 0.0 0.498)\\nset palette maxcolors 9\"\n        @test @gpkw ps({palette=\"x\"}) == \"set palette x\"\n        # view\n        @test @gpkw ps({view=(50,60)}) == \"set view 50, 60\"\n        @test @gpkw ps({view=5}) == \"set view 5\"\n        # linetype\n        @test @gpkw ps({linetype = 5}) == \"set linetype 5\"\n        @test @gpkw ps({lt = :jet}) == \"set lt 1 lc rgb '#00007f'\\nset lt 2 lc rgb '#0000ff'\\nset lt 3 lc rgb '#007fff'\\nset lt 4 lc rgb '#00ffff'\\nset lt 5 lc rgb '#7fff7f'\\nset lt 6 lc rgb '#ffff00'\\nset lt 7 lc rgb '#ff7f00'\\nset lt 8 lc rgb '#ff0000'\\nset lt 9 lc rgb '#7f0000'\\nset linetype cycle 9\"\n        # margins\n        @test ps(@gpkw {margins = (.2, .3, .4, .5)}) == \"set lmargin at screen 0.2\\nset rmargin at screen 0.3\\nset bmargin at screen 0.4\\nset tmargin at screen 0.5\"\n        @test ps(@gpkw {margins = \"1,2,3,4\"}) == \"set margins 1,2,3,4\"\n    end\nend\n\n@testset \"Parsing plotlines\" begin\n    if Gaston.state.enabled\n        pp = Gaston.parse_plotline\n        @test @gpkw pp({w=1,w=2}) == \"w 2\"\n        @test @gpkw pp({marker=:dot}) == \"pointtype 0\"\n        @test @gpkw pp({pointtype=:⋅}) == \"pointtype 0\"\n        @test @gpkw pp({pt=:+}) == \"pointtype 1\"\n        @test @gpkw pp({marker=Q\"λ\"}) == \"pointtype 'λ'\"\n        @test @gpkw pp({marker=\"'k'\"}) == \"pointtype 'k'\"\n        @test @gpkw pp({plotstyle=\"lt\"}) == \"with lt\"\n        @test @gpkw pp({markersize=8}) == \"pointsize 8\"\n        @test @gpkw pp({ms=5}) == \"pointsize 5\"\n        @test @gpkw pp({legend=Q\"title\"}) == \"title 'title'\"\n    end\nend\n\n@testset \"Argument conversion\" begin\n    if Gaston.state.enabled\n        for arg in (1, (1,2), ComplexF64[1,2,3], (1:10, sin), ((1,10), sin), ((1,10,10), sin),\n                    [1 2; 3 4], rand(4,3,4))\n            p = convert_args(arg...)\n            @test p isa PlotRecipe\n            @test p.plotline == \"\"\n        end\n        for arg in (1, (1,2), ComplexF64[1,2,3], (1:10, sin), ((1,10), sin), ((1,10,10), sin),\n                    [1 2; 3 4], rand(4,3,4))\n            p = convert_args(arg..., pl = \"test\")\n            @test p isa PlotRecipe\n            @test p.plotline == \"test\"\n        end\n        for arg in ((1,2,3), rand(2,2), ([1,2], [3,4], (x,y)->y*sin(x)), ((x,y)->y*sin(x),))\n            p = convert_args3(arg..., pl = \"test\")\n            @test p isa PlotRecipe\n            @test p.plotline == \"test\"\n        end\n\n        struct TestType end\n        tt = TestType()\n        Gaston.convert_args(x::TestType, args... ; pl = \"\", kwargs...) = true\n        @test convert_args(tt)\n        Gaston.convert_args3(x::TestType, args... ; pl = \"\", kwargs...) = true\n        @test convert_args3(tt)\n\n        # TODO: test histograms\n    end\nend\n\n@testset \"plot\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        f = plot(1:10)\n        @test f isa Figure\n        @test f.handle == 1\n        @test f(1).settings == \"\"\n        @test f(1,1).plotline == \"\"\n        f = @gpkw plot({grid}, 1:10)\n        @test f.handle == 1\n        @test f(1).settings == \"set grid\"\n        @test f(1,1).plotline == \"\"\n        f = @gpkw plot({grid}, 1:10, \"w l\")\n        @test f(1,1).plotline == \"w l\"\n        f = @gpkw plot({grid}, 1:10, {w=\"l\"})\n        @test f(1,1).plotline == \"w l\"\n        plot(f[2], (1:10).^2)\n        @test length(f) == 2\n        @test f(2).settings == \"\"\n        @test f(2,1).plotline == \"\"\n        plot(f[2], \"set view\", (1:10).^2, \"w p\")\n        @test f(2).settings == \"set view\"\n        @test f(2,1).plotline == \"w p\"\n        plot!(f[2], 1:10, \"w lp\")\n        @test f(2,2).plotline == \"w lp\"\n        plot!(f[2], 1:10, \"w lp pt 1\")\n        @test f(2,3).plotline == \"w lp pt 1\"\n        @gpkw plot!(f[2], 1:10, {w=\"p\", marker=Q\"λ\"})\n        @test f(2,4).plotline == \"w p pointtype 'λ'\"\n        plot(1:10)\n        @test length(f) == 1\n        f = plot(1:10, handle=\"aa\")\n        @test f isa Figure\n        @test f.handle == \"aa\"\n        # test plotwithtable\n        x = y = range(-5, 5, 100)\n        f4(x,y) = sin(1.3x) * cos(0.9y) + cos(0.8x) * sin(1.9y) + cos(0.2x*y)\n        settings = \"set contour base\\nset cntrparam level incremental -3, 0.5, 3\\nunset surface\"\n        contours = Gaston.plotwithtable(settings, x, y, f4)\n        z = Gaston.meshgrid(x, y, f4)\n        plot(\"unset key\\nunset colorbox\\nset palette rgbformulae 33,13,10\", x, y, z, \"with image\")\n        pwt = plot!(contours, \"w l lw 1.5 lc 'slategray'\")\n        @test pwt isa Figure\n    end\nend\n\n@testset \"plot with 'themes'\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        f = @gpkw plot({grid},{xtics}, 1:10)\n        @test f(1).settings == \"set grid\\nset xtics\"\n        f = @gpkw plot({grid},\"set xtics\", 1:10)\n        @test f(1).settings == \"set grid\\nset xtics\"\n        f = @gpkw plot(\"set grid\",{xtics}, 1:10)\n        @test f(1).settings == \"set grid\\nset xtics\"\n        f = @gpkw plot(\"set grid\",{xtics},\"set view\", 1:10)\n        @test f(1).settings == \"set grid\\nset xtics\\nset view\"\n        f = plot(1:10, \"1\", \"2\", \"3\")\n        @test f(1,1).plotline == \"1 2 3\"\n        plot!(f, 1:10, \"1\", \"2\", \"3\")\n        @test f(1,2).plotline == \"1 2 3\"\n        f = @gpkw plot(1:10, \"1\", {2}, \"3\")\n        @test f(1,1).plotline == \"1 2 3\"\n        @gpkw plot!(f, 1:10, \"1\", {2}, \"3\")\n        @test f(1,2).plotline == \"1 2 3\"\n        f = @gpkw plot(1:10, {1}, {2}, {w=\"l\"})\n        @test f(1,1).plotline == \"1 2 w l\"\n        @gpkw plot!(f, 1:10, {1}, {2}, {w=\"l\"})\n        @test f(1,2).plotline == \"1 2 w l\"\n        f = @gpkw plot(1:10, {1}, {2}, :scatter)\n        @test f(1,1).plotline == \"1 2 with points\"\n        @gpkw plot!(f, 1:10, {1}, {2}, :scatter)\n        @test f(1,2).plotline == \"1 2 with points\"\n        f = @gpkw plot({grid}, :heatmap, 1:10)\n        @test f(1).settings == \"set grid\\nset view map\"\n    end\nend\n\n@testset \"2d plot plot styles\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        f = @gpkw scatter({grid}, rand(2), rand(2), {1}, \"2\")\n        @test f isa Figure\n        @test f(1).settings == \"set grid\"\n        @test f(1,1).plotline == \"with points 1 2\"\n        @gpkw scatter!(rand(2), rand(2), {3}, \"4\")\n        @test f isa Figure\n        @test f(1).settings == \"set grid\"\n        @test f(1,2).plotline == \"with points 3 4\"\n        f = @gpkw stem({grid}, rand(2), {1}, \"2\")\n        @test f isa Figure\n        @test f(1).settings == \"set grid\"\n        @test f(1,1).plotline == \"with impulses 1 2 linecolor 'blue'\"\n        @test f(1,2).plotline == \"with points pointtype 6 1 2 linecolor 'blue'\"\n        @gpkw stem!(rand(2), rand(2), {3}, \"4\")\n        @test f isa Figure\n        @test f(1).settings == \"set grid\"\n        @test f(1,3).plotline == \"with impulses 3 4 linecolor 'blue'\"\n        @test f(1,4).plotline == \"with points pointtype 6 3 4 linecolor 'blue'\"\n        f = bar(1:10, rand(10))\n        bar!(1.5:10.5, 0.5*rand(10), \"lc 'green'\")\n        @test f(1).settings == \"set boxwidth 0.8 relative\\nset style fill solid 0.5\"\n        @test f(1,1).plotline == \"with boxes\"\n        @test f(1,2).plotline == \"with boxes lc 'green'\"\n        f = barerror(1:10, rand(10), rand(10))\n        barerror!(1.5:10.5, 0.5*rand(10), rand(10), \"lc 'green'\")\n        @test f(1).settings == \"set boxwidth 0.8 relative\\nset style fill solid 0.5\"\n        @test f(1,1).plotline == \"with boxerrorbars\"\n        @test f(1,2).plotline == \"with boxerrorbars lc 'green'\"\n        f = histogram(rand(10), nbins = 20, mode = :pdf)\n        @test f isa Figure\n        @test f(1).settings == \"set boxwidth 0.8 relative\\nset style fill solid 0.5\\nset yrange [0:*]\"\n        @test f(1,1).plotline == \"with boxes\"\n        f = histogram(rand(10), edges = [-1, 0, 1])\n        @test f isa Figure\n        @test f(1).settings == \"set boxwidth 0.8 relative\\nset style fill solid 0.5\\nset yrange [0:*]\"\n        @test f(1,1).plotline == \"with boxes\"\n        f = histogram(rand(10), edges = -5:0.5:5)\n        @test f isa Figure\n        @test f(1).settings == \"set boxwidth 0.8 relative\\nset style fill solid 0.5\\nset yrange [0:*]\"\n        @test f(1,1).plotline == \"with boxes\"\n        f = histogram(rand(10), rand(10))\n        @test f isa Figure\n        @test f(1).settings == \"\"\n        @test f(1,1).plotline == \"with image\"\n        f = histogram(rand(10), rand(10), nbins = 20)\n        @test f isa Figure\n        @test f(1).settings == \"\"\n        @test f(1,1).plotline == \"with image\"\n        f = histogram(rand(10), rand(10), nbins = (20,10))\n        @test f isa Figure\n        @test f(1).settings == \"\"\n        @test f(1,1).plotline == \"with image\"\n        f = histogram(rand(10), rand(10), edges = [1,2,3])\n        @test f isa Figure\n        @test f(1).settings == \"\"\n        @test f(1,1).plotline == \"with image\"\n        f = histogram(rand(10), rand(10), edges = ([1,2,3],[1,2,3]))\n        @test f isa Figure\n        @test f(1).settings == \"\"\n        @test f(1,1).plotline == \"with image\"\n        f = histogram(rand(10), rand(10), nbins = 20, mode = :pdf)\n        @test f isa Figure\n        @test f(1).settings == \"\"\n        @test f(1,1).plotline == \"with image\"\n        Z = [5 4 3 1 0 ;\n             2 2 0 0 1 ;\n             0 0 0 1 0 ;\n             0 1 2 4 3]\n        f = imagesc(Z)\n        @test f isa Figure\n        @test f(1,1).plotline == \"with image\"\n        Z = 255*randn(3,10,10)\n        f = imagesc(Z)\n        @test f isa Figure\n        @test f(1,1).plotline == \"with rgbimage\"\n    end\nend\n\n@testset \"3d plot plot styles\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        x = y = -15:0.4:15\n        f1 = (x,y) -> @. sin(sqrt(x*x+y*y))/sqrt(x*x+y*y)\n        f = @gpkw wireframe({title=\"'wireframe'\"},x,y,f1,\"lc 'turquoise'\")\n        @test f isa Figure\n        @test f(1).settings == \"set hidden3d\\nset title 'wireframe'\"\n        @test f(1,1).plotline == \"lc 'turquoise'\"\n        @gpkw wireframe!({title=\"'wireframe'\"},x.-5,y,f1,\"lc 'orange'\")\n        @test length(f) == 1\n        @test length(f(1)) == 2\n        @test f(1,2).plotline == \"lc 'orange'\"\n        plot(f[2], 1:10)\n        @test length(f) == 2\n        f = @gpkw surf({title=\"'surf'\"},x,y,f1,\"lc 'turquoise'\")\n        @test f isa Figure\n        @test f(1).settings == \"set hidden3d\\nset title 'surf'\"\n        @test f(1,1).plotline == \"with pm3d lc 'turquoise'\"\n        @gpkw surf!({title=\"'surf'\"},x.-5,y,f1,\"lc 'orange'\")\n        @test length(f) == 1\n        @test length(f(1)) == 2\n        @test f(1,2).plotline == \"with pm3d lc 'orange'\"\n        f = @gpkw surfcontour(x,y,f1,\"lc 'turquoise'\")\n        @test f isa Figure\n        @test f(1).settings == \"set hidden3d\\nunset key\\nset contour base\\nset cntrlabel font ',7'\\nset cntrparam levels auto 10\"\n        @test f(1,1).plotline == \"lc 'turquoise'\"\n        @test length(f) == 1\n        @test length(f(1)) == 2\n        @test f(1,2).plotline == \"with labels lc 'turquoise'\"\n        f = @gpkw surfcontour(x,y,f1,labels=false)\n        @test length(f(1)) == 1\n        f = wiresurf(x,y,f1)\n        @test f isa Figure\n        @test f(1).settings == \"set hidden3d\\nset pm3d implicit depthorder border lc 'black' lw 0.3\"\n        f = scatter3(rand(10),rand(10),rand(10))\n        scatter3!(f,rand(10),rand(10),rand(10))\n        @test f isa Figure\n        @test length(f(1)) == 2\n        f = contour(x,y,f1,labels=false)\n        @test f isa Figure\n        @test length(f(1)) == 1\n        f = contour(x,y,f1)\n        @test f isa Figure\n        @test length(f(1)) == 2\n        f = @gpkw heatmap({palette=:summer},x,y,f1)\n        @test f isa Figure\n        @test f(1,1).plotline == \"with pm3d\"\n    end\nend\n\n@testset \"Multiplot\" begin\n    if Gaston.state.enabled\n        closeall()\n        reset()\n        null()\n        f = plot(1:10)\n        plot(f[10], 1:10)\n        @test f isa Figure\n        @test length(f) == 10\n        @test imagesc(f[1], rand(5,5)) isa Figure\n        @test stem(f[2], rand(5)) isa Figure\n        @test stem!(f[2], rand(5)) isa Figure\n        @test scatter(f[4], rand(5), rand(5)) isa Figure\n        @test stem!(f[4], rand(5)) isa Figure\n        @test scatter3(f[5], rand(5), rand(5), rand(5)) isa Figure\n        @test contour(f[7], rand(5,5)) isa Figure\n        @test surfcontour(f[6], rand(5,5)) isa Figure\n        @test contour(f[7], rand(5,5)) isa Figure\n    end\nend\n\n@testset \"Saving plots\" begin\n    if Gaston.state.enabled\n        closeall()\n        null()\n        reset()\n        t = mktempdir()\n        cd(t)\n        f1 = plot(1:10)\n        save()\n        @test isfile(\"figure-1.png\")\n        rm(\"figure-1.png\", force=true)\n        save(filename = \"test.png\")\n        @test isfile(\"test.png\")\n        rm(\"test.png\", force=true)\n        save(term=\"pdf enhanced\")\n        @test isfile(\"figure-1.pdf\")\n        rm(\"figure-1.pdf\", force=true)\n        Figure()\n        f2 = plot(1:10)\n        @test f2.handle == 2\n        save(f2)\n        @test isfile(\"figure-2.png\")\n        rm(\"figure-2.png\", force=true)\n        save(f2, filename = \"test2.png\")\n        @test isfile(\"test2.png\")\n        rm(\"test2.png\", force=true)\n        save(f2,term=\"pdf\")\n        @test isfile(\"figure-2.pdf\")\n        rm(\"figure-2.pdf\", force=true)\n        save(f2, filename=\"test.pdf\",term=\"pdf\")\n        @test isfile(\"test.pdf\")\n        rm(\"test.pdf\", force=true)\n    end\nend\n\n@testset \"Recipes\" begin\n    if Gaston.state.enabled\n        null()\n        struct Data1\n            samples\n        end\n        x = Data1(rand(10))\n        function Gaston.convert_args(d::Data1, args... ; pl = \"\", kwargs...)\n            x = 1:length(d.samples)\n            y = d.samples\n            PlotRecipe((x, y), pl)\n        end\n        r1 = plot(x)\n        @test r1 isa Figure\n        struct Data2 end\n        function Gaston.convert_args(::Data2)\n            x = range(0,1,10)\n            p1 = PlotRecipe((x, sin.(x)), \"\")\n            p2 = PlotRecipe((x, cos.(x)), \"\")\n            @gpkw s = {\"grid\"}\n            AxisRecipe(s, [p1, p2], false)\n        end\n        r2 = plot(Data2())\n        @test r2 isa Figure\n        struct Data3 end\n        function Gaston.convert_args(::Data3)\n            t1 = range(0, 1, 40)\n            t2 = range(-5, 5, 50)\n            z = Gaston.meshgrid(t2, t2, (x,y) -> cos(x)*cos(y))\n            @gpkw a1 = AxisRecipe({title = Q\"First Axis\"}, [PlotRecipe((1:10, rand(10)))])\n            @gpkw a2 = AxisRecipe({title = Q\"Trig\"}, [PlotRecipe((t1, sin.(5t1)), {lc = Q\"black\"}),\n                                                      PlotRecipe((t1, cos.(5t1)), {w = \"p\", pt = 16})])\n            @gpkw a3 = AxisRecipe({title = Q\"Surface\", tics = false, palette = (:matter, :reverse)},\n                                  [PlotRecipe((t2, t2, z), {w = \"pm3d\"})], true)\n            @gpkw a4 = AxisRecipe({tics, title = false, title = Q\"Last Axis\"},\n                                  [PlotRecipe((1:10, 1:10, rand(10,10)), \"w image\")])\n            # return named tuple with four axes\n            FigureRecipe([a1, a2, a3, a4],\n                         \"title 'A Four-Axes Recipe' layout 2,2\",\n                         false)\n        end\n        r3 = plot(Data3())\n        @test r3 isa Figure\n    end\nend\n\n@testset \"Misc\" begin\n    if Gaston.state.enabled\n        @test Gaston.gp_exec(\"set grid\") == \"\"\n        null()\n        t = Gaston.terminals()\n        @test isnothing(t)\n    end\nend\n\ncloseall()\n\ninclude(\"preferences.jl\")\ninclude(\"downstream.jl\")\n"
  }
]