Compare commits
197 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f3d31a62db | |||
| ee15b2c166 | |||
| 4d84ab155a | |||
| accc063db8 | |||
| b88d26ce77 | |||
| 886386508c | |||
| d893034f47 | |||
| f4b798765d | |||
| 5e6a9da3ed | |||
| 598211c349 | |||
| 7843821109 | |||
| 96f3baca41 | |||
| 67b4157e25 | |||
| a523903c15 | |||
| 92e9fa2084 | |||
| c75e1df190 | |||
| 79bf45b823 | |||
| 58989465f4 | |||
| 1e6b442c6c | |||
| 33addf4449 | |||
| c5fed9524a | |||
| cbfadde43c | |||
| cebe1eedeb | |||
| c22a9e51a1 | |||
| e41022c7be | |||
| 56a9389023 | |||
| 1188230641 | |||
| e760b42560 | |||
| 627c03193a | |||
| 0f837f3438 | |||
| af072da0b0 | |||
| c4f3b93da5 | |||
| 906f11ed76 | |||
| 2e7329931c | |||
| 5e5e1850ac | |||
| 6b250d1ab6 | |||
| 1ee6affcb3 | |||
| 7a1dd0db29 | |||
| e41918569b | |||
| ec6f75b59e | |||
| ec595c23e5 | |||
| 2c5a37d38e | |||
| 3ec61cd012 | |||
| a3b4b9c8b2 | |||
| 5166d16406 | |||
| 3e7dd9bb77 | |||
| 0ab57b3d76 | |||
| 0be0824207 | |||
| 7def4e8e73 | |||
| 2397c78e9f | |||
| 9a59df793d | |||
| 1852ba99b3 | |||
| 0d665c661f | |||
| f11828075b | |||
| 9a1efefbd4 | |||
| 066ff96b58 | |||
| 7d8b00d1b9 | |||
| 5d4f3182fc | |||
| b8d80f5285 | |||
| ccb9df2a08 | |||
| c3ae9e1ee2 | |||
| abcc4b6739 | |||
| dd3f65496a | |||
| 2d69fc39de | |||
| 731cac4262 | |||
| b4846bd5d5 | |||
| fac39344f4 | |||
| ffb9878893 | |||
| 034a1af649 | |||
| 06fd894f79 | |||
| 5731648479 | |||
| 1ebc2cb9c1 | |||
| 0bd76ef7a4 | |||
| ea15f7746d | |||
| ed102269e7 | |||
| 76e1f4720b | |||
| 2317b77314 | |||
| 1a7bb10a22 | |||
| 0732006d5a | |||
| fb2e12089f | |||
| 7a0e03b374 | |||
| f2235abf08 | |||
| 5435ce20a9 | |||
| 124f1fe701 | |||
| 37e2c18c3c | |||
| 032f5a5dab | |||
| 7e41a2f40d | |||
| 8b90eb37c5 | |||
| ff797b11d1 | |||
| 31c665a837 | |||
| 68c39697a6 | |||
| 6f8881b610 | |||
| 1b468ba5d8 | |||
| 7aca2b1584 | |||
| 41e56b1f2e | |||
| cad96e652f | |||
| c7bde0ff19 | |||
| 6b94bbac2d | |||
| f7de9fbea4 | |||
| 5465479e57 | |||
| cd3ce86dbb | |||
| b109025fac | |||
| 6e74411a8e | |||
| 6c51982793 | |||
| 8f841cdea4 | |||
| eab4cfdf2d | |||
| 34c0361d1a | |||
| 24c27737c1 | |||
| b0dabaafe8 | |||
| 9472c0c5e5 | |||
| 89fe3c9150 | |||
| 9e1d551173 | |||
| 2d61b413d6 | |||
| aec08c74e0 | |||
| 48e8cb5eb1 | |||
| fd947ab887 | |||
| 5395b41852 | |||
| b7536f129b | |||
| 28b026c9ad | |||
| d4b7bfbe1d | |||
| 4b2ce036e8 | |||
| 2a350df5eb | |||
| bdc815066e | |||
| 44f4f22fdd | |||
| 5ae2044d02 | |||
| 5e80a05f32 | |||
| eb7534d319 | |||
| 471b09bd6c | |||
| a4549cbfa2 | |||
| d663267cde | |||
| 35a56962fc | |||
| 82161e60bc | |||
| 51a47cb2b1 | |||
| 9804debe86 | |||
| c93e2b80ca | |||
| 1555876280 | |||
| 1d08e6619b | |||
| 0880487be2 | |||
| 2ac4ffc576 | |||
| 7b2a0a92c4 | |||
| d883274586 | |||
| 803de95367 | |||
| 3ca39728d8 | |||
| 1111000217 | |||
| dd505fedcc | |||
| 48ff2fb8b8 | |||
| d5eafad48e | |||
| 2e1c86c0c7 | |||
| c728bf52b8 | |||
| 95a26f5080 | |||
| 85eaef6aa9 | |||
| dee160f2b8 | |||
| 6d38e52321 | |||
| caed94328f | |||
| ebf92d3033 | |||
| fdadfa3a80 | |||
| f8b84185ac | |||
| caf86686dd | |||
| bcd5d9ef90 | |||
| 45b5922d01 | |||
| 1a91eacdcb | |||
| 0421fe9a82 | |||
| 43a213643b | |||
| cd8939fe0b | |||
| c250d7c073 | |||
| 3b570edea1 | |||
| 199a3306db | |||
| 3236e06fb9 | |||
| 492618ae67 | |||
| c5737db4f5 | |||
| 40b49b9873 | |||
| 1fa34574e8 | |||
| 4c2db68037 | |||
| 1c522159df | |||
| 967dca7912 | |||
| 7d66c502f4 | |||
| 9b9e2fe6cd | |||
| 47ee0a4c97 | |||
| f10bda463c | |||
| 72d336265b | |||
| a087a6694b | |||
| 5eaf6b2adc | |||
| 57840a37d0 | |||
| aedcb372f6 | |||
| 7f554d3c84 | |||
| edf1daa7d7 | |||
| f554bf925e | |||
| 9676309ba2 | |||
| c78444fc24 | |||
| 4ab51d4bfa | |||
| e381c13dc6 | |||
| ec31898f25 | |||
| a88d78a2e9 | |||
| 92430afba9 | |||
| 8f60a4df8d | |||
| 08c9e627ab | |||
| 46ad076751 |
@@ -3,14 +3,47 @@
|
||||
|
||||
#### notes on release changes, ongoing development, and future planned work
|
||||
|
||||
- All new development should target 0.9!
|
||||
- Minor version 0.8 is the last one to support Julia 0.4!!
|
||||
- All new development should target 0.12!
|
||||
- Minor version 0.11 is the last one to support Julia 0.5!!
|
||||
- Critical bugfixes only
|
||||
- `backports` branch is for Julia 0.4
|
||||
|
||||
---
|
||||
|
||||
## 0.9 (current master/dev)
|
||||
## 0.11 (current master/dev)
|
||||
|
||||
#### 0.11.0
|
||||
|
||||
- julia 0.6 compatibility
|
||||
- matplotlib 0.2.0 compatibility
|
||||
- add inspectdr backend
|
||||
- improved histogram functionality:
|
||||
- added a `:stephist` and `:scatterhist` series type as well as ``:barhist` (the default)
|
||||
- support for log scale axes with histograms
|
||||
- support for plotting `StatsBase.Histogram`
|
||||
- allowing bins to be specified as `:sturges`, `:rice`, `:scott` or :fd
|
||||
- allow `normalization` to be specified as :density (for unequal bins) or :pdf (sum to 1)
|
||||
- add a `plotattr` function to access documentation for Plots attribute
|
||||
- add `fill_z` attribute for pyplot
|
||||
- add colorbar_title to plotlyjs
|
||||
- enable standalone window for plotlyjs
|
||||
- improved support for pgfplots, ticks rotation, clims, series_annotations
|
||||
- restore colorbars for GR
|
||||
- better axis labels for heatmap in GR
|
||||
- better marker sizes in GR
|
||||
- fix color representation in GR
|
||||
- update GR legend
|
||||
- fix image bug on GR
|
||||
- fix glvisualize dependencies
|
||||
- set dotted grid lines for pyplot
|
||||
- several improvements to inspectdr
|
||||
- improved tick positions for TimeType x axes
|
||||
- support for improved color gradient capability in PlotUtils
|
||||
- add a showlibrary recipe to display color libraries
|
||||
- add a showgradient recipe to display color gradients
|
||||
- add `vectorfield` as an alias for `quiver`
|
||||
- use `PlotUtils.adaptedgrid` for functions
|
||||
|
||||
|
||||
#### 0.9.5
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Plots
|
||||
|
||||
[](https://travis-ci.org/tbreloff/Plots.jl)
|
||||
[](https://travis-ci.org/JuliaPlots/Plots.jl)
|
||||
[](https://gitter.im/tbreloff/Plots.jl?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.3) -->
|
||||
<!-- [](http://pkg.julialang.org/?pkg=Plots&ver=0.4) -->
|
||||
|
||||
+9
-1
@@ -1,3 +1,4 @@
|
||||
__precompile__(true)
|
||||
|
||||
module Plots
|
||||
|
||||
@@ -8,6 +9,7 @@ using Base.Meta
|
||||
@reexport using PlotUtils
|
||||
@reexport using PlotThemes
|
||||
import Showoff
|
||||
import StatsBase
|
||||
|
||||
export
|
||||
grid,
|
||||
@@ -98,7 +100,9 @@ export
|
||||
center,
|
||||
P2,
|
||||
P3,
|
||||
BezierCurve
|
||||
BezierCurve,
|
||||
|
||||
plotattr
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -126,6 +130,7 @@ include("animation.jl")
|
||||
include("output.jl")
|
||||
include("examples.jl")
|
||||
include("arg_desc.jl")
|
||||
include("plotattr.jl")
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
@@ -144,6 +149,9 @@ end
|
||||
@shorthands bar
|
||||
@shorthands barh
|
||||
@shorthands histogram
|
||||
@shorthands barhist
|
||||
@shorthands stephist
|
||||
@shorthands scatterhist
|
||||
@shorthands histogram2d
|
||||
@shorthands density
|
||||
@shorthands heatmap
|
||||
|
||||
+2
-1
@@ -80,7 +80,8 @@ function buildanimation(animdir::AbstractString, fn::AbstractString;
|
||||
|
||||
catch err
|
||||
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
|
||||
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`
|
||||
ImageMagick can be installed by executing `Pkg.add("ImageMagick")`.
|
||||
You may also need to install the imagemagick c++ library through your operating system.
|
||||
Will try ffmpeg, but it's lower quality...)""")
|
||||
|
||||
# low quality
|
||||
|
||||
+3
-2
@@ -21,7 +21,7 @@ const _arg_desc = KW(
|
||||
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
|
||||
:markerstrokecolor => "Color Type. Color of the marker stroke (border). `:match` will take the value from `:foreground_color_subplot`.",
|
||||
:markerstrokealpha => "Number in [0,1]. The alpha/opacity override for the marker stroke (border). `nothing` (the default) means it will take the alpha value of markerstrokecolor.",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector. For histogram-types, defines the number of bins, or the edges, of the histogram.",
|
||||
:bins => "Integer, NTuple{2,Integer}, AbstractVector or Symbol. Default is :auto. For histogram-types, defines the number of bins, or the edges, of the histogram, or the auto-binning algorithm to use (:sturges, :sqrt, :rice, :scott or :fd)",
|
||||
:smooth => "Bool. Add a regression line?",
|
||||
:group => "AbstractVector. Data is split into a separate series, one for each unique value in `group`.",
|
||||
:x => "Various. Input data. First Dimension",
|
||||
@@ -29,6 +29,7 @@ const _arg_desc = KW(
|
||||
:z => "Various. Input data. Third Dimension. May be wrapped by a `Surface` for surface and heatmap types.",
|
||||
:marker_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series data point, which correspond to the color to be used from a markercolor gradient.",
|
||||
:line_z => "AbstractVector, Function `f(x,y,z) -> z_value`, or nothing. z-values for each series line segment, which correspond to the color to be used from a linecolor gradient. Note that for N points, only the first N-1 values are used (one per line-segment).",
|
||||
:fill_z => "Matrix{Float64} of the same size as z matrix, which specifies the color of the 3D surface; the default value is `nothing`.",
|
||||
:levels => "Integer, NTuple{2,Integer}. Number of levels (or x-levels/y-levels) for a contour type.",
|
||||
:orientation => "Symbol. Horizontal or vertical orientation for bar types. Values `:h`, `:hor`, `:horizontal` correspond to horizontal (sideways, anchored to y-axis), and `:v`, `:vert`, and `:vertical` correspond to vertical (the default).",
|
||||
:bar_position => "Symbol. Choose from `:overlay` (default), `:stack`. (warning: May not be implemented fully)",
|
||||
@@ -39,7 +40,7 @@ const _arg_desc = KW(
|
||||
:ribbon => "Number or AbstractVector. Creates a fillrange around the data points.",
|
||||
:quiver => "AbstractVector or 2-Tuple of vectors. The directional vectors U,V which specify velocity/gradient vectors for a quiver plot.",
|
||||
:arrow => "nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths. Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point). Used in quiverplot, streamplot, or similar.",
|
||||
:normalize => "Bool. Should normalize histogram types? Trying for area == 1.",
|
||||
:normalize => "Bool or Symbol. Histogram normalization mode. Possible values are: false/:none (no normalization, default), true/:pdf (normalize to a PDF with integral of 1) and :density (only normalize in respect to bin sizes).",
|
||||
:weights => "AbstractVector. Used in histogram types for weighted counts.",
|
||||
:contours => "Bool. Add contours to the side-grids of 3D plots? Used in surface/wireframe.",
|
||||
:match_dimensions => "Bool. For heatmap types... should the first dimension of a matrix (rows) correspond to the first dimension of the plot (x-axis)? The default is false, which matches the behavior of Matplotlib, Plotly, and others. Note: when passing a function for z, the function should still map `(x,y) -> z`.",
|
||||
|
||||
+12
-5
@@ -35,7 +35,9 @@ const _3dTypes = [
|
||||
]
|
||||
const _allTypes = vcat([
|
||||
:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
|
||||
:heatmap, :hexbin, :histogram, :histogram2d, :histogram3d, :density, :bar, :hline, :vline,
|
||||
:heatmap, :hexbin, :barbins, :barhist, :histogram, :scatterbins,
|
||||
:scatterhist, :stepbins, :stephist, :bins2d, :histogram2d, :histogram3d,
|
||||
:density, :bar, :hline, :vline,
|
||||
:contour, :pie, :shape, :image
|
||||
], _3dTypes)
|
||||
|
||||
@@ -65,6 +67,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
|
||||
:polygon => :shape,
|
||||
:box => :boxplot,
|
||||
:velocity => :quiver,
|
||||
:vectorfield => :quiver,
|
||||
:gradient => :quiver,
|
||||
:img => :image,
|
||||
:imshow => :image,
|
||||
@@ -77,7 +80,7 @@ const _typeAliases = Dict{Symbol,Symbol}(
|
||||
|
||||
add_non_underscore_aliases!(_typeAliases)
|
||||
|
||||
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
|
||||
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :barhist, :barbins)
|
||||
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
|
||||
like_surface(seriestype::Symbol) = seriestype in (:contour, :contourf, :contour3d, :heatmap, :surface, :wireframe, :image)
|
||||
|
||||
@@ -153,6 +156,8 @@ const _markerAliases = Dict{Symbol,Symbol}(
|
||||
)
|
||||
|
||||
const _allScales = [:identity, :ln, :log2, :log10, :asinh, :sqrt]
|
||||
const _logScales = [:ln, :log2, :log10]
|
||||
const _logScaleBases = Dict(:ln => e, :log2 => 2.0, :log10 => 10.0)
|
||||
const _scaleAliases = Dict{Symbol,Symbol}(
|
||||
:none => :identity,
|
||||
:log => :log10,
|
||||
@@ -180,7 +185,7 @@ const _series_defaults = KW(
|
||||
:markerstrokewidth => 1,
|
||||
:markerstrokecolor => :match,
|
||||
:markerstrokealpha => nothing,
|
||||
:bins => 30, # number of bins for hists
|
||||
:bins => :auto, # number of bins for hists
|
||||
:smooth => false, # regression line?
|
||||
:group => nothing, # groupby vector
|
||||
:x => nothing,
|
||||
@@ -188,6 +193,7 @@ const _series_defaults = KW(
|
||||
:z => nothing, # depth for contour, surface, etc
|
||||
:marker_z => nothing, # value for color scale
|
||||
:line_z => nothing,
|
||||
:fill_z => nothing,
|
||||
:levels => 15,
|
||||
:orientation => :vertical,
|
||||
:bar_position => :overlay, # for bar plots and histograms: could also be stack (stack up) or dodge (side by side)
|
||||
@@ -431,6 +437,7 @@ add_aliases(:zguide, :zlabel, :zlab, :zl)
|
||||
add_aliases(:zlims, :zlim, :zlimit, :zlimits)
|
||||
add_aliases(:zticks, :ztick)
|
||||
add_aliases(:zrotation, :zrot, :zr)
|
||||
add_aliases(:fill_z, :fillz, :fz, :surfacecolor, :surfacecolour, :sc, :surfcolor, :surfcolour)
|
||||
add_aliases(:legend, :leg, :key)
|
||||
add_aliases(:colorbar, :cb, :cbar, :colorkey)
|
||||
add_aliases(:clims, :clim, :cbarlims, :cbar_lims, :climits, :color_limits)
|
||||
@@ -443,7 +450,7 @@ add_aliases(:color_palette, :palette)
|
||||
add_aliases(:overwrite_figure, :clf, :clearfig, :overwrite, :reuse)
|
||||
add_aliases(:xerror, :xerr, :xerrorbar)
|
||||
add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar)
|
||||
add_aliases(:quiver, :velocity, :quiver2d, :gradient)
|
||||
add_aliases(:quiver, :velocity, :quiver2d, :gradient, :vectorfield)
|
||||
add_aliases(:normalize, :norm, :normed, :normalized)
|
||||
add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio)
|
||||
add_aliases(:match_dimensions, :transpose, :transpose_z)
|
||||
@@ -1258,7 +1265,7 @@ function _add_defaults!(d::KW, plt::Plot, sp::Subplot, commandIndex::Int)
|
||||
end
|
||||
|
||||
# scatter plots don't have a line, but must have a shape
|
||||
if d[:seriestype] in (:scatter, :scatter3d)
|
||||
if d[:seriestype] in (:scatter, :scatterbins, :scatterhist, :scatter3d)
|
||||
d[:linewidth] = 0
|
||||
if d[:markershape] == :none
|
||||
d[:markershape] = :circle
|
||||
|
||||
+25
-1
@@ -156,6 +156,30 @@ function optimal_ticks_and_labels(axis::Axis, ticks = nothing)
|
||||
scale = axis[:scale]
|
||||
sf = scalefunc(scale)
|
||||
|
||||
# If the axis input was a Date or DateTime use a special logic to find
|
||||
# "round" Date(Time)s as ticks
|
||||
# This bypasses the rest of optimal_ticks_and_labels, because
|
||||
# optimize_datetime_ticks returns ticks AND labels: the label format (Date
|
||||
# or DateTime) is chosen based on the time span between amin and amax
|
||||
# rather than on the input format
|
||||
# TODO: maybe: non-trivial scale (:ln, :log2, :log10) for date/datetime
|
||||
if ticks == nothing && scale == :identity
|
||||
if axis[:formatter] == dateformatter
|
||||
# optimize_datetime_ticks returns ticks and labels(!) based on
|
||||
# integers/floats corresponding to the DateTime type. Thus, the axes
|
||||
# limits, which resulted from converting the Date type to integers,
|
||||
# are converted to 'DateTime integers' (actually floats) before
|
||||
# being passed to optimize_datetime_ticks.
|
||||
# (convert(Int, convert(DateTime, convert(Date, i))) == 87600000*i)
|
||||
ticks, labels = optimize_datetime_ticks(864e5 * amin, 864e5 * amax;
|
||||
k_min = 2, k_max = 4)
|
||||
# Now the ticks are converted back to floats corresponding to Dates.
|
||||
return ticks / 864e5, labels
|
||||
elseif axis[:formatter] == datetimeformatter
|
||||
return optimize_datetime_ticks(amin, amax; k_min = 2, k_max = 4)
|
||||
end
|
||||
end
|
||||
|
||||
# get a list of well-laid-out ticks
|
||||
scaled_ticks = if ticks == nothing
|
||||
optimize_ticks(
|
||||
@@ -214,7 +238,7 @@ function get_ticks(axis::Axis)
|
||||
# @show ticks dvals cv dv
|
||||
|
||||
# TODO: better/smarter cutoff values for sampling ticks
|
||||
if length(cv) > 30
|
||||
if length(cv) > 30 && ticks == :auto
|
||||
rng = Int[round(Int,i) for i in linspace(1, length(cv), 15)]
|
||||
cv[rng], dv[rng]
|
||||
else
|
||||
|
||||
@@ -276,6 +276,7 @@ end
|
||||
@init_backend GR
|
||||
@init_backend GLVisualize
|
||||
@init_backend PGFPlots
|
||||
@init_backend InspectDR
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
|
||||
+13
-14
@@ -1,4 +1,4 @@
|
||||
``#=
|
||||
#=
|
||||
TODO
|
||||
* move all gl_ methods to GLPlot
|
||||
* integrate GLPlot UI
|
||||
@@ -7,7 +7,6 @@ TODO
|
||||
* polar plots
|
||||
* labes and axis
|
||||
* fix units in all visuals (e.g dotted lines, marker scale, surfaces)
|
||||
* why is there so little unicode supported in the font!??!?
|
||||
=#
|
||||
|
||||
const _glvisualize_attr = merge_with_base_supported([
|
||||
@@ -134,11 +133,6 @@ function empty_screen!(screen)
|
||||
end
|
||||
nothing
|
||||
end
|
||||
function poll_reactive()
|
||||
# run_till_now blocks when message queue is empty!
|
||||
Base.n_avail(Reactive._messages) > 0 && Reactive.run_till_now()
|
||||
end
|
||||
|
||||
|
||||
function get_plot_screen(list::Vector, name, result = [])
|
||||
for elem in list
|
||||
@@ -155,19 +149,20 @@ function get_plot_screen(screen, name, result = [])
|
||||
end
|
||||
|
||||
function create_window(plt::Plot{GLVisualizeBackend}, visible)
|
||||
name = Symbol("Plots.jl")
|
||||
name = Symbol("__Plots.jl")
|
||||
# make sure we have any screen open
|
||||
if isempty(GLVisualize.get_screens())
|
||||
# create a fresh, new screen
|
||||
parent_screen = GLVisualize.glscreen(
|
||||
"Plot",
|
||||
"Plots",
|
||||
resolution = plt[:size],
|
||||
visible = visible
|
||||
)
|
||||
@async GLWindow.waiting_renderloop(parent_screen)
|
||||
GLVisualize.add_screen(parent_screen)
|
||||
end
|
||||
# now lets get ourselves a permanent Plotting screen
|
||||
plot_screens = get_plot_screen(GLVisualize.get_screens(), name)
|
||||
plot_screens = get_plot_screen(GLVisualize.current_screen(), name)
|
||||
screen = if isempty(plot_screens) # no screen with `name`
|
||||
parent = GLVisualize.current_screen()
|
||||
screen = GLWindow.Screen(
|
||||
@@ -183,7 +178,7 @@ function create_window(plt::Plot{GLVisualizeBackend}, visible)
|
||||
else
|
||||
# okay this is silly! Lets see if we can. There is an ID we could use
|
||||
# will not be fine for more than 255 screens though -.-.
|
||||
error("multiple Plot screens. Please don't use any screen with the name Plots.jl")
|
||||
error("multiple Plot screens. Please don't use any screen with the name $name")
|
||||
end
|
||||
# Since we own this window, we can do deep cleansing
|
||||
empty_screen!(screen)
|
||||
@@ -1141,8 +1136,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
|
||||
vis = gl_bar(d, kw_args)
|
||||
elseif st == :image
|
||||
extract_extrema(d, kw_args)
|
||||
z = transpose_z(series, d[:z].surf, false)
|
||||
vis = GL.gl_image(z, kw_args)
|
||||
vis = GL.gl_image(d[:z].surf, kw_args)
|
||||
elseif st == :boxplot
|
||||
extract_c(d, kw_args, :fill)
|
||||
vis = gl_boxplot(d, kw_args)
|
||||
@@ -1182,7 +1176,7 @@ function _display(plt::Plot{GLVisualizeBackend}, visible = true)
|
||||
if _3d
|
||||
GLAbstraction.center!(sp_screen)
|
||||
end
|
||||
Reactive.post_empty()
|
||||
GLAbstraction.post_empty()
|
||||
yield()
|
||||
end
|
||||
end
|
||||
@@ -1422,6 +1416,8 @@ function label_scatter(d, w, ho)
|
||||
color = get(kw, :color, nothing)
|
||||
kw[:color] = isa(color, Array) ? first(color) : color
|
||||
end
|
||||
strcolor = get(kw, :stroke_color, RGBA{Float32}(0,0,0,0))
|
||||
kw[:stroke_color] = isa(strcolor, Array) ? first(strcolor) : strcolor
|
||||
p = get(kw, :primitive, GeometryTypes.Circle)
|
||||
if isa(p, GLNormalMesh)
|
||||
bb = GeometryTypes.AABB{Float32}(GeometryTypes.vertices(p))
|
||||
@@ -1436,6 +1432,9 @@ function label_scatter(d, w, ho)
|
||||
kw[:scale] = Vec3f0(w/2)
|
||||
delete!(kw, :offset)
|
||||
end
|
||||
if isa(p, Array)
|
||||
kw[:primitive] = GeometryTypes.Circle
|
||||
end
|
||||
GL.gl_scatter(Point2f0[(w/2, ho)], kw)
|
||||
end
|
||||
|
||||
|
||||
+25
-16
@@ -172,6 +172,8 @@ function gr_polyline(x, y, func = GR.polyline; arrowside=:none)
|
||||
end
|
||||
end
|
||||
|
||||
gr_inqtext(x, y, s::Symbol) = gr_inqtext(x, y, string(s))
|
||||
|
||||
function gr_inqtext(x, y, s)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.inqtextext(x, y, s[2:end-1])
|
||||
@@ -182,6 +184,8 @@ function gr_inqtext(x, y, s)
|
||||
end
|
||||
end
|
||||
|
||||
gr_text(x, y, s::Symbol) = gr_text(x, y, string(s))
|
||||
|
||||
function gr_text(x, y, s)
|
||||
if length(s) >= 2 && s[1] == '$' && s[end] == '$'
|
||||
GR.mathtex(x, y, s[2:end-1])
|
||||
@@ -283,7 +287,8 @@ end
|
||||
# draw ONE symbol marker
|
||||
function gr_draw_marker(xi, yi, msize::Number, shape::Symbol)
|
||||
GR.setmarkertype(gr_markertype[shape])
|
||||
GR.setmarkersize(0.3msize)
|
||||
w, h = gr_plot_size
|
||||
GR.setmarkersize(0.3msize / ((w + h) * 0.001))
|
||||
GR.polymarker([xi], [yi])
|
||||
end
|
||||
|
||||
@@ -330,9 +335,10 @@ end
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
function gr_set_line(w, style, c) #, a)
|
||||
function gr_set_line(lw, style, c) #, a)
|
||||
GR.setlinetype(gr_linetype[style])
|
||||
GR.setlinewidth(w)
|
||||
w, h = gr_plot_size
|
||||
GR.setlinewidth(max(0, lw / ((w + h) * 0.001)))
|
||||
gr_set_linecolor(c) #, a)
|
||||
end
|
||||
|
||||
@@ -421,7 +427,7 @@ end
|
||||
function gr_colorbar(sp::Subplot)
|
||||
if sp[:colorbar] != :none
|
||||
gr_set_viewport_cmap(sp)
|
||||
GR.colormap()
|
||||
GR.colorbar()
|
||||
gr_set_viewport_plotarea()
|
||||
end
|
||||
end
|
||||
@@ -545,6 +551,10 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
if st == :heatmap
|
||||
outside_ticks = true
|
||||
x, y = heatmap_edges(series[:x]), heatmap_edges(series[:y])
|
||||
expand_extrema!(sp[:xaxis], x)
|
||||
expand_extrema!(sp[:yaxis], y)
|
||||
data_lims = gr_xy_axislims(sp)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -653,6 +663,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
flip = sp[:yaxis][:flip]
|
||||
mirror = sp[:xaxis][:mirror]
|
||||
gr_set_font(sp[:xaxis][:tickfont],
|
||||
halign = (:left, :hcenter, :right)[sign(sp[:xaxis][:rotation]) + 2],
|
||||
valign = (mirror ? :bottom : :top),
|
||||
color = sp[:xaxis][:foreground_color_axis],
|
||||
rotation = sp[:xaxis][:rotation])
|
||||
@@ -670,6 +681,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
mirror = sp[:yaxis][:mirror]
|
||||
gr_set_font(sp[:yaxis][:tickfont],
|
||||
halign = (mirror ? :left : :right),
|
||||
valign = (:top, :vcenter, :bottom)[sign(sp[:yaxis][:rotation]) + 2],
|
||||
color = sp[:yaxis][:foreground_color_axis],
|
||||
rotation = sp[:yaxis][:rotation])
|
||||
for (cv, dv) in zip(yticks...)
|
||||
@@ -757,10 +769,6 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# recompute data
|
||||
if typeof(z) <: Surface
|
||||
# if st == :heatmap
|
||||
# expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
|
||||
# expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
|
||||
# end
|
||||
z = vec(transpose_z(series, z.surf, false))
|
||||
elseif ispolar(sp)
|
||||
if frng != nothing
|
||||
@@ -805,12 +813,12 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
if typeof(series[:levels]) <: Array
|
||||
h = series[:levels]
|
||||
else
|
||||
h = linspace(zmin, zmax, series[:levels])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
if series[:fillrange] != nothing
|
||||
GR.surface(x, y, z, GR.OPTION_CELL_ARRAY)
|
||||
else
|
||||
@@ -848,6 +856,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
isfinite(clims[1]) && (zmin = clims[1])
|
||||
isfinite(clims[2]) && (zmax = clims[2])
|
||||
end
|
||||
GR.setspace(zmin, zmax, 0, 90)
|
||||
grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
|
||||
colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z]
|
||||
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
|
||||
@@ -942,7 +951,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
elseif st == :image
|
||||
z = transpose_z(series, series[:z].surf, true)
|
||||
h, w = size(z)
|
||||
w, h = size(z)
|
||||
if eltype(z) <: Colors.AbstractGray
|
||||
grey = round(UInt8, float(z) * 255)
|
||||
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
|
||||
@@ -1015,7 +1024,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
end
|
||||
|
||||
if series[:markershape] != :none
|
||||
gr_draw_markers(series, xpos-[0.06,0.02], [ypos,ypos], 10, nothing)
|
||||
gr_draw_markers(series, xpos - .035, ypos, 6, nothing)
|
||||
end
|
||||
|
||||
if typeof(series[:label]) <: Array
|
||||
@@ -1077,14 +1086,14 @@ else
|
||||
"use_default"
|
||||
end
|
||||
|
||||
const _gr_wstype = Ref(get(ENV, "GKS_WSTYPE", _gr_wstype_default))
|
||||
const _gr_wstype = Ref(get(ENV, "GKSwstype", _gr_wstype_default))
|
||||
gr_set_output(wstype::String) = (_gr_wstype[] = wstype)
|
||||
|
||||
for (mime, fmt) in _gr_mimeformats
|
||||
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
|
||||
GR.emergencyclosegks()
|
||||
filepath = tempname() * "." * $fmt
|
||||
ENV["GKS_WSTYPE"] = $fmt
|
||||
ENV["GKSwstype"] = $fmt
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
GR.emergencyclosegks()
|
||||
@@ -1097,17 +1106,17 @@ function _display(plt::Plot{GRBackend})
|
||||
if plt[:display_type] == :inline
|
||||
GR.emergencyclosegks()
|
||||
filepath = tempname() * ".pdf"
|
||||
ENV["GKS_WSTYPE"] = "pdf"
|
||||
ENV["GKSwstype"] = "pdf"
|
||||
ENV["GKS_FILEPATH"] = filepath
|
||||
gr_display(plt)
|
||||
GR.emergencyclosegks()
|
||||
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(readbytes, filepath)), "\a")
|
||||
content = string("\033]1337;File=inline=1;preserveAspectRatio=0:", base64encode(open(read, filepath)), "\a")
|
||||
println(content)
|
||||
rm(filepath)
|
||||
else
|
||||
ENV["GKS_DOUBLE_BUF"] = true
|
||||
if _gr_wstype[] != "use_default"
|
||||
ENV["GKS_WSTYPE"] = _gr_wstype[]
|
||||
ENV["GKSwstype"] = _gr_wstype[]
|
||||
end
|
||||
gr_display(plt)
|
||||
end
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
|
||||
# https://github.com/ma-laforge/InspectDR.jl
|
||||
|
||||
#=TODO:
|
||||
Tweak scale factor for width & other sizes
|
||||
|
||||
Not supported by InspectDR:
|
||||
:foreground_color_grid
|
||||
:foreground_color_border
|
||||
:polar,
|
||||
|
||||
Add in functionality to Plots.jl:
|
||||
:aspect_ratio,
|
||||
=#
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
#TODO: remove features
|
||||
const _inspectdr_attr = merge_with_base_supported([
|
||||
:annotations,
|
||||
: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,
|
||||
:linecolor, :linestyle, :linewidth, :linealpha,
|
||||
:markershape, :markercolor, :markersize, :markeralpha,
|
||||
:markerstrokewidth, :markerstrokecolor, :markerstrokealpha,
|
||||
:markerstrokestyle, #Causes warning not to have it... what is this?
|
||||
:fillcolor, :fillalpha, #:fillrange,
|
||||
# :bins, :bar_width, :bar_edges, :bar_position,
|
||||
:title, :title_location, :titlefont,
|
||||
:window_title,
|
||||
:guide, :lims, :scale, #:ticks, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, #:colorbar,
|
||||
# :marker_z,
|
||||
# :line_z,
|
||||
# :levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
:overwrite_figure,
|
||||
:polar,
|
||||
# :normalize, :weights,
|
||||
# :contours, :aspect_ratio,
|
||||
:match_dimensions,
|
||||
# :clims,
|
||||
# :inset_subplots,
|
||||
:dpi,
|
||||
# :colorbar_title,
|
||||
])
|
||||
const _inspectdr_style = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
const _inspectdr_seriestype = [
|
||||
:path, :scatter, :shape #, :steppre, :steppost
|
||||
]
|
||||
#see: _allMarkers, _shape_keys
|
||||
const _inspectdr_marker = Symbol[
|
||||
:none, :auto,
|
||||
:circle, :rect, :diamond,
|
||||
:cross, :xcross,
|
||||
:utriangle, :dtriangle, :rtriangle, :ltriangle,
|
||||
:pentagon, :hexagon, :heptagon, :octagon,
|
||||
:star4, :star5, :star6, :star7, :star8,
|
||||
:vline, :hline, :+, :x,
|
||||
]
|
||||
|
||||
const _inspectdr_scale = [:identity, :ln, :log2, :log10]
|
||||
|
||||
is_marker_supported(::InspectDRBackend, shape::Shape) = true
|
||||
|
||||
_inspectdr_to_pixels(bb::BoundingBox) =
|
||||
InspectDR.BoundingBox(to_pixels(left(bb)), to_pixels(right(bb)), to_pixels(top(bb)), to_pixels(bottom(bb)))
|
||||
|
||||
#Do we avoid Map to avoid possible pre-comile issues?
|
||||
function _inspectdr_mapglyph(s::Symbol)
|
||||
s == :rect && return :square
|
||||
return s
|
||||
end
|
||||
|
||||
function _inspectdr_mapglyph(s::Shape)
|
||||
x, y = coords(s)
|
||||
return InspectDR.GlyphPolyline(x, y)
|
||||
end
|
||||
|
||||
# py_marker(markers::AVec) = map(py_marker, markers)
|
||||
function _inspectdr_mapglyph(markers::AVec)
|
||||
warn("Vectors of markers are currently unsupported in InspectDR.")
|
||||
_inspectdr_mapglyph(markers[1])
|
||||
end
|
||||
|
||||
_inspectdr_mapglyphsize(v::Real) = v
|
||||
function _inspectdr_mapglyphsize(v::Vector)
|
||||
warn("Vectors of marker sizes are currently unsupported in InspectDR.")
|
||||
_inspectdr_mapglyphsize(v[1])
|
||||
end
|
||||
|
||||
_inspectdr_mapcolor(v::Colorant) = v
|
||||
function _inspectdr_mapcolor(g::PlotUtils.ColorGradient)
|
||||
warn("Color gradients are currently unsupported in InspectDR.")
|
||||
#Pick middle color:
|
||||
_inspectdr_mapcolor(g.colors[div(1+end,2)])
|
||||
end
|
||||
function _inspectdr_mapcolor(v::AVec)
|
||||
warn("Vectors of colors are currently unsupported in InspectDR.")
|
||||
#Pick middle color:
|
||||
_inspectdr_mapcolor(v[div(1+end,2)])
|
||||
end
|
||||
|
||||
#Hack: suggested point size does not seem adequate relative to plot size, for some reason.
|
||||
_inspectdr_mapptsize(v) = 1.5*v
|
||||
|
||||
function _inspectdr_add_annotations(plot, x, y, val)
|
||||
#What kind of annotation is this?
|
||||
end
|
||||
|
||||
#plot::InspectDR.Plot2D
|
||||
function _inspectdr_add_annotations(plot, x, y, val::PlotText)
|
||||
vmap = Dict{Symbol, Symbol}(:top=>:t, :bottom=>:b) #:vcenter
|
||||
hmap = Dict{Symbol, Symbol}(:left=>:l, :right=>:r) #:hcenter
|
||||
align = Symbol(get(vmap, val.font.valign, :c), get(hmap, val.font.halign, :c))
|
||||
fnt = InspectDR.Font(val.font.family, val.font.pointsize,
|
||||
color =_inspectdr_mapcolor(val.font.color)
|
||||
)
|
||||
ann = InspectDR.atext(val.str, x=x, y=y,
|
||||
font=fnt, angle=val.font.rotation, align=align
|
||||
)
|
||||
InspectDR.add(plot, ann)
|
||||
return
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _inspectdr_getscale(s::Symbol, yaxis::Bool)
|
||||
#TODO: Support :asinh, :sqrt
|
||||
kwargs = yaxis? (:tgtmajor=>8, :tgtminor=>2): () #More grid lines on y-axis
|
||||
if :log2 == s
|
||||
return InspectDR.AxisScale(:log2; kwargs...)
|
||||
elseif :log10 == s
|
||||
return InspectDR.AxisScale(:log10; kwargs...)
|
||||
elseif :ln == s
|
||||
return InspectDR.AxisScale(:ln; kwargs...)
|
||||
else #identity
|
||||
return InspectDR.AxisScale(:lin; kwargs...)
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function add_backend_string(::InspectDRBackend)
|
||||
"""
|
||||
if !Plots.is_installed("InspectDR")
|
||||
Pkg.add("InspectDR")
|
||||
end
|
||||
"""
|
||||
end
|
||||
|
||||
function _initialize_backend(::InspectDRBackend; kw...)
|
||||
@eval begin
|
||||
import InspectDR
|
||||
export InspectDR
|
||||
|
||||
#Glyph used when plotting "Shape"s:
|
||||
const INSPECTDR_GLYPH_SHAPE = InspectDR.GlyphPolyline(
|
||||
2*InspectDR.GLYPH_SQUARE.x, InspectDR.GLYPH_SQUARE.y
|
||||
)
|
||||
|
||||
type InspecDRPlotRef
|
||||
mplot::Union{Void, InspectDR.Multiplot}
|
||||
gui::Union{Void, InspectDR.GtkPlot}
|
||||
end
|
||||
|
||||
_inspectdr_getmplot(::Any) = nothing
|
||||
_inspectdr_getmplot(r::InspecDRPlotRef) = r.mplot
|
||||
|
||||
_inspectdr_getgui(::Any) = nothing
|
||||
_inspectdr_getgui(gplot::InspectDR.GtkPlot) = (gplot.destroyed? nothing: gplot)
|
||||
_inspectdr_getgui(r::InspecDRPlotRef) = _inspectdr_getgui(r.gui)
|
||||
end
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Create the window/figure for this backend.
|
||||
function _create_backend_figure(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
gplot = _inspectdr_getgui(plt.o)
|
||||
|
||||
#:overwrite_figure: want to reuse current figure
|
||||
if plt[:overwrite_figure] && mplot != nothing
|
||||
mplot.subplots = [] #Reset
|
||||
if gplot != nothing #Ensure still references current plot
|
||||
gplot.src = mplot
|
||||
end
|
||||
else #want new one:
|
||||
mplot = InspectDR.Multiplot()
|
||||
gplot = nothing #Will be created later
|
||||
end
|
||||
|
||||
#break link with old subplots
|
||||
for sp in plt.subplots
|
||||
sp.o = nothing
|
||||
end
|
||||
|
||||
return InspecDRPlotRef(mplot, gplot)
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# # this is called early in the pipeline, use it to make the plot current or something
|
||||
# function _prepare_plot_object(plt::Plot{InspectDRBackend})
|
||||
# end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Set up the subplot within the backend object.
|
||||
function _initialize_subplot(plt::Plot{InspectDRBackend}, sp::Subplot{InspectDRBackend})
|
||||
plot = sp.o
|
||||
#Don't do anything without a "subplot" object: Will process later.
|
||||
if nothing == plot; return; end
|
||||
plot.data = []
|
||||
plot.userannot = [] #Clear old markers/text annotation/polyline "annotation"
|
||||
return plot
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Add one series to the underlying backend object.
|
||||
# Called once per series
|
||||
# NOTE: Seems to be called when user calls plot()... even if backend
|
||||
# plot, sp.o has not yet been constructed...
|
||||
function _series_added(plt::Plot{InspectDRBackend}, series::Series)
|
||||
st = series[:seriestype]
|
||||
sp = series[:subplot]
|
||||
plot = sp.o
|
||||
|
||||
#Don't do anything without a "subplot" object: Will process later.
|
||||
if nothing == plot; return; end
|
||||
|
||||
_vectorize(v) = isa(v, Vector)? v: collect(v) #InspectDR only supports vectors
|
||||
x = _vectorize(series[:x]); y = _vectorize(series[:y])
|
||||
|
||||
#No support for polar grid... but can still perform polar transformation:
|
||||
if ispolar(sp)
|
||||
Θ = x; r = y
|
||||
x = r.*cos(Θ); y = r.*sin(Θ)
|
||||
end
|
||||
|
||||
# doesn't handle mismatched x/y - wrap data (pyplot behaviour):
|
||||
nx = length(x); ny = length(y)
|
||||
if nx < ny
|
||||
series[:x] = Float64[x[mod1(i,nx)] for i=1:ny]
|
||||
elseif ny > nx
|
||||
series[:y] = Float64[y[mod1(i,ny)] for i=1:nx]
|
||||
end
|
||||
|
||||
#= TODO: Eventually support
|
||||
series[:fillcolor] #I think this is fill under line
|
||||
zorder = series[:series_plotindex]
|
||||
|
||||
For st in :shape:
|
||||
zorder = series[:series_plotindex],
|
||||
=#
|
||||
|
||||
if st in (:shape,)
|
||||
nmax = 0
|
||||
for (i,rng) in enumerate(iter_segments(x, y))
|
||||
nmax = i
|
||||
if length(rng) > 1
|
||||
linewidth = series[:linewidth]
|
||||
linecolor = _inspectdr_mapcolor(cycle(series[:linecolor], i))
|
||||
fillcolor = _inspectdr_mapcolor(cycle(series[:fillcolor], i))
|
||||
line = InspectDR.line(
|
||||
style=:solid, width=linewidth, color=linecolor
|
||||
)
|
||||
apline = InspectDR.PolylineAnnotation(
|
||||
x[rng], y[rng], line=line, fillcolor=fillcolor
|
||||
)
|
||||
InspectDR.add(plot, apline)
|
||||
end
|
||||
end
|
||||
|
||||
i = (nmax >= 2? div(nmax, 2): nmax) #Must pick one set of colors for legend
|
||||
if i > 1 #Add dummy waveform for legend entry:
|
||||
linewidth = series[:linewidth]
|
||||
linecolor = _inspectdr_mapcolor(cycle(series[:linecolor], i))
|
||||
fillcolor = _inspectdr_mapcolor(cycle(series[:fillcolor], i))
|
||||
wfrm = InspectDR.add(plot, Float64[], Float64[], id=series[:label])
|
||||
wfrm.line = InspectDR.line(
|
||||
style=:none, width=linewidth, #linewidth affects glyph
|
||||
)
|
||||
wfrm.glyph = InspectDR.glyph(
|
||||
shape = INSPECTDR_GLYPH_SHAPE, size = 8,
|
||||
color = linecolor, fillcolor = fillcolor
|
||||
)
|
||||
end
|
||||
elseif st in (:path, :scatter) #, :steppre, :steppost)
|
||||
#NOTE: In Plots.jl, :scatter plots have 0-linewidths (I think).
|
||||
linewidth = series[:linewidth]
|
||||
#More efficient & allows some support for markerstrokewidth:
|
||||
_style = (0==linewidth? :none: series[:linestyle])
|
||||
wfrm = InspectDR.add(plot, x, y, id=series[:label])
|
||||
wfrm.line = InspectDR.line(
|
||||
style = _style,
|
||||
width = series[:linewidth],
|
||||
color = series[:linecolor],
|
||||
)
|
||||
#InspectDR does not control markerstrokewidth independently.
|
||||
if :none == _style
|
||||
#Use this property only if no line is displayed:
|
||||
wfrm.line.width = series[:markerstrokewidth]
|
||||
end
|
||||
wfrm.glyph = InspectDR.glyph(
|
||||
shape = _inspectdr_mapglyph(series[:markershape]),
|
||||
size = _inspectdr_mapglyphsize(series[:markersize]),
|
||||
color = _inspectdr_mapcolor(series[:markerstrokecolor]),
|
||||
fillcolor = _inspectdr_mapcolor(series[:markercolor]),
|
||||
)
|
||||
end
|
||||
|
||||
# this is all we need to add the series_annotations text
|
||||
anns = series[:series_annotations]
|
||||
for (xi,yi,str,fnt) in EachAnn(anns, x, y)
|
||||
_inspectdr_add_annotations(plot, xi, yi, PlotText(str, fnt))
|
||||
end
|
||||
return
|
||||
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{InspectDRBackend}, series::Series)
|
||||
#Nothing to do
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function _inspectdr_setupsubplot(sp::Subplot{InspectDRBackend})
|
||||
const gridon = InspectDR.GridRect(vmajor=true, hmajor=true)
|
||||
const gridoff = InspectDR.GridRect()
|
||||
const plot = sp.o
|
||||
const strip = plot.strips[1] #Only 1 strip supported with Plots.jl
|
||||
|
||||
#No independent control of grid???
|
||||
strip.grid = sp[:grid]? gridon: gridoff
|
||||
|
||||
xaxis = sp[:xaxis]; yaxis = sp[:yaxis]
|
||||
plot.xscale = _inspectdr_getscale(xaxis[:scale], false)
|
||||
strip.yscale = _inspectdr_getscale(yaxis[:scale], true)
|
||||
xmin, xmax = axis_limits(xaxis)
|
||||
ymin, ymax = axis_limits(yaxis)
|
||||
if ispolar(sp)
|
||||
#Plots.jl appears to give (xmin,xmax) ≜ (Θmin,Θmax) & (ymin,ymax) ≜ (rmin,rmax)
|
||||
rmax = max(abs(ymin), abs(ymax))
|
||||
xmin, xmax = -rmax, rmax
|
||||
ymin, ymax = -rmax, rmax
|
||||
end
|
||||
plot.xext = InspectDR.PExtents1D() #reset
|
||||
strip.yext = InspectDR.PExtents1D() #reset
|
||||
plot.xext_full = InspectDR.PExtents1D(xmin, xmax)
|
||||
strip.yext_full = InspectDR.PExtents1D(ymin, ymax)
|
||||
a = plot.annotation
|
||||
a.title = sp[:title]
|
||||
a.xlabel = xaxis[:guide]; a.ylabels = [yaxis[:guide]]
|
||||
|
||||
l = plot.layout
|
||||
l.frame.fillcolor = _inspectdr_mapcolor(sp[:background_color_subplot])
|
||||
l.framedata.fillcolor = _inspectdr_mapcolor(sp[:background_color_inside])
|
||||
l.framedata.line.color = _inspectdr_mapcolor(xaxis[:foreground_color_axis])
|
||||
l.fnttitle = InspectDR.Font(sp[:titlefont].family,
|
||||
_inspectdr_mapptsize(sp[:titlefont].pointsize),
|
||||
color = _inspectdr_mapcolor(sp[:foreground_color_title])
|
||||
)
|
||||
#Cannot independently control fonts of axes with InspectDR:
|
||||
l.fntaxlabel = InspectDR.Font(xaxis[:guidefont].family,
|
||||
_inspectdr_mapptsize(xaxis[:guidefont].pointsize),
|
||||
color = _inspectdr_mapcolor(xaxis[:foreground_color_guide])
|
||||
)
|
||||
l.fntticklabel = InspectDR.Font(xaxis[:tickfont].family,
|
||||
_inspectdr_mapptsize(xaxis[:tickfont].pointsize),
|
||||
color = _inspectdr_mapcolor(xaxis[:foreground_color_text])
|
||||
)
|
||||
leg = l.legend
|
||||
leg.enabled = (sp[:legend] != :none)
|
||||
#leg.width = 150 #TODO: compute???
|
||||
leg.font = InspectDR.Font(sp[:legendfont].family,
|
||||
_inspectdr_mapptsize(sp[:legendfont].pointsize),
|
||||
color = _inspectdr_mapcolor(sp[:foreground_color_legend])
|
||||
)
|
||||
leg.frame.fillcolor = _inspectdr_mapcolor(sp[:background_color_legend])
|
||||
end
|
||||
|
||||
# called just before updating layout bounding boxes... in case you need to prep
|
||||
# for the calcs
|
||||
function _before_layout_calcs(plt::Plot{InspectDRBackend})
|
||||
const mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
|
||||
mplot.title = plt[:plot_title]
|
||||
if "" == mplot.title
|
||||
#Don't use window_title... probably not what you want.
|
||||
#mplot.title = plt[:window_title]
|
||||
end
|
||||
mplot.frame.fillcolor = _inspectdr_mapcolor(plt[:background_color_outside])
|
||||
|
||||
resize!(mplot.subplots, length(plt.subplots))
|
||||
nsubplots = length(plt.subplots)
|
||||
for (i, sp) in enumerate(plt.subplots)
|
||||
if !isassigned(mplot.subplots, i)
|
||||
mplot.subplots[i] = InspectDR.Plot2D()
|
||||
end
|
||||
sp.o = mplot.subplots[i]
|
||||
plot = sp.o
|
||||
_initialize_subplot(plt, sp)
|
||||
_inspectdr_setupsubplot(sp)
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
_inspectdr_add_annotations(plot, ann...)
|
||||
end
|
||||
end
|
||||
|
||||
#Do not yet support absolute plot positionning.
|
||||
#Just try to make things look more-or less ok:
|
||||
if nsubplots <= 1
|
||||
mplot.ncolumns = 1
|
||||
elseif nsubplots <= 4
|
||||
mplot.ncolumns = 2
|
||||
elseif nsubplots <= 6
|
||||
mplot.ncolumns = 3
|
||||
elseif nsubplots <= 12
|
||||
mplot.ncolumns = 4
|
||||
else
|
||||
mplot.ncolumns = 5
|
||||
end
|
||||
|
||||
for series in plt.series_list
|
||||
_series_added(plt, series)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Set the (left, top, right, bottom) minimum padding around the plot area
|
||||
# to fit ticks, tick labels, guides, colorbars, etc.
|
||||
function _update_min_padding!(sp::Subplot{InspectDRBackend})
|
||||
plot = sp.o
|
||||
if !isa(plot, InspectDR.Plot2D); return sp.minpad; end
|
||||
#Computing plotbounds with 0-BoundingBox returns required padding:
|
||||
bb = InspectDR.plotbounds(plot.layout, InspectDR.BoundingBox(0,0,0,0))
|
||||
#NOTE: plotbounds always pads for titles, legends, etc. even if not in use.
|
||||
#TODO: possibly zero-out items not in use??
|
||||
|
||||
# add in the user-specified margin to InspectDR padding:
|
||||
leftpad = abs(bb.xmin)*px + sp[:left_margin]
|
||||
toppad = abs(bb.ymin)*px + sp[:top_margin]
|
||||
rightpad = abs(bb.xmax)*px + sp[:right_margin]
|
||||
bottompad = abs(bb.ymax)*px + sp[:bottom_margin]
|
||||
sp.minpad = (leftpad, toppad, rightpad, bottompad)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Override this to update plot items (title, xlabel, etc), and add annotations (d[:annotations])
|
||||
function _update_plot_object(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
|
||||
for (i, sp) in enumerate(plt.subplots)
|
||||
graphbb = _inspectdr_to_pixels(plotarea(sp))
|
||||
plot = mplot.subplots[i]
|
||||
plot.plotbb = InspectDR.plotbounds(plot.layout, graphbb)
|
||||
end
|
||||
|
||||
gplot = _inspectdr_getgui(plt.o)
|
||||
if nothing == gplot; return; end
|
||||
|
||||
gplot.src = mplot #Ensure still references current plot
|
||||
InspectDR.refresh(gplot)
|
||||
return
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
const _inspectdr_mimeformats_dpi = Dict(
|
||||
"image/png" => "png"
|
||||
)
|
||||
const _inspectdr_mimeformats_nodpi = Dict(
|
||||
"image/svg+xml" => "svg",
|
||||
"application/eps" => "eps",
|
||||
"image/eps" => "eps",
|
||||
# "application/postscript" => "ps", #TODO: support once Cairo supports PSSurface
|
||||
"application/pdf" => "pdf"
|
||||
)
|
||||
_inspectdr_show(io::IO, mime::MIME, ::Void, w, h) =
|
||||
throw(ErrorException("Cannot show(::IO, ...) plot - not yet generated"))
|
||||
function _inspectdr_show(io::IO, mime::MIME, mplot, w, h)
|
||||
InspectDR._show(io, mime, mplot, Float64(w), Float64(h))
|
||||
end
|
||||
|
||||
for (mime, fmt) in _inspectdr_mimeformats_dpi
|
||||
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend})
|
||||
dpi = plt[:dpi]#TODO: support
|
||||
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
|
||||
end
|
||||
end
|
||||
for (mime, fmt) in _inspectdr_mimeformats_nodpi
|
||||
@eval function _show(io::IO, mime::MIME{Symbol($mime)}, plt::Plot{InspectDRBackend})
|
||||
_inspectdr_show(io, mime, _inspectdr_getmplot(plt.o), plt[:size]...)
|
||||
end
|
||||
end
|
||||
_show(io::IO, mime::MIME"text/plain", plt::Plot{InspectDRBackend}) = nothing #Don't show
|
||||
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
# Display/show the plot (open a GUI window, or browser page, for example).
|
||||
function _display(plt::Plot{InspectDRBackend})
|
||||
mplot = _inspectdr_getmplot(plt.o)
|
||||
if nothing == mplot; return; end
|
||||
gplot = _inspectdr_getgui(plt.o)
|
||||
|
||||
if nothing == gplot
|
||||
gplot = display(InspectDR.GtkDisplay(), mplot)
|
||||
else
|
||||
#redundant... Plots.jl will call _update_plot_object:
|
||||
#InspectDR.refresh(gplot)
|
||||
end
|
||||
plt.o = InspecDRPlotRef(mplot, gplot)
|
||||
return gplot
|
||||
end
|
||||
+108
-13
@@ -3,7 +3,7 @@
|
||||
# significant contributions by: @pkofod
|
||||
|
||||
const _pgfplots_attr = merge_with_base_supported([
|
||||
# :annotations,
|
||||
:annotations,
|
||||
# :background_color_legend,
|
||||
:background_color_inside,
|
||||
# :background_color_outside,
|
||||
@@ -22,17 +22,17 @@ const _pgfplots_attr = merge_with_base_supported([
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend,
|
||||
# :colorbar,
|
||||
# :marker_z, :levels,
|
||||
:colorbar,
|
||||
:marker_z, #:levels,
|
||||
# :ribbon, :quiver, :arrow,
|
||||
# :orientation,
|
||||
# :overwrite_figure,
|
||||
# :polar,
|
||||
:polar,
|
||||
# :normalize, :weights, :contours,
|
||||
:aspect_ratio,
|
||||
# :match_dimensions,
|
||||
])
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour]
|
||||
const _pgfplots_seriestype = [:path, :path3d, :scatter, :steppre, :stepmid, :steppost, :histogram2d, :ysticks, :xsticks, :contour, :shape]
|
||||
const _pgfplots_style = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
const _pgfplots_marker = [:none, :auto, :circle, :rect, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :pentagon] #vcat(_allMarkers, Shape)
|
||||
const _pgfplots_scale = [:identity, :ln, :log2, :log10]
|
||||
@@ -98,14 +98,35 @@ const _pgf_series_extrastyle = KW(
|
||||
:xsticks => "xcomb",
|
||||
)
|
||||
|
||||
# PGFPlots uses the anchors to define orientations for example to align left
|
||||
# one needs to use the right edge as anchor
|
||||
const _pgf_annotation_halign = KW(
|
||||
:center => "",
|
||||
:left => "right",
|
||||
:right => "left"
|
||||
)
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
# takes in color,alpha, and returns color and alpha appropriate for pgf style
|
||||
function pgf_color(c)
|
||||
function pgf_color(c::Colorant)
|
||||
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", red(c), green(c), blue(c))
|
||||
cstr, alpha(c)
|
||||
end
|
||||
|
||||
function pgf_color(grad::ColorGradient)
|
||||
# Can't handle ColorGradient here, fallback to defaults.
|
||||
cstr = @sprintf("{rgb,1:red,%.8f;green,%.8f;blue,%.8f}", 0.0, 0.60560316,0.97868012)
|
||||
cstr, 1
|
||||
end
|
||||
|
||||
# Generates a colormap for pgfplots based on a ColorGradient
|
||||
function pgf_colormap(grad::ColorGradient)
|
||||
join(map(grad.colors) do c
|
||||
@sprintf("rgb=(%.8f,%.8f,%.8f)", red(c), green(c),blue(c))
|
||||
end,", ")
|
||||
end
|
||||
|
||||
function pgf_fillstyle(d::KW)
|
||||
cstr,a = pgf_color(d[:fillcolor])
|
||||
"fill = $cstr, fill opacity=$a"
|
||||
@@ -136,6 +157,19 @@ function pgf_marker(d::KW)
|
||||
}"""
|
||||
end
|
||||
|
||||
function pgf_add_annotation!(o,x,y,val)
|
||||
# Construct the style string.
|
||||
# Currently supports color and orientation
|
||||
cstr,a = pgf_color(val.font.color)
|
||||
push!(o, PGFPlots.Plots.Node(val.str, # Annotation Text
|
||||
x, y,
|
||||
style="""
|
||||
$(get(_pgf_annotation_halign,val.font.halign,"")),
|
||||
color=$cstr, draw opacity=$(convert(Float16,a)),
|
||||
rotate=$(val.font.rotation)
|
||||
"""))
|
||||
end
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
function pgf_series(sp::Subplot, series::Series)
|
||||
@@ -143,11 +177,10 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
st = d[:seriestype]
|
||||
style = []
|
||||
kw = KW()
|
||||
|
||||
push!(style, pgf_linestyle(d))
|
||||
push!(style, pgf_marker(d))
|
||||
|
||||
if d[:fillrange] != nothing
|
||||
if d[:fillrange] != nothing || st in (:shape,)
|
||||
push!(style, pgf_fillstyle(d))
|
||||
end
|
||||
|
||||
@@ -163,6 +196,10 @@ function pgf_series(sp::Subplot, series::Series)
|
||||
d[:z].surf, d[:x], d[:y]
|
||||
elseif is3d(st)
|
||||
d[:x], d[:y], d[:z]
|
||||
elseif d[:marker_z] != nothing
|
||||
# If a marker_z is used pass it as third coordinate to a 2D plot.
|
||||
# See "Scatter Plots" in PGFPlots documentation
|
||||
d[:x], d[:y], d[:marker_z]
|
||||
else
|
||||
d[:x], d[:y]
|
||||
end
|
||||
@@ -211,6 +248,9 @@ function pgf_axis(sp::Subplot, letter)
|
||||
# axis guide
|
||||
kw[Symbol(letter,:label)] = axis[:guide]
|
||||
|
||||
# Add ticklabel rotations
|
||||
push!(style, "$(letter)ticklabel style={rotate = $(axis[:rotation])}")
|
||||
|
||||
# flip/reverse?
|
||||
axis[:flip] && push!(style, "$letter dir=reverse")
|
||||
|
||||
@@ -249,8 +289,12 @@ end
|
||||
|
||||
function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
plt.o = PGFPlots.Axis[]
|
||||
# Obtain the total height of the plot by extracting the maximal bottom
|
||||
# coordinate from the bounding box.
|
||||
total_height = bottom(bbox(plt.layout))
|
||||
|
||||
for sp in plt.subplots
|
||||
# first build the PGFPlots.Axis object
|
||||
# first build the PGFPlots.Axis object
|
||||
style = ["unbounded coords=jump"]
|
||||
kw = KW()
|
||||
|
||||
@@ -265,10 +309,12 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
|
||||
# bounding box values are in mm
|
||||
# note: bb origin is top-left, pgf is bottom-left
|
||||
# A round on 2 decimal places should be enough precision for 300 dpi
|
||||
# plots.
|
||||
bb = bbox(sp)
|
||||
push!(style, """
|
||||
xshift = $(left(bb).value)mm,
|
||||
yshift = $((height(bb) - (bottom(bb))).value)mm,
|
||||
yshift = $(round((total_height - (bottom(bb))).value,2))mm,
|
||||
axis background/.style={fill=$(pgf_color(sp[:background_color_inside])[1])}
|
||||
""")
|
||||
kw[:width] = "$(width(bb).value)mm"
|
||||
@@ -288,19 +334,62 @@ function _update_plot_object(plt::Plot{PGFPlotsBackend})
|
||||
kw[:legendPos] = _pgfplots_legend_pos[legpos]
|
||||
end
|
||||
|
||||
o = PGFPlots.Axis(; style = style, kw...)
|
||||
axisf = PGFPlots.Axis
|
||||
if sp[:projection] == :polar
|
||||
axisf = PGFPlots.PolarAxis
|
||||
end
|
||||
|
||||
# Search series for any gradient. In case one series uses a gradient set
|
||||
# the colorbar and colomap.
|
||||
# The reasoning behind doing this on the axis level is that pgfplots
|
||||
# colorbar seems to only works on axis level and needs the proper colormap for
|
||||
# correctly displaying it.
|
||||
# It's also possible to assign the colormap to the series itself but
|
||||
# then the colormap needs to be added twice, once for the axis and once for the
|
||||
# series.
|
||||
# As it is likely that all series within the same axis use the same
|
||||
# colormap this should not cause any problem.
|
||||
for series in series_list(sp)
|
||||
for col in (:markercolor, :fillcolor)
|
||||
if typeof(series.d[col]) == ColorGradient
|
||||
push!(style,"colormap={plots}{$(pgf_colormap(series.d[col]))}")
|
||||
|
||||
if sp[:colorbar] == :none
|
||||
kw[:colorbar] = "false"
|
||||
else
|
||||
kw[:colorbar] = "true"
|
||||
end
|
||||
# goto is needed to break out of col and series for
|
||||
@goto colorbar_end
|
||||
end
|
||||
end
|
||||
end
|
||||
@label colorbar_end
|
||||
|
||||
o = axisf(; style = style, kw...)
|
||||
|
||||
# add the series object to the PGFPlots.Axis
|
||||
for series in series_list(sp)
|
||||
push!(o, pgf_series(sp, series))
|
||||
|
||||
# add series annotations
|
||||
anns = series[:series_annotations]
|
||||
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
|
||||
pgf_add_annotation!(o, xi, yi, PlotText(str, fnt))
|
||||
end
|
||||
end
|
||||
|
||||
# add the annotations
|
||||
for ann in sp[:annotations]
|
||||
pgf_add_annotation!(o,ann...)
|
||||
end
|
||||
|
||||
|
||||
# add the PGFPlots.Axis to the list
|
||||
push!(plt.o, o)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
function _show(io::IO, mime::MIME"image/svg+xml", plt::Plot{PGFPlotsBackend})
|
||||
show(io, mime, plt.o)
|
||||
end
|
||||
@@ -314,12 +403,18 @@ function _show(io::IO, mime::MIME"application/pdf", plt::Plot{PGFPlotsBackend})
|
||||
PGFPlots.save(PGFPlots.PDF(fn), pgfplt)
|
||||
|
||||
# read it into io
|
||||
write(io, readall(open(fn)))
|
||||
write(io, readstring(open(fn)))
|
||||
|
||||
# cleanup
|
||||
PGFPlots.cleanup(plt.o)
|
||||
end
|
||||
|
||||
function _show(io::IO, mime::MIME"application/x-tex", plt::Plot{PGFPlotsBackend})
|
||||
fn = tempname()*".tex"
|
||||
PGFPlots.save(fn, backend_object(plt), include_preamble=false)
|
||||
write(io, readstring(open(fn)))
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PGFPlotsBackend})
|
||||
# prepare the object
|
||||
pgfplt = PGFPlots.plot(plt.o)
|
||||
|
||||
+25
-5
@@ -19,8 +19,8 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:window_title,
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z, :levels,
|
||||
:grid, :legend, :colorbar, :colorbar_title,
|
||||
:marker_z, :fill_z, :levels,
|
||||
:ribbon, :quiver,
|
||||
:orientation,
|
||||
# :overwrite_figure,
|
||||
@@ -31,6 +31,7 @@ const _plotly_attr = merge_with_base_supported([
|
||||
:hover,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:clims,
|
||||
])
|
||||
|
||||
const _plotly_seriestype = [
|
||||
@@ -268,7 +269,7 @@ function plotly_layout(plt::Plot)
|
||||
w, h = plt[:size]
|
||||
d_out[:width], d_out[:height] = w, h
|
||||
d_out[:paper_bgcolor] = rgba_string(plt[:background_color_outside])
|
||||
d_out[:margin] = KW(:l=>0, :b=>0, :r=>0, :t=>20)
|
||||
d_out[:margin] = KW(:l=>0, :b=>20, :r=>0, :t=>20)
|
||||
|
||||
d_out[:annotations] = KW[]
|
||||
|
||||
@@ -371,6 +372,7 @@ end
|
||||
plotly_colorscale(c, α) = plotly_colorscale(cgrad(alpha=α), α)
|
||||
# plotly_colorscale(c, alpha = nothing) = plotly_colorscale(cgrad(), alpha)
|
||||
|
||||
|
||||
const _plotly_markers = KW(
|
||||
:rect => "square",
|
||||
:xcross => "x",
|
||||
@@ -407,6 +409,13 @@ plotly_surface_data(series::Series, a::AbstractVector) = a
|
||||
plotly_surface_data(series::Series, a::AbstractMatrix) = transpose_z(series, a, false)
|
||||
plotly_surface_data(series::Series, a::Surface) = plotly_surface_data(series, a.surf)
|
||||
|
||||
#ensures that a gradient is called if a single color is supplied where a gradient is needed (e.g. if a series recipe defines marker_z)
|
||||
as_gradient(grad::ColorGradient, α) = grad
|
||||
as_gradient(grad, α) = cgrad(alpha = α)
|
||||
|
||||
# allows passing a ColorGradient where a single color is expected - the other backends allow this
|
||||
PlotUtils.rgba_string(cg::ColorGradient) = rgba_string(cg[1])
|
||||
|
||||
# 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)
|
||||
st = series[:seriestype]
|
||||
@@ -437,6 +446,13 @@ function plotly_series(plt::Plot, series::Series)
|
||||
end
|
||||
end
|
||||
|
||||
d_out[:colorbar] = KW(:title => sp[:colorbar_title])
|
||||
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
d_out[:zmin], d_out[:zmax] = clims
|
||||
end
|
||||
|
||||
# set the "type"
|
||||
if st in (:path, :scatter, :scattergl)
|
||||
d_out[:type] = st==:scattergl ? "scattergl" : "scatter"
|
||||
@@ -488,6 +504,9 @@ function plotly_series(plt::Plot, series::Series)
|
||||
d_out[:contours] = KW(:x => wirelines, :y => wirelines, :z => wirelines)
|
||||
else
|
||||
d_out[:colorscale] = plotly_colorscale(series[:fillcolor], series[:fillalpha])
|
||||
if series[:fill_z] != nothing
|
||||
d_out[:surfacecolor] = plotly_surface_data(series, series[:fill_z])
|
||||
end
|
||||
end
|
||||
|
||||
elseif st == :pie
|
||||
@@ -529,9 +548,10 @@ function plotly_series(plt::Plot, series::Series)
|
||||
rgba_string(series[:markercolor])
|
||||
else
|
||||
# grad = ColorGradient(series[:markercolor], alpha=series[:markeralpha])
|
||||
grad = series[:markercolor]
|
||||
grad = as_gradient(series[:markercolor], series[:markeralpha])
|
||||
zmin, zmax = extrema(series[:marker_z])
|
||||
[rgba_string(grad[(zi - zmin) / (zmax - zmin)]) for zi in series[:marker_z]]
|
||||
zrange = zmax == zmin ? 1 : zmax - zmin # if all marker_z values are the same, plot all markers same color (avoids division by zero in next line)
|
||||
[rgba_string(grad[(zi - zmin) / zrange]) for zi in series[:marker_z]]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -86,8 +86,8 @@ end
|
||||
# ----------------------------------------------------------------
|
||||
|
||||
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
|
||||
if Plots.isijulia()
|
||||
print(io, PlotlyJS.html_body(plt.o))
|
||||
if isijulia() && !_use_remote[]
|
||||
write(io, PlotlyJS.html_body(PlotlyJS.JupyterPlot(plt.o)))
|
||||
else
|
||||
show(io, MIME("text/html"), plt.o)
|
||||
end
|
||||
@@ -102,8 +102,18 @@ _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hac
|
||||
_show(io::IO, ::MIME"application/pdf", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "pdf")
|
||||
_show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hack(io, plt, "eps")
|
||||
|
||||
function write_temp_html(plt::Plot{PlotlyJSBackend})
|
||||
filename = string(tempname(), ".html")
|
||||
savefig(plt, filename)
|
||||
filename
|
||||
end
|
||||
|
||||
function _display(plt::Plot{PlotlyJSBackend})
|
||||
display(plt.o)
|
||||
if get(ENV, "PLOTS_USE_ATOM_PLOTPANE", true) in (true, 1, "1", "true", "yes")
|
||||
display(plt.o)
|
||||
else
|
||||
standalone_html_window(plt)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
+19
-16
@@ -18,8 +18,7 @@ const _pyplot_attr = merge_with_base_supported([
|
||||
:guide, :lims, :ticks, :scale, :flip, :rotation,
|
||||
:tickfont, :guidefont, :legendfont,
|
||||
:grid, :legend, :colorbar,
|
||||
:marker_z,
|
||||
:line_z,
|
||||
:marker_z, :line_z, :fill_z,
|
||||
:levels,
|
||||
:ribbon, :quiver, :arrow,
|
||||
:orientation,
|
||||
@@ -56,6 +55,10 @@ function add_backend_string(::PyPlotBackend)
|
||||
withenv("PYTHON" => "") do
|
||||
Pkg.build("PyPlot")
|
||||
end
|
||||
import Conda
|
||||
Conda.add("qt=4.8.5")
|
||||
|
||||
# now restart julia!
|
||||
"""
|
||||
end
|
||||
|
||||
@@ -124,8 +127,8 @@ end
|
||||
py_colormap(c) = py_colormap(cgrad())
|
||||
|
||||
|
||||
function py_shading(c, z, α=nothing)
|
||||
cmap = py_colormap(c, α)
|
||||
function py_shading(c, z)
|
||||
cmap = py_colormap(c)
|
||||
ls = pycolors.pymember("LightSource")(270,45)
|
||||
ls[:shade](z, cmap, vert_exag=0.1, blend_mode="soft")
|
||||
end
|
||||
@@ -668,21 +671,21 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
x = repmat(x', length(y), 1)
|
||||
y = repmat(y, 1, length(series[:x]))
|
||||
end
|
||||
# z = z'
|
||||
z = transpose_z(series, z)
|
||||
if st == :surface
|
||||
if series[:marker_z] != nothing
|
||||
extrakw[:facecolors] = py_shading(series[:fillcolor], series[:marker_z], series[:fillalpha])
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
if series[:fill_z] != nothing
|
||||
# the surface colors are different than z-value
|
||||
extrakw[:facecolors] = py_shading(series[:fillcolor], transpose_z(series, series[:fill_z].surf))
|
||||
extrakw[:shade] = false
|
||||
clims = sp[:clims]
|
||||
if is_2tuple(clims)
|
||||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
||||
end
|
||||
else
|
||||
extrakw[:cmap] = py_fillcolormap(series)
|
||||
needs_colorbar = true
|
||||
end
|
||||
needs_colorbar = true
|
||||
end
|
||||
handle = ax[st == :surface ? :plot_surface : :plot_wireframe](x, y, z;
|
||||
label = series[:label],
|
||||
@@ -1051,7 +1054,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
end
|
||||
if sp[:grid]
|
||||
fgcolor = py_color(sp[:foreground_color_grid])
|
||||
pyaxis[:grid](true, color = fgcolor)
|
||||
pyaxis[:grid](true, color = fgcolor, linestyle = ":")
|
||||
ax[:set_axisbelow](true)
|
||||
end
|
||||
py_set_axis_colors(ax, axis)
|
||||
@@ -1067,7 +1070,7 @@ function _before_layout_calcs(plt::Plot{PyPlotBackend})
|
||||
py_add_legend(plt, sp, ax)
|
||||
|
||||
# this sets the bg color inside the grid
|
||||
ax[:set_axis_bgcolor](py_color(sp[:background_color_inside]))
|
||||
ax[:set_facecolor](py_color(sp[:background_color_inside]))
|
||||
end
|
||||
py_drawfig(fig)
|
||||
end
|
||||
@@ -1099,7 +1102,7 @@ function _update_min_padding!(sp::Subplot{PyPlotBackend})
|
||||
# optionally add the width of colorbar labels and colorbar to rightpad
|
||||
if haskey(sp.attr, :cbar_ax)
|
||||
bb = py_bbox(sp.attr[:cbar_handle][:ax][:get_yticklabels]())
|
||||
sp.attr[:cbar_width] = _cbar_width + width(bb) + 1mm + (sp[:colorbar_title] == "" ? 0px : 30px)
|
||||
sp.attr[:cbar_width] = _cbar_width + width(bb) + 2.3mm + (sp[:colorbar_title] == "" ? 0px : 30px)
|
||||
rightpad = rightpad + sp.attr[:cbar_width]
|
||||
end
|
||||
|
||||
|
||||
+2
-2
@@ -23,11 +23,11 @@ function open_browser_window(filename::AbstractString)
|
||||
@static if is_apple()
|
||||
return run(`open $(filename)`)
|
||||
end
|
||||
@static if is_linux()
|
||||
@static if is_linux() || is_bsd() # is_bsd() addition is as yet untested, but based on suggestion in https://github.com/JuliaPlots/Plots.jl/issues/681
|
||||
return run(`xdg-open $(filename)`)
|
||||
end
|
||||
@static if is_windows()
|
||||
return run(`$(ENV["COMSPEC"]) /c start $(filename)`)
|
||||
return run(`$(ENV["COMSPEC"]) /c start "" "$(filename)"`)
|
||||
end
|
||||
warn("Unknown OS... cannot open browser window.")
|
||||
end
|
||||
|
||||
+4
-4
@@ -174,7 +174,7 @@ function center(shape::Shape)
|
||||
Cx / 6A, Cy / 6A
|
||||
end
|
||||
|
||||
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
function scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
sx, sy = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(sx)
|
||||
@@ -184,7 +184,7 @@ function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
shape
|
||||
end
|
||||
|
||||
function Base.scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
function scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||
shapecopy = deepcopy(shape)
|
||||
scale!(shapecopy, x, y, c)
|
||||
end
|
||||
@@ -248,7 +248,7 @@ end
|
||||
function font(args...)
|
||||
|
||||
# defaults
|
||||
family = "Helvetica"
|
||||
family = "sans-serif"
|
||||
pointsize = 14
|
||||
halign = :hcenter
|
||||
valign = :vcenter
|
||||
@@ -521,7 +521,7 @@ Base.Array(surf::Surface) = surf.surf
|
||||
for f in (:length, :size)
|
||||
@eval Base.$f(surf::Surface, args...) = $f(surf.surf, args...)
|
||||
end
|
||||
Base.copy(surf::Surface) = Surface{typeof(surf.surf)}(copy(surf.surf))
|
||||
Base.copy(surf::Surface) = Surface(copy(surf.surf))
|
||||
Base.eltype{T}(surf::Surface{T}) = eltype(T)
|
||||
|
||||
function expand_extrema!(a::Axis, surf::Surface)
|
||||
|
||||
@@ -172,6 +172,24 @@ PlotExample("",
|
||||
end)]
|
||||
),
|
||||
|
||||
PlotExample("Animation with subplots",
|
||||
"The `layout` macro can be used to create an animation with subplots.",
|
||||
[:(begin
|
||||
l = @layout([[a; b] c])
|
||||
p = plot(plot([sin,cos],1,leg=false),
|
||||
scatter([atan,cos],1,leg=false),
|
||||
plot(log,1,xlims=(1,10π),ylims=(0,5),leg=false),layout=l)
|
||||
|
||||
anim = Animation()
|
||||
for x = linspace(1,10π,100)
|
||||
plot(push!(p,x,Float64[sin(x),cos(x),atan(x),cos(x),log(x)]))
|
||||
frame(anim)
|
||||
end
|
||||
end)]
|
||||
),
|
||||
|
||||
|
||||
|
||||
PlotExample("Open/High/Low/Close",
|
||||
"Create an OHLC chart. Pass in a list of (open,high,low,close) tuples as your `y` argument. This uses recipes to first convert the tuples to OHLC objects, and subsequently create a :path series with the appropriate line segments.",
|
||||
[:(begin
|
||||
|
||||
+6
-7
@@ -154,6 +154,7 @@ const _mimeformats = Dict(
|
||||
"application/postscript" => "ps",
|
||||
"image/svg+xml" => "svg",
|
||||
"text/plain" => "txt",
|
||||
"application/x-tex" => "tex",
|
||||
)
|
||||
|
||||
const _best_html_output_type = KW(
|
||||
@@ -301,6 +302,10 @@ function setup_atom()
|
||||
Media.render(pane, Atom.div(".fill", Atom.HTML(stringmime(MIME("text/html"), plt))))
|
||||
plt[:size] = sz
|
||||
end
|
||||
# special handling for PlotlyJS
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
end
|
||||
else
|
||||
#
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot)
|
||||
@@ -313,14 +318,8 @@ function setup_atom()
|
||||
# special handling for plotly... use PlotsDisplay
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyBackend})
|
||||
display(Plots.PlotsDisplay(), plt)
|
||||
s = "PlotPane turned off. The plotly and plotlyjs backends cannot render in the PlotPane due to javascript issues."
|
||||
s = "PlotPane turned off. The plotly backend cannot render in the PlotPane due to javascript issues. Plotlyjs is similar to plotly and is compatible with the plot pane."
|
||||
Media.render(pane, Atom.div(Atom.HTML(s)))
|
||||
end
|
||||
|
||||
# special handling for PlotlyJS to pass through to that render method
|
||||
function Media.render(pane::Atom.PlotPane, plt::Plot{PlotlyJSBackend})
|
||||
Plots.prepare_output(plt)
|
||||
Media.render(pane, plt.o)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
+8
-1
@@ -277,6 +277,13 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
attr[Symbol(letter,k)] = v
|
||||
end
|
||||
end
|
||||
for k in (:scale,), letter in (:x,:y,:z)
|
||||
# Series recipes may need access to this information
|
||||
lk = Symbol(letter,k)
|
||||
if haskey(attr, lk)
|
||||
kw[lk] = attr[lk]
|
||||
end
|
||||
end
|
||||
end
|
||||
sp_attrs[sp] = attr
|
||||
end
|
||||
@@ -357,7 +364,7 @@ function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
expand_extrema!(sp[:xaxis], (0,w))
|
||||
expand_extrema!(sp[:yaxis], (0,h))
|
||||
sp[:yaxis].d[:flip] = true
|
||||
elseif !(st in (:pie, :histogram, :histogram2d))
|
||||
elseif !(st in (:pie, :histogram, :bins2d, :histogram2d))
|
||||
expand_extrema!(sp, d)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
|
||||
const _attribute_defaults = Dict(:Series => _series_defaults,
|
||||
:Subplot => _subplot_defaults,
|
||||
:Plot => _plot_defaults,
|
||||
:Axis => _axis_defaults)
|
||||
|
||||
attrtypes() = join(keys(_attribute_defaults), ", ")
|
||||
attributes(attrtype::Symbol) = sort(collect(keys(_attribute_defaults[attrtype])))
|
||||
|
||||
function lookup_aliases(attrtype, attribute)
|
||||
attribute = Symbol(attribute)
|
||||
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
|
||||
in(attribute, keys(_attribute_defaults[attrtype])) && return attribute
|
||||
error("There is no attribute named $attribute in $attrtype")
|
||||
end
|
||||
|
||||
function plotattr()
|
||||
println("Specify an attribute type to get a list of supported attributes. Options are $(attrtypes())")
|
||||
end
|
||||
|
||||
function plotattr(attrtype::Symbol)
|
||||
in(attrtype, keys(_attribute_defaults)) || error("Viable options are $(attrtypes())")
|
||||
println("Defined $attrtype attributes are:\n$(join(attributes(attrtype), ", "))")
|
||||
end
|
||||
|
||||
function plotattr(attribute::AbstractString)
|
||||
attribute = Symbol(attribute)
|
||||
attribute = in(attribute, keys(_keyAliases)) ? _keyAliases[attribute] : attribute
|
||||
for (k, v) in _attribute_defaults
|
||||
if in(attribute, keys(v))
|
||||
return plotattr(k, "$attribute")
|
||||
end
|
||||
end
|
||||
error("There is no attribute named $attribute")
|
||||
end
|
||||
|
||||
function plotattr(attrtype::Symbol, attribute::AbstractString)
|
||||
in(attrtype, keys(_attribute_defaults)) || ArgumentError("`attrtype` must match one of $(attrtypes())")
|
||||
|
||||
attribute = Symbol(lookup_aliases(attrtype, attribute))
|
||||
|
||||
desc = get(_arg_desc, attribute, "")
|
||||
first_period_idx = findfirst(desc, '.')
|
||||
typedesc = desc[1:first_period_idx-1]
|
||||
desc = strip(desc[first_period_idx+1:end])
|
||||
als = keys(filter((_,v)->v==attribute, _keyAliases)) |> collect |> sort
|
||||
als = join(map(string,als), ", ")
|
||||
def = _attribute_defaults[attrtype][attribute]
|
||||
|
||||
|
||||
# Looks up the different elements and plots them
|
||||
println("$attribute ", typedesc == "" ? "" : "{$typedesc}", "\n",
|
||||
als == "" ? "" : "$als\n",
|
||||
"\n$desc\n",
|
||||
"$(attrtype) attribute, ", def == "" ? "" : " default: $def")
|
||||
end
|
||||
+392
-87
@@ -323,10 +323,11 @@ end
|
||||
|
||||
# create a bar plot as a filled step function
|
||||
@recipe function f(::Type{Val{:bar}}, x, y, z)
|
||||
nx, ny = length(x), length(y)
|
||||
procx, procy, xscale, yscale, baseline, wautolims = _preprocess_barlike(d, x, y)
|
||||
nx, ny = length(procx), length(procy)
|
||||
axis = d[:subplot][isvertical(d) ? :xaxis : :yaxis]
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=x]
|
||||
x = if nx == ny
|
||||
cv = [discrete_value!(axis, xi)[1] for xi=procx]
|
||||
procx = if nx == ny
|
||||
cv
|
||||
elseif nx == ny + 1
|
||||
0.5diff(cv) + cv[1:end-1]
|
||||
@@ -337,9 +338,9 @@ end
|
||||
# compute half-width of bars
|
||||
bw = d[:bar_width]
|
||||
hw = if bw == nothing
|
||||
0.5mean(diff(x))
|
||||
0.5mean(diff(procx))
|
||||
else
|
||||
Float64[0.5cycle(bw,i) for i=1:length(x)]
|
||||
Float64[0.5cycle(bw,i) for i=1:length(procx)]
|
||||
end
|
||||
|
||||
# make fillto a vector... default fills to 0
|
||||
@@ -347,16 +348,21 @@ end
|
||||
if fillto == nothing
|
||||
fillto = 0
|
||||
end
|
||||
if (yscale in _logScales) && !all(_is_positive, fillto)
|
||||
fillto = map(x -> _is_positive(x) ? typeof(baseline)(x) : baseline, fillto)
|
||||
end
|
||||
|
||||
# create the bar shapes by adding x/y segments
|
||||
xseg, yseg = Segments(), Segments()
|
||||
for i=1:ny
|
||||
center = x[i]
|
||||
hwi = cycle(hw,i)
|
||||
yi = y[i]
|
||||
fi = cycle(fillto,i)
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
yi = procy[i]
|
||||
if !isnan(yi)
|
||||
center = procx[i]
|
||||
hwi = cycle(hw,i)
|
||||
fi = cycle(fillto,i)
|
||||
push!(xseg, center-hwi, center-hwi, center+hwi, center+hwi, center-hwi)
|
||||
push!(yseg, yi, fi, fi, yi, yi)
|
||||
end
|
||||
end
|
||||
|
||||
# widen limits out a bit
|
||||
@@ -374,110 +380,355 @@ end
|
||||
x := xseg.pts
|
||||
y := yseg.pts
|
||||
seriestype := :shape
|
||||
ylims --> wautolims
|
||||
()
|
||||
end
|
||||
@deps bar shape
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histograms
|
||||
|
||||
# edges from number of bins
|
||||
function calc_edges(v, bins::Integer)
|
||||
vmin, vmax = extrema(v)
|
||||
linspace(vmin, vmax, bins+1)
|
||||
_bin_centers(v::AVec) = (v[1:end-1] + v[2:end]) / 2
|
||||
|
||||
_is_positive(x) = (x > 0) && !(x ≈ 0)
|
||||
|
||||
_positive_else_nan{T}(::Type{T}, x::Real) = _is_positive(x) ? T(x) : T(NaN)
|
||||
|
||||
function _scale_adjusted_values{T<:AbstractFloat}(::Type{T}, V::AbstractVector, scale::Symbol)
|
||||
if scale in _logScales
|
||||
[_positive_else_nan(T, x) for x in V]
|
||||
else
|
||||
[T(x) for x in V]
|
||||
end
|
||||
end
|
||||
|
||||
# just pass through arrays
|
||||
calc_edges(v, bins::AVec) = bins
|
||||
|
||||
# find the bucket index of this value
|
||||
function bucket_index(vi, edges)
|
||||
for (i,e) in enumerate(edges)
|
||||
if vi <= e
|
||||
return max(1,i-1)
|
||||
function _binbarlike_baseline{T<:Real}(min_value::T, scale::Symbol)
|
||||
if (scale in _logScales)
|
||||
!isnan(min_value) ? min_value / T(_logScaleBases[scale]^log10(2)) : T(1E-3)
|
||||
else
|
||||
zero(T)
|
||||
end
|
||||
end
|
||||
|
||||
function _binbarlike_autolims{T<:Real}(min_value::T, max_value::T, scale::Symbol)
|
||||
lo = if (scale in _logScales)
|
||||
_binbarlike_baseline(min_value, scale)
|
||||
else
|
||||
min(min_value * T(1.1), zero(T))
|
||||
end::T
|
||||
|
||||
hi = if !isnan(max_value)
|
||||
if (scale in _logScales)
|
||||
max_value * T(_logScaleBases[scale]^log10(2))
|
||||
else
|
||||
max(max_value * T(1.1), zero(T))
|
||||
end
|
||||
end
|
||||
return length(edges)-1
|
||||
else
|
||||
one(T)
|
||||
end::T
|
||||
|
||||
(lo, hi)
|
||||
end
|
||||
|
||||
function my_hist(v, bins; normed = false, weights = nothing)
|
||||
edges = calc_edges(v, bins)
|
||||
counts = zeros(length(edges)-1)
|
||||
|
||||
# add a weighted count
|
||||
for (i,vi) in enumerate(v)
|
||||
idx = bucket_index(vi, edges)
|
||||
counts[idx] += (weights == nothing ? 1.0 : weights[i])
|
||||
function _preprocess_binbarlike_weights{T<:AbstractFloat}(::Type{T}, w, wscale::Symbol)
|
||||
w_adj = _scale_adjusted_values(T, w, wscale)
|
||||
w_min = minimum(w_adj)
|
||||
w_max = maximum(w_adj)
|
||||
baseline = _binbarlike_baseline(w_min, wscale)
|
||||
autolims = _binbarlike_autolims(w_min, w_max,wscale)
|
||||
w_adj, baseline, autolims
|
||||
end
|
||||
|
||||
function _preprocess_barlike(d, x, y)
|
||||
xscale = get(d, :xscale, :identity)
|
||||
yscale = get(d, :yscale, :identity)
|
||||
weights, baseline, wautolims = _preprocess_binbarlike_weights(float(eltype(y)), y, yscale)
|
||||
x, weights, xscale, yscale, baseline, wautolims
|
||||
end
|
||||
|
||||
function _preprocess_binlike(d, x, y)
|
||||
xscale = get(d, :xscale, :identity)
|
||||
yscale = get(d, :yscale, :identity)
|
||||
T = float(promote_type(eltype(x), eltype(y)))
|
||||
edge = T.(x)
|
||||
weights, baseline, wautolims = _preprocess_binbarlike_weights(T, y, yscale)
|
||||
edge, weights, xscale, yscale, baseline, wautolims
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:barbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline, wautolims = _preprocess_binlike(d, x, y)
|
||||
if (d[:bar_width] == nothing)
|
||||
bar_width := diff(edge)
|
||||
end
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
seriestype := :bar
|
||||
()
|
||||
end
|
||||
@deps barbins bar
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:scatterbins}}, x, y, z)
|
||||
edge, weights, xscale, yscale, baseline, wautolims = _preprocess_binlike(d, x, y)
|
||||
xerror := diff(edge)/2
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
seriestype := :scatter
|
||||
()
|
||||
end
|
||||
@deps scatterbins scatter
|
||||
|
||||
|
||||
function _stepbins_path(edge, weights, baseline::Real, xscale::Symbol, yscale::Symbol)
|
||||
log_scale_x = xscale in _logScales
|
||||
log_scale_y = yscale in _logScales
|
||||
|
||||
nbins = length(linearindices(weights))
|
||||
if length(linearindices(edge)) != nbins + 1
|
||||
error("Edge vector must be 1 longer than weight vector")
|
||||
end
|
||||
|
||||
# normalize by bar area?
|
||||
norm_denom = normed ? sum(diff(edges) .* counts) : 1.0
|
||||
if norm_denom == 0
|
||||
norm_denom = 1.0
|
||||
x = eltype(edge)[]
|
||||
y = eltype(weights)[]
|
||||
|
||||
it_e, it_w = start(edge), start(weights)
|
||||
a, it_e = next(edge, it_e)
|
||||
last_w = eltype(weights)(NaN)
|
||||
i = 1
|
||||
while (!done(edge, it_e) && !done(edge, it_e))
|
||||
b, it_e = next(edge, it_e)
|
||||
w, it_w = next(weights, it_w)
|
||||
|
||||
if (log_scale_x && a ≈ 0)
|
||||
a = b/_logScaleBases[xscale]^3
|
||||
end
|
||||
|
||||
if isnan(w)
|
||||
if !isnan(last_w)
|
||||
push!(x, a)
|
||||
push!(y, baseline)
|
||||
end
|
||||
else
|
||||
if isnan(last_w)
|
||||
push!(x, a)
|
||||
push!(y, baseline)
|
||||
end
|
||||
push!(x, a)
|
||||
push!(y, w)
|
||||
push!(x, b)
|
||||
push!(y, w)
|
||||
end
|
||||
|
||||
a = b
|
||||
last_w = w
|
||||
end
|
||||
if (last_w != baseline)
|
||||
push!(x, a)
|
||||
push!(y, baseline)
|
||||
end
|
||||
|
||||
edges, counts ./ norm_denom
|
||||
(x, y)
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:stepbins}}, x, y, z)
|
||||
axis = d[:subplot][Plots.isvertical(d) ? :xaxis : :yaxis]
|
||||
|
||||
edge, weights, xscale, yscale, baseline, wautolims = _preprocess_binlike(d, x, y)
|
||||
|
||||
xpts, ypts = _stepbins_path(edge, weights, baseline, xscale, yscale)
|
||||
if !isvertical(d)
|
||||
xpts, ypts = ypts, xpts
|
||||
end
|
||||
|
||||
# create a secondary series for the markers
|
||||
if d[:markershape] != :none
|
||||
@series begin
|
||||
seriestype := :scatter
|
||||
x := _bin_centers(edge)
|
||||
y := weights
|
||||
fillrange := nothing
|
||||
label := ""
|
||||
primary := false
|
||||
()
|
||||
end
|
||||
markershape := :none
|
||||
xerror := :none
|
||||
yerror := :none
|
||||
end
|
||||
|
||||
x := xpts
|
||||
y := ypts
|
||||
seriestype := :path
|
||||
|
||||
ylims --> wautolims
|
||||
()
|
||||
end
|
||||
Plots.@deps stepbins path
|
||||
|
||||
|
||||
function _auto_binning_nbins{N}(vs::NTuple{N,AbstractVector}, dim::Integer; mode::Symbol = :auto)
|
||||
_cl(x) = max(ceil(Int, x), 1)
|
||||
_iqr(v) = quantile(v, 0.75) - quantile(v, 0.25)
|
||||
_span(v) = maximum(v) - minimum(v)
|
||||
|
||||
n_samples = length(linearindices(first(vs)))
|
||||
# Estimator for number of samples in one row/column of bins along each axis:
|
||||
n = max(1, n_samples^(1/N))
|
||||
|
||||
v = vs[dim]
|
||||
|
||||
if mode == :auto
|
||||
30
|
||||
elseif mode == :sqrt # Square-root choice
|
||||
_cl(sqrt(n))
|
||||
elseif mode == :sturges # Sturges' formula
|
||||
_cl(log2(n)) + 1
|
||||
elseif mode == :rice # Rice Rule
|
||||
_cl(2 * n^(1/3))
|
||||
elseif mode == :scott # Scott's normal reference rule
|
||||
_cl(_span(v) / (3.5 * std(v) / n^(1/3)))
|
||||
elseif mode == :fd # Freedman–Diaconis rule
|
||||
_cl(_span(v) / (2 * _iqr(v) / n^(1/3)))
|
||||
else
|
||||
error("Unknown auto-binning mode $mode")
|
||||
end::Int
|
||||
end
|
||||
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Integer) = StatsBase.histrange(vs[dim], binning, :left)
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::Symbol) = _hist_edge(vs, dim, _auto_binning_nbins(vs, dim, mode = binning))
|
||||
_hist_edge{N}(vs::NTuple{N,AbstractVector}, dim::Integer, binning::AbstractVector) = binning
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::NTuple{N}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning[dim]), (1:N...))
|
||||
|
||||
_hist_edges{N}(vs::NTuple{N,AbstractVector}, binning::Union{Integer, Symbol, AbstractVector}) =
|
||||
map(dim -> _hist_edge(vs, dim, binning), (1:N...))
|
||||
|
||||
_hist_norm_mode(mode::Symbol) = mode
|
||||
_hist_norm_mode(mode::Bool) = mode ? :pdf : :none
|
||||
|
||||
function _make_hist{N}(vs::NTuple{N,AbstractVector}, binning; normed = false, weights = nothing)
|
||||
info("binning = $binning")
|
||||
edges = _hist_edges(vs, binning)
|
||||
h = float( weights == nothing ?
|
||||
StatsBase.fit(StatsBase.Histogram, vs, edges, closed = :left) :
|
||||
StatsBase.fit(StatsBase.Histogram, vs, weights, edges, closed = :left)
|
||||
)
|
||||
normalize!(h, mode = _hist_norm_mode(normed))
|
||||
end
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram}}, x, y, z)
|
||||
edges, counts = my_hist(y, d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights])
|
||||
x := edges
|
||||
y := counts
|
||||
seriestype := :bar
|
||||
seriestype := :barhist
|
||||
()
|
||||
end
|
||||
@deps histogram bar
|
||||
@deps histogram barhist
|
||||
|
||||
@recipe function f(::Type{Val{:barhist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :barbins
|
||||
()
|
||||
end
|
||||
@deps barhist barbins
|
||||
|
||||
@recipe function f(::Type{Val{:stephist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :stepbins
|
||||
()
|
||||
end
|
||||
@deps stephist stepbins
|
||||
|
||||
@recipe function f(::Type{Val{:scatterhist}}, x, y, z)
|
||||
h = _make_hist((y,), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.weights
|
||||
seriestype := :scatterbins
|
||||
()
|
||||
end
|
||||
@deps scatterhist scatterbins
|
||||
|
||||
|
||||
@recipe function f{T, E}(h::StatsBase.Histogram{T, 1, E})
|
||||
seriestype --> :barbins
|
||||
|
||||
st_map = Dict(
|
||||
:bar => :barbins, :scatter => :scatterbins, :step => :stepbins,
|
||||
:steppost => :stepbins # :step can be mapped to :steppost in pre-processing
|
||||
)
|
||||
seriestype := get(st_map, d[:seriestype], d[:seriestype])
|
||||
|
||||
if d[:seriestype] == :scatterbins
|
||||
# Workaround, error bars currently not set correctly by scatterbins
|
||||
edge, weights, xscale, yscale, baseline, wautolims = _preprocess_binlike(d, h.edges[1], h.weights)
|
||||
xerror --> diff(h.edges[1])/2
|
||||
seriestype := :scatter
|
||||
(Plots._bin_centers(edge), weights)
|
||||
else
|
||||
(h.edges[1], h.weights)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@recipe function f{H <: StatsBase.Histogram}(hv::AbstractVector{H})
|
||||
for h in hv
|
||||
@series begin
|
||||
h
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Histogram 2D
|
||||
|
||||
# if tuple, map out bins, otherwise use the same for both
|
||||
calc_edges_2d(x, y, bins) = calc_edges(x, bins), calc_edges(y, bins)
|
||||
calc_edges_2d{X,Y}(x, y, bins::Tuple{X,Y}) = calc_edges(x, bins[1]), calc_edges(y, bins[2])
|
||||
@recipe function f(::Type{Val{:bins2d}}, x, y, z)
|
||||
edge_x, edge_y, weights = x, y, z.surf
|
||||
|
||||
# the 2D version
|
||||
function my_hist_2d(x, y, bins; normed = false, weights = nothing)
|
||||
xedges, yedges = calc_edges_2d(x, y, bins)
|
||||
counts = zeros(length(yedges)-1, length(xedges)-1)
|
||||
|
||||
# add a weighted count
|
||||
for i=1:length(x)
|
||||
r = bucket_index(y[i], yedges)
|
||||
c = bucket_index(x[i], xedges)
|
||||
counts[r,c] += (weights == nothing ? 1.0 : weights[i])
|
||||
float_weights = float(weights)
|
||||
if is(float_weights, weights)
|
||||
float_weights = deepcopy(float_weights)
|
||||
end
|
||||
|
||||
# normalize to cubic area of the imaginary surface towers
|
||||
norm_denom = normed ? sum((diff(yedges) * diff(xedges)') .* counts) : 1.0
|
||||
if norm_denom == 0
|
||||
norm_denom = 1.0
|
||||
end
|
||||
|
||||
xedges, yedges, counts ./ norm_denom
|
||||
end
|
||||
|
||||
centers(v::AVec) = 0.5 * (v[1:end-1] + v[2:end])
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
|
||||
normed = d[:normalize],
|
||||
weights = d[:weights])
|
||||
for (i,c) in enumerate(counts)
|
||||
for (i, c) in enumerate(float_weights)
|
||||
if c == 0
|
||||
counts[i] = NaN
|
||||
float_weights[i] = NaN
|
||||
end
|
||||
end
|
||||
x := centers(xedges)
|
||||
y := centers(yedges)
|
||||
z := Surface(counts)
|
||||
linewidth := 0
|
||||
|
||||
x := Plots._bin_centers(edge_x)
|
||||
y := Plots._bin_centers(edge_y)
|
||||
z := Surface(float_weights)
|
||||
|
||||
match_dimensions := true
|
||||
seriestype := :heatmap
|
||||
()
|
||||
end
|
||||
@deps histogram2d heatmap
|
||||
Plots.@deps bins2d heatmap
|
||||
|
||||
|
||||
@recipe function f(::Type{Val{:histogram2d}}, x, y, z)
|
||||
h = _make_hist((x, y), d[:bins], normed = d[:normalize], weights = d[:weights])
|
||||
x := h.edges[1]
|
||||
y := h.edges[2]
|
||||
z := Surface(h.weights)
|
||||
seriestype := :bins2d
|
||||
()
|
||||
end
|
||||
@deps histogram2d bins2d
|
||||
|
||||
|
||||
@recipe function f{T, E}(h::StatsBase.Histogram{T, 2, E})
|
||||
seriestype --> :bins2d
|
||||
(h.edges[1], h.edges[2], Surface(h.weights))
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -789,16 +1040,70 @@ abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
# -------------------------------------------------
|
||||
# Dates
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int,dt), dt -> string(convert(Date,dt)))
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int,dt), dt -> string(convert(DateTime,dt)))
|
||||
dateformatter(dt) = string(convert(Date, dt))
|
||||
datetimeformatter(dt) = string(convert(DateTime, dt))
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int, dt), dateformatter)
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int, dt), datetimeformatter)
|
||||
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
@userplot ComplexPlot
|
||||
@recipe function f(cp::ComplexPlot)
|
||||
xguide --> "Real Part"
|
||||
yguide --> "Imaginary Part"
|
||||
seriestype --> :scatter
|
||||
real(cp.args[1]), imag(cp.args[1])
|
||||
@recipe function f{T<:Number}(A::Array{Complex{T}})
|
||||
xguide --> "Re(x)"
|
||||
yguide --> "Im(x)"
|
||||
real.(A), imag.(A)
|
||||
end
|
||||
|
||||
# Splits a complex matrix to its real and complex parts
|
||||
# Reals defaults solid, imaginary defaults dashed
|
||||
# Label defaults are changed to match the real-imaginary reference / indexing
|
||||
@recipe function f{T<:Real,T2}(x::AbstractArray{T},y::Array{Complex{T2}})
|
||||
ylabel --> "Re(y)"
|
||||
zlabel --> "Im(y)"
|
||||
x,real.(y),imag.(y)
|
||||
end
|
||||
|
||||
|
||||
# --------------------------------------------------
|
||||
# Color Gradients
|
||||
|
||||
@userplot ShowLibrary
|
||||
@recipe function f(cl::ShowLibrary)
|
||||
if !(length(cl.args) == 1 && isa(cl.args[1], Symbol))
|
||||
error("showlibrary takes the name of a color library as a Symbol")
|
||||
end
|
||||
|
||||
library = PlotUtils.color_libraries[cl.args[1]]
|
||||
z = sqrt.((1:15)*(1:20)')
|
||||
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
legend := false
|
||||
|
||||
layout --> length(library.lib)
|
||||
|
||||
i = 0
|
||||
for grad in sort(collect(keys(library.lib)))
|
||||
@series begin
|
||||
seriescolor := cgrad(grad, cl.args[1])
|
||||
title := string(grad)
|
||||
subplot := i += 1
|
||||
z
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@userplot ShowGradient
|
||||
@recipe function f(grad::ShowGradient)
|
||||
if !(length(grad.args) == 1 && isa(grad.args[1], Symbol))
|
||||
error("showgradient takes the name of a color gradient as a Symbol")
|
||||
end
|
||||
z = sqrt.((1:15)*(1:20)')
|
||||
seriestype := :heatmap
|
||||
ticks := nothing
|
||||
legend := false
|
||||
seriescolor := grad.args[1]
|
||||
title := string(grad.args[1])
|
||||
z
|
||||
end
|
||||
|
||||
+39
-12
@@ -6,7 +6,7 @@
|
||||
# This should cut down on boilerplate code and allow more focused dispatch on type
|
||||
# note: returns meta information... mainly for use with automatic labeling from DataFrames for now
|
||||
|
||||
typealias FuncOrFuncs Union{Function, AVec{Function}}
|
||||
typealias FuncOrFuncs{F} Union{F, Vector{F}, Matrix{F}}
|
||||
|
||||
all3D(d::KW) = trueOrAllTrue(st -> st in (:contour, :contourf, :heatmap, :surface, :wireframe, :contour3d, :image), get(d, :seriestype, :none))
|
||||
|
||||
@@ -96,8 +96,8 @@ nobigs(v) = v
|
||||
end
|
||||
|
||||
# not allowed
|
||||
compute_xyz(x::Void, y::FuncOrFuncs, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz(x::Void, y::Void, z::FuncOrFuncs) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz{F<:Function}(x::Void, y::FuncOrFuncs{F}, z) = error("If you want to plot the function `$y`, you need to define the x values!")
|
||||
compute_xyz{F<:Function}(x::Void, y::Void, z::FuncOrFuncs{F}) = error("If you want to plot the function `$z`, you need to define x and y values!")
|
||||
compute_xyz(x::Void, y::Void, z::Void) = error("x/y/z are all nothing!")
|
||||
|
||||
# --------------------------------------------------------------------
|
||||
@@ -257,12 +257,23 @@ end
|
||||
# # 1 argument
|
||||
# # --------------------------------------------------------------------
|
||||
|
||||
# helper function to ensure relevant attributes are wrapped by Surface
|
||||
function wrap_surfaces(d::KW)
|
||||
if haskey(d, :fill_z)
|
||||
v = d[:fill_z]
|
||||
if !isa(v, Surface)
|
||||
d[:fill_z] = Surface(v)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@recipe f(n::Integer) = is3d(get(d,:seriestype,:path)) ? (SliceIt, n, n, n) : (SliceIt, n, n, nothing)
|
||||
|
||||
# return a surface if this is a 3d plot, otherwise let it be sliced up
|
||||
@recipe function f{T<:Union{Integer,AbstractFloat}}(mat::AMat{T})
|
||||
if all3D(d)
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(d)
|
||||
SliceIt, 1:m, 1:n, Surface(mat)
|
||||
else
|
||||
SliceIt, nothing, mat, nothing
|
||||
@@ -274,6 +285,7 @@ end
|
||||
if all3D(d)
|
||||
mat = fmt.data
|
||||
n,m = size(mat)
|
||||
wrap_surfaces(d)
|
||||
SliceIt, 1:m, 1:n, Formatted(Surface(mat), fmt.formatter)
|
||||
else
|
||||
SliceIt, nothing, fmt, nothing
|
||||
@@ -341,9 +353,13 @@ end
|
||||
|
||||
# function without range... use the current range of the x-axis
|
||||
|
||||
@recipe function f(f::FuncOrFuncs)
|
||||
@recipe function f{F<:Function}(f::FuncOrFuncs{F})
|
||||
plt = d[:plot_object]
|
||||
xmin,xmax = axis_limits(plt[1][:xaxis])
|
||||
xmin, xmax = try
|
||||
axis_limits(plt[1][:xaxis])
|
||||
catch
|
||||
-5, 5
|
||||
end
|
||||
f, xmin, xmax
|
||||
end
|
||||
|
||||
@@ -356,8 +372,9 @@ end
|
||||
# # if functions come first, just swap the order (not to be confused with parametric functions...
|
||||
# # as there would be more than one function passed in)
|
||||
|
||||
@recipe function f(f::FuncOrFuncs, x)
|
||||
@assert !(typeof(x) <: FuncOrFuncs) # otherwise we'd hit infinite recursion here
|
||||
@recipe function f{F<:Function}(f::FuncOrFuncs{F}, x)
|
||||
F2 = typeof(x)
|
||||
@assert !(F2 <: Function || (F2 <: AbstractArray && F2.parameters[1] <: Function)) # otherwise we'd hit infinite recursion here
|
||||
x, f
|
||||
end
|
||||
|
||||
@@ -386,6 +403,7 @@ end
|
||||
# seriestype := :path3d
|
||||
# end
|
||||
# end
|
||||
wrap_surfaces(d)
|
||||
SliceIt, x, y, z
|
||||
end
|
||||
|
||||
@@ -395,6 +413,7 @@ end
|
||||
@recipe function f(x::AVec, y::AVec, zf::Function)
|
||||
# x = X <: Number ? sort(x) : x
|
||||
# y = Y <: Number ? sort(y) : y
|
||||
wrap_surfaces(d)
|
||||
SliceIt, x, y, Surface(zf, x, y) # TODO: replace with SurfaceFunction when supported
|
||||
end
|
||||
|
||||
@@ -405,6 +424,7 @@ end
|
||||
if !like_surface(get(d, :seriestype, :none))
|
||||
d[:seriestype] = :contour
|
||||
end
|
||||
wrap_surfaces(d)
|
||||
SliceIt, x, y, Surface(z)
|
||||
end
|
||||
|
||||
@@ -416,16 +436,23 @@ end
|
||||
|
||||
#
|
||||
# # special handling... xmin/xmax with parametric function(s)
|
||||
@recipe f(f::FuncOrFuncs, xmin::Number, xmax::Number) = linspace(xmin, xmax, 100), f
|
||||
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||
@recipe f(fx::FuncOrFuncs, fy::FuncOrFuncs, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n)
|
||||
@recipe function f(f::Function, xmin::Number, xmax::Number)
|
||||
xs = adapted_grid(f, (xmin, xmax))
|
||||
xs, f
|
||||
end
|
||||
@recipe function f{F<:Function}(fs::AbstractArray{F}, xmin::Number, xmax::Number)
|
||||
xs = Any[adapted_grid(f, (xmin, xmax)) for f in fs]
|
||||
xs, fs
|
||||
end
|
||||
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, u::AVec) = mapFuncOrFuncs(fx, u), mapFuncOrFuncs(fy, u)
|
||||
@recipe f{F<:Function,G<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, umin::Number, umax::Number, n = 200) = fx, fy, linspace(umin, umax, n)
|
||||
|
||||
#
|
||||
# # special handling... 3D parametric function(s)
|
||||
@recipe function f(fx::FuncOrFuncs, fy::FuncOrFuncs, fz::FuncOrFuncs, u::AVec)
|
||||
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, 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, numPoints = 200)
|
||||
@recipe function f{F<:Function,G<:Function,H<:Function}(fx::FuncOrFuncs{F}, fy::FuncOrFuncs{G}, fz::FuncOrFuncs{H}, umin::Number, umax::Number, numPoints = 200)
|
||||
fx, fy, fz, linspace(umin, umax, numPoints)
|
||||
end
|
||||
|
||||
|
||||
+1
-1
@@ -39,7 +39,7 @@ series_list(sp::Subplot) = sp.series_list # filter(series -> series.d[:subplot]
|
||||
function should_add_to_legend(series::Series)
|
||||
series.d[:primary] && series.d[:label] != "" &&
|
||||
!(series.d[:seriestype] in (
|
||||
:hexbin,:histogram2d,:hline,:vline,
|
||||
:hexbin,:bins2d,:histogram2d,:hline,:vline,
|
||||
:contour,:contourf,:contour3d,:surface,:wireframe,
|
||||
:heatmap, :pie, :image
|
||||
))
|
||||
|
||||
+4
-2
@@ -2,7 +2,8 @@
|
||||
function theme(s::Symbol; kw...)
|
||||
# reset?
|
||||
if s == :none || s == :default
|
||||
PlotUtils._default_gradient[] = :inferno
|
||||
PlotUtils.clibrary(:Plots)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = :inferno)
|
||||
default(;
|
||||
bg = :white,
|
||||
bglegend = :match,
|
||||
@@ -23,7 +24,8 @@ function theme(s::Symbol; kw...)
|
||||
# update the default gradient and other defaults
|
||||
thm = PlotThemes._themes[s]
|
||||
if thm.gradient != nothing
|
||||
PlotUtils._default_gradient[] = PlotThemes.gradient_name(s)
|
||||
PlotUtils.clibrary(:misc)
|
||||
PlotUtils.default_cgrad(default = :sequential, sequential = PlotThemes.gradient_name(s))
|
||||
end
|
||||
default(;
|
||||
bg = thm.bg_secondary,
|
||||
|
||||
+1
-1
@@ -265,7 +265,7 @@ maketuple(x::Real) = (x,x)
|
||||
maketuple{T,S}(x::Tuple{T,S}) = x
|
||||
|
||||
mapFuncOrFuncs(f::Function, u::AVec) = map(f, u)
|
||||
mapFuncOrFuncs(fs::AVec{Function}, u::AVec) = [map(f, u) for f in fs]
|
||||
mapFuncOrFuncs{F<:Function}(fs::AVec{F}, u::AVec) = [map(f, u) for f in fs]
|
||||
|
||||
unzip{X,Y}(xy::AVec{Tuple{X,Y}}) = [t[1] for t in xy], [t[2] for t in xy]
|
||||
unzip{X,Y,Z}(xyz::AVec{Tuple{X,Y,Z}}) = [t[1] for t in xyz], [t[2] for t in xyz], [t[3] for t in xyz]
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ facts("PyPlot") do
|
||||
@fact pyplot() --> Plots.PyPlotBackend()
|
||||
@fact backend() --> Plots.PyPlotBackend()
|
||||
|
||||
image_comparison_facts(:pyplot, skip=[25,30], eps=img_eps)
|
||||
image_comparison_facts(:pyplot, skip=[6,25,30], eps=img_eps)
|
||||
end
|
||||
|
||||
facts("GR") do
|
||||
|
||||
Reference in New Issue
Block a user