Compare commits
192 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 40cbddab29 | |||
| 853cda4b80 | |||
| 91ec0ad077 | |||
| 4be2304ec1 | |||
| d0ee402939 | |||
| 2d095daddc | |||
| cf4fcf3351 | |||
| 42d57fb8c2 | |||
| ba13c1528f | |||
| 857fb3342a | |||
| 17189e3125 | |||
| d0209d945d | |||
| 1b6bfa1259 | |||
| 4ea813c983 | |||
| 71d2af07da | |||
| b36f8c701e | |||
| d64d3ef101 | |||
| 05fa4a92de | |||
| 990314cefc | |||
| 60d97ad9c9 | |||
| 6bfa31a89f | |||
| 8d8ac10429 | |||
| 56f398fb55 | |||
| cf5aed00e2 | |||
| de616dfdf0 | |||
| 3cf428d7ea | |||
| 29d93ba33e | |||
| 76c84c7615 | |||
| e129c29e2f | |||
| 52d325b446 | |||
| 74efd98699 | |||
| 28effaea36 | |||
| 6256549f44 | |||
| 2adec2fa7b | |||
| 32e816b713 | |||
| 6c7b113de5 | |||
| 1d5cce61ac | |||
| 85f91b9075 | |||
| aa64a0d3ce | |||
| e9d95a85b7 | |||
| 625c92a985 | |||
| 3cc9425219 | |||
| 0a86ccb142 | |||
| ce82e07dc9 | |||
| 048c60614c | |||
| 148fa6d151 | |||
| 088543176f | |||
| 536746d47e | |||
| c15cbad396 | |||
| d3baa58c7e | |||
| 7c885de6c7 | |||
| 010b8a382e | |||
| 314053af54 | |||
| 34a7f8a99c | |||
| 87031984d9 | |||
| 83687f96a5 | |||
| f33905ed80 | |||
| f91c4d586c | |||
| 77ec4adcad | |||
| b5a8186d99 | |||
| ed25e554e1 | |||
| a388ec71e3 | |||
| bde3c2e19d | |||
| d1d8d196ba | |||
| 7be5aebbc9 | |||
| d15fcb6b24 | |||
| b3ac38a6f1 | |||
| dae9dad2f7 | |||
| 603dc30bb1 | |||
| 71070da13e | |||
| 8fa1d4eb9b | |||
| 41f91f9cfb | |||
| 5699f3c6bc | |||
| 300babd6b1 | |||
| c1baca181c | |||
| 851fe9f72c | |||
| cf635cbdd9 | |||
| abbd8635dd | |||
| 2c2218bcb9 | |||
| bb6c06d3f7 | |||
| 90e22f179e | |||
| 0a00d1fbf3 | |||
| 6f0b8d1d7b | |||
| ef0d99340a | |||
| d4d388a94b | |||
| 7022ce7553 | |||
| 4a96122067 | |||
| 862ac3af8a | |||
| 333c2765fe | |||
| 89feb68b7c | |||
| 1b901673fc | |||
| fb60c244f6 | |||
| 3cea2df459 | |||
| 1cf514d1e6 | |||
| 65e18a8669 | |||
| 3e8f325ddc | |||
| 0d96c49f4a | |||
| bfc3fc1dec | |||
| 327f235af9 | |||
| df7fc0c4df | |||
| 33d9664df5 | |||
| 0d237028e7 | |||
| 80b9115393 | |||
| 606229a08f | |||
| 2ac17afe30 | |||
| 70f23e695b | |||
| 3084b0c41c | |||
| b75db9e473 | |||
| c89e9c5f73 | |||
| 61107b8577 | |||
| 35ff449dc3 | |||
| ae1f5b8b06 | |||
| c5bcae1e34 | |||
| e9ab6e7301 | |||
| a6ce2e691d | |||
| bfe04bdd15 | |||
| 2d0f9f01d5 | |||
| 87a967ad9c | |||
| 290b17a5f9 | |||
| 76572b990b | |||
| 54efb48d62 | |||
| 83bd780f2a | |||
| 2ba4ac6d20 | |||
| 98dc52f124 | |||
| fbb2f30f3f | |||
| 8ec3d18339 | |||
| 26779cbf4a | |||
| 4f429fb536 | |||
| 7e1706f700 | |||
| d491b29037 | |||
| cf9b60660d | |||
| a5e9ad9f19 | |||
| 6049a9fa0a | |||
| 5196ce5263 | |||
| 9231da4610 | |||
| 89f8030ba1 | |||
| 534f57b227 | |||
| 4d3ebf31d4 | |||
| d778c7922d | |||
| 5109c6f72b | |||
| 5005d32afd | |||
| 88be655dca | |||
| fe5789d747 | |||
| 00201b5f9c | |||
| 85f7acf9d5 | |||
| 44d1cdc2b2 | |||
| 90c2202f6e | |||
| d30574de24 | |||
| dec3748fe5 | |||
| e8cdff2310 | |||
| a0ca9d675f | |||
| baf410c712 | |||
| a81af2b449 | |||
| 678dde710b | |||
| 93a35d8fdc | |||
| 0878f44b09 | |||
| b89ae245ec | |||
| 536da01714 | |||
| a1b991aaeb | |||
| 7bee9a5829 | |||
| 17a90e3b00 | |||
| 43ba9b1172 | |||
| fdb01046e6 | |||
| 09421c9189 | |||
| c563204b0a | |||
| 33b3ee89f3 | |||
| 008b61c9f6 | |||
| 7546db4d42 | |||
| 925243969c | |||
| 7bce4a963e | |||
| 42d09a6d80 | |||
| b6334176e4 | |||
| 42a175bd53 | |||
| b4fb9af8e4 | |||
| ed6c9fe432 | |||
| 7e04aa438f | |||
| f1679f82c1 | |||
| 1808b81d59 | |||
| cd25c7acb2 | |||
| cf03779c5b | |||
| d3439f0d7e | |||
| 2df7f84939 | |||
| 74c76c48d5 | |||
| cdc0d22b01 | |||
| 5a520aadc0 | |||
| ec87489dd4 | |||
| e30affdfc2 | |||
| 66bd2559b9 | |||
| c8d6b1fab0 | |||
| 548c59719b | |||
| 59c1e24371 | |||
| 3c09921cbd |
+5
-3
@@ -4,9 +4,11 @@ os:
|
||||
- linux
|
||||
- osx
|
||||
julia:
|
||||
- 0.4
|
||||
#- nightly
|
||||
|
||||
- release
|
||||
- nightly
|
||||
matrix:
|
||||
allow_failures:
|
||||
- julia: nightly
|
||||
|
||||
# # before install:
|
||||
# # - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then brew update ; fi
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
|
||||
# Plots.jl NEWS
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- All new development should target 0.7!
|
||||
|
||||
---
|
||||
|
||||
## 0.7 (current master/dev)
|
||||
|
||||
#### 0.7.0
|
||||
|
||||
- Check out [the summary](http://plots.readthedocs.io/en/latest/plots_v0.7/)
|
||||
- Revamped and simplified internals
|
||||
- [Recipes, recipes, recipes](https://github.com/JuliaPlots/RecipesBase.jl/issues/6)
|
||||
- [Layouts and Subplots](https://github.com/tbreloff/Plots.jl/issues/60)
|
||||
- DataFrames is loaded automatically when installed
|
||||
- Overhaul to GroupBy mechanic (now offloads to a recipe)
|
||||
- Replaced much of the argument processing with recipes
|
||||
- Added series recipes, and began to strip down un-needed backend code. Some recipes:
|
||||
- line, step, sticks, bar, histogram, histogram2d, boxplot, violin, quiver, errorbars, density, ohlc
|
||||
- Added `@shorthands` and `@userplot` macros for recipe convenience
|
||||
- Better handling of errorbars and ribbons
|
||||
- New Axis type
|
||||
- Tracks extrema and discrete values
|
||||
- New `link_axes` functionality
|
||||
- `linetype` has been renamed `seriestype` (the alias is reversed)
|
||||
- Many fixes and huge cleanup in GR
|
||||
- Brand new subplot layout mechanics:
|
||||
- `@layout` macro
|
||||
- AbstractLayout, Subplot, GridLayout, and everything related
|
||||
- Added dependency on Measures.jl
|
||||
- Computations of axis/guide sizes and precise positioning
|
||||
- Refactored and compartmentalized default dictionaries for attributes
|
||||
- Deprecated Gadfly and Immerse backends
|
||||
- Added `series_annotations` attribute (previously that functionality was merged with `annotations`, which are not series-specific)
|
||||
- Removed `axis` attribute... currently not supporting twin (right) y axes
|
||||
- Check for `ENV["PLOTS_USE_ATOM_PLOTPANE"]` and default to false
|
||||
- Improved backend interface to reduce redundant code. Template updated.
|
||||
- Added `html_output_format`, primarily for choosing between png and svg output in IJulia.
|
||||
- Partial support of Julia v0.5
|
||||
- Switched testing to dump reference images to JuliaPlots/PlotReferenceImages.jl
|
||||
- Moved docs-specific code to new JuliaPlots/PlotDocs.jl
|
||||
- Moved example list from ExamplePlots into Plots.
|
||||
- Added several examples and improved others.
|
||||
- Many other smaller changes and bug fixes.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Version 0.6
|
||||
|
||||
#### 0.6.2
|
||||
|
||||
- `linewidth` fixes
|
||||
- `markershape` fix
|
||||
- converted center calc to centroid for shapes
|
||||
- new dependency on [RecipesBase](https://github.com/JuliaPlots/RecipesBase.jl)
|
||||
- REQUIRE upper limit for RecipesBase: 0.0.1
|
||||
- GR fixes/improvements (@jheinen)
|
||||
- support `zlims`, `bins`
|
||||
- allow Plots colormaps
|
||||
- other bug fixes
|
||||
- native image support
|
||||
- PGFPlots fixes/improvements (@pkofod)
|
||||
- DataFrames are handled by recipes
|
||||
- Plotly: zaxis, tick rotation, 3d axis fix
|
||||
- Improvements in handling discrete data
|
||||
- Support for image display
|
||||
- `arrow` keyword and support for adding arrows to paths
|
||||
- changed quiver recipe to use arrows
|
||||
- Bug fixes for boxplots, heatmaps, and more
|
||||
|
||||
#### 0.6.1
|
||||
|
||||
- `rotation` keyword
|
||||
- improved supported graphs
|
||||
- subplot bug fix
|
||||
|
||||
#### 0.6.0
|
||||
|
||||
- `apply_series_recipe` framework for built-in recipes
|
||||
- [boxplot/violin recipes](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/boxplot.ipynb)
|
||||
- [errorbar/ribbon recipes](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/errorbars.ipynb)
|
||||
- [quiver recipe](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/quiver.ipynb)
|
||||
- `polar` coordinates
|
||||
- better support for shapes and custom polygons (see [batman](https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/batman.ipynb))
|
||||
- z-axis keywords
|
||||
- 3D indexing overhaul: `push!`, `append!` support
|
||||
- matplotlib colormap constants (`:inferno` is the new default colormap for Plots)
|
||||
- `typealias KW Dict{Symbol,Any}` used in place of splatting in many places
|
||||
- png generation for plotly backend using wkhtmltoimage
|
||||
- `normalize` and `weights` keywords
|
||||
- background/foreground subcategories for fine-tuning of looks
|
||||
- `add_theme`/`set_theme` and ggplot2 theme (see [this issue](https://github.com/tbreloff/Plots.jl/issues/201))
|
||||
- `PLOTS_DEFAULT_BACKEND` environment variable
|
||||
- `barh` linetype
|
||||
- support for non-gridded surfaces with pyplot's trisurface
|
||||
- pyplot surface zcolor
|
||||
- internal refactor of supported.jl
|
||||
- `wrap` method to bypass input processing
|
||||
- `translate`, `scale` and `rotate` methods for coordinates and shapes
|
||||
- and many more minor fixes and improvements
|
||||
|
||||
---
|
||||
|
||||
## Version 0.5
|
||||
|
||||
#### 0.5.4
|
||||
|
||||
- old heatmaps have been renamed to hist2d, and true heatmaps implemented (see https://github.com/tbreloff/Plots.jl/issues/147)
|
||||
- lots of reorganization and redesign of the internals
|
||||
- lots of renaming to keep to conventions: AbstractPlot, AbstractBackend, etc
|
||||
- initial redesign of layouts
|
||||
- integration with Atom PlotPane
|
||||
- arc diagram and chord diagram (thanks to @diegozea: see https://github.com/tbreloff/Plots.jl/issues/163)
|
||||
- work on GR, GLVisualize, and PGFPlots backends (thanks @jheinen @dlfivefifty @pkofod)
|
||||
- improvements to Plotly setup (thanks @spencerlyon2)
|
||||
- overhaul to series creation logic and groupby mechanic
|
||||
- replace Dict with `typealias KW Dict{Symbol,Any}` in many places, also replacing keyword arg splatting
|
||||
- new `shape` linetype for plotting polygons in plot-coordinates (see https://github.com/tbreloff/ExamplePlots.jl/blob/master/notebooks/batman.ipynb)
|
||||
- many other fixes
|
||||
|
||||
#### 0.5.3
|
||||
|
||||
- `@gif` macro with `every`/`when` syntax
|
||||
- bezier curves and other graph drawing helpers
|
||||
- added FixedSizeArrays dependency with relevant functionality
|
||||
- merged lots of improvements to GR (thanks @jheinen)
|
||||
- `overwrite_figure`/`reuse` arg for reusing the same figure window
|
||||
- deprecated Qwt, Winston, and Bokeh backends
|
||||
- improved handling of 3D inputs (call `z=rand(10,10); surface(z)` for example)
|
||||
- fix IJulia display issue
|
||||
- lots of progress on PlotlyJS backend
|
||||
- and many other changes and fixes...
|
||||
|
||||
#### 0.5.2
|
||||
|
||||
- Added [GR.jl](https://github.com/jheinen/GR.jl) as a backend (unfinished but functional) All credit to @jheinen
|
||||
- Set defaults within backend calls (i.e. `gadfly(legend=false)`)
|
||||
- `abline!`; also extrema allows plotting functions without giving x (i.e. `plot(cos, 0, 10); plot!(sin)`) @pkofod @joshday
|
||||
- Integration with [PlotlyJS.jl](https://github.com/spencerlyon2/PlotlyJS.jl) for using Plotly inside a Blink window @spencerlyon2
|
||||
- The Plotly backend has been split into my built-in version (`plotly()`) and @spencerlyon2's backend (`plotlyjs()`)
|
||||
- Revamped backend setup code for easily adding new backends
|
||||
- New docs (WIP) at http://plots.readthedocs.org/
|
||||
- Overhaul to `:legend` keyword (see https://github.com/tbreloff/Plots.jl/issues/135)
|
||||
- New dependency on Requires, allows auto-loading of DataFrames support
|
||||
- Support for plotting lists of Tuples and FixedSizeArrays
|
||||
- new `@animate` macro for super simple animations (see https://github.com/tbreloff/Plots.jl/issues/111#issuecomment-181515616)
|
||||
- allow Function for `:fillrange` and `zcolor` arguments (for example: `scatter(sin, 0:10, marker=15, fill=(cos,0.4), zcolor=sin)`)
|
||||
- allow vectors of PlotText without x/y coords (for example: `scatter(rand(10), m=20, ann=map(text, 1:10))`)
|
||||
- Lots and lots of fixes
|
||||
|
||||
#### 0.5.1
|
||||
|
||||
#### 0.5.0
|
||||
|
||||
- `with` function for temporary defaults
|
||||
- contours
|
||||
- basic 3D plotting
|
||||
- preliminary support for Bokeh
|
||||
- `stroke` and `brush` for more fine-tuned control over visuals
|
||||
- smarter "magic" arguments: `line`, `marker`
|
||||
@@ -1,7 +1,8 @@
|
||||
julia 0.4
|
||||
|
||||
RecipesBase
|
||||
Colors
|
||||
Reexport
|
||||
Compat
|
||||
Requires
|
||||
FixedSizeArrays
|
||||
Measures
|
||||
|
||||
+170
-105
@@ -6,20 +6,27 @@ module Plots
|
||||
using Compat
|
||||
using Reexport
|
||||
@reexport using Colors
|
||||
using Requires
|
||||
# using Requires
|
||||
using FixedSizeArrays
|
||||
@reexport using RecipesBase
|
||||
using Base.Meta
|
||||
|
||||
export
|
||||
AbstractPlot,
|
||||
Plot,
|
||||
Subplot,
|
||||
SubplotLayout,
|
||||
AbstractLayout,
|
||||
GridLayout,
|
||||
RowsLayout,
|
||||
FlexLayout,
|
||||
grid,
|
||||
EmptyLayout,
|
||||
@layout,
|
||||
# RowsLayout,
|
||||
# FlexLayout,
|
||||
AVec,
|
||||
AMat,
|
||||
KW,
|
||||
# attr,
|
||||
# attr!,
|
||||
|
||||
wrap,
|
||||
set_theme,
|
||||
@@ -27,60 +34,62 @@ export
|
||||
|
||||
plot,
|
||||
plot!,
|
||||
subplot,
|
||||
subplot!,
|
||||
# subplot,
|
||||
# subplot!,
|
||||
|
||||
current,
|
||||
default,
|
||||
with,
|
||||
|
||||
scatter,
|
||||
scatter!,
|
||||
bar,
|
||||
bar!,
|
||||
barh,
|
||||
barh!,
|
||||
histogram,
|
||||
histogram!,
|
||||
histogram2d,
|
||||
histogram2d!,
|
||||
density,
|
||||
density!,
|
||||
heatmap,
|
||||
heatmap!,
|
||||
hexbin,
|
||||
hexbin!,
|
||||
sticks,
|
||||
sticks!,
|
||||
hline,
|
||||
hline!,
|
||||
vline,
|
||||
vline!,
|
||||
ohlc,
|
||||
ohlc!,
|
||||
@userplot,
|
||||
@shorthands,
|
||||
# scatter,
|
||||
# scatter!,
|
||||
# bar,
|
||||
# bar!,
|
||||
# barh,
|
||||
# barh!,
|
||||
# histogram,
|
||||
# histogram!,
|
||||
# histogram2d,
|
||||
# histogram2d!,
|
||||
# density,
|
||||
# density!,
|
||||
# heatmap,
|
||||
# heatmap!,
|
||||
# hexbin,
|
||||
# hexbin!,
|
||||
# sticks,
|
||||
# sticks!,
|
||||
# hline,
|
||||
# hline!,
|
||||
# vline,
|
||||
# vline!,
|
||||
# ohlc,
|
||||
# ohlc!,
|
||||
pie,
|
||||
pie!,
|
||||
contour,
|
||||
contour!,
|
||||
contour3d,
|
||||
contour3d!,
|
||||
surface,
|
||||
surface!,
|
||||
wireframe,
|
||||
wireframe!,
|
||||
path3d,
|
||||
path3d!,
|
||||
# contour,
|
||||
# contour!,
|
||||
# contour3d,
|
||||
# contour3d!,
|
||||
# surface,
|
||||
# surface!,
|
||||
# wireframe,
|
||||
# wireframe!,
|
||||
# path3d,
|
||||
# path3d!,
|
||||
plot3d,
|
||||
plot3d!,
|
||||
scatter3d,
|
||||
scatter3d!,
|
||||
abline!,
|
||||
boxplot,
|
||||
boxplot!,
|
||||
violin,
|
||||
violin!,
|
||||
quiver,
|
||||
quiver!,
|
||||
# scatter3d,
|
||||
# scatter3d!,
|
||||
# abline!,
|
||||
# boxplot,
|
||||
# boxplot!,
|
||||
# violin,
|
||||
# violin!,
|
||||
# quiver,
|
||||
# quiver!,
|
||||
|
||||
title!,
|
||||
xlabel!,
|
||||
@@ -109,10 +118,15 @@ export
|
||||
Shape,
|
||||
text,
|
||||
font,
|
||||
Axis,
|
||||
# xaxis,
|
||||
# yaxis,
|
||||
# zaxis,
|
||||
stroke,
|
||||
brush,
|
||||
Surface,
|
||||
OHLC,
|
||||
arrow,
|
||||
|
||||
colorscheme,
|
||||
ColorScheme,
|
||||
@@ -144,6 +158,12 @@ export
|
||||
arcdiagram,
|
||||
chorddiagram,
|
||||
|
||||
# @kw,
|
||||
# @recipe,
|
||||
# @plotrecipe,
|
||||
|
||||
test_examples,
|
||||
|
||||
translate,
|
||||
translate!,
|
||||
rotate,
|
||||
@@ -157,69 +177,117 @@ export
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
import Measures
|
||||
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height
|
||||
typealias BBox Measures.Absolute2DBox
|
||||
export BBox, BoundingBox, mm, cm, inch, pt, px, pct
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
include("types.jl")
|
||||
include("utils.jl")
|
||||
include("colors.jl")
|
||||
include("components.jl")
|
||||
include("axes.jl")
|
||||
include("backends.jl")
|
||||
include("args.jl")
|
||||
include("themes.jl")
|
||||
include("plot.jl")
|
||||
include("series_args.jl")
|
||||
include("subplot.jl")
|
||||
include("series_new.jl")
|
||||
include("layouts.jl")
|
||||
include("subplots.jl")
|
||||
include("recipes.jl")
|
||||
include("animation.jl")
|
||||
include("output.jl")
|
||||
include("examples.jl")
|
||||
include("arg_desc.jl")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
scatter(args...; kw...) = plot(args...; kw..., linetype = :scatter)
|
||||
scatter!(args...; kw...) = plot!(args...; kw..., linetype = :scatter)
|
||||
bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
|
||||
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
|
||||
barh(args...; kw...) = plot(args...; kw..., linetype = :barh, orientation = :h)
|
||||
barh!(args...; kw...) = plot!(args...; kw..., linetype = :barh, orientation = :h)
|
||||
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
|
||||
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
|
||||
histogram2d(args...; kw...) = plot(args...; kw..., linetype = :hist2d)
|
||||
histogram2d!(args...; kw...) = plot!(args...; kw..., linetype = :hist2d)
|
||||
density(args...; kw...) = plot(args...; kw..., linetype = :density)
|
||||
density!(args...; kw...) = plot!(args...; kw..., linetype = :density)
|
||||
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
|
||||
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
|
||||
hexbin(args...; kw...) = plot(args...; kw..., linetype = :hexbin)
|
||||
hexbin!(args...; kw...) = plot!(args...; kw..., linetype = :hexbin)
|
||||
sticks(args...; kw...) = plot(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
sticks!(args...; kw...) = plot!(args...; kw..., linetype = :sticks, marker = :ellipse)
|
||||
hline(args...; kw...) = plot(args...; kw..., linetype = :hline)
|
||||
hline!(args...; kw...) = plot!(args...; kw..., linetype = :hline)
|
||||
vline(args...; kw...) = plot(args...; kw..., linetype = :vline)
|
||||
vline!(args...; kw...) = plot!(args...; kw..., linetype = :vline)
|
||||
ohlc(args...; kw...) = plot(args...; kw..., linetype = :ohlc)
|
||||
ohlc!(args...; kw...) = plot!(args...; kw..., linetype = :ohlc)
|
||||
pie(args...; kw...) = plot(args...; kw..., linetype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
pie!(args...; kw...) = plot!(args...; kw..., linetype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
contour(args...; kw...) = plot(args...; kw..., linetype = :contour)
|
||||
contour!(args...; kw...) = plot!(args...; kw..., linetype = :contour)
|
||||
contour3d(args...; kw...) = plot(args...; kw..., linetype = :contour3d)
|
||||
contour3d!(args...; kw...) = plot!(args...; kw..., linetype = :contour3d)
|
||||
surface(args...; kw...) = plot(args...; kw..., linetype = :surface)
|
||||
surface!(args...; kw...) = plot!(args...; kw..., linetype = :surface)
|
||||
wireframe(args...; kw...) = plot(args...; kw..., linetype = :wireframe)
|
||||
wireframe!(args...; kw...) = plot!(args...; kw..., linetype = :wireframe)
|
||||
path3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
|
||||
path3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
|
||||
plot3d(args...; kw...) = plot(args...; kw..., linetype = :path3d)
|
||||
plot3d!(args...; kw...) = plot!(args...; kw..., linetype = :path3d)
|
||||
scatter3d(args...; kw...) = plot(args...; kw..., linetype = :scatter3d)
|
||||
scatter3d!(args...; kw...) = plot!(args...; kw..., linetype = :scatter3d)
|
||||
boxplot(args...; kw...) = plot(args...; kw..., linetype = :box)
|
||||
boxplot!(args...; kw...) = plot!(args...; kw..., linetype = :box)
|
||||
violin(args...; kw...) = plot(args...; kw..., linetype = :violin)
|
||||
violin!(args...; kw...) = plot!(args...; kw..., linetype = :violin)
|
||||
quiver(args...; kw...) = plot(args...; kw..., linetype = :quiver)
|
||||
quiver!(args...; kw...) = plot!(args...; kw..., linetype = :quiver)
|
||||
# define and export shorthand plotting method definitions
|
||||
macro shorthands(funcname::Symbol)
|
||||
funcname2 = Symbol(funcname, "!")
|
||||
ret = esc(quote
|
||||
export $funcname, $funcname2
|
||||
$funcname(args...; kw...) = plot(args...; kw..., seriestype = $(quot(funcname)))
|
||||
$funcname2(args...; kw...) = plot!(args...; kw..., seriestype = $(quot(funcname)))
|
||||
end)
|
||||
# dump(ret,20)
|
||||
# @show ret
|
||||
ret
|
||||
end
|
||||
|
||||
@shorthands scatter
|
||||
@shorthands bar
|
||||
@shorthands barh
|
||||
@shorthands histogram
|
||||
@shorthands histogram2d
|
||||
@shorthands density
|
||||
@shorthands heatmap
|
||||
@shorthands hexbin
|
||||
@shorthands sticks
|
||||
@shorthands hline
|
||||
@shorthands vline
|
||||
@shorthands ohlc
|
||||
# @shorthands pie
|
||||
@shorthands contour
|
||||
@shorthands contour3d
|
||||
@shorthands surface
|
||||
@shorthands wireframe
|
||||
@shorthands path3d
|
||||
@shorthands scatter3d
|
||||
@shorthands boxplot
|
||||
@shorthands violin
|
||||
@shorthands quiver
|
||||
|
||||
# scatter(args...; kw...) = plot(args...; kw..., seriestype = :scatter)
|
||||
# scatter!(args...; kw...) = plot!(args...; kw..., seriestype = :scatter)
|
||||
# bar(args...; kw...) = plot(args...; kw..., seriestype = :bar)
|
||||
# bar!(args...; kw...) = plot!(args...; kw..., seriestype = :bar)
|
||||
# barh(args...; kw...) = plot(args...; kw..., seriestype = :barh, orientation = :h)
|
||||
# barh!(args...; kw...) = plot!(args...; kw..., seriestype = :barh, orientation = :h)
|
||||
# histogram(args...; kw...) = plot(args...; kw..., seriestype = :histogram)
|
||||
# histogram!(args...; kw...) = plot!(args...; kw..., seriestype = :histogram)
|
||||
# histogram2d(args...; kw...) = plot(args...; kw..., seriestype = :histogram2d)
|
||||
# histogram2d!(args...; kw...) = plot!(args...; kw..., seriestype = :histogram2d)
|
||||
# density(args...; kw...) = plot(args...; kw..., seriestype = :density)
|
||||
# density!(args...; kw...) = plot!(args...; kw..., seriestype = :density)
|
||||
# heatmap(args...; kw...) = plot(args...; kw..., seriestype = :heatmap)
|
||||
# heatmap!(args...; kw...) = plot!(args...; kw..., seriestype = :heatmap)
|
||||
# hexbin(args...; kw...) = plot(args...; kw..., seriestype = :hexbin)
|
||||
# hexbin!(args...; kw...) = plot!(args...; kw..., seriestype = :hexbin)
|
||||
# sticks(args...; kw...) = plot(args...; kw..., seriestype = :sticks, marker = :ellipse)
|
||||
# sticks!(args...; kw...) = plot!(args...; kw..., seriestype = :sticks, marker = :ellipse)
|
||||
# hline(args...; kw...) = plot(args...; kw..., seriestype = :hline)
|
||||
# hline!(args...; kw...) = plot!(args...; kw..., seriestype = :hline)
|
||||
# vline(args...; kw...) = plot(args...; kw..., seriestype = :vline)
|
||||
# vline!(args...; kw...) = plot!(args...; kw..., seriestype = :vline)
|
||||
# ohlc(args...; kw...) = plot(args...; kw..., seriestype = :ohlc)
|
||||
# ohlc!(args...; kw...) = plot!(args...; kw..., seriestype = :ohlc)
|
||||
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||
# contour(args...; kw...) = plot(args...; kw..., seriestype = :contour)
|
||||
# contour!(args...; kw...) = plot!(args...; kw..., seriestype = :contour)
|
||||
# contour3d(args...; kw...) = plot(args...; kw..., seriestype = :contour3d)
|
||||
# contour3d!(args...; kw...) = plot!(args...; kw..., seriestype = :contour3d)
|
||||
# surface(args...; kw...) = plot(args...; kw..., seriestype = :surface)
|
||||
# surface!(args...; kw...) = plot!(args...; kw..., seriestype = :surface)
|
||||
# wireframe(args...; kw...) = plot(args...; kw..., seriestype = :wireframe)
|
||||
# wireframe!(args...; kw...) = plot!(args...; kw..., seriestype = :wireframe)
|
||||
# path3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
|
||||
# path3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
|
||||
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
|
||||
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
|
||||
# scatter3d(args...; kw...) = plot(args...; kw..., seriestype = :scatter3d)
|
||||
# scatter3d!(args...; kw...) = plot!(args...; kw..., seriestype = :scatter3d)
|
||||
# boxplot(args...; kw...) = plot(args...; kw..., seriestype = :boxplot)
|
||||
# boxplot!(args...; kw...) = plot!(args...; kw..., seriestype = :boxplot)
|
||||
# violin(args...; kw...) = plot(args...; kw..., seriestype = :violin)
|
||||
# violin!(args...; kw...) = plot!(args...; kw..., seriestype = :violin)
|
||||
# quiver(args...; kw...) = plot(args...; kw..., seriestype = :quiver)
|
||||
# quiver!(args...; kw...) = plot!(args...; kw..., seriestype = :quiver)
|
||||
|
||||
|
||||
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
|
||||
@@ -272,16 +340,13 @@ yaxis!(plt::Plot, args...; kw...) = plot!(pl
|
||||
|
||||
const CURRENT_BACKEND = CurrentBackend(:none)
|
||||
|
||||
# setup_dataframes()
|
||||
|
||||
function __init__()
|
||||
|
||||
# override IJulia inline display
|
||||
if isijulia()
|
||||
@eval import IJulia
|
||||
IJulia.display_dict(plt::AbstractPlot) = Dict{ASCIIString, ByteString}("text/html" => sprint(writemime, "text/html", plt))
|
||||
end
|
||||
|
||||
setup_dataframes()
|
||||
setup_atom()
|
||||
setup_ijulia()
|
||||
# setup_dataframes()
|
||||
setup_atom()
|
||||
# add_axis_letter_defaults()
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
+15
-8
@@ -1,12 +1,12 @@
|
||||
|
||||
immutable Animation
|
||||
dir::ASCIIString
|
||||
frames::Vector{ASCIIString}
|
||||
dir::Compat.ASCIIString
|
||||
frames::Vector{Compat.ASCIIString}
|
||||
end
|
||||
|
||||
function Animation()
|
||||
tmpdir = convert(ASCIIString, mktempdir())
|
||||
Animation(tmpdir, ASCIIString[])
|
||||
tmpdir = convert(Compat.ASCIIString, mktempdir())
|
||||
Animation(tmpdir, Compat.ASCIIString[])
|
||||
end
|
||||
|
||||
function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
|
||||
@@ -21,20 +21,27 @@ end
|
||||
|
||||
"Wraps the location of an animated gif so that it can be displayed"
|
||||
immutable AnimatedGif
|
||||
filename::ASCIIString
|
||||
filename::Compat.ASCIIString
|
||||
end
|
||||
|
||||
function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Integer = 20)
|
||||
function gif(anim::Animation, fn::Compat.ASCIIString = "tmp.gif"; fps::Integer = 20)
|
||||
fn = abspath(fn)
|
||||
|
||||
try
|
||||
|
||||
# high quality
|
||||
speed = round(Int, 100 / fps)
|
||||
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png -alpha off $fn`)
|
||||
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
|
||||
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
|
||||
include(file)
|
||||
end
|
||||
prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
||||
run(`$(joinpath(prefix, "convert")) -delay $speed -loop 0 $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
|
||||
|
||||
catch err
|
||||
warn("Tried to create gif using convert (ImageMagick), but got error: $err\nWill try ffmpeg, but it's lower quality...)")
|
||||
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
|
||||
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`
|
||||
Will try ffmpeg, but it's lower quality...)""")
|
||||
|
||||
# low quality
|
||||
run(`ffmpeg -v 0 -framerate $fps -i $(anim.dir)/%06d.png -y $fn`)
|
||||
|
||||
+103
@@ -0,0 +1,103 @@
|
||||
|
||||
const _arg_desc = KW(
|
||||
|
||||
# series args
|
||||
:label => "String type. The label for a series, which appears in a legend. If empty, no legend entry is added.",
|
||||
:seriescolor => "Color Type. The base color for this series. `:auto` (the default) will select a color from the subplot's `color_palette`, based on the order it was added to the subplot",
|
||||
:seriesalpha => "Number in [0,1]. The alpha/opacity override for the series. `nothing` (the default) means it will take the alpha value of the color.",
|
||||
:seriestype => "Symbol. This is the identifier of the type of visualization for this series. Choose from $(_allTypes) or any series recipes which are defined.",
|
||||
:linestyle => "Symbol. Style of the line (for path and bar stroke). Choose from $(_allStyles)",
|
||||
:linewidth => "Number. Width of the line (in pixels)",
|
||||
:linecolor => "Color Type. Color of the line (for path and bar stroke). `:match` will take the value from `:seriescolor`, (though histogram/bar types use `:black` as a default).",
|
||||
:linealpha => "Number in [0,1]. The alpha/opacity override for the line. `nothing` (the default) means it will take the alpha value of linecolor.",
|
||||
:fillrange => "Number or AbstractVector. Fills area from this to y for line-types, sets the base for bar/stick types, and similar for other types.",
|
||||
:fillcolor => "Color Type. Color of the filled area of path or bar types. `:match` will take the value from `:seriescolor`.",
|
||||
:fillalpha => "Number in [0,1]. The alpha/opacity override for the fill area. `nothing` (the default) means it will take the alpha value of fillcolor.",
|
||||
:markershape => "Symbol, Shape, or AbstractVector. Choose from $(_allMarkers).",
|
||||
:markercolor => "Color Type. Color of the interior of the marker or shape. `:match` will take the value from `:seriescolor`.",
|
||||
:markeralpha => "Number in [0,1]. The alpha/opacity override for the marker interior. `nothing` (the default) means it will take the alpha value of markercolor.",
|
||||
:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.",
|
||||
:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)",
|
||||
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
|
||||
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:seriescolor`.",
|
||||
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector. For histogram-types, defines the number of bins, or the edges, of the histogram.",
|
||||
:smooth => "Bool. Add a regression line?",
|
||||
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
|
||||
:x => "Various. Input data. First Dimension",
|
||||
:y => "Various. Input data. Second Dimension",
|
||||
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
|
||||
:marker_z => "AbstractVector. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
|
||||
:levels => "Integer, NTuple{2,Integer}. Number of levels (or x-levels/y-levels) for a contour type.",
|
||||
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
|
||||
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
|
||||
:bar_width => "nothing or Number. Width of bars in data coordinates. When nothing, chooses based on x (or y when `orientation = :h`).",
|
||||
:bar_edges => "Bool. Align bars to edges (true), or centers (the default)?",
|
||||
:xerror => "AbstractVector or 2-Tuple of Vectors. x (horizontal) error relative to x-value. If 2-tuple of vectors, the first vector corresponds to the left error (and the second to the right)",
|
||||
:yerror => "AbstractVector or 2-Tuple of Vectors. y (vertical) error relative to y-value. If 2-tuple of vectors, the first vector corresponds to the bottom error (and the second to the top)",
|
||||
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
|
||||
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
|
||||
:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.",
|
||||
:normalize => "Bool. Should normalize histogram types? Trying for area == 1.",
|
||||
:weights => "AbstractVector. Used in histogram types for weighted counts.",
|
||||
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
|
||||
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
|
||||
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
|
||||
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
|
||||
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
|
||||
|
||||
# plot args
|
||||
:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
|
||||
:background_color => "Color Type. Base color for all backgrounds.",
|
||||
:background_color_outside => "Color Type or `:match` (matches `:background_color`). Color outside the plot area(s)",
|
||||
:foreground_color => "Color Type. Base color for all foregrounds.",
|
||||
:size => "NTuple{2,Int}. (width_px, height_px) of the whole Plot",
|
||||
:pos => "NTuple{2,Int}. (left_px, top_px) position of the GUI window (note: currently unimplemented)",
|
||||
:window_title => "String. Title of the window.",
|
||||
:show => "Bool. Should this command open/refresh a GUI/display? This allows displaying in scripts or functions without explicitly calling `display`",
|
||||
:layout => "Integer (number of subplots), NTuple{2,Integer} (grid dimensions), AbstractLayout (for example `grid(2,2)`), or the return from the `@layout` macro. This builds the layout of subplots.",
|
||||
:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).",
|
||||
:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).",
|
||||
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
|
||||
|
||||
# subplot args
|
||||
:title => "String. Subplot title.",
|
||||
:title_location => "Symbol. Position of subplot title. Values: `:left`, `:center`, `:right`",
|
||||
:titlefont => "Font. Font of subplot title.",
|
||||
:background_color_subplot => "Color Type or `:match` (matches `:background_color`). Base background color of the subplot.",
|
||||
:background_color_legend => "Color Type or `:match` (matches `:background_color_subplot`). Background color of the legend.",
|
||||
:background_color_inside => "Color Type or `:match` (matches `:background_color_subplot`). Background color inside the plot area (under the grid).",
|
||||
:foreground_color_subplot => "Color Type or `:match` (matches `:foreground_color`). Base foreground color of the subplot.",
|
||||
:foreground_color_legend => "Color Type or `:match` (matches `:foreground_color_subplot`). Foreground color of the legend.",
|
||||
:foreground_color_grid => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of grid lines.",
|
||||
:foreground_color_title => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of subplot title.",
|
||||
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
|
||||
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)",
|
||||
:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)",
|
||||
:legendfont => "Font. Font of legend items.",
|
||||
:grid => "Bool. Show the grid lines?",
|
||||
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
|
||||
:projection => "Symbol or String. '3d' or 'polar'",
|
||||
:aspect_ratio => "Symbol (:equal) or Number (width to height ratio of plot area).",
|
||||
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
|
||||
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
|
||||
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
|
||||
:right_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the right of the subplot.",
|
||||
:bottom_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the bottom of the subplot.",
|
||||
:subplot_index => "Integer. Internal (not set by user). Specifies the index of this subplot in the Plot's `plt.subplot` list.",
|
||||
|
||||
# axis args
|
||||
:guide => "String. Axis guide (label).",
|
||||
:lims => "NTuple{2,Number}. Force axis limits. Only finite values are used (you can set only the right limit with `xlims = (-Inf, 2)` for example).",
|
||||
:ticks => "Vector of numbers (set the tick values), Tuple of (tickvalues, ticklabels), or `:auto`",
|
||||
:scale => "Symbol. Scale of the axis: `:none`, `:ln`, `:log2`, `:log10`",
|
||||
:rotation => "Number. Degrees rotation of tick labels.",
|
||||
:flip => "Bool. Should we flip (reverse) the axis?",
|
||||
:tickfont => "Font. Font of axis tick labels.",
|
||||
:guidefont => "Font. Font of axis guide (label).",
|
||||
:foreground_color_axis => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis ticks.",
|
||||
:foreground_color_border => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of plot area border (spines).",
|
||||
:foreground_color_text => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of tick labels.",
|
||||
:foreground_color_guide => "Color Type or `:match` (matches `:foreground_color_subplot`). Color of axis guides (axis labels).",
|
||||
|
||||
)
|
||||
+548
-453
File diff suppressed because it is too large
Load Diff
+314
@@ -0,0 +1,314 @@
|
||||
|
||||
|
||||
# xaxis(args...; kw...) = Axis(:x, args...; kw...)
|
||||
# yaxis(args...; kw...) = Axis(:y, args...; kw...)
|
||||
# zaxis(args...; kw...) = Axis(:z, args...; kw...)
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
# init with values from _plot_defaults
|
||||
d = KW(
|
||||
:letter => letter,
|
||||
# :extrema => (Inf, -Inf),
|
||||
:extrema => Extrema(),
|
||||
:discrete_map => Dict(), # map discrete values to discrete indices
|
||||
:continuous_values => zeros(0),
|
||||
:use_minor => false,
|
||||
:show => true, # show or hide the axis? (useful for linked subplots)
|
||||
)
|
||||
|
||||
# get defaults from letter version, unless match
|
||||
for (k,v) in _axis_defaults
|
||||
lk = Symbol(letter, k)
|
||||
lv = _axis_defaults_byletter[lk]
|
||||
d[k] = (lv == :match ? v : lv)
|
||||
end
|
||||
|
||||
# merge!(d, _axis_defaults)
|
||||
d[:discrete_values] = []
|
||||
|
||||
# update the defaults
|
||||
update!(Axis(sp, d), args...; kw...)
|
||||
end
|
||||
|
||||
function process_axis_arg!(d::KW, arg, letter = "")
|
||||
T = typeof(arg)
|
||||
arg = get(_scaleAliases, arg, arg)
|
||||
|
||||
if typeof(arg) <: Font
|
||||
d[Symbol(letter,:tickfont)] = arg
|
||||
d[Symbol(letter,:guidefont)] = arg
|
||||
|
||||
elseif arg in _allScales
|
||||
d[Symbol(letter,:scale)] = arg
|
||||
|
||||
elseif arg in (:flip, :invert, :inverted)
|
||||
d[Symbol(letter,:flip)] = true
|
||||
|
||||
elseif T <: AbstractString
|
||||
d[Symbol(letter,:guide)] = arg
|
||||
|
||||
# xlims/ylims
|
||||
elseif (T <: Tuple || T <: AVec) && length(arg) == 2
|
||||
sym = typeof(arg[1]) <: Number ? :lims : :ticks
|
||||
d[Symbol(letter,sym)] = arg
|
||||
|
||||
# xticks/yticks
|
||||
elseif T <: AVec
|
||||
d[Symbol(letter,:ticks)] = arg
|
||||
|
||||
elseif arg == nothing
|
||||
d[Symbol(letter,:ticks)] = []
|
||||
|
||||
elseif typeof(arg) <: Number
|
||||
d[Symbol(letter,:rotation)] = arg
|
||||
|
||||
else
|
||||
warn("Skipped $(letter)axis arg $arg")
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
# update an Axis object with magic args and keywords
|
||||
function update!(axis::Axis, args...; kw...)
|
||||
# first process args
|
||||
d = axis.d
|
||||
for arg in args
|
||||
process_axis_arg!(d, arg)
|
||||
end
|
||||
|
||||
# then override for any keywords... only those keywords that already exists in d
|
||||
for (k,v) in kw
|
||||
if haskey(d, k)
|
||||
if k == :discrete_values
|
||||
# add these discrete values to the axis
|
||||
for vi in v
|
||||
discrete_value!(axis, vi)
|
||||
end
|
||||
else
|
||||
d[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
axis
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
Base.show(io::IO, axis::Axis) = dumpdict(axis.d, "Axis", true)
|
||||
# Base.getindex(axis::Axis, k::Symbol) = getindex(axis.d, k)
|
||||
Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
|
||||
Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
|
||||
|
||||
# get discrete ticks, or not
|
||||
function get_ticks(axis::Axis)
|
||||
ticks = axis[:ticks]
|
||||
dvals = axis[:discrete_values]
|
||||
if !isempty(dvals) && ticks == :auto
|
||||
axis[:continuous_values], dvals
|
||||
else
|
||||
ticks
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function expand_extrema!(ex::Extrema, v::Number)
|
||||
ex.emin = min(v, ex.emin)
|
||||
ex.emax = max(v, ex.emax)
|
||||
ex
|
||||
end
|
||||
|
||||
function expand_extrema!(axis::Axis, v::Number)
|
||||
expand_extrema!(axis[:extrema], v)
|
||||
end
|
||||
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
|
||||
ex = axis[:extrema]
|
||||
ex.emin = min(v[1], ex.emin)
|
||||
ex.emax = max(v[2], ex.emax)
|
||||
ex
|
||||
end
|
||||
function expand_extrema!{N<:Number}(axis::Axis, v::AVec{N})
|
||||
ex = axis[:extrema]
|
||||
for vi in v
|
||||
expand_extrema!(ex, vi)
|
||||
end
|
||||
ex
|
||||
end
|
||||
|
||||
|
||||
function expand_extrema!(sp::Subplot, d::KW)
|
||||
# first expand for the data
|
||||
for letter in (:x, :y, :z)
|
||||
data = d[letter]
|
||||
axis = sp.attr[Symbol(letter, "axis")]
|
||||
if eltype(data) <: Number
|
||||
expand_extrema!(axis, data)
|
||||
elseif isa(data, Surface) && eltype(data.surf) <: Number
|
||||
expand_extrema!(axis, data)
|
||||
elseif data != nothing
|
||||
# TODO: need more here... gotta track the discrete reference value
|
||||
# as well as any coord offset (think of boxplot shape coords... they all
|
||||
# correspond to the same x-value)
|
||||
# @show letter,eltype(data),typeof(data)
|
||||
d[letter], d[Symbol(letter,"_discrete_indices")] = discrete_value!(axis, data)
|
||||
end
|
||||
end
|
||||
|
||||
# # expand for fillrange/bar_width
|
||||
# fillaxis, baraxis = sp.attr[:yaxis], sp.attr[:xaxis]
|
||||
# if isvertical(d)
|
||||
# fillaxis, baraxis = baraxis, fillaxis
|
||||
# end
|
||||
|
||||
# expand for fillrange
|
||||
vert = isvertical(d)
|
||||
fr = d[:fillrange]
|
||||
if fr == nothing && d[:seriestype] == :bar
|
||||
fr = 0.0
|
||||
end
|
||||
if fr != nothing
|
||||
axis = sp.attr[vert ? :yaxis : :xaxis]
|
||||
if typeof(fr) <: Tuple
|
||||
for fri in fr
|
||||
expand_extrema!(axis, fri)
|
||||
end
|
||||
else
|
||||
expand_extrema!(axis, fr)
|
||||
end
|
||||
end
|
||||
|
||||
# expand for bar_width
|
||||
if d[:seriestype] == :bar
|
||||
dsym = vert ? :x : :y
|
||||
data = d[dsym]
|
||||
|
||||
bw = d[:bar_width]
|
||||
if bw == nothing
|
||||
bw = d[:bar_width] = mean(diff(data))
|
||||
end
|
||||
# @show data bw
|
||||
|
||||
axis = sp.attr[Symbol(dsym, :axis)]
|
||||
expand_extrema!(axis, maximum(data) + 0.5maximum(bw))
|
||||
expand_extrema!(axis, minimum(data) - 0.5minimum(bw))
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
function expand_extrema!(sp::Subplot, xmin, xmax, ymin, ymax)
|
||||
expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# push the limits out slightly
|
||||
function widen(lmin, lmax)
|
||||
span = lmax - lmin
|
||||
# eps = max(1e-16, min(1e-2span, 1e-10))
|
||||
eps = max(1e-16, 0.03span)
|
||||
lmin-eps, lmax+eps
|
||||
end
|
||||
|
||||
# figure out if widening is a good idea
|
||||
function default_should_widen(axis::Axis)
|
||||
should_widen = false
|
||||
for series in series_list(axis.sp)
|
||||
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
|
||||
should_widen = true
|
||||
end
|
||||
end
|
||||
should_widen
|
||||
end
|
||||
|
||||
# using the axis extrema and limit overrides, return the min/max value for this axis
|
||||
function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis))
|
||||
ex = axis[:extrema]
|
||||
amin, amax = ex.emin, ex.emax
|
||||
lims = axis[:lims]
|
||||
if isa(lims, Tuple) && length(lims) == 2
|
||||
if isfinite(lims[1])
|
||||
amin = lims[1]
|
||||
end
|
||||
if isfinite(lims[2])
|
||||
amax = lims[2]
|
||||
end
|
||||
end
|
||||
if amax <= amin
|
||||
amax = amin + 1.0
|
||||
end
|
||||
if should_widen
|
||||
widen(amin, amax)
|
||||
else
|
||||
amin, amax
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# these methods track the discrete (categorical) values which correspond to axis continuous values (cv)
|
||||
# whenever we have discrete values, we automatically set the ticks to match.
|
||||
# we return (continuous_value, discrete_index)
|
||||
function discrete_value!(axis::Axis, dv)
|
||||
cv_idx = get(axis[:discrete_map], dv, -1)
|
||||
# @show axis[:discrete_map], axis[:discrete_values], dv
|
||||
if cv_idx == -1
|
||||
ex = axis[:extrema]
|
||||
cv = max(0.5, ex.emax + 1.0)
|
||||
expand_extrema!(axis, cv)
|
||||
push!(axis[:discrete_values], dv)
|
||||
push!(axis[:continuous_values], cv)
|
||||
cv_idx = length(axis[:discrete_values])
|
||||
axis[:discrete_map][dv] = cv_idx
|
||||
cv, cv_idx
|
||||
else
|
||||
cv = axis[:continuous_values][cv_idx]
|
||||
cv, cv_idx
|
||||
end
|
||||
end
|
||||
|
||||
# continuous value... just pass back with axis negative index
|
||||
function discrete_value!(axis::Axis, cv::Number)
|
||||
cv, -1
|
||||
end
|
||||
|
||||
# add the discrete value for each item. return the continuous values and the indices
|
||||
function discrete_value!(axis::Axis, v::AVec)
|
||||
n = length(v)
|
||||
cvec = zeros(n)
|
||||
discrete_indices = zeros(Int, n)
|
||||
for i=1:n
|
||||
cvec[i], discrete_indices[i] = discrete_value!(axis, v[i])
|
||||
end
|
||||
cvec, discrete_indices
|
||||
end
|
||||
|
||||
# add the discrete value for each item. return the continuous values and the indices
|
||||
function discrete_value!(axis::Axis, v::AMat)
|
||||
n,m = size(v)
|
||||
cmat = zeros(n,m)
|
||||
discrete_indices = zeros(Int, n, m)
|
||||
for i=1:n, j=1:m
|
||||
cmat[i,j], discrete_indices[i,j] = discrete_value!(axis, v[i,j])
|
||||
end
|
||||
cmat, discrete_indices
|
||||
end
|
||||
|
||||
function discrete_value!(axis::Axis, v::Surface)
|
||||
map(Surface, discrete_value!(axis, v.surf))
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
function pie_labels(sp::Subplot, series::Series)
|
||||
d = series.d
|
||||
if haskey(d,:x_discrete_indices)
|
||||
dvals = sp.attr[:xaxis].d[:discrete_values]
|
||||
[dvals[idx] for idx in d[:x_discrete_indices]]
|
||||
else
|
||||
d[:x]
|
||||
end
|
||||
end
|
||||
+49
-19
@@ -12,16 +12,16 @@ _backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]()
|
||||
|
||||
macro init_backend(s)
|
||||
str = lowercase(string(s))
|
||||
sym = symbol(str)
|
||||
T = symbol(string(s) * "Backend")
|
||||
sym = Symbol(str)
|
||||
T = Symbol(string(s) * "Backend")
|
||||
esc(quote
|
||||
immutable $T <: AbstractBackend end
|
||||
export $sym
|
||||
$sym(; kw...) = (default(; kw...); backend(symbol($str)))
|
||||
backend_name(::$T) = symbol($str)
|
||||
push!(_backends, symbol($str))
|
||||
_backendType[symbol($str)] = $T
|
||||
_backendSymbol[$T] = symbol($str)
|
||||
$sym(; kw...) = (default(; kw...); backend(Symbol($str)))
|
||||
backend_name(::$T) = Symbol($str)
|
||||
push!(_backends, Symbol($str))
|
||||
_backendType[Symbol($str)] = $T
|
||||
_backendSymbol[$T] = Symbol($str)
|
||||
include("backends/" * $str * ".jl")
|
||||
end)
|
||||
end
|
||||
@@ -44,13 +44,31 @@ include("backends/web.jl")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
# don't do anything as a default
|
||||
_create_backend_figure(plt::Plot) = nothing
|
||||
_prepare_plot_object(plt::Plot) = nothing
|
||||
_initialize_subplot(plt::Plot, sp::Subplot) = nothing
|
||||
|
||||
plot(pkg::AbstractBackend; kw...) = error("plot($pkg; kw...) is not implemented")
|
||||
plot!(pkg::AbstractBackend, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
|
||||
_update_plot(pkg::AbstractBackend, plt::Plot, d::KW) = error("_update_plot($pkg, plt, d) is not implemented")
|
||||
_update_plot_pos_size{P<:AbstractBackend}(plt::AbstractPlot{P}, d::KW) = nothing
|
||||
subplot(pkg::AbstractBackend; kw...) = error("subplot($pkg; kw...) is not implemented")
|
||||
subplot!(pkg::AbstractBackend, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
|
||||
_series_added(plt::Plot, series::Series) = nothing
|
||||
_series_updated(plt::Plot, series::Series) = nothing
|
||||
|
||||
_before_layout_calcs(plt::Plot) = nothing
|
||||
|
||||
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
|
||||
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
|
||||
|
||||
# Set the (left, top, right, bottom) minimum padding around the plot area
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot)
|
||||
leftpad = 10mm + sp[:left_margin] + guide_padding(sp[:yaxis])
|
||||
toppad = 2mm + sp[:top_margin] + title_padding(sp)
|
||||
rightpad = 3mm + sp[:right_margin]
|
||||
bottompad = 5mm + sp[:bottom_margin] + guide_padding(sp[:xaxis])
|
||||
# @show (leftpad, toppad, rightpad, bottompad)
|
||||
sp.minpad = (leftpad, toppad, rightpad, bottompad)
|
||||
end
|
||||
|
||||
_update_plot_object(plt::Plot) = nothing
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -68,7 +86,7 @@ function pickDefaultBackend()
|
||||
if env_default != ""
|
||||
try
|
||||
Pkg.installed(env_default) # this will error if not installed
|
||||
sym = symbol(lowercase(env_default))
|
||||
sym = Symbol(lowercase(env_default))
|
||||
if haskey(_backendType, sym)
|
||||
return backend(sym)
|
||||
else
|
||||
@@ -85,7 +103,7 @@ function pickDefaultBackend()
|
||||
# features, speed, and robustness
|
||||
for pkgstr in ("PyPlot", "GR", "PlotlyJS", "Immerse", "Gadfly", "UnicodePlots")
|
||||
if Pkg.installed(pkgstr) != nothing
|
||||
return backend(symbol(lowercase(pkgstr)))
|
||||
return backend(Symbol(lowercase(pkgstr)))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -130,13 +148,23 @@ end
|
||||
Set the plot backend.
|
||||
"""
|
||||
function backend(pkg::AbstractBackend)
|
||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
CURRENT_BACKEND.sym = backend_name(pkg)
|
||||
warn_on_deprecated_backend(CURRENT_BACKEND.sym)
|
||||
CURRENT_BACKEND.pkg = pkg
|
||||
end
|
||||
|
||||
function backend(modname::Symbol)
|
||||
CURRENT_BACKEND.sym = modname
|
||||
CURRENT_BACKEND.pkg = _backend_instance(modname)
|
||||
warn_on_deprecated_backend(modname)
|
||||
CURRENT_BACKEND.sym = modname
|
||||
CURRENT_BACKEND.pkg = _backend_instance(modname)
|
||||
end
|
||||
|
||||
const _deprecated_backends = [:qwt, :winston, :bokeh, :gadfly, :immerse]
|
||||
|
||||
function warn_on_deprecated_backend(bsym::Symbol)
|
||||
if bsym in _deprecated_backends
|
||||
warn("Backend $bsym has been deprecated. It may not work as originally intended.")
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -148,6 +176,7 @@ supportedMarkers(::AbstractBackend) = [:none]
|
||||
supportedScales(::AbstractBackend) = [:identity]
|
||||
subplotSupported(::AbstractBackend) = false
|
||||
stringsSupported(::AbstractBackend) = false
|
||||
nativeImagesSupported(b::AbstractBackend) = :image in supportedTypes(b)
|
||||
|
||||
supportedAxes() = supportedAxes(backend())
|
||||
supportedTypes() = supportedTypes(backend())
|
||||
@@ -156,5 +185,6 @@ supportedMarkers() = supportedMarkers(backend())
|
||||
supportedScales() = supportedScales(backend())
|
||||
subplotSupported() = subplotSupported(backend())
|
||||
stringsSupported() = stringsSupported(backend())
|
||||
nativeImagesSupported() = nativeImagesSupported(backend())
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
+27
-55
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
supportedArgs(::BokehBackend) = [
|
||||
# :annotation,
|
||||
# :annotations,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
:linecolor,
|
||||
@@ -18,7 +18,7 @@ supportedArgs(::BokehBackend) = [
|
||||
# :legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:seriestype,
|
||||
:linewidth,
|
||||
# :linealpha,
|
||||
:markershape,
|
||||
@@ -37,13 +37,13 @@ supportedArgs(::BokehBackend) = [
|
||||
# :show,
|
||||
:size,
|
||||
:title,
|
||||
# :windowtitle,
|
||||
# :window_title,
|
||||
:x,
|
||||
# :xlabel,
|
||||
# :xguide,
|
||||
# :xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
# :ylabel,
|
||||
# :yguide,
|
||||
# :ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
@@ -60,7 +60,7 @@ supportedArgs(::BokehBackend) = [
|
||||
# :levels,
|
||||
]
|
||||
supportedAxes(::BokehBackend) = [:auto, :left]
|
||||
supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :histogram2d, :hexbin, :histogram, :bar, :hline, :vline, :contour]
|
||||
supportedStyles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::BokehBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::BokehBackend) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
@@ -79,13 +79,6 @@ function _initialize_backend(::BokehBackend; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
# make255(x) = round(Int, 255 * x)
|
||||
|
||||
# function bokehcolor(c::Colorant)
|
||||
# @sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
|
||||
# end
|
||||
# bokehcolor(cs::ColorScheme) = bokehcolor(getColor(cs))
|
||||
|
||||
|
||||
const _glyphtypes = KW(
|
||||
:ellipse => :Circle,
|
||||
@@ -104,14 +97,14 @@ const _glyphtypes = KW(
|
||||
|
||||
|
||||
function bokeh_glyph_type(d::KW)
|
||||
lt = d[:linetype]
|
||||
st = d[:seriestype]
|
||||
mt = d[:markershape]
|
||||
if lt == :scatter && mt == :none
|
||||
if st == :scatter && mt == :none
|
||||
mt = :ellipse
|
||||
end
|
||||
|
||||
# if we have a marker, use that
|
||||
if lt == :scatter || mt != :none
|
||||
if st == :scatter || mt != :none
|
||||
return _glyphtypes[mt]
|
||||
end
|
||||
|
||||
@@ -133,28 +126,30 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::BokehBackend, d::KW)
|
||||
# function _create_plot(pkg::BokehBackend, d::KW)
|
||||
function _create_backend_figure(plt::Plot{BokehBackend})
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
|
||||
datacolumns = Bokeh.BokehDataSet[]
|
||||
tools = Bokeh.tools()
|
||||
filename = tempname() * ".html"
|
||||
title = d[:title]
|
||||
w, h = d[:size]
|
||||
xaxis_type = d[:xscale] == :log10 ? :log : :auto
|
||||
yaxis_type = d[:yscale] == :log10 ? :log : :auto
|
||||
# legend = d[:legend] ? xxxx : nothing
|
||||
title = plt.attr[:title]
|
||||
w, h = plt.attr[:size]
|
||||
xaxis_type = plt.attr[:xscale] == :log10 ? :log : :auto
|
||||
yaxis_type = plt.attr[:yscale] == :log10 ? :log : :auto
|
||||
# legend = plt.attr[:legend] ? xxxx : nothing
|
||||
legend = nothing
|
||||
extra_args = KW() # TODO: we'll put extra settings (xlim, etc) here
|
||||
bplt = Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
|
||||
Bokeh.Plot(datacolumns, tools, filename, title, w, h, xaxis_type, yaxis_type, legend) #, extra_args)
|
||||
|
||||
Plot(bplt, pkg, 0, d, KW[])
|
||||
# Plot(bplt, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::BokehBackend, plt::Plot, d::KW)
|
||||
bdata = Dict{Symbol, Vector}(:x => collect(d[:x]), :y => collect(d[:y]))
|
||||
# function _series_added(::BokehBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{BokehBackend}, series::Series)
|
||||
bdata = Dict{Symbol, Vector}(:x => collect(series.d[:x]), :y => collect(series.d[:y]))
|
||||
|
||||
glyph = Bokeh.Bokehjs.Glyph(
|
||||
glyphtype = bokeh_glyph_type(d),
|
||||
@@ -168,17 +163,14 @@ function _add_series(::BokehBackend, plt::Plot, d::KW)
|
||||
legend = nothing # TODO
|
||||
push!(plt.o.datacolumns, Bokeh.BokehDataSet(bdata, glyph, legend))
|
||||
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot(plt::Plot{BokehBackend}, d::KW)
|
||||
end
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{BokehBackend}, d::KW)
|
||||
function _update_plot_object(plt::Plot{BokehBackend}, d::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
@@ -197,29 +189,9 @@ end
|
||||
# end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{BokehBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
# TODO: add the annotation to the plot
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _create_subplot(subplt::Subplot{BokehBackend}, isbefore::Bool)
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
|
||||
end
|
||||
|
||||
|
||||
function _expand_limits(lims, plt::Plot{BokehBackend}, isx::Bool)
|
||||
# TODO: call expand limits for each plot data
|
||||
end
|
||||
|
||||
function _remove_axis(plt::Plot{BokehBackend}, isx::Bool)
|
||||
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -232,6 +204,6 @@ function Base.display(::PlotsDisplay, plt::Plot{BokehBackend})
|
||||
Bokeh.showplot(plt.o)
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
|
||||
# TODO: display/show the subplot
|
||||
end
|
||||
# function Base.display(::PlotsDisplay, plt::Subplot{BokehBackend})
|
||||
# # TODO: display/show the subplot
|
||||
# end
|
||||
|
||||
+125
-121
@@ -3,19 +3,19 @@
|
||||
|
||||
|
||||
supportedArgs(::GadflyBackend) = [
|
||||
:annotation,
|
||||
:annotations,
|
||||
:background_color, :foreground_color, :color_palette,
|
||||
:group, :label, :linetype,
|
||||
:group, :label, :seriestype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins, :n, :nc, :nr, :layout, :smooth,
|
||||
:title, :windowtitle, :show, :size,
|
||||
:x, :xlabel, :xlims, :xticks, :xscale, :xflip,
|
||||
:y, :ylabel, :ylims, :yticks, :yscale, :yflip,
|
||||
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
|
||||
:title, :window_title, :show, :size,
|
||||
:x, :xguide, :xlims, :xticks, :xscale, :xflip,
|
||||
:y, :yguide, :ylims, :yticks, :yscale, :yflip,
|
||||
# :z, :zguide, :zlims, :zticks, :zscale, :zflip,
|
||||
:z,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
@@ -27,8 +27,8 @@ supportedArgs(::GadflyBackend) = [
|
||||
supportedAxes(::GadflyBackend) = [:auto, :left]
|
||||
supportedTypes(::GadflyBackend) = [
|
||||
:none, :line, :path, :steppre, :steppost, :sticks,
|
||||
:scatter, :hist2d, :hexbin, :hist,
|
||||
:bar, :box, :violin, :quiver,
|
||||
:scatter, :histogram2d, :hexbin, :histogram,
|
||||
:bar, #:box, :violin, :quiver,
|
||||
:hline, :vline, :contour, :shape
|
||||
]
|
||||
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
@@ -59,8 +59,8 @@ function createGadflyPlotObject(d::KW)
|
||||
gplt.data_source = Gadfly.DataFrames.DataFrame()
|
||||
# gplt.layers = gplt.layers[1:0]
|
||||
gplt.layers = [Gadfly.layer(Gadfly.Geom.point(tag=:remove), x=zeros(1), y=zeros(1));] # x=MissingVec(), y=MissingVec());]
|
||||
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xlabel]),
|
||||
Gadfly.Guide.ylabel(d[:ylabel]),
|
||||
gplt.guides = Gadfly.GuideElement[Gadfly.Guide.xlabel(d[:xguide]),
|
||||
Gadfly.Guide.ylabel(d[:yguide]),
|
||||
Gadfly.Guide.title(d[:title])]
|
||||
gplt
|
||||
end
|
||||
@@ -69,31 +69,31 @@ end
|
||||
|
||||
|
||||
function getLineGeom(d::KW)
|
||||
lt = d[:linetype]
|
||||
st = d[:seriestype]
|
||||
xbins, ybins = maketuple(d[:bins])
|
||||
if lt == :hexb
|
||||
if st == :hexb
|
||||
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
|
||||
elseif lt == :hist2d
|
||||
elseif st == :histogram2d
|
||||
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
|
||||
elseif lt == :hist
|
||||
elseif st == :histogram
|
||||
Gadfly.Geom.histogram(bincount = xbins,
|
||||
orientation = isvertical(d) ? :vertical : :horizontal,
|
||||
position = d[:bar_position] == :stack ? :stack : :dodge)
|
||||
elseif lt == :path
|
||||
elseif st == :path
|
||||
Gadfly.Geom.path
|
||||
elseif lt in (:bar, :sticks)
|
||||
elseif st in (:bar, :sticks)
|
||||
Gadfly.Geom.bar
|
||||
elseif lt == :steppost
|
||||
elseif st == :steppost
|
||||
Gadfly.Geom.step
|
||||
elseif lt == :steppre
|
||||
elseif st == :steppre
|
||||
Gadfly.Geom.step(direction = :vh)
|
||||
elseif lt == :hline
|
||||
elseif st == :hline
|
||||
Gadfly.Geom.hline
|
||||
elseif lt == :vline
|
||||
elseif st == :vline
|
||||
Gadfly.Geom.vline
|
||||
elseif lt == :contour
|
||||
elseif st == :contour
|
||||
Gadfly.Geom.contour(levels = d[:levels])
|
||||
# elseif lt == :shape
|
||||
# elseif st == :shape
|
||||
# Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
else
|
||||
nothing
|
||||
@@ -116,13 +116,13 @@ function get_extra_theme_args(d::KW, k::Symbol)
|
||||
end
|
||||
|
||||
function getGadflyLineTheme(d::KW)
|
||||
lt = d[:linetype]
|
||||
st = d[:seriestype]
|
||||
lc = convertColor(getColor(d[:linecolor]), d[:linealpha])
|
||||
fc = convertColor(getColor(d[:fillcolor]), d[:fillalpha])
|
||||
|
||||
Gadfly.Theme(;
|
||||
default_color = (lt in (:hist,:hist2d,:hexbin,:bar,:sticks) ? fc : lc),
|
||||
line_width = (lt == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
|
||||
default_color = (st in (:histogram,:histogram2d,:hexbin,:bar,:sticks) ? fc : lc),
|
||||
line_width = (st == :sticks ? 1 : d[:linewidth]) * Gadfly.px,
|
||||
# line_style = Gadfly.get_stroke_vector(d[:linestyle]),
|
||||
lowlight_color = x->RGB(fc), # fill/ribbon
|
||||
lowlight_opacity = alpha(fc), # fill/ribbon
|
||||
@@ -136,10 +136,10 @@ function addGadflyLine!(plt::Plot, numlayers::Int, d::KW, geoms...)
|
||||
gplt = getGadflyContext(plt)
|
||||
gfargs = vcat(geoms..., getGadflyLineTheme(d))
|
||||
kwargs = KW()
|
||||
lt = d[:linetype]
|
||||
st = d[:seriestype]
|
||||
|
||||
# add a fill?
|
||||
if d[:fillrange] != nothing && lt != :contour
|
||||
if d[:fillrange] != nothing && st != :contour
|
||||
fillmin, fillmax = map(makevec, maketuple(d[:fillrange]))
|
||||
nmin, nmax = length(fillmin), length(fillmax)
|
||||
kwargs[:ymin] = Float64[min(y, fillmin[mod1(i, nmin)], fillmax[mod1(i, nmax)]) for (i,y) in enumerate(d[:y])]
|
||||
@@ -147,20 +147,20 @@ function addGadflyLine!(plt::Plot, numlayers::Int, d::KW, geoms...)
|
||||
push!(gfargs, Gadfly.Geom.ribbon)
|
||||
end
|
||||
|
||||
if lt in (:hline, :vline)
|
||||
kwargs[lt == :hline ? :yintercept : :xintercept] = d[:y]
|
||||
if st in (:hline, :vline)
|
||||
kwargs[st == :hline ? :yintercept : :xintercept] = d[:y]
|
||||
|
||||
else
|
||||
if lt == :sticks
|
||||
if st == :sticks
|
||||
w = 0.01 * mean(diff(d[:x]))
|
||||
kwargs[:xmin] = d[:x] - w
|
||||
kwargs[:xmax] = d[:x] + w
|
||||
elseif lt == :contour
|
||||
elseif st == :contour
|
||||
kwargs[:z] = d[:z].surf
|
||||
addGadflyContColorScale(plt, d[:linecolor])
|
||||
end
|
||||
|
||||
kwargs[:x] = d[lt == :hist ? :y : :x]
|
||||
kwargs[:x] = d[st == :histogram ? :y : :x]
|
||||
kwargs[:y] = d[:y]
|
||||
|
||||
end
|
||||
@@ -183,14 +183,14 @@ getMarkerGeom(other) = gadflyshape(get_shape(other))
|
||||
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
||||
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
|
||||
function getMarkerGeom(d::KW)
|
||||
if d[:linetype] == :shape
|
||||
if d[:seriestype] == :shape
|
||||
Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
else
|
||||
getMarkerGeom(d[:markershape])
|
||||
end
|
||||
end
|
||||
|
||||
function getGadflyMarkerTheme(d::KW, plotargs::KW)
|
||||
function getGadflyMarkerTheme(d::KW, attr::KW)
|
||||
c = getColor(d[:markercolor])
|
||||
α = d[:markeralpha]
|
||||
if α != nothing
|
||||
@@ -216,15 +216,15 @@ function getGadflyMarkerTheme(d::KW, plotargs::KW)
|
||||
end
|
||||
|
||||
function addGadflyContColorScale(plt::Plot{GadflyBackend}, c)
|
||||
plt.plotargs[:colorbar] == :none && return
|
||||
plt.attr[:colorbar] == :none && return
|
||||
if !isa(c, ColorGradient)
|
||||
c = default_gradient()
|
||||
end
|
||||
push!(getGadflyContext(plt).scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(c, p))))
|
||||
end
|
||||
|
||||
function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, plotargs::KW, geoms...)
|
||||
gfargs = vcat(geoms..., getGadflyMarkerTheme(d, plotargs), getMarkerGeom(d))
|
||||
function addGadflyMarker!(plt::Plot, numlayers::Int, d::KW, attr::KW, geoms...)
|
||||
gfargs = vcat(geoms..., getGadflyMarkerTheme(d, attr), getMarkerGeom(d))
|
||||
kwargs = KW()
|
||||
|
||||
# handle continuous color scales for the markers
|
||||
@@ -241,7 +241,7 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function addToGadflyLegend(plt::Plot, d::KW)
|
||||
if plt.plotargs[:legend] != :none && d[:label] != ""
|
||||
if plt.attr[:legend] != :none && d[:label] != ""
|
||||
gplt = getGadflyContext(plt)
|
||||
|
||||
# add the legend if needed
|
||||
@@ -297,21 +297,21 @@ function addGadflySeries!(plt::Plot, d::KW)
|
||||
end
|
||||
|
||||
# special handling for ohlc and scatter
|
||||
lt = d[:linetype]
|
||||
if lt == :ohlc
|
||||
error("Haven't re-implemented after refactoring")
|
||||
elseif lt in (:hist2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
|
||||
st = d[:seriestype]
|
||||
# if st == :ohlc
|
||||
# error("Haven't re-implemented after refactoring")
|
||||
if st in (:histogram2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
|
||||
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
|
||||
elseif lt == :scatter && d[:markershape] == :none
|
||||
elseif st == :scatter && d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
# markers
|
||||
if d[:markershape] != :none || lt == :shape
|
||||
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.plotargs, smooth...))
|
||||
if d[:markershape] != :none || st == :shape
|
||||
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.attr, smooth...))
|
||||
end
|
||||
|
||||
lt in (:hist2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
|
||||
st in (:histogram2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
|
||||
|
||||
# now save the layers that apply to this series
|
||||
d[:gadflylayers] = layers
|
||||
@@ -469,8 +469,8 @@ end
|
||||
function updateGadflyGuides(plt::Plot, d::KW)
|
||||
gplt = getGadflyContext(plt)
|
||||
haskey(d, :title) && findGuideAndSet(gplt, Gadfly.Guide.title, string(d[:title]))
|
||||
haskey(d, :xlabel) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xlabel]))
|
||||
haskey(d, :ylabel) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:ylabel]))
|
||||
haskey(d, :xguide) && findGuideAndSet(gplt, Gadfly.Guide.xlabel, string(d[:xguide]))
|
||||
haskey(d, :yguide) && findGuideAndSet(gplt, Gadfly.Guide.ylabel, string(d[:yguide]))
|
||||
|
||||
xlims, xfunc = addGadflyLimitsScale(gplt, d, true)
|
||||
ylims, yfunc = addGadflyLimitsScale(gplt, d, false)
|
||||
@@ -570,28 +570,32 @@ end
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
function _create_plot(pkg::GadflyBackend, d::KW)
|
||||
gplt = createGadflyPlotObject(d)
|
||||
Plot(gplt, pkg, 0, d, KW[])
|
||||
# function _create_plot(pkg::GadflyBackend, d::KW)
|
||||
# gplt = createGadflyPlotObject(d)
|
||||
# Plot(gplt, pkg, 0, d, KW[])
|
||||
# end
|
||||
function _create_backend_figure(plt::Plot{GadflyBackend})
|
||||
createGadflyPlotObject(plt.attr)
|
||||
end
|
||||
|
||||
|
||||
# plot one data series
|
||||
function _add_series(::GadflyBackend, plt::Plot, d::KW)
|
||||
# function _series_added(::GadflyBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{GadflyBackend}, series::Series)
|
||||
# first clear out the temporary layer
|
||||
gplt = getGadflyContext(plt)
|
||||
if gplt.layers[1].geom.tag == :remove
|
||||
gplt.layers = gplt.layers[2:end]
|
||||
end
|
||||
|
||||
addGadflySeries!(plt, d)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
addGadflySeries!(plt, series.d)
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
|
||||
function _update_plot(plt::Plot{GadflyBackend}, d::KW)
|
||||
function _update_plot_object(plt::Plot{GadflyBackend}, d::KW)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
end
|
||||
@@ -622,12 +626,12 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
# create the underlying object (each backend will do this differently)
|
||||
function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
|
||||
isbefore && return false # wait until after plotting to create the subplots
|
||||
subplt.o = nothing
|
||||
true
|
||||
end
|
||||
# # create the underlying object (each backend will do this differently)
|
||||
# function _create_subplot(subplt::Subplot{GadflyBackend}, isbefore::Bool)
|
||||
# isbefore && return false # wait until after plotting to create the subplots
|
||||
# subplt.o = nothing
|
||||
# true
|
||||
# end
|
||||
|
||||
|
||||
function _remove_axis(plt::Plot{GadflyBackend}, isx::Bool)
|
||||
@@ -647,31 +651,31 @@ end
|
||||
|
||||
|
||||
getGadflyContext(plt::Plot{GadflyBackend}) = plt.o
|
||||
getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
|
||||
# getGadflyContext(subplt::Subplot{GadflyBackend}) = buildGadflySubplotContext(subplt)
|
||||
|
||||
# create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
|
||||
function buildGadflySubplotContext(subplt::Subplot)
|
||||
rows = Any[]
|
||||
row = Any[]
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
|
||||
# add the Plot object to the row
|
||||
push!(row, getGadflyContext(subplt.plts[i]))
|
||||
|
||||
# add the row
|
||||
if c == ncols(subplt.layout, r)
|
||||
push!(rows, Gadfly.hstack(row...))
|
||||
row = Any[]
|
||||
end
|
||||
end
|
||||
|
||||
# stack the rows
|
||||
Gadfly.vstack(rows...)
|
||||
end
|
||||
# # create my Compose.Context grid by hstacking and vstacking the Gadfly.Plot objects
|
||||
# function buildGadflySubplotContext(subplt::Subplot)
|
||||
# rows = Any[]
|
||||
# row = Any[]
|
||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
||||
#
|
||||
# # add the Plot object to the row
|
||||
# push!(row, getGadflyContext(subplt.plts[i]))
|
||||
#
|
||||
# # add the row
|
||||
# if c == ncols(subplt.layout, r)
|
||||
# push!(rows, Gadfly.hstack(row...))
|
||||
# row = Any[]
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # stack the rows
|
||||
# Gadfly.vstack(rows...)
|
||||
# end
|
||||
|
||||
setGadflyDisplaySize(w,h) = Compose.set_default_graphic_size(w * Compose.px, h * Compose.px)
|
||||
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.plotargs[:size]...)
|
||||
setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getplotargs(subplt, 1)[:size]...)
|
||||
setGadflyDisplaySize(plt::Plot) = setGadflyDisplaySize(plt.attr[:size]...)
|
||||
# setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getattr(subplt, 1)[:size]...)
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -699,44 +703,44 @@ end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{GadflyBackend})
|
||||
setGadflyDisplaySize(plt.plotargs[:size]...)
|
||||
setGadflyDisplaySize(plt.attr[:size]...)
|
||||
display(plt.o)
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
|
||||
setGadflyDisplaySize(getplotargs(subplt,1)[:size]...)
|
||||
ctx = buildGadflySubplotContext(subplt)
|
||||
|
||||
# taken from Gadfly since I couldn't figure out how to do it directly
|
||||
|
||||
filename = string(Gadfly.tempname(), ".html")
|
||||
output = open(filename, "w")
|
||||
|
||||
plot_output = IOBuffer()
|
||||
Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
|
||||
Compose.default_graphic_height, false), ctx)
|
||||
plotsvg = takebuf_string(plot_output)
|
||||
|
||||
write(output,
|
||||
"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Gadfly Plot</title>
|
||||
<meta charset="utf-8">
|
||||
</head>
|
||||
<body>
|
||||
<script charset="utf-8">
|
||||
$(readall(Compose.snapsvgjs))
|
||||
</script>
|
||||
<script charset="utf-8">
|
||||
$(readall(Gadfly.gadflyjs))
|
||||
</script>
|
||||
$(plotsvg)
|
||||
</body>
|
||||
</html>
|
||||
""")
|
||||
close(output)
|
||||
Gadfly.open_file(filename)
|
||||
end
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{GadflyBackend})
|
||||
# setGadflyDisplaySize(getattr(subplt,1)[:size]...)
|
||||
# ctx = buildGadflySubplotContext(subplt)
|
||||
#
|
||||
# # taken from Gadfly since I couldn't figure out how to do it directly
|
||||
#
|
||||
# filename = string(Gadfly.tempname(), ".html")
|
||||
# output = open(filename, "w")
|
||||
#
|
||||
# plot_output = IOBuffer()
|
||||
# Gadfly.draw(Gadfly.SVGJS(plot_output, Compose.default_graphic_width,
|
||||
# Compose.default_graphic_height, false), ctx)
|
||||
# plotsvg = takebuf_string(plot_output)
|
||||
#
|
||||
# write(output,
|
||||
# """
|
||||
# <!DOCTYPE html>
|
||||
# <html>
|
||||
# <head>
|
||||
# <title>Gadfly Plot</title>
|
||||
# <meta charset="utf-8">
|
||||
# </head>
|
||||
# <body>
|
||||
# <script charset="utf-8">
|
||||
# $(readall(Compose.snapsvgjs))
|
||||
# </script>
|
||||
# <script charset="utf-8">
|
||||
# $(readall(Gadfly.gadflyjs))
|
||||
# </script>
|
||||
# $(plotsvg)
|
||||
# </body>
|
||||
# </html>
|
||||
# """)
|
||||
# close(output)
|
||||
# Gadfly.open_file(filename)
|
||||
# end
|
||||
|
||||
+28
-63
@@ -3,7 +3,7 @@
|
||||
# [WEBSITE]
|
||||
|
||||
supportedArgs(::GLVisualizeBackend) = [
|
||||
# :annotation,
|
||||
# :annotations,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
# :color_palette,
|
||||
@@ -17,7 +17,7 @@ supportedArgs(::GLVisualizeBackend) = [
|
||||
# :legend,
|
||||
# :linecolor,
|
||||
# :linestyle,
|
||||
:linetype
|
||||
:seriestype
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linewidth,
|
||||
# :linealpha,
|
||||
@@ -37,13 +37,13 @@ supportedArgs(::GLVisualizeBackend) = [
|
||||
# :show,
|
||||
# :size,
|
||||
# :title,
|
||||
# :windowtitle,
|
||||
# :window_title,
|
||||
# :x,
|
||||
# :xlabel,
|
||||
# :xguide,
|
||||
# :xlims,
|
||||
# :xticks,
|
||||
# :y,
|
||||
# :ylabel,
|
||||
# :yguide,
|
||||
# :ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
@@ -60,7 +60,7 @@ supportedArgs(::GLVisualizeBackend) = [
|
||||
# :levels,
|
||||
]
|
||||
supportedAxes(::GLVisualizeBackend) = [:auto, :left]
|
||||
supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :histogram, :bar, :hline, :vline, :contour]
|
||||
supportedStyles(::GLVisualizeBackend) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::GLVisualizeBackend) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::GLVisualizeBackend) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
|
||||
@@ -82,90 +82,55 @@ immutable GLScreenWrapper
|
||||
window
|
||||
end
|
||||
|
||||
function _create_plot(pkg::GLVisualizeBackend, d::KW)
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# function _create_plot(pkg::GLVisualizeBackend, d::KW)
|
||||
function _create_backend_figure(plt::Plot{GLVisualizeBackend})
|
||||
# TODO: create the window/canvas/context that is the plot within the backend
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
|
||||
# TODO: this should be moved to the display method?
|
||||
w=GLVisualize.glscreen()
|
||||
@async GLVisualize.renderloop(w)
|
||||
Plot(GLScreenWrapper(w), pkg, 0, d, KW[])
|
||||
GLScreenWrapper(w)
|
||||
# Plot(GLScreenWrapper(w), pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::GLVisualizeBackend, plt::Plot, d::KW)
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _series_added(plt::Plot{GLVisualizeBackend}, series::Series)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
# TODO: this should be moved to the display method?
|
||||
x,y,z=map(Float32,d[:x]), map(Float32,d[:y]), map(Float32,d[:z].surf)
|
||||
GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface),plt.o.window)
|
||||
plt
|
||||
x, y, z = map(Float32, series.d[:x]), map(Float32, series.d[:y]), map(Float32, series.d[:z].surf)
|
||||
GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface), plt.o.window)
|
||||
# plt
|
||||
end
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{GLVisualizeBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
# TODO: add the annotation to the plot
|
||||
end
|
||||
|
||||
# When series data is added/changed, this callback can do dynamic updates to the backend object.
|
||||
# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
|
||||
function _series_updated(plt::Plot{GLVisualizeBackend}, series::Series)
|
||||
# TODO
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _before_update_plot(plt::Plot{GLVisualizeBackend})
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
function _update_plot_object(plt::Plot{GLVisualizeBackend})
|
||||
# TODO
|
||||
end
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot(plt::Plot{GLVisualizeBackend}, d::KW)
|
||||
end
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{GLVisualizeBackend}, d::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
function getxy(plt::Plot{GLVisualizeBackend}, i::Int)
|
||||
# TODO:
|
||||
# series = plt.o.lines[i]
|
||||
# series.x, series.y
|
||||
nothing, nothing
|
||||
end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot{GLVisualizeBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# TODO:
|
||||
# series = plt.o.lines[i]
|
||||
# series.x, series.y = xy
|
||||
plt
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _create_subplot(subplt::Subplot{GLVisualizeBackend})
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{GLVisualizeBackend}, isx::Bool)
|
||||
# TODO: call expand limits for each plot data
|
||||
end
|
||||
|
||||
function _remove_axis(plt::Plot{GLVisualizeBackend}, isx::Bool)
|
||||
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
|
||||
function _writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
|
||||
# TODO: write a png to io
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{GLVisualizeBackend})
|
||||
function _display(plt::Plot{GLVisualizeBackend})
|
||||
# TODO: display/show the plot
|
||||
|
||||
# NOTE: I think maybe this should be empty? We can start with the assumption that creating
|
||||
# and adding to a plot will automatically open a window and draw to it, then the display
|
||||
# wouldn't actually need to do anything
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizeBackend})
|
||||
# TODO: display/show the subplot
|
||||
end
|
||||
|
||||
+1291
-821
File diff suppressed because it is too large
Load Diff
+84
-77
@@ -21,7 +21,7 @@ end
|
||||
|
||||
function createImmerseFigure(d::KW)
|
||||
w,h = d[:size]
|
||||
figidx = Immerse.figure(; name = d[:windowtitle], width = w, height = h)
|
||||
figidx = Immerse.figure(; name = d[:window_title], width = w, height = h)
|
||||
Immerse.Figure(figidx)
|
||||
end
|
||||
|
||||
@@ -29,24 +29,31 @@ end
|
||||
|
||||
|
||||
# create a blank Gadfly.Plot object
|
||||
function _create_plot(pkg::ImmerseBackend, d::KW)
|
||||
# create the underlying Gadfly.Plot object
|
||||
gplt = createGadflyPlotObject(d)
|
||||
|
||||
# save both the Immerse.Figure and the Gadfly.Plot
|
||||
Plot((nothing,gplt), pkg, 0, d, KW[])
|
||||
# function _create_plot(pkg::ImmerseBackend, d::KW)
|
||||
# # create the underlying Gadfly.Plot object
|
||||
# gplt = createGadflyPlotObject(d)
|
||||
#
|
||||
# # save both the Immerse.Figure and the Gadfly.Plot
|
||||
# Plot((nothing,gplt), pkg, 0, d, KW[])
|
||||
# end
|
||||
function _create_backend_figure(plt::Plot{ImmerseBackend})
|
||||
(nothing, createGadflyPlotObject(plt.attr))
|
||||
end
|
||||
|
||||
|
||||
# plot one data series
|
||||
function _add_series(::ImmerseBackend, plt::Plot, d::KW)
|
||||
addGadflySeries!(plt, d)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
# # plot one data series
|
||||
# function _series_added(::ImmerseBackend, plt::Plot, d::KW)
|
||||
# addGadflySeries!(plt, d)
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
# end
|
||||
|
||||
function _series_added(plt::Plot{ImmerseBackend}, series::Series)
|
||||
addGadflySeries!(plt, series.d)
|
||||
end
|
||||
|
||||
|
||||
function _update_plot(plt::Plot{ImmerseBackend}, d::KW)
|
||||
function _update_plot_object(plt::Plot{ImmerseBackend}, d::KW)
|
||||
updateGadflyGuides(plt, d)
|
||||
updateGadflyPlotTheme(plt, d)
|
||||
end
|
||||
@@ -81,51 +88,51 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
|
||||
function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
|
||||
return false
|
||||
# isbefore && return false
|
||||
end
|
||||
|
||||
function showSubplotObject(subplt::Subplot{ImmerseBackend})
|
||||
# create the Gtk window with vertical box vsep
|
||||
d = getplotargs(subplt,1)
|
||||
w,h = d[:size]
|
||||
vsep = Gtk.GtkBoxLeaf(:v)
|
||||
win = Gtk.GtkWindowLeaf(vsep, d[:windowtitle], w, h)
|
||||
|
||||
figindices = []
|
||||
row = Gtk.GtkBoxLeaf(:h)
|
||||
push!(vsep, row)
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
plt = subplt.plts[i]
|
||||
|
||||
# get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
|
||||
box, toolbar, canvas = Immerse.createPlotGuiComponents()
|
||||
|
||||
# add the plot's box to the row
|
||||
push!(row, box)
|
||||
|
||||
# create the figure and store the index returned for destruction later
|
||||
figidx = Immerse.figure(canvas)
|
||||
push!(figindices, figidx)
|
||||
|
||||
fig = Immerse.figure(figidx)
|
||||
plt.o = (fig, plt.o[2])
|
||||
|
||||
# add the row
|
||||
if c == ncols(subplt.layout, r)
|
||||
row = Gtk.GtkBoxLeaf(:h)
|
||||
push!(vsep, row)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# destructor... clean up plots
|
||||
Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
|
||||
|
||||
subplt.o = win
|
||||
true
|
||||
end
|
||||
# function _create_subplot(subplt::Subplot{ImmerseBackend}, isbefore::Bool)
|
||||
# return false
|
||||
# # isbefore && return false
|
||||
# end
|
||||
#
|
||||
# function showSubplotObject(subplt::Subplot{ImmerseBackend})
|
||||
# # create the Gtk window with vertical box vsep
|
||||
# d = getattr(subplt,1)
|
||||
# w,h = d[:size]
|
||||
# vsep = Gtk.GtkBoxLeaf(:v)
|
||||
# win = Gtk.GtkWindowLeaf(vsep, d[:window_title], w, h)
|
||||
#
|
||||
# figindices = []
|
||||
# row = Gtk.GtkBoxLeaf(:h)
|
||||
# push!(vsep, row)
|
||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
||||
# plt = subplt.plts[i]
|
||||
#
|
||||
# # get the components... box is the main plot GtkBox, and canvas is the GtkCanvas where it's plotted
|
||||
# box, toolbar, canvas = Immerse.createPlotGuiComponents()
|
||||
#
|
||||
# # add the plot's box to the row
|
||||
# push!(row, box)
|
||||
#
|
||||
# # create the figure and store the index returned for destruction later
|
||||
# figidx = Immerse.figure(canvas)
|
||||
# push!(figindices, figidx)
|
||||
#
|
||||
# fig = Immerse.figure(figidx)
|
||||
# plt.o = (fig, plt.o[2])
|
||||
#
|
||||
# # add the row
|
||||
# if c == ncols(subplt.layout, r)
|
||||
# row = Gtk.GtkBoxLeaf(:h)
|
||||
# push!(vsep, row)
|
||||
# end
|
||||
#
|
||||
# end
|
||||
#
|
||||
# # destructor... clean up plots
|
||||
# Gtk.on_signal_destroy((x...) -> ([Immerse.dropfig(Immerse._display,i) for i in figindices]; subplt.o = nothing), win)
|
||||
#
|
||||
# subplt.o = win
|
||||
# true
|
||||
# end
|
||||
|
||||
|
||||
function _remove_axis(plt::Plot{ImmerseBackend}, isx::Bool)
|
||||
@@ -144,14 +151,14 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
getGadflyContext(plt::Plot{ImmerseBackend}) = plt.o[2]
|
||||
getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
|
||||
# getGadflyContext(subplt::Subplot{ImmerseBackend}) = buildGadflySubplotContext(subplt)
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
|
||||
|
||||
fig, gplt = plt.o
|
||||
if fig == nothing
|
||||
fig = createImmerseFigure(plt.plotargs)
|
||||
fig = createImmerseFigure(plt.attr)
|
||||
Gtk.on_signal_destroy((x...) -> (Immerse.dropfig(Immerse._display, fig.figno); plt.o = (nothing,gplt)), fig.canvas)
|
||||
plt.o = (fig, gplt)
|
||||
end
|
||||
@@ -161,20 +168,20 @@ function Base.display(::PlotsDisplay, plt::Plot{ImmerseBackend})
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
|
||||
|
||||
# if we haven't created the window yet, do it
|
||||
if subplt.o == nothing
|
||||
showSubplotObject(subplt)
|
||||
end
|
||||
|
||||
# display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
|
||||
for plt in subplt.plts
|
||||
fig, gplt = plt.o
|
||||
Immerse.figure(fig.figno; displayfig = false)
|
||||
display(gplt)
|
||||
end
|
||||
|
||||
# o is the window... show it
|
||||
showall(subplt.o)
|
||||
end
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{ImmerseBackend})
|
||||
#
|
||||
# # if we haven't created the window yet, do it
|
||||
# if subplt.o == nothing
|
||||
# showSubplotObject(subplt)
|
||||
# end
|
||||
#
|
||||
# # display the plots by creating a fresh Immerse.Figure object from the GtkCanvas and Gadfly.Plot
|
||||
# for plt in subplt.plts
|
||||
# fig, gplt = plt.o
|
||||
# Immerse.figure(fig.figno; displayfig = false)
|
||||
# display(gplt)
|
||||
# end
|
||||
#
|
||||
# # o is the window... show it
|
||||
# showall(subplt.o)
|
||||
# end
|
||||
|
||||
+233
-291
@@ -1,68 +1,53 @@
|
||||
# https://github.com/sisl/PGFPlots.jl
|
||||
|
||||
# significant contributions by: @pkofod
|
||||
|
||||
supportedArgs(::PGFPlotsBackend) = [
|
||||
# :annotation,
|
||||
# :axis,
|
||||
:background_color,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
# :foreground_color,
|
||||
# :group,
|
||||
# :label,
|
||||
# :layout,
|
||||
# :legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
# :markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokestyle,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
# :annotations,
|
||||
:background_color, :foreground_color,
|
||||
:color_palette,
|
||||
# :background_color_legend,
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
:group,
|
||||
:label,
|
||||
:seriestype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
# :bar_width, :bar_edges,
|
||||
# :n, :nc, :nr,
|
||||
:layout,
|
||||
# :smooth,
|
||||
# :show,
|
||||
# :size,
|
||||
:title,
|
||||
# :windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
:xflip,
|
||||
:yflip,
|
||||
:z,
|
||||
:zscale,
|
||||
# :tickfont,
|
||||
# :guidefont,
|
||||
# :legendfont,
|
||||
:grid,
|
||||
# :surface
|
||||
# :levels,
|
||||
:title,
|
||||
# :window_title,
|
||||
:show, :size,
|
||||
:x, :xguide, :xlims, :xticks, :xscale, :xflip, :xrotation,
|
||||
:y, :yguide, :ylims, :yticks, :yscale, :yflip, :yrotation,
|
||||
:z, :zguide, :zlims, :zticks, :zscale, :zflip, :zrotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
# :colorbar,
|
||||
# :marker_z, :levels,
|
||||
# :xerror, :yerror,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
# :polar,
|
||||
# :normalize, :weights, :contours,
|
||||
:aspect_ratio,
|
||||
# :match_dimensions,
|
||||
]
|
||||
supportedAxes(::PGFPlotsBackend) = [:auto, :left]
|
||||
supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :line, :steppre, :stepmid, :steppost, :hist, :bar, :hist2d, :sticks, :ysticks, :xsticks, :contour] # :hexbin, :hline, :vline,]
|
||||
supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
|
||||
supportedStyles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::PGFPlotsBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
|
||||
supportedScales(::PGFPlotsBackend) = [:identity, :log, :ln, :log2, :log10] # :asinh, :sqrt]
|
||||
supportedScales(::PGFPlotsBackend) = [:identity, :ln, :log2, :log10] # :asinh, :sqrt]
|
||||
subplotSupported(::PGFPlotsBackend) = false
|
||||
|
||||
|
||||
@@ -73,282 +58,239 @@ function _initialize_backend(::PGFPlotsBackend; kw...)
|
||||
@eval begin
|
||||
import PGFPlots
|
||||
export PGFPlots
|
||||
# TODO: other initialization that needs to be eval-ed
|
||||
end
|
||||
# TODO: other initialization
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
const _pgfplots_linestyles = KW(
|
||||
:solid => "solid",
|
||||
:dash => "dashed",
|
||||
:dot => "dotted",
|
||||
:dashdot => "dashdotted",
|
||||
:dashdotdot => "dashdotdotted"
|
||||
:dashdotdot => "dashdotdotted",
|
||||
)
|
||||
|
||||
const _pgfplots_markers = KW(
|
||||
:none => "mark = none,",
|
||||
:cross => "mark = +,",
|
||||
:xcross => "mark = x,",
|
||||
:utriangle => "mark = triangle*,",
|
||||
:dtriangle => "mark = triangle*,",
|
||||
:ellipse => "mark = o*,",
|
||||
:rect => "mark = square*,",
|
||||
:star5 => "mark = star,",
|
||||
:star6 => "mark = asterisk,",
|
||||
:diamond => "mark = diamond*,",
|
||||
:pentagon => "mark = pentagon*,"
|
||||
:none => "none",
|
||||
:cross => "+",
|
||||
:xcross => "x",
|
||||
:utriangle => "triangle*",
|
||||
:dtriangle => "triangle*",
|
||||
:ellipse => "*",
|
||||
:rect => "square*",
|
||||
:star5 => "star",
|
||||
:star6 => "asterisk",
|
||||
:diamond => "diamond*",
|
||||
:pentagon => "pentagon*",
|
||||
)
|
||||
|
||||
function _pgfplots_get_color(kwargs, symb)
|
||||
c = typeof(kwargs[symb]) == Symbol ? convertColor(kwargs[symb]) : kwargs[symb].c
|
||||
"{rgb,1:red,$(float(c.r));green,$(float(c.g));blue,$(float(c.b))}"
|
||||
const _pgfplots_legend_pos = KW(
|
||||
:bottomleft => "south west",
|
||||
:bottomright => "south east",
|
||||
:topright => "north east",
|
||||
:topleft => "north west",
|
||||
)
|
||||
|
||||
|
||||
const _pgf_series_extrastyle = KW(
|
||||
:steppre => "const plot mark right",
|
||||
:stepmid => "const plot mark mid",
|
||||
:steppost => "const plot",
|
||||
:sticks => "ycomb",
|
||||
:ysticks => "ycomb",
|
||||
:xsticks => "xcomb",
|
||||
)
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
# takes in color,alpha, and returns color and alpha appropriate for pgf style
|
||||
function pgf_color(c, a = nothing)
|
||||
c = getColor(c)
|
||||
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c))
|
||||
a = float(a == nothing ? alpha(c) : a)
|
||||
cstr, a
|
||||
end
|
||||
|
||||
function _pgfplots_get_linestyle!(kwargs, plt)
|
||||
ls = plt[:linestyle]
|
||||
if haskey(_pgfplots_linestyles, ls)
|
||||
kwargs[:style] *= _pgfplots_linestyles[ls]*","
|
||||
function pgf_fillstyle(d::KW)
|
||||
cstr,a = pgf_color(d[:fillcolor], d[:fillalpha])
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
end
|
||||
|
||||
function pgf_linestyle(d::KW)
|
||||
cstr,a = pgf_color(d[:linecolor], d[:linealpha])
|
||||
"""
|
||||
color = $cstr,
|
||||
draw opacity=$a,
|
||||
line width=$(d[:linewidth]),
|
||||
$(get(_pgfplots_linestyles, d[:linestyle], "solid"))"""
|
||||
end
|
||||
|
||||
function pgf_marker(d::KW)
|
||||
shape = d[:markershape]
|
||||
cstr, a = pgf_color(d[:markercolor], d[:markeralpha])
|
||||
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
"""
|
||||
mark = $(get(_pgfplots_markers, shape, "*")),
|
||||
mark size = $(0.5 * d[:markersize]),
|
||||
mark options = {
|
||||
color = $cstr_stroke, draw opacity = $a_stroke,
|
||||
fill = $cstr, fill opacity = $a,
|
||||
line width = $(d[:markerstrokewidth]),
|
||||
rotate = $(shape == :dtriangle ? 180 : 0),
|
||||
$(get(_pgfplots_linestyles, d[:markerstrokestyle], "solid"))
|
||||
}"""
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function pgf_series(sp::Subplot, series::Series)
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
|
||||
push!(style, pgf_linestyle(d))
|
||||
push!(style, pgf_marker(d))
|
||||
|
||||
if d[:fillrange] != nothing
|
||||
push!(style, pgf_fillstyle(d))
|
||||
end
|
||||
|
||||
kwargs[:style] *= "line width = $(plt[:linewidth]) pt"*","
|
||||
end
|
||||
|
||||
|
||||
function _pgfplots_get_marker!(kwargs, plt)
|
||||
# Control marker shape
|
||||
mark = plt[:markershape]
|
||||
kwargs[:style] *= _pgfplots_markers[mark]
|
||||
|
||||
# Control marker size
|
||||
kwargs[:style] *= "mark size = $(plt[:markersize]/2),"
|
||||
|
||||
# Control marker colors and alphas
|
||||
α = plt[:markeralpha] == nothing ? 1.0 : plt[:markeralpha]
|
||||
kwargs[:style] *= "mark options = {color=$(_pgfplots_get_color(plt, :markerstrokecolor)),"
|
||||
kwargs[:style] *= mark == :dtriangle ? "rotate=180," : ""
|
||||
kwargs[:style] *= "fill=$(_pgfplots_get_color(plt, :markercolor)),"
|
||||
kwargs[:style] *= "fill opacity = $α,"
|
||||
markstrokestyle = plt[:markerstrokestyle]
|
||||
if haskey(_pgfplots_linestyles, markstrokestyle)
|
||||
kwargs[:style] *= _pgfplots_linestyles[markstrokestyle]
|
||||
# add to legend?
|
||||
if sp[:legend] != :none && should_add_to_legend(series)
|
||||
kw[:legendentry] = d[:label]
|
||||
end
|
||||
kwargs[:style] *= "},"
|
||||
end
|
||||
|
||||
function _pgfplots_get_series_color!(kwargs, plt)
|
||||
α = plt[:seriesalpha] == nothing ? 1.0 : plt[:seriesalpha]
|
||||
kwargs[:style] *= "color=$(_pgfplots_get_color(plt, :seriescolor)),"
|
||||
kwargs[:style] *= "draw opacity = $α,"
|
||||
end
|
||||
|
||||
function _pgfplots_get_line_color!(kwargs, plt)
|
||||
α = plt[:linealpha] == nothing ? 1.0 : plt[:linealpha]
|
||||
kwargs[:style] *= "color=$(_pgfplots_get_color(plt, :linecolor)),"
|
||||
kwargs[:style] *= "draw opacity = $α,"
|
||||
end
|
||||
|
||||
function _pgfplots_get_fill_color!(kwargs, plt)
|
||||
α = plt[:fillalpha] == nothing ? 1.0 : plt[:fillalpha]
|
||||
kwargs[:style] *= "fill=$(_pgfplots_get_color(plt, :fillcolor)),"
|
||||
kwargs[:style] *= "fill opacity = $α,"
|
||||
end
|
||||
|
||||
function _pgfplots_get_plot_kwargs(plt)
|
||||
kwargs = KW()
|
||||
kwargs[:style] = ""
|
||||
_pgfplots_get_linestyle!(kwargs, plt)
|
||||
_pgfplots_get_marker!(kwargs, plt)
|
||||
_pgfplots_get_series_color!(kwargs, plt)
|
||||
kwargs
|
||||
end
|
||||
|
||||
function _pgfplots_axis(plt_series)
|
||||
line_type = plt_series[:linetype]
|
||||
plt_kwargs = _pgfplots_get_plot_kwargs(plt_series)
|
||||
if line_type == :path
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :path3d
|
||||
PGFPlots.Linear3(plt_series[:x], plt_series[:y], plt_series[:z]; plt_kwargs...)
|
||||
elseif line_type == :scatter
|
||||
PGFPlots.Scatter(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :steppre
|
||||
plt_kwargs[:style] *= "const plot mark right,"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :stepmid
|
||||
plt_kwargs[:style] *= "const plot mark mid,"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :steppost
|
||||
plt_kwargs[:style] *= "const plot,"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :hist
|
||||
#TODO patch this in PGFPlots.jl instead; the problem is that PGFPlots will
|
||||
# save _all_ data points in the figure which can be quite heavy
|
||||
plt_hist = hist(plt_series[:y])
|
||||
plt_kwargs[:style] *= "ybar interval,"
|
||||
_pgfplots_get_line_color!(plt_kwargs, plt_series)
|
||||
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
|
||||
PGFPlots.Linear(plt_hist[1][1:end-1]+plt_hist[1].step/2, plt_hist[2]; plt_kwargs...)
|
||||
elseif line_type == :hist2d
|
||||
PGFPlots.Histogram2(plt_series[:x], plt_series[:y])
|
||||
elseif line_type == :bar
|
||||
plt_kwargs[:style] *= "ybar,"
|
||||
_pgfplots_get_line_color!(plt_kwargs, plt_series)
|
||||
_pgfplots_get_fill_color!(plt_kwargs, plt_series)
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :sticks || line_type == :ysticks
|
||||
plt_kwargs[:style] *= "ycomb"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :xsticks
|
||||
plt_kwargs[:style] *= "xcomb"
|
||||
PGFPlots.Linear(plt_series[:x], plt_series[:y]; plt_kwargs...)
|
||||
elseif line_type == :contour
|
||||
PGFPlots.Contour(plt_series[:z].surf, plt_series[:x], plt_series[:y])
|
||||
# function args
|
||||
args = if st == :contour
|
||||
d[:z].surf, d[:x], d[:y]
|
||||
elseif is3d(st)
|
||||
d[:x], d[:y], d[:z]
|
||||
else
|
||||
d[:x], d[:y]
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# include additional style, then add to the kw
|
||||
if haskey(_pgf_series_extrastyle, st)
|
||||
push!(style, _pgf_series_extrastyle[st])
|
||||
end
|
||||
kw[:style] = join(style, ',')
|
||||
|
||||
function _create_plot(pkg::PGFPlotsBackend, d::KW)
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
Plot(nothing, pkg, 0, d, KW[])
|
||||
# build/return the series object
|
||||
func = if st == :path3d
|
||||
PGFPlots.Linear3
|
||||
elseif st == :scatter
|
||||
PGFPlots.Scatter
|
||||
elseif st == :histogram2d
|
||||
PGFPlots.Histogram2
|
||||
elseif st == :contour
|
||||
PGFPlots.Contour
|
||||
else
|
||||
PGFPlots.Linear
|
||||
end
|
||||
func(args...; kw...)
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::PGFPlotsBackend, plt::Plot, d::KW)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{PGFPlotsBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
# set or add to the annotation_list
|
||||
if haskey(plt.plotargs, :annotation_list)
|
||||
append!(plt.plotargs[:annotation_list], anns)
|
||||
else
|
||||
plt.plotargs[:annotation_list] = anns
|
||||
end
|
||||
function pgf_axis(sp::Subplot, letter)
|
||||
axis = sp[Symbol(letter,:axis)]
|
||||
style = []
|
||||
kw = KW()
|
||||
|
||||
# axis guide
|
||||
kw[Symbol(letter,:label)] = axis[:guide]
|
||||
|
||||
# flip/reverse?
|
||||
axis[:flip] && push!(style, "$letter dir=reverse")
|
||||
|
||||
# scale
|
||||
scale = axis[:scale]
|
||||
if scale in (:log2, :ln, :log10)
|
||||
kw[Symbol(letter,:mode)] = "log"
|
||||
scale == :ln || push!(style, "log basis $letter=$(scale == :log2 ? 2 : 10)")
|
||||
end
|
||||
|
||||
# limits
|
||||
lims = axis_limits(axis)
|
||||
kw[Symbol(letter,:min)] = lims[1]
|
||||
kw[Symbol(letter,:max)] = lims[2]
|
||||
|
||||
# return the style list and KW args
|
||||
style, kw
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _before_update_plot(plt::Plot{PGFPlotsBackend})
|
||||
end
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot(plt::Plot{PGFPlotsBackend}, d::KW)
|
||||
end
|
||||
function _make_pgf_plot!(plt::Plot)
|
||||
plt.o = PGFPlots.Axis[]
|
||||
for sp in plt.subplots
|
||||
# first build the PGFPlots.Axis object
|
||||
style = ["unbounded coords=jump"]
|
||||
kw = KW()
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{PGFPlotsBackend}, d::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
# function getxy(plt::Plot{PGFPlotsBackend}, i::Int)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y]
|
||||
# end
|
||||
#
|
||||
# function setxy!{X,Y}(plt::Plot{PGFPlotsBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y] = xy
|
||||
# plt
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _create_subplot(subplt::Subplot{PGFPlotsBackend}, isbefore::Bool)
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
true
|
||||
end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{PGFPlotsBackend}, isx::Bool)
|
||||
# TODO: call expand limits for each plot data
|
||||
end
|
||||
|
||||
function _remove_axis(plt::Plot{PGFPlotsBackend}, isx::Bool)
|
||||
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _pgfplots_get_axis_kwargs(d)
|
||||
axisargs = KW()
|
||||
for arg in (:xlabel, :ylabel, :zlabel, :title)
|
||||
axisargs[arg] = d[arg]
|
||||
end
|
||||
axisargs[:style] = ""
|
||||
axisargs[:style] *= d[:xflip] == true ? "x dir=reverse," : ""
|
||||
axisargs[:style] *= d[:yflip] == true ? "y dir=reverse," : ""
|
||||
if d[:xscale] in (:log, :log2, :ln, :log10)
|
||||
axisargs[:xmode] = "log"
|
||||
if d[:xscale] == :log2
|
||||
axisargs[:style] *= "log basis x=2,"
|
||||
elseif d[:xscale] in (:log, :log10)
|
||||
axisargs[:style] *= "log basis x=10,"
|
||||
# add to style/kw for each axis
|
||||
for letter in (:x, :y, :z)
|
||||
if letter != :z || is3d(sp)
|
||||
axisstyle, axiskw = pgf_axis(sp, letter)
|
||||
merge!(kw, axiskw)
|
||||
end
|
||||
end
|
||||
end
|
||||
if d[:yscale] in (:log, :log2, :ln, :log10)
|
||||
axisargs[:ymode] = "log"
|
||||
if d[:yscale] == :log2
|
||||
axisargs[:style] *= "log basis y=2,"
|
||||
elseif d[:yscale] in (:log, :log10)
|
||||
axisargs[:style] *= "log basis x=10,"
|
||||
|
||||
# bounding box values are in mm
|
||||
# note: bb origin is top-left, pgf is bottom-left
|
||||
bb = bbox(sp)
|
||||
push!(style, """
|
||||
xshift = $(left(bb).value)mm,
|
||||
yshift = $((height(bb) - (bottom(bb))).value)mm,
|
||||
width = $(width(bb).value)mm,
|
||||
height = $(height(bb).value)mm,
|
||||
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
|
||||
""")
|
||||
|
||||
if sp[:title] != ""
|
||||
push!(style, "title = $(sp[:title])")
|
||||
end
|
||||
end
|
||||
if d[:zscale] in (:log, :log2, :ln, :log10)
|
||||
axisargs[:zmode] = "log"
|
||||
if d[:zscale] == :log2
|
||||
axisargs[:style] *= "log basis z=2,"
|
||||
elseif d[:zscale] in (:log, :log10)
|
||||
axisargs[:style] *= "log basis x=10,"
|
||||
|
||||
sp[:grid] && push!(style, "grid = major")
|
||||
if sp[:aspect_ratio] in (1, :equal)
|
||||
kw[:axisEqual] = "true"
|
||||
end
|
||||
end
|
||||
|
||||
# Control background color
|
||||
axisargs[:style] *= "axis background/.style={fill=$(_pgfplots_get_color(d, :background_color))},"
|
||||
# Control x/y-limits
|
||||
if d[:xlims] !== :auto
|
||||
axisargs[:xmin] = d[:xlims][1]
|
||||
axisargs[:xmax] = d[:xlims][2]
|
||||
end
|
||||
if d[:ylims] !== :auto
|
||||
axisargs[:ymin] = d[:ylims][1]
|
||||
axisargs[:ymax] = d[:ylims][2]
|
||||
end
|
||||
if d[:grid] == true
|
||||
axisargs[:style] *= "grid = major"
|
||||
elseif d[:grid] == false
|
||||
legpos = sp[:legend]
|
||||
if haskey(_pgfplots_legend_pos, legpos)
|
||||
kw[:legendPos] = _pgfplots_legend_pos[legpos]
|
||||
end
|
||||
|
||||
end
|
||||
o = PGFPlots.Axis(; style = style, kw...)
|
||||
|
||||
axisargs
|
||||
# add the series object to the PGFPlots.Axis
|
||||
for series in series_list(sp)
|
||||
push!(o, pgf_series(sp, series))
|
||||
end
|
||||
|
||||
# add the PGFPlots.Axis to the list
|
||||
push!(plt.o, o)
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
################# This is the important method to implement!!! #################
|
||||
function _make_pgf_plot(plt::Plot{PGFPlotsBackend})
|
||||
os = [_pgfplots_axis(plt_series) for plt_series in plt.seriesargs]
|
||||
axisargs =_pgfplots_get_axis_kwargs(plt.plotargs)
|
||||
plt.o = PGFPlots.Axis([os...]; axisargs...)
|
||||
end
|
||||
|
||||
function Base.writemime(io::IO, mime::MIME"image/svg+xml", plt::AbstractPlot{PGFPlotsBackend})
|
||||
plt.o = _make_pgf_plot(plt)
|
||||
function _writemime(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
|
||||
_make_pgf_plot!(plt)
|
||||
writemime(io, mime, plt.o)
|
||||
end
|
||||
|
||||
# function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PGFPlotsBackend})
|
||||
# end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::AbstractPlot{PGFPlotsBackend})
|
||||
plt.o = _make_pgf_plot(plt)
|
||||
display(plt.o)
|
||||
function _writemime(io::IO, mime::MIME"image/png", plt::Plot{PGFPlotsBackend})
|
||||
_make_pgf_plot!(plt)
|
||||
writemime(io, mime, plt.o)
|
||||
end
|
||||
|
||||
# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsBackend})
|
||||
# # TODO: display/show the subplot
|
||||
# end
|
||||
function _display(plt::Plot{PGFPlotsBackend})
|
||||
_make_pgf_plot!(plt)
|
||||
display(plt.o)
|
||||
end
|
||||
|
||||
+380
-439
@@ -2,71 +2,41 @@
|
||||
# https://plot.ly/javascript/getting-started
|
||||
|
||||
supportedArgs(::PlotlyBackend) = [
|
||||
:annotation,
|
||||
# :axis,
|
||||
:background_color,
|
||||
:color_palette,
|
||||
:fillrange,
|
||||
:fillcolor,
|
||||
:fillalpha,
|
||||
:foreground_color,
|
||||
:annotations,
|
||||
:background_color, :foreground_color, :color_palette,
|
||||
# :background_color_legend, :background_color_inside, :background_color_outside,
|
||||
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
:group,
|
||||
:label,
|
||||
:layout,
|
||||
:legend,
|
||||
:seriestype,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
:markersize,
|
||||
:markeralpha,
|
||||
:markerstrokewidth,
|
||||
:markerstrokecolor,
|
||||
:markerstrokestyle,
|
||||
:n,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:fillrange, :fillcolor, :fillalpha,
|
||||
:bins,
|
||||
:nc,
|
||||
:nr,
|
||||
# :pos,
|
||||
:n, :nc, :nr, :layout,
|
||||
# :smooth,
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
:yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
:xflip,
|
||||
:yflip,
|
||||
:title, :window_title, :show, :size,
|
||||
:x, :xguide, :xlims, :xticks, :xscale, :xflip, :xrotation,
|
||||
:y, :yguide, :ylims, :yticks, :yscale, :yflip, :yrotation,
|
||||
:z, :zguide, :zlims, :zticks, :zscale, :zflip, :zrotation,
|
||||
:z,
|
||||
:marker_z,
|
||||
:tickfont,
|
||||
:guidefont,
|
||||
:legendfont,
|
||||
:grid,
|
||||
:levels,
|
||||
:xerror,
|
||||
:yerror,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:xerror, :yerror,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
# :overwrite_figure,
|
||||
:polar,
|
||||
# :normalize, :weights, :contours, :aspect_ratio
|
||||
]
|
||||
|
||||
supportedAxes(::PlotlyBackend) = [:auto, :left]
|
||||
supportedTypes(::PlotlyBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
|
||||
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
:histogram2d, :histogram, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
|
||||
supportedStyles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supportedMarkers(::PlotlyBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
|
||||
@@ -84,7 +54,7 @@ function _initialize_backend(::PlotlyBackend; kw...)
|
||||
JSON._print(io::IO, state::JSON.State, dt::Union{Date,DateTime}) = print(io, '"', dt, '"')
|
||||
|
||||
_js_path = Pkg.dir("Plots", "deps", "plotly-latest.min.js")
|
||||
_js_code = open(readall, _js_path, "r")
|
||||
_js_code = open(@compat(readstring), _js_path, "r")
|
||||
|
||||
# borrowed from https://github.com/plotly/plotly.py/blob/2594076e29584ede2d09f2aa40a8a195b3f3fc66/plotly/offline/offline.py#L64-L71 c/o @spencerlyon2
|
||||
_js_script = """
|
||||
@@ -112,230 +82,232 @@ function _initialize_backend(::PlotlyBackend; kw...)
|
||||
# TODO: other initialization
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::PlotlyBackend, d::KW)
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
Plot(nothing, pkg, 0, d, KW[])
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::PlotlyBackend, plt::Plot, d::KW)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{PlotlyBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
# set or add to the annotation_list
|
||||
if haskey(plt.plotargs, :annotation_list)
|
||||
append!(plt.plotargs[:annotation_list], anns)
|
||||
else
|
||||
plt.plotargs[:annotation_list] = anns
|
||||
end
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _before_update_plot(plt::Plot{PlotlyBackend})
|
||||
end
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot(plt::Plot{PlotlyBackend}, d::KW)
|
||||
end
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{PlotlyBackend}, d::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
# function getxy(plt::Plot{PlotlyBackend}, i::Int)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y]
|
||||
# end
|
||||
#
|
||||
# function setxy!{X,Y}(plt::Plot{PlotlyBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# d = plt.seriesargs[i]
|
||||
# d[:x], d[:y] = xy
|
||||
# plt
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _create_subplot(subplt::Subplot{PlotlyBackend}, isbefore::Bool)
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
true
|
||||
end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{PlotlyBackend}, isx::Bool)
|
||||
# TODO: call expand limits for each plot data
|
||||
end
|
||||
|
||||
function _remove_axis(plt::Plot{PlotlyBackend}, isx::Bool)
|
||||
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# TODO:
|
||||
# _plotDefaults[:yrightlabel] = ""
|
||||
# _plotDefaults[:xlims] = :auto
|
||||
# _plotDefaults[:ylims] = :auto
|
||||
# _plotDefaults[:xticks] = :auto
|
||||
# _plotDefaults[:yticks] = :auto
|
||||
# _plotDefaults[:xscale] = :identity
|
||||
# _plotDefaults[:yscale] = :identity
|
||||
# _plotDefaults[:xflip] = false
|
||||
# _plotDefaults[:yflip] = false
|
||||
|
||||
function plotlyfont(font::Font, color = font.color)
|
||||
KW(
|
||||
:family => font.family,
|
||||
:size => round(Int, font.pointsize*1.4),
|
||||
:color => webcolor(color),
|
||||
KW(
|
||||
:family => font.family,
|
||||
:size => round(Int, font.pointsize*1.4),
|
||||
:color => webcolor(color),
|
||||
)
|
||||
end
|
||||
|
||||
function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
|
||||
KW(
|
||||
:text => val,
|
||||
:xref => "x",
|
||||
:x => x,
|
||||
:yref => "y",
|
||||
:y => y,
|
||||
:showarrow => false,
|
||||
function get_annotation_dict(x, y, val)
|
||||
KW(
|
||||
:text => val,
|
||||
:xref => "x",
|
||||
:x => x,
|
||||
:yref => "y",
|
||||
:y => y,
|
||||
:showarrow => false,
|
||||
)
|
||||
end
|
||||
|
||||
function get_annotation_dict(x, y, ptxt::PlotText)
|
||||
merge(get_annotation_dict(x, y, ptxt.str), KW(
|
||||
:font => plotlyfont(ptxt.font),
|
||||
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
|
||||
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
|
||||
:rotation => ptxt.font.rotation,
|
||||
merge(get_annotation_dict(x, y, ptxt.str), KW(
|
||||
:font => plotlyfont(ptxt.font),
|
||||
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
|
||||
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
|
||||
:rotation => ptxt.font.rotation,
|
||||
))
|
||||
end
|
||||
|
||||
# function get_annotation_dict_for_arrow(d::KW, xyprev::Tuple, xy::Tuple, a::Arrow)
|
||||
# xdiff = xyprev[1] - xy[1]
|
||||
# ydiff = xyprev[2] - xy[2]
|
||||
# dist = sqrt(xdiff^2 + ydiff^2)
|
||||
# KW(
|
||||
# :showarrow => true,
|
||||
# :x => xy[1],
|
||||
# :y => xy[2],
|
||||
# # :ax => xyprev[1] - xy[1],
|
||||
# # :ay => xy[2] - xyprev[2],
|
||||
# # :ax => 0,
|
||||
# # :ay => -40,
|
||||
# :ax => 10xdiff / dist,
|
||||
# :ay => -10ydiff / dist,
|
||||
# :arrowcolor => webcolor(d[:linecolor], d[:linealpha]),
|
||||
# :xref => "x",
|
||||
# :yref => "y",
|
||||
# :arrowsize => 10a.headwidth,
|
||||
# # :arrowwidth => a.headlength,
|
||||
# :arrowwidth => 0.1,
|
||||
# )
|
||||
# end
|
||||
|
||||
function plotlyscale(scale::Symbol)
|
||||
if scale == :log10
|
||||
"log"
|
||||
else
|
||||
"-"
|
||||
end
|
||||
if scale == :log10
|
||||
"log"
|
||||
else
|
||||
"-"
|
||||
end
|
||||
end
|
||||
|
||||
use_axis_field(ticks) = !(ticks in (nothing, :none))
|
||||
|
||||
tickssym(isx::Bool) = symbol((isx ? "x" : "y") * "ticks")
|
||||
limssym(isx::Bool) = symbol((isx ? "x" : "y") * "lims")
|
||||
flipsym(isx::Bool) = symbol((isx ? "x" : "y") * "flip")
|
||||
scalesym(isx::Bool) = symbol((isx ? "x" : "y") * "scale")
|
||||
labelsym(isx::Bool) = symbol((isx ? "x" : "y") * "label")
|
||||
# this method gets the start/end in percentage of the canvas for this axis direction
|
||||
function plotly_domain(sp::Subplot, letter)
|
||||
figw, figh = sp.plt[:size]
|
||||
pcts = bbox_to_pcts(sp.plotarea, figw*px, figh*px)
|
||||
i1,i2 = (letter == :x ? (1,3) : (2,4))
|
||||
[pcts[i1], pcts[i1]+pcts[i2]]
|
||||
end
|
||||
|
||||
function plotlyaxis(d::KW, isx::Bool)
|
||||
ax = KW(
|
||||
:title => d[labelsym(isx)],
|
||||
:showgrid => d[:grid],
|
||||
:zeroline => false,
|
||||
|
||||
function plotly_axis(axis::Axis, sp::Subplot)
|
||||
letter = axis[:letter]
|
||||
# d = axis.d
|
||||
ax = KW(
|
||||
:title => axis[:guide],
|
||||
:showgrid => sp[:grid],
|
||||
:zeroline => false,
|
||||
)
|
||||
|
||||
fgcolor = webcolor(d[:foreground_color])
|
||||
tsym = tickssym(isx)
|
||||
# fgcolor = webcolor(axis[:foreground_color])
|
||||
# tsym = tickssym(letter)
|
||||
|
||||
if use_axis_field(d[tsym])
|
||||
ax[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
|
||||
ax[:type] = plotlyscale(d[scalesym(isx)])
|
||||
ax[:tickfont] = plotlyfont(d[:tickfont], fgcolor)
|
||||
ax[:tickcolor] = fgcolor
|
||||
ax[:linecolor] = fgcolor
|
||||
|
||||
# xlims
|
||||
lims = d[limssym(isx)]
|
||||
if lims != :auto && limsType(lims) == :limits
|
||||
ax[:range] = lims
|
||||
# spidx = sp[:subplot_index]
|
||||
# d_out[:xaxis] = "x$spidx"
|
||||
# d_out[:yaxis] = "y$spidx"
|
||||
if letter in (:x,:y)
|
||||
ax[:domain] = plotly_domain(sp, letter)
|
||||
ax[:anchor] = "$(letter==:x ? :y : :x)$(plotly_subplot_index(sp))"
|
||||
end
|
||||
|
||||
# xflip
|
||||
if d[flipsym(isx)]
|
||||
ax[:autorange] = "reversed"
|
||||
rot = axis[:rotation]
|
||||
if rot != 0
|
||||
ax[:tickangle] = rot
|
||||
end
|
||||
|
||||
# xticks
|
||||
ticks = d[tsym]
|
||||
if ticks != :auto
|
||||
ttype = ticksType(ticks)
|
||||
if ttype == :ticks
|
||||
ax[:tickmode] = "array"
|
||||
ax[:tickvals] = ticks
|
||||
elseif ttype == :ticks_and_labels
|
||||
ax[:tickmode] = "array"
|
||||
ax[:tickvals], ax[:ticktext] = ticks
|
||||
end
|
||||
if use_axis_field(axis[:ticks])
|
||||
ax[:titlefont] = plotlyfont(axis[:guidefont], webcolor(axis[:foreground_color_guide]))
|
||||
ax[:type] = plotlyscale(axis[:scale])
|
||||
ax[:tickfont] = plotlyfont(axis[:tickfont], webcolor(axis[:foreground_color_text]))
|
||||
ax[:tickcolor] = webcolor(axis[:foreground_color_border])
|
||||
ax[:linecolor] = webcolor(axis[:foreground_color_border])
|
||||
|
||||
# lims
|
||||
lims = axis[:lims]
|
||||
if lims != :auto && limsType(lims) == :limits
|
||||
ax[:range] = lims
|
||||
end
|
||||
|
||||
# flip
|
||||
if axis[:flip]
|
||||
ax[:autorange] = "reversed"
|
||||
end
|
||||
|
||||
# ticks
|
||||
ticks = axis[:ticks]
|
||||
if ticks != :auto
|
||||
ttype = ticksType(ticks)
|
||||
if ttype == :ticks
|
||||
ax[:tickmode] = "array"
|
||||
ax[:tickvals] = ticks
|
||||
elseif ttype == :ticks_and_labels
|
||||
ax[:tickmode] = "array"
|
||||
ax[:tickvals], ax[:ticktext] = ticks
|
||||
end
|
||||
end
|
||||
else
|
||||
ax[:showticklabels] = false
|
||||
ax[:showgrid] = false
|
||||
end
|
||||
|
||||
ax
|
||||
else
|
||||
ax[:showticklabels] = false
|
||||
ax[:showgrid] = false
|
||||
end
|
||||
|
||||
ax
|
||||
end
|
||||
|
||||
# function get_plot_json(plt::Plot{PlotlyBackend})
|
||||
# d = plt.plotargs
|
||||
function plotly_layout(d::KW)
|
||||
d_out = KW()
|
||||
# function plotly_layout_json(plt::Plot{PlotlyBackend})
|
||||
# d = plt
|
||||
# function plotly_layout(d::KW, seriesargs::AVec{KW})
|
||||
function plotly_layout(plt::Plot)
|
||||
d_out = KW()
|
||||
|
||||
d_out[:width], d_out[:height] = d[:size]
|
||||
# # for now, we only support 1 subplot
|
||||
# if length(plt.subplots) > 1
|
||||
# warn("Subplots not supported yet")
|
||||
# end
|
||||
# sp = plt.subplots[1]
|
||||
|
||||
bgcolor = webcolor(d[:background_color])
|
||||
fgcolor = webcolor(d[:foreground_color])
|
||||
d_out[:width], d_out[:height] = plt[:size]
|
||||
d_out[:paper_bgcolor] = webcolor(plt[:background_color_outside])
|
||||
|
||||
# set the fields for the plot
|
||||
d_out[:title] = d[:title]
|
||||
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
|
||||
d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
|
||||
d_out[:plot_bgcolor] = bgcolor
|
||||
d_out[:paper_bgcolor] = bgcolor
|
||||
for sp in plt.subplots
|
||||
sp_out = KW()
|
||||
spidx = plotly_subplot_index(sp)
|
||||
|
||||
# TODO: x/y axis tick values/labels
|
||||
d_out[:xaxis] = plotlyaxis(d, true)
|
||||
d_out[:yaxis] = plotlyaxis(d, false)
|
||||
# set the fields for the plot
|
||||
d_out[:title] = sp[:title]
|
||||
d_out[:titlefont] = plotlyfont(sp[:titlefont], webcolor(sp[:foreground_color_title]))
|
||||
|
||||
# legend
|
||||
d_out[:showlegend] = d[:legend] != :none
|
||||
if d[:legend] != :none
|
||||
d_out[:legend] = KW(
|
||||
:bgcolor => bgcolor,
|
||||
:bordercolor => fgcolor,
|
||||
:font => plotlyfont(d[:legendfont]),
|
||||
)
|
||||
end
|
||||
# # TODO: use subplot positioning logic
|
||||
# d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
|
||||
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>30)
|
||||
|
||||
# annotations
|
||||
anns = get(d, :annotation_list, [])
|
||||
if !isempty(anns)
|
||||
d_out[:annotations] = [get_annotation_dict(ann...) for ann in anns]
|
||||
end
|
||||
d_out[:plot_bgcolor] = webcolor(sp[:background_color_inside])
|
||||
|
||||
if get(d, :polar, false)
|
||||
d_out[:direction] = "counterclockwise"
|
||||
end
|
||||
# TODO: x/y axis tick values/labels
|
||||
|
||||
d_out
|
||||
# if any(is3d, seriesargs)
|
||||
if is3d(sp)
|
||||
d_out[:scene] = KW(
|
||||
Symbol("xaxis$spidx") => plotly_axis(sp[:xaxis], sp),
|
||||
Symbol("yaxis$spidx") => plotly_axis(sp[:yaxis], sp),
|
||||
Symbol("zaxis$spidx") => plotly_axis(sp[:zaxis], sp),
|
||||
)
|
||||
else
|
||||
d_out[Symbol("xaxis$spidx")] = plotly_axis(sp[:xaxis], sp)
|
||||
d_out[Symbol("yaxis$spidx")] = plotly_axis(sp[:yaxis], sp)
|
||||
end
|
||||
|
||||
# legend
|
||||
d_out[:showlegend] = sp[:legend] != :none
|
||||
if sp[:legend] != :none
|
||||
d_out[:legend] = KW(
|
||||
:bgcolor => webcolor(sp[:background_color_legend]),
|
||||
:bordercolor => webcolor(sp[:foreground_color_legend]),
|
||||
:font => plotlyfont(sp[:legendfont]),
|
||||
)
|
||||
end
|
||||
|
||||
# annotations
|
||||
anns = sp[:annotations]
|
||||
d_out[:annotations] = if isempty(anns)
|
||||
KW[]
|
||||
else
|
||||
KW[get_annotation_dict(ann...) for ann in anns]
|
||||
end
|
||||
|
||||
# # arrows
|
||||
# for sargs in seriesargs
|
||||
# a = sargs[:arrow]
|
||||
# if sargs[:seriestype] in (:path, :line) && typeof(a) <: Arrow
|
||||
# add_arrows(sargs[:x], sargs[:y]) do xyprev, xy
|
||||
# push!(d_out[:annotations], get_annotation_dict_for_arrow(sargs, xyprev, xy, a))
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# dumpdict(d_out,"",true)
|
||||
# @show d_out[:annotations]
|
||||
|
||||
if ispolar(sp)
|
||||
d_out[:direction] = "counterclockwise"
|
||||
end
|
||||
|
||||
d_out
|
||||
end
|
||||
|
||||
d_out
|
||||
end
|
||||
|
||||
function get_plot_json(plt::Plot{PlotlyBackend})
|
||||
JSON.json(plotly_layout(plt.plotargs))
|
||||
function plotly_layout_json(plt::Plot)
|
||||
JSON.json(plotly_layout(plt))
|
||||
end
|
||||
|
||||
|
||||
function plotly_colorscale(grad::ColorGradient, alpha = nothing)
|
||||
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
|
||||
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
|
||||
end
|
||||
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(default_gradient(), alpha)
|
||||
|
||||
@@ -347,246 +319,215 @@ const _plotly_markers = KW(
|
||||
:star5 => "star-triangle-up",
|
||||
:vline => "line-ns",
|
||||
:hline => "line-ew",
|
||||
)
|
||||
)
|
||||
|
||||
function plotly_subplot_index(sp::Subplot)
|
||||
spidx = sp[:subplot_index]
|
||||
spidx == 1 ? "" : spidx
|
||||
end
|
||||
|
||||
|
||||
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
|
||||
function plotly_series(d::KW, plotargs::KW; plot_index = nothing)
|
||||
# dumpdict(d,"series",true)
|
||||
d_out = KW()
|
||||
function plotly_series(plt::Plot, series::Series)
|
||||
d = series.d
|
||||
sp = d[:subplot]
|
||||
d_out = KW()
|
||||
|
||||
x, y = collect(d[:x]), collect(d[:y])
|
||||
d_out[:name] = d[:label]
|
||||
# these are the axes that the series should be mapped to
|
||||
spidx = plotly_subplot_index(sp)
|
||||
d_out[:xaxis] = "x$spidx"
|
||||
d_out[:yaxis] = "y$spidx"
|
||||
|
||||
lt = d[:linetype]
|
||||
isscatter = lt in (:scatter, :scatter3d)
|
||||
hasmarker = isscatter || d[:markershape] != :none
|
||||
hasline = !isscatter
|
||||
x, y = collect(d[:x]), collect(d[:y])
|
||||
d_out[:name] = d[:label]
|
||||
st = d[:seriestype]
|
||||
isscatter = st in (:scatter, :scatter3d)
|
||||
hasmarker = isscatter || d[:markershape] != :none
|
||||
hasline = !isscatter
|
||||
|
||||
# set the "type"
|
||||
if lt in (:line, :path, :scatter, :steppre, :steppost)
|
||||
d_out[:type] = "scatter"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
if d[:fillrange] == true || d[:fillrange] == 0
|
||||
d_out[:fill] = "tozeroy"
|
||||
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
|
||||
elseif !(d[:fillrange] in (false, nothing))
|
||||
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(d[:fillrange])")
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
|
||||
elseif lt == :bar
|
||||
d_out[:type] = "bar"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
|
||||
elseif lt == :hist2d
|
||||
d_out[:type] = "histogram2d"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
if isa(d[:bins], Tuple)
|
||||
xbins, ybins = d[:bins]
|
||||
else
|
||||
xbins = ybins = d[:bins]
|
||||
end
|
||||
d_out[:nbinsx] = xbins
|
||||
d_out[:nbinsy] = ybins
|
||||
|
||||
elseif lt in (:hist, :density)
|
||||
d_out[:type] = "histogram"
|
||||
isvert = isvertical(d)
|
||||
d_out[isvert ? :x : :y] = y
|
||||
d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
|
||||
if lt == :density
|
||||
d_out[:histnorm] = "probability density"
|
||||
end
|
||||
|
||||
elseif lt == :heatmap
|
||||
d_out[:type] = "heatmap"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = d[:z].surf
|
||||
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
|
||||
elseif lt == :contour
|
||||
d_out[:type] = "contour"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = d[:z].surf
|
||||
# d_out[:showscale] = d[:colorbar] != :none
|
||||
d_out[:ncontours] = d[:levels]
|
||||
d_out[:contours] = KW(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
|
||||
d_out[:colorscale] = plotly_colorscale(d[:linecolor], d[:linealpha])
|
||||
|
||||
elseif lt in (:surface, :wireframe)
|
||||
d_out[:type] = "surface"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = d[:z].surf
|
||||
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
|
||||
elseif lt == :pie
|
||||
d_out[:type] = "pie"
|
||||
d_out[:labels] = x
|
||||
d_out[:values] = y
|
||||
d_out[:hoverinfo] = "label+percent+name"
|
||||
|
||||
elseif lt in (:path3d, :scatter3d)
|
||||
d_out[:type] = "scatter3d"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = collect(d[:z])
|
||||
|
||||
else
|
||||
warn("Plotly: linetype $lt isn't supported.")
|
||||
return KW()
|
||||
end
|
||||
|
||||
# add "marker"
|
||||
if hasmarker
|
||||
d_out[:marker] = KW(
|
||||
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
|
||||
:opacity => d[:markeralpha],
|
||||
:size => 2 * d[:markersize],
|
||||
:color => webcolor(d[:markercolor], d[:markeralpha]),
|
||||
:line => KW(
|
||||
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
|
||||
:width => d[:markerstrokewidth],
|
||||
),
|
||||
)
|
||||
|
||||
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
|
||||
if d[:marker_z] != nothing
|
||||
# d_out[:marker][:color] = d[:marker_z]
|
||||
# d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor], d[:markeralpha])
|
||||
# d_out[:showscale] = true
|
||||
grad = ColorGradient(d[:markercolor], alpha=d[:markeralpha])
|
||||
zmin, zmax = extrema(d[:marker_z])
|
||||
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:marker_z]]
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# add "line"
|
||||
if hasline
|
||||
d_out[:line] = KW(
|
||||
:color => webcolor(d[:linecolor], d[:linealpha]),
|
||||
:width => d[:linewidth],
|
||||
:shape => if lt == :steppre
|
||||
"vh"
|
||||
elseif lt == :steppost
|
||||
"hv"
|
||||
# set the "type"
|
||||
if st in (:line, :path, :scatter, :steppre, :steppost)
|
||||
d_out[:type] = "scatter"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
"linear"
|
||||
end,
|
||||
:dash => string(d[:linestyle]),
|
||||
# :dash => "solid",
|
||||
)
|
||||
end
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
if d[:fillrange] == true || d[:fillrange] == 0
|
||||
d_out[:fill] = "tozeroy"
|
||||
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
|
||||
elseif !(d[:fillrange] in (false, nothing))
|
||||
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(d[:fillrange])")
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
|
||||
# convert polar plots x/y to theta/radius
|
||||
if get(plotargs, :polar, false)
|
||||
d_out[:t] = rad2deg(pop!(d_out, :x))
|
||||
d_out[:r] = pop!(d_out, :y)
|
||||
end
|
||||
elseif st == :bar
|
||||
d_out[:type] = "bar"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
|
||||
# # for subplots, we need to add the xaxis/yaxis fields
|
||||
# if plot_index != nothing
|
||||
# d_out[:xaxis] = "x$(plot_index)"
|
||||
# d_out[:yaxis] = "y$(plot_index)"
|
||||
# end
|
||||
elseif st == :histogram2d
|
||||
d_out[:type] = "histogram2d"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
if isa(d[:bins], Tuple)
|
||||
xbins, ybins = d[:bins]
|
||||
else
|
||||
xbins = ybins = d[:bins]
|
||||
end
|
||||
d_out[:nbinsx] = xbins
|
||||
d_out[:nbinsy] = ybins
|
||||
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
|
||||
d_out
|
||||
elseif st in (:histogram, :density)
|
||||
d_out[:type] = "histogram"
|
||||
isvert = isvertical(d)
|
||||
d_out[isvert ? :x : :y] = y
|
||||
d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
|
||||
if st == :density
|
||||
d_out[:histogramnorm] = "probability density"
|
||||
end
|
||||
|
||||
elseif st == :heatmap
|
||||
d_out[:type] = "heatmap"
|
||||
# d_out[:x], d_out[:y] = x, y
|
||||
# d_out[:z] = d[:z].surf
|
||||
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
|
||||
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
|
||||
elseif st == :contour
|
||||
d_out[:type] = "contour"
|
||||
# d_out[:x], d_out[:y] = x, y
|
||||
# d_out[:z] = d[:z].surf
|
||||
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
|
||||
# d_out[:showscale] = d[:colorbar] != :none
|
||||
d_out[:ncontours] = d[:levels]
|
||||
d_out[:contours] = KW(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
|
||||
d_out[:colorscale] = plotly_colorscale(d[:linecolor], d[:linealpha])
|
||||
|
||||
elseif st in (:surface, :wireframe)
|
||||
d_out[:type] = "surface"
|
||||
# d_out[:x], d_out[:y] = x, y
|
||||
# d_out[:z] = d[:z].surf
|
||||
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
|
||||
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
|
||||
elseif st == :pie
|
||||
d_out[:type] = "pie"
|
||||
d_out[:labels] = pie_labels(sp, series)
|
||||
d_out[:values] = y
|
||||
d_out[:hoverinfo] = "label+percent+name"
|
||||
|
||||
elseif st in (:path3d, :scatter3d)
|
||||
d_out[:type] = "scatter3d"
|
||||
d_out[:mode] = if hasmarker
|
||||
hasline ? "lines+markers" : "markers"
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = collect(d[:z])
|
||||
|
||||
else
|
||||
warn("Plotly: seriestype $st isn't supported.")
|
||||
return KW()
|
||||
end
|
||||
|
||||
# add "marker"
|
||||
if hasmarker
|
||||
d_out[:marker] = KW(
|
||||
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
|
||||
:opacity => d[:markeralpha],
|
||||
:size => 2 * d[:markersize],
|
||||
:color => webcolor(d[:markercolor], d[:markeralpha]),
|
||||
:line => KW(
|
||||
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
|
||||
:width => d[:markerstrokewidth],
|
||||
),
|
||||
)
|
||||
|
||||
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
|
||||
if d[:marker_z] != nothing
|
||||
# d_out[:marker][:color] = d[:marker_z]
|
||||
# d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor], d[:markeralpha])
|
||||
# d_out[:showscale] = true
|
||||
grad = ColorGradient(d[:markercolor], alpha=d[:markeralpha])
|
||||
zmin, zmax = extrema(d[:marker_z])
|
||||
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:marker_z]]
|
||||
end
|
||||
end
|
||||
|
||||
# add "line"
|
||||
if hasline
|
||||
d_out[:line] = KW(
|
||||
:color => webcolor(d[:linecolor], d[:linealpha]),
|
||||
:width => d[:linewidth],
|
||||
:shape => if st == :steppre
|
||||
"vh"
|
||||
elseif st == :steppost
|
||||
"hv"
|
||||
else
|
||||
"linear"
|
||||
end,
|
||||
:dash => string(d[:linestyle]),
|
||||
# :dash => "solid",
|
||||
)
|
||||
end
|
||||
|
||||
# convert polar plots x/y to theta/radius
|
||||
if ispolar(d[:subplot])
|
||||
d_out[:t] = rad2deg(pop!(d_out, :x))
|
||||
d_out[:r] = pop!(d_out, :y)
|
||||
end
|
||||
|
||||
d_out
|
||||
end
|
||||
|
||||
# get a list of dictionaries, each representing the series params
|
||||
function get_series_json(plt::Plot{PlotlyBackend})
|
||||
JSON.json(map(d -> plotly_series(d, plt.plotargs), plt.seriesargs))
|
||||
end
|
||||
|
||||
function get_series_json(subplt::Subplot{PlotlyBackend})
|
||||
ds = KW[]
|
||||
for (i,plt) in enumerate(subplt.plts)
|
||||
for d in plt.seriesargs
|
||||
push!(ds, plotly_series(d, plt.plotargs, plot_index = i))
|
||||
end
|
||||
end
|
||||
JSON.json(ds)
|
||||
function plotly_series_json(plt::Plot)
|
||||
JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function html_head(plt::AbstractPlot{PlotlyBackend})
|
||||
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
|
||||
function html_head(plt::Plot{PlotlyBackend})
|
||||
"<script src=\"$(Pkg.dir("Plots","deps","plotly-latest.min.js"))\"></script>"
|
||||
end
|
||||
|
||||
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||
if style == nothing
|
||||
w, h = plt.plotargs[:size]
|
||||
style = "width:$(w)px;height:$(h)px;"
|
||||
end
|
||||
uuid = Base.Random.uuid4()
|
||||
html = """
|
||||
<div id=\"$(uuid)\" style=\"$(style)\"></div>
|
||||
<script>
|
||||
PLOT = document.getElementById('$(uuid)');
|
||||
Plotly.plot(PLOT, $(get_series_json(plt)), $(get_plot_json(plt)));
|
||||
</script>
|
||||
"""
|
||||
# @show html
|
||||
html
|
||||
if style == nothing
|
||||
w, h = plt[:size]
|
||||
style = "width:$(w)px;height:$(h)px;"
|
||||
end
|
||||
uuid = Base.Random.uuid4()
|
||||
html = """
|
||||
<div id=\"$(uuid)\" style=\"$(style)\"></div>
|
||||
<script>
|
||||
PLOT = document.getElementById('$(uuid)');
|
||||
Plotly.plot(PLOT, $(plotly_series_json(plt)), $(plotly_layout_json(plt)));
|
||||
</script>
|
||||
"""
|
||||
html
|
||||
end
|
||||
|
||||
function js_body(plt::Plot{PlotlyBackend}, uuid)
|
||||
js = """
|
||||
PLOT = document.getElementById('$(uuid)');
|
||||
Plotly.plot(PLOT, $(get_series_json(plt)), $(get_plot_json(plt)));
|
||||
Plotly.plot(PLOT, $(plotly_series_json(plt)), $(plotly_layout_json(plt)));
|
||||
"""
|
||||
end
|
||||
|
||||
|
||||
function html_body(subplt::Subplot{PlotlyBackend})
|
||||
w, h = subplt.plts[1].plotargs[:size]
|
||||
html = ["<div style=\"width:$(w)px;height:$(h)px;\">"]
|
||||
nr = nrows(subplt.layout)
|
||||
ph = h / nr
|
||||
|
||||
for r in 1:nr
|
||||
push!(html, "<div style=\"clear:both;\">")
|
||||
|
||||
nc = ncols(subplt.layout, r)
|
||||
pw = w / nc
|
||||
|
||||
for c in 1:nc
|
||||
plt = subplt[r,c]
|
||||
push!(html, html_body(plt, "float:left; width:$(pw)px; height:$(ph)px;"))
|
||||
end
|
||||
|
||||
push!(html, "</div>")
|
||||
end
|
||||
push!(html, "</div>")
|
||||
|
||||
join(html)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyBackend})
|
||||
|
||||
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
|
||||
writemime_png_from_html(io, plt)
|
||||
end
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot{PlotlyBackend})
|
||||
function _writemime(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
|
||||
write(io, html_head(plt) * html_body(plt))
|
||||
# write(io, html_body(plt))
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::AbstractPlot{PlotlyBackend})
|
||||
standalone_html_window(plt)
|
||||
# function Base.display(::PlotsDisplay, plt::Plot{PlotlyBackend})
|
||||
function _display(plt::Plot{PlotlyBackend})
|
||||
standalone_html_window(plt)
|
||||
end
|
||||
|
||||
# function Base.display(::PlotsDisplay, plt::Subplot{PlotlyBackend})
|
||||
# # TODO: display/show the subplot
|
||||
# end
|
||||
|
||||
+57
-76
@@ -17,7 +17,7 @@ supportedArgs(::PlotlyJSBackend) = [
|
||||
:seriescolor, :seriesalpha,
|
||||
:linecolor,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:seriestype,
|
||||
:linewidth,
|
||||
:linealpha,
|
||||
:markershape,
|
||||
@@ -36,13 +36,13 @@ supportedArgs(::PlotlyJSBackend) = [
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:window_title,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xguide,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:yguide,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
:yticks,
|
||||
@@ -66,7 +66,7 @@ supportedArgs(::PlotlyJSBackend) = [
|
||||
]
|
||||
supportedAxes(::PlotlyJSBackend) = [:auto, :left]
|
||||
supportedTypes(::PlotlyJSBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
|
||||
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
:histogram2d, :histogram, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
|
||||
supportedStyles(::PlotlyJSBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supportedMarkers(::PlotlyJSBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
|
||||
@@ -85,7 +85,7 @@ function _initialize_backend(::PlotlyJSBackend; kw...)
|
||||
|
||||
for (mime, fmt) in PlotlyJS._mimeformats
|
||||
# mime == "image/png" && continue # don't use plotlyjs's writemime for png
|
||||
@eval Base.writemime(io::IO, m::MIME{symbol($mime)}, p::Plot{PlotlyJSBackend}) = writemime(io, m, p.o)
|
||||
@eval Base.writemime(io::IO, m::MIME{Symbol($mime)}, p::Plot{PlotlyJSBackend}) = writemime(io, m, p.o)
|
||||
end
|
||||
|
||||
# override IJulia inline display
|
||||
@@ -96,61 +96,53 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::PlotlyJSBackend, d::KW)
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
# o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(),
|
||||
# Base.Random.uuid4(), PlotlyJS.ElectronDisplay())
|
||||
# T = isijulia() ? PlotlyJS.JupyterPlot : PlotlyJS.ElectronPlot
|
||||
# o = T(PlotlyJS.Plot())
|
||||
o = PlotlyJS.plot()
|
||||
# function _create_plot(pkg::PlotlyJSBackend, d::KW)
|
||||
# # TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# # TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
# # o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(),
|
||||
# # Base.Random.uuid4(), PlotlyJS.ElectronDisplay())
|
||||
# # T = isijulia() ? PlotlyJS.JupyterPlot : PlotlyJS.ElectronPlot
|
||||
# # o = T(PlotlyJS.Plot())
|
||||
# o = PlotlyJS.plot()
|
||||
#
|
||||
# Plot(o, pkg, 0, d, KW[])
|
||||
# end
|
||||
|
||||
Plot(o, pkg, 0, d, KW[])
|
||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||
PlotlyJS.plot()
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::PlotlyJSBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
syncplot = plt.o
|
||||
|
||||
# add to the data array
|
||||
pdict = plotly_series(d, plt.plotargs)
|
||||
pdict = plotly_series(plt, series)
|
||||
typ = pop!(pdict, :type)
|
||||
gt = PlotlyJS.GenericTrace(typ; pdict...)
|
||||
PlotlyJS.addtraces!(syncplot, gt)
|
||||
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{PlotlyJSBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
# set or add to the annotation_list
|
||||
if !haskey(plt.plotargs, :annotation_list)
|
||||
plt.plotargs[:annotation_list] = Any[]
|
||||
end
|
||||
append!(plt.plotargs[:annotation_list], anns)
|
||||
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
xsym, ysym = (ispolar(series) ? (:t,:r) : (:x,:y))
|
||||
PlotlyJS.restyle!(
|
||||
plt.o,
|
||||
findfirst(plt.series_list, series),
|
||||
KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _before_update_plot(plt::Plot{PlotlyJSBackend})
|
||||
end
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot(plt::Plot{PlotlyJSBackend}, d::KW)
|
||||
pdict = plotly_layout(d)
|
||||
function _update_plot_object(plt::Plot{PlotlyJSBackend})
|
||||
pdict = plotly_layout(plt)
|
||||
syncplot = plt.o
|
||||
w,h = d[:size]
|
||||
w,h = plt[:size]
|
||||
# DD(pdict)
|
||||
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
|
||||
end
|
||||
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{PlotlyJSBackend}, d::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
@@ -160,47 +152,36 @@ end
|
||||
# d[:x], d[:y]
|
||||
# end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
ispolar = get(plt.plotargs, :polar, false)
|
||||
xsym = ispolar ? :t : :x
|
||||
ysym = ispolar ? :r : :y
|
||||
d[xsym], d[ysym] = xy
|
||||
# TODO: this is likely ineffecient... we should make a call that ONLY changes the plot data
|
||||
PlotlyJS.restyle!(plt.o, i, KW(xsym=>(d[xsym],), ysym=>(d[ysym],)))
|
||||
plt
|
||||
end
|
||||
# function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# d = plt.seriesargs[i]
|
||||
# ispolar = get(plt.attr, :polar, false)
|
||||
# xsym = ispolar ? :t : :x
|
||||
# ysym = ispolar ? :r : :y
|
||||
# d[xsym], d[ysym] = xy
|
||||
# # TODO: this is likely ineffecient... we should make a call that ONLY changes the plot data
|
||||
# PlotlyJS.restyle!(plt.o, i, KW(xsym=>(d[xsym],), ysym=>(d[ysym],)))
|
||||
# plt
|
||||
# end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _create_subplot(subplt::Subplot{PlotlyJSBackend}, isbefore::Bool)
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
true
|
||||
end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{PlotlyJSBackend}, isx::Bool)
|
||||
# TODO: call expand limits for each plot data
|
||||
end
|
||||
|
||||
function _remove_axis(plt::Plot{PlotlyJSBackend}, isx::Bool)
|
||||
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# function Base.writemime(io::IO, m::MIME"text/html", plt::AbstractPlot{PlotlyJSBackend})
|
||||
# Base.writemime(io, m, plt.o)
|
||||
# function _update_min_padding!(sp::Subplot{PlotlyBackend})
|
||||
# sp.minpad = plotly_minpad(sp)
|
||||
# end
|
||||
|
||||
# function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{PlotlyJSBackend})
|
||||
# println("here!")
|
||||
# writemime_png_from_html(io, plt)
|
||||
# function plotlyjs_finalize(plt::Plot)
|
||||
# plotly_finalize(plt)
|
||||
# PlotlyJS.relayout!(plt.o, plotly_layout(plt))
|
||||
# end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSBackend})
|
||||
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
|
||||
tmpfn = tempname() * "png"
|
||||
PlotlyJS.savefig(plt.o, tmpfn)
|
||||
write(io, read(open(tmpfn)))
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PlotlyJSBackend})
|
||||
# plotlyjs_finalize(plt)
|
||||
display(plt.o)
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSBackend})
|
||||
error()
|
||||
end
|
||||
|
||||
+580
-456
File diff suppressed because it is too large
Load Diff
+66
-64
@@ -3,7 +3,7 @@
|
||||
|
||||
|
||||
supportedArgs(::QwtBackend) = [
|
||||
:annotation,
|
||||
:annotations,
|
||||
:axis,
|
||||
:background_color,
|
||||
:linecolor,
|
||||
@@ -17,7 +17,7 @@ supportedArgs(::QwtBackend) = [
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:seriestype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
@@ -31,20 +31,20 @@ supportedArgs(::QwtBackend) = [
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:window_title,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xguide,
|
||||
:xlims,
|
||||
:xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:yguide,
|
||||
:ylims,
|
||||
:yrightlabel,
|
||||
:yticks,
|
||||
:xscale,
|
||||
:yscale,
|
||||
]
|
||||
supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
|
||||
supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :histogram2d, :hexbin, :histogram, :bar, :hline, :vline]
|
||||
supportedMarkers(::QwtBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
|
||||
supportedScales(::QwtBackend) = [:identity, :log10]
|
||||
subplotSupported(::QwtBackend) = true
|
||||
@@ -91,29 +91,29 @@ end
|
||||
|
||||
function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
|
||||
d = KW(kw)
|
||||
lt = d[:linetype]
|
||||
if lt == :scatter
|
||||
d[:linetype] = :none
|
||||
st = d[:seriestype]
|
||||
if st == :scatter
|
||||
d[:seriestype] = :none
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
elseif lt in (:hline, :vline)
|
||||
elseif st in (:hline, :vline)
|
||||
addLineMarker(plt, d)
|
||||
d[:linetype] = :none
|
||||
d[:seriestype] = :none
|
||||
d[:markershape] = :ellipse
|
||||
d[:markersize] = 1
|
||||
if lt == :vline
|
||||
if st == :vline
|
||||
d[:x], d[:y] = d[:y], d[:x]
|
||||
end
|
||||
|
||||
elseif !iscreating && lt == :bar
|
||||
elseif !iscreating && st == :bar
|
||||
d = barHack(; kw...)
|
||||
elseif !iscreating && lt == :hist
|
||||
elseif !iscreating && st == :histogram
|
||||
d = barHack(; histogramHack(; kw...)...)
|
||||
end
|
||||
|
||||
replaceQwtAliases(d, :linetype)
|
||||
replaceQwtAliases(d, :seriestype)
|
||||
replaceQwtAliases(d, :markershape)
|
||||
|
||||
for k in keys(d)
|
||||
@@ -128,21 +128,23 @@ function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
|
||||
d
|
||||
end
|
||||
|
||||
function _create_plot(pkg::QwtBackend, d::KW)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot")
|
||||
o = Qwt.plot(zeros(0,0); d..., show=false)
|
||||
plt = Plot(o, pkg, 0, d, KW[])
|
||||
plt
|
||||
# function _create_plot(pkg::QwtBackend, d::KW)
|
||||
function _create_backend_figure(plt::Plot{QwtBackend})
|
||||
fixcolors(plt.attr)
|
||||
dumpdict(plt.attr,"\n\n!!! plot")
|
||||
o = Qwt.plot(zeros(0,0); plt.attr..., show=false)
|
||||
# plt = Plot(o, pkg, 0, d, KW[])
|
||||
# plt
|
||||
end
|
||||
|
||||
function _add_series(::QwtBackend, plt::Plot, d::KW)
|
||||
d = adjustQwtKeywords(plt, false; d...)
|
||||
# function _series_added(::QwtBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{QwtBackend}, series::Series)
|
||||
d = adjustQwtKeywords(plt, false; series.d...)
|
||||
fixcolors(d)
|
||||
dumpdict(d,"\n\n!!! plot!")
|
||||
Qwt.oplot(plt.o; d...)
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
@@ -188,10 +190,10 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
|
||||
end
|
||||
|
||||
|
||||
function _update_plot(plt::Plot{QwtBackend}, d::KW)
|
||||
function _update_plot_object(plt::Plot{QwtBackend}, d::KW)
|
||||
haskey(d, :title) && Qwt.title(plt.o, d[:title])
|
||||
haskey(d, :xlabel) && Qwt.xlabel(plt.o, d[:xlabel])
|
||||
haskey(d, :ylabel) && Qwt.ylabel(plt.o, d[:ylabel])
|
||||
haskey(d, :xguide) && Qwt.xlabel(plt.o, d[:xguide])
|
||||
haskey(d, :yguide) && Qwt.ylabel(plt.o, d[:yguide])
|
||||
updateLimsAndTicks(plt, d, true)
|
||||
updateLimsAndTicks(plt, d, false)
|
||||
end
|
||||
@@ -208,7 +210,7 @@ end
|
||||
function addLineMarker(plt::Plot{QwtBackend}, d::KW)
|
||||
for yi in d[:y]
|
||||
marker = Qwt.QWT.QwtPlotMarker()
|
||||
ishorizontal = (d[:linetype] == :hline)
|
||||
ishorizontal = (d[:seriestype] == :hline)
|
||||
marker[:setLineStyle](ishorizontal ? 1 : 2)
|
||||
marker[ishorizontal ? :setYValue : :setXValue](yi)
|
||||
qcolor = Qwt.convertRGBToQColor(getColor(d[:linecolor]))
|
||||
@@ -267,28 +269,28 @@ end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# create the underlying object (each backend will do this differently)
|
||||
function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
|
||||
isbefore && return false
|
||||
i = 0
|
||||
rows = Any[]
|
||||
row = Any[]
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
push!(row, subplt.plts[i].o)
|
||||
if c == ncols(subplt.layout, r)
|
||||
push!(rows, Qwt.hsplitter(row...))
|
||||
row = Any[]
|
||||
end
|
||||
end
|
||||
# for rowcnt in subplt.layout.rowcounts
|
||||
# push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
|
||||
# i += rowcnt
|
||||
# end
|
||||
subplt.o = Qwt.vsplitter(rows...)
|
||||
# Qwt.resizewidget(subplt.o, getplotargs(subplt,1)[:size]...)
|
||||
# Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
|
||||
true
|
||||
end
|
||||
# # create the underlying object (each backend will do this differently)
|
||||
# function _create_subplot(subplt::Subplot{QwtBackend}, isbefore::Bool)
|
||||
# isbefore && return false
|
||||
# i = 0
|
||||
# rows = Any[]
|
||||
# row = Any[]
|
||||
# for (i,(r,c)) in enumerate(subplt.layout)
|
||||
# push!(row, subplt.plts[i].o)
|
||||
# if c == ncols(subplt.layout, r)
|
||||
# push!(rows, Qwt.hsplitter(row...))
|
||||
# row = Any[]
|
||||
# end
|
||||
# end
|
||||
# # for rowcnt in subplt.layout.rowcounts
|
||||
# # push!(rows, Qwt.hsplitter([plt.o for plt in subplt.plts[(1:rowcnt) + i]]...))
|
||||
# # i += rowcnt
|
||||
# # end
|
||||
# subplt.o = Qwt.vsplitter(rows...)
|
||||
# # Qwt.resizewidget(subplt.o, getattr(subplt,1)[:size]...)
|
||||
# # Qwt.moveToLastScreen(subplt.o) # hack so it goes to my center monitor... sorry
|
||||
# true
|
||||
# end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{QwtBackend}, isx::Bool)
|
||||
for series in plt.o.lines
|
||||
@@ -309,13 +311,13 @@ function Base.writemime(io::IO, ::MIME"image/png", plt::Plot{QwtBackend})
|
||||
write(io, readall("/tmp/dfskjdhfkh.png"))
|
||||
end
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
|
||||
for plt in subplt.plts
|
||||
Qwt.refresh(plt.o)
|
||||
end
|
||||
Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
|
||||
write(io, readall("/tmp/dfskjdhfkh.png"))
|
||||
end
|
||||
# function Base.writemime(io::IO, ::MIME"image/png", subplt::Subplot{QwtBackend})
|
||||
# for plt in subplt.plts
|
||||
# Qwt.refresh(plt.o)
|
||||
# end
|
||||
# Qwt.savepng(subplt.o, "/tmp/dfskjdhfkh.png")
|
||||
# write(io, readall("/tmp/dfskjdhfkh.png"))
|
||||
# end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
|
||||
@@ -323,9 +325,9 @@ function Base.display(::PlotsDisplay, plt::Plot{QwtBackend})
|
||||
Qwt.showwidget(plt.o)
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
|
||||
for plt in subplt.plts
|
||||
Qwt.refresh(plt.o)
|
||||
end
|
||||
Qwt.showwidget(subplt.o)
|
||||
end
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{QwtBackend})
|
||||
# for plt in subplt.plts
|
||||
# Qwt.refresh(plt.o)
|
||||
# end
|
||||
# Qwt.showwidget(subplt.o)
|
||||
# end
|
||||
|
||||
@@ -1,698 +0,0 @@
|
||||
#
|
||||
# supportedAxes(::AbstractBackend) = [:left]
|
||||
# supportedTypes(::AbstractBackend) = []
|
||||
# supportedStyles(::AbstractBackend) = [:solid]
|
||||
# supportedMarkers(::AbstractBackend) = [:none]
|
||||
# supportedScales(::AbstractBackend) = [:identity]
|
||||
# subplotSupported(::AbstractBackend) = false
|
||||
# stringsSupported(::AbstractBackend) = false
|
||||
#
|
||||
# supportedAxes() = supportedAxes(backend())
|
||||
# supportedTypes() = supportedTypes(backend())
|
||||
# supportedStyles() = supportedStyles(backend())
|
||||
# supportedMarkers() = supportedMarkers(backend())
|
||||
# supportedScales() = supportedScales(backend())
|
||||
# subplotSupported() = subplotSupported(backend())
|
||||
# stringsSupported() = stringsSupported(backend())
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
# supportedArgs(::GadflyBackend) = [
|
||||
# :annotation,
|
||||
# :background_color, :foreground_color, :color_palette,
|
||||
# :group,
|
||||
# :label,
|
||||
# :linetype,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linecolor, :linestyle, :linewidth, :linealpha,
|
||||
# :markershape, :markercolor, :markersize, :markeralpha,
|
||||
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
# :fillrange, :fillcolor, :fillalpha,
|
||||
# :bins,
|
||||
# :n, :nc, :nr, :layout,
|
||||
# :smooth,
|
||||
# :title, :windowtitle, :show, :size,
|
||||
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
|
||||
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
|
||||
# # :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
|
||||
# :z,
|
||||
# :tickfont, :guidefont, :legendfont,
|
||||
# :grid, :legend, :colorbar,
|
||||
# :marker_z, :levels,
|
||||
# :xerror, :yerror,
|
||||
# :ribbon, :quiver,
|
||||
# :orientation,
|
||||
# ]
|
||||
# supportedAxes(::GadflyBackend) = [:auto, :left]
|
||||
# supportedTypes(::GadflyBackend) = [
|
||||
# :none, :line, :path, :steppre, :steppost, :sticks,
|
||||
# :scatter, :hist2d, :hexbin, :hist,
|
||||
# :bar, :box, :violin, :quiver,
|
||||
# :hline, :vline, :contour, :shape
|
||||
# ]
|
||||
# supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
# supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
|
||||
# supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
# subplotSupported(::GadflyBackend) = true
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# supportedArgs(::ImmerseBackend) = supportedArgs(GadflyBackend())
|
||||
# supportedAxes(::ImmerseBackend) = supportedAxes(GadflyBackend())
|
||||
# supportedTypes(::ImmerseBackend) = supportedTypes(GadflyBackend())
|
||||
# supportedStyles(::ImmerseBackend) = supportedStyles(GadflyBackend())
|
||||
# supportedMarkers(::ImmerseBackend) = supportedMarkers(GadflyBackend())
|
||||
# supportedScales(::ImmerseBackend) = supportedScales(GadflyBackend())
|
||||
# subplotSupported(::ImmerseBackend) = true
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
#
|
||||
# supportedArgs(::PyPlotBackend) = [
|
||||
# :annotation,
|
||||
# :background_color, :foreground_color, :color_palette,
|
||||
# :background_color_legend, :background_color_inside, :background_color_outside,
|
||||
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
|
||||
# :foreground_color_text, :foreground_color_border,
|
||||
# :group,
|
||||
# :label,
|
||||
# :linetype,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linecolor, :linestyle, :linewidth, :linealpha,
|
||||
# :markershape, :markercolor, :markersize, :markeralpha,
|
||||
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
# :fillrange, :fillcolor, :fillalpha,
|
||||
# :bins,
|
||||
# :n, :nc, :nr, :layout,
|
||||
# :smooth,
|
||||
# :title, :windowtitle, :show, :size,
|
||||
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
|
||||
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
|
||||
# :axis, :yrightlabel,
|
||||
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
|
||||
# :z,
|
||||
# :tickfont, :guidefont, :legendfont,
|
||||
# :grid, :legend, :colorbar,
|
||||
# :marker_z, :levels,
|
||||
# :xerror, :yerror,
|
||||
# :ribbon, :quiver,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
# :polar,
|
||||
# :normalize, :weights, :contours, :aspect_ratio
|
||||
# ]
|
||||
# supportedAxes(::PyPlotBackend) = _allAxes
|
||||
# supportedTypes(::PyPlotBackend) = [
|
||||
# :none, :line, :path, :steppre, :steppost, :shape,
|
||||
# :scatter, :hist2d, :hexbin, :hist, :density,
|
||||
# :bar, :sticks, :box, :violin, :quiver,
|
||||
# :hline, :vline, :heatmap, :pie,
|
||||
# :contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
|
||||
# ]
|
||||
# supportedStyles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
# supportedMarkers(::PyPlotBackend) = vcat(_allMarkers, Shape)
|
||||
# supportedScales(::PyPlotBackend) = [:identity, :ln, :log2, :log10]
|
||||
# subplotSupported(::PyPlotBackend) = true
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
#
|
||||
# supportedArgs(::GRBackend) = [
|
||||
# :annotation,
|
||||
# :background_color, :foreground_color, :color_palette,
|
||||
# :group,
|
||||
# :label,
|
||||
# :linetype,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linecolor, :linestyle, :linewidth, :linealpha,
|
||||
# :markershape, :markercolor, :markersize, :markeralpha,
|
||||
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
# :fillrange, :fillcolor, :fillalpha,
|
||||
# :bins,
|
||||
# :n, :nc, :nr, :layout,
|
||||
# :smooth,
|
||||
# :title, :windowtitle, :show, :size,
|
||||
# :x, :xlabel, :xlims, :xticks, :xscale, :xflip,
|
||||
# :y, :ylabel, :ylims, :yticks, :yscale, :yflip,
|
||||
# :axis, :yrightlabel,
|
||||
# :z, :zlabel, :zlims, :zticks, :zscale, :zflip,
|
||||
# :z,
|
||||
# :tickfont, :guidefont, :legendfont,
|
||||
# :grid, :legend, :colorbar,
|
||||
# :marker_z, :levels,
|
||||
# :xerror, :yerror,
|
||||
# :ribbon, :quiver,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
# :polar,
|
||||
# ]
|
||||
# supportedAxes(::GRBackend) = _allAxes
|
||||
# supportedTypes(::GRBackend) = [:none, :line, :path, :steppre, :steppost, :sticks,
|
||||
# :scatter, :hist2d, :hexbin, :hist, :density, :bar,
|
||||
# :hline, :vline, :contour, :heatmap, :path3d, :scatter3d, :surface,
|
||||
# :wireframe, :ohlc, :pie]
|
||||
# supportedStyles(::GRBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
# supportedMarkers(::GRBackend) = vcat(_allMarkers, Shape)
|
||||
# supportedScales(::GRBackend) = [:identity, :log10]
|
||||
# subplotSupported(::GRBackend) = true
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
#
|
||||
# supportedArgs(::QwtBackend) = [
|
||||
# :annotation,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
# :linecolor,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
# :fillcolor,
|
||||
# :foreground_color,
|
||||
# :group,
|
||||
# :label,
|
||||
# :layout,
|
||||
# :legend,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linestyle,
|
||||
# :linetype,
|
||||
# :linewidth,
|
||||
# :markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# :pos,
|
||||
# :smooth,
|
||||
# :show,
|
||||
# :size,
|
||||
# :title,
|
||||
# :windowtitle,
|
||||
# :x,
|
||||
# :xlabel,
|
||||
# :xlims,
|
||||
# :xticks,
|
||||
# :y,
|
||||
# :ylabel,
|
||||
# :ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# ]
|
||||
# supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
|
||||
# supportedMarkers(::QwtBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
|
||||
# supportedScales(::QwtBackend) = [:identity, :log10]
|
||||
# subplotSupported(::QwtBackend) = true
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# supportedArgs(::UnicodePlotsBackend) = [
|
||||
# # :annotation,
|
||||
# # :args,
|
||||
# # :axis,
|
||||
# # :background_color,
|
||||
# # :linecolor,
|
||||
# # :fill,
|
||||
# # :foreground_color,
|
||||
# :group,
|
||||
# # :heatmap_c,
|
||||
# # :kwargs,
|
||||
# :label,
|
||||
# # :layout,
|
||||
# :legend,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linestyle,
|
||||
# :linetype,
|
||||
# # :linewidth,
|
||||
# :markershape,
|
||||
# # :markercolor,
|
||||
# # :markersize,
|
||||
# # :markerstrokewidth,
|
||||
# # :markerstrokecolor,
|
||||
# # :markerstrokestyle,
|
||||
# # :n,
|
||||
# :bins,
|
||||
# # :nc,
|
||||
# # :nr,
|
||||
# # :pos,
|
||||
# # :reg,
|
||||
# # :ribbon,
|
||||
# :show,
|
||||
# :size,
|
||||
# :title,
|
||||
# :windowtitle,
|
||||
# :x,
|
||||
# :xlabel,
|
||||
# :xlims,
|
||||
# # :xticks,
|
||||
# :y,
|
||||
# :ylabel,
|
||||
# :ylims,
|
||||
# # :yrightlabel,
|
||||
# # :yticks,
|
||||
# # :xscale,
|
||||
# # :yscale,
|
||||
# # :xflip,
|
||||
# # :yflip,
|
||||
# # :z,
|
||||
# ]
|
||||
# supportedAxes(::UnicodePlotsBackend) = [:auto, :left]
|
||||
# supportedTypes(::UnicodePlotsBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
|
||||
# supportedStyles(::UnicodePlotsBackend) = [:auto, :solid]
|
||||
# supportedMarkers(::UnicodePlotsBackend) = [:none, :auto, :ellipse]
|
||||
# supportedScales(::UnicodePlotsBackend) = [:identity]
|
||||
# subplotSupported(::UnicodePlotsBackend) = true
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# supportedArgs(::WinstonBackend) = [
|
||||
# :annotation,
|
||||
# # :args,
|
||||
# # :axis,
|
||||
# # :background_color,
|
||||
# :linecolor,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
# :fillcolor,
|
||||
# # :foreground_color,
|
||||
# :group,
|
||||
# # :heatmap_c,
|
||||
# # :kwargs,
|
||||
# :label,
|
||||
# # :layout,
|
||||
# :legend,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linestyle,
|
||||
# :linetype,
|
||||
# :linewidth,
|
||||
# :markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# # :markerstrokewidth,
|
||||
# # :markerstrokecolor,
|
||||
# # :markerstrokestyle,
|
||||
# # :n,
|
||||
# :bins,
|
||||
# # :nc,
|
||||
# # :nr,
|
||||
# # :pos,
|
||||
# :smooth,
|
||||
# # :ribbon,
|
||||
# :show,
|
||||
# :size,
|
||||
# :title,
|
||||
# :windowtitle,
|
||||
# :x,
|
||||
# :xlabel,
|
||||
# :xlims,
|
||||
# # :xticks,
|
||||
# :y,
|
||||
# :ylabel,
|
||||
# :ylims,
|
||||
# # :yrightlabel,
|
||||
# # :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# # :xflip,
|
||||
# # :yflip,
|
||||
# # :z,
|
||||
# ]
|
||||
# supportedAxes(::WinstonBackend) = [:auto, :left]
|
||||
# supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
|
||||
# supportedStyles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
# supportedMarkers(::WinstonBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
||||
# supportedScales(::WinstonBackend) = [:identity, :log10]
|
||||
# subplotSupported(::WinstonBackend) = false
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
|
||||
#
|
||||
#
|
||||
# supportedArgs(::BokehBackend) = [
|
||||
# # :annotation,
|
||||
# # :axis,
|
||||
# # :background_color,
|
||||
# :linecolor,
|
||||
# # :color_palette,
|
||||
# # :fillrange,
|
||||
# # :fillcolor,
|
||||
# # :fillalpha,
|
||||
# # :foreground_color,
|
||||
# :group,
|
||||
# # :label,
|
||||
# # :layout,
|
||||
# # :legend,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linestyle,
|
||||
# :linetype,
|
||||
# :linewidth,
|
||||
# # :linealpha,
|
||||
# :markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# # :markeralpha,
|
||||
# # :markerstrokewidth,
|
||||
# # :markerstrokecolor,
|
||||
# # :markerstrokestyle,
|
||||
# # :n,
|
||||
# # :bins,
|
||||
# # :nc,
|
||||
# # :nr,
|
||||
# # :pos,
|
||||
# # :smooth,
|
||||
# # :show,
|
||||
# :size,
|
||||
# :title,
|
||||
# # :windowtitle,
|
||||
# :x,
|
||||
# # :xlabel,
|
||||
# # :xlims,
|
||||
# # :xticks,
|
||||
# :y,
|
||||
# # :ylabel,
|
||||
# # :ylims,
|
||||
# # :yrightlabel,
|
||||
# # :yticks,
|
||||
# # :xscale,
|
||||
# # :yscale,
|
||||
# # :xflip,
|
||||
# # :yflip,
|
||||
# # :z,
|
||||
# # :tickfont,
|
||||
# # :guidefont,
|
||||
# # :legendfont,
|
||||
# # :grid,
|
||||
# # :surface,
|
||||
# # :levels,
|
||||
# ]
|
||||
# supportedAxes(::BokehBackend) = [:auto, :left]
|
||||
# supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
# supportedStyles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
# supportedMarkers(::BokehBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
|
||||
# supportedScales(::BokehBackend) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
# subplotSupported(::BokehBackend) = false
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
#
|
||||
# supportedArgs(::PlotlyBackend) = [
|
||||
# :annotation,
|
||||
# # :axis,
|
||||
# :background_color,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
# :fillcolor,
|
||||
# :fillalpha,
|
||||
# :foreground_color,
|
||||
# :group,
|
||||
# :label,
|
||||
# :layout,
|
||||
# :legend,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linecolor,
|
||||
# :linestyle,
|
||||
# :linetype,
|
||||
# :linewidth,
|
||||
# :linealpha,
|
||||
# :markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# :markeralpha,
|
||||
# :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# # :pos,
|
||||
# # :smooth,
|
||||
# :show,
|
||||
# :size,
|
||||
# :title,
|
||||
# :windowtitle,
|
||||
# :x,
|
||||
# :xlabel,
|
||||
# :xlims,
|
||||
# :xticks,
|
||||
# :y,
|
||||
# :ylabel,
|
||||
# :ylims,
|
||||
# # :yrightlabel,
|
||||
# :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
# :marker_z,
|
||||
# :tickfont,
|
||||
# :guidefont,
|
||||
# :legendfont,
|
||||
# :grid,
|
||||
# :levels,
|
||||
# :xerror,
|
||||
# :yerror,
|
||||
# :ribbon,
|
||||
# :quiver,
|
||||
# :orientation,
|
||||
# :polar,
|
||||
# ]
|
||||
# supportedAxes(::PlotlyBackend) = [:auto, :left]
|
||||
# supportedTypes(::PlotlyBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
|
||||
# :hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
# :pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
|
||||
# supportedStyles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
# supportedMarkers(::PlotlyBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
|
||||
# :pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
|
||||
# supportedScales(::PlotlyBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
# subplotSupported(::PlotlyBackend) = true
|
||||
# stringsSupported(::PlotlyBackend) = true
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
#
|
||||
# supportedArgs(::PlotlyJSBackend) = [
|
||||
# :annotation,
|
||||
# # :axis,
|
||||
# :background_color,
|
||||
# :color_palette,
|
||||
# :fillrange,
|
||||
# :fillcolor,
|
||||
# :fillalpha,
|
||||
# :foreground_color,
|
||||
# :group,
|
||||
# :label,
|
||||
# :layout,
|
||||
# :legend,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linecolor,
|
||||
# :linestyle,
|
||||
# :linetype,
|
||||
# :linewidth,
|
||||
# :linealpha,
|
||||
# :markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# :markeralpha,
|
||||
# :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# :n,
|
||||
# :bins,
|
||||
# :nc,
|
||||
# :nr,
|
||||
# # :pos,
|
||||
# # :smooth,
|
||||
# :show,
|
||||
# :size,
|
||||
# :title,
|
||||
# :windowtitle,
|
||||
# :x,
|
||||
# :xlabel,
|
||||
# :xlims,
|
||||
# :xticks,
|
||||
# :y,
|
||||
# :ylabel,
|
||||
# :ylims,
|
||||
# # :yrightlabel,
|
||||
# :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
# :marker_z,
|
||||
# :tickfont,
|
||||
# :guidefont,
|
||||
# :legendfont,
|
||||
# :grid,
|
||||
# :levels,
|
||||
# :xerror,
|
||||
# :yerror,
|
||||
# :ribbon,
|
||||
# :quiver,
|
||||
# :orientation,
|
||||
# :polar,
|
||||
# ]
|
||||
# supportedAxes(::PlotlyJSBackend) = [:auto, :left]
|
||||
# supportedTypes(::PlotlyJSBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
|
||||
# :hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
|
||||
# :pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
|
||||
# supportedStyles(::PlotlyJSBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
# supportedMarkers(::PlotlyJSBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
|
||||
# :pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
|
||||
# supportedScales(::PlotlyJSBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
# subplotSupported(::PlotlyJSBackend) = true
|
||||
# stringsSupported(::PlotlyJSBackend) = true
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
#
|
||||
# supportedArgs(::GLVisualizeBackend) = [
|
||||
# # :annotation,
|
||||
# # :axis,
|
||||
# # :background_color,
|
||||
# # :color_palette,
|
||||
# # :fillrange,
|
||||
# # :fillcolor,
|
||||
# # :fillalpha,
|
||||
# # :foreground_color,
|
||||
# # :group,
|
||||
# # :label,
|
||||
# # :layout,
|
||||
# # :legend,
|
||||
# # :linecolor,
|
||||
# # :linestyle,
|
||||
# :linetype
|
||||
# # :seriescolor, :seriesalpha,
|
||||
# # :linewidth,
|
||||
# # :linealpha,
|
||||
# # :markershape,
|
||||
# # :markercolor,
|
||||
# # :markersize,
|
||||
# # :markeralpha,
|
||||
# # :markerstrokewidth,
|
||||
# # :markerstrokecolor,
|
||||
# # :markerstrokestyle,
|
||||
# # :n,
|
||||
# # :bins,
|
||||
# # :nc,
|
||||
# # :nr,
|
||||
# # :pos,
|
||||
# # :smooth,
|
||||
# # :show,
|
||||
# # :size,
|
||||
# # :title,
|
||||
# # :windowtitle,
|
||||
# # :x,
|
||||
# # :xlabel,
|
||||
# # :xlims,
|
||||
# # :xticks,
|
||||
# # :y,
|
||||
# # :ylabel,
|
||||
# # :ylims,
|
||||
# # :yrightlabel,
|
||||
# # :yticks,
|
||||
# # :xscale,
|
||||
# # :yscale,
|
||||
# # :xflip,
|
||||
# # :yflip,
|
||||
# # :z,
|
||||
# # :tickfont,
|
||||
# # :guidefont,
|
||||
# # :legendfont,
|
||||
# # :grid,
|
||||
# # :surface
|
||||
# # :levels,
|
||||
# ]
|
||||
# supportedAxes(::GLVisualizeBackend) = [:auto, :left]
|
||||
# supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
|
||||
# supportedStyles(::GLVisualizeBackend) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
|
||||
# supportedMarkers(::GLVisualizeBackend) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
|
||||
# supportedScales(::GLVisualizeBackend) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
|
||||
# subplotSupported(::GLVisualizeBackend) = false
|
||||
#
|
||||
# # --------------------------------------------------------------------------------------
|
||||
#
|
||||
# supportedArgs(::PGFPlotsBackend) = [
|
||||
# # :annotation,
|
||||
# # :axis,
|
||||
# :background_color,
|
||||
# # :color_palette,
|
||||
# # :fillrange,
|
||||
# :fillcolor,
|
||||
# :fillalpha,
|
||||
# # :foreground_color,
|
||||
# # :group,
|
||||
# # :label,
|
||||
# # :layout,
|
||||
# # :legend,
|
||||
# :seriescolor, :seriesalpha,
|
||||
# :linecolor,
|
||||
# :linestyle,
|
||||
# :linetype,
|
||||
# :linewidth,
|
||||
# :linealpha,
|
||||
# :markershape,
|
||||
# :markercolor,
|
||||
# :markersize,
|
||||
# :markeralpha,
|
||||
# # :markerstrokewidth,
|
||||
# :markerstrokecolor,
|
||||
# :markerstrokestyle,
|
||||
# # :n,
|
||||
# # :bins,
|
||||
# # :nc,
|
||||
# # :nr,
|
||||
# # :pos,
|
||||
# # :smooth,
|
||||
# # :show,
|
||||
# # :size,
|
||||
# :title,
|
||||
# # :windowtitle,
|
||||
# :x,
|
||||
# :xlabel,
|
||||
# :xlims,
|
||||
# # :xticks,
|
||||
# :y,
|
||||
# :ylabel,
|
||||
# :ylims,
|
||||
# # :yrightlabel,
|
||||
# # :yticks,
|
||||
# :xscale,
|
||||
# :yscale,
|
||||
# :xflip,
|
||||
# :yflip,
|
||||
# :z,
|
||||
# :zscale,
|
||||
# # :tickfont,
|
||||
# # :guidefont,
|
||||
# # :legendfont,
|
||||
# :grid,
|
||||
# # :surface
|
||||
# # :levels,
|
||||
# ]
|
||||
# supportedAxes(::PGFPlotsBackend) = [:auto, :left]
|
||||
# supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :line, :steppre, :stepmid, :steppost, :hist, :bar, :hist2d, :sticks, :ysticks, :xsticks, :contour] # :hexbin, :hline, :vline,]
|
||||
# supportedStyles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
# supportedMarkers(::PGFPlotsBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
|
||||
# supportedScales(::PGFPlotsBackend) = [:identity, :log, :ln, :log2, :log10] # :asinh, :sqrt]
|
||||
# subplotSupported(::PGFPlotsBackend) = false
|
||||
+57
-69
@@ -1,87 +1,75 @@
|
||||
|
||||
# TODO: find/replace all [PkgName] with CamelCase, all [pkgname] with lowercase
|
||||
# TODO: find/replace all [PkgName] with CamelCase
|
||||
|
||||
# [WEBSITE]
|
||||
# [ADD BACKEND WEBSITE]
|
||||
|
||||
function _initialize_backend(::[PkgName]AbstractBackend; kw...)
|
||||
@eval begin
|
||||
import [PkgName]
|
||||
export [PkgName]
|
||||
# TODO: other initialization that needs to be eval-ed
|
||||
end
|
||||
# TODO: other initialization
|
||||
function _initialize_backend(::[PkgName]Backend; kw...)
|
||||
@eval begin
|
||||
import [PkgName]
|
||||
export [PkgName]
|
||||
# todo: other initialization that needs to be eval-ed
|
||||
end
|
||||
# todo: other initialization
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _create_plot(pkg::[PkgName]AbstractBackend, d::KW)
|
||||
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
|
||||
# TODO: initialize the plot... title, xlabel, bgcolor, etc
|
||||
Plot(nothing, pkg, 0, d, KW[])
|
||||
# Create the window/figure for this backend.
|
||||
function _create_backend_figure(plt::Plot{[PkgName]Backend})
|
||||
nothing
|
||||
end
|
||||
|
||||
# this is called early in the pipeline, use it to make the plot current or something
|
||||
function _prepare_plot_object(plt::Plot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
# Set up the subplot within the backend object.
|
||||
function _initialize_subplot(plt::Plot{[PkgName]Backend}, sp::Subplot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Add one series to the underlying backend object.
|
||||
function _series_added(plt::Plot{[PkgName]Backend}, series::Series)
|
||||
end
|
||||
|
||||
# When series data is added/changed, this callback can do dynamic updates to the backend object.
|
||||
# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
|
||||
function _series_updated(plt::Plot{[PkgName]Backend}, series::Series)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# called just before updating layout bounding boxes... in case you need to prep
|
||||
# for the calcs
|
||||
function _before_layout_calcs(plt::Plot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
# Set the (left, top, right, bottom) minimum padding around the plot area
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot{[PkgName]Backend})
|
||||
sp.minpad = (20mm, 5mm, 2mm, 10mm)
|
||||
end
|
||||
|
||||
|
||||
function _add_series(::[PkgName]AbstractBackend, plt::Plot, d::KW)
|
||||
# TODO: add one series to the underlying package
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _add_annotations{X,Y,V}(plt::Plot{[PkgName]AbstractBackend}, anns::AVec{@compat(Tuple{X,Y,V})})
|
||||
for ann in anns
|
||||
# TODO: add the annotation to the plot
|
||||
end
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
function _update_plot_object(plt::Plot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _before_update_plot(plt::Plot{[PkgName]AbstractBackend})
|
||||
# Write a png to io. You could define methods for:
|
||||
# "application/eps" => "eps",
|
||||
# "image/eps" => "eps",
|
||||
# "application/pdf" => "pdf",
|
||||
# "image/png" => "png",
|
||||
# "application/postscript" => "ps",
|
||||
# "image/svg+xml" => "svg"
|
||||
function _writemime(io::IO, ::MIME"image/png", plt::Plot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
# TODO: override this to update plot items (title, xlabel, etc) after creation
|
||||
function _update_plot(plt::Plot{[PkgName]AbstractBackend}, d::KW)
|
||||
end
|
||||
|
||||
function _update_plot_pos_size(plt::AbstractPlot{[PkgName]AbstractBackend}, d::KW)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# accessors for x/y data
|
||||
|
||||
# function getxy(plt::Plot{[PkgName]AbstractBackend}, i::Int)
|
||||
# # TODO: return a tuple of (x, y) vectors
|
||||
# end
|
||||
#
|
||||
# function setxy!{X,Y}(plt::Plot{[PkgName]AbstractBackend}, xy::Tuple{X,Y}, i::Integer)
|
||||
# # TODO: set the plot data from the (x,y) tuple
|
||||
# plt
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _create_subplot(subplt::Subplot{[PkgName]AbstractBackend}, isbefore::Bool)
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
end
|
||||
|
||||
function _expand_limits(lims, plt::Plot{[PkgName]AbstractBackend}, isx::Bool)
|
||||
# TODO: call expand limits for each plot data
|
||||
end
|
||||
|
||||
function _remove_axis(plt::Plot{[PkgName]AbstractBackend}, isx::Bool)
|
||||
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function Base.writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{[PkgName]AbstractBackend})
|
||||
# TODO: write a png to io
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{[PkgName]AbstractBackend})
|
||||
# TODO: display/show the plot
|
||||
end
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Subplot{[PkgName]AbstractBackend})
|
||||
# TODO: display/show the subplot
|
||||
# Display/show the plot (open a GUI window, or browser page, for example).
|
||||
function _display(plt::Plot{[PkgName]Backend})
|
||||
end
|
||||
|
||||
+182
-113
@@ -2,7 +2,7 @@
|
||||
# https://github.com/Evizero/UnicodePlots.jl
|
||||
|
||||
supportedArgs(::UnicodePlotsBackend) = [
|
||||
# :annotation,
|
||||
# :annotations,
|
||||
# :args,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
@@ -17,7 +17,7 @@ supportedArgs(::UnicodePlotsBackend) = [
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:seriestype,
|
||||
# :linewidth,
|
||||
:markershape,
|
||||
# :markercolor,
|
||||
@@ -35,13 +35,13 @@ supportedArgs(::UnicodePlotsBackend) = [
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:window_title,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xguide,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:yguide,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
@@ -52,14 +52,18 @@ supportedArgs(::UnicodePlotsBackend) = [
|
||||
# :z,
|
||||
]
|
||||
supportedAxes(::UnicodePlotsBackend) = [:auto, :left]
|
||||
supportedTypes(::UnicodePlotsBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
|
||||
supportedTypes(::UnicodePlotsBackend) = [
|
||||
:path, :steppre, :steppost, :scatter,
|
||||
:histogram2d, :hline, :vline
|
||||
]
|
||||
supportedStyles(::UnicodePlotsBackend) = [:auto, :solid]
|
||||
supportedMarkers(::UnicodePlotsBackend) = [:none, :auto, :ellipse]
|
||||
supportedScales(::UnicodePlotsBackend) = [:identity]
|
||||
subplotSupported(::UnicodePlotsBackend) = true
|
||||
|
||||
|
||||
|
||||
# don't warn on unsupported... there's just too many warnings!!
|
||||
warnOnUnsupportedArgs(pkg::UnicodePlotsBackend, d::KW) = nothing
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
@@ -72,93 +76,149 @@ end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# convert_size_from_pixels(sz) =
|
||||
|
||||
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
||||
function rebuildUnicodePlot!(plt::Plot)
|
||||
plt.o = []
|
||||
for sp in plt.subplots
|
||||
xaxis = sp[:xaxis]
|
||||
yaxis = sp[:yaxis]
|
||||
xlim = axis_limits(xaxis)
|
||||
ylim = axis_limits(yaxis)
|
||||
|
||||
# figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
|
||||
sargs = plt.seriesargs
|
||||
iargs = plt.plotargs
|
||||
# make vectors
|
||||
xlim = [xlim[1], xlim[2]]
|
||||
ylim = [ylim[1], ylim[2]]
|
||||
|
||||
# get the x/y limits
|
||||
if get(iargs, :xlims, :auto) == :auto
|
||||
xlim = [Inf, -Inf]
|
||||
for d in sargs
|
||||
_expand_limits(xlim, d[:x])
|
||||
# we set x/y to have a single point, since we need to create the plot with some data.
|
||||
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
|
||||
x = Float64[xlim[1]]
|
||||
y = Float64[ylim[1]]
|
||||
|
||||
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||
width, height = plt[:size]
|
||||
o = UnicodePlots.Plot(x, y;
|
||||
width = width,
|
||||
height = height,
|
||||
title = sp[:title],
|
||||
xlim = xlim,
|
||||
ylim = ylim
|
||||
)
|
||||
|
||||
# set the axis labels
|
||||
UnicodePlots.xlabel!(o, xaxis[:guide])
|
||||
UnicodePlots.ylabel!(o, yaxis[:guide])
|
||||
|
||||
# now use the ! functions to add to the plot
|
||||
for series in series_list(sp)
|
||||
addUnicodeSeries!(o, series.d, sp[:legend] != :none, xlim, ylim)
|
||||
end
|
||||
|
||||
# save the object
|
||||
push!(plt.o, o)
|
||||
end
|
||||
else
|
||||
xmin, xmax = iargs[:xlims]
|
||||
xlim = [xmin, xmax]
|
||||
end
|
||||
|
||||
if get(iargs, :ylims, :auto) == :auto
|
||||
ylim = [Inf, -Inf]
|
||||
for d in sargs
|
||||
_expand_limits(ylim, d[:y])
|
||||
end
|
||||
else
|
||||
ymin, ymax = iargs[:ylims]
|
||||
ylim = [ymin, ymax]
|
||||
end
|
||||
|
||||
# we set x/y to have a single point, since we need to create the plot with some data.
|
||||
# since this point is at the bottom left corner of the plot, it shouldn't actually be shown
|
||||
x = Float64[xlim[1]]
|
||||
y = Float64[ylim[1]]
|
||||
|
||||
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||
width, height = iargs[:size]
|
||||
o = UnicodePlots.Plot(x, y; width = width,
|
||||
height = height,
|
||||
title = iargs[:title],
|
||||
# labels = iargs[:legend],
|
||||
xlim = xlim,
|
||||
ylim = ylim)
|
||||
|
||||
# set the axis labels
|
||||
UnicodePlots.xlabel!(o, iargs[:xlabel])
|
||||
UnicodePlots.ylabel!(o, iargs[:ylabel])
|
||||
|
||||
# now use the ! functions to add to the plot
|
||||
for d in sargs
|
||||
addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
|
||||
end
|
||||
|
||||
# save the object
|
||||
plt.o = o
|
||||
end
|
||||
|
||||
# # do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
||||
# function rebuildUnicodePlot!(plt::Plot)
|
||||
#
|
||||
# # figure out the plotting area xlim = [xmin, xmax] and ylim = [ymin, ymax]
|
||||
# sargs = plt.seriesargs
|
||||
# iargs = plt.attr
|
||||
#
|
||||
# # get the x/y limits
|
||||
# if get(iargs, :xlims, :auto) == :auto
|
||||
# xlim = [Inf, -Inf]
|
||||
# for d in sargs
|
||||
# _expand_limits(xlim, d[:x])
|
||||
# end
|
||||
# else
|
||||
# xmin, xmax = iargs[:xlims]
|
||||
# xlim = [xmin, xmax]
|
||||
# end
|
||||
#
|
||||
# if get(iargs, :ylims, :auto) == :auto
|
||||
# ylim = [Inf, -Inf]
|
||||
# for d in sargs
|
||||
# _expand_limits(ylim, d[:y])
|
||||
# end
|
||||
# else
|
||||
# ymin, ymax = iargs[:ylims]
|
||||
# ylim = [ymin, ymax]
|
||||
# end
|
||||
#
|
||||
# # we set x/y to have a single point, since we need to create the plot with some data.
|
||||
# # since this point is at the bottom left corner of the plot, it shouldn't actually be shown
|
||||
# x = Float64[xlim[1]]
|
||||
# y = Float64[ylim[1]]
|
||||
#
|
||||
# # create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||
# width, height = iargs[:size]
|
||||
# o = UnicodePlots.Plot(x, y; width = width,
|
||||
# height = height,
|
||||
# title = iargs[:title],
|
||||
# # labels = iargs[:legend],
|
||||
# xlim = xlim,
|
||||
# ylim = ylim)
|
||||
#
|
||||
# # set the axis labels
|
||||
# UnicodePlots.xlabel!(o, iargs[:xguide])
|
||||
# UnicodePlots.ylabel!(o, iargs[:yguide])
|
||||
#
|
||||
# # now use the ! functions to add to the plot
|
||||
# for d in sargs
|
||||
# addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
|
||||
# end
|
||||
#
|
||||
# # save the object
|
||||
# plt.o = o
|
||||
# end
|
||||
|
||||
|
||||
# add a single series
|
||||
function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
|
||||
# get the function, or special handling for step/bar/hist
|
||||
lt = d[:linetype]
|
||||
# get the function, or special handling for step/bar/hist
|
||||
st = d[:seriestype]
|
||||
|
||||
# handle hline/vline separately
|
||||
if st in (:hline,:vline)
|
||||
for yi in d[:y]
|
||||
if st == :hline
|
||||
UnicodePlots.lineplot!(o, xlim, [yi,yi])
|
||||
else
|
||||
UnicodePlots.lineplot!(o, [yi,yi], ylim)
|
||||
end
|
||||
end
|
||||
return
|
||||
|
||||
# elseif st == :bar
|
||||
# UnicodePlots.barplot!(o, d[:x], d[:y])
|
||||
# return
|
||||
|
||||
# elseif st == :histogram
|
||||
# UnicodePlots.histogram!(o, d[:y], bins = d[:bins])
|
||||
# return
|
||||
|
||||
elseif st == :histogram2d
|
||||
UnicodePlots.densityplot!(o, d[:x], d[:y])
|
||||
return
|
||||
|
||||
# handle hline/vline separately
|
||||
if lt in (:hline,:vline)
|
||||
for yi in d[:y]
|
||||
if lt == :hline
|
||||
UnicodePlots.lineplot!(o, xlim, [yi,yi])
|
||||
else
|
||||
UnicodePlots.lineplot!(o, [yi,yi], ylim)
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
stepstyle = :post
|
||||
if lt == :path
|
||||
if st == :path
|
||||
func = UnicodePlots.lineplot!
|
||||
elseif lt == :scatter || d[:markershape] != :none
|
||||
elseif st == :scatter || d[:markershape] != :none
|
||||
func = UnicodePlots.scatterplot!
|
||||
elseif lt == :steppost
|
||||
elseif st == :steppost
|
||||
func = UnicodePlots.stairs!
|
||||
elseif lt == :steppre
|
||||
elseif st == :steppre
|
||||
func = UnicodePlots.stairs!
|
||||
stepstyle = :pre
|
||||
else
|
||||
error("Linestyle $lt not supported by UnicodePlots")
|
||||
error("Linestyle $st not supported by UnicodePlots")
|
||||
end
|
||||
|
||||
# get the series data and label
|
||||
@@ -173,56 +233,64 @@ function addUnicodeSeries!(o, d::KW, addlegend::Bool, xlim, ylim)
|
||||
end
|
||||
|
||||
|
||||
function handlePlotColors(::UnicodePlotsBackend, d::KW)
|
||||
# TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
|
||||
d[:color_palette] = [RGB(0,0,0)]
|
||||
end
|
||||
# function handlePlotColors(::UnicodePlotsBackend, d::KW)
|
||||
# # TODO: something special for unicodeplots, since it doesn't take kindly to people messing with its color palette
|
||||
# d[:color_palette] = [RGB(0,0,0)]
|
||||
# end
|
||||
|
||||
# -------------------------------
|
||||
|
||||
|
||||
function _create_plot(pkg::UnicodePlotsBackend, d::KW)
|
||||
plt = Plot(nothing, pkg, 0, d, KW[])
|
||||
# function _create_plot(pkg::UnicodePlotsBackend, d::KW)
|
||||
# plt = Plot(nothing, pkg, 0, d, KW[])
|
||||
|
||||
function _create_backend_figure(plt::Plot{UnicodePlotsBackend})
|
||||
# do we want to give a new default size?
|
||||
if !haskey(plt.plotargs, :size) || plt.plotargs[:size] == _plotDefaults[:size]
|
||||
plt.plotargs[:size] = (60,20)
|
||||
end
|
||||
# if !haskey(plt.attr, :size) || plt.attr[:size] == default(:size)
|
||||
# plt.attr[:size] = (60,20)
|
||||
# end
|
||||
w, h = plt[:size]
|
||||
plt.attr[:size] = div(w, 10), div(h, 20)
|
||||
plt.attr[:color_palette] = [RGB(0,0,0)]
|
||||
nothing
|
||||
|
||||
plt
|
||||
# plt
|
||||
end
|
||||
|
||||
function _add_series(::UnicodePlotsBackend, plt::Plot, d::KW)
|
||||
if d[:linetype] in (:sticks, :bar)
|
||||
d = barHack(; d...)
|
||||
elseif d[:linetype] == :hist
|
||||
d = barHack(; histogramHack(; d...)...)
|
||||
end
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
function _update_plot(plt::Plot{UnicodePlotsBackend}, d::KW)
|
||||
for k in (:title, :xlabel, :ylabel, :xlims, :ylims)
|
||||
if haskey(d, k)
|
||||
plt.plotargs[k] = d[k]
|
||||
end
|
||||
end
|
||||
end
|
||||
# function _series_added(plt::Plot{UnicodePlotsBackend}, series::Series)
|
||||
# d = series.d
|
||||
# # TODO don't need these once the "bar" series recipe is done
|
||||
# if d[:seriestype] in (:sticks, :bar)
|
||||
# d = barHack(; d...)
|
||||
# elseif d[:seriestype] == :histogram
|
||||
# d = barHack(; histogramHack(; d...)...)
|
||||
# end
|
||||
# # push!(plt.seriesargs, d)
|
||||
# # plt
|
||||
# end
|
||||
#
|
||||
#
|
||||
# function _update_plot_object(plt::Plot{UnicodePlotsBackend}, d::KW)
|
||||
# for k in (:title, :xguide, :yguide, :xlims, :ylims)
|
||||
# if haskey(d, k)
|
||||
# plt.attr[k] = d[k]
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
# -------------------------------
|
||||
|
||||
# since this is such a hack, it's only callable using `png`... should error during normal `writemime`
|
||||
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::@compat(AbstractString))
|
||||
function png(plt::AbstractPlot{UnicodePlotsBackend}, fn::AbstractString)
|
||||
fn = addExtension(fn, "png")
|
||||
|
||||
# make some whitespace and show the plot
|
||||
println("\n\n\n\n\n\n")
|
||||
gui(plt)
|
||||
|
||||
@osx_only begin
|
||||
# @osx_only begin
|
||||
@compat @static if is_apple()
|
||||
# BEGIN HACK
|
||||
|
||||
# wait while the plot gets drawn
|
||||
@@ -243,21 +311,22 @@ end
|
||||
|
||||
# we don't do very much for subplots... just stack them vertically
|
||||
|
||||
function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
|
||||
isbefore && return false
|
||||
true
|
||||
end
|
||||
# function _create_subplot(subplt::Subplot{UnicodePlotsBackend}, isbefore::Bool)
|
||||
# isbefore && return false
|
||||
# true
|
||||
# end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, plt::Plot{UnicodePlotsBackend})
|
||||
function _display(plt::Plot{UnicodePlotsBackend})
|
||||
rebuildUnicodePlot!(plt)
|
||||
show(plt.o)
|
||||
map(show, plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
|
||||
for plt in subplt.plts
|
||||
gui(plt)
|
||||
end
|
||||
end
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{UnicodePlotsBackend})
|
||||
# for plt in subplt.plts
|
||||
# gui(plt)
|
||||
# end
|
||||
# end
|
||||
|
||||
+12
-6
@@ -4,7 +4,7 @@
|
||||
# CREDIT: parts of this implementation were inspired by @joshday's PlotlyLocal.jl
|
||||
|
||||
|
||||
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.plotargs, :window_title, "Plots.jl"))
|
||||
function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.attr, :window_title, "Plots.jl"))
|
||||
"""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
@@ -20,14 +20,20 @@ function standalone_html(plt::AbstractPlot; title::AbstractString = get(plt.plot
|
||||
end
|
||||
|
||||
function open_browser_window(filename::AbstractString)
|
||||
@osx_only return run(`open $(filename)`)
|
||||
@linux_only return run(`xdg-open $(filename)`)
|
||||
@windows_only return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
|
||||
@compat @static if is_apple()
|
||||
return run(`open $(filename)`)
|
||||
end
|
||||
@compat @static if is_linux()
|
||||
return run(`xdg-open $(filename)`)
|
||||
end
|
||||
@compat @static if is_windows()
|
||||
return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
|
||||
end
|
||||
warn("Unknown OS... cannot open browser window.")
|
||||
end
|
||||
|
||||
function write_temp_html(plt::AbstractPlot)
|
||||
html = standalone_html(plt; title = plt.plotargs[:title])
|
||||
html = standalone_html(plt; title = plt.attr[:window_title])
|
||||
filename = string(tempname(), ".html")
|
||||
output = open(filename, "w")
|
||||
write(output, html)
|
||||
@@ -51,7 +57,7 @@ function writemime_png_from_html(io::IO, plt::AbstractPlot)
|
||||
|
||||
# convert that html file to a temporary png file using wkhtmltoimage
|
||||
png_fn = tempname() * ".png"
|
||||
w, h = plt.plotargs[:size]
|
||||
w, h = plt.attr[:size]
|
||||
html_to_png(html_fn, png_fn, w, h)
|
||||
|
||||
# now read that file data into io
|
||||
|
||||
+43
-41
@@ -4,7 +4,7 @@
|
||||
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
|
||||
|
||||
supportedArgs(::WinstonBackend) = [
|
||||
:annotation,
|
||||
:annotations,
|
||||
# :args,
|
||||
# :axis,
|
||||
# :background_color,
|
||||
@@ -21,7 +21,7 @@ supportedArgs(::WinstonBackend) = [
|
||||
:legend,
|
||||
:seriescolor, :seriesalpha,
|
||||
:linestyle,
|
||||
:linetype,
|
||||
:seriestype,
|
||||
:linewidth,
|
||||
:markershape,
|
||||
:markercolor,
|
||||
@@ -39,13 +39,13 @@ supportedArgs(::WinstonBackend) = [
|
||||
:show,
|
||||
:size,
|
||||
:title,
|
||||
:windowtitle,
|
||||
:window_title,
|
||||
:x,
|
||||
:xlabel,
|
||||
:xguide,
|
||||
:xlims,
|
||||
# :xticks,
|
||||
:y,
|
||||
:ylabel,
|
||||
:yguide,
|
||||
:ylims,
|
||||
# :yrightlabel,
|
||||
# :yticks,
|
||||
@@ -56,7 +56,7 @@ supportedArgs(::WinstonBackend) = [
|
||||
# :z,
|
||||
]
|
||||
supportedAxes(::WinstonBackend) = [:auto, :left]
|
||||
supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :hist, :bar]
|
||||
supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :histogram, :bar]
|
||||
supportedStyles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
supportedMarkers(::WinstonBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
|
||||
supportedScales(::WinstonBackend) = [:identity, :log10]
|
||||
@@ -96,17 +96,18 @@ end
|
||||
:star5 => "asterisk"
|
||||
)
|
||||
|
||||
function _before_add_series(plt::Plot{WinstonBackend})
|
||||
function _before_update(plt::Plot{WinstonBackend})
|
||||
Winston.ghf(plt.o)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _create_plot(pkg::WinstonBackend, d::KW)
|
||||
wplt = Winston.FramedPlot(title = d[:title], xlabel = d[:xlabel], ylabel = d[:ylabel])
|
||||
|
||||
Plot(wplt, pkg, 0, d, KW[])
|
||||
function _create_backend_figure(plt::Plot{WinstonBackend})
|
||||
Winston.FramedPlot(
|
||||
title = plt.attr[:title],
|
||||
xlabel = plt.attr[:xguide],
|
||||
ylabel = plt.attr[:yguide]
|
||||
)
|
||||
end
|
||||
|
||||
copy_remove(d::KW, s::Symbol) = delete!(copy(d), s)
|
||||
@@ -126,11 +127,12 @@ function getWinstonItems(plt::Plot)
|
||||
window, canvas, wplt
|
||||
end
|
||||
|
||||
function _add_series(::WinstonBackend, plt::Plot, d::KW)
|
||||
function _series_added(plt::Plot{WinstonBackend}, series::Series)
|
||||
d = series.d
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
|
||||
# until we call it normally, do the hack
|
||||
if d[:linetype] == :bar
|
||||
if d[:seriestype] == :bar
|
||||
d = barHack(;d...)
|
||||
end
|
||||
|
||||
@@ -148,11 +150,11 @@ function _add_series(::WinstonBackend, plt::Plot, d::KW)
|
||||
|
||||
|
||||
|
||||
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :hist2d, :hexbin, :hist, :bar
|
||||
if d[:linetype] == :none
|
||||
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :histogram2d, :hexbin, :histogram, :bar
|
||||
if d[:seriestype] == :none
|
||||
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
|
||||
|
||||
elseif d[:linetype] == :path
|
||||
elseif d[:seriestype] == :path
|
||||
x, y = d[:x], d[:y]
|
||||
Winston.add(wplt, Winston.Curve(x, y; e...))
|
||||
|
||||
@@ -166,38 +168,38 @@ function _add_series(::WinstonBackend, plt::Plot, d::KW)
|
||||
Winston.add(wplt, Winston.FillBetween(x, y, x, y2, fillcolor=getColor(d[:fillcolor])))
|
||||
end
|
||||
|
||||
elseif d[:linetype] == :scatter
|
||||
elseif d[:seriestype] == :scatter
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
|
||||
# elseif d[:linetype] == :step
|
||||
# elseif d[:seriestype] == :step
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:linetype] == :stepinverted
|
||||
# elseif d[:seriestype] == :stepinverted
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:linetype] == :sticks
|
||||
elseif d[:seriestype] == :sticks
|
||||
Winston.add(wplt, Winston.Stems(d[:x], d[:y]; e...))
|
||||
|
||||
# elseif d[:linetype] == :dots
|
||||
# elseif d[:seriestype] == :dots
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:linetype] == :hist2d
|
||||
# elseif d[:seriestype] == :histogram2d
|
||||
# fn = Winston.XXX
|
||||
|
||||
# elseif d[:linetype] == :hexbin
|
||||
# elseif d[:seriestype] == :hexbin
|
||||
# fn = Winston.XXX
|
||||
|
||||
elseif d[:linetype] == :hist
|
||||
elseif d[:seriestype] == :histogram
|
||||
hst = hist(d[:y], d[:bins])
|
||||
Winston.add(wplt, Winston.Histogram(hst...; copy_remove(e, :bins)...))
|
||||
|
||||
# elseif d[:linetype] == :bar
|
||||
# elseif d[:seriestype] == :bar
|
||||
# # fn = Winston.XXX
|
||||
|
||||
else
|
||||
error("linetype $(d[:linetype]) not supported by Winston.")
|
||||
error("seriestype $(d[:seriestype]) not supported by Winston.")
|
||||
|
||||
end
|
||||
|
||||
@@ -209,10 +211,10 @@ function _add_series(::WinstonBackend, plt::Plot, d::KW)
|
||||
|
||||
|
||||
# optionally add a regression line
|
||||
d[:smooth] && d[:linetype] != :hist && addRegressionLineWinston(d, wplt)
|
||||
d[:smooth] && d[:seriestype] != :histogram && addRegressionLineWinston(d, wplt)
|
||||
|
||||
push!(plt.seriesargs, d)
|
||||
plt
|
||||
# push!(plt.seriesargs, d)
|
||||
# plt
|
||||
end
|
||||
|
||||
|
||||
@@ -225,9 +227,9 @@ end
|
||||
:yscale => :ylog,
|
||||
)
|
||||
|
||||
function _update_plot(plt::Plot{WinstonBackend}, d::KW)
|
||||
function _update_plot_object(plt::Plot{WinstonBackend}, d::KW)
|
||||
window, canvas, wplt = getWinstonItems(plt)
|
||||
for k in (:xlabel, :ylabel, :title, :xlims, :ylims)
|
||||
for k in (:xguide, :yguide, :title, :xlims, :ylims)
|
||||
if haskey(d, k)
|
||||
Winston.setattr(wplt, string(get(_winstonNames, k, k)), d[k])
|
||||
end
|
||||
@@ -259,14 +261,14 @@ end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
|
||||
# TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
end
|
||||
# function _create_subplot(subplt::Subplot{WinstonBackend}, isbefore::Bool)
|
||||
# # TODO: build the underlying Subplot object. this is where you might layout the panes within a GUI window, for example
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function addWinstonLegend(plt::Plot, wplt)
|
||||
if plt.plotargs[:legend] != :none
|
||||
if plt.attr[:legend] != :none
|
||||
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
|
||||
end
|
||||
end
|
||||
@@ -287,9 +289,9 @@ function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
|
||||
error("Gtk is the only supported display for Winston in Plots. Set `output_surface = gtk` in src/Winston.ini")
|
||||
end
|
||||
# initialize window
|
||||
w,h = plt.plotargs[:size]
|
||||
w,h = plt.attr[:size]
|
||||
canvas = Gtk.GtkCanvasLeaf()
|
||||
window = Gtk.GtkWindowLeaf(canvas, plt.plotargs[:windowtitle], w, h)
|
||||
window = Gtk.GtkWindowLeaf(canvas, plt.attr[:window_title], w, h)
|
||||
plt.o = (window, canvas, wplt)
|
||||
end
|
||||
|
||||
@@ -300,6 +302,6 @@ function Base.display(::PlotsDisplay, plt::Plot{WinstonBackend})
|
||||
end
|
||||
|
||||
|
||||
function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
|
||||
# TODO: display/show the Subplot object
|
||||
end
|
||||
# function Base.display(::PlotsDisplay, subplt::Subplot{WinstonBackend})
|
||||
# # TODO: display/show the Subplot object
|
||||
# end
|
||||
|
||||
+9
-58
@@ -18,7 +18,8 @@ colorscheme(c::Colorant; kw...) = ColorWrapper(c; kw...)
|
||||
# --------------------------------------------------------------
|
||||
|
||||
|
||||
convertColor(c::@compat(Union{AbstractString, Symbol})) = parse(Colorant, string(c))
|
||||
convertColor(c::AbstractString) = parse(Colorant, c)
|
||||
convertColor(c::Symbol) = parse(Colorant, string(c))
|
||||
convertColor(c::Colorant) = c
|
||||
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
|
||||
convertColor(c::ColorScheme) = c
|
||||
@@ -294,7 +295,7 @@ function generate_colorgradient(bgcolor = colorant"white";
|
||||
gradient_from_list(colors)
|
||||
end
|
||||
|
||||
function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
|
||||
function get_color_palette(palette, bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
|
||||
grad = if palette == :auto
|
||||
generate_colorgradient(bgcolor)
|
||||
else
|
||||
@@ -304,7 +305,8 @@ function get_color_palette(palette, bgcolor::@compat(Union{Colorant,ColorWrapper
|
||||
RGBA[getColorZ(grad, z) for z in zrng]
|
||||
end
|
||||
|
||||
function get_color_palette(palette::Vector{RGBA}, bgcolor::@compat(Union{Colorant,ColorWrapper}), numcolors::Integer)
|
||||
function get_color_palette{C<:Colorant}(palette::Vector{C},
|
||||
bgcolor::Union{Colorant,ColorWrapper}, numcolors::Integer)
|
||||
palette
|
||||
end
|
||||
|
||||
@@ -347,10 +349,10 @@ end
|
||||
make255(x) = round(Int, 255 * x)
|
||||
|
||||
function webcolor(c::Color)
|
||||
@sprintf("rgb(%d, %d, %d)", [make255(f(c)) for f in [red,green,blue]]...)
|
||||
@sprintf("rgb(%d, %d, %d)", [make255(f(c)) for f in [red,green,blue]]...)
|
||||
end
|
||||
function webcolor(c::TransparentColor)
|
||||
@sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
|
||||
@sprintf("rgba(%d, %d, %d, %1.3f)", [make255(f(c)) for f in [red,green,blue]]..., alpha(c))
|
||||
end
|
||||
webcolor(cs::ColorScheme) = webcolor(getColor(cs))
|
||||
webcolor(c) = webcolor(convertColor(c))
|
||||
@@ -358,63 +360,12 @@ webcolor(c, α) = webcolor(convertColor(getColor(c), α))
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
# TODO: allow the setting of the algorithm, either by passing a symbol (:colordiff, :fixed, etc) or a function?
|
||||
|
||||
function handlePlotColors(::AbstractBackend, d::KW)
|
||||
if :background_color in supportedArgs()
|
||||
bgcolor = convertColor(d[:background_color])
|
||||
else
|
||||
bgcolor = _plotDefaults[:background_color]
|
||||
if d[:background_color] != _plotDefaults[:background_color]
|
||||
warn("Cannot set background_color with backend $(backend())")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
d[:color_palette] = get_color_palette(get(d, :color_palette, :auto), bgcolor, 100)
|
||||
|
||||
|
||||
# set the foreground color (text, ticks, gridlines) to be white or black depending
|
||||
# on how dark the background is.
|
||||
fgcolor = get(d, :foreground_color, :auto)
|
||||
fgcolor = if fgcolor == :auto
|
||||
isdark(bgcolor) ? colorant"white" : colorant"black"
|
||||
else
|
||||
convertColor(fgcolor)
|
||||
end
|
||||
|
||||
# bg/fg color
|
||||
d[:background_color] = colorscheme(bgcolor)
|
||||
d[:foreground_color] = colorscheme(fgcolor)
|
||||
|
||||
# update sub-background colors
|
||||
for bgtype in ("legend", "inside", "outside")
|
||||
bgsym = symbol("background_color_" * bgtype)
|
||||
if d[bgsym] == :match
|
||||
d[bgsym] = d[:background_color]
|
||||
elseif d[bgsym] == nothing
|
||||
d[bgsym] = colorscheme(RGBA(0,0,0,0))
|
||||
end
|
||||
end
|
||||
|
||||
# update sub-foreground colors
|
||||
for fgtype in ("legend", "grid", "axis", "text", "border", "guide")
|
||||
fgsym = symbol("foreground_color_" * fgtype)
|
||||
if d[fgsym] == :match
|
||||
d[fgsym] = d[:foreground_color]
|
||||
elseif d[fgsym] == nothing
|
||||
d[fgsym] = colorscheme(RGBA(0,0,0,0))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
||||
function getSeriesRGBColor(c, plotargs::KW, n::Int)
|
||||
function getSeriesRGBColor(c, sp::Subplot, n::Int)
|
||||
|
||||
if c == :auto
|
||||
c = autopick(plotargs[:color_palette], n)
|
||||
c = autopick(sp[:color_palette], n)
|
||||
end
|
||||
|
||||
# c should now be a subtype of ColorScheme
|
||||
|
||||
+138
-90
@@ -12,23 +12,17 @@ compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : ang
|
||||
# -------------------------------------------------------------
|
||||
|
||||
immutable Shape
|
||||
# vertices::AVec
|
||||
x::AVec
|
||||
y::AVec
|
||||
end
|
||||
|
||||
# Shape(x, y) = Shape(collect(zip(x, y)))
|
||||
Shape(verts::AVec) = Shape(unzip(verts)...)
|
||||
|
||||
# get_xs(shape::Shape) = Float64[v[1] for v in shape.vertices]
|
||||
# get_ys(shape::Shape) = Float64[v[2] for v in shape.vertices]
|
||||
get_xs(shape::Shape) = shape.x
|
||||
get_ys(shape::Shape) = shape.y
|
||||
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
|
||||
|
||||
|
||||
function shape_coords(shape::Shape)
|
||||
# unzip(shape.vertices)
|
||||
shape.x, shape.y
|
||||
end
|
||||
|
||||
@@ -36,14 +30,10 @@ function shape_coords(shapes::AVec{Shape})
|
||||
length(shapes) == 0 && return zeros(0), zeros(0)
|
||||
xs = map(get_xs, shapes)
|
||||
ys = map(get_ys, shapes)
|
||||
# x, y = shapes[1].x, shapes[1].y #unzip(shapes[1].vertices)
|
||||
x, y = map(copy, shape_coords(shapes[1]))
|
||||
for shape in shapes[2:end]
|
||||
# tmpx, tmpy = unzip(shape.vertices)
|
||||
nanappend!(x, shape.x)
|
||||
nanappend!(y, shape.y)
|
||||
# x = vcat(x, NaN, tmpx)
|
||||
# y = vcat(y, NaN, tmpy)
|
||||
end
|
||||
x, y
|
||||
end
|
||||
@@ -62,9 +52,6 @@ function weave(x,y; ordering = Vector[x,y])
|
||||
try
|
||||
push!(ret, shift!(o))
|
||||
end
|
||||
# try
|
||||
# push!(ret, shift!(y))
|
||||
# end
|
||||
end
|
||||
done = isempty(x) && isempty(y)
|
||||
end
|
||||
@@ -123,12 +110,30 @@ const _shapes = KW(
|
||||
)
|
||||
|
||||
for n in [4,5,6,7,8]
|
||||
_shapes[symbol("star$n")] = makestar(n)
|
||||
_shapes[Symbol("star$n")] = makestar(n)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
center(shape::Shape) = (mean(shape.x), mean(shape.y))
|
||||
|
||||
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
|
||||
function center(shape::Shape)
|
||||
x, y = shape_coords(shape)
|
||||
n = length(x)
|
||||
A, Cx, Cy = 0.0, 0.0, 0.0
|
||||
for i=1:n
|
||||
ip1 = i==n ? 1 : i+1
|
||||
A += x[i] * y[ip1] - x[ip1] * y[i]
|
||||
end
|
||||
A *= 0.5
|
||||
for i=1:n
|
||||
ip1 = i==n ? 1 : i+1
|
||||
m = (x[i] * y[ip1] - x[ip1] * y[i])
|
||||
Cx += (x[i] + x[ip1]) * m
|
||||
Cy += (y[i] + y[ip1]) * m
|
||||
end
|
||||
Cx / 6A, Cy / 6A
|
||||
end
|
||||
|
||||
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = shape_coords(shape)
|
||||
@@ -222,7 +227,7 @@ function font(args...)
|
||||
valign = arg
|
||||
elseif T <: Colorant
|
||||
color = arg
|
||||
elseif T <: @compat Union{Symbol,AbstractString}
|
||||
elseif T <: Symbol || T <: AbstractString
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
catch
|
||||
@@ -242,15 +247,24 @@ end
|
||||
|
||||
"Wrap a string with font info"
|
||||
immutable PlotText
|
||||
str::@compat(AbstractString)
|
||||
str::AbstractString
|
||||
font::Font
|
||||
end
|
||||
PlotText(str) = PlotText(string(str), font())
|
||||
|
||||
text(t::PlotText) = t
|
||||
function text(str, args...)
|
||||
PlotText(string(str), font(args...))
|
||||
end
|
||||
|
||||
|
||||
annotations(::Void) = []
|
||||
annotations(anns::AVec) = anns
|
||||
annotations(anns) = Any[anns]
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
immutable Stroke
|
||||
@@ -261,10 +275,6 @@ immutable Stroke
|
||||
end
|
||||
|
||||
function stroke(args...; alpha = nothing)
|
||||
# defaults
|
||||
# width = 1
|
||||
# color = colorant"black"
|
||||
# style = :solid
|
||||
width = nothing
|
||||
color = nothing
|
||||
style = nothing
|
||||
@@ -277,14 +287,12 @@ function stroke(args...; alpha = nothing)
|
||||
style = arg
|
||||
elseif T <: Colorant
|
||||
color = arg
|
||||
elseif T <: @compat Union{Symbol,AbstractString}
|
||||
elseif T <: Symbol || T <: AbstractString
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
end
|
||||
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
|
||||
elseif allAlphas(arg)
|
||||
alpha = arg
|
||||
# elseif typeof(arg) <: Real
|
||||
elseif allReals(arg)
|
||||
width = arg
|
||||
else
|
||||
@@ -303,9 +311,6 @@ immutable Brush
|
||||
end
|
||||
|
||||
function brush(args...; alpha = nothing)
|
||||
# defaults
|
||||
# sz = 1
|
||||
# color = colorant"black"
|
||||
size = nothing
|
||||
color = nothing
|
||||
|
||||
@@ -314,14 +319,12 @@ function brush(args...; alpha = nothing)
|
||||
|
||||
if T <: Colorant
|
||||
color = arg
|
||||
elseif T <: @compat Union{Symbol,AbstractString}
|
||||
elseif T <: Symbol || T <: AbstractString
|
||||
try
|
||||
color = parse(Colorant, string(arg))
|
||||
end
|
||||
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
|
||||
elseif allAlphas(arg)
|
||||
alpha = arg
|
||||
# elseif typeof(arg) <: Real
|
||||
elseif allReals(arg)
|
||||
size = arg
|
||||
else
|
||||
@@ -350,12 +353,10 @@ abstract AbstractSurface
|
||||
|
||||
"represents a contour or surface mesh"
|
||||
immutable Surface{M<:AMat} <: AbstractSurface
|
||||
# x::AVec
|
||||
# y::AVec
|
||||
surf::M
|
||||
end
|
||||
|
||||
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for xi in x, yi in y])
|
||||
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for yi in y, xi in x])
|
||||
|
||||
Base.Array(surf::Surface) = surf.surf
|
||||
|
||||
@@ -363,7 +364,15 @@ for f in (:length, :size)
|
||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||
end
|
||||
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
||||
Base.eltype(surf::Surface) = eltype(surf.surf)
|
||||
|
||||
function expand_extrema!(a::Axis, surf::Surface)
|
||||
ex = a[:extrema]
|
||||
for vi in surf.surf
|
||||
expand_extrema!(ex, vi)
|
||||
end
|
||||
ex
|
||||
end
|
||||
|
||||
"For the case of representing a surface as a function of x/y... can possibly avoid allocations."
|
||||
immutable SurfaceFunction <: AbstractSurface
|
||||
@@ -372,67 +381,106 @@ end
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
type OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
# style is :open or :closed (for now)
|
||||
immutable Arrow
|
||||
style::Symbol
|
||||
headlength::Float64
|
||||
headwidth::Float64
|
||||
end
|
||||
|
||||
function arrow(args...)
|
||||
style = :simple
|
||||
headlength = 0.3
|
||||
headwidth = 0.3
|
||||
setlength = false
|
||||
for arg in args
|
||||
T = typeof(arg)
|
||||
if T == Symbol
|
||||
style = arg
|
||||
elseif T <: Number
|
||||
# first we apply to both, but if there's more, then only change width after the first number
|
||||
headwidth = Float64(arg)
|
||||
if !setlength
|
||||
headlength = headwidth
|
||||
end
|
||||
setlength = true
|
||||
elseif T <: Tuple && length(arg) == 2
|
||||
headlength, headwidth = Float64(arg[1]), Float64(arg[2])
|
||||
else
|
||||
warn("Skipped arrow arg $arg")
|
||||
end
|
||||
end
|
||||
Arrow(style, headlength, headwidth)
|
||||
end
|
||||
|
||||
|
||||
# @require FixedSizeArrays begin
|
||||
|
||||
type BezierCurve{T <: FixedSizeArrays.Vec}
|
||||
control_points::Vector{T}
|
||||
end
|
||||
|
||||
function Base.call(bc::BezierCurve, t::Real)
|
||||
p = zero(P2)
|
||||
n = length(bc.control_points)-1
|
||||
for i in 0:n
|
||||
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
|
||||
end
|
||||
p
|
||||
end
|
||||
|
||||
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
||||
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
||||
|
||||
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||
|
||||
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
|
||||
# may create a loop if necessary. Assumes the view is [0,1]
|
||||
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
|
||||
mn = mean(p, q)
|
||||
diff = q - p
|
||||
|
||||
minx, maxx = minimum(xview), maximum(xview)
|
||||
miny, maxy = minimum(yview), maximum(yview)
|
||||
diffpct = P2(diff[1] / (maxx - minx),
|
||||
diff[2] / (maxy - miny))
|
||||
|
||||
# these points give the initial/final "rise"
|
||||
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
|
||||
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
|
||||
upper_control = p + vertical_offset
|
||||
lower_control = q - vertical_offset
|
||||
|
||||
# try to figure out when to loop around vs just connecting straight
|
||||
# TODO: choose loop direction based on sign of p[1]??
|
||||
# x_close_together = abs(diffpct[1]) <= 0.05
|
||||
p_is_higher = diff[2] <= 0
|
||||
inside_control_points = if p_is_higher
|
||||
# add curve points which will create a loop
|
||||
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
|
||||
inside_offset = P2(0.3 * (maxx - minx), 0)
|
||||
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
|
||||
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
|
||||
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
|
||||
else
|
||||
[]
|
||||
# allow for do-block notation which gets called on every valid start/end pair which
|
||||
# we need to draw an arrow
|
||||
function add_arrows(func::Function, x::AVec, y::AVec)
|
||||
for i=2:length(x)
|
||||
xyprev = (x[i-1], y[i-1])
|
||||
xy = (x[i], y[i])
|
||||
if ok(xyprev) && ok(xy)
|
||||
if i==length(x) || !ok(x[i+1], y[i+1])
|
||||
# add the arrow from xyprev to xy
|
||||
func(xyprev, xy)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
type BezierCurve{T <: FixedSizeArrays.Vec}
|
||||
control_points::Vector{T}
|
||||
end
|
||||
|
||||
@compat function (bc::BezierCurve)(t::Real)
|
||||
p = zero(P2)
|
||||
n = length(bc.control_points)-1
|
||||
for i in 0:n
|
||||
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
|
||||
end
|
||||
p
|
||||
end
|
||||
|
||||
# end
|
||||
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
||||
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
||||
|
||||
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||
|
||||
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
|
||||
# may create a loop if necessary. Assumes the view is [0,1]
|
||||
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
|
||||
mn = mean(p, q)
|
||||
diff = q - p
|
||||
|
||||
minx, maxx = minimum(xview), maximum(xview)
|
||||
miny, maxy = minimum(yview), maximum(yview)
|
||||
diffpct = P2(diff[1] / (maxx - minx),
|
||||
diff[2] / (maxy - miny))
|
||||
|
||||
# these points give the initial/final "rise"
|
||||
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
|
||||
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
|
||||
upper_control = p + vertical_offset
|
||||
lower_control = q - vertical_offset
|
||||
|
||||
# try to figure out when to loop around vs just connecting straight
|
||||
# TODO: choose loop direction based on sign of p[1]??
|
||||
# x_close_together = abs(diffpct[1]) <= 0.05
|
||||
p_is_higher = diff[2] <= 0
|
||||
inside_control_points = if p_is_higher
|
||||
# add curve points which will create a loop
|
||||
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
|
||||
inside_offset = P2(0.3 * (maxx - minx), 0)
|
||||
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
|
||||
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
|
||||
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
|
||||
else
|
||||
[]
|
||||
end
|
||||
|
||||
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
|
||||
end
|
||||
|
||||
+342
@@ -0,0 +1,342 @@
|
||||
"""
|
||||
Holds all data needed for a documentation example... header, description, and plotting expression (Expr)
|
||||
"""
|
||||
type PlotExample
|
||||
header::AbstractString
|
||||
desc::AbstractString
|
||||
exprs::Vector{Expr}
|
||||
end
|
||||
|
||||
# the _examples we'll run for each
|
||||
const _examples = PlotExample[
|
||||
|
||||
PlotExample("Lines",
|
||||
"A simple line plot of the columns.",
|
||||
[:(begin
|
||||
plot(Plots.fakedata(50,5), w=3)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Functions, adding data, and animations",
|
||||
"Plot multiple functions. You can also put the function first, or use the form `plot(f, xmin, xmax)` where f is a Function or AbstractVector{Function}.\n\nGet series data: `x, y = plt[i]`. Set series data: `plt[i] = (x,y)`. Add to the series with `push!`/`append!`.\n\nEasily build animations. (`convert` or `ffmpeg` must be available to generate the animation.) Use command `gif(anim, filename, fps=15)` to save the animation.",
|
||||
[:(begin
|
||||
p = plot([sin,cos], zeros(0), leg=false)
|
||||
anim = Animation()
|
||||
for x in linspace(0, 10π, 100)
|
||||
push!(p, x, Float64[sin(x), cos(x)])
|
||||
frame(anim)
|
||||
end
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Parametric plots",
|
||||
"Plot function pair (x(u), y(u)).",
|
||||
[:(begin
|
||||
plot(sin, x->sin(2x), 0, 2π, line=4, leg=false, fill=(0,:orange))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Colors",
|
||||
"Access predefined palettes (or build your own with the `colorscheme` method). Line/marker colors are auto-generated from the plot's palette, unless overridden. Set the `z` argument to turn on series gradients.",
|
||||
[:(begin
|
||||
y = rand(100)
|
||||
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=(0,:auto), α=0.6)
|
||||
scatter!(y, zcolor=abs(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs(y-0.5)+4, lab="grad")
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Global",
|
||||
"Change the guides/background/limits/ticks. Convenience args `xaxis` and `yaxis` allow you to pass a tuple or value which will be mapped to the relevant args automatically. The `xaxis` below will be replaced with `xlabel` and `xlims` args automatically during the preprocessing step. You can also use shorthand functions: `title!`, `xaxis!`, `yaxis!`, `xlabel!`, `ylabel!`, `xlims!`, `ylims!`, `xticks!`, `yticks!`",
|
||||
[:(begin
|
||||
y = rand(20,3)
|
||||
plot(y, xaxis=("XLABEL",(-5,30),0:2:20,:flip), background_color = RGB(0.2,0.2,0.2), leg=false)
|
||||
hline!(mean(y,1)+rand(1,3), line=(4,:dash,0.6,[:lightgreen :green :darkgreen]))
|
||||
vline!([5,10])
|
||||
title!("TITLE")
|
||||
yaxis!("YLABEL", :log10)
|
||||
end)]
|
||||
),
|
||||
|
||||
# PlotExample("Two-axis",
|
||||
# "Use the `axis` arguments.",
|
||||
# [
|
||||
# :(plot(Vector[randn(100), randn(100)*100], axis = [:l :r], ylabel="LEFT", yrightlabel="RIGHT", xlabel="X", title="TITLE"))
|
||||
# ]),
|
||||
|
||||
PlotExample("Images",
|
||||
"Plot an image. y-axis is set to flipped",
|
||||
[:(begin
|
||||
import Images
|
||||
img = Images.load(Pkg.dir("PlotReferenceImages","Plots","pyplot","0.7.0","ref1.png"))
|
||||
plot(img)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Arguments",
|
||||
"Plot multiple series with different numbers of points. Mix arguments that apply to all series (marker/markersize) with arguments unique to each series (colors). Special arguments `line`, `marker`, and `fill` will automatically figure out what arguments to set (for example, we are setting the `linestyle`, `linewidth`, and `color` arguments with `line`.) Note that we pass a matrix of colors, and this applies the colors to each series.",
|
||||
[:(begin
|
||||
ys = Vector[rand(10), rand(20)]
|
||||
plot(ys, color=[:black :orange], line=(:dot,4), marker=([:hex :d],12,0.8,stroke(3,:gray)))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Build plot in pieces",
|
||||
"Start with a base plot...",
|
||||
[:(begin
|
||||
plot(rand(100)/3, reg=true, fill=(0,:green))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("",
|
||||
"and add to it later.",
|
||||
[:(begin
|
||||
scatter!(rand(100), markersize=6, c=:orange)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Histogram2D",
|
||||
"",
|
||||
[:(begin
|
||||
histogram2d(randn(10000), randn(10000), nbins=20)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Line types",
|
||||
"",
|
||||
[:(begin
|
||||
linetypes = [:path :steppre :steppost :sticks :scatter]
|
||||
n = length(linetypes)
|
||||
x = Vector[sort(rand(20)) for i in 1:n]
|
||||
y = rand(20,n)
|
||||
plot(x, y, line=(linetypes,3), lab=map(string,linetypes), ms=15)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Line styles",
|
||||
"",
|
||||
[:(begin
|
||||
styles = setdiff(supportedStyles(), [:auto])'
|
||||
plot(cumsum(randn(20,length(styles)),1), style=:auto, label=map(string,styles), w=5)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Marker types",
|
||||
"",
|
||||
[:(begin
|
||||
markers = setdiff(supportedMarkers(), [:none,:auto,Shape])'
|
||||
n = length(markers)
|
||||
x = linspace(0,10,n+2)[2:end-1]
|
||||
y = repmat(reverse(x)', n, 1)
|
||||
scatter(x, y, m=(8,:auto), lab=map(string,markers), bg=:linen, xlim=(0,10), ylim=(0,10))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Bar",
|
||||
"x is the midpoint of the bar. (todo: allow passing of edges instead of midpoints)",
|
||||
[:(begin
|
||||
bar(randn(99))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Histogram",
|
||||
"",
|
||||
[:(begin
|
||||
histogram(randn(1000), nbins=20)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Subplots",
|
||||
"""
|
||||
Use the `layout` keyword, and optionally the convenient `@layout` macro to generate arbitrarily complex subplot layouts.
|
||||
""",
|
||||
[:(begin
|
||||
l = @layout([a{0.1h}; b [c;d e]])
|
||||
plot(randn(100,5), layout=l, t=[:line :histogram :scatter :steppre :bar], leg=false, ticks=nothing, border=false)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Adding to subplots",
|
||||
"Note here the automatic grid layout, as well as the order in which new series are added to the plots.",
|
||||
[:(begin
|
||||
plot(Plots.fakedata(100,10), layout=4, palette=[:grays :blues :heat :lightrainbow], bg_inside=[:orange :pink :darkblue :black])
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("",
|
||||
"",
|
||||
[:(begin
|
||||
srand(111)
|
||||
plot!(Plots.fakedata(100,10))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Open/High/Low/Close",
|
||||
"Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y` argument. This uses recipes to first convert the tuples to OHLC objects, and subsequently create a :path series with the appropriate line segments.",
|
||||
[:(begin
|
||||
n=20
|
||||
hgt=rand(n)+1
|
||||
bot=randn(n)
|
||||
openpct=rand(n)
|
||||
closepct=rand(n)
|
||||
y = OHLC[(openpct[i]*hgt[i]+bot[i], bot[i]+hgt[i], bot[i], closepct[i]*hgt[i]+bot[i]) for i in 1:n]
|
||||
ohlc(y)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Annotations",
|
||||
"The `annotations` keyword is used for text annotations in data-coordinates. Pass in a tuple (x,y,text) or a vector of annotations. `annotate!(ann)` is shorthand for `plot!(; annotation=ann)`. Series annotations are used for annotating individual data points. They require only the annotation... x/y values are computed. A `PlotText` object can be build with the method `text(string, attr...)`, which wraps font and color attributes.",
|
||||
[:(begin
|
||||
y = rand(10)
|
||||
plot(y, annotations = (3,y[3],text("this is #3",:left)), leg=false)
|
||||
annotate!([(5, y[5], text("this is #5",16,:red,:center)), (10, y[10], text("this is #10",:right,20,"courier"))])
|
||||
scatter!(linspace(2,8,6), rand(6), marker=(50,0.2,:orange), series_annotations = ["series","annotations","map","to","series",text("data",:green)])
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Custom Markers",
|
||||
"A `Plots.Shape` is a light wrapper around vertices of a polygon. For supported backends, pass arbitrary polygons as the marker shapes. Note: The center is (0,0) and the size is expected to be rougly the area of the unit circle.",
|
||||
[:(begin
|
||||
verts = [(-1.0,1.0),(-1.28,0.6),(-0.2,-1.4),(0.2,-1.4),(1.28,0.6),(1.0,1.0),
|
||||
(-1.0,1.0),(-0.2,-0.6),(0.0,-0.2),(-0.4,0.6),(1.28,0.6),(0.2,-1.4),
|
||||
(-0.2,-1.4),(0.6,0.2),(-0.2,0.2),(0.0,-0.2),(0.2,0.2),(-0.2,-0.6)]
|
||||
x = 0.1:0.2:0.9
|
||||
y = 0.7rand(5)+0.15
|
||||
plot(x, y, line = (3,:dash,:lightblue), marker = (Shape(verts),30,RGBA(0,0,0,0.2)),
|
||||
bg=:pink, fg=:darkblue, xlim = (0,1), ylim=(0,1), leg=false)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Contours",
|
||||
"Any value for fill works here. We first build a filled contour from a function, then an unfilled contour from a matrix.",
|
||||
[:(begin
|
||||
x = 1:0.5:20
|
||||
y = 1:0.5:10
|
||||
f(x,y) = (3x+y^2)*abs(sin(x)+cos(y))
|
||||
X = repmat(x', length(y), 1)
|
||||
Y = repmat(y, 1, length(x))
|
||||
Z = map(f, X, Y)
|
||||
p1 = contour(x, y, f, fill=true)
|
||||
p2 = contour(x, y, Z)
|
||||
plot(p1, p2)
|
||||
end)]
|
||||
# [:(begin
|
||||
# x = 1:0.3:20
|
||||
# y = x
|
||||
# f(x,y) = sin(x)+cos(y)
|
||||
# contour(x, y, f, fill=true)
|
||||
# end)]
|
||||
),
|
||||
|
||||
PlotExample("Pie",
|
||||
"",
|
||||
[:(begin
|
||||
x = ["Nerds", "Hackers", "Scientists"]
|
||||
y = [0.4, 0.35, 0.25]
|
||||
pie(x, y, title="The Julia Community", l=0.5)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("3D",
|
||||
"",
|
||||
[:(begin
|
||||
n = 100
|
||||
ts = linspace(0,8π,n)
|
||||
x = ts .* map(cos,ts)
|
||||
y = 0.1ts .* map(sin,ts)
|
||||
z = 1:n
|
||||
plot(x, y, z, zcolor=reverse(z), m=(10,0.8,:blues,stroke(0)), leg=false, cbar=true, w=5)
|
||||
plot!(zeros(n),zeros(n),1:n, w=10)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("DataFrames",
|
||||
"Plot using DataFrame column symbols.",
|
||||
[:(begin
|
||||
import RDatasets
|
||||
iris = RDatasets.dataset("datasets", "iris")
|
||||
scatter(iris, :SepalLength, :SepalWidth, group=:Species,
|
||||
title = "My awesome plot", xlabel = "Length", ylabel = "Width",
|
||||
marker = (0.5, [:+ :h :star7], 12), bg=RGB(.2,.2,.2))
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Groups and Subplots",
|
||||
"",
|
||||
[:(begin
|
||||
group = rand(map(i->"group $i",1:4),100)
|
||||
plot(rand(100), layout=@layout([a b;c]), group=group, n=3, linetype=[:bar :scatter :steppre])
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Polar Plots",
|
||||
"",
|
||||
[:(begin
|
||||
Θ = linspace(0,1.5π,100)
|
||||
r = abs(0.1randn(100)+sin(3Θ))
|
||||
plot(Θ, r, proj=:polar, m=2)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Heatmap, categorical axes, and aspect_ratio",
|
||||
"",
|
||||
[:(begin
|
||||
xs = [string("x",i) for i=1:10]
|
||||
ys = [string("y",i) for i=1:4]
|
||||
z = float((1:4)*(1:10)')
|
||||
heatmap(xs, ys, z, aspect_ratio=1)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Layouts, margins, label rotation, title location",
|
||||
"",
|
||||
[:(begin
|
||||
plot(rand(100,6),layout=@layout([a b; c]),title=["A" "B" "C"],
|
||||
title_location=:left, left_margin=[20mm 0mm],
|
||||
bottom_margin=50px, xrotation=60)
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Boxplot and Violin series recipes",
|
||||
"",
|
||||
[:(begin
|
||||
import RDatasets
|
||||
singers = RDatasets.dataset("lattice", "singer")
|
||||
violin(singers, :VoicePart, :Height, marker = (0.2, :blue, stroke(0)))
|
||||
boxplot!(singers, :VoicePart, :Height, marker = (0.3, :orange, stroke(2)))
|
||||
end)]
|
||||
)
|
||||
|
||||
]
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# make and display one plot
|
||||
function test_examples(pkgname::Symbol, idx::Int; debug = false, disp = true)
|
||||
Plots._debugMode.on = debug
|
||||
info("Testing plot: $pkgname:$idx:$(_examples[idx].header)")
|
||||
backend(pkgname)
|
||||
backend()
|
||||
map(eval, _examples[idx].exprs)
|
||||
plt = current()
|
||||
if disp
|
||||
gui(plt)
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
# generate all plots and create a dict mapping idx --> plt
|
||||
function test_examples(pkgname::Symbol; debug = false, disp = true)
|
||||
Plots._debugMode.on = debug
|
||||
plts = Dict()
|
||||
for i in 1:length(_examples)
|
||||
|
||||
try
|
||||
plt = test_examples(pkgname, i, debug=debug, disp=disp)
|
||||
plts[i] = plt
|
||||
catch ex
|
||||
# TODO: put error info into markdown?
|
||||
warn("Example $pkgname:$i:$(_examples[i].header) failed with: $ex")
|
||||
end
|
||||
end
|
||||
plts
|
||||
end
|
||||
+646
-150
@@ -1,178 +1,674 @@
|
||||
|
||||
# NOTE: (0,0) is the top-left !!!
|
||||
|
||||
# allow pixels and percentages
|
||||
const px = AbsoluteLength(0.254)
|
||||
const pct = Length{:pct, Float64}(1.0)
|
||||
|
||||
const _cbar_width = 5mm
|
||||
|
||||
@compat Base.:.*(m::Measure, n::Number) = m * n
|
||||
@compat Base.:.*(n::Number, m::Measure) = m * n
|
||||
@compat Base.:-(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
|
||||
@compat Base.:-(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
|
||||
Base.zero(::Type{typeof(mm)}) = 0mm
|
||||
Base.one(::Type{typeof(mm)}) = 1mm
|
||||
Base.typemin(::typeof(mm)) = -Inf*mm
|
||||
Base.typemax(::typeof(mm)) = Inf*mm
|
||||
Base.convert{F<:AbstractFloat}(::Type{F}, l::AbsoluteLength) = convert(F, l.value)
|
||||
|
||||
# TODO: these are unintuitive and may cause tricky bugs
|
||||
# @compat Base.:+(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
|
||||
# @compat Base.:+(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (1 + m1.value))
|
||||
# @compat Base.:-(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 - m2.value))
|
||||
# @compat Base.:-(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (m1.value - 1))
|
||||
|
||||
@compat Base.:*(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
|
||||
@compat Base.:*(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
|
||||
@compat Base.:/(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
|
||||
@compat Base.:/(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
|
||||
|
||||
|
||||
Base.zero(::Type{typeof(pct)}) = 0pct
|
||||
Base.one(::Type{typeof(pct)}) = 1pct
|
||||
Base.typemin(::typeof(pct)) = 0pct
|
||||
Base.typemax(::typeof(pct)) = 1pct
|
||||
|
||||
const defaultbox = BoundingBox(0mm, 0mm, 0mm, 0mm)
|
||||
|
||||
left(bbox::BoundingBox) = bbox.x0[1]
|
||||
top(bbox::BoundingBox) = bbox.x0[2]
|
||||
right(bbox::BoundingBox) = left(bbox) + width(bbox)
|
||||
bottom(bbox::BoundingBox) = top(bbox) + height(bbox)
|
||||
Base.size(bbox::BoundingBox) = (width(bbox), height(bbox))
|
||||
|
||||
# @compat Base.:*{T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value)
|
||||
ispositive(m::Measure) = m.value > 0
|
||||
|
||||
# union together bounding boxes
|
||||
@compat function Base.:+(bb1::BoundingBox, bb2::BoundingBox)
|
||||
# empty boxes don't change the union
|
||||
ispositive(width(bb1)) || return bb2
|
||||
ispositive(height(bb1)) || return bb2
|
||||
ispositive(width(bb2)) || return bb1
|
||||
ispositive(height(bb2)) || return bb1
|
||||
|
||||
l = min(left(bb1), left(bb2))
|
||||
t = min(top(bb1), top(bb2))
|
||||
r = max(right(bb1), right(bb2))
|
||||
b = max(bottom(bb1), bottom(bb2))
|
||||
BoundingBox(l, t, r-l, b-t)
|
||||
end
|
||||
|
||||
# this creates a bounding box in the parent's scope, where the child bounding box
|
||||
# is relative to the parent
|
||||
function crop(parent::BoundingBox, child::BoundingBox)
|
||||
l = left(parent) + left(child)
|
||||
t = top(parent) + top(child)
|
||||
w = width(child)
|
||||
h = height(child)
|
||||
BoundingBox(l, t, w, h)
|
||||
end
|
||||
|
||||
# convert a bounding box from absolute coords to percentages...
|
||||
# returns an array of percentages of figure size: [left, bottom, width, height]
|
||||
function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true)
|
||||
mms = Float64[f(bb).value for f in (left,bottom,width,height)]
|
||||
if flipy
|
||||
mms[2] = figh.value - mms[2] # flip y when origin in bottom-left
|
||||
end
|
||||
mms ./ Float64[figw.value, figh.value, figw.value, figh.value]
|
||||
end
|
||||
|
||||
function Base.show(io::IO, bbox::BoundingBox)
|
||||
print(io, "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}")
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# AbstractLayout
|
||||
|
||||
Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))")
|
||||
|
||||
# this is the available area for drawing everything in this layout... as percentages of total canvas
|
||||
bbox(layout::AbstractLayout) = layout.bbox
|
||||
bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb)
|
||||
|
||||
# layouts are recursive, tree-like structures, and most will have a parent field
|
||||
Base.parent(layout::AbstractLayout) = layout.parent
|
||||
parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
|
||||
|
||||
# NOTE: these should be implemented for subplots in each backend!
|
||||
# they represent the minimum size of the axes and guides
|
||||
min_padding_left(layout::AbstractLayout) = 0mm
|
||||
min_padding_top(layout::AbstractLayout) = 0mm
|
||||
min_padding_right(layout::AbstractLayout) = 0mm
|
||||
min_padding_bottom(layout::AbstractLayout) = 0mm
|
||||
|
||||
padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout)
|
||||
padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout)
|
||||
padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
|
||||
|
||||
_update_position!(layout::AbstractLayout) = nothing
|
||||
update_child_bboxes!(layout::AbstractLayout) = nothing
|
||||
|
||||
width(layout::AbstractLayout) = width(layout.bbox)
|
||||
height(layout::AbstractLayout) = height(layout.bbox)
|
||||
|
||||
plotarea(layout::AbstractLayout) = defaultbox
|
||||
plotarea!(layout::AbstractLayout, bbox::BoundingBox) = nothing
|
||||
|
||||
attr(layout::AbstractLayout, k::Symbol) = layout.attr[k]
|
||||
attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v)
|
||||
attr!(layout::AbstractLayout, v, k::Symbol) = (layout.attr[k] = v)
|
||||
hasattr(layout::AbstractLayout, k::Symbol) = haskey(layout.attr, k)
|
||||
|
||||
leftpad(layout::AbstractLayout) = 0mm
|
||||
toppad(layout::AbstractLayout) = 0mm
|
||||
rightpad(layout::AbstractLayout) = 0mm
|
||||
bottompad(layout::AbstractLayout) = 0mm
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# RootLayout
|
||||
|
||||
# this is the parent of the top-level layout
|
||||
immutable RootLayout <: AbstractLayout end
|
||||
|
||||
Base.parent(::RootLayout) = nothing
|
||||
parent_bbox(::RootLayout) = defaultbox
|
||||
bbox(::RootLayout) = defaultbox
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# EmptyLayout
|
||||
|
||||
# contains blank space
|
||||
type EmptyLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
bbox::BoundingBox
|
||||
attr::KW # store label, width, and height for initialization
|
||||
# label # this is the label that the subplot will take (since we create a layout before initialization)
|
||||
end
|
||||
EmptyLayout(parent = RootLayout(); kw...) = EmptyLayout(parent, defaultbox, KW(kw))
|
||||
|
||||
Base.size(layout::EmptyLayout) = (0,0)
|
||||
Base.length(layout::EmptyLayout) = 0
|
||||
Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing
|
||||
|
||||
_update_min_padding!(layout::EmptyLayout) = nothing
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# GridLayout
|
||||
# -----------------------------------------------------------
|
||||
|
||||
"Simple grid, indices are row-major."
|
||||
immutable GridLayout <: SubplotLayout
|
||||
nr::Int
|
||||
nc::Int
|
||||
# nested, gridded layout with optional size percentages
|
||||
type GridLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
bbox::BoundingBox
|
||||
grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion
|
||||
widths::Vector{Measure}
|
||||
heights::Vector{Measure}
|
||||
attr::KW
|
||||
end
|
||||
|
||||
Base.length(layout::GridLayout) = layout.nr * layout.nc
|
||||
Base.start(layout::GridLayout) = 1
|
||||
Base.done(layout::GridLayout, state) = state > length(layout)
|
||||
function Base.next(layout::GridLayout, state)
|
||||
r = div(state-1, layout.nc) + 1
|
||||
c = mod1(state, layout.nc)
|
||||
(r,c), state + 1
|
||||
grid(args...; kw...) = GridLayout(args...; kw...)
|
||||
|
||||
function GridLayout(dims...;
|
||||
parent = RootLayout(),
|
||||
widths = zeros(dims[2]),
|
||||
heights = zeros(dims[1]),
|
||||
kw...)
|
||||
grid = Matrix{AbstractLayout}(dims...)
|
||||
layout = GridLayout(
|
||||
parent,
|
||||
(20mm, 5mm, 2mm, 10mm),
|
||||
defaultbox,
|
||||
grid,
|
||||
Measure[w*pct for w in widths],
|
||||
Measure[h*pct for h in heights],
|
||||
# convert(Vector{Float64}, widths),
|
||||
# convert(Vector{Float64}, heights),
|
||||
KW(kw))
|
||||
fill!(grid, EmptyLayout(layout))
|
||||
layout
|
||||
end
|
||||
|
||||
nrows(layout::GridLayout) = layout.nr
|
||||
ncols(layout::GridLayout) = layout.nc
|
||||
ncols(layout::GridLayout, row::Int) = layout.nc
|
||||
|
||||
# get the plot index given row and column
|
||||
Base.getindex(layout::GridLayout, r::Int, c::Int) = (r-1) * layout.nc + c
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# RowsLayout
|
||||
# -----------------------------------------------------------
|
||||
|
||||
"Number of plots per row"
|
||||
immutable RowsLayout <: SubplotLayout
|
||||
numplts::Int
|
||||
rowcounts::AbstractVector{Int}
|
||||
Base.size(layout::GridLayout) = size(layout.grid)
|
||||
Base.length(layout::GridLayout) = length(layout.grid)
|
||||
Base.getindex(layout::GridLayout, r::Int, c::Int) = layout.grid[r,c]
|
||||
function Base.setindex!(layout::GridLayout, v, r::Int, c::Int)
|
||||
layout.grid[r,c] = v
|
||||
end
|
||||
|
||||
Base.length(layout::RowsLayout) = layout.numplts
|
||||
Base.start(layout::RowsLayout) = 1
|
||||
Base.done(layout::RowsLayout, state) = state > length(layout)
|
||||
function Base.next(layout::RowsLayout, state)
|
||||
r = 1
|
||||
c = 0
|
||||
for i = 1:state
|
||||
c += 1
|
||||
if c > layout.rowcounts[r]
|
||||
r += 1
|
||||
c = 1
|
||||
end
|
||||
end
|
||||
(r,c), state + 1
|
||||
leftpad(layout::GridLayout) = layout.minpad[1]
|
||||
toppad(layout::GridLayout) = layout.minpad[2]
|
||||
rightpad(layout::GridLayout) = layout.minpad[3]
|
||||
bottompad(layout::GridLayout) = layout.minpad[4]
|
||||
|
||||
|
||||
|
||||
# leftpad, toppad, rightpad, bottompad
|
||||
function _update_min_padding!(layout::GridLayout)
|
||||
map(_update_min_padding!, layout.grid)
|
||||
layout.minpad = (
|
||||
maximum(map(leftpad, layout.grid[:,1])),
|
||||
maximum(map(toppad, layout.grid[1,:])),
|
||||
maximum(map(rightpad, layout.grid[:,end])),
|
||||
maximum(map(bottompad, layout.grid[end,:]))
|
||||
)
|
||||
end
|
||||
|
||||
nrows(layout::RowsLayout) = length(layout.rowcounts)
|
||||
ncols(layout::RowsLayout, row::Int) = row < 1 ? 0 : (row > nrows(layout) ? 0 : layout.rowcounts[row])
|
||||
|
||||
# get the plot index given row and column
|
||||
Base.getindex(layout::RowsLayout, r::Int, c::Int) = sum(layout.rowcounts[1:r-1]) + c
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# FlexLayout
|
||||
# -----------------------------------------------------------
|
||||
|
||||
"Flexible, nested layout with optional size percentages."
|
||||
immutable FlexLayout <: SubplotLayout
|
||||
n::Int
|
||||
grid::Matrix # Nested layouts. Each position
|
||||
# can be a plot index or another FlexLayout
|
||||
widths::Vector{Float64}
|
||||
heights::Vector{Float64}
|
||||
function _update_position!(layout::GridLayout)
|
||||
map(_update_position!, layout.grid)
|
||||
end
|
||||
|
||||
typealias IntOrFlex Union{Int,FlexLayout}
|
||||
|
||||
Base.length(layout::FlexLayout) = layout.n
|
||||
Base.start(layout::FlexLayout) = 1
|
||||
Base.done(layout::FlexLayout, state) = state > length(layout)
|
||||
function Base.next(layout::FlexLayout, state)
|
||||
# TODO: change this method to return more info
|
||||
# TODO: might consider multiple iterator types.. some backends might have an easier time row-by-row for example
|
||||
error()
|
||||
r = 1
|
||||
c = 0
|
||||
for i = 1:state
|
||||
c += 1
|
||||
if c > layout.rowcounts[r]
|
||||
r += 1
|
||||
c = 1
|
||||
end
|
||||
end
|
||||
(r,c), state + 1
|
||||
end
|
||||
|
||||
nrows(layout::FlexLayout) = size(layout.grid, 1)
|
||||
ncols(layout::FlexLayout, row::Int) = size(layout.grid, 2)
|
||||
|
||||
# get the plot index given row and column
|
||||
Base.getindex(layout::FlexLayout, r::Int, c::Int) = layout.grid[r,c]
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# we're taking in a nested structure of some kind... parse it out and build a FlexLayout
|
||||
function subplotlayout(mat::AbstractVecOrMat; widths = nothing, heights = nothing)
|
||||
n = 0
|
||||
nr, nc = size(mat)
|
||||
grid = Array(IntOrFlex, nr, nc)
|
||||
for i=1:nr, j=1:nc
|
||||
v = mat[i,j]
|
||||
|
||||
if isa(v, Integer)
|
||||
grid[i,j] = Int(v)
|
||||
n += 1
|
||||
|
||||
elseif isa(v, Tuple)
|
||||
warn("need to handle tuples somehow... (idx, sizepct)")
|
||||
grid[i,j] = nothing
|
||||
|
||||
elseif v == nothing
|
||||
grid[i,j] = nothing
|
||||
|
||||
elseif isa(v, AbstractVecOrMat)
|
||||
grid[i,j] = layout(v)
|
||||
n += grid[i,j].n
|
||||
|
||||
function recompute_lengths(v)
|
||||
# dump(v)
|
||||
tot = 0pct
|
||||
cnt = 0
|
||||
for vi in v
|
||||
if vi == 0pct
|
||||
cnt += 1
|
||||
else
|
||||
error("How do we process? $v")
|
||||
tot += vi
|
||||
end
|
||||
end
|
||||
|
||||
if widths == nothing
|
||||
widths = ones(nc) ./ nc
|
||||
end
|
||||
if heights == nothing
|
||||
heights = ones(nr) ./ nr
|
||||
leftover = 1.0pct - tot
|
||||
if cnt > 1 && leftover.value <= 0
|
||||
error("Not enough length left over in layout! v = $v, cnt = $cnt, leftover = $leftover")
|
||||
end
|
||||
|
||||
FlexLayout(n, grid, widths, heights)
|
||||
# now fill in the blanks
|
||||
Measure[(vi == 0pct ? leftover / cnt : vi) for vi in v]
|
||||
end
|
||||
|
||||
# recursively compute the bounding boxes for the layout and plotarea (relative to canvas!)
|
||||
function update_child_bboxes!(layout::GridLayout)
|
||||
nr, nc = size(layout)
|
||||
|
||||
# create a matrix for each minimum padding direction
|
||||
_update_min_padding!(layout)
|
||||
|
||||
minpad_left = map(leftpad, layout.grid)
|
||||
minpad_top = map(toppad, layout.grid)
|
||||
minpad_right = map(rightpad, layout.grid)
|
||||
minpad_bottom = map(bottompad, layout.grid)
|
||||
# @show minpad_left minpad_top minpad_right minpad_bottom
|
||||
|
||||
# get the max horizontal (left and right) padding over columns,
|
||||
# and max vertical (bottom and top) padding over rows
|
||||
# TODO: add extra padding here
|
||||
pad_left = maximum(minpad_left, 1)
|
||||
pad_top = maximum(minpad_top, 2)
|
||||
pad_right = maximum(minpad_right, 1)
|
||||
pad_bottom = maximum(minpad_bottom, 2)
|
||||
# @show pad_left pad_top pad_right pad_bottom
|
||||
|
||||
# scale this up to the total padding in each direction
|
||||
total_pad_horizontal = sum(pad_left + pad_right)
|
||||
total_pad_vertical = sum(pad_top + pad_bottom)
|
||||
# @show total_pad_horizontal total_pad_vertical
|
||||
|
||||
# now we can compute the total plot area in each direction
|
||||
total_plotarea_horizontal = width(layout) - total_pad_horizontal
|
||||
total_plotarea_vertical = height(layout) - total_pad_vertical
|
||||
# @show total_plotarea_horizontal total_plotarea_vertical
|
||||
|
||||
# recompute widths/heights
|
||||
layout.widths = recompute_lengths(layout.widths)
|
||||
layout.heights = recompute_lengths(layout.heights)
|
||||
# @show layout.widths layout.heights
|
||||
|
||||
# normalize widths/heights so they sum to 1
|
||||
# denom_w = sum(layout.widths)
|
||||
# denom_h = sum(layout.heights)
|
||||
# @show layout.widths layout.heights denom_w, denom_h
|
||||
|
||||
# we have all the data we need... lets compute the plot areas and set the bounding boxes
|
||||
for r=1:nr, c=1:nc
|
||||
child = layout[r,c]
|
||||
|
||||
# get the top-left corner of this child... the first one is top-left of the parent (i.e. layout)
|
||||
child_left = (c == 1 ? left(layout.bbox) : right(layout[r, c-1].bbox))
|
||||
child_top = (r == 1 ? top(layout.bbox) : bottom(layout[r-1, c].bbox))
|
||||
|
||||
# compute plot area
|
||||
plotarea_left = child_left + pad_left[c]
|
||||
plotarea_top = child_top + pad_top[r]
|
||||
plotarea_width = total_plotarea_horizontal * layout.widths[c]
|
||||
plotarea_height = total_plotarea_vertical * layout.heights[r]
|
||||
plotarea!(child, BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height))
|
||||
|
||||
# compute child bbox
|
||||
child_width = pad_left[c] + plotarea_width + pad_right[c]
|
||||
child_height = pad_top[r] + plotarea_height + pad_bottom[r]
|
||||
bbox!(child, BoundingBox(child_left, child_top, child_width, child_height))
|
||||
|
||||
# recursively update the child's children
|
||||
update_child_bboxes!(child)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function subplotlayout(sz::Tuple{Int,Int})
|
||||
GridLayout(sz...)
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
calc_num_subplots(layout::AbstractLayout) = 1
|
||||
function calc_num_subplots(layout::GridLayout)
|
||||
tot = 0
|
||||
for l in layout.grid
|
||||
tot += calc_num_subplots(l)
|
||||
end
|
||||
tot
|
||||
end
|
||||
|
||||
function subplotlayout(rowcounts::AVec{Int})
|
||||
RowsLayout(sum(rowcounts), rowcounts)
|
||||
end
|
||||
|
||||
function subplotlayout(numplts::Int, nr::Int, nc::Int)
|
||||
|
||||
# figure out how many rows/columns we need
|
||||
if nr == -1
|
||||
if nc == -1
|
||||
nr = round(Int, sqrt(numplts))
|
||||
nc = ceil(Int, numplts / nr)
|
||||
function compute_gridsize(numplts::Int, nr::Int, nc::Int)
|
||||
# figure out how many rows/columns we need
|
||||
if nr < 1
|
||||
if nc < 1
|
||||
nr = round(Int, sqrt(numplts))
|
||||
nc = ceil(Int, numplts / nr)
|
||||
else
|
||||
nr = ceil(Int, numplts / nc)
|
||||
end
|
||||
else
|
||||
nr = ceil(Int, numplts / nc)
|
||||
nc = ceil(Int, numplts / nr)
|
||||
end
|
||||
nr, nc
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# constructors
|
||||
|
||||
# pass the layout arg through
|
||||
function layout_args(d::KW)
|
||||
layout_args(get(d, :layout, default(:layout)))
|
||||
end
|
||||
|
||||
function layout_args(d::KW, n_override::Integer)
|
||||
layout, n = layout_args(get(d, :layout, n_override))
|
||||
if n != n_override
|
||||
error("When doing layout, n != n_override. You're probably trying to force existing plots into a layout that doesn't fit them.")
|
||||
end
|
||||
layout, n
|
||||
end
|
||||
|
||||
function layout_args(n::Integer)
|
||||
nr, nc = compute_gridsize(n, -1, -1)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
function layout_args{I<:Integer}(sztup::NTuple{2,I})
|
||||
nr, nc = sztup
|
||||
GridLayout(nr, nc), nr*nc
|
||||
end
|
||||
|
||||
function layout_args{I<:Integer}(sztup::NTuple{3,I})
|
||||
n, nr, nc = sztup
|
||||
nr, nc = compute_gridsize(n, nr, nc)
|
||||
GridLayout(nr, nc), n
|
||||
end
|
||||
|
||||
# compute number of subplots
|
||||
function layout_args(layout::GridLayout)
|
||||
# recursively get the size of the grid
|
||||
n = calc_num_subplots(layout)
|
||||
layout, n
|
||||
end
|
||||
|
||||
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
|
||||
|
||||
# # pass the layout arg through
|
||||
# function build_layout(d::KW)
|
||||
# build_layout(get(d, :layout, default(:layout)))
|
||||
# end
|
||||
#
|
||||
# function build_layout(n::Integer)
|
||||
# nr, nc = compute_gridsize(n, -1, -1)
|
||||
# build_layout(GridLayout(nr, nc), n)
|
||||
# end
|
||||
#
|
||||
# function build_layout{I<:Integer}(sztup::NTuple{2,I})
|
||||
# nr, nc = sztup
|
||||
# build_layout(GridLayout(nr, nc))
|
||||
# end
|
||||
#
|
||||
# function build_layout{I<:Integer}(sztup::NTuple{3,I})
|
||||
# n, nr, nc = sztup
|
||||
# nr, nc = compute_gridsize(n, nr, nc)
|
||||
# build_layout(GridLayout(nr, nc), n)
|
||||
# end
|
||||
#
|
||||
# # compute number of subplots
|
||||
# function build_layout(layout::GridLayout)
|
||||
# # recursively get the size of the grid
|
||||
# n = calc_num_subplots(layout)
|
||||
# build_layout(layout, n)
|
||||
# end
|
||||
|
||||
function build_layout(args...)
|
||||
layout, n = layout_args(args...)
|
||||
build_layout(layout, n)
|
||||
end
|
||||
|
||||
# n is the number of subplots
|
||||
function build_layout(layout::GridLayout, n::Integer)
|
||||
nr, nc = size(layout)
|
||||
subplots = Subplot[]
|
||||
spmap = SubplotMap()
|
||||
i = 0
|
||||
for r=1:nr, c=1:nc
|
||||
l = layout[r,c]
|
||||
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
|
||||
sp = Subplot(backend(), parent=layout)
|
||||
layout[r,c] = sp
|
||||
push!(subplots, sp)
|
||||
spmap[attr(l,:label,gensym())] = sp
|
||||
if get(l.attr, :width, :auto) != :auto
|
||||
layout.widths[c] = attr(l,:width)
|
||||
end
|
||||
if get(l.attr, :height, :auto) != :auto
|
||||
layout.heights[r] = attr(l,:height)
|
||||
end
|
||||
i += 1
|
||||
elseif isa(l, GridLayout)
|
||||
# sub-grid
|
||||
l, sps, m = build_layout(l, n-i)
|
||||
append!(subplots, sps)
|
||||
merge!(spmap, m)
|
||||
i += length(sps)
|
||||
end
|
||||
i >= n && break # only add n subplots
|
||||
end
|
||||
|
||||
layout, subplots, spmap
|
||||
end
|
||||
|
||||
# build a layout from a list of existing Plot objects
|
||||
# TODO... much of the logic overlaps with the method above... can we merge?
|
||||
function build_layout(layout::GridLayout, numsp::Integer, plts::AVec{Plot})
|
||||
nr, nc = size(layout)
|
||||
subplots = Subplot[]
|
||||
spmap = SubplotMap()
|
||||
i = 0
|
||||
for r=1:nr, c=1:nc
|
||||
l = layout[r,c]
|
||||
if isa(l, EmptyLayout) && !get(l.attr, :blank, false)
|
||||
plt = shift!(plts) # grab the first plot out of the list
|
||||
layout[r,c] = plt.layout
|
||||
append!(subplots, plt.subplots)
|
||||
merge!(spmap, plt.spmap)
|
||||
if get(l.attr, :width, :auto) != :auto
|
||||
layout.widths[c] = attr(l,:width)
|
||||
end
|
||||
if get(l.attr, :height, :auto) != :auto
|
||||
layout.heights[r] = attr(l,:height)
|
||||
end
|
||||
i += length(plt.subplots)
|
||||
elseif isa(l, GridLayout)
|
||||
# sub-grid
|
||||
l, sps, m = build_layout(l, numsp-i, plts)
|
||||
append!(subplots, sps)
|
||||
merge!(spmap, m)
|
||||
i += length(sps)
|
||||
end
|
||||
i >= numsp && break # only add n subplots
|
||||
end
|
||||
layout, subplots, spmap
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# @layout macro
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Expr, idx::Integer, nidx::Integer)
|
||||
# dump(v)
|
||||
# something like {0.2w}?
|
||||
if v.head == :call && v.args[1] == :*
|
||||
num = v.args[2]
|
||||
if length(v.args) == 3 && isa(num, Number)
|
||||
units = v.args[3]
|
||||
if units == :h
|
||||
return kw[:h] = num*pct
|
||||
elseif units == :w
|
||||
return kw[:w] = num*pct
|
||||
elseif units in (:pct, :px, :mm, :cm, :inch)
|
||||
idx == 1 && (kw[:w] = v)
|
||||
(idx == 2 || nidx == 1) && (kw[:h] = v)
|
||||
# return kw[idx == 1 ? :w : :h] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
error("Couldn't match layout curly (idx=$idx): $v")
|
||||
end
|
||||
|
||||
function add_layout_pct!(kw::KW, v::Number, idx::Integer)
|
||||
# kw[idx == 1 ? :w : :h] = v*pct
|
||||
idx == 1 && (kw[:w] = v*pct)
|
||||
(idx == 2 || nidx == 1) && (kw[:h] = v*pct)
|
||||
end
|
||||
|
||||
isrow(v) = isa(v, Expr) && v.head in (:hcat,:row)
|
||||
iscol(v) = isa(v, Expr) && v.head == :vcat
|
||||
rowsize(v) = isrow(v) ? length(v.args) : 1
|
||||
|
||||
|
||||
function create_grid(expr::Expr)
|
||||
# cellsym = gensym(:cell)
|
||||
# @show expr
|
||||
if iscol(expr)
|
||||
create_grid_vcat(expr)
|
||||
# rowsizes = map(rowsize, expr.args)
|
||||
# rmin, rmax = extrema(rowsizes)
|
||||
# if rmin > 0 && rmin == rmax
|
||||
# # we have a grid... build the whole thing
|
||||
# # note: rmin is the number of columns
|
||||
# nr = length(expr.args)
|
||||
# nc = rmin
|
||||
#
|
||||
# :(let cell = GridLayout($nr, $nc)
|
||||
# $([:(cell[$r,$c] = $(create_grid(expr.args[r], c))) for r=1:nr, c=1:nc]...)
|
||||
# for r=1:nr
|
||||
# layout = $(create_grid(expr.args[r])
|
||||
# cell[r,]
|
||||
# $([:($cellsym[$r,1] = $(create_grid(expr.args[r]))) for r=1:length(expr.args)]...)
|
||||
# $cellsym
|
||||
# end)
|
||||
# else
|
||||
# # otherwise just build one row at a time
|
||||
# :(let
|
||||
# $cellsym = GridLayout($(length(expr.args)), 1)
|
||||
# $([:($cellsym[$i,1] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
|
||||
# $cellsym
|
||||
# end)
|
||||
# end
|
||||
elseif isrow(expr)
|
||||
:(let cell = GridLayout(1, $(length(expr.args)))
|
||||
$([:(cell[1,$i] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
|
||||
cell
|
||||
end)
|
||||
# :(let
|
||||
# $cellsym = GridLayout(1, $(length(expr.args)))
|
||||
# $([:($cellsym[1,$i] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
|
||||
# $cellsym
|
||||
# end)
|
||||
|
||||
elseif expr.head == :curly
|
||||
create_grid_curly(expr)
|
||||
else
|
||||
# if it's something else, just return that (might be an existing layout?)
|
||||
expr
|
||||
end
|
||||
end
|
||||
|
||||
function create_grid_vcat(expr::Expr)
|
||||
rowsizes = map(rowsize, expr.args)
|
||||
rmin, rmax = extrema(rowsizes)
|
||||
# @show rmin, rmax
|
||||
if rmin > 0 && rmin == rmax
|
||||
# we have a grid... build the whole thing
|
||||
# note: rmin is the number of columns
|
||||
nr = length(expr.args)
|
||||
nc = rmin
|
||||
# @show nr, nc
|
||||
body = Expr(:block)
|
||||
for r=1:nr
|
||||
arg = expr.args[r]
|
||||
# @show r, arg
|
||||
if isrow(arg)
|
||||
for (c,item) in enumerate(arg.args)
|
||||
push!(body.args, :(cell[$r,$c] = $(create_grid(item))))
|
||||
end
|
||||
else
|
||||
push!(body.args, :(cell[$r,1] = $(create_grid(arg))))
|
||||
end
|
||||
end
|
||||
# @show body
|
||||
:(let cell = GridLayout($nr, $nc)
|
||||
$body
|
||||
cell
|
||||
end)
|
||||
# :(let cell = GridLayout($nr, $nc)
|
||||
# $([:(cell[$r,$c] = $(create_grid(expr.args[r], c))) for r=1:nr, c=1:nc]...)
|
||||
# for r=1:nr
|
||||
# layout = $(create_grid(expr.args[r])
|
||||
# cell[r,]
|
||||
# $([:($cellsym[$r,1] = $(create_grid(expr.args[r]))) for r=1:length(expr.args)]...)
|
||||
# $cellsym
|
||||
# end)
|
||||
else
|
||||
# otherwise just build one row at a time
|
||||
:(let cell = GridLayout($(length(expr.args)), 1)
|
||||
$([:(cell[$i,1] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
|
||||
cell
|
||||
end)
|
||||
# :(let
|
||||
# $cellsym = GridLayout($(length(expr.args)), 1)
|
||||
# $([:($cellsym[$i,1] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
|
||||
# $cellsym
|
||||
# end)
|
||||
end
|
||||
end
|
||||
|
||||
function create_grid_curly(expr::Expr)
|
||||
s = expr.args[1]
|
||||
kw = KW()
|
||||
for (i,arg) in enumerate(expr.args[2:end])
|
||||
add_layout_pct!(kw, arg, i, length(expr.args)-1)
|
||||
end
|
||||
# @show kw
|
||||
:(EmptyLayout(label = $(QuoteNode(s)), width = $(get(kw, :w, QuoteNode(:auto))), height = $(get(kw, :h, QuoteNode(:auto)))))
|
||||
end
|
||||
|
||||
function create_grid(s::Symbol)
|
||||
:(EmptyLayout(label = $(QuoteNode(s)), blank = $(s == :_)))
|
||||
end
|
||||
|
||||
macro layout(mat::Expr)
|
||||
create_grid(mat)
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# make all reference the same axis extrema/values
|
||||
function link_axes!(axes::Axis...)
|
||||
a1 = axes[1]
|
||||
for i=2:length(axes)
|
||||
a2 = axes[i]
|
||||
for k in (:extrema, :discrete_values, :continuous_values, :discrete_map)
|
||||
a2[k] = a1[k]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# for some vector or matrix of layouts, filter only the Subplots and link those axes
|
||||
function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
|
||||
subplots = filter(l -> isa(l, Subplot), a)
|
||||
axes = [sp.attr[axissym] for sp in subplots]
|
||||
link_axes!(axes...)
|
||||
end
|
||||
|
||||
# don't do anything for most layout types
|
||||
function link_axes!(l::AbstractLayout, link::Symbol)
|
||||
end
|
||||
|
||||
# process a GridLayout, recursively linking axes according to the link symbol
|
||||
function link_axes!(layout::GridLayout, link::Symbol)
|
||||
nr, nc = size(layout)
|
||||
if link in (:x, :both)
|
||||
for c=1:nc
|
||||
link_axes!(layout.grid[:,c], :xaxis)
|
||||
end
|
||||
end
|
||||
if link in (:y, :both)
|
||||
for r=1:nr
|
||||
link_axes!(layout.grid[r,:], :yaxis)
|
||||
end
|
||||
end
|
||||
if link == :all
|
||||
link_axes!(layout.grid, :xaxis)
|
||||
link_axes!(layout.grid, :yaxis)
|
||||
end
|
||||
for l in layout.grid
|
||||
link_axes!(l, link)
|
||||
end
|
||||
else
|
||||
nc = ceil(Int, numplts / nr)
|
||||
end
|
||||
|
||||
# if it's a perfect rectangle, just create a grid
|
||||
if numplts == nr * nc
|
||||
return GridLayout(nr, nc)
|
||||
end
|
||||
|
||||
# create the rowcounts vector
|
||||
i = 0
|
||||
rowcounts = Int[]
|
||||
for r in 1:nr
|
||||
cnt = min(nc, numplts - i)
|
||||
push!(rowcounts, cnt)
|
||||
i += cnt
|
||||
end
|
||||
|
||||
RowsLayout(numplts, rowcounts)
|
||||
end
|
||||
|
||||
+108
-34
@@ -1,49 +1,49 @@
|
||||
|
||||
|
||||
defaultOutputFormat(plt::AbstractPlot) = "png"
|
||||
defaultOutputFormat(plt::Plot) = "png"
|
||||
|
||||
function png(plt::AbstractPlot, fn::@compat(AbstractString))
|
||||
function png(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "png")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("image/png"), plt)
|
||||
close(io)
|
||||
end
|
||||
png(fn::@compat(AbstractString)) = png(current(), fn)
|
||||
png(fn::AbstractString) = png(current(), fn)
|
||||
|
||||
function svg(plt::AbstractPlot, fn::@compat(AbstractString))
|
||||
function svg(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "svg")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("image/svg+xml"), plt)
|
||||
close(io)
|
||||
end
|
||||
svg(fn::@compat(AbstractString)) = svg(current(), fn)
|
||||
svg(fn::AbstractString) = svg(current(), fn)
|
||||
|
||||
|
||||
function pdf(plt::AbstractPlot, fn::@compat(AbstractString))
|
||||
function pdf(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "pdf")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("application/pdf"), plt)
|
||||
close(io)
|
||||
end
|
||||
pdf(fn::@compat(AbstractString)) = pdf(current(), fn)
|
||||
pdf(fn::AbstractString) = pdf(current(), fn)
|
||||
|
||||
|
||||
function ps(plt::AbstractPlot, fn::@compat(AbstractString))
|
||||
function ps(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "ps")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("application/postscript"), plt)
|
||||
close(io)
|
||||
end
|
||||
ps(fn::@compat(AbstractString)) = ps(current(), fn)
|
||||
ps(fn::AbstractString) = ps(current(), fn)
|
||||
|
||||
|
||||
function tex(plt::AbstractPlot, fn::@compat(AbstractString))
|
||||
function tex(plt::Plot, fn::AbstractString)
|
||||
fn = addExtension(fn, "tex")
|
||||
io = open(fn, "w")
|
||||
writemime(io, MIME("application/x-tex"), plt)
|
||||
close(io)
|
||||
end
|
||||
tex(fn::@compat(AbstractString)) = tex(current(), fn)
|
||||
tex(fn::AbstractString) = tex(current(), fn)
|
||||
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
@@ -57,7 +57,7 @@ tex(fn::@compat(AbstractString)) = tex(current(), fn)
|
||||
"tex" => tex,
|
||||
)
|
||||
|
||||
function getExtension(fn::@compat(AbstractString))
|
||||
function getExtension(fn::AbstractString)
|
||||
pieces = split(fn, ".")
|
||||
length(pieces) > 1 || error("Can't extract file extension: ", fn)
|
||||
ext = pieces[end]
|
||||
@@ -65,7 +65,7 @@ function getExtension(fn::@compat(AbstractString))
|
||||
ext
|
||||
end
|
||||
|
||||
function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
|
||||
function addExtension(fn::AbstractString, ext::AbstractString)
|
||||
try
|
||||
oldext = getExtension(fn)
|
||||
if oldext == ext
|
||||
@@ -78,7 +78,7 @@ function addExtension(fn::@compat(AbstractString), ext::@compat(AbstractString))
|
||||
end
|
||||
end
|
||||
|
||||
function savefig(plt::AbstractPlot, fn::@compat(AbstractString))
|
||||
function savefig(plt::Plot, fn::AbstractString)
|
||||
|
||||
# get the extension
|
||||
local ext
|
||||
@@ -96,28 +96,102 @@ function savefig(plt::AbstractPlot, fn::@compat(AbstractString))
|
||||
end
|
||||
func(plt, fn)
|
||||
end
|
||||
savefig(fn::@compat(AbstractString)) = savefig(current(), fn)
|
||||
|
||||
|
||||
# savepng(args...; kw...) = savepng(current(), args...; kw...)
|
||||
# savepng(plt::AbstractPlot, fn::@compat(AbstractString); kw...) = (io = open(fn, "w"); writemime(io, MIME("image/png"), plt); close(io))
|
||||
|
||||
|
||||
savefig(fn::AbstractString) = savefig(current(), fn)
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
gui(plt::AbstractPlot = current()) = display(PlotsDisplay(), plt)
|
||||
gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
|
||||
|
||||
|
||||
# override the REPL display to open a gui window
|
||||
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::AbstractPlot) = gui(plt)
|
||||
|
||||
# a backup for html... passes to svg
|
||||
function Base.writemime(io::IO, ::MIME"text/html", plt::AbstractPlot)
|
||||
writemime(io, MIME("image/svg+xml"), plt)
|
||||
function Base.display(::PlotsDisplay, plt::Plot)
|
||||
prepare_output(plt)
|
||||
_display(plt)
|
||||
end
|
||||
|
||||
# override the REPL display to open a gui window
|
||||
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _mimeformats = Dict(
|
||||
"application/eps" => "eps",
|
||||
"image/eps" => "eps",
|
||||
"application/pdf" => "pdf",
|
||||
"image/png" => "png",
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg"
|
||||
)
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
:pyplot => :png,
|
||||
)
|
||||
|
||||
# a backup for html... passes to svg or png depending on the html_output_format arg
|
||||
function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
|
||||
output_type = Symbol(plt.attr[:html_output_format])
|
||||
if output_type == :auto
|
||||
output_type = get(_best_html_output_type, backend_name(plt.backend), :svg)
|
||||
end
|
||||
if output_type == :png
|
||||
# info("writing png to html output")
|
||||
print(io, "<img src=\"data:image/png;base64,", base64encode(writemime, MIME("image/png"), plt), "\" />")
|
||||
elseif output_type == :svg
|
||||
# info("writing svg to html output")
|
||||
writemime(io, MIME("image/svg+xml"), plt)
|
||||
else
|
||||
error("only png or svg allowed. got: $output_type")
|
||||
end
|
||||
end
|
||||
|
||||
# for writing to io streams... first prepare, then callback
|
||||
for mime in keys(_mimeformats)
|
||||
@eval function Base.writemime(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
|
||||
prepare_output(plt)
|
||||
_writemime(io, m, plt)
|
||||
end
|
||||
end
|
||||
|
||||
# function html_output_format(fmt)
|
||||
# if fmt == "png"
|
||||
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
|
||||
# print(io, "<img src=\"data:image/png;base64,", base64(writemime, MIME("image/png"), plt), "\" />")
|
||||
# end
|
||||
# elseif fmt == "svg"
|
||||
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
|
||||
# writemime(io, MIME("image/svg+xml"), plt)
|
||||
# end
|
||||
# else
|
||||
# error("only png or svg allowed. got: $fmt")
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# html_output_format("svg")
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# IJulia
|
||||
# ---------------------------------------------------------
|
||||
|
||||
const _ijulia_output = Compat.ASCIIString["text/html"]
|
||||
|
||||
function setup_ijulia()
|
||||
# override IJulia inline display
|
||||
if isijulia()
|
||||
@eval begin
|
||||
import IJulia
|
||||
export set_ijulia_output
|
||||
function set_ijulia_output(mimestr::AbstractString)
|
||||
# info("Setting IJulia output format to $mimestr")
|
||||
global _ijulia_output
|
||||
_ijulia_output[1] = mimestr
|
||||
end
|
||||
function IJulia.display_dict(plt::Plot)
|
||||
global _ijulia_output
|
||||
Dict{Compat.ASCIIString, ByteString}(_ijulia_output[1] => sprint(writemime, _ijulia_output[1], plt))
|
||||
end
|
||||
end
|
||||
set_ijulia_output("text/html")
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
# Atom PlotPane
|
||||
@@ -125,21 +199,21 @@ end
|
||||
|
||||
function setup_atom()
|
||||
# @require Atom begin
|
||||
if isatom()
|
||||
if isatom() && get(ENV, "PLOTS_USE_ATOM_PLOTPANE", false) in (true, 1, "1", "true", "yes")
|
||||
# @eval import Atom, Media
|
||||
@eval import Atom
|
||||
|
||||
# connects the render function
|
||||
for T in (GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend)
|
||||
Atom.Media.media(AbstractPlot{T}, Atom.Media.Plot)
|
||||
Atom.Media.media(Plot{T}, Atom.Media.Plot)
|
||||
end
|
||||
# Atom.Media.media{T <: Union{GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend}}(Plot{T}, Atom.Media.Plot)
|
||||
|
||||
# Atom.displaysize(::AbstractPlot) = (535, 379)
|
||||
# Atom.displaytitle(plt::AbstractPlot) = "Plots.jl (backend: $(backend(plt)))"
|
||||
# Atom.displaysize(::Plot) = (535, 379)
|
||||
# Atom.displaytitle(plt::Plot) = "Plots.jl (backend: $(backend(plt)))"
|
||||
|
||||
# this is like "display"... sends an html div with the plot to the PlotPane
|
||||
function Atom.Media.render(pane::Atom.PlotPane, plt::AbstractPlot)
|
||||
function Atom.Media.render(pane::Atom.PlotPane, plt::Plot)
|
||||
Atom.Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
|
||||
end
|
||||
|
||||
|
||||
+404
-168
@@ -22,7 +22,7 @@ Base.print(io::IO, plt::Plot) = print(io, string(plt))
|
||||
Base.show(io::IO, plt::Plot) = print(io, string(plt))
|
||||
|
||||
getplot(plt::Plot) = plt
|
||||
getplotargs(plt::Plot, idx::Int = 1) = plt.plotargs
|
||||
getattr(plt::Plot, idx::Int = 1) = plt.attr
|
||||
convertSeriesIndex(plt::Plot, n::Int) = n
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -43,17 +43,71 @@ When you pass in matrices, it splits by columns. See the documentation for more
|
||||
|
||||
# this creates a new plot with args/kw and sets it to be the current plot
|
||||
function plot(args...; kw...)
|
||||
pkg = backend()
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
dumpdict(d, "After plot preprocessing")
|
||||
|
||||
plotargs = merge(d, getPlotArgs(pkg, d, 1))
|
||||
dumpdict(plotargs, "Plot args")
|
||||
plt = _create_plot(pkg, plotargs) # create a new, blank plot
|
||||
# create an empty Plot then process
|
||||
plt = Plot()
|
||||
# plt.user_attr = d
|
||||
_plot!(plt, d, args...)
|
||||
end
|
||||
|
||||
delete!(d, :background_color)
|
||||
plot!(plt, args...; d...) # add to it
|
||||
# build a new plot from existing plots
|
||||
# note: we split into plt1 and plts_tail so we can dispatch correctly
|
||||
function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
|
||||
# build our plot vector from the args
|
||||
n = length(plts_tail) + 1
|
||||
plts = Array(Plot, n)
|
||||
plts[1] = plt1
|
||||
for (i,plt) in enumerate(plts_tail)
|
||||
plts[i+1] = plt
|
||||
end
|
||||
|
||||
# compute the layout
|
||||
layout = layout_args(d, n)[1]
|
||||
num_sp = sum([length(p.subplots) for p in plts])
|
||||
|
||||
# create a new plot object, with subplot list/map made of existing subplots.
|
||||
# note: we create a new backend figure for this new plot object
|
||||
# note: all subplots and series "belong" to this new plot...
|
||||
plt = Plot()
|
||||
|
||||
# TODO: build the user_attr dict by creating "Any matrices" for the args of each subplot
|
||||
|
||||
# TODO: replace this with proper processing from a merged user_attr KW
|
||||
# update plot args, first with existing plots, then override with d
|
||||
for p in plts
|
||||
_update_plot_args(plt, p.attr)
|
||||
plt.n += p.n
|
||||
end
|
||||
_update_plot_args(plt, d)
|
||||
|
||||
# pass new plot to the backend
|
||||
plt.o = _create_backend_figure(plt)
|
||||
plt.init = true
|
||||
|
||||
# create the layout and initialize the subplots
|
||||
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
|
||||
for (idx, sp) in enumerate(plt.subplots)
|
||||
_initialize_subplot(plt, sp)
|
||||
serieslist = series_list(sp)
|
||||
sp.plt = plt
|
||||
sp.attr[:subplot_index] = idx
|
||||
for series in serieslist
|
||||
push!(plt.series_list, series)
|
||||
_series_added(plt, series)
|
||||
end
|
||||
end
|
||||
|
||||
# finish up
|
||||
current(plt)
|
||||
if get(d, :show, default(:show))
|
||||
gui()
|
||||
end
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
@@ -73,206 +127,388 @@ end
|
||||
function plot!(plt::Plot, args...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
# merge!(plt.user_attr, d)
|
||||
_plot!(plt, d, args...)
|
||||
end
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
args = _apply_recipe(d, args...; kw...)
|
||||
function strip_first_letter(s::Symbol)
|
||||
str = string(s)
|
||||
str[1:1], Symbol(str[2:end])
|
||||
end
|
||||
|
||||
dumpdict(d, "After plot! preprocessing")
|
||||
warnOnUnsupportedArgs(plt.backend, d)
|
||||
|
||||
# just in case the backend needs to set up the plot (make it current or something)
|
||||
_before_add_series(plt)
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _apply_series_recipe(plt::Plot, d::KW)
|
||||
st = d[:seriestype]
|
||||
# @show st
|
||||
if st in supportedTypes()
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
# that might have been added during series recipes
|
||||
sp = d[:subplot]
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
_update_subplot_args(plt, sp, d, sp_idx)
|
||||
|
||||
# change to a 3d projection for this subplot?
|
||||
if is3d(st)
|
||||
sp.attr[:projection] = "3d"
|
||||
end
|
||||
|
||||
# initialize now that we know the first series type
|
||||
if !haskey(sp.attr, :init)
|
||||
_initialize_subplot(plt, sp)
|
||||
sp.attr[:init] = true
|
||||
end
|
||||
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
w, h = size(d[:z])
|
||||
expand_extrema!(sp[:xaxis], (0,w))
|
||||
expand_extrema!(sp[:yaxis], (0,h))
|
||||
sp[:yaxis].d[:flip] = true
|
||||
elseif !(st in (:pie, :histogram, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
end
|
||||
|
||||
|
||||
# add the series!
|
||||
warnOnUnsupportedArgs(plt.backend, d)
|
||||
warnOnUnsupported(plt.backend, d)
|
||||
series = Series(d)
|
||||
push!(plt.series_list, series)
|
||||
# @show series
|
||||
|
||||
_series_added(plt, series)
|
||||
|
||||
# # grouping
|
||||
groupby = if haskey(d, :group)
|
||||
extractGroupArgs(d[:group], args...)
|
||||
else
|
||||
nothing
|
||||
end
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = try
|
||||
RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
catch
|
||||
warn("Exception during apply_recipe(Val{$st}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))")
|
||||
rethrow()
|
||||
end
|
||||
|
||||
# merge plot args
|
||||
if !haskey(d, :subplot)
|
||||
for k in keys(_plotDefaults)
|
||||
if haskey(d, k)
|
||||
plt.plotargs[k] = d[k]
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
if isa(data, RecipeData)
|
||||
_apply_series_recipe(plt, data.d)
|
||||
else
|
||||
warn("Unhandled recipe: $(data)")
|
||||
break
|
||||
end
|
||||
end
|
||||
# merge!(plt.plotargs, d)
|
||||
handlePlotColors(plt.backend, plt.plotargs)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, d::KW, args...)
|
||||
# d = plt.user_attr
|
||||
d[:plot_object] = plt
|
||||
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
if haskey(d, :group)
|
||||
args = (extractGroupArgs(d[:group], args...), args...)
|
||||
end
|
||||
|
||||
_add_series(plt, d, groupby, args...)
|
||||
_add_annotations(plt, d)
|
||||
|
||||
warnOnUnsupportedScales(plt.backend, d)
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a queue of series that still need to be processed.
|
||||
# each pass through the loop, we pop one off and apply the recipe.
|
||||
# the recipe will return a list a Series objects... the ones that are
|
||||
# finished (no more args) get added to the kw_list, and the rest go into the queue
|
||||
# for processing.
|
||||
kw_list = KW[]
|
||||
still_to_process = isempty(args) ? [] : [RecipeData(copy(d), args)]
|
||||
while !isempty(still_to_process)
|
||||
|
||||
# grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes)
|
||||
next_series = shift!(still_to_process)
|
||||
for recipedata in RecipesBase.apply_recipe(next_series.d, next_series.args...)
|
||||
|
||||
# add title, axis labels, ticks, etc
|
||||
if !haskey(d, :subplot)
|
||||
# merge!(plt.plotargs, d)
|
||||
# handlePlotColors(plt.backend, plt.plotargs)
|
||||
dumpdict(plt.plotargs, "Updating plot items")
|
||||
_update_plot(plt, plt.plotargs)
|
||||
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
|
||||
if !(typeof(recipedata) <: RecipeData)
|
||||
error("Inputs couldn't be processed... expected RecipeData but got: $recipedata")
|
||||
end
|
||||
|
||||
if isempty(recipedata.args)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
kw = recipedata.d
|
||||
_add_markershape(kw)
|
||||
|
||||
# if there was a grouping, filter the data here
|
||||
_filter_input_data!(kw)
|
||||
|
||||
# map marker_z if it's a Function
|
||||
if isa(get(kw, :marker_z, nothing), Function)
|
||||
# TODO: should this take y and/or z as arguments?
|
||||
kw[:marker_z] = map(kw[:marker_z], kw[:x])
|
||||
end
|
||||
|
||||
# convert a ribbon into a fillrange
|
||||
if get(kw, :ribbon, nothing) != nothing
|
||||
make_fillrange_from_ribbon(kw)
|
||||
end
|
||||
|
||||
# add the plot index
|
||||
plt.n += 1
|
||||
kw[:series_plotindex] = plt.n
|
||||
|
||||
# check that the backend will support the command and add it to the list
|
||||
warnOnUnsupportedScales(plt.backend, kw)
|
||||
push!(kw_list, kw)
|
||||
|
||||
# handle error bars by creating new recipedata data... these will have
|
||||
# the same recipedata index as the recipedata they are copied from
|
||||
for esym in (:xerror, :yerror)
|
||||
if get(d, esym, nothing) != nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
errkw = copy(kw)
|
||||
errkw[:seriestype] = esym
|
||||
errkw[:label] = ""
|
||||
errkw[:primary] = false
|
||||
push!(kw_list, errkw)
|
||||
end
|
||||
end
|
||||
|
||||
# handle smoothing by adding a new series
|
||||
if get(d, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
sx = [minimum(x), maximum(x)]
|
||||
sy = β * sx + α
|
||||
push!(kw_list, merge(copy(kw), KW(
|
||||
:seriestype => :path,
|
||||
:x => sx,
|
||||
:y => sy,
|
||||
:fillrange => nothing,
|
||||
:label => "",
|
||||
:primary => false,
|
||||
)))
|
||||
|
||||
# don't allow something else to handle it
|
||||
d[:smooth] = false
|
||||
end
|
||||
|
||||
else
|
||||
# args are non-empty, so there's still processing to do... add it back to the queue
|
||||
push!(still_to_process, recipedata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
_update_plot_pos_size(plt, d)
|
||||
# merge in anything meant for plot/subplot
|
||||
for kw in kw_list
|
||||
for (k,v) in kw
|
||||
for defdict in (_plot_defaults, _subplot_defaults)
|
||||
if haskey(defdict, k)
|
||||
d[k] = pop!(kw, k)
|
||||
end
|
||||
end
|
||||
# if haskey(_plot_defaults, k) || haskey(_subplot_defaults, k)
|
||||
# d[k] = v
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
# TODO: init subplots here
|
||||
_update_plot_args(plt, d)
|
||||
if !plt.init
|
||||
plt.o = _create_backend_figure(plt)
|
||||
|
||||
# create the layout and subplots from the inputs
|
||||
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
sp.plt = plt
|
||||
sp.attr[:subplot_index] = idx
|
||||
end
|
||||
|
||||
plt.init = true
|
||||
end
|
||||
|
||||
# just in case the backend needs to set up the plot (make it current or something)
|
||||
_prepare_plot_object(plt)
|
||||
|
||||
# first apply any args for the subplots
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
_update_subplot_args(plt, sp, d, idx, remove_pair = false)
|
||||
end
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
|
||||
|
||||
# this is it folks!
|
||||
# TODO: we probably shouldn't use i for tracking series index, but rather explicitly track it in recipes
|
||||
for (i,kw) in enumerate(kw_list)
|
||||
# if !(get(kw, :seriestype, :none) in (:xerror, :yerror))
|
||||
# plt.n += 1
|
||||
# end
|
||||
command_idx = kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
|
||||
|
||||
# get the Subplot object to which the series belongs
|
||||
sp = get(kw, :subplot, :auto)
|
||||
sp = if sp == :auto
|
||||
mod1(i,length(plt.subplots))
|
||||
else
|
||||
slice_arg(sp, i)
|
||||
end
|
||||
sp = kw[:subplot] = get_subplot(plt, sp)
|
||||
idx = get_subplot_index(plt, sp)
|
||||
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
anns = annotations(pop!(kw, :series_annotations, []))
|
||||
if length(anns) > 0
|
||||
x, y = kw[:x], kw[:y]
|
||||
nx, ny, na = map(length, (x,y,anns))
|
||||
n = max(nx, ny, na)
|
||||
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
|
||||
end
|
||||
sp.attr[:annotations] = vcat(sp_anns, anns)
|
||||
|
||||
# we update subplot args in case something like the color palatte is part of the recipe
|
||||
_update_subplot_args(plt, sp, kw, idx)
|
||||
|
||||
# set default values, select from attribute cycles, and generally set the final attributes
|
||||
_add_defaults!(kw, plt, sp, command_idx)
|
||||
|
||||
# now we have a fully specified series, with colors chosen. we must recursively handle
|
||||
# series recipes, which dispatch on seriestype. If a backend does not natively support a seriestype,
|
||||
# we check for a recipe that will convert that series type into one made up of lower-level components.
|
||||
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
|
||||
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
|
||||
# be able to support step, bar, and histogram plots (and any recipes that use those components).
|
||||
_apply_series_recipe(plt, kw)
|
||||
end
|
||||
|
||||
# # everything is processed, time to compute the layout bounding boxes
|
||||
# _before_layout_calcs(plt)
|
||||
# w, h = plt.attr[:size]
|
||||
# plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
|
||||
# update_child_bboxes!(plt.layout)
|
||||
#
|
||||
# # TODO just need to pass plt... and we should do all non-series updates here
|
||||
# _update_plot_object(plt)
|
||||
|
||||
current(plt)
|
||||
|
||||
# note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
|
||||
# # do we want to show it?
|
||||
if haskey(d, :show) && d[:show]
|
||||
# if haskey(d, :show) && d[:show]
|
||||
if get(d, :show, default(:show))
|
||||
gui()
|
||||
end
|
||||
|
||||
plt
|
||||
end
|
||||
|
||||
# handle the grouping
|
||||
function _add_series(plt::Plot, d::KW, groupby::GroupBy, args...)
|
||||
starting_n = plt.n
|
||||
for (i, glab) in enumerate(groupby.groupLabels)
|
||||
tmpd = copy(d)
|
||||
tmpd[:numUncounted] = plt.n - starting_n
|
||||
_add_series(plt, tmpd, nothing, args...;
|
||||
idxfilter = groupby.groupIds[i],
|
||||
grouplabel = string(glab))
|
||||
|
||||
function _replace_linewidth(d::KW)
|
||||
# get a good default linewidth... 0 for surface and heatmaps
|
||||
if get(d, :linewidth, :auto) == :auto
|
||||
d[:linewidth] = (get(d, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
|
||||
end
|
||||
end
|
||||
|
||||
filter_data(v::AVec, idxfilter::AVec{Int}) = v[idxfilter]
|
||||
filter_data(v, idxfilter) = v
|
||||
# we're getting ready to display/output. prep for layout calcs, then update
|
||||
# the plot object after
|
||||
function prepare_output(plt::Plot)
|
||||
_before_layout_calcs(plt)
|
||||
|
||||
function filter_data!(d::KW, idxfilter)
|
||||
for s in (:x, :y, :z)
|
||||
d[s] = filter_data(get(d, s, nothing), idxfilter)
|
||||
end
|
||||
w, h = plt.attr[:size]
|
||||
plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
|
||||
update_child_bboxes!(plt.layout)
|
||||
|
||||
_update_plot_object(plt)
|
||||
end
|
||||
|
||||
# no grouping
|
||||
function _add_series(plt::Plot, d::KW, ::Void, args...;
|
||||
idxfilter = nothing,
|
||||
grouplabel = "")
|
||||
|
||||
# get the list of dictionaries, one per series
|
||||
dumpdict(d, "before process_inputs")
|
||||
process_inputs(plt, d, args...)
|
||||
dumpdict(d, "after process_inputs")
|
||||
|
||||
if idxfilter != nothing
|
||||
# add the group name as the label if there isn't one passed in
|
||||
get!(d, :label, grouplabel)
|
||||
# filter the data
|
||||
filter_data!(d, idxfilter)
|
||||
end
|
||||
|
||||
seriesArgList, xmeta, ymeta = build_series_args(plt, d) #, idxfilter)
|
||||
# seriesArgList, xmeta, ymeta = build_series_args(plt, groupargs..., args...; d...)
|
||||
|
||||
# if we were able to extract guide information from the series inputs, then update the plot
|
||||
# @show xmeta, ymeta
|
||||
updateDictWithMeta(d, plt.plotargs, xmeta, true)
|
||||
updateDictWithMeta(d, plt.plotargs, ymeta, false)
|
||||
|
||||
# now we can plot the series
|
||||
for (i,di) in enumerate(seriesArgList)
|
||||
plt.n += 1
|
||||
|
||||
if !stringsSupported() && di[:linetype] != :pie
|
||||
setTicksFromStringVector(d, di, :x, :xticks)
|
||||
setTicksFromStringVector(d, di, :y, :yticks)
|
||||
setTicksFromStringVector(d, di, :z, :zticks)
|
||||
end
|
||||
|
||||
# remove plot args
|
||||
for k in keys(_plotDefaults)
|
||||
delete!(di, k)
|
||||
end
|
||||
|
||||
# merge in plotarg_overrides
|
||||
plotarg_overrides = pop!(di, :plotarg_overrides, nothing)
|
||||
if plotarg_overrides != nothing
|
||||
merge!(plt.plotargs, plotarg_overrides)
|
||||
end
|
||||
# dumpdict(plt.plotargs, "pargs", true)
|
||||
|
||||
dumpdict(di, "Series $i")
|
||||
|
||||
_add_series(plt.backend, plt, di)
|
||||
end
|
||||
function prepared_object(plt::Plot)
|
||||
prepare_output(plt)
|
||||
plt.o
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# if x or y are a vector of strings, we should create a list of unique strings,
|
||||
# and map x/y to be the index of the string... then set the x/y tick labels
|
||||
function setTicksFromStringVector(d::KW, di::KW, sym::Symbol, ticksym::Symbol)
|
||||
# if the x or y values are strings, set ticks to the unique values, and x/y to the indices of the ticks
|
||||
# function get_indices(orig, labels)
|
||||
# Int[findnext(labels, l, 1) for l in orig]
|
||||
# end
|
||||
|
||||
v = di[sym]
|
||||
isa(v, AbstractArray) || return
|
||||
|
||||
T = eltype(v)
|
||||
if T <: @compat(AbstractString) || (!isempty(T.types) && all(x -> x <: @compat(AbstractString), T.types))
|
||||
ticks = unique(di[sym])
|
||||
di[sym] = Int[findnext(ticks, v, 1) for v in di[sym]]
|
||||
|
||||
if !haskey(d, ticksym) || d[ticksym] == :auto
|
||||
d[ticksym] = (collect(1:length(ticks)), UTF8String[t for t in ticks])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
_before_add_series(plt::Plot) = nothing
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# should we update the x/y label given the meta info during input slicing?
|
||||
function updateDictWithMeta(d::KW, plotargs::KW, meta::Symbol, isx::Bool)
|
||||
lsym = isx ? :xlabel : :ylabel
|
||||
if plotargs[lsym] == default(lsym)
|
||||
d[lsym] = string(meta)
|
||||
end
|
||||
end
|
||||
updateDictWithMeta(d::KW, plotargs::KW, meta, isx::Bool) = nothing
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
annotations(::@compat(Void)) = []
|
||||
annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v
|
||||
annotations{X,Y,V}(t::@compat(Tuple{X,Y,V})) = [t]
|
||||
annotations(v::AVec{PlotText}) = v
|
||||
annotations(v::AVec) = map(PlotText, v)
|
||||
annotations(anns) = error("Expecting a tuple (or vector of tuples) for annotations: ",
|
||||
"(x, y, annotation)\n got: $(typeof(anns))")
|
||||
|
||||
function _add_annotations(plt::Plot, d::KW)
|
||||
anns = annotations(get(d, :annotation, nothing))
|
||||
if !isempty(anns)
|
||||
|
||||
# if we just have a list of PlotText objects, then create (x,y,text) tuples
|
||||
if typeof(anns) <: AVec{PlotText}
|
||||
x, y = plt[plt.n]
|
||||
anns = Tuple{Float64,Float64,PlotText}[(x[i], y[i], t) for (i,t) in enumerate(anns)]
|
||||
end
|
||||
|
||||
_add_annotations(plt, anns)
|
||||
end
|
||||
end
|
||||
# # TODO: remove?? this is the old way of handling discrete data... should be
|
||||
# # replaced by the Axis type and logic
|
||||
# function setTicksFromStringVector(plt::Plot, d::KW, di::KW, letter)
|
||||
# sym = Symbol(letter)
|
||||
# ticksym = Symbol(letter * "ticks")
|
||||
# pargs = plt.attr
|
||||
# v = di[sym]
|
||||
#
|
||||
# # do we really want to do this?
|
||||
# typeof(v) <: AbstractArray || return
|
||||
# isempty(v) && return
|
||||
# trueOrAllTrue(_ -> typeof(_) <: AbstractString, v) || return
|
||||
#
|
||||
# # compute the ticks and labels
|
||||
# ticks, labels = if ticksType(pargs[ticksym]) == :ticks_and_labels
|
||||
# # extend the existing ticks and labels. only add to labels if they're new!
|
||||
# ticks, labels = pargs[ticksym]
|
||||
# newlabels = filter(_ -> !(_ in labels), unique(v))
|
||||
# newticks = if isempty(ticks)
|
||||
# collect(1:length(newlabels))
|
||||
# else
|
||||
# maximum(ticks) + collect(1:length(newlabels))
|
||||
# end
|
||||
# ticks = vcat(ticks, newticks)
|
||||
# labels = vcat(labels, newlabels)
|
||||
# ticks, labels
|
||||
# else
|
||||
# # create new ticks and labels
|
||||
# newlabels = unique(v)
|
||||
# collect(1:length(newlabels)), newlabels
|
||||
# end
|
||||
#
|
||||
# d[ticksym] = ticks, labels
|
||||
# plt.attr[ticksym] = ticks, labels
|
||||
#
|
||||
# # add an origsym field so that later on we can re-compute the x vector if ticks change
|
||||
# origsym = Symbol(letter * "orig")
|
||||
# di[origsym] = v
|
||||
# di[sym] = get_indices(v, labels)
|
||||
#
|
||||
# # loop through existing plt.seriesargs and adjust indices if there is an origsym key
|
||||
# for sargs in plt.seriesargs
|
||||
# if haskey(sargs, origsym)
|
||||
# # TODO: might need to call the setindex function instead to trigger a plot update for some backends??
|
||||
# sargs[sym] = get_indices(sargs[origsym], labels)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
function Base.copy(plt::Plot)
|
||||
backend(plt.backend)
|
||||
plt2 = plot(; plt.plotargs...)
|
||||
for sargs in plt.seriesargs
|
||||
sargs = filter((k,v) -> haskey(_seriesDefaults,k), sargs)
|
||||
plot!(plt2; sargs...)
|
||||
end
|
||||
plt2
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# function Base.copy(plt::Plot)
|
||||
# backend(plt.backend)
|
||||
# plt2 = plot(; plt.attr...)
|
||||
# for sargs in plt.seriesargs
|
||||
# sargs = filter((k,v) -> haskey(_series_defaults,k), sargs)
|
||||
# plot!(plt2; sargs...)
|
||||
# end
|
||||
# plt2
|
||||
# end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
+687
-153
@@ -5,6 +5,59 @@
|
||||
# a recipe should fully describe the plotting command(s) and call them, likewise for updating.
|
||||
# actually... maybe those should explicitly derive from AbstractPlot???
|
||||
|
||||
|
||||
"""
|
||||
You can easily define your own plotting recipes with convenience methods:
|
||||
|
||||
```
|
||||
@userplot type GroupHist
|
||||
args
|
||||
end
|
||||
|
||||
@recipe function f(gh::GroupHist)
|
||||
# set some attributes, add some series, using gh.args as input
|
||||
end
|
||||
|
||||
# now you can plot like:
|
||||
grouphist(rand(1000,4))
|
||||
```
|
||||
"""
|
||||
macro userplot(expr)
|
||||
_userplot(expr)
|
||||
end
|
||||
|
||||
function _userplot(expr::Expr)
|
||||
if expr.head != :type
|
||||
errror("Must call userplot on a type/immutable expression. Got: $expr")
|
||||
end
|
||||
|
||||
typename = expr.args[2]
|
||||
funcname = Symbol(lowercase(string(typename)))
|
||||
funcname2 = Symbol(funcname, "!")
|
||||
|
||||
# return a code block with the type definition and convenience plotting methods
|
||||
esc(quote
|
||||
$expr
|
||||
export $funcname, $funcname2
|
||||
$funcname(args...; kw...) = plot($typename(args); kw...)
|
||||
$funcname2(args...; kw...) = plot!($typename(args); kw...)
|
||||
end)
|
||||
end
|
||||
|
||||
function _userplot(sym::Symbol)
|
||||
_userplot(:(type $sym
|
||||
args
|
||||
end))
|
||||
end
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
abstract PlotRecipe
|
||||
|
||||
getRecipeXY(recipe::PlotRecipe) = Float64[], Float64[]
|
||||
@@ -17,57 +70,452 @@ plot!(plt::Plot, recipe::PlotRecipe, args...; kw...) = plot!(getRecipeXY(recipe)
|
||||
num_series(x::AMat) = size(x,2)
|
||||
num_series(x) = 1
|
||||
|
||||
_apply_recipe(d::KW; kw...) = ()
|
||||
|
||||
# if it's not a recipe, just do nothing and return the args
|
||||
function _apply_recipe(d::KW, args...; issubplot=false, kw...)
|
||||
if issubplot && !haskey(d, :n) && !haskey(d, :layout)
|
||||
# put in a sensible default
|
||||
d[:n] = maximum(map(num_series, args))
|
||||
# # if it's not a recipe, just do nothing and return the args
|
||||
# function RecipesBase.apply_recipe(d::KW, args...; issubplot=false)
|
||||
# if issubplot && !isempty(args) && !haskey(d, :n) && !haskey(d, :layout)
|
||||
# # put in a sensible default
|
||||
# d[:n] = maximum(map(num_series, args))
|
||||
# end
|
||||
# args
|
||||
# end
|
||||
|
||||
|
||||
if is_installed("DataFrames")
|
||||
@eval begin
|
||||
import DataFrames
|
||||
DFS = Union{Symbol, AbstractArray{Symbol}}
|
||||
|
||||
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS)
|
||||
if isa(dfs, Symbol)
|
||||
get!(d, Symbol(letter * "guide"), string(dfs))
|
||||
collect(df[dfs])
|
||||
else
|
||||
get!(d, :label, reshape(dfs, 1, length(dfs)))
|
||||
Any[collect(df[s]) for s in dfs]
|
||||
end
|
||||
end
|
||||
|
||||
function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
||||
extractGroupArgs(collect(df[group]))
|
||||
end
|
||||
|
||||
|
||||
function handle_group(df::DataFrames.AbstractDataFrame, d::KW)
|
||||
if haskey(d, :group)
|
||||
g = d[:group]
|
||||
if isa(g, Symbol)
|
||||
d[:group] = collect(df[g])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@recipe function f(df::DataFrames.AbstractDataFrame, sy::DFS)
|
||||
handle_group(df, d)
|
||||
handle_dfs(df, d, "y", sy)
|
||||
end
|
||||
|
||||
@recipe function f(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS)
|
||||
handle_group(df, d)
|
||||
x = handle_dfs(df, d, "x", sx)
|
||||
y = handle_dfs(df, d, "y", sy)
|
||||
x, y
|
||||
end
|
||||
|
||||
@recipe function f(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS)
|
||||
handle_group(df, d)
|
||||
x = handle_dfs(df, d, "x", sx)
|
||||
y = handle_dfs(df, d, "y", sy)
|
||||
z = handle_dfs(df, d, "z", sz)
|
||||
x, y, z
|
||||
end
|
||||
end
|
||||
args
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
`apply_series_recipe` should take a processed series KW dict and break it up
|
||||
into component parts. For example, a box plot is made up of `shape` for the
|
||||
boxes, `path` for the lines, and `scatter` for the outliers.
|
||||
# """
|
||||
# `apply_series_recipe` should take a processed series KW dict and break it up
|
||||
# into component parts. For example, a box plot is made up of `shape` for the
|
||||
# boxes, `path` for the lines, and `scatter` for the outliers.
|
||||
#
|
||||
# Returns a Vector{KW}.
|
||||
# """
|
||||
# apply_series_recipe(d::KW, st) = KW[d]
|
||||
|
||||
Returns a Vector{KW}.
|
||||
"""
|
||||
apply_series_recipe(d::KW, lt) = KW[d]
|
||||
|
||||
# for seriestype `line`, need to sort by x values
|
||||
@recipe function f(::Type{Val{:line}}, x, y, z)
|
||||
indices = sortperm(x)
|
||||
x := x[indices]
|
||||
y := y[indices]
|
||||
if typeof(z) <: AVec
|
||||
z := z[indices]
|
||||
end
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:sticks}}, x, y, z)
|
||||
nx = length(x)
|
||||
n = 3nx
|
||||
newx, newy = zeros(n), zeros(n)
|
||||
for i=1:nx
|
||||
rng = 3i-2:3i
|
||||
newx[rng] = x[i]
|
||||
newy[rng] = [0., y[i], 0.]
|
||||
end
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:hline}}, x, y, z)
|
||||
xmin, xmax = axis_limits(d[:subplot][:xaxis])
|
||||
n = length(y)
|
||||
newx = repmat(Float64[xmin, xmax, NaN], n)
|
||||
newy = vec(Float64[yi for i=1:3,yi=y])
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
|
||||
@recipe function f(::Type{Val{:vline}}, x, y, z)
|
||||
ymin, ymax = axis_limits(d[:subplot][:yaxis])
|
||||
n = length(y)
|
||||
newx = vec(Float64[yi for i=1:3,yi=y])
|
||||
newy = repmat(Float64[ymin, ymax, NaN], n)
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# steps
|
||||
|
||||
function make_steps(x, y, st)
|
||||
n = length(x)
|
||||
newx, newy = zeros(2n-1), zeros(2n-1)
|
||||
for i=1:n
|
||||
idx = 2i-1
|
||||
newx[idx] = x[i]
|
||||
newy[idx] = y[i]
|
||||
if i > 1
|
||||
newx[idx-1] = x[st == :steppre ? i-1 : i]
|
||||
newy[idx-1] = y[st == :steppre ? i : i-1]
|
||||
end
|
||||
end
|
||||
newx, newy
|
||||
end
|
||||
|
||||
# create a path from steps
|
||||
@recipe function f(::Type{Val{:steppre}}, x, y, z)
|
||||
d[:x], d[:y] = make_steps(x, y, :steppre)
|
||||
seriestype := :path
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := x
|
||||
y := y
|
||||
label := ""
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
markershape := :none
|
||||
end
|
||||
()
|
||||
end
|
||||
|
||||
# create a path from steps
|
||||
@recipe function f(::Type{Val{:steppost}}, x, y, z)
|
||||
d[:x], d[:y] = make_steps(x, y, :steppost)
|
||||
seriestype := :path
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := x
|
||||
y := y
|
||||
label := ""
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
markershape := :none
|
||||
end
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# sticks
|
||||
|
||||
sticks_fillfrom(fr::Void, i::Integer) = 0.0
|
||||
sticks_fillfrom(fr::Number, i::Integer) = fr
|
||||
sticks_fillfrom(fr::AVec, i::Integer) = fr[mod1(i, length(fr))]
|
||||
|
||||
# create vertical line segments from fill
|
||||
@recipe function f(::Type{Val{:sticks}}, x, y, z)
|
||||
n = length(x)
|
||||
fr = d[:fillrange]
|
||||
newx, newy = zeros(3n), zeros(3n)
|
||||
for i=1:n
|
||||
rng = 3i-2:3i
|
||||
newx[rng] = [x[i], x[i], NaN]
|
||||
newy[rng] = [sticks_fillfrom(fr,i), y[i], NaN]
|
||||
end
|
||||
x := newx
|
||||
y := newy
|
||||
fillrange := nothing
|
||||
seriestype := :path
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := x
|
||||
y := y
|
||||
label := ""
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
markershape := :none
|
||||
end
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
nx, ny = length(x), length(y)
|
||||
edges = if nx == ny
|
||||
# x is centers, calc the edges
|
||||
# TODO: use bar_width, etc
|
||||
midpoints = x
|
||||
halfwidths = diff(midpoints) * 0.5
|
||||
Float64[if i == 1
|
||||
midpoints[1] - halfwidths[1]
|
||||
elseif i == ny+1
|
||||
midpoints[i-1] + halfwidths[i-2]
|
||||
else
|
||||
midpoints[i-1] + halfwidths[i-1]
|
||||
end for i=1:ny+1]
|
||||
elseif nx == ny + 1
|
||||
# x is edges
|
||||
x
|
||||
else
|
||||
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
fillto = d[:fillrange]
|
||||
if fillto == nothing
|
||||
fillto = zeros(1)
|
||||
elseif isa(fillto, Number)
|
||||
fillto = Float64[fillto]
|
||||
end
|
||||
nf = length(fillto)
|
||||
|
||||
npts = 3ny + 1
|
||||
heights = y
|
||||
x = zeros(npts)
|
||||
y = zeros(npts)
|
||||
fillrng = zeros(npts)
|
||||
|
||||
# create the path in triplets. after the first bottom-left coord of the first bar:
|
||||
# add the top-left, top-right, and bottom-right coords for each height
|
||||
x[1] = edges[1]
|
||||
y[1] = fillto[1]
|
||||
fillrng[1] = fillto[1]
|
||||
for i=1:ny
|
||||
idx = 3i
|
||||
rng = idx-1:idx+1
|
||||
fi = fillto[mod1(i,nf)]
|
||||
x[rng] = [edges[i], edges[i+1], edges[i+1]]
|
||||
y[rng] = [heights[i], heights[i], fi]
|
||||
fillrng[rng] = [fi, fi, fi]
|
||||
end
|
||||
|
||||
x := x
|
||||
y := y
|
||||
fillrange := fillrng
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
|
||||
# edges from number of bins
|
||||
function calc_edges(v, bins::Integer)
|
||||
vmin, vmax = extrema(v)
|
||||
linspace(vmin, vmax, bins+1)
|
||||
end
|
||||
|
||||
# just pass through arrays
|
||||
calc_edges(v, bins::AVec) = v
|
||||
|
||||
# find the bucket index of this value
|
||||
function bucket_index(vi, edges)
|
||||
for (i,e) in enumerate(edges)
|
||||
if vi <= e
|
||||
return max(1,i-1)
|
||||
end
|
||||
end
|
||||
return length(edges)-1
|
||||
end
|
||||
|
||||
function my_hist(v, bins; normed = false, weights = nothing)
|
||||
edges = calc_edges(v, bins)
|
||||
counts = zeros(length(edges)-1)
|
||||
|
||||
# add a weighted count
|
||||
for (i,vi) in enumerate(v)
|
||||
idx = bucket_index(vi, edges)
|
||||
counts[idx] += (weights == nothing ? 1.0 : weights[i])
|
||||
end
|
||||
|
||||
# normalize by bar area?
|
||||
norm_denom = normed ? sum(diff(edges) .* counts) : 1.0
|
||||
if norm_denom == 0
|
||||
norm_denom = 1.0
|
||||
end
|
||||
|
||||
edges, counts ./ norm_denom
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram}}, x, y, z)
|
||||
edges, counts = my_hist(y, d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights])
|
||||
x := edges
|
||||
y := counts
|
||||
seriestype := :bar
|
||||
()
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histogram 2D
|
||||
|
||||
# if tuple, map out bins, otherwise use the same for both
|
||||
calc_edges_2d(x, y, bins) = calc_edges(x, bins), calc_edges(y, bins)
|
||||
calc_edges_2d{X,Y}(x, y, bins::Tuple{X,Y}) = calc_edges(x, bins[1]), calc_edges(y, bins[2])
|
||||
|
||||
# the 2D version
|
||||
function my_hist_2d(x, y, bins; normed = false, weights = nothing)
|
||||
xedges, yedges = calc_edges_2d(x, y, bins)
|
||||
counts = zeros(length(yedges)-1, length(xedges)-1)
|
||||
|
||||
# add a weighted count
|
||||
for i=1:length(x)
|
||||
r = bucket_index(y[i], yedges)
|
||||
c = bucket_index(x[i], xedges)
|
||||
counts[r,c] += (weights == nothing ? 1.0 : weights[i])
|
||||
end
|
||||
|
||||
# normalize to cubic area of the imaginary surface towers
|
||||
norm_denom = normed ? sum((diff(yedges) * diff(xedges)') .* counts) : 1.0
|
||||
if norm_denom == 0
|
||||
norm_denom = 1.0
|
||||
end
|
||||
|
||||
xedges, yedges, counts ./ norm_denom
|
||||
end
|
||||
|
||||
centers(v::AVec) = v[1] + cumsum(diff(v))
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights])
|
||||
x := centers(xedges)
|
||||
y := centers(yedges)
|
||||
z := Surface(counts)
|
||||
seriestype := :heatmap
|
||||
()
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Box Plot
|
||||
|
||||
const _box_halfwidth = 0.4
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
||||
# dumpdict(d, "box before", true)
|
||||
# TODO: add scatter series with outliers
|
||||
notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
||||
@recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
|
||||
# Plots.dumpdict(d, "box before", true)
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
d[:linetype] = :shape
|
||||
groupby = extractGroupArgs(d[:x])
|
||||
groupby = extractGroupArgs(x)
|
||||
outliers_y = Float64[]
|
||||
outliers_x = Float64[]
|
||||
|
||||
warning = false
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
|
||||
# filter y values, then compute quantiles
|
||||
q1,q2,q3,q4,q5 = quantile(d[:y][groupby.groupIds[i]], linspace(0,1,5))
|
||||
# filter y values
|
||||
values = d[:y][groupby.groupIds[i]]
|
||||
# then compute quantiles
|
||||
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
||||
# notch
|
||||
n = notch_width(q2, q4, length(values))
|
||||
|
||||
if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
||||
warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||
warning = true # Show the warning only one time
|
||||
end
|
||||
|
||||
# make the shape
|
||||
l, m, r = i - _box_halfwidth, i, i + _box_halfwidth
|
||||
xcoords = [
|
||||
center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
l, m, r = center - _box_halfwidth, center, center + _box_halfwidth
|
||||
# internal nodes for notches
|
||||
L, R = center - 0.5 * _box_halfwidth, center + 0.5 * _box_halfwidth
|
||||
# outliers
|
||||
if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
|
||||
limit = range*(q4-q2)
|
||||
inside = Float64[]
|
||||
for value in values
|
||||
if (value < (q2 - limit)) || (value > (q4 + limit))
|
||||
push!(outliers_y, value)
|
||||
push!(outliers_x, center)
|
||||
else
|
||||
push!(inside, value)
|
||||
end
|
||||
end
|
||||
# change q1 and q5 to show outliers
|
||||
# using maximum and minimum values inside the limits
|
||||
q1, q5 = extrema(inside)
|
||||
end
|
||||
# Box
|
||||
xcoords = notch::Bool ? [
|
||||
m, l, r, m, m, NaN, # lower T
|
||||
l, l, L, R, r, r, l, NaN, # lower box
|
||||
l, l, L, R, r, r, l, NaN, # upper box
|
||||
m, l, r, m, m, NaN, # upper T
|
||||
] : [
|
||||
m, l, r, m, m, NaN, # lower T
|
||||
l, l, r, r, l, NaN, # lower box
|
||||
l, l, r, r, l, NaN, # upper box
|
||||
m, l, r, m, m # upper T
|
||||
m, l, r, m, m, NaN, # upper T
|
||||
]
|
||||
ycoords = [
|
||||
ycoords = notch::Bool ? [
|
||||
q1, q1, q1, q1, q2, NaN, # lower T
|
||||
q2, q3-n, q3, q3, q3-n, q2, q2, NaN, # lower box
|
||||
q4, q3+n, q3, q3, q3+n, q4, q4, NaN, # upper box
|
||||
q5, q5, q5, q5, q4, NaN, # upper T
|
||||
] : [
|
||||
q1, q1, q1, q1, q2, NaN, # lower T
|
||||
q2, q3, q3, q2, q2, NaN, # lower box
|
||||
q4, q3, q3, q4, q4, NaN, # upper box
|
||||
@@ -76,10 +524,33 @@ function apply_series_recipe(d::KW, ::Type{Val{:box}})
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
end
|
||||
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
|
||||
KW[d]
|
||||
seriestype := :shape
|
||||
# n = length(groupby.groupLabels)
|
||||
# xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
|
||||
|
||||
# clean d
|
||||
pop!(d, :notch)
|
||||
pop!(d, :range)
|
||||
|
||||
# we want to set the fields directly inside series recipes... args are ignored
|
||||
d[:x], d[:y] = Plots.shape_coords(shapes)
|
||||
|
||||
# Outliers
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
markershape := :ellipse
|
||||
x := outliers_x
|
||||
y := outliers_y
|
||||
label := ""
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
|
||||
() # expects a tuple returned
|
||||
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -87,19 +558,15 @@ end
|
||||
|
||||
# if the user has KernelDensity installed, use this for violin plots.
|
||||
# otherwise, just use a histogram
|
||||
try
|
||||
Pkg.installed("KernelDensity")
|
||||
import KernelDensity
|
||||
|
||||
# warn("using KD for violin")
|
||||
@eval function violin_coords(y)
|
||||
kd = KernelDensity.kde(y, npoints = 30)
|
||||
if is_installed("KernelDensity")
|
||||
@eval import KernelDensity
|
||||
@eval function violin_coords(y, bins = 30)
|
||||
kd = KernelDensity.kde(y, npoints = isa(bins, Integer) ? bins : 30)
|
||||
kd.density, kd.x
|
||||
end
|
||||
catch
|
||||
# warn("using hist for violin")
|
||||
@eval function violin_coords(y)
|
||||
edges, widths = hist(y, 20)
|
||||
else
|
||||
@eval function violin_coords(y, bins = 30)
|
||||
edges, widths = hist(y, isa(bins, Integer) ? bins : 30)
|
||||
centers = 0.5 * (edges[1:end-1] + edges[2:end])
|
||||
ymin, ymax = extrema(y)
|
||||
vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
|
||||
@@ -107,13 +574,13 @@ catch
|
||||
end
|
||||
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
@recipe function f(::Type{Val{:violin}}, x, y, z)
|
||||
# dumpdict(d, "box before", true)
|
||||
# TODO: add scatter series with outliers
|
||||
|
||||
# create a list of shapes, where each shape is a single boxplot
|
||||
shapes = Shape[]
|
||||
d[:linetype] = :shape
|
||||
groupby = extractGroupArgs(d[:x])
|
||||
|
||||
for (i, glabel) in enumerate(groupby.groupLabels)
|
||||
@@ -126,22 +593,44 @@ function apply_series_recipe(d::KW, ::Type{Val{:violin}})
|
||||
widths = _box_halfwidth * widths / maximum(widths)
|
||||
|
||||
# make the violin
|
||||
xcoords = vcat(widths, -reverse(widths)) + i
|
||||
xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
xcoords = vcat(widths, -reverse(widths)) + xcenter
|
||||
ycoords = vcat(centers, reverse(centers))
|
||||
push!(shapes, Shape(xcoords, ycoords))
|
||||
end
|
||||
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
# d[:plotarg_overrides] = KW(:xticks => (1:length(shapes), groupby.groupLabels))
|
||||
seriestype := :shape
|
||||
# n = length(groupby.groupLabels)
|
||||
# xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
|
||||
|
||||
KW[d]
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
()
|
||||
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# density
|
||||
|
||||
@recipe function f(::Type{Val{:density}}, x, y, z)
|
||||
newx, newy = violin_coords(y, d[:bins])
|
||||
if isvertical(d)
|
||||
newx, newy = newy, newx
|
||||
end
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Error Bars
|
||||
|
||||
function error_style!(d::KW)
|
||||
d[:linetype] = :path
|
||||
d[:seriestype] = :path
|
||||
d[:linecolor] = d[:markerstrokecolor]
|
||||
d[:linewidth] = d[:markerstrokewidth]
|
||||
d[:label] = ""
|
||||
@@ -180,18 +669,18 @@ end
|
||||
|
||||
# we will create a series of path segments, where each point represents one
|
||||
# side of an errorbar
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:yerror}})
|
||||
@recipe function f(::Type{Val{:yerror}}, x, y, z)
|
||||
error_style!(d)
|
||||
d[:markershape] = :hline
|
||||
markershape := :hline
|
||||
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
|
||||
KW[d]
|
||||
()
|
||||
end
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:xerror}})
|
||||
@recipe function f(::Type{Val{:xerror}}, x, y, z)
|
||||
error_style!(d)
|
||||
d[:markershape] = :vline
|
||||
markershape := :vline
|
||||
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
|
||||
KW[d]
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
@@ -199,71 +688,48 @@ end
|
||||
# quiver
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
# d[:label] = ""
|
||||
# d[:linetype] = :scatter
|
||||
#
|
||||
# # create a second series to draw the arrow shaft
|
||||
# dpath = copy(d)
|
||||
# error_style!(dpath)
|
||||
# dpath[:markershape] = :none
|
||||
#
|
||||
# velocity = error_zipit(d[:quiver])
|
||||
# xorig, yorig = d[:x], d[:y]
|
||||
#
|
||||
# # for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
# # x, y = zeros(0), zeros(0)
|
||||
# paths = P2[]
|
||||
# arrows = P2[]
|
||||
# arrowshapes = Shape[]
|
||||
# for i = 1:max(length(xorig), length(yorig))
|
||||
#
|
||||
# # get the starting position
|
||||
# xi = get_mod(xorig, i)
|
||||
# yi = get_mod(yorig, i)
|
||||
# p = P2(xi, yi)
|
||||
#
|
||||
# # get the velocity
|
||||
# vi = get_mod(velocity, i)
|
||||
# vx, vy = if istuple(vi)
|
||||
# first(vi), last(vi)
|
||||
# elseif isscalar(vi)
|
||||
# vi, vi
|
||||
# else
|
||||
# error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
# end
|
||||
# v = P2(vx, vy)
|
||||
#
|
||||
# nanappend!(paths, [p, p+v])
|
||||
# push!(arrows, p+v)
|
||||
# push!(arrowshapes, makearrowhead(compute_angle(v)))
|
||||
#
|
||||
# # # dist = sqrt(vx^2 + vy^2)
|
||||
# # dist = norm(v)
|
||||
# # arrow_h = 0.1dist # height of arrowhead
|
||||
# # arrow_w = 0.5arrow_h # halfwidth of arrowhead
|
||||
# # U1 = v ./ dist # vector of arrowhead height
|
||||
# # U2 = P2(-U1[2], U1[1]) # vector of arrowhead halfwidth
|
||||
# # U1 *= arrow_h
|
||||
# # U2 *= arrow_w
|
||||
# #
|
||||
# # append!(pts, P2(xi, yi) .+ P2[(0,0), v-U1, v-U1+U2, v, v-U1-U2, v-U1, (NaN,NaN)])
|
||||
# # # a1 = v - arrow_h * U1 + arrow_w * U2
|
||||
# # # a2 = v - arrow_h * U1 - arrow_w * U2
|
||||
# # # nanappend!(x, xi + [0.0, vx, a1[1], a2[1], vx])
|
||||
# # # nanappend!(y, yi + [0.0, vy, a1[2], a2[2], vy])
|
||||
# end
|
||||
#
|
||||
# # d[:x], d[:y] = Plots.unzip(pts)
|
||||
# dpath[:x], dpath[:y] = Plots.unzip(paths)
|
||||
# d[:x], d[:y] = Plots.unzip(arrows)
|
||||
# d[:markershape] = arrowshapes
|
||||
#
|
||||
# KW[dpath, d]
|
||||
# end
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_arrows(d::KW)
|
||||
d[:label] = ""
|
||||
d[:linetype] = :shape
|
||||
d[:seriestype] = :path
|
||||
if !isa(d[:arrow], Arrow)
|
||||
d[:arrow] = arrow()
|
||||
end
|
||||
|
||||
velocity = error_zipit(d[:quiver])
|
||||
xorig, yorig = d[:x], d[:y]
|
||||
|
||||
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
|
||||
x, y = zeros(0), zeros(0)
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
# get the starting position
|
||||
xi = get_mod(xorig, i)
|
||||
yi = get_mod(yorig, i)
|
||||
|
||||
# get the velocity
|
||||
vi = get_mod(velocity, i)
|
||||
vx, vy = if istuple(vi)
|
||||
first(vi), last(vi)
|
||||
elseif isscalar(vi)
|
||||
vi, vi
|
||||
elseif isa(vi,Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
end
|
||||
|
||||
# add the points
|
||||
nanappend!(x, [xi, xi+vx, NaN])
|
||||
nanappend!(y, [yi, yi+vy, NaN])
|
||||
end
|
||||
|
||||
d[:x], d[:y] = x, y
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
function quiver_using_hack(d::KW)
|
||||
label := ""
|
||||
seriestype := :shape
|
||||
|
||||
velocity = error_zipit(d[:quiver])
|
||||
xorig, yorig = d[:x], d[:y]
|
||||
@@ -303,59 +769,127 @@ function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
end
|
||||
|
||||
d[:x], d[:y] = Plots.unzip(pts[2:end])
|
||||
KW[d]
|
||||
# KW[d]
|
||||
end
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
@recipe function f(::Type{Val{:quiver}}, x, y, z)
|
||||
if :arrow in supportedArgs()
|
||||
quiver_using_arrows(d)
|
||||
else
|
||||
quiver_using_hack(d)
|
||||
end
|
||||
()
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function rotate(x::Real, y::Real, θ::Real; center = (0,0))
|
||||
cx = x - center[1]
|
||||
cy = y - center[2]
|
||||
xrot = cx * cos(θ) - cy * sin(θ)
|
||||
yrot = cy * cos(θ) + cx * sin(θ)
|
||||
xrot + center[1], yrot + center[2]
|
||||
# function rotate(x::Real, y::Real, θ::Real; center = (0,0))
|
||||
# cx = x - center[1]
|
||||
# cy = y - center[2]
|
||||
# xrot = cx * cos(θ) - cy * sin(θ)
|
||||
# yrot = cy * cos(θ) + cx * sin(θ)
|
||||
# xrot + center[1], yrot + center[2]
|
||||
# end
|
||||
#
|
||||
# # ---------------------------------------------------------------------------
|
||||
#
|
||||
# type EllipseRecipe <: PlotRecipe
|
||||
# w::Float64
|
||||
# h::Float64
|
||||
# x::Float64
|
||||
# y::Float64
|
||||
# θ::Float64
|
||||
# end
|
||||
# EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
|
||||
#
|
||||
# # return x,y coords of a rotated ellipse, centered at the origin
|
||||
# function rotatedEllipse(w, h, x, y, θ, rotθ)
|
||||
# # # coord before rotation
|
||||
# xpre = w * cos(θ)
|
||||
# ypre = h * sin(θ)
|
||||
#
|
||||
# # rotate and translate
|
||||
# r = rotate(xpre, ypre, rotθ)
|
||||
# x + r[1], y + r[2]
|
||||
# end
|
||||
#
|
||||
# function getRecipeXY(ep::EllipseRecipe)
|
||||
# x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
|
||||
# top = rotate(0, ep.h, ep.θ)
|
||||
# right = rotate(ep.w, 0, ep.θ)
|
||||
# linex = Float64[top[1], 0, right[1]] + ep.x
|
||||
# liney = Float64[top[2], 0, right[2]] + ep.y
|
||||
# Any[x, linex], Any[y, liney]
|
||||
# end
|
||||
#
|
||||
# function getRecipeArgs(ep::EllipseRecipe)
|
||||
# [(:line, (3, [:dot :solid], [:red :blue], :path))]
|
||||
# end
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
# TODO: this should really be in another package...
|
||||
type OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
low::T
|
||||
close::T
|
||||
end
|
||||
Base.convert(::Type{OHLC}, tup::Tuple) = OHLC(tup...)
|
||||
# Base.tuple(ohlc::OHLC) = (ohlc.open, ohlc.high, ohlc.low, ohlc.close)
|
||||
|
||||
# get one OHLC path
|
||||
function get_xy(o::OHLC, x, xdiff)
|
||||
xl, xm, xr = x-xdiff, x, x+xdiff
|
||||
ox = [xl, xm, NaN,
|
||||
xm, xm, NaN,
|
||||
xm, xr]
|
||||
oy = [o.open, o.open, NaN,
|
||||
o.low, o.high, NaN,
|
||||
o.close, o.close]
|
||||
ox, oy
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
type EllipseRecipe <: PlotRecipe
|
||||
w::Float64
|
||||
h::Float64
|
||||
x::Float64
|
||||
y::Float64
|
||||
θ::Float64
|
||||
end
|
||||
EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
|
||||
|
||||
# return x,y coords of a rotated ellipse, centered at the origin
|
||||
function rotatedEllipse(w, h, x, y, θ, rotθ)
|
||||
# # coord before rotation
|
||||
xpre = w * cos(θ)
|
||||
ypre = h * sin(θ)
|
||||
|
||||
# rotate and translate
|
||||
r = rotate(xpre, ypre, rotθ)
|
||||
x + r[1], y + r[2]
|
||||
# get the joined vector
|
||||
function get_xy(v::AVec{OHLC}, x = 1:length(v))
|
||||
xdiff = 0.3mean(abs(diff(x)))
|
||||
x_out, y_out = zeros(0), zeros(0)
|
||||
for (i,ohlc) in enumerate(v)
|
||||
ox,oy = get_xy(ohlc, x[i], xdiff)
|
||||
nanappend!(x_out, ox)
|
||||
nanappend!(y_out, oy)
|
||||
end
|
||||
x_out, y_out
|
||||
end
|
||||
|
||||
function getRecipeXY(ep::EllipseRecipe)
|
||||
x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
|
||||
top = rotate(0, ep.h, ep.θ)
|
||||
right = rotate(ep.w, 0, ep.θ)
|
||||
linex = Float64[top[1], 0, right[1]] + ep.x
|
||||
liney = Float64[top[2], 0, right[2]] + ep.y
|
||||
Any[x, linex], Any[y, liney]
|
||||
# these are for passing in a vector of OHLC objects
|
||||
# TODO: when I allow `@recipe f(::Type{T}, v::T) = ...` definitions to replace convertToAnyVector,
|
||||
# then I should replace these with one definition to convert to a vector of 4-tuples
|
||||
|
||||
# to squash ambiguity warnings...
|
||||
@recipe f(x::AVec{Function}, v::AVec{OHLC}) = error()
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec{Function}, v::AVec{Tuple{R1,R2,R3,R4}}) = error()
|
||||
|
||||
# this must be OHLC?
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(x::AVec, ohlc::AVec{Tuple{R1,R2,R3,R4}}) = x, OHLC[OHLC(t...) for t in ohlc]
|
||||
|
||||
@recipe function f(x::AVec, v::AVec{OHLC})
|
||||
seriestype := :path
|
||||
get_xy(v, x)
|
||||
end
|
||||
|
||||
function getRecipeArgs(ep::EllipseRecipe)
|
||||
[(:line, (3, [:dot :solid], [:red :blue], :path))]
|
||||
@recipe function f(v::AVec{OHLC})
|
||||
seriestype := :path
|
||||
get_xy(v)
|
||||
end
|
||||
|
||||
# # -------------------------------------------------
|
||||
# the series recipe, when passed vectors of 4-tuples
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
|
||||
"Sparsity plot... heatmap of non-zero values of a matrix"
|
||||
|
||||
+448
-365
@@ -7,7 +7,7 @@
|
||||
|
||||
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(lt -> lt in (:contour, :heatmap, :surface, :wireframe, :contour3d), get(d, :linetype, :none))
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :heatmap, :surface, :wireframe, :contour3d), get(d, :seriestype, :none))
|
||||
|
||||
# missing
|
||||
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
|
||||
@@ -44,8 +44,8 @@ convertToAnyVector(f::Function, d::KW) = Any[f], nothing
|
||||
# surface
|
||||
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
|
||||
|
||||
# vector of OHLC
|
||||
convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
# # vector of OHLC
|
||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
|
||||
# dates
|
||||
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
||||
@@ -62,6 +62,12 @@ function convertToAnyVector(v::AVec, d::KW)
|
||||
end
|
||||
end
|
||||
|
||||
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
|
||||
|
||||
|
||||
function convertToAnyVector(args...)
|
||||
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
@@ -83,11 +89,15 @@ compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||
compute_z(x, y, z::Void) = nothing
|
||||
compute_z(x, y, z) = copy(z)
|
||||
|
||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
||||
nobigs(v::AVec{BigInt}) = map(Int64, v)
|
||||
nobigs(v) = v
|
||||
|
||||
@noinline function compute_xyz(x, y, z)
|
||||
x = compute_x(x,y,z)
|
||||
y = compute_y(x,y,z)
|
||||
z = compute_z(x,y,z)
|
||||
x, y, z
|
||||
nobigs(x), nobigs(y), nobigs(z)
|
||||
end
|
||||
|
||||
# not allowed
|
||||
@@ -97,371 +107,444 @@ compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# create n=max(mx,my) series arguments. the shorter list is cycled through
|
||||
# note: everything should flow through this
|
||||
function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
x, y, z = map(sym -> pop!(kw, sym, nothing), (:x, :y, :z))
|
||||
if nothing == x == y == z
|
||||
return [], nothing, nothing
|
||||
end
|
||||
|
||||
xs, xmeta = convertToAnyVector(x, kw)
|
||||
ys, ymeta = convertToAnyVector(y, kw)
|
||||
zs, zmeta = convertToAnyVector(z, kw)
|
||||
|
||||
fr = pop!(kw, :fillrange, nothing)
|
||||
fillranges, _ = if typeof(fr) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(fr, kw)
|
||||
end
|
||||
|
||||
mx = length(xs)
|
||||
my = length(ys)
|
||||
mz = length(zs)
|
||||
ret = Any[]
|
||||
for i in 1:max(mx, my, mz)
|
||||
|
||||
# try to set labels using ymeta
|
||||
d = copy(kw)
|
||||
if !haskey(d, :label) && ymeta != nothing
|
||||
if isa(ymeta, Symbol)
|
||||
d[:label] = string(ymeta)
|
||||
elseif isa(ymeta, AVec{Symbol})
|
||||
d[:label] = string(ymeta[mod1(i,length(ymeta))])
|
||||
end
|
||||
end
|
||||
|
||||
# build the series arg dict
|
||||
numUncounted = pop!(d, :numUncounted, 0)
|
||||
commandIndex = i + numUncounted
|
||||
n = plt.n + i
|
||||
|
||||
dumpdict(d, "before getSeriesArgs")
|
||||
d = getSeriesArgs(plt.backend, getplotargs(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n)
|
||||
dumpdict(d, "after getSeriesArgs")
|
||||
|
||||
d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])
|
||||
lt = d[:linetype]
|
||||
|
||||
# for linetype `line`, need to sort by x values
|
||||
if lt == :line
|
||||
# order by x
|
||||
indices = sortperm(d[:x])
|
||||
d[:x] = d[:x][indices]
|
||||
d[:y] = d[:y][indices]
|
||||
d[:linetype] = :path
|
||||
end
|
||||
|
||||
# special handling for missing x in box plot... all the same category
|
||||
if lt == :box && xs[mod1(i,mx)] == nothing
|
||||
d[:x] = ones(Int, length(d[:y]))
|
||||
end
|
||||
|
||||
# map functions to vectors
|
||||
if isa(d[:marker_z], Function)
|
||||
d[:marker_z] = map(d[:marker_z], d[:x])
|
||||
end
|
||||
|
||||
# @show fillranges
|
||||
d[:fillrange] = fillranges[mod1(i,length(fillranges))]
|
||||
if isa(d[:fillrange], Function)
|
||||
d[:fillrange] = map(d[:fillrange], d[:x])
|
||||
end
|
||||
|
||||
# handle error bars
|
||||
for esym in (:xerror, :yerror)
|
||||
if get(d, esym, nothing) != nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
append!(ret, apply_series_recipe(copy(d), Val{esym}))
|
||||
end
|
||||
end
|
||||
|
||||
# handle ribbons
|
||||
if get(d, :ribbon, nothing) != nothing
|
||||
rib = d[:ribbon]
|
||||
d[:fillrange] = (d[:y] - rib, d[:y] + rib)
|
||||
end
|
||||
|
||||
# handle quiver plots
|
||||
if lt == :quiver
|
||||
d[:linetype] = lt = :path
|
||||
d[:linewidth] = 0
|
||||
end
|
||||
if get(d, :quiver, nothing) != nothing
|
||||
append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
|
||||
end
|
||||
|
||||
|
||||
|
||||
# now that we've processed a given series... optionally split into
|
||||
# multiple dicts through a recipe (for example, a box plot is split into component
|
||||
# parts... polygons, lines, and scatters)
|
||||
# note: we pass in a Val type (i.e. Val{:box}) so that we can dispatch on the linetype
|
||||
kwlist = apply_series_recipe(d, Val{lt})
|
||||
append!(ret, kwlist)
|
||||
|
||||
# # add it to our series list
|
||||
# push!(ret, d)
|
||||
end
|
||||
|
||||
ret, xmeta, ymeta
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# process_inputs
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# These methods take a plot and the keyword arguments, and processes the input
|
||||
# arguments (x/y/z, group, etc), populating the KW dict with appropriate values.
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 0 arguments
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# don't do anything
|
||||
function process_inputs(plt::AbstractPlot, d::KW)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 1 argument
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
function process_inputs(plt::AbstractPlot, d::KW, n::Integer)
|
||||
# d[:x], d[:y], d[:z] = zeros(0), zeros(0), zeros(0)
|
||||
d[:x] = d[:y] = d[:z] = n
|
||||
end
|
||||
|
||||
# no special handling... assume x and z are nothing
|
||||
function process_inputs(plt::AbstractPlot, d::KW, y)
|
||||
d[:y] = y
|
||||
end
|
||||
|
||||
# matrix... is it z or y?
|
||||
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
if all3D(d)
|
||||
n,m = size(mat)
|
||||
d[:x], d[:y], d[:z] = 1:n, 1:m, mat
|
||||
else
|
||||
d[:y] = mat
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# plotting arbitrary shapes/polygons
|
||||
function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
|
||||
d[:x], d[:y] = shape_coords(shape)
|
||||
d[:linetype] = :shape
|
||||
end
|
||||
function process_inputs(plt::AbstractPlot, d::KW, shapes::AVec{Shape})
|
||||
d[:x], d[:y] = shape_coords(shapes)
|
||||
d[:linetype] = :shape
|
||||
end
|
||||
function process_inputs(plt::AbstractPlot, d::KW, shapes::AMat{Shape})
|
||||
x, y = [], []
|
||||
for j in 1:size(shapes, 2)
|
||||
tmpx, tmpy = shape_coords(vec(shapes[:,j]))
|
||||
push!(x, tmpx)
|
||||
push!(y, tmpy)
|
||||
end
|
||||
d[:x], d[:y] = x, y
|
||||
d[:linetype] = :shape
|
||||
end
|
||||
|
||||
|
||||
# function without range... use the current range of the x-axis
|
||||
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs)
|
||||
process_inputs(plt, d, f, xmin(plt), xmax(plt))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 2 arguments
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
function process_inputs(plt::AbstractPlot, d::KW, x, y)
|
||||
d[:x], d[:y] = x, y
|
||||
end
|
||||
|
||||
# if functions come first, just swap the order (not to be confused with parametric functions...
|
||||
# as there would be more than one function passed in)
|
||||
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, x)
|
||||
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
|
||||
process_inputs(plt, d, x, f)
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# 3 arguments
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# no special handling... just pass them through
|
||||
function process_inputs(plt::AbstractPlot, d::KW, x, y, z)
|
||||
d[:x], d[:y], d[:z] = x, y, z
|
||||
end
|
||||
|
||||
# 3d line or scatter
|
||||
function process_inputs(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zvec::AVec)
|
||||
# default to path3d if we haven't set a 3d linetype
|
||||
lt = get(d, :linetype, :none)
|
||||
if lt == :scatter
|
||||
d[:linetype] = :scatter3d
|
||||
elseif !(lt in _3dTypes)
|
||||
d[:linetype] = :path3d
|
||||
end
|
||||
d[:x], d[:y], d[:z] = x, y, zvec
|
||||
end
|
||||
|
||||
# surface-like... function
|
||||
function process_inputs{TX,TY}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zf::Function)
|
||||
x = TX <: Number ? sort(x) : x
|
||||
y = TY <: Number ? sort(y) : y
|
||||
# x, y = sort(x), sort(y)
|
||||
d[:z] = Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
d[:x], d[:y] = x, y
|
||||
end
|
||||
|
||||
# surface-like... matrix grid
|
||||
function process_inputs{TX,TY,TZ}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zmat::AMat{TZ})
|
||||
@assert size(zmat) == (length(x), length(y))
|
||||
if TX <: Number && !issorted(x)
|
||||
idx = sortperm(x)
|
||||
x, zmat = x[idx], zmat[idx, :]
|
||||
end
|
||||
if TY <: Number && !issorted(y)
|
||||
idx = sortperm(y)
|
||||
y, zmat = y[idx], zmat[:, idx]
|
||||
end
|
||||
d[:x], d[:y], d[:z] = x, y, Surface{Matrix{TZ}}(zmat)
|
||||
if !like_surface(get(d, :linetype, :none))
|
||||
d[:linetype] = :contour
|
||||
end
|
||||
end
|
||||
|
||||
# surfaces-like... general x, y grid
|
||||
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, x::AMat{T}, y::AMat{T}, zmat::AMat{T})
|
||||
@assert size(zmat) == size(x) == size(y)
|
||||
# d[:x], d[:y], d[:z] = Any[x], Any[y], Surface{Matrix{Float64}}(zmat)
|
||||
d[:x], d[:y], d[:z] = map(Surface{Matrix{Float64}}, (x, y, zmat))
|
||||
if !like_surface(get(d, :linetype, :none))
|
||||
d[:linetype] = :contour
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Parametric functions
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# special handling... xmin/xmax with function(s)
|
||||
function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, xmin::Number, xmax::Number)
|
||||
width = get(plt.plotargs, :size, (100,))[1]
|
||||
x = linspace(xmin, xmax, width)
|
||||
process_inputs(plt, d, x, f)
|
||||
end
|
||||
|
||||
# special handling... xmin/xmax with parametric function(s)
|
||||
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
|
||||
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
|
||||
process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, linspace(umin, umax, numPoints))
|
||||
|
||||
# special handling... 3D parametric function(s)
|
||||
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
|
||||
process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
|
||||
process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, fz, linspace(umin, umax, numPoints))
|
||||
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# Lists of tuples and FixedSizeArrays
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# if we get an unhandled tuple, just splat it in
|
||||
function process_inputs(plt::AbstractPlot, d::KW, tup::Tuple)
|
||||
process_inputs(plt, d, tup...)
|
||||
end
|
||||
|
||||
# (x,y) tuples
|
||||
function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::AVec{Tuple{R1,R2}})
|
||||
process_inputs(plt, d, unzip(xy)...)
|
||||
end
|
||||
function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::Tuple{R1,R2})
|
||||
process_inputs(plt, d, [xy[1]], [xy[2]])
|
||||
end
|
||||
|
||||
# (x,y,z) tuples
|
||||
function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{Tuple{R1,R2,R3}})
|
||||
process_inputs(plt, d, unzip(xyz)...)
|
||||
end
|
||||
function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::Tuple{R1,R2,R3})
|
||||
process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
|
||||
end
|
||||
|
||||
# 2D FixedSizeArrays
|
||||
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}})
|
||||
process_inputs(plt, d, unzip(xy)...)
|
||||
end
|
||||
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::FixedSizeArrays.Vec{2,T})
|
||||
process_inputs(plt, d, [xy[1]], [xy[2]])
|
||||
end
|
||||
|
||||
# 3D FixedSizeArrays
|
||||
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{FixedSizeArrays.Vec{3,T}})
|
||||
process_inputs(plt, d, unzip(xyz)...)
|
||||
end
|
||||
function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::FixedSizeArrays.Vec{3,T})
|
||||
process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# handle grouping
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...)
|
||||
# ret = Any[]
|
||||
# error("unfinished after series reorg")
|
||||
# for (i,glab) in enumerate(groupby.groupLabels)
|
||||
# # TODO: don't automatically overwrite labels
|
||||
# kwlist, xmeta, ymeta = process_inputs(plt, d, args...,
|
||||
# idxfilter = groupby.groupIds[i],
|
||||
# label = string(glab),
|
||||
# numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
|
||||
# append!(ret, kwlist)
|
||||
# # create n=max(mx,my) series arguments. the shorter list is cycled through
|
||||
# # note: everything should flow through this
|
||||
# function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
# x, y, z = map(sym -> pop!(kw, sym, nothing), (:x, :y, :z))
|
||||
# if nothing == x == y == z
|
||||
# return [], nothing, nothing
|
||||
# end
|
||||
# ret, nothing, nothing # TODO: handle passing meta through
|
||||
#
|
||||
# xs, xmeta = convertToAnyVector(x, kw)
|
||||
# ys, ymeta = convertToAnyVector(y, kw)
|
||||
# zs, zmeta = convertToAnyVector(z, kw)
|
||||
#
|
||||
# fr = pop!(kw, :fillrange, nothing)
|
||||
# fillranges, _ = if typeof(fr) <: Number
|
||||
# ([fr],nothing)
|
||||
# else
|
||||
# convertToAnyVector(fr, kw)
|
||||
# end
|
||||
#
|
||||
# mx = length(xs)
|
||||
# my = length(ys)
|
||||
# mz = length(zs)
|
||||
# ret = Any[]
|
||||
# for i in 1:max(mx, my, mz)
|
||||
#
|
||||
# # try to set labels using ymeta
|
||||
# d = copy(kw)
|
||||
# if !haskey(d, :label) && ymeta != nothing
|
||||
# if isa(ymeta, Symbol)
|
||||
# d[:label] = string(ymeta)
|
||||
# elseif isa(ymeta, AVec{Symbol})
|
||||
# d[:label] = string(ymeta[mod1(i,length(ymeta))])
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # build the series arg dict
|
||||
# numUncounted = pop!(d, :numUncounted, 0)
|
||||
# commandIndex = i + numUncounted
|
||||
# n = plt.n + i
|
||||
#
|
||||
# dumpdict(d, "before getSeriesArgs")
|
||||
# d = getSeriesArgs(plt.backend, getattr(plt, n), d, commandIndex, convertSeriesIndex(plt, n), n)
|
||||
# dumpdict(d, "after getSeriesArgs")
|
||||
#
|
||||
# d[:x], d[:y], d[:z] = compute_xyz(xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)])
|
||||
# st = d[:seriestype]
|
||||
#
|
||||
# # for seriestype `line`, need to sort by x values
|
||||
# if st == :line
|
||||
# # order by x
|
||||
# indices = sortperm(d[:x])
|
||||
# d[:x] = d[:x][indices]
|
||||
# d[:y] = d[:y][indices]
|
||||
# d[:seriestype] = :path
|
||||
# end
|
||||
#
|
||||
# # special handling for missing x in box plot... all the same category
|
||||
# if st == :box && xs[mod1(i,mx)] == nothing
|
||||
# d[:x] = ones(Int, length(d[:y]))
|
||||
# end
|
||||
#
|
||||
# # map functions to vectors
|
||||
# if isa(d[:marker_z], Function)
|
||||
# d[:marker_z] = map(d[:marker_z], d[:x])
|
||||
# end
|
||||
#
|
||||
# # @show fillranges
|
||||
# d[:fillrange] = fillranges[mod1(i,length(fillranges))]
|
||||
# if isa(d[:fillrange], Function)
|
||||
# d[:fillrange] = map(d[:fillrange], d[:x])
|
||||
# end
|
||||
#
|
||||
# # handle error bars
|
||||
# for esym in (:xerror, :yerror)
|
||||
# if get(d, esym, nothing) != nothing
|
||||
# # we make a copy of the KW and apply an errorbar recipe
|
||||
# append!(ret, apply_series_recipe(copy(d), Val{esym}))
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # handle ribbons
|
||||
# if get(d, :ribbon, nothing) != nothing
|
||||
# rib = d[:ribbon]
|
||||
# d[:fillrange] = (d[:y] - rib, d[:y] + rib)
|
||||
# end
|
||||
#
|
||||
# # handle quiver plots
|
||||
# # either a series of velocity vectors are passed in (`:quiver` keyword),
|
||||
# # or we just add arrows to the path
|
||||
#
|
||||
# # if st == :quiver
|
||||
# # d[:seriestype] = st = :path
|
||||
# # d[:linewidth] = 0
|
||||
# # end
|
||||
# if get(d, :quiver, nothing) != nothing
|
||||
# append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
|
||||
# elseif st == :quiver
|
||||
# d[:seriestype] = st = :path
|
||||
# d[:arrow] = arrow()
|
||||
# end
|
||||
#
|
||||
# # now that we've processed a given series... optionally split into
|
||||
# # multiple dicts through a recipe (for example, a box plot is split into component
|
||||
# # parts... polygons, lines, and scatters)
|
||||
# # note: we pass in a Val type (i.e. Val{:box}) so that we can dispatch on the seriestype
|
||||
# kwlist = apply_series_recipe(d, Val{st})
|
||||
# append!(ret, kwlist)
|
||||
#
|
||||
# # # add it to our series list
|
||||
# # push!(ret, d)
|
||||
# end
|
||||
#
|
||||
# ret, xmeta, ymeta
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # process_inputs
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # These methods take a plot and the keyword arguments, and processes the input
|
||||
# # arguments (x/y/z, group, etc), populating the KW dict with appropriate values.
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 0 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # don't do anything
|
||||
# function process_inputs(plt::AbstractPlot, d::KW)
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 1 argument
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, n::Integer)
|
||||
# # d[:x], d[:y], d[:z] = zeros(0), zeros(0), zeros(0)
|
||||
# d[:x] = d[:y] = d[:z] = n
|
||||
# end
|
||||
#
|
||||
# # no special handling... assume x and z are nothing
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, y)
|
||||
# d[:y] = y
|
||||
# end
|
||||
#
|
||||
# # matrix... is it z or y?
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
# if all3D(d)
|
||||
# n,m = size(mat)
|
||||
# d[:x], d[:y], d[:z] = 1:n, 1:m, mat
|
||||
# else
|
||||
# d[:y] = mat
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # images - grays
|
||||
# function process_inputs{T<:Gray}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
# d[:seriestype] = :image
|
||||
# n,m = size(mat)
|
||||
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
|
||||
# # handle images... when not supported natively, do a hack to use heatmap machinery
|
||||
# if !nativeImagesSupported()
|
||||
# d[:seriestype] = :heatmap
|
||||
# d[:yflip] = true
|
||||
# d[:z] = Surface(convert(Matrix{Float64}, mat.surf))
|
||||
# d[:fillcolor] = ColorGradient([:black, :white])
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # images - colors
|
||||
# function process_inputs{T<:Colorant}(plt::AbstractPlot, d::KW, mat::AMat{T})
|
||||
# d[:seriestype] = :image
|
||||
# n,m = size(mat)
|
||||
# d[:x], d[:y], d[:z] = 1:n, 1:m, Surface(mat)
|
||||
# # handle images... when not supported natively, do a hack to use heatmap machinery
|
||||
# if !nativeImagesSupported()
|
||||
# d[:yflip] = true
|
||||
# imageHack(d)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # plotting arbitrary shapes/polygons
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, shape::Shape)
|
||||
# d[:x], d[:y] = shape_coords(shape)
|
||||
# d[:seriestype] = :shape
|
||||
# end
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, shapes::AVec{Shape})
|
||||
# d[:x], d[:y] = shape_coords(shapes)
|
||||
# d[:seriestype] = :shape
|
||||
# end
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, shapes::AMat{Shape})
|
||||
# x, y = [], []
|
||||
# for j in 1:size(shapes, 2)
|
||||
# tmpx, tmpy = shape_coords(vec(shapes[:,j]))
|
||||
# push!(x, tmpx)
|
||||
# push!(y, tmpy)
|
||||
# end
|
||||
# d[:x], d[:y] = x, y
|
||||
# d[:seriestype] = :shape
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # function without range... use the current range of the x-axis
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs)
|
||||
# process_inputs(plt, d, f, xmin(plt), xmax(plt))
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 2 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, x, y)
|
||||
# d[:x], d[:y] = x, y
|
||||
# end
|
||||
#
|
||||
# # if functions come first, just swap the order (not to be confused with parametric functions...
|
||||
# # as there would be more than one function passed in)
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, x)
|
||||
# @assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
|
||||
# process_inputs(plt, d, x, f)
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 3 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # no special handling... just pass them through
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, x, y, z)
|
||||
# d[:x], d[:y], d[:z] = x, y, z
|
||||
# end
|
||||
#
|
||||
# # 3d line or scatter
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, x::AVec, y::AVec, zvec::AVec)
|
||||
# # default to path3d if we haven't set a 3d seriestype
|
||||
# st = get(d, :seriestype, :none)
|
||||
# if st == :scatter
|
||||
# d[:seriestype] = :scatter3d
|
||||
# elseif !(st in _3dTypes)
|
||||
# d[:seriestype] = :path3d
|
||||
# end
|
||||
# d[:x], d[:y], d[:z] = x, y, zvec
|
||||
# end
|
||||
#
|
||||
# # surface-like... function
|
||||
# function process_inputs{TX,TY}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zf::Function)
|
||||
# x = TX <: Number ? sort(x) : x
|
||||
# y = TY <: Number ? sort(y) : y
|
||||
# # x, y = sort(x), sort(y)
|
||||
# d[:z] = Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
# d[:x], d[:y] = x, y
|
||||
# end
|
||||
#
|
||||
# # surface-like... matrix grid
|
||||
# function process_inputs{TX,TY,TZ}(plt::AbstractPlot, d::KW, x::AVec{TX}, y::AVec{TY}, zmat::AMat{TZ})
|
||||
# # @assert size(zmat) == (length(x), length(y))
|
||||
# # if TX <: Number && !issorted(x)
|
||||
# # idx = sortperm(x)
|
||||
# # x, zmat = x[idx], zmat[idx, :]
|
||||
# # end
|
||||
# # if TY <: Number && !issorted(y)
|
||||
# # idx = sortperm(y)
|
||||
# # y, zmat = y[idx], zmat[:, idx]
|
||||
# # end
|
||||
# d[:x], d[:y], d[:z] = x, y, Surface{Matrix{TZ}}(zmat)
|
||||
# if !like_surface(get(d, :seriestype, :none))
|
||||
# d[:seriestype] = :contour
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# # surfaces-like... general x, y grid
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, x::AMat{T}, y::AMat{T}, zmat::AMat{T})
|
||||
# @assert size(zmat) == size(x) == size(y)
|
||||
# # d[:x], d[:y], d[:z] = Any[x], Any[y], Surface{Matrix{Float64}}(zmat)
|
||||
# d[:x], d[:y], d[:z] = map(Surface{Matrix{Float64}}, (x, y, zmat))
|
||||
# if !like_surface(get(d, :seriestype, :none))
|
||||
# d[:seriestype] = :contour
|
||||
# end
|
||||
# end
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Parametric functions
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # special handling... xmin/xmax with function(s)
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, f::FuncOrFuncs, xmin::Number, xmax::Number)
|
||||
# width = get(plt.attr, :size, (100,))[1]
|
||||
# x = linspace(xmin, xmax, width)
|
||||
# process_inputs(plt, d, x, f)
|
||||
# end
|
||||
#
|
||||
# # special handling... xmin/xmax with parametric function(s)
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u))
|
||||
# process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, linspace(umin, umax, numPoints))
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
|
||||
# process_inputs{T<:Number}(plt::AbstractPlot, d::KW, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs) = process_inputs(plt, d, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u))
|
||||
# process_inputs(plt::AbstractPlot, d::KW, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints::Int = 1000) = process_inputs(plt, d, fx, fy, fz, linspace(umin, umax, numPoints))
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Lists of tuples and FixedSizeArrays
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # if we get an unhandled tuple, just splat it in
|
||||
# function process_inputs(plt::AbstractPlot, d::KW, tup::Tuple)
|
||||
# process_inputs(plt, d, tup...)
|
||||
# end
|
||||
#
|
||||
# # (x,y) tuples
|
||||
# function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::AVec{Tuple{R1,R2}})
|
||||
# process_inputs(plt, d, unzip(xy)...)
|
||||
# end
|
||||
# function process_inputs{R1<:Number,R2<:Number}(plt::AbstractPlot, d::KW, xy::Tuple{R1,R2})
|
||||
# process_inputs(plt, d, [xy[1]], [xy[2]])
|
||||
# end
|
||||
#
|
||||
# # (x,y,z) tuples
|
||||
# function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{Tuple{R1,R2,R3}})
|
||||
# process_inputs(plt, d, unzip(xyz)...)
|
||||
# end
|
||||
# function process_inputs{R1<:Number,R2<:Number,R3<:Number}(plt::AbstractPlot, d::KW, xyz::Tuple{R1,R2,R3})
|
||||
# process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
|
||||
# end
|
||||
#
|
||||
# # 2D FixedSizeArrays
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::AVec{FixedSizeArrays.Vec{2,T}})
|
||||
# process_inputs(plt, d, unzip(xy)...)
|
||||
# end
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xy::FixedSizeArrays.Vec{2,T})
|
||||
# process_inputs(plt, d, [xy[1]], [xy[2]])
|
||||
# end
|
||||
#
|
||||
# # 3D FixedSizeArrays
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::AVec{FixedSizeArrays.Vec{3,T}})
|
||||
# process_inputs(plt, d, unzip(xyz)...)
|
||||
# end
|
||||
# function process_inputs{T<:Number}(plt::AbstractPlot, d::KW, xyz::FixedSizeArrays.Vec{3,T})
|
||||
# process_inputs(plt, d, [xyz[1]], [xyz[2]], [xyz[3]])
|
||||
# end
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # handle grouping
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # function process_inputs(plt::AbstractPlot, d::KW, groupby::GroupBy, args...)
|
||||
# # ret = Any[]
|
||||
# # error("unfinished after series reorg")
|
||||
# # for (i,glab) in enumerate(groupby.groupLabels)
|
||||
# # # TODO: don't automatically overwrite labels
|
||||
# # kwlist, xmeta, ymeta = process_inputs(plt, d, args...,
|
||||
# # idxfilter = groupby.groupIds[i],
|
||||
# # label = string(glab),
|
||||
# # numUncounted = length(ret)) # we count the idx from plt.n + numUncounted + i
|
||||
# # append!(ret, kwlist)
|
||||
# # end
|
||||
# # ret, nothing, nothing # TODO: handle passing meta through
|
||||
# # end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
# For DataFrame support. Imports DataFrames and defines the necessary methods which support them.
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
function setup_dataframes()
|
||||
@require DataFrames begin
|
||||
|
||||
get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
|
||||
get_data(df::DataFrames.AbstractDataFrame, arg) = arg
|
||||
|
||||
function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
|
||||
# d[:dataframe] = df
|
||||
process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
|
||||
end
|
||||
|
||||
# expecting the column name of a dataframe that was passed in... anything else should error
|
||||
function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
||||
if haskey(df, s)
|
||||
return extractGroupArgs(df[s])
|
||||
else
|
||||
error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
|
||||
end
|
||||
end
|
||||
|
||||
# function getDataFrameFromKW(d::KW)
|
||||
# get(d, :dataframe) do
|
||||
# error("Missing dataframe argument!")
|
||||
# end
|
||||
# end
|
||||
|
||||
# # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
|
||||
# convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s
|
||||
# convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v
|
||||
|
||||
end
|
||||
end
|
||||
# function setup_dataframes()
|
||||
# @require DataFrames begin
|
||||
# # @eval begin
|
||||
# # import DataFrames
|
||||
#
|
||||
# DFS = Union{Symbol, AbstractArray{Symbol}}
|
||||
#
|
||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, dfs::DFS)
|
||||
# if isa(dfs, Symbol)
|
||||
# get!(d, Symbol(letter * "label"), string(dfs))
|
||||
# collect(df[dfs])
|
||||
# else
|
||||
# get!(d, :label, reshape(dfs, 1, length(dfs)))
|
||||
# Any[collect(df[s]) for s in dfs]
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# function handle_group(df::DataFrames.AbstractDataFrame, d::KW)
|
||||
# if haskey(d, :group)
|
||||
# g = d[:group]
|
||||
# if isa(g, Symbol)
|
||||
# d[:group] = collect(df[g])
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# @recipe function plot(df::DataFrames.AbstractDataFrame, sy::DFS)
|
||||
# handle_group(df, d)
|
||||
# handle_dfs(df, d, "y", sy)
|
||||
# end
|
||||
#
|
||||
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS)
|
||||
# handle_group(df, d)
|
||||
# x = handle_dfs(df, d, "x", sx)
|
||||
# y = handle_dfs(df, d, "y", sy)
|
||||
# x, y
|
||||
# end
|
||||
#
|
||||
# @recipe function plot(df::DataFrames.AbstractDataFrame, sx::DFS, sy::DFS, sz::DFS)
|
||||
# handle_group(df, d)
|
||||
# x = handle_dfs(df, d, "x", sx)
|
||||
# y = handle_dfs(df, d, "y", sy)
|
||||
# z = handle_dfs(df, d, "z", sz)
|
||||
# x, y, z
|
||||
# end
|
||||
#
|
||||
# # get_data(df::DataFrames.AbstractDataFrame, arg::Symbol) = df[arg]
|
||||
# # get_data(df::DataFrames.AbstractDataFrame, arg) = arg
|
||||
# #
|
||||
# # function process_inputs(plt::AbstractPlot, d::KW, df::DataFrames.AbstractDataFrame, args...)
|
||||
# # # d[:dataframe] = df
|
||||
# # process_inputs(plt, d, map(arg -> get_data(df, arg), args)...)
|
||||
# # end
|
||||
# #
|
||||
# # # expecting the column name of a dataframe that was passed in... anything else should error
|
||||
# # function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
||||
# # if haskey(df, s)
|
||||
# # return extractGroupArgs(df[s])
|
||||
# # else
|
||||
# # error("Got a symbol, and expected that to be a key in d[:dataframe]. s=$s d=$d")
|
||||
# # end
|
||||
# # end
|
||||
#
|
||||
# # function getDataFrameFromKW(d::KW)
|
||||
# # get(d, :dataframe) do
|
||||
# # error("Missing dataframe argument!")
|
||||
# # end
|
||||
# # end
|
||||
#
|
||||
# # # the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
|
||||
# # convertToAnyVector(s::Symbol, d::KW) = Any[getDataFrameFromKW(d)[s]], s
|
||||
# # convertToAnyVector(v::AVec{Symbol}, d::KW) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v
|
||||
#
|
||||
# end
|
||||
# end
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
|
||||
# we are going to build recipes to do the processing and splitting of the args
|
||||
|
||||
|
||||
function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
pkg = plt.backend
|
||||
globalIndex = d[:series_plotindex]
|
||||
|
||||
# add default values to our dictionary, being careful not to delete what we just added!
|
||||
for (k,v) in _series_defaults
|
||||
slice_arg!(d, d, k, v, commandIndex, remove_pair = false)
|
||||
end
|
||||
|
||||
# this is how many series belong to this subplot
|
||||
plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
|
||||
if get(d, :primary, true)
|
||||
plotIndex += 1
|
||||
end
|
||||
|
||||
aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex)
|
||||
aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex)
|
||||
|
||||
# update color
|
||||
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], sp, plotIndex)
|
||||
|
||||
# update colors
|
||||
for csym in (:linecolor, :markercolor, :fillcolor)
|
||||
d[csym] = if d[csym] == :match
|
||||
if has_black_border_for_default(d[:seriestype]) && csym == :linecolor
|
||||
:black
|
||||
else
|
||||
d[:seriescolor]
|
||||
end
|
||||
else
|
||||
getSeriesRGBColor(d[csym], sp, plotIndex)
|
||||
end
|
||||
end
|
||||
|
||||
# update markerstrokecolor
|
||||
c = d[:markerstrokecolor]
|
||||
c = if c == :match
|
||||
sp[:foreground_color_subplot]
|
||||
else
|
||||
getSeriesRGBColor(c, sp, plotIndex)
|
||||
end
|
||||
d[:markerstrokecolor] = c
|
||||
|
||||
# update alphas
|
||||
for asym in (:linealpha, :markeralpha, :markerstrokealpha, :fillalpha)
|
||||
if d[asym] == nothing
|
||||
d[asym] = d[:seriesalpha]
|
||||
end
|
||||
end
|
||||
|
||||
# scatter plots don't have a line, but must have a shape
|
||||
if d[:seriestype] in (:scatter, :scatter3d)
|
||||
d[:linewidth] = 0
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :ellipse
|
||||
end
|
||||
end
|
||||
|
||||
# set label
|
||||
label = d[:label]
|
||||
label = (label == "AUTO" ? "y$globalIndex" : label)
|
||||
d[:label] = label
|
||||
|
||||
_replace_linewidth(d)
|
||||
d
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------
|
||||
|
||||
# instead of process_inputs:
|
||||
|
||||
# ensure we dispatch to the slicer
|
||||
immutable SliceIt end
|
||||
|
||||
# the catch-all recipes
|
||||
@recipe function f(::Type{SliceIt}, x, y, z)
|
||||
# @show "HERE", typeof((x,y,z))
|
||||
xs, _ = convertToAnyVector(x, d)
|
||||
ys, _ = convertToAnyVector(y, d)
|
||||
zs, _ = convertToAnyVector(z, d)
|
||||
|
||||
fr = pop!(d, :fillrange, nothing)
|
||||
fillranges, _ = if typeof(fr) <: Number
|
||||
([fr],nothing)
|
||||
else
|
||||
convertToAnyVector(fr, d)
|
||||
end
|
||||
mf = length(fillranges)
|
||||
|
||||
# @show zs
|
||||
|
||||
mx = length(xs)
|
||||
my = length(ys)
|
||||
mz = length(zs)
|
||||
# ret = Any[]
|
||||
for i in 1:max(mx, my, mz)
|
||||
# add a new series
|
||||
di = copy(d)
|
||||
xi, yi, zi = xs[mod1(i,mx)], ys[mod1(i,my)], zs[mod1(i,mz)]
|
||||
# @show i, typeof((xi, yi, zi))
|
||||
di[:x], di[:y], di[:z] = compute_xyz(xi, yi, zi)
|
||||
# @show i, typeof((di[:x], di[:y], di[:z]))
|
||||
|
||||
# handle fillrange
|
||||
fr = fillranges[mod1(i,mf)]
|
||||
di[:fillrange] = isa(fr, Function) ? map(fr, di[:x]) : fr
|
||||
|
||||
# @show i, di[:x], di[:y], di[:z]
|
||||
push!(series_list, RecipeData(di, ()))
|
||||
end
|
||||
nothing # don't add a series for the main block
|
||||
end
|
||||
|
||||
# this is the default "type recipe"... just pass the object through
|
||||
@recipe f{T}(::Type{T}, v::T) = v
|
||||
|
||||
_apply_type_recipe(d, v) = RecipesBase.apply_recipe(d, typeof(v), v)[1].args[1]
|
||||
|
||||
# handle "type recipes" by converting inputs, and then either re-calling or slicing
|
||||
@recipe function f(x, y, z)
|
||||
did_replace = false
|
||||
newx = _apply_type_recipe(d, x)
|
||||
x === newx || (did_replace = true)
|
||||
newy = _apply_type_recipe(d, y)
|
||||
y === newy || (did_replace = true)
|
||||
newz = _apply_type_recipe(d, z)
|
||||
z === newz || (did_replace = true)
|
||||
if did_replace
|
||||
newx, newy, newz
|
||||
else
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
end
|
||||
@recipe function f(x, y)
|
||||
did_replace = false
|
||||
newx = _apply_type_recipe(d, x)
|
||||
x === newx || (did_replace = true)
|
||||
newy = _apply_type_recipe(d, y)
|
||||
y === newy || (did_replace = true)
|
||||
if did_replace
|
||||
newx, newy
|
||||
else
|
||||
SliceIt, x, y, nothing
|
||||
end
|
||||
end
|
||||
@recipe function f(y)
|
||||
newy = _apply_type_recipe(d, y)
|
||||
if y !== newy
|
||||
newy
|
||||
else
|
||||
SliceIt, nothing, y, nothing
|
||||
end
|
||||
end
|
||||
# @recipe f(x, y, z) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), apply_recipe(typeof(z), z)
|
||||
# @recipe f(x, y) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), nothing
|
||||
# @recipe f(y) = SliceIt, nothing, apply_recipe(typeof(y), y), nothing
|
||||
|
||||
# # pass these through to the slicer
|
||||
# @recipe f(x, y, z) = SliceIt, x, y, z
|
||||
# @recipe f(x, y) = SliceIt, x, y, nothing
|
||||
# @recipe f(y) = SliceIt, nothing, y, nothing
|
||||
|
||||
|
||||
# # --------------------------------------------------------------------
|
||||
# # 1 argument
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
@recipe f(n::Integer) = n, n, n
|
||||
|
||||
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
||||
@recipe function f{T<:Number}(mat::AMat{T})
|
||||
if all3D(d)
|
||||
n,m = size(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
SliceIt, nothing, mat, nothing
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# # images - grays
|
||||
|
||||
@recipe function f{T<:Gray}(mat::AMat{T})
|
||||
if nativeImagesSupported()
|
||||
seriestype := :image
|
||||
n, m = size(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
fillcolor --> ColorGradient([:black, :white])
|
||||
SliceIt, 1:m, 1:n, Surface(convert(Matrix{Float64}, mat))
|
||||
end
|
||||
end
|
||||
|
||||
# # images - colors
|
||||
|
||||
@recipe function f{T<:Colorant}(mat::AMat{T})
|
||||
if nativeImagesSupported()
|
||||
seriestype := :image
|
||||
n, m = size(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
seriestype := :heatmap
|
||||
yflip --> true
|
||||
z, d[:fillcolor] = replace_image_with_heatmap(mat)
|
||||
SliceIt, 1:m, 1:n, Surface(z)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# # plotting arbitrary shapes/polygons
|
||||
|
||||
@recipe function f(shape::Shape)
|
||||
seriestype := :shape
|
||||
shape_coords(shape)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AVec{Shape})
|
||||
seriestype := :shape
|
||||
shape_coords(shapes)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AMat{Shape})
|
||||
for j in 1:size(shapes,2)
|
||||
# create one series for each column
|
||||
# @series shape_coords(vec(shapes[:,j]))
|
||||
di = copy(d)
|
||||
push!(series_list, RecipeData(di, shape_coords(vec(shapes[:,j]))))
|
||||
end
|
||||
nothing # don't create a series for the main block
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # function without range... use the current range of the x-axis
|
||||
|
||||
@recipe function f(f::FuncOrFuncs)
|
||||
plt = d[:plot_object]
|
||||
f, xmin(plt), xmax(plt)
|
||||
end
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 2 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
# # if functions come first, just swap the order (not to be confused with parametric functions...
|
||||
# # as there would be more than one function passed in)
|
||||
|
||||
@recipe function f(f::FuncOrFuncs, x)
|
||||
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
|
||||
x, f
|
||||
end
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # 3 arguments
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
#
|
||||
# # 3d line or scatter
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, z::AVec)
|
||||
st = get(d, :seriestype, :none)
|
||||
if st == :scatter
|
||||
d[:seriestype] = :scatter3d
|
||||
elseif !is3d(st)
|
||||
d[:seriestype] = :path3d
|
||||
end
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@recipe function f(x::AMat, y::AMat, z::AMat)
|
||||
st = get(d, :seriestype, :none)
|
||||
if size(x) == size(y) == size(z)
|
||||
if !is3d(st)
|
||||
seriestype := :path3d
|
||||
end
|
||||
end
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
#
|
||||
# # surface-like... function
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, zf::Function)
|
||||
# x = X <: Number ? sort(x) : x
|
||||
# y = Y <: Number ? sort(y) : y
|
||||
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
end
|
||||
|
||||
#
|
||||
# # surface-like... matrix grid
|
||||
|
||||
@recipe function f(x::AVec, y::AVec, z::AMat)
|
||||
if !like_surface(get(d, :seriestype, :none))
|
||||
d[:seriestype] = :contour
|
||||
end
|
||||
SliceIt, x, y, Surface(z)
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Parametric functions
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
#
|
||||
# # special handling... xmin/xmax with parametric function(s)
|
||||
@recipe f(f::FuncOrFuncs, xmin::Number, xmax::Number) = linspace(xmin, xmax, 100), f
|
||||
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n)
|
||||
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec)
|
||||
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
|
||||
end
|
||||
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPointsn = 200)
|
||||
fx, fy, fz, linspace(umin, umax, numPoints)
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # Lists of tuples and FixedSizeArrays
|
||||
# # --------------------------------------------------------------------
|
||||
#
|
||||
# # if we get an unhandled tuple, just splat it in
|
||||
@recipe f(tup::Tuple) = tup
|
||||
|
||||
#
|
||||
# # (x,y) tuples
|
||||
@recipe f{R1<:Number,R2<:Number}(xy::AVec{Tuple{R1,R2}}) = unzip(xy)
|
||||
@recipe f{R1<:Number,R2<:Number}(xy::Tuple{R1,R2}) = [xy[1]], [xy[2]]
|
||||
|
||||
#
|
||||
# # (x,y,z) tuples
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::AVec{Tuple{R1,R2,R3}}) = unzip(xyz)
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number}(xyz::Tuple{R1,R2,R3}) = [xyz[1]], [xyz[2]], [xyz[3]]
|
||||
|
||||
# these might be points+velocity, or OHLC or something else
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::AVec{Tuple{R1,R2,R3,R4}}) = get(d,:seriestype,:path)==:ohlc ? OHLC[OHLC(t...) for t in xyuv] : unzip(xyuv)
|
||||
@recipe f{R1<:Number,R2<:Number,R3<:Number,R4<:Number}(xyuv::Tuple{R1,R2,R3,R4}) = [xyuv[1]], [xyuv[2]], [xyuv[3]], [xyuv[4]]
|
||||
|
||||
|
||||
#
|
||||
# # 2D FixedSizeArrays
|
||||
@recipe f{T<:Number}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = unzip(xy)
|
||||
@recipe f{T<:Number}(xy::FixedSizeArrays.Vec{2,T}) = [xy[1]], [xy[2]]
|
||||
|
||||
#
|
||||
# # 3D FixedSizeArrays
|
||||
@recipe f{T<:Number}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = unzip(xyz)
|
||||
@recipe f{T<:Number}(xyz::FixedSizeArrays.Vec{3,T}) = [xyz[1]], [xyz[2]], [xyz[3]]
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
# # handle grouping
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# @recipe function f(groupby::GroupBy, args...)
|
||||
# for (i,glab) in enumerate(groupby.groupLabels)
|
||||
# # create a new series, with the label of the group, and an idxfilter (to be applied in slice_and_dice)
|
||||
# # TODO: use @series instead
|
||||
# @show i, glab, groupby.groupIds[i]
|
||||
# di = copy(d)
|
||||
# get!(di, :label, string(glab))
|
||||
# get!(di, :idxfilter, groupby.groupIds[i])
|
||||
# push!(series_list, RecipeData(di, args))
|
||||
# end
|
||||
# nothing
|
||||
# end
|
||||
|
||||
# split the group into 1 series per group, and set the label and idxfilter for each
|
||||
@recipe function f(groupby::GroupBy, args...)
|
||||
for (i,glab) in enumerate(groupby.groupLabels)
|
||||
@series begin
|
||||
label --> string(glab)
|
||||
idxfilter --> groupby.groupIds[i]
|
||||
args
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-331
@@ -1,331 +0,0 @@
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
Base.string(subplt::Subplot) = "Subplot{$(subplt.backend) p=$(subplt.p) n=$(subplt.n)}"
|
||||
Base.print(io::IO, subplt::Subplot) = print(io, string(subplt))
|
||||
Base.show(io::IO, subplt::Subplot) = print(io, string(subplt))
|
||||
|
||||
function Base.copy(subplt::Subplot)
|
||||
subplot(subplt.plts, subplt.layout, subplt.plotargs)
|
||||
end
|
||||
|
||||
Base.getindex(subplt::Subplot, args...) = subplt.plts[subplt.layout[args...]]
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
getplot(subplt::Subplot, idx::Int = subplt.n) = subplt.plts[mod1(idx, subplt.p)]
|
||||
getplotargs(subplt::Subplot, idx::Int) = getplot(subplt, idx).plotargs
|
||||
convertSeriesIndex(subplt::Subplot, n::Int) = ceil(Int, n / subplt.p)
|
||||
|
||||
# ------------------------------------------------------------
|
||||
|
||||
function validateSubplotSupported()
|
||||
if !subplotSupported()
|
||||
error(CURRENT_BACKEND.sym, " does not support the subplot/subplot! commands at this time. Try one of: ", join(filter(pkg->subplotSupported(_backend_instance(pkg)), backends()),", "))
|
||||
end
|
||||
end
|
||||
|
||||
"""
|
||||
Create a series of plots:
|
||||
```
|
||||
y = rand(100,3)
|
||||
subplot(y; n = 3) # create an automatic grid, and let it figure out the nr/nc... will put plots 1 and 2 on the first row, and plot 3 by itself on the 2nd row
|
||||
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows to 1 (so there are n columns)
|
||||
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns to 1 (so there are n rows)
|
||||
subplot(y; layout = [1, 2]) # explicit layout by row... plot #1 goes by itself in the first row, plots 2 and 3 split the 2nd row (note the n kw is unnecessary)
|
||||
subplot(plts, n; nr = -1, nc = -1) # build a layout from existing plots
|
||||
subplot(plts, layout) # build a layout from existing plots
|
||||
```
|
||||
"""
|
||||
function subplot(args...; kw...)
|
||||
validateSubplotSupported()
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
args = _apply_recipe(d, args...; kw..., issubplot=true)
|
||||
|
||||
# figure out the layout
|
||||
layoutarg = get(d, :layout, nothing)
|
||||
if layoutarg != nothing
|
||||
layout = subplotlayout(layoutarg)
|
||||
else
|
||||
n = get(d, :n, -1)
|
||||
if n < 0
|
||||
error("You must specify either layout or n when creating a subplot: ", d)
|
||||
end
|
||||
layout = subplotlayout(n, get(d, :nr, -1), get(d, :nc, -1))
|
||||
end
|
||||
|
||||
# initialize the individual plots
|
||||
pkg = backend()
|
||||
plts = Plot{typeof(pkg)}[]
|
||||
for i in 1:length(layout)
|
||||
di = getPlotArgs(pkg, d, i)
|
||||
di[:subplot] = true
|
||||
dumpdict(di, "Plot args (subplot $i)")
|
||||
push!(plts, _create_plot(pkg, di))
|
||||
end
|
||||
|
||||
# create the object and do the plotting
|
||||
subplt = Subplot(nothing, plts, pkg, length(layout), 0, layout, d, false, false, false, (r,c) -> (nothing,nothing))
|
||||
subplot!(subplt, args...; d...)
|
||||
|
||||
subplt
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------------------
|
||||
|
||||
# NOTE: for the subplot calls building from existing plots, we need the first plot to be separate to ensure dispatch calls this instead of the more general subplot(args...; kw...)
|
||||
|
||||
# grid layout
|
||||
function subplot{P}(plt1::Plot{P}, plts::Plot{P}...; kw...)
|
||||
d = KW(kw)
|
||||
layout = if haskey(d, :layout)
|
||||
subplotlayout(d[:layout])
|
||||
else
|
||||
subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
|
||||
end
|
||||
# layout = subplotlayout(length(plts)+1, get(d, :nr, -1), get(d, :nc, -1))
|
||||
subplot(vcat(plt1, plts...), layout, d)
|
||||
end
|
||||
|
||||
# explicit layout
|
||||
function subplot{P,I<:Integer}(pltsPerRow::AVec{I}, plt1::Plot{P}, plts::Plot{P}...; kw...)
|
||||
layout = subplotlayout(pltsPerRow)
|
||||
subplot(vcat(plt1, plts...), layout, KW(kw))
|
||||
end
|
||||
|
||||
# this will be called internally
|
||||
function subplot{P<:AbstractBackend}(plts::AVec{Plot{P}}, layout::SubplotLayout, d::KW)
|
||||
validateSubplotSupported()
|
||||
p = length(layout)
|
||||
n = sum([plt.n for plt in plts])
|
||||
subplt = Subplot(nothing, collect(plts), P(), p, n, layout, KW(), false, false, false, (r,c) -> (nothing,nothing))
|
||||
|
||||
_preprocess_subplot(subplt, d)
|
||||
_postprocess_subplot(subplt, d)
|
||||
|
||||
subplt
|
||||
end
|
||||
|
||||
# TODO: hcat/vcat subplots and plots together arbitrarily
|
||||
|
||||
# ------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
function _preprocess_subplot(subplt::Subplot, d::KW, args = ())
|
||||
validateSubplotSupported()
|
||||
preprocessArgs!(d)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
args = _apply_recipe(d, args...; d..., issubplot=true)
|
||||
|
||||
dumpdict(d, "After subplot! preprocessing")
|
||||
|
||||
# get the full plotargs, overriding any new settings
|
||||
# TODO: subplt.plotargs should probably be merged sooner and actually used
|
||||
# for color selection, etc. (i.e. if we overwrite the subplot palettes to [:heat :rainbow])
|
||||
# then we need to overwrite plt[1].plotargs[:color_palette] to :heat before it's actually used
|
||||
# for color selection!
|
||||
|
||||
# first merge the new args into the subplot's plotargs. then process the plot args and merge
|
||||
# those into the plot's plotargs. (example... `palette = [:blues :reds]` goes into subplt.plotargs,
|
||||
# then the ColorGradient for :blues/:reds is merged into plot 1/2 plotargs, which is then used for color selection)
|
||||
for i in 1:length(subplt.layout)
|
||||
subplt.plts[i].plotargs = getPlotArgs(backend(), merge(subplt.plts[i].plotargs, d), i)
|
||||
end
|
||||
merge!(subplt.plotargs, d)
|
||||
|
||||
# process links. TODO: extract to separate function
|
||||
for s in (:linkx, :linky, :linkfunc)
|
||||
if haskey(d, s)
|
||||
setfield!(subplt, s, d[s])
|
||||
delete!(d, s)
|
||||
end
|
||||
end
|
||||
|
||||
args
|
||||
end
|
||||
|
||||
function _postprocess_subplot(subplt::Subplot, d::KW)
|
||||
# init (after plot creation)
|
||||
if !subplt.initialized
|
||||
subplt.initialized = _create_subplot(subplt, false)
|
||||
end
|
||||
|
||||
# add title, axis labels, ticks, etc
|
||||
for (i,plt) in enumerate(subplt.plts)
|
||||
di = plt.plotargs
|
||||
dumpdict(di, "Updating sp $i")
|
||||
_update_plot(plt, di)
|
||||
end
|
||||
|
||||
_update_plot_pos_size(subplt, d)
|
||||
|
||||
# handle links
|
||||
subplt.linkx && link_axis(subplt, true)
|
||||
subplt.linky && link_axis(subplt, false)
|
||||
|
||||
# set this to be current
|
||||
current(subplt)
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------------------
|
||||
|
||||
"""
|
||||
Adds to a subplot.
|
||||
"""
|
||||
|
||||
# current subplot
|
||||
function subplot!(args...; kw...)
|
||||
validateSubplotSupported()
|
||||
subplot!(current(), args...; kw...)
|
||||
end
|
||||
|
||||
|
||||
# not allowed:
|
||||
function subplot!(plt::Plot, args...; kw...)
|
||||
error("Can't call subplot! on a Plot!")
|
||||
end
|
||||
|
||||
|
||||
# # this adds to a specific subplot... most plot commands will flow through here
|
||||
function subplot!(subplt::Subplot, args...; kw...)
|
||||
# validateSubplotSupported()
|
||||
|
||||
d = KW(kw)
|
||||
args = _preprocess_subplot(subplt, d, args)
|
||||
|
||||
# create the underlying object (each backend will do this differently)
|
||||
# note: we call it once before doing the individual plots, and once after
|
||||
# this is because some backends need to set up the subplots and then plot,
|
||||
# and others need to do it the other way around
|
||||
if !subplt.initialized
|
||||
subplt.initialized = _create_subplot(subplt, true)
|
||||
end
|
||||
|
||||
groupby = if haskey(d, :group)
|
||||
extractGroupArgs(d[:group], args...)
|
||||
else
|
||||
nothing
|
||||
end
|
||||
|
||||
_add_series_subplot(subplt, d, groupby, args...)
|
||||
_postprocess_subplot(subplt, d)
|
||||
|
||||
# show it automatically?
|
||||
if haskey(d, :show) && d[:show]
|
||||
gui()
|
||||
end
|
||||
|
||||
subplt
|
||||
end
|
||||
|
||||
|
||||
# not allowed:
|
||||
function plot!(subplt::Subplot, args...; kw...)
|
||||
error("Can't call plot! on a Subplot!")
|
||||
end
|
||||
|
||||
# given a fully processed KW, add the series to the Plot
|
||||
function _add_series_subplot(plt::Plot, d::KW)
|
||||
setTicksFromStringVector(d, d, :x, :xticks)
|
||||
setTicksFromStringVector(d, d, :y, :yticks)
|
||||
|
||||
# this is the actual call to the backend
|
||||
_add_series(plt.backend, plt, d)
|
||||
|
||||
_add_annotations(plt, d)
|
||||
warnOnUnsupportedScales(plt.backend, d)
|
||||
end
|
||||
|
||||
|
||||
# handle the grouping... add a series for each group
|
||||
function _add_series_subplot(subplt::Subplot, d::KW, groupby::GroupBy, args...)
|
||||
starting_n = subplt.n
|
||||
for (i, glab) in enumerate(groupby.groupLabels)
|
||||
tmpd = copy(d)
|
||||
tmpd[:numUncounted] = subplt.n - starting_n
|
||||
_add_series_subplot(subplt, tmpd, nothing, args...;
|
||||
idxfilter = groupby.groupIds[i],
|
||||
grouplabel = string(glab))
|
||||
end
|
||||
end
|
||||
|
||||
# process, filter, and add to the correct plot
|
||||
function _add_series_subplot(subplt::Subplot, d::KW, ::Void, args...;
|
||||
idxfilter = nothing,
|
||||
grouplabel = "")
|
||||
process_inputs(subplt, d, args...)
|
||||
|
||||
if idxfilter != nothing
|
||||
# add the group name as the label if there isn't one passed in
|
||||
get!(d, :label, grouplabel)
|
||||
# filter the data
|
||||
filter_data!(d, idxfilter)
|
||||
end
|
||||
|
||||
kwList, xmeta, ymeta = build_series_args(subplt, d)
|
||||
|
||||
# TODO: something useful with meta info?
|
||||
|
||||
for (i,di) in enumerate(kwList)
|
||||
|
||||
subplt.n += 1
|
||||
plt = getplot(subplt)
|
||||
plt.n += 1
|
||||
|
||||
# cleanup the dictionary that we pass into the plot! command
|
||||
di[:show] = false
|
||||
di[:subplot] = true
|
||||
for k in (:title, :xlabel, :xticks, :xlims, :xscale, :xflip,
|
||||
:ylabel, :yticks, :ylims, :yscale, :yflip)
|
||||
delete!(di, k)
|
||||
end
|
||||
dumpdict(di, "subplot! kwList $i")
|
||||
dumpdict(plt.plotargs, "plt.plotargs before plotting")
|
||||
|
||||
_add_series_subplot(plt, di)
|
||||
end
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# handle "linking" the subplot axes together
|
||||
# each backend should implement the _remove_axis and _expand_limits methods
|
||||
function link_axis(subplt::Subplot, isx::Bool)
|
||||
|
||||
# collect the list of plots and the expanded limits for those plots that should be linked on this axis
|
||||
includedPlots = Any[]
|
||||
# lims = [Inf, -Inf]
|
||||
lims = Dict{Int,Any}() # maps column to xlim
|
||||
for (i,(r,c)) in enumerate(subplt.layout)
|
||||
|
||||
# shouldlink will be a bool or nothing. if nothing, then use linkx/y (which is true if we get to this code)
|
||||
shouldlink = subplt.linkfunc(r,c)[isx ? 1 : 2]
|
||||
if shouldlink == nothing || shouldlink
|
||||
plt = subplt.plts[i]
|
||||
|
||||
# if we don't have this
|
||||
k = isx ? c : r
|
||||
if (firstone = !haskey(lims, k))
|
||||
lims[k] = [Inf, -Inf]
|
||||
end
|
||||
|
||||
isinner = (isx && r < nrows(subplt.layout)) || (!isx && !firstone)
|
||||
push!(includedPlots, (plt, isinner, k))
|
||||
|
||||
_expand_limits(lims[k], plt, isx)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
# do the axis adjustments
|
||||
for (plt, isinner, k) in includedPlots
|
||||
if isinner
|
||||
_remove_axis(plt, isx)
|
||||
end
|
||||
(isx ? xlims! : ylims!)(plt, lims[k]...)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
|
||||
function Subplot{T<:AbstractBackend}(::T; parent = RootLayout())
|
||||
Subplot{T}(
|
||||
parent,
|
||||
(20mm, 5mm, 2mm, 10mm),
|
||||
defaultbox,
|
||||
defaultbox,
|
||||
KW(),
|
||||
nothing,
|
||||
nothing
|
||||
)
|
||||
end
|
||||
|
||||
plotarea(sp::Subplot) = sp.plotarea
|
||||
plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox)
|
||||
|
||||
|
||||
Base.size(sp::Subplot) = (1,1)
|
||||
Base.length(sp::Subplot) = 1
|
||||
Base.getindex(sp::Subplot, r::Int, c::Int) = sp
|
||||
|
||||
leftpad(sp::Subplot) = sp.minpad[1]
|
||||
toppad(sp::Subplot) = sp.minpad[2]
|
||||
rightpad(sp::Subplot) = sp.minpad[3]
|
||||
bottompad(sp::Subplot) = sp.minpad[4]
|
||||
|
||||
get_subplot(plt::Plot, sp::Subplot) = sp
|
||||
get_subplot(plt::Plot, i::Integer) = plt.subplots[i]
|
||||
get_subplot(plt::Plot, k) = plt.spmap[k]
|
||||
get_subplot(series::Series) = series.d[:subplot]
|
||||
|
||||
get_subplot_index(plt::Plot, idx::Integer) = idx
|
||||
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(_ -> _ === sp, plt.subplots)
|
||||
|
||||
series_list(sp::Subplot) = filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
|
||||
|
||||
function should_add_to_legend(series::Series)
|
||||
!(series.d[:label] == "" || series.d[:seriestype] in (
|
||||
:hexbin,:histogram2d,:hline,:vline,
|
||||
:contour,:contour3d,:surface,:wireframe,
|
||||
:heatmap,:path3d,:scatter3d, :pie, :image
|
||||
))
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
@@ -0,0 +1,65 @@
|
||||
|
||||
const _invisible = RGBA(0,0,0,0)
|
||||
|
||||
const _themes = KW(
|
||||
:default => KW(
|
||||
:bg => :white,
|
||||
:bglegend => :match,
|
||||
:bginside => :match,
|
||||
:bgoutside => :match,
|
||||
:fg => :auto,
|
||||
:fglegend => :match,
|
||||
:fggrid => :match,
|
||||
:fgaxis => :match,
|
||||
:fgtext => :match,
|
||||
:fgborder => :match,
|
||||
:fgguide => :match,
|
||||
)
|
||||
)
|
||||
|
||||
function add_theme(sym::Symbol, theme::KW)
|
||||
_themes[sym] = theme
|
||||
end
|
||||
|
||||
# add a new theme, using an existing theme as the base
|
||||
function add_theme(sym::Symbol;
|
||||
base = :default, # start with this theme
|
||||
bg = _themes[base][:bg],
|
||||
bglegend = _themes[base][:bglegend],
|
||||
bginside = _themes[base][:bginside],
|
||||
bgoutside = _themes[base][:bgoutside],
|
||||
fg = _themes[base][:fg],
|
||||
fglegend = _themes[base][:fglegend],
|
||||
fggrid = _themes[base][:fggrid],
|
||||
fgaxis = _themes[base][:fgaxis],
|
||||
fgtext = _themes[base][:fgtext],
|
||||
fgborder = _themes[base][:fgborder],
|
||||
fgguide = _themes[base][:fgguide],
|
||||
kw...)
|
||||
_themes[sym] = merge(KW(
|
||||
:bg => bg,
|
||||
:bglegend => bglegend,
|
||||
:bginside => bginside,
|
||||
:bgoutside => bgoutside,
|
||||
:fg => fg,
|
||||
:fglegend => fglegend,
|
||||
:fggrid => fggrid,
|
||||
:fgaxis => fgaxis,
|
||||
:fgtext => fgtext,
|
||||
:fgborder => fgborder,
|
||||
:fgguide => fgguide,
|
||||
), KW(kw))
|
||||
end
|
||||
|
||||
add_theme(:ggplot2,
|
||||
bglegend = _invisible,
|
||||
bginside = :lightgray,
|
||||
fg = :white,
|
||||
fglegend = _invisible,
|
||||
fgtext = :gray,
|
||||
fgguide = :black
|
||||
)
|
||||
|
||||
function set_theme(sym::Symbol)
|
||||
default(; _themes[sym]...)
|
||||
end
|
||||
+67
-30
@@ -1,55 +1,92 @@
|
||||
|
||||
# TODO: I declare lots of types here because of the lacking ability to do forward declarations in current Julia
|
||||
# I should move these to the relevant files when something like "extern" is implemented
|
||||
|
||||
typealias AVec AbstractVector
|
||||
typealias AMat AbstractMatrix
|
||||
typealias KW Dict{Symbol,Any}
|
||||
|
||||
immutable PlotsDisplay <: Display end
|
||||
|
||||
abstract AbstractBackend
|
||||
abstract AbstractPlot{T<:AbstractBackend}
|
||||
abstract AbstractLayout
|
||||
|
||||
typealias KW Dict{Symbol,Any}
|
||||
# -----------------------------------------------------------
|
||||
|
||||
immutable InputWrapper{T}
|
||||
obj::T
|
||||
end
|
||||
|
||||
wrap{T}(obj::T) = InputWrapper{T}(obj)
|
||||
Base.isempty(wrapper::InputWrapper) = false
|
||||
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Plot
|
||||
|
||||
# a single subplot
|
||||
type Subplot{T<:AbstractBackend} <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
minpad::Tuple # leftpad, toppad, rightpad, bottompad
|
||||
bbox::BoundingBox # the canvas area which is available to this subplot
|
||||
plotarea::BoundingBox # the part where the data goes
|
||||
attr::KW # args specific to this subplot
|
||||
o # can store backend-specific data... like a pyplot ax
|
||||
plt # the enclosing Plot object (can't give it a type because of no forward declarations)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# simple wrapper around a KW so we can hold all attributes pertaining to the axis in one place
|
||||
type Axis
|
||||
sp::Subplot
|
||||
d::KW
|
||||
end
|
||||
|
||||
type Extrema
|
||||
emin::Float64
|
||||
emax::Float64
|
||||
end
|
||||
Extrema() = Extrema(Inf, -Inf)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
typealias SubplotMap Dict{Any, Subplot}
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
type Series
|
||||
d::KW
|
||||
end
|
||||
|
||||
attr(series::Series, k::Symbol) = series.d[k]
|
||||
attr!(series::Series, v, k::Symbol) = (series.d[k] = v)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
type Plot{T<:AbstractBackend} <: AbstractPlot{T}
|
||||
o # the backend's plot object
|
||||
backend::T # the backend type
|
||||
n::Int # number of series
|
||||
plotargs::KW # arguments for the whole plot
|
||||
seriesargs::Vector{KW} # arguments for each series
|
||||
backend::T # the backend type
|
||||
n::Int # number of series
|
||||
attr::KW # arguments for the whole plot
|
||||
user_attr::KW # raw arg inputs (after aliases). these are used as the input dict in `_plot!`
|
||||
series_list::Vector{Series} # arguments for each series
|
||||
o # the backend's plot object
|
||||
subplots::Vector{Subplot}
|
||||
spmap::SubplotMap # provide any label as a map to a subplot
|
||||
layout::AbstractLayout
|
||||
init::Bool
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Layout
|
||||
# -----------------------------------------------------------
|
||||
|
||||
abstract SubplotLayout
|
||||
|
||||
# -----------------------------------------------------------
|
||||
# Subplot
|
||||
# -----------------------------------------------------------
|
||||
|
||||
type Subplot{T<:AbstractBackend, L<:SubplotLayout} <: AbstractPlot{T}
|
||||
o # the underlying object
|
||||
plts::Vector{Plot{T}} # the individual plots
|
||||
backend::T
|
||||
p::Int # number of plots
|
||||
n::Int # number of series
|
||||
layout::L
|
||||
plotargs::KW
|
||||
initialized::Bool
|
||||
linkx::Bool
|
||||
linky::Bool
|
||||
linkfunc::Function # maps (row,column) -> (BoolOrNothing, BoolOrNothing)... if xlink/ylink are nothing, then use subplt.linkx/y
|
||||
function Plot()
|
||||
Plot(backend(), 0, KW(), KW(), Series[], nothing,
|
||||
Subplot[], SubplotMap(), EmptyLayout(), false)
|
||||
end
|
||||
|
||||
# TODO: make a decision... should plt[1] return the first subplot or the first series??
|
||||
# Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
|
||||
Base.getindex(plt::Plot, s::Symbol) = plt.spmap[s]
|
||||
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
|
||||
attr(plt::Plot, k::Symbol) = plt.attr[k]
|
||||
attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
+111
-64
@@ -25,7 +25,7 @@ function histogramHack(; kw...)
|
||||
edges, midpoints, buckets, counts = binData(d[:y], d[:bins])
|
||||
d[:x] = midpoints
|
||||
d[:y] = float(counts)
|
||||
d[:linetype] = :bar
|
||||
d[:seriestype] = :bar
|
||||
d[:fillrange] = d[:fillrange] == nothing ? 0.0 : d[:fillrange]
|
||||
d
|
||||
end
|
||||
@@ -64,7 +64,7 @@ function barHack(; kw...)
|
||||
|
||||
d[:x] = x
|
||||
d[:y] = y
|
||||
d[:linetype] = :path
|
||||
d[:seriestype] = :path
|
||||
d[:fillrange] = fillrange
|
||||
d
|
||||
end
|
||||
@@ -94,12 +94,12 @@ function sticksHack(; kw...)
|
||||
# change the line args
|
||||
dLine[:x] = x
|
||||
dLine[:y] = y
|
||||
dLine[:linetype] = :path
|
||||
dLine[:seriestype] = :path
|
||||
dLine[:markershape] = :none
|
||||
dLine[:fillrange] = nothing
|
||||
|
||||
# change the scatter args
|
||||
dScatter[:linetype] = :none
|
||||
dScatter[:seriestype] = :none
|
||||
|
||||
dLine, dScatter
|
||||
end
|
||||
@@ -114,6 +114,27 @@ function regressionXY(x, y)
|
||||
regx, regy
|
||||
end
|
||||
|
||||
function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
|
||||
@show T, size(z)
|
||||
n, m = size(z)
|
||||
# idx = 0
|
||||
colors = ColorGradient(vec(z))
|
||||
newz = reshape(linspace(0, 1, n*m), n, m)
|
||||
newz, colors
|
||||
# newz = zeros(n, m)
|
||||
# for i=1:n, j=1:m
|
||||
# push!(colors, T(z[i,j]...))
|
||||
# newz[i,j] = idx / (n*m-1)
|
||||
# idx += 1
|
||||
# end
|
||||
# newz, ColorGradient(colors)
|
||||
end
|
||||
|
||||
function imageHack(d::KW)
|
||||
:heatmap in supportedTypes() || error("Neither :image or :heatmap are supported!")
|
||||
d[:seriestype] = :heatmap
|
||||
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
|
||||
end
|
||||
# ---------------------------------------------------------------
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
@@ -135,13 +156,19 @@ maketuple{T,S}(x::@compat(Tuple{T,S})) = x
|
||||
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
||||
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
|
||||
|
||||
unzip{T,S}(xy::AVec{Tuple{T,S}}) = [x[1] for x in xy], [y[2] for y in xy]
|
||||
unzip{T,S,R}(xyz::AVec{Tuple{T,S,R}}) = [x[1] for x in xyz], [y[2] for y in xyz], [z[3] for z in xyz]
|
||||
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[x[1] for x in xy], T[y[2] for y in xy]
|
||||
unzip{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t in xy]
|
||||
unzip{X,Y,Z}(xyz::AVec{Tuple{X,Y,Z}}) = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
|
||||
unzip{X,Y,U,V}(xyuv::AVec{Tuple{X,Y,U,V}}) = [t[1] for t in xyuv], [t[2] for t in xyuv], [t[3] for t in xyuv], [t[4] for t in xyuv]
|
||||
|
||||
unzip{T}(xy::AVec{FixedSizeArrays.Vec{2,T}}) = T[t[1] for t in xy], T[t[2] for t in xy]
|
||||
unzip{T}(xy::FixedSizeArrays.Vec{2,T}) = T[xy[1]], T[xy[2]]
|
||||
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[x[1] for x in xyz], T[y[2] for y in xyz], T[z[3] for z in xyz]
|
||||
|
||||
unzip{T}(xyz::AVec{FixedSizeArrays.Vec{3,T}}) = T[t[1] for t in xyz], T[t[2] for t in xyz], T[t[3] for t in xyz]
|
||||
unzip{T}(xyz::FixedSizeArrays.Vec{3,T}) = T[xyz[1]], T[xyz[2]], T[xyz[3]]
|
||||
|
||||
unzip{T}(xyuv::AVec{FixedSizeArrays.Vec{4,T}}) = T[t[1] for t in xyuv], T[t[2] for t in xyuv], T[t[3] for t in xyuv], T[t[4] for t in xyuv]
|
||||
unzip{T}(xyuv::FixedSizeArrays.Vec{4,T}) = T[xyuv[1]], T[xyuv[2]], T[xyuv[3]], T[xyuv[4]]
|
||||
|
||||
# given 2-element lims and a vector of data x, widen lims to account for the extrema of x
|
||||
function _expand_limits(lims, x)
|
||||
try
|
||||
@@ -172,13 +199,20 @@ function replaceType(vec, val)
|
||||
push!(vec, val)
|
||||
end
|
||||
|
||||
function replaceAlias!(d::KW, k::Symbol, aliases::KW)
|
||||
if haskey(aliases, k)
|
||||
d[aliases[k]] = pop!(d, k)
|
||||
end
|
||||
end
|
||||
|
||||
function replaceAliases!(d::KW, aliases::KW)
|
||||
ks = collect(keys(d))
|
||||
for k in ks
|
||||
if haskey(aliases, k)
|
||||
d[aliases[k]] = d[k]
|
||||
delete!(d, k)
|
||||
end
|
||||
replaceAlias!(d, k, aliases)
|
||||
# if haskey(aliases, k)
|
||||
# d[aliases[k]] = d[k]
|
||||
# delete!(d, k)
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -201,7 +235,7 @@ end
|
||||
function fakedata(sz...)
|
||||
y = zeros(sz...)
|
||||
for r in 2:size(y,1)
|
||||
y[r,:] = 0.95 * y[r-1,:] + randn(size(y,2))'
|
||||
y[r,:] = 0.95 * vec(y[r-1,:]) + randn(size(y,2))
|
||||
end
|
||||
y
|
||||
end
|
||||
@@ -209,6 +243,14 @@ end
|
||||
isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited
|
||||
isatom() = isdefined(Main, :Atom) && Main.Atom.isconnected()
|
||||
|
||||
function is_installed(pkgstr::AbstractString)
|
||||
try
|
||||
Pkg.installed(pkgstr) === nothing ? false: true
|
||||
catch
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
istuple(::Tuple) = true
|
||||
istuple(::Any) = false
|
||||
isvector(::AVec) = true
|
||||
@@ -232,8 +274,8 @@ limsType{T<:Real,S<:Real}(lims::@compat(Tuple{T,S})) = :limits
|
||||
limsType(lims::Symbol) = lims == :auto ? :auto : :invalid
|
||||
limsType(lims) = :invalid
|
||||
|
||||
axis_symbol(letter, postfix) = symbol(letter * postfix)
|
||||
axis_symbols(letter, postfix...) = map(s -> axis_symbol(letter, s), postfix)
|
||||
# axis_Symbol(letter, postfix) = Symbol(letter * postfix)
|
||||
# axis_symbols(letter, postfix...) = map(s -> axis_Symbol(letter, s), postfix)
|
||||
|
||||
Base.convert{T<:Real}(::Type{Vector{T}}, rng::Range{T}) = T[x for x in rng]
|
||||
Base.convert{T<:Real,S<:Real}(::Type{Vector{T}}, rng::Range{S}) = T[x for x in rng]
|
||||
@@ -252,6 +294,40 @@ function indices_and_unique_values(z::AbstractArray)
|
||||
newz, vals
|
||||
end
|
||||
|
||||
# this is a helper function to determine whether we need to transpose a surface matrix.
|
||||
# it depends on whether the backend matches rows to x (transpose_on_match == true) or vice versa
|
||||
# for example: PyPlot sends rows to y, so transpose_on_match should be true
|
||||
function transpose_z(d::KW, z, transpose_on_match::Bool = true)
|
||||
if d[:match_dimensions] == transpose_on_match
|
||||
z'
|
||||
else
|
||||
z
|
||||
end
|
||||
end
|
||||
|
||||
function ok(x::Number, y::Number, z::Number = 0)
|
||||
isfinite(x) && isfinite(y) && isfinite(z)
|
||||
end
|
||||
ok(tup::Tuple) = ok(tup...)
|
||||
|
||||
# compute one side of a fill range from a ribbon
|
||||
function make_fillrange_side(y, rib)
|
||||
frs = zeros(length(y))
|
||||
for (i, (yi, ri)) in enumerate(zip(y, cycle(rib)))
|
||||
frs[i] = yi + ri
|
||||
end
|
||||
frs
|
||||
end
|
||||
|
||||
# turn a ribbon into a fillrange
|
||||
function make_fillrange_from_ribbon(kw::KW)
|
||||
y, rib = kw[:y], kw[:ribbon]
|
||||
rib = wraptuple(rib)
|
||||
rib1, rib2 = -first(rib), last(rib)
|
||||
kw[:ribbon] = nothing
|
||||
kw[:fillrange] = make_fillrange_side(y, rib1), make_fillrange_side(y, rib2)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
wraptuple(x::@compat(Tuple)) = x
|
||||
@@ -276,7 +352,7 @@ allFunctions(arg) = trueOrAllTrue(a -> isa(a, Function), arg)
|
||||
"""
|
||||
Allows temporary setting of backend and defaults for Plots. Settings apply only for the `do` block. Example:
|
||||
```
|
||||
with(:gadfly, size=(400,400), type=:hist) do
|
||||
with(:gadfly, size=(400,400), type=:histogram) do
|
||||
plot(rand(10))
|
||||
plot(rand(10))
|
||||
end
|
||||
@@ -315,7 +391,7 @@ function with(f::Function, args...; kw...)
|
||||
# # as in: with(:gadfly, :scatter, :legend, :grid) do; ...; end
|
||||
# # TODO: can we generalize this enough to also do something similar in the plot commands??
|
||||
|
||||
# k = :linetype
|
||||
# k = :seriestype
|
||||
# if arg in _allTypes
|
||||
# olddefs[k] = default(k)
|
||||
# newdefs[k] = arg
|
||||
@@ -386,6 +462,7 @@ function dumpdict(d::KW, prefix = "", alwaysshow = false)
|
||||
end
|
||||
println()
|
||||
end
|
||||
DD(d::KW, prefix = "") = dumpdict(d, prefix, true)
|
||||
|
||||
|
||||
function dumpcallstack()
|
||||
@@ -411,24 +488,32 @@ tovec(v::AbstractVector) = v
|
||||
tovec(v::Void) = zeros(0)
|
||||
|
||||
function getxy(plt::Plot, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d = plt.series_list[i].d
|
||||
tovec(d[:x]), tovec(d[:y])
|
||||
end
|
||||
function getxyz(plt::Plot, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d = plt.series_list[i].d
|
||||
tovec(d[:x]), tovec(d[:y]), tovec(d[:z])
|
||||
end
|
||||
|
||||
function setxy!{X,Y}(plt::Plot, xy::Tuple{X,Y}, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y] = xy
|
||||
series = plt.series_list[i]
|
||||
series.d[:x], series.d[:y] = xy
|
||||
sp = series.d[:subplot]
|
||||
expand_extrema!(sp.attr[:xaxis], xy[1])
|
||||
expand_extrema!(sp.attr[:yaxis], xy[2])
|
||||
_series_updated(plt, series)
|
||||
end
|
||||
function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
|
||||
d = plt.seriesargs[i]
|
||||
d[:x], d[:y], d[:z] = xyz
|
||||
series = plt.series_list[i]
|
||||
series.d[:x], series.d[:y], series.d[:z] = xyz
|
||||
sp = series.d[:subplot]
|
||||
expand_extrema!(sp.attr[:xaxis], xy[1])
|
||||
expand_extrema!(sp.attr[:yaxis], xy[2])
|
||||
expand_extrema!(sp.attr[:zaxis], xy[3])
|
||||
_series_updated(plt, series)
|
||||
end
|
||||
|
||||
|
||||
# -------------------------------------------------------
|
||||
# indexing notation
|
||||
|
||||
@@ -535,44 +620,6 @@ end
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# ---------------------------------------------------------------
|
||||
# graphs detailing the features that each backend supports
|
||||
|
||||
function supportGraph(allvals, func)
|
||||
vals = reverse(sort(allvals))
|
||||
bs = sort(backends())
|
||||
x = ASCIIString[]
|
||||
y = ASCIIString[]
|
||||
for val in vals
|
||||
for b in bs
|
||||
supported = func(Plots._backend_instance(b))
|
||||
if val in supported
|
||||
push!(x, string(b))
|
||||
push!(y, string(val))
|
||||
end
|
||||
end
|
||||
end
|
||||
n = length(vals)
|
||||
scatter(x, y, m=:rect, ms=10, size=(300,100+18*n), leg=false)
|
||||
end
|
||||
|
||||
supportGraphArgs() = supportGraph(_allArgs, supportedArgs)
|
||||
supportGraphTypes() = supportGraph(_allTypes, supportedTypes)
|
||||
supportGraphStyles() = supportGraph(_allStyles, supportedStyles)
|
||||
supportGraphMarkers() = supportGraph(_allMarkers, supportedMarkers)
|
||||
supportGraphScales() = supportGraph(_allScales, supportedScales)
|
||||
supportGraphAxes() = supportGraph(_allAxes, supportedAxes)
|
||||
|
||||
function dumpSupportGraphs()
|
||||
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
|
||||
supportGraphMarkers, supportGraphScales, supportGraphAxes)
|
||||
plt = func()
|
||||
png(joinpath(Pkg.dir("ExamplePlots"), "docs", "examples", "img", "supported", "$(string(func))"))
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -593,9 +640,9 @@ mm2px(mm::Real) = float(px / MM_PER_PX)
|
||||
|
||||
|
||||
"Smallest x in plot"
|
||||
xmin(plt::Plot) = minimum([minimum(d[:x]) for d in plt.seriesargs])
|
||||
xmin(plt::Plot) = minimum([minimum(series.d[:x]) for series in plt.series_list])
|
||||
"Largest x in plot"
|
||||
xmax(plt::Plot) = maximum([maximum(d[:x]) for d in plt.seriesargs])
|
||||
xmax(plt::Plot) = maximum([maximum(series.d[:x]) for series in plt.series_list])
|
||||
|
||||
"Extrema of x-values in plot"
|
||||
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))
|
||||
|
||||
+3
-3
@@ -1,11 +1,10 @@
|
||||
julia 0.4
|
||||
|
||||
RecipesBase
|
||||
Colors
|
||||
Reexport
|
||||
Requires
|
||||
Measures
|
||||
FactCheck
|
||||
Cairo
|
||||
Gadfly
|
||||
Images
|
||||
PyPlot
|
||||
@osx QuartzImageIO
|
||||
@@ -14,3 +13,4 @@ DataFrames
|
||||
RDatasets
|
||||
VisualRegressionTests
|
||||
UnicodePlots
|
||||
Glob
|
||||
|
||||
+57
-26
@@ -1,6 +1,6 @@
|
||||
|
||||
using VisualRegressionTests
|
||||
using ExamplePlots
|
||||
# using ExamplePlots
|
||||
|
||||
import DataFrames, RDatasets
|
||||
|
||||
@@ -14,6 +14,7 @@ end
|
||||
|
||||
|
||||
using Plots, FactCheck
|
||||
using Glob
|
||||
|
||||
default(size=(500,300))
|
||||
|
||||
@@ -21,37 +22,67 @@ default(size=(500,300))
|
||||
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
||||
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
||||
|
||||
const _current_plots_version = v"0.7.0"
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||
Plots._debugMode.on = debug
|
||||
example = Plots._examples[idx]
|
||||
info("Testing plot: $pkg:$idx:$(example.header)")
|
||||
backend(pkg)
|
||||
backend()
|
||||
|
||||
# first
|
||||
Plots._debugMode.on = debug
|
||||
example = ExamplePlots._examples[idx]
|
||||
info("Testing plot: $pkg:$idx:$(example.header)")
|
||||
backend(pkg)
|
||||
backend()
|
||||
# ensure consistent results
|
||||
srand(1234)
|
||||
|
||||
# ensure consistent results
|
||||
srand(1234)
|
||||
# reference image directory setup
|
||||
# refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
|
||||
refdir = Pkg.dir("PlotReferenceImages", "Plots", string(pkg))
|
||||
fn = "ref$idx.png"
|
||||
|
||||
# reference image directory setup
|
||||
refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
|
||||
# firgure out version info
|
||||
G = glob(relpath(refdir) * "/*")
|
||||
# @show refdir fn G
|
||||
versions = map(fn -> VersionNumber(split(fn,"/")[end]), G)
|
||||
versions = reverse(sort(versions))
|
||||
# @show refdir fn versions
|
||||
|
||||
# test function
|
||||
func = (fn, idx) -> begin
|
||||
map(eval, example.exprs)
|
||||
png(fn)
|
||||
end
|
||||
reffn = nothing
|
||||
newdir = joinpath(refdir, string(_current_plots_version))
|
||||
newfn = joinpath(newdir, fn)
|
||||
for v in versions
|
||||
try
|
||||
tmpfn = joinpath(refdir, string(v), fn)
|
||||
# @show "trying", tmpfn
|
||||
f = open(tmpfn)
|
||||
reffn = tmpfn
|
||||
end
|
||||
end
|
||||
|
||||
try
|
||||
run(`mkdir -p $refdir`)
|
||||
catch err
|
||||
display(err)
|
||||
end
|
||||
reffn = joinpath(refdir, "ref$idx.png")
|
||||
# now we have the fn (if any)... do the comparison
|
||||
# @show reffn
|
||||
if reffn == nothing
|
||||
reffn = newfn
|
||||
end
|
||||
# @show reffn
|
||||
# return
|
||||
|
||||
# the test
|
||||
vtest = VisualTest(func, reffn, idx)
|
||||
test_images(vtest, popup=popup, sigma=sigma, eps=eps)
|
||||
# test function
|
||||
func = (fn, idx) -> begin
|
||||
map(eval, example.exprs)
|
||||
png(fn)
|
||||
end
|
||||
|
||||
try
|
||||
run(`mkdir -p $newdir`)
|
||||
catch err
|
||||
display(err)
|
||||
end
|
||||
# reffn = joinpath(refdir, "ref$idx.png")
|
||||
|
||||
# the test
|
||||
vtest = VisualTest(func, reffn, idx)
|
||||
test_images(vtest, popup=popup, sigma=sigma, eps=eps, newfn = newfn)
|
||||
end
|
||||
|
||||
function image_comparison_facts(pkg::Symbol;
|
||||
@@ -60,7 +91,7 @@ function image_comparison_facts(pkg::Symbol;
|
||||
debug = false, # print debug information?
|
||||
sigma = [1,1], # number of pixels to "blur"
|
||||
eps = 1e-2) # acceptable error (percent)
|
||||
for i in 1:length(ExamplePlots._examples)
|
||||
for i in 1:length(Plots._examples)
|
||||
i in skip && continue
|
||||
if only == nothing || i in only
|
||||
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
|
||||
|
||||
+28
-13
@@ -7,30 +7,30 @@ srand(1234)
|
||||
default(show=false, reuse=true)
|
||||
img_eps = 5e-2
|
||||
|
||||
facts("Gadfly") do
|
||||
@fact gadfly() --> Plots.GadflyBackend()
|
||||
@fact backend() --> Plots.GadflyBackend()
|
||||
|
||||
@fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
|
||||
@fact plot(Int[1,2,3], rand(3)) --> not(nothing)
|
||||
@fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
|
||||
@fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
|
||||
|
||||
image_comparison_facts(:gadfly, skip=[4,6,19,23,24,27], eps=img_eps)
|
||||
end
|
||||
# facts("Gadfly") do
|
||||
# @fact gadfly() --> Plots.GadflyBackend()
|
||||
# @fact backend() --> Plots.GadflyBackend()
|
||||
#
|
||||
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyBackend}
|
||||
# @fact plot(Int[1,2,3], rand(3)) --> not(nothing)
|
||||
# @fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
|
||||
# @fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
|
||||
#
|
||||
# image_comparison_facts(:gadfly, skip=[4,6,23,24,27], eps=img_eps)
|
||||
# end
|
||||
|
||||
facts("PyPlot") do
|
||||
@fact pyplot() --> Plots.PyPlotBackend()
|
||||
@fact backend() --> Plots.PyPlotBackend()
|
||||
|
||||
image_comparison_facts(:pyplot, skip=[19], eps=img_eps)
|
||||
image_comparison_facts(:pyplot, skip=[30], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("GR") do
|
||||
@fact gr() --> Plots.GRBackend()
|
||||
@fact backend() --> Plots.GRBackend()
|
||||
|
||||
@linux_only image_comparison_facts(:gr, skip=[24], eps=img_eps)
|
||||
@linux_only image_comparison_facts(:gr, skip=[30], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("Plotly") do
|
||||
@@ -70,6 +70,21 @@ end
|
||||
|
||||
|
||||
|
||||
facts("Axes") do
|
||||
p = plot()
|
||||
axis = p.subplots[1][:xaxis]
|
||||
@fact typeof(axis) --> Axis
|
||||
@fact Plots.discrete_value!(axis, "HI") --> (0.5, 1)
|
||||
@fact Plots.discrete_value!(axis, :yo) --> (1.5, 2)
|
||||
@fact extrema(axis) --> (0.5,1.5)
|
||||
@fact axis[:discrete_map] --> Dict{Any,Any}(:yo => 2, "HI" => 1)
|
||||
|
||||
Plots.discrete_value!(axis, ["x$i" for i=1:5])
|
||||
Plots.discrete_value!(axis, ["x$i" for i=0:2])
|
||||
@fact extrema(axis) --> (0.5, 7.5)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
FactCheck.exitstatus()
|
||||
|
||||
@@ -7,7 +7,9 @@ Pkg.build("ImageMagick")
|
||||
Pkg.clone("GR")
|
||||
Pkg.build("GR")
|
||||
|
||||
Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
|
||||
# Pkg.clone("https://github.com/JuliaPlots/RecipesBase.jl.git")
|
||||
# Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
|
||||
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
|
||||
|
||||
# Pkg.clone("https://github.com/JunoLab/Blink.jl.git")
|
||||
# Pkg.build("Blink")
|
||||
@@ -15,6 +17,9 @@ Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")
|
||||
|
||||
Pkg.checkout("RecipesBase")
|
||||
Pkg.clone("VisualRegressionTests")
|
||||
|
||||
ENV["PYTHON"] = ""
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.build("PyPlot")
|
||||
|
||||
Reference in New Issue
Block a user