Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 01b9cc92fd | |||
| 7a650d918b | |||
| b6fa4bcda4 | |||
| 73af635be6 | |||
| c1c97a5fc1 | |||
| 6a97dc8825 | |||
| d832f34733 | |||
| 859a600b92 | |||
| 6b3cf50f38 | |||
| 3ccd1bc368 | |||
| 624d33b96f | |||
| 156f61f526 | |||
| fd3a04fce3 | |||
| 62be4e1274 | |||
| db096196c9 | |||
| a0ac70be3c | |||
| 1f0f89f83c | |||
| ee706ad8c7 | |||
| ce31ea8bf3 | |||
| dcab8e3ac2 | |||
| 1ad9a7e176 | |||
| f1bd7ab1dc | |||
| 79399913d7 | |||
| 4a2e88a81c | |||
| 8d5b748b09 | |||
| b2dba8af73 | |||
| c258bc49c3 | |||
| e3e2a354e7 | |||
| 32c1c31139 | |||
| b8b5a33833 | |||
| 5407fa73f8 | |||
| 1cb0c0071b | |||
| a456ac4c90 | |||
| 71b48427c5 | |||
| ec06a01b6c | |||
| e3ccc34a2c | |||
| 396721568f | |||
| e53ab85745 | |||
| 4d99b37def | |||
| 2e24da3dcd | |||
| 0cc1bd2dc6 | |||
| aec162c50e | |||
| d5e9340f8f | |||
| 6fe87c4c69 | |||
| 85f0a87213 | |||
| 853f85be2e | |||
| a7448e3a1d | |||
| 521e753183 | |||
| 9193941fd0 | |||
| f56c6effe4 | |||
| 4cfcb239c1 | |||
| a4be274718 |
@@ -7,7 +7,54 @@
|
||||
|
||||
---
|
||||
|
||||
## 0.7 (current master/dev)
|
||||
## 0.8 (current master/dev)
|
||||
|
||||
#### 0.8.0
|
||||
|
||||
- added dependency on PlotUtils
|
||||
- BREAKING: removed DataFrames support (now in StatPlots.jl)
|
||||
- BREAKING: removed boxplot/violin/density recipes (now in StatPlots.jl)
|
||||
- GR:
|
||||
- inline iterm2 support
|
||||
- trisurface support
|
||||
- heatmap fix
|
||||
- PyPlot:
|
||||
- ijulia display fix
|
||||
- GLVisualize:
|
||||
- first try with shapes
|
||||
- iter_segments improvements
|
||||
- bar_width support
|
||||
- horizontal bars
|
||||
- improve tick display
|
||||
- better shape handling in pyplot, plotly
|
||||
- improved padding calcs
|
||||
- internal reorg of _plots method, add pipeline.jl
|
||||
|
||||
---
|
||||
|
||||
## 0.7
|
||||
|
||||
#### 0.7.5
|
||||
|
||||
- GR: LaTeX support
|
||||
- Changed docs url to juliaplots.github.io
|
||||
- added `contourf` seriestype
|
||||
- allow `plt[1]` to return first Subplot
|
||||
- allow `sp[1]` to return the first Series of the Subplot
|
||||
- `series[k]` now passes through to `series.d[k]`
|
||||
- allow calling `plot!(sp, ...)` to update a target Subplot
|
||||
- PyPlot: zorder fix
|
||||
- new DataFrames logic/recipe: more flexible/robust and allow Symbols for:
|
||||
- `(:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror)`
|
||||
- new `display_type` and `extra_kwargs` plot attributes
|
||||
- surface fix
|
||||
|
||||
#### 0.7.4
|
||||
|
||||
- added snooped precompiles, but left commented out
|
||||
- GR fixes: markersize, shapes, legends
|
||||
- fixes to recipes
|
||||
- turned on Appveyor
|
||||
|
||||
#### 0.7.3
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
julia 0.4
|
||||
|
||||
RecipesBase
|
||||
Colors
|
||||
PlotUtils
|
||||
Reexport
|
||||
Compat
|
||||
FixedSizeArrays
|
||||
Measures
|
||||
Showoff
|
||||
|
||||
+8
-17
@@ -5,12 +5,13 @@ module Plots
|
||||
|
||||
using Compat
|
||||
using Reexport
|
||||
@reexport using Colors
|
||||
# @reexport using Colors
|
||||
# using Requires
|
||||
using FixedSizeArrays
|
||||
@reexport using RecipesBase
|
||||
using Base.Meta
|
||||
# using PlotUtils
|
||||
@reexport using PlotUtils
|
||||
import Showoff
|
||||
|
||||
export
|
||||
AbstractPlot,
|
||||
@@ -81,16 +82,6 @@ export
|
||||
arrow,
|
||||
Segments,
|
||||
|
||||
colorscheme,
|
||||
ColorScheme,
|
||||
ColorGradient,
|
||||
ColorVector,
|
||||
ColorWrapper,
|
||||
ColorFunction,
|
||||
ColorZFunction,
|
||||
getColor,
|
||||
getColorZ,
|
||||
|
||||
debugplots,
|
||||
|
||||
supported_args,
|
||||
@@ -109,6 +100,7 @@ export
|
||||
|
||||
test_examples,
|
||||
iter_segments,
|
||||
coords,
|
||||
|
||||
translate,
|
||||
translate!,
|
||||
@@ -132,15 +124,14 @@ export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h
|
||||
|
||||
include("types.jl")
|
||||
include("utils.jl")
|
||||
include("colors.jl")
|
||||
include("components.jl")
|
||||
include("axes.jl")
|
||||
include("backends.jl")
|
||||
include("args.jl")
|
||||
include("themes.jl")
|
||||
include("plot.jl")
|
||||
include("series_args.jl")
|
||||
include("series_new.jl")
|
||||
include("pipeline.jl")
|
||||
include("series.jl")
|
||||
include("layouts.jl")
|
||||
include("subplots.jl")
|
||||
include("recipes.jl")
|
||||
@@ -246,8 +237,8 @@ function __init__()
|
||||
setup_ijulia()
|
||||
setup_atom()
|
||||
|
||||
if haskey(ENV, "PLOTS_DEFAULTS")
|
||||
for (k,v) in eval(parse(ENV["PLOTS_DEFAULTS"]))
|
||||
if isdefined(Main, :PLOTS_DEFAULTS)
|
||||
for (k,v) in Main.PLOTS_DEFAULTS
|
||||
default(k, v)
|
||||
end
|
||||
end
|
||||
|
||||
+239
-105
@@ -1,6 +1,6 @@
|
||||
|
||||
|
||||
const _keyAliases = KW()
|
||||
const _keyAliases = Dict{Symbol,Symbol}()
|
||||
|
||||
function add_aliases(sym::Symbol, aliases::Symbol...)
|
||||
for alias in aliases
|
||||
@@ -11,7 +11,7 @@ function add_aliases(sym::Symbol, aliases::Symbol...)
|
||||
end
|
||||
end
|
||||
|
||||
function add_non_underscore_aliases!(aliases::KW)
|
||||
function add_non_underscore_aliases!(aliases::Dict{Symbol,Symbol})
|
||||
for (k,v) in aliases
|
||||
s = string(k)
|
||||
if '_' in s
|
||||
@@ -24,7 +24,7 @@ end
|
||||
# ------------------------------------------------------------
|
||||
|
||||
const _allAxes = [:auto, :left, :right]
|
||||
const _axesAliases = KW(
|
||||
const _axesAliases = Dict{Symbol,Symbol}(
|
||||
:a => :auto,
|
||||
:l => :left,
|
||||
:r => :right
|
||||
@@ -39,7 +39,7 @@ const _allTypes = vcat([
|
||||
:contour, :pie, :shape, :image
|
||||
], _3dTypes)
|
||||
|
||||
@compat const _typeAliases = KW(
|
||||
@compat const _typeAliases = Dict{Symbol,Symbol}(
|
||||
:n => :none,
|
||||
:no => :none,
|
||||
:l => :line,
|
||||
@@ -83,7 +83,7 @@ like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contou
|
||||
|
||||
is3d(seriestype::Symbol) = seriestype in _3dTypes
|
||||
is3d(series::Series) = is3d(series.d)
|
||||
is3d(d::KW) = trueOrAllTrue(is3d, d[:seriestype])
|
||||
is3d(d::KW) = trueOrAllTrue(is3d, Symbol(d[:seriestype]))
|
||||
|
||||
is3d(sp::Subplot) = string(sp.attr[:projection]) == "3d"
|
||||
ispolar(sp::Subplot) = string(sp.attr[:projection]) == "polar"
|
||||
@@ -92,7 +92,7 @@ ispolar(series::Series) = ispolar(series.d[:subplot])
|
||||
# ------------------------------------------------------------
|
||||
|
||||
const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
@compat const _styleAliases = KW(
|
||||
@compat const _styleAliases = Dict{Symbol,Symbol}(
|
||||
:a => :auto,
|
||||
:s => :solid,
|
||||
:d => :dash,
|
||||
@@ -101,7 +101,7 @@ const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
)
|
||||
|
||||
const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))))
|
||||
@compat const _markerAliases = KW(
|
||||
@compat const _markerAliases = Dict{Symbol,Symbol}(
|
||||
:n => :none,
|
||||
:no => :none,
|
||||
:a => :auto,
|
||||
@@ -143,7 +143,7 @@ const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))
|
||||
)
|
||||
|
||||
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
@compat const _scaleAliases = KW(
|
||||
@compat const _scaleAliases = Dict{Symbol,Symbol}(
|
||||
:none => :identity,
|
||||
:log => :log10,
|
||||
)
|
||||
@@ -242,7 +242,7 @@ const _subplot_defaults = KW(
|
||||
:annotations => [], # annotation tuples... list of (x,y,annotation)
|
||||
:projection => :none, # can also be :polar or :3d
|
||||
:aspect_ratio => :none, # choose from :none or :equal
|
||||
:margin => 2mm,
|
||||
:margin => 1mm,
|
||||
:left_margin => :match,
|
||||
:top_margin => :match,
|
||||
:right_margin => :match,
|
||||
@@ -330,7 +330,7 @@ autopick(notarr, idx::Integer) = notarr
|
||||
autopick_ignore_none_auto(arr::AVec, idx::Integer) = autopick(setdiff(arr, [:none, :auto]), idx)
|
||||
autopick_ignore_none_auto(notarr, idx::Integer) = notarr
|
||||
|
||||
function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plotIndex::Int)
|
||||
function aliasesAndAutopick(d::KW, sym::Symbol, aliases::Dict{Symbol,Symbol}, options::AVec, plotIndex::Int)
|
||||
if d[sym] == :auto
|
||||
d[sym] = autopick_ignore_none_auto(options, plotIndex)
|
||||
elseif haskey(aliases, d[sym])
|
||||
@@ -338,7 +338,7 @@ function aliasesAndAutopick(d::KW, sym::Symbol, aliases::KW, options::AVec, plot
|
||||
end
|
||||
end
|
||||
|
||||
function aliases(aliasMap::KW, val)
|
||||
function aliases(aliasMap::Dict{Symbol,Symbol}, val)
|
||||
sortedkeys(filter((k,v)-> v==val, aliasMap))
|
||||
end
|
||||
|
||||
@@ -498,7 +498,8 @@ function handleColors!(d::KW, arg, csym::Symbol)
|
||||
if arg == :auto
|
||||
d[csym] = :auto
|
||||
else
|
||||
c = colorscheme(arg)
|
||||
# c = colorscheme(arg)
|
||||
c = plot_color(arg)
|
||||
d[csym] = c
|
||||
end
|
||||
return true
|
||||
@@ -519,13 +520,13 @@ function processLineArg(d::KW, arg)
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (d[:linewidth] = arg.width)
|
||||
arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
|
||||
arg.color == nothing || (d[:linecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (d[:linealpha] = arg.alpha)
|
||||
arg.style == nothing || (d[:linestyle] = arg.style)
|
||||
|
||||
elseif typeof(arg) <: Brush
|
||||
arg.size == nothing || (d[:fillrange] = arg.size)
|
||||
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
|
||||
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
|
||||
|
||||
elseif typeof(arg) <: Arrow || arg in (:arrow, :arrows)
|
||||
@@ -558,13 +559,13 @@ function processMarkerArg(d::KW, arg)
|
||||
|
||||
elseif typeof(arg) <: Stroke
|
||||
arg.width == nothing || (d[:markerstrokewidth] = arg.width)
|
||||
arg.color == nothing || (d[:markerstrokecolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
|
||||
arg.color == nothing || (d[:markerstrokecolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (d[:markerstrokealpha] = arg.alpha)
|
||||
arg.style == nothing || (d[:markerstrokestyle] = arg.style)
|
||||
|
||||
elseif typeof(arg) <: Brush
|
||||
arg.size == nothing || (d[:markersize] = arg.size)
|
||||
arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
|
||||
arg.color == nothing || (d[:markercolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (d[:markeralpha] = arg.alpha)
|
||||
|
||||
# linealpha
|
||||
@@ -584,9 +585,10 @@ end
|
||||
|
||||
|
||||
function processFillArg(d::KW, arg)
|
||||
# fr = get(d, :fillrange, 0)
|
||||
if typeof(arg) <: Brush
|
||||
arg.size == nothing || (d[:fillrange] = arg.size)
|
||||
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
|
||||
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : plot_color(arg.color))
|
||||
arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
|
||||
|
||||
# fillrange function
|
||||
@@ -601,6 +603,8 @@ function processFillArg(d::KW, arg)
|
||||
|
||||
d[:fillrange] = arg
|
||||
end
|
||||
# d[:fillrange] = fr
|
||||
return
|
||||
end
|
||||
|
||||
_replace_markershape(shape::Symbol) = get(_markerAliases, shape, shape)
|
||||
@@ -687,21 +691,13 @@ function preprocessArgs!(d::KW)
|
||||
d[:colorbar] = convertLegendValue(d[:colorbar])
|
||||
end
|
||||
|
||||
# # handle subplot links
|
||||
# if haskey(d, :link)
|
||||
# l = d[:link]
|
||||
# if isa(l, Bool)
|
||||
# d[:linkx] = l
|
||||
# d[:linky] = l
|
||||
# elseif isa(l, Function)
|
||||
# d[:linkx] = true
|
||||
# d[:linky] = true
|
||||
# d[:linkfunc] = l
|
||||
# else
|
||||
# warn("Unhandled/invalid link $l. Should be a Bool or a function mapping (row,column) -> (linkx, linky), where linkx/y can be Bool or Void (nothing)")
|
||||
# end
|
||||
# delete!(d, :link)
|
||||
# end
|
||||
# warnings for moved recipes
|
||||
st = get(d, :seriestype, :path)
|
||||
if st in (:boxplot, :violin, :density) && !isdefined(Main, :StatPlots)
|
||||
warn("seriestype $st has been moved to StatPlots. To use: \`Pkg.add(\"StatPlots\"); using StatPlots\`")
|
||||
end
|
||||
|
||||
return
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -782,7 +778,7 @@ function warnOnUnsupported(pkg::AbstractBackend, d::KW)
|
||||
end
|
||||
|
||||
function warnOnUnsupported_scales(pkg::AbstractBackend, d::KW)
|
||||
for k in (:xscale, :yscale, :zscale)
|
||||
for k in (:xscale, :yscale, :zscale, :scale)
|
||||
if haskey(d, k)
|
||||
v = d[k]
|
||||
v = get(_scaleAliases, v, v)
|
||||
@@ -809,6 +805,7 @@ function convertLegendValue(val::Symbol)
|
||||
end
|
||||
convertLegendValue(val::Bool) = val ? :best : :none
|
||||
convertLegendValue(val::Void) = :none
|
||||
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
@@ -828,9 +825,9 @@ slice_arg(v, idx) = v
|
||||
# given an argument key (k), we want to extract the argument value for this index.
|
||||
# matrices are sliced by column, otherwise we
|
||||
# if nothing is set (or container is empty), return the default or the existing value.
|
||||
function slice_arg!(d_in::KW, d_out::KW, k::Symbol, default_value, idx::Int = 1; new_key::Symbol = k, remove_pair::Bool = true)
|
||||
v = get(d_in, k, get(d_out, new_key, default_value))
|
||||
d_out[new_key] = if haskey(d_in, k) && typeof(v) <: AMat && !isempty(v)
|
||||
function slice_arg!(d_in::KW, d_out::KW, k::Symbol, default_value, idx::Int, remove_pair::Bool)
|
||||
v = get(d_in, k, get(d_out, k, default_value))
|
||||
d_out[k] = if haskey(d_in, k) && typeof(v) <: AMat && !isempty(v)
|
||||
slice_arg(v, idx)
|
||||
else
|
||||
v
|
||||
@@ -838,6 +835,7 @@ function slice_arg!(d_in::KW, d_out::KW, k::Symbol, default_value, idx::Int = 1;
|
||||
if remove_pair
|
||||
delete!(d_in, k)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -849,7 +847,7 @@ end
|
||||
# d[k] = if v == :match
|
||||
# match_color
|
||||
# elseif v == nothing
|
||||
# colorscheme(RGBA(0,0,0,0))
|
||||
# plot_color(RGBA(0,0,0,0))
|
||||
# else
|
||||
# v
|
||||
# end
|
||||
@@ -858,10 +856,13 @@ end
|
||||
function color_or_nothing!(d::KW, k::Symbol)
|
||||
v = d[k]
|
||||
d[k] = if v == nothing || v == false
|
||||
colorscheme(RGBA(0,0,0,0))
|
||||
RGBA{Float64}(0,0,0,0)
|
||||
elseif v != :match
|
||||
plot_color(v)
|
||||
else
|
||||
v
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
@@ -934,36 +935,39 @@ function Base.getindex(series::Series, k::Symbol)
|
||||
series.d[k]
|
||||
end
|
||||
|
||||
Base.setindex!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
|
||||
Base.setindex!(sp::Subplot, v, k::Symbol) = (sp.attr[k] = v)
|
||||
Base.setindex!(axis::Axis, v, k::Symbol) = (axis.d[k] = v)
|
||||
Base.setindex!(series::Series, v, k::Symbol) = (series.d[k] = v)
|
||||
|
||||
Base.get(plt::Plot, k::Symbol, v) = get(plt.attr, k, v)
|
||||
Base.get(sp::Subplot, k::Symbol, v) = get(sp.attr, k, v)
|
||||
Base.get(axis::Axis, k::Symbol, v) = get(axis.d, k, v)
|
||||
Base.get(series::Series, k::Symbol, v) = get(series.d, k, v)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# update attr from an input dictionary
|
||||
function _update_plot_args(plt::Plot, d_in::KW)
|
||||
for (k,v) in _plot_defaults
|
||||
slice_arg!(d_in, plt.attr, k, v)
|
||||
slice_arg!(d_in, plt.attr, k, v, 1, true)
|
||||
end
|
||||
|
||||
# handle colors
|
||||
bg = convertColor(plt.attr[:background_color])
|
||||
bg = plot_color(plt.attr[:background_color])
|
||||
fg = plt.attr[:foreground_color]
|
||||
if fg == :auto
|
||||
fg = isdark(bg) ? colorant"white" : colorant"black"
|
||||
end
|
||||
plt.attr[:background_color] = bg
|
||||
plt.attr[:foreground_color] = convertColor(fg)
|
||||
# color_or_match!(plt.attr, :background_color_outside, bg)
|
||||
plt.attr[:foreground_color] = plot_color(fg)
|
||||
color_or_nothing!(plt.attr, :background_color_outside)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# update a subplots args and axes
|
||||
function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::Integer; remove_pair = true)
|
||||
anns = pop!(sp.attr, :annotations, [])
|
||||
|
||||
# grab those args which apply to this subplot
|
||||
for (k,v) in _subplot_defaults
|
||||
slice_arg!(d_in, sp.attr, k, v, subplot_index, remove_pair = remove_pair)
|
||||
end
|
||||
|
||||
function _update_subplot_periphery(sp::Subplot, anns::AVec)
|
||||
# extend annotations
|
||||
sp.attr[:annotations] = vcat(anns, sp[:annotations])
|
||||
|
||||
@@ -973,11 +977,13 @@ function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::I
|
||||
if sp.attr[:colorbar] == :legend
|
||||
sp.attr[:colorbar] = sp.attr[:legend]
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function _update_subplot_colors(sp::Subplot)
|
||||
# background colors
|
||||
# bg = color_or_match!(sp.attr, :background_color_subplot, plt.attr[:background_color])
|
||||
color_or_nothing!(sp.attr, :background_color_subplot)
|
||||
bg = convertColor(sp[:background_color_subplot])
|
||||
bg = plot_color(sp[:background_color_subplot])
|
||||
sp.attr[:color_palette] = get_color_palette(sp.attr[:color_palette], bg, 30)
|
||||
color_or_nothing!(sp.attr, :background_color_legend)
|
||||
color_or_nothing!(sp.attr, :background_color_inside)
|
||||
@@ -987,64 +993,192 @@ function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::I
|
||||
color_or_nothing!(sp.attr, :foreground_color_legend)
|
||||
color_or_nothing!(sp.attr, :foreground_color_grid)
|
||||
color_or_nothing!(sp.attr, :foreground_color_title)
|
||||
return
|
||||
end
|
||||
|
||||
# for k in (:left_margin, :top_margin, :right_margin, :bottom_margin)
|
||||
# if sp.attr[k] == :match
|
||||
# sp.attr[k] = sp.attr[:margin]
|
||||
# end
|
||||
# end
|
||||
function _update_axis(plt::Plot, sp::Subplot, d_in::KW, letter::Symbol, subplot_index::Int)
|
||||
# get (maybe initialize) the axis
|
||||
axis = get_axis(sp, letter)
|
||||
|
||||
_update_axis(axis, d_in, letter, subplot_index)
|
||||
|
||||
# convert a bool into auto or nothing
|
||||
if isa(axis[:ticks], Bool)
|
||||
axis[:ticks] = axis[:ticks] ? :auto : nothing
|
||||
end
|
||||
|
||||
_update_axis_colors(axis)
|
||||
_update_axis_links(plt, axis, letter)
|
||||
return
|
||||
end
|
||||
|
||||
function _update_axis(axis::Axis, d_in::KW, letter::Symbol, subplot_index::Int)
|
||||
# grab magic args (for example `xaxis = (:flip, :log)`)
|
||||
args = wraptuple(get(d_in, Symbol(letter, :axis), ()))
|
||||
|
||||
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
|
||||
kw = KW()
|
||||
for (k,v) in _axis_defaults
|
||||
# first get the args without the letter: `tickfont = font(10)`
|
||||
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
|
||||
if haskey(d_in, k)
|
||||
kw[k] = slice_arg(d_in[k], subplot_index)
|
||||
end
|
||||
|
||||
# then get those args that were passed with a leading letter: `xlabel = "X"`
|
||||
lk = Symbol(letter, k)
|
||||
if haskey(d_in, lk)
|
||||
kw[k] = slice_arg(d_in[lk], subplot_index)
|
||||
end
|
||||
end
|
||||
|
||||
# update the axis
|
||||
update!(axis, args...; kw...)
|
||||
return
|
||||
end
|
||||
|
||||
function _update_axis_colors(axis::Axis)
|
||||
# # update the axis colors
|
||||
color_or_nothing!(axis.d, :foreground_color_axis)
|
||||
color_or_nothing!(axis.d, :foreground_color_border)
|
||||
color_or_nothing!(axis.d, :foreground_color_guide)
|
||||
color_or_nothing!(axis.d, :foreground_color_text)
|
||||
return
|
||||
end
|
||||
|
||||
function _update_axis_links(plt::Plot, axis::Axis, letter::Symbol)
|
||||
# handle linking here. if we're passed a list of
|
||||
# other subplots to link to, link them together
|
||||
link = axis[:link]
|
||||
if !isempty(link)
|
||||
for other_sp in link
|
||||
other_sp = get_subplot(plt, other_sp)
|
||||
link_axes!(axis, get_axis(other_sp, letter))
|
||||
end
|
||||
axis.d[:link] = []
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# update a subplots args and axes
|
||||
function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::Int, remove_pair::Bool)
|
||||
anns = pop!(sp.attr, :annotations, [])
|
||||
|
||||
# grab those args which apply to this subplot
|
||||
for (k,v) in _subplot_defaults
|
||||
slice_arg!(d_in, sp.attr, k, v, subplot_index, remove_pair)
|
||||
end
|
||||
|
||||
_update_subplot_periphery(sp, anns)
|
||||
_update_subplot_colors(sp)
|
||||
|
||||
for letter in (:x, :y, :z)
|
||||
# get (maybe initialize) the axis
|
||||
axis = get_axis(sp, letter)
|
||||
|
||||
# grab magic args (for example `xaxis = (:flip, :log)`)
|
||||
args = wraptuple(get(d_in, Symbol(letter, :axis), ()))
|
||||
|
||||
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
|
||||
kw = KW()
|
||||
for (k,v) in _axis_defaults
|
||||
# first get the args without the letter: `tickfont = font(10)`
|
||||
# note: we don't pop because we want this to apply to all axes! (delete after all have finished)
|
||||
if haskey(d_in, k)
|
||||
kw[k] = slice_arg(d_in[k], subplot_index)
|
||||
end
|
||||
|
||||
# then get those args that were passed with a leading letter: `xlabel = "X"`
|
||||
lk = Symbol(letter, k)
|
||||
if haskey(d_in, lk)
|
||||
kw[k] = slice_arg(d_in[lk], subplot_index)
|
||||
end
|
||||
end
|
||||
|
||||
# update the axis
|
||||
update!(axis, args...; kw...)
|
||||
|
||||
# convert a bool into auto or nothing
|
||||
if isa(axis[:ticks], Bool)
|
||||
axis[:ticks] = axis[:ticks] ? :auto : nothing
|
||||
end
|
||||
|
||||
# # update the axis colors
|
||||
color_or_nothing!(axis.d, :foreground_color_axis)
|
||||
color_or_nothing!(axis.d, :foreground_color_border)
|
||||
color_or_nothing!(axis.d, :foreground_color_guide)
|
||||
color_or_nothing!(axis.d, :foreground_color_text)
|
||||
|
||||
# handle linking here. if we're passed a list of
|
||||
# other subplots to link to, link them together
|
||||
link = axis[:link]
|
||||
if !isempty(link)
|
||||
for other_sp in link
|
||||
other_sp = get_subplot(plt, other_sp)
|
||||
link_axes!(axis, get_axis(other_sp, letter))
|
||||
end
|
||||
axis.d[:link] = []
|
||||
end
|
||||
_update_axis(plt, sp, d_in, letter, subplot_index)
|
||||
end
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function has_black_border_for_default(st::Symbol)
|
||||
like_histogram(st) || st in (:hexbin, :bar)
|
||||
like_histogram(st) || st in (:hexbin, :bar, :shape)
|
||||
end
|
||||
|
||||
|
||||
# converts a symbol or string into a colorant (Colors.RGB), and assigns a color automatically
|
||||
function getSeriesRGBColor(c, α, sp::Subplot, n::Int)
|
||||
if c == :auto
|
||||
c = autopick(sp[:color_palette], n)
|
||||
end
|
||||
plot_color(c, α)
|
||||
end
|
||||
|
||||
function ensure_gradient!(d::KW, csym::Symbol, asym::Symbol)
|
||||
if !isa(d[csym], ColorGradient)
|
||||
d[csym] = cgrad(alpha = d[asym])
|
||||
end
|
||||
end
|
||||
|
||||
function _replace_linewidth(d::KW)
|
||||
# get a good default linewidth... 0 for surface and heatmaps
|
||||
if get(d, :linewidth, :auto) == :auto
|
||||
d[:linewidth] = (get(d, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
pkg = plt.backend
|
||||
globalIndex = d[:series_plotindex]
|
||||
|
||||
# add default values to our dictionary, being careful not to delete what we just added!
|
||||
for (k,v) in _series_defaults
|
||||
slice_arg!(d, d, k, v, commandIndex, false)
|
||||
end
|
||||
|
||||
# this is how many series belong to this subplot
|
||||
plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
|
||||
if get(d, :primary, true)
|
||||
plotIndex += 1
|
||||
end
|
||||
|
||||
aliasesAndAutopick(d, :linestyle, _styleAliases, supported_styles(pkg), plotIndex)
|
||||
aliasesAndAutopick(d, :markershape, _markerAliases, supported_markers(pkg), plotIndex)
|
||||
|
||||
# update alphas
|
||||
for asym in (:linealpha, :markeralpha, :fillalpha)
|
||||
if d[asym] == nothing
|
||||
d[asym] = d[:seriesalpha]
|
||||
end
|
||||
end
|
||||
if d[:markerstrokealpha] == nothing
|
||||
d[:markerstrokealpha] = d[:markeralpha]
|
||||
end
|
||||
|
||||
# update series color
|
||||
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], d[:seriesalpha], sp, plotIndex)
|
||||
|
||||
# update other colors
|
||||
for s in (:line, :marker, :fill)
|
||||
csym, asym = Symbol(s,:color), Symbol(s,:alpha)
|
||||
d[csym] = if d[csym] == :match
|
||||
plot_color(if has_black_border_for_default(d[:seriestype]) && s == :line
|
||||
sp[:foreground_color_subplot]
|
||||
else
|
||||
d[:seriescolor]
|
||||
end, d[asym])
|
||||
else
|
||||
getSeriesRGBColor(d[csym], d[asym], sp, plotIndex)
|
||||
end
|
||||
end
|
||||
|
||||
# update markerstrokecolor
|
||||
d[:markerstrokecolor] = if d[:markerstrokecolor] == :match
|
||||
plot_color(sp[:foreground_color_subplot], d[:markerstrokealpha])
|
||||
else
|
||||
getSeriesRGBColor(d[:markerstrokecolor], d[:markerstrokealpha], sp, plotIndex)
|
||||
end
|
||||
|
||||
# if marker_z or line_z are set, ensure we have a gradient
|
||||
if d[:marker_z] != nothing
|
||||
ensure_gradient!(d, :markercolor, :markeralpha)
|
||||
end
|
||||
if d[:line_z] != nothing
|
||||
ensure_gradient!(d, :linecolor, :linealpha)
|
||||
end
|
||||
|
||||
# scatter plots don't have a line, but must have a shape
|
||||
if d[:seriestype] in (:scatter, :scatter3d)
|
||||
d[:linewidth] = 0
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
end
|
||||
end
|
||||
|
||||
# set label
|
||||
label = d[:label]
|
||||
label = (label == "AUTO" ? "y$globalIndex" : label)
|
||||
d[:label] = label
|
||||
|
||||
_replace_linewidth(d)
|
||||
d
|
||||
end
|
||||
|
||||
|
||||
+141
-7
@@ -38,7 +38,7 @@ function get_axis(sp::Subplot, letter::Symbol)
|
||||
sp.attr[axissym]
|
||||
else
|
||||
sp.attr[axissym] = Axis(sp, letter)
|
||||
end
|
||||
end::Axis
|
||||
end
|
||||
|
||||
function process_axis_arg!(d::KW, arg, letter = "")
|
||||
@@ -117,15 +117,97 @@ Base.setindex!(axis::Axis, v, ks::Symbol...) = setindex!(axis.d, v, ks...)
|
||||
Base.haskey(axis::Axis, k::Symbol) = haskey(axis.d, k)
|
||||
Base.extrema(axis::Axis) = (ex = axis[:extrema]; (ex.emin, ex.emax))
|
||||
|
||||
# get discrete ticks, or not
|
||||
function get_ticks(axis::Axis)
|
||||
ticks = axis[:ticks]
|
||||
dvals = axis[:discrete_values]
|
||||
if !isempty(dvals) && ticks == :auto
|
||||
axis[:continuous_values], dvals
|
||||
|
||||
const _scale_funcs = Dict{Symbol,Function}(
|
||||
:log10 => log10,
|
||||
:log2 => log2,
|
||||
:ln => log,
|
||||
)
|
||||
const _inv_scale_funcs = Dict{Symbol,Function}(
|
||||
:log10 => exp10,
|
||||
:log2 => exp2,
|
||||
:ln => exp,
|
||||
)
|
||||
|
||||
const _label_func = Dict{Symbol,Function}(
|
||||
:log10 => x -> "10^$x",
|
||||
:log2 => x -> "2^$x",
|
||||
:ln => x -> "e^$x",
|
||||
)
|
||||
|
||||
|
||||
scalefunc(scale::Symbol) = x -> get(_scale_funcs, scale, identity)(Float64(x))
|
||||
invscalefunc(scale::Symbol) = x -> get(_inv_scale_funcs, scale, identity)(Float64(x))
|
||||
labelfunc(scale::Symbol, backend::AbstractBackend) = get(_label_func, scale, string)
|
||||
|
||||
function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
lims = axis_limits(axis)
|
||||
|
||||
# scale the limits
|
||||
scale = axis[:scale]
|
||||
scaled_lims = map(scalefunc(scale), lims)
|
||||
# @show lims scaled_lims
|
||||
|
||||
# get a list of well-laid-out ticks
|
||||
cv = if ticks == nothing
|
||||
optimize_ticks(scaled_lims...,
|
||||
k_min = 5, # minimum number of ticks
|
||||
k_max = 8, # maximum number of ticks
|
||||
# span_buffer = 0.0 # padding buffer in case nice ticks are closeby
|
||||
)[1]
|
||||
else
|
||||
ticks
|
||||
end
|
||||
|
||||
# # expand to ensure we see all the ticks
|
||||
# expand_extrema!(axis, cv)
|
||||
|
||||
# rescale and return values and labels
|
||||
# @show cv
|
||||
ticklabels = if any(isfinite, cv)
|
||||
map(labelfunc(scale, backend()), Showoff.showoff(cv, :plain))
|
||||
else
|
||||
UTF8String[]
|
||||
end
|
||||
|
||||
tickvals = map(invscalefunc(scale), cv)
|
||||
# @show tickvals ticklabels
|
||||
# ticklabels = Showoff.showoff(tickvals, scale == :log10 ? :scientific : :auto)
|
||||
tickvals, ticklabels
|
||||
# basestr = scale == :log10 ? "10^" : scale == :log2 ? "2^" : scale == :ln ? "e^" : ""
|
||||
# tickvals, ["$basestr$cvi" for cvi in cv]
|
||||
end
|
||||
|
||||
# return (continuous_values, discrete_values) for the ticks on this axis
|
||||
function get_ticks(axis::Axis)
|
||||
ticks = axis[:ticks]
|
||||
ticks in (nothing, false) && return nothing
|
||||
|
||||
dvals = axis[:discrete_values]
|
||||
cv, dv = if !isempty(dvals) && ticks == :auto
|
||||
# discrete ticks...
|
||||
axis[:continuous_values], dvals
|
||||
elseif ticks == :auto
|
||||
# compute optimal ticks and labels
|
||||
optimal_ticks_and_labels(axis)
|
||||
elseif typeof(ticks) <: AVec
|
||||
# override ticks, but get the labels
|
||||
optimal_ticks_and_labels(axis, ticks)
|
||||
elseif typeof(ticks) <: NTuple{2}
|
||||
# assuming we're passed (ticks, labels)
|
||||
ticks
|
||||
else
|
||||
error("Unknown ticks type in get_ticks: $(typeof(ticks))")
|
||||
end
|
||||
# @show ticks dvals cv dv
|
||||
|
||||
# TODO: better/smarter cutoff values for sampling ticks
|
||||
if length(cv) > 30
|
||||
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
|
||||
cv[rng], dv[rng]
|
||||
else
|
||||
cv, dv
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
@@ -139,6 +221,12 @@ end
|
||||
function expand_extrema!(axis::Axis, v::Number)
|
||||
expand_extrema!(axis[:extrema], v)
|
||||
end
|
||||
|
||||
# these shouldn't impact the extrema
|
||||
expand_extrema!(axis::Axis, ::Void) = axis[:extrema]
|
||||
expand_extrema!(axis::Axis, ::Bool) = axis[:extrema]
|
||||
|
||||
|
||||
function expand_extrema!{MIN<:Number,MAX<:Number}(axis::Axis, v::Tuple{MIN,MAX})
|
||||
ex = axis[:extrema]
|
||||
ex.emin = min(v[1], ex.emin)
|
||||
@@ -332,3 +420,49 @@ function pie_labels(sp::Subplot, series::Series)
|
||||
d[:x]
|
||||
end
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
|
||||
# compute the line segments which should be drawn for this axis
|
||||
function axis_drawing_info(sp::Subplot)
|
||||
xaxis, yaxis = sp[:xaxis], sp[:yaxis]
|
||||
xmin, xmax = axis_limits(xaxis)
|
||||
ymin, ymax = axis_limits(yaxis)
|
||||
xticks = get_ticks(xaxis)
|
||||
yticks = get_ticks(yaxis)
|
||||
spine_segs = Segments(2)
|
||||
grid_segs = Segments(2)
|
||||
|
||||
if !(xaxis[:ticks] in (nothing, false))
|
||||
f = scalefunc(yaxis[:scale])
|
||||
invf = invscalefunc(yaxis[:scale])
|
||||
t1 = invf(f(ymin) + 0.015*(f(ymax)-f(ymin)))
|
||||
t2 = invf(f(ymax) - 0.015*(f(ymax)-f(ymin)))
|
||||
|
||||
push!(spine_segs, (xmin,ymin), (xmax,ymin)) # bottom spine
|
||||
push!(spine_segs, (xmin,ymax), (xmax,ymax)) # top spine
|
||||
for xtick in xticks[1]
|
||||
push!(spine_segs, (xtick, ymin), (xtick, t1)) # bottom tick
|
||||
push!(grid_segs, (xtick, t1), (xtick, t2)) # vertical grid
|
||||
push!(spine_segs, (xtick, ymax), (xtick, t2)) # top tick
|
||||
end
|
||||
end
|
||||
|
||||
if !(yaxis[:ticks] in (nothing, false))
|
||||
f = scalefunc(xaxis[:scale])
|
||||
invf = invscalefunc(xaxis[:scale])
|
||||
t1 = invf(f(xmin) + 0.015*(f(xmax)-f(xmin)))
|
||||
t2 = invf(f(xmax) - 0.015*(f(xmax)-f(xmin)))
|
||||
|
||||
push!(spine_segs, (xmin,ymin), (xmin,ymax)) # left spine
|
||||
push!(spine_segs, (xmax,ymin), (xmax,ymax)) # right spine
|
||||
for ytick in yticks[1]
|
||||
push!(spine_segs, (xmin, ytick), (t1, ytick)) # left tick
|
||||
push!(grid_segs, (t1, ytick), (t2, ytick)) # horizontal grid
|
||||
push!(spine_segs, (xmax, ytick), (t2, ytick)) # right tick
|
||||
end
|
||||
end
|
||||
|
||||
xticks, yticks, spine_segs, grid_segs
|
||||
end
|
||||
|
||||
|
||||
+17
-4
@@ -57,13 +57,26 @@ _before_layout_calcs(plt::Plot) = nothing
|
||||
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
|
||||
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
|
||||
|
||||
# TODO: this should account for both tick font and the size/length/rotation of tick labels
|
||||
function tick_padding(axis::Axis)
|
||||
ptsz = axis[:tickfont].pointsize * pt
|
||||
if axis[:ticks] in (nothing,false)
|
||||
0mm
|
||||
elseif axis[:letter] == :x
|
||||
2mm + ptsz
|
||||
else
|
||||
8mm
|
||||
end
|
||||
end
|
||||
|
||||
# Set the (left, top, right, bottom) minimum padding around the plot area
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot)
|
||||
leftpad = 10mm + sp[:left_margin] + guide_padding(sp[:yaxis])
|
||||
toppad = 2mm + sp[:top_margin] + title_padding(sp)
|
||||
rightpad = 3mm + sp[:right_margin]
|
||||
bottompad = 5mm + sp[:bottom_margin] + guide_padding(sp[:xaxis])
|
||||
# TODO: something different when `is3d(sp) == true`
|
||||
leftpad = tick_padding(sp[:yaxis]) + sp[:left_margin] + guide_padding(sp[:yaxis])
|
||||
toppad = sp[:top_margin] + title_padding(sp)
|
||||
rightpad = sp[:right_margin]
|
||||
bottompad = tick_padding(sp[:xaxis]) + sp[:bottom_margin] + guide_padding(sp[:xaxis])
|
||||
# @show (leftpad, toppad, rightpad, bottompad)
|
||||
sp.minpad = (leftpad, toppad, rightpad, bottompad)
|
||||
end
|
||||
|
||||
+34
-13
@@ -29,7 +29,7 @@ supported_args(::GLVisualizeBackend) = merge_with_base_supported([
|
||||
# :clims,
|
||||
# :inset_subplots,
|
||||
])
|
||||
supported_types(::GLVisualizeBackend) = [:surface, :scatter, :scatter3d, :path, :path3d]
|
||||
supported_types(::GLVisualizeBackend) = [:surface, :scatter, :scatter3d, :path, :path3d, :shape]
|
||||
supported_styles(::GLVisualizeBackend) = [:auto, :solid]
|
||||
supported_markers(::GLVisualizeBackend) = vcat([:none, :auto, :circle], collect(keys(_gl_marker_map)))
|
||||
supported_scales(::GLVisualizeBackend) = [:identity]
|
||||
@@ -44,8 +44,8 @@ function _initialize_backend(::GLVisualizeBackend; kw...)
|
||||
import GeometryTypes: Point2f0, Point3f0, Vec2f0, Vec3f0
|
||||
export GLVisualize
|
||||
|
||||
# TODO: remove this when PlotUtils is registered
|
||||
import PlotUtils
|
||||
# # TODO: remove this when PlotUtils is registered
|
||||
# import PlotUtils
|
||||
end
|
||||
end
|
||||
|
||||
@@ -101,8 +101,10 @@ gl_color(c::RGBA{Float32}) = c
|
||||
|
||||
# convert to RGBA
|
||||
function gl_color(c, a=nothing)
|
||||
@show c, a
|
||||
c = convertColor(c, a)
|
||||
RGBA{Float32}(getColor(c))
|
||||
@show c
|
||||
RGBA{Float32}(c)
|
||||
end
|
||||
|
||||
function gl_viewport(bb, rect)
|
||||
@@ -125,11 +127,13 @@ function gl_draw_lines_2d(x, y, color, linewidth, sp_screen)
|
||||
for rng in iter_segments(x, y)
|
||||
n = length(rng)
|
||||
n < 2 && continue
|
||||
pts = gl_make_points(x[rng], y[rng])
|
||||
@show pts, n
|
||||
viz = GLVisualize.visualize(
|
||||
gl_make_points(x[rng], y[rng]),
|
||||
pts,
|
||||
n==2 ? :linesegment : :lines,
|
||||
color=color,
|
||||
thickness = Float32(linewidth)
|
||||
color = color,
|
||||
thickness = thickness
|
||||
)
|
||||
GLVisualize.view(viz, sp_screen, camera=:orthographic_pixel)
|
||||
end
|
||||
@@ -141,11 +145,12 @@ function gl_draw_lines_3d(x, y, z, color, linewidth, sp_screen)
|
||||
for rng in iter_segments(x, y, z)
|
||||
n = length(rng)
|
||||
n < 2 && continue
|
||||
pts = gl_make_points(x[rng], y[rng], z[rng])
|
||||
viz = GLVisualize.visualize(
|
||||
gl_make_points(x[rng], y[rng], z[rng]),
|
||||
pts,
|
||||
n==2 ? :linesegment : :lines,
|
||||
color=color,
|
||||
thickness = Float32(linewidth)
|
||||
thickness = thickness
|
||||
)
|
||||
GLVisualize.view(viz, sp_screen, camera=:perspective)
|
||||
end
|
||||
@@ -206,7 +211,7 @@ function gl_display(plt::Plot{GLVisualizeBackend})
|
||||
|
||||
sp.o = sp_screen
|
||||
if !is3d(sp)
|
||||
gl_draw_axes_2d(sp)
|
||||
# gl_draw_axes_2d(sp)
|
||||
end
|
||||
|
||||
# loop over the series and add them to the subplot
|
||||
@@ -216,16 +221,16 @@ function gl_display(plt::Plot{GLVisualizeBackend})
|
||||
x, y = map(Float32, d[:x]), map(Float32, d[:y])
|
||||
msize = gl_relative_size(plt, d[:markersize])
|
||||
|
||||
viz = if st == :surface
|
||||
if st == :surface
|
||||
# TODO: can pass just the ranges and surface
|
||||
ismatrix(x) || (x = repmat(x', length(y), 1))
|
||||
ismatrix(y) || (y = repmat(y, 1, length(x)))
|
||||
z = transpose_z(d, map(Float32, d[:z].surf), false)
|
||||
viz = GLVisualize.visualize((x, y, z), :surface)
|
||||
GLVisualize.view(viz, sp_screen, camera = :perspective)
|
||||
GLVisualize.view(viz, sp_screen, camera = camera)
|
||||
|
||||
else
|
||||
# paths and scatters
|
||||
# paths, scatters, and shape
|
||||
|
||||
_3d && (z = map(Float32, d[:z]))
|
||||
|
||||
@@ -274,6 +279,22 @@ function gl_display(plt::Plot{GLVisualizeBackend})
|
||||
# billboard=true
|
||||
#))
|
||||
end
|
||||
|
||||
if st == :shape
|
||||
for rng in iter_segments(x, y)
|
||||
pts = Point2f0[Point2f0(x[i], y[i]) for i in rng]
|
||||
@show pts
|
||||
mesh = GeometryTypes.GLNormalMesh(pts)
|
||||
@show mesh
|
||||
if !isempty(GeometryTypes.faces(mesh))
|
||||
viz = GLVisualize.visualize(
|
||||
mesh,
|
||||
color = gl_color(d[:fillcolor], d[:fillalpha])
|
||||
)
|
||||
GLVisualize.view(viz, sp_screen, camera = camera)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
GLAbstraction.center!(sp_screen, camera)
|
||||
|
||||
+162
-104
@@ -28,6 +28,7 @@ supported_args(::GRBackend) = merge_with_base_supported([
|
||||
:aspect_ratio,
|
||||
:normalize, :weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
])
|
||||
supported_types(::GRBackend) = [
|
||||
:path, :scatter,
|
||||
@@ -106,17 +107,15 @@ const gr_font_family = Dict(
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function gr_getcolorind(v, a =nothing)
|
||||
c = getColor(v)
|
||||
idx = convert(Int, GR.inqcolorfromrgb(c.r, c.g, c.b))
|
||||
GR.settransparency(float(a==nothing ? alpha(c) : a))
|
||||
idx
|
||||
function gr_getcolorind(c)
|
||||
GR.settransparency(float(alpha(c)))
|
||||
convert(Int, GR.inqcolorfromrgb(red(c), green(c), blue(c)))
|
||||
end
|
||||
|
||||
gr_set_linecolor(c, a=nothing) = GR.setlinecolorind(gr_getcolorind(c, a))
|
||||
gr_set_fillcolor(c, a=nothing) = GR.setfillcolorind(gr_getcolorind(c, a))
|
||||
gr_set_markercolor(c, a=nothing) = GR.setmarkercolorind(gr_getcolorind(c, a))
|
||||
gr_set_textcolor(c, a=nothing) = GR.settextcolorind(gr_getcolorind(c, a))
|
||||
gr_set_linecolor(c) = GR.setlinecolorind(gr_getcolorind(cycle(c,1)))
|
||||
gr_set_fillcolor(c) = GR.setfillcolorind(gr_getcolorind(cycle(c,1)))
|
||||
gr_set_markercolor(c) = GR.setmarkercolorind(gr_getcolorind(cycle(c,1)))
|
||||
gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(cycle(c,1)))
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
@@ -272,8 +271,8 @@ end
|
||||
|
||||
|
||||
# draw the markers, one at a time
|
||||
function gr_draw_markers(d::KW, x, y, msize, mz)
|
||||
shape = d[:markershape]
|
||||
function gr_draw_markers(series::Series, x, y, msize, mz)
|
||||
shape = series[:markershape]
|
||||
if shape != :none
|
||||
for i=1:length(x)
|
||||
msi = cycle(msize, i)
|
||||
@@ -281,16 +280,19 @@ function gr_draw_markers(d::KW, x, y, msize, mz)
|
||||
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
|
||||
|
||||
# draw a filled in shape, slightly bigger, to estimate a stroke
|
||||
cfunc(d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
gr_draw_marker(x[i], y[i], msi*1.2, shape, )
|
||||
if series[:markerstrokewidth] > 0
|
||||
cfunc(cycle(series[:markerstrokecolor], i)) #, series[:markerstrokealpha])
|
||||
gr_draw_marker(x[i], y[i], msi + series[:markerstrokewidth], shape)
|
||||
end
|
||||
|
||||
# draw the shape
|
||||
if mz == nothing
|
||||
cfunc(d[:markercolor], d[:markeralpha])
|
||||
cfunc(cycle(series[:markercolor], i)) #, series[:markeralpha])
|
||||
else
|
||||
# pick a color from the pre-loaded gradient
|
||||
ci = round(Int, 1000 + cycle(mz, i) * 255)
|
||||
cfuncind(ci)
|
||||
GR.settransparency(_gr_gradient_alpha[ci-999])
|
||||
end
|
||||
gr_draw_marker(x[i], y[i], msi, shape)
|
||||
end
|
||||
@@ -299,27 +301,26 @@ end
|
||||
|
||||
function gr_draw_markers(series::Series, x, y)
|
||||
isempty(x) && return
|
||||
d = series.d
|
||||
mz = normalize_zvals(d[:marker_z])
|
||||
mz = normalize_zvals(series[:marker_z])
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
gr_draw_markers(d, x, y, d[:markersize], mz)
|
||||
gr_draw_markers(series, x, y, series[:markersize], mz)
|
||||
if mz != nothing
|
||||
gr_colorbar(d[:subplot])
|
||||
gr_colorbar(series[:subplot])
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function gr_set_line(w, style, c, a)
|
||||
function gr_set_line(w, style, c) #, a)
|
||||
GR.setlinetype(gr_linetype[style])
|
||||
GR.setlinewidth(w)
|
||||
gr_set_linecolor(c, a)
|
||||
gr_set_linecolor(c) #, a)
|
||||
end
|
||||
|
||||
|
||||
|
||||
function gr_set_fill(c, a)
|
||||
gr_set_fillcolor(c, a)
|
||||
function gr_set_fill(c) #, a)
|
||||
gr_set_fillcolor(c) #, a)
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
end
|
||||
|
||||
@@ -327,15 +328,15 @@ end
|
||||
const _gr_point_mult = zeros(1)
|
||||
|
||||
# set the font attributes... assumes _gr_point_mult has been populated already
|
||||
function gr_set_font(f::Font)
|
||||
function gr_set_font(f::Font; halign = f.halign, valign = f.valign, color = f.color)
|
||||
family = lowercase(f.family)
|
||||
GR.setcharheight(_gr_point_mult[1] * f.pointsize)
|
||||
GR.setcharup(sin(f.rotation), cos(f.rotation))
|
||||
if haskey(gr_font_family, family)
|
||||
GR.settextfontprec(100 + gr_font_family[family], GR.TEXT_PRECISION_STRING)
|
||||
end
|
||||
gr_set_textcolor(f.color)
|
||||
GR.settextalign(gr_halign[f.halign], gr_valign[f.valign])
|
||||
gr_set_textcolor(color)
|
||||
GR.settextalign(gr_halign[halign], gr_valign[valign])
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
@@ -402,13 +403,14 @@ gr_view_ydiff() = viewport_plotarea[4] - viewport_plotarea[3]
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
const _gr_gradient_alpha = ones(256)
|
||||
|
||||
function gr_set_gradient(c, a)
|
||||
grad = isa(c, ColorGradient) ? c : default_gradient()
|
||||
grad = ColorGradient(grad, alpha=a)
|
||||
function gr_set_gradient(c)
|
||||
grad = isa(c, ColorGradient) ? c : cgrad()
|
||||
for (i,z) in enumerate(linspace(0, 1, 256))
|
||||
c = getColorZ(grad, z)
|
||||
c = grad[z]
|
||||
GR.setcolorrep(999+i, red(c), green(c), blue(c))
|
||||
_gr_gradient_alpha[i] = alpha(c)
|
||||
end
|
||||
grad
|
||||
end
|
||||
@@ -446,7 +448,7 @@ function gr_display(plt::Plot)
|
||||
|
||||
# update point mult
|
||||
px_per_pt = px / pt
|
||||
_gr_point_mult[1] = px_per_pt / h
|
||||
_gr_point_mult[1] = 1.5 * px_per_pt / max(h,w)
|
||||
|
||||
# subplots:
|
||||
for sp in plt.subplots
|
||||
@@ -463,7 +465,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
viewport_plotarea[:] = gr_viewport_from_bbox(plotarea(sp), w, h, viewport_canvas)
|
||||
|
||||
# fill in the plot area background
|
||||
bg = getColor(sp[:background_color_inside])
|
||||
bg = plot_color(sp[:background_color_inside])
|
||||
gr_fill_viewport(viewport_plotarea, bg)
|
||||
|
||||
# reduced from before... set some flags based on the series in this subplot
|
||||
@@ -473,8 +475,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
draw_axes = true
|
||||
# axes_2d = true
|
||||
for series in series_list(sp)
|
||||
st = series.d[:seriestype]
|
||||
if st in (:contour, :surface, :heatmap) || series.d[:marker_z] != nothing
|
||||
st = series[:seriestype]
|
||||
if st in (:contour, :surface, :heatmap) || series[:marker_z] != nothing
|
||||
cmap = true
|
||||
end
|
||||
if st == :pie
|
||||
@@ -559,26 +561,61 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
gr_polaraxes(rmin, rmax)
|
||||
|
||||
elseif draw_axes
|
||||
if xmax > xmin && ymax > ymin
|
||||
GR.setwindow(xmin, xmax, ymin, ymax)
|
||||
end
|
||||
|
||||
xticks, yticks, spine_segs, grid_segs = axis_drawing_info(sp)
|
||||
# @show xticks yticks #spine_segs grid_segs
|
||||
|
||||
# draw the grid lines
|
||||
# TODO: control line style/width
|
||||
# GR.setlinetype(GR.LINETYPE_DOTTED)
|
||||
if sp[:grid]
|
||||
gr_set_linecolor(sp[:foreground_color_grid])
|
||||
GR.grid(xtick, ytick, 0, 0, majorx, majory)
|
||||
# gr_set_linecolor(sp[:foreground_color_grid])
|
||||
# GR.grid(xtick, ytick, 0, 0, majorx, majory)
|
||||
gr_set_line(1, :dot, sp[:foreground_color_grid])
|
||||
GR.settransparency(0.5)
|
||||
gr_polyline(coords(grid_segs)...)
|
||||
end
|
||||
GR.settransparency(1.0)
|
||||
|
||||
# spine (border) and tick marks
|
||||
gr_set_line(1, :solid, sp[:xaxis][:foreground_color_axis])
|
||||
gr_polyline(coords(spine_segs)...)
|
||||
|
||||
if !(xticks in (nothing, false))
|
||||
# x labels
|
||||
flip = sp[:yaxis][:flip]
|
||||
gr_set_font(sp[:xaxis][:tickfont], valign = :top, color = sp[:xaxis][:foreground_color_axis])
|
||||
for (cv, dv) in zip(xticks...)
|
||||
xi, yi = GR.wctondc(cv, flip ? ymax : ymin)
|
||||
# @show cv dv ymin xi yi
|
||||
gr_text(xi, yi-0.01, string(dv))
|
||||
end
|
||||
end
|
||||
|
||||
window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
|
||||
ticksize = 0.0075 * window_diag
|
||||
if outside_ticks
|
||||
ticksize = -ticksize
|
||||
if !(yticks in (nothing, false))
|
||||
# y labels
|
||||
flip = sp[:xaxis][:flip]
|
||||
gr_set_font(sp[:yaxis][:tickfont], halign = :right, color = sp[:yaxis][:foreground_color_axis])
|
||||
for (cv, dv) in zip(yticks...)
|
||||
xi, yi = GR.wctondc(flip ? xmax : xmin, cv)
|
||||
# @show cv dv xmin xi yi
|
||||
gr_text(xi-0.01, yi, string(dv))
|
||||
end
|
||||
end
|
||||
# TODO: this should be done for each axis separately
|
||||
gr_set_linecolor(xaxis[:foreground_color_axis])
|
||||
|
||||
x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
|
||||
y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
|
||||
GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
|
||||
GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
|
||||
# window_diag = sqrt(gr_view_xdiff()^2 + gr_view_ydiff()^2)
|
||||
# ticksize = 0.0075 * window_diag
|
||||
# if outside_ticks
|
||||
# ticksize = -ticksize
|
||||
# end
|
||||
# # TODO: this should be done for each axis separately
|
||||
# gr_set_linecolor(xaxis[:foreground_color_axis])
|
||||
|
||||
# x1, x2 = xaxis[:flip] ? (xmax,xmin) : (xmin,xmax)
|
||||
# y1, y2 = yaxis[:flip] ? (ymax,ymin) : (ymin,ymax)
|
||||
# GR.axes(xtick, ytick, x1, y1, 1, 1, ticksize)
|
||||
# GR.axes(xtick, ytick, x2, y2, -1, -1, -ticksize)
|
||||
end
|
||||
# end
|
||||
|
||||
@@ -612,14 +649,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.setcolormap(1000 + GR.COLORMAP_COOLWARM)
|
||||
|
||||
for (idx, series) in enumerate(series_list(sp))
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
st = series[:seriestype]
|
||||
|
||||
# update the current stored gradient
|
||||
if st in (:contour, :surface, :wireframe, :heatmap)
|
||||
gr_set_gradient(d[:fillcolor], d[:fillalpha])
|
||||
elseif d[:marker_z] != nothing
|
||||
d[:markercolor] = gr_set_gradient(d[:markercolor], d[:markeralpha])
|
||||
gr_set_gradient(series[:fillcolor]) #, series[:fillalpha])
|
||||
elseif series[:marker_z] != nothing
|
||||
series[:markercolor] = gr_set_gradient(series[:markercolor])
|
||||
end
|
||||
|
||||
GR.savestate()
|
||||
@@ -634,12 +670,21 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
end
|
||||
|
||||
x, y, z = d[:x], d[:y], d[:z]
|
||||
frng = d[:fillrange]
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
frng = series[:fillrange]
|
||||
|
||||
# recompute data
|
||||
if st in (:contour, :surface, :wireframe)
|
||||
z = vec(transpose_z(d, z.surf, false))
|
||||
if typeof(z) <: Surface
|
||||
if st == :heatmap #&& size(z.surf) == (length(y), length(x))
|
||||
expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
|
||||
expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
|
||||
# # coords are centers... turn into edges
|
||||
# xd = diff(x)
|
||||
# x = vcat(x[1]-0.5xd[1], x[1]+xd, x[end]+0.5xd[end])
|
||||
# yd = diff(y)
|
||||
# y = vcat(y[1]-0.5yd[1], y[1]+yd, y[end]+0.5yd[end])
|
||||
end
|
||||
z = vec(transpose_z(series, z.surf, false))
|
||||
elseif ispolar(sp)
|
||||
if frng != nothing
|
||||
_, frng = convert_to_polar(x, frng, (rmin, rmax))
|
||||
@@ -652,7 +697,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# do area fill
|
||||
if frng != nothing
|
||||
gr_set_fillcolor(d[:fillcolor], d[:fillalpha])
|
||||
gr_set_fillcolor(series[:fillcolor]) #, series[:fillalpha])
|
||||
GR.setfillintstyle(GR.INTSTYLE_SOLID)
|
||||
frng = isa(frng, Number) ? Float64[frng] : frng
|
||||
nx, ny, nf = length(x), length(y), length(frng)
|
||||
@@ -668,24 +713,24 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# draw the line(s)
|
||||
if st == :path
|
||||
gr_set_line(d[:linewidth], d[:linestyle], d[:linecolor], d[:linealpha])
|
||||
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
|
||||
gr_polyline(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
if d[:markershape] != :none
|
||||
if series[:markershape] != :none
|
||||
gr_draw_markers(series, x, y)
|
||||
end
|
||||
|
||||
elseif st == :contour
|
||||
zmin, zmax = gr_lims(zaxis, false)
|
||||
if typeof(d[:levels]) <: Array
|
||||
h = d[:levels]
|
||||
if typeof(series[:levels]) <: Array
|
||||
h = series[:levels]
|
||||
else
|
||||
h = linspace(zmin, zmax, d[:levels])
|
||||
h = linspace(zmin, zmax, series[:levels])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
if d[:fillrange] != nothing
|
||||
if series[:fillrange] != nothing
|
||||
GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
|
||||
else
|
||||
GR.contour(x, y, h, z, 1000)
|
||||
@@ -704,7 +749,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st in [:surface, :wireframe]
|
||||
if st == :surface
|
||||
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
|
||||
if length(x) == length(y) == length(z)
|
||||
GR.trisurface(x, y, z)
|
||||
else
|
||||
GR.gr3.surface(x, y, z, GR.OPTION_COLORED_MESH)
|
||||
end
|
||||
else
|
||||
GR.setfillcolorind(0)
|
||||
GR.surface(x, y, z, GR.OPTION_FILLED_MESH)
|
||||
@@ -712,23 +761,24 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
cmap && gr_colorbar(sp)
|
||||
|
||||
elseif st == :heatmap
|
||||
z = vec(transpose_z(d, z.surf, false))
|
||||
# z = vec(transpose_z(series, z.surf, false))
|
||||
zmin, zmax = gr_lims(zaxis, true)
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
|
||||
# GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
|
||||
GR.surface(x, y, z, GR.OPTION_HEATMAP)
|
||||
cmap && gr_colorbar(sp)
|
||||
|
||||
elseif st in (:path3d, :scatter3d)
|
||||
# draw path
|
||||
if st == :path3d
|
||||
if length(x) > 1
|
||||
gr_set_line(d[:linewidth], d[:linestyle], d[:linecolor], d[:linealpha])
|
||||
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
|
||||
GR.polyline3d(x, y, z)
|
||||
end
|
||||
end
|
||||
|
||||
# draw markers
|
||||
if st == :scatter3d || d[:markershape] != :none
|
||||
if st == :scatter3d || series[:markershape] != :none
|
||||
x2, y2 = unzip(map(GR.wc3towc, x, y, z))
|
||||
gr_draw_markers(series, x2, y2)
|
||||
end
|
||||
@@ -749,7 +799,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
ymin, ymax = ycenter - r, ycenter + r
|
||||
end
|
||||
labels = pie_labels(sp, series)
|
||||
slices = d[:y]
|
||||
slices = series[:y]
|
||||
numslices = length(slices)
|
||||
total = sum(slices)
|
||||
a1 = 0
|
||||
@@ -782,17 +832,27 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.selntran(1)
|
||||
|
||||
elseif st == :shape
|
||||
# draw the interior
|
||||
gr_set_fill(d[:fillcolor], d[:fillalpha])
|
||||
gr_polyline(d[:x], d[:y], GR.fillarea)
|
||||
for (i,rng) in enumerate(iter_segments(series[:x], series[:y]))
|
||||
if length(rng) > 1
|
||||
# connect to the beginning
|
||||
rng = vcat(rng, rng[1])
|
||||
|
||||
# draw the shapes
|
||||
gr_set_line(d[:linewidth], :solid, d[:linecolor], d[:linealpha])
|
||||
gr_polyline(d[:x], d[:y])
|
||||
# get the segments
|
||||
x, y = series[:x][rng], series[:y][rng]
|
||||
|
||||
# draw the interior
|
||||
gr_set_fill(cycle(series[:fillcolor], i))
|
||||
GR.fillarea(x, y)
|
||||
|
||||
# draw the shapes
|
||||
gr_set_line(series[:linewidth], :solid, cycle(series[:linecolor], i))
|
||||
GR.polyline(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
elseif st == :image
|
||||
img = d[:z].surf
|
||||
img = series[:z].surf
|
||||
w, h = size(img)
|
||||
if eltype(img) <: Colors.AbstractGray
|
||||
grey = round(UInt8, float(img) * 255)
|
||||
@@ -822,11 +882,11 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
for series in series_list(sp)
|
||||
should_add_to_legend(series) || continue
|
||||
n += 1
|
||||
if typeof(series.d[:label]) <: Array
|
||||
if typeof(series[:label]) <: Array
|
||||
i += 1
|
||||
lab = series.d[:label][i]
|
||||
lab = series[:label][i]
|
||||
else
|
||||
lab = series.d[:label]
|
||||
lab = series[:label]
|
||||
end
|
||||
tbx, tby = gr_inqtext(0, 0, lab)
|
||||
w = max(w, tbx[3] - tbx[1])
|
||||
@@ -844,13 +904,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
i = 0
|
||||
for series in series_list(sp)
|
||||
should_add_to_legend(series) || continue
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
gr_set_line(d[:linewidth], d[:linestyle], d[:linecolor], d[:linealpha])
|
||||
st = series[:seriestype]
|
||||
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
|
||||
if st == :path
|
||||
GR.polyline([xpos - 0.07, xpos - 0.01], [ypos, ypos])
|
||||
elseif st == :shape
|
||||
gr_set_fill(d[:fillcolor], d[:fillalpha])
|
||||
gr_set_fill(series[:fillcolor]) #, series[:fillalpha])
|
||||
l, r = xpos-0.07, xpos-0.01
|
||||
b, t = ypos-0.4dy, ypos+0.4dy
|
||||
x = [l, r, r, l, l]
|
||||
@@ -859,28 +918,15 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
gr_polyline(x, y)
|
||||
end
|
||||
|
||||
gr_draw_markers(d, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
|
||||
# shape = d[:markershape]
|
||||
# if shape != :none #st == :scatter || d[:markershape] != :none
|
||||
# msize = 10
|
||||
# for xoff in [0.06,0.02]
|
||||
# gr_set_markercolor(d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
# gr_draw_marker(xpos-xoff, ypos, msize*1.1, shape)
|
||||
# gr_set_markercolor(d[:markercolor], d[:markeralpha])
|
||||
# gr_draw_marker(xpos-xoff, ypos, msize, shape)
|
||||
# end
|
||||
# # gr_setmarkershape(d)
|
||||
# # if st == :path
|
||||
# # gr_polymarker(d, [xpos - 0.06, xpos - 0.02], [ypos, ypos])
|
||||
# # else
|
||||
# # gr_polymarker(d, [xpos - 0.06, xpos - 0.04, xpos - 0.02], [ypos, ypos, ypos])
|
||||
# # end
|
||||
# end
|
||||
if typeof(d[:label]) <: Array
|
||||
if series[:markershape] != :none
|
||||
gr_draw_markers(series, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
|
||||
end
|
||||
|
||||
if typeof(series[:label]) <: Array
|
||||
i += 1
|
||||
lab = d[:label][i]
|
||||
lab = series[:label][i]
|
||||
else
|
||||
lab = d[:label]
|
||||
lab = series[:label]
|
||||
end
|
||||
GR.settextalign(GR.TEXT_HALIGN_LEFT, GR.TEXT_VALIGN_HALF)
|
||||
gr_set_textcolor(sp[:foreground_color_legend])
|
||||
@@ -930,5 +976,17 @@ for (mime, fmt) in _gr_mimeformats
|
||||
end
|
||||
|
||||
function _display(plt::Plot{GRBackend})
|
||||
gr_display(plt)
|
||||
if plt[:display_type] == :inline
|
||||
GR.emergencyclosegks()
|
||||
filepath = tempname() * ".pdf"
|
||||
ENV["GKS_WSTYPE"] = "pdf"
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
GR.emergencyclosegks()
|
||||
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(readbytes, filepath)), "\a")
|
||||
println(content)
|
||||
rm(filepath)
|
||||
else
|
||||
gr_display(plt)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -94,20 +94,18 @@ const _pgf_series_extrastyle = KW(
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
# takes in color,alpha, and returns color and alpha appropriate for pgf style
|
||||
function pgf_color(c, a = nothing)
|
||||
c = getColor(c)
|
||||
function pgf_color(c)
|
||||
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c))
|
||||
a = float(a == nothing ? alpha(c) : a)
|
||||
cstr, a
|
||||
cstr, alpha(c)
|
||||
end
|
||||
|
||||
function pgf_fillstyle(d::KW)
|
||||
cstr,a = pgf_color(d[:fillcolor], d[:fillalpha])
|
||||
cstr,a = pgf_color(d[:fillcolor])
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
end
|
||||
|
||||
function pgf_linestyle(d::KW)
|
||||
cstr,a = pgf_color(d[:linecolor], d[:linealpha])
|
||||
cstr,a = pgf_color(d[:linecolor])
|
||||
"""
|
||||
color = $cstr,
|
||||
draw opacity=$a,
|
||||
@@ -117,8 +115,8 @@ end
|
||||
|
||||
function pgf_marker(d::KW)
|
||||
shape = d[:markershape]
|
||||
cstr, a = pgf_color(d[:markercolor], d[:markeralpha])
|
||||
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
cstr, a = pgf_color(d[:markercolor])
|
||||
cstr_stroke, a_stroke = pgf_color(d[:markerstrokecolor])
|
||||
"""
|
||||
mark = $(get(_pgfplots_markers, shape, "*")),
|
||||
mark size = $(0.5 * d[:markersize]),
|
||||
@@ -214,6 +212,11 @@ function pgf_axis(sp::Subplot, letter)
|
||||
scale == :ln || push!(style, "log basis $letter=$(scale == :log2 ? 2 : 10)")
|
||||
end
|
||||
|
||||
# ticks on or off
|
||||
if axis[:ticks] in (nothing, false)
|
||||
push!(style, "$(letter)majorticks=false")
|
||||
end
|
||||
|
||||
# limits
|
||||
# TODO: support zlims
|
||||
if letter != :z
|
||||
@@ -241,6 +244,9 @@ function _make_pgf_plot!(plt::Plot)
|
||||
if letter != :z || is3d(sp)
|
||||
axisstyle, axiskw = pgf_axis(sp, letter)
|
||||
merge!(kw, axiskw)
|
||||
for sty in axisstyle
|
||||
push!(style, sty)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+115
-95
@@ -88,7 +88,7 @@ function plotly_font(font::Font, color = font.color)
|
||||
KW(
|
||||
:family => font.family,
|
||||
:size => round(Int, font.pointsize*1.4),
|
||||
:color => webcolor(color),
|
||||
:color => rgba_string(color),
|
||||
)
|
||||
end
|
||||
|
||||
@@ -126,7 +126,7 @@ end
|
||||
# # :ay => -40,
|
||||
# :ax => 10xdiff / dist,
|
||||
# :ay => -10ydiff / dist,
|
||||
# :arrowcolor => webcolor(d[:linecolor], d[:linealpha]),
|
||||
# :arrowcolor => rgba_string(d[:linecolor]),
|
||||
# :xref => "x",
|
||||
# :yref => "y",
|
||||
# :arrowsize => 10a.headwidth,
|
||||
@@ -175,8 +175,8 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
||||
ax[:titlefont] = plotly_font(axis[:guidefont], axis[:foreground_color_guide])
|
||||
ax[:type] = plotly_scale(axis[:scale])
|
||||
ax[:tickfont] = plotly_font(axis[:tickfont], axis[:foreground_color_text])
|
||||
ax[:tickcolor] = webcolor(axis[:foreground_color_border])
|
||||
ax[:linecolor] = webcolor(axis[:foreground_color_border])
|
||||
ax[:tickcolor] = rgba_string(axis[:foreground_color_border])
|
||||
ax[:linecolor] = rgba_string(axis[:foreground_color_border])
|
||||
|
||||
# lims
|
||||
lims = axis[:lims]
|
||||
@@ -190,7 +190,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
||||
end
|
||||
|
||||
# ticks
|
||||
ticks = axis[:ticks]
|
||||
ticks = get_ticks(axis)
|
||||
if ticks != :auto
|
||||
ttype = ticksType(ticks)
|
||||
if ttype == :ticks
|
||||
@@ -214,7 +214,7 @@ function plotly_layout(plt::Plot)
|
||||
|
||||
w, h = plt[:size]
|
||||
d_out[:width], d_out[:height] = w, h
|
||||
d_out[:paper_bgcolor] = webcolor(plt[:background_color_outside])
|
||||
d_out[:paper_bgcolor] = rgba_string(plt[:background_color_outside])
|
||||
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>20)
|
||||
|
||||
d_out[:annotations] = KW[]
|
||||
@@ -239,7 +239,7 @@ function plotly_layout(plt::Plot)
|
||||
push!(d_out[:annotations], plotly_annotation_dict(titlex, titley, text(sp[:title], titlefont)))
|
||||
end
|
||||
|
||||
d_out[:plot_bgcolor] = webcolor(sp[:background_color_inside])
|
||||
d_out[:plot_bgcolor] = rgba_string(sp[:background_color_inside])
|
||||
|
||||
# TODO: x/y axis tick values/labels
|
||||
|
||||
@@ -259,8 +259,8 @@ function plotly_layout(plt::Plot)
|
||||
d_out[:showlegend] = sp[:legend] != :none
|
||||
if sp[:legend] != :none
|
||||
d_out[:legend] = KW(
|
||||
:bgcolor => webcolor(sp[:background_color_legend]),
|
||||
:bordercolor => webcolor(sp[:foreground_color_legend]),
|
||||
:bgcolor => rgba_string(sp[:background_color_legend]),
|
||||
:bordercolor => rgba_string(sp[:foreground_color_legend]),
|
||||
:font => plotly_font(sp[:legendfont], sp[:foreground_color_legend]),
|
||||
)
|
||||
end
|
||||
@@ -298,10 +298,11 @@ function plotly_layout_json(plt::Plot)
|
||||
end
|
||||
|
||||
|
||||
function plotly_colorscale(grad::ColorGradient, alpha = nothing)
|
||||
[[grad.values[i], webcolor(grad.colors[i], alpha)] for i in 1:length(grad.colors)]
|
||||
function plotly_colorscale(grad::ColorGradient, α)
|
||||
[[grad.values[i], rgb_string(grad.colors[i])] for i in 1:length(grad.colors)]
|
||||
end
|
||||
plotly_colorscale(c, alpha = nothing) = plotly_colorscale(default_gradient(), alpha)
|
||||
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
|
||||
# plotly_colorscale(c, alpha = nothing) = plotly_colorscale(cgrad(), alpha)
|
||||
|
||||
const _plotly_markers = KW(
|
||||
:rect => "square",
|
||||
@@ -330,10 +331,17 @@ function plotly_close_shapes(x, y)
|
||||
nanvcat(xs), nanvcat(ys)
|
||||
end
|
||||
|
||||
plotly_data(v) = collect(v)
|
||||
plotly_data{R<:Rational}(v::AbstractArray{R}) = float(v)
|
||||
|
||||
# get a dictionary representing the series params (d is the Plots-dict, d_out is the Plotly-dict)
|
||||
function plotly_series(plt::Plot, series::Series)
|
||||
d = series.d
|
||||
sp = d[:subplot]
|
||||
st = series[:seriestype]
|
||||
if st == :shape
|
||||
return plotly_series_shapes(plt, series)
|
||||
end
|
||||
|
||||
sp = series[:subplot]
|
||||
d_out = KW()
|
||||
|
||||
# these are the axes that the series should be mapped to
|
||||
@@ -342,12 +350,11 @@ function plotly_series(plt::Plot, series::Series)
|
||||
d_out[:yaxis] = "y$spidx"
|
||||
d_out[:showlegend] = should_add_to_legend(series)
|
||||
|
||||
x, y = collect(d[:x]), collect(d[:y])
|
||||
d_out[:name] = d[:label]
|
||||
st = d[:seriestype]
|
||||
x, y = plotly_data(series[:x]), plotly_data(series[:y])
|
||||
d_out[:name] = series[:label]
|
||||
|
||||
isscatter = st in (:scatter, :scatter3d, :scattergl)
|
||||
hasmarker = isscatter || d[:markershape] != :none
|
||||
# hasline = !isscatter
|
||||
hasmarker = isscatter || series[:markershape] != :none
|
||||
hasline = st in (:path, :path3d)
|
||||
|
||||
# set the "type"
|
||||
@@ -358,76 +365,36 @@ function plotly_series(plt::Plot, series::Series)
|
||||
else
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
if d[:fillrange] == true || d[:fillrange] == 0
|
||||
if series[:fillrange] == true || series[:fillrange] == 0
|
||||
d_out[:fill] = "tozeroy"
|
||||
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
|
||||
elseif !(d[:fillrange] in (false, nothing))
|
||||
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(d[:fillrange])")
|
||||
d_out[:fillcolor] = rgba_string(series[:fillcolor])
|
||||
elseif !(series[:fillrange] in (false, nothing))
|
||||
warn("fillrange ignored... plotly only supports filling to zero. fillrange: $(series[:fillrange])")
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
|
||||
elseif st == :shape
|
||||
# to draw polygons, we actually draw lines with fill
|
||||
d_out[:type] = "scatter"
|
||||
d_out[:mode] = "lines"
|
||||
d_out[:x], d_out[:y] = plotly_close_shapes(x, y)
|
||||
# @show map(length, (x,y,d_out[:x],d_out[:y]))
|
||||
# @show d_out[:x] d_out[:y]
|
||||
d_out[:fill] = "tozeroy"
|
||||
d_out[:fillcolor] = webcolor(d[:fillcolor], d[:fillalpha])
|
||||
if d[:markerstrokewidth] > 0
|
||||
d_out[:line] = KW(
|
||||
:color => webcolor(d[:linecolor], d[:linealpha]),
|
||||
:width => d[:linewidth],
|
||||
:dash => string(d[:linestyle]),
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
|
||||
elseif st == :bar
|
||||
d_out[:type] = "bar"
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:orientation] = isvertical(d) ? "v" : "h"
|
||||
|
||||
# elseif st == :histogram2d
|
||||
# d_out[:type] = "histogram2d"
|
||||
# d_out[:x], d_out[:y] = x, y
|
||||
# if isa(d[:bins], Tuple)
|
||||
# xbins, ybins = d[:bins]
|
||||
# else
|
||||
# xbins = ybins = d[:bins]
|
||||
# end
|
||||
# d_out[:nbinsx] = xbins
|
||||
# d_out[:nbinsy] = ybins
|
||||
# d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
|
||||
# elseif st in (:histogram, :density)
|
||||
# d_out[:type] = "histogram"
|
||||
# isvert = isvertical(d)
|
||||
# d_out[isvert ? :x : :y] = y
|
||||
# d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
|
||||
# if st == :density
|
||||
# d_out[:histogramnorm] = "probability density"
|
||||
# end
|
||||
d_out[:orientation] = isvertical(series) ? "v" : "h"
|
||||
|
||||
elseif st == :heatmap
|
||||
d_out[:type] = "heatmap"
|
||||
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
|
||||
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
|
||||
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
|
||||
elseif st == :contour
|
||||
d_out[:type] = "contour"
|
||||
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
|
||||
# d_out[:showscale] = d[:colorbar] != :none
|
||||
d_out[:ncontours] = d[:levels]
|
||||
d_out[:contours] = KW(:coloring => d[:fillrange] != nothing ? "fill" : "lines")
|
||||
d_out[:colorscale] = plotly_colorscale(d[:linecolor], d[:linealpha])
|
||||
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
|
||||
# d_out[:showscale] = series[:colorbar] != :none
|
||||
d_out[:ncontours] = series[:levels]
|
||||
d_out[:contours] = KW(:coloring => series[:fillrange] != nothing ? "fill" : "lines")
|
||||
d_out[:colorscale] = plotly_colorscale(series[:linecolor], series[:linealpha])
|
||||
|
||||
elseif st in (:surface, :wireframe)
|
||||
d_out[:type] = "surface"
|
||||
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
|
||||
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
|
||||
d_out[:x], d_out[:y], d_out[:z] = series[:x], series[:y], transpose_z(series, series[:z].surf, false)
|
||||
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
|
||||
elseif st == :pie
|
||||
d_out[:type] = "pie"
|
||||
@@ -443,7 +410,7 @@ function plotly_series(plt::Plot, series::Series)
|
||||
hasline ? "lines" : "none"
|
||||
end
|
||||
d_out[:x], d_out[:y] = x, y
|
||||
d_out[:z] = collect(d[:z])
|
||||
d_out[:z] = plotly_data(series[:z])
|
||||
|
||||
else
|
||||
warn("Plotly: seriestype $st isn't supported.")
|
||||
@@ -453,32 +420,32 @@ function plotly_series(plt::Plot, series::Series)
|
||||
# add "marker"
|
||||
if hasmarker
|
||||
d_out[:marker] = KW(
|
||||
:symbol => get(_plotly_markers, d[:markershape], string(d[:markershape])),
|
||||
:opacity => d[:markeralpha],
|
||||
:size => 2 * d[:markersize],
|
||||
:color => webcolor(d[:markercolor], d[:markeralpha]),
|
||||
:symbol => get(_plotly_markers, series[:markershape], string(series[:markershape])),
|
||||
# :opacity => series[:markeralpha],
|
||||
:size => 2 * series[:markersize],
|
||||
# :color => rgba_string(series[:markercolor]),
|
||||
:line => KW(
|
||||
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
|
||||
:width => d[:markerstrokewidth],
|
||||
:color => rgba_string(series[:markerstrokecolor]),
|
||||
:width => series[:markerstrokewidth],
|
||||
),
|
||||
)
|
||||
|
||||
# gotta hack this (for now?) since plotly can't handle rgba values inside the gradient
|
||||
if d[:marker_z] != nothing
|
||||
# d_out[:marker][:color] = d[:marker_z]
|
||||
# d_out[:marker][:colorscale] = plotly_colorscale(d[:markercolor], d[:markeralpha])
|
||||
# d_out[:showscale] = true
|
||||
grad = ColorGradient(d[:markercolor], alpha=d[:markeralpha])
|
||||
zmin, zmax = extrema(d[:marker_z])
|
||||
d_out[:marker][:color] = [webcolor(getColorZ(grad, (zi - zmin) / (zmax - zmin))) for zi in d[:marker_z]]
|
||||
d_out[:marker][:color] = if series[:marker_z] == nothing
|
||||
rgba_string(series[:markercolor])
|
||||
else
|
||||
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
|
||||
grad = series[:markercolor]
|
||||
zmin, zmax = extrema(series[:marker_z])
|
||||
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]]
|
||||
end
|
||||
end
|
||||
|
||||
# add "line"
|
||||
if hasline
|
||||
d_out[:line] = KW(
|
||||
:color => webcolor(d[:linecolor], d[:linealpha]),
|
||||
:width => d[:linewidth],
|
||||
:color => rgba_string(series[:linecolor]),
|
||||
:width => series[:linewidth],
|
||||
:shape => if st == :steppre
|
||||
"vh"
|
||||
elseif st == :steppost
|
||||
@@ -486,32 +453,85 @@ function plotly_series(plt::Plot, series::Series)
|
||||
else
|
||||
"linear"
|
||||
end,
|
||||
:dash => string(d[:linestyle]),
|
||||
:dash => string(series[:linestyle]),
|
||||
# :dash => "solid",
|
||||
)
|
||||
end
|
||||
|
||||
plotly_polar!(d_out, series)
|
||||
plotly_hover!(d_out, series[:hover])
|
||||
|
||||
[d_out]
|
||||
end
|
||||
|
||||
function plotly_series_shapes(plt::Plot, series::Series)
|
||||
d_outs = []
|
||||
|
||||
# TODO: create a d_out for each polygon
|
||||
# x, y = series[:x], series[:y]
|
||||
|
||||
# these are the axes that the series should be mapped to
|
||||
spidx = plotly_subplot_index(series[:subplot])
|
||||
base_d = KW()
|
||||
base_d[:xaxis] = "x$spidx"
|
||||
base_d[:yaxis] = "y$spidx"
|
||||
base_d[:name] = series[:label]
|
||||
# base_d[:legendgroup] = series[:label]
|
||||
|
||||
x, y = plotly_data(series[:x]), plotly_data(series[:y])
|
||||
for (i,rng) in enumerate(iter_segments(x,y))
|
||||
length(rng) < 2 && continue
|
||||
|
||||
# to draw polygons, we actually draw lines with fill
|
||||
d_out = merge(base_d, KW(
|
||||
:type => "scatter",
|
||||
:mode => "lines",
|
||||
:x => vcat(x[rng], x[rng[1]]),
|
||||
:y => vcat(y[rng], y[rng[1]]),
|
||||
:fill => "tozeroy",
|
||||
:fillcolor => rgba_string(cycle(series[:fillcolor], i)),
|
||||
))
|
||||
if series[:markerstrokewidth] > 0
|
||||
d_out[:line] = KW(
|
||||
:color => rgba_string(cycle(series[:linecolor], i)),
|
||||
:width => series[:linewidth],
|
||||
:dash => string(series[:linestyle]),
|
||||
)
|
||||
end
|
||||
d_out[:showlegend] = i==1 ? should_add_to_legend(series) : false
|
||||
plotly_polar!(d_out, series)
|
||||
plotly_hover!(d_out, cycle(series[:hover], i))
|
||||
push!(d_outs, d_out)
|
||||
end
|
||||
d_outs
|
||||
end
|
||||
|
||||
function plotly_polar!(d_out::KW, series::Series)
|
||||
# convert polar plots x/y to theta/radius
|
||||
if ispolar(d[:subplot])
|
||||
if ispolar(series[:subplot])
|
||||
d_out[:t] = rad2deg(pop!(d_out, :x))
|
||||
d_out[:r] = pop!(d_out, :y)
|
||||
end
|
||||
end
|
||||
|
||||
function plotly_hover!(d_out::KW, hover)
|
||||
# hover text
|
||||
hover = d[:hover]
|
||||
if hover in (:none, false)
|
||||
d_out[:hoverinfo] = "none"
|
||||
elseif hover != nothing
|
||||
d_out[:hoverinfo] = "text"
|
||||
d_out[:text] = hover
|
||||
end
|
||||
|
||||
d_out
|
||||
end
|
||||
|
||||
# get a list of dictionaries, each representing the series params
|
||||
function plotly_series_json(plt::Plot)
|
||||
JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
|
||||
slist = []
|
||||
for series in plt.series_list
|
||||
append!(slist, plotly_series(plt, series))
|
||||
end
|
||||
JSON.json(slist)
|
||||
# JSON.json(map(series -> plotly_series(plt, series), plt.series_list))
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
@@ -33,10 +33,12 @@ end
|
||||
|
||||
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
syncplot = plt.o
|
||||
pdict = plotly_series(plt, series)
|
||||
typ = pop!(pdict, :type)
|
||||
gt = PlotlyJS.GenericTrace(typ; pdict...)
|
||||
PlotlyJS.addtraces!(syncplot, gt)
|
||||
pdicts = plotly_series(plt, series)
|
||||
for pdict in pdicts
|
||||
typ = pop!(pdict, :type)
|
||||
gt = PlotlyJS.GenericTrace(typ; pdict...)
|
||||
PlotlyJS.addtraces!(syncplot, gt)
|
||||
end
|
||||
end
|
||||
|
||||
function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
|
||||
+226
-318
@@ -50,10 +50,13 @@ is_subplot_supported(::PyPlotBackend) = true
|
||||
|
||||
function _initialize_backend(::PyPlotBackend)
|
||||
@eval begin
|
||||
# see: https://github.com/tbreloff/Plots.jl/issues/308
|
||||
ENV["OVERRIDE_PYPLOT_DISPLAY"] = true
|
||||
|
||||
# problem: https://github.com/tbreloff/Plots.jl/issues/308
|
||||
# solution: hack from @stevengj: https://github.com/stevengj/PyPlot.jl/pull/223#issuecomment-229747768
|
||||
otherdisplays = splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays))
|
||||
import PyPlot
|
||||
import LaTeXStrings: latexstring
|
||||
append!(Base.Multimedia.displays, otherdisplays)
|
||||
|
||||
export PyPlot
|
||||
const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
|
||||
const pypath = PyPlot.pywrap(PyPlot.pyimport("matplotlib.path"))
|
||||
@@ -76,26 +79,37 @@ end
|
||||
# --------------------------------------------------------------------------------------
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
# convert colorant to 4-tuple RGBA
|
||||
py_color(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
|
||||
py_color(cvec::ColorVector, α=nothing) = map(py_color, convertColor(cvec, α).v)
|
||||
py_color(grad::ColorGradient, α=nothing) = map(c -> py_color(c, α), grad.colors)
|
||||
py_color(scheme::ColorScheme, α=nothing) = py_color(convertColor(getColor(scheme), α))
|
||||
py_color(vec::AVec, α=nothing) = map(c->py_color(c,α), vec)
|
||||
py_color(c, α=nothing) = py_color(convertColor(c, α))
|
||||
# # convert colorant to 4-tuple RGBA
|
||||
# py_color(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
|
||||
# py_color(cvec::ColorVector, α=nothing) = map(py_color, convertColor(cvec, α).v)
|
||||
# py_color(grad::ColorGradient, α=nothing) = map(c -> py_color(c, α), grad.colors)
|
||||
# py_color(scheme::ColorScheme, α=nothing) = py_color(convertColor(getColor(scheme), α))
|
||||
# py_color(vec::AVec, α=nothing) = map(c->py_color(c,α), vec)
|
||||
# py_color(c, α=nothing) = py_color(convertColor(c, α))
|
||||
|
||||
function py_colormap(c::ColorGradient, α=nothing)
|
||||
pyvals = [(v, py_color(getColorZ(c, v), α)) for v in c.values]
|
||||
pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
|
||||
# function py_colormap(c::ColorGradient, α=nothing)
|
||||
# pyvals = [(v, py_color(getColorZ(c, v), α)) for v in c.values]
|
||||
# pycolors.pymember("LinearSegmentedColormap")[:from_list]("tmp", pyvals)
|
||||
# end
|
||||
|
||||
# # convert vectors and ColorVectors to standard ColorGradients
|
||||
# # TODO: move this logic to colors.jl and keep a barebones wrapper for pyplot
|
||||
# py_colormap(cv::ColorVector, α=nothing) = py_colormap(ColorGradient(cv.v), α)
|
||||
# py_colormap(v::AVec, α=nothing) = py_colormap(ColorGradient(v), α)
|
||||
|
||||
# # anything else just gets a bluesred gradient
|
||||
# py_colormap(c, α=nothing) = py_colormap(default_gradient(), α)
|
||||
|
||||
py_color(c::Colorant) = (red(c), green(c), blue(c), alpha(c))
|
||||
py_color(cs::AVec) = map(py_color, cs)
|
||||
py_color(grad::ColorGradient) = py_color(grad.colors)
|
||||
|
||||
function py_colormap(grad::ColorGradient)
|
||||
pyvals = [(z, py_color(grad[z])) for z in grad.values]
|
||||
pycolors.LinearSegmentedColormap[:from_list]("tmp", pyvals)
|
||||
end
|
||||
py_colormap(c) = py_colormap(cgrad())
|
||||
|
||||
# convert vectors and ColorVectors to standard ColorGradients
|
||||
# TODO: move this logic to colors.jl and keep a barebones wrapper for pyplot
|
||||
py_colormap(cv::ColorVector, α=nothing) = py_colormap(ColorGradient(cv.v), α)
|
||||
py_colormap(v::AVec, α=nothing) = py_colormap(ColorGradient(v), α)
|
||||
|
||||
# anything else just gets a bluesred gradient
|
||||
py_colormap(c, α=nothing) = py_colormap(default_gradient(), α)
|
||||
|
||||
function py_shading(c, z, α=nothing)
|
||||
cmap = py_colormap(c, α)
|
||||
@@ -130,27 +144,27 @@ const _path_MOVETO = UInt8(1)
|
||||
const _path_LINETO = UInt8(2)
|
||||
const _path_CLOSEPOLY = UInt8(79)
|
||||
|
||||
# see http://matplotlib.org/users/path_tutorial.html
|
||||
# and http://matplotlib.org/api/path_api.html#matplotlib.path.Path
|
||||
function py_path(x, y)
|
||||
n = length(x)
|
||||
mat = zeros(n+1, 2)
|
||||
codes = zeros(UInt8, n+1)
|
||||
lastnan = true
|
||||
for i=1:n
|
||||
mat[i,1] = x[i]
|
||||
mat[i,2] = y[i]
|
||||
nan = !ok(x[i], y[i])
|
||||
codes[i] = if nan && i>1
|
||||
_path_CLOSEPOLY
|
||||
else
|
||||
lastnan ? _path_MOVETO : _path_LINETO
|
||||
end
|
||||
lastnan = nan
|
||||
end
|
||||
codes[n+1] = _path_CLOSEPOLY
|
||||
pypath.pymember("Path")(mat, codes)
|
||||
end
|
||||
# # see http://matplotlib.org/users/path_tutorial.html
|
||||
# # and http://matplotlib.org/api/path_api.html#matplotlib.path.Path
|
||||
# function py_path(x, y)
|
||||
# n = length(x)
|
||||
# mat = zeros(n+1, 2)
|
||||
# codes = zeros(UInt8, n+1)
|
||||
# lastnan = true
|
||||
# for i=1:n
|
||||
# mat[i,1] = x[i]
|
||||
# mat[i,2] = y[i]
|
||||
# nan = !ok(x[i], y[i])
|
||||
# codes[i] = if nan && i>1
|
||||
# _path_CLOSEPOLY
|
||||
# else
|
||||
# lastnan ? _path_MOVETO : _path_LINETO
|
||||
# end
|
||||
# lastnan = nan
|
||||
# end
|
||||
# codes[n+1] = _path_CLOSEPOLY
|
||||
# pypath.pymember("Path")(mat, codes)
|
||||
# end
|
||||
|
||||
# get the marker shape
|
||||
function py_marker(marker::Symbol)
|
||||
@@ -160,8 +174,8 @@ function py_marker(marker::Symbol)
|
||||
marker == :diamond && return "D"
|
||||
marker == :utriangle && return "^"
|
||||
marker == :dtriangle && return "v"
|
||||
marker == :cross && return "+"
|
||||
marker == :xcross && return "x"
|
||||
# marker == :cross && return "+"
|
||||
# marker == :xcross && return "x"
|
||||
marker == :star5 && return "*"
|
||||
marker == :pentagon && return "p"
|
||||
marker == :hexagon && return "h"
|
||||
@@ -207,29 +221,29 @@ function add_pyfixedformatter(cbar, vals::AVec)
|
||||
cbar[:update_ticks]()
|
||||
end
|
||||
|
||||
# # TODO: smoothing should be moved into the SliceIt method, should not touch backends
|
||||
# function handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Bool)
|
||||
# if smooth
|
||||
# xs, ys = regressionXY(d[:x], d[:y])
|
||||
# ax[:plot](xs, ys,
|
||||
# # linestyle = py_linestyle(:path, :dashdot),
|
||||
# color = py_color(d[:linecolor]),
|
||||
# linewidth = 2
|
||||
# )
|
||||
# end
|
||||
# end
|
||||
# handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Real) = handleSmooth(plt, ax, d, true)
|
||||
|
||||
function labelfunc(scale::Symbol, backend::PyPlotBackend)
|
||||
if scale == :log10
|
||||
x -> latexstring("10^{$x}")
|
||||
elseif scale == :log2
|
||||
x -> latexstring("2^{$x}")
|
||||
elseif scale == :ln
|
||||
x -> latexstring("e^{$x}")
|
||||
else
|
||||
string
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, d::KW)
|
||||
x, y = d[:x], d[:y]
|
||||
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
|
||||
x, y = series[:x], series[:y]
|
||||
nx, ny = length(x), length(y)
|
||||
if !isa(get(d, :z, nothing), Surface) && nx != ny
|
||||
if !isa(get(series.d, :z, nothing), Surface) && nx != ny
|
||||
if nx < ny
|
||||
d[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
|
||||
series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
|
||||
else
|
||||
d[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
|
||||
series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -245,14 +259,14 @@ function py_color_fix(c, x)
|
||||
end
|
||||
end
|
||||
|
||||
py_linecolor(d::KW) = py_color(d[:linecolor], d[:linealpha])
|
||||
py_markercolor(d::KW) = py_color(d[:markercolor], d[:markeralpha])
|
||||
py_markerstrokecolor(d::KW) = py_color(d[:markerstrokecolor], d[:markerstrokealpha])
|
||||
py_fillcolor(d::KW) = py_color(d[:fillcolor], d[:fillalpha])
|
||||
py_linecolor(series::Series) = py_color(series[:linecolor])
|
||||
py_markercolor(series::Series) = py_color(series[:markercolor])
|
||||
py_markerstrokecolor(series::Series) = py_color(series[:markerstrokecolor])
|
||||
py_fillcolor(series::Series) = py_color(series[:fillcolor])
|
||||
|
||||
py_linecolormap(d::KW) = py_colormap(d[:linecolor], d[:linealpha])
|
||||
py_markercolormap(d::KW) = py_colormap(d[:markercolor], d[:markeralpha])
|
||||
py_fillcolormap(d::KW) = py_colormap(d[:fillcolor], d[:fillalpha])
|
||||
py_linecolormap(series::Series) = py_colormap(series[:linecolor])
|
||||
py_markercolormap(series::Series) = py_colormap(series[:markercolor])
|
||||
py_fillcolormap(series::Series) = py_colormap(series[:fillcolor])
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -379,9 +393,9 @@ end
|
||||
# function _series_added(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
d = series.d
|
||||
st = d[:seriestype]
|
||||
sp = d[:subplot]
|
||||
# d = series.d
|
||||
st = series[:seriestype]
|
||||
sp = series[:subplot]
|
||||
ax = sp.o
|
||||
|
||||
if !(st in supported_types(plt.backend))
|
||||
@@ -389,10 +403,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
|
||||
# PyPlot doesn't handle mismatched x/y
|
||||
fix_xy_lengths!(plt, d)
|
||||
fix_xy_lengths!(plt, series)
|
||||
|
||||
# ax = getAxis(plt, series)
|
||||
x, y, z = d[:x], d[:y], d[:z]
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
xyargs = (st in _3dTypes ? (x,y,z) : (x,y))
|
||||
|
||||
# handle zcolor and get c/cmap
|
||||
@@ -405,7 +419,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
|
||||
# pass in an integer value as an arg, but a levels list as a keyword arg
|
||||
levels = d[:levels]
|
||||
levels = series[:levels]
|
||||
levelargs = if isscalar(levels)
|
||||
(levels)
|
||||
elseif isvector(levels)
|
||||
@@ -419,14 +433,14 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
# line plot
|
||||
if st in (:path, :path3d, :steppre, :steppost)
|
||||
if d[:linewidth] > 0
|
||||
if d[:line_z] == nothing
|
||||
if series[:linewidth] > 0
|
||||
if series[:line_z] == nothing
|
||||
handle = ax[:plot](xyargs...;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex],
|
||||
color = py_linecolor(d),
|
||||
linewidth = py_dpi_scale(plt, d[:linewidth]),
|
||||
linestyle = py_linestyle(st, d[:linestyle]),
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
color = py_linecolor(series),
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
linestyle = py_linestyle(st, series[:linestyle]),
|
||||
solid_capstyle = "round",
|
||||
drawstyle = py_stepstyle(st)
|
||||
)[1]
|
||||
@@ -435,28 +449,38 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
else
|
||||
# multicolored line segments
|
||||
n = length(x) - 1
|
||||
segments = Array(Any,n)
|
||||
# segments = Array(Any,n)
|
||||
segments = []
|
||||
kw = KW(
|
||||
:label => d[:label],
|
||||
:label => series[:label],
|
||||
:zorder => plt.n,
|
||||
:cmap => py_linecolormap(d),
|
||||
:linewidth => py_dpi_scale(plt, d[:linewidth]),
|
||||
:linestyle => py_linestyle(st, d[:linestyle])
|
||||
:cmap => py_linecolormap(series),
|
||||
:linewidth => py_dpi_scale(plt, series[:linewidth]),
|
||||
:linestyle => py_linestyle(st, series[:linestyle])
|
||||
)
|
||||
lz = collect(series[:line_z])
|
||||
handle = if is3d(st)
|
||||
for i=1:n
|
||||
segments[i] = [(cycle(x,i), cycle(y,i), cycle(z,i)), (cycle(x,i+1), cycle(y,i+1), cycle(z,i+1))]
|
||||
for rng in iter_segments(x, y, z)
|
||||
length(rng) < 2 && continue
|
||||
push!(segments, [(cycle(x,i),cycle(y,i),cycle(z,i)) for i in rng])
|
||||
end
|
||||
# for i=1:n
|
||||
# segments[i] = [(cycle(x,i), cycle(y,i), cycle(z,i)), (cycle(x,i+1), cycle(y,i+1), cycle(z,i+1))]
|
||||
# end
|
||||
lc = pyart3d.Line3DCollection(segments; kw...)
|
||||
lc[:set_array](d[:line_z])
|
||||
lc[:set_array](lz)
|
||||
ax[:add_collection3d](lc, zs=z) #, zdir='y')
|
||||
lc
|
||||
else
|
||||
for i=1:n
|
||||
segments[i] = [(cycle(x,i), cycle(y,i)), (cycle(x,i+1), cycle(y,i+1))]
|
||||
for rng in iter_segments(x, y)
|
||||
length(rng) < 2 && continue
|
||||
push!(segments, [(cycle(x,i),cycle(y,i)) for i in rng])
|
||||
end
|
||||
# for i=1:n
|
||||
# segments[i] = [(cycle(x,i), cycle(y,i)), (cycle(x,i+1), cycle(y,i+1))]
|
||||
# end
|
||||
lc = pycollections.LineCollection(segments; kw...)
|
||||
lc[:set_array](d[:line_z])
|
||||
lc[:set_array](lz)
|
||||
ax[:add_collection](lc)
|
||||
lc
|
||||
end
|
||||
@@ -464,7 +488,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
needs_colorbar = true
|
||||
end
|
||||
|
||||
a = d[:arrow]
|
||||
a = series[:arrow]
|
||||
if a != nothing && !is3d(st) # TODO: handle 3d later
|
||||
if typeof(a) != Arrow
|
||||
warn("Unexpected type for arrow: $(typeof(a))")
|
||||
@@ -473,10 +497,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
:arrowstyle => "simple,head_length=$(a.headlength),head_width=$(a.headwidth)",
|
||||
:shrinkA => 0,
|
||||
:shrinkB => 0,
|
||||
:edgecolor => py_linecolor(d),
|
||||
:facecolor => py_linecolor(d),
|
||||
:linewidth => py_dpi_scale(plt, d[:linewidth]),
|
||||
:linestyle => py_linestyle(st, d[:linestyle]),
|
||||
:edgecolor => py_linecolor(series),
|
||||
:facecolor => py_linecolor(series),
|
||||
:linewidth => py_dpi_scale(plt, series[:linewidth]),
|
||||
:linestyle => py_linestyle(st, series[:linestyle]),
|
||||
)
|
||||
add_arrows(x, y) do xyprev, xy
|
||||
ax[:annotate]("",
|
||||
@@ -491,53 +515,16 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
end
|
||||
|
||||
# if st == :bar
|
||||
# bw = d[:bar_width]
|
||||
# if bw == nothing
|
||||
# bw = mean(diff(isvertical(d) ? x : y))
|
||||
# end
|
||||
# extrakw[isvertical(d) ? :width : :height] = bw
|
||||
# fr = get(d, :fillrange, nothing)
|
||||
# if fr != nothing
|
||||
# extrakw[:bottom] = fr
|
||||
# d[:fillrange] = nothing
|
||||
# end
|
||||
# handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||||
# label = d[:label],
|
||||
# zorder = d[:series_plotindex],
|
||||
# color = py_fillcolor(d),
|
||||
# edgecolor = py_linecolor(d),
|
||||
# linewidth = d[:linewidth],
|
||||
# align = d[:bar_edges] ? "edge" : "center",
|
||||
# extrakw...
|
||||
# )[1]
|
||||
# push!(handles, handle)
|
||||
# end
|
||||
|
||||
# if st == :sticks
|
||||
# extrakw[isvertical(d) ? :width : :height] = 0.0
|
||||
# handle = ax[isvertical(d) ? :bar : :barh](x, y;
|
||||
# label = d[:label],
|
||||
# zorder = d[:series_plotindex],
|
||||
# color = py_linecolor(d),
|
||||
# edgecolor = py_linecolor(d),
|
||||
# linewidth = d[:linewidth],
|
||||
# align = "center",
|
||||
# extrakw...
|
||||
# )[1]
|
||||
# push!(handles, handle)
|
||||
# end
|
||||
|
||||
# add markers?
|
||||
if d[:markershape] != :none && st in (:path, :scatter, :path3d,
|
||||
if series[:markershape] != :none && st in (:path, :scatter, :path3d,
|
||||
:scatter3d, :steppre, :steppost,
|
||||
:bar)
|
||||
extrakw = KW()
|
||||
if d[:marker_z] == nothing
|
||||
extrakw[:c] = py_color_fix(py_markercolor(d), x)
|
||||
if series[:marker_z] == nothing
|
||||
extrakw[:c] = py_color_fix(py_markercolor(series), x)
|
||||
else
|
||||
extrakw[:c] = convert(Vector{Float64}, d[:marker_z])
|
||||
extrakw[:cmap] = py_markercolormap(d)
|
||||
extrakw[:c] = convert(Vector{Float64}, series[:marker_z])
|
||||
extrakw[:cmap] = py_markercolormap(series)
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
@@ -545,72 +532,23 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
needs_colorbar = true
|
||||
end
|
||||
xyargs = if st == :bar && !isvertical(d)
|
||||
xyargs = if st == :bar && !isvertical(series)
|
||||
(y, x)
|
||||
else
|
||||
xyargs
|
||||
end
|
||||
handle = ax[:scatter](xyargs...;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex] + 0.5,
|
||||
marker = py_marker(d[:markershape]),
|
||||
s = py_dpi_scale(plt, d[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(d),
|
||||
linewidths = py_dpi_scale(plt, d[:markerstrokewidth]),
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(series[:markershape]),
|
||||
s = py_dpi_scale(plt, series[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(series),
|
||||
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
|
||||
# if st == :histogram
|
||||
# handle = ax[:hist](y;
|
||||
# label = d[:label],
|
||||
# zorder = d[:series_plotindex],
|
||||
# color = py_fillcolor(d),
|
||||
# edgecolor = py_linecolor(d),
|
||||
# linewidth = d[:linewidth],
|
||||
# bins = d[:bins],
|
||||
# normed = d[:normalize],
|
||||
# weights = d[:weights],
|
||||
# orientation = (isvertical(d) ? "vertical" : "horizontal"),
|
||||
# histtype = (d[:bar_position] == :stack ? "barstacked" : "bar")
|
||||
# )[3]
|
||||
# push!(handles, handle)
|
||||
|
||||
# # expand the extrema... handle is a list of Rectangle objects
|
||||
# for rect in handle
|
||||
# xmin, ymin, xmax, ymax = rect[:get_bbox]()[:extents]
|
||||
# expand_extrema!(sp, xmin, xmax, ymin, ymax)
|
||||
# # expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
# # expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
# end
|
||||
# end
|
||||
|
||||
# if st == :histogram2d
|
||||
# clims = sp[:clims]
|
||||
# if is_2tuple(clims)
|
||||
# isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
# isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
# end
|
||||
# handle = ax[:hist2d](x, y;
|
||||
# label = d[:label],
|
||||
# zorder = d[:series_plotindex],
|
||||
# bins = d[:bins],
|
||||
# normed = d[:normalize],
|
||||
# weights = d[:weights],
|
||||
# cmap = py_fillcolormap(d), # applies to the pcolorfast object
|
||||
# extrakw...
|
||||
# )[4]
|
||||
# push!(handles, handle)
|
||||
# needs_colorbar = true
|
||||
|
||||
# # expand the extrema... handle is a AxesImage object
|
||||
# expand_extrema!(sp, handle[:get_extent]()...)
|
||||
# # xmin, xmax, ymin, ymax = handle[:get_extent]()
|
||||
# # expand_extrema!(sp[:xaxis], (xmin, xmax))
|
||||
# # expand_extrema!(sp[:yaxis], (ymin, ymax))
|
||||
# end
|
||||
|
||||
if st == :hexbin
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
@@ -618,33 +556,20 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
handle = ax[:hexbin](x, y;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex],
|
||||
gridsize = d[:bins],
|
||||
linewidths = py_dpi_scale(plt, d[:linewidth]),
|
||||
edgecolors = py_linecolor(d),
|
||||
cmap = py_fillcolormap(d), # applies to the pcolorfast object
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
gridsize = series[:bins],
|
||||
linewidths = py_dpi_scale(plt, series[:linewidth]),
|
||||
edgecolors = py_linecolor(series),
|
||||
cmap = py_fillcolormap(series), # applies to the pcolorfast object
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
needs_colorbar = true
|
||||
end
|
||||
|
||||
# if st in (:hline,:vline)
|
||||
# for yi in d[:y]
|
||||
# func = ax[st == :hline ? :axhline : :axvline]
|
||||
# handle = func(yi;
|
||||
# linewidth=d[:linewidth],
|
||||
# color=py_linecolor(d),
|
||||
# linestyle=py_linestyle(st, d[:linestyle])
|
||||
# )
|
||||
# push!(handles, handle)
|
||||
# end
|
||||
# end
|
||||
|
||||
if st in (:contour, :contour3d)
|
||||
# z = z.surf'
|
||||
z = transpose_z(d, z.surf)
|
||||
z = transpose_z(series, z.surf)
|
||||
needs_colorbar = true
|
||||
|
||||
clims = sp[:clims]
|
||||
@@ -659,21 +584,21 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
# contour lines
|
||||
handle = ax[:contour](x, y, z, levelargs...;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex],
|
||||
linewidths = py_dpi_scale(plt, d[:linewidth]),
|
||||
linestyles = py_linestyle(st, d[:linestyle]),
|
||||
cmap = py_linecolormap(d),
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
linewidths = py_dpi_scale(plt, series[:linewidth]),
|
||||
linestyles = py_linestyle(st, series[:linestyle]),
|
||||
cmap = py_linecolormap(series),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
|
||||
# contour fills
|
||||
if d[:fillrange] != nothing
|
||||
if series[:fillrange] != nothing
|
||||
handle = ax[:contourf](x, y, z, levelargs...;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex] + 0.5,
|
||||
cmap = py_fillcolormap(d),
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
cmap = py_fillcolormap(series),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
@@ -685,13 +610,13 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
x, y, z = map(Array, (x,y,z))
|
||||
if !ismatrix(x) || !ismatrix(y)
|
||||
x = repmat(x', length(y), 1)
|
||||
y = repmat(y, 1, length(d[:x]))
|
||||
y = repmat(y, 1, length(series[:x]))
|
||||
end
|
||||
# z = z'
|
||||
z = transpose_z(d, z)
|
||||
z = transpose_z(series, z)
|
||||
if st == :surface
|
||||
if d[:marker_z] != nothing
|
||||
extrakw[:facecolors] = py_shading(d[:fillcolor], d[:marker_z], d[:fillalpha])
|
||||
if series[:marker_z] != nothing
|
||||
extrakw[:facecolors] = py_shading(series[:fillcolor], series[:marker_z], series[:fillalpha])
|
||||
extrakw[:shade] = false
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
@@ -699,28 +624,28 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
else
|
||||
extrakw[:cmap] = py_fillcolormap(d)
|
||||
extrakw[:cmap] = py_fillcolormap(series)
|
||||
needs_colorbar = true
|
||||
end
|
||||
end
|
||||
handle = ax[st == :surface ? :plot_surface : :plot_wireframe](x, y, z;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex],
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
rstride = 1,
|
||||
cstride = 1,
|
||||
linewidth = py_dpi_scale(plt, d[:linewidth]),
|
||||
edgecolor = py_linecolor(d),
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
edgecolor = py_linecolor(series),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
|
||||
# contours on the axis planes
|
||||
if d[:contours]
|
||||
if series[:contours]
|
||||
for (zdir,mat) in (("x",x), ("y",y), ("z",z))
|
||||
offset = (zdir == "y" ? maximum : minimum)(mat)
|
||||
handle = ax[:contourf](x, y, z, levelargs...;
|
||||
zdir = zdir,
|
||||
cmap = py_fillcolormap(d),
|
||||
cmap = py_fillcolormap(series),
|
||||
offset = (zdir == "y" ? maximum : minimum)(mat) # where to draw the contour plane
|
||||
)
|
||||
push!(handles, handle)
|
||||
@@ -741,11 +666,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
handle = ax[:plot_trisurf](x, y, z;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex],
|
||||
cmap = py_fillcolormap(d),
|
||||
linewidth = py_dpi_scale(plt, d[:linewidth]),
|
||||
edgecolor = py_linecolor(d),
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_fillcolormap(series),
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
edgecolor = py_linecolor(series),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
@@ -757,7 +682,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
|
||||
if st == :image
|
||||
# @show typeof(z)
|
||||
img = Array(transpose_z(d, z.surf))
|
||||
img = Array(transpose_z(series, z.surf))
|
||||
z = if eltype(img) <: Colors.AbstractGray
|
||||
float(img)
|
||||
elseif eltype(img) <: Colorant
|
||||
@@ -766,7 +691,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
z # hopefully it's in a data format that will "just work" with imshow
|
||||
end
|
||||
handle = ax[:imshow](z;
|
||||
zorder = d[:series_plotindex],
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_colormap([:black, :white]),
|
||||
vmin = 0.0,
|
||||
vmax = 1.0
|
||||
@@ -776,11 +701,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# expand extrema... handle is AxesImage object
|
||||
xmin, xmax, ymax, ymin = handle[:get_extent]()
|
||||
expand_extrema!(sp, xmin, xmax, ymin, ymax)
|
||||
# sp[:yaxis].d[:flip] = true
|
||||
# sp[:yaxis].series[:flip] = true
|
||||
end
|
||||
|
||||
if st == :heatmap
|
||||
x, y, z = heatmap_edges(x), heatmap_edges(y), transpose_z(d, z.surf)
|
||||
x, y, z = heatmap_edges(x), heatmap_edges(y), transpose_z(series, z.surf)
|
||||
# if !(eltype(z) <: Number)
|
||||
# z, discrete_colorbar_values = indices_and_unique_values(z)
|
||||
# end
|
||||
@@ -796,10 +721,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
|
||||
handle = ax[:pcolormesh](x, y, z;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex],
|
||||
cmap = py_fillcolormap(d),
|
||||
edgecolors = (d[:linewidth] > 0 ? py_linecolor(d) : "face"),
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
cmap = py_fillcolormap(series),
|
||||
edgecolors = (series[:linewidth] > 0 ? py_linecolor(series) : "face"),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
@@ -817,16 +742,32 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
|
||||
if st == :shape
|
||||
path = py_path(x, y)
|
||||
patches = pypatches.pymember("PathPatch")(path;
|
||||
label = d[:label],
|
||||
zorder = d[:series_plotindex],
|
||||
edgecolor = py_linecolor(d),
|
||||
facecolor = py_fillcolor(d),
|
||||
linewidth = py_dpi_scale(plt, d[:linewidth]),
|
||||
fill = true
|
||||
)
|
||||
handle = ax[:add_patch](patches)
|
||||
handle = []
|
||||
for (i,rng) in enumerate(iter_segments(x, y))
|
||||
if length(rng) > 1
|
||||
path = pypath.pymember("Path")(hcat(x[rng], y[rng]))
|
||||
patches = pypatches.pymember("PathPatch")(
|
||||
path;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex],
|
||||
edgecolor = py_color(cycle(series[:linecolor], i)),
|
||||
facecolor = py_color(cycle(series[:fillcolor], i)),
|
||||
linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
fill = true
|
||||
)
|
||||
push!(handle, ax[:add_patch](patches))
|
||||
end
|
||||
end
|
||||
# path = py_path(x, y)
|
||||
# patches = pypatches.pymember("PathPatch")(path;
|
||||
# label = series[:label],
|
||||
# zorder = series[:series_plotindex],
|
||||
# edgecolor = py_linecolor(series),
|
||||
# facecolor = py_fillcolor(series),
|
||||
# linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
# fill = true
|
||||
# )
|
||||
# handle = ax[:add_patch](patches)
|
||||
push!(handles, handle)
|
||||
end
|
||||
|
||||
@@ -845,10 +786,10 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
expand_extrema!(sp, -lim, lim, -lim, lim)
|
||||
end
|
||||
|
||||
d[:serieshandle] = handles
|
||||
series[:serieshandle] = handles
|
||||
|
||||
# # smoothing
|
||||
# handleSmooth(plt, ax, d, d[:smooth])
|
||||
# handleSmooth(plt, ax, series, series[:smooth])
|
||||
|
||||
# add the colorbar legend
|
||||
if needs_colorbar && sp[:colorbar] != :none
|
||||
@@ -873,55 +814,29 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
end
|
||||
|
||||
# handle area filling
|
||||
fillrange = d[:fillrange]
|
||||
fillrange = series[:fillrange]
|
||||
if fillrange != nothing && st != :contour
|
||||
f, dim1, dim2 = if isvertical(d)
|
||||
f, dim1, dim2 = if isvertical(series)
|
||||
:fill_between, x, y
|
||||
else
|
||||
:fill_betweenx, y, x
|
||||
end
|
||||
n = length(dim1)
|
||||
args = if typeof(fillrange) <: Union{Real, AVec}
|
||||
dim1, fillrange, dim2
|
||||
else
|
||||
dim1, fillrange...
|
||||
dim1, expand_data(fillrange, n), dim2
|
||||
elseif is_2tuple(fillrange)
|
||||
dim1, expand_data(fillrange[1], n), expand_data(fillrange[2], n)
|
||||
end
|
||||
|
||||
handle = ax[f](args...;
|
||||
zorder = d[:series_plotindex],
|
||||
facecolor = py_fillcolor(d),
|
||||
zorder = series[:series_plotindex],
|
||||
facecolor = py_fillcolor(series),
|
||||
linewidths = 0
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
# function update_limits!(sp::Subplot{PyPlotBackend}, series::Series, letters)
|
||||
# for letter in letters
|
||||
# py_set_lims(sp.o, sp[Symbol(letter, :axis)])
|
||||
# end
|
||||
# end
|
||||
|
||||
# function _series_updated(plt::Plot{PyPlotBackend}, series::Series)
|
||||
# d = series.d
|
||||
# for handle in get(d, :serieshandle, [])
|
||||
# if is3d(series)
|
||||
# handle[:set_data](d[:x], d[:y])
|
||||
# handle[:set_3d_properties](d[:z])
|
||||
# else
|
||||
# try
|
||||
# handle[:set_data](d[:x], d[:y])
|
||||
# catch
|
||||
# handle[:set_offsets](hcat(d[:x], d[:y]))
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
# update_limits!(d[:subplot], series, is3d(series) ? (:x,:y,:z) : (:x,:y))
|
||||
# end
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
function py_set_lims(ax, axis::Axis)
|
||||
@@ -933,7 +848,7 @@ end
|
||||
function py_set_ticks(ax, ticks, letter)
|
||||
ticks == :auto && return
|
||||
axis = ax[Symbol(letter,"axis")]
|
||||
if ticks == :none || ticks == nothing
|
||||
if ticks == :none || ticks == nothing || ticks == false
|
||||
kw = KW()
|
||||
for dir in (:top,:bottom,:left,:right)
|
||||
kw[dir] = kw[Symbol(:label,dir)] = "off"
|
||||
@@ -1016,7 +931,6 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
w, h = plt[:size]
|
||||
fig = plt.o
|
||||
fig[:clear]()
|
||||
# fig[:set_size_inches](px2inch(w), px2inch(h), forward = true)
|
||||
dpi = plt[:dpi]
|
||||
fig[:set_size_inches](w/dpi, h/dpi, forward = true)
|
||||
fig[:set_facecolor](py_color(plt[:background_color_outside]))
|
||||
@@ -1114,10 +1028,10 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
# TODO: this should initialize to the margin from sp.attr
|
||||
# figure out how much the axis components and title "stick out" from the plot area
|
||||
# leftpad = toppad = rightpad = bottompad = 1mm
|
||||
leftpad = sp[:left_margin]
|
||||
toppad = sp[:top_margin]
|
||||
rightpad = sp[:right_margin]
|
||||
bottompad = sp[:bottom_margin]
|
||||
leftpad = 0mm
|
||||
toppad = 0mm
|
||||
rightpad = 0mm
|
||||
bottompad = 0mm
|
||||
for bb in (py_bbox_axis(ax, "x"), py_bbox_axis(ax, "y"), py_bbox_title(ax))
|
||||
if ispositive(width(bb)) && ispositive(height(bb))
|
||||
leftpad = max(leftpad, left(plotbb) - left(bb))
|
||||
@@ -1134,6 +1048,12 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
rightpad = rightpad + sp.attr[:cbar_width]
|
||||
end
|
||||
|
||||
# add in the user-specified margin
|
||||
leftpad += sp[:left_margin]
|
||||
toppad += sp[:top_margin]
|
||||
rightpad += sp[:right_margin]
|
||||
bottompad += sp[:bottom_margin]
|
||||
|
||||
sp.minpad = (leftpad, toppad, rightpad, bottompad)
|
||||
end
|
||||
|
||||
@@ -1162,21 +1082,6 @@ end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
# function _remove_axis(plt::Plot{PyPlotBackend}, isx::Bool)
|
||||
# if isx
|
||||
# plot!(plt, xticks=zeros(0), xlabel="")
|
||||
# else
|
||||
# plot!(plt, yticks=zeros(0), ylabel="")
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# function _expand_limits(lims, plt::Plot{PyPlotBackend}, isx::Bool)
|
||||
# pltlims = plt.o.ax[isx ? :get_xbound : :get_ybound]()
|
||||
# _expand_limits(lims, pltlims)
|
||||
# end
|
||||
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
const _pyplot_legend_pos = KW(
|
||||
:right => "right",
|
||||
:left => "center left",
|
||||
@@ -1197,12 +1102,15 @@ function py_add_legend(plt::Plot, sp::Subplot, ax)
|
||||
for series in series_list(sp)
|
||||
if should_add_to_legend(series)
|
||||
# add a line/marker and a label
|
||||
push!(handles, if series.d[:seriestype] == :histogram
|
||||
PyPlot.plt[:Line2D]((0,1),(0,0), color=py_fillcolor(series.d), linewidth=py_dpi_scale(plt, 4))
|
||||
push!(handles, if series[:seriestype] == :shape
|
||||
PyPlot.plt[:Line2D]((0,1),(0,0),
|
||||
color = py_color(cycle(series[:fillcolor],1)),
|
||||
linewidth = py_dpi_scale(plt, 4)
|
||||
)
|
||||
else
|
||||
series.d[:serieshandle][1]
|
||||
series[:serieshandle][1]
|
||||
end)
|
||||
push!(labels, series.d[:label])
|
||||
push!(labels, series[:label])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ end
|
||||
|
||||
|
||||
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
||||
function rebuildUnicodePlot!(plt::Plot)
|
||||
function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||
plt.o = []
|
||||
|
||||
for sp in plt.subplots
|
||||
@@ -57,7 +57,6 @@ function rebuildUnicodePlot!(plt::Plot)
|
||||
y = Float64[ylim[1]]
|
||||
|
||||
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||
width, height = plt[:size]
|
||||
canvas_type = isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
|
||||
o = UnicodePlots.Plot(x, y, canvas_type;
|
||||
width = width,
|
||||
@@ -143,21 +142,22 @@ end
|
||||
|
||||
# we don't do very much for subplots... just stack them vertically
|
||||
|
||||
function _update_plot_object(plt::Plot{UnicodePlotsBackend})
|
||||
w, h = plt[:size]
|
||||
plt.attr[:size] = div(w, 10), div(h, 20)
|
||||
plt.attr[:color_palette] = [RGB(0,0,0)]
|
||||
rebuildUnicodePlot!(plt)
|
||||
function unicodeplots_rebuild(plt::Plot{UnicodePlotsBackend})
|
||||
w, h = plt[:size]
|
||||
plt.attr[:color_palette] = [RGB(0,0,0)]
|
||||
rebuildUnicodePlot!(plt, div(w, 10), div(h, 20))
|
||||
end
|
||||
|
||||
function _writemime(io::IO, ::MIME"text/plain", plt::Plot{UnicodePlotsBackend})
|
||||
map(show, plt.o)
|
||||
nothing
|
||||
unicodeplots_rebuild(plt)
|
||||
map(show, plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
function _display(plt::Plot{UnicodePlotsBackend})
|
||||
map(show, plt.o)
|
||||
nothing
|
||||
unicodeplots_rebuild(plt)
|
||||
map(show, plt.o)
|
||||
nothing
|
||||
end
|
||||
|
||||
|
||||
+7
-7
@@ -14,13 +14,13 @@ compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : ang
|
||||
immutable Shape
|
||||
x::Vector{Float64}
|
||||
y::Vector{Float64}
|
||||
function Shape(x::AVec, y::AVec)
|
||||
if x[1] != x[end] || y[1] != y[end]
|
||||
new(vcat(x, x[1]), vcat(y, y[1]))
|
||||
else
|
||||
new(x, y)
|
||||
end
|
||||
end
|
||||
# function Shape(x::AVec, y::AVec)
|
||||
# # if x[1] != x[end] || y[1] != y[end]
|
||||
# # new(vcat(x, x[1]), vcat(y, y[1]))
|
||||
# # else
|
||||
# new(x, y)
|
||||
# end
|
||||
# end
|
||||
end
|
||||
Shape(verts::AVec) = Shape(unzip(verts)...)
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ getColor(scheme::ColorScheme) = getColor(scheme, 1)
|
||||
getColorVector(scheme::ColorScheme) = [getColor(scheme)]
|
||||
|
||||
colorscheme(scheme::ColorScheme) = scheme
|
||||
colorscheme(s::AbstractString; kw...) = colorscheme(Symbol(s); kw...)
|
||||
colorscheme(s::Symbol; kw...) = haskey(_gradients, s) ? ColorGradient(s; kw...) : ColorWrapper(convertColor(s); kw...)
|
||||
colorscheme{T<:Real}(s::Symbol, vals::AVec{T}; kw...) = ColorGradient(s, vals; kw...)
|
||||
colorscheme(cs::AVec, vs::AVec; kw...) = ColorGradient(cs, vs; kw...)
|
||||
@@ -55,10 +56,10 @@ convertColor(b::Bool) = b ? RGBA(0,0,0,1) : RGBA(0,0,0,0)
|
||||
|
||||
function convertColor(c, α::Real)
|
||||
c = convertColor(c)
|
||||
RGBA(RGB(c), α)
|
||||
RGBA(RGB(getColor(c)), α)
|
||||
end
|
||||
convertColor(cs::AVec, α::Real) = map(c -> convertColor(c, α), cs)
|
||||
convertColor(c, α::@compat(Void)) = convertColor(c)
|
||||
convertColor(c, α::Void) = convertColor(c)
|
||||
|
||||
# backup... try to convert
|
||||
getColor(c) = convertColor(c)
|
||||
@@ -78,7 +79,7 @@ end
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
const _rainbowColors = [colorant"blue", colorant"purple", colorant"green", colorant"orange", colorant"red"]
|
||||
const _rainbowColors = [colorant"purple", colorant"blue", colorant"green", colorant"orange", colorant"red"]
|
||||
const _testColors = [colorant"darkblue", colorant"blueviolet", colorant"darkcyan",colorant"green",
|
||||
darken(colorant"yellow",0.3), colorant"orange", darken(colorant"red",0.2)]
|
||||
|
||||
@@ -220,7 +221,7 @@ immutable ColorZFunction <: ColorScheme
|
||||
f::Function
|
||||
end
|
||||
|
||||
getColorZ(scheme::ColorFunction, z::Real) = scheme.f(z)
|
||||
getColorZ(scheme::ColorZFunction, z::Real) = scheme.f(z)
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
@@ -246,6 +247,7 @@ ColorWrapper(s::Symbol; alpha = nothing) = ColorWrapper(convertColor(parse(Color
|
||||
|
||||
getColor(scheme::ColorWrapper, idx::Int) = scheme.c
|
||||
getColorZ(scheme::ColorWrapper, z::Real) = scheme.c
|
||||
convertColor(c::ColorWrapper, α::Void) = c.c
|
||||
|
||||
# --------------------------------------------------------------
|
||||
|
||||
@@ -221,12 +221,6 @@ PlotExample("Contours",
|
||||
p2 = contour(x, y, Z)
|
||||
plot(p1, p2)
|
||||
end)]
|
||||
# [:(begin
|
||||
# x = 1:0.3:20
|
||||
# y = x
|
||||
# f(x,y) = sin(x)+cos(y)
|
||||
# contour(x, y, f, fill=true)
|
||||
# end)]
|
||||
),
|
||||
|
||||
PlotExample("Pie",
|
||||
|
||||
+30
-24
@@ -147,14 +147,15 @@ function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
|
||||
end
|
||||
end
|
||||
|
||||
function _writemime(io::IO, m, plt::Plot)
|
||||
warn("_writemime is not defined for this backend. m=", string(m))
|
||||
end
|
||||
function _display(plt::Plot)
|
||||
warn("_display is not defined for this backend.")
|
||||
end
|
||||
|
||||
# for writing to io streams... first prepare, then callback
|
||||
for mime in keys(_mimeformats)
|
||||
@eval function _writemime(io::IO, m, plt::Plot)
|
||||
warn("_writemime is not defined for this backend. m=", string(m))
|
||||
end
|
||||
@eval function _display(plt::Plot)
|
||||
warn("_display is not defined for this backend.")
|
||||
end
|
||||
@eval function Base.writemime(io::IO, m::MIME{Symbol($mime)}, plt::Plot)
|
||||
prepare_output(plt)
|
||||
_writemime(io, m, plt)
|
||||
@@ -166,24 +167,22 @@ end
|
||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||
|
||||
if is_installed("FileIO")
|
||||
@eval begin
|
||||
import FileIO
|
||||
function _writemime(io::IO, ::MIME"image/png", plt::Plot)
|
||||
fn = tempname()
|
||||
@eval import FileIO
|
||||
function _writemime(io::IO, ::MIME"image/png", plt::Plot)
|
||||
fn = tempname()
|
||||
|
||||
# first save a pdf file
|
||||
pdf(plt, fn)
|
||||
# first save a pdf file
|
||||
pdf(plt, fn)
|
||||
|
||||
# load that pdf into a FileIO Stream
|
||||
s = FileIO.load(fn * ".pdf")
|
||||
# load that pdf into a FileIO Stream
|
||||
s = FileIO.load(fn * ".pdf")
|
||||
|
||||
# save a png
|
||||
pngfn = fn * ".png"
|
||||
FileIO.save(pngfn, s)
|
||||
# save a png
|
||||
pngfn = fn * ".png"
|
||||
FileIO.save(pngfn, s)
|
||||
|
||||
# now write from the file
|
||||
write(io, readall(open(pngfn)))
|
||||
end
|
||||
# now write from the file
|
||||
write(io, readall(open(pngfn)))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -248,10 +247,11 @@ function setup_atom()
|
||||
# @eval import Atom, Media
|
||||
@eval import Atom
|
||||
|
||||
# connects the render function
|
||||
for T in (GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend)
|
||||
Atom.Media.media(Plot{T}, Atom.Media.Plot)
|
||||
end
|
||||
# # connects the render function
|
||||
# for T in (GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend)
|
||||
# Atom.Media.media(Plot{T}, Atom.Media.Plot)
|
||||
# end
|
||||
Atom.Media.media(Plot, Atom.Media.Graphical)
|
||||
# Atom.Media.media{T <: Union{GadflyBackend,ImmerseBackend,PyPlotBackend,GRBackend}}(Plot{T}, Atom.Media.Plot)
|
||||
|
||||
# Atom.displaysize(::Plot) = (535, 379)
|
||||
@@ -259,9 +259,15 @@ function setup_atom()
|
||||
|
||||
# this is like "display"... sends an html div with the plot to the PlotPane
|
||||
function Atom.Media.render(pane::Atom.PlotPane, plt::Plot)
|
||||
@show "here"
|
||||
Atom.Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
|
||||
end
|
||||
|
||||
# # force text/plain to output to the PlotPane
|
||||
# function Base.writemime(io::IO, ::MIME"text/plain", plt::Plot)
|
||||
# # writemime(io::IO, MIME("text/html"), plt)
|
||||
# Atom.Media.render(pane)
|
||||
# end
|
||||
|
||||
# function Atom.Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend})
|
||||
# html = Media.render(pane, Atom.div(Atom.d(), Atom.HTML(stringmime(MIME("text/html"), plt))))
|
||||
|
||||
+399
@@ -0,0 +1,399 @@
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# preprocessing
|
||||
|
||||
function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
Int(kw[:series_plotindex]) - Int(kw_list[1][:series_plotindex]) + 1
|
||||
end
|
||||
|
||||
function _expand_seriestype_array(d::KW, args)
|
||||
sts = get(d, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(d, :seriestype)
|
||||
RecipeData[begin
|
||||
dc = copy(d)
|
||||
dc[:seriestype] = sts[r,:]
|
||||
RecipeData(dc, args)
|
||||
end for r=1:size(sts,1)]
|
||||
else
|
||||
RecipeData[RecipeData(copy(d), args)]
|
||||
end
|
||||
end
|
||||
|
||||
function _preprocess_args(d::KW, args, still_to_process::Vector{RecipeData})
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
if haskey(d, :group)
|
||||
args = (extractGroupArgs(d[:group], args...), args...)
|
||||
end
|
||||
|
||||
# if we were passed a vector/matrix of seriestypes and there's more than one row,
|
||||
# we want to duplicate the inputs, once for each seriestype row.
|
||||
if !isempty(args)
|
||||
append!(still_to_process, _expand_seriestype_array(d, args))
|
||||
end
|
||||
|
||||
# remove subplot and axis args from d... they will be passed through in the kw_list
|
||||
if !isempty(args)
|
||||
for (k,v) in d
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
delete!(d, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
args
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# user recipes
|
||||
|
||||
|
||||
function _process_userrecipes(plt::Plot, d::KW, args)
|
||||
still_to_process = RecipeData[]
|
||||
args = _preprocess_args(d, args, still_to_process)
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a queue of series that still need to be processed.
|
||||
# each pass through the loop, we pop one off and apply the recipe.
|
||||
# the recipe will return a list a Series objects... the ones that are
|
||||
# finished (no more args) get added to the kw_list, and the rest go into the queue
|
||||
# for processing.
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
# grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes)
|
||||
next_series = shift!(still_to_process)
|
||||
rd_list = RecipesBase.apply_recipe(next_series.d, next_series.args...)
|
||||
for recipedata in rd_list
|
||||
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
|
||||
if !(typeof(recipedata) <: RecipeData)
|
||||
error("Inputs couldn't be processed... expected RecipeData but got: $recipedata")
|
||||
end
|
||||
|
||||
if isempty(recipedata.args)
|
||||
_process_userrecipe(plt, kw_list, recipedata)
|
||||
else
|
||||
# args are non-empty, so there's still processing to do... add it back to the queue
|
||||
push!(still_to_process, recipedata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# don't allow something else to handle it
|
||||
d[:smooth] = false
|
||||
kw_list
|
||||
end
|
||||
|
||||
function _process_userrecipe(plt::Plot, kw_list::Vector{KW}, recipedata::RecipeData)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
kw = recipedata.d
|
||||
_preprocess_userrecipe(kw)
|
||||
warnOnUnsupported_scales(plt.backend, kw)
|
||||
|
||||
# add the plot index
|
||||
plt.n += 1
|
||||
kw[:series_plotindex] = plt.n
|
||||
|
||||
push!(kw_list, kw)
|
||||
_add_errorbar_kw(kw_list, kw)
|
||||
_add_smooth_kw(kw_list, kw)
|
||||
return
|
||||
end
|
||||
|
||||
function _preprocess_userrecipe(kw::KW)
|
||||
_add_markershape(kw)
|
||||
|
||||
# if there was a grouping, filter the data here
|
||||
_filter_input_data!(kw)
|
||||
|
||||
# map marker_z if it's a Function
|
||||
if isa(get(kw, :marker_z, nothing), Function)
|
||||
# TODO: should this take y and/or z as arguments?
|
||||
kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# map line_z if it's a Function
|
||||
if isa(get(kw, :line_z, nothing), Function)
|
||||
kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# convert a ribbon into a fillrange
|
||||
if get(kw, :ribbon, nothing) != nothing
|
||||
make_fillrange_from_ribbon(kw)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function _add_errorbar_kw(kw_list::Vector{KW}, kw::KW)
|
||||
# handle error bars by creating new recipedata data... these will have
|
||||
# the same recipedata index as the recipedata they are copied from
|
||||
for esym in (:xerror, :yerror)
|
||||
if get(kw, esym, nothing) != nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
errkw = copy(kw)
|
||||
errkw[:seriestype] = esym
|
||||
errkw[:label] = ""
|
||||
errkw[:primary] = false
|
||||
push!(kw_list, errkw)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _add_smooth_kw(kw_list::Vector{KW}, kw::KW)
|
||||
# handle smoothing by adding a new series
|
||||
if get(kw, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
sx = [minimum(x), maximum(x)]
|
||||
sy = β * sx + α
|
||||
push!(kw_list, merge(copy(kw), KW(
|
||||
:seriestype => :path,
|
||||
:x => sx,
|
||||
:y => sy,
|
||||
:fillrange => nothing,
|
||||
:label => "",
|
||||
:primary => false,
|
||||
)))
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# plot recipes
|
||||
|
||||
# Grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes).
|
||||
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
|
||||
# otherwise we just add the original KW.
|
||||
function _process_plotrecipe(plt::Plot, kw::KW, kw_list::Vector{KW}, still_to_process::Vector{KW})
|
||||
if !isa(get(kw, :seriestype, nothing), Symbol)
|
||||
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
|
||||
push!(kw_list, kw)
|
||||
return
|
||||
end
|
||||
try
|
||||
st = kw[:seriestype]
|
||||
st = kw[:seriestype] = get(_typeAliases, st, st)
|
||||
datalist = RecipesBase.apply_recipe(kw, Val{st}, plt)
|
||||
for data in datalist
|
||||
if data.d[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.d)")
|
||||
end
|
||||
push!(still_to_process, data.d)
|
||||
end
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
push!(kw_list, kw)
|
||||
else
|
||||
rethrow()
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# setup plot and subplot
|
||||
|
||||
function _plot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (d[k] = pop!(kw, k))
|
||||
end
|
||||
|
||||
# TODO: init subplots here
|
||||
_update_plot_args(plt, d)
|
||||
if !plt.init
|
||||
plt.o = _create_backend_figure(plt)
|
||||
|
||||
# create the layout and subplots from the inputs
|
||||
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
sp.plt = plt
|
||||
sp.attr[:subplot_index] = idx
|
||||
end
|
||||
|
||||
plt.init = true
|
||||
end
|
||||
|
||||
|
||||
# handle inset subplots
|
||||
insets = plt[:inset_subplots]
|
||||
if insets != nothing
|
||||
if !(typeof(insets) <: AVec)
|
||||
insets = [insets]
|
||||
end
|
||||
for inset in insets
|
||||
parent, bb = is_2tuple(inset) ? inset : (nothing, inset)
|
||||
P = typeof(parent)
|
||||
if P <: Integer
|
||||
parent = plt.subplots[parent]
|
||||
elseif P == Symbol
|
||||
parent = plt.spmap[parent]
|
||||
else
|
||||
parent = plt.layout
|
||||
end
|
||||
sp = Subplot(backend(), parent=parent)
|
||||
sp.plt = plt
|
||||
sp.attr[:relative_bbox] = bb
|
||||
sp.attr[:subplot_index] = length(plt.subplots)
|
||||
push!(plt.subplots, sp)
|
||||
push!(plt.inset_subplots, sp)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
# we'll keep a map of subplot to an attribute override dict.
|
||||
# Subplot/Axis attributes set by a user/series recipe apply only to the
|
||||
# Subplot object which they belong to.
|
||||
# TODO: allow matrices to still apply to all subplots
|
||||
sp_attrs = Dict{Subplot,Any}()
|
||||
for kw in kw_list
|
||||
# get the Subplot object to which the series belongs.
|
||||
sps = get(kw, :subplot, :auto)
|
||||
sp = get_subplot(plt, cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
|
||||
kw[:subplot] = sp
|
||||
|
||||
# extract subplot/axis attributes from kw and add to sp_attr
|
||||
attr = KW()
|
||||
for (k,v) in kw
|
||||
if haskey(_subplot_defaults, k) || haskey(_axis_defaults_byletter, k)
|
||||
attr[k] = pop!(kw, k)
|
||||
end
|
||||
if haskey(_axis_defaults, k)
|
||||
v = pop!(kw, k)
|
||||
for letter in (:x,:y,:z)
|
||||
attr[Symbol(letter,k)] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
sp_attrs[sp] = attr
|
||||
end
|
||||
|
||||
# override subplot/axis args. `sp_attrs` take precendence
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
attr = merge(d, get(sp_attrs, sp, KW()))
|
||||
_update_subplot_args(plt, sp, attr, idx, false)
|
||||
end
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
end
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
# that might have been added during series recipes
|
||||
function _prepare_subplot{T}(plt::Plot{T}, d::KW)
|
||||
st::Symbol = d[:seriestype]
|
||||
sp::Subplot{T} = d[:subplot]
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
_update_subplot_args(plt, sp, d, sp_idx, true)
|
||||
|
||||
st = _override_seriestype_check(d, st)
|
||||
|
||||
# change to a 3d projection for this subplot?
|
||||
if is3d(st)
|
||||
sp.attr[:projection] = "3d"
|
||||
end
|
||||
|
||||
# initialize now that we know the first series type
|
||||
if !haskey(sp.attr, :init)
|
||||
_initialize_subplot(plt, sp)
|
||||
sp.attr[:init] = true
|
||||
end
|
||||
sp
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# series types
|
||||
|
||||
function _override_seriestype_check(d::KW, st::Symbol)
|
||||
# do we want to override the series type?
|
||||
if !is3d(st)
|
||||
z = d[:z]
|
||||
if !isa(z, Void) && (size(d[:x]) == size(d[:y]) == size(z))
|
||||
st = (st == :scatter ? :scatter3d : :path3d)
|
||||
d[:seriestype] = st
|
||||
end
|
||||
end
|
||||
st
|
||||
end
|
||||
|
||||
function _prepare_annotations(sp::Subplot, d::KW)
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
anns = annotations(pop!(d, :series_annotations, []))
|
||||
if length(anns) > 0
|
||||
x, y = d[:x], d[:y]
|
||||
nx, ny, na = map(length, (x,y,anns))
|
||||
n = max(nx, ny, na)
|
||||
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
|
||||
end
|
||||
sp.attr[:annotations] = vcat(sp_anns, anns)
|
||||
end
|
||||
|
||||
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
w, h = size(d[:z])
|
||||
expand_extrema!(sp[:xaxis], (0,w))
|
||||
expand_extrema!(sp[:yaxis], (0,h))
|
||||
sp[:yaxis].d[:flip] = true
|
||||
elseif !(st in (:pie, :histogram, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
end
|
||||
end
|
||||
|
||||
function _add_the_series(plt, d)
|
||||
warnOnUnsupported_args(plt.backend, d)
|
||||
warnOnUnsupported(plt.backend, d)
|
||||
series = Series(d)
|
||||
push!(plt.series_list, series)
|
||||
_series_added(plt, series)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _process_seriesrecipe(plt::Plot, d::KW)
|
||||
# replace seriestype aliases
|
||||
st = Symbol(d[:seriestype])
|
||||
st = d[:seriestype] = get(_typeAliases, st, st)
|
||||
|
||||
# shapes shouldn't have fillrange set
|
||||
if d[:seriestype] == :shape
|
||||
d[:fillrange] = nothing
|
||||
end
|
||||
|
||||
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
|
||||
if st in supported_types()
|
||||
sp = _prepare_subplot(plt, d)
|
||||
_prepare_annotations(sp, d)
|
||||
_expand_subplot_extrema(sp, d, st)
|
||||
_add_the_series(plt, d)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
if isa(data, RecipeData)
|
||||
_process_seriesrecipe(plt, data.d)
|
||||
else
|
||||
warn("Unhandled recipe: $(data)")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
nothing
|
||||
end
|
||||
+15
-356
@@ -49,7 +49,7 @@ function plot(args...; kw...)
|
||||
# create an empty Plot then process
|
||||
plt = Plot()
|
||||
# plt.user_attr = d
|
||||
_plot!(plt, d, args...)
|
||||
_plot!(plt, d, args)
|
||||
end
|
||||
|
||||
# build a new plot from existing plots
|
||||
@@ -105,22 +105,11 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
# # just in case the backend needs to set up the plot (make it current or something)
|
||||
# _prepare_plot_object(plt)
|
||||
|
||||
# first apply any args for the subplots
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
_update_subplot_args(plt, sp, d, idx, remove_pair = false)
|
||||
_update_subplot_args(plt, sp, d, idx, false)
|
||||
end
|
||||
|
||||
# # now we can get rid of the axis keys without a letter
|
||||
# for k in keys(_axis_defaults)
|
||||
# delete!(d, k)
|
||||
# for letter in (:x,:y,:z)
|
||||
# delete!(d, Symbol(letter,k))
|
||||
# end
|
||||
# end
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
@@ -150,244 +139,23 @@ function plot!(plt::Plot, args...; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
# merge!(plt.user_attr, d)
|
||||
_plot!(plt, d, args...)
|
||||
end
|
||||
|
||||
function strip_first_letter(s::Symbol)
|
||||
str = string(s)
|
||||
str[1:1], Symbol(str[2:end])
|
||||
end
|
||||
|
||||
|
||||
# this method recursively applies series recipes when the seriestype is not supported
|
||||
# natively by the backend
|
||||
function _apply_series_recipe(plt::Plot, d::KW)
|
||||
# replace seriestype aliases
|
||||
st = d[:seriestype]
|
||||
st = d[:seriestype] = get(_typeAliases, st, st)
|
||||
|
||||
# if it's natively supported, finalize processing and pass along to the backend, otherwise recurse
|
||||
if st in supported_types()
|
||||
|
||||
# getting ready to add the series... last update to subplot from anything
|
||||
# that might have been added during series recipes
|
||||
sp = d[:subplot]
|
||||
sp_idx = get_subplot_index(plt, sp)
|
||||
_update_subplot_args(plt, sp, d, sp_idx)
|
||||
|
||||
# do we want to override the series type?
|
||||
if !is3d(st) && d[:z] != nothing && (size(d[:x]) == size(d[:y]) == size(d[:z]))
|
||||
st = d[:seriestype] = (st == :scatter ? :scatter3d : :path3d)
|
||||
end
|
||||
|
||||
# change to a 3d projection for this subplot?
|
||||
if is3d(st)
|
||||
sp.attr[:projection] = "3d"
|
||||
end
|
||||
|
||||
# initialize now that we know the first series type
|
||||
if !haskey(sp.attr, :init)
|
||||
_initialize_subplot(plt, sp)
|
||||
sp.attr[:init] = true
|
||||
end
|
||||
|
||||
# strip out series annotations (those which are based on series x/y coords)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
anns = annotations(pop!(d, :series_annotations, []))
|
||||
if length(anns) > 0
|
||||
x, y = d[:x], d[:y]
|
||||
nx, ny, na = map(length, (x,y,anns))
|
||||
n = max(nx, ny, na)
|
||||
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
|
||||
end
|
||||
sp.attr[:annotations] = vcat(sp_anns, anns)
|
||||
|
||||
# adjust extrema and discrete info
|
||||
if st == :image
|
||||
w, h = size(d[:z])
|
||||
expand_extrema!(sp[:xaxis], (0,w))
|
||||
expand_extrema!(sp[:yaxis], (0,h))
|
||||
sp[:yaxis].d[:flip] = true
|
||||
elseif !(st in (:pie, :histogram, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
end
|
||||
|
||||
|
||||
# add the series!
|
||||
warnOnUnsupported_args(plt.backend, d)
|
||||
warnOnUnsupported(plt.backend, d)
|
||||
series = Series(d)
|
||||
push!(plt.series_list, series)
|
||||
# @show series
|
||||
|
||||
_series_added(plt, series)
|
||||
|
||||
else
|
||||
# get a sub list of series for this seriestype
|
||||
datalist = RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
# datalist = try
|
||||
# RecipesBase.apply_recipe(d, Val{st}, d[:x], d[:y], d[:z])
|
||||
# catch
|
||||
# warn("Exception during apply_recipe(Val{$st}, ...) with types ($(typeof(d[:x])), $(typeof(d[:y])), $(typeof(d[:z])))")
|
||||
# rethrow()
|
||||
# end
|
||||
|
||||
# assuming there was no error, recursively apply the series recipes
|
||||
for data in datalist
|
||||
if isa(data, RecipeData)
|
||||
_apply_series_recipe(plt, data.d)
|
||||
else
|
||||
warn("Unhandled recipe: $(data)")
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function command_idx(kw_list::AVec{KW}, kw::KW)
|
||||
kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
|
||||
_plot!(plt, d, args)
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------------------
|
||||
|
||||
# this is the core plotting function. recursively apply recipes to build
|
||||
# a list of series KW dicts.
|
||||
# note: at entry, we only have those preprocessed args which were passed in... no default values yet
|
||||
function _plot!(plt::Plot, d::KW, args...)
|
||||
d[:plot_object] = plt
|
||||
|
||||
# the grouping mechanism is a recipe on a GroupBy object
|
||||
# we simply add the GroupBy object to the front of the args list to allow
|
||||
# the recipe to be applied
|
||||
if haskey(d, :group)
|
||||
args = (extractGroupArgs(d[:group], args...), args...)
|
||||
end
|
||||
|
||||
# if we were passed a vector/matrix of seriestypes and there's more than one row,
|
||||
# we want to duplicate the inputs, once for each seriestype row.
|
||||
kw_list = KW[]
|
||||
still_to_process = if isempty(args)
|
||||
[]
|
||||
else
|
||||
sts = get(d, :seriestype, :path)
|
||||
if typeof(sts) <: AbstractArray
|
||||
delete!(d, :seriestype)
|
||||
[begin
|
||||
dc = copy(d)
|
||||
dc[:seriestype] = sts[r,:]
|
||||
RecipeData(dc, args)
|
||||
end for r=1:size(sts,1)]
|
||||
else
|
||||
[RecipeData(copy(d), args)]
|
||||
end
|
||||
end
|
||||
|
||||
# remove subplot and axis args from d... they will be passed through in the kw_list
|
||||
if !isempty(args)
|
||||
for (k,v) in d
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
delete!(d, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||
# d[:plot_object] = plt
|
||||
|
||||
# --------------------------------
|
||||
# "USER RECIPES"
|
||||
# --------------------------------
|
||||
|
||||
# for plotting recipes, swap out the args and update the parameter dictionary
|
||||
# we are keeping a queue of series that still need to be processed.
|
||||
# each pass through the loop, we pop one off and apply the recipe.
|
||||
# the recipe will return a list a Series objects... the ones that are
|
||||
# finished (no more args) get added to the kw_list, and the rest go into the queue
|
||||
# for processing.
|
||||
while !isempty(still_to_process)
|
||||
kw_list = _process_userrecipes(plt, d, args)
|
||||
|
||||
# grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes)
|
||||
next_series = shift!(still_to_process)
|
||||
for recipedata in RecipesBase.apply_recipe(next_series.d, next_series.args...)
|
||||
|
||||
# recipedata should be of type RecipeData. if it's not then the inputs must not have been fully processed by recipes
|
||||
if !(typeof(recipedata) <: RecipeData)
|
||||
error("Inputs couldn't be processed... expected RecipeData but got: $recipedata")
|
||||
end
|
||||
|
||||
if isempty(recipedata.args)
|
||||
# when the arg tuple is empty, that means there's nothing left to recursively
|
||||
# process... finish up and add to the kw_list
|
||||
kw = recipedata.d
|
||||
_add_markershape(kw)
|
||||
|
||||
# if there was a grouping, filter the data here
|
||||
_filter_input_data!(kw)
|
||||
|
||||
# map marker_z if it's a Function
|
||||
if isa(get(kw, :marker_z, nothing), Function)
|
||||
# TODO: should this take y and/or z as arguments?
|
||||
kw[:marker_z] = map(kw[:marker_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# map line_z if it's a Function
|
||||
if isa(get(kw, :line_z, nothing), Function)
|
||||
kw[:line_z] = map(kw[:line_z], kw[:x], kw[:y], kw[:z])
|
||||
end
|
||||
|
||||
# convert a ribbon into a fillrange
|
||||
if get(kw, :ribbon, nothing) != nothing
|
||||
make_fillrange_from_ribbon(kw)
|
||||
end
|
||||
|
||||
# add the plot index
|
||||
plt.n += 1
|
||||
kw[:series_plotindex] = plt.n
|
||||
|
||||
# check that the backend will support the command and add it to the list
|
||||
warnOnUnsupported_scales(plt.backend, kw)
|
||||
push!(kw_list, kw)
|
||||
|
||||
# handle error bars by creating new recipedata data... these will have
|
||||
# the same recipedata index as the recipedata they are copied from
|
||||
for esym in (:xerror, :yerror)
|
||||
if get(d, esym, nothing) != nothing
|
||||
# we make a copy of the KW and apply an errorbar recipe
|
||||
errkw = copy(kw)
|
||||
errkw[:seriestype] = esym
|
||||
errkw[:label] = ""
|
||||
errkw[:primary] = false
|
||||
push!(kw_list, errkw)
|
||||
end
|
||||
end
|
||||
|
||||
# handle smoothing by adding a new series
|
||||
if get(d, :smooth, false)
|
||||
x, y = kw[:x], kw[:y]
|
||||
β, α = convert(Matrix{Float64}, [x ones(length(x))]) \ convert(Vector{Float64}, y)
|
||||
sx = [minimum(x), maximum(x)]
|
||||
sy = β * sx + α
|
||||
push!(kw_list, merge(copy(kw), KW(
|
||||
:seriestype => :path,
|
||||
:x => sx,
|
||||
:y => sy,
|
||||
:fillrange => nothing,
|
||||
:label => "",
|
||||
:primary => false,
|
||||
)))
|
||||
end
|
||||
|
||||
else
|
||||
# args are non-empty, so there's still processing to do... add it back to the queue
|
||||
push!(still_to_process, recipedata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# don't allow something else to handle it
|
||||
d[:smooth] = false
|
||||
|
||||
# --------------------------------
|
||||
# "PLOT RECIPES"
|
||||
@@ -400,117 +168,15 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
still_to_process = kw_list
|
||||
kw_list = KW[]
|
||||
while !isempty(still_to_process)
|
||||
# Grab the first in line to be processed and pass it through apply_recipe
|
||||
# to generate a list of RecipeData objects (data + attributes).
|
||||
# If we applied a "plot recipe" without error, then add the returned datalist's KWs,
|
||||
# otherwise we just add the original KW.
|
||||
next_kw = shift!(still_to_process)
|
||||
if !isa(get(next_kw, :seriestype, nothing), Symbol)
|
||||
# seriestype was never set, or it's not a Symbol, so it can't be a plot recipe
|
||||
push!(kw_list, next_kw)
|
||||
continue
|
||||
end
|
||||
try
|
||||
st = next_kw[:seriestype]
|
||||
st = next_kw[:seriestype] = get(_typeAliases, st, st)
|
||||
datalist = RecipesBase.apply_recipe(next_kw, Val{st}, plt)
|
||||
for data in datalist
|
||||
if data.d[:seriestype] == st
|
||||
error("Plot recipe $st returned the same seriestype: $(data.d)")
|
||||
end
|
||||
push!(still_to_process, data.d)
|
||||
end
|
||||
catch err
|
||||
if isa(err, MethodError)
|
||||
push!(kw_list, next_kw)
|
||||
else
|
||||
rethrow()
|
||||
end
|
||||
end
|
||||
_process_plotrecipe(plt, next_kw, kw_list, still_to_process)
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
# Plot/Subplot/Layout setup
|
||||
# --------------------------------
|
||||
|
||||
# merge in anything meant for the Plot
|
||||
for kw in kw_list, (k,v) in kw
|
||||
haskey(_plot_defaults, k) && (d[k] = pop!(kw, k))
|
||||
end
|
||||
|
||||
# TODO: init subplots here
|
||||
_update_plot_args(plt, d)
|
||||
if !plt.init
|
||||
plt.o = _create_backend_figure(plt)
|
||||
|
||||
# create the layout and subplots from the inputs
|
||||
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
sp.plt = plt
|
||||
sp.attr[:subplot_index] = idx
|
||||
end
|
||||
|
||||
plt.init = true
|
||||
end
|
||||
|
||||
|
||||
# handle inset subplots
|
||||
insets = plt[:inset_subplots]
|
||||
if insets != nothing
|
||||
if !(typeof(insets) <: AVec)
|
||||
insets = [insets]
|
||||
end
|
||||
for inset in insets
|
||||
parent, bb = is_2tuple(inset) ? inset : (nothing, inset)
|
||||
P = typeof(parent)
|
||||
if P <: Integer
|
||||
parent = plt.subplots[parent]
|
||||
elseif P == Symbol
|
||||
parent = plt.spmap[parent]
|
||||
else
|
||||
parent = plt.layout
|
||||
end
|
||||
sp = Subplot(backend(), parent=parent)
|
||||
sp.plt = plt
|
||||
sp.attr[:relative_bbox] = bb
|
||||
sp.attr[:subplot_index] = length(plt.subplots)
|
||||
push!(plt.subplots, sp)
|
||||
push!(plt.inset_subplots, sp)
|
||||
end
|
||||
end
|
||||
|
||||
# we'll keep a map of subplot to an attribute override dict.
|
||||
# Subplot/Axis attributes set by a user/series recipe apply only to the
|
||||
# Subplot object which they belong to.
|
||||
# TODO: allow matrices to still apply to all subplots
|
||||
sp_attrs = Dict{Subplot,Any}()
|
||||
for kw in kw_list
|
||||
# get the Subplot object to which the series belongs.
|
||||
sps = get(kw, :subplot, :auto)
|
||||
sp = get_subplot(plt, cycle(sps == :auto ? plt.subplots : plt.subplots[sps], command_idx(kw_list,kw)))
|
||||
kw[:subplot] = sp
|
||||
|
||||
attr = KW()
|
||||
for (k,v) in kw
|
||||
for defdict in (_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
attr[k] = pop!(kw, k)
|
||||
end
|
||||
end
|
||||
end
|
||||
sp_attrs[sp] = attr
|
||||
end
|
||||
|
||||
# override subplot/axis args. `sp_attrs` take precendence
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
attr = merge(d, get(sp_attrs, sp, KW()))
|
||||
_update_subplot_args(plt, sp, attr, idx, remove_pair = false)
|
||||
end
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
_plot_setup(plt, d, kw_list)
|
||||
_subplot_setup(plt, d, kw_list)
|
||||
|
||||
# !!! note: At this point, kw_list is fully decomposed into individual series... one KW per series. !!!
|
||||
# !!! The next step is to recursively apply series recipes until the backend supports that series type !!!
|
||||
@@ -520,11 +186,11 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
# --------------------------------
|
||||
|
||||
for kw in kw_list
|
||||
sp = kw[:subplot]
|
||||
idx = get_subplot_index(plt, sp)
|
||||
sp::Subplot = kw[:subplot]
|
||||
# idx = get_subplot_index(plt, sp)
|
||||
|
||||
# # we update subplot args in case something like the color palatte is part of the recipe
|
||||
# _update_subplot_args(plt, sp, kw, idx)
|
||||
# _update_subplot_args(plt, sp, kw, idx, true)
|
||||
|
||||
# set default values, select from attribute cycles, and generally set the final attributes
|
||||
_add_defaults!(kw, plt, sp, command_idx(kw_list,kw))
|
||||
@@ -535,7 +201,7 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
# For example, a histogram is just a bar plot with binned data, a bar plot is really a filled step plot,
|
||||
# and a step plot is really just a path. So any backend that supports drawing a path will implicitly
|
||||
# be able to support step, bar, and histogram plots (and any recipes that use those components).
|
||||
_apply_series_recipe(plt, kw)
|
||||
_process_seriesrecipe(plt, kw)
|
||||
end
|
||||
|
||||
# --------------------------------
|
||||
@@ -544,20 +210,13 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
|
||||
# do we want to force display?
|
||||
if plt[:show]
|
||||
gui()
|
||||
gui(plt)
|
||||
end
|
||||
|
||||
plt
|
||||
end
|
||||
|
||||
|
||||
function _replace_linewidth(d::KW)
|
||||
# get a good default linewidth... 0 for surface and heatmaps
|
||||
if get(d, :linewidth, :auto) == :auto
|
||||
d[:linewidth] = (get(d, :seriestype, :path) in (:surface,:heatmap,:image) ? 0 : 1)
|
||||
end
|
||||
end
|
||||
|
||||
# we're getting ready to display/output. prep for layout calcs, then update
|
||||
# the plot object after
|
||||
function prepare_output(plt::Plot)
|
||||
|
||||
+220
-287
@@ -1,10 +1,5 @@
|
||||
|
||||
|
||||
# TODO: there should be a distinction between an object that will manage a full plot, vs a component of a plot.
|
||||
# the PlotRecipe as currently implemented is more of a "custom component"
|
||||
# a recipe should fully describe the plotting command(s) and call them, likewise for updating.
|
||||
# actually... maybe those should explicitly derive from AbstractPlot???
|
||||
|
||||
|
||||
"""
|
||||
You can easily define your own plotting recipes with convenience methods:
|
||||
@@ -82,7 +77,7 @@ function seriestype_supported(pkg::AbstractBackend, st::Symbol)
|
||||
end
|
||||
|
||||
macro deps(st, args...)
|
||||
:(series_recipe_dependencies($(quot(st)), $(map(quot, args)...)))
|
||||
:(Plots.series_recipe_dependencies($(quot(st)), $(map(quot, args)...)))
|
||||
end
|
||||
|
||||
# get a list of all seriestypes
|
||||
@@ -106,60 +101,51 @@ num_series(x) = 1
|
||||
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T"))
|
||||
|
||||
|
||||
# # TODO: remove when StatPlots is ready
|
||||
# if is_installed("DataFrames")
|
||||
# @eval begin
|
||||
# import DataFrames
|
||||
|
||||
if is_installed("DataFrames")
|
||||
@eval begin
|
||||
import DataFrames
|
||||
# # if it's one symbol, set the guide and return the column
|
||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol)
|
||||
# get!(d, Symbol(letter * "guide"), string(sym))
|
||||
# collect(df[sym])
|
||||
# end
|
||||
|
||||
# if it's one symbol, set the guide and return the column
|
||||
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol)
|
||||
get!(d, Symbol(letter * "guide"), string(sym))
|
||||
collect(df[sym])
|
||||
end
|
||||
# # if it's an array of symbols, set the labels and return a Vector{Any} of columns
|
||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, syms::AbstractArray{Symbol})
|
||||
# get!(d, :label, reshape(syms, 1, length(syms)))
|
||||
# Any[collect(df[s]) for s in syms]
|
||||
# end
|
||||
|
||||
# if it's an array of symbols, set the labels and return a Vector{Any} of columns
|
||||
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, syms::AbstractArray{Symbol})
|
||||
get!(d, :label, reshape(syms, 1, length(syms)))
|
||||
Any[collect(df[s]) for s in syms]
|
||||
end
|
||||
# # for anything else, no-op
|
||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything)
|
||||
# anything
|
||||
# end
|
||||
|
||||
# for anything else, no-op
|
||||
function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything)
|
||||
anything
|
||||
end
|
||||
# # handle grouping by DataFrame column
|
||||
# function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
||||
# extractGroupArgs(collect(df[group]))
|
||||
# end
|
||||
|
||||
# handle grouping by DataFrame column
|
||||
function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
||||
extractGroupArgs(collect(df[group]))
|
||||
end
|
||||
# # if a DataFrame is the first arg, lets swap symbols out for columns
|
||||
# @recipe function f(df::DataFrames.AbstractDataFrame, args...)
|
||||
# # if any of these attributes are symbols, swap out for the df column
|
||||
# for k in (:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror)
|
||||
# if haskey(d, k) && isa(d[k], Symbol)
|
||||
# d[k] = collect(df[d[k]])
|
||||
# end
|
||||
# end
|
||||
|
||||
# if a DataFrame is the first arg, lets swap symbols out for columns
|
||||
@recipe function f(df::DataFrames.AbstractDataFrame, args...)
|
||||
# if any of these attributes are symbols, swap out for the df column
|
||||
for k in (:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror)
|
||||
if haskey(d, k) && isa(d[k], Symbol)
|
||||
d[k] = collect(df[d[k]])
|
||||
end
|
||||
end
|
||||
|
||||
# return a list of new arguments
|
||||
tuple(Any[handle_dfs(df, d, (i==1 ? "x" : i==2 ? "y" : "z"), arg) for (i,arg) in enumerate(args)]...)
|
||||
end
|
||||
end
|
||||
end
|
||||
# # return a list of new arguments
|
||||
# tuple(Any[handle_dfs(df, d, (i==1 ? "x" : i==2 ? "y" : "z"), arg) for (i,arg) in enumerate(args)]...)
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# """
|
||||
# `apply_series_recipe` should take a processed series KW dict and break it up
|
||||
# into component parts. For example, a box plot is made up of `shape` for the
|
||||
# boxes, `path` for the lines, and `scatter` for the outliers.
|
||||
#
|
||||
# Returns a Vector{KW}.
|
||||
# """
|
||||
# apply_series_recipe(d::KW, st) = KW[d]
|
||||
|
||||
|
||||
# for seriestype `line`, need to sort by x values
|
||||
@recipe function f(::Type{Val{:line}}, x, y, z)
|
||||
@@ -174,21 +160,6 @@ end
|
||||
end
|
||||
@deps line path
|
||||
|
||||
# @recipe function f(::Type{Val{:sticks}}, x, y, z)
|
||||
# nx = length(x)
|
||||
# n = 3nx
|
||||
# newx, newy = zeros(n), zeros(n)
|
||||
# for i=1:nx
|
||||
# rng = 3i-2:3i
|
||||
# newx[rng] = x[i]
|
||||
# newy[rng] = [0., y[i], 0.]
|
||||
# end
|
||||
# x := newx
|
||||
# y := newy
|
||||
# seriestype := :path
|
||||
# ()
|
||||
# end
|
||||
# @deps sticks path
|
||||
|
||||
function hvline_limits(axis::Axis)
|
||||
vmin, vmax = axis_limits(axis)
|
||||
@@ -346,8 +317,8 @@ end
|
||||
fr = d[:fillrange]
|
||||
newfr = fr != nothing ? zeros(0) : nothing
|
||||
newz = z != nothing ? zeros(0) : nothing
|
||||
lz = d[:line_z]
|
||||
newlz = lz != nothing ? zeros(0) : nothing
|
||||
# lz = d[:line_z]
|
||||
# newlz = lz != nothing ? zeros(0) : nothing
|
||||
|
||||
# for each line segment (point series with no NaNs), convert it into a bezier curve
|
||||
# where the points are the control points of the curve
|
||||
@@ -362,11 +333,11 @@ end
|
||||
if fr != nothing
|
||||
nanappend!(newfr, map(t -> bezier_value(cycle(fr,rng), t), ts))
|
||||
end
|
||||
if lz != nothing
|
||||
lzrng = cycle(lz, rng) # the line_z's for this segment
|
||||
push!(newlz, 0.0)
|
||||
append!(newlz, map(t -> lzrng[1+floor(Int, t * (length(rng)-1))], ts))
|
||||
end
|
||||
# if lz != nothing
|
||||
# lzrng = cycle(lz, rng) # the line_z's for this segment
|
||||
# push!(newlz, 0.0)
|
||||
# append!(newlz, map(t -> lzrng[1+floor(Int, t * (length(rng)-1))], ts))
|
||||
# end
|
||||
end
|
||||
|
||||
x := newx
|
||||
@@ -380,10 +351,10 @@ end
|
||||
if fr != nothing
|
||||
fillrange := newfr
|
||||
end
|
||||
if lz != nothing
|
||||
line_z := newlz
|
||||
linecolor := (isa(d[:linecolor], ColorGradient) ? d[:linecolor] : default_gradient())
|
||||
end
|
||||
# if lz != nothing
|
||||
# # line_z := newlz
|
||||
# linecolor := (isa(d[:linecolor], ColorGradient) ? d[:linecolor] : cgrad())
|
||||
# end
|
||||
# Plots.DD(d)
|
||||
()
|
||||
end
|
||||
@@ -393,31 +364,25 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
# if horizontal, switch x/y
|
||||
if !isvertical(d)
|
||||
x, y = y, x
|
||||
end
|
||||
|
||||
nx, ny = length(x), length(y)
|
||||
edges = if nx == ny
|
||||
# x is centers, calc the edges
|
||||
# TODO: use bar_width, etc
|
||||
midpoints = x
|
||||
halfwidths = diff(midpoints) * 0.5
|
||||
Float64[if i == 1
|
||||
midpoints[1] - halfwidths[1]
|
||||
elseif i == ny+1
|
||||
midpoints[i-1] + halfwidths[i-2]
|
||||
else
|
||||
midpoints[i-1] + halfwidths[i-1]
|
||||
end for i=1:ny+1]
|
||||
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=x]
|
||||
x = if nx == ny
|
||||
cv
|
||||
elseif nx == ny + 1
|
||||
# x is edges
|
||||
x
|
||||
0.5diff(cv) + cv[1:end-1]
|
||||
else
|
||||
error("bar recipe: x must be same length as y (centers), or one more than y (edges).\n\t\tlength(x)=$(length(x)), length(y)=$(length(y))")
|
||||
end
|
||||
|
||||
# compute half-width of bars
|
||||
bw = d[:bar_width]
|
||||
hw = if bw == nothing
|
||||
0.5mean(diff(x))
|
||||
else
|
||||
Float64[0.5cycle(bw,i) for i=1:length(x)]
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
fillto = d[:fillrange]
|
||||
if fillto == nothing
|
||||
@@ -427,9 +392,12 @@ end
|
||||
# create the bar shapes by adding x/y segments
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i=1:ny
|
||||
center = x[i]
|
||||
hwi = cycle(hw,i)
|
||||
yi = y[i]
|
||||
fi = cycle(fillto,i)
|
||||
push!(xseg, edges[i], edges[i], edges[i+1], edges[i+1])
|
||||
push!(yseg, y[i], fi, fi, y[i])
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
end
|
||||
|
||||
# switch back
|
||||
@@ -442,7 +410,7 @@ end
|
||||
seriestype := :shape
|
||||
()
|
||||
end
|
||||
@deps bar path
|
||||
@deps bar shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
@@ -454,7 +422,7 @@ function calc_edges(v, bins::Integer)
|
||||
end
|
||||
|
||||
# just pass through arrays
|
||||
calc_edges(v, bins::AVec) = v
|
||||
calc_edges(v, bins::AVec) = bins
|
||||
|
||||
# find the bucket index of this value
|
||||
function bucket_index(vi, edges)
|
||||
@@ -525,7 +493,7 @@ function my_hist_2d(x, y, bins; normed = false, weights = nothing)
|
||||
xedges, yedges, counts ./ norm_denom
|
||||
end
|
||||
|
||||
centers(v::AVec) = v[1] + cumsum(diff(v))
|
||||
centers(v::AVec) = 0.5 * (v[1:end-1] + v[2:end])
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
|
||||
@@ -556,170 +524,179 @@ end
|
||||
|
||||
# note: don't add dependencies because this really isn't a drop-in replacement
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Box Plot
|
||||
# # TODO: move boxplots and violin plots to StatPlots when it's ready
|
||||
|
||||
const _box_halfwidth = 0.4
|
||||
# # ---------------------------------------------------------------------------
|
||||
# # Box Plot
|
||||
|
||||
notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||
# const _box_halfwidth = 0.4
|
||||
|
||||
# notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
|
||||
xsegs, ysegs = Segments(), Segments()
|
||||
glabels = sort(collect(unique(x)))
|
||||
warning = false
|
||||
outliers_x, outliers_y = zeros(0), zeros(0)
|
||||
for glabel in glabels
|
||||
# filter y
|
||||
values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
|
||||
# @recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
|
||||
# xsegs, ysegs = Segments(), Segments()
|
||||
# glabels = sort(collect(unique(x)))
|
||||
# warning = false
|
||||
# outliers_x, outliers_y = zeros(0), zeros(0)
|
||||
# for (i,glabel) in enumerate(glabels)
|
||||
# # filter y
|
||||
# values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
|
||||
|
||||
# compute quantiles
|
||||
q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
||||
# # compute quantiles
|
||||
# q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
||||
|
||||
# notch
|
||||
n = notch_width(q2, q4, length(values))
|
||||
# # notch
|
||||
# n = notch_width(q2, q4, length(values))
|
||||
|
||||
# warn on inverted notches?
|
||||
if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
||||
warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||
warning = true # Show the warning only one time
|
||||
end
|
||||
# # warn on inverted notches?
|
||||
# if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
||||
# warn("Boxplot's notch went outside hinges. Set notch to false.")
|
||||
# warning = true # Show the warning only one time
|
||||
# end
|
||||
|
||||
# make the shape
|
||||
center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
l, m, r = center - _box_halfwidth, center, center + _box_halfwidth
|
||||
# # make the shape
|
||||
# center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
# hw = d[:bar_width] == nothing ? _box_halfwidth : 0.5cycle(d[:bar_width], i)
|
||||
# l, m, r = center - hw, center, center + hw
|
||||
|
||||
# internal nodes for notches
|
||||
L, R = center - 0.5 * _box_halfwidth, center + 0.5 * _box_halfwidth
|
||||
# # internal nodes for notches
|
||||
# L, R = center - 0.5 * hw, center + 0.5 * hw
|
||||
|
||||
# outliers
|
||||
if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
|
||||
limit = range*(q4-q2)
|
||||
inside = Float64[]
|
||||
for value in values
|
||||
if (value < (q2 - limit)) || (value > (q4 + limit))
|
||||
push!(outliers_y, value)
|
||||
push!(outliers_x, center)
|
||||
else
|
||||
push!(inside, value)
|
||||
end
|
||||
end
|
||||
# change q1 and q5 to show outliers
|
||||
# using maximum and minimum values inside the limits
|
||||
q1, q5 = extrema(inside)
|
||||
end
|
||||
# # outliers
|
||||
# if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
|
||||
# limit = range*(q4-q2)
|
||||
# inside = Float64[]
|
||||
# for value in values
|
||||
# if (value < (q2 - limit)) || (value > (q4 + limit))
|
||||
# push!(outliers_y, value)
|
||||
# push!(outliers_x, center)
|
||||
# else
|
||||
# push!(inside, value)
|
||||
# end
|
||||
# end
|
||||
# # change q1 and q5 to show outliers
|
||||
# # using maximum and minimum values inside the limits
|
||||
# q1, q5 = extrema(inside)
|
||||
# end
|
||||
|
||||
# Box
|
||||
if notch
|
||||
push!(xsegs, m, l, r, m, m) # lower T
|
||||
push!(xsegs, l, l, L, R, r, r, l) # lower box
|
||||
push!(xsegs, l, l, L, R, r, r, l) # upper box
|
||||
push!(xsegs, m, l, r, m, m) # upper T
|
||||
# # Box
|
||||
# if notch
|
||||
# push!(xsegs, m, l, r, m, m) # lower T
|
||||
# push!(xsegs, l, l, L, R, r, r, l) # lower box
|
||||
# push!(xsegs, l, l, L, R, r, r, l) # upper box
|
||||
# push!(xsegs, m, l, r, m, m) # upper T
|
||||
|
||||
push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
||||
push!(ysegs, q2, q3-n, q3, q3, q3-n, q2, q2) # lower box
|
||||
push!(ysegs, q4, q3+n, q3, q3, q3+n, q4, q4) # upper box
|
||||
push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
||||
else
|
||||
push!(xsegs, m, l, r, m, m) # lower T
|
||||
push!(xsegs, l, l, r, r, l) # lower box
|
||||
push!(xsegs, l, l, r, r, l) # upper box
|
||||
push!(xsegs, m, l, r, m, m) # upper T
|
||||
# push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
||||
# push!(ysegs, q2, q3-n, q3, q3, q3-n, q2, q2) # lower box
|
||||
# push!(ysegs, q4, q3+n, q3, q3, q3+n, q4, q4) # upper box
|
||||
# push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
||||
# else
|
||||
# push!(xsegs, m, l, r, m, m) # lower T
|
||||
# push!(xsegs, l, l, r, r, l) # lower box
|
||||
# push!(xsegs, l, l, r, r, l) # upper box
|
||||
# push!(xsegs, m, l, r, m, m) # upper T
|
||||
|
||||
push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
||||
push!(ysegs, q2, q3, q3, q2, q2) # lower box
|
||||
push!(ysegs, q4, q3, q3, q4, q4) # upper box
|
||||
push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
||||
end
|
||||
end
|
||||
# push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
||||
# push!(ysegs, q2, q3, q3, q2, q2) # lower box
|
||||
# push!(ysegs, q4, q3, q3, q4, q4) # upper box
|
||||
# push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
||||
# end
|
||||
# end
|
||||
|
||||
# Outliers
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
markershape := :circle
|
||||
markercolor := d[:fillcolor]
|
||||
markeralpha := d[:fillalpha]
|
||||
markerstrokecolor := d[:linecolor]
|
||||
markerstrokealpha := d[:linealpha]
|
||||
x := outliers_x
|
||||
y := outliers_y
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
# # Outliers
|
||||
# @series begin
|
||||
# seriestype := :scatter
|
||||
# markershape := :circle
|
||||
# markercolor := d[:fillcolor]
|
||||
# markeralpha := d[:fillalpha]
|
||||
# markerstrokecolor := d[:linecolor]
|
||||
# markerstrokealpha := d[:linealpha]
|
||||
# x := outliers_x
|
||||
# y := outliers_y
|
||||
# primary := false
|
||||
# ()
|
||||
# end
|
||||
|
||||
seriestype := :shape
|
||||
x := xsegs.pts
|
||||
y := ysegs.pts
|
||||
()
|
||||
end
|
||||
@deps boxplot shape scatter
|
||||
# seriestype := :shape
|
||||
# x := xsegs.pts
|
||||
# y := ysegs.pts
|
||||
# ()
|
||||
# end
|
||||
# @deps boxplot shape scatter
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Violin Plot
|
||||
# # ---------------------------------------------------------------------------
|
||||
# # Violin Plot
|
||||
|
||||
# if the user has KernelDensity installed, use this for violin plots.
|
||||
# otherwise, just use a histogram
|
||||
if is_installed("KernelDensity")
|
||||
@eval import KernelDensity
|
||||
@eval function violin_coords(y; trim::Bool=false)
|
||||
kd = KernelDensity.kde(y, npoints = 200)
|
||||
if trim
|
||||
xmin, xmax = extrema(y)
|
||||
inside = Bool[ xmin <= x <= xmax for x in kd.x]
|
||||
return(kd.density[inside], kd.x[inside])
|
||||
end
|
||||
kd.density, kd.x
|
||||
end
|
||||
else
|
||||
@eval function violin_coords(y; trim::Bool=false)
|
||||
edges, widths = hist(y, 30)
|
||||
centers = 0.5 * (edges[1:end-1] + edges[2:end])
|
||||
ymin, ymax = extrema(y)
|
||||
vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
|
||||
end
|
||||
end
|
||||
# const _violin_warned = [false]
|
||||
|
||||
# # if the user has KernelDensity installed, use this for violin plots.
|
||||
# # otherwise, just use a histogram
|
||||
# if is_installed("KernelDensity")
|
||||
# @eval import KernelDensity
|
||||
# @eval function violin_coords(y; trim::Bool=false)
|
||||
# kd = KernelDensity.kde(y, npoints = 200)
|
||||
# if trim
|
||||
# xmin, xmax = extrema(y)
|
||||
# inside = Bool[ xmin <= x <= xmax for x in kd.x]
|
||||
# return(kd.density[inside], kd.x[inside])
|
||||
# end
|
||||
# kd.density, kd.x
|
||||
# end
|
||||
# else
|
||||
# @eval function violin_coords(y; trim::Bool=false)
|
||||
# if !_violin_warned[1]
|
||||
# warn("Install the KernelDensity package for best results.")
|
||||
# _violin_warned[1] = true
|
||||
# end
|
||||
# edges, widths = my_hist(y, 10)
|
||||
# centers = 0.5 * (edges[1:end-1] + edges[2:end])
|
||||
# ymin, ymax = extrema(y)
|
||||
# vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
|
||||
# end
|
||||
# end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:violin}}, x, y, z; trim=true)
|
||||
xsegs, ysegs = Segments(), Segments()
|
||||
glabels = sort(collect(unique(x)))
|
||||
for glabel in glabels
|
||||
widths, centers = violin_coords(y[filter(i -> cycle(x,i) == glabel, 1:length(y))], trim=trim)
|
||||
isempty(widths) && continue
|
||||
# @recipe function f(::Type{Val{:violin}}, x, y, z; trim=true)
|
||||
# xsegs, ysegs = Segments(), Segments()
|
||||
# glabels = sort(collect(unique(x)))
|
||||
# for glabel in glabels
|
||||
# widths, centers = violin_coords(y[filter(i -> cycle(x,i) == glabel, 1:length(y))], trim=trim)
|
||||
# isempty(widths) && continue
|
||||
|
||||
# normalize
|
||||
widths = _box_halfwidth * widths / maximum(widths)
|
||||
# # normalize
|
||||
# widths = _box_halfwidth * widths / maximum(widths)
|
||||
|
||||
# make the violin
|
||||
xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
xcoords = vcat(widths, -reverse(widths)) + xcenter
|
||||
ycoords = vcat(centers, reverse(centers))
|
||||
# # make the violin
|
||||
# xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
||||
# xcoords = vcat(widths, -reverse(widths)) + xcenter
|
||||
# ycoords = vcat(centers, reverse(centers))
|
||||
|
||||
push!(xsegs, xcoords)
|
||||
push!(ysegs, ycoords)
|
||||
end
|
||||
# push!(xsegs, xcoords)
|
||||
# push!(ysegs, ycoords)
|
||||
# end
|
||||
|
||||
seriestype := :shape
|
||||
x := xsegs.pts
|
||||
y := ysegs.pts
|
||||
()
|
||||
end
|
||||
@deps violin shape
|
||||
# seriestype := :shape
|
||||
# x := xsegs.pts
|
||||
# y := ysegs.pts
|
||||
# ()
|
||||
# end
|
||||
# @deps violin shape
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# density
|
||||
# # ---------------------------------------------------------------------------
|
||||
# # density
|
||||
|
||||
@recipe function f(::Type{Val{:density}}, x, y, z; trim=false)
|
||||
newx, newy = violin_coords(y, trim=trim)
|
||||
if isvertical(d)
|
||||
newx, newy = newy, newx
|
||||
end
|
||||
x := newx
|
||||
y := newy
|
||||
seriestype := :path
|
||||
()
|
||||
end
|
||||
@deps density path
|
||||
# @recipe function f(::Type{Val{:density}}, x, y, z; trim=false)
|
||||
# newx, newy = violin_coords(y, trim=trim)
|
||||
# if isvertical(d)
|
||||
# newx, newy = newy, newx
|
||||
# end
|
||||
# x := newx
|
||||
# y := newy
|
||||
# seriestype := :path
|
||||
# ()
|
||||
# end
|
||||
# @deps density path
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# contourf - filled contours
|
||||
@@ -790,6 +767,8 @@ end
|
||||
@deps xerror path
|
||||
|
||||
|
||||
# TODO: move quiver to PlotRecipes
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# quiver
|
||||
|
||||
@@ -890,56 +869,10 @@ end
|
||||
@deps quiver shape path
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# function rotate(x::Real, y::Real, θ::Real; center = (0,0))
|
||||
# cx = x - center[1]
|
||||
# cy = y - center[2]
|
||||
# xrot = cx * cos(θ) - cy * sin(θ)
|
||||
# yrot = cy * cos(θ) + cx * sin(θ)
|
||||
# xrot + center[1], yrot + center[2]
|
||||
# end
|
||||
#
|
||||
# # ---------------------------------------------------------------------------
|
||||
#
|
||||
# type EllipseRecipe <: PlotRecipe
|
||||
# w::Float64
|
||||
# h::Float64
|
||||
# x::Float64
|
||||
# y::Float64
|
||||
# θ::Float64
|
||||
# end
|
||||
# EllipseRecipe(w,h,x,y) = EllipseRecipe(w,h,x,y,0)
|
||||
#
|
||||
# # return x,y coords of a rotated ellipse, centered at the origin
|
||||
# function rotatedEllipse(w, h, x, y, θ, rotθ)
|
||||
# # # coord before rotation
|
||||
# xpre = w * cos(θ)
|
||||
# ypre = h * sin(θ)
|
||||
#
|
||||
# # rotate and translate
|
||||
# r = rotate(xpre, ypre, rotθ)
|
||||
# x + r[1], y + r[2]
|
||||
# end
|
||||
#
|
||||
# function getRecipeXY(ep::EllipseRecipe)
|
||||
# x, y = unzip([rotatedEllipse(ep.w, ep.h, ep.x, ep.y, u, ep.θ) for u in linspace(0,2π,100)])
|
||||
# top = rotate(0, ep.h, ep.θ)
|
||||
# right = rotate(ep.w, 0, ep.θ)
|
||||
# linex = Float64[top[1], 0, right[1]] + ep.x
|
||||
# liney = Float64[top[2], 0, right[2]] + ep.y
|
||||
# Any[x, linex], Any[y, liney]
|
||||
# end
|
||||
#
|
||||
# function getRecipeArgs(ep::EllipseRecipe)
|
||||
# [(:line, (3, [:dot :solid], [:red :blue], :path))]
|
||||
# end
|
||||
|
||||
# -------------------------------------------------
|
||||
|
||||
# TODO: this should really be in another package...
|
||||
# TODO: move OHLC to PlotRecipes finance.jl
|
||||
|
||||
type OHLC{T<:Real}
|
||||
open::T
|
||||
high::T
|
||||
|
||||
@@ -1,81 +1,106 @@
|
||||
|
||||
# we are going to build recipes to do the processing and splitting of the args
|
||||
|
||||
|
||||
function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
pkg = plt.backend
|
||||
globalIndex = d[:series_plotindex]
|
||||
# create a new "build_series_args" which converts all inputs into xs = Any[xitems], ys = Any[yitems].
|
||||
# Special handling for: no args, xmin/xmax, parametric, dataframes
|
||||
# Then once inputs have been converted, build the series args, map functions, etc.
|
||||
# This should cut down on boilerplate code and allow more focused dispatch on type
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
# add default values to our dictionary, being careful not to delete what we just added!
|
||||
for (k,v) in _series_defaults
|
||||
slice_arg!(d, d, k, v, commandIndex, remove_pair = false)
|
||||
end
|
||||
typealias FuncOrFuncs @compat(Union{Function, AVec{Function}})
|
||||
|
||||
# this is how many series belong to this subplot
|
||||
plotIndex = count(series -> series.d[:subplot] === sp && series.d[:primary], plt.series_list)
|
||||
if get(d, :primary, true)
|
||||
plotIndex += 1
|
||||
end
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
|
||||
aliasesAndAutopick(d, :linestyle, _styleAliases, supported_styles(pkg), plotIndex)
|
||||
aliasesAndAutopick(d, :markershape, _markerAliases, supported_markers(pkg), plotIndex)
|
||||
# missing
|
||||
convertToAnyVector(v::@compat(Void), d::KW) = Any[nothing], nothing
|
||||
|
||||
# update color
|
||||
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], sp, plotIndex)
|
||||
# fixed number of blank series
|
||||
convertToAnyVector(n::Integer, d::KW) = Any[zeros(0) for i in 1:n], nothing
|
||||
|
||||
# update colors
|
||||
for csym in (:linecolor, :markercolor, :fillcolor)
|
||||
d[csym] = if d[csym] == :match
|
||||
if has_black_border_for_default(d[:seriestype]) && csym == :linecolor
|
||||
:black
|
||||
else
|
||||
d[:seriescolor]
|
||||
end
|
||||
else
|
||||
getSeriesRGBColor(d[csym], sp, plotIndex)
|
||||
end
|
||||
end
|
||||
# numeric vector
|
||||
convertToAnyVector{T<:Number}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
|
||||
# update markerstrokecolor
|
||||
c = d[:markerstrokecolor]
|
||||
c = if c == :match
|
||||
sp[:foreground_color_subplot]
|
||||
# string vector
|
||||
convertToAnyVector{T<:@compat(AbstractString)}(v::AVec{T}, d::KW) = Any[v], nothing
|
||||
|
||||
function convertToAnyVector(v::AMat, d::KW)
|
||||
if all3D(d)
|
||||
Any[Surface(v)]
|
||||
else
|
||||
getSeriesRGBColor(c, sp, plotIndex)
|
||||
end
|
||||
d[:markerstrokecolor] = c
|
||||
|
||||
# update alphas
|
||||
for asym in (:linealpha, :markeralpha, :fillalpha)
|
||||
if d[asym] == nothing
|
||||
d[asym] = d[:seriesalpha]
|
||||
end
|
||||
end
|
||||
if d[:markerstrokealpha] == nothing
|
||||
d[:markerstrokealpha] = d[:markeralpha]
|
||||
end
|
||||
|
||||
# scatter plots don't have a line, but must have a shape
|
||||
if d[:seriestype] in (:scatter, :scatter3d)
|
||||
d[:linewidth] = 0
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
end
|
||||
end
|
||||
|
||||
# set label
|
||||
label = d[:label]
|
||||
label = (label == "AUTO" ? "y$globalIndex" : label)
|
||||
d[:label] = label
|
||||
|
||||
_replace_linewidth(d)
|
||||
d
|
||||
Any[v[:,i] for i in 1:size(v,2)]
|
||||
end, nothing
|
||||
end
|
||||
|
||||
# -------------------------------------------------------------------
|
||||
# -------------------------------------------------------------------
|
||||
# function
|
||||
convertToAnyVector(f::Function, d::KW) = Any[f], nothing
|
||||
|
||||
# instead of process_inputs:
|
||||
# surface
|
||||
convertToAnyVector(s::Surface, d::KW) = Any[s], nothing
|
||||
|
||||
# # vector of OHLC
|
||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||
|
||||
# dates
|
||||
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
||||
|
||||
# list of things (maybe other vectors, functions, or something else)
|
||||
function convertToAnyVector(v::AVec, d::KW)
|
||||
if all(x -> typeof(x) <: Number, v)
|
||||
# all real numbers wrap the whole vector as one item
|
||||
Any[convert(Vector{Float64}, v)], nothing
|
||||
else
|
||||
# something else... treat each element as an item
|
||||
vcat(Any[convertToAnyVector(vi, d)[1] for vi in v]...), nothing
|
||||
# Any[vi for vi in v], nothing
|
||||
end
|
||||
end
|
||||
|
||||
convertToAnyVector(t::Tuple, d::KW) = Any[t], nothing
|
||||
|
||||
|
||||
function convertToAnyVector(args...)
|
||||
error("In convertToAnyVector, could not handle the argument types: $(map(typeof, args[1:end-1]))")
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
# TODO: can we avoid the copy here? one error that crops up is that mapping functions over the same array
|
||||
# result in that array being shared. push!, etc will add too many items to that array
|
||||
|
||||
compute_x(x::Void, y::Void, z) = 1:size(z,1)
|
||||
compute_x(x::Void, y, z) = 1:size(y,1)
|
||||
compute_x(x::Function, y, z) = map(x, y)
|
||||
compute_x(x, y, z) = copy(x)
|
||||
|
||||
# compute_y(x::Void, y::Function, z) = error()
|
||||
compute_y(x::Void, y::Void, z) = 1:size(z,2)
|
||||
compute_y(x, y::Function, z) = map(y, x)
|
||||
compute_y(x, y, z) = copy(y)
|
||||
|
||||
compute_z(x, y, z::Function) = map(z, x, y)
|
||||
compute_z(x, y, z::AbstractMatrix) = Surface(z)
|
||||
compute_z(x, y, z::Void) = nothing
|
||||
compute_z(x, y, z) = copy(z)
|
||||
|
||||
nobigs(v::AVec{BigFloat}) = map(Float64, v)
|
||||
nobigs(v::AVec{BigInt}) = map(Int64, v)
|
||||
nobigs(v) = v
|
||||
|
||||
@noinline function compute_xyz(x, y, z)
|
||||
x = compute_x(x,y,z)
|
||||
y = compute_y(x,y,z)
|
||||
z = compute_z(x,y,z)
|
||||
nobigs(x), nobigs(y), nobigs(z)
|
||||
end
|
||||
|
||||
# not allowed
|
||||
compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
|
||||
|
||||
# we are going to build recipes to do the processing and splitting of the args
|
||||
|
||||
# ensure we dispatch to the slicer
|
||||
immutable SliceIt end
|
||||
@@ -232,33 +257,30 @@ end
|
||||
# # plotting arbitrary shapes/polygons
|
||||
|
||||
@recipe function f(shape::Shape)
|
||||
seriestype := :shape
|
||||
seriestype --> :shape
|
||||
shape_coords(shape)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AVec{Shape})
|
||||
seriestype := :shape
|
||||
seriestype --> :shape
|
||||
shape_coords(shapes)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AMat{Shape})
|
||||
seriestype --> :shape
|
||||
for j in 1:size(shapes,2)
|
||||
# create one series for each column
|
||||
# @series shape_coords(vec(shapes[:,j]))
|
||||
di = copy(d)
|
||||
push!(series_list, RecipeData(di, shape_coords(vec(shapes[:,j]))))
|
||||
@series shape_coords(vec(shapes[:,j]))
|
||||
end
|
||||
nothing # don't create a series for the main block
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# # function without range... use the current range of the x-axis
|
||||
|
||||
@recipe function f(f::FuncOrFuncs)
|
||||
plt = d[:plot_object]
|
||||
f, xmin(plt), xmax(plt)
|
||||
end
|
||||
# @recipe function f(f::FuncOrFuncs)
|
||||
# plt = d[:plot_object]
|
||||
# f, xmin(plt), xmax(plt)
|
||||
# end
|
||||
|
||||
#
|
||||
# # --------------------------------------------------------------------
|
||||
+1
-1
@@ -30,7 +30,7 @@ get_subplot(plt::Plot, i::Integer) = plt.subplots[i]
|
||||
get_subplot(plt::Plot, k) = plt.spmap[k]
|
||||
get_subplot(series::Series) = series.d[:subplot]
|
||||
|
||||
get_subplot_index(plt::Plot, idx::Integer) = idx
|
||||
get_subplot_index(plt::Plot, idx::Integer) = Int(idx)
|
||||
get_subplot_index(plt::Plot, sp::Subplot) = findfirst(_ -> _ === sp, plt.subplots)
|
||||
|
||||
series_list(sp::Subplot) = filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
|
||||
|
||||
+51
-30
@@ -138,24 +138,41 @@ end
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
|
||||
type Segments
|
||||
pts::Vector{Float64}
|
||||
type Segments{T}
|
||||
pts::Vector{T}
|
||||
end
|
||||
|
||||
Segments() = Segments(zeros(0))
|
||||
# Segments() = Segments{Float64}(zeros(0))
|
||||
|
||||
function Base.push!(segments::Segments, vs...)
|
||||
push!(segments.pts, NaN)
|
||||
Segments() = Segments(Float64)
|
||||
Segments{T}(::Type{T}) = Segments(T[])
|
||||
Segments(p::Int) = Segments(NTuple{2,Float64}[])
|
||||
|
||||
|
||||
# Segments() = Segments(zeros(0))
|
||||
|
||||
to_nan(::Type{Float64}) = NaN
|
||||
to_nan(::Type{NTuple{2,Float64}}) = (NaN, NaN)
|
||||
|
||||
coords(segs::Segments{Float64}) = segs.pts
|
||||
coords(segs::Segments{NTuple{2,Float64}}) = Float64[p[1] for p in segs.pts], Float64[p[2] for p in segs.pts]
|
||||
|
||||
function Base.push!{T}(segments::Segments{T}, vs...)
|
||||
if !isempty(segments.pts)
|
||||
push!(segments.pts, to_nan(T))
|
||||
end
|
||||
for v in vs
|
||||
push!(segments.pts, v)
|
||||
push!(segments.pts, convert(T,v))
|
||||
end
|
||||
segments
|
||||
end
|
||||
|
||||
function Base.push!(segments::Segments, vs::AVec)
|
||||
push!(segments.pts, NaN)
|
||||
function Base.push!{T}(segments::Segments{T}, vs::AVec)
|
||||
if !isempty(segments.pts)
|
||||
push!(segments.pts, to_nan(T))
|
||||
end
|
||||
for v in vs
|
||||
push!(segments.pts, v)
|
||||
push!(segments.pts, convert(T,v))
|
||||
end
|
||||
segments
|
||||
end
|
||||
@@ -166,28 +183,33 @@ end
|
||||
|
||||
type SegmentsIterator
|
||||
args::Tuple
|
||||
nextidx::Int
|
||||
n::Int
|
||||
end
|
||||
function iter_segments(args...)
|
||||
tup = Plots.wraptuple(args)
|
||||
n = maximum(map(length, tup))
|
||||
SegmentsIterator(tup, 0, n)
|
||||
SegmentsIterator(tup, n)
|
||||
end
|
||||
|
||||
# helpers to figure out if there are NaN values in a list of array types
|
||||
anynan(i::Int, args...) = any(a -> !isfinite(cycle(a,i)), args)
|
||||
anynan(istart::Int, iend::Int, args...) = any(i -> anynan(i, args...), istart:iend)
|
||||
allnan(istart::Int, iend::Int, args...) = all(i -> anynan(i, args...), istart:iend)
|
||||
anynan(i::Int, args::Tuple) = any(a -> !isfinite(cycle(a,i)), args)
|
||||
anynan(istart::Int, iend::Int, args::Tuple) = any(i -> anynan(i, args), istart:iend)
|
||||
allnan(istart::Int, iend::Int, args::Tuple) = all(i -> anynan(i, args), istart:iend)
|
||||
|
||||
Base.start(itr::SegmentsIterator) = (itr.nextidx = 1) #resets
|
||||
Base.done(itr::SegmentsIterator, unused::Int) = itr.nextidx > itr.n
|
||||
function Base.next(itr::SegmentsIterator, unused::Int)
|
||||
i = istart = iend = itr.nextidx
|
||||
function Base.start(itr::SegmentsIterator)
|
||||
nextidx = 1
|
||||
if anynan(1, itr.args)
|
||||
_, nextidx = next(itr, 1)
|
||||
end
|
||||
nextidx
|
||||
end
|
||||
Base.done(itr::SegmentsIterator, nextidx::Int) = nextidx > itr.n
|
||||
function Base.next(itr::SegmentsIterator, nextidx::Int)
|
||||
i = istart = iend = nextidx
|
||||
|
||||
# find the next NaN, and iend is the one before
|
||||
while i <= itr.n + 1
|
||||
if i > itr.n || anynan(i, itr.args...)
|
||||
if i > itr.n || anynan(i, itr.args)
|
||||
# done... array end or found NaN
|
||||
iend = i-1
|
||||
break
|
||||
@@ -195,16 +217,15 @@ function Base.next(itr::SegmentsIterator, unused::Int)
|
||||
i += 1
|
||||
end
|
||||
|
||||
# find the next non-NaN, and set itr.nextidx
|
||||
# find the next non-NaN, and set nextidx
|
||||
while i <= itr.n
|
||||
if !anynan(i, itr.args...)
|
||||
if !anynan(i, itr.args)
|
||||
break
|
||||
end
|
||||
i += 1
|
||||
end
|
||||
|
||||
itr.nextidx = i
|
||||
istart:iend, 0
|
||||
istart:iend, i
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
@@ -224,6 +245,8 @@ Base.cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
Base.cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
Base.cycle(v, idx::AVec{Int}) = v
|
||||
|
||||
Base.cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
|
||||
|
||||
makevec(v::AVec) = v
|
||||
makevec{T}(v::T) = T[v]
|
||||
|
||||
@@ -259,6 +282,7 @@ function _expand_limits(lims, x)
|
||||
nothing
|
||||
end
|
||||
|
||||
expand_data(v, n::Integer) = [cycle(v, i) for i=1:n]
|
||||
|
||||
# if the type exists in a list, replace the first occurence. otherwise add it to the end
|
||||
function addOrReplace(v::AbstractVector, t::DataType, args...; kw...)
|
||||
@@ -277,20 +301,16 @@ function replaceType(vec, val)
|
||||
push!(vec, val)
|
||||
end
|
||||
|
||||
function replaceAlias!(d::KW, k::Symbol, aliases::KW)
|
||||
function replaceAlias!(d::KW, k::Symbol, aliases::Dict{Symbol,Symbol})
|
||||
if haskey(aliases, k)
|
||||
d[aliases[k]] = pop!(d, k)
|
||||
end
|
||||
end
|
||||
|
||||
function replaceAliases!(d::KW, aliases::KW)
|
||||
function replaceAliases!(d::KW, aliases::Dict{Symbol,Symbol})
|
||||
ks = collect(keys(d))
|
||||
for k in ks
|
||||
replaceAlias!(d, k, aliases)
|
||||
# if haskey(aliases, k)
|
||||
# d[aliases[k]] = d[k]
|
||||
# delete!(d, k)
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -363,6 +383,7 @@ is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2
|
||||
|
||||
|
||||
isvertical(d::KW) = get(d, :orientation, :vertical) in (:vertical, :v, :vert)
|
||||
isvertical(series::Series) = isvertical(series.d)
|
||||
|
||||
|
||||
# ticksType{T<:Real,S<:Real}(ticks::@compat(Tuple{T,S})) = :limits
|
||||
@@ -422,7 +443,7 @@ end
|
||||
# this is a helper function to determine whether we need to transpose a surface matrix.
|
||||
# it depends on whether the backend matches rows to x (transpose_on_match == true) or vice versa
|
||||
# for example: PyPlot sends rows to y, so transpose_on_match should be true
|
||||
function transpose_z(d::KW, z, transpose_on_match::Bool = true)
|
||||
function transpose_z(d, z, transpose_on_match::Bool = true)
|
||||
if d[:match_dimensions] == transpose_on_match
|
||||
z'
|
||||
else
|
||||
|
||||
+4
-2
@@ -1,12 +1,14 @@
|
||||
julia 0.4
|
||||
|
||||
RecipesBase
|
||||
Colors
|
||||
PlotUtils
|
||||
StatPlots
|
||||
Reexport
|
||||
Measures
|
||||
Showoff
|
||||
FactCheck
|
||||
Images
|
||||
PyPlot
|
||||
ImageMagick
|
||||
@osx QuartzImageIO
|
||||
GR
|
||||
DataFrames
|
||||
|
||||
+10
-8
@@ -13,7 +13,9 @@ try
|
||||
end
|
||||
|
||||
|
||||
using Plots, FactCheck
|
||||
using Plots
|
||||
using StatPlots
|
||||
using FactCheck
|
||||
using Glob
|
||||
|
||||
default(size=(500,300))
|
||||
@@ -22,7 +24,7 @@ default(size=(500,300))
|
||||
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
||||
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
||||
|
||||
const _current_plots_version = v"0.7.5"
|
||||
const _current_plots_version = v"0.8.1"
|
||||
|
||||
|
||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||
@@ -76,12 +78,12 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
|
||||
png(fn)
|
||||
end
|
||||
|
||||
try
|
||||
run(`mkdir -p $newdir`)
|
||||
catch err
|
||||
display(err)
|
||||
end
|
||||
# reffn = joinpath(refdir, "ref$idx.png")
|
||||
# try
|
||||
# run(`mkdir -p $newdir`)
|
||||
# catch err
|
||||
# display(err)
|
||||
# end
|
||||
# # reffn = joinpath(refdir, "ref$idx.png")
|
||||
|
||||
# the test
|
||||
vtest = VisualTest(func, reffn, idx)
|
||||
|
||||
+2
-2
@@ -23,14 +23,14 @@ facts("PyPlot") do
|
||||
@fact pyplot() --> Plots.PyPlotBackend()
|
||||
@fact backend() --> Plots.PyPlotBackend()
|
||||
|
||||
image_comparison_facts(:pyplot, skip=[30], eps=img_eps)
|
||||
image_comparison_facts(:pyplot, skip=[], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("GR") do
|
||||
@fact gr() --> Plots.GRBackend()
|
||||
@fact backend() --> Plots.GRBackend()
|
||||
|
||||
@linux_only image_comparison_facts(:gr, skip=[30], eps=img_eps)
|
||||
# @linux_only image_comparison_facts(:gr, skip=[], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("Plotly") do
|
||||
|
||||
+12
-11
@@ -1,25 +1,26 @@
|
||||
# Pkg.clone(pwd())
|
||||
# Pkg.build("Plots")
|
||||
# Pkg.clone("ImageMagick")
|
||||
# Pkg.build("ImageMagick")
|
||||
|
||||
Pkg.clone("ImageMagick")
|
||||
Pkg.build("ImageMagick")
|
||||
# Pkg.clone("GR")
|
||||
# Pkg.build("GR")
|
||||
|
||||
Pkg.clone("GR")
|
||||
Pkg.build("GR")
|
||||
|
||||
# Pkg.clone("https://github.com/JuliaPlots/RecipesBase.jl.git")
|
||||
# Pkg.clone("https://github.com/tbreloff/ExamplePlots.jl.git");
|
||||
Pkg.clone("https://github.com/JuliaPlots/PlotReferenceImages.jl.git")
|
||||
|
||||
# Pkg.clone("https://github.com/JuliaStats/KernelDensity.jl.git")
|
||||
|
||||
Pkg.clone("StatPlots")
|
||||
Pkg.checkout("PlotUtils")
|
||||
|
||||
# Pkg.clone("https://github.com/JunoLab/Blink.jl.git")
|
||||
# Pkg.build("Blink")
|
||||
# import Blink
|
||||
# Blink.AtomShell.install()
|
||||
# Pkg.clone("https://github.com/spencerlyon2/PlotlyJS.jl.git")
|
||||
|
||||
Pkg.checkout("RecipesBase")
|
||||
Pkg.clone("VisualRegressionTests")
|
||||
# Pkg.checkout("RecipesBase")
|
||||
# Pkg.clone("VisualRegressionTests")
|
||||
|
||||
# need this to use Conda
|
||||
ENV["PYTHON"] = ""
|
||||
Pkg.add("PyPlot")
|
||||
Pkg.build("PyPlot")
|
||||
|
||||
Reference in New Issue
Block a user