Merge updated glvisualize

This commit is contained in:
Sheehan Olver 2016-03-11 10:44:51 +11:00
commit 552810359e
91 changed files with 2424 additions and 1016 deletions

View File

@ -13,8 +13,8 @@ script:
- if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots")'
# - julia -e 'Pkg.clone("https://github.com/tbreloff/Images.jl.git"); Pkg.checkout("Images","tom_imgcompare");'
- julia -e 'Pkg.clone("Images"); Pkg.build("Images")'
- julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
# - julia -e 'Pkg.clone("Images"); Pkg.build("Images")'
# - julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")'
- julia -e 'Pkg.clone("https://github.com/tbreloff/VisualRegressionTests.jl.git");'
- julia -e 'Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");'
- julia -e 'Pkg.clone("Cairo"); Pkg.build("Cairo")'

397
README.md
View File

@ -18,399 +18,4 @@ Plots is a plotting API and toolset. My goals with the package are:
Use the preprocessing pipeline in Plots to fully describe your visualization before it calls the backend code. This maintains modularity and allows for efficient separation of front end code, algorithms, and backend graphics. New graphical backends can be added with minimal effort.
Check out the [summary graphs](img/supported/supported.md) for the features that each backend supports.
Please add wishlist items, bugs, or any other comments/questions to the issues list.
## Examples for each implemented backend:
- [Gadfly.jl/Immerse.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/gadfly_examples.md)
- [PyPlot.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/pyplot_examples.md)
- [UnicodePlots.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/unicodeplots_examples.md)
- [Qwt.jl](https://github.com/tbreloff/ExamplePlots.jl/tree/master/docs/qwt_examples.md)
Also check out the many [IJulia notebooks](http://nbviewer.ipython.org/github/tbreloff/ExamplePlots.jl/tree/master/examples/) with many examples.
## Installation
First, add the package
```julia
Pkg.add("Plots")
# if you want the latest features:
Pkg.checkout("Plots")
# or for the bleeding edge:
Pkg.checkout("Plots", "dev")
```
then get any plotting packages you need (obviously, you should get at least one backend).
```julia
Pkg.add("Gadfly")
Pkg.add("Immerse")
Pkg.add("PyPlot")
Pkg.add("UnicodePlots")
Pkg.add("Qwt")
Pkg.add("Bokeh")
```
## Use
Load it in. The underlying plotting backends are not imported until `backend()` is called (which happens
on your first call to `plot` or `subplot`). This means that you don't need any backends to be installed when you call `using Plots`.
Plots will try to figure out a good default backend for you automatically based on what backends are installed.
```julia
using Plots
```
Do a plot in Gadfly (inspired by [this example](http://gadflyjl.org/geom_point.html)), then save a png:
```julia
gadfly() # switch to Gadfly as a backend
dataframes() # turn on support for DataFrames inputs
# load some data
using RDatasets
iris = dataset("datasets", "iris");
# This will bring up a browser window with the plot. Add a semicolon at the end to skip display.
scatter(iris, :SepalLength, :SepalWidth, group=:Species, m=([:+ :d :s], 12), smooth=0.99, bg=:black)
# save a png (equivalent to png("gadfly1.png") and savefig("gadfly1.png"))
png("gadfly1")
```
![gadfly_plt](img/gadfly1.png)
## API
Call `backend(backend::Symbol)` or the shorthands (`gadfly()`, `qwt()`, `unicodeplots()`, etc) to set the current plotting backend.
Subsequent commands are converted into the relevant plotting commands for that package:
```julia
gadfly()
plot(1:10) # this effectively calls `y = 1:10; Gadfly.plot(x=1:length(y), y=y)`
qwt()
plot(1:10) # this effectively calls `Qwt.plot(1:10)`
```
Use `plot` to create a new plot object, and `plot!` to add to an existing one:
```julia
plot(args...; kw...) # creates a new plot window, and sets it to be the `current`
plot!(args...; kw...) # adds to the `current`
plot!(plotobj, args...; kw...) # adds to the plot `plotobj`
```
Now that you know which plot object you're updating (new, current, or other), I'll leave it off for simplicity.
There are many ways to pass in data to the plot functions... some examples:
- Vector-like (subtypes of AbstractArray{T,1})
- Matrix-like (subtypes of AbstractArray{T,2})
- Vectors of Vectors
- Functions
- Vectors of Functions
- DataFrames with column symbols (initialize with `dataframes()`)
In general, you can pass in a `y` only, or an `x` and `y`, both of whatever type(s) you want, and Plots will slice up the data as needed.
For matrices, data is split by columns. For functions, data is mapped. For DataFrames, a Symbol/Symbols in place of x/y will map to
the relevant column(s).
Here are some example usages... remember you can always use `plot!` to update an existing plot, and that, unless specified, you will update the `current()`.
```julia
plot() # empty plot object
plot(4) # initialize with 4 empty series
plot(rand(10)) # plot 1 series... x = 1:10
plot(rand(10,5)) # plot 5 series... x = 1:10
plot(rand(10), rand(10)) # plot 1 series
plot(rand(10,5), rand(10)) # plot 5 series... y is the same for all
plot(sin, rand(10)) # y = sin(x)
plot(rand(10), sin) # same... y = sin(x)
plot([sin,cos], 0:0.1:π) # plot 2 series, sin(x) and cos(x)
plot([sin,cos], 0, π) # plot sin and cos on the range [0, π]
plot(1:10, Any[rand(10), sin]) # plot 2 series, y = rand(10) for the first, y = sin(x) for the second... x = 1:10 for both
plot(dataset("Ecdat", "Airline"), :Cost) # plot from a DataFrame (call `dataframes()` first to import DataFrames and initialize)
```
All plot methods accept a number of keyword arguments (see the tables below), which follow some rules:
- Many arguments have aliases which are replaced during preprocessing. `c` is the same as `color`, `m` is the same as `marker`, etc. You can choose how verbose you'd like to be. (see the tables below)
- There are some special arguments (`xaxis`, `yaxis`, `line`, `marker`, `fill` and the aliases `l`, `m`, `f`) which magically set many related things at once. (see the __Tip__ below)
- If the argument is a "matrix-type", then each column will map to a series, cycling through columns if there are fewer columns than series. Anything else will apply the argument value to every series.
- Many arguments accept many different types... for example the `color` (also `markercolor`, `fillcolor`, etc) argument will accept strings or symbols with a color name, or any `Colors.Colorant`, or a `ColorScheme`, or a symbol representing a `ColorGradient`, or an AbstractVector of colors/symbols/etc...
You can update certain plot settings after plot creation (not supported on all backends):
```julia
plot!(title = "New Title", xlabel = "New xlabel", ylabel = "New ylabel")
plot!(xlims = (0, 5.5), ylims = (-2.2, 6), xticks = 0:0.5:10, yticks = [0,1,5,10])
# using shorthands:
xaxis!("mylabel", :log10, :flip)
```
With `subplot`, create multiple plots at once, with flexible layout options:
```julia
y = rand(100,3)
subplot(y; n = 3) # create an automatic grid, and let it figure out the shape
subplot(y; n = 3, nr = 1) # create an automatic grid, but fix the number of rows
subplot(y; n = 3, nc = 1) # create an automatic grid, but fix the number of columns
subplot(y; layout = [1, 2]) # explicit layout. Lists the number of plots in each row
```
__Tip__: You can call `subplot!(args...; kw...)` to add to an existing subplot.
__Tip__: Calling `subplot!` on a `Plot` object, or `plot!` on a `Subplot` object will throw an error.
Shorthands:
```julia
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)
histogram(args...; kw...) = plot(args...; kw..., linetype = :hist)
histogram!(args...; kw...) = plot!(args...; kw..., linetype = :hist)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
heatmap!(args...; kw...) = plot!(args...; kw..., linetype = :heatmap)
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)
title!(s::AbstractString) = plot!(title = s)
xlabel!(s::AbstractString) = plot!(xlabel = s)
ylabel!(s::AbstractString) = plot!(ylabel = s)
xlims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(xlims = lims)
ylims!{T<:Real,S<:Real}(lims::Tuple{T,S}) = plot!(ylims = lims)
xticks!{T<:Real}(v::AVec{T}) = plot!(xticks = v)
yticks!{T<:Real}(v::AVec{T}) = plot!(yticks = v)
xflip!(flip::Bool = true) = plot!(xflip = flip)
yflip!(flip::Bool = true) = plot!(yflip = flip)
xaxis!(args...) = plot!(xaxis = args)
yaxis!(args...) = plot!(yaxis = args)
annotate!(anns) = plot!(annotation = anns)
```
### Keyword arguments:
Keyword | Default | Type | Aliases
---- | ---- | ---- | ----
`:annotation` | `nothing` | Series | `:ann`, `:annotate`, `:annotations`, `:anns`
`:axis` | `left` | Series | `:axiss`
`:background_color` | `RGB{U8}(1.0,1.0,1.0)` | Plot | `:background`, `:background_colour`, `:bg`, `:bg_color`, `:bgcolor`
`:color_palette` | `auto` | Plot | `:palette`
`:fill` | `nothing` | Series | `:area`, `:f`
`:fillalpha` | `nothing` | Series | `:fa`, `:fillalphas`, `:fillopacity`
`:fillcolor` | `match` | Series | `:fc`, `:fcolor`, `:fillcolors`, `:fillcolour`
`:fillrange` | `nothing` | Series | `:fillranges`, `:fillrng`
`:foreground_color` | `auto` | Plot | `:fg`, `:fg_color`, `:fgcolor`, `:foreground`, `:foreground_colour`
`:grid` | `true` | Plot |
`:group` | `nothing` | Series | `:g`, `:groups`
`:guidefont` | `Plots.Font("Helvetica",11,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
`:label` | `AUTO` | Series | `:lab`, `:labels`
`:layout` | `nothing` | Plot |
`:legend` | `true` | Plot | `:leg`
`:legendfont` | `Plots.Font("Helvetica",8,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
`:line` | `nothing` | Series | `:l`
`:linealpha` | `nothing` | Series | `:la`, `:linealphas`, `:lineopacity`
`:linecolor` | `auto` | Series | `:c`, `:color`, `:colour`, `:linecolors`
`:linestyle` | `solid` | Series | `:linestyles`, `:ls`, `:s`, `:style`
`:linetype` | `path` | Series | `:linetypes`, `:lt`, `:t`, `:type`
`:linewidth` | `1` | Series | `:linewidths`, `:lw`, `:w`, `:width`
`:link` | `false` | Plot |
`:linkfunc` | `nothing` | Plot |
`:linkx` | `false` | Plot | `:xlink`
`:linky` | `false` | Plot | `:ylink`
`:marker` | `nothing` | Series | `:m`, `:mark`
`:markeralpha` | `nothing` | Series | `:alpha`, `:ma`, `:markeralphas`, `:markeropacity`, `:opacity`
`:markercolor` | `match` | Series | `:markercolors`, `:markercolour`, `:mc`, `:mcolor`
`:markershape` | `none` | Series | `:markershapes`, `:shape`
`:markersize` | `6` | Series | `:markersizes`, `:ms`, `:msize`
`:markerstrokealpha` | `nothing` | Series | `:markerstrokealphas`
`:markerstrokecolor` | `match` | Series | `:markerstrokecolors`
`:markerstrokestyle` | `solid` | Series | `:markerstrokestyles`
`:markerstrokewidth` | `1` | Series | `:markerstrokewidths`
`:n` | `-1` | Plot |
`:nbins` | `30` | Series | `:nb`, `:nbin`, `:nbinss`
`:nc` | `-1` | Plot |
`:nlevels` | `15` | Series | `:nlevelss`
`:nr` | `-1` | Plot |
`:pos` | `(0,0)` | Plot |
`:show` | `false` | Plot | `:display`, `:gui`
`:size` | `(500,300)` | Plot | `:windowsize`, `:wsize`
`:smooth` | `false` | Series | `:reg`, `:regression`, `:smooths`
`:surface` | `nothing` | Series | `:surfaces`
`:tickfont` | `Plots.Font("Helvetica",8,:hcenter,:vcenter,0.0,RGB{U8}(0.0,0.0,0.0))` | Plot |
`:title` | `` | Plot |
`:windowtitle` | `Plots.jl` | Plot | `:wtitle`
`:xaxis` | `nothing` | Plot |
`:xflip` | `false` | Plot |
`:xlabel` | `` | Plot | `:xlab`
`:xlims` | `auto` | Plot | `:xlim`, `:xlimit`, `:xlimits`
`:xscale` | `identity` | Plot |
`:xticks` | `auto` | Plot | `:xtick`
`:yaxis` | `nothing` | Plot |
`:yflip` | `false` | Plot |
`:ylabel` | `` | Plot | `:ylab`
`:ylims` | `auto` | Plot | `:ylim`, `:ylimit`, `:ylimits`
`:yrightlabel` | `` | Plot | `:y2lab`, `:y2label`, `:ylab2`, `:ylabel2`, `:ylabelright`, `:ylabr`, `:yrlab`
`:yscale` | `identity` | Plot |
`:yticks` | `auto` | Plot | `:ytick`
`:z` | `nothing` | Series | `:zs`
### Plot types:
Type | Desc | Aliases
---- | ---- | ----
`:none` | No line | `:n`, `:no`
`:line` | Lines with sorted x-axis | `:l`
`:path` | Lines | `:p`
`:steppre` | Step plot (vertical then horizontal) | `:stepinv`, `:stepinverted`, `:stepsinv`, `:stepsinverted`
`:steppost` | Step plot (horizontal then vertical) | `:stair`, `:stairs`, `:step`, `:steps`
`:sticks` | Vertical lines | `:stem`, `:stems`
`:scatter` | Points, no lines | `:dots`
`:heatmap` | Colored regions by density |
`:hexbin` | Similar to heatmap |
`:hist` | Histogram (doesn't use x) | `:histogram`
`:bar` | Bar plot (centered on x values) |
`:hline` | Horizontal line (doesn't use x) |
`:vline` | Vertical line (doesn't use x) |
`:ohlc` | Open/High/Low/Close chart (expects y is AbstractVector{Plots.OHLC}) |
`:contour` | Contour lines (uses z) |
`:path3d` | 3D path (uses z) | `:line3d`
`:scatter3d` | 3D scatter plot (uses z) |
### Line styles:
Type | Aliases
---- | ----
`:auto` | `:a`
`:solid` | `:s`
`:dash` | `:d`
`:dot` |
`:dashdot` | `:dd`
`:dashdotdot` | `:ddd`
### Markers:
Type | Aliases
---- | ----
`:none` | `:n`, `:no`
`:auto` | `:a`
`:cross` | `:+`, `:plus`
`:diamond` | `:d`
`:dtriangle` | `:V`, `:downtri`, `:downtriangle`, `:dt`, `:dtri`, `:v`
`:ellipse` | `:c`, `:circle`
`:heptagon` | `:hep`
`:hexagon` | `:h`, `:hex`
`:octagon` | `:o`, `:oct`
`:pentagon` | `:p`, `:pent`
`:rect` | `:r`, `:sq`, `:square`
`:star4` |
`:star5` | `:s`, `:star`, `:star1`
`:star6` |
`:star7` |
`:star8` | `:s2`, `:star2`
`:utriangle` | `:^`, `:uptri`, `:uptriangle`, `:ut`, `:utri`
`:xcross` | `:X`, `:x`
__Tip__: With supported backends, you can pass a `Plots.Shape` object for the `marker`/`markershape` arguments. `Shape` takes a vector of 2-tuples in the constructor, defining the points of the polygon's shape in a unit-scaled coordinate space. To make a square, for example, you could do `Shape([(1,1),(1,-1),(-1,-1),(-1,1)])`
__Tip__: You can see the default value for a given argument with `default(arg::Symbol)`, and set the default value with `default(arg::Symbol, value)` or `default(; kw...)`. For example set the default window size and whether we should show a legend with `default(size=(600,400), leg=false)`.
__Tip__: There are some helper arguments you can set: `xaxis`, `yaxis`, `line`, `marker`, `fill`. These go through special preprocessing to extract values into individual arguments. The order doesn't matter, and if you pass a single value it's equivalent to wrapping it in a Tuple. Examples:
```
plot(y, xaxis = ("mylabel", :log, :flip, (-1,1))) # this sets the `xlabel`, `xscale`, `xflip`, and `xlims` arguments automatically
plot(y, line = (:bar, :blue, :dot, 10)) # this sets the `linetype`, `color`, `linestyle`, and `linewidth` arguments automatically
plot(y, marker = (:rect, :red, 10)) # this sets the `markershape`, `markercolor`, and `markersize` arguments automatically
plot(y, fill = (:green, 10)) # this sets the `fillcolor` and `fillrange` arguments automatically
# Note: `fillrange` can be:
a number (fill to horizontal line)
a vector of numbers (different for each data point)
a tuple of vectors (fill a band)
```
__Tip__: When plotting multiple lines, you can set all series to use the same value, or pass in a matrix to cycle through values. Example:
```julia
plot(rand(100,4); color = [:red RGB(0,0,1)], # (Matrix) lines 1 and 3 are red, lines 2 and 4 are blue
axis = :auto, # lines 1 and 3 are on the left axis, lines 2 and 4 are on the right
markershape = [:rect, :star] # (Vector) ALL lines are passed the vector [:rect, :star1]
width = 5) # all lines have a width of 5
```
__Tip__: Not all features are supported for each backend, but you can see what's supported by calling the functions: `supportedArgs()`, `supportedAxes()`, `supportedTypes()`, `supportedStyles()`, `supportedMarkers()`, `subplotSupported()`
__Tip__: Call `gui()` to display the plot in a window. Interactivity depends on backend. Plotting at the REPL (without semicolon) implicitly calls `gui()`.
### Animations
Animations are created in 3 steps (see example #2):
- Initialize an `Animation` object.
- Save each frame of the animation with `frame(anim)`.
- Convert the frames to an animated gif with `gif(anim, filename, fps=15)`
## TODO features:
- [x] Plot vectors/matrices/functions
- [x] Plot DataFrames
- [x] Histograms
- [x] Grouping
- [x] Annotations
- [x] Scales
- [x] Categorical Inputs (strings, etc... for hist, bar? or can split one series into multiple?)
- [x] Custom markers
- [x] Animations
- [x] Subplots
- [ ] Contours
- [ ] Boxplots
- [ ] 3D plotting
- [ ] Scenes/Drawing
- [ ] Graphs
- [ ] Interactivity (GUIs)
## TODO backends:
- [x] Gadfly.jl
- [x] Immerse.jl
- [x] PyPlot.jl
- [x] UnicodePlots.jl
- [x] Qwt.jl
- [x] Winston.jl (deprecated)
- [ ] GLPlot.jl
- [ ] Bokeh.jl
- [ ] Vega.jl
- [ ] Gaston.jl
- [ ] Plotly.jl
- [ ] GoogleCharts.jl
- [ ] PLplot.jl
- [ ] TextPlots.jl
- [ ] ASCIIPlots.jl
- [ ] Sparklines.jl
- [ ] Hinton.jl
- [ ] ImageTerm.jl
- [ ] GraphViz.jl
- [ ] TikzGraphs.jl
- [ ] GraphLayout.jl
## More information on backends (both supported and unsupported)
See the wiki at: https://github.com/JuliaPlot/juliaplot_docs/wiki
View the [full documentation](http://plots.readthedocs.org).

View File

@ -3,3 +3,5 @@ julia 0.4
Colors
Reexport
Compat
Requires
FixedSizeArrays

View File

Before

Width:  |  Height:  |  Size: 86 KiB

After

Width:  |  Height:  |  Size: 86 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

Before

Width:  |  Height:  |  Size: 7.5 KiB

After

Width:  |  Height:  |  Size: 7.5 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

@ -8,6 +8,7 @@ immutable UnicodePlotsPackage <: PlottingPackage end
immutable WinstonPackage <: PlottingPackage end
immutable BokehPackage <: PlottingPackage end
immutable PlotlyPackage <: PlottingPackage end
immutable GRPackage <: PlottingPackage end
immutable GLVisualizePackage <: PlottingPackage end
immutable NoPackage <: PlottingPackage end
@ -21,18 +22,20 @@ export
unicodeplots,
bokeh,
plotly,
gr,
glvisualize
# winston
gadfly() = backend(:gadfly)
immerse() = backend(:immerse)
pyplot() = backend(:pyplot)
qwt() = backend(:qwt)
unicodeplots() = backend(:unicodeplots)
bokeh() = backend(:bokeh)
plotly() = backend(:plotly)
glvisualize() = backend(:glvisualize)
# winston() = backend(:winston)
gadfly(; kw...) = (default(; kw...); backend(:gadfly))
immerse(; kw...) = (default(; kw...); backend(:immerse))
pyplot(; kw...) = (default(; kw...); backend(:pyplot))
qwt(; kw...) = (default(; kw...); backend(:qwt))
unicodeplots(; kw...) = (default(; kw...); backend(:unicodeplots))
bokeh(; kw...) = (default(; kw...); backend(:bokeh))
plotly(; kw...) = (default(; kw...); backend(:plotly))
gr(; kw...) = (default(; kw...); backend(:gr))
glvisualize(; kw...) = (default(; kw...); backend(:glvisualize))
# winston(; kw...) = (default(; kw...); backend(:winston))
backend_name(::GadflyPackage) = :gadfly
backend_name(::ImmersePackage) = :immerse
@ -41,6 +44,7 @@ backend_name(::UnicodePlotsPackage) = :unicodeplots
backend_name(::QwtPackage) = :qwt
backend_name(::BokehPackage) = :bokeh
backend_name(::PlotlyPackage) = :plotly
backend_name(::GRPackage) = :gr
backend_name(::GLVisualizePackage) = :glvisualize
backend_name(::NoPackage) = :none
@ -56,6 +60,7 @@ include("backends/winston.jl")
include("backends/web.jl")
include("backends/bokeh.jl")
include("backends/plotly.jl")
include("backends/gr.jl")
include("backends/glvisualize.jl")
@ -77,12 +82,12 @@ subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, s
# ---------------------------------------------------------
const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly]
const INITIALIZED_BACKENDS = Set{Symbol}()
const BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly, :gr]
const _initialized_backends = Set{Symbol}()
backends() = BACKENDS
function backendInstance(sym::Symbol)
function _backend_instance(sym::Symbol)
sym == :qwt && return QwtPackage()
sym == :gadfly && return GadflyPackage()
sym == :unicodeplots && return UnicodePlotsPackage()
@ -91,6 +96,7 @@ function backendInstance(sym::Symbol)
sym == :winston && return WinstonPackage()
sym == :bokeh && return BokehPackage()
sym == :plotly && return PlotlyPackage()
sym == :gr && return GRPackage()
sym == :glvisualize && return GLVisualizePackage()
sym == :none && return NoPackage()
error("Unsupported backend $sym")
@ -101,12 +107,12 @@ type CurrentBackend
sym::Symbol
pkg::PlottingPackage
end
CurrentBackend(sym::Symbol) = CurrentBackend(sym, backendInstance(sym))
CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
# ---------------------------------------------------------
function pickDefaultBackend()
for pkgstr in ("PyPlot", "Immerse", "Qwt", "Gadfly", "UnicodePlots", "Bokeh", "GLVisualize")
for pkgstr in ("PyPlot", "Immerse", "Qwt", "Gadfly", "GR", "UnicodePlots", "Bokeh", "GLVisualize")
if Pkg.installed(pkgstr) != nothing
return backend(symbol(lowercase(pkgstr)))
end
@ -128,7 +134,7 @@ function backend()
end
currentBackendSymbol = CURRENT_BACKEND.sym
if !(currentBackendSymbol in INITIALIZED_BACKENDS)
if !(currentBackendSymbol in _initialized_backends)
# initialize
println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym)
@ -235,13 +241,26 @@ function backend()
# end borrowing (thanks :)
###########################
try
include(joinpath(Pkg.dir("Plots"), "src", "backends", "plotly_blink.jl"))
catch err
warn("Error including PlotlyJS: $err\n Note: Will fall back to built-in display.")
end
end
catch err
warn("Couldn't setup Plotly")
rethrow(err)
end
elseif currentBackendSymbol == :glvisualize
elseif currentBackendSymbol == :gr
try
@eval import GR
catch err
warn("Couldn't import GR. Install it with: Pkg.add(\"GR\").")
end
elseif currentBackendSymbol == :glvisualize
try
@eval import GLVisualize
@eval export GLVisualize
@ -264,7 +283,7 @@ function backend()
else
error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS")
end
push!(INITIALIZED_BACKENDS, currentBackendSymbol)
push!(_initialized_backends, currentBackendSymbol)
end
CURRENT_BACKEND.pkg
@ -297,7 +316,9 @@ function backend(modname)
CURRENT_BACKEND.pkg = BokehPackage()
elseif modname == :plotly
CURRENT_BACKEND.pkg = PlotlyPackage()
elseif modname == :glvisualize
elseif modname == :gr
CURRENT_BACKEND.pkg = GRPackage()
elseif modname == :glvisualize
CURRENT_BACKEND.pkg = GLVisualizePackage()
else
error("Unknown backend $modname. Choose from: $BACKENDS")

View File

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 54 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 184 KiB

View File

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 43 KiB

View File

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 44 KiB

View File

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 98 KiB

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

View File

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 60 KiB

View File

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 68 KiB

View File

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 42 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

View File

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 40 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 52 KiB

After

Width:  |  Height:  |  Size: 52 KiB

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -6,6 +6,8 @@ module Plots
using Compat
using Reexport
@reexport using Colors
using Requires
using FixedSizeArrays
export
Plot,
@ -18,8 +20,6 @@ export
plot,
plot!,
# plot_display,
# plot_display!,
subplot,
subplot!,
@ -33,6 +33,8 @@ export
bar!,
histogram,
histogram!,
histogram2d,
histogram2d!,
density,
density!,
heatmap,
@ -57,8 +59,11 @@ export
wireframe!,
path3d,
path3d!,
plot3d,
plot3d!,
scatter3d,
scatter3d!,
abline!,
title!,
xlabel!,
@ -79,6 +84,7 @@ export
backend,
backends,
backend_name,
aliases,
dataframes,
@ -112,17 +118,14 @@ export
Animation,
frame,
gif,
@animate,
@gif,
# recipes
PlotRecipe,
# EllipseRecipe,
# spy,
corrplot
# ---------------------------------------------------------
const IMG_DIR = Pkg.dir("Plots") * "/img/"
spy
# corrplot
# ---------------------------------------------------------
@ -131,7 +134,7 @@ include("types.jl")
include("utils.jl")
include("colors.jl")
include("components.jl")
include("plotter.jl")
include("plotter2.jl")
include("args.jl")
include("plot.jl")
include("subplot.jl")
@ -148,6 +151,8 @@ bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
bar!(args...; kw...) = plot!(args...; kw..., linetype = :bar)
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)
@ -172,6 +177,8 @@ 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)
@ -220,38 +227,16 @@ yaxis!(plt::Plot, args...; kw...) = plot!(pl
# ---------------------------------------------------------
# try
# import DataFrames
# dataframes()
# end
# const CURRENT_BACKEND = pickDefaultBackend()
# for be in backends()
# try
# backend(be)
# backend()
# catch err
# @show err
# end
# end
const CURRENT_BACKEND = CurrentBackend(:none)
# function __init__()
# # global const CURRENT_BACKEND = pickDefaultBackend()
# # global const CURRENT_BACKEND = CurrentBackend(:none)
function __init__()
# # global CURRENT_BACKEND
# # println("[Plots.jl] Default backend: ", CURRENT_BACKEND.sym)
# # # auto init dataframes if the import statement doesn't error out
# # try
# # @eval import DataFrames
# # dataframes()
# # end
# end
# override IJulia inline display
if isijulia()
@eval import IJulia
IJulia.display_dict(plt::PlottingObject) = Dict{ASCIIString, ByteString}("text/html" => sprint(writemime, "text/html", plt))
end
end
# ---------------------------------------------------------

View File

@ -1,19 +1,18 @@
immutable Animation{P<:PlottingObject}
plt::P
immutable Animation
dir::ASCIIString
frames::Vector{ASCIIString}
end
function Animation(plt::PlottingObject)
Animation(plt, mktempdir(), ASCIIString[])
function Animation()
tmpdir = convert(ASCIIString, mktempdir())
Animation(tmpdir, ASCIIString[])
end
Animation() = Animation(current())
function frame(anim::Animation)
function frame{P<:PlottingObject}(anim::Animation, plt::P=current())
i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i)
png(anim.plt, joinpath(anim.dir, filename))
png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename)
end
@ -29,11 +28,11 @@ function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Inte
fn = abspath(fn)
try
# high quality
speed = round(Int, 100 / fps)
run(`convert -delay $speed -loop 0 $(anim.dir)/*.png $fn`)
run(`convert -delay $speed -loop 0 $(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...)")
@ -52,3 +51,86 @@ end
function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif)
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
end
# -----------------------------------------------
function _animate(forloop::Expr, args...; callgif = false)
if forloop.head != :for
error("@animate macro expects a for-block. got: $(forloop.head)")
end
# add the call to frame to the end of each iteration
animsym = gensym("anim")
countersym = gensym("counter")
block = forloop.args[2]
# create filter
n = length(args)
filterexpr = if n == 0
# no filter... every iteration gets a frame
true
elseif args[1] == :every
# filter every `freq` frames (starting with the first frame)
@assert n == 2
freq = args[2]
@assert isa(freq, Integer) && freq > 0
:(mod1($countersym, $freq) == 1)
elseif args[1] == :when
# filter on custom expression
@assert n == 2
args[2]
else
error("Unsupported animate filter: $args")
end
push!(block.args, :(if $filterexpr; frame($animsym); end))
push!(block.args, :($countersym += 1))
# add a final call to `gif(anim)`?
retval = callgif ? :(gif($animsym)) : animsym
# full expression:
esc(quote
$animsym = Animation() # init animation object
$countersym = 1 # init iteration counter
$forloop # for loop, saving a frame after each iteration
$retval # return the animation object, or the gif
end)
end
"""
Builds an `Animation` using one frame per loop iteration, then create an animated GIF.
Example:
```
p = plot(1)
@gif for x=0:0.1:5
push!(p, 1, sin(x))
end
```
"""
macro gif(forloop::Expr, args...)
_animate(forloop, args...; callgif = true)
end
"""
Collect one frame per for-block iteration and return an `Animation` object.
Example:
```
p = plot(1)
anim = @animate for x=0:0.1:5
push!(p, 1, sin(x))
end
gif(anim)
```
"""
macro animate(forloop::Expr, args...)
_animate(forloop, args...)
end

View File

@ -2,15 +2,15 @@
const _allAxes = [:auto, :left, :right]
@compat const _axesAliases = Dict(
:a => :auto,
:l => :left,
:a => :auto,
:l => :left,
:r => :right
)
const _3dTypes = [:path3d, :scatter3d, :surface, :wireframe]
const _allTypes = vcat([
:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
:heatmap, :hexbin, :hist, :density, :bar, :hline, :vline, :ohlc,
:heatmap, :hexbin, :hist, :hist2d, :hist3d, :density, :bar, :hline, :vline, :ohlc,
:contour, :pie
], _3dTypes)
@compat const _typeAliases = Dict(
@ -39,7 +39,6 @@ const _allTypes = vcat([
ishistlike(lt::Symbol) = lt in (:hist, :density)
islinelike(lt::Symbol) = lt in (:line, :path, :steppre, :steppost)
isheatmaplike(lt::Symbol) = lt in (:heatmap, :hexbin)
const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
@ -129,7 +128,7 @@ _seriesDefaults[:markerstrokecolor] = :match
_seriesDefaults[:markerstrokealpha] = nothing
# _seriesDefaults[:ribbon] = nothing
# _seriesDefaults[:ribboncolor] = :match
_seriesDefaults[:nbins] = 30 # number of bins for heatmaps and hists
_seriesDefaults[:nbins] = 30 # number of bins for hists
_seriesDefaults[:smooth] = false # regression line?
_seriesDefaults[:group] = nothing # groupby vector
# _seriesDefaults[:annotation] = nothing # annotation tuple(s)... (x,y,annotation)
@ -138,7 +137,8 @@ _seriesDefaults[:y] = nothing
_seriesDefaults[:z] = nothing # depth for contour, surface, etc
_seriesDefaults[:zcolor] = nothing # value for color scale
# _seriesDefaults[:surface] = nothing
_seriesDefaults[:nlevels] = 15
# _seriesDefaults[:nlevels] = 15
_seriesDefaults[:levels] = 15
_seriesDefaults[:orientation] = :vertical
@ -149,7 +149,8 @@ _plotDefaults[:title] = ""
_plotDefaults[:xlabel] = ""
_plotDefaults[:ylabel] = ""
_plotDefaults[:yrightlabel] = ""
_plotDefaults[:legend] = true
_plotDefaults[:legend] = :best
_plotDefaults[:colorbar] = :legend
_plotDefaults[:background_color] = colorant"white"
_plotDefaults[:foreground_color] = :auto
_plotDefaults[:xlims] = :auto
@ -177,14 +178,14 @@ _plotDefaults[:tickfont] = font(8)
_plotDefaults[:guidefont] = font(11)
_plotDefaults[:legendfont] = font(8)
_plotDefaults[:grid] = true
_plotDefaults[:annotation] = nothing # annotation tuple(s)... (x,y,annotation)
_plotDefaults[:annotation] = nothing # annotation tuple(s)... (x,y,annotation)
_plotDefaults[:overwrite_figure] = false
# TODO: x/y scales
const _allArgs = sort(collect(union(keys(_seriesDefaults), keys(_plotDefaults))))
supportedArgs(::PlottingPackage) = _allArgs
supportedArgs(::PlottingPackage) = error("supportedArgs not defined") #_allArgs
supportedArgs() = supportedArgs(backend())
@ -230,6 +231,7 @@ end
:type => :linetype,
:lt => :linetype,
:t => :linetype,
:seriestype => :linetype,
:style => :linestyle,
:s => :linestyle,
:ls => :linestyle,
@ -271,6 +273,9 @@ end
:ylabel2 => :yrightlabel,
:y2label => :yrightlabel,
:leg => :legend,
:key => :legend,
:cbar => :colorbar,
:cb => :colorbar,
:bg => :background_color,
:bgcolor => :background_color,
:bg_color => :background_color,
@ -283,6 +288,9 @@ end
:foreground_colour => :foreground_color,
:regression => :smooth,
:reg => :smooth,
:nlevels => :levels,
:nlev => :levels,
:levs => :levels,
:xlim => :xlims,
:xlimit => :xlims,
:xlimits => :xlims,
@ -299,6 +307,14 @@ end
:palette => :color_palette,
:xlink => :linkx,
:ylink => :linky,
:nrow => :nr,
:nrows => :nr,
:ncol => :nc,
:ncols => :nc,
:clf => :overwrite_figure,
:clearfig => :overwrite_figure,
:overwrite => :overwrite_figure,
:reuse => :overwrite_figure,
)
# add all pluralized forms to the _keyAliases dict
@ -348,12 +364,6 @@ end
# -----------------------------------------------------------------------------
wraptuple(x::@compat(Tuple)) = x
wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
trueOrAllTrue(f::Function, x) = f(x)
function handleColors!(d::Dict, arg, csym::Symbol)
try
if arg == :auto
@ -405,11 +415,13 @@ end
function processLineArg(d::Dict, arg)
# linetype
if trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
# if trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
if allLineTypes(arg)
d[:linetype] = arg
# linestyle
elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
# elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
elseif allStyles(arg)
d[:linestyle] = arg
elseif typeof(arg) <: Stroke
@ -424,11 +436,13 @@ function processLineArg(d::Dict, arg)
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
# linealpha
elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
d[:linealpha] = arg
# linewidth
elseif trueOrAllTrue(a -> typeof(a) <: Real, arg)
# elseif trueOrAllTrue(a -> typeof(a) <: Real, arg)
elseif allReals(arg)
d[:linewidth] = arg
# color
@ -442,13 +456,16 @@ end
function processMarkerArg(d::Dict, arg)
# markershape
if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg)
# if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg)
# d[:markershape] = arg
# elseif trueOrAllTrue(a -> isa(a, Shape), arg)
if allShapes(arg)
d[:markershape] = arg
elseif trueOrAllTrue(a -> isa(a, Shape), arg)
d[:markershape] = arg
# stroke style
elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
# elseif trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
elseif allStyles(arg)
d[:markerstrokestyle] = arg
elseif typeof(arg) <: Stroke
@ -463,17 +480,19 @@ function processMarkerArg(d::Dict, arg)
arg.alpha == nothing || (d[:markeralpha] = arg.alpha)
# linealpha
elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
d[:markeralpha] = arg
# markersize
elseif trueOrAllTrue(a -> typeof(a) <: Real, arg)
# elseif trueOrAllTrue(a -> typeof(a) <: Real, arg)
elseif allReals(arg)
d[:markersize] = arg
# markercolor
elseif !handleColors!(d, arg, :markercolor)
warn("Skipped marker arg $arg.")
end
end
@ -485,15 +504,31 @@ function processFillArg(d::Dict, arg)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
# fillrange function
# elseif trueOrAllTrue(a -> isa(a, Function), arg)
elseif allFunctions(arg)
d[:fillrange] = arg
# fillalpha
# elseif trueOrAllTrue(a -> typeof(a) <: Real && a > 0 && a < 1, arg)
elseif allAlphas(arg)
d[:fillalpha] = arg
elseif !handleColors!(d, arg, :fillcolor)
d[:fillrange] = arg
end
end
_replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
_replace_markershape(shapes::AVec) = map(_replace_markershape, shapes)
_replace_markershape(shape) = shape
"Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
function preprocessArgs!(d::Dict)
replaceAliases!(d, _keyAliases)
# handle axis args
for axisletter in ("x", "y")
asym = symbol(axisletter * "axis")
@ -501,6 +536,12 @@ function preprocessArgs!(d::Dict)
processAxisArg(d, axisletter, arg)
end
delete!(d, asym)
# turn :labels into :ticks_and_labels
tsym = symbol(axisletter * "ticks")
if haskey(d, tsym) && ticksType(d[tsym]) == :labels
d[tsym] = (1:length(d[tsym]), d[tsym])
end
end
# handle line args
@ -516,7 +557,12 @@ function preprocessArgs!(d::Dict)
anymarker = true
end
delete!(d, :marker)
if anymarker && !haskey(d, :markershape)
# if anymarker && !haskey(d, :markershape)
# d[:markershape] = :ellipse
# end
if haskey(d, :markershape)
d[:markershape] = _replace_markershape(d[:markershape])
elseif anymarker
d[:markershape] = :ellipse
end
@ -528,6 +574,14 @@ function preprocessArgs!(d::Dict)
# convert into strokes and brushes
# legends
if haskey(d, :legend)
d[:legend] = convertLegendValue(d[:legend])
end
if haskey(d, :colorbar)
d[:colorbar] = convertLegendValue(d[:colorbar])
end
# handle subplot links
if haskey(d, :link)
l = d[:link]
@ -588,9 +642,12 @@ function warnOnUnsupportedArgs(pkg::PlottingPackage, d::Dict)
end
end
_markershape_supported(pkg::PlottingPackage, shape::Symbol) = shape in supportedMarkers(pkg)
_markershape_supported(pkg::PlottingPackage, shape::Shape) = Shape in supportedMarkers(pkg)
_markershape_supported(pkg::PlottingPackage, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
(d[:axis] in supportedAxes(pkg)
(d[:axis] in supportedAxes(pkg)
|| warn("axis $(d[:axis]) is unsupported with $pkg. Choose from: $(supportedAxes(pkg))"))
(d[:linetype] == :none
|| d[:linetype] in supportedTypes(pkg)
@ -598,8 +655,9 @@ function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
(d[:linestyle] in supportedStyles(pkg)
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))"))
(d[:markershape] == :none
|| d[:markershape] in supportedMarkers(pkg)
|| (Shape in supportedMarkers(pkg) && typeof(d[:markershape]) <: Shape)
|| _markershape_supported(pkg, d[:markershape])
# || d[:markershape] in supportedMarkers(pkg)
# || (Shape in supportedMarkers(pkg) && typeof(d[:markershape]) <: Shape)
|| warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))"))
end
@ -635,6 +693,19 @@ function setDictValue(d_in::Dict, d_out::Dict, k::Symbol, idx::Int, defaults::Di
end
end
function convertLegendValue(val::Symbol)
if val in (:both, :all, :yes)
:best
elseif val in (:no, :none)
:none
elseif val in (:right, :left, :top, :bottom, :inside, :best, :legend)
val
else
error("Invalid symbol for legend: $val")
end
end
convertLegendValue(val::Bool) = val ? :best : :none
# -----------------------------------------------------------------------------
# build the argument dictionary for the plot
@ -655,6 +726,13 @@ function getPlotArgs(pkg::PlottingPackage, kw, idx::Int; set_defaults = true)
end
end
# handle legend/colorbar
d[:legend] = convertLegendValue(d[:legend])
d[:colorbar] = convertLegendValue(d[:colorbar])
if d[:colorbar] == :legend
d[:colorbar] = d[:legend]
end
# convert color
handlePlotColors(pkg, d)
@ -683,7 +761,7 @@ function getSeriesArgs(pkg::PlottingPackage, plotargs::Dict, kw, commandIndex::I
d[k] = kwdict[k]
end
end
if haskey(_typeAliases, d[:linetype])
d[:linetype] = _typeAliases[d[:linetype]]
end
@ -710,8 +788,6 @@ function getSeriesArgs(pkg::PlottingPackage, plotargs::Dict, kw, commandIndex::I
c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex))
d[:fillcolor] = c
# TODO: rebuild
# set label
label = d[:label]
label = (label == "AUTO" ? "y$globalIndex" : label)

View File

@ -1,6 +1,15 @@
# https://github.com/bokeh/Bokeh.jl
function _initialize_backend(::BokehPackage; kw...)
@eval begin
warn("Bokeh is no longer supported... many features will likely be broken.")
import Bokeh
export Bokeh
end
end
# make255(x) = round(Int, 255 * x)
# function bokehcolor(c::Colorant)

View File

@ -1,6 +1,15 @@
# https://github.com/dcjones/Gadfly.jl
function _initialize_backend(::GadflyPackage; kw...)
@eval begin
import Gadfly, Compose
export Gadfly, Compose
include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
end
end
# ---------------------------------------------------------------------------
# immutable MissingVec <: AbstractVector{Float64} end
@ -27,7 +36,7 @@ function getLineGeom(d::Dict)
xbins, ybins = maketuple(d[:nbins])
if lt == :hexbin
Gadfly.Geom.hexbin(xbincount = xbins, ybincount = ybins)
elseif lt == :heatmap
elseif lt == :hist2d
Gadfly.Geom.histogram2d(xbincount = xbins, ybincount = ybins)
elseif lt == :hist
Gadfly.Geom.histogram(bincount = xbins)
@ -44,7 +53,7 @@ function getLineGeom(d::Dict)
elseif lt == :vline
Gadfly.Geom.vline
elseif lt == :contour
Gadfly.Geom.contour(levels = d[:nlevels])
Gadfly.Geom.contour(levels = d[:levels])
else
nothing
end
@ -159,11 +168,16 @@ end
# ---------------------------------------------------------------------------
# extract the underlying ShapeGeometry object(s)
getMarkerGeom(shape::Shape) = gadflyshape(shape)
getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
getMarkerGeom(shapes::AVec) = map(getMarkerGeom, shapes)
getMarkerGeom(d::Dict) = getMarkerGeom(d[:markershape])
function getMarkerGeom(d::Dict)
shape = d[:markershape]
gadflyshape(isa(shape, Shape) ? shape : _shapes[shape])
end
# function getMarkerGeom(d::Dict)
# shape = d[:markershape]
# gadflyshape(isa(shape, Shape) ? shape : _shapes[shape])
# end
function getGadflyMarkerTheme(d::Dict, plotargs::Dict)
@ -192,6 +206,7 @@ function getGadflyMarkerTheme(d::Dict, plotargs::Dict)
end
function addGadflyContColorScale(plt::Plot{GadflyPackage}, c)
plt.plotargs[:colorbar] == :none && return
if !isa(c, ColorGradient)
c = colorscheme(:bluesreds)
end
@ -224,7 +239,7 @@ end
function addToGadflyLegend(plt::Plot, d::Dict)
# add the legend?
if plt.plotargs[:legend]
if plt.plotargs[:legend] != :none && d[:label] != ""
gplt = getGadflyContext(plt)
# add the legend if needed
@ -288,7 +303,7 @@ function addGadflySeries!(plt::Plot, d::Dict)
lt = d[:linetype]
if lt == :ohlc
error("Haven't re-implemented after refactoring")
elseif lt in (:heatmap, :hexbin) && (isa(d[:linecolor], ColorGradient) || isa(d[:linecolor], ColorFunction))
elseif lt in (:hist2d, :hexbin) && (isa(d[:linecolor], ColorGradient) || isa(d[:linecolor], ColorFunction))
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:linecolor], p))))
elseif lt == :scatter && d[:markershape] == :none
d[:markershape] = :ellipse
@ -299,7 +314,7 @@ function addGadflySeries!(plt::Plot, d::Dict)
prepend!(layers, addGadflyMarker!(plt, length(gplt.layers), d, plt.plotargs, smooth...))
end
lt in (:hist, :heatmap, :hexbin, :contour) || addToGadflyLegend(plt, d)
lt in (:hist2d, :hexbin, :contour) || addToGadflyLegend(plt, d)
# now save the layers that apply to this series
d[:gadflylayers] = layers
@ -359,7 +374,7 @@ function addGadflyTicksGuide(gplt, ticks, isx::Bool)
gfunc = isx ? Gadfly.Scale.x_discrete : Gadfly.Scale.y_discrete
labelmap = Dict(zip(ticks...))
labelfunc = val -> labelmap[val]
push!(gplt.scales, gfunc(levels = ticks[1], labels = labelfunc))
push!(gplt.scales, gfunc(levels = collect(ticks[1]), labels = labelfunc))
else
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
@ -482,9 +497,13 @@ function updateGadflyPlotTheme(plt::Plot, d::Dict)
# # TODO: should this be part of the main `plot` command in plot.jl???
# d = merge!(plt.plotargs, d)
# hide the legend?
if !get(d, :legend, true)
kwargs[:key_position] = :none
# # hide the legend?
# if !get(d, :legend, true)
# kwargs[:key_position] = :none
# end
leg = d[d[:legend] == :none ? :colorbar : :legend]
if leg != :best
kwargs[:key_position] = leg == :inside ? :right : leg
end
if !get(d, :grid, true)
@ -656,7 +675,7 @@ setGadflyDisplaySize(subplt::Subplot) = setGadflyDisplaySize(getplotargs(subplt,
# -------------------------------------------------------------------------
function dowritemime{P<:GadflyOrImmerse}(io::IO, func, plt::PlottingObject{P})
function dowritemime{P<:Union{GadflyPackage,ImmersePackage}}(io::IO, func, plt::PlottingObject{P})
gplt = getGadflyContext(plt)
setGadflyDisplaySize(plt)
Gadfly.draw(func(io, Compose.default_graphic_width, Compose.default_graphic_height), gplt)
@ -667,10 +686,11 @@ getGadflyWriteFunc(::MIME"image/svg+xml") = Gadfly.SVG
# getGadflyWriteFunc(::MIME"text/html") = Gadfly.SVGJS
getGadflyWriteFunc(::MIME"application/pdf") = Gadfly.PDF
getGadflyWriteFunc(::MIME"application/postscript") = Gadfly.PS
getGadflyWriteFunc(::MIME"application/x-tex") = Gadfly.PGF
getGadflyWriteFunc(m::MIME) = error("Unsupported in Gadfly/Immerse: ", m)
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript")
@eval function Base.writemime{P<:GadflyOrImmerse}(io::IO, ::$mime, plt::PlottingObject{P})
for mime in (MIME"image/png", MIME"image/svg+xml", MIME"application/pdf", MIME"application/postscript", MIME"application/x-tex")
@eval function Base.writemime{P<:Union{GadflyPackage,ImmersePackage}}(io::IO, ::$mime, plt::PlottingObject{P})
func = getGadflyWriteFunc($mime())
dowritemime(io, func, plt)
end

View File

@ -66,7 +66,7 @@ function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aes
end
function gadflyshape(sv::Shape)
ShapeGeometry([(x,-y) for (x,y) in sv.vertices])
ShapeGeometry(sv.vertices)
end

View File

@ -2,6 +2,13 @@
# [WEBSITE]
function _initialize_backend(::GLVisualizePackage; kw...)
@eval begin
import GLVisualize
export GLVisualize
end
end
# ---------------------------------------------------------------------------
immutable GLScreenWrapper
@ -12,6 +19,8 @@ function _create_plot(pkg::GLVisualizePackage; kw...)
d = Dict(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
# TODO: this should be moved to the display method?
w=GLVisualize.glscreen()
@async GLVisualize.renderloop(w)
Plot(GLScreenWrapper(w), pkg, 0, d, Dict[])
@ -22,6 +31,7 @@ function _add_series(::GLVisualizePackage, plt::Plot; kw...)
d = Dict(kw)
# 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
@ -50,13 +60,16 @@ end
# accessors for x/y data
function Base.getindex(plt::Plot{GLVisualizePackage}, i::Int)
series = plt.o.lines[i]
series.x, series.y
# TODO:
# series = plt.o.lines[i]
# series.x, series.y
nothing, nothing
end
function Base.setindex!(plt::Plot{GLVisualizePackage}, xy::Tuple, i::Integer)
series = plt.o.lines[i]
series.x, series.y = xy
# TODO:
# series = plt.o.lines[i]
# series.x, series.y = xy
plt
end
@ -82,6 +95,10 @@ end
function Base.display(::PlotsDisplay, plt::Plot{GLVisualizePackage})
# 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{GLVisualizePackage})

790
src/backends/gr.jl Normal file
View File

@ -0,0 +1,790 @@
# https://github.com/jheinen/GR.jl
function _initialize_backend(::GRPackage; kw...)
@eval begin
import GR
export GR
end
end
const gr_linetype = Dict(
:auto => 1, :solid => 1, :dash => 2, :dot => 3, :dashdot => 4,
:dashdotdot => -1 )
const gr_markertype = Dict(
:auto => 1, :none => -1, :ellipse => -1, :rect => -7, :diamond => -13,
:utriangle => -3, :dtriangle => -5, :pentagon => -21, :hexagon => -22,
:heptagon => -23, :octagon => -24, :cross => 2, :xcross => 5,
:star4 => -25, :star5 => -26, :star6 => -27, :star7 => -28, :star8 => -29,
:vline => -30, :hline => -31 )
const gr_halign = Dict(:left => 1, :hcenter => 2, :right => 3)
const gr_valign = Dict(:top => 1, :vcenter => 3, :bottom => 5)
const gr_font_family = Dict(
"times" => 1, "helvetica" => 5, "courier" => 9, "bookman" => 14,
"newcenturyschlbk" => 18, "avantgarde" => 22, "palatino" => 26)
function gr_getcolorind(v)
c = getColor(v)
return convert(Int, GR.inqcolorfromrgb(c.r, c.g, c.b))
end
function gr_getaxisind(p)
axis = get(p, :axis, :none)
if axis in [:none, :left]
return 1
else
return 2
end
end
function gr_setmarkershape(p)
if haskey(p, :markershape)
shape = p[:markershape]
if isa(shape, Shape)
p[:vertices] = shape.vertices
else
GR.setmarkertype(gr_markertype[shape])
p[:vertices] = :none
end
end
end
function gr_polymarker(p, x, y)
if p[:vertices] != :none
vertices= p[:vertices]
dx = Float64[el[1] for el in vertices] * 0.01
dy = Float64[el[2] for el in vertices] * 0.01
GR.selntran(0)
GR.setfillcolorind(gr_getcolorind(p[:markercolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
for i = 1:length(x)
xn, yn = GR.wctondc(x[i], y[i])
GR.fillarea(xn + dx, yn + dy)
end
GR.selntran(1)
else
GR.polymarker(x, y)
end
end
function gr_polyline(x, y)
if NaN in x || NaN in y
i = 1
j = 1
n = length(x)
while i < n
while j < n && x[j] != Nan && y[j] != NaN
j += 1
end
if i < j
GR.polyline(x[i:j], y[i:j])
end
i = j + 1
end
else
GR.polyline(x, y)
end
end
function gr_display(plt::Plot{GRPackage}, clear=true, update=true,
subplot=[0, 1, 0, 1])
d = plt.plotargs
clear && GR.clearws()
mwidth, mheight, width, height = GR.inqdspsize()
w, h = d[:size]
viewport = zeros(4)
if w > h
ratio = float(h) / w
msize = mwidth * w / width
GR.setwsviewport(0, msize, 0, msize * ratio)
GR.setwswindow(0, 1, 0, ratio)
viewport[1] = subplot[1] + 0.125 * (subplot[2] - subplot[1])
viewport[2] = subplot[1] + 0.95 * (subplot[2] - subplot[1])
viewport[3] = ratio * (subplot[3] + 0.125 * (subplot[4] - subplot[3]))
viewport[4] = ratio * (subplot[3] + 0.95 * (subplot[4] - subplot[3]))
else
ratio = float(w) / h
msize = mheight * h / height
GR.setwsviewport(0, msize * ratio, 0, msize)
GR.setwswindow(0, ratio, 0, 1)
viewport[1] = ratio * (subplot[1] + 0.125 * (subplot[2] - subplot[1]))
viewport[2] = ratio * (subplot[1] + 0.95 * (subplot[2] - subplot[1]))
viewport[3] = subplot[3] + 0.125 * (subplot[4] - subplot[3])
viewport[4] = subplot[3] + 0.95 * (subplot[4] - subplot[3])
end
if haskey(d, :background_color)
GR.savestate()
GR.selntran(0)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
GR.setfillcolorind(gr_getcolorind(d[:background_color]))
if w > h
GR.fillrect(subplot[1], subplot[2], ratio*subplot[3], ratio*subplot[4])
else
GR.fillrect(ratio*subplot[1], ratio*subplot[2], subplot[3], subplot[4])
end
GR.selntran(1)
GR.restorestate()
c = getColor(d[:background_color])
if 0.21 * c.r + 0.72 * c.g + 0.07 * c.b < 0.9
fg = convert(Int, GR.inqcolorfromrgb(1-c.r, 1-c.g, 1-c.b))
else
fg = 1
end
else
fg = 1
end
extrema = zeros(2, 4)
num_axes = 1
cmap = false
axes_2d = true
grid_flag = get(d, :grid, true)
for axis = 1:2
xmin = ymin = typemax(Float64)
xmax = ymax = typemin(Float64)
for p in plt.seriesargs
if axis == gr_getaxisind(p)
if axis == 2
num_axes = 2
end
if p[:linetype] == :bar
x, y = 1:length(p[:y]), p[:y]
elseif p[:linetype] == :ohlc
x, y = 1:size(p[:y], 1), p[:y]
elseif p[:linetype] in [:hist, :density]
x, y = Base.hist(p[:y])
elseif p[:linetype] in [:hist2d, :hexbin]
E = zeros(length(p[:x]),2)
E[:,1] = p[:x]
E[:,2] = p[:y]
if isa(p[:nbins], Tuple)
xbins, ybins = p[:nbins]
else
xbins = ybins = p[:nbins]
end
cmap = true
x, y, H = Base.hist2d(E, xbins, ybins)
elseif p[:linetype] == :pie
axes_2d = false
xmin, xmax, ymin, ymax = 0, 1, 0, 1
x, y = p[:x], p[:y]
else
if p[:linetype] in [:contour, :surface]
cmap = true
end
if p[:linetype] in [:surface, :wireframe, :path3d, :scatter3d]
axes_2d = false
end
x, y = p[:x], p[:y]
end
if p[:linetype] != :pie
xmin = min(minimum(x), xmin)
xmax = max(maximum(x), xmax)
if p[:linetype] == :ohlc
for val in y
ymin = min(val.open, val.high, val.low, val.close, ymin)
ymax = max(val.open, val.high, val.low, val.close, ymax)
end
else
ymin = min(minimum(y), ymin)
ymax = max(maximum(y), ymax)
end
end
end
end
if d[:xlims] != :auto
xmin, xmax = d[:xlims]
end
if d[:ylims] != :auto
ymin, ymax = d[:ylims]
end
if xmax <= xmin
xmax = xmin + 1
end
if ymax <= ymin
ymax = ymin + 1
end
extrema[axis,:] = [xmin, xmax, ymin, ymax]
end
if num_axes == 2 || !axes_2d
viewport[2] -= 0.0525
end
if cmap
viewport[2] -= 0.1
end
GR.setviewport(viewport[1], viewport[2], viewport[3], viewport[4])
scale = 0
d[:xscale] == :log10 && (scale |= GR.OPTION_X_LOG)
d[:yscale] == :log10 && (scale |= GR.OPTION_Y_LOG)
get(d, :xflip, false) && (scale |= GR.OPTION_FLIP_X)
get(d, :yflip, false) && (scale |= GR.OPTION_FLIP_Y)
for axis = 1:num_axes
xmin, xmax, ymin, ymax = extrema[axis,:]
if scale & GR.OPTION_X_LOG == 0
xmin, xmax = GR.adjustlimits(xmin, xmax)
majorx = 5
xtick = GR.tick(xmin, xmax) / majorx
else
xtick = majorx = 1
end
if scale & GR.OPTION_Y_LOG == 0
ymin, ymax = GR.adjustlimits(ymin, ymax)
majory = 5
ytick = GR.tick(ymin, ymax) / majory
else
ytick = majory = 1
end
if scale & GR.OPTION_FLIP_X == 0
xorg = (xmin, xmax)
else
xorg = (xmax, xmin)
end
if scale & GR.OPTION_FLIP_Y == 0
yorg = (ymin, ymax)
else
yorg = (ymax, ymin)
end
GR.setwindow(xmin, xmax, ymin, ymax)
GR.setscale(scale)
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.018 * diag, 0.01)
GR.setcharheight(charheight)
GR.settextcolorind(fg)
if axes_2d
GR.setlinewidth(1)
GR.setlinecolorind(fg)
ticksize = 0.0075 * diag
if grid_flag && fg == 1
GR.grid(xtick, ytick, 0, 0, majorx, majory)
end
if num_axes == 1
GR.axes(xtick, ytick, xorg[1], yorg[1], majorx, majory, ticksize)
GR.axes(xtick, ytick, xorg[2], yorg[2], -majorx, -majory, -ticksize)
elseif axis == 1
GR.axes(xtick, ytick, xorg[1], yorg[1], majorx, majory, ticksize)
else
GR.axes(xtick, ytick, xorg[2], yorg[2], -majorx, majory, -ticksize)
end
end
end
if get(d, :title, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
GR.settextcolorind(fg)
GR.text(0.5 * (viewport[1] + viewport[2]), min(ratio, 1), d[:title])
GR.restorestate()
end
if get(d, :xlabel, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_BOTTOM)
GR.settextcolorind(fg)
GR.text(0.5 * (viewport[1] + viewport[2]), 0, d[:xlabel])
GR.restorestate()
end
if get(d, :ylabel, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
GR.setcharup(-1, 0)
GR.settextcolorind(fg)
GR.text(0, 0.5 * (viewport[3] + viewport[4]), d[:ylabel])
GR.restorestate()
end
if get(d, :yrightlabel, "") != ""
GR.savestate()
GR.settextalign(GR.TEXT_HALIGN_CENTER, GR.TEXT_VALIGN_TOP)
GR.setcharup(1, 0)
GR.settextcolorind(fg)
GR.text(1, 0.5 * (viewport[3] + viewport[4]), d[:yrightlabel])
GR.restorestate()
end
legend = false
for p in plt.seriesargs
GR.savestate()
xmin, xmax, ymin, ymax = extrema[gr_getaxisind(p),:]
GR.setwindow(xmin, xmax, ymin, ymax)
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks, :hline, :vline, :ohlc]
haskey(p, :linestyle) && GR.setlinetype(gr_linetype[p[:linestyle]])
haskey(p, :linewidth) && GR.setlinewidth(p[:linewidth])
haskey(p, :linecolor) && GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
end
if p[:linetype] == :path
if haskey(p, :fillcolor)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
end
if length(p[:x]) > 1
if p[:fillrange] != nothing
GR.fillarea([p[:x][1]; p[:x]; p[:x][length(p[:x])]], [p[:fillrange]; p[:y]; p[:fillrange]])
end
GR.polyline(p[:x], p[:y])
end
legend = true
end
if p[:linetype] == :line
if length(p[:x]) > 1
gr_polyline(p[:x], p[:y])
end
legend = true
elseif p[:linetype] in [:steppre, :steppost]
n = length(p[:x])
x = zeros(2*n + 1)
y = zeros(2*n + 1)
x[1], y[1] = p[:x][1], p[:y][1]
j = 2
for i = 2:n
if p[:linetype] == :steppre
x[j], x[j+1] = p[:x][i-1], p[:x][i]
y[j], y[j+1] = p[:y][i], p[:y][i]
else
x[j], x[j+1] = p[:x][i], p[:x][i]
y[j], y[j+1] = p[:y][i-1], p[:y][i]
end
j += 2
end
if n > 1
GR.polyline(x, y)
end
legend = true
elseif p[:linetype] == :sticks
x, y = p[:x], p[:y]
for i = 1:length(y)
GR.polyline([x[i], x[i]], [ymin, y[i]])
end
legend = true
elseif p[:linetype] == :scatter || (p[:markershape] != :none && axes_2d)
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
gr_setmarkershape(p)
if haskey(d, :markersize)
if typeof(d[:markersize]) <: Number
GR.setmarkersize(d[:markersize] / 4.0)
if length(p[:x]) > 0
gr_polymarker(p, p[:x], p[:y])
end
else
c = p[:markercolor]
GR.setcolormap(-GR.COLORMAP_GLOWING)
for i = 1:length(p[:x])
if isa(c, ColorGradient) && p[:zcolor] != nothing
ci = round(Int, 1000 + p[:zcolor][i] * 255)
GR.setmarkercolorind(ci)
end
GR.setmarkersize(d[:markersize][i] / 4.0)
gr_polymarker(p, [p[:x][i]], [p[:y][i]])
end
end
else
if length(p[:x]) > 0
gr_polymarker(p, p[:x], p[:y])
end
end
legend = true
elseif p[:linetype] == :bar
y = p[:y]
for i = 1:length(y)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
GR.fillrect(i-0.4, i+0.4, max(0, ymin), y[i])
GR.setfillcolorind(1)
GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
GR.fillrect(i-0.4, i+0.4, max(0, ymin), y[i])
end
elseif p[:linetype] in [:hist, :density]
h = Base.hist(p[:y])
x, y = float(collect(h[1])), float(h[2])
for i = 2:length(y)
GR.setfillcolorind(gr_getcolorind(p[:fillcolor]))
GR.setfillintstyle(GR.INTSTYLE_SOLID)
GR.fillrect(x[i-1], x[i], ymin, y[i])
GR.setfillcolorind(1)
GR.setfillintstyle(GR.INTSTYLE_HOLLOW)
GR.fillrect(x[i-1], x[i], ymin, y[i])
end
elseif p[:linetype] in [:hline, :vline]
for xy in p[:y]
if p[:linetype] == :hline
GR.polyline([xmin, xmax], [xy, xy])
else
GR.polyline([xy, xy], [ymin, ymax])
end
end
elseif p[:linetype] in [:hist2d, :hexbin]
E = zeros(length(p[:x]),2)
E[:,1] = p[:x]
E[:,2] = p[:y]
if isa(p[:nbins], Tuple)
xbins, ybins = p[:nbins]
else
xbins = ybins = p[:nbins]
end
x, y, H = Base.hist2d(E, xbins, ybins)
counts = round(Int32, 1000 + 255 * H / maximum(H))
n, m = size(counts)
GR.setcolormap(GR.COLORMAP_COOLWARM)
GR.cellarray(xmin, xmax, ymin, ymax, n, m, counts)
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
viewport[3], viewport[4])
GR.setspace(0, maximum(counts), 0, 90)
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.016 * diag, 0.01)
GR.setcharheight(charheight)
GR.colormap()
elseif p[:linetype] == :contour
x, y, z = p[:x], p[:y], p[:z].surf
zmin, zmax = minimum(z), maximum(z)
if typeof(p[:levels]) <: Array
h = p[:levels]
else
h = linspace(zmin, zmax, p[:levels])
end
GR.setspace(zmin, zmax, 0, 90)
GR.setcolormap(GR.COLORMAP_COOLWARM)
GR.contour(x, y, h, reshape(z, length(x) * length(y)), 1000)
GR.setviewport(viewport[2] + 0.02, viewport[2] + 0.05,
viewport[3], viewport[4])
l = round(Int32, 1000 + (h - minimum(h)) / (maximum(h) - minimum(h)) * 255)
GR.setwindow(xmin, xmax, zmin, zmax)
GR.cellarray(xmin, xmax, zmax, zmin, 1, length(l), l)
ztick = 0.5 * GR.tick(zmin, zmax)
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.016 * diag, 0.01)
GR.setcharheight(charheight)
GR.axes(0, ztick, xmax, zmin, 0, 1, 0.005)
elseif p[:linetype] in [:surface, :wireframe]
x, y, z = p[:x], p[:y], p[:z].surf
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
GR.setspace(zmin, zmax, 40, 70)
xtick = GR.tick(xmin, xmax) / 2
ytick = GR.tick(ymin, ymax) / 2
ztick = GR.tick(zmin, zmax) / 2
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.018 * diag, 0.01)
ticksize = 0.01 * (viewport[2] - viewport[1])
GR.setlinewidth(1)
if grid_flag
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
end
z = reshape(z, length(x) * length(y))
if p[:linetype] == :surface
GR.setcolormap(GR.COLORMAP_COOLWARM)
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
else
GR.setfillcolorind(0)
GR.surface(x, y, z, GR.OPTION_FILLED_MESH)
end
GR.setlinewidth(1)
GR.setcharheight(charheight)
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
if cmap
GR.setviewport(viewport[2] + 0.07, viewport[2] + 0.1,
viewport[3], viewport[4])
GR.colormap()
end
elseif p[:linetype] in [:path3d, :scatter3d]
x, y, z = p[:x], p[:y], p[:z]
zmin, zmax = GR.adjustrange(minimum(z), maximum(z))
GR.setspace(zmin, zmax, 40, 70)
xtick = GR.tick(xmin, xmax) / 2
ytick = GR.tick(ymin, ymax) / 2
ztick = GR.tick(zmin, zmax) / 2
diag = sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
charheight = max(0.018 * diag, 0.01)
ticksize = 0.01 * (viewport[2] - viewport[1])
GR.setlinewidth(1)
if grid_flag && p[:linetype] == :path3d
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
end
if p[:linetype] == :scatter3d
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
gr_setmarkershape(p)
for i = 1:length(z)
px, py = GR.wc3towc(x[i], y[i], z[i])
gr_polymarker(p, [px], [py])
end
else
haskey(p, :linewidth) && GR.setlinewidth(p[:linewidth])
if length(x) > 0
GR.polyline3d(x, y, z)
end
end
GR.setlinewidth(1)
GR.setcharheight(charheight)
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
elseif p[:linetype] == :ohlc
y = p[:y]
n = size(y, 1)
ticksize = 0.5 * (xmax - xmin) / n
for i in 1:n
GR.polyline([i-ticksize, i], [y[i].open, y[i].open])
GR.polyline([i, i], [y[i].low, y[i].high])
GR.polyline([i, i+ticksize], [y[i].close, y[i].close])
end
elseif p[:linetype] == :pie
GR.selntran(0)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
xmin, xmax, ymin, ymax = viewport
ymax -= 0.05 * (xmax - xmin)
xcenter = 0.5 * (xmin + xmax)
ycenter = 0.5 * (ymin + ymax)
if xmax - xmin > ymax - ymin
r = 0.5 * (ymax - ymin)
xmin, xmax = xcenter - r, xcenter + r
else
r = 0.5 * (xmax - xmin)
ymin, ymax = ycenter - r, ycenter + r
end
labels, slices = p[:x], p[:y]
numslices = length(slices)
total = sum(slices)
a1 = 0
x = zeros(3)
y = zeros(3)
for i in 1:numslices
a2 = round(Int, a1 + (slices[i] / total) * 360.0)
GR.setfillcolorind(980 + (i-1) % 20)
GR.fillarc(xmin, xmax, ymin, ymax, a1, a2)
alpha = 0.5 * (a1 + a2)
cosf = r * cos(alpha * pi / 180)
sinf = r * sin(alpha * pi / 180)
x[1] = xcenter + cosf
y[1] = ycenter + sinf
x[2] = x[1] + 0.1 * cosf
y[2] = y[1] + 0.1 * sinf
y[3] = y[2]
if 90 <= alpha < 270
x[3] = x[2] - 0.05
GR.settextalign(GR.TEXT_HALIGN_RIGHT, GR.TEXT_VALIGN_HALF)
GR.text(x[3] - 0.01, y[3], string(labels[i]))
else
x[3] = x[2] + 0.05
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
GR.text(x[3] + 0.01, y[3], string(labels[i]))
end
GR.polyline(x, y)
a1 = a2
end
GR.selntran(1)
end
GR.restorestate()
end
if d[:legend] != :none && legend
GR.savestate()
GR.selntran(0)
GR.setscale(0)
w = 0
i = 0
for p in plt.seriesargs
if typeof(p[:label]) <: Array
i += 1
lab = p[:label][i]
else
lab = p[:label]
end
tbx, tby = GR.inqtext(0, 0, lab)
w = max(w, tbx[3])
end
px = viewport[2] - 0.05 - w
py = viewport[4] - 0.06
dy = 0.03 * sqrt((viewport[2] - viewport[1])^2 + (viewport[4] - viewport[3])^2)
GR.setfillintstyle(GR.INTSTYLE_SOLID)
GR.setfillcolorind(0)
GR.fillrect(px - 0.08, px + w + 0.02, py + dy, py - dy * length(plt.seriesargs))
GR.setlinetype(1)
GR.setlinewidth(1)
GR.drawrect(px - 0.08, px + w + 0.02, py + dy, py - dy * length(plt.seriesargs))
haskey(d, :linewidth) && GR.setlinewidth(d[:linewidth])
i = 0
for p in plt.seriesargs
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks]
haskey(p, :linecolor) && GR.setlinecolorind(gr_getcolorind(p[:linecolor]))
haskey(p, :linestyle) && GR.setlinetype(gr_linetype[p[:linestyle]])
GR.polyline([px - 0.07, px - 0.01], [py, py])
end
if p[:linetype] == :scatter || p[:markershape] != :none
haskey(p, :markercolor) && GR.setmarkercolorind(gr_getcolorind(p[:markercolor]))
gr_setmarkershape(p)
if p[:linetype] in [:path, :line, :steppre, :steppost, :sticks]
gr_polymarker(p, [px - 0.06, px - 0.02], [py, py])
else
gr_polymarker(p, [px - 0.06, px - 0.04, px - 0.02], [py, py, py])
end
end
if typeof(p[:label]) <: Array
i += 1
lab = p[:label][i]
else
lab = p[:label]
end
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
GR.settextcolorind(1)
GR.text(px, py, lab)
py -= dy
end
GR.selntran(1)
GR.restorestate()
end
if haskey(d, :anns)
GR.savestate()
for ann in d[:anns]
x, y, val = ann
x, y = GR.wctondc(x, y)
alpha = val.font.rotation
family = lowercase(val.font.family)
GR.setcharheight(0.7 * val.font.pointsize / d[:size][2])
GR.setcharup(sin(val.font.rotation), cos(val.font.rotation))
if haskey(gr_font_family, family)
GR.settextfontprec(100 + gr_font_family[family], GR.TEXT_PRECISION_STRING)
end
GR.settextcolorind(gr_getcolorind(val.font.color))
GR.settextalign(gr_halign[val.font.halign], gr_valign[val.font.valign])
GR.text(x, y, val.str)
end
GR.restorestate()
end
update && GR.updatews()
end
function gr_display(subplt::Subplot{GRPackage})
clear = true
update = false
l = enumerate(subplt.layout)
nr = nrows(subplt.layout)
for (i, (r, c)) in l
nc = ncols(subplt.layout, r)
if i == length(l)
update = true
end
subplot = [(c-1)/nc, c/nc, 1-r/nr, 1-(r-1)/nr]
gr_display(subplt.plts[i], clear, update, subplot)
clear = false
end
end
function _create_plot(pkg::GRPackage; kw...)
d = Dict(kw)
Plot(nothing, pkg, 0, d, Dict[])
end
function _add_series(::GRPackage, plt::Plot; kw...)
d = Dict(kw)
push!(plt.seriesargs, d)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{GRPackage}, anns::AVec{@compat(Tuple{X,Y,V})})
if haskey(plt.plotargs, :anns)
append!(plt.plotargs[:anns], anns)
else
plt.plotargs[:anns] = anns
end
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{GRPackage})
end
function _update_plot(plt::Plot{GRPackage}, d::Dict)
for k in (:title, :xlabel, :ylabel)
haskey(d, k) && (plt.plotargs[k] = d[k])
end
end
function _update_plot_pos_size(plt::PlottingObject{GRPackage}, d::Dict)
end
# ----------------------------------------------------------------
function Base.getindex(plt::Plot{GRPackage}, i::Int)
d = plt.seriesargs[i]
d[:x], d[:y]
end
function Base.setindex!(plt::Plot{GRPackage}, xy::Tuple, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y] = xy
plt
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{GRPackage}, isbefore::Bool)
true
end
function _expand_limits(lims, plt::Plot{GRPackage}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{GRPackage}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
function Base.writemime(io::IO, m::MIME"image/png", plt::PlottingObject{GRPackage})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "png"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.png"))
end
function Base.writemime(io::IO, m::MIME"image/svg+xml", plt::PlottingObject{GRPackage})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "svg"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.svg"))
end
function Base.writemime(io::IO, m::MIME"text/html", plt::PlottingObject{GRPackage})
writemime(io, MIME("image/svg+xml"), plt)
end
function Base.writemime(io::IO, m::MIME"application/pdf", plt::PlottingObject{GRPackage})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "pdf"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.pdf"))
end
function Base.writemime(io::IO, m::MIME"application/postscript", plt::PlottingObject{GRPackage})
GR.emergencyclosegks()
ENV["GKS_WSTYPE"] = "ps"
gr_display(plt)
GR.emergencyclosegks()
write(io, readall("gks.ps"))
end
function Base.display(::PlotsDisplay, plt::Plot{GRPackage})
gr_display(plt)
end
function Base.display(::PlotsDisplay, plt::Subplot{GRPackage})
gr_display(plt)
true
end

View File

@ -1,6 +1,13 @@
# https://github.com/JuliaGraphics/Immerse.jl
function _initialize_backend(::ImmersePackage; kw...)
@eval begin
import Immerse, Gadfly, Compose, Gtk
export Immerse, Gadfly, Compose, Gtk
include(joinpath(Pkg.dir("Plots"), "src", "backends", "gadfly_shapes.jl"))
end
end
function createImmerseFigure(d::Dict)
w,h = d[:size]

107
src/backends/pgfplots.jl Normal file
View File

@ -0,0 +1,107 @@
# https://github.com/sisl/PGFPlots.jl
function _initialize_backend(::PGFPlotsPackage; kw...)
@eval begin
import PGFPlots
export PGFPlots
# TODO: other initialization that needs to be eval-ed
end
# TODO: other initialization
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PGFPlotsPackage; kw...)
d = Dict{Symbol,Any}(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, Dict[])
end
function _add_series(::PGFPlotsPackage, plt::Plot; kw...)
d = Dict{Symbol,Any}(kw)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
end
function _add_annotations{X,Y,V}(plt::Plot{PGFPlotsPackage}, 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{PGFPlotsPackage})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PGFPlotsPackage}, d::Dict)
end
function _update_plot_pos_size(plt::PlottingObject{PGFPlotsPackage}, d::Dict)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{PGFPlotsPackage}, i::Int)
d = plt.seriesargs[i]
d[:x], d[:y]
end
function Base.setindex!(plt::Plot{PGFPlotsPackage}, xy::Tuple, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y] = xy
plt
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PGFPlotsPackage}, 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{PGFPlotsPackage}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PGFPlotsPackage}, isx::Bool)
# TODO: if plot is inner subplot, might need to remove ticks or axis labels
end
# ----------------------------------------------------------------
# ----------------------------------------------------------------
################# This is the important method to implement!!! #################
function _make_pgf_plot(plt::Plot{PGFPlotsPackage})
# TODO: convert plt.plotargs and plt.seriesargs into PGFPlots calls
# TODO: return the PGFPlots object
end
function Base.writemime(io::IO, mime::MIME"image/png", plt::PlottingObject{PGFPlotsPackage})
plt.o = _make_pgf_plot(plt)
writemime(io, mime, plt.o)
end
# function Base.writemime(io::IO, ::MIME"text/html", plt::PlottingObject{PGFPlotsPackage})
# end
function Base.display(::PlotsDisplay, plt::PlottingObject{PGFPlotsPackage})
plt.o = _make_pgf_plot(plt)
display(plt.o)
end
# function Base.display(::PlotsDisplay, plt::Subplot{PGFPlotsPackage})
# # TODO: display/show the subplot
# end

View File

@ -1,10 +1,40 @@
# https://plot.ly/javascript/getting-started
function _initialize_backend(::PlotlyPackage; kw...)
@eval begin
import JSON
JSON._print(io::IO, state::JSON.State, dt::Union{Date,DateTime}) = print(io, '"', dt, '"')
############################
# borrowed from https://github.com/spencerlyon2/Plotlyjs.jl/blob/master/src/display.jl
_js_path = joinpath(Pkg.dir("Plots"), "deps", "plotly-latest.min.js")
# if we're in IJulia call setupnotebook to load js and css
if isijulia()
# the first script is some hack I needed to do in order for the notebook
# to not complain about Plotly being undefined
display("text/html", """
<script type="text/javascript">
require=requirejs=define=undefined;
</script>
<script type="text/javascript">
$(open(readall, _js_path, "r"))
</script>
""")
# display("text/html", "<p>Plotly javascript loaded.</p>")
end
# end borrowing (thanks :)
###########################
end
# TODO: other initialization
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PlotlyPackage; kw...)
d = Dict(kw)
d = Dict{Symbol,Any}(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, Dict[])
@ -12,7 +42,7 @@ end
function _add_series(::PlotlyPackage, plt::Plot; kw...)
d = Dict(kw)
d = Dict{Symbol,Any}(kw)
# TODO: add one series to the underlying package
push!(plt.seriesargs, d)
plt
@ -83,7 +113,7 @@ end
# _plotDefaults[:yflip] = false
function plotlyfont(font::Font, color = font.color)
Dict(
Dict{Symbol,Any}(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
:color => webcolor(color),
@ -91,7 +121,7 @@ function plotlyfont(font::Font, color = font.color)
end
function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
Dict(
Dict{Symbol,Any}(
:text => val,
:xref => "x",
:x => x,
@ -102,7 +132,7 @@ function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
end
function get_annotation_dict(x, y, ptxt::PlotText)
merge(get_annotation_dict(x, y, ptxt.str), Dict(
merge(get_annotation_dict(x, y, ptxt.str), Dict{Symbol,Any}(
:font => plotlyfont(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
@ -127,7 +157,7 @@ scalesym(isx::Bool) = symbol((isx ? "x" : "y") * "scale")
labelsym(isx::Bool) = symbol((isx ? "x" : "y") * "label")
function plotlyaxis(d::Dict, isx::Bool)
ax = Dict(
ax = Dict{Symbol,Any}(
:title => d[labelsym(isx)],
:showgrid => d[:grid],
:zeroline => false,
@ -176,9 +206,10 @@ function plotlyaxis(d::Dict, isx::Bool)
ax
end
function get_plot_json(plt::Plot{PlotlyPackage})
d = plt.plotargs
d_out = Dict()
# function get_plot_json(plt::Plot{PlotlyPackage})
# d = plt.plotargs
function plotly_layout(d::Dict)
d_out = Dict{Symbol,Any}()
bgcolor = webcolor(d[:background_color])
fgcolor = webcolor(d[:foreground_color])
@ -186,96 +217,18 @@ function get_plot_json(plt::Plot{PlotlyPackage})
# set the fields for the plot
d_out[:title] = d[:title]
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor)
d_out[:margin] = Dict(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:margin] = Dict{Symbol,Any}(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:plot_bgcolor] = bgcolor
d_out[:paper_bgcolor] = bgcolor
# TODO: x/y axis tick values/labels
d_out[:xaxis] = plotlyaxis(d, true)
d_out[:yaxis] = plotlyaxis(d, false)
# # x-axis
# d_out[:xaxis] = Dict(
# :title => d[:xlabel],
# :showgrid => d[:grid],
# :zeroline => false,
# )
# merge!(d_out[:xaxis], if use_axis_field(d[:xticks])
# ax = Dict(
# :titlefont => plotlyfont(d[:guidefont]),
# :type => plotlyscale(d[:xscale]),
# :tickfont => plotlyfont(d[:tickfont]),
# :tickcolor => fgcolor,
# :linecolor => fgcolor,
# )
# # xlims
# lims = d[:xlims]
# if lims != :auto && limsType(lims) == :limits
# ax[:range] = lims
# end
# # xflip
# if d[:xflip]
# ax[:autorange] = "reversed"
# end
# # xticks
# ticks = d[:xticks]
# 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
# ax
# else
# Dict(
# :showticklabels => false,
# :showgrid => false,
# )
# end)
# # y-axis
# d_out[:yaxis] = Dict(
# :title => d[:ylabel],
# :showgrid => d[:grid],
# :zeroline => false,
# )
# merge!(d_out[:yaxis], if use_axis_field(d[:yticks])
# Dict(
# :titlefont => plotlyfont(d[:guidefont]),
# :type => plotlyscale(d[:yscale]),
# :tickfont => plotlyfont(d[:tickfont]),
# :tickcolor => fgcolor,
# :linecolor => fgcolor,
# )
# else
# Dict(
# :showticklabels => false,
# :showgrid => false,
# )
# end)
# lims = d[:ylims]
# if lims != :auto && limsType(lims) == :limits
# d_out[:yaxis][:range] = lims
# end
# if d[:yflip]
# d_out[:yaxis][:autorange] = "reversed"
# end
# legend
d_out[:showlegend] = d[:legend]
if d[:legend]
d_out[:legend] = Dict(
d_out[:showlegend] = d[:legend] != :none
if d[:legend] != :none
d_out[:legend] = Dict{Symbol,Any}(
:bgcolor => bgcolor,
:bordercolor => fgcolor,
:font => plotlyfont(d[:legendfont]),
@ -288,17 +241,20 @@ function get_plot_json(plt::Plot{PlotlyPackage})
d_out[:annotations] = [get_annotation_dict(ann...) for ann in anns]
end
# finally build and return the json
JSON.json(d_out)
d_out
end
function get_plot_json(plt::Plot{PlotlyPackage})
JSON.json(plotly_layout(plt.plotargs))
end
function plotly_colorscale(grad::ColorGradient)
[[grad.values[i], webcolor(grad.colors[i])] for i in 1:length(grad.colors)]
function plotly_colorscale(grad::ColorGradient, alpha = nothing)
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
end
plotly_colorscale(c) = plotly_colorscale(ColorGradient(:bluesreds))
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(ColorGradient(:bluesreds), alpha)
const _plotly_markers = Dict(
const _plotly_markers = Dict{Symbol,Any}(
:rect => "square",
:xcross => "x",
:utriangle => "triangle-up",
@ -309,8 +265,8 @@ const _plotly_markers = Dict(
)
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
function get_series_json(d::Dict; plot_index = nothing)
d_out = Dict()
function plotly_series(d::Dict; plot_index = nothing)
d_out = Dict{Symbol,Any}()
x, y = collect(d[:x]), collect(d[:y])
d_out[:name] = d[:label]
@ -340,7 +296,7 @@ function get_series_json(d::Dict; plot_index = nothing)
d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y
elseif lt == :heatmap
elseif lt == :hist2d
d_out[:type] = "histogram2d"
d_out[:x], d_out[:y] = x, y
if isa(d[:nbins], Tuple)
@ -360,16 +316,26 @@ function get_series_json(d::Dict; plot_index = nothing)
d_out[:histnorm] = "probability density"
end
elseif lt in (:contour, :surface, :wireframe)
d_out[:type] = lt == :wireframe ? :surface : string(lt)
elseif lt == :heatmap
d_out[:type] = "heatmap"
d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf
# d_out[:showscale] = d[:legend]
if lt == :contour
d_out[:ncontours] = d[:nlevels]
d_out[:contours] = Dict(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
end
d_out[:colorscale] = plotly_colorscale(d[lt == :contour ? :linecolor : :fillcolor])
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] = Dict{Symbol,Any}(: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"
@ -389,30 +355,37 @@ function get_series_json(d::Dict; plot_index = nothing)
else
warn("Plotly: linetype $lt isn't supported.")
return Dict()
return Dict{Symbol,Any}()
end
# add "marker"
if hasmarker
d_out[:marker] = Dict(
d_out[:marker] = Dict{Symbol,Any}(
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
:opacity => d[:markeralpha],
:size => 2 * d[:markersize],
:color => webcolor(d[:markercolor], d[:markeralpha]),
:line => Dict(
:line => Dict{Symbol,Any}(
: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[:zcolor] != nothing
d_out[:marker][:color] = d[:zcolor]
d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor])
# d_out[:marker][:color] = d[:zcolor]
# 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[:zcolor])
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:zcolor]]
end
end
# add "line"
if hasline
d_out[:line] = Dict(
d_out[:line] = Dict{Symbol,Any}(
:color => webcolor(d[:linecolor], d[:linealpha]),
:width => d[:linewidth],
:shape => if lt == :steppre
@ -438,14 +411,14 @@ end
# get a list of dictionaries, each representing the series params
function get_series_json(plt::Plot{PlotlyPackage})
JSON.json(map(get_series_json, plt.seriesargs))
JSON.json(map(plotly_series, plt.seriesargs))
end
function get_series_json(subplt::Subplot{PlotlyPackage})
ds = Dict[]
for (i,plt) in enumerate(subplt.plts)
for d in plt.seriesargs
push!(ds, get_series_json(d, plot_index = i))
push!(ds, plotly_series(d, plot_index = i))
end
end
JSON.json(ds)
@ -479,10 +452,10 @@ function html_body(subplt::Subplot{PlotlyPackage})
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
@ -490,7 +463,7 @@ function html_body(subplt::Subplot{PlotlyPackage})
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>")
@ -501,11 +474,8 @@ end
# ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{PlotlyPackage})
isijulia() && return
# TODO: write a png to io
println("todo: png")
warn("todo: png")
end
function Base.writemime(io::IO, ::MIME"text/html", plt::PlottingObject{PlotlyPackage})

128
src/backends/plotlyjs.jl Normal file
View File

@ -0,0 +1,128 @@
# https://github.com/spencerlyon2/PlotlyJS.jl
function _initialize_backend(::PlotlyJSPackage; kw...)
@eval begin
import PlotlyJS
export PlotlyJS
end
for (mime, fmt) in PlotlyJS._mimeformats
@eval Base.writemime(io::IO, m::MIME{symbol($mime)}, p::Plot{PlotlyJSPackage}) = writemime(io, m, p.o)
end
# override IJulia inline display
if isijulia()
IJulia.display_dict(plt::PlottingObject{PlotlyJSPackage}) = IJulia.display_dict(plt.o)
end
end
# ---------------------------------------------------------------------------
function _create_plot(pkg::PlotlyJSPackage; kw...)
d = Dict(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, Dict[])
end
function _add_series(::PlotlyJSPackage, plt::Plot; kw...)
d = Dict(kw)
syncplot = plt.o
# dumpdict(d, "addseries", true)
# add to the data array
pdict = plotly_series(d)
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{PlotlyJSPackage}, 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)
end
# ----------------------------------------------------------------
function _before_update_plot(plt::Plot{PlotlyJSPackage})
end
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot(plt::Plot{PlotlyJSPackage}, d::Dict)
pdict = plotly_layout(d)
# dumpdict(pdict, "pdict updateplot", true)
syncplot = plt.o
w,h = d[:size]
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
end
function _update_plot_pos_size(plt::PlottingObject{PlotlyJSPackage}, d::Dict)
end
# ----------------------------------------------------------------
# accessors for x/y data
function Base.getindex(plt::Plot{PlotlyJSPackage}, i::Int)
d = plt.seriesargs[i]
d[:x], d[:y]
end
function Base.setindex!(plt::Plot{PlotlyJSPackage}, xy::Tuple, i::Integer)
d = plt.seriesargs[i]
d[:x], d[:y] = xy
# TODO: this is likely ineffecient... we should make a call that ONLY changes the plot data
# PlotlyJS.restyle!(plt.o, i, plotly_series(d))
PlotlyJS.restyle!(plt.o, i, Dict(:x=>(d[:x],), :y=>(d[:y],)))
plt
end
# ----------------------------------------------------------------
function _create_subplot(subplt::Subplot{PlotlyJSPackage}, 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{PlotlyJSPackage}, isx::Bool)
# TODO: call expand limits for each plot data
end
function _remove_axis(plt::Plot{PlotlyJSPackage}, 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::PlottingObject{PlotlyJSPackage})
Base.writemime(io, m, plt.o)
end
function Base.display(::PlotsDisplay, plt::Plot{PlotlyJSPackage})
display(plt.o)
end
function Base.display(::PlotsDisplay, plt::Subplot{PlotlyJSPackage})
error()
end

View File

@ -1,10 +1,37 @@
# https://github.com/stevengj/PyPlot.jl
function _initialize_backend(::PyPlotPackage)
@eval begin
import PyPlot
export PyPlot
const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path"))
const mplot3d = PyPlot.pywrap(PyPlot.pyimport("mpl_toolkits.mplot3d"))
# const pycolorbar = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colorbar"))
end
if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia
# # TODO: how the hell can I use PyQt4??
# "pyqt4"=>:qt_pyqt4
# PyPlot.backend[1] = "pyqt4"
# PyPlot.gui[1] = :qt_pyqt4
# PyPlot.switch_backend("Qt4Agg")
# only turn on the gui if we want it
if PyPlot.gui != :none
PyPlot.pygui(true)
end
end
end
# -------------------------------
# convert colorant to 4-tuple RGBA
getPyPlotColor(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
getPyPlotColor(cvec::ColorVector, α=nothing) = map(getPyPlotColor, convertColor(cvec, α).v)
getPyPlotColor(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α))
getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
# getPyPlotColor(c, alpha) = getPyPlotColor(colorscheme(c, alpha))
@ -12,12 +39,14 @@ getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
function getPyPlotColorMap(c::ColorGradient, α=nothing)
# c = ColorGradient(c.colors, c.values, alpha=α)
# pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", map(getPyPlotColor, getColorVector(c)))
pyvals = [(c.values[i], getPyPlotColor(c.colors[i], α)) for i in 1:length(c.colors)]
# pyvals = [(c.values[i], getPyPlotColor(c.colors[i], α)) for i in 1:length(c.colors)]
pyvals = [(v, getPyPlotColor(getColorZ(c, v), α)) for v in c.values]
# @show c α pyvals
pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
end
# anything else just gets a redsblue gradient
getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:redsblues), α)
# anything else just gets a bluesred gradient
getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:bluesreds), α)
# get the style (solid, dashed, etc)
function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol)
@ -62,8 +91,14 @@ function getPyPlotMarker(marker::Symbol)
return "o"
end
# getPyPlotMarker(markers::AVec) = map(getPyPlotMarker, markers)
function getPyPlotMarker(markers::AVec)
warn("Vectors of markers are currently unsupported in PyPlot: $markers")
getPyPlotMarker(markers[1])
end
# pass through
function getPyPlotMarker(marker::@compat(AbstractString))
function getPyPlotMarker(marker::AbstractString)
@assert length(marker) == 1
marker
end
@ -134,13 +169,14 @@ function getPyPlotFunction(plt::Plot, axis::Symbol, linetype::Symbol)
:density => :hist,
:sticks => :bar,
:bar => :bar,
:heatmap => :hexbin,
:hist2d => :hexbin,
:hexbin => :hexbin,
:scatter => :scatter,
:contour => :contour,
:scatter3d => :scatter,
:surface => :plot_surface,
:wireframe => :plot_wireframe,
:heatmap => :pcolor,
# :surface => pycolors.pymember("LinearSegmentedColormap")[:from_list]
)
return ax[get(fmap, linetype, :plot)]
@ -188,6 +224,41 @@ end
# ------------------------------------------------------------------
function pyplot_figure(plotargs::Dict)
w,h = map(px2inch, plotargs[:size])
bgcolor = getPyPlotColor(plotargs[:background_color])
# reuse the current figure?
fig = if plotargs[:overwrite_figure]
PyPlot.gcf()
else
PyPlot.figure()
end
# update the specs
# fig[:set_size_inches](w,h, (isijulia() ? [] : [true])...)
fig[:set_size_inches](w, h, forward = true)
fig[:set_facecolor](bgcolor)
fig[:set_dpi](DPI)
fig[:set_tight_layout](true)
# clear the figure
PyPlot.clf()
# resize the window
PyPlot.plt[:get_current_fig_manager]()[:resize](plotargs[:size]...)
fig
end
function pyplot_3d_setup!(wrap, d)
# 3D?
# if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper)
if trueOrAllTrue(lt -> lt in _3dTypes, get(d, :linetype, :none))
push!(wrap.kwargs, (:projection, "3d"))
end
end
# TODO:
# fillto # might have to use barHack/histogramHack??
# reg # true or false, add a regression line for each line
@ -204,9 +275,13 @@ function _create_plot(pkg::PyPlotPackage; kw...)
if haskey(d, :subplot)
wrap = nothing
else
w,h = map(px2inch, d[:size])
bgcolor = getPyPlotColor(d[:background_color])
wrap = PyPlotAxisWrapper(nothing, nothing, PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true), [])
wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(d), [])
# wrap = PyPlotAxisWrapper(nothing, nothing, PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true), [])
# if haskey(d, :linetype) && first(d[:linetype]) in _3dTypes # && isa(plt.o, PyPlotFigWrapper)
# push!(wrap.kwargs, (:projection, "3d"))
# end
pyplot_3d_setup!(wrap, d)
end
plt = Plot(wrap, pkg, 0, d, Dict[])
@ -217,11 +292,21 @@ end
function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
d = Dict(kw)
# 3D plots have a different underlying Axes object in PyPlot
lt = d[:linetype]
if lt in _3dTypes # && isa(plt.o, PyPlotFigWrapper)
if lt in _3dTypes && isempty(plt.o.kwargs)
push!(plt.o.kwargs, (:projection, "3d"))
end
# handle mismatched x/y sizes, as PyPlot doesn't like that
x, y = d[:x], d[:y]
nx, ny = map(length, (x,y))
if nx < ny
d[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
else
d[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
end
ax = getAxis(plt, d[:axis])
if !(lt in supportedTypes(pkg))
error("linetype $(lt) is unsupported in PyPlot. Choose from: $(supportedTypes(pkg))")
@ -232,7 +317,7 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
if lt == :sticks
d,_ = sticksHack(;d...)
elseif lt in (:scatter, :scatter3d)
if d[:markershape] == :none
d[:markershape] = :ellipse
@ -249,7 +334,7 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
end
lt = d[:linetype]
# lt = d[:linetype]
extra_kwargs = Dict()
plotfunc = getPyPlotFunction(plt, d[:axis], lt)
@ -267,7 +352,7 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
extra_kwargs[:linewidth] = (lt == :sticks ? 0.1 : 0.9)
end
elseif lt in (:heatmap, :hexbin)
elseif lt in (:hist2d, :hexbin)
extra_kwargs[:gridsize] = d[:nbins]
extra_kwargs[:cmap] = getPyPlotColorMap(d[:linecolor])
@ -286,6 +371,9 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
extra_kwargs[:linewidth] = d[:linewidth]
extra_kwargs[:edgecolor] = getPyPlotColor(d[:linecolor], d[:linealpha])
elseif lt == :heatmap
extra_kwargs[:cmap] = getPyPlotColorMap(d[:fillcolor], d[:fillalpha])
else
extra_kwargs[:linestyle] = getPyPlotLineStyle(lt, d[:linestyle])
@ -294,15 +382,28 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
if lt in (:scatter, :scatter3d)
extra_kwargs[:s] = d[:markersize].^2
c = d[:markercolor]
if isa(c, ColorGradient) && d[:zcolor] != nothing
if d[:zcolor] != nothing
if !isa(c, ColorGradient)
c = colorscheme(:bluesreds)
end
extra_kwargs[:c] = convert(Vector{Float64}, d[:zcolor])
extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeralpha])
else
extra_kwargs[:c] = getPyPlotColor(c, d[:markeralpha])
end
if d[:markeralpha] != nothing
extra_kwargs[:alpha] = d[:markeralpha]
# extra_kwargs[:c] = getPyPlotColor(c, d[:markeralpha])
ppc = getPyPlotColor(c, d[:markeralpha])
# total hack due to PyPlot bug (see issue #145).
# hack: duplicate the color vector when the total rgba fields is the same as the series length
if (typeof(ppc) <: AbstractArray && length(ppc)*4 == length(x)) ||
(typeof(ppc) <: Tuple && length(x) == 4)
ppc = vcat(ppc, ppc)
end
extra_kwargs[:c] = ppc
end
# if d[:markeralpha] != nothing
# extra_kwargs[:alpha] = d[:markeralpha]
# end
extra_kwargs[:edgecolors] = getPyPlotColor(d[:markerstrokecolor], d[:markerstrokealpha])
extra_kwargs[:linewidths] = d[:markerstrokewidth]
else
@ -321,7 +422,7 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
# end
# set these for all types
if !(lt in (:contour,:surface,:wireframe))
if !(lt in (:contour,:surface,:wireframe,:heatmap))
if !(lt in (:scatter, :scatter3d))
extra_kwargs[:color] = color
extra_kwargs[:linewidth] = d[:linewidth]
@ -333,31 +434,56 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
# do the plot
d[:serieshandle] = if ishistlike(lt)
plotfunc(d[:y]; extra_kwargs...)[1]
elseif lt == :contour
# NOTE: x/y are backwards in pyplot, so we switch the x and y args (also y is reversed),
# and take the transpose of the surface matrix
x, y = d[:x], d[:y]
surf = d[:z].surf'
handle = plotfunc(x, y, surf, d[:nlevels]; extra_kwargs...)
levels = d[:levels]
if isscalar(levels)
extra_args = (levels)
elseif isvector(levels)
extra_args = ()
extra_kwargs[:levels] = levels
else
error("Only numbers and vectors are supported with levels keyword")
end
handle = plotfunc(x, y, surf, extra_args...; extra_kwargs...)
if d[:fillrange] != nothing
handle = ax[:contourf](x, y, surf, d[:nlevels]; cmap = getPyPlotColorMap(d[:fillcolor], d[:fillalpha]))
extra_kwargs[:cmap] = getPyPlotColorMap(d[:fillcolor], d[:fillalpha])
delete!(extra_kwargs, :linewidths)
handle = ax[:contourf](x, y, surf, extra_args...; extra_kwargs...)
end
handle
elseif lt in (:surface,:wireframe)
plotfunc(repmat(d[:x]',length(d[:y]),1), repmat(d[:y],1,length(d[:x])), d[:z].surf'; extra_kwargs...)
x, y, z = d[:x], d[:y], Array(d[:z])
if !ismatrix(x) || !ismatrix(y)
x = repmat(x', length(y), 1)
y = repmat(y, 1, length(d[:x]))
z = z'
end
plotfunc(x, y, z; extra_kwargs...)
elseif lt in _3dTypes
plotfunc(d[:x], d[:y], d[:z]; extra_kwargs...)
elseif lt in (:scatter, :heatmap, :hexbin)
elseif lt in (:scatter, :hist2d, :hexbin)
plotfunc(d[:x], d[:y]; extra_kwargs...)
else
elseif lt == :heatmap
x, y, z = d[:x], d[:y], d[:z].surf'
plotfunc(heatmap_edges(x), heatmap_edges(y), z; extra_kwargs...)
else # plot
plotfunc(d[:x], d[:y]; extra_kwargs...)[1]
end
# smoothing
handleSmooth(plt, ax, d, d[:smooth])
# add the colorbar legend
if plt.plotargs[:legend] && haskey(extra_kwargs, :cmap)
PyPlot.colorbar(d[:serieshandle])
if plt.plotargs[:colorbar] != :none && haskey(extra_kwargs, :cmap)
PyPlot.colorbar(d[:serieshandle], ax=ax)
end
# @show extra_kwargs
@ -390,30 +516,52 @@ function Base.getindex(plt::Plot{PyPlotPackage}, i::Integer)
xy = series[:get_offsets]()
return vec(xy[:,1]), vec(xy[:,2])
end
# series[:relim]()
# mapping = getGadflyMappings(plt, i)[1]
# mapping[:x], mapping[:y]
end
function minmaxseries(ds, vec, axis)
lo, hi = Inf, -Inf
for d in ds
d[:axis] == axis || continue
v = d[vec]
if length(v) > 0
vlo, vhi = extrema(v)
lo = min(lo, vlo)
hi = max(hi, vhi)
end
end
if lo == hi
hi = if lo == 0
1e-6
else
hi + min(abs(1e-2hi), 1e-6)
end
end
lo, hi
end
# TODO: this needs to handle one-sided fixed limits
function set_lims!(plt::Plot{PyPlotPackage}, axis::Symbol)
ax = getAxis(plt, axis)
if plt.plotargs[:xlims] == :auto
ax[:set_xlim](minmaxseries(plt.seriesargs, :x, axis)...)
end
if plt.plotargs[:ylims] == :auto
ax[:set_ylim](minmaxseries(plt.seriesargs, :y, axis)...)
end
end
function Base.setindex!{X,Y}(plt::Plot{PyPlotPackage}, xy::Tuple{X,Y}, i::Integer)
series = plt.seriesargs[i][:serieshandle]
d = plt.seriesargs[i]
series = d[:serieshandle]
x, y = xy
d[:x], d[:y] = x, y
try
series[:set_data](x, y)
catch
series[:set_offsets](hcat(x, y))
end
ax = series[:axes]
if plt.plotargs[:xlims] == :auto
xmin, xmax = ax[:get_xlim]()
ax[:set_xlim](min(xmin, minimum(x)), max(xmax, maximum(x)))
end
if plt.plotargs[:ylims] == :auto
ymin, ymax = ax[:get_ylim]()
ax[:set_ylim](min(ymin, minimum(y)), max(ymax, maximum(y)))
end
set_lims!(plt, d[:axis])
plt
end
@ -428,7 +576,13 @@ function addPyPlotLims(ax, lims, isx::Bool)
lims == :auto && return
ltype = limsType(lims)
if ltype == :limits
ax[isx ? :set_xlim : :set_ylim](lims...)
if isx
isfinite(lims[1]) && ax[:set_xlim](left = lims[1])
isfinite(lims[2]) && ax[:set_xlim](right = lims[2])
else
isfinite(lims[1]) && ax[:set_ylim](bottom = lims[1])
isfinite(lims[2]) && ax[:set_ylim](top = lims[2])
end
else
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
end
@ -444,7 +598,8 @@ function addPyPlotTicks(ax, ticks, isx::Bool)
if ttype == :ticks
ax[isx ? :set_xticks : :set_yticks](ticks)
elseif ttype == :ticks_and_labels
ax[isx ? :set_xticks : :set_yticks](ticks...)
ax[isx ? :set_xticks : :set_yticks](ticks[1])
ax[isx ? :set_xticklabels : :set_yticklabels](ticks[2])
else
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
end
@ -465,7 +620,7 @@ function _update_plot(plt::Plot{PyPlotPackage}, d::Dict)
ax[:set_ylabel](d[:ylabel])
end
if usingRightAxis(plt) && get(d, :yrightlabel, "") != ""
rightax = getRightAxis(figorax)
rightax = getRightAxis(figorax)
rightax[:set_ylabel](d[:yrightlabel])
end
@ -494,7 +649,7 @@ function _update_plot(plt::Plot{PyPlotPackage}, d::Dict)
# font sizes
for ax in axes
# haskey(d, :yrightlabel) || continue
# guides
sz = get(d, :guidefont, plt.plotargs[:guidefont]).pointsize
@ -509,7 +664,7 @@ function _update_plot(plt::Plot{PyPlotPackage}, d::Dict)
lab[:set_fontsize](sz)
end
end
# grid
if get(d, :grid, false)
ax[:xaxis][:grid](true)
@ -562,9 +717,11 @@ end
function _create_subplot(subplt::Subplot{PyPlotPackage}, isbefore::Bool)
l = subplt.layout
w,h = map(px2inch, getplotargs(subplt,1)[:size])
bgcolor = getPyPlotColor(getplotargs(subplt,1)[:background_color])
fig = PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true)
# w,h = map(px2inch, getplotargs(subplt,1)[:size])
# bgcolor = getPyPlotColor(getplotargs(subplt,1)[:background_color])
# fig = PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true)
plotargs = getplotargs(subplt, 1)
fig = pyplot_figure(plotargs)
nr = nrows(l)
for (i,(r,c)) in enumerate(l)
@ -575,10 +732,12 @@ function _create_subplot(subplt::Subplot{PyPlotPackage}, isbefore::Bool)
ax = fig[:add_subplot](nr, nc, fakeidx)
subplt.plts[i].o = PyPlotAxisWrapper(ax, nothing, fig, [])
pyplot_3d_setup!(subplt.plts[i].o, plotargs)
end
# subplt.o = PyPlotFigWrapper(fig, [])
subplt.o = PyPlotAxisWrapper(nothing, nothing, fig, [])
pyplot_3d_setup!(subplt.o, plotargs)
true
end
@ -625,15 +784,24 @@ end
# -----------------------------------------------------------------
const _pyplot_legend_pos = Dict(
:right => "right",
:left => "center left",
:top => "upper center",
:bottom => "lower center"
)
# function addPyPlotLegend(plt::Plot)
function addPyPlotLegend(plt::Plot, ax)
if plt.plotargs[:legend]
leg = plt.plotargs[:legend]
if leg != :none
# gotta do this to ensure both axes are included
args = filter(x -> !(x[:linetype] in (:hist,:density,:hexbin,:heatmap,:hline,:vline,:contour, :path3d, :scatter3d)), plt.seriesargs)
args = filter(x -> !(x[:linetype] in (:hist,:density,:hexbin,:hist2d,:hline,:vline,:contour,:surface,:wireframe,:heatmap,:path3d,:scatter3d)), plt.seriesargs)
args = filter(x -> x[:label] != "", args)
if length(args) > 0
leg = ax[:legend]([d[:serieshandle] for d in args],
[d[:label] for d in args],
loc="best",
loc = get(_pyplot_legend_pos, leg, "best"),
fontsize = plt.plotargs[:legendfont].pointsize
# framealpha = 0.6
)
@ -713,7 +881,7 @@ const _pyplot_mimeformats = @compat Dict(
"application/pdf" => "pdf",
"image/png" => "png",
"application/postscript" => "ps",
# "image/svg+xml" => "svg"
"image/svg+xml" => "svg"
)
@ -732,6 +900,7 @@ for (mime, fmt) in _pyplot_mimeformats
end
end
# function Base.writemime(io::IO, m::MIME"image/png", subplt::Subplot{PyPlotPackage})
# finalizePlot(subplt)
# writemime(io, m, getfig(subplt.o))

View File

@ -1,6 +1,13 @@
# https://github.com/tbreloff/Qwt.jl
function _initialize_backend(::QwtPackage; kw...)
@eval begin
warn("Qwt is no longer supported... many features will likely be broken.")
import Qwt
export Qwt
end
end
# -------------------------------

View File

@ -33,6 +33,7 @@ supportedArgs(::GadflyPackage) = [
:label,
:layout,
:legend,
:colorbar,
:linestyle,
:linetype,
:linewidth,
@ -43,6 +44,7 @@ supportedArgs(::GadflyPackage) = [
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokealpha,
# :markerstrokestyle,
:n,
:nbins,
@ -74,11 +76,11 @@ supportedArgs(::GadflyPackage) = [
:legendfont,
:grid,
# :surface,
:nlevels,
:levels,
]
supportedAxes(::GadflyPackage) = [:auto, :left]
supportedTypes(::GadflyPackage) = [:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :heatmap, :hexbin, :hist, :bar,
:scatter, :hist2d, :hexbin, :hist, :bar,
:hline, :vline, :contour]
supportedStyles(::GadflyPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GadflyPackage) = vcat(_allMarkers, Shape)
@ -114,6 +116,83 @@ supportedArgs(::PyPlotPackage) = [
:label,
:layout,
:legend,
:colorbar,
:linestyle,
:linetype,
:linewidth,
:markershape,
:markercolor,
:markersize,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokealpha,
# :markerstrokestyle,
:n,
:nbins,
:nc,
:nr,
# :pos,
:smooth,
# :ribbon,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
:yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z,
:zcolor, # only supported for scatter/scatter3d
:tickfont,
:guidefont,
:legendfont,
:grid,
# :surface,
:levels,
:fillalpha,
:linealpha,
:markeralpha,
:overwrite_figure,
]
supportedAxes(::PyPlotPackage) = _allAxes
supportedTypes(::PyPlotPackage) = [:none, :line, :path, :steppre, :steppost, #:sticks,
:scatter, :hist2d, :hexbin, :hist, :density, :bar,
:hline, :vline, :contour, :path3d, :scatter3d, :surface, :wireframe, :heatmap]
supportedStyles(::PyPlotPackage) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::PyPlotPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon]
supportedMarkers(::PyPlotPackage) = vcat(_allMarkers, Shape)
supportedScales(::PyPlotPackage) = [:identity, :ln, :log2, :log10]
subplotSupported(::PyPlotPackage) = true
# --------------------------------------------------------------------------------------
supportedArgs(::GRPackage) = [
:annotation,
:axis,
:background_color,
:linecolor,
:color_palette,
:fillrange,
:fillcolor,
:foreground_color,
:group,
:label,
:layout,
:legend,
:colorbar,
:linestyle,
:linetype,
:linewidth,
@ -159,15 +238,15 @@ supportedArgs(::PyPlotPackage) = [
:linealpha,
:markeralpha,
]
supportedAxes(::PyPlotPackage) = _allAxes
supportedTypes(::PyPlotPackage) = [:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :heatmap, :hexbin, :hist, :density, :bar,
:hline, :vline, :contour, :path3d, :scatter3d, :surface, :wireframe]
supportedStyles(::PyPlotPackage) = [:auto, :solid, :dash, :dot, :dashdot]
# supportedMarkers(::PyPlotPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon]
supportedMarkers(::PyPlotPackage) = vcat(_allMarkers, Shape)
supportedScales(::PyPlotPackage) = [:identity, :ln, :log2, :log10]
subplotSupported(::PyPlotPackage) = true
supportedAxes(::GRPackage) = _allAxes
supportedTypes(::GRPackage) = [:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :hist2d, :hexbin, :hist, :density, :bar,
:hline, :vline, :contour, :path3d, :scatter3d, :surface,
:wireframe, :ohlc, :pie]
supportedStyles(::GRPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GRPackage) = vcat(_allMarkers, Shape)
supportedScales(::GRPackage) = [:identity, :log10]
subplotSupported(::GRPackage) = true
# --------------------------------------------------------------------------------------
@ -225,7 +304,7 @@ supportedArgs(::QwtPackage) = [
# :yflip,
# :z,
]
supportedTypes(::QwtPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline]
supportedTypes(::QwtPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
supportedMarkers(::QwtPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
supportedScales(::QwtPackage) = [:identity, :log10]
subplotSupported(::QwtPackage) = true
@ -284,7 +363,7 @@ supportedArgs(::UnicodePlotsPackage) = [
# :z,
]
supportedAxes(::UnicodePlotsPackage) = [:auto, :left]
supportedTypes(::UnicodePlotsPackage) = [:none, :line, :path, :steppost, :sticks, :scatter, :heatmap, :hexbin, :hist, :bar, :hline, :vline]
supportedTypes(::UnicodePlotsPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :hist2d, :hexbin, :hist, :bar, :hline, :vline]
supportedStyles(::UnicodePlotsPackage) = [:auto, :solid]
supportedMarkers(::UnicodePlotsPackage) = [:none, :auto, :ellipse]
supportedScales(::UnicodePlotsPackage) = [:identity]
@ -413,10 +492,10 @@ supportedArgs(::BokehPackage) = [
# :legendfont,
# :grid,
# :surface,
# :nlevels,
# :levels,
]
supportedAxes(::BokehPackage) = [:auto, :left]
supportedTypes(::BokehPackage) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedTypes(::BokehPackage) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedStyles(::BokehPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::BokehPackage) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::BokehPackage) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
@ -479,12 +558,12 @@ supportedArgs(::PlotlyPackage) = [
:guidefont,
:legendfont,
:grid,
:nlevels,
:levels,
]
supportedAxes(::PlotlyPackage) = [:auto, :left]
supportedTypes(::PlotlyPackage) = [:none, :line, :path, :scatter, :steppre, :steppost,
:heatmap, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie] #,, :sticks, :hexbin, :hline, :vline]
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyPackage) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyPackage) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
@ -493,6 +572,75 @@ subplotSupported(::PlotlyPackage) = true
stringsSupported(::PlotlyPackage) = true
# --------------------------------------------------------------------------------------
supportedArgs(::PlotlyJSPackage) = [
:annotation,
# :axis,
:background_color,
:color_palette,
:fillrange,
:fillcolor,
:fillalpha,
:foreground_color,
:group,
:label,
:layout,
:legend,
:linecolor,
:linestyle,
:linetype,
:linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokestyle,
:n,
:nbins,
:nc,
:nr,
# :pos,
# :smooth,
:show,
:size,
:title,
:windowtitle,
:x,
:xlabel,
:xlims,
:xticks,
:y,
:ylabel,
:ylims,
# :yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z,
:zcolor,
:tickfont,
:guidefont,
:legendfont,
:grid,
:levels,
]
supportedAxes(::PlotlyJSPackage) = [:auto, :left]
supportedTypes(::PlotlyJSPackage) = [:none, :line, :path, :scatter, :steppre, :steppost,
:hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyJSPackage) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyJSPackage) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
supportedScales(::PlotlyJSPackage) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::PlotlyJSPackage) = true
stringsSupported(::PlotlyJSPackage) = true
# --------------------------------------------------------------------------------------
supportedArgs(::GLVisualizePackage) = [
@ -549,7 +697,7 @@ supportedArgs(::GLVisualizePackage) = [
# :legendfont,
# :grid,
# :surface
# :nlevels,
# :levels,
]
supportedAxes(::GLVisualizePackage) = [:auto, :left]
supportedTypes(::GLVisualizePackage) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour]
@ -557,3 +705,69 @@ supportedStyles(::GLVisualizePackage) = [:auto, :solid] #, :dash, :dot, :dashdot
supportedMarkers(::GLVisualizePackage) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::GLVisualizePackage) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
subplotSupported(::GLVisualizePackage) = false
# --------------------------------------------------------------------------------------
supportedArgs(::PGFPlotsPackage) = [
# :annotation,
# :axis,
# :background_color,
# :color_palette,
# :fillrange,
# :fillcolor,
# :fillalpha,
# :foreground_color,
# :group,
# :label,
# :layout,
# :legend,
# :linecolor,
# :linestyle,
# :linetype,
# :linewidth,
# :linealpha,
# :markershape,
# :markercolor,
# :markersize,
# :markeralpha,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
# :nbins,
# :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(::PGFPlotsPackage) = [:auto, :left]
supportedTypes(::PGFPlotsPackage) = [:contour] #, :path, :scatter ,:steppre, :steppost, :sticks, :hist2d, :hexbin, :hist, :bar, :hline, :vline, :contour]
supportedStyles(::PGFPlotsPackage) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::PGFPlotsPackage) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::PGFPlotsPackage) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
subplotSupported(::PGFPlotsPackage) = false

View File

@ -3,6 +3,15 @@
# [WEBSITE]
function _initialize_backend(::[PkgName]Package; 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]Package; kw...)
@ -43,13 +52,11 @@ end
# accessors for x/y data
function Base.getindex(plt::Plot{[PkgName]Package}, i::Int)
series = plt.o.lines[i]
series.x, series.y
# TODO: return a tuple of (x, y) vectors
end
function Base.setindex!(plt::Plot{[PkgName]Package}, xy::Tuple, i::Integer)
series = plt.o.lines[i]
series.x, series.y = xy
# TODO: set the plot data from the (x,y) tuple
plt
end

View File

@ -1,6 +1,13 @@
# https://github.com/Evizero/UnicodePlots.jl
function _initialize_backend(::UnicodePlotsPackage; kw...)
@eval begin
import UnicodePlots
export UnicodePlots
end
end
# -------------------------------
@ -52,7 +59,7 @@ function rebuildUnicodePlot!(plt::Plot)
# now use the ! functions to add to the plot
for d in sargs
addUnicodeSeries!(o, d, iargs[:legend], xlim, ylim)
addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
end
# save the object
@ -91,13 +98,13 @@ function addUnicodeSeries!(o, d::Dict, addlegend::Bool, xlim, ylim)
else
error("Linestyle $lt not supported by UnicodePlots")
end
# get the series data and label
x, y = [collect(float(d[s])) for s in (:x, :y)]
label = addlegend ? d[:label] : ""
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide
color = d[:linecolor] in UnicodePlots.autoColors ? d[:linecolor] : :auto
color = d[:linecolor] in UnicodePlots.color_cycle ? d[:linecolor] : :auto
# add the series
func(o, x, y; color = color, name = label, style = stepstyle)

View File

@ -3,6 +3,14 @@
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
function _initialize_backend(::WinstonPackage; kw...)
@eval begin
# ENV["WINSTON_OUTPUT"] = "gtk"
warn("Winston is no longer supported... many features will likely be broken.")
import Winston, Gtk
export Winston, Gtk
end
end
# ---------------------------------------------------------------------------
@ -80,7 +88,7 @@ function _add_series(::WinstonPackage, plt::Plot; kw...)
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :heatmap, :hexbin, :hist, :bar
## lintype :path, :step, :stepinverted, :sticks, :dots, :none, :hist2d, :hexbin, :hist, :bar
if d[:linetype] == :none
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor])))
@ -115,7 +123,7 @@ function _add_series(::WinstonPackage, plt::Plot; kw...)
# elseif d[:linetype] == :dots
# fn = Winston.XXX
# elseif d[:linetype] == :heatmap
# elseif d[:linetype] == :hist2d
# fn = Winston.XXX
# elseif d[:linetype] == :hexbin
@ -198,7 +206,7 @@ end
# ----------------------------------------------------------------
function addWinstonLegend(plt::Plot, wplt)
if plt.plotargs[:legend]
if plt.plotargs[:legend] != :none
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
end
end

View File

@ -72,33 +72,44 @@ const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcya
"Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
immutable ColorGradient <: ColorScheme
colors::Vector{Colorant}
values::Vector{Float64}
colors::Vector
values::Vector
function ColorGradient{T<:Colorant,S<:Real}(cs::AVec{T}, vals::AVec{S} = 0:1; alpha = nothing)
function ColorGradient{S<:Real}(cs::AVec, vals::AVec{S} = linspace(0, 1, length(cs)); alpha = nothing)
if length(cs) == length(vals)
return new(convertColor(cs,alpha), collect(vals))
end
# otherwise interpolate evenly between the minval and maxval
minval, maxval = minimum(vals), maximum(vals)
vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
new(convertColor(cs,alpha), vs)
# # otherwise interpolate evenly between the minval and maxval
# minval, maxval = minimum(vals), maximum(vals)
# vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
# new(convertColor(cs,alpha), vs)
# interpolate the colors for each value
vals = merge(linspace(0, 1, length(cs)), vals)
grad = ColorGradient(cs)
cs = [getColorZ(grad, z) for z in linspace(0, 1, length(vals))]
new(convertColor(cs, alpha), vals)
end
end
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:1; kw...)
function ColorGradient{T<:Real}(s::Symbol, vals::AVec{T} = 0:0; kw...)
haskey(_gradients, s) || error("Invalid gradient symbol. Choose from: ", sort(collect(keys(_gradients))))
# if we passed in the right number of values, create the gradient directly
cs = _gradients[s]
if vals == 0:0
vals = linspace(0, 1, length(cs))
end
ColorGradient(cs, vals; kw...)
end
function ColorGradient{T<:Real}(cs::AVec{Symbol}, vals::AVec{T} = 0:1; kw...)
ColorGradient(map(convertColor, cs), vals; kw...)
end
# function ColorGradient{T<:Real}(cs::AVec, vals::AVec{T} = linspace(0, 1, length(cs)); kw...)
# ColorGradient(map(convertColor, cs), vals; kw...)
# end
# function ColorGradient(grad::ColorGradient; alpha = nothing)
# ColorGradient(convertColor(grad.colors, alpha), grad.values)
# end
getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]

View File

@ -131,6 +131,7 @@ immutable PlotText
str::@compat(AbstractString)
font::Font
end
PlotText(str) = PlotText(string(str), font())
function text(str, args...)
PlotText(string(str), font(args...))
@ -157,7 +158,8 @@ function stroke(args...; alpha = nothing)
for arg in args
T = typeof(arg)
if arg in _allStyles
# if arg in _allStyles
if allStyles(arg)
style = arg
elseif T <: Colorant
color = arg
@ -165,7 +167,11 @@ function stroke(args...; alpha = nothing)
try
color = parse(Colorant, string(arg))
end
elseif typeof(arg) <: Real
# 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
warn("Unused stroke arg: $arg ($(typeof(arg)))")
@ -198,7 +204,11 @@ function brush(args...; alpha = nothing)
try
color = parse(Colorant, string(arg))
end
elseif typeof(arg) <: Real
# 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
warn("Unused brush arg: $arg ($(typeof(arg)))")
@ -231,6 +241,13 @@ end
Surface(f::Function, x, y) = Surface(Float64[f(xi,yi) for xi in x, yi in y])
Base.Array(surf::Surface) = surf.surf
for f in (:length, :size)
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
end
Base.copy(surf::Surface) = Surface(copy(surf.surf))
# -----------------------------------------------------------------------
type OHLC{T<:Real}
@ -239,3 +256,71 @@ type OHLC{T<:Real}
low::T
close::T
end
# @require FixedSizeArrays begin
export
P2,
P3,
BezierCurve,
curve_points,
directed_curve
typealias P2 FixedSizeArrays.Vec{2,Float64}
typealias P3 FixedSizeArrays.Vec{3,Float64}
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
[]
end
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
end
# end

View File

@ -37,6 +37,15 @@ end
ps(fn::@compat(AbstractString)) = ps(current(), fn)
function tex(plt::PlottingObject, fn::@compat(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)
# ----------------------------------------------------------------
@ -45,6 +54,7 @@ ps(fn::@compat(AbstractString)) = ps(current(), fn)
"svg" => svg,
"pdf" => pdf,
"ps" => ps,
"tex" => tex,
)
function getExtension(fn::@compat(AbstractString))
@ -102,3 +112,8 @@ gui(plt::PlottingObject = current()) = display(PlotsDisplay(), plt)
# override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::PlottingObject) = gui(plt)
# a backup for html... passes to svg
function Base.writemime(io::IO, ::MIME"text/html", plt::PlottingObject)
writemime(io, MIME("image/svg+xml"), plt)
end

View File

@ -48,7 +48,7 @@ function plot(args...; kw...)
preprocessArgs!(d)
dumpdict(d, "After plot preprocessing")
plotargs = getPlotArgs(pkg, d, 1)
plotargs = merge(d, getPlotArgs(pkg, d, 1))
dumpdict(plotargs, "Plot args")
plt = _create_plot(pkg; plotargs...) # create a new, blank plot
@ -186,12 +186,21 @@ updateDictWithMeta(d::Dict, plotargs::Dict, 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::Dict)
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
@ -219,41 +228,50 @@ end
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
all3D(d::Dict) = trueOrAllTrue(lt -> lt in (:contour, :heatmap, :surface, :wireframe), get(d, :linetype, :none))
# missing
convertToAnyVector(v::@compat(Void); kw...) = Any[nothing], nothing
convertToAnyVector(v::@compat(Void), d::Dict) = Any[nothing], nothing
# fixed number of blank series
convertToAnyVector(n::Integer; kw...) = Any[zeros(0) for i in 1:n], nothing
convertToAnyVector(n::Integer, d::Dict) = Any[zeros(0) for i in 1:n], nothing
# numeric vector
convertToAnyVector{T<:Real}(v::AVec{T}; kw...) = Any[v], nothing
convertToAnyVector{T<:Real}(v::AVec{T}, d::Dict) = Any[v], nothing
# string vector
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}; kw...) = Any[v], nothing
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::Dict) = Any[v], nothing
# numeric matrix
convertToAnyVector{T<:Real}(v::AMat{T}; kw...) = Any[v[:,i] for i in 1:size(v,2)], nothing
function convertToAnyVector{T<:Real}(v::AMat{T}, d::Dict)
if all3D(d)
Any[Surface(v)]
else
Any[v[:,i] for i in 1:size(v,2)]
end, nothing
end
# function
convertToAnyVector(f::Function; kw...) = Any[f], nothing
convertToAnyVector(f::Function, d::Dict) = Any[f], nothing
# surface
convertToAnyVector(s::Surface; kw...) = Any[s], nothing
convertToAnyVector(s::Surface, d::Dict) = Any[s], nothing
# vector of OHLC
convertToAnyVector(v::AVec{OHLC}; kw...) = Any[v], nothing
convertToAnyVector(v::AVec{OHLC}, d::Dict) = Any[v], nothing
# dates
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}; kw...) = Any[dts], nothing
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::Dict) = Any[dts], nothing
# list of things (maybe other vectors, functions, or something else)
function convertToAnyVector(v::AVec; kw...)
function convertToAnyVector(v::AVec, d::Dict)
if all(x -> typeof(x) <: Real, v)
# all real numbers wrap the whole vector as one item
Any[convert(Vector{Float64}, v)], nothing
else
# something else... treat each element as an item
Any[vi for vi in v], nothing
vcat(Any[convertToAnyVector(vi, d)[1] for vi in v]...), nothing
# Any[vi for vi in v], nothing
end
end
@ -262,7 +280,7 @@ end
# in computeXandY, we take in any of the possible items, convert into proper x/y vectors, then return.
# this is also where all the "set x to 1:length(y)" happens, and also where we assert on lengths.
computeX(x::@compat(Void), y) = 1:length(y)
computeX(x::@compat(Void), y) = 1:size(y,1)
computeX(x, y) = copy(x)
computeY(x, y::Function) = map(y, x)
computeY(x, y) = copy(y)
@ -281,8 +299,9 @@ end
# create n=max(mx,my) series arguments. the shorter list is cycled through
# note: everything should flow through this
function createKWargsList(plt::PlottingObject, x, y; kw...)
xs, xmeta = convertToAnyVector(x; kw...)
ys, ymeta = convertToAnyVector(y; kw...)
kwdict = Dict(kw)
xs, xmeta = convertToAnyVector(x, kwdict)
ys, ymeta = convertToAnyVector(y, kwdict)
mx = length(xs)
my = length(ys)
@ -290,7 +309,7 @@ function createKWargsList(plt::PlottingObject, x, y; kw...)
for i in 1:max(mx, my)
# try to set labels using ymeta
d = Dict(kw)
d = copy(kwdict)
if !haskey(d, :label) && ymeta != nothing
if isa(ymeta, Symbol)
d[:label] = string(ymeta)
@ -307,13 +326,22 @@ function createKWargsList(plt::PlottingObject, x, y; kw...)
dumpdict(d, "after getSeriesArgs")
d[:x], d[:y] = computeXandY(xs[mod1(i,mx)], ys[mod1(i,my)])
lt = d[:linetype]
if isa(d[:y], Surface)
if lt in (:contour, :heatmap, :surface, :wireframe)
z = d[:y]
d[:y] = 1:size(z,2)
d[:z] = z
end
end
if haskey(d, :idxfilter)
d[:x] = d[:x][d[:idxfilter]]
d[:y] = d[:y][d[:idxfilter]]
end
# for linetype `line`, need to sort by x values
if d[:linetype] == :line
if lt == :line
# order by x
indices = sortperm(d[:x])
d[:x] = d[:x][indices]
@ -321,6 +349,14 @@ function createKWargsList(plt::PlottingObject, x, y; kw...)
d[:linetype] = :path
end
# map functions to vectors
if isa(d[:zcolor], Function)
d[:zcolor] = map(d[:zcolor], d[:x])
end
if isa(d[:fillrange], Function)
d[:fillrange] = map(d[:fillrange], d[:x])
end
# cleanup those fields that were used only for generating kw args
for k in (:idxfilter, :numUncounted, :dataframe)
delete!(d, k)
@ -361,6 +397,16 @@ function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zvec::AVec; kw.
createKWargsList(plt, x, y; z=zvec, d...)
end
function createKWargsList{T<:Real}(plt::PlottingObject, z::AMat{T}; kw...)
d = Dict(kw)
if all3D(d)
n,m = size(z)
createKWargsList(plt, 1:n, 1:m, z; kw...)
else
createKWargsList(plt, nothing, z; kw...)
end
end
# contours or surfaces... function grid
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zf::Function; kw...)
# only allow sorted x/y for now
@ -378,14 +424,15 @@ function createKWargsList{T<:Real}(plt::PlottingObject, x::AVec, y::AVec, zmat::
@assert x == sort(x)
@assert y == sort(y)
@assert size(zmat) == (length(x), length(y))
surf = Surface(convert(Matrix{Float64}, zmat))
# surf = Surface(convert(Matrix{Float64}, zmat))
# surf = Array(Any,1,1)
# surf[1,1] = convert(Matrix{Float64}, zmat)
d = Dict(kw)
if !(get(d, :linetype, :none) in (:contour, :surface, :wireframe))
d[:z] = Surface(convert(Matrix{Float64}, zmat))
if !(get(d, :linetype, :none) in (:contour, :heatmap, :surface, :wireframe))
d[:linetype] = :contour
end
createKWargsList(plt, x, y; d..., z = surf)
createKWargsList(plt, x, y; d...) #, z = surf)
end
# contours or surfaces... general x, y grid
@ -394,7 +441,12 @@ function createKWargsList{T<:Real}(plt::PlottingObject, x::AMat{T}, y::AMat{T},
surf = Surface(convert(Matrix{Float64}, zmat))
# surf = Array(Any,1,1)
# surf[1,1] = convert(Matrix{Float64}, zmat)
createKWargsList(plt, x, y; kw..., surface = surf, linetype = :contour)
d = Dict(kw)
d[:z] = Surface(convert(Matrix{Float64}, zmat))
if !(get(d, :linetype, :none) in (:contour, :heatmap, :surface, :wireframe))
d[:linetype] = :contour
end
createKWargsList(plt, Any[x], Any[y]; d...) #kw..., z = surf, linetype = :contour)
end
@ -407,7 +459,7 @@ function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, surf::Surface;
end
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs; kw...)
error("Can't pass a Function or Vector{Function} for y without also passing x")
createKWargsList(plt, f, xmin(plt), xmax(plt); kw...)
end
# list of functions
@ -431,6 +483,19 @@ createKWargsList{T<:Real}(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs,
createKWargsList{T<:Real}(plt::PlottingObject, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u); kw...)
createKWargsList(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Real, umax::Real, numPoints::Int = 1000; kw...) = createKWargsList(plt, fx, fy, linspace(umin, umax, numPoints); kw...)
# special handling... 3D parametric function(s)
createKWargsList{T<:Real}(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec{T}; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u); kw...)
createKWargsList{T<:Real}(plt::PlottingObject, u::AVec{T}, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs; kw...) = createKWargsList(plt, mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u); kw...)
createKWargsList(plt::PlottingObject, fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Real, umax::Real, numPoints::Int = 1000; kw...) = createKWargsList(plt, fx, fy, fz, linspace(umin, umax, numPoints); kw...)
# (x,y) tuples
function createKWargsList{R1<:Real,R2<:Real}(plt::PlottingObject, xy::AVec{Tuple{R1,R2}}; kw...)
createKWargsList(plt, unzip(xy)...; kw...)
end
function createKWargsList{R1<:Real,R2<:Real}(plt::PlottingObject, xy::Tuple{R1,R2}; kw...)
createKWargsList(plt, [xy[1]], [xy[2]]; kw...)
end
# special handling... no args... 1 series
@ -451,16 +516,34 @@ end
# --------------------------------------------------------------------
"For DataFrame support. Imports DataFrames and defines the necessary methods which support them."
function dataframes()
@eval import DataFrames
@eval function createKWargsList(plt::PlottingObject, df::DataFrames.DataFrame, args...; kw...)
# @require FixedSizeArrays begin
unzip{T}(x::AVec{FixedSizeArrays.Vec{2,T}}) = T[xi[1] for xi in x], T[xi[2] for xi in x]
unzip{T}(x::FixedSizeArrays.Vec{2,T}) = T[x[1]], T[x[2]]
function createKWargsList{T<:Real}(plt::PlottingObject, xy::AVec{FixedSizeArrays.Vec{2,T}}; kw...)
createKWargsList(plt, unzip(xy)...; kw...)
end
function createKWargsList{T<:Real}(plt::PlottingObject, xy::FixedSizeArrays.Vec{2,T}; kw...)
createKWargsList(plt, [xy[1]], [xy[2]]; kw...)
end
# end
# --------------------------------------------------------------------
# For DataFrame support. Imports DataFrames and defines the necessary methods which support them.
@require DataFrames begin
function createKWargsList(plt::PlottingObject, df::DataFrames.AbstractDataFrame, args...; kw...)
createKWargsList(plt, args...; kw..., dataframe = df)
end
# expecting the column name of a dataframe that was passed in... anything else should error
@eval function extractGroupArgs(s::Symbol, df::DataFrames.DataFrame, args...)
function extractGroupArgs(s::Symbol, df::DataFrames.AbstractDataFrame, args...)
if haskey(df, s)
return extractGroupArgs(df[s])
else
@ -468,18 +551,23 @@ function dataframes()
end
end
@eval function getDataFrameFromKW(; kw...)
for (k,v) in kw
if k == :dataframe
return v
end
function getDataFrameFromKW(d::Dict)
# for (k,v) in kw
# if k == :dataframe
# return v
# end
# end
get(d, :dataframe) do
error("Missing dataframe argument!")
end
error("Missing dataframe argument in arguments!")
end
# the conversion functions for when we pass symbols or vectors of symbols to reference dataframes
@eval convertToAnyVector(s::Symbol; kw...) = Any[getDataFrameFromKW(;kw...)[s]], s
@eval convertToAnyVector(v::AVec{Symbol}; kw...) = (df = getDataFrameFromKW(;kw...); Any[df[s] for s in v]), v
# convertToAnyVector(s::Symbol; kw...) = Any[getDataFrameFromKW(;kw...)[s]], s
# convertToAnyVector(v::AVec{Symbol}; kw...) = (df = getDataFrameFromKW(;kw...); Any[df[s] for s in v]), v
convertToAnyVector(s::Symbol, d::Dict) = Any[getDataFrameFromKW(d)[s]], s
convertToAnyVector(v::AVec{Symbol}, d::Dict) = (df = getDataFrameFromKW(d); Any[df[s] for s in v]), v
end

121
src/plotter2.jl Normal file
View File

@ -0,0 +1,121 @@
immutable NoPackage <: PlottingPackage end
const _backendType = Dict{Symbol, DataType}(:none => NoPackage)
const _backendSymbol = Dict{DataType, Symbol}(NoPackage => :none)
const _backends = Symbol[]
const _initialized_backends = Set{Symbol}()
backends() = _backends
backend_name() = CURRENT_BACKEND.sym
_backend_instance(sym::Symbol) = haskey(_backendType, sym) ? _backendType[sym]() : error("Unsupported backend $sym")
macro init_plotting_pkg(s)
str = lowercase(string(s))
sym = symbol(str)
T = symbol(string(s) * "Package")
esc(quote
immutable $T <: PlottingPackage 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)
include("backends/" * $str * ".jl")
end)
end
@init_plotting_pkg Immerse
@init_plotting_pkg Gadfly
@init_plotting_pkg PyPlot
@init_plotting_pkg Qwt
@init_plotting_pkg UnicodePlots
@init_plotting_pkg Winston
@init_plotting_pkg Bokeh
@init_plotting_pkg Plotly
@init_plotting_pkg PlotlyJS
@init_plotting_pkg GR
@init_plotting_pkg GLVisualize
@init_plotting_pkg PGFPlots
include("backends/web.jl")
include("backends/supported.jl")
# ---------------------------------------------------------
plot(pkg::PlottingPackage; kw...) = error("plot($pkg; kw...) is not implemented")
plot!(pkg::PlottingPackage, plt::Plot; kw...) = error("plot!($pkg, plt; kw...) is not implemented")
_update_plot(pkg::PlottingPackage, plt::Plot, d::Dict) = error("_update_plot($pkg, plt, d) is not implemented")
_update_plot_pos_size{P<:PlottingPackage}(plt::PlottingObject{P}, d::Dict) = nothing
subplot(pkg::PlottingPackage; kw...) = error("subplot($pkg; kw...) is not implemented")
subplot!(pkg::PlottingPackage, subplt::Subplot; kw...) = error("subplot!($pkg, subplt; kw...) is not implemented")
# ---------------------------------------------------------
type CurrentBackend
sym::Symbol
pkg::PlottingPackage
end
CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
# ---------------------------------------------------------
function pickDefaultBackend()
for pkgstr in ("PyPlot", "Immerse", "Qwt", "Gadfly", "GR", "UnicodePlots", "Bokeh", "GLVisualize")
if Pkg.installed(pkgstr) != nothing
return backend(symbol(lowercase(pkgstr)))
end
end
# the default if nothing else is installed
backend(:plotly)
end
# ---------------------------------------------------------
"""
Returns the current plotting package name. Initializes package on first call.
"""
function backend()
global CURRENT_BACKEND
if CURRENT_BACKEND.sym == :none
pickDefaultBackend()
end
sym = CURRENT_BACKEND.sym
if !(sym in _initialized_backends)
# initialize
println("[Plots.jl] Initializing backend: ", sym)
inst = _backend_instance(sym)
try
_initialize_backend(inst)
catch err
warn("Couldn't initialize $sym. (might need to install it?)")
rethrow(err)
end
push!(_initialized_backends, sym)
end
CURRENT_BACKEND.pkg
end
"""
Set the plot backend.
"""
function backend(pkg::PlottingPackage)
CURRENT_BACKEND.sym = backend_name(pkg)
CURRENT_BACKEND.pkg = pkg
end
function backend(modname::Symbol)
CURRENT_BACKEND.sym = modname
CURRENT_BACKEND.pkg = _backend_instance(modname)
end

View File

@ -125,8 +125,15 @@ end
"Sparsity plot... heatmap of non-zero values of a matrix"
function spy{T<:Real}(y::AMat{T}; kw...)
I,J,V = findnz(y)
heatmap(J, I; leg=false, yflip=true, nbins=size(y), kw...)
function spy{T<:Real}(z::AMat{T}; kw...)
# I,J,V = findnz(z)
# heatmap(J, I; leg=false, yflip=true, kw...)
heatmap(map(zi->float(zi!=0), z); leg=false, yflip=true, kw...)
end
"Adds a+bx... straight line over the current plot"
function abline!(plt::Plot, a, b; kw...)
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
end
abline!(args...; kw...) = abline!(current(), args...; kw...)

View File

@ -135,7 +135,7 @@ 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(backendInstance(pkg)), backends()),", "))
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

View File

@ -174,11 +174,20 @@ function replaceAliases!(d::Dict, aliases::Dict)
end
createSegments(z) = collect(repmat(z',2,1))[2:end]
Base.first(c::Colorant) = c
Base.first(x::Symbol) = x
sortedkeys(d::Dict) = sort(collect(keys(d)))
"create an (n+1) list of the outsides of heatmap rectangles"
function heatmap_edges(v::AVec)
vmin, vmax = extrema(v)
extra = 0.5 * (vmax-vmin) / (length(v)-1)
vcat(vmin-extra, 0.5 * (v[1:end-1] + v[2:end]), vmax+extra)
end
function fakedata(sz...)
y = zeros(sz...)
@ -190,9 +199,21 @@ end
isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited
istuple(::Tuple) = true
istuple(::Any) = false
isvector(::AVec) = true
isvector(::Any) = false
ismatrix(::AMat) = true
ismatrix(::Any) = false
isscalar(::Real) = true
isscalar(::Any) = false
# ticksType{T<:Real,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
ticksType{T<:Real}(ticks::AVec{T}) = :ticks
ticksType{T<:AbstractString}(ticks::AVec{T}) = :labels
ticksType{T<:AVec,S<:AVec}(ticks::@compat(Tuple{T,S})) = :ticks_and_labels
ticksType(ticks) = :invalid
@ -204,9 +225,28 @@ limsType(lims) = :invalid
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]
Base.merge(a::AbstractVector, b::AbstractVector) = sort(unique(vcat(a,b)))
# ---------------------------------------------------------------
wraptuple(x::@compat(Tuple)) = x
wraptuple(x) = (x,)
trueOrAllTrue(f::Function, x::AbstractArray) = all(f, x)
trueOrAllTrue(f::Function, x) = f(x)
allLineTypes(arg) = trueOrAllTrue(a -> get(_typeAliases, a, a) in _allTypes, arg)
allStyles(arg) = trueOrAllTrue(a -> get(_styleAliases, a, a) in _allStyles, arg)
allShapes(arg) = trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg) ||
trueOrAllTrue(a -> isa(a, Shape), arg)
allAlphas(arg) = trueOrAllTrue(a -> (typeof(a) <: Real && a > 0 && a < 1) ||
(typeof(a) <: AbstractFloat && (a == zero(typeof(a)) || a == one(typeof(a)))), arg)
allReals(arg) = trueOrAllTrue(a -> typeof(a) <: Real, arg)
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:
```
@ -226,10 +266,13 @@ function with(f::Function, args...; kw...)
end
# save the backend
if CURRENT_BACKEND.sym == :none
pickDefaultBackend()
end
oldbackend = CURRENT_BACKEND.sym
for arg in args
# change backend?
if arg in backends()
backend(arg)
@ -399,15 +442,15 @@ function supportGraph(allvals, func)
y = ASCIIString[]
for val in vals
for b in bs
supported = func(Plots.backendInstance(b))
supported = func(Plots._backend_instance(b))
if val in supported
push!(x, string(b))
push!(y, string(val))
end
end
end
end
n = length(vals)
scatter(x,y,
m=:rect,
ms=10,
@ -428,7 +471,7 @@ function dumpSupportGraphs()
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
supportGraphMarkers, supportGraphScales, supportGraphAxes)
plt = func()
png(IMG_DIR * "/supported/$(string(func))")
png(joinpath(Pkg.dir("ExamplePlots"), "docs", "examples", "img", "supported", "$(string(func))"))
end
end
@ -448,3 +491,11 @@ mm2inch(mm::Real) = float(mm / MM_PER_INCH)
px2mm(px::Real) = float(px * MM_PER_PX)
mm2px(mm::Real) = float(px / MM_PER_PX)
"Smallest x in plot"
xmin(plt::Plot) = minimum([minimum(d[:x]) for d in plt.seriesargs])
"Largest x in plot"
xmax(plt::Plot) = maximum([maximum(d[:x]) for d in plt.seriesargs])
"Extrema of x-values in plot"
Base.extrema(plt::Plot) = (xmin(plt), xmax(plt))

View File

@ -2,9 +2,11 @@ julia 0.4
Colors
Reexport
Requires
FactCheck
Gadfly
Images
ImageMagick
PyPlot
@osx QuartzImageIO
GR

View File

@ -1,11 +1,4 @@
# # include this first to help with crashing??
# try
# @eval using Gtk
# catch err
# warn("Gtk not loaded. err: $err")
# end
using VisualRegressionTests
using ExamplePlots
@ -17,53 +10,10 @@ try
info("Matplotlib version: $(PyPlot.matplotlib[:__version__])")
end
# include("../docs/example_generation.jl")
using Plots, FactCheck
# import Images, ImageMagick
# if !isdefined(ImageMagick, :init_deps)
# function ImageMagick.init_deps()
# ccall((:MagickWandGenesis,libwand), Void, ())
# end
# end
# function makeImageWidget(fn)
# img = Gtk.GtkImageLeaf(fn)
# vbox = Gtk.GtkBoxLeaf(:v)
# push!(vbox, Gtk.GtkLabelLeaf(fn))
# push!(vbox, img)
# show(img)
# vbox
# end
# function replaceReferenceImage(tmpfn, reffn)
# cmd = `cp $tmpfn $reffn`
# run(cmd)
# info("Replaced reference image with: $cmd")
# end
# "Show a Gtk popup with both images and a confirmation whether we should replace the new image with the old one"
# function compareToReferenceImage(tmpfn, reffn)
# # add the images
# imgbox = Gtk.GtkBoxLeaf(:h)
# push!(imgbox, makeImageWidget(tmpfn))
# push!(imgbox, makeImageWidget(reffn))
# win = Gtk.GtkWindowLeaf("Should we make this the new reference image?")
# push!(win, Gtk.GtkFrameLeaf(imgbox))
# showall(win)
# # now ask the question
# if Gtk.ask_dialog("Should we make this the new reference image?", "No", "Yes")
# replaceReferenceImage(tmpfn, reffn)
# end
# destroy(win)
# end
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
@ -73,7 +23,7 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
# first
Plots._debugMode.on = debug
example = ExamplePlots.examples[idx]
example = ExamplePlots._examples[idx]
info("Testing plot: $pkg:$idx:$(example.header)")
backend(pkg)
backend()
@ -81,24 +31,15 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
# ensure consistent results
srand(1234)
# reference image directory setup
refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
# test function
func = (fn, idx) -> begin
map(eval, example.exprs)
png(fn)
end
# run the example
# map(eval, PlotExamples.examples[idx].exprs)
# # save the png
# tmpfn = tempname() * ".png"
# png(tmpfn)
# # load the saved png
# tmpimg = Images.load(tmpfn)
# reference image directory setup
refdir = joinpath(Pkg.dir("Plots"), "test", "refimg", string(pkg))
try
run(`mkdir -p $refdir`)
catch err
@ -109,47 +50,18 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
# the test
vtest = VisualTest(func, reffn, idx)
test_images(vtest, popup=popup, sigma=sigma, eps=eps)
# try
# # info("Comparing $tmpfn to reference $reffn")
# # load the reference image
# refimg = Images.load(reffn)
# # run the comparison test... a difference will throw an error
# # NOTE: sigma is a 2-length vector with x/y values for the number of pixels
# # to blur together when comparing images
# diffpct = Images.test_approx_eq_sigma_eps(tmpimg, refimg, sigma, eps)
# # we passed!
# info("Reference image $reffn matches. Difference: $diffpct")
# return true
# catch err
# warn("Image did not match reference image $reffn. err: $err")
# # showerror(Base.STDERR, err)
# if isinteractive()
# # if we're in interactive mode, open a popup and give us a chance to examine the images
# warn("Should we make this the new reference image?")
# compareToReferenceImage(tmpfn, reffn)
# # println("exited")
# return
# else
# # if we rejected the image, or if we're in automated tests, throw the error
# rethrow(err)
# end
# end
end
function image_comparison_facts(pkg::Symbol; skip = [], debug = false, sigma = [1,1], eps = 1e-2)
for i in 1:length(ExamplePlots.examples)
function image_comparison_facts(pkg::Symbol;
skip = [], # skip these examples (int index)
only = nothing, # limit to these examples (int index)
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)
i in skip && continue
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
if only == nothing || i in only
@fact image_comparison_tests(pkg, i, debug=debug, sigma=sigma, eps=eps) |> success --> true
end
end
end

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

View File

@ -1,152 +1,37 @@
module PlotsTests
# # don't let pyplot use a gui... it'll crash
# # note: Agg will set gui -> :none in PyPlot
# ENV["MPLBACKEND"] = "Agg"
# try
# @eval import PyPlot
# catch err
# warn("Couldn't import PyPlot: $err")
# end
# using Plots
# using FactCheck
# # note: wrap first include in a try block because of the ImageMagick init_deps bug
# try
# include("imgcomp.jl")
# end
include("imgcomp.jl")
# don't actually show the plots
srand(1234)
default(show=false)
# note: we wrap in a try block so that the tests only run if we have the backend installed
# try
# Pkg.installed("Gadfly")
# gadfly()
# backend()
img_eps = 2e-2
img_eps = 5e-2
facts("Gadfly") do
@fact gadfly() --> Plots.GadflyPackage()
@fact backend() --> Plots.GadflyPackage()
@fact typeof(plot(1:10)) --> Plots.Plot{Plots.GadflyPackage}
# plot(x::AVec, y::AVec; kw...) # one line (will assert length(x) == length(y))
@fact plot(Int[1,2,3], rand(3)) --> not(nothing)
# @fact_throws plot(1:5, 1:4)
# plot(x::AVec, y::AMat; kw...) # multiple lines (one per column of x), all sharing x (will assert length(x) == size(y,1))
@fact plot(sort(rand(10)), rand(Int, 10, 3)) --> not(nothing)
# @fact_throws(plot!(rand(10), rand(9,2)))
# plot(x::AMat, y::AMat; kw...) # multiple lines (one per column of x/y... will assert size(x) == size(y))
@fact plot!(rand(10,3), rand(10,3)) --> not(nothing)
image_comparison_facts(:gadfly, skip=[4,19,23,24], eps=img_eps)
image_comparison_facts(:gadfly, skip=[4,6,19,23,24], eps=img_eps)
end
facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotPackage()
@fact backend() --> Plots.PyPlotPackage()
image_comparison_facts(:pyplot, skip=[19,21,23], eps=img_eps)
image_comparison_facts(:pyplot, skip=[4,10,13,19,21,23], eps=img_eps)
end
facts("GR") do
@fact gr() --> Plots.GRPackage()
@fact backend() --> Plots.GRPackage()
# catch err
# warn("Skipped Gadfly due to: ", string(err))
# end
# # note: we wrap in a try block so that the tests only run if we have the backend installed
# try
# Pkg.installed("Qwt")
# qwt()
# backend()
# facts("Qwt") do
# @fact backend(:qwt) --> Plots.QwtPackage()
# @fact backend() --> Plots.QwtPackage()
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.QwtPackage}
# # plot(y::AVec; kw...) # one line... x = 1:length(y)
# @fact plot(1:10) --> not(nothing)
# @fact length(current().o.lines) --> 1
# # plot(x::AVec, f::Function; kw...) # one line, y = f(x)
# @fact plot(1:10, sin) --> not(nothing)
# @fact current().o.lines[1].y --> sin(collect(1:10))
# # plot(x::AMat, f::Function; kw...) # multiple lines, yᵢⱼ = f(xᵢⱼ)
# @fact plot(rand(10,2), sin) --> not(nothing)
# @fact length(current().o.lines) --> 2
# # plot(y::AMat; kw...) # multiple lines (one per column of x), all sharing x = 1:size(y,1)
# @fact plot!(rand(10,2)) --> not(nothing)
# @fact length(current().o.lines) --> 4
# # plot(x::AVec, fs::AVec{Function}; kw...) # multiple lines, yᵢⱼ = fⱼ(xᵢ)
# @fact plot(1:10, Function[sin,cos]) --> not(nothing)
# @fact current().o.lines[1].y --> sin(collect(1:10))
# @fact current().o.lines[2].y --> cos(collect(1:10))
# # plot(y::AVec{AVec}; kw...) # multiple lines, each with x = 1:length(y[i])
# @fact plot([11:20 ; rand(10)]) --> not(nothing)
# @fact current().o.lines[1].x[4] --> 4
# @fact current().o.lines[1].y[4] --> 14
# end
# catch err
# warn("Skipped Qwt due to: ", string(err))
# end
# try
# Pkg.installed("PyPlot")
# pyplot()
# backend()
# facts("PyPlot") do
# @fact backend(:pyplot) --> Plots.PyPlotPackage()
# @fact backend() --> Plots.PyPlotPackage()
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.PyPlotPackage}
# # image_comparison_facts(:pyplot, skip=[19])
# end
# catch err
# warn("Skipped PyPlot due to: ", string(err))
# end
# try
# Pkg.installed("UnicodePlots")
# unicodeplots()
# backend()
# facts("UnicodePlots") do
# @fact backend(:unicodeplots) --> Plots.UnicodePlotsPackage()
# @fact backend() --> Plots.UnicodePlotsPackage()
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.UnicodePlotsPackage}
# end
# catch err
# warn("Skipped UnicodePlots due to: ", string(err))
# end
# try
# Pkg.installed("Winston")
# winston()
# backend()
# facts("Winston") do
# @fact backend(:winston) --> Plots.WinstonPackage()
# @fact backend() --> Plots.WinstonPackage()
# @fact typeof(plot(1:10)) --> Plots.Plot{Plots.WinstonPackage}
# end
# catch err
# warn("Skipped Winston due to: ", string(err))
# end
# image_comparison_facts(:gr, only=[1], eps=img_eps)
end
FactCheck.exitstatus()
end # module