Compare commits

...

39 Commits

Author SHA1 Message Date
Thomas Breloff c4bdc5b856 NEWS.md 2016-06-14 00:27:22 -04:00
Thomas Breloff b5b4023056 axis link arg; change those subplot/axis args set in kwlist to apply just to its own subplot; markerstrokealpha matches markeralpha 2016-06-14 00:10:26 -04:00
Thomas Breloff 0e598cc51d resolve relative coords for inset subplots 2016-06-13 17:46:19 -04:00
Thomas Breloff 9fd09924eb _shape_keys for consistent shape ordering 2016-06-13 16:57:26 -04:00
Thomas Breloff 10d218089f working on glvisualize 2016-06-13 16:15:45 -04:00
Thomas Breloff 1ba0d198a7 rename ellipse to circle; cleanup glvisualize 2016-06-13 15:59:51 -04:00
Thomas Breloff c44132d29d merge_with_base_supported updates 2016-06-13 15:45:35 -04:00
Thomas Breloff a018a2c07a series type dependencies and cleanup; bbox anchors in construction; fix for insets 2016-06-13 14:53:28 -04:00
Thomas Breloff 266d2efde5 inset_subplots; GR log axes fix 2016-06-13 12:27:48 -04:00
Thomas Breloff 3b33b054d3 plotly hover and scattergl 2016-06-13 11:04:56 -04:00
Thomas Breloff 1140b51e91 PLOTS_DEFAULTS and removed pyplot finalizer 2016-06-13 09:42:50 -04:00
Thomas Breloff 9d25c8fcd4 added PLOTS_DEFAULTS env var; check isfinite in axis_limits 2016-06-13 09:27:49 -04:00
Thomas Breloff e8ade18d47 code and utils for plotly shapes 2016-06-13 01:45:53 -04:00
Thomas Breloff ed243f4e3e added hover attribute and desc; fixed plotly title location; already_warned check 2016-06-13 00:15:28 -04:00
Thomas Breloff e0af4b7925 pyplot log scale fix; plotly title as annotation, title_location, hover placeholder 2016-06-12 23:48:18 -04:00
Thomas Breloff 80e53adbd8 getting plotly/plotlyjs up to speed 2016-06-12 21:03:11 -04:00
Thomas Breloff 36a29357a3 only widen axis limits for identity scale; closes #326 2016-06-12 20:22:57 -04:00
Thomas Breloff 5bb9aceaac pyplot log scale threshold fix; imgcomp fix to filter out higher versions 2016-06-12 13:06:15 -04:00
Thomas Breloff 02d893b316 big cleanup: changed camelcase to underscores; removed supportedAxes; changed pyplot methods to be py_ which matches gr_/plotly_/etc convention 2016-06-12 12:34:41 -04:00
Thomas Breloff b1850e58f4 gr cleanup commented code; bump version in imgcomp 2016-06-12 11:35:24 -04:00
Thomas Breloff f1e566dddc fix scale aliases 2016-06-12 11:00:42 -04:00
Thomas Breloff 2f60da33b7 changed pyplot log scale to symlog; #326 2016-06-12 10:28:41 -04:00
Thomas Breloff b0456d155a allow nothing for colorbar; gr fixes: marker_z colors, check colorbar arg 2016-06-11 15:55:55 -04:00
Tom Breloff 5f57a48996 Merge pull request #322 from jheinen/dev
Fixed GR output problem
2016-06-11 15:54:00 -04:00
Josef Heinen bc9d137001 Fixed GR output problem 2016-06-11 11:11:57 +02:00
Thomas Breloff 483946787a big GR refactor: axes drawing and cleanup; convert_to_polar and GR polar fill; should_add_to_legend fix; rename get_mod to Base.cycle 2016-06-11 02:03:46 -04:00
Thomas Breloff b165f233a8 animation fix; gr transparency 2016-06-10 16:17:30 -04:00
Thomas Breloff 3b325b2482 pyplot figure finalizer; accept 2-len AVec for limits (#289); sleep/skip/only options in test_examples 2016-06-10 10:04:33 -04:00
Tom Breloff 2dd480a3a6 Merge pull request #313 from jheinen/dev
Improved GR output
2016-06-10 08:56:53 -04:00
Josef Heinen df33ed40d6 Improved GR output
- slightly reduced the nominal marker size
- use character height as line spacing criterion when plotting legends
- use GR surface function for filled contours
- avoid calling GR setwindow/adjust* functions with Inf arguments
- corrected bounding box for legend
2016-06-10 13:39:57 +02:00
Thomas Breloff cf4d78c87c added clims attribute and support it in pyplot 2016-06-10 01:25:34 -04:00
Thomas Breloff 8007d0dd6a smarter cleanup of dicts; layout fixes: split out update_min_padding and added minimum_perimeter logic; check for nonempty axes when linking; add subplot processing and linking when combining plots 2016-06-10 00:16:13 -04:00
Thomas Breloff d7098f77c2 pgfplots display popup 2016-06-09 17:31:06 -04:00
Thomas Breloff 514284e784 pyplot display fixes for #308; setxyz fix; generic png conversion from pdf; pgfplots pdf output 2016-06-09 17:13:16 -04:00
Thomas Breloff 2a3029edb4 add build.jl 2016-06-09 12:08:36 -04:00
Thomas Breloff 45a1ed69d4 removed plotly-latest.min.js and added build.jl which downloads it; fix for quiver_using_hack; working on pyplot display behavior 2016-06-09 11:44:05 -04:00
Thomas Breloff 7248d8d7ce several fixes for type recipes; collect/zmin fixes in pgfplots; scatter3d recipe 2016-06-09 01:48:43 -04:00
Tom Breloff fa84401866 Merge pull request #310 from diegozea/dev
Add trim kw arg to violin and density
2016-06-09 01:45:43 -04:00
Diego Javier Zea 910b94c5fd Add trim kw arg to violin and density 2016-06-09 02:38:19 -03:00
36 changed files with 1589 additions and 1992 deletions
+1
View File
@@ -4,3 +4,4 @@
.DS_Store
examples/.ipynb_checkpoints/*
examples/meetup/.ipynb_checkpoints/*
deps/plotly-latest.min.js
+31
View File
@@ -9,6 +9,37 @@
## 0.7 (current master/dev)
#### 0.7.1
- inset (floating) subplots
- change: when setting subplot/axis args from user recipes, they should apply only to their own subplot
- trim for violin/boxplot
- scatter3d recipe
- removed plotly.js in favor of build.jl download
- improvements/fixes to pgfplots backend
- improvements/fixes to plotly/plotlyjs backends
- titles are annotations and properly placed with title_position
- hover attribute
- shapes (almost)
- scattergl
- minimum perimeter logic in layout calc... fixed misaligned subplots
- new clims attribute
- more options for test_examples
- GR refactor
- added transparency
- moved axis/grid logic out of series loop
- generalized 3d and polar projections
- renamed get_mod to Base.cycle
- pyplot log scale fixes
- PLOTS_DEFAULTS environment var processing
- rename :ellipse to :circle, :ellipse is now an alias
- supported args/types cleanup
- seriestype dependency methods and `@deps` macro
- bbox `h_anchor`/`v_anchor`
- new axis arg: `:link` is a list of subplots to link axes with
- cleanup/simplification of glvisualize backend
#### 0.7.0
- Check out [the summary](http://plots.readthedocs.io/en/latest/plots_v0.7/)
+8
View File
@@ -0,0 +1,8 @@
#TODO: download https://cdn.plot.ly/plotly-latest.min.js to deps/ if it doesn't exist
local_fn = joinpath(dirname(@__FILE__), "plotly-latest.min.js")
if !isfile(local_fn)
info("Cannot find deps/plotly-latest.min.js... downloading latest version.")
download("https://cdn.plot.ly/plotly-latest.min.js", local_fn)
end
-41
View File
File diff suppressed because one or more lines are too long
+17 -115
View File
@@ -19,14 +19,12 @@ export
GridLayout,
grid,
EmptyLayout,
bbox,
plotarea,
@layout,
# RowsLayout,
# FlexLayout,
AVec,
AMat,
KW,
# attr,
# attr!,
wrap,
set_theme,
@@ -34,8 +32,6 @@ export
plot,
plot!,
# subplot,
# subplot!,
current,
default,
@@ -43,53 +39,11 @@ export
@userplot,
@shorthands,
# scatter,
# scatter!,
# bar,
# bar!,
# barh,
# barh!,
# histogram,
# histogram!,
# histogram2d,
# histogram2d!,
# density,
# density!,
# heatmap,
# heatmap!,
# hexbin,
# hexbin!,
# sticks,
# sticks!,
# hline,
# hline!,
# vline,
# vline!,
# ohlc,
# ohlc!,
pie,
pie!,
# contour,
# contour!,
# contour3d,
# contour3d!,
# surface,
# surface!,
# wireframe,
# wireframe!,
# path3d,
# path3d!,
plot3d,
plot3d!,
# scatter3d,
# scatter3d!,
# abline!,
# boxplot,
# boxplot!,
# violin,
# violin!,
# quiver,
# quiver!,
title!,
xlabel!,
@@ -119,9 +73,6 @@ export
text,
font,
Axis,
# xaxis,
# yaxis,
# zaxis,
stroke,
brush,
Surface,
@@ -140,12 +91,11 @@ export
debugplots,
supportedArgs,
supportedAxes,
supportedTypes,
supportedStyles,
supportedMarkers,
subplotSupported,
supported_args,
supported_types,
supported_styles,
supported_markers,
is_subplot_supported,
Animation,
frame,
@@ -158,10 +108,6 @@ export
arcdiagram,
chorddiagram,
# @kw,
# @recipe,
# @plotrecipe,
test_examples,
translate,
@@ -178,9 +124,9 @@ export
# ---------------------------------------------------------
import Measures
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height, w, h
typealias BBox Measures.Absolute2DBox
export BBox, BoundingBox, mm, cm, inch, pt, px, pct
export BBox, BoundingBox, mm, cm, inch, pt, px, pct, w, h
# ---------------------------------------------------------
@@ -209,14 +155,11 @@ include("arg_desc.jl")
# define and export shorthand plotting method definitions
macro shorthands(funcname::Symbol)
funcname2 = Symbol(funcname, "!")
ret = esc(quote
esc(quote
export $funcname, $funcname2
$funcname(args...; kw...) = plot(args...; kw..., seriestype = $(quot(funcname)))
$funcname2(args...; kw...) = plot!(args...; kw..., seriestype = $(quot(funcname)))
end)
# dump(ret,20)
# @show ret
ret
end
@shorthands scatter
@@ -231,7 +174,6 @@ end
@shorthands hline
@shorthands vline
@shorthands ohlc
# @shorthands pie
@shorthands contour
@shorthands contour3d
@shorthands surface
@@ -242,52 +184,10 @@ end
@shorthands violin
@shorthands quiver
# scatter(args...; kw...) = plot(args...; kw..., seriestype = :scatter)
# scatter!(args...; kw...) = plot!(args...; kw..., seriestype = :scatter)
# bar(args...; kw...) = plot(args...; kw..., seriestype = :bar)
# bar!(args...; kw...) = plot!(args...; kw..., seriestype = :bar)
# barh(args...; kw...) = plot(args...; kw..., seriestype = :barh, orientation = :h)
# barh!(args...; kw...) = plot!(args...; kw..., seriestype = :barh, orientation = :h)
# histogram(args...; kw...) = plot(args...; kw..., seriestype = :histogram)
# histogram!(args...; kw...) = plot!(args...; kw..., seriestype = :histogram)
# histogram2d(args...; kw...) = plot(args...; kw..., seriestype = :histogram2d)
# histogram2d!(args...; kw...) = plot!(args...; kw..., seriestype = :histogram2d)
# density(args...; kw...) = plot(args...; kw..., seriestype = :density)
# density!(args...; kw...) = plot!(args...; kw..., seriestype = :density)
# heatmap(args...; kw...) = plot(args...; kw..., seriestype = :heatmap)
# heatmap!(args...; kw...) = plot!(args...; kw..., seriestype = :heatmap)
# hexbin(args...; kw...) = plot(args...; kw..., seriestype = :hexbin)
# hexbin!(args...; kw...) = plot!(args...; kw..., seriestype = :hexbin)
# sticks(args...; kw...) = plot(args...; kw..., seriestype = :sticks, marker = :ellipse)
# sticks!(args...; kw...) = plot!(args...; kw..., seriestype = :sticks, marker = :ellipse)
# hline(args...; kw...) = plot(args...; kw..., seriestype = :hline)
# hline!(args...; kw...) = plot!(args...; kw..., seriestype = :hline)
# vline(args...; kw...) = plot(args...; kw..., seriestype = :vline)
# vline!(args...; kw...) = plot!(args...; kw..., seriestype = :vline)
# ohlc(args...; kw...) = plot(args...; kw..., seriestype = :ohlc)
# ohlc!(args...; kw...) = plot!(args...; kw..., seriestype = :ohlc)
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
# contour(args...; kw...) = plot(args...; kw..., seriestype = :contour)
# contour!(args...; kw...) = plot!(args...; kw..., seriestype = :contour)
# contour3d(args...; kw...) = plot(args...; kw..., seriestype = :contour3d)
# contour3d!(args...; kw...) = plot!(args...; kw..., seriestype = :contour3d)
# surface(args...; kw...) = plot(args...; kw..., seriestype = :surface)
# surface!(args...; kw...) = plot!(args...; kw..., seriestype = :surface)
# wireframe(args...; kw...) = plot(args...; kw..., seriestype = :wireframe)
# wireframe!(args...; kw...) = plot!(args...; kw..., seriestype = :wireframe)
# path3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
# path3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
plot3d(args...; kw...) = plot(args...; kw..., seriestype = :path3d)
plot3d!(args...; kw...) = plot!(args...; kw..., seriestype = :path3d)
# scatter3d(args...; kw...) = plot(args...; kw..., seriestype = :scatter3d)
# scatter3d!(args...; kw...) = plot!(args...; kw..., seriestype = :scatter3d)
# boxplot(args...; kw...) = plot(args...; kw..., seriestype = :boxplot)
# boxplot!(args...; kw...) = plot!(args...; kw..., seriestype = :boxplot)
# violin(args...; kw...) = plot(args...; kw..., seriestype = :violin)
# violin!(args...; kw...) = plot!(args...; kw..., seriestype = :violin)
# quiver(args...; kw...) = plot(args...; kw..., seriestype = :quiver)
# quiver!(args...; kw...) = plot!(args...; kw..., seriestype = :quiver)
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
@@ -340,13 +240,15 @@ yaxis!(plt::Plot, args...; kw...) = plot!(pl
const CURRENT_BACKEND = CurrentBackend(:none)
# setup_dataframes()
function __init__()
setup_ijulia()
# setup_dataframes()
setup_atom()
# add_axis_letter_defaults()
if haskey(ENV, "PLOTS_DEFAULTS")
for (k,v) in eval(parse(ENV["PLOTS_DEFAULTS"]))
default(k, v)
end
end
end
# ---------------------------------------------------------
+1 -1
View File
@@ -24,7 +24,7 @@ immutable AnimatedGif
filename::Compat.ASCIIString
end
function gif(anim::Animation, fn::Compat.ASCIIString = "tmp.gif"; fps::Integer = 20)
function gif(anim::Animation, fn = "tmp.gif"; fps::Integer = 20)
fn = abspath(fn)
try
+3
View File
@@ -45,6 +45,7 @@ const _arg_desc = KW(
:subplot => "Integer (subplot index) or Subplot object. The subplot that this series belongs to.",
:series_annotations => "AbstractVector of String or PlotText. These are annotations which are mapped to data points/positions.",
:primary => "Bool. Does this count as a 'real series'? For example, you could have a path (primary), and a scatter (secondary) as 2 separate series, maybe with different data (see sticks recipe for an example). The secondary series will get the same color, etc as the primary.",
:hover => "nothing or vector of strings. Text to display when hovering over each data point.",
# plot args
:plot_title => "String. Title for the whole plot (not the subplots) (Note: Not currently implemented)",
@@ -59,6 +60,7 @@ const _arg_desc = KW(
:link => "Symbol. How/whether to link axis limits between subplots. Values: `:none`, `:x` (x axes are linked by columns), `:y` (y axes are linked by rows), `:both` (x and y are linked), `:all` (every subplot is linked together regardless of layout position).",
:overwrite_figure => "Bool. Should we reuse the same GUI window/figure when plotting (true) or open a new one (false).",
:html_output_format => "Symbol. When writing html output, what is the format? `:png` and `:svg` are currently supported.",
:inset_subplots => "nothing or vector of 2-tuple (parent,bbox). optionally pass a vector of (parent,bbox) tuples which are the parent layout and the relative bounding box of inset subplots",
# subplot args
:title => "String. Subplot title.",
@@ -74,6 +76,7 @@ const _arg_desc = KW(
:color_palette => "Vector of colors (cycle through) or color gradient (generate list from gradient) or `:auto` (generate a color list using `Colors.distiguishable_colors` and custom seed colors chosen to contrast with the background). The color palette is a color list from which series colors are automatically chosen.",
:legend => "Bool (show the legend?) or Symbol (legend position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:inside`, `:legend`, `:topright`, `:topleft`, `:bottomleft`, `:bottomright` (note: only some may be supported in each backend)",
:colorbar => "Bool (show the colorbar?) or Symbol (colorbar position). Symbol values: `:none`, `:best`, `:right`, `:left`, `:top`, `:bottom`, `:legend` (matches legend value) (note: only some may be supported in each backend)",
:clims => "`:auto` or NTuple{2,Number}. Fixes the limits of the colorbar.",
:legendfont => "Font. Font of legend items.",
:grid => "Bool. Show the grid lines?",
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
+83 -77
View File
@@ -86,13 +86,14 @@ const _allStyles = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
:ddd => :dashdotdot,
)
const _allMarkers = vcat(:none, :auto, sort(collect(keys(_shapes))))
const _allMarkers = vcat(:none, :auto, _shape_keys) #sort(collect(keys(_shapes))))
@compat const _markerAliases = KW(
:n => :none,
:no => :none,
:a => :auto,
:circle => :ellipse,
:c => :ellipse,
:ellipse => :circle,
:c => :circle,
:circ => :circle,
:square => :rect,
:sq => :rect,
:r => :rect,
@@ -181,6 +182,7 @@ const _series_defaults = KW(
:series_annotations => [], # a list of annotations which apply to the coordinates of this series
:primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow
# one logical series to be broken up (path and markers, for example)
:hover => nothing, # text to display when hovering over the data points
)
@@ -197,6 +199,8 @@ const _plot_defaults = KW(
:link => :none,
:overwrite_figure => true,
:html_output_format => :auto,
:inset_subplots => nothing, # optionally pass a vector of (parent,bbox) tuples which are
# the parent layout and the relative bounding box of inset subplots
)
@@ -214,6 +218,7 @@ const _subplot_defaults = KW(
:color_palette => :auto,
:legend => :best,
:colorbar => :legend,
:clims => :auto,
:legendfont => font(8),
:grid => true,
:annotations => [], # annotation tuples... list of (x,y,annotation)
@@ -234,6 +239,7 @@ const _axis_defaults = KW(
:scale => :identity,
:rotation => 0,
:flip => false,
:link => [],
:tickfont => font(8),
:guidefont => font(11),
:foreground_color_axis => :match, # axis border/tick colors,
@@ -254,6 +260,7 @@ const _suppress_warnings = Set{Symbol}([
:plot_object,
:primary,
:smooth,
:relative_bbox,
])
# add defaults for the letter versions
@@ -266,6 +273,7 @@ for letter in (:x,:y,:z)
:scale,
:rotation,
:flip,
:link,
:tickfont,
:guidefont,
:foreground_color_axis,
@@ -289,10 +297,10 @@ const _all_defaults = KW[
]
const _all_args = sort(collect(union(map(keys, _all_defaults)...)))
supportedArgs(::AbstractBackend) = error("supportedArgs not defined") #_all_args
supportedArgs() = supportedArgs(backend())
supported_args(::AbstractBackend) = error("supported_args not defined") #_all_args
supported_args() = supported_args(backend())
RecipesBase.is_key_supported(k::Symbol) = (k in supportedArgs())
RecipesBase.is_key_supported(k::Symbol) = (k in supported_args())
# -----------------------------------------------------------------------------
@@ -388,14 +396,13 @@ add_aliases(:zticks, :ztick)
add_aliases(:zrotation, :zrot, :zr)
add_aliases(:legend, :leg, :key)
add_aliases(:colorbar, :cb, :cbar, :colorkey)
add_aliases(:clims, :clim, :cbarlims, :cbar_lims, :climits, :color_limits)
add_aliases(:smooth, :regression, :reg)
add_aliases(:levels, :nlevels, :nlev, :levs)
add_aliases(:size, :windowsize, :wsize)
add_aliases(:window_title, :windowtitle, :wtitle)
add_aliases(:show, :gui, :display)
add_aliases(:color_palette, :palette)
# add_aliases(:linkx, :xlink)
# add_aliases(:linky, :ylink)
add_aliases(:overwrite_figure, :clf, :clearfig, :overwrite, :reuse)
add_aliases(:xerror, :xerr, :xerrorbar)
add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar)
@@ -622,7 +629,7 @@ function preprocessArgs!(d::KW)
if haskey(d, :markershape)
d[:markershape] = _replace_markershape(d[:markershape])
elseif anymarker
d[:markershape_to_add] = :ellipse # add it after _apply_recipe
d[:markershape_to_add] = :circle # add it after _apply_recipe
end
# handle fill
@@ -723,37 +730,46 @@ end
# -----------------------------------------------------------------------------
function warnOnUnsupportedArgs(pkg::AbstractBackend, d::KW)
const _already_warned = Set()
function warnOnUnsupported_args(pkg::AbstractBackend, d::KW)
for k in sortedkeys(d)
k in supportedArgs(pkg) && continue
k in supported_args(pkg) && continue
k in _suppress_warnings && continue
if d[k] != default(k)
warn("Keyword argument $k not supported with $pkg. Choose from: $(supportedArgs(pkg))")
if !((pkg, k) in _already_warned)
push!(_already_warned, (pkg,k))
warn("Keyword argument $k not supported with $pkg. Choose from: $(supported_args(pkg))")
end
end
end
end
_markershape_supported(pkg::AbstractBackend, shape::Symbol) = shape in supportedMarkers(pkg)
_markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supportedMarkers(pkg)
_markershape_supported(pkg::AbstractBackend, shape::Symbol) = shape in supported_markers(pkg)
_markershape_supported(pkg::AbstractBackend, shape::Shape) = Shape in supported_markers(pkg)
_markershape_supported(pkg::AbstractBackend, shapes::AVec) = all([_markershape_supported(pkg, shape) for shape in shapes])
function warnOnUnsupported(pkg::AbstractBackend, d::KW)
(d[:seriestype] == :none
|| d[:seriestype] in supportedTypes(pkg)
|| warn("seriestype $(d[:seriestype]) is unsupported with $pkg. Choose from: $(supportedTypes(pkg))"))
(d[:linestyle] in supportedStyles(pkg)
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supportedStyles(pkg))"))
|| d[:seriestype] in supported_types(pkg)
|| warn("seriestype $(d[:seriestype]) is unsupported with $pkg. Choose from: $(supported_types(pkg))"))
(d[:linestyle] in supported_styles(pkg)
|| warn("linestyle $(d[:linestyle]) is unsupported with $pkg. Choose from: $(supported_styles(pkg))"))
(d[:markershape] == :none
|| _markershape_supported(pkg, d[:markershape])
|| warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supportedMarkers(pkg))"))
|| warn("markershape $(d[:markershape]) is unsupported with $pkg. Choose from: $(supported_markers(pkg))"))
end
function warnOnUnsupportedScales(pkg::AbstractBackend, d::KW)
for k in (:xscale, :yscale)
if haskey(d, k)
d[k] in supportedScales(pkg) || warn("scale $(d[k]) is unsupported with $pkg. Choose from: $(supportedScales(pkg))")
function warnOnUnsupported_scales(pkg::AbstractBackend, d::KW)
for k in (:xscale, :yscale, :zscale)
if haskey(d, k)
v = d[k]
v = get(_scaleAliases, v, v)
if !(v in supported_scales(pkg))
warn("scale $(d[k]) is unsupported with $pkg. Choose from: $(supported_scales(pkg))")
end
end
end
end
end
@@ -771,6 +787,7 @@ function convertLegendValue(val::Symbol)
end
end
convertLegendValue(val::Bool) = val ? :best : :none
convertLegendValue(val::Void) = :none
# -----------------------------------------------------------------------------
@@ -896,87 +913,71 @@ end
# update attr from an input dictionary
function _update_plot_args(plt::Plot, d_in::KW)
pargs = plt.attr
for (k,v) in _plot_defaults
slice_arg!(d_in, pargs, k, v)
slice_arg!(d_in, plt.attr, k, v)
end
# handle colors
bg = convertColor(pargs[:background_color])
fg = pargs[:foreground_color]
bg = convertColor(plt.attr[:background_color])
fg = plt.attr[:foreground_color]
if fg == :auto
fg = isdark(bg) ? colorant"white" : colorant"black"
end
pargs[:background_color] = bg
pargs[:foreground_color] = convertColor(fg)
# color_or_match!(pargs, :background_color_outside, bg)
color_or_nothing!(pargs, :background_color_outside)
plt.attr[:background_color] = bg
plt.attr[:foreground_color] = convertColor(fg)
# color_or_match!(plt.attr, :background_color_outside, bg)
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)
pargs = plt.attr
spargs = sp.attr
anns = pop!(sp.attr, :annotations, [])
# grab those args which apply to this subplot
for (k,v) in _subplot_defaults
slice_arg!(d_in, spargs, k, v, subplot_index, remove_pair = remove_pair)
slice_arg!(d_in, sp.attr, k, v, subplot_index, remove_pair = remove_pair)
end
# extend annotations
sp.attr[:annotations] = vcat(anns, sp[:annotations])
# handle legend/colorbar
spargs[:legend] = convertLegendValue(spargs[:legend])
spargs[:colorbar] = convertLegendValue(spargs[:colorbar])
if spargs[:colorbar] == :legend
spargs[:colorbar] = spargs[:legend]
sp.attr[:legend] = convertLegendValue(sp.attr[:legend])
sp.attr[:colorbar] = convertLegendValue(sp.attr[:colorbar])
if sp.attr[:colorbar] == :legend
sp.attr[:colorbar] = sp.attr[:legend]
end
# background colors
# bg = color_or_match!(spargs, :background_color_subplot, pargs[:background_color])
color_or_nothing!(spargs, :background_color_subplot)
bg = sp[:background_color_subplot]
spargs[:color_palette] = get_color_palette(spargs[:color_palette], bg, 30)
# color_or_match!(spargs, :background_color_legend, bg)
color_or_nothing!(spargs, :background_color_legend)
# color_or_match!(spargs, :background_color_inside, bg)
color_or_nothing!(spargs, :background_color_inside)
# 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])
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)
# foreground colors
# fg = color_or_match!(spargs, :foreground_color_subplot, pargs[:foreground_color])
color_or_nothing!(spargs, :foreground_color_subplot)
# color_or_match!(spargs, :foreground_color_legend, fg)
color_or_nothing!(spargs, :foreground_color_legend)
# color_or_match!(spargs, :foreground_color_grid, fg)
color_or_nothing!(spargs, :foreground_color_grid)
# color_or_match!(spargs, :foreground_color_title, fg)
color_or_nothing!(spargs, :foreground_color_title)
color_or_nothing!(sp.attr, :foreground_color_subplot)
color_or_nothing!(sp.attr, :foreground_color_legend)
color_or_nothing!(sp.attr, :foreground_color_grid)
color_or_nothing!(sp.attr, :foreground_color_title)
# for k in (:left_margin, :top_margin, :right_margin, :bottom_margin)
# if spargs[k] == :match
# spargs[k] = spargs[:margin]
# if sp.attr[k] == :match
# sp.attr[k] = sp.attr[:margin]
# end
# end
for letter in (:x, :y, :z)
# get (maybe initialize) the axis
axissym = Symbol(letter, :axis)
axis = if haskey(spargs, axissym)
spargs[axissym]
else
spargs[axissym] = Axis(sp, letter)
end
axis = get_axis(sp, letter)
# grab magic args (for example `xaxis = (:flip, :log)`)
args = wraptuple(get(d_in, axissym, ()))
args = wraptuple(get(d_in, Symbol(letter, :axis), ()))
# build the KW of arguments from the letter version (i.e. xticks --> ticks)
kw = KW()
# DD(d_in, "d_in before")
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)
@@ -987,29 +988,34 @@ function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::I
# 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(pop!(d_in, lk), subplot_index)
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_match!(axis.d, :foreground_color_axis, fg)
color_or_nothing!(axis.d, :foreground_color_axis)
# color_or_match!(axis.d, :foreground_color_border, fg)
color_or_nothing!(axis.d, :foreground_color_border)
# color_or_match!(axis.d, :foreground_color_guide, fg)
color_or_nothing!(axis.d, :foreground_color_guide)
# color_or_match!(axis.d, :foreground_color_text, fg)
color_or_nothing!(axis.d, :foreground_color_text)
# TODO: need to handle linking here?
end
# now we can get rid of the axis keys without a letter
for k in keys(_axis_defaults)
delete!(d_in, k)
# 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
end
end
+24 -6
View File
@@ -32,6 +32,15 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
update!(Axis(sp, d), args...; kw...)
end
function get_axis(sp::Subplot, letter::Symbol)
axissym = Symbol(letter, :axis)
if haskey(sp.attr, axissym)
sp.attr[axissym]
else
sp.attr[axissym] = Axis(sp, letter)
end
end
function process_axis_arg!(d::KW, arg, letter = "")
T = typeof(arg)
arg = get(_scaleAliases, arg, arg)
@@ -91,6 +100,12 @@ function update!(axis::Axis, args...; kw...)
end
end
end
# replace scale aliases
if haskey(_scaleAliases, d[:scale])
d[:scale] = _scaleAliases[d[:scale]]
end
axis
end
@@ -213,12 +228,15 @@ function widen(lmin, lmax)
lmin-eps, lmax+eps
end
# figure out if widening is a good idea
# figure out if widening is a good idea. if there's a scale set it's too tricky,
# so lazy out and don't widen
function default_should_widen(axis::Axis)
should_widen = false
for series in series_list(axis.sp)
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
should_widen = true
if axis[:scale] == :identity
for series in series_list(axis.sp)
if series.d[:seriestype] in (:scatter,) || series.d[:markershape] != :none
should_widen = true
end
end
end
should_widen
@@ -229,7 +247,7 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
ex = axis[:extrema]
amin, amax = ex.emin, ex.emax
lims = axis[:lims]
if isa(lims, Tuple) && length(lims) == 2
if (isa(lims, Tuple) || isa(lims, AVec)) && length(lims) == 2
if isfinite(lims[1])
amin = lims[1]
end
@@ -237,7 +255,7 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
amax = lims[2]
end
end
if amax <= amin
if amax <= amin && isfinite(amin)
amax = amin + 1.0
end
if should_widen
+54 -15
View File
@@ -169,22 +169,61 @@ end
# ---------------------------------------------------------
supportedAxes(::AbstractBackend) = [:left]
supportedTypes(::AbstractBackend) = []
supportedStyles(::AbstractBackend) = [:solid]
supportedMarkers(::AbstractBackend) = [:none]
supportedScales(::AbstractBackend) = [:identity]
subplotSupported(::AbstractBackend) = false
stringsSupported(::AbstractBackend) = false
nativeImagesSupported(b::AbstractBackend) = :image in supportedTypes(b)
supported_types(::AbstractBackend) = []
supported_styles(::AbstractBackend) = [:solid]
supported_markers(::AbstractBackend) = [:none]
supported_scales(::AbstractBackend) = [:identity]
is_subplot_supported(::AbstractBackend) = false
is_string_supported(::AbstractBackend) = false
nativeImagesSupported(b::AbstractBackend) = :image in supported_types(b)
supportedAxes() = supportedAxes(backend())
supportedTypes() = supportedTypes(backend())
supportedStyles() = supportedStyles(backend())
supportedMarkers() = supportedMarkers(backend())
supportedScales() = supportedScales(backend())
subplotSupported() = subplotSupported(backend())
stringsSupported() = stringsSupported(backend())
supported_types() = supported_types(backend())
supported_styles() = supported_styles(backend())
supported_markers() = supported_markers(backend())
supported_scales() = supported_scales(backend())
is_subplot_supported() = is_subplot_supported(backend())
is_string_supported() = is_string_supported(backend())
nativeImagesSupported() = nativeImagesSupported(backend())
# ---------------------------------------------------------
# these are args which every backend supports because they're not used in the backend code
const _base_supported_args = [
:color_palette,
:background_color, :background_color_subplot,
:foreground_color, :foreground_color_subplot,
:group,
:seriestype,
:seriescolor, :seriesalpha,
:smooth,
:xerror, :yerror,
:subplot,
:x, :y, :z,
:show, :size,
:margin,
:left_margin,
:right_margin,
:top_margin,
:bottom_margin,
:html_output_format,
:layout,
:link,
:primary,
:series_annotations,
:subplot_index,
:discrete_values,
:projection,
]
function merge_with_base_supported(v::AVec)
v = vcat(v, _base_supported_args)
for vi in v
if haskey(_axis_defaults, vi)
for letter in (:x,:y,:z)
push!(v, Symbol(letter,vi))
end
end
end
v
end
+9 -10
View File
@@ -2,7 +2,7 @@
# https://github.com/bokeh/Bokeh.jl
supportedArgs(::BokehBackend) = [
supported_args(::BokehBackend) = merge_with_base_supported([
# :annotations,
# :axis,
# :background_color,
@@ -58,13 +58,12 @@ supportedArgs(::BokehBackend) = [
# :grid,
# :surface,
# :levels,
]
supportedAxes(::BokehBackend) = [:auto, :left]
supportedTypes(::BokehBackend) = [:none, :path, :scatter] #,:steppre, :steppost, :sticks, :histogram2d, :hexbin, :histogram, :bar, :hline, :vline, :contour]
supportedStyles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::BokehBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::BokehBackend) = [:identity, :ln] #, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::BokehBackend) = false
])
supported_types(::BokehBackend) = [:path, :scatter]
supported_styles(::BokehBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supported_markers(::BokehBackend) = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
supported_scales(::BokehBackend) = [:identity, :ln]
is_subplot_supported(::BokehBackend) = false
# --------------------------------------------------------------------------------------
@@ -81,7 +80,7 @@ end
const _glyphtypes = KW(
:ellipse => :Circle,
:circle => :Circle,
:rect => :Square,
:diamond => :Diamond,
:utriangle => :Triangle,
@@ -100,7 +99,7 @@ function bokeh_glyph_type(d::KW)
st = d[:seriestype]
mt = d[:markershape]
if st == :scatter && mt == :none
mt = :ellipse
mt = :circle
end
# if we have a marker, use that
+12 -14
View File
@@ -2,7 +2,7 @@
# https://github.com/dcjones/Gadfly.jl
supportedArgs(::GadflyBackend) = [
supported_args(::GadflyBackend) = merge_with_base_supported([
:annotations,
:background_color, :foreground_color, :color_palette,
:group, :label, :seriestype,
@@ -15,7 +15,6 @@ supportedArgs(::GadflyBackend) = [
:title, :window_title, :show, :size,
:x, :xguide, :xlims, :xticks, :xscale, :xflip,
:y, :yguide, :ylims, :yticks, :yscale, :yflip,
# :z, :zguide, :zlims, :zticks, :zscale, :zflip,
:z,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
@@ -23,18 +22,17 @@ supportedArgs(::GadflyBackend) = [
:xerror, :yerror,
:ribbon, :quiver,
:orientation,
]
supportedAxes(::GadflyBackend) = [:auto, :left]
supportedTypes(::GadflyBackend) = [
:none, :line, :path, :steppre, :steppost, :sticks,
:scatter, :histogram2d, :hexbin, :histogram,
:bar, #:box, :violin, :quiver,
:hline, :vline, :contour, :shape
])
supported_types(::GadflyBackend) = [
:path,
:scatter, :hexbin,
:bar,
:contour, :shape
]
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
supportedScales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::GadflyBackend) = true
supported_styles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supported_markers(::GadflyBackend) = vcat(_allMarkers, Shape)
supported_scales(::GadflyBackend) = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
is_subplot_supported(::GadflyBackend) = true
# --------------------------------------------------------------------------------------
@@ -303,7 +301,7 @@ function addGadflySeries!(plt::Plot, d::KW)
if st in (:histogram2d, :hexbin) && (isa(d[:fillcolor], ColorGradient) || isa(d[:fillcolor], ColorFunction))
push!(gplt.scales, Gadfly.Scale.ContinuousColorScale(p -> RGB(getColorZ(d[:fillcolor], p))))
elseif st == :scatter && d[:markershape] == :none
d[:markershape] = :ellipse
d[:markershape] = :circle
end
# markers
+1 -1
View File
@@ -84,7 +84,7 @@ function make_polygon(geom::ShapeGeometry, xs::AbstractArray, ys::AbstractArray,
x = Compose.x_measure(xs[mod1(i, length(xs))])
y = Compose.y_measure(ys[mod1(i, length(ys))])
r = rs[mod1(i, length(rs))]
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in get_mod(geom.vertices, i)]
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in cycle(geom.vertices, i)]
end
Gadfly.polygon(polys, geom.tag)
end
+66 -106
View File
@@ -2,78 +2,47 @@
# [WEBSITE]
supportedArgs(::GLVisualizeBackend) = [
supported_args(::GLVisualizeBackend) = merge_with_base_supported([
# :annotations,
# :axis,
# :background_color,
# :color_palette,
# :fillrange,
# :fillcolor,
# :fillalpha,
# :foreground_color,
# :group,
# :background_color_legend, :background_color_inside, :background_color_outside,
# :foreground_color_grid, :foreground_color_legend, :foreground_color_title,
# :foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
# :label,
# :layout,
# :legend,
# :linecolor,
# :linestyle,
:seriestype
# :seriescolor, :seriesalpha,
# :linewidth,
# :linealpha,
# :markershape,
# :markercolor,
# :markersize,
# :markeralpha,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
# :bins,
# :nc,
# :nr,
# :pos,
# :smooth,
# :show,
# :size,
# :title,
# :linecolor, :linestyle, :linewidth, :linealpha,
# :markershape, :markercolor, :markersize, :markeralpha,
# :markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
# :fillrange, :fillcolor, :fillalpha,
# :bins, :bar_width, :bar_edges, :bar_position,
# :title, :title_location, :titlefont,
# :window_title,
# :x,
# :xguide,
# :xlims,
# :xticks,
# :y,
# :yguide,
# :ylims,
# :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
# :tickfont,
# :guidefont,
# :legendfont,
# :grid,
# :surface
# :levels,
]
supportedAxes(::GLVisualizeBackend) = [:auto, :left]
supportedTypes(::GLVisualizeBackend) = [:surface] #, :path, :scatter ,:steppre, :steppost, :sticks, :heatmap, :hexbin, :histogram, :bar, :hline, :vline, :contour]
supportedStyles(::GLVisualizeBackend) = [:auto, :solid] #, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::GLVisualizeBackend) = [:none, :auto, :ellipse] #, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5] #vcat(_allMarkers, Shape)
supportedScales(::GLVisualizeBackend) = [:identity] #, :log, :log2, :log10, :asinh, :sqrt]
subplotSupported(::GLVisualizeBackend) = false
# :guide, :lims, :ticks, :scale, :flip, :rotation,
# :tickfont, :guidefont, :legendfont,
# :grid, :legend, :colorbar,
# :marker_z, :levels,
# :ribbon, :quiver, :arrow,
# :orientation,
# :overwrite_figure,
# :polar,
# :normalize, :weights,
# :contours, :aspect_ratio,
# :match_dimensions,
# :clims,
# :inset_subplots,
])
supported_types(::GLVisualizeBackend) = [:surface]
supported_styles(::GLVisualizeBackend) = [:auto, :solid]
supported_markers(::GLVisualizeBackend) = [:none, :auto, :circle]
supported_scales(::GLVisualizeBackend) = [:identity]
is_subplot_supported(::GLVisualizeBackend) = false
# --------------------------------------------------------------------------------------
function _initialize_backend(::GLVisualizeBackend; kw...)
@eval begin
import GLVisualize
export GLVisualize
end
@eval begin
import GLVisualize
export GLVisualize
end
end
# ---------------------------------------------------------------------------
@@ -82,55 +51,46 @@ immutable GLScreenWrapper
window
end
# function _create_plot(pkg::GLVisualizeBackend, d::KW)
function _create_backend_figure(plt::Plot{GLVisualizeBackend})
# TODO: create the window/canvas/context that is the plot within the backend
# TODO: initialize the plot... title, xlabel, bgcolor, etc
# init a window
window = GLVisualize.glscreen()
@async GLVisualize.renderloop(window)
window
end
# TODO: this should be moved to the display method?
w=GLVisualize.glscreen()
@async GLVisualize.renderloop(w)
GLScreenWrapper(w)
# Plot(GLScreenWrapper(w), pkg, 0, d, KW[])
function gl_display(plt::Plot{GLVisualizeBackend})
for sp in plt.subplots
# TODO: setup subplot
for series in series_list(sp)
# TODO: setup series
d = series.d
st = d[:seriestype]
x, y, z = map(Float32, d[:x]), map(Float32, d[:y]), d[:z]
if st == :surface
ismatrix(x) || (x = repmat(x', length(y), 1))
ismatrix(y) || (y = repmat(y, 1, length(x)))
z = transpose_z(d, map(Float32, z.surf), false)
viz = GLVisualize.visualize((x, y, z), :surface)
GLVisualize.view(viz, plt.o)
return
else
error("Series type $st not supported by GLVisualize")
end
end
end
end
# ----------------------------------------------------------------
function _series_added(plt::Plot{GLVisualizeBackend}, series::Series)
# TODO: add one series to the underlying package
# TODO: this should be moved to the display method?
x, y, z = map(Float32, series.d[:x]), map(Float32, series.d[:y]), map(Float32, series.d[:z].surf)
GLVisualize.view(GLVisualize.visualize((x*ones(y)', ones(x)*y', z), :surface), plt.o.window)
# plt
end
# When series data is added/changed, this callback can do dynamic updates to the backend object.
# note: if the backend rebuilds the plot from scratch on display, then you might not do anything here.
function _series_updated(plt::Plot{GLVisualizeBackend}, series::Series)
# TODO
end
# ----------------------------------------------------------------
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
function _update_plot_object(plt::Plot{GLVisualizeBackend})
# TODO
end
# ----------------------------------------------------------------
function _writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
# TODO: write a png to io
end
# function _writemime(io::IO, ::MIME"image/png", plt::AbstractPlot{GLVisualizeBackend})
# # TODO: write a png to io
# end
function _display(plt::Plot{GLVisualizeBackend})
# TODO: display/show the plot
# NOTE: I think maybe this should be empty? We can start with the assumption that creating
# and adding to a plot will automatically open a window and draw to it, then the display
# wouldn't actually need to do anything
gl_display(plt)
end
+275 -747
View File
File diff suppressed because it is too large Load Diff
+6 -7
View File
@@ -1,13 +1,12 @@
# https://github.com/JuliaGraphics/Immerse.jl
supportedArgs(::ImmerseBackend) = supportedArgs(GadflyBackend())
supportedAxes(::ImmerseBackend) = supportedAxes(GadflyBackend())
supportedTypes(::ImmerseBackend) = supportedTypes(GadflyBackend())
supportedStyles(::ImmerseBackend) = supportedStyles(GadflyBackend())
supportedMarkers(::ImmerseBackend) = supportedMarkers(GadflyBackend())
supportedScales(::ImmerseBackend) = supportedScales(GadflyBackend())
subplotSupported(::ImmerseBackend) = true
supported_args(::ImmerseBackend) = supported_args(GadflyBackend())
supported_types(::ImmerseBackend) = supported_types(GadflyBackend())
supported_styles(::ImmerseBackend) = supported_styles(GadflyBackend())
supported_markers(::ImmerseBackend) = supported_markers(GadflyBackend())
supported_scales(::ImmerseBackend) = supported_scales(GadflyBackend())
is_subplot_supported(::ImmerseBackend) = true
# --------------------------------------------------------------------------------------
+62 -37
View File
@@ -2,39 +2,28 @@
# significant contributions by: @pkofod
supportedArgs(::PGFPlotsBackend) = [
supported_args(::PGFPlotsBackend) = merge_with_base_supported([
# :annotations,
:background_color, :foreground_color,
:color_palette,
# :background_color_legend,
:background_color_inside,
# :background_color_outside,
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
# :foreground_color_text, :foreground_color_border,
:group,
:label,
:seriestype,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
:fillrange, :fillcolor, :fillalpha,
:bins,
# :bar_width, :bar_edges,
# :n, :nc, :nr,
:layout,
# :smooth,
:title,
# :window_title,
:show, :size,
:x, :xguide, :xlims, :xticks, :xscale, :xflip, :xrotation,
:y, :yguide, :ylims, :yticks, :yscale, :yflip, :yrotation,
:z, :zguide, :zlims, :zticks, :zscale, :zflip, :zrotation,
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend,
# :colorbar,
# :marker_z, :levels,
# :xerror, :yerror,
# :ribbon, :quiver, :arrow,
# :orientation,
# :overwrite_figure,
@@ -42,23 +31,22 @@ supportedArgs(::PGFPlotsBackend) = [
# :normalize, :weights, :contours,
:aspect_ratio,
# :match_dimensions,
]
supportedAxes(::PGFPlotsBackend) = [:auto, :left]
supportedTypes(::PGFPlotsBackend) = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
supportedStyles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supportedMarkers(::PGFPlotsBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
supportedScales(::PGFPlotsBackend) = [:identity, :ln, :log2, :log10] # :asinh, :sqrt]
subplotSupported(::PGFPlotsBackend) = false
])
supported_types(::PGFPlotsBackend) = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
supported_styles(::PGFPlotsBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
supported_markers(::PGFPlotsBackend) = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
supported_scales(::PGFPlotsBackend) = [:identity, :ln, :log2, :log10]
is_subplot_supported(::PGFPlotsBackend) = false
# --------------------------------------------------------------------------------------
function _initialize_backend(::PGFPlotsBackend; kw...)
@eval begin
import PGFPlots
export PGFPlots
end
@eval begin
import PGFPlots
export PGFPlots
end
end
@@ -78,7 +66,7 @@ const _pgfplots_markers = KW(
:xcross => "x",
:utriangle => "triangle*",
:dtriangle => "triangle*",
:ellipse => "*",
:circle => "*",
:rect => "square*",
:star5 => "star",
:star6 => "asterisk",
@@ -145,7 +133,6 @@ end
# --------------------------------------------------------------------------------------
function pgf_series(sp::Subplot, series::Series)
d = series.d
st = d[:seriestype]
@@ -173,6 +160,18 @@ function pgf_series(sp::Subplot, series::Series)
d[:x], d[:y]
end
# PGFPlots can't handle non-Vector?
args = map(a -> if typeof(a) <: AbstractVector && typeof(a) != Vector
collect(a)
else
a
end, args)
# for (i,a) in enumerate(args)
# if typeof(a) <: AbstractVector && typeof(a) != Vector
# args[i] = collect(a)
# end
# end
# include additional style, then add to the kw
if haskey(_pgf_series_extrastyle, st)
push!(style, _pgf_series_extrastyle[st])
@@ -216,9 +215,12 @@ function pgf_axis(sp::Subplot, letter)
end
# limits
lims = axis_limits(axis)
kw[Symbol(letter,:min)] = lims[1]
kw[Symbol(letter,:max)] = lims[2]
# TODO: support zlims
if letter != :z
lims = axis_limits(axis)
kw[Symbol(letter,:min)] = lims[1]
kw[Symbol(letter,:max)] = lims[2]
end
# return the style list and KW args
style, kw
@@ -281,16 +283,39 @@ end
function _writemime(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
writemime(io, mime, plt.o)
_make_pgf_plot!(plt)
writemime(io, mime, plt.o)
end
function _writemime(io::IO, mime::MIME"image/png", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
writemime(io, mime, plt.o)
function _writemime(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
# prepare the object
pgfplt = PGFPlots.plot(plt.o)
# save a pdf
fn = tempname()*".pdf"
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
# read it into io
write(io, readall(open(fn)))
# cleanup
PGFPlots.cleanup(plt.o)
end
function _display(plt::Plot{PGFPlotsBackend})
_make_pgf_plot!(plt)
display(plt.o)
# prepare the object
_make_pgf_plot!(plt)
pgfplt = PGFPlots.plot(plt.o)
# save an svg
fn = string(tempname(), ".svg")
PGFPlots.save(PGFPlots.SVG(fn), pgfplt)
# show it
open_browser_window(fn)
# cleanup
PGFPlots.cleanup(plt.o)
end
+133 -106
View File
@@ -1,49 +1,48 @@
# https://plot.ly/javascript/getting-started
supportedArgs(::PlotlyBackend) = [
supported_args(::PlotlyBackend) = merge_with_base_supported([
:annotations,
:background_color, :foreground_color, :color_palette,
# :background_color_legend, :background_color_inside, :background_color_outside,
# :foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
# :foreground_color_text, :foreground_color_border,
:group,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_legend, :foreground_color_guide,
# :foreground_color_grid, :foreground_color_axis,
:foreground_color_text, :foreground_color_border,
:foreground_color_title,
:label,
:seriestype,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha, :markerstrokestyle,
:fillrange, :fillcolor, :fillalpha,
:bins,
:n, :nc, :nr, :layout,
# :smooth,
:title, :window_title, :show, :size,
:x, :xguide, :xlims, :xticks, :xscale, :xflip, :xrotation,
:y, :yguide, :ylims, :yticks, :yscale, :yflip, :yrotation,
:z, :zguide, :zlims, :zticks, :zscale, :zflip, :zrotation,
:z,
:title, :title_location, :titlefont,
:window_title,
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
:marker_z, :levels,
:xerror, :yerror,
:ribbon, :quiver,
:orientation,
# :overwrite_figure,
:polar,
# :normalize, :weights, :contours, :aspect_ratio
]
:normalize, :weights,
# :contours, :aspect_ratio,
:hover,
:inset_subplots,
])
supportedAxes(::PlotlyBackend) = [:auto, :left]
supportedTypes(::PlotlyBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
:histogram2d, :histogram, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
supportedScales(::PlotlyBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::PlotlyBackend) = true
stringsSupported(::PlotlyBackend) = true
supported_types(::PlotlyBackend) = [
:path, :scatter, :bar, :pie, :heatmap,
:contour, :surface, :path3d, :scatter3d, :shape, :scattergl,
]
supported_styles(::PlotlyBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supported_markers(::PlotlyBackend) = [
:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle,
:cross, :xcross, :pentagon, :hexagon, :octagon, :vline, :hline
]
supported_scales(::PlotlyBackend) = [:identity, :log10]
is_subplot_supported(::PlotlyBackend) = true
is_string_supported(::PlotlyBackend) = true
# --------------------------------------------------------------------------------------
@@ -85,7 +84,7 @@ end
# ----------------------------------------------------------------
function plotlyfont(font::Font, color = font.color)
function plotly_font(font::Font, color = font.color)
KW(
:family => font.family,
:size => round(Int, font.pointsize*1.4),
@@ -93,20 +92,20 @@ function plotlyfont(font::Font, color = font.color)
)
end
function get_annotation_dict(x, y, val)
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
KW(
:text => val,
:xref => "x",
:xref => xref,
:x => x,
:yref => "y",
:yref => xref,
:y => y,
:showarrow => false,
)
end
function get_annotation_dict(x, y, ptxt::PlotText)
merge(get_annotation_dict(x, y, ptxt.str), KW(
:font => plotlyfont(ptxt.font),
function plotly_annotation_dict(x, y, ptxt::PlotText; xref="paper", yref="paper")
merge(plotly_annotation_dict(x, y, ptxt.str; xref=xref, yref=yref), KW(
:font => plotly_font(ptxt.font),
:xanchor => ptxt.font.halign == :hcenter ? :center : ptxt.font.halign,
:yanchor => ptxt.font.valign == :vcenter ? :middle : ptxt.font.valign,
:rotation => ptxt.font.rotation,
@@ -136,7 +135,7 @@ end
# )
# end
function plotlyscale(scale::Symbol)
function plotly_scale(scale::Symbol)
if scale == :log10
"log"
else
@@ -144,7 +143,6 @@ function plotlyscale(scale::Symbol)
end
end
use_axis_field(ticks) = !(ticks in (nothing, :none))
# this method gets the start/end in percentage of the canvas for this axis direction
function plotly_domain(sp::Subplot, letter)
@@ -157,19 +155,12 @@ end
function plotly_axis(axis::Axis, sp::Subplot)
letter = axis[:letter]
# d = axis.d
ax = KW(
:title => axis[:guide],
:showgrid => sp[:grid],
:zeroline => false,
)
# fgcolor = webcolor(axis[:foreground_color])
# tsym = tickssym(letter)
# spidx = sp[:subplot_index]
# d_out[:xaxis] = "x$spidx"
# d_out[:yaxis] = "y$spidx"
if letter in (:x,:y)
ax[:domain] = plotly_domain(sp, letter)
ax[:anchor] = "$(letter==:x ? :y : :x)$(plotly_subplot_index(sp))"
@@ -180,10 +171,10 @@ function plotly_axis(axis::Axis, sp::Subplot)
ax[:tickangle] = rot
end
if use_axis_field(axis[:ticks])
ax[:titlefont] = plotlyfont(axis[:guidefont], webcolor(axis[:foreground_color_guide]))
ax[:type] = plotlyscale(axis[:scale])
ax[:tickfont] = plotlyfont(axis[:tickfont], webcolor(axis[:foreground_color_text]))
if !(axis[:ticks] in (nothing, :none))
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])
@@ -218,32 +209,35 @@ function plotly_axis(axis::Axis, sp::Subplot)
ax
end
# function plotly_layout_json(plt::Plot{PlotlyBackend})
# d = plt
# function plotly_layout(d::KW, seriesargs::AVec{KW})
function plotly_layout(plt::Plot)
d_out = KW()
# # for now, we only support 1 subplot
# if length(plt.subplots) > 1
# warn("Subplots not supported yet")
# end
# sp = plt.subplots[1]
d_out[:width], d_out[:height] = plt[:size]
w, h = plt[:size]
d_out[:width], d_out[:height] = w, h
d_out[:paper_bgcolor] = webcolor(plt[:background_color_outside])
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>20)
d_out[:annotations] = KW[]
for sp in plt.subplots
sp_out = KW()
spidx = plotly_subplot_index(sp)
# set the fields for the plot
d_out[:title] = sp[:title]
d_out[:titlefont] = plotlyfont(sp[:titlefont], webcolor(sp[:foreground_color_title]))
# # TODO: use subplot positioning logic
# d_out[:margin] = KW(:l=>35, :b=>30, :r=>8, :t=>20)
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>30)
# add an annotation for the title... positioned horizontally relative to plotarea,
# but vertically just below the top of the subplot bounding box
if sp[:title] != ""
bb = plotarea(sp)
tpos = sp[:title_location]
xmm = if tpos == :left
left(bb)
elseif tpos == :right
right(bb)
else
0.5 * (left(bb) + right(bb))
end
titlex, titley = xy_mm_to_pcts(xmm, top(bbox(sp)), w*px, h*px)
titlefont = font(sp[:titlefont], :top, sp[:foreground_color_title])
push!(d_out[:annotations], plotly_annotation_dict(titlex, titley, text(sp[:title], titlefont)))
end
d_out[:plot_bgcolor] = webcolor(sp[:background_color_inside])
@@ -267,17 +261,12 @@ function plotly_layout(plt::Plot)
d_out[:legend] = KW(
:bgcolor => webcolor(sp[:background_color_legend]),
:bordercolor => webcolor(sp[:foreground_color_legend]),
:font => plotlyfont(sp[:legendfont]),
:font => plotly_font(sp[:legendfont], sp[:foreground_color_legend]),
)
end
# annotations
anns = sp[:annotations]
d_out[:annotations] = if isempty(anns)
KW[]
else
KW[get_annotation_dict(ann...) for ann in anns]
end
append!(d_out[:annotations], KW[plotly_annotation_dict(ann...; xref = "x$spidx", yref = "y$spidx") for ann in sp[:annotations]])
# # arrows
# for sargs in seriesargs
@@ -288,8 +277,6 @@ function plotly_layout(plt::Plot)
# end
# end
# end
# dumpdict(d_out,"",true)
# @show d_out[:annotations]
if ispolar(sp)
d_out[:direction] = "counterclockwise"
@@ -298,6 +285,11 @@ function plotly_layout(plt::Plot)
d_out
end
# turn off hover if nothing's using it
if all(series -> series.d[:hover] in (false,:none), plt.series_list)
d_out[:hovermode] = "none"
end
d_out
end
@@ -327,6 +319,17 @@ function plotly_subplot_index(sp::Subplot)
end
# the Shape contructor will automatically close the shape. since we need it closed,
# we split by NaNs and then construct/destruct the shapes to get the closed coords
function plotly_close_shapes(x, y)
xs, ys = nansplit(x), nansplit(y)
for i=1:length(xs)
shape = Shape(xs[i], ys[i])
xs[i], ys[i] = shape_coords(shape)
end
nanvcat(xs), nanvcat(ys)
end
# 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
@@ -337,17 +340,19 @@ function plotly_series(plt::Plot, series::Series)
spidx = plotly_subplot_index(sp)
d_out[:xaxis] = "x$spidx"
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]
isscatter = st in (:scatter, :scatter3d)
isscatter = st in (:scatter, :scatter3d, :scattergl)
hasmarker = isscatter || d[:markershape] != :none
hasline = !isscatter
# hasline = !isscatter
hasline = st in (:path, :path3d)
# set the "type"
if st in (:line, :path, :scatter, :steppre, :steppost)
d_out[:type] = "scatter"
if st in (:path, :scatter, :scattergl)
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
d_out[:mode] = if hasmarker
hasline ? "lines+markers" : "markers"
else
@@ -361,42 +366,58 @@ function plotly_series(plt::Plot, series::Series)
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[:markercolor], d[:markeralpha])
if d[:markerstrokewidth] > 0
d_out[:line] = KW(
:color => webcolor(d[:markerstrokecolor], d[:markerstrokealpha]),
:width => d[:markerstrokewidth],
:dash => string(d[:markerstrokestyle]),
)
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 == :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
# elseif st in (:histogram, :density)
# d_out[:type] = "histogram"
# isvert = isvertical(d)
# d_out[isvert ? :x : :y] = y
# d_out[isvert ? :nbinsx : :nbinsy] = d[:bins]
# if st == :density
# d_out[:histogramnorm] = "probability density"
# end
elseif st == :heatmap
d_out[:type] = "heatmap"
# d_out[:x], d_out[:y] = x, y
# d_out[:z] = d[:z].surf
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
elseif st == :contour
d_out[:type] = "contour"
# d_out[:x], d_out[:y] = x, y
# d_out[:z] = d[:z].surf
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
# d_out[:showscale] = d[:colorbar] != :none
d_out[:ncontours] = d[:levels]
@@ -405,8 +426,6 @@ function plotly_series(plt::Plot, series::Series)
elseif st in (:surface, :wireframe)
d_out[:type] = "surface"
# d_out[:x], d_out[:y] = x, y
# d_out[:z] = d[:z].surf
d_out[:x], d_out[:y], d_out[:z] = d[:x], d[:y], transpose_z(d, d[:z].surf, false)
d_out[:colorscale] = plotly_colorscale(d[:fillcolor], d[:fillalpha])
@@ -478,6 +497,15 @@ function plotly_series(plt::Plot, series::Series)
d_out[:r] = pop!(d_out, :y)
end
# 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
@@ -523,11 +551,10 @@ function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
writemime_png_from_html(io, plt)
end
function _writemime(io::IO, ::MIME"text/html", plt::Plot{PlotlyBackend})
function _writemime(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
write(io, html_head(plt) * html_body(plt))
end
# function Base.display(::PlotsDisplay, plt::Plot{PlotlyBackend})
function _display(plt::Plot{PlotlyBackend})
standalone_html_window(plt)
end
+13 -125
View File
@@ -1,79 +1,12 @@
# https://github.com/spencerlyon2/PlotlyJS.jl
supportedArgs(::PlotlyJSBackend) = [
:annotation,
# :axis,
:background_color,
:color_palette,
:fillrange,
:fillcolor,
:fillalpha,
:foreground_color,
:group,
:label,
:layout,
:legend,
:seriescolor, :seriesalpha,
:linecolor,
:linestyle,
:seriestype,
:linewidth,
:linealpha,
:markershape,
:markercolor,
:markersize,
:markeralpha,
:markerstrokewidth,
:markerstrokecolor,
:markerstrokestyle,
:n,
:bins,
:nc,
:nr,
# :pos,
# :smooth,
:show,
:size,
:title,
:window_title,
:x,
:xguide,
:xlims,
:xticks,
:y,
:yguide,
:ylims,
# :yrightlabel,
:yticks,
:xscale,
:yscale,
:xflip,
:yflip,
:z,
:marker_z,
:tickfont,
:guidefont,
:legendfont,
:grid,
:levels,
:xerror,
:yerror,
:ribbon,
:quiver,
:orientation,
:polar,
]
supportedAxes(::PlotlyJSBackend) = [:auto, :left]
supportedTypes(::PlotlyJSBackend) = [:none, :line, :path, :scatter, :steppre, :steppost,
:histogram2d, :histogram, :density, :bar, :contour, :surface, :path3d, :scatter3d,
:pie, :heatmap] #,, :sticks, :hexbin, :hline, :vline]
supportedStyles(::PlotlyJSBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PlotlyJSBackend) = [:none, :auto, :ellipse, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross,
:pentagon, :hexagon, :octagon, :vline, :hline] #vcat(_allMarkers, Shape)
supportedScales(::PlotlyJSBackend) = [:identity, :log10] #, :ln, :log2, :log10, :asinh, :sqrt]
subplotSupported(::PlotlyJSBackend) = true
stringsSupported(::PlotlyJSBackend) = true
supported_args(::PlotlyJSBackend) = supported_args(PlotlyBackend())
supported_types(::PlotlyJSBackend) = supported_types(PlotlyBackend())
supported_styles(::PlotlyJSBackend) = supported_styles(PlotlyBackend())
supported_markers(::PlotlyJSBackend) = supported_markers(PlotlyBackend())
is_subplot_supported(::PlotlyJSBackend) = true
is_string_supported(::PlotlyJSBackend) = true
# --------------------------------------------------------------------------------------
@@ -83,30 +16,14 @@ function _initialize_backend(::PlotlyJSBackend; kw...)
export PlotlyJS
end
for (mime, fmt) in PlotlyJS._mimeformats
# mime == "image/png" && continue # don't use plotlyjs's writemime for png
@eval Base.writemime(io::IO, m::MIME{Symbol($mime)}, p::Plot{PlotlyJSBackend}) = writemime(io, m, p.o)
end
# override IJulia inline display
if isijulia()
IJulia.display_dict(plt::AbstractPlot{PlotlyJSBackend}) = IJulia.display_dict(plt.o)
end
# # override IJulia inline display
# if isijulia()
# IJulia.display_dict(plt::AbstractPlot{PlotlyJSBackend}) = IJulia.display_dict(plt.o)
# end
end
# ---------------------------------------------------------------------------
# function _create_plot(pkg::PlotlyJSBackend, d::KW)
# # TODO: create the window/canvas/context that is the plot within the backend (call it `o`)
# # TODO: initialize the plot... title, xlabel, bgcolor, etc
# # o = PlotlyJS.Plot(PlotlyJS.GenericTrace[], PlotlyJS.Layout(),
# # Base.Random.uuid4(), PlotlyJS.ElectronDisplay())
# # T = isijulia() ? PlotlyJS.JupyterPlot : PlotlyJS.ElectronPlot
# # o = T(PlotlyJS.Plot())
# o = PlotlyJS.plot()
#
# Plot(o, pkg, 0, d, KW[])
# end
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
PlotlyJS.plot()
@@ -133,47 +50,19 @@ end
# ----------------------------------------------------------------
# TODO: override this to update plot items (title, xlabel, etc) after creation
function _update_plot_object(plt::Plot{PlotlyJSBackend})
pdict = plotly_layout(plt)
syncplot = plt.o
w,h = plt[:size]
# DD(pdict)
PlotlyJS.relayout!(syncplot, pdict, width = w, height = h)
end
# ----------------------------------------------------------------
# accessors for x/y data
# function getxy(plt::Plot{PlotlyJSBackend}, i::Int)
# d = plt.seriesargs[i]
# d[:x], d[:y]
# end
# function setxy!{X,Y}(plt::Plot{PlotlyJSBackend}, xy::Tuple{X,Y}, i::Integer)
# d = plt.seriesargs[i]
# ispolar = get(plt.attr, :polar, false)
# xsym = ispolar ? :t : :x
# ysym = ispolar ? :r : :y
# d[xsym], d[ysym] = xy
# # TODO: this is likely ineffecient... we should make a call that ONLY changes the plot data
# PlotlyJS.restyle!(plt.o, i, KW(xsym=>(d[xsym],), ysym=>(d[ysym],)))
# plt
# end
# ----------------------------------------------------------------
# function _update_min_padding!(sp::Subplot{PlotlyBackend})
# sp.minpad = plotly_minpad(sp)
# end
# function plotlyjs_finalize(plt::Plot)
# plotly_finalize(plt)
# PlotlyJS.relayout!(plt.o, plotly_layout(plt))
# end
function _writemime(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
writemime(io, MIME("text/html"), plt.o)
end
function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
tmpfn = tempname() * "png"
@@ -182,6 +71,5 @@ function _writemime(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend})
end
function _display(plt::Plot{PlotlyJSBackend})
# plotlyjs_finalize(plt)
display(plt.o)
end
+236 -248
View File
@@ -2,54 +2,44 @@
# https://github.com/stevengj/PyPlot.jl
supportedArgs(::PyPlotBackend) = [
supported_args(::PyPlotBackend) = merge_with_base_supported([
:annotations,
:background_color, :foreground_color, :color_palette,
:background_color_legend, :background_color_inside, :background_color_outside,
:foreground_color_legend, :foreground_color_grid, :foreground_color_axis,
:foreground_color_text, :foreground_color_border,
:group,
:foreground_color_grid, :foreground_color_legend, :foreground_color_title,
:foreground_color_axis, :foreground_color_border, :foreground_color_guide, :foreground_color_text,
:label,
:seriestype,
:seriescolor, :seriesalpha,
:linecolor, :linestyle, :linewidth, :linealpha,
:markershape, :markercolor, :markersize, :markeralpha,
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
:fillrange, :fillcolor, :fillalpha,
:bins, :bar_width, :bar_edges,
:n, :nc, :nr, :layout,
:smooth,
:title, :window_title, :show, :size,
:x, :xguide, :xlims, :xticks, :xscale, :xflip, :xrotation,
:y, :yguide, :ylims, :yticks, :yscale, :yflip, :yrotation,
# :axis, :yrightlabel,
:z, :zguide, :zlims, :zticks, :zscale, :zflip, :zrotation,
:z,
:bins, :bar_width, :bar_edges, :bar_position,
:title, :title_location, :titlefont,
:window_title,
:guide, :lims, :ticks, :scale, :flip, :rotation,
:tickfont, :guidefont, :legendfont,
:grid, :legend, :colorbar,
:marker_z, :levels,
:xerror, :yerror,
:ribbon, :quiver, :arrow,
:orientation,
:overwrite_figure,
:polar,
:normalize, :weights, :contours, :aspect_ratio,
:normalize, :weights,
:contours, :aspect_ratio,
:match_dimensions,
:subplot,
]
supportedAxes(::PyPlotBackend) = _allAxes
supportedTypes(::PyPlotBackend) = [
:none, :line, :path, :steppre, :steppost, :shape,
:scatter, :histogram2d, :hexbin, :histogram, #:density,
:bar, :sticks, #:box, :violin, :quiver,
:clims,
:inset_subplots,
])
supported_types(::PyPlotBackend) = [
:path, :steppre, :steppost, :shape,
:scatter, :histogram2d, :hexbin, :histogram,
:bar, :sticks,
:hline, :vline, :heatmap, :pie, :image,
:contour, :contour3d, :path3d, :scatter3d, :surface, :wireframe
]
supportedStyles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::PyPlotBackend) = vcat(_allMarkers, Shape)
supportedScales(::PyPlotBackend) = [:identity, :ln, :log2, :log10]
subplotSupported(::PyPlotBackend) = true
# nativeImagesSupported(::PyPlotBackend) = true
supported_styles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supported_markers(::PyPlotBackend) = vcat(_allMarkers, Shape)
supported_scales(::PyPlotBackend) = [:identity, :ln, :log2, :log10]
is_subplot_supported(::PyPlotBackend) = true
# --------------------------------------------------------------------------------------
@@ -57,6 +47,9 @@ subplotSupported(::PyPlotBackend) = true
function _initialize_backend(::PyPlotBackend)
@eval begin
# see: https://github.com/tbreloff/Plots.jl/issues/308
ENV["OVERRIDE_PYPLOT_DISPLAY"] = true
import PyPlot
export PyPlot
const pycolors = PyPlot.pywrap(PyPlot.pyimport("matplotlib.colors"))
@@ -70,59 +63,42 @@ function _initialize_backend(::PyPlotBackend)
const pytransforms = PyPlot.pywrap(PyPlot.pyimport("matplotlib.transforms"))
end
# we don't want every command to update the figure
PyPlot.ioff()
if !isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
PyPlot.ioff() # stops wierd behavior of displaying incomplete graphs in IJulia
# # TODO: how the hell can I use PyQt4??
# "pyqt4"=>:qt_pyqt4
# PyPlot.backend[1] = "pyqt4"
# PyPlot.gui[1] = :qt_pyqt4
# PyPlot.switch_backend("Qt4Agg")
# only turn on the gui if we want it
if PyPlot.gui != :none
PyPlot.pygui(true)
end
end
end
# --------------------------------------------------------------------------------------
# --------------------------------------------------------------------------------------
# convert colorant to 4-tuple RGBA
getPyPlotColor(c::Colorant, α=nothing) = map(f->float(f(convertColor(c,α))), (red, green, blue, alpha))
getPyPlotColor(cvec::ColorVector, α=nothing) = map(getPyPlotColor, convertColor(cvec, α).v)
getPyPlotColor(grad::ColorGradient, α=nothing) = map(c -> getPyPlotColor(c, α), grad.colors)
getPyPlotColor(scheme::ColorScheme, α=nothing) = getPyPlotColor(convertColor(getColor(scheme), α))
getPyPlotColor(vec::AVec, α=nothing) = map(c->getPyPlotColor(c,α), vec)
getPyPlotColor(c, α=nothing) = getPyPlotColor(convertColor(c, α))
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 getPyPlotColorMap(c::ColorGradient, α=nothing)
pyvals = [(v, getPyPlotColor(getColorZ(c, v), α)) for v in c.values]
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
getPyPlotColorMap(cv::ColorVector, α=nothing) = getPyPlotColorMap(ColorGradient(cv.v), α)
getPyPlotColorMap(v::AVec, α=nothing) = getPyPlotColorMap(ColorGradient(v), α)
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
getPyPlotColorMap(c, α=nothing) = getPyPlotColorMap(default_gradient(), α)
py_colormap(c, α=nothing) = py_colormap(default_gradient(), α)
function getPyPlotCustomShading(c, z, α=nothing)
cmap = getPyPlotColorMap(c, α)
# sm = pycmap.pymember("ScalarMappable")(cmap = cmap)
# sm[:set_array](z)
# sm
function py_shading(c, z, α=nothing)
cmap = py_colormap(c, α)
ls = pycolors.pymember("LightSource")(270,45)
ls[:shade](z, cmap, vert_exag=0.1, blend_mode="soft")
end
# get the style (solid, dashed, etc)
function getPyPlotLineStyle(seriestype::Symbol, linestyle::Symbol)
function py_linestyle(seriestype::Symbol, linestyle::Symbol)
seriestype == :none && return " "
linestyle == :solid && return "-"
linestyle == :dash && return "--"
@@ -132,7 +108,7 @@ function getPyPlotLineStyle(seriestype::Symbol, linestyle::Symbol)
return "-"
end
function getPyPlotMarker(marker::Shape)
function py_marker(marker::Shape)
x, y = shape_coords(marker)
n = length(x)
mat = zeros(n+1,2)
@@ -150,7 +126,7 @@ 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 buildPyPlotPath(x, y)
function py_path(x, y)
n = length(x)
mat = zeros(n+1, 2)
codes = zeros(UInt8, n+1)
@@ -171,9 +147,9 @@ function buildPyPlotPath(x, y)
end
# get the marker shape
function getPyPlotMarker(marker::Symbol)
function py_marker(marker::Symbol)
marker == :none && return " "
marker == :ellipse && return "o"
marker == :circle && return "o"
marker == :rect && return "s"
marker == :diamond && return "D"
marker == :utriangle && return "^"
@@ -184,32 +160,32 @@ function getPyPlotMarker(marker::Symbol)
marker == :pentagon && return "p"
marker == :hexagon && return "h"
marker == :octagon && return "8"
haskey(_shapes, marker) && return getPyPlotMarker(_shapes[marker])
haskey(_shapes, marker) && return py_marker(_shapes[marker])
warn("Unknown marker $marker")
return "o"
end
# getPyPlotMarker(markers::AVec) = map(getPyPlotMarker, markers)
function getPyPlotMarker(markers::AVec)
# py_marker(markers::AVec) = map(py_marker, markers)
function py_marker(markers::AVec)
warn("Vectors of markers are currently unsupported in PyPlot: $markers")
getPyPlotMarker(markers[1])
py_marker(markers[1])
end
# pass through
function getPyPlotMarker(marker::AbstractString)
function py_marker(marker::AbstractString)
@assert length(marker) == 1
marker
end
function getPyPlotStepStyle(seriestype::Symbol)
function py_stepstyle(seriestype::Symbol)
seriestype == :steppost && return "steps-post"
seriestype == :steppre && return "steps-pre"
return "default"
end
# untested... return a FontProperties object from a Plots.Font
function getPyPlotFont(font::Font)
function py_font(font::Font)
pyfont.pymember("FontProperties")(
family = font.family,
size = font.size
@@ -225,18 +201,18 @@ 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 = getPyPlotLineStyle(:path, :dashdot),
color = getPyPlotColor(d[:linecolor]),
linewidth = 2
)
end
end
handleSmooth(plt::Plot{PyPlotBackend}, ax, d::KW, smooth::Real) = handleSmooth(plt, ax, d, true)
# # 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)
# ---------------------------------------------------------------------------
@@ -254,7 +230,7 @@ end
# total hack due to PyPlot bug (see issue #145).
# hack: duplicate the color vector when the total rgba fields is the same as the series length
function color_fix(c, x)
function py_color_fix(c, x)
if (typeof(c) <: AbstractArray && length(c)*4 == length(x)) ||
(typeof(c) <: Tuple && length(x) == 4)
vcat(c, c)
@@ -263,55 +239,49 @@ function color_fix(c, x)
end
end
pylinecolor(d::KW) = getPyPlotColor(d[:linecolor], d[:linealpha])
pymarkercolor(d::KW) = getPyPlotColor(d[:markercolor], d[:markeralpha])
pymarkerstrokecolor(d::KW) = getPyPlotColor(d[:markerstrokecolor], d[:markerstrokealpha])
pyfillcolor(d::KW) = getPyPlotColor(d[:fillcolor], d[:fillalpha])
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])
pylinecolormap(d::KW) = getPyPlotColorMap(d[:linecolor], d[:linealpha])
pymarkercolormap(d::KW) = getPyPlotColorMap(d[:markercolor], d[:markeralpha])
pyfillcolormap(d::KW) = getPyPlotColorMap(d[:fillcolor], d[:fillalpha])
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])
# ---------------------------------------------------------------------------
# TODO: these can probably be removed eventually... right now they're just keeping things working before cleanup
getAxis(sp::Subplot) = sp.o
# getAxis(sp::Subplot) = sp.o
function getAxis(plt::Plot{PyPlotBackend}, series::Series)
sp = get_subplot(plt, get(series.d, :subplot, 1))
getAxis(sp)
end
# function getAxis(plt::Plot{PyPlotBackend}, series::Series)
# sp = get_subplot(plt, get(series.d, :subplot, 1))
# getAxis(sp)
# end
getfig(o) = o
# getfig(o) = o
# ---------------------------------------------------------------------------
# Figure utils -- F*** matplotlib for making me work so hard to figure this crap out
# the drawing surface
canvas(fig) = fig[:canvas]
py_canvas(fig) = fig[:canvas]
# the object controlling draw commands
renderer(fig) = canvas(fig)[:get_renderer]()
py_renderer(fig) = py_canvas(fig)[:get_renderer]()
# draw commands... paint the screen (probably updating internals too)
drawfig(fig) = fig[:draw](renderer(fig))
drawax(ax) = ax[:draw](renderer(ax[:get_figure]()))
py_drawfig(fig) = fig[:draw](py_renderer(fig))
# py_drawax(ax) = ax[:draw](py_renderer(ax[:get_figure]()))
# get a vector [left, right, bottom, top] in PyPlot coords (origin is bottom-left!)
get_extents(obj) = obj[:get_window_extent]()[:get_points]()
py_extents(obj) = obj[:get_window_extent]()[:get_points]()
# # bounding box of the figure
# function py_bbox_fig(fig)
# fl, fr, fb, ft = get_extents(fig)
# BoundingBox(0px, 0px, (fr-fl)*px, (ft-fb)*px)
# end
# py_bbox_fig(plt::Plot) = py_bbox_fig(plt.o)
# compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left
function py_bbox(obj)
fl, fr, fb, ft = get_extents(obj[:get_figure]())
l, r, b, t = get_extents(obj)
fl, fr, fb, ft = py_extents(obj[:get_figure]())
l, r, b, t = py_extents(obj)
BoundingBox(l*px, (ft-t)*px, (r-l)*px, (t-b)*px)
end
@@ -359,24 +329,17 @@ end
function _create_backend_figure(plt::Plot{PyPlotBackend})
w,h = map(px2inch, plt[:size])
# reuse the current figure?
# # reuse the current figure?
fig = if plt[:overwrite_figure]
PyPlot.gcf()
else
PyPlot.figure()
fig = PyPlot.figure()
# finalizer(fig, close)
fig
end
# # update the specs
# fig[:set_size_inches](w, h, forward = true)
# fig[:set_facecolor](getPyPlotColor(plt[:background_color_outside]))
# fig[:set_dpi](DPI)
# # fig[:set_tight_layout](true)
# clear the figure
PyPlot.clf()
# # resize the window
# PyPlot.plt[:get_current_fig_manager]()[:resize](plt[:size]...)
fig
end
@@ -405,15 +368,16 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
d = series.d
st = d[:seriestype]
sp = d[:subplot]
ax = sp.o
if !(st in supportedTypes(plt.backend))
error("seriestype $(st) is unsupported in PyPlot. Choose from: $(supportedTypes(plt.backend))")
if !(st in supported_types(plt.backend))
error("seriestype $(st) is unsupported in PyPlot. Choose from: $(supported_types(plt.backend))")
end
# PyPlot doesn't handle mismatched x/y
fix_xy_lengths!(plt, d)
ax = getAxis(plt, series)
# ax = getAxis(plt, series)
x, y, z = d[:x], d[:y], d[:z]
xyargs = (st in _3dTypes ? (x,y,z) : (x,y))
@@ -440,17 +404,17 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
# for each plotting command, optionally build and add a series handle to the list
# line plot
if st in (:path, :line, :path3d, :steppre, :steppost)
if st in (:path, :path3d, :steppre, :steppost)
if d[:linewidth] > 0
handle = ax[:plot](xyargs...;
label = d[:label],
zorder = plt.n,
color = pylinecolor(d),
color = py_linecolor(d),
linewidth = d[:linewidth],
linestyle = getPyPlotLineStyle(st, d[:linestyle]),
linestyle = py_linestyle(st, d[:linestyle]),
solid_capstyle = "round",
# dash_capstyle = "round",
drawstyle = getPyPlotStepStyle(st)
drawstyle = py_stepstyle(st)
)[1]
push!(handles, handle)
@@ -463,10 +427,10 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
:arrowstyle => "simple,head_length=$(a.headlength),head_width=$(a.headwidth)",
:shrinkA => 0,
:shrinkB => 0,
:edgecolor => pylinecolor(d),
:facecolor => pylinecolor(d),
:edgecolor => py_linecolor(d),
:facecolor => py_linecolor(d),
:linewidth => d[:linewidth],
:linestyle => getPyPlotLineStyle(st, d[:linestyle]),
:linestyle => py_linestyle(st, d[:linestyle]),
)
add_arrows(x, y) do xyprev, xy
ax[:annotate]("",
@@ -495,8 +459,8 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
handle = ax[isvertical(d) ? :bar : :barh](x, y;
label = d[:label],
zorder = plt.n,
color = pyfillcolor(d),
edgecolor = pylinecolor(d),
color = py_fillcolor(d),
edgecolor = py_linecolor(d),
linewidth = d[:linewidth],
align = d[:bar_edges] ? "edge" : "center",
extrakw...
@@ -509,8 +473,8 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
handle = ax[isvertical(d) ? :bar : :barh](x, y;
label = d[:label],
zorder = plt.n,
color = pylinecolor(d),
edgecolor = pylinecolor(d),
color = py_linecolor(d),
edgecolor = py_linecolor(d),
linewidth = d[:linewidth],
align = "center",
extrakw...
@@ -519,15 +483,20 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
end
# add markers?
if d[:markershape] != :none && st in (:path, :line, :scatter, :path3d,
if d[:markershape] != :none && st in (:path, :scatter, :path3d,
:scatter3d, :steppre, :steppost,
:bar, :sticks)
extrakw = KW()
if d[:marker_z] == nothing
extrakw[:c] = color_fix(pymarkercolor(d), x)
extrakw[:c] = py_color_fix(py_markercolor(d), x)
else
extrakw[:c] = convert(Vector{Float64}, d[:marker_z])
extrakw[:cmap] = pymarkercolormap(d)
extrakw[:cmap] = py_markercolormap(d)
clims = sp[:clims]
if isa(clims, Tuple) && length(clims) == 2
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
needs_colorbar = true
end
xyargs = if st in (:bar, :sticks) && !isvertical(d)
@@ -538,9 +507,9 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
handle = ax[:scatter](xyargs...;
label = d[:label],
zorder = plt.n + 0.5,
marker = getPyPlotMarker(d[:markershape]),
marker = py_marker(d[:markershape]),
s = d[:markersize] .^ 2,
edgecolors = pymarkerstrokecolor(d),
edgecolors = py_markerstrokecolor(d),
linewidths = d[:markerstrokewidth],
extrakw...
)
@@ -551,8 +520,8 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
handle = ax[:hist](y;
label = d[:label],
zorder = plt.n,
color = pyfillcolor(d),
edgecolor = pylinecolor(d),
color = py_fillcolor(d),
edgecolor = py_linecolor(d),
linewidth = d[:linewidth],
bins = d[:bins],
normed = d[:normalize],
@@ -572,13 +541,19 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
end
if st == :histogram2d
clims = sp[:clims]
if isa(clims, Tuple) && length(clims) == 2
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
handle = ax[:hist2d](x, y;
label = d[:label],
zorder = plt.n,
bins = d[:bins],
normed = d[:normalize],
weights = d[:weights],
cmap = pyfillcolormap(d) # applies to the pcolorfast object
cmap = py_fillcolormap(d), # applies to the pcolorfast object
extrakw...
)[4]
push!(handles, handle)
needs_colorbar = true
@@ -591,13 +566,19 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
end
if st == :hexbin
clims = sp[:clims]
if isa(clims, Tuple) && length(clims) == 2
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
handle = ax[:hexbin](x, y;
label = d[:label],
zorder = plt.n,
gridsize = d[:bins],
linewidths = d[:linewidth],
edgecolors = pylinecolor(d),
cmap = pyfillcolormap(d) # applies to the pcolorfast object
edgecolors = py_linecolor(d),
cmap = py_fillcolormap(d), # applies to the pcolorfast object
extrakw...
)
push!(handles, handle)
needs_colorbar = true
@@ -608,8 +589,8 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
func = ax[st == :hline ? :axhline : :axvline]
handle = func(yi;
linewidth=d[:linewidth],
color=pylinecolor(d),
linestyle=getPyPlotLineStyle(st, d[:linestyle])
color=py_linecolor(d),
linestyle=py_linestyle(st, d[:linestyle])
)
push!(handles, handle)
end
@@ -620,6 +601,11 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
z = transpose_z(d, z.surf)
needs_colorbar = true
clims = sp[:clims]
if isa(clims, Tuple) && length(clims) == 2
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
if st == :contour3d
extrakw[:extend3d] = true
@@ -630,8 +616,8 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
label = d[:label],
zorder = plt.n,
linewidths = d[:linewidth],
linestyles = getPyPlotLineStyle(st, d[:linestyle]),
cmap = pylinecolormap(d),
linestyles = py_linestyle(st, d[:linestyle]),
cmap = py_linecolormap(d),
extrakw...
)
push!(handles, handle)
@@ -641,7 +627,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
handle = ax[:contourf](x, y, z, levelargs...;
label = d[:label],
zorder = plt.n + 0.5,
cmap = pyfillcolormap(d),
cmap = py_fillcolormap(d),
extrakw...
)
push!(handles, handle)
@@ -659,10 +645,15 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
z = transpose_z(d, z)
if st == :surface
if d[:marker_z] != nothing
extrakw[:facecolors] = getPyPlotCustomShading(d[:fillcolor], d[:marker_z], d[:fillalpha])
extrakw[:facecolors] = py_shading(d[:fillcolor], d[:marker_z], d[:fillalpha])
extrakw[:shade] = false
clims = sp[:clims]
if isa(clims, Tuple) && length(clims) == 2
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
else
extrakw[:cmap] = pyfillcolormap(d)
extrakw[:cmap] = py_fillcolormap(d)
needs_colorbar = true
end
end
@@ -672,7 +663,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
rstride = 1,
cstride = 1,
linewidth = d[:linewidth],
edgecolor = pylinecolor(d),
edgecolor = py_linecolor(d),
extrakw...
)
push!(handles, handle)
@@ -683,7 +674,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
offset = (zdir == "y" ? maximum : minimum)(mat)
handle = ax[:contourf](x, y, z, levelargs...;
zdir = zdir,
cmap = pyfillcolormap(d),
cmap = py_fillcolormap(d),
offset = (zdir == "y" ? maximum : minimum)(mat) # where to draw the contour plane
)
push!(handles, handle)
@@ -698,12 +689,18 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
elseif typeof(z) <: AbstractVector
# tri-surface plot (http://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html#tri-surface-plots)
clims = sp[:clims]
if isa(clims, Tuple) && length(clims) == 2
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
handle = ax[:plot_trisurf](x, y, z;
label = d[:label],
zorder = plt.n,
cmap = pyfillcolormap(d),
cmap = py_fillcolormap(d),
linewidth = d[:linewidth],
edgecolor = pylinecolor(d)
edgecolor = py_linecolor(d),
extrakw...
)
push!(handles, handle)
needs_colorbar = true
@@ -724,7 +721,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
end
handle = ax[:imshow](z;
zorder = plt.n,
cmap = getPyPlotColorMap([:black, :white]),
cmap = py_colormap([:black, :white]),
vmin = 0.0,
vmax = 1.0
)
@@ -745,11 +742,19 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
if !isempty(dvals)
discrete_colorbar_values = dvals
end
clims = sp[:clims]
if isa(clims, Tuple) && length(clims) == 2
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
end
handle = ax[:pcolormesh](x, y, z;
label = d[:label],
zorder = plt.n,
cmap = pyfillcolormap(d),
edgecolors = (d[:linewidth] > 0 ? pylinecolor(d) : "face")
cmap = py_fillcolormap(d),
edgecolors = (d[:linewidth] > 0 ? py_linecolor(d) : "face"),
extrakw...
)
push!(handles, handle)
needs_colorbar = true
@@ -766,12 +771,12 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
end
if st == :shape
path = buildPyPlotPath(x, y)
path = py_path(x, y)
patches = pypatches.pymember("PathPatch")(path;
label = d[:label],
zorder = plt.n,
edgecolor = pymarkerstrokecolor(d),
facecolor = pymarkercolor(d),
edgecolor = py_markerstrokecolor(d),
facecolor = py_markercolor(d),
linewidth = d[:markerstrokewidth],
fill = true
)
@@ -796,8 +801,8 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
d[:serieshandle] = handles
# smoothing
handleSmooth(plt, ax, d, d[:smooth])
# # smoothing
# handleSmooth(plt, ax, d, d[:smooth])
# add the colorbar legend
if needs_colorbar && sp[:colorbar] != :none
@@ -837,7 +842,7 @@ function _series_added(plt::Plot{PyPlotBackend}, series::Series)
handle = ax[f](args...;
zorder = plt.n,
facecolor = pyfillcolor(d),
facecolor = py_fillcolor(d),
linewidths = 0
)
push!(handles, handle)
@@ -849,7 +854,7 @@ end
function update_limits!(sp::Subplot{PyPlotBackend}, series::Series, letters)
for letter in letters
setPyPlotLims(sp.o, sp[Symbol(letter, :axis)])
py_set_lims(sp.o, sp[Symbol(letter, :axis)])
end
end
@@ -873,13 +878,13 @@ end
# --------------------------------------------------------------------------
function setPyPlotLims(ax, axis::Axis)
function py_set_lims(ax, axis::Axis)
letter = axis[:letter]
lfrom, lto = axis_limits(axis)
ax[Symbol("set_", letter, "lim")](lfrom, lto)
end
function addPyPlotTicks(ax, ticks, letter)
function py_set_ticks(ax, ticks, letter)
ticks == :auto && return
axis = ax[Symbol(letter,"axis")]
if ticks == :none || ticks == nothing
@@ -902,26 +907,54 @@ function addPyPlotTicks(ax, ticks, letter)
end
end
function applyPyPlotScale(ax, scaleType::Symbol, letter)
function py_compute_axis_minval(axis::Axis)
# compute the smallest absolute value for the log scale's linear threshold
minval = 1.0
sp = axis.sp
for series in series_list(axis.sp)
minval = min(minval, minimum(abs(series.d[axis[:letter]])))
end
# now if the axis limits go to a smaller abs value, use that instead
vmin, vmax = axis_limits(axis)
minval = min(minval, abs(vmin), abs(vmax))
minval
end
function py_set_scale(ax, axis::Axis)
scale = axis[:scale]
letter = axis[:letter]
scale in supported_scales() || return warn("Unhandled scale value in pyplot: $scale")
func = ax[Symbol("set_", letter, "scale")]
scaleType == :identity && return func("linear")
scaleType == :ln && return func("log", basex = e, basey = e)
scaleType == :log2 && return func("log", basex = 2, basey = 2)
scaleType == :log10 && return func("log", basex = 10, basey = 10)
warn("Unhandled scaleType: ", scaleType)
kw = KW()
arg = if scale == :identity
"linear"
else
kw[Symbol(:base,letter)] = if scale == :ln
e
elseif scale == :log2
2
elseif scale == :log10
10
end
kw[Symbol(:linthresh,letter)] = max(1e-16, py_compute_axis_minval(axis))
"symlog"
end
func(arg; kw...)
end
function updateAxisColors(ax, a::Axis)
function py_set_axis_colors(ax, a::Axis)
for (loc, spine) in ax[:spines]
spine[:set_color](getPyPlotColor(a[:foreground_color_border]))
spine[:set_color](py_color(a[:foreground_color_border]))
end
axissym = Symbol(a[:letter], :axis)
if haskey(ax, axissym)
ax[:tick_params](axis=string(a[:letter]), which="both",
colors=getPyPlotColor(a[:foreground_color_axis]),
labelcolor=getPyPlotColor(a[:foreground_color_text]))
ax[axissym][:label][:set_color](getPyPlotColor(a[:foreground_color_guide]))
colors=py_color(a[:foreground_color_axis]),
labelcolor=py_color(a[:foreground_color_text]))
ax[axissym][:label][:set_color](py_color(a[:foreground_color_guide]))
end
end
@@ -934,7 +967,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
w, h = plt[:size]
fig = plt.o
fig[:set_size_inches](px2inch(w), px2inch(h), forward = true)
fig[:set_facecolor](getPyPlotColor(plt[:background_color_outside]))
fig[:set_facecolor](py_color(plt[:background_color_outside]))
fig[:set_dpi](DPI)
# resize the window
@@ -942,14 +975,15 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
# update subplots
for sp in plt.subplots
ax = getAxis(sp)
# ax = getAxis(sp)
ax = sp.o
if ax == nothing
continue
end
# add the annotations
for ann in sp[:annotations]
createPyPlotAnnotationObject(sp, ann...)
py_add_annotations(sp, ann...)
end
# title
@@ -964,7 +998,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
end
ax[field][:set_text](sp[:title])
ax[field][:set_fontsize](sp[:titlefont].pointsize)
ax[field][:set_color](getPyPlotColor(sp[:foreground_color_title]))
ax[field][:set_color](py_color(sp[:foreground_color_title]))
# ax[:set_title](sp[:title], loc = loc)
end
@@ -973,9 +1007,9 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
axissym = Symbol(letter, :axis)
axis = sp[axissym]
haskey(ax, axissym) || continue
applyPyPlotScale(ax, axis[:scale], letter)
setPyPlotLims(ax, axis)
addPyPlotTicks(ax, get_ticks(axis), letter)
py_set_scale(ax, axis)
py_set_lims(ax, axis)
py_set_ticks(ax, get_ticks(axis), letter)
ax[Symbol("set_", letter, "label")](axis[:guide])
if get(axis.d, :flip, false)
ax[Symbol("invert_", letter, "axis")]()
@@ -986,11 +1020,11 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
lab[:set_rotation](axis[:rotation])
end
if sp[:grid]
fgcolor = getPyPlotColor(sp[:foreground_color_grid])
fgcolor = py_color(sp[:foreground_color_grid])
ax[axissym][:grid](true, color = fgcolor)
ax[:set_axisbelow](true)
end
updateAxisColors(ax, axis)
py_set_axis_colors(ax, axis)
end
# aspect ratio
@@ -1000,12 +1034,12 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
end
# legend
addPyPlotLegend(plt, sp, ax)
py_add_legend(plt, sp, ax)
# this sets the bg color inside the grid
ax[:set_axis_bgcolor](getPyPlotColor(sp[:background_color_inside]))
ax[:set_axis_bgcolor](py_color(sp[:background_color_inside]))
end
drawfig(fig)
py_drawfig(fig)
end
@@ -1045,18 +1079,18 @@ end
# -----------------------------------------------------------------
function createPyPlotAnnotationObject(sp::Subplot{PyPlotBackend}, x, y, val)
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val)
ax = sp.o
ax[:annotate](val, xy = (x,y), zorder = 999)
end
function createPyPlotAnnotationObject(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
function py_add_annotations(sp::Subplot{PyPlotBackend}, x, y, val::PlotText)
ax = sp.o
ax[:annotate](val.str,
xy = (x,y),
family = val.font.family,
color = getPyPlotColor(val.font.color),
color = py_color(val.font.color),
horizontalalignment = val.font.halign == :hcenter ? "center" : string(val.font.halign),
verticalalignment = val.font.valign == :vcenter ? "center" : string(val.font.valign),
rotation = val.font.rotation * 180 / π,
@@ -1093,24 +1127,17 @@ const _pyplot_legend_pos = KW(
:topleft => "upper left"
)
function addPyPlotLegend(plt::Plot, sp::Subplot, ax)
function py_add_legend(plt::Plot, sp::Subplot, ax)
leg = sp[:legend]
if leg != :none
# gotta do this to ensure both axes are included
labels = []
handles = []
# for series in plt.series_list
# if get_subplot(series) === sp &&
# series.d[:label] != "" &&
# !(series.d[:seriestype] in (
# :hexbin,:histogram2d,:hline,:vline,
# :contour,:contour3d,:surface,:wireframe,
# :heatmap,:path3d,:scatter3d, :pie, :image))
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=pyfillcolor(series.d), linewidth=4)
PyPlot.plt[:Line2D]((0,1),(0,0), color=py_fillcolor(series.d), linewidth=4)
else
series.d[:serieshandle][1]
end)
@@ -1129,14 +1156,14 @@ function addPyPlotLegend(plt::Plot, sp::Subplot, ax)
)
leg[:set_zorder](1000)
fgcolor = getPyPlotColor(sp[:foreground_color_legend])
fgcolor = py_color(sp[:foreground_color_legend])
for txt in leg[:get_texts]()
PyPlot.plt[:setp](txt, color = fgcolor)
end
# set some legend properties
frame = leg[:get_frame]()
frame[:set_facecolor](getPyPlotColor(sp[:background_color_legend]))
frame[:set_facecolor](py_color(sp[:background_color_legend]))
frame[:set_edgecolor](fgcolor)
end
end
@@ -1144,30 +1171,6 @@ end
# -----------------------------------------------------------------
# # add legend, update colors and positions, then draw
# function finalizePlot(plt::Plot{PyPlotBackend})
# # for sp in plt.subplots
# # # ax = getLeftAxis(plt)
# # ax = getAxis(sp)
# # ax == nothing && continue
# # addPyPlotLegend(plt, sp, ax)
# # for asym in (:xaxis, :yaxis, :zaxis)
# # updateAxisColors(ax, sp.attr[asym])
# # end
# # end
# drawfig(plt.o)
# # plt.layout.bbox = py_bbox_fig(plt)
#
# # TODO: these should be called outside of pyplot... how?
# update_child_bboxes!(plt.layout)
# _update_position!(plt.layout)
#
# PyPlot.draw()
# end
# function _before_layout_calcs(plt::Plot{PyPlotBackend})
# drawfig(plt.o)
# end
# Use the bounding boxes (and methods left/top/right/bottom/width/height) `sp.bbox` and `sp.plotarea` to
# position the subplot in the backend.
@@ -1175,7 +1178,6 @@ function _update_plot_object(plt::Plot{PyPlotBackend})
for sp in plt.subplots
ax = sp.o
ax == nothing && return
# figw, figh = size(py_bbox_fig(sp.plt))
figw, figh = sp.plt[:size]
figw, figh = figw*px, figh*px
pcts = bbox_to_pcts(sp.plotarea, figw, figh)
@@ -1196,20 +1198,8 @@ end
# -----------------------------------------------------------------
# display/output
# function Base.display(::PlotsDisplay, plt::Plot{PyPlotBackend})
# finalizePlot(plt)
# if isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
# display(getfig(plt.o))
# end
# getfig(plt.o)[:show]()
# end
function _display(plt::Plot{PyPlotBackend})
# finalizePlot(plt)
if isa(Base.Multimedia.displays[end], Base.REPL.REPLDisplay)
display(getfig(plt.o))
end
getfig(plt.o)[:show]()
plt.o[:show]()
end
@@ -1225,10 +1215,8 @@ const _pyplot_mimeformats = Dict(
for (mime, fmt) in _pyplot_mimeformats
# @eval function Base.writemime(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PyPlotBackend})
@eval function _writemime(io::IO, ::MIME{Symbol($mime)}, plt::Plot{PyPlotBackend})
# finalizePlot(plt)
fig = getfig(plt.o)
fig = plt.o
fig.o["canvas"][:print_figure](
io,
format=$fmt,
+10 -33
View File
@@ -2,52 +2,29 @@
# https://github.com/tbreloff/Qwt.jl
supportedArgs(::QwtBackend) = [
supported_args(::QwtBackend) = merge_with_base_supported([
:annotations,
:axis,
:background_color,
:linecolor,
:color_palette,
:fillrange,
:fillcolor,
:foreground_color,
:group,
:label,
:layout,
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:seriestype,
:linewidth,
:markershape,
:markercolor,
:markersize,
:n,
:bins,
:nc,
:nr,
:pos,
:smooth,
:show,
:size,
:title,
:window_title,
:x,
:xguide,
:xlims,
:xticks,
:y,
:yguide,
:ylims,
:yrightlabel,
:yticks,
:xscale,
:yscale,
]
supportedTypes(::QwtBackend) = [:none, :line, :path, :steppre, :steppost, :sticks, :scatter, :histogram2d, :hexbin, :histogram, :bar, :hline, :vline]
supportedMarkers(::QwtBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
supportedScales(::QwtBackend) = [:identity, :log10]
subplotSupported(::QwtBackend) = true
:guide, :lims, :ticks, :scale,
])
supported_types(::QwtBackend) = [:path, :scatter, :hexbin, :bar]
supported_markers(::QwtBackend) = [:none, :auto, :rect, :circle, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :star8, :hexagon]
supported_scales(::QwtBackend) = [:identity, :log10]
is_subplot_supported(::QwtBackend) = true
# --------------------------------------------------------------------------------------
@@ -95,13 +72,13 @@ function adjustQwtKeywords(plt::Plot{QwtBackend}, iscreating::Bool; kw...)
if st == :scatter
d[:seriestype] = :none
if d[:markershape] == :none
d[:markershape] = :ellipse
d[:markershape] = :circle
end
elseif st in (:hline, :vline)
addLineMarker(plt, d)
d[:seriestype] = :none
d[:markershape] = :ellipse
d[:markershape] = :circle
d[:markersize] = 1
if st == :vline
d[:x], d[:y] = d[:y], d[:x]
@@ -184,7 +161,7 @@ function updateLimsAndTicks(plt::Plot{QwtBackend}, d::KW, isx::Bool)
# scaletype == :log && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(e))
# scaletype == :log2 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLogScaleEngine(2))
scaletype == :log10 && w[:setAxisScaleEngine](axisid, Qwt.QWT.QwtLog10ScaleEngine())
scaletype in supportedScales() || warn("Unsupported scale type: ", scaletype)
scaletype in supported_scales() || warn("Unsupported scale type: ", scaletype)
end
end
+3 -3
View File
@@ -19,9 +19,9 @@ function _create_backend_figure(plt::Plot{[PkgName]Backend})
nothing
end
# this is called early in the pipeline, use it to make the plot current or something
function _prepare_plot_object(plt::Plot{[PkgName]Backend})
end
# # this is called early in the pipeline, use it to make the plot current or something
# function _prepare_plot_object(plt::Plot{[PkgName]Backend})
# end
# Set up the subplot within the backend object.
function _initialize_subplot(plt::Plot{[PkgName]Backend}, sp::Subplot{[PkgName]Backend})
+11 -51
View File
@@ -1,69 +1,29 @@
# https://github.com/Evizero/UnicodePlots.jl
supportedArgs(::UnicodePlotsBackend) = [
# :annotations,
# :args,
# :axis,
# :background_color,
# :linecolor,
# :fill,
# :foreground_color,
:group,
# :heatmap_c,
# :kwargs,
supported_args(::UnicodePlotsBackend) = merge_with_base_supported([
:label,
# :layout,
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:seriestype,
# :linewidth,
:markershape,
# :markercolor,
# :markersize,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
:bins,
# :nc,
# :nr,
# :pos,
# :reg,
# :ribbon,
:show,
:size,
:title,
:window_title,
:x,
:xguide,
:xlims,
# :xticks,
:y,
:yguide,
:ylims,
# :yrightlabel,
# :yticks,
# :xscale,
# :yscale,
# :xflip,
# :yflip,
# :z,
]
supportedAxes(::UnicodePlotsBackend) = [:auto, :left]
supportedTypes(::UnicodePlotsBackend) = [
:path, :steppre, :steppost, :scatter,
:histogram2d, :hline, :vline
:guide, :lims,
])
supported_types(::UnicodePlotsBackend) = [
:path, :scatter,
:histogram2d
]
supportedStyles(::UnicodePlotsBackend) = [:auto, :solid]
supportedMarkers(::UnicodePlotsBackend) = [:none, :auto, :ellipse]
supportedScales(::UnicodePlotsBackend) = [:identity]
subplotSupported(::UnicodePlotsBackend) = true
supported_styles(::UnicodePlotsBackend) = [:auto, :solid]
supported_markers(::UnicodePlotsBackend) = [:none, :auto, :circle]
supported_scales(::UnicodePlotsBackend) = [:identity]
is_subplot_supported(::UnicodePlotsBackend) = true
# don't warn on unsupported... there's just too many warnings!!
warnOnUnsupportedArgs(pkg::UnicodePlotsBackend, d::KW) = nothing
warnOnUnsupported_args(pkg::UnicodePlotsBackend, d::KW) = nothing
# --------------------------------------------------------------------------------------
+10 -45
View File
@@ -3,64 +3,29 @@
# credit goes to https://github.com/jverzani for contributing to the first draft of this backend implementation
supportedArgs(::WinstonBackend) = [
supported_args(::WinstonBackend) = merge_with_base_supported([
:annotations,
# :args,
# :axis,
# :background_color,
:linecolor,
:color_palette,
:fillrange,
:fillcolor,
# :foreground_color,
:group,
# :heatmap_c,
# :kwargs,
:label,
# :layout,
:legend,
:seriescolor, :seriesalpha,
:linestyle,
:seriestype,
:linewidth,
:markershape,
:markercolor,
:markersize,
# :markerstrokewidth,
# :markerstrokecolor,
# :markerstrokestyle,
# :n,
:bins,
# :nc,
# :nr,
# :pos,
:smooth,
# :ribbon,
:show,
:size,
:title,
:window_title,
:x,
:xguide,
:xlims,
# :xticks,
:y,
:yguide,
:ylims,
# :yrightlabel,
# :yticks,
:xscale,
:yscale,
# :xflip,
# :yflip,
# :z,
]
supportedAxes(::WinstonBackend) = [:auto, :left]
supportedTypes(::WinstonBackend) = [:none, :line, :path, :sticks, :scatter, :histogram, :bar]
supportedStyles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supportedMarkers(::WinstonBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
supportedScales(::WinstonBackend) = [:identity, :log10]
subplotSupported(::WinstonBackend) = false
:guide, :lims, :scale,
])
supported_types(::WinstonBackend) = [:path, :scatter, :bar]
supported_styles(::WinstonBackend) = [:auto, :solid, :dash, :dot, :dashdot]
supported_markers(::WinstonBackend) = [:none, :auto, :rect, :circle, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5]
supported_scales(::WinstonBackend) = [:identity, :log10]
is_subplot_supported(::WinstonBackend) = false
# --------------------------------------------------------------------------------------
@@ -87,7 +52,7 @@ end
@compat const winston_marker = KW(:none=>".",
:rect => "square",
:ellipse=>"circle",
:circle=>"circle",
:diamond=>"diamond",
:utriangle=>"triangle",
:dtriangle=>"down-triangle",
@@ -170,7 +135,7 @@ function _series_added(plt::Plot{WinstonBackend}, series::Series)
elseif d[:seriestype] == :scatter
if d[:markershape] == :none
d[:markershape] = :ellipse
d[:markershape] = :circle
end
# elseif d[:seriestype] == :step
+7
View File
@@ -23,6 +23,8 @@ convertColor(c::Symbol) = parse(Colorant, string(c))
convertColor(c::Colorant) = c
convertColor(cvec::AbstractVector) = map(convertColor, cvec)
convertColor(c::ColorScheme) = c
convertColor(v::Void) = RGBA(0,0,0,0)
convertColor(b::Bool) = b ? RGBA(0,0,0,1) : RGBA(0,0,0,0)
function convertColor(c, α::Real)
c = convertColor(c)
@@ -119,6 +121,11 @@ function ColorGradient(grad::ColorGradient; alpha = nothing)
ColorGradient(convertColor(grad.colors, alpha), grad.values)
end
# anything else just gets the default gradient
function ColorGradient(cw; alpha=nothing)
ColorGradient(default_gradient(), alpha=alpha)
end
getColor(gradient::ColorGradient, idx::Int) = gradient.colors[mod1(idx, length(gradient.colors))]
function getColorZ(gradient::ColorGradient, z::Real)
+40 -4
View File
@@ -12,8 +12,15 @@ compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : ang
# -------------------------------------------------------------
immutable Shape
x::AVec
y::AVec
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
end
Shape(verts::AVec) = Shape(unzip(verts)...)
@@ -93,8 +100,29 @@ function makearrowhead(angle; h = 2.0, w = 0.4)
from_polar(angle + 0.5π, w) - tip, (0,0)])
end
const _shape_keys = Symbol[
:circle,
:rect,
:star5,
:diamond,
:hexagon,
:cross,
:xcross,
:utriangle,
:dtriangle,
:pentagon,
:heptagon,
:octagon,
:star4,
:star6,
:star7,
:star8,
:vline,
:hline,
]
const _shapes = KW(
:ellipse => makeshape(20),
:circle => makeshape(20),
:rect => makeshape(4, offset=-0.25),
:diamond => makeshape(4),
:utriangle => makeshape(3),
@@ -218,7 +246,14 @@ function font(args...)
for arg in args
T = typeof(arg)
if arg == :center
if T == Font
family = arg.family
pointsize = arg.pointsize
halign = arg.halign
valign = arg.valign
rotation = arg.rotation
color = arg.color
elseif arg == :center
halign = :hcenter
valign = :vcenter
elseif arg in (:hcenter, :left, :right)
@@ -253,6 +288,7 @@ end
PlotText(str) = PlotText(string(str), font())
text(t::PlotText) = t
text(str::AbstractString, f::Font) = PlotText(str, f)
function text(str, args...)
PlotText(string(str), font(args...))
end
+13 -6
View File
@@ -115,15 +115,17 @@ PlotExample("Line types",
PlotExample("Line styles",
"",
[:(begin
styles = setdiff(supportedStyles(), [:auto])'
plot(cumsum(randn(20,length(styles)),1), style=:auto, label=map(string,styles), w=5)
styles = filter(s -> s in supported_styles(), [:solid, :dash, :dot, :dashdot, :dashdotdot])'
n = length(styles)
y = cumsum(randn(20,n),1)
plot(y, line = (5, styles), label = map(string,styles))
end)]
),
PlotExample("Marker types",
"",
[:(begin
markers = setdiff(supportedMarkers(), [:none,:auto,Shape])'
markers = filter(m -> m in supported_markers(), Plots._shape_keys)'
n = length(markers)
x = linspace(0,10,n+2)[2:end-1]
y = repmat(reverse(x)', n, 1)
@@ -264,7 +266,7 @@ PlotExample("Groups and Subplots",
"",
[:(begin
group = rand(map(i->"group $i",1:4),100)
plot(rand(100), layout=@layout([a b;c]), group=group, n=3, linetype=[:bar :scatter :steppre])
plot(rand(100), layout=@layout([a b;c]), group=group, linetype=[:bar :scatter :steppre])
end)]
),
@@ -325,11 +327,13 @@ function test_examples(pkgname::Symbol, idx::Int; debug = false, disp = true)
end
# generate all plots and create a dict mapping idx --> plt
function test_examples(pkgname::Symbol; debug = false, disp = true)
function test_examples(pkgname::Symbol; debug = false, disp = true, sleep = nothing,
skip = [], only = nothing)
Plots._debugMode.on = debug
plts = Dict()
for i in 1:length(_examples)
only != nothing && !(i in only) && continue
i in skip && continue
try
plt = test_examples(pkgname, i, debug=debug, disp=disp)
plts[i] = plt
@@ -337,6 +341,9 @@ function test_examples(pkgname::Symbol; debug = false, disp = true)
# TODO: put error info into markdown?
warn("Example $pkgname:$i:$(_examples[i].header) failed with: $ex")
end
if sleep != nothing
Base.sleep(sleep)
end
end
plts
end
+97 -99
View File
@@ -5,6 +5,8 @@
const px = AbsoluteLength(0.254)
const pct = Length{:pct, Float64}(1.0)
to_pixels(m::AbsoluteLength) = m.value / 0.254
const _cbar_width = 5mm
@compat Base.:.*(m::Measure, n::Number) = m * n
@@ -70,6 +72,16 @@ function crop(parent::BoundingBox, child::BoundingBox)
BoundingBox(l, t, w, h)
end
# convert x,y coordinates from absolute coords to percentages...
# returns x_pct, y_pct
function xy_mm_to_pcts(x::AbsoluteLength, y::AbsoluteLength, figw, figh, flipy = true)
xmm, ymm = x.value, y.value
if flipy
ymm = figh.value - ymm # flip y when origin in bottom-left
end
xmm / figw.value, ymm / figh.value
end
# convert a bounding box from absolute coords to percentages...
# returns an array of percentages of figure size: [left, bottom, width, height]
function bbox_to_pcts(bb::BoundingBox, figw, figh, flipy = true)
@@ -89,6 +101,21 @@ end
Base.show(io::IO, layout::AbstractLayout) = print(io, "$(typeof(layout))$(size(layout))")
# create a new bbox
function bbox(x, y, w, h; h_anchor = :left, v_anchor = :top)
left = if h_anchor == :left
x
else
x - w * (h_anchor == :right ? 1.0 : 0.5)
end
top = if v_anchor == :top
y
else
y - h * (v_anchor == :bottom ? 1.0 : 0.5)
end
BoundingBox(left, top, w, h)
end
# this is the available area for drawing everything in this layout... as percentages of total canvas
bbox(layout::AbstractLayout) = layout.bbox
bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb)
@@ -97,25 +124,23 @@ bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb)
Base.parent(layout::AbstractLayout) = layout.parent
parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
# NOTE: these should be implemented for subplots in each backend!
# they represent the minimum size of the axes and guides
min_padding_left(layout::AbstractLayout) = 0mm
min_padding_top(layout::AbstractLayout) = 0mm
min_padding_right(layout::AbstractLayout) = 0mm
min_padding_bottom(layout::AbstractLayout) = 0mm
# padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout)
# padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout)
# padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout)
padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout)
padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
update_position!(layout::AbstractLayout) = nothing
update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm,0mm,0mm,0mm]) = nothing
_update_position!(layout::AbstractLayout) = nothing
update_child_bboxes!(layout::AbstractLayout) = nothing
left(layout::AbstractLayout) = left(bbox(layout))
top(layout::AbstractLayout) = top(bbox(layout))
right(layout::AbstractLayout) = right(bbox(layout))
bottom(layout::AbstractLayout) = bottom(bbox(layout))
width(layout::AbstractLayout) = width(bbox(layout))
height(layout::AbstractLayout) = height(bbox(layout))
width(layout::AbstractLayout) = width(layout.bbox)
height(layout::AbstractLayout) = height(layout.bbox)
plotarea(layout::AbstractLayout) = defaultbox
plotarea!(layout::AbstractLayout, bbox::BoundingBox) = nothing
# pass these through to the bbox methods if there's no plotarea
plotarea(layout::AbstractLayout) = bbox(layout)
plotarea!(layout::AbstractLayout, bb::BoundingBox) = bbox!(layout, bb)
attr(layout::AbstractLayout, k::Symbol) = layout.attr[k]
attr(layout::AbstractLayout, k::Symbol, v) = get(layout.attr, k, v)
@@ -204,6 +229,12 @@ rightpad(layout::GridLayout) = layout.minpad[3]
bottompad(layout::GridLayout) = layout.minpad[4]
# here's how this works... first we recursively "update the minimum padding" (which
# means to calculate the minimum size needed from the edge of the subplot to plot area)
# for the whole layout tree. then we can compute the "padding borders" of this
# layout as the biggest padding of the children on the perimeter. then we need to
# recursively pass those borders back down the tree, one side at a time, but ONLY
# to those perimeter children.
# leftpad, toppad, rightpad, bottompad
function _update_min_padding!(layout::GridLayout)
@@ -217,10 +248,11 @@ function _update_min_padding!(layout::GridLayout)
end
function _update_position!(layout::GridLayout)
map(_update_position!, layout.grid)
function update_position!(layout::GridLayout)
map(update_position!, layout.grid)
end
# some lengths are fixed... we have to split up the free space among the list v
function recompute_lengths(v)
# dump(v)
tot = 0pct
@@ -242,11 +274,11 @@ function recompute_lengths(v)
end
# recursively compute the bounding boxes for the layout and plotarea (relative to canvas!)
function update_child_bboxes!(layout::GridLayout)
function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0mm,0mm])
nr, nc = size(layout)
# create a matrix for each minimum padding direction
_update_min_padding!(layout)
# # create a matrix for each minimum padding direction
# _update_min_padding!(layout)
minpad_left = map(leftpad, layout.grid)
minpad_top = map(toppad, layout.grid)
@@ -263,6 +295,12 @@ function update_child_bboxes!(layout::GridLayout)
pad_bottom = maximum(minpad_bottom, 2)
# @show pad_left pad_top pad_right pad_bottom
# make sure the perimeter match the parent
pad_left[1] = max(pad_left[1], minimum_perimeter[1])
pad_top[1] = max(pad_top[1], minimum_perimeter[2])
pad_right[end] = max(pad_right[end], minimum_perimeter[3])
pad_bottom[end] = max(pad_bottom[end], minimum_perimeter[4])
# scale this up to the total padding in each direction
total_pad_horizontal = sum(pad_left + pad_right)
total_pad_vertical = sum(pad_top + pad_bottom)
@@ -303,11 +341,36 @@ function update_child_bboxes!(layout::GridLayout)
child_height = pad_top[r] + plotarea_height + pad_bottom[r]
bbox!(child, BoundingBox(child_left, child_top, child_width, child_height))
# this is the minimum perimeter as decided by this child's parent, so that
# all children on this border have the same value
min_child_perimeter = [
c == 1 ? layout.minpad[1] : 0mm,
r == 1 ? layout.minpad[2] : 0mm,
c == nc ? layout.minpad[3] : 0mm,
r == nr ? layout.minpad[4] : 0mm
]
# recursively update the child's children
update_child_bboxes!(child)
update_child_bboxes!(child, min_child_perimeter)
end
end
# for each inset (floating) subplot, resolve the relative position
# to absolute canvas coordinates, relative to the parent's plotarea
function update_inset_bboxes!(plt::Plot)
for sp in plt.inset_subplots
p_area = Measures.resolve(plotarea(sp.parent), sp[:relative_bbox])
# @show bbox(sp.parent) sp[:relative_bbox] p_area
plotarea!(sp, p_area)
bbox!(sp, bbox(
left(p_area) - leftpad(sp),
top(p_area) - toppad(sp),
width(p_area) + leftpad(sp) + rightpad(sp),
height(p_area) + toppad(sp) + bottompad(sp)
))
end
end
# ----------------------------------------------------------------------
@@ -376,40 +439,21 @@ end
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
# # pass the layout arg through
# function build_layout(d::KW)
# build_layout(get(d, :layout, default(:layout)))
# end
#
# function build_layout(n::Integer)
# nr, nc = compute_gridsize(n, -1, -1)
# build_layout(GridLayout(nr, nc), n)
# end
#
# function build_layout{I<:Integer}(sztup::NTuple{2,I})
# nr, nc = sztup
# build_layout(GridLayout(nr, nc))
# end
#
# function build_layout{I<:Integer}(sztup::NTuple{3,I})
# n, nr, nc = sztup
# nr, nc = compute_gridsize(n, nr, nc)
# build_layout(GridLayout(nr, nc), n)
# end
#
# # compute number of subplots
# function build_layout(layout::GridLayout)
# # recursively get the size of the grid
# n = calc_num_subplots(layout)
# build_layout(layout, n)
# end
# ----------------------------------------------------------------------
function build_layout(args...)
layout, n = layout_args(args...)
build_layout(layout, n)
end
# n is the number of subplots
# # just a single subplot
# function build_layout(sp::Subplot, n::Integer)
# sp, Subplot[sp], SubplotMap(gensym() => sp)
# end
# n is the number of subplots... build a grid and initialize the inner subplots recursively
function build_layout(layout::GridLayout, n::Integer)
nr, nc = size(layout)
subplots = Subplot[]
@@ -512,44 +556,13 @@ rowsize(v) = isrow(v) ? length(v.args) : 1
function create_grid(expr::Expr)
# cellsym = gensym(:cell)
# @show expr
if iscol(expr)
create_grid_vcat(expr)
# rowsizes = map(rowsize, expr.args)
# rmin, rmax = extrema(rowsizes)
# if rmin > 0 && rmin == rmax
# # we have a grid... build the whole thing
# # note: rmin is the number of columns
# nr = length(expr.args)
# nc = rmin
#
# :(let cell = GridLayout($nr, $nc)
# $([:(cell[$r,$c] = $(create_grid(expr.args[r], c))) for r=1:nr, c=1:nc]...)
# for r=1:nr
# layout = $(create_grid(expr.args[r])
# cell[r,]
# $([:($cellsym[$r,1] = $(create_grid(expr.args[r]))) for r=1:length(expr.args)]...)
# $cellsym
# end)
# else
# # otherwise just build one row at a time
# :(let
# $cellsym = GridLayout($(length(expr.args)), 1)
# $([:($cellsym[$i,1] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
# $cellsym
# end)
# end
elseif isrow(expr)
:(let cell = GridLayout(1, $(length(expr.args)))
$([:(cell[1,$i] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
cell
end)
# :(let
# $cellsym = GridLayout(1, $(length(expr.args)))
# $([:($cellsym[1,$i] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
# $cellsym
# end)
elseif expr.head == :curly
create_grid_curly(expr)
@@ -562,17 +575,14 @@ end
function create_grid_vcat(expr::Expr)
rowsizes = map(rowsize, expr.args)
rmin, rmax = extrema(rowsizes)
# @show rmin, rmax
if rmin > 0 && rmin == rmax
# we have a grid... build the whole thing
# note: rmin is the number of columns
nr = length(expr.args)
nc = rmin
# @show nr, nc
body = Expr(:block)
for r=1:nr
arg = expr.args[r]
# @show r, arg
if isrow(arg)
for (c,item) in enumerate(arg.args)
push!(body.args, :(cell[$r,$c] = $(create_grid(item))))
@@ -581,30 +591,16 @@ function create_grid_vcat(expr::Expr)
push!(body.args, :(cell[$r,1] = $(create_grid(arg))))
end
end
# @show body
:(let cell = GridLayout($nr, $nc)
$body
cell
end)
# :(let cell = GridLayout($nr, $nc)
# $([:(cell[$r,$c] = $(create_grid(expr.args[r], c))) for r=1:nr, c=1:nc]...)
# for r=1:nr
# layout = $(create_grid(expr.args[r])
# cell[r,]
# $([:($cellsym[$r,1] = $(create_grid(expr.args[r]))) for r=1:length(expr.args)]...)
# $cellsym
# end)
else
# otherwise just build one row at a time
:(let cell = GridLayout($(length(expr.args)), 1)
$([:(cell[$i,1] = $(create_grid(v))) for (i,v) in enumerate(expr.args)]...)
cell
end)
# :(let
# $cellsym = GridLayout($(length(expr.args)), 1)
# $([:($cellsym[$i,1] = $(create_grid(expr.args[i]))) for i=1:length(expr.args)]...)
# $cellsym
# end)
end
end
@@ -644,7 +640,9 @@ end
function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
subplots = filter(l -> isa(l, Subplot), a)
axes = [sp.attr[axissym] for sp in subplots]
link_axes!(axes...)
if length(axes) > 0
link_axes!(axes...)
end
end
# don't do anything for most layout types
+30
View File
@@ -151,6 +151,36 @@ for mime in keys(_mimeformats)
end
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()
# first save a pdf file
pdf(plt, fn)
# load that pdf into a FileIO Stream
s = FileIO.load(fn * ".pdf")
# save a png
pngfn = fn * ".png"
FileIO.save(pngfn, s)
# now write from the file
write(io, readall(open(pngfn)))
end
end
end
# function html_output_format(fmt)
# if fmt == "png"
# @eval function Base.writemime(io::IO, ::MIME"text/html", plt::Plot)
+113 -31
View File
@@ -94,6 +94,9 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
for (idx, sp) in enumerate(plt.subplots)
_initialize_subplot(plt, sp)
serieslist = series_list(sp)
if sp in sp.plt.inset_subplots
push!(plt.inset_subplots, sp)
end
sp.plt = plt
sp.attr[:subplot_index] = idx
for series in serieslist
@@ -102,6 +105,25 @@ 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)
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])
# finish up
current(plt)
if get(d, :show, default(:show))
@@ -142,7 +164,7 @@ end
function _apply_series_recipe(plt::Plot, d::KW)
st = d[:seriestype]
# @show st
if st in supportedTypes()
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
@@ -173,7 +195,7 @@ function _apply_series_recipe(plt::Plot, d::KW)
# add the series!
warnOnUnsupportedArgs(plt.backend, d)
warnOnUnsupported_args(plt.backend, d)
warnOnUnsupported(plt.backend, d)
series = Series(d)
push!(plt.series_list, series)
@@ -263,7 +285,7 @@ function _plot!(plt::Plot, d::KW, args...)
kw[:series_plotindex] = plt.n
# check that the backend will support the command and add it to the list
warnOnUnsupportedScales(plt.backend, kw)
warnOnUnsupported_scales(plt.backend, kw)
push!(kw_list, kw)
# handle error bars by creating new recipedata data... these will have
@@ -305,17 +327,17 @@ function _plot!(plt::Plot, d::KW, args...)
end
end
# merge in anything meant for plot/subplot
# merge in anything meant for plot/subplot/axis
for kw in kw_list
for (k,v) in kw
for defdict in (_plot_defaults, _subplot_defaults)
for defdict in (_plot_defaults,)
# _subplot_defaults,
# _axis_defaults,
# _axis_defaults_byletter)
if haskey(defdict, k)
d[k] = pop!(kw, k)
end
end
# if haskey(_plot_defaults, k) || haskey(_subplot_defaults, k)
# d[k] = v
# end
end
end
@@ -323,6 +345,7 @@ function _plot!(plt::Plot, d::KW, args...)
_update_plot_args(plt, d)
if !plt.init
plt.o = _create_backend_figure(plt)
# DD(d)
# create the layout and subplots from the inputs
plt.layout, plt.subplots, plt.spmap = build_layout(plt.attr)
@@ -334,14 +357,71 @@ function _plot!(plt::Plot, d::KW, args...)
plt.init = true
end
# just in case the backend needs to set up the plot (make it current or something)
_prepare_plot_object(plt)
# handle inset subplots
insets = plt[:inset_subplots]
if insets != nothing
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
push!(plt.subplots, sp)
sp.attr[:subplot_index] = length(plt.subplots)
push!(plt.inset_subplots, sp)
end
end
# we'll keep a map of subplot to an attribute override dict.
# any series which belong to that subplot
sp_attrs = Dict{Subplot,Any}()
for (i,kw) in enumerate(kw_list)
# get the Subplot object to which the series belongs
sp = get(kw, :subplot, :auto)
sp = if sp == :auto
mod1(i,length(plt.subplots))
else
slice_arg(sp, i)
end
sp = kw[:subplot] = get_subplot(plt, sp)
# idx = get_subplot_index(plt, sp)
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
# # 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)
# if we picked up any subplot-specific overrides, merge them here
attr = merge(d, get(sp_attrs, sp, KW()))
# DD(attr, "sp$idx")
_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])
@@ -351,19 +431,17 @@ function _plot!(plt::Plot, d::KW, args...)
# this is it folks!
# TODO: we probably shouldn't use i for tracking series index, but rather explicitly track it in recipes
for (i,kw) in enumerate(kw_list)
# if !(get(kw, :seriestype, :none) in (:xerror, :yerror))
# plt.n += 1
# end
command_idx = kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
# get the Subplot object to which the series belongs
sp = get(kw, :subplot, :auto)
sp = if sp == :auto
mod1(i,length(plt.subplots))
else
slice_arg(sp, i)
end
sp = kw[:subplot] = get_subplot(plt, sp)
# # get the Subplot object to which the series belongs
# sp = get(kw, :subplot, :auto)
# sp = if sp == :auto
# mod1(i,length(plt.subplots))
# else
# slice_arg(sp, i)
# end
# sp = kw[:subplot] = get_subplot(plt, sp)
sp = kw[:subplot]
idx = get_subplot_index(plt, sp)
# strip out series annotations (those which are based on series x/y coords)
@@ -393,15 +471,6 @@ function _plot!(plt::Plot, d::KW, args...)
_apply_series_recipe(plt, kw)
end
# # everything is processed, time to compute the layout bounding boxes
# _before_layout_calcs(plt)
# w, h = plt.attr[:size]
# plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
# update_child_bboxes!(plt.layout)
#
# # TODO just need to pass plt... and we should do all non-series updates here
# _update_plot_object(plt)
current(plt)
# note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
@@ -429,8 +498,21 @@ function prepare_output(plt::Plot)
w, h = plt.attr[:size]
plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
# One pass down and back up the tree to compute the minimum padding
# of the children on the perimeter. This is an backend callback.
_update_min_padding!(plt.layout)
for sp in plt.inset_subplots
_update_min_padding!(sp)
end
# now another pass down, to update the bounding boxes
update_child_bboxes!(plt.layout)
# update those bounding boxes of inset subplots
update_inset_bboxes!(plt)
# the backend callback, to reposition subplots, etc
_update_plot_object(plt)
end
+119 -39
View File
@@ -53,7 +53,47 @@ end
# ----------------------------------------------------------------------------------
const _series_recipe_deps = Dict()
function series_recipe_dependencies(st::Symbol, deps::Symbol...)
_series_recipe_deps[st] = deps
end
function seriestype_supported(st::Symbol)
seriestype_supported(backend(), st)
end
# returns :no, :native, or :recipe depending on how it's supported
function seriestype_supported(pkg::AbstractBackend, st::Symbol)
# is it natively supported
if st in supported_types(pkg)
return :native
end
haskey(_series_recipe_deps, st) || return :no
supported = true
for dep in _series_recipe_deps[st]
if seriestype_supported(pkg, dep) == :no
supported = false
end
end
supported ? :recipe : :no
end
macro deps(st, args...)
:(series_recipe_dependencies($(quot(st)), $(map(quot, args)...)))
end
# get a list of all seriestypes
function all_seriestypes()
sts = Set{Symbol}(keys(_series_recipe_deps))
for bsym in backends()
btype = _backendType[bsym]
sts = union(sts, Set{Symbol}(supported_types(btype())))
end
sort(collect(sts))
end
# ----------------------------------------------------------------------------------
@@ -156,21 +196,23 @@ end
seriestype := :path
()
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
# @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
@recipe function f(::Type{Val{:hline}}, x, y, z)
xmin, xmax = axis_limits(d[:subplot][:xaxis])
@@ -182,6 +224,7 @@ end
seriestype := :path
()
end
@deps hline path
@recipe function f(::Type{Val{:vline}}, x, y, z)
ymin, ymax = axis_limits(d[:subplot][:yaxis])
@@ -193,6 +236,7 @@ end
seriestype := :path
()
end
@deps vline path
# ---------------------------------------------------------------------------
# steps
@@ -231,6 +275,7 @@ end
end
()
end
@deps steppre path scatter
# create a path from steps
@recipe function f(::Type{Val{:steppost}}, x, y, z)
@@ -251,6 +296,7 @@ end
end
()
end
@deps steppost path scatter
# ---------------------------------------------------------------------------
@@ -289,6 +335,7 @@ sticks_fillfrom(fr::AVec, i::Integer) = fr[mod1(i, length(fr))]
end
()
end
@deps sticks path scatter
# ---------------------------------------------------------------------------
@@ -350,6 +397,7 @@ end
seriestype := :path
()
end
@deps bar path
# ---------------------------------------------------------------------------
# Histograms
@@ -402,6 +450,7 @@ end
seriestype := :bar
()
end
@deps histogram bar
# ---------------------------------------------------------------------------
# Histogram 2D
@@ -443,6 +492,23 @@ centers(v::AVec) = v[1] + cumsum(diff(v))
seriestype := :heatmap
()
end
@deps histogram2d heatmap
# ---------------------------------------------------------------------------
# scatter 3d
@recipe function f(::Type{Val{:scatter3d}}, x, y, z)
seriestype := :path3d
if d[:markershape] == :none
markershape := :circle
end
linewidth := 0
linealpha := 0
()
end
# note: don't add dependencies because this really isn't a drop-in replacement
# ---------------------------------------------------------------------------
# Box Plot
@@ -540,7 +606,7 @@ notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
# Outliers
@series begin
seriestype := :scatter
markershape := :ellipse
markershape := :circle
x := outliers_x
y := outliers_y
label := ""
@@ -549,9 +615,8 @@ notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
end
() # expects a tuple returned
# KW[d]
end
@deps boxplot shape scatter
# ---------------------------------------------------------------------------
# Violin Plot
@@ -560,13 +625,18 @@ end
# otherwise, just use a histogram
if is_installed("KernelDensity")
@eval import KernelDensity
@eval function violin_coords(y, bins = 30)
kd = KernelDensity.kde(y, npoints = isa(bins, Integer) ? bins : 30)
@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, bins = 30)
edges, widths = hist(y, isa(bins, Integer) ? bins : 30)
@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)
@@ -575,7 +645,7 @@ end
# function apply_series_recipe(d::KW, ::Type{Val{:violin}})
@recipe function f(::Type{Val{:violin}}, x, y, z)
@recipe function f(::Type{Val{:violin}}, x, y, z; trim=true)
# dumpdict(d, "box before", true)
# TODO: add scatter series with outliers
@@ -587,7 +657,7 @@ end
# get the edges and widths
y = d[:y][groupby.groupIds[i]]
widths, centers = violin_coords(y)
widths, centers = violin_coords(y, trim=trim)
# normalize
widths = _box_halfwidth * widths / maximum(widths)
@@ -604,25 +674,32 @@ end
# n = length(groupby.groupLabels)
# xticks --> (linspace(0.5,n-0.5,n), groupby.groupLabels)
# clean up d
pop!(d, :trim)
d[:x], d[:y] = shape_coords(shapes)
()
# KW[d]
end
@deps violin shape
# ---------------------------------------------------------------------------
# density
@recipe function f(::Type{Val{:density}}, x, y, z)
newx, newy = violin_coords(y, d[:bins])
@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
# clean up d
pop!(d, :trim)
()
end
@deps density path
@@ -651,9 +728,9 @@ function error_coords(xorig, yorig, ebar)
# for each point, create a line segment from the bottom to the top of the errorbar
for i = 1:max(length(xorig), length(yorig))
xi = get_mod(xorig, i)
yi = get_mod(yorig, i)
ebi = get_mod(ebar, i)
xi = cycle(xorig, i)
yi = cycle(yorig, i)
ebi = cycle(ebar, i)
nanappend!(x, [xi, xi])
e1, e2 = if istuple(ebi)
first(ebi), last(ebi)
@@ -675,6 +752,7 @@ end
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
()
end
@deps yerror path
@recipe function f(::Type{Val{:xerror}}, x, y, z)
error_style!(d)
@@ -682,6 +760,7 @@ end
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
()
end
@deps xerror path
# ---------------------------------------------------------------------------
@@ -702,11 +781,11 @@ function quiver_using_arrows(d::KW)
x, y = zeros(0), zeros(0)
for i = 1:max(length(xorig), length(yorig))
# get the starting position
xi = get_mod(xorig, i)
yi = get_mod(yorig, i)
xi = cycle(xorig, i)
yi = cycle(yorig, i)
# get the velocity
vi = get_mod(velocity, i)
vi = cycle(velocity, i)
vx, vy = if istuple(vi)
first(vi), last(vi)
elseif isscalar(vi)
@@ -728,8 +807,8 @@ end
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
function quiver_using_hack(d::KW)
label := ""
seriestype := :shape
d[:label] = ""
d[:seriestype] = :shape
velocity = error_zipit(d[:quiver])
xorig, yorig = d[:x], d[:y]
@@ -739,12 +818,12 @@ function quiver_using_hack(d::KW)
for i = 1:max(length(xorig), length(yorig))
# get the starting position
xi = get_mod(xorig, i)
yi = get_mod(yorig, i)
xi = cycle(xorig, i)
yi = cycle(yorig, i)
p = P2(xi, yi)
# get the velocity
vi = get_mod(velocity, i)
vi = cycle(velocity, i)
vx, vy = if istuple(vi)
first(vi), last(vi)
elseif isscalar(vi)
@@ -774,13 +853,14 @@ end
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
@recipe function f(::Type{Val{:quiver}}, x, y, z)
if :arrow in supportedArgs()
if :arrow in supported_args()
quiver_using_arrows(d)
else
quiver_using_hack(d)
end
()
end
@deps quiver shape path
# ---------------------------------------------------------------------------
+30 -6
View File
@@ -17,8 +17,8 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
plotIndex += 1
end
aliasesAndAutopick(d, :linestyle, _styleAliases, supportedStyles(pkg), plotIndex)
aliasesAndAutopick(d, :markershape, _markerAliases, supportedMarkers(pkg), plotIndex)
aliasesAndAutopick(d, :linestyle, _styleAliases, supported_styles(pkg), plotIndex)
aliasesAndAutopick(d, :markershape, _markerAliases, supported_markers(pkg), plotIndex)
# update color
d[:seriescolor] = getSeriesRGBColor(d[:seriescolor], sp, plotIndex)
@@ -46,17 +46,20 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
d[:markerstrokecolor] = c
# update alphas
for asym in (:linealpha, :markeralpha, :markerstrokealpha, :fillalpha)
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] = :ellipse
d[:markershape] = :circle
end
end
@@ -117,7 +120,10 @@ immutable SliceIt end
end
# this is the default "type recipe"... just pass the object through
@recipe f{T}(::Type{T}, v::T) = v
@recipe f{T<:Any}(::Type{T}, v::T) = v
# this should catch unhandled "series recipes" and error with a nice message
@recipe f{V<:Val}(::Type{V}, x, y, z) = error("The backend must not support the series type $V, and there isn't a series recipe defined.")
_apply_type_recipe(d, v) = RecipesBase.apply_recipe(d, typeof(v), v)[1].args[1]
@@ -156,6 +162,24 @@ end
SliceIt, nothing, y, nothing
end
end
# if there's more than 3 inputs, it can't be passed directly to SliceIt
# so we'll apply_type_recipe to all of them
@recipe function f(v1, v2, v3, v4, vrest...)
did_replace = false
newargs = map(v -> begin
newv = _apply_type_recipe(d, v)
if newv !== v
did_replace = true
end
newv
end, (v1, v2, v3, v4, vrest...))
if !did_replace
error("Couldn't process recipe args: $(map(typeof, (v1, v2, v3, v4, vrest...)))")
end
newargs
end
# @recipe f(x, y, z) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), apply_recipe(typeof(z), z)
# @recipe f(x, y) = SliceIt, apply_recipe(typeof(x), x), apply_recipe(typeof(y), y), nothing
# @recipe f(y) = SliceIt, nothing, apply_recipe(typeof(y), y), nothing
@@ -323,7 +347,7 @@ end
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec)
mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u), mapFuncOrFuncs(fz, u)
end
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPointsn = 200)
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, umin::Number, umax::Number, numPoints = 200)
fx, fy, fz, linspace(umin, umax, numPoints)
end
+6 -5
View File
@@ -36,11 +36,12 @@ get_subplot_index(plt::Plot, sp::Subplot) = findfirst(_ -> _ === sp, plt.subplot
series_list(sp::Subplot) = filter(series -> series.d[:subplot] === sp, sp.plt.series_list)
function should_add_to_legend(series::Series)
!(series.d[:label] == "" || series.d[:seriestype] in (
:hexbin,:histogram2d,:hline,:vline,
:contour,:contour3d,:surface,:wireframe,
:heatmap,:path3d,:scatter3d, :pie, :image
))
series.d[:primary] && series.d[:label] != "" &&
!(series.d[:seriestype] in (
:hexbin,:histogram2d,:hline,:vline,
:contour,:contour3d,:surface,:wireframe,
:heatmap, :pie, :image
))
end
# ----------------------------------------------------------------------
+3 -1
View File
@@ -73,12 +73,14 @@ type Plot{T<:AbstractBackend} <: AbstractPlot{T}
subplots::Vector{Subplot}
spmap::SubplotMap # provide any label as a map to a subplot
layout::AbstractLayout
inset_subplots::Vector{Subplot} # list of inset subplots
init::Bool
end
function Plot()
Plot(backend(), 0, KW(), KW(), Series[], nothing,
Subplot[], SubplotMap(), EmptyLayout(), false)
Subplot[], SubplotMap(), EmptyLayout(),
Subplot[], false)
end
# TODO: make a decision... should plt[1] return the first subplot or the first series??
+54 -7
View File
@@ -131,7 +131,7 @@ function replace_image_with_heatmap{T<:Colorant}(z::Array{T})
end
function imageHack(d::KW)
:heatmap in supportedTypes() || error("Neither :image or :heatmap are supported!")
:heatmap in supported_types() || error("Neither :image or :heatmap are supported!")
d[:seriestype] = :heatmap
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
end
@@ -142,9 +142,9 @@ end
nop() = nothing
notimpl() = error("This has not been implemented yet")
get_mod(v::AVec, idx::Int) = v[mod1(idx, length(v))]
get_mod(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
get_mod(v, idx::Int) = v
Base.cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
Base.cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
Base.cycle(v, idx::Int) = v
makevec(v::AVec) = v
makevec{T}(v::T) = T[v]
@@ -232,6 +232,27 @@ function heatmap_edges(v::AVec)
end
function calc_r_extrema(x, y)
xmin, xmax = extrema(x)
ymin, ymax = extrema(y)
r = 0.5 * min(xmax - xmin, ymax - ymin)
extrema(r)
end
function convert_to_polar(x, y, r_extrema = calc_r_extrema(x, y))
rmin, rmax = r_extrema
phi, r = x, y
r = 0.5 * (r - rmin) / (rmax - rmin)
n = max(length(phi), length(r))
x = zeros(n)
y = zeros(n)
for i in 1:n
x[i] = cycle(r,i) * cos(cycle(phi,i))
y[i] = cycle(r,i) * sin(cycle(phi,i))
end
x, y
end
function fakedata(sz...)
y = zeros(sz...)
for r in 2:size(y,1)
@@ -260,6 +281,8 @@ ismatrix(::Any) = false
isscalar(::Real) = true
isscalar(::Any) = false
is_2tuple(v) = typeof(v) <: Tuple && length(v) == 2
isvertical(d::KW) = get(d, :orientation, :vertical) in (:vertical, :v, :vert)
@@ -285,6 +308,30 @@ Base.merge(a::AbstractVector, b::AbstractVector) = sort(unique(vcat(a,b)))
nanpush!(a::AbstractVector, b) = (push!(a, NaN); push!(a, b))
nanappend!(a::AbstractVector, b) = (push!(a, NaN); append!(a, b))
function nansplit(v::AVec)
vs = Vector{eltype(v)}[]
while true
idx = findfirst(isnan, v)
if idx <= 0
# no nans
push!(vs, v)
break
elseif idx > 1
push!(vs, v[1:idx-1])
end
v = v[idx+1:end]
end
vs
end
function nanvcat(vs::AVec)
v_out = zeros(0)
for v in vs
nanappend!(v_out, v)
end
v_out
end
# given an array of discrete values, turn it into an array of indices of the unique values
# returns the array of indices (znew) and a vector of unique values (vals)
function indices_and_unique_values(z::AbstractArray)
@@ -508,9 +555,9 @@ function setxyz!{X,Y,Z}(plt::Plot, xyz::Tuple{X,Y,Z}, i::Integer)
series = plt.series_list[i]
series.d[:x], series.d[:y], series.d[:z] = xyz
sp = series.d[:subplot]
expand_extrema!(sp.attr[:xaxis], xy[1])
expand_extrema!(sp.attr[:yaxis], xy[2])
expand_extrema!(sp.attr[:zaxis], xy[3])
expand_extrema!(sp.attr[:xaxis], xyz[1])
expand_extrema!(sp.attr[:yaxis], xyz[2])
expand_extrema!(sp.attr[:zaxis], xyz[3])
_series_updated(plt, series)
end
+8 -6
View File
@@ -22,7 +22,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.0"
const _current_plots_version = v"0.7.1"
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
@@ -45,17 +45,19 @@ function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = is
# @show refdir fn G
versions = map(fn -> VersionNumber(split(fn,"/")[end]), G)
versions = reverse(sort(versions))
versions = filter(v -> v <= _current_plots_version, versions)
# @show refdir fn versions
reffn = nothing
newdir = joinpath(refdir, string(_current_plots_version))
newfn = joinpath(newdir, fn)
# figure out which reference file we should compare to, by finding the highest versioned file
reffn = nothing
for v in versions
try
tmpfn = joinpath(refdir, string(v), fn)
# @show "trying", tmpfn
f = open(tmpfn)
tmpfn = joinpath(refdir, string(v), fn)
if isfile(tmpfn)
reffn = tmpfn
break
end
end