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 - if [[ -a .git/shallow ]]; then git fetch --unshallow; fi
- julia -e 'Pkg.clone(pwd()); Pkg.build("Plots")' - 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("https://github.com/tbreloff/Images.jl.git"); Pkg.checkout("Images","tom_imgcompare");'
- julia -e 'Pkg.clone("Images"); Pkg.build("Images")' # - julia -e 'Pkg.clone("Images"); Pkg.build("Images")'
- julia -e 'Pkg.clone("ImageMagick"); Pkg.build("ImageMagick")' # - 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/VisualRegressionTests.jl.git");'
- julia -e 'Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");' - julia -e 'Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");'
- julia -e 'Pkg.clone("Cairo"); Pkg.build("Cairo")' - 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. 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. View the [full documentation](http://plots.readthedocs.org).
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 File

@ -3,3 +3,5 @@ julia 0.4
Colors Colors
Reexport Reexport
Compat 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 WinstonPackage <: PlottingPackage end
immutable BokehPackage <: PlottingPackage end immutable BokehPackage <: PlottingPackage end
immutable PlotlyPackage <: PlottingPackage end immutable PlotlyPackage <: PlottingPackage end
immutable GRPackage <: PlottingPackage end
immutable GLVisualizePackage <: PlottingPackage end immutable GLVisualizePackage <: PlottingPackage end
immutable NoPackage <: PlottingPackage end immutable NoPackage <: PlottingPackage end
@ -21,18 +22,20 @@ export
unicodeplots, unicodeplots,
bokeh, bokeh,
plotly, plotly,
gr,
glvisualize glvisualize
# winston # winston
gadfly() = backend(:gadfly) gadfly(; kw...) = (default(; kw...); backend(:gadfly))
immerse() = backend(:immerse) immerse(; kw...) = (default(; kw...); backend(:immerse))
pyplot() = backend(:pyplot) pyplot(; kw...) = (default(; kw...); backend(:pyplot))
qwt() = backend(:qwt) qwt(; kw...) = (default(; kw...); backend(:qwt))
unicodeplots() = backend(:unicodeplots) unicodeplots(; kw...) = (default(; kw...); backend(:unicodeplots))
bokeh() = backend(:bokeh) bokeh(; kw...) = (default(; kw...); backend(:bokeh))
plotly() = backend(:plotly) plotly(; kw...) = (default(; kw...); backend(:plotly))
glvisualize() = backend(:glvisualize) gr(; kw...) = (default(; kw...); backend(:gr))
# winston() = backend(:winston) glvisualize(; kw...) = (default(; kw...); backend(:glvisualize))
# winston(; kw...) = (default(; kw...); backend(:winston))
backend_name(::GadflyPackage) = :gadfly backend_name(::GadflyPackage) = :gadfly
backend_name(::ImmersePackage) = :immerse backend_name(::ImmersePackage) = :immerse
@ -41,6 +44,7 @@ backend_name(::UnicodePlotsPackage) = :unicodeplots
backend_name(::QwtPackage) = :qwt backend_name(::QwtPackage) = :qwt
backend_name(::BokehPackage) = :bokeh backend_name(::BokehPackage) = :bokeh
backend_name(::PlotlyPackage) = :plotly backend_name(::PlotlyPackage) = :plotly
backend_name(::GRPackage) = :gr
backend_name(::GLVisualizePackage) = :glvisualize backend_name(::GLVisualizePackage) = :glvisualize
backend_name(::NoPackage) = :none backend_name(::NoPackage) = :none
@ -56,6 +60,7 @@ include("backends/winston.jl")
include("backends/web.jl") include("backends/web.jl")
include("backends/bokeh.jl") include("backends/bokeh.jl")
include("backends/plotly.jl") include("backends/plotly.jl")
include("backends/gr.jl")
include("backends/glvisualize.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 BACKENDS = [:qwt, :gadfly, :unicodeplots, :pyplot, :immerse, :bokeh, :plotly, :gr]
const INITIALIZED_BACKENDS = Set{Symbol}() const _initialized_backends = Set{Symbol}()
backends() = BACKENDS backends() = BACKENDS
function backendInstance(sym::Symbol) function _backend_instance(sym::Symbol)
sym == :qwt && return QwtPackage() sym == :qwt && return QwtPackage()
sym == :gadfly && return GadflyPackage() sym == :gadfly && return GadflyPackage()
sym == :unicodeplots && return UnicodePlotsPackage() sym == :unicodeplots && return UnicodePlotsPackage()
@ -91,6 +96,7 @@ function backendInstance(sym::Symbol)
sym == :winston && return WinstonPackage() sym == :winston && return WinstonPackage()
sym == :bokeh && return BokehPackage() sym == :bokeh && return BokehPackage()
sym == :plotly && return PlotlyPackage() sym == :plotly && return PlotlyPackage()
sym == :gr && return GRPackage()
sym == :glvisualize && return GLVisualizePackage() sym == :glvisualize && return GLVisualizePackage()
sym == :none && return NoPackage() sym == :none && return NoPackage()
error("Unsupported backend $sym") error("Unsupported backend $sym")
@ -101,12 +107,12 @@ type CurrentBackend
sym::Symbol sym::Symbol
pkg::PlottingPackage pkg::PlottingPackage
end end
CurrentBackend(sym::Symbol) = CurrentBackend(sym, backendInstance(sym)) CurrentBackend(sym::Symbol) = CurrentBackend(sym, _backend_instance(sym))
# --------------------------------------------------------- # ---------------------------------------------------------
function pickDefaultBackend() 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 if Pkg.installed(pkgstr) != nothing
return backend(symbol(lowercase(pkgstr))) return backend(symbol(lowercase(pkgstr)))
end end
@ -128,7 +134,7 @@ function backend()
end end
currentBackendSymbol = CURRENT_BACKEND.sym currentBackendSymbol = CURRENT_BACKEND.sym
if !(currentBackendSymbol in INITIALIZED_BACKENDS) if !(currentBackendSymbol in _initialized_backends)
# initialize # initialize
println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym) println("[Plots.jl] Initializing backend: ", CURRENT_BACKEND.sym)
@ -235,13 +241,26 @@ function backend()
# end borrowing (thanks :) # 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 end
catch err catch err
warn("Couldn't setup Plotly") warn("Couldn't setup Plotly")
rethrow(err) rethrow(err)
end 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 try
@eval import GLVisualize @eval import GLVisualize
@eval export GLVisualize @eval export GLVisualize
@ -264,7 +283,7 @@ function backend()
else else
error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS") error("Unknown backend $currentBackendSymbol. Choose from: $BACKENDS")
end end
push!(INITIALIZED_BACKENDS, currentBackendSymbol) push!(_initialized_backends, currentBackendSymbol)
end end
CURRENT_BACKEND.pkg CURRENT_BACKEND.pkg
@ -297,7 +316,9 @@ function backend(modname)
CURRENT_BACKEND.pkg = BokehPackage() CURRENT_BACKEND.pkg = BokehPackage()
elseif modname == :plotly elseif modname == :plotly
CURRENT_BACKEND.pkg = PlotlyPackage() CURRENT_BACKEND.pkg = PlotlyPackage()
elseif modname == :glvisualize elseif modname == :gr
CURRENT_BACKEND.pkg = GRPackage()
elseif modname == :glvisualize
CURRENT_BACKEND.pkg = GLVisualizePackage() CURRENT_BACKEND.pkg = GLVisualizePackage()
else else
error("Unknown backend $modname. Choose from: $BACKENDS") 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 Compat
using Reexport using Reexport
@reexport using Colors @reexport using Colors
using Requires
using FixedSizeArrays
export export
Plot, Plot,
@ -18,8 +20,6 @@ export
plot, plot,
plot!, plot!,
# plot_display,
# plot_display!,
subplot, subplot,
subplot!, subplot!,
@ -33,6 +33,8 @@ export
bar!, bar!,
histogram, histogram,
histogram!, histogram!,
histogram2d,
histogram2d!,
density, density,
density!, density!,
heatmap, heatmap,
@ -57,8 +59,11 @@ export
wireframe!, wireframe!,
path3d, path3d,
path3d!, path3d!,
plot3d,
plot3d!,
scatter3d, scatter3d,
scatter3d!, scatter3d!,
abline!,
title!, title!,
xlabel!, xlabel!,
@ -79,6 +84,7 @@ export
backend, backend,
backends, backends,
backend_name,
aliases, aliases,
dataframes, dataframes,
@ -112,17 +118,14 @@ export
Animation, Animation,
frame, frame,
gif, gif,
@animate,
@gif,
# recipes # recipes
PlotRecipe, PlotRecipe,
# EllipseRecipe, # EllipseRecipe,
# spy, spy
corrplot # corrplot
# ---------------------------------------------------------
const IMG_DIR = Pkg.dir("Plots") * "/img/"
# --------------------------------------------------------- # ---------------------------------------------------------
@ -131,7 +134,7 @@ include("types.jl")
include("utils.jl") include("utils.jl")
include("colors.jl") include("colors.jl")
include("components.jl") include("components.jl")
include("plotter.jl") include("plotter2.jl")
include("args.jl") include("args.jl")
include("plot.jl") include("plot.jl")
include("subplot.jl") include("subplot.jl")
@ -148,6 +151,8 @@ bar(args...; kw...) = plot(args...; kw..., linetype = :bar)
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)
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)
density!(args...; kw...) = plot!(args...; kw..., linetype = :density) density!(args...; kw...) = plot!(args...; kw..., linetype = :density)
heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap) heatmap(args...; kw...) = plot(args...; kw..., linetype = :heatmap)
@ -172,6 +177,8 @@ wireframe(args...; kw...) = plot(args...; kw..., linetype = :wireframe)
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)
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)
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) const CURRENT_BACKEND = CurrentBackend(:none)
# function __init__() function __init__()
# # global const CURRENT_BACKEND = pickDefaultBackend()
# # global const CURRENT_BACKEND = CurrentBackend(:none)
# # global CURRENT_BACKEND # override IJulia inline display
# # println("[Plots.jl] Default backend: ", CURRENT_BACKEND.sym) if isijulia()
@eval import IJulia
# # # auto init dataframes if the import statement doesn't error out IJulia.display_dict(plt::PlottingObject) = Dict{ASCIIString, ByteString}("text/html" => sprint(writemime, "text/html", plt))
# # try end
# # @eval import DataFrames end
# # dataframes()
# # end
# end
# --------------------------------------------------------- # ---------------------------------------------------------

View File

@ -1,19 +1,18 @@
immutable Animation{P<:PlottingObject} immutable Animation
plt::P
dir::ASCIIString dir::ASCIIString
frames::Vector{ASCIIString} frames::Vector{ASCIIString}
end end
function Animation(plt::PlottingObject) function Animation()
Animation(plt, mktempdir(), ASCIIString[]) tmpdir = convert(ASCIIString, mktempdir())
Animation(tmpdir, ASCIIString[])
end end
Animation() = Animation(current())
function frame(anim::Animation) function frame{P<:PlottingObject}(anim::Animation, plt::P=current())
i = length(anim.frames) + 1 i = length(anim.frames) + 1
filename = @sprintf("%06d.png", i) filename = @sprintf("%06d.png", i)
png(anim.plt, joinpath(anim.dir, filename)) png(plt, joinpath(anim.dir, filename))
push!(anim.frames, filename) push!(anim.frames, filename)
end end
@ -32,7 +31,7 @@ function gif(anim::Animation, fn::@compat(AbstractString) = "tmp.gif"; fps::Inte
# high quality # high quality
speed = round(Int, 100 / fps) 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 catch err
warn("Tried to create gif using convert (ImageMagick), but got error: $err\nWill try ffmpeg, but it's lower quality...)") warn("Tried to create gif using convert (ImageMagick), but got error: $err\nWill try ffmpeg, but it's lower quality...)")
@ -52,3 +51,86 @@ end
function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif) function Base.writemime(io::IO, ::MIME"text/html", agif::AnimatedGif)
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />") write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
end 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

@ -10,7 +10,7 @@ const _allAxes = [:auto, :left, :right]
const _3dTypes = [:path3d, :scatter3d, :surface, :wireframe] const _3dTypes = [:path3d, :scatter3d, :surface, :wireframe]
const _allTypes = vcat([ const _allTypes = vcat([
:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :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 :contour, :pie
], _3dTypes) ], _3dTypes)
@compat const _typeAliases = Dict( @compat const _typeAliases = Dict(
@ -39,7 +39,6 @@ const _allTypes = vcat([
ishistlike(lt::Symbol) = lt in (:hist, :density) ishistlike(lt::Symbol) = lt in (:hist, :density)
islinelike(lt::Symbol) = lt in (:line, :path, :steppre, :steppost) islinelike(lt::Symbol) = lt in (:line, :path, :steppre, :steppost)
isheatmaplike(lt::Symbol) = lt in (:heatmap, :hexbin)
const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
@ -129,7 +128,7 @@ _seriesDefaults[:markerstrokecolor] = :match
_seriesDefaults[:markerstrokealpha] = nothing _seriesDefaults[:markerstrokealpha] = nothing
# _seriesDefaults[:ribbon] = nothing # _seriesDefaults[:ribbon] = nothing
# _seriesDefaults[:ribboncolor] = :match # _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[:smooth] = false # regression line?
_seriesDefaults[:group] = nothing # groupby vector _seriesDefaults[:group] = nothing # groupby vector
# _seriesDefaults[:annotation] = nothing # annotation tuple(s)... (x,y,annotation) # _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[:z] = nothing # depth for contour, surface, etc
_seriesDefaults[:zcolor] = nothing # value for color scale _seriesDefaults[:zcolor] = nothing # value for color scale
# _seriesDefaults[:surface] = nothing # _seriesDefaults[:surface] = nothing
_seriesDefaults[:nlevels] = 15 # _seriesDefaults[:nlevels] = 15
_seriesDefaults[:levels] = 15
_seriesDefaults[:orientation] = :vertical _seriesDefaults[:orientation] = :vertical
@ -149,7 +149,8 @@ _plotDefaults[:title] = ""
_plotDefaults[:xlabel] = "" _plotDefaults[:xlabel] = ""
_plotDefaults[:ylabel] = "" _plotDefaults[:ylabel] = ""
_plotDefaults[:yrightlabel] = "" _plotDefaults[:yrightlabel] = ""
_plotDefaults[:legend] = true _plotDefaults[:legend] = :best
_plotDefaults[:colorbar] = :legend
_plotDefaults[:background_color] = colorant"white" _plotDefaults[:background_color] = colorant"white"
_plotDefaults[:foreground_color] = :auto _plotDefaults[:foreground_color] = :auto
_plotDefaults[:xlims] = :auto _plotDefaults[:xlims] = :auto
@ -177,14 +178,14 @@ _plotDefaults[:tickfont] = font(8)
_plotDefaults[:guidefont] = font(11) _plotDefaults[:guidefont] = font(11)
_plotDefaults[:legendfont] = font(8) _plotDefaults[:legendfont] = font(8)
_plotDefaults[:grid] = true _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 # TODO: x/y scales
const _allArgs = sort(collect(union(keys(_seriesDefaults), keys(_plotDefaults)))) const _allArgs = sort(collect(union(keys(_seriesDefaults), keys(_plotDefaults))))
supportedArgs(::PlottingPackage) = _allArgs supportedArgs(::PlottingPackage) = error("supportedArgs not defined") #_allArgs
supportedArgs() = supportedArgs(backend()) supportedArgs() = supportedArgs(backend())
@ -230,6 +231,7 @@ end
:type => :linetype, :type => :linetype,
:lt => :linetype, :lt => :linetype,
:t => :linetype, :t => :linetype,
:seriestype => :linetype,
:style => :linestyle, :style => :linestyle,
:s => :linestyle, :s => :linestyle,
:ls => :linestyle, :ls => :linestyle,
@ -271,6 +273,9 @@ end
:ylabel2 => :yrightlabel, :ylabel2 => :yrightlabel,
:y2label => :yrightlabel, :y2label => :yrightlabel,
:leg => :legend, :leg => :legend,
:key => :legend,
:cbar => :colorbar,
:cb => :colorbar,
:bg => :background_color, :bg => :background_color,
:bgcolor => :background_color, :bgcolor => :background_color,
:bg_color => :background_color, :bg_color => :background_color,
@ -283,6 +288,9 @@ end
:foreground_colour => :foreground_color, :foreground_colour => :foreground_color,
:regression => :smooth, :regression => :smooth,
:reg => :smooth, :reg => :smooth,
:nlevels => :levels,
:nlev => :levels,
:levs => :levels,
:xlim => :xlims, :xlim => :xlims,
:xlimit => :xlims, :xlimit => :xlims,
:xlimits => :xlims, :xlimits => :xlims,
@ -299,6 +307,14 @@ end
:palette => :color_palette, :palette => :color_palette,
:xlink => :linkx, :xlink => :linkx,
:ylink => :linky, :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 # 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) function handleColors!(d::Dict, arg, csym::Symbol)
try try
if arg == :auto if arg == :auto
@ -405,11 +415,13 @@ end
function processLineArg(d::Dict, arg) function processLineArg(d::Dict, arg)
# linetype # 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 d[:linetype] = arg
# linestyle # 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 d[:linestyle] = arg
elseif typeof(arg) <: Stroke elseif typeof(arg) <: Stroke
@ -424,11 +436,13 @@ function processLineArg(d::Dict, arg)
arg.alpha == nothing || (d[:fillalpha] = arg.alpha) arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
# linealpha # 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 d[:linealpha] = arg
# linewidth # linewidth
elseif trueOrAllTrue(a -> typeof(a) <: Real, arg) # elseif trueOrAllTrue(a -> typeof(a) <: Real, arg)
elseif allReals(arg)
d[:linewidth] = arg d[:linewidth] = arg
# color # color
@ -442,13 +456,16 @@ end
function processMarkerArg(d::Dict, arg) function processMarkerArg(d::Dict, arg)
# markershape # markershape
if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg) # if trueOrAllTrue(a -> get(_markerAliases, a, a) in _allMarkers, arg)
d[:markershape] = arg # d[:markershape] = arg
elseif trueOrAllTrue(a -> isa(a, Shape), arg)
# elseif trueOrAllTrue(a -> isa(a, Shape), arg)
if allShapes(arg)
d[:markershape] = arg d[:markershape] = arg
# stroke style # 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 d[:markerstrokestyle] = arg
elseif typeof(arg) <: Stroke elseif typeof(arg) <: Stroke
@ -463,11 +480,13 @@ function processMarkerArg(d::Dict, arg)
arg.alpha == nothing || (d[:markeralpha] = arg.alpha) arg.alpha == nothing || (d[:markeralpha] = arg.alpha)
# linealpha # 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 d[:markeralpha] = arg
# markersize # markersize
elseif trueOrAllTrue(a -> typeof(a) <: Real, arg) # elseif trueOrAllTrue(a -> typeof(a) <: Real, arg)
elseif allReals(arg)
d[:markersize] = arg d[:markersize] = arg
# markercolor # markercolor
@ -485,11 +504,27 @@ function processFillArg(d::Dict, arg)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.alpha == nothing || (d[:fillalpha] = arg.alpha) 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) elseif !handleColors!(d, arg, :fillcolor)
d[:fillrange] = arg d[:fillrange] = arg
end end
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." "Handle all preprocessing of args... break out colors/sizes/etc and replace aliases."
function preprocessArgs!(d::Dict) function preprocessArgs!(d::Dict)
replaceAliases!(d, _keyAliases) replaceAliases!(d, _keyAliases)
@ -501,6 +536,12 @@ function preprocessArgs!(d::Dict)
processAxisArg(d, axisletter, arg) processAxisArg(d, axisletter, arg)
end end
delete!(d, asym) 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 end
# handle line args # handle line args
@ -516,7 +557,12 @@ function preprocessArgs!(d::Dict)
anymarker = true anymarker = true
end end
delete!(d, :marker) 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 d[:markershape] = :ellipse
end end
@ -528,6 +574,14 @@ function preprocessArgs!(d::Dict)
# convert into strokes and brushes # 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 # handle subplot links
if haskey(d, :link) if haskey(d, :link)
l = d[:link] l = d[:link]
@ -588,6 +642,9 @@ function warnOnUnsupportedArgs(pkg::PlottingPackage, d::Dict)
end end
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) function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
(d[:axis] in supportedAxes(pkg) (d[:axis] in supportedAxes(pkg)
@ -598,8 +655,9 @@ function warnOnUnsupported(pkg::PlottingPackage, d::Dict)
(d[:linestyle] in supportedStyles(pkg) (d[:linestyle] in supportedStyles(pkg)
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))")) || warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))"))
(d[:markershape] == :none (d[:markershape] == :none
|| d[:markershape] in supportedMarkers(pkg) || _markershape_supported(pkg, d[:markershape])
|| (Shape in supportedMarkers(pkg) && typeof(d[:markershape]) <: Shape) # || 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))")) || warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))"))
end end
@ -635,6 +693,19 @@ function setDictValue(d_in::Dict, d_out::Dict, k::Symbol, idx::Int, defaults::Di
end end
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 # build the argument dictionary for the plot
@ -655,6 +726,13 @@ function getPlotArgs(pkg::PlottingPackage, kw, idx::Int; set_defaults = true)
end end
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 # convert color
handlePlotColors(pkg, d) handlePlotColors(pkg, d)
@ -710,8 +788,6 @@ function getSeriesArgs(pkg::PlottingPackage, plotargs::Dict, kw, commandIndex::I
c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex)) c = (c == :match ? d[:linecolor] : getSeriesRGBColor(c, plotargs, plotIndex))
d[:fillcolor] = c d[:fillcolor] = c
# TODO: rebuild
# set label # set label
label = d[:label] label = d[:label]
label = (label == "AUTO" ? "y$globalIndex" : label) label = (label == "AUTO" ? "y$globalIndex" : label)

View File

@ -1,6 +1,15 @@
# https://github.com/bokeh/Bokeh.jl # 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) # make255(x) = round(Int, 255 * x)
# function bokehcolor(c::Colorant) # function bokehcolor(c::Colorant)

View File

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

View File

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

View File

@ -2,6 +2,13 @@
# [WEBSITE] # [WEBSITE]
function _initialize_backend(::GLVisualizePackage; kw...)
@eval begin
import GLVisualize
export GLVisualize
end
end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
immutable GLScreenWrapper immutable GLScreenWrapper
@ -12,6 +19,8 @@ function _create_plot(pkg::GLVisualizePackage; kw...)
d = Dict(kw) d = Dict(kw)
# TODO: create the window/canvas/context that is the plot within the backend (call it `o`) # 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: initialize the plot... title, xlabel, bgcolor, etc
# TODO: this should be moved to the display method?
w=GLVisualize.glscreen() w=GLVisualize.glscreen()
@async GLVisualize.renderloop(w) @async GLVisualize.renderloop(w)
Plot(GLScreenWrapper(w), pkg, 0, d, Dict[]) Plot(GLScreenWrapper(w), pkg, 0, d, Dict[])
@ -22,6 +31,7 @@ function _add_series(::GLVisualizePackage, plt::Plot; kw...)
d = Dict(kw) d = Dict(kw)
# TODO: add one series to the underlying package # TODO: add one series to the underlying package
push!(plt.seriesargs, d) 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) 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) GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface),plt.o.window)
plt plt
@ -50,13 +60,16 @@ end
# accessors for x/y data # accessors for x/y data
function Base.getindex(plt::Plot{GLVisualizePackage}, i::Int) function Base.getindex(plt::Plot{GLVisualizePackage}, i::Int)
series = plt.o.lines[i] # TODO:
series.x, series.y # series = plt.o.lines[i]
# series.x, series.y
nothing, nothing
end end
function Base.setindex!(plt::Plot{GLVisualizePackage}, xy::Tuple, i::Integer) function Base.setindex!(plt::Plot{GLVisualizePackage}, xy::Tuple, i::Integer)
series = plt.o.lines[i] # TODO:
series.x, series.y = xy # series = plt.o.lines[i]
# series.x, series.y = xy
plt plt
end end
@ -82,6 +95,10 @@ end
function Base.display(::PlotsDisplay, plt::Plot{GLVisualizePackage}) function Base.display(::PlotsDisplay, plt::Plot{GLVisualizePackage})
# TODO: display/show the plot # 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 end
function Base.display(::PlotsDisplay, plt::Subplot{GLVisualizePackage}) 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 # 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) function createImmerseFigure(d::Dict)
w,h = d[:size] 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 # 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...) 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: create the window/canvas/context that is the plot within the backend (call it `o`)
# TODO: initialize the plot... title, xlabel, bgcolor, etc # TODO: initialize the plot... title, xlabel, bgcolor, etc
Plot(nothing, pkg, 0, d, Dict[]) Plot(nothing, pkg, 0, d, Dict[])
@ -12,7 +42,7 @@ end
function _add_series(::PlotlyPackage, plt::Plot; kw...) function _add_series(::PlotlyPackage, plt::Plot; kw...)
d = Dict(kw) d = Dict{Symbol,Any}(kw)
# TODO: add one series to the underlying package # TODO: add one series to the underlying package
push!(plt.seriesargs, d) push!(plt.seriesargs, d)
plt plt
@ -83,7 +113,7 @@ end
# _plotDefaults[:yflip] = false # _plotDefaults[:yflip] = false
function plotlyfont(font::Font, color = font.color) function plotlyfont(font::Font, color = font.color)
Dict( Dict{Symbol,Any}(
:family => font.family, :family => font.family,
:size => round(Int, font.pointsize*1.4), :size => round(Int, font.pointsize*1.4),
:color => webcolor(color), :color => webcolor(color),
@ -91,7 +121,7 @@ function plotlyfont(font::Font, color = font.color)
end end
function get_annotation_dict(x, y, val::Union{AbstractString,Symbol}) function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
Dict( Dict{Symbol,Any}(
:text => val, :text => val,
:xref => "x", :xref => "x",
:x => x, :x => x,
@ -102,7 +132,7 @@ function get_annotation_dict(x, y, val::Union{AbstractString,Symbol})
end end
function get_annotation_dict(x, y, ptxt::PlotText) 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), :font => plotlyfont(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign, :xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign, :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") labelsym(isx::Bool) = symbol((isx ? "x" : "y") * "label")
function plotlyaxis(d::Dict, isx::Bool) function plotlyaxis(d::Dict, isx::Bool)
ax = Dict( ax = Dict{Symbol,Any}(
:title => d[labelsym(isx)], :title => d[labelsym(isx)],
:showgrid => d[:grid], :showgrid => d[:grid],
:zeroline => false, :zeroline => false,
@ -176,9 +206,10 @@ function plotlyaxis(d::Dict, isx::Bool)
ax ax
end end
function get_plot_json(plt::Plot{PlotlyPackage}) # function get_plot_json(plt::Plot{PlotlyPackage})
d = plt.plotargs # d = plt.plotargs
d_out = Dict() function plotly_layout(d::Dict)
d_out = Dict{Symbol,Any}()
bgcolor = webcolor(d[:background_color]) bgcolor = webcolor(d[:background_color])
fgcolor = webcolor(d[:foreground_color]) fgcolor = webcolor(d[:foreground_color])
@ -186,7 +217,7 @@ function get_plot_json(plt::Plot{PlotlyPackage})
# set the fields for the plot # set the fields for the plot
d_out[:title] = d[:title] d_out[:title] = d[:title]
d_out[:titlefont] = plotlyfont(d[:guidefont], fgcolor) 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[:plot_bgcolor] = bgcolor
d_out[:paper_bgcolor] = bgcolor d_out[:paper_bgcolor] = bgcolor
@ -194,88 +225,10 @@ function get_plot_json(plt::Plot{PlotlyPackage})
d_out[:xaxis] = plotlyaxis(d, true) d_out[:xaxis] = plotlyaxis(d, true)
d_out[:yaxis] = plotlyaxis(d, false) 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 # legend
d_out[:showlegend] = d[:legend] d_out[:showlegend] = d[:legend] != :none
if d[:legend] if d[:legend] != :none
d_out[:legend] = Dict( d_out[:legend] = Dict{Symbol,Any}(
:bgcolor => bgcolor, :bgcolor => bgcolor,
:bordercolor => fgcolor, :bordercolor => fgcolor,
:font => plotlyfont(d[:legendfont]), :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] d_out[:annotations] = [get_annotation_dict(ann...) for ann in anns]
end end
# finally build and return the json d_out
JSON.json(d_out) end
function get_plot_json(plt::Plot{PlotlyPackage})
JSON.json(plotly_layout(plt.plotargs))
end end
function plotly_colorscale(grad::ColorGradient) function plotly_colorscale(grad::ColorGradient, alpha = nothing)
[[grad.values[i], webcolor(grad.colors[i])] for i in 1:length(grad.colors)] [[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
end 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", :rect => "square",
:xcross => "x", :xcross => "x",
:utriangle => "triangle-up", :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) # 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) function plotly_series(d::Dict; plot_index = nothing)
d_out = Dict() d_out = Dict{Symbol,Any}()
x, y = collect(d[:x]), collect(d[:y]) x, y = collect(d[:x]), collect(d[:y])
d_out[:name] = d[:label] d_out[:name] = d[:label]
@ -340,7 +296,7 @@ function get_series_json(d::Dict; plot_index = nothing)
d_out[:type] = "bar" d_out[:type] = "bar"
d_out[:x], d_out[:y] = x, y d_out[:x], d_out[:y] = x, y
elseif lt == :heatmap elseif lt == :hist2d
d_out[:type] = "histogram2d" d_out[:type] = "histogram2d"
d_out[:x], d_out[:y] = x, y d_out[:x], d_out[:y] = x, y
if isa(d[:nbins], Tuple) if isa(d[:nbins], Tuple)
@ -360,16 +316,26 @@ function get_series_json(d::Dict; plot_index = nothing)
d_out[:histnorm] = "probability density" d_out[:histnorm] = "probability density"
end end
elseif lt in (:contour, :surface, :wireframe) elseif lt == :heatmap
d_out[:type] = lt == :wireframe ? :surface : string(lt) d_out[:type] = "heatmap"
d_out[:x], d_out[:y] = x, y d_out[:x], d_out[:y] = x, y
d_out[:z] = d[:z].surf d_out[:z] = d[:z].surf
# d_out[:showscale] = d[:legend] d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
if lt == :contour
d_out[:ncontours] = d[:nlevels] elseif lt == :contour
d_out[:contours] = Dict(:coloring => d[:fillrange] != nothing ? "fill" : "lines") d_out[:type] = "contour"
end d_out[:x], d_out[:y] = x, y
d_out[:colorscale] = plotly_colorscale(d[lt == :contour ? :linecolor : :fillcolor]) 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 elseif lt == :pie
d_out[:type] = "pie" d_out[:type] = "pie"
@ -389,30 +355,37 @@ function get_series_json(d::Dict; plot_index = nothing)
else else
warn("Plotly: linetype $lt isn't supported.") warn("Plotly: linetype $lt isn't supported.")
return Dict() return Dict{Symbol,Any}()
end end
# add "marker" # add "marker"
if hasmarker if hasmarker
d_out[:marker] = Dict( d_out[:marker] = Dict{Symbol,Any}(
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])), :symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
:opacity => d[:markeralpha], :opacity => d[:markeralpha],
:size => 2 * d[:markersize], :size => 2 * d[:markersize],
:color => webcolor(d[:markercolor], d[:markeralpha]), :color => webcolor(d[:markercolor], d[:markeralpha]),
:line => Dict( :line => Dict{Symbol,Any}(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]), :color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth], :width => d[:markerstrokewidth],
), ),
) )
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
if d[:zcolor] != nothing if d[:zcolor] != nothing
d_out[:marker][:color] = d[:zcolor] # d_out[:marker][:color] = d[:zcolor]
d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor]) # 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
end end
# add "line" # add "line"
if hasline if hasline
d_out[:line] = Dict( d_out[:line] = Dict{Symbol,Any}(
:color => webcolor(d[:linecolor], d[:linealpha]), :color => webcolor(d[:linecolor], d[:linealpha]),
:width => d[:linewidth], :width => d[:linewidth],
:shape => if lt == :steppre :shape => if lt == :steppre
@ -438,14 +411,14 @@ end
# get a list of dictionaries, each representing the series params # get a list of dictionaries, each representing the series params
function get_series_json(plt::Plot{PlotlyPackage}) function get_series_json(plt::Plot{PlotlyPackage})
JSON.json(map(get_series_json, plt.seriesargs)) JSON.json(map(plotly_series, plt.seriesargs))
end end
function get_series_json(subplt::Subplot{PlotlyPackage}) function get_series_json(subplt::Subplot{PlotlyPackage})
ds = Dict[] ds = Dict[]
for (i,plt) in enumerate(subplt.plts) for (i,plt) in enumerate(subplt.plts)
for d in plt.seriesargs for d in plt.seriesargs
push!(ds, get_series_json(d, plot_index = i)) push!(ds, plotly_series(d, plot_index = i))
end end
end end
JSON.json(ds) JSON.json(ds)
@ -501,11 +474,8 @@ end
# ---------------------------------------------------------------- # ----------------------------------------------------------------
function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{PlotlyPackage}) function Base.writemime(io::IO, ::MIME"image/png", plt::PlottingObject{PlotlyPackage})
isijulia() && return warn("todo: png")
# TODO: write a png to io
println("todo: png")
end end
function Base.writemime(io::IO, ::MIME"text/html", plt::PlottingObject{PlotlyPackage}) 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 # 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 # convert colorant to 4-tuple RGBA
getPyPlotColor(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha)) 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(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α))
getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α)) getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
# getPyPlotColor(c, alpha) = getPyPlotColor(colorscheme(c, alpha)) # getPyPlotColor(c, alpha) = getPyPlotColor(colorscheme(c, alpha))
@ -12,12 +39,14 @@ getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
function getPyPlotColorMap(c::ColorGradient, α=nothing) function getPyPlotColorMap(c::ColorGradient, α=nothing)
# c = ColorGradient(c.colors, c.values, alpha=α) # c = ColorGradient(c.colors, c.values, alpha=α)
# pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", map(getPyPlotColor, getColorVector(c))) # 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) pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
end end
# anything else just gets a redsblue gradient # anything else just gets a bluesred gradient
getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:redsblues), α) getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(ColorGradient(:bluesreds), α)
# get the style (solid, dashed, etc) # get the style (solid, dashed, etc)
function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol) function getPyPlotLineStyle(linetype::Symbol, linestyle::Symbol)
@ -62,8 +91,14 @@ function getPyPlotMarker(marker::Symbol)
return "o" return "o"
end 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 # pass through
function getPyPlotMarker(marker::@compat(AbstractString)) function getPyPlotMarker(marker::AbstractString)
@assert length(marker) == 1 @assert length(marker) == 1
marker marker
end end
@ -134,13 +169,14 @@ function getPyPlotFunction(plt::Plot, axis::Symbol, linetype::Symbol)
:density => :hist, :density => :hist,
:sticks => :bar, :sticks => :bar,
:bar => :bar, :bar => :bar,
:heatmap => :hexbin, :hist2d => :hexbin,
:hexbin => :hexbin, :hexbin => :hexbin,
:scatter => :scatter, :scatter => :scatter,
:contour => :contour, :contour => :contour,
:scatter3d => :scatter, :scatter3d => :scatter,
:surface => :plot_surface, :surface => :plot_surface,
:wireframe => :plot_wireframe, :wireframe => :plot_wireframe,
:heatmap => :pcolor,
# :surface => pycolors.pymember("LinearSegmentedColormap")[:from_list] # :surface => pycolors.pymember("LinearSegmentedColormap")[:from_list]
) )
return ax[get(fmap, linetype, :plot)] 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: # TODO:
# fillto # might have to use barHack/histogramHack?? # fillto # might have to use barHack/histogramHack??
# reg # true or false, add a regression line for each line # 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) if haskey(d, :subplot)
wrap = nothing wrap = nothing
else else
w,h = map(px2inch, d[:size]) wrap = PyPlotAxisWrapper(nothing, nothing, pyplot_figure(d), [])
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(; 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 end
plt = Plot(wrap, pkg, 0, d, Dict[]) plt = Plot(wrap, pkg, 0, d, Dict[])
@ -217,11 +292,21 @@ end
function _add_series(pkg::PyPlotPackage, plt::Plot; kw...) function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
d = Dict(kw) d = Dict(kw)
# 3D plots have a different underlying Axes object in PyPlot
lt = d[:linetype] 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")) push!(plt.o.kwargs, (:projection, "3d"))
end 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]) ax = getAxis(plt, d[:axis])
if !(lt in supportedTypes(pkg)) if !(lt in supportedTypes(pkg))
error("linetype $(lt) is unsupported in PyPlot. Choose from: $(supportedTypes(pkg))") error("linetype $(lt) is unsupported in PyPlot. Choose from: $(supportedTypes(pkg))")
@ -249,7 +334,7 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
end end
lt = d[:linetype] # lt = d[:linetype]
extra_kwargs = Dict() extra_kwargs = Dict()
plotfunc = getPyPlotFunction(plt, d[:axis], lt) 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) extra_kwargs[:linewidth] = (lt == :sticks ? 0.1 : 0.9)
end end
elseif lt in (:heatmap, :hexbin) elseif lt in (:hist2d, :hexbin)
extra_kwargs[:gridsize] = d[:nbins] extra_kwargs[:gridsize] = d[:nbins]
extra_kwargs[:cmap] = getPyPlotColorMap(d[:linecolor]) 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[:linewidth] = d[:linewidth]
extra_kwargs[:edgecolor] = getPyPlotColor(d[:linecolor], d[:linealpha]) extra_kwargs[:edgecolor] = getPyPlotColor(d[:linecolor], d[:linealpha])
elseif lt == :heatmap
extra_kwargs[:cmap] = getPyPlotColorMap(d[:fillcolor], d[:fillalpha])
else else
extra_kwargs[:linestyle] = getPyPlotLineStyle(lt, d[:linestyle]) extra_kwargs[:linestyle] = getPyPlotLineStyle(lt, d[:linestyle])
@ -294,15 +382,28 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
if lt in (:scatter, :scatter3d) if lt in (:scatter, :scatter3d)
extra_kwargs[:s] = d[:markersize].^2 extra_kwargs[:s] = d[:markersize].^2
c = d[:markercolor] 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[:c] = convert(Vector{Float64}, d[:zcolor])
extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeralpha]) extra_kwargs[:cmap] = getPyPlotColorMap(c, d[:markeralpha])
else else
extra_kwargs[:c] = getPyPlotColor(c, d[:markeralpha]) # extra_kwargs[:c] = getPyPlotColor(c, d[:markeralpha])
end ppc = getPyPlotColor(c, d[:markeralpha])
if d[:markeralpha] != nothing
extra_kwargs[:alpha] = 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 end
# if d[:markeralpha] != nothing
# extra_kwargs[:alpha] = d[:markeralpha]
# end
extra_kwargs[:edgecolors] = getPyPlotColor(d[:markerstrokecolor], d[:markerstrokealpha]) extra_kwargs[:edgecolors] = getPyPlotColor(d[:markerstrokecolor], d[:markerstrokealpha])
extra_kwargs[:linewidths] = d[:markerstrokewidth] extra_kwargs[:linewidths] = d[:markerstrokewidth]
else else
@ -321,7 +422,7 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
# end # end
# set these for all types # set these for all types
if !(lt in (:contour,:surface,:wireframe)) if !(lt in (:contour,:surface,:wireframe,:heatmap))
if !(lt in (:scatter, :scatter3d)) if !(lt in (:scatter, :scatter3d))
extra_kwargs[:color] = color extra_kwargs[:color] = color
extra_kwargs[:linewidth] = d[:linewidth] extra_kwargs[:linewidth] = d[:linewidth]
@ -333,31 +434,56 @@ function _add_series(pkg::PyPlotPackage, plt::Plot; kw...)
# do the plot # do the plot
d[:serieshandle] = if ishistlike(lt) d[:serieshandle] = if ishistlike(lt)
plotfunc(d[:y]; extra_kwargs...)[1] plotfunc(d[:y]; extra_kwargs...)[1]
elseif lt == :contour 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] x, y = d[:x], d[:y]
surf = d[:z].surf' 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 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 end
handle handle
elseif lt in (:surface,:wireframe) 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 elseif lt in _3dTypes
plotfunc(d[:x], d[:y], d[:z]; extra_kwargs...) 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...) 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] plotfunc(d[:x], d[:y]; extra_kwargs...)[1]
end end
# smoothing
handleSmooth(plt, ax, d, d[:smooth]) handleSmooth(plt, ax, d, d[:smooth])
# add the colorbar legend # add the colorbar legend
if plt.plotargs[:legend] && haskey(extra_kwargs, :cmap) if plt.plotargs[:colorbar] != :none && haskey(extra_kwargs, :cmap)
PyPlot.colorbar(d[:serieshandle]) PyPlot.colorbar(d[:serieshandle], ax=ax)
end end
# @show extra_kwargs # @show extra_kwargs
@ -390,30 +516,52 @@ function Base.getindex(plt::Plot{PyPlotPackage}, i::Integer)
xy = series[:get_offsets]() xy = series[:get_offsets]()
return vec(xy[:,1]), vec(xy[:,2]) return vec(xy[:,1]), vec(xy[:,2])
end end
# series[:relim]() end
# mapping = getGadflyMappings(plt, i)[1]
# mapping[:x], mapping[:y] 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 end
function Base.setindex!{X,Y}(plt::Plot{PyPlotPackage}, xy::Tuple{X,Y}, i::Integer) 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 x, y = xy
d[:x], d[:y] = x, y
try try
series[:set_data](x, y) series[:set_data](x, y)
catch catch
series[:set_offsets](hcat(x, y)) series[:set_offsets](hcat(x, y))
end end
ax = series[:axes] set_lims!(plt, d[:axis])
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
plt plt
end end
@ -428,7 +576,13 @@ function addPyPlotLims(ax, lims, isx::Bool)
lims == :auto && return lims == :auto && return
ltype = limsType(lims) ltype = limsType(lims)
if ltype == :limits 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 else
error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims) error("Invalid input for $(isx ? "xlims" : "ylims"): ", lims)
end end
@ -444,7 +598,8 @@ function addPyPlotTicks(ax, ticks, isx::Bool)
if ttype == :ticks if ttype == :ticks
ax[isx ? :set_xticks : :set_yticks](ticks) ax[isx ? :set_xticks : :set_yticks](ticks)
elseif ttype == :ticks_and_labels 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 else
error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks) error("Invalid input for $(isx ? "xticks" : "yticks"): ", ticks)
end end
@ -562,9 +717,11 @@ end
function _create_subplot(subplt::Subplot{PyPlotPackage}, isbefore::Bool) function _create_subplot(subplt::Subplot{PyPlotPackage}, isbefore::Bool)
l = subplt.layout l = subplt.layout
w,h = map(px2inch, getplotargs(subplt,1)[:size]) # w,h = map(px2inch, getplotargs(subplt,1)[:size])
bgcolor = getPyPlotColor(getplotargs(subplt,1)[:background_color]) # bgcolor = getPyPlotColor(getplotargs(subplt,1)[:background_color])
fig = PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true) # fig = PyPlot.figure(; figsize = (w,h), facecolor = bgcolor, dpi = DPI, tight_layout = true)
plotargs = getplotargs(subplt, 1)
fig = pyplot_figure(plotargs)
nr = nrows(l) nr = nrows(l)
for (i,(r,c)) in enumerate(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) ax = fig[:add_subplot](nr, nc, fakeidx)
subplt.plts[i].o = PyPlotAxisWrapper(ax, nothing, fig, []) subplt.plts[i].o = PyPlotAxisWrapper(ax, nothing, fig, [])
pyplot_3d_setup!(subplt.plts[i].o, plotargs)
end end
# subplt.o = PyPlotFigWrapper(fig, []) # subplt.o = PyPlotFigWrapper(fig, [])
subplt.o = PyPlotAxisWrapper(nothing, nothing, fig, []) subplt.o = PyPlotAxisWrapper(nothing, nothing, fig, [])
pyplot_3d_setup!(subplt.o, plotargs)
true true
end 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)
function addPyPlotLegend(plt::Plot, ax) 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 # 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 if length(args) > 0
leg = ax[:legend]([d[:serieshandle] for d in args], leg = ax[:legend]([d[:serieshandle] for d in args],
[d[:label] for d in args], [d[:label] for d in args],
loc="best", loc = get(_pyplot_legend_pos, leg, "best"),
fontsize = plt.plotargs[:legendfont].pointsize fontsize = plt.plotargs[:legendfont].pointsize
# framealpha = 0.6 # framealpha = 0.6
) )
@ -713,7 +881,7 @@ const _pyplot_mimeformats = @compat Dict(
"application/pdf" => "pdf", "application/pdf" => "pdf",
"image/png" => "png", "image/png" => "png",
"application/postscript" => "ps", "application/postscript" => "ps",
# "image/svg+xml" => "svg" "image/svg+xml" => "svg"
) )
@ -732,6 +900,7 @@ for (mime, fmt) in _pyplot_mimeformats
end end
end end
# function Base.writemime(io::IO, m::MIME"image/png", subplt::Subplot{PyPlotPackage}) # function Base.writemime(io::IO, m::MIME"image/png", subplt::Subplot{PyPlotPackage})
# finalizePlot(subplt) # finalizePlot(subplt)
# writemime(io, m, getfig(subplt.o)) # writemime(io, m, getfig(subplt.o))

View File

@ -1,6 +1,13 @@
# https://github.com/tbreloff/Qwt.jl # 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, :label,
:layout, :layout,
:legend, :legend,
:colorbar,
:linestyle, :linestyle,
:linetype, :linetype,
:linewidth, :linewidth,
@ -43,6 +44,7 @@ supportedArgs(::GadflyPackage) = [
:markeralpha, :markeralpha,
:markerstrokewidth, :markerstrokewidth,
:markerstrokecolor, :markerstrokecolor,
:markerstrokealpha,
# :markerstrokestyle, # :markerstrokestyle,
:n, :n,
:nbins, :nbins,
@ -74,11 +76,11 @@ supportedArgs(::GadflyPackage) = [
:legendfont, :legendfont,
:grid, :grid,
# :surface, # :surface,
:nlevels, :levels,
] ]
supportedAxes(::GadflyPackage) = [:auto, :left] supportedAxes(::GadflyPackage) = [:auto, :left]
supportedTypes(::GadflyPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, supportedTypes(::GadflyPackage) = [:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :heatmap, :hexbin, :hist, :bar, :scatter, :hist2d, :hexbin, :hist, :bar,
:hline, :vline, :contour] :hline, :vline, :contour]
supportedStyles(::GadflyPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot] supportedStyles(::GadflyPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GadflyPackage) = vcat(_allMarkers, Shape) supportedMarkers(::GadflyPackage) = vcat(_allMarkers, Shape)
@ -114,6 +116,83 @@ supportedArgs(::PyPlotPackage) = [
:label, :label,
:layout, :layout,
:legend, :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, :linestyle,
:linetype, :linetype,
:linewidth, :linewidth,
@ -159,15 +238,15 @@ supportedArgs(::PyPlotPackage) = [
:linealpha, :linealpha,
:markeralpha, :markeralpha,
] ]
supportedAxes(::PyPlotPackage) = _allAxes supportedAxes(::GRPackage) = _allAxes
supportedTypes(::PyPlotPackage) = [:none, :line, :path, :steppre, :steppost, :sticks, supportedTypes(::GRPackage) = [:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :heatmap, :hexbin, :hist, :density, :bar, :scatter, :hist2d, :hexbin, :hist, :density, :bar,
:hline, :vline, :contour, :path3d, :scatter3d, :surface, :wireframe] :hline, :vline, :contour, :path3d, :scatter3d, :surface,
supportedStyles(::PyPlotPackage) = [:auto, :solid, :dash, :dot, :dashdot] :wireframe, :ohlc, :pie]
# supportedMarkers(::PyPlotPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon] supportedStyles(::GRPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::PyPlotPackage) = vcat(_allMarkers, Shape) supportedMarkers(::GRPackage) = vcat(_allMarkers, Shape)
supportedScales(::PyPlotPackage) = [:identity, :ln, :log2, :log10] supportedScales(::GRPackage) = [:identity, :log10]
subplotSupported(::PyPlotPackage) = true subplotSupported(::GRPackage) = true
# -------------------------------------------------------------------------------------- # --------------------------------------------------------------------------------------
@ -225,7 +304,7 @@ supportedArgs(::QwtPackage) = [
# :yflip, # :yflip,
# :z, # :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] supportedMarkers(::QwtPackage) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
supportedScales(::QwtPackage) = [:identity, :log10] supportedScales(::QwtPackage) = [:identity, :log10]
subplotSupported(::QwtPackage) = true subplotSupported(::QwtPackage) = true
@ -284,7 +363,7 @@ supportedArgs(::UnicodePlotsPackage) = [
# :z, # :z,
] ]
supportedAxes(::UnicodePlotsPackage) = [:auto, :left] 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] supportedStyles(::UnicodePlotsPackage) = [:auto, :solid]
supportedMarkers(::UnicodePlotsPackage) = [:none, :auto, :ellipse] supportedMarkers(::UnicodePlotsPackage) = [:none, :auto, :ellipse]
supportedScales(::UnicodePlotsPackage) = [:identity] supportedScales(::UnicodePlotsPackage) = [:identity]
@ -413,10 +492,10 @@ supportedArgs(::BokehPackage) = [
# :legendfont, # :legendfont,
# :grid, # :grid,
# :surface, # :surface,
# :nlevels, # :levels,
] ]
supportedAxes(::BokehPackage) = [:auto, :left] 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] supportedStyles(::BokehPackage) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::BokehPackage) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape) supportedMarkers(::BokehPackage) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::BokehPackage) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt] supportedScales(::BokehPackage) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
@ -479,12 +558,12 @@ supportedArgs(::PlotlyPackage) = [
:guidefont, :guidefont,
:legendfont, :legendfont,
:grid, :grid,
:nlevels, :levels,
] ]
supportedAxes(::PlotlyPackage) = [:auto, :left] supportedAxes(::PlotlyPackage) = [:auto, :left]
supportedTypes(::PlotlyPackage) = [:none, :line, :path, :scatter, :steppre, :steppost, supportedTypes(::PlotlyPackage) = [:none, :line, :path, :scatter, :steppre, :steppost,
:heatmap, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d, :hist2d, :hist, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie] #,, :sticks, :hexbin, :hline, :vline] :pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyPackage) = [:auto, :solid, :dash, :dot, :dashdot] supportedStyles(::PlotlyPackage) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyPackage) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, supportedMarkers(::PlotlyPackage) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape) :pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
@ -493,6 +572,75 @@ subplotSupported(::PlotlyPackage) = true
stringsSupported(::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) = [ supportedArgs(::GLVisualizePackage) = [
@ -549,7 +697,7 @@ supportedArgs(::GLVisualizePackage) = [
# :legendfont, # :legendfont,
# :grid, # :grid,
# :surface # :surface
# :nlevels, # :levels,
] ]
supportedAxes(::GLVisualizePackage) = [:auto, :left] supportedAxes(::GLVisualizePackage) = [:auto, :left]
supportedTypes(::GLVisualizePackage) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :hist, :bar, :hline, :vline, :contour] 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) supportedMarkers(::GLVisualizePackage) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::GLVisualizePackage) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt] supportedScales(::GLVisualizePackage) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
subplotSupported(::GLVisualizePackage) = false 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] # [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...) function _create_plot(pkg::[PkgName]Package; kw...)
@ -43,13 +52,11 @@ end
# accessors for x/y data # accessors for x/y data
function Base.getindex(plt::Plot{[PkgName]Package}, i::Int) function Base.getindex(plt::Plot{[PkgName]Package}, i::Int)
series = plt.o.lines[i] # TODO: return a tuple of (x, y) vectors
series.x, series.y
end end
function Base.setindex!(plt::Plot{[PkgName]Package}, xy::Tuple, i::Integer) function Base.setindex!(plt::Plot{[PkgName]Package}, xy::Tuple, i::Integer)
series = plt.o.lines[i] # TODO: set the plot data from the (x,y) tuple
series.x, series.y = xy
plt plt
end end

View File

@ -1,6 +1,13 @@
# https://github.com/Evizero/UnicodePlots.jl # 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 # now use the ! functions to add to the plot
for d in sargs for d in sargs
addUnicodeSeries!(o, d, iargs[:legend], xlim, ylim) addUnicodeSeries!(o, d, iargs[:legend] != :none, xlim, ylim)
end end
# save the object # save the object
@ -97,7 +104,7 @@ function addUnicodeSeries!(o, d::Dict, addlegend::Bool, xlim, ylim)
label = addlegend ? d[:label] : "" label = addlegend ? d[:label] : ""
# if we happen to pass in allowed color symbols, great... otherwise let UnicodePlots decide # 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 # add the series
func(o, x, y; color = color, name = label, style = stepstyle) 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 # 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 if d[:linetype] == :none
Winston.add(wplt, Winston.Points(d[:x], d[:y]; copy_remove(e, :kind)..., color=getColor(d[:markercolor]))) 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 # elseif d[:linetype] == :dots
# fn = Winston.XXX # fn = Winston.XXX
# elseif d[:linetype] == :heatmap # elseif d[:linetype] == :hist2d
# fn = Winston.XXX # fn = Winston.XXX
# elseif d[:linetype] == :hexbin # elseif d[:linetype] == :hexbin
@ -198,7 +206,7 @@ end
# ---------------------------------------------------------------- # ----------------------------------------------------------------
function addWinstonLegend(plt::Plot, wplt) function addWinstonLegend(plt::Plot, wplt)
if plt.plotargs[:legend] if plt.plotargs[:legend] != :none
Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs]) Winston.legend(wplt, [sd[:label] for sd in plt.seriesargs])
end end
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." "Continuous gradient between values. Wraps a list of bounding colors and the values they represent."
immutable ColorGradient <: ColorScheme immutable ColorGradient <: ColorScheme
colors::Vector{Colorant} colors::Vector
values::Vector{Float64} 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) if length(cs) == length(vals)
return new(convertColor(cs,alpha), collect(vals)) return new(convertColor(cs,alpha), collect(vals))
end end
# otherwise interpolate evenly between the minval and maxval # # otherwise interpolate evenly between the minval and maxval
minval, maxval = minimum(vals), maximum(vals) # minval, maxval = minimum(vals), maximum(vals)
vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))] # vs = Float64[interpolate(minval, maxval, w) for w in linspace(0, 1, length(cs))]
new(convertColor(cs,alpha), vs) # 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
end end
# create a gradient from a symbol (blues, reds, etc) and vector of boundary values # 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)))) 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] cs = _gradients[s]
if vals == 0:0
vals = linspace(0, 1, length(cs))
end
ColorGradient(cs, vals; kw...) ColorGradient(cs, vals; kw...)
end end
function ColorGradient{T<:Real}(cs::AVec{Symbol}, vals::AVec{T} = 0:1; kw...) # function ColorGradient{T<:Real}(cs::AVec, vals::AVec{T} = linspace(0, 1, length(cs)); kw...)
ColorGradient(map(convertColor, cs), vals; kw...) # ColorGradient(map(convertColor, cs), vals; kw...)
end # 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))] getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]

View File

@ -131,6 +131,7 @@ immutable PlotText
str::@compat(AbstractString) str::@compat(AbstractString)
font::Font font::Font
end end
PlotText(str) = PlotText(string(str), font())
function text(str, args...) function text(str, args...)
PlotText(string(str), font(args...)) PlotText(string(str), font(args...))
@ -157,7 +158,8 @@ function stroke(args...; alpha = nothing)
for arg in args for arg in args
T = typeof(arg) T = typeof(arg)
if arg in _allStyles # if arg in _allStyles
if allStyles(arg)
style = arg style = arg
elseif T <: Colorant elseif T <: Colorant
color = arg color = arg
@ -165,7 +167,11 @@ function stroke(args...; alpha = nothing)
try try
color = parse(Colorant, string(arg)) color = parse(Colorant, string(arg))
end 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 width = arg
else else
warn("Unused stroke arg: $arg ($(typeof(arg)))") warn("Unused stroke arg: $arg ($(typeof(arg)))")
@ -198,7 +204,11 @@ function brush(args...; alpha = nothing)
try try
color = parse(Colorant, string(arg)) color = parse(Colorant, string(arg))
end 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 size = arg
else else
warn("Unused brush arg: $arg ($(typeof(arg)))") 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]) 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} type OHLC{T<:Real}
@ -239,3 +256,71 @@ type OHLC{T<:Real}
low::T low::T
close::T close::T
end 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) 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, "svg" => svg,
"pdf" => pdf, "pdf" => pdf,
"ps" => ps, "ps" => ps,
"tex" => tex,
) )
function getExtension(fn::@compat(AbstractString)) 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 # override the REPL display to open a gui window
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::PlottingObject) = gui(plt) 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) preprocessArgs!(d)
dumpdict(d, "After plot preprocessing") dumpdict(d, "After plot preprocessing")
plotargs = getPlotArgs(pkg, d, 1) plotargs = merge(d, getPlotArgs(pkg, d, 1))
dumpdict(plotargs, "Plot args") dumpdict(plotargs, "Plot args")
plt = _create_plot(pkg; plotargs...) # create a new, blank plot 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(::@compat(Void)) = []
annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v annotations{X,Y,V}(v::AVec{@compat(Tuple{X,Y,V})}) = v
annotations{X,Y,V}(t::@compat(Tuple{X,Y,V})) = [t] 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: ", annotations(anns) = error("Expecting a tuple (or vector of tuples) for annotations: ",
"(x, y, annotation)\n got: $(typeof(anns))") "(x, y, annotation)\n got: $(typeof(anns))")
function _add_annotations(plt::Plot, d::Dict) function _add_annotations(plt::Plot, d::Dict)
anns = annotations(get(d, :annotation, nothing)) anns = annotations(get(d, :annotation, nothing))
if !isempty(anns) 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) _add_annotations(plt, anns)
end end
end end
@ -219,41 +228,50 @@ end
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}}) typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
all3D(d::Dict) = trueOrAllTrue(lt -> lt in (:contour, :heatmap, :surface, :wireframe), get(d, :linetype, :none))
# missing # missing
convertToAnyVector(v::@compat(Void); kw...) = Any[nothing], nothing convertToAnyVector(v::@compat(Void), d::Dict) = Any[nothing], nothing
# fixed number of blank series # 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 # 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 # 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 # 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 # function
convertToAnyVector(f::Function; kw...) = Any[f], nothing convertToAnyVector(f::Function, d::Dict) = Any[f], nothing
# surface # surface
convertToAnyVector(s::Surface; kw...) = Any[s], nothing convertToAnyVector(s::Surface, d::Dict) = Any[s], nothing
# vector of OHLC # vector of OHLC
convertToAnyVector(v::AVec{OHLC}; kw...) = Any[v], nothing convertToAnyVector(v::AVec{OHLC}, d::Dict) = Any[v], nothing
# dates # 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) # 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) if all(x -> typeof(x) <: Real, v)
# all real numbers wrap the whole vector as one item # all real numbers wrap the whole vector as one item
Any[convert(Vector{Float64}, v)], nothing Any[convert(Vector{Float64}, v)], nothing
else else
# something else... treat each element as an item # 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
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. # 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. # 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) computeX(x, y) = copy(x)
computeY(x, y::Function) = map(y, x) computeY(x, y::Function) = map(y, x)
computeY(x, y) = copy(y) computeY(x, y) = copy(y)
@ -281,8 +299,9 @@ end
# create n=max(mx,my) series arguments. the shorter list is cycled through # create n=max(mx,my) series arguments. the shorter list is cycled through
# note: everything should flow through this # note: everything should flow through this
function createKWargsList(plt::PlottingObject, x, y; kw...) function createKWargsList(plt::PlottingObject, x, y; kw...)
xs, xmeta = convertToAnyVector(x; kw...) kwdict = Dict(kw)
ys, ymeta = convertToAnyVector(y; kw...) xs, xmeta = convertToAnyVector(x, kwdict)
ys, ymeta = convertToAnyVector(y, kwdict)
mx = length(xs) mx = length(xs)
my = length(ys) my = length(ys)
@ -290,7 +309,7 @@ function createKWargsList(plt::PlottingObject, x, y; kw...)
for i in 1:max(mx, my) for i in 1:max(mx, my)
# try to set labels using ymeta # try to set labels using ymeta
d = Dict(kw) d = copy(kwdict)
if !haskey(d, :label) && ymeta != nothing if !haskey(d, :label) && ymeta != nothing
if isa(ymeta, Symbol) if isa(ymeta, Symbol)
d[:label] = string(ymeta) d[:label] = string(ymeta)
@ -307,13 +326,22 @@ function createKWargsList(plt::PlottingObject, x, y; kw...)
dumpdict(d, "after getSeriesArgs") dumpdict(d, "after getSeriesArgs")
d[:x], d[:y] = computeXandY(xs[mod1(i,mx)], ys[mod1(i,my)]) 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) if haskey(d, :idxfilter)
d[:x] = d[:x][d[:idxfilter]] d[:x] = d[:x][d[:idxfilter]]
d[:y] = d[:y][d[:idxfilter]] d[:y] = d[:y][d[:idxfilter]]
end end
# for linetype `line`, need to sort by x values # for linetype `line`, need to sort by x values
if d[:linetype] == :line if lt == :line
# order by x # order by x
indices = sortperm(d[:x]) indices = sortperm(d[:x])
d[:x] = d[:x][indices] d[:x] = d[:x][indices]
@ -321,6 +349,14 @@ function createKWargsList(plt::PlottingObject, x, y; kw...)
d[:linetype] = :path d[:linetype] = :path
end 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 # cleanup those fields that were used only for generating kw args
for k in (:idxfilter, :numUncounted, :dataframe) for k in (:idxfilter, :numUncounted, :dataframe)
delete!(d, k) 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...) createKWargsList(plt, x, y; z=zvec, d...)
end 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 # contours or surfaces... function grid
function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zf::Function; kw...) function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, zf::Function; kw...)
# only allow sorted x/y for now # 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 x == sort(x)
@assert y == sort(y) @assert y == sort(y)
@assert size(zmat) == (length(x), length(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 = Array(Any,1,1)
# surf[1,1] = convert(Matrix{Float64}, zmat) # surf[1,1] = convert(Matrix{Float64}, zmat)
d = Dict(kw) 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 d[:linetype] = :contour
end end
createKWargsList(plt, x, y; d..., z = surf) createKWargsList(plt, x, y; d...) #, z = surf)
end end
# contours or surfaces... general x, y grid # 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 = Surface(convert(Matrix{Float64}, zmat))
# surf = Array(Any,1,1) # surf = Array(Any,1,1)
# surf[1,1] = convert(Matrix{Float64}, zmat) # 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 end
@ -407,7 +459,7 @@ function createKWargsList(plt::PlottingObject, x::AVec, y::AVec, surf::Surface;
end end
function createKWargsList(plt::PlottingObject, f::FuncOrFuncs; kw...) 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 end
# list of functions # 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{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...) 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 # 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) createKWargsList(plt, args...; kw..., dataframe = df)
end end
# expecting the column name of a dataframe that was passed in... anything else should error # 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) if haskey(df, s)
return extractGroupArgs(df[s]) return extractGroupArgs(df[s])
else else
@ -468,18 +551,23 @@ function dataframes()
end end
end end
@eval function getDataFrameFromKW(; kw...) function getDataFrameFromKW(d::Dict)
for (k,v) in kw # for (k,v) in kw
if k == :dataframe # if k == :dataframe
return v # return v
end # end
# end
get(d, :dataframe) do
error("Missing dataframe argument!")
end end
error("Missing dataframe argument in arguments!")
end end
# the conversion functions for when we pass symbols or vectors of symbols to reference dataframes # 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 # 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(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 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" "Sparsity plot... heatmap of non-zero values of a matrix"
function spy{T<:Real}(y::AMat{T}; kw...) function spy{T<:Real}(z::AMat{T}; kw...)
I,J,V = findnz(y) # I,J,V = findnz(z)
heatmap(J, I; leg=false, yflip=true, nbins=size(y), kw...) # heatmap(J, I; leg=false, yflip=true, kw...)
heatmap(map(zi->float(zi!=0), z); leg=false, yflip=true, kw...)
end 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() function validateSubplotSupported()
if !subplotSupported() 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
end end

View File

@ -174,11 +174,20 @@ function replaceAliases!(d::Dict, aliases::Dict)
end end
createSegments(z) = collect(repmat(z',2,1))[2:end] createSegments(z) = collect(repmat(z',2,1))[2:end]
Base.first(c::Colorant) = c Base.first(c::Colorant) = c
Base.first(x::Symbol) = x
sortedkeys(d::Dict) = sort(collect(keys(d))) 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...) function fakedata(sz...)
y = zeros(sz...) y = zeros(sz...)
@ -190,9 +199,21 @@ end
isijulia() = isdefined(Main, :IJulia) && Main.IJulia.inited 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,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
ticksType{T<:Real}(ticks::AVec{T}) = :ticks 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{T<:AVec,S<:AVec}(ticks::@compat(Tuple{T,S})) = :ticks_and_labels
ticksType(ticks) = :invalid 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}(::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.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: Allows temporary setting of backend and defaults for Plots. Settings apply only for the `do` block. Example:
``` ```
@ -226,6 +266,9 @@ function with(f::Function, args...; kw...)
end end
# save the backend # save the backend
if CURRENT_BACKEND.sym == :none
pickDefaultBackend()
end
oldbackend = CURRENT_BACKEND.sym oldbackend = CURRENT_BACKEND.sym
for arg in args for arg in args
@ -399,7 +442,7 @@ function supportGraph(allvals, func)
y = ASCIIString[] y = ASCIIString[]
for val in vals for val in vals
for b in bs for b in bs
supported = func(Plots.backendInstance(b)) supported = func(Plots._backend_instance(b))
if val in supported if val in supported
push!(x, string(b)) push!(x, string(b))
push!(y, string(val)) push!(y, string(val))
@ -428,7 +471,7 @@ function dumpSupportGraphs()
for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles, for func in (supportGraphArgs, supportGraphTypes, supportGraphStyles,
supportGraphMarkers, supportGraphScales, supportGraphAxes) supportGraphMarkers, supportGraphScales, supportGraphAxes)
plt = func() plt = func()
png(IMG_DIR * "/supported/$(string(func))") png(joinpath(Pkg.dir("ExamplePlots"), "docs", "examples", "img", "supported", "$(string(func))"))
end end
end end
@ -448,3 +491,11 @@ mm2inch(mm::Real) = float(mm / MM_PER_INCH)
px2mm(px::Real) = float(px * MM_PER_PX) px2mm(px::Real) = float(px * MM_PER_PX)
mm2px(mm::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 Colors
Reexport Reexport
Requires
FactCheck FactCheck
Gadfly Gadfly
Images Images
ImageMagick ImageMagick
PyPlot PyPlot
@osx QuartzImageIO @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 VisualRegressionTests
using ExamplePlots using ExamplePlots
@ -17,53 +10,10 @@ try
info("Matplotlib version: $(PyPlot.matplotlib[:__version__])") info("Matplotlib version: $(PyPlot.matplotlib[:__version__])")
end end
# include("../docs/example_generation.jl")
using Plots, FactCheck using Plots, FactCheck
# import Images, ImageMagick
# if !isdefined(ImageMagick, :init_deps) default(size=(500,300))
# 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
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that # 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 # first
Plots._debugMode.on = debug Plots._debugMode.on = debug
example = ExamplePlots.examples[idx] example = ExamplePlots._examples[idx]
info("Testing plot: $pkg:$idx:$(example.header)") info("Testing plot: $pkg:$idx:$(example.header)")
backend(pkg) backend(pkg)
backend() backend()
@ -81,24 +31,15 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
# ensure consistent results # ensure consistent results
srand(1234) srand(1234)
# reference image directory setup
refdir = joinpath(Pkg.dir("ExamplePlots"), "test", "refimg", string(pkg))
# test function # test function
func = (fn, idx) -> begin func = (fn, idx) -> begin
map(eval, example.exprs) map(eval, example.exprs)
png(fn) png(fn)
end 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 try
run(`mkdir -p $refdir`) run(`mkdir -p $refdir`)
catch err catch err
@ -109,47 +50,18 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
# the test # the test
vtest = VisualTest(func, reffn, idx) vtest = VisualTest(func, reffn, idx)
test_images(vtest, popup=popup, sigma=sigma, eps=eps) 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 end
function image_comparison_facts(pkg::Symbol; skip = [], debug = false, sigma = [1,1], eps = 1e-2) function image_comparison_facts(pkg::Symbol;
for i in 1:length(ExamplePlots.examples) 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 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
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 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") include("imgcomp.jl")
# don't actually show the plots # don't actually show the plots
srand(1234) srand(1234)
default(show=false) default(show=false)
img_eps = 5e-2
# 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
facts("Gadfly") do facts("Gadfly") do
@fact gadfly() --> Plots.GadflyPackage() @fact gadfly() --> Plots.GadflyPackage()
@fact backend() --> Plots.GadflyPackage() @fact backend() --> Plots.GadflyPackage()
@fact typeof(plot(1:10)) --> Plots.Plot{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 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 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) @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 end
facts("PyPlot") do facts("PyPlot") do
@fact pyplot() --> Plots.PyPlotPackage() @fact pyplot() --> Plots.PyPlotPackage()
@fact backend() --> 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 end
facts("GR") do
@fact gr() --> Plots.GRPackage()
@fact backend() --> Plots.GRPackage()
# catch err # image_comparison_facts(:gr, only=[1], eps=img_eps)
# warn("Skipped Gadfly due to: ", string(err)) end
# 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
FactCheck.exitstatus() FactCheck.exitstatus()
end # module end # module