Merge remote-tracking branch 'tbreloff/master'
This commit is contained in:
commit
08c9e627ab
@ -2,10 +2,9 @@
|
|||||||
language: julia
|
language: julia
|
||||||
os:
|
os:
|
||||||
- linux
|
- linux
|
||||||
- osx
|
# - osx
|
||||||
julia:
|
julia:
|
||||||
- 0.5
|
- 0.5
|
||||||
- nightly
|
|
||||||
matrix:
|
matrix:
|
||||||
allow_failures:
|
allow_failures:
|
||||||
- julia: nightly
|
- julia: nightly
|
||||||
|
|||||||
37
NEWS.md
37
NEWS.md
@ -12,6 +12,43 @@
|
|||||||
|
|
||||||
## 0.9 (current master/dev)
|
## 0.9 (current master/dev)
|
||||||
|
|
||||||
|
#### 0.9.5
|
||||||
|
|
||||||
|
- added dependency on PlotThemes
|
||||||
|
- set_theme --> theme
|
||||||
|
- remove Compat from REQUIRE
|
||||||
|
- warning for DataFrames without StatPlots
|
||||||
|
- closeall exported and implemented for gr/pyplot
|
||||||
|
- fix DateTime recipe
|
||||||
|
- reset theme with theme(:none)
|
||||||
|
- fix link_axes! for nested subplots
|
||||||
|
- fix plotly lims for log scale
|
||||||
|
|
||||||
|
#### 0.9.4
|
||||||
|
|
||||||
|
- optimizations surrounding Subplot.series_list
|
||||||
|
- better Atom support, support plotlyjs
|
||||||
|
- gr:
|
||||||
|
- gks_wstype defaults and gr_set_output
|
||||||
|
- heatmap uses GR.drawimage
|
||||||
|
- histogram2d puts NaN for zeros
|
||||||
|
- improved support of NaN in heatmaps
|
||||||
|
- rebuilt spy recipes to output scatters with marker_z set
|
||||||
|
- deprecate png support in plotly... point to plotlyjs
|
||||||
|
- fixes:
|
||||||
|
- axis widen with lims set
|
||||||
|
- reset_extrema, setxyz
|
||||||
|
- bar plot widen
|
||||||
|
- better tick padding
|
||||||
|
- consistent tick rotation
|
||||||
|
- consistent aspect_ratio
|
||||||
|
- pyplot dpi
|
||||||
|
- plotly horizontal bars
|
||||||
|
- handle series attributes when combining subplots
|
||||||
|
- gr images transposed
|
||||||
|
- converted Date/DateTime to new type recipe approach for arrays
|
||||||
|
- issues closed include: #505 #513 #479 #523 #526 #529
|
||||||
|
|
||||||
#### 0.9.3
|
#### 0.9.3
|
||||||
|
|
||||||
- support pdf and eps in plotlyjs backend
|
- support pdf and eps in plotlyjs backend
|
||||||
|
|||||||
4
REQUIRE
4
REQUIRE
@ -1,9 +1,9 @@
|
|||||||
julia 0.5-
|
julia 0.5
|
||||||
|
|
||||||
RecipesBase
|
RecipesBase
|
||||||
PlotUtils
|
PlotUtils
|
||||||
|
PlotThemes
|
||||||
Reexport
|
Reexport
|
||||||
Compat
|
|
||||||
FixedSizeArrays
|
FixedSizeArrays
|
||||||
Measures
|
Measures
|
||||||
Showoff
|
Showoff
|
||||||
|
|||||||
80
src/Plots.jl
80
src/Plots.jl
@ -1,40 +1,27 @@
|
|||||||
|
|
||||||
__precompile__()
|
|
||||||
|
|
||||||
module Plots
|
module Plots
|
||||||
|
|
||||||
# using Compat
|
|
||||||
using Reexport
|
using Reexport
|
||||||
# @reexport using Colors
|
|
||||||
# using Requires
|
|
||||||
using FixedSizeArrays
|
using FixedSizeArrays
|
||||||
@reexport using RecipesBase
|
@reexport using RecipesBase
|
||||||
using Base.Meta
|
using Base.Meta
|
||||||
@reexport using PlotUtils
|
@reexport using PlotUtils
|
||||||
|
@reexport using PlotThemes
|
||||||
import Showoff
|
import Showoff
|
||||||
|
|
||||||
export
|
export
|
||||||
AbstractPlot,
|
|
||||||
Plot,
|
|
||||||
Subplot,
|
|
||||||
AbstractLayout,
|
|
||||||
GridLayout,
|
|
||||||
grid,
|
grid,
|
||||||
EmptyLayout,
|
|
||||||
bbox,
|
bbox,
|
||||||
plotarea,
|
plotarea,
|
||||||
@layout,
|
@layout,
|
||||||
AVec,
|
|
||||||
AMat,
|
|
||||||
KW,
|
KW,
|
||||||
|
|
||||||
wrap,
|
wrap,
|
||||||
set_theme,
|
theme,
|
||||||
add_theme,
|
|
||||||
|
|
||||||
plot,
|
plot,
|
||||||
plot!,
|
plot!,
|
||||||
update!,
|
attr!,
|
||||||
|
|
||||||
current,
|
current,
|
||||||
default,
|
default,
|
||||||
@ -70,6 +57,8 @@ export
|
|||||||
savefig,
|
savefig,
|
||||||
png,
|
png,
|
||||||
gui,
|
gui,
|
||||||
|
inline,
|
||||||
|
closeall,
|
||||||
|
|
||||||
backend,
|
backend,
|
||||||
backends,
|
backends,
|
||||||
@ -77,12 +66,10 @@ export
|
|||||||
backend_object,
|
backend_object,
|
||||||
add_backend,
|
add_backend,
|
||||||
aliases,
|
aliases,
|
||||||
# dataframes,
|
|
||||||
|
|
||||||
Shape,
|
Shape,
|
||||||
text,
|
text,
|
||||||
font,
|
font,
|
||||||
Axis,
|
|
||||||
stroke,
|
stroke,
|
||||||
brush,
|
brush,
|
||||||
Surface,
|
Surface,
|
||||||
@ -94,11 +81,12 @@ export
|
|||||||
Animation,
|
Animation,
|
||||||
frame,
|
frame,
|
||||||
gif,
|
gif,
|
||||||
|
mov,
|
||||||
|
mp4,
|
||||||
|
animate,
|
||||||
@animate,
|
@animate,
|
||||||
@gif,
|
@gif,
|
||||||
|
|
||||||
spy,
|
|
||||||
|
|
||||||
test_examples,
|
test_examples,
|
||||||
iter_segments,
|
iter_segments,
|
||||||
coords,
|
coords,
|
||||||
@ -110,9 +98,7 @@ export
|
|||||||
center,
|
center,
|
||||||
P2,
|
P2,
|
||||||
P3,
|
P3,
|
||||||
BezierCurve,
|
BezierCurve
|
||||||
curve_points,
|
|
||||||
directed_curve
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
@ -206,28 +192,29 @@ yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip
|
|||||||
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
||||||
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
||||||
|
|
||||||
title!(plt::Plot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
let PlotOrSubplot = Union{Plot, Subplot}
|
||||||
xlabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||||
ylabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
||||||
xlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
|
ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
||||||
ylims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
|
xlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
|
||||||
zlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
|
ylims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
|
||||||
xlims!(plt::Plot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
zlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
|
||||||
ylims!(plt::Plot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
||||||
zlims!(plt::Plot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
||||||
xticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
|
zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
||||||
yticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
|
xticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
|
||||||
xticks!{T<:Real,S<:AbstractString}(plt::Plot,
|
yticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
|
||||||
|
xticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
|
||||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
|
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
|
||||||
yticks!{T<:Real,S<:AbstractString}(plt::Plot,
|
yticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
|
||||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
|
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
|
||||||
annotate!(plt::Plot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||||
annotate!{T<:Tuple}(plt::Plot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
|
annotate!{T<:Tuple}(plt::PlotOrSubplot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
|
||||||
xflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
||||||
yflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
||||||
xaxis!(plt::Plot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
||||||
yaxis!(plt::Plot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
@ -247,11 +234,4 @@ end
|
|||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
# if VERSION >= v"0.4.0-dev+5512"
|
|
||||||
# include("precompile.jl")
|
|
||||||
# _precompile_()
|
|
||||||
# end
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
|
||||||
|
|
||||||
end # module
|
end # module
|
||||||
|
|||||||
@ -16,6 +16,35 @@ function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
|
|||||||
push!(anim.frames, filename)
|
push!(anim.frames, filename)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
giffn() = (isijulia() ? "tmp.gif" : tempname()*".gif")
|
||||||
|
movfn() = (isijulia() ? "tmp.mov" : tempname()*".mov")
|
||||||
|
mp4fn() = (isijulia() ? "tmp.mp4" : tempname()*".mp4")
|
||||||
|
|
||||||
|
type FrameIterator
|
||||||
|
itr
|
||||||
|
every::Int
|
||||||
|
kw
|
||||||
|
end
|
||||||
|
FrameIterator(itr; every=1, kw...) = FrameIterator(itr, every, kw)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Animate from an iterator which returns the plot args each iteration.
|
||||||
|
"""
|
||||||
|
function animate(fitr::FrameIterator, fn = giffn(); kw...)
|
||||||
|
anim = Animation()
|
||||||
|
for (i, plotargs) in enumerate(fitr.itr)
|
||||||
|
if mod1(i, fitr.every) == 1
|
||||||
|
plot(wraptuple(plotargs)...; fitr.kw...)
|
||||||
|
frame(anim)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
gif(anim, fn; kw...)
|
||||||
|
end
|
||||||
|
|
||||||
|
# most things will implement this
|
||||||
|
function animate(obj, fn = giffn(); every=1, fps=20, loop=0, kw...)
|
||||||
|
animate(FrameIterator(obj, every, kw), fn; fps=fps, loop=loop)
|
||||||
|
end
|
||||||
|
|
||||||
# -----------------------------------------------
|
# -----------------------------------------------
|
||||||
|
|
||||||
@ -24,19 +53,30 @@ immutable AnimatedGif
|
|||||||
filename::String
|
filename::String
|
||||||
end
|
end
|
||||||
|
|
||||||
function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif"); fps::Integer = 20, loop::Integer = 0)
|
file_extension(fn) = Base.Filesystem.splitext(fn)[2][2:end]
|
||||||
|
|
||||||
|
gif(anim::Animation, fn = giffn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||||
|
mov(anim::Animation, fn = movfn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||||
|
mp4(anim::Animation, fn = mp4fn(); kw...) = buildanimation(anim.dir, fn; kw...)
|
||||||
|
|
||||||
|
const _imagemagick_initialized = Ref(false)
|
||||||
|
|
||||||
|
function buildanimation(animdir::AbstractString, fn::AbstractString;
|
||||||
|
fps::Integer = 20, loop::Integer = 0)
|
||||||
fn = abspath(fn)
|
fn = abspath(fn)
|
||||||
|
|
||||||
try
|
try
|
||||||
|
if !_imagemagick_initialized[]
|
||||||
# high quality
|
|
||||||
speed = round(Int, 100 / fps)
|
|
||||||
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
|
file = joinpath(Pkg.dir("ImageMagick"), "deps","deps.jl")
|
||||||
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
|
if isfile(file) && !haskey(ENV, "MAGICK_CONFIGURE_PATH")
|
||||||
include(file)
|
include(file)
|
||||||
end
|
end
|
||||||
|
_imagemagick_initialized[] = true
|
||||||
|
end
|
||||||
|
|
||||||
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
# prefix = get(ENV, "MAGICK_CONFIGURE_PATH", "")
|
||||||
run(`convert -delay $speed -loop $loop $(joinpath(anim.dir, "*.png")) -alpha off $fn`)
|
# high quality
|
||||||
|
speed = round(Int, 100 / fps)
|
||||||
|
run(`convert -delay $speed -loop $loop $(joinpath(animdir, "*.png")) -alpha off $fn`)
|
||||||
|
|
||||||
catch err
|
catch err
|
||||||
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
|
warn("""Tried to create gif using convert (ImageMagick), but got error: $err
|
||||||
@ -44,7 +84,7 @@ function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif");
|
|||||||
Will try ffmpeg, but it's lower quality...)""")
|
Will try ffmpeg, but it's lower quality...)""")
|
||||||
|
|
||||||
# low quality
|
# low quality
|
||||||
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(anim.dir)/%06d.png -y $fn`)
|
run(`ffmpeg -v 0 -framerate $fps -loop $loop -i $(animdir)/%06d.png -y $fn`)
|
||||||
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
|
# run(`ffmpeg -v warning -i "fps=$fps,scale=320:-1:flags=lanczos"`)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -56,7 +96,14 @@ end
|
|||||||
|
|
||||||
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
|
# write out html to view the gif... note the rand call which is a hack so the image doesn't get cached
|
||||||
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
function Base.show(io::IO, ::MIME"text/html", agif::AnimatedGif)
|
||||||
write(io, "<img src=\"$(relpath(agif.filename))?$(rand())>\" />")
|
ext = file_extension(agif.filename)
|
||||||
|
write(io, if ext == "gif"
|
||||||
|
"<img src=\"$(relpath(agif.filename))?$(rand())>\" />"
|
||||||
|
elseif ext in ("mov", "mp4")
|
||||||
|
"<video controls><source src=\"$(relpath(agif.filename))?$(rand())>\" type=\"video/$ext\"></video>"
|
||||||
|
else
|
||||||
|
error("Cannot show animation with extension $ext: $agif")
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -19,7 +19,7 @@ const _arg_desc = KW(
|
|||||||
:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.",
|
:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.",
|
||||||
:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)",
|
:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)",
|
||||||
:markerstrokewidth => "Number. Width of the marker stroke (border. in pixels)",
|
: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 `:seriescolor`.",
|
: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.",
|
: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. For histogram-types, defines the number of bins, or the edges, of the histogram.",
|
||||||
:smooth => "Bool. Add a regression line?",
|
:smooth => "Bool. Add a regression line?",
|
||||||
@ -85,7 +85,7 @@ const _arg_desc = KW(
|
|||||||
:grid => "Bool. Show the grid lines?",
|
:grid => "Bool. Show the grid lines?",
|
||||||
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
|
:annotations => "(x,y,text) tuple(s). Can be a single tuple or a list of them. Text can be String or PlotText (created with `text(args...)`) Add one-off text annotations at the x,y coordinates.",
|
||||||
:projection => "Symbol or String. '3d' or 'polar'",
|
:projection => "Symbol or String. '3d' or 'polar'",
|
||||||
:aspect_ratio => "Symbol (:equal) or Number (width to height ratio of plot area).",
|
:aspect_ratio => "Symbol (:equal) or Number. Plot area is resized so that 1 y-unit is the same size as `apect_ratio` x-units.",
|
||||||
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
|
:margin => "Measure (multiply by `mm`, `px`, etc). Base for individual margins... not directly used. Specifies the extra padding around subplots.",
|
||||||
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
|
:left_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding to the left of the subplot.",
|
||||||
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
|
:top_margin => "Measure (multiply by `mm`, `px`, etc) or `:match` (matches `:margin`). Specifies the extra padding on the top of the subplot.",
|
||||||
|
|||||||
37
src/args.jl
37
src/args.jl
@ -123,6 +123,16 @@ const _markerAliases = Dict{Symbol,Symbol}(
|
|||||||
:dtri => :dtriangle,
|
:dtri => :dtriangle,
|
||||||
:downtri => :dtriangle,
|
:downtri => :dtriangle,
|
||||||
:downtriangle => :dtriangle,
|
:downtriangle => :dtriangle,
|
||||||
|
:> => :rtriangle,
|
||||||
|
:rt => :rtriangle,
|
||||||
|
:rtri => :rtriangle,
|
||||||
|
:righttri => :rtriangle,
|
||||||
|
:righttriangle => :rtriangle,
|
||||||
|
:< => :ltriangle,
|
||||||
|
:lt => :ltriangle,
|
||||||
|
:ltri => :ltriangle,
|
||||||
|
:lighttri => :ltriangle,
|
||||||
|
:lighttriangle => :ltriangle,
|
||||||
# :+ => :cross,
|
# :+ => :cross,
|
||||||
:plus => :cross,
|
:plus => :cross,
|
||||||
# :x => :xcross,
|
# :x => :xcross,
|
||||||
@ -165,7 +175,7 @@ const _series_defaults = KW(
|
|||||||
:markershape => :none,
|
:markershape => :none,
|
||||||
:markercolor => :match,
|
:markercolor => :match,
|
||||||
:markeralpha => nothing,
|
:markeralpha => nothing,
|
||||||
:markersize => 6,
|
:markersize => 4,
|
||||||
:markerstrokestyle => :solid,
|
:markerstrokestyle => :solid,
|
||||||
:markerstrokewidth => 1,
|
:markerstrokewidth => 1,
|
||||||
:markerstrokecolor => :match,
|
:markerstrokecolor => :match,
|
||||||
@ -194,7 +204,7 @@ const _series_defaults = KW(
|
|||||||
:match_dimensions => false, # do rows match x (true) or y (false) for heatmap/image/spy? see issue 196
|
:match_dimensions => false, # do rows match x (true) or y (false) for heatmap/image/spy? see issue 196
|
||||||
# this ONLY effects whether or not the z-matrix is transposed for a heatmap display!
|
# this ONLY effects whether or not the z-matrix is transposed for a heatmap display!
|
||||||
:subplot => :auto, # which subplot(s) does this series belong to?
|
:subplot => :auto, # which subplot(s) does this series belong to?
|
||||||
:series_annotations => [], # a list of annotations which apply to the coordinates of this series
|
:series_annotations => nothing, # a list of annotations which apply to the coordinates of this series
|
||||||
:primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow
|
:primary => true, # when true, this "counts" as a series for color selection, etc. the main use is to allow
|
||||||
# one logical series to be broken up (path and markers, for example)
|
# one logical series to be broken up (path and markers, for example)
|
||||||
:hover => nothing, # text to display when hovering over the data points
|
:hover => nothing, # text to display when hovering over the data points
|
||||||
@ -353,6 +363,7 @@ add_aliases(:seriescolor, :c, :color, :colour)
|
|||||||
add_aliases(:linecolor, :lc, :lcolor, :lcolour, :linecolour)
|
add_aliases(:linecolor, :lc, :lcolor, :lcolour, :linecolour)
|
||||||
add_aliases(:markercolor, :mc, :mcolor, :mcolour, :markercolour)
|
add_aliases(:markercolor, :mc, :mcolor, :mcolour, :markercolour)
|
||||||
add_aliases(:markerstrokecolor, :msc, :mscolor, :mscolour, :markerstrokecolour)
|
add_aliases(:markerstrokecolor, :msc, :mscolor, :mscolour, :markerstrokecolour)
|
||||||
|
add_aliases(:markerstrokewidth, :msw, :mswidth)
|
||||||
add_aliases(:fillcolor, :fc, :fcolor, :fcolour, :fillcolour)
|
add_aliases(:fillcolor, :fc, :fcolor, :fcolour, :fillcolour)
|
||||||
|
|
||||||
add_aliases(:background_color, :bg, :bgcolor, :bg_color, :background,
|
add_aliases(:background_color, :bg, :bgcolor, :bg_color, :background,
|
||||||
@ -387,7 +398,7 @@ add_aliases(:foreground_color_guide, :fg_guide, :fgguide, :fgcolor_guide, :fg_co
|
|||||||
# alphas
|
# alphas
|
||||||
add_aliases(:seriesalpha, :alpha, :α, :opacity)
|
add_aliases(:seriesalpha, :alpha, :α, :opacity)
|
||||||
add_aliases(:linealpha, :la, :lalpha, :lα, :lineopacity, :lopacity)
|
add_aliases(:linealpha, :la, :lalpha, :lα, :lineopacity, :lopacity)
|
||||||
add_aliases(:makeralpha, :ma, :malpha, :mα, :makeropacity, :mopacity)
|
add_aliases(:markeralpha, :ma, :malpha, :mα, :markeropacity, :mopacity)
|
||||||
add_aliases(:markerstrokealpha, :msa, :msalpha, :msα, :markerstrokeopacity, :msopacity)
|
add_aliases(:markerstrokealpha, :msa, :msalpha, :msα, :markerstrokeopacity, :msopacity)
|
||||||
add_aliases(:fillalpha, :fa, :falpha, :fα, :fillopacity, :fopacity)
|
add_aliases(:fillalpha, :fa, :falpha, :fα, :fillopacity, :fopacity)
|
||||||
|
|
||||||
@ -439,7 +450,7 @@ add_aliases(:match_dimensions, :transpose, :transpose_z)
|
|||||||
add_aliases(:subplot, :sp, :subplt, :splt)
|
add_aliases(:subplot, :sp, :subplt, :splt)
|
||||||
add_aliases(:projection, :proj)
|
add_aliases(:projection, :proj)
|
||||||
add_aliases(:title_location, :title_loc, :titleloc, :title_position, :title_pos, :titlepos, :titleposition, :title_align, :title_alignment)
|
add_aliases(:title_location, :title_loc, :titleloc, :title_position, :title_pos, :titlepos, :titleposition, :title_align, :title_alignment)
|
||||||
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation)
|
add_aliases(:series_annotations, :series_ann, :seriesann, :series_anns, :seriesanns, :series_annotation, :text, :txt, :texts, :txts)
|
||||||
add_aliases(:html_output_format, :format, :fmt, :html_format)
|
add_aliases(:html_output_format, :format, :fmt, :html_format)
|
||||||
add_aliases(:orientation, :direction, :dir)
|
add_aliases(:orientation, :direction, :dir)
|
||||||
add_aliases(:inset_subplots, :inset, :floating)
|
add_aliases(:inset_subplots, :inset, :floating)
|
||||||
@ -691,6 +702,11 @@ function preprocessArgs!(d::KW)
|
|||||||
end
|
end
|
||||||
delete!(d, :fill)
|
delete!(d, :fill)
|
||||||
|
|
||||||
|
# handle series annotations
|
||||||
|
if haskey(d, :series_annotations)
|
||||||
|
d[:series_annotations] = series_annotations(wraptuple(d[:series_annotations])...)
|
||||||
|
end
|
||||||
|
|
||||||
# convert into strokes and brushes
|
# convert into strokes and brushes
|
||||||
|
|
||||||
if haskey(d, :arrow)
|
if haskey(d, :arrow)
|
||||||
@ -841,6 +857,7 @@ function convertLegendValue(val::Symbol)
|
|||||||
end
|
end
|
||||||
convertLegendValue(val::Bool) = val ? :best : :none
|
convertLegendValue(val::Bool) = val ? :best : :none
|
||||||
convertLegendValue(val::Void) = :none
|
convertLegendValue(val::Void) = :none
|
||||||
|
convertLegendValue{S<:Real, T<:Real}(v::Tuple{S,T}) = v
|
||||||
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
convertLegendValue(v::AbstractArray) = map(convertLegendValue, v)
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@ -1028,8 +1045,14 @@ end
|
|||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
function _update_subplot_periphery(sp::Subplot, anns::AVec)
|
function _update_subplot_periphery(sp::Subplot, anns::AVec)
|
||||||
# extend annotations
|
# extend annotations, and ensure we always have a (x,y,PlotText) tuple
|
||||||
sp.attr[:annotations] = vcat(anns, sp[:annotations])
|
newanns = vcat(anns, sp[:annotations])
|
||||||
|
for (i,ann) in enumerate(newanns)
|
||||||
|
x,y,tmp = ann
|
||||||
|
ptxt = isa(tmp, PlotText) ? tmp : text(tmp)
|
||||||
|
newanns[i] = (x,y,ptxt)
|
||||||
|
end
|
||||||
|
sp.attr[:annotations] = newanns
|
||||||
|
|
||||||
# handle legend/colorbar
|
# handle legend/colorbar
|
||||||
sp.attr[:legend] = convertLegendValue(sp.attr[:legend])
|
sp.attr[:legend] = convertLegendValue(sp.attr[:legend])
|
||||||
@ -1093,7 +1116,7 @@ function _update_axis(axis::Axis, d_in::KW, letter::Symbol, subplot_index::Int)
|
|||||||
end
|
end
|
||||||
|
|
||||||
# update the axis
|
# update the axis
|
||||||
update!(axis, args...; kw...)
|
attr!(axis, args...; kw...)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -29,7 +29,7 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
|||||||
d[:discrete_values] = []
|
d[:discrete_values] = []
|
||||||
|
|
||||||
# update the defaults
|
# update the defaults
|
||||||
update!(Axis([sp], d), args...; kw...)
|
attr!(Axis([sp], d), args...; kw...)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_axis(sp::Subplot, letter::Symbol)
|
function get_axis(sp::Subplot, letter::Symbol)
|
||||||
@ -83,7 +83,7 @@ function process_axis_arg!(d::KW, arg, letter = "")
|
|||||||
end
|
end
|
||||||
|
|
||||||
# update an Axis object with magic args and keywords
|
# update an Axis object with magic args and keywords
|
||||||
function update!(axis::Axis, args...; kw...)
|
function attr!(axis::Axis, args...; kw...)
|
||||||
# first process args
|
# first process args
|
||||||
d = axis.d
|
d = axis.d
|
||||||
for arg in args
|
for arg in args
|
||||||
@ -381,6 +381,9 @@ function axis_limits(axis::Axis, should_widen::Bool = default_should_widen(axis)
|
|||||||
if amax <= amin && isfinite(amin)
|
if amax <= amin && isfinite(amin)
|
||||||
amax = amin + 1.0
|
amax = amin + 1.0
|
||||||
end
|
end
|
||||||
|
if !isfinite(amin) && !isfinite(amax)
|
||||||
|
amin, amax = 0.0, 1.0
|
||||||
|
end
|
||||||
if should_widen
|
if should_widen
|
||||||
widen(amin, amax)
|
widen(amin, amax)
|
||||||
else
|
else
|
||||||
|
|||||||
@ -51,6 +51,21 @@ _before_layout_calcs(plt::Plot) = nothing
|
|||||||
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
|
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
|
||||||
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
|
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
|
||||||
|
|
||||||
|
"Returns the (width,height) of a text label."
|
||||||
|
function text_size(lablen::Int, sz::Number, rot::Number = 0)
|
||||||
|
# we need to compute the size of the ticks generically
|
||||||
|
# this means computing the bounding box and then getting the width/height
|
||||||
|
# note:
|
||||||
|
ptsz = sz * pt
|
||||||
|
width = 0.8lablen * ptsz
|
||||||
|
|
||||||
|
# now compute the generalized "height" after rotation as the "opposite+adjacent" of 2 triangles
|
||||||
|
height = abs(sind(rot)) * width + abs(cosd(rot)) * ptsz
|
||||||
|
width = abs(sind(rot+90)) * width + abs(cosd(rot+90)) * ptsz
|
||||||
|
width, height
|
||||||
|
end
|
||||||
|
text_size(lab::AbstractString, sz::Number, rot::Number = 0) = text_size(length(lab), sz, rot)
|
||||||
|
|
||||||
# account for the size/length/rotation of tick labels
|
# account for the size/length/rotation of tick labels
|
||||||
function tick_padding(axis::Axis)
|
function tick_padding(axis::Axis)
|
||||||
ticks = get_ticks(axis)
|
ticks = get_ticks(axis)
|
||||||
@ -58,19 +73,24 @@ function tick_padding(axis::Axis)
|
|||||||
0mm
|
0mm
|
||||||
else
|
else
|
||||||
vals, labs = ticks
|
vals, labs = ticks
|
||||||
ptsz = axis[:tickfont].pointsize * pt
|
isempty(labs) && return 0mm
|
||||||
|
# ptsz = axis[:tickfont].pointsize * pt
|
||||||
# we need to compute the size of the ticks generically
|
|
||||||
# this means computing the bounding box and then getting the width/height
|
|
||||||
longest_label = maximum(length(lab) for lab in labs)
|
longest_label = maximum(length(lab) for lab in labs)
|
||||||
labelwidth = 0.8longest_label * ptsz
|
|
||||||
|
|
||||||
# generalize by "rotating" y labels
|
# generalize by "rotating" y labels
|
||||||
rot = axis[:rotation] + (axis[:letter] == :y ? 90 : 0)
|
rot = axis[:rotation] + (axis[:letter] == :y ? 90 : 0)
|
||||||
|
|
||||||
# now compute the generalized "height" after rotation as the "opposite+adjacent" of 2 triangles
|
# # we need to compute the size of the ticks generically
|
||||||
hgt = abs(sind(rot)) * labelwidth + abs(cosd(rot)) * ptsz + 1mm
|
# # this means computing the bounding box and then getting the width/height
|
||||||
hgt
|
# labelwidth = 0.8longest_label * ptsz
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # now compute the generalized "height" after rotation as the "opposite+adjacent" of 2 triangles
|
||||||
|
# hgt = abs(sind(rot)) * labelwidth + abs(cosd(rot)) * ptsz + 1mm
|
||||||
|
# hgt
|
||||||
|
|
||||||
|
# get the height of the rotated label
|
||||||
|
text_size(longest_label, axis[:tickfont].pointsize, rot)[2]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
#=
|
``#=
|
||||||
TODO
|
TODO
|
||||||
* move all gl_ methods to GLPlot
|
* move all gl_ methods to GLPlot
|
||||||
* integrate GLPlot UI
|
* integrate GLPlot UI
|
||||||
@ -66,6 +66,7 @@ function _initialize_backend(::GLVisualizeBackend; kw...)
|
|||||||
import GLAbstraction: Style
|
import GLAbstraction: Style
|
||||||
import GLVisualize: visualize
|
import GLVisualize: visualize
|
||||||
import Plots.GL
|
import Plots.GL
|
||||||
|
import UnicodeFun
|
||||||
Plots.slice_arg(img::Images.AbstractImage, idx::Int) = img
|
Plots.slice_arg(img::Images.AbstractImage, idx::Int) = img
|
||||||
is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true
|
is_marker_supported(::GLVisualizeBackend, shape::GLVisualize.AllPrimitives) = true
|
||||||
is_marker_supported{Img<:Images.AbstractImage}(::GLVisualizeBackend, shape::Union{Vector{Img}, Img}) = true
|
is_marker_supported{Img<:Images.AbstractImage}(::GLVisualizeBackend, shape::Union{Vector{Img}, Img}) = true
|
||||||
@ -78,11 +79,11 @@ end
|
|||||||
function add_backend_string(b::GLVisualizeBackend)
|
function add_backend_string(b::GLVisualizeBackend)
|
||||||
"""
|
"""
|
||||||
For those incredibly brave souls who assume full responsibility for what happens next...
|
For those incredibly brave souls who assume full responsibility for what happens next...
|
||||||
There's an easy way to get what you need for the GLVisualize backend to work:
|
There's an easy way to get what you need for the GLVisualize backend to work (until Pkg3 is usable):
|
||||||
|
|
||||||
Pkg.clone("https://github.com/tbreloff/MetaPkg.jl")
|
Pkg.clone("https://github.com/tbreloff/MetaPkg.jl")
|
||||||
import MetaPkg
|
using MetaPkg
|
||||||
MetaPkg.checkout("MetaGL")
|
meta_checkout("MetaGL")
|
||||||
|
|
||||||
See the MetaPkg readme for details...
|
See the MetaPkg readme for details...
|
||||||
"""
|
"""
|
||||||
@ -97,13 +98,34 @@ end
|
|||||||
# GLPlot.init()
|
# GLPlot.init()
|
||||||
# end
|
# end
|
||||||
const _glplot_deletes = []
|
const _glplot_deletes = []
|
||||||
|
|
||||||
|
function close_child_signals!(screen)
|
||||||
|
for child in screen.children
|
||||||
|
for (k, s) in child.inputs
|
||||||
|
empty!(s.actions)
|
||||||
|
end
|
||||||
|
for (k, cam) in child.cameras
|
||||||
|
for f in fieldnames(cam)
|
||||||
|
s = getfield(cam, f)
|
||||||
|
if isa(s, Signal)
|
||||||
|
close(s, false)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
empty!(child.cameras)
|
||||||
|
close_child_signals!(child)
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
function empty_screen!(screen)
|
function empty_screen!(screen)
|
||||||
if isempty(_glplot_deletes)
|
if isempty(_glplot_deletes)
|
||||||
screen.renderlist = ()
|
close_child_signals!(screen)
|
||||||
for c in screen.children
|
empty!(screen)
|
||||||
empty!(c)
|
empty!(screen.cameras)
|
||||||
|
for (k, s) in screen.inputs
|
||||||
|
empty!(s.actions)
|
||||||
end
|
end
|
||||||
empty!(screen.children)
|
empty!(screen)
|
||||||
else
|
else
|
||||||
for del_signal in _glplot_deletes
|
for del_signal in _glplot_deletes
|
||||||
push!(del_signal, true) # trigger delete
|
push!(del_signal, true) # trigger delete
|
||||||
@ -112,36 +134,63 @@ function empty_screen!(screen)
|
|||||||
end
|
end
|
||||||
nothing
|
nothing
|
||||||
end
|
end
|
||||||
function _create_backend_figure(plt::Plot{GLVisualizeBackend})
|
function poll_reactive()
|
||||||
# init a screen
|
# 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
|
||||||
|
get_plot_screen(elem, name, result)
|
||||||
|
end
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
function get_plot_screen(screen, name, result = [])
|
||||||
|
if screen.name == name
|
||||||
|
push!(result, screen)
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
get_plot_screen(screen.children, name, result)
|
||||||
|
end
|
||||||
|
|
||||||
|
function create_window(plt::Plot{GLVisualizeBackend}, visible)
|
||||||
|
name = Symbol("Plots.jl")
|
||||||
|
# make sure we have any screen open
|
||||||
if isempty(GLVisualize.get_screens())
|
if isempty(GLVisualize.get_screens())
|
||||||
s = GLVisualize.glscreen()
|
# create a fresh, new screen
|
||||||
Reactive.stop()
|
parent_screen = GLVisualize.glscreen(
|
||||||
@async begin
|
"Plot",
|
||||||
while isopen(s)
|
resolution = plt[:size],
|
||||||
tic()
|
visible = visible
|
||||||
GLWindow.pollevents()
|
)
|
||||||
if Base.n_avail(Reactive._messages) > 0
|
@async GLWindow.waiting_renderloop(parent_screen)
|
||||||
Reactive.run_till_now()
|
|
||||||
GLWindow.render_frame(s)
|
|
||||||
GLWindow.swapbuffers(s)
|
|
||||||
end
|
end
|
||||||
yield()
|
# now lets get ourselves a permanent Plotting screen
|
||||||
diff = (1/60) - toq()
|
plot_screens = get_plot_screen(GLVisualize.get_screens(), name)
|
||||||
while diff >= 0.001
|
screen = if isempty(plot_screens) # no screen with `name`
|
||||||
tic()
|
parent = GLVisualize.current_screen()
|
||||||
sleep(0.001) # sleep for the minimal amount of time
|
screen = GLWindow.Screen(
|
||||||
diff -= toq()
|
parent, area = map(GLWindow.zeroposition, parent.area),
|
||||||
end
|
name = name
|
||||||
end
|
)
|
||||||
GLWindow.destroy!(s)
|
for (k, s) in screen.inputs # copy signals, so we can clean them up better
|
||||||
GLVisualize.cleanup_old_screens()
|
screen.inputs[k] = map(identity, s)
|
||||||
end
|
end
|
||||||
|
screen
|
||||||
|
elseif length(plot_screens) == 1
|
||||||
|
plot_screens[1]
|
||||||
else
|
else
|
||||||
s = GLVisualize.current_screen()
|
# okay this is silly! Lets see if we can. There is an ID we could use
|
||||||
empty_screen!(s)
|
# 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")
|
||||||
end
|
end
|
||||||
s
|
# Since we own this window, we can do deep cleansing
|
||||||
|
empty_screen!(screen)
|
||||||
|
plt.o = screen
|
||||||
|
GLWindow.set_visibility!(screen, visible)
|
||||||
|
resize!(screen, plt[:size]...)
|
||||||
|
screen
|
||||||
end
|
end
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -154,6 +203,8 @@ const _gl_marker_map = KW(
|
|||||||
:xcross => '❌',
|
:xcross => '❌',
|
||||||
:utriangle => '▲',
|
:utriangle => '▲',
|
||||||
:dtriangle => '▼',
|
:dtriangle => '▼',
|
||||||
|
:ltriangle => '◀',
|
||||||
|
:rtriangle => '▶',
|
||||||
:pentagon => '⬟',
|
:pentagon => '⬟',
|
||||||
:octagon => '⯄',
|
:octagon => '⯄',
|
||||||
:star4 => '✦',
|
:star4 => '✦',
|
||||||
@ -163,26 +214,37 @@ const _gl_marker_map = KW(
|
|||||||
:hline => '━',
|
:hline => '━',
|
||||||
:+ => '+',
|
:+ => '+',
|
||||||
:x => 'x',
|
:x => 'x',
|
||||||
|
:circle => '●'
|
||||||
)
|
)
|
||||||
|
|
||||||
function gl_marker(shape, size)
|
function gl_marker(shape)
|
||||||
shape
|
shape
|
||||||
end
|
end
|
||||||
function gl_marker(shape::Shape, size::FixedSizeArrays.Vec{2,Float32})
|
function gl_marker(shape::Shape)
|
||||||
points = Point2f0[Vec{2,Float32}(p)*10f0 for p in zip(shape.x, shape.y)]
|
points = Point2f0[Vec{2,Float32}(p) for p in zip(shape.x, shape.y)]
|
||||||
|
bb = GeometryTypes.AABB(points)
|
||||||
|
mini, maxi = minimum(bb), maximum(bb)
|
||||||
|
w3 = maxi-mini
|
||||||
|
origin, width = Point2f0(mini[1], mini[2]), Point2f0(w3[1], w3[2])
|
||||||
|
map!(p -> ((p - origin) ./ width) - 0.5f0, points) # normalize and center
|
||||||
GeometryTypes.GLNormalMesh(points)
|
GeometryTypes.GLNormalMesh(points)
|
||||||
end
|
end
|
||||||
# create a marker/shape type
|
# create a marker/shape type
|
||||||
function gl_marker(shape::Symbol, msize)
|
function gl_marker(shape::Vector{Symbol})
|
||||||
isa(msize, Array) && (msize = first(msize)) # size doesn't really matter now
|
String(map(shape) do sym
|
||||||
|
get(_gl_marker_map, sym, '●')
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
function gl_marker(shape::Symbol)
|
||||||
if shape == :rect
|
if shape == :rect
|
||||||
GeometryTypes.HyperRectangle(Vec{2, Float32}(0), msize)
|
GeometryTypes.HyperRectangle(Vec2f0(0), Vec2f0(1))
|
||||||
elseif shape == :circle || shape == :none
|
elseif shape == :circle || shape == :none
|
||||||
GeometryTypes.HyperSphere(Point{2, Float32}(0), maximum(msize))
|
GeometryTypes.HyperSphere(Point2f0(0), 1f0)
|
||||||
elseif haskey(_gl_marker_map, shape)
|
elseif haskey(_gl_marker_map, shape)
|
||||||
_gl_marker_map[shape]
|
_gl_marker_map[shape]
|
||||||
elseif haskey(_shapes, shape)
|
elseif haskey(_shapes, shape)
|
||||||
gl_marker(_shapes[shape], msize)
|
gl_marker(_shapes[shape])
|
||||||
else
|
else
|
||||||
error("Shape $shape not supported by GLVisualize")
|
error("Shape $shape not supported by GLVisualize")
|
||||||
end
|
end
|
||||||
@ -198,24 +260,32 @@ function extract_limits(sp, d, kw_args)
|
|||||||
nothing
|
nothing
|
||||||
end
|
end
|
||||||
|
|
||||||
|
to_vec{T <: FixedVector}(::Type{T}, vec::T) = vec
|
||||||
|
to_vec{T <: FixedVector}(::Type{T}, s::Number) = T(s)
|
||||||
|
|
||||||
|
to_vec{T <: FixedVector{2}}(::Type{T}, vec::FixedVector{3}) = T(vec[1], vec[2])
|
||||||
|
to_vec{T <: FixedVector{3}}(::Type{T}, vec::FixedVector{2}) = T(vec[1], vec[2], 0)
|
||||||
|
|
||||||
|
to_vec{T <: FixedVector}(::Type{T}, vecs::AbstractVector) = map(x-> to_vec(T, x), vecs)
|
||||||
|
|
||||||
function extract_marker(d, kw_args)
|
function extract_marker(d, kw_args)
|
||||||
dim = Plots.is3d(d) ? 3 : 2
|
dim = Plots.is3d(d) ? 3 : 2
|
||||||
scaling = dim == 3 ? 0.003 : 2
|
scaling = dim == 3 ? 0.003 : 2
|
||||||
if haskey(d, :markersize)
|
|
||||||
msize = d[:markersize]
|
|
||||||
if isa(msize, AbstractArray)
|
|
||||||
kw_args[:scale] = map(x->GeometryTypes.Vec{dim, Float32}(x*scaling), msize)
|
|
||||||
else
|
|
||||||
kw_args[:scale] = GeometryTypes.Vec{dim, Float32}(msize*scaling)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
if haskey(d, :markershape)
|
if haskey(d, :markershape)
|
||||||
shape = d[:markershape]
|
shape = d[:markershape]
|
||||||
shape = gl_marker(shape, kw_args[:scale])
|
shape = gl_marker(shape)
|
||||||
if shape != :none
|
if shape != :none
|
||||||
kw_args[:primitive] = shape
|
kw_args[:primitive] = shape
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
dim = isa(kw_args[:primitive], GLVisualize.Sprites) ? 2 : 3
|
||||||
|
if haskey(d, :markersize)
|
||||||
|
msize = d[:markersize]
|
||||||
|
kw_args[:scale] = to_vec(GeometryTypes.Vec{dim, Float32}, msize .* scaling)
|
||||||
|
end
|
||||||
|
if haskey(d, :offset)
|
||||||
|
kw_args[:offset] = d[:offset]
|
||||||
|
end
|
||||||
# get the color
|
# get the color
|
||||||
key = :markercolor
|
key = :markercolor
|
||||||
haskey(d, key) || return
|
haskey(d, key) || return
|
||||||
@ -246,6 +316,7 @@ end
|
|||||||
function _extract_surface(d::AbstractArray)
|
function _extract_surface(d::AbstractArray)
|
||||||
d
|
d
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO when to transpose??
|
# TODO when to transpose??
|
||||||
function extract_surface(d)
|
function extract_surface(d)
|
||||||
map(_extract_surface, (d[:x], d[:y], d[:z]))
|
map(_extract_surface, (d[:x], d[:y], d[:z]))
|
||||||
@ -307,6 +378,7 @@ function extract_stroke(d, kw_args)
|
|||||||
kw_args[:thickness] = d[:linewidth] * 3
|
kw_args[:thickness] = d[:linewidth] * 3
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function extract_color(d, sym)
|
function extract_color(d, sym)
|
||||||
d[Symbol("$(sym)color")]
|
d[Symbol("$(sym)color")]
|
||||||
end
|
end
|
||||||
@ -344,6 +416,7 @@ end
|
|||||||
|
|
||||||
dist(a, b) = abs(a-b)
|
dist(a, b) = abs(a-b)
|
||||||
mindist(x, a, b) = min(dist(a, x), dist(b, x))
|
mindist(x, a, b) = min(dist(a, x), dist(b, x))
|
||||||
|
|
||||||
function gappy(x, ps)
|
function gappy(x, ps)
|
||||||
n = length(ps)
|
n = length(ps)
|
||||||
x <= first(ps) && return first(ps) - x
|
x <= first(ps) && return first(ps) - x
|
||||||
@ -392,9 +465,11 @@ function extract_linestyle(d, kw_args)
|
|||||||
extract_c(d, kw_args, :line)
|
extract_c(d, kw_args, :line)
|
||||||
nothing
|
nothing
|
||||||
end
|
end
|
||||||
|
|
||||||
function hover(to_hover::Vector, to_display, window)
|
function hover(to_hover::Vector, to_display, window)
|
||||||
hover(to_hover[], to_display, window)
|
hover(to_hover[], to_display, window)
|
||||||
end
|
end
|
||||||
|
|
||||||
function get_cam(x)
|
function get_cam(x)
|
||||||
if isa(x, GLAbstraction.Context)
|
if isa(x, GLAbstraction.Context)
|
||||||
return get_cam(x.children)
|
return get_cam(x.children)
|
||||||
@ -405,6 +480,7 @@ function get_cam(x)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function hover(to_hover, to_display, window)
|
function hover(to_hover, to_display, window)
|
||||||
if isa(to_hover, GLAbstraction.Context)
|
if isa(to_hover, GLAbstraction.Context)
|
||||||
return hover(to_hover.children, to_display, window)
|
return hover(to_hover.children, to_display, window)
|
||||||
@ -412,13 +488,13 @@ function hover(to_hover, to_display, window)
|
|||||||
area = map(window.inputs[:mouseposition]) do mp
|
area = map(window.inputs[:mouseposition]) do mp
|
||||||
SimpleRectangle{Int}(round(Int, mp+10)..., 100, 70)
|
SimpleRectangle{Int}(round(Int, mp+10)..., 100, 70)
|
||||||
end
|
end
|
||||||
background = visualize((GLVisualize.ROUNDED_RECTANGLE, Point2f0[0]),
|
|
||||||
color=RGBA{Float32}(0,0,0,0), scale=Vec2f0(100, 70), offset=Vec2f0(0),
|
|
||||||
stroke_color=RGBA{Float32}(0,0,0,0.4),
|
|
||||||
stroke_width=-1.0f0
|
|
||||||
)
|
|
||||||
mh = GLWindow.mouse2id(window)
|
mh = GLWindow.mouse2id(window)
|
||||||
popup = GLWindow.Screen(window, area=area, hidden=true)
|
popup = GLWindow.Screen(
|
||||||
|
window,
|
||||||
|
hidden = map(mh-> !(mh.id == to_hover.id), mh),
|
||||||
|
area = area,
|
||||||
|
stroke = (2f0, RGBA(0f0, 0f0, 0f0, 0.8f0))
|
||||||
|
)
|
||||||
cam = get!(popup.cameras, :perspective) do
|
cam = get!(popup.cameras, :perspective) do
|
||||||
GLAbstraction.PerspectiveCamera(
|
GLAbstraction.PerspectiveCamera(
|
||||||
popup.inputs, Vec3f0(3), Vec3f0(0),
|
popup.inputs, Vec3f0(3), Vec3f0(0),
|
||||||
@ -426,9 +502,6 @@ function hover(to_hover, to_display, window)
|
|||||||
theta = Signal(Vec3f0(0)), trans = Signal(Vec3f0(0))
|
theta = Signal(Vec3f0(0)), trans = Signal(Vec3f0(0))
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
Reactive.preserve(map(mh) do mh
|
|
||||||
popup.hidden = !(mh.id == to_hover.id)
|
|
||||||
end)
|
|
||||||
|
|
||||||
map(enumerate(to_display)) do id
|
map(enumerate(to_display)) do id
|
||||||
i,d = id
|
i,d = id
|
||||||
@ -444,12 +517,11 @@ function hover(to_hover, to_display, window)
|
|||||||
cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
|
cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
|
||||||
end
|
end
|
||||||
GLVisualize._view(robj, popup, camera = cam)
|
GLVisualize._view(robj, popup, camera = cam)
|
||||||
GLVisualize._view(background, popup, camera=:fixed_pixel)
|
|
||||||
bb = GLAbstraction.boundingbox(robj).value
|
bb = GLAbstraction.boundingbox(robj).value
|
||||||
mini = minimum(bb)
|
mini = minimum(bb)
|
||||||
w = GeometryTypes.widths(bb)
|
w = GeometryTypes.widths(bb)
|
||||||
wborder = w * 0.08f0 #8 percent border
|
wborder = w * 0.08f0 #8 percent border
|
||||||
bb = GeometryTypes.AABB{Float32}(mini-wborder, w+2f0*wborder)
|
bb = GeometryTypes.AABB{Float32}(mini - wborder, w + 2 * wborder)
|
||||||
GLAbstraction.center!(cam, bb)
|
GLAbstraction.center!(cam, bb)
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
@ -465,7 +537,7 @@ end
|
|||||||
|
|
||||||
function extract_font(font, kw_args)
|
function extract_font(font, kw_args)
|
||||||
kw_args[:family] = font.family
|
kw_args[:family] = font.family
|
||||||
kw_args[:relative_scale] = font.pointsize*1.5 ./ GLVisualize.glyph_scale!('X')
|
kw_args[:relative_scale] = pointsize(font)
|
||||||
kw_args[:color] = gl_color(font.color)
|
kw_args[:color] = gl_color(font.color)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -489,6 +561,7 @@ function extract_colornorm(d, kw_args)
|
|||||||
kw_args[:intensity] = map(Float32, collect(z))
|
kw_args[:intensity] = map(Float32, collect(z))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function extract_gradient(d, kw_args, sym)
|
function extract_gradient(d, kw_args, sym)
|
||||||
key = Symbol("$(sym)color")
|
key = Symbol("$(sym)color")
|
||||||
haskey(d, key) || return
|
haskey(d, key) || return
|
||||||
@ -498,6 +571,7 @@ function extract_gradient(d, kw_args, sym)
|
|||||||
kw_args[:color_map] = c
|
kw_args[:color_map] = c
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
function extract_c(d, kw_args, sym)
|
function extract_c(d, kw_args, sym)
|
||||||
key = Symbol("$(sym)color")
|
key = Symbol("$(sym)color")
|
||||||
haskey(d, key) || return
|
haskey(d, key) || return
|
||||||
@ -505,7 +579,11 @@ function extract_c(d, kw_args, sym)
|
|||||||
kw_args[:color] = nothing
|
kw_args[:color] = nothing
|
||||||
kw_args[:color_map] = nothing
|
kw_args[:color_map] = nothing
|
||||||
kw_args[:color_norm] = nothing
|
kw_args[:color_norm] = nothing
|
||||||
if isa(c, AbstractVector)
|
if (
|
||||||
|
isa(c, AbstractVector) &&
|
||||||
|
((haskey(d, :marker_z) && d[:marker_z] != nothing) ||
|
||||||
|
(haskey(d, :line_z) && d[:line_z] != nothing))
|
||||||
|
)
|
||||||
extract_colornorm(d, kw_args)
|
extract_colornorm(d, kw_args)
|
||||||
kw_args[:color_map] = c
|
kw_args[:color_map] = c
|
||||||
else
|
else
|
||||||
@ -545,7 +623,7 @@ function draw_grid_lines(sp, grid_segs, thickness, style, model, color)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function align_offset(startpos, lastpos, atlas, rscale, font, align)
|
function align_offset(startpos, lastpos, atlas, rscale, font, align)
|
||||||
xscale, yscale = GLVisualize.glyph_scale!('X').*rscale
|
xscale, yscale = GLVisualize.glyph_scale!('X', rscale)
|
||||||
xmove = (lastpos-startpos)[1]+xscale
|
xmove = (lastpos-startpos)[1]+xscale
|
||||||
if align == :top
|
if align == :top
|
||||||
return -Vec2f0(xmove/2f0, yscale)
|
return -Vec2f0(xmove/2f0, yscale)
|
||||||
@ -555,60 +633,81 @@ function align_offset(startpos, lastpos, atlas, rscale, font, align)
|
|||||||
error("Align $align not known")
|
error("Align $align not known")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function align_offset(startpos, lastpos, atlas, rscale, font, align::Vec)
|
function align_offset(startpos, lastpos, atlas, rscale, font, align::Vec)
|
||||||
xscale, yscale = GLVisualize.glyph_scale!('X').*rscale
|
xscale, yscale = GLVisualize.glyph_scale!('X', rscale)
|
||||||
xmove = (lastpos-startpos)[1] + xscale
|
xmove = (lastpos-startpos)[1] + xscale
|
||||||
return -Vec2f0(xmove, yscale) .* align
|
return -Vec2f0(xmove, yscale) .* align
|
||||||
end
|
end
|
||||||
|
|
||||||
function alignment2num(x::Symbol)
|
function alignment2num(x::Symbol)
|
||||||
(x in (:hcenter, :vcenter)) && return 0.5
|
(x in (:hcenter, :vcenter)) && return 0.5
|
||||||
(x in (:left, :bottom)) && return 0.0
|
(x in (:left, :bottom)) && return 0.0
|
||||||
(x in (:right, :top)) && return 1.0
|
(x in (:right, :top)) && return 1.0
|
||||||
0.0 # 0 default, or better to error?
|
0.0 # 0 default, or better to error?
|
||||||
end
|
end
|
||||||
|
|
||||||
function alignment2num(font::Plots.Font)
|
function alignment2num(font::Plots.Font)
|
||||||
Vec2f0(map(alignment2num, (font.halign, font.valign)))
|
Vec2f0(map(alignment2num, (font.halign, font.valign)))
|
||||||
end
|
end
|
||||||
|
|
||||||
function draw_ticks(axis, ticks, align, move, isx, lims, model, text = "", positions = Point2f0[], offsets=Vec2f0[])
|
pointsize(font) = font.pointsize * 2
|
||||||
sz = axis[:tickfont].pointsize
|
|
||||||
rscale2 = Vec2f0(3/sz)
|
function draw_ticks(
|
||||||
m = Reactive.value(model)
|
axis, ticks, isx, lims, m, text = "",
|
||||||
xs, ys = m[1,1], m[2,2]
|
positions = Point2f0[], offsets=Vec2f0[]
|
||||||
rscale = rscale2 ./ Vec2f0(xs, ys)
|
)
|
||||||
|
sz = pointsize(axis[:tickfont])
|
||||||
atlas = GLVisualize.get_texture_atlas()
|
atlas = GLVisualize.get_texture_atlas()
|
||||||
font = GLVisualize.DEFAULT_FONT_FACE
|
font = GLVisualize.defaultfont()
|
||||||
if !(ticks in (nothing, false))
|
|
||||||
# x labels
|
flip = axis[:flip]; mirror = axis[:mirror]
|
||||||
flip = axis[:flip]
|
|
||||||
|
align = if isx
|
||||||
|
mirror ? :bottom : :top
|
||||||
|
else
|
||||||
|
mirror ? :left : :right
|
||||||
|
end
|
||||||
|
axis_gap = Point2f0(isx ? 0 : sz / 2, isx ? sz / 2 : 0)
|
||||||
for (cv, dv) in zip(ticks...)
|
for (cv, dv) in zip(ticks...)
|
||||||
x,y = cv, (flip ? lims[2] : lims[1])
|
|
||||||
startpos = Point2f0(isx ? (x,y) : (y,x))-move
|
x, y = cv, lims[1]
|
||||||
# @show cv dv ymin xi yi
|
xy = isx ? (x, y) : (y, x)
|
||||||
|
_pos = m * GeometryTypes.Vec4f0(xy[1], xy[2], 0, 1)
|
||||||
|
startpos = Point2f0(_pos[1], _pos[2]) - axis_gap
|
||||||
str = string(dv)
|
str = string(dv)
|
||||||
position = GLVisualize.calc_position(str, startpos, rscale, font, atlas)
|
# need to tag a new UnicodeFun version for this... also the numbers become
|
||||||
offset = GLVisualize.calc_offset(str, rscale2, font, atlas)
|
# so small that it looks terrible -.-
|
||||||
alignoff = align_offset(startpos, last(position), atlas, rscale, font, align)
|
# _str = split(string(dv), "^")
|
||||||
|
# if length(_str) == 2
|
||||||
|
# _str[2] = UnicodeFun.to_superscript(_str[2])
|
||||||
|
# end
|
||||||
|
# str = join(_str, "")
|
||||||
|
position = GLVisualize.calc_position(str, startpos, sz, font, atlas)
|
||||||
|
offset = GLVisualize.calc_offset(str, sz, font, atlas)
|
||||||
|
alignoff = align_offset(startpos, last(position), atlas, sz, font, align)
|
||||||
map!(position) do pos
|
map!(position) do pos
|
||||||
pos .+ alignoff
|
pos .+ alignoff
|
||||||
end
|
end
|
||||||
append!(positions, position)
|
append!(positions, position)
|
||||||
append!(offsets, offset)
|
append!(offsets, offset)
|
||||||
text *= str
|
text *= str
|
||||||
end
|
|
||||||
end
|
end
|
||||||
text, positions, offsets
|
text, positions, offsets
|
||||||
end
|
end
|
||||||
|
|
||||||
function text(position, text, kw_args)
|
function text(position, text, kw_args)
|
||||||
text_align = alignment2num(text.font)
|
text_align = alignment2num(text.font)
|
||||||
startpos = Vec2f0(position)
|
startpos = Vec2f0(position)
|
||||||
atlas = GLVisualize.get_texture_atlas()
|
atlas = GLVisualize.get_texture_atlas()
|
||||||
font = GLVisualize.DEFAULT_FONT_FACE
|
font = GLVisualize.defaultfont()
|
||||||
rscale = kw_args[:relative_scale]
|
rscale = kw_args[:relative_scale]
|
||||||
m = Reactive.value(kw_args[:model])
|
|
||||||
position = GLVisualize.calc_position(text.str, startpos, rscale, font, atlas)
|
position = GLVisualize.calc_position(text.str, startpos, rscale, font, atlas)
|
||||||
offset = GLVisualize.calc_offset(text.str, rscale, font, atlas)
|
offset = GLVisualize.calc_offset(text.str, rscale, font, atlas)
|
||||||
alignoff = align_offset(startpos, last(position), atlas, rscale, font, text_align)
|
alignoff = align_offset(startpos, last(position), atlas, rscale, font, text_align)
|
||||||
|
|
||||||
map!(position) do pos
|
map!(position) do pos
|
||||||
pos .+ alignoff
|
pos .+ alignoff
|
||||||
end
|
end
|
||||||
@ -646,28 +745,32 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
|||||||
|
|
||||||
xlim = Plots.axis_limits(xaxis)
|
xlim = Plots.axis_limits(xaxis)
|
||||||
ylim = Plots.axis_limits(yaxis)
|
ylim = Plots.axis_limits(yaxis)
|
||||||
m = Reactive.value(model)
|
|
||||||
xs, ys = m[1,1], m[2,2]
|
if !(xaxis[:ticks] in (nothing, false, :none))
|
||||||
# TODO: we should make sure we actually need to draw these...
|
ticklabels = map(model) do m
|
||||||
t, positions, offsets = draw_ticks(xaxis, xticks, :top, Point2f0(0, 7/ys), true, ylim, model)
|
mirror = xaxis[:mirror]
|
||||||
t, positions, offsets = draw_ticks(yaxis, yticks, :right, Point2f0(7/xs, 0), false, xlim, model, t, positions, offsets)
|
t, positions, offsets = draw_ticks(xaxis, xticks, true, ylim, m)
|
||||||
sz = xaxis[:tickfont].pointsize
|
mirror = xaxis[:mirror]
|
||||||
|
t, positions, offsets = draw_ticks(
|
||||||
|
yaxis, yticks, false, xlim, m,
|
||||||
|
t, positions, offsets
|
||||||
|
)
|
||||||
|
end
|
||||||
kw_args = Dict{Symbol, Any}(
|
kw_args = Dict{Symbol, Any}(
|
||||||
:position => positions,
|
:position => map(x-> x[2], ticklabels),
|
||||||
:offset => offsets,
|
:offset => map(last, ticklabels),
|
||||||
:color => fcolor,
|
:color => fcolor,
|
||||||
:relative_scale => Vec2f0(3/sz),
|
:relative_scale => pointsize(xaxis[:tickfont]),
|
||||||
:model => model,
|
|
||||||
:scale_primitive => false
|
:scale_primitive => false
|
||||||
)
|
)
|
||||||
if !(xaxis[:ticks] in (nothing,false,:none))
|
push!(axis_vis, visualize(map(first, ticklabels), Style(:default), kw_args))
|
||||||
push!(axis_vis, visualize(t, Style(:default), kw_args))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
area_w = GeometryTypes.widths(area)
|
area_w = GeometryTypes.widths(area)
|
||||||
if sp[:title] != ""
|
if sp[:title] != ""
|
||||||
tf = sp[:titlefont]; color = gl_color(sp[:foreground_color_title])
|
tf = sp[:titlefont]; color = gl_color(sp[:foreground_color_title])
|
||||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, tf.rotation, color)
|
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, tf.rotation, color)
|
||||||
xy = Point2f0(area.w/2, area_w[2])
|
xy = Point2f0(area.w/2, area_w[2] + pointsize(tf)/2)
|
||||||
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
|
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
|
||||||
extract_font(font, kw)
|
extract_font(font, kw)
|
||||||
t = PlotText(sp[:title], font)
|
t = PlotText(sp[:title], font)
|
||||||
@ -675,7 +778,7 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
|||||||
end
|
end
|
||||||
if xaxis[:guide] != ""
|
if xaxis[:guide] != ""
|
||||||
tf = xaxis[:guidefont]; color = gl_color(xaxis[:foreground_color_guide])
|
tf = xaxis[:guidefont]; color = gl_color(xaxis[:foreground_color_guide])
|
||||||
xy = Point2f0(area.w/2, 0)
|
xy = Point2f0(area.w/2, - pointsize(tf)/2)
|
||||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :bottom, tf.rotation, color)
|
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :bottom, tf.rotation, color)
|
||||||
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
|
kw = Dict(:model => text_model(font, xy), :scale_primitive => true)
|
||||||
t = PlotText(xaxis[:guide], font)
|
t = PlotText(xaxis[:guide], font)
|
||||||
@ -686,7 +789,7 @@ function gl_draw_axes_2d(sp::Plots.Subplot{Plots.GLVisualizeBackend}, model, are
|
|||||||
if yaxis[:guide] != ""
|
if yaxis[:guide] != ""
|
||||||
tf = yaxis[:guidefont]; color = gl_color(yaxis[:foreground_color_guide])
|
tf = yaxis[:guidefont]; color = gl_color(yaxis[:foreground_color_guide])
|
||||||
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, 90f0, color)
|
font = Plots.Font(tf.family, tf.pointsize, :hcenter, :top, 90f0, color)
|
||||||
xy = Point2f0(0, area.h/2)
|
xy = Point2f0(-pointsize(tf)/2, area.h/2)
|
||||||
kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
|
kw = Dict(:model => text_model(font, xy), :scale_primitive=>true)
|
||||||
t = PlotText(yaxis[:guide], font)
|
t = PlotText(yaxis[:guide], font)
|
||||||
extract_font(font, kw)
|
extract_font(font, kw)
|
||||||
@ -854,8 +957,8 @@ function gl_viewport(bb, rect)
|
|||||||
l, b, bw, bh = bb
|
l, b, bw, bh = bb
|
||||||
rw, rh = rect.w, rect.h
|
rw, rh = rect.w, rect.h
|
||||||
GLVisualize.SimpleRectangle(
|
GLVisualize.SimpleRectangle(
|
||||||
round(Int, rect.x + rw * l),
|
round(Int, rw * l),
|
||||||
round(Int, rect.y + rh * b),
|
round(Int, rh * b),
|
||||||
round(Int, rw * bw),
|
round(Int, rw * bw),
|
||||||
round(Int, rh * bh)
|
round(Int, rh * bh)
|
||||||
)
|
)
|
||||||
@ -881,27 +984,58 @@ end
|
|||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
function _display(plt::Plot{GLVisualizeBackend})
|
|
||||||
screen = plt.o
|
function scale_for_annotations!(series::Series, scaletype::Symbol = :pixels)
|
||||||
empty_screen!(screen)
|
anns = series[:series_annotations]
|
||||||
|
if anns != nothing && !isnull(anns.baseshape)
|
||||||
|
# we use baseshape to overwrite the markershape attribute
|
||||||
|
# with a list of custom shapes for each
|
||||||
|
msw, msh = anns.scalefactor
|
||||||
|
offsets = Array(Vec2f0, length(anns.strs))
|
||||||
|
series[:markersize] = map(1:length(anns.strs)) do i
|
||||||
|
str = cycle(anns.strs, i)
|
||||||
|
# get the width and height of the string (in mm)
|
||||||
|
sw, sh = text_size(str, anns.font.pointsize)
|
||||||
|
|
||||||
|
# how much to scale the base shape?
|
||||||
|
# note: it's a rough assumption that the shape fills the unit box [-1,-1,1,1],
|
||||||
|
# so we scale the length-2 shape by 1/2 the total length
|
||||||
|
xscale = 0.5to_pixels(sw) * 1.8
|
||||||
|
yscale = 0.5to_pixels(sh) * 1.8
|
||||||
|
|
||||||
|
# we save the size of the larger direction to the markersize list,
|
||||||
|
# and then re-scale a copy of baseshape to match the w/h ratio
|
||||||
|
s = Vec2f0(xscale, yscale)
|
||||||
|
offsets[i] = -s
|
||||||
|
s
|
||||||
|
end
|
||||||
|
series[:offset] = offsets
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function _display(plt::Plot{GLVisualizeBackend}, visible = true)
|
||||||
|
screen = create_window(plt, visible)
|
||||||
sw, sh = plt[:size]
|
sw, sh = plt[:size]
|
||||||
sw, sh = sw*px, sh*px
|
sw, sh = sw*px, sh*px
|
||||||
# TODO: use plt.subplots... plt.spmap can't be trusted
|
|
||||||
for (name, sp) in plt.spmap
|
for sp in plt.subplots
|
||||||
_3d = Plots.is3d(sp)
|
_3d = Plots.is3d(sp)
|
||||||
# camera = :perspective
|
# camera = :perspective
|
||||||
# initialize the sub-screen for this subplot
|
# initialize the sub-screen for this subplot
|
||||||
# note: we create a lift function to update the size on resize
|
|
||||||
|
|
||||||
rel_bbox = Plots.bbox_to_pcts(bbox(sp), sw, sh)
|
rel_bbox = Plots.bbox_to_pcts(bbox(sp), sw, sh)
|
||||||
sub_area = map(screen.area) do rect
|
sub_area = map(screen.area) do rect
|
||||||
Plots.gl_viewport(rel_bbox, rect)
|
Plots.gl_viewport(rel_bbox, rect)
|
||||||
end
|
end
|
||||||
c = plt[:background_color_outside]
|
c = plt[:background_color_outside]
|
||||||
sp_screen = GLVisualize.Screen(
|
sp_screen = GLVisualize.Screen(
|
||||||
screen, name = name, color = c,
|
screen, color = c,
|
||||||
area = sub_area
|
area = sub_area
|
||||||
)
|
)
|
||||||
|
sp.o = sp_screen
|
||||||
cam = get!(sp_screen.cameras, :perspective) do
|
cam = get!(sp_screen.cameras, :perspective) do
|
||||||
inside = sp_screen.inputs[:mouseinside]
|
inside = sp_screen.inputs[:mouseinside]
|
||||||
theta = _3d ? nothing : Signal(Vec3f0(0)) # surpress rotation for 2D (nothing will get usual rotation controle)
|
theta = _3d ? nothing : Signal(Vec3f0(0)) # surpress rotation for 2D (nothing will get usual rotation controle)
|
||||||
@ -911,41 +1045,38 @@ function _display(plt::Plot{GLVisualizeBackend})
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
sp.o = sp_screen
|
|
||||||
rel_plotarea = Plots.bbox_to_pcts(plotarea(sp), sw, sh)
|
rel_plotarea = Plots.bbox_to_pcts(plotarea(sp), sw, sh)
|
||||||
model_m = map(Plots.to_modelmatrix, screen.area, sub_area, Signal(rel_plotarea), Signal(sp))
|
model_m = map(Plots.to_modelmatrix,
|
||||||
for ann in sp[:annotations]
|
screen.area, sub_area,
|
||||||
x, y, plot_text = ann
|
Signal(rel_plotarea), Signal(sp)
|
||||||
txt_args = Dict{Symbol, Any}(:model => eye(GeometryTypes.Mat4f0))
|
)
|
||||||
x, y, _1, _1 = Reactive.value(model_m) * Vec{4,Float32}(x, y, 0, 1)
|
|
||||||
extract_font(plot_text.font, txt_args)
|
|
||||||
t = text(Point2f0(x, y), plot_text, txt_args)
|
|
||||||
GLVisualize._view(t, sp_screen, camera=cam)
|
|
||||||
end
|
|
||||||
# loop over the series and add them to the subplot
|
# loop over the series and add them to the subplot
|
||||||
if !_3d
|
if !_3d
|
||||||
axis = gl_draw_axes_2d(sp, model_m, Reactive.value(sub_area))
|
axis = gl_draw_axes_2d(sp, model_m, Reactive.value(sub_area))
|
||||||
GLVisualize._view(axis, sp_screen, camera=cam)
|
GLVisualize._view(axis, sp_screen, camera=:perspective)
|
||||||
cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
|
cam.projectiontype.value = GLVisualize.ORTHOGRAPHIC
|
||||||
Reactive.run_till_now() # make sure Reactive.push! arrives
|
Reactive.run_till_now() # make sure Reactive.push! arrives
|
||||||
GLAbstraction.center!(cam,
|
GLAbstraction.center!(cam,
|
||||||
GeometryTypes.AABB(
|
GeometryTypes.AABB(
|
||||||
Vec3f0(-10), Vec3f0((GeometryTypes.widths(sp_screen)+20f0)..., 1)
|
Vec3f0(-20), Vec3f0((GeometryTypes.widths(sp_screen)+40f0)..., 1)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
else
|
else
|
||||||
axis = gl_draw_axes_3d(sp, model_m)
|
axis = gl_draw_axes_3d(sp, model_m)
|
||||||
GLVisualize._view(axis, sp_screen, camera=cam)
|
GLVisualize._view(axis, sp_screen, camera=:perspective)
|
||||||
push!(cam.projectiontype, GLVisualize.PERSPECTIVE)
|
push!(cam.projectiontype, GLVisualize.PERSPECTIVE)
|
||||||
end
|
end
|
||||||
for series in Plots.series_list(sp)
|
for series in Plots.series_list(sp)
|
||||||
|
|
||||||
d = series.d
|
d = series.d
|
||||||
st = d[:seriestype]; kw_args = KW() # exctract kw
|
st = d[:seriestype]; kw_args = KW() # exctract kw
|
||||||
|
|
||||||
kw_args[:model] = model_m # add transformation
|
kw_args[:model] = model_m # add transformation
|
||||||
if !_3d # 3D is treated differently, since we need boundingboxes for camera
|
if !_3d # 3D is treated differently, since we need boundingboxes for camera
|
||||||
kw_args[:boundingbox] = nothing # don't calculate bb, we dont need it
|
kw_args[:boundingbox] = nothing # don't calculate bb, we dont need it
|
||||||
end
|
end
|
||||||
|
scale_for_annotations!(series)
|
||||||
if st in (:surface, :wireframe)
|
if st in (:surface, :wireframe)
|
||||||
x, y, z = extract_surface(d)
|
x, y, z = extract_surface(d)
|
||||||
extract_gradient(d, kw_args, :fill)
|
extract_gradient(d, kw_args, :fill)
|
||||||
@ -1026,37 +1157,47 @@ function _display(plt::Plot{GLVisualizeBackend})
|
|||||||
else
|
else
|
||||||
error("failed to display plot type $st")
|
error("failed to display plot type $st")
|
||||||
end
|
end
|
||||||
if isa(vis, Array) && isempty(vis)
|
|
||||||
continue # nothing to see here
|
isa(vis, Array) && isempty(vis) && continue # nothing to see here
|
||||||
end
|
|
||||||
GLVisualize._view(vis, sp_screen, camera=cam)
|
GLVisualize._view(vis, sp_screen, camera=:perspective)
|
||||||
if haskey(d, :hover) && !(d[:hover] in (false, :none, nothing))
|
if haskey(d, :hover) && !(d[:hover] in (false, :none, nothing))
|
||||||
hover(vis, d[:hover], sp_screen)
|
hover(vis, d[:hover], sp_screen)
|
||||||
end
|
end
|
||||||
if isdefined(:GLPlot) && isdefined(Main.GLPlot, :(register_plot!))
|
if isdefined(:GLPlot) && isdefined(Main.GLPlot, :(register_plot!))
|
||||||
del_signal = Main.GLPlot.register_plot!(vis, sp_screen)
|
del_signal = Main.GLPlot.register_plot!(vis, sp_screen, create_gizmo=false)
|
||||||
append!(_glplot_deletes, del_signal)
|
append!(_glplot_deletes, del_signal)
|
||||||
end
|
end
|
||||||
|
anns = series[:series_annotations]
|
||||||
|
for (x, y, str, font) in EachAnn(anns, d[:x], d[:y])
|
||||||
|
txt_args = Dict{Symbol, Any}(:model => eye(GLAbstraction.Mat4f0))
|
||||||
|
x, y = Reactive.value(model_m) * Vec{4, Float32}(x, y, 0, 1)
|
||||||
|
extract_font(font, txt_args)
|
||||||
|
t = text(Point2f0(x, y), PlotText(str, font), txt_args)
|
||||||
|
GLVisualize._view(t, sp_screen, camera = :perspective)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
generate_legend(sp, sp_screen, model_m)
|
||||||
if _3d
|
if _3d
|
||||||
GLAbstraction.center!(sp_screen)
|
GLAbstraction.center!(sp_screen)
|
||||||
end
|
end
|
||||||
end
|
|
||||||
Reactive.post_empty()
|
Reactive.post_empty()
|
||||||
yield()
|
yield()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function _show(io::IO, ::MIME"image/png", plt::Plot{GLVisualizeBackend})
|
function _show(io::IO, ::MIME"image/png", plt::Plot{GLVisualizeBackend})
|
||||||
_display(plt)
|
_display(plt, false)
|
||||||
GLWindow.pollevents()
|
GLWindow.poll_glfw()
|
||||||
yield()
|
|
||||||
if Base.n_avail(Reactive._messages) > 0
|
if Base.n_avail(Reactive._messages) > 0
|
||||||
Reactive.run_till_now()
|
Reactive.run_till_now()
|
||||||
end
|
end
|
||||||
GLWindow.render_frame(plt.o)
|
yield()
|
||||||
|
GLWindow.render_frame(GLWindow.rootscreen(plt.o))
|
||||||
GLWindow.swapbuffers(plt.o)
|
GLWindow.swapbuffers(plt.o)
|
||||||
buff = GLWindow.screenbuffer(plt.o)
|
buff = GLWindow.screenbuffer(plt.o)
|
||||||
png = Images.Image(buff,
|
png = Images.Image(map(RGB{U8}, buff),
|
||||||
colorspace = "sRGB",
|
colorspace = "sRGB",
|
||||||
spatialorder = ["y", "x"]
|
spatialorder = ["y", "x"]
|
||||||
)
|
)
|
||||||
@ -1106,6 +1247,7 @@ function gl_lines(points, kw_args)
|
|||||||
end
|
end
|
||||||
return result
|
return result
|
||||||
end
|
end
|
||||||
|
|
||||||
function gl_shape(d, kw_args)
|
function gl_shape(d, kw_args)
|
||||||
points = Plots.extract_points(d)
|
points = Plots.extract_points(d)
|
||||||
result = []
|
result = []
|
||||||
@ -1116,21 +1258,25 @@ function gl_shape(d, kw_args)
|
|||||||
end
|
end
|
||||||
result
|
result
|
||||||
end
|
end
|
||||||
tovec2(x::FixedSizeArrays.Vec{2, Float32}) = x
|
|
||||||
tovec2(x::AbstractVector) = map(tovec2, x)
|
|
||||||
tovec2(x::FixedSizeArrays.Vec) = Vec2f0(x[1], x[2])
|
|
||||||
|
|
||||||
|
|
||||||
function gl_scatter(points, kw_args)
|
function gl_scatter(points, kw_args)
|
||||||
prim = get(kw_args, :primitive, GeometryTypes.Circle)
|
prim = get(kw_args, :primitive, GeometryTypes.Circle)
|
||||||
if isa(prim, GLNormalMesh)
|
if isa(prim, GLNormalMesh)
|
||||||
kw_args[:scale] = map(kw_args[:model]) do m
|
if haskey(kw_args, :model)
|
||||||
s = m[1,1], m[2,2], m[3,3]
|
p = get(kw_args, :perspective, eye(GeometryTypes.Mat4f0))
|
||||||
1f0./Vec3f0(s)
|
kw_args[:scale] = GLAbstraction.const_lift(kw_args[:model], kw_args[:scale], p) do m, sc, p
|
||||||
|
s = Vec3f0(m[1,1], m[2,2], m[3,3])
|
||||||
|
ps = Vec3f0(p[1,1], p[2,2], p[3,3])
|
||||||
|
r = sc ./ (s .* ps)
|
||||||
|
r
|
||||||
|
end
|
||||||
end
|
end
|
||||||
else # 2D prim
|
else # 2D prim
|
||||||
kw_args[:scale] = tovec2(kw_args[:scale])
|
kw_args[:scale] = to_vec(Vec2f0, kw_args[:scale])
|
||||||
end
|
end
|
||||||
|
|
||||||
if haskey(kw_args, :stroke_width)
|
if haskey(kw_args, :stroke_width)
|
||||||
s = Reactive.value(kw_args[:scale])
|
s = Reactive.value(kw_args[:scale])
|
||||||
sw = kw_args[:stroke_width]
|
sw = kw_args[:stroke_width]
|
||||||
@ -1139,6 +1285,15 @@ function gl_scatter(points, kw_args)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
kw_args[:scale_primitive] = false
|
kw_args[:scale_primitive] = false
|
||||||
|
if isa(prim, String)
|
||||||
|
kw_args[:position] = points
|
||||||
|
if !isa(kw_args[:scale], Vector) # if not vector, we can assume it's relative scale
|
||||||
|
kw_args[:relative_scale] = kw_args[:scale]
|
||||||
|
delete!(kw_args, :scale)
|
||||||
|
end
|
||||||
|
return visualize(prim, Style(:default), kw_args)
|
||||||
|
end
|
||||||
|
|
||||||
visualize((prim, points), Style(:default), kw_args)
|
visualize((prim, points), Style(:default), kw_args)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1158,6 +1313,9 @@ function gl_poly(points, kw_args)
|
|||||||
result
|
result
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function gl_surface(x,y,z, kw_args)
|
function gl_surface(x,y,z, kw_args)
|
||||||
if isa(x, Range) && isa(y, Range)
|
if isa(x, Range) && isa(y, Range)
|
||||||
main = z
|
main = z
|
||||||
@ -1177,8 +1335,10 @@ function gl_surface(x,y,z, kw_args)
|
|||||||
faces = Cuint[]
|
faces = Cuint[]
|
||||||
idx = (i,j) -> sub2ind(size(z), i, j) - 1
|
idx = (i,j) -> sub2ind(size(z), i, j) - 1
|
||||||
for i=1:size(z,1), j=1:size(z,2)
|
for i=1:size(z,1), j=1:size(z,2)
|
||||||
|
|
||||||
i < size(z,1) && push!(faces, idx(i, j), idx(i+1, j))
|
i < size(z,1) && push!(faces, idx(i, j), idx(i+1, j))
|
||||||
j < size(z,2) && push!(faces, idx(i, j), idx(i, j+1))
|
j < size(z,2) && push!(faces, idx(i, j), idx(i, j+1))
|
||||||
|
|
||||||
end
|
end
|
||||||
color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1))
|
color = get(kw_args, :stroke_color, RGBA{Float32}(0,0,0,1))
|
||||||
kw_args[:color] = color
|
kw_args[:color] = color
|
||||||
@ -1196,13 +1356,16 @@ end
|
|||||||
|
|
||||||
function gl_contour(x, y, z, kw_args)
|
function gl_contour(x, y, z, kw_args)
|
||||||
if kw_args[:fillrange] != nothing
|
if kw_args[:fillrange] != nothing
|
||||||
|
|
||||||
delete!(kw_args, :intensity)
|
delete!(kw_args, :intensity)
|
||||||
I = GLVisualize.Intensity{1, Float32}
|
I = GLVisualize.Intensity{1, Float32}
|
||||||
main = I[z[j,i] for i=1:size(z, 2), j=1:size(z, 1)]
|
main = I[z[j,i] for i=1:size(z, 2), j=1:size(z, 1)]
|
||||||
return visualize(main, Style(:default), kw_args)
|
return visualize(main, Style(:default), kw_args)
|
||||||
|
|
||||||
else
|
else
|
||||||
h = kw_args[:levels]
|
h = kw_args[:levels]
|
||||||
levels = Contour.contours(x, y, z, h)
|
T = eltype(z)
|
||||||
|
levels = Contour.contours(map(T, x), map(T, y), z, h)
|
||||||
result = Point2f0[]
|
result = Point2f0[]
|
||||||
zmin, zmax = get(kw_args, :limits, Vec2f0(extrema(z)))
|
zmin, zmax = get(kw_args, :limits, Vec2f0(extrema(z)))
|
||||||
cmap = get(kw_args, :color_map, get(kw_args, :color, RGBA{Float32}(0,0,0,1)))
|
cmap = get(kw_args, :color_map, get(kw_args, :color, RGBA{Float32}(0,0,0,1)))
|
||||||
@ -1218,6 +1381,7 @@ function gl_contour(x,y,z, kw_args)
|
|||||||
kw_args[:color] = colors
|
kw_args[:color] = colors
|
||||||
kw_args[:color_map] = nothing
|
kw_args[:color_map] = nothing
|
||||||
kw_args[:color_norm] = nothing
|
kw_args[:color_norm] = nothing
|
||||||
|
kw_args[:intensity] = nothing
|
||||||
return visualize(result, Style(:lines),kw_args)
|
return visualize(result, Style(:lines),kw_args)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -1238,20 +1402,120 @@ end
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
function text_plot(text, alignment, kw_args)
|
"""
|
||||||
transmat = kw_args[:model]
|
Ugh, so much special casing (╯°□°)╯︵ ┻━┻
|
||||||
obj = visualize(text, Style(:default), kw_args)
|
"""
|
||||||
bb = value(GLAbstraction.boundingbox(obj))
|
function label_scatter(d, w, ho)
|
||||||
w,h,_ = widths(bb)
|
kw = KW()
|
||||||
x,y,_ = minimum(bb)
|
extract_stroke(d, kw)
|
||||||
pivot = origin(alignment)
|
extract_marker(d, kw)
|
||||||
pos = pivot - (Point2f0(x, y) .* widths(alignment))
|
kw[:scale] = Vec2f0(w/2)
|
||||||
if kw_args[:rotation] != 0.0
|
kw[:offset] = Vec2f0(-w/4)
|
||||||
rot = GLAbstraction.rotationmatrix_z(Float32(font.rotation))
|
if haskey(kw, :intensity)
|
||||||
transmat *= translationmatrix(pivot)*rot*translationmatrix(-pivot)
|
cmap = kw[:color_map]
|
||||||
|
norm = kw[:color_norm]
|
||||||
|
kw[:color] = GLVisualize.color_lookup(cmap, kw[:intensity][1], norm)
|
||||||
|
delete!(kw, :intensity)
|
||||||
|
delete!(kw, :color_map)
|
||||||
|
delete!(kw, :color_norm)
|
||||||
|
else
|
||||||
|
color = get(kw, :color, nothing)
|
||||||
|
kw[:color] = isa(color, Array) ? first(color) : color
|
||||||
|
end
|
||||||
|
p = get(kw, :primitive, GeometryTypes.Circle)
|
||||||
|
if isa(p, GLNormalMesh)
|
||||||
|
bb = GeometryTypes.AABB{Float32}(GeometryTypes.vertices(p))
|
||||||
|
bbw = GeometryTypes.widths(bb)
|
||||||
|
if isapprox(bbw[3], 0)
|
||||||
|
bbw = Vec3f0(bbw[1], bbw[2], 1)
|
||||||
|
end
|
||||||
|
mini = minimum(bb)
|
||||||
|
m = GLAbstraction.translationmatrix(-mini)
|
||||||
|
m *= GLAbstraction.scalematrix(1 ./ bbw)
|
||||||
|
kw[:primitive] = m * p
|
||||||
|
kw[:scale] = Vec3f0(w/2)
|
||||||
|
delete!(kw, :offset)
|
||||||
|
end
|
||||||
|
GL.gl_scatter(Point2f0[(w/2, ho)], kw)
|
||||||
end
|
end
|
||||||
|
|
||||||
transmat *= GLAbstraction.translationmatrix(Vec3f0(pos..., 0))
|
|
||||||
GLAbstraction.transformation(obj, transmat)
|
function make_label(sp, series, i)
|
||||||
view(obj, img.screen, camera=:orthographic_pixel)
|
GL = Plots
|
||||||
|
w, gap, ho = 20f0, 5f0, 5
|
||||||
|
result = []
|
||||||
|
d = series.d
|
||||||
|
st = d[:seriestype]
|
||||||
|
kw_args = KW()
|
||||||
|
if (st in (:path, :path3d)) && d[:linewidth] > 0
|
||||||
|
points = Point2f0[(0, ho), (w, ho)]
|
||||||
|
kw = KW()
|
||||||
|
extract_linestyle(d, kw)
|
||||||
|
append!(result, GL.gl_lines(points, kw))
|
||||||
|
if d[:markershape] != :none
|
||||||
|
push!(result, label_scatter(d, w, ho))
|
||||||
|
end
|
||||||
|
elseif st in (:scatter, :scatter3d) #|| d[:markershape] != :none
|
||||||
|
push!(result, label_scatter(d, w, ho))
|
||||||
|
else
|
||||||
|
extract_c(d, kw_args, :fill)
|
||||||
|
if isa(kw_args[:color], AbstractVector)
|
||||||
|
kw_args[:color] = first(kw_args[:color])
|
||||||
|
end
|
||||||
|
push!(result, visualize(
|
||||||
|
GeometryTypes.SimpleRectangle(-w/2, ho-w/4, w/2, w/2),
|
||||||
|
Style(:default), kw_args
|
||||||
|
))
|
||||||
|
end
|
||||||
|
labeltext = if isa(series[:label], Array)
|
||||||
|
i += 1
|
||||||
|
series[:label][i]
|
||||||
|
else
|
||||||
|
series[:label]
|
||||||
|
end
|
||||||
|
color = sp[:foreground_color_legend]
|
||||||
|
ft = sp[:legendfont]
|
||||||
|
font = Plots.Font(ft.family, ft.pointsize, :left, :bottom, 0.0, color)
|
||||||
|
xy = Point2f0(w+gap, 0.0)
|
||||||
|
kw = Dict(:model => text_model(font, xy), :scale_primitive=>false)
|
||||||
|
extract_font(font, kw)
|
||||||
|
t = PlotText(labeltext, font)
|
||||||
|
push!(result, text(xy, t, kw))
|
||||||
|
GLAbstraction.Context(result...), i
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function generate_legend(sp, screen, model_m)
|
||||||
|
legend = GLAbstraction.Context[]
|
||||||
|
if sp[:legend] != :none
|
||||||
|
i = 0
|
||||||
|
for series in series_list(sp)
|
||||||
|
should_add_to_legend(series) || continue
|
||||||
|
result, i = make_label(sp, series, i)
|
||||||
|
push!(legend, result)
|
||||||
|
end
|
||||||
|
if isempty(legend)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
list = visualize(legend, gap=Vec3f0(0,5,0))
|
||||||
|
bb = GLAbstraction._boundingbox(list)
|
||||||
|
wx,wy,_ = GeometryTypes.widths(bb)
|
||||||
|
xmin, _ = Plots.axis_limits(sp[:xaxis])
|
||||||
|
_, ymax = Plots.axis_limits(sp[:yaxis])
|
||||||
|
area = map(model_m) do m
|
||||||
|
p = m * GeometryTypes.Vec4f0(xmin, ymax, 0, 1)
|
||||||
|
h = round(Int, wy)+20
|
||||||
|
w = round(Int, wx)+20
|
||||||
|
x,y = round(Int, p[1])+30, round(Int, p[2]-h)-30
|
||||||
|
GeometryTypes.SimpleRectangle(x, y, w, h)
|
||||||
|
end
|
||||||
|
sscren = GLWindow.Screen(
|
||||||
|
screen, area = area,
|
||||||
|
color = sp[:background_color_legend],
|
||||||
|
stroke = (2f0, RGBA(0.3, 0.3, 0.3, 0.9))
|
||||||
|
)
|
||||||
|
GLAbstraction.translate!(list, Vec3f0(10,10,0))
|
||||||
|
GLVisualize._view(list, sscren, camera=:fixed_pixel)
|
||||||
|
end
|
||||||
|
return
|
||||||
end
|
end
|
||||||
|
|||||||
@ -30,6 +30,7 @@ const _gr_attr = merge_with_base_supported([
|
|||||||
:normalize, :weights,
|
:normalize, :weights,
|
||||||
:inset_subplots,
|
:inset_subplots,
|
||||||
:bar_width,
|
:bar_width,
|
||||||
|
:arrow,
|
||||||
])
|
])
|
||||||
const _gr_seriestype = [
|
const _gr_seriestype = [
|
||||||
:path, :scatter,
|
:path, :scatter,
|
||||||
@ -131,7 +132,7 @@ gr_set_textcolor(c) = GR.settextcolorind(gr_getcolorind(cycle(c,1)))
|
|||||||
|
|
||||||
# draw line segments, splitting x/y into contiguous/finite segments
|
# draw line segments, splitting x/y into contiguous/finite segments
|
||||||
# note: this can be used for shapes by passing func `GR.fillarea`
|
# note: this can be used for shapes by passing func `GR.fillarea`
|
||||||
function gr_polyline(x, y, func = GR.polyline)
|
function gr_polyline(x, y, func = GR.polyline; arrowside=:none)
|
||||||
iend = 0
|
iend = 0
|
||||||
n = length(x)
|
n = length(x)
|
||||||
while iend < n-1
|
while iend < n-1
|
||||||
@ -159,6 +160,12 @@ function gr_polyline(x, y, func = GR.polyline)
|
|||||||
# if we found a start and end, draw the line segment, otherwise we're done
|
# if we found a start and end, draw the line segment, otherwise we're done
|
||||||
if istart > 0 && iend > 0
|
if istart > 0 && iend > 0
|
||||||
func(x[istart:iend], y[istart:iend])
|
func(x[istart:iend], y[istart:iend])
|
||||||
|
if arrowside in (:head,:both)
|
||||||
|
GR.drawarrow(x[iend-1], y[iend-1], x[iend], y[iend])
|
||||||
|
end
|
||||||
|
if arrowside in (:tail,:both)
|
||||||
|
GR.drawarrow(x[istart+1], y[istart+1], x[istart], y[istart])
|
||||||
|
end
|
||||||
else
|
else
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
@ -263,11 +270,13 @@ end
|
|||||||
|
|
||||||
# draw ONE Shape
|
# draw ONE Shape
|
||||||
function gr_draw_marker(xi, yi, msize, shape::Shape)
|
function gr_draw_marker(xi, yi, msize, shape::Shape)
|
||||||
sx, sy = shape_coords(shape)
|
sx, sy = coords(shape)
|
||||||
|
# convert to ndc coords (percentages of window)
|
||||||
GR.selntran(0)
|
GR.selntran(0)
|
||||||
xi, yi = GR.wctondc(xi, yi)
|
xi, yi = GR.wctondc(xi, yi)
|
||||||
GR.fillarea(xi + sx * 0.0015msize,
|
ms_ndc_x, ms_ndc_y = gr_pixels_to_ndc(msize, msize)
|
||||||
yi + sy * 0.0015msize)
|
GR.fillarea(xi .+ sx .* ms_ndc_x,
|
||||||
|
yi .+ sy .* ms_ndc_y)
|
||||||
GR.selntran(1)
|
GR.selntran(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -281,10 +290,11 @@ end
|
|||||||
|
|
||||||
# draw the markers, one at a time
|
# draw the markers, one at a time
|
||||||
function gr_draw_markers(series::Series, x, y, msize, mz)
|
function gr_draw_markers(series::Series, x, y, msize, mz)
|
||||||
shape = series[:markershape]
|
shapes = series[:markershape]
|
||||||
if shape != :none
|
if shapes != :none
|
||||||
for i=1:length(x)
|
for i=1:length(x)
|
||||||
msi = cycle(msize, i)
|
msi = cycle(msize, i)
|
||||||
|
shape = cycle(shapes, i)
|
||||||
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
|
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
|
||||||
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
|
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
|
||||||
|
|
||||||
@ -349,6 +359,14 @@ function gr_set_font(f::Font; halign = f.halign, valign = f.valign,
|
|||||||
GR.settextalign(gr_halign[halign], gr_valign[valign])
|
GR.settextalign(gr_halign[halign], gr_valign[valign])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function gr_nans_to_infs!(z)
|
||||||
|
for (i,zi) in enumerate(z)
|
||||||
|
if zi == NaN
|
||||||
|
z[i] = Inf
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------
|
||||||
# viewport plot area
|
# viewport plot area
|
||||||
|
|
||||||
@ -356,6 +374,9 @@ end
|
|||||||
# values are [xmin, xmax, ymin, ymax]. they range [0,1].
|
# values are [xmin, xmax, ymin, ymax]. they range [0,1].
|
||||||
const viewport_plotarea = zeros(4)
|
const viewport_plotarea = zeros(4)
|
||||||
|
|
||||||
|
# the size of the current plot in pixels
|
||||||
|
const gr_plot_size = zeros(2)
|
||||||
|
|
||||||
function gr_viewport_from_bbox(bb::BoundingBox, w, h, viewport_canvas)
|
function gr_viewport_from_bbox(bb::BoundingBox, w, h, viewport_canvas)
|
||||||
viewport = zeros(4)
|
viewport = zeros(4)
|
||||||
viewport[1] = viewport_canvas[2] * (left(bb) / w)
|
viewport[1] = viewport_canvas[2] * (left(bb) / w)
|
||||||
@ -410,6 +431,13 @@ gr_view_ycenter() = 0.5 * (viewport_plotarea[3] + viewport_plotarea[4])
|
|||||||
gr_view_xdiff() = viewport_plotarea[2] - viewport_plotarea[1]
|
gr_view_xdiff() = viewport_plotarea[2] - viewport_plotarea[1]
|
||||||
gr_view_ydiff() = viewport_plotarea[4] - viewport_plotarea[3]
|
gr_view_ydiff() = viewport_plotarea[4] - viewport_plotarea[3]
|
||||||
|
|
||||||
|
function gr_pixels_to_ndc(x_pixels, y_pixels)
|
||||||
|
w,h = gr_plot_size
|
||||||
|
totx = w * gr_view_xdiff()
|
||||||
|
toty = h * gr_view_ydiff()
|
||||||
|
x_pixels / totx, y_pixels / toty
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# --------------------------------------------------------------------------------------
|
# --------------------------------------------------------------------------------------
|
||||||
|
|
||||||
@ -437,6 +465,7 @@ function gr_display(plt::Plot)
|
|||||||
# compute the viewport_canvas, normalized to the larger dimension
|
# compute the viewport_canvas, normalized to the larger dimension
|
||||||
viewport_canvas = Float64[0,1,0,1]
|
viewport_canvas = Float64[0,1,0,1]
|
||||||
w, h = plt[:size]
|
w, h = plt[:size]
|
||||||
|
gr_plot_size[:] = [w, h]
|
||||||
if w > h
|
if w > h
|
||||||
ratio = float(h) / w
|
ratio = float(h) / w
|
||||||
msize = display_width_ratio * w
|
msize = display_width_ratio * w
|
||||||
@ -534,16 +563,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
|
|
||||||
# set the scale flags and window
|
# set the scale flags and window
|
||||||
xmin, xmax, ymin, ymax = data_lims
|
xmin, xmax, ymin, ymax = data_lims
|
||||||
scale = 0
|
scaleop = 0
|
||||||
xtick, ytick = 1, 1
|
xtick, ytick = 1, 1
|
||||||
if xmax > xmin && ymax > ymin
|
if xmax > xmin && ymax > ymin
|
||||||
# NOTE: for log axes, the major_x and major_y - if non-zero (omit labels) - control the minor grid lines (1 = draw 9 minor grid lines, 2 = no minor grid lines)
|
# NOTE: for log axes, the major_x and major_y - if non-zero (omit labels) - control the minor grid lines (1 = draw 9 minor grid lines, 2 = no minor grid lines)
|
||||||
# NOTE: for log axes, the x_tick and y_tick - if non-zero (omit axes) - only affect the output appearance (1 = nomal, 2 = scientiic notation)
|
# NOTE: for log axes, the x_tick and y_tick - if non-zero (omit axes) - only affect the output appearance (1 = nomal, 2 = scientiic notation)
|
||||||
xaxis[:scale] == :log10 && (scale |= GR.OPTION_X_LOG)
|
xaxis[:scale] == :log10 && (scaleop |= GR.OPTION_X_LOG)
|
||||||
yaxis[:scale] == :log10 && (scale |= GR.OPTION_Y_LOG)
|
yaxis[:scale] == :log10 && (scaleop |= GR.OPTION_Y_LOG)
|
||||||
xaxis[:flip] && (scale |= GR.OPTION_FLIP_X)
|
xaxis[:flip] && (scaleop |= GR.OPTION_FLIP_X)
|
||||||
yaxis[:flip] && (scale |= GR.OPTION_FLIP_Y)
|
yaxis[:flip] && (scaleop |= GR.OPTION_FLIP_Y)
|
||||||
if scale & GR.OPTION_X_LOG == 0
|
if scaleop & GR.OPTION_X_LOG == 0
|
||||||
majorx = 1 #5
|
majorx = 1 #5
|
||||||
xtick = GR.tick(xmin, xmax) / majorx
|
xtick = GR.tick(xmin, xmax) / majorx
|
||||||
else
|
else
|
||||||
@ -551,7 +580,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
xtick = 2 # scientific notation
|
xtick = 2 # scientific notation
|
||||||
majorx = 2 # no minor grid lines
|
majorx = 2 # no minor grid lines
|
||||||
end
|
end
|
||||||
if scale & GR.OPTION_Y_LOG == 0
|
if scaleop & GR.OPTION_Y_LOG == 0
|
||||||
majory = 1 #5
|
majory = 1 #5
|
||||||
ytick = GR.tick(ymin, ymax) / majory
|
ytick = GR.tick(ymin, ymax) / majory
|
||||||
else
|
else
|
||||||
@ -562,7 +591,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
|
|
||||||
# NOTE: setwindow sets the "data coordinate" limits of the current "viewport"
|
# NOTE: setwindow sets the "data coordinate" limits of the current "viewport"
|
||||||
GR.setwindow(xmin, xmax, ymin, ymax)
|
GR.setwindow(xmin, xmax, ymin, ymax)
|
||||||
GR.setscale(scale)
|
GR.setscale(scaleop)
|
||||||
end
|
end
|
||||||
|
|
||||||
# draw the axes
|
# draw the axes
|
||||||
@ -585,8 +614,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
|
|
||||||
# GR.setlinetype(GR.LINETYPE_DOTTED)
|
# GR.setlinetype(GR.LINETYPE_DOTTED)
|
||||||
if sp[:grid]
|
if sp[:grid]
|
||||||
GR.grid3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2)
|
GR.grid3d(xtick, 0, ztick, xmin, ymax, zmin, 2, 0, 2)
|
||||||
GR.grid3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0)
|
GR.grid3d(0, ytick, 0, xmin, ymax, zmin, 0, 2, 0)
|
||||||
end
|
end
|
||||||
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
|
GR.axes3d(xtick, 0, ztick, xmin, ymin, zmin, 2, 0, 2, -ticksize)
|
||||||
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
|
GR.axes3d(0, ytick, 0, xmax, ymin, zmin, 0, 2, 0, ticksize)
|
||||||
@ -722,12 +751,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
x, y, z = series[:x], series[:y], series[:z]
|
x, y, z = series[:x], series[:y], series[:z]
|
||||||
frng = series[:fillrange]
|
frng = series[:fillrange]
|
||||||
|
|
||||||
|
# add custom frame shapes to markershape?
|
||||||
|
series_annotations_shapes!(series)
|
||||||
|
# -------------------------------------------------------
|
||||||
|
|
||||||
# recompute data
|
# recompute data
|
||||||
if typeof(z) <: Surface
|
if typeof(z) <: Surface
|
||||||
if st == :heatmap
|
# 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[: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])))
|
# expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
|
||||||
end
|
# end
|
||||||
z = vec(transpose_z(series, z.surf, false))
|
z = vec(transpose_z(series, z.surf, false))
|
||||||
elseif ispolar(sp)
|
elseif ispolar(sp)
|
||||||
if frng != nothing
|
if frng != nothing
|
||||||
@ -756,7 +789,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
# draw the line(s)
|
# draw the line(s)
|
||||||
if st == :path
|
if st == :path
|
||||||
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
|
gr_set_line(series[:linewidth], series[:linestyle], series[:linecolor]) #, series[:linealpha])
|
||||||
gr_polyline(x, y)
|
arrowside = isa(series[:arrow], Arrow) ? series[:arrow].side : :none
|
||||||
|
gr_polyline(x, y; arrowside = arrowside)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -808,16 +842,19 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
cmap && gr_colorbar(sp)
|
cmap && gr_colorbar(sp)
|
||||||
|
|
||||||
elseif st == :heatmap
|
elseif st == :heatmap
|
||||||
# z = vec(transpose_z(series, z.surf, false))
|
|
||||||
zmin, zmax = gr_lims(zaxis, true)
|
zmin, zmax = gr_lims(zaxis, true)
|
||||||
clims = sp[:clims]
|
clims = sp[:clims]
|
||||||
if is_2tuple(clims)
|
if is_2tuple(clims)
|
||||||
isfinite(clims[1]) && (zmin = clims[1])
|
isfinite(clims[1]) && (zmin = clims[1])
|
||||||
isfinite(clims[2]) && (zmax = clims[2])
|
isfinite(clims[2]) && (zmax = clims[2])
|
||||||
end
|
end
|
||||||
GR.setspace(zmin, zmax, 0, 90)
|
grad = isa(series[:fillcolor], ColorGradient) ? series[:fillcolor] : cgrad()
|
||||||
# GR.surface(x, y, z, GR.OPTION_COLORED_MESH)
|
colors = [grad[clamp((zi-zmin) / (zmax-zmin), 0, 1)] for zi=z]
|
||||||
GR.surface(x, y, z, GR.OPTION_HEATMAP)
|
rgba = map(c -> UInt32( round(Int, alpha(c) * 255) << 24 +
|
||||||
|
round(Int, blue(c) * 255) << 16 +
|
||||||
|
round(Int, green(c) * 255) << 8 +
|
||||||
|
round(Int, red(c) * 255) ), colors)
|
||||||
|
GR.drawimage(xmin, xmax, ymax, ymin, length(x), length(y), rgba)
|
||||||
cmap && gr_colorbar(sp)
|
cmap && gr_colorbar(sp)
|
||||||
|
|
||||||
elseif st in (:path3d, :scatter3d)
|
elseif st in (:path3d, :scatter3d)
|
||||||
@ -904,8 +941,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
|
|
||||||
|
|
||||||
elseif st == :image
|
elseif st == :image
|
||||||
img = series[:z].surf
|
z = transpose_z(series, series[:z].surf, true)
|
||||||
h, w = size(img)
|
h, w = size(z)
|
||||||
if eltype(z) <: Colors.AbstractGray
|
if eltype(z) <: Colors.AbstractGray
|
||||||
grey = round(UInt8, float(z) * 255)
|
grey = round(UInt8, float(z) * 255)
|
||||||
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
|
rgba = map(c -> UInt32( 0xff000000 + Int(c)<<16 + Int(c)<<8 + Int(c) ), grey)
|
||||||
@ -918,6 +955,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
|||||||
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
||||||
end
|
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)
|
||||||
|
gr_set_font(fnt)
|
||||||
|
gr_text(GR.wctondc(xi, yi)..., str)
|
||||||
|
end
|
||||||
|
|
||||||
GR.restorestate()
|
GR.restorestate()
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -1025,22 +1069,25 @@ const _gr_mimeformats = Dict(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const _gr_wstype_default = @static if is_linux()
|
const _gr_wstype_default = @static if is_linux()
|
||||||
"cairox11"
|
"x11"
|
||||||
|
# "cairox11"
|
||||||
elseif is_apple()
|
elseif is_apple()
|
||||||
"quartz"
|
"quartz"
|
||||||
else
|
else
|
||||||
"windows"
|
"use_default"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
const _gr_wstype = Ref(get(ENV, "GKS_WSTYPE", _gr_wstype_default))
|
||||||
|
gr_set_output(wstype::String) = (_gr_wstype[] = wstype)
|
||||||
|
|
||||||
for (mime, fmt) in _gr_mimeformats
|
for (mime, fmt) in _gr_mimeformats
|
||||||
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
|
@eval function _show(io::IO, ::MIME{Symbol($mime)}, plt::Plot{GRBackend})
|
||||||
GR.emergencyclosegks()
|
GR.emergencyclosegks()
|
||||||
filepath = tempname() * "." * $fmt
|
filepath = tempname() * "." * $fmt
|
||||||
withenv("GKS_WSTYPE" => $fmt, # $fmt == "png" ? "cairopng" : $fmt,
|
ENV["GKS_WSTYPE"] = $fmt
|
||||||
"GKS_FILEPATH" => filepath) do
|
ENV["GKS_FILEPATH"] = filepath
|
||||||
gr_display(plt)
|
gr_display(plt)
|
||||||
GR.emergencyclosegks()
|
GR.emergencyclosegks()
|
||||||
end
|
|
||||||
write(io, readstring(filepath))
|
write(io, readstring(filepath))
|
||||||
rm(filepath)
|
rm(filepath)
|
||||||
end
|
end
|
||||||
@ -1050,18 +1097,20 @@ function _display(plt::Plot{GRBackend})
|
|||||||
if plt[:display_type] == :inline
|
if plt[:display_type] == :inline
|
||||||
GR.emergencyclosegks()
|
GR.emergencyclosegks()
|
||||||
filepath = tempname() * ".pdf"
|
filepath = tempname() * ".pdf"
|
||||||
withenv("GKS_WSTYPE" => "pdf",
|
ENV["GKS_WSTYPE"] = "pdf"
|
||||||
"GKS_FILEPATH" => filepath) do
|
ENV["GKS_FILEPATH"] = filepath
|
||||||
gr_display(plt)
|
gr_display(plt)
|
||||||
GR.emergencyclosegks()
|
GR.emergencyclosegks()
|
||||||
end
|
|
||||||
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(readbytes, filepath)), "\a")
|
||||||
println(content)
|
println(content)
|
||||||
rm(filepath)
|
rm(filepath)
|
||||||
else
|
else
|
||||||
withenv("GKS_WSTYPE" => get(ENV, "GKS_WSTYPE", _gr_wstype_default),
|
ENV["GKS_DOUBLE_BUF"] = true
|
||||||
"GKS_DOUBLE_BUF" => get(ENV ,"GKS_DOUBLE_BUF", "true")) do
|
if _gr_wstype[] != "use_default"
|
||||||
|
ENV["GKS_WSTYPE"] = _gr_wstype[]
|
||||||
|
end
|
||||||
gr_display(plt)
|
gr_display(plt)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
closeall(::GRBackend) = GR.emergencyclosegks()
|
||||||
|
|||||||
@ -30,6 +30,7 @@ const _plotly_attr = merge_with_base_supported([
|
|||||||
:aspect_ratio,
|
:aspect_ratio,
|
||||||
:hover,
|
:hover,
|
||||||
:inset_subplots,
|
:inset_subplots,
|
||||||
|
:bar_width,
|
||||||
])
|
])
|
||||||
|
|
||||||
const _plotly_seriestype = [
|
const _plotly_seriestype = [
|
||||||
@ -56,6 +57,7 @@ end
|
|||||||
|
|
||||||
|
|
||||||
const _plotly_js_path = joinpath(dirname(@__FILE__), "..", "..", "deps", "plotly-latest.min.js")
|
const _plotly_js_path = joinpath(dirname(@__FILE__), "..", "..", "deps", "plotly-latest.min.js")
|
||||||
|
const _plotly_js_path_remote = "https://cdn.plot.ly/plotly-latest.min.js"
|
||||||
|
|
||||||
function _initialize_backend(::PlotlyBackend; kw...)
|
function _initialize_backend(::PlotlyBackend; kw...)
|
||||||
@eval begin
|
@eval begin
|
||||||
@ -92,6 +94,20 @@ end
|
|||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
const _plotly_legend_pos = KW(
|
||||||
|
:right => [1., 0.5],
|
||||||
|
:left => [0., 0.5],
|
||||||
|
:top => [0.5, 1.],
|
||||||
|
:bottom => [0.5, 0.],
|
||||||
|
:bottomleft => [0., 0.],
|
||||||
|
:bottomright => [1., 0.],
|
||||||
|
:topright => [1., 1.],
|
||||||
|
:topleft => [0., 1.]
|
||||||
|
)
|
||||||
|
|
||||||
|
plotly_legend_pos(pos::Symbol) = get(_plotly_legend_pos, pos, [1.,1.])
|
||||||
|
plotly_legend_pos{S<:Real, T<:Real}(v::Tuple{S,T}) = v
|
||||||
|
|
||||||
function plotly_font(font::Font, color = font.color)
|
function plotly_font(font::Font, color = font.color)
|
||||||
KW(
|
KW(
|
||||||
:family => font.family,
|
:family => font.family,
|
||||||
@ -100,6 +116,7 @@ function plotly_font(font::Font, color = font.color)
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
|
function plotly_annotation_dict(x, y, val; xref="paper", yref="paper")
|
||||||
KW(
|
KW(
|
||||||
:text => val,
|
:text => val,
|
||||||
@ -162,14 +179,17 @@ function plotly_apply_aspect_ratio(sp::Subplot, plotarea, pcts)
|
|||||||
if aspect_ratio == :equal
|
if aspect_ratio == :equal
|
||||||
aspect_ratio = 1.0
|
aspect_ratio = 1.0
|
||||||
end
|
end
|
||||||
|
xmin,xmax = axis_limits(sp[:xaxis])
|
||||||
|
ymin,ymax = axis_limits(sp[:yaxis])
|
||||||
|
want_ratio = ((xmax-xmin) / (ymax-ymin)) / aspect_ratio
|
||||||
parea_ratio = width(plotarea) / height(plotarea)
|
parea_ratio = width(plotarea) / height(plotarea)
|
||||||
if aspect_ratio > parea_ratio
|
if want_ratio > parea_ratio
|
||||||
# need to shrink y
|
# need to shrink y
|
||||||
ratio = parea_ratio / aspect_ratio
|
ratio = parea_ratio / want_ratio
|
||||||
pcts[2], pcts[4] = shrink_by(pcts[2], pcts[4], ratio)
|
pcts[2], pcts[4] = shrink_by(pcts[2], pcts[4], ratio)
|
||||||
elseif aspect_ratio < parea_ratio
|
elseif want_ratio < parea_ratio
|
||||||
# need to shrink x
|
# need to shrink x
|
||||||
ratio = aspect_ratio / parea_ratio
|
ratio = want_ratio / parea_ratio
|
||||||
pcts[1], pcts[3] = shrink_by(pcts[1], pcts[3], ratio)
|
pcts[1], pcts[3] = shrink_by(pcts[1], pcts[3], ratio)
|
||||||
end
|
end
|
||||||
pcts
|
pcts
|
||||||
@ -214,7 +234,7 @@ function plotly_axis(axis::Axis, sp::Subplot)
|
|||||||
# lims
|
# lims
|
||||||
lims = axis[:lims]
|
lims = axis[:lims]
|
||||||
if lims != :auto && limsType(lims) == :limits
|
if lims != :auto && limsType(lims) == :limits
|
||||||
ax[:range] = lims
|
ax[:range] = map(scalefunc(axis[:scale]), lims)
|
||||||
end
|
end
|
||||||
|
|
||||||
# flip
|
# flip
|
||||||
@ -289,17 +309,32 @@ function plotly_layout(plt::Plot)
|
|||||||
|
|
||||||
# legend
|
# legend
|
||||||
d_out[:showlegend] = sp[:legend] != :none
|
d_out[:showlegend] = sp[:legend] != :none
|
||||||
|
xpos,ypos = plotly_legend_pos(sp[:legend])
|
||||||
if sp[:legend] != :none
|
if sp[:legend] != :none
|
||||||
d_out[:legend] = KW(
|
d_out[:legend] = KW(
|
||||||
:bgcolor => rgba_string(sp[:background_color_legend]),
|
:bgcolor => rgba_string(sp[:background_color_legend]),
|
||||||
:bordercolor => rgba_string(sp[:foreground_color_legend]),
|
:bordercolor => rgba_string(sp[:foreground_color_legend]),
|
||||||
:font => plotly_font(sp[:legendfont], sp[:foreground_color_legend]),
|
:font => plotly_font(sp[:legendfont], sp[:foreground_color_legend]),
|
||||||
|
:x => xpos,
|
||||||
|
:y => ypos
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# annotations
|
# annotations
|
||||||
append!(d_out[:annotations], KW[plotly_annotation_dict(ann...; xref = "x$spidx", yref = "y$spidx") for ann in sp[:annotations]])
|
append!(d_out[:annotations], KW[plotly_annotation_dict(ann...; xref = "x$spidx", yref = "y$spidx") for ann in sp[:annotations]])
|
||||||
|
|
||||||
|
# series_annotations
|
||||||
|
for series in series_list(sp)
|
||||||
|
anns = series[:series_annotations]
|
||||||
|
for (xi,yi,str,fnt) in EachAnn(anns, series[:x], series[:y])
|
||||||
|
push!(d_out[:annotations], plotly_annotation_dict(
|
||||||
|
xi,
|
||||||
|
yi,
|
||||||
|
PlotText(str,fnt); xref = "x$spidx", yref = "y$spidx")
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# # arrows
|
# # arrows
|
||||||
# for sargs in seriesargs
|
# for sargs in seriesargs
|
||||||
# a = sargs[:arrow]
|
# a = sargs[:arrow]
|
||||||
@ -359,7 +394,7 @@ function plotly_close_shapes(x, y)
|
|||||||
xs, ys = nansplit(x), nansplit(y)
|
xs, ys = nansplit(x), nansplit(y)
|
||||||
for i=1:length(xs)
|
for i=1:length(xs)
|
||||||
shape = Shape(xs[i], ys[i])
|
shape = Shape(xs[i], ys[i])
|
||||||
xs[i], ys[i] = shape_coords(shape)
|
xs[i], ys[i] = coords(shape)
|
||||||
end
|
end
|
||||||
nanvcat(xs), nanvcat(ys)
|
nanvcat(xs), nanvcat(ys)
|
||||||
end
|
end
|
||||||
@ -420,8 +455,11 @@ function plotly_series(plt::Plot, series::Series)
|
|||||||
|
|
||||||
elseif st == :bar
|
elseif st == :bar
|
||||||
d_out[:type] = "bar"
|
d_out[:type] = "bar"
|
||||||
d_out[:x], d_out[:y] = x, y
|
d_out[:x], d_out[:y], d_out[:orientation] = if isvertical(series)
|
||||||
d_out[:orientation] = isvertical(series) ? "v" : "h"
|
x, y, "v"
|
||||||
|
else
|
||||||
|
y, x, "h"
|
||||||
|
end
|
||||||
d_out[:marker] = KW(:color => rgba_string(series[:fillcolor]))
|
d_out[:marker] = KW(:color => rgba_string(series[:fillcolor]))
|
||||||
|
|
||||||
elseif st == :heatmap
|
elseif st == :heatmap
|
||||||
@ -592,8 +630,12 @@ end
|
|||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
|
const _use_remote = Ref(false)
|
||||||
|
|
||||||
function html_head(plt::Plot{PlotlyBackend})
|
function html_head(plt::Plot{PlotlyBackend})
|
||||||
"<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
|
jsfilename = _use_remote[] ? _plotly_js_path_remote : _plotly_js_path
|
||||||
|
# "<script src=\"$(joinpath(dirname(@__FILE__),"..","..","deps","plotly-latest.min.js"))\"></script>"
|
||||||
|
"<script src=\"$jsfilename\"></script>"
|
||||||
end
|
end
|
||||||
|
|
||||||
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
function html_body(plt::Plot{PlotlyBackend}, style = nothing)
|
||||||
@ -624,7 +666,8 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
|
function _show(io::IO, ::MIME"image/png", plt::Plot{PlotlyBackend})
|
||||||
show_png_from_html(io, plt)
|
# show_png_from_html(io, plt)
|
||||||
|
error("png output from the plotly backend is not supported. Please use plotlyjs instead.")
|
||||||
end
|
end
|
||||||
|
|
||||||
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
|
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyBackend})
|
||||||
|
|||||||
@ -40,8 +40,12 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||||
|
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||||
|
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
|
||||||
|
else
|
||||||
PlotlyJS.plot()
|
PlotlyJS.plot()
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
|
function _series_added(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||||
@ -59,7 +63,7 @@ function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
|||||||
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
|
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
|
||||||
z = series[:z]
|
z = series[:z]
|
||||||
if z != nothing
|
if z != nothing
|
||||||
kw[:z] = (transpose_z(series, series[:z].surf, false),)
|
kw[:z] = (isa(z,Surface) ? transpose_z(series, series[:z].surf, false) : z,)
|
||||||
end
|
end
|
||||||
PlotlyJS.restyle!(
|
PlotlyJS.restyle!(
|
||||||
plt.o,
|
plt.o,
|
||||||
@ -82,8 +86,12 @@ end
|
|||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
|
function _show(io::IO, ::MIME"image/svg+xml", plt::Plot{PlotlyJSBackend})
|
||||||
|
if Plots.isijulia()
|
||||||
|
print(io, PlotlyJS.html_body(plt.o))
|
||||||
|
else
|
||||||
show(io, MIME("text/html"), plt.o)
|
show(io, MIME("text/html"), plt.o)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
|
function plotlyjs_save_hack(io::IO, plt::Plot{PlotlyJSBackend}, ext::String)
|
||||||
tmpfn = tempname() * "." * ext
|
tmpfn = tempname() * "." * ext
|
||||||
@ -97,3 +105,10 @@ _show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hac
|
|||||||
function _display(plt::Plot{PlotlyJSBackend})
|
function _display(plt::Plot{PlotlyJSBackend})
|
||||||
display(plt.o)
|
display(plt.o)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
function closeall(::PlotlyJSBackend)
|
||||||
|
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
|
||||||
|
close(current().o)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|||||||
@ -64,7 +64,7 @@ function _initialize_backend(::PyPlotBackend)
|
|||||||
# problem: https://github.com/tbreloff/Plots.jl/issues/308
|
# problem: https://github.com/tbreloff/Plots.jl/issues/308
|
||||||
# solution: hack from @stevengj: https://github.com/stevengj/PyPlot.jl/pull/223#issuecomment-229747768
|
# solution: hack from @stevengj: https://github.com/stevengj/PyPlot.jl/pull/223#issuecomment-229747768
|
||||||
otherdisplays = splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays))
|
otherdisplays = splice!(Base.Multimedia.displays, 2:length(Base.Multimedia.displays))
|
||||||
import PyPlot
|
import PyPlot, PyCall
|
||||||
import LaTeXStrings: latexstring
|
import LaTeXStrings: latexstring
|
||||||
append!(Base.Multimedia.displays, otherdisplays)
|
append!(Base.Multimedia.displays, otherdisplays)
|
||||||
|
|
||||||
@ -117,7 +117,9 @@ py_color(grad::ColorGradient) = py_color(grad.colors)
|
|||||||
|
|
||||||
function py_colormap(grad::ColorGradient)
|
function py_colormap(grad::ColorGradient)
|
||||||
pyvals = [(z, py_color(grad[z])) for z in grad.values]
|
pyvals = [(z, py_color(grad[z])) for z in grad.values]
|
||||||
pycolors.LinearSegmentedColormap[:from_list]("tmp", pyvals)
|
cm = pycolors.LinearSegmentedColormap[:from_list]("tmp", pyvals)
|
||||||
|
cm[:set_bad](color=(0,0,0,0.0), alpha=0.0)
|
||||||
|
cm
|
||||||
end
|
end
|
||||||
py_colormap(c) = py_colormap(cgrad())
|
py_colormap(c) = py_colormap(cgrad())
|
||||||
|
|
||||||
@ -140,7 +142,7 @@ function py_linestyle(seriestype::Symbol, linestyle::Symbol)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function py_marker(marker::Shape)
|
function py_marker(marker::Shape)
|
||||||
x, y = shape_coords(marker)
|
x, y = coords(marker)
|
||||||
n = length(x)
|
n = length(x)
|
||||||
mat = zeros(n+1,2)
|
mat = zeros(n+1,2)
|
||||||
for i=1:n
|
for i=1:n
|
||||||
@ -246,6 +248,12 @@ function labelfunc(scale::Symbol, backend::PyPlotBackend)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function py_mask_nans(z)
|
||||||
|
# PyPlot.pywrap(pynp.ma[:masked_invalid](PyPlot.pywrap(z)))
|
||||||
|
PyCall.pycall(pynp.ma[:masked_invalid], Any, z)
|
||||||
|
# pynp.ma[:masked_where](pynp.isnan(z),z)
|
||||||
|
end
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
|
function fix_xy_lengths!(plt::Plot{PyPlotBackend}, series::Series)
|
||||||
@ -437,6 +445,9 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
error("Only numbers and vectors are supported with levels keyword")
|
error("Only numbers and vectors are supported with levels keyword")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# add custom frame shapes to markershape?
|
||||||
|
series_annotations_shapes!(series, :xy)
|
||||||
|
|
||||||
# for each plotting command, optionally build and add a series handle to the list
|
# for each plotting command, optionally build and add a series handle to the list
|
||||||
|
|
||||||
# line plot
|
# line plot
|
||||||
@ -552,6 +563,35 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
else
|
else
|
||||||
xyargs
|
xyargs
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if isa(series[:markershape], AbstractVector{Shape})
|
||||||
|
# this section will create one scatter per data point to accomodate the
|
||||||
|
# vector of shapes
|
||||||
|
handle = []
|
||||||
|
x,y = xyargs
|
||||||
|
shapes = series[:markershape]
|
||||||
|
msc = py_markerstrokecolor(series)
|
||||||
|
lw = py_dpi_scale(plt, series[:markerstrokewidth])
|
||||||
|
for i=1:length(y)
|
||||||
|
extrakw[:c] = if series[:marker_z] == nothing
|
||||||
|
py_color_fix(py_color(cycle(series[:markercolor],i)), x)
|
||||||
|
else
|
||||||
|
extrakw[:c]
|
||||||
|
end
|
||||||
|
|
||||||
|
push!(handle, ax[:scatter](cycle(x,i), cycle(y,i);
|
||||||
|
label = series[:label],
|
||||||
|
zorder = series[:series_plotindex] + 0.5,
|
||||||
|
marker = py_marker(cycle(shapes,i)),
|
||||||
|
s = py_dpi_scale(plt, cycle(series[:markersize],i) .^ 2),
|
||||||
|
edgecolors = msc,
|
||||||
|
linewidths = lw,
|
||||||
|
extrakw...
|
||||||
|
))
|
||||||
|
end
|
||||||
|
push!(handles, handle)
|
||||||
|
else
|
||||||
|
# do a normal scatter plot
|
||||||
handle = ax[:scatter](xyargs...;
|
handle = ax[:scatter](xyargs...;
|
||||||
label = series[:label],
|
label = series[:label],
|
||||||
zorder = series[:series_plotindex] + 0.5,
|
zorder = series[:series_plotindex] + 0.5,
|
||||||
@ -563,6 +603,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
)
|
)
|
||||||
push!(handles, handle)
|
push!(handles, handle)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if st == :hexbin
|
if st == :hexbin
|
||||||
clims = sp[:clims]
|
clims = sp[:clims]
|
||||||
@ -730,12 +771,11 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
end
|
end
|
||||||
|
|
||||||
clims = sp[:clims]
|
clims = sp[:clims]
|
||||||
if is_2tuple(clims)
|
zmin, zmax = extrema(z)
|
||||||
isfinite(clims[1]) && (extrakw[:vmin] = clims[1])
|
extrakw[:vmin] = (is_2tuple(clims) && isfinite(clims[1])) ? clims[1] : zmin
|
||||||
isfinite(clims[2]) && (extrakw[:vmax] = clims[2])
|
extrakw[:vmax] = (is_2tuple(clims) && isfinite(clims[2])) ? clims[2] : zmax
|
||||||
end
|
|
||||||
|
|
||||||
handle = ax[:pcolormesh](x, y, z;
|
handle = ax[:pcolormesh](x, y, py_mask_nans(z);
|
||||||
label = series[:label],
|
label = series[:label],
|
||||||
zorder = series[:series_plotindex],
|
zorder = series[:series_plotindex],
|
||||||
cmap = py_fillcolormap(series),
|
cmap = py_fillcolormap(series),
|
||||||
@ -763,16 +803,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
push!(handle, ax[:add_patch](patches))
|
push!(handle, ax[:add_patch](patches))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
# path = py_path(x, y)
|
|
||||||
# patches = pypatches.pymember("PathPatch")(path;
|
|
||||||
# label = series[:label],
|
|
||||||
# zorder = series[:series_plotindex],
|
|
||||||
# edgecolor = py_linecolor(series),
|
|
||||||
# facecolor = py_fillcolor(series),
|
|
||||||
# linewidth = py_dpi_scale(plt, series[:linewidth]),
|
|
||||||
# fill = true
|
|
||||||
# )
|
|
||||||
# handle = ax[:add_patch](patches)
|
|
||||||
push!(handles, handle)
|
push!(handles, handle)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -842,6 +872,12 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
)
|
)
|
||||||
push!(handles, handle)
|
push!(handles, handle)
|
||||||
end
|
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)
|
||||||
|
py_add_annotations(sp, xi, yi, PlotText(str, fnt))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# --------------------------------------------------------------------------
|
# --------------------------------------------------------------------------
|
||||||
@ -1217,3 +1253,5 @@ for (mime, fmt) in _pyplot_mimeformats
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
closeall(::PyPlotBackend) = PyPlot.plt[:close]("all")
|
||||||
|
|||||||
@ -16,7 +16,8 @@ const _unicodeplots_seriestype = [
|
|||||||
:path, :scatter,
|
:path, :scatter,
|
||||||
# :bar,
|
# :bar,
|
||||||
:shape,
|
:shape,
|
||||||
:histogram2d
|
:histogram2d,
|
||||||
|
:spy
|
||||||
]
|
]
|
||||||
const _unicodeplots_style = [:auto, :solid]
|
const _unicodeplots_style = [:auto, :solid]
|
||||||
const _unicodeplots_marker = [:none, :auto, :circle]
|
const _unicodeplots_marker = [:none, :auto, :circle]
|
||||||
@ -44,6 +45,18 @@ end
|
|||||||
|
|
||||||
# -------------------------------
|
# -------------------------------
|
||||||
|
|
||||||
|
const _canvas_type = Ref(:auto)
|
||||||
|
|
||||||
|
function _canvas_map()
|
||||||
|
KW(
|
||||||
|
:braille => UnicodePlots.BrailleCanvas,
|
||||||
|
:ascii => UnicodePlots.AsciiCanvas,
|
||||||
|
:block => UnicodePlots.BlockCanvas,
|
||||||
|
:dot => UnicodePlots.DotCanvas,
|
||||||
|
:density => UnicodePlots.DensityCanvas,
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
# do all the magic here... build it all at once, since we need to know about all the series at the very beginning
|
||||||
function rebuildUnicodePlot!(plt::Plot, width, height)
|
function rebuildUnicodePlot!(plt::Plot, width, height)
|
||||||
@ -65,7 +78,27 @@ function rebuildUnicodePlot!(plt::Plot, width, height)
|
|||||||
y = Float64[ylim[1]]
|
y = Float64[ylim[1]]
|
||||||
|
|
||||||
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
# create a plot window with xlim/ylim set, but the X/Y vectors are outside the bounds
|
||||||
canvas_type = isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
|
ct = _canvas_type[]
|
||||||
|
canvas_type = if ct == :auto
|
||||||
|
isijulia() ? UnicodePlots.AsciiCanvas : UnicodePlots.BrailleCanvas
|
||||||
|
else
|
||||||
|
_canvas_map()[ct]
|
||||||
|
end
|
||||||
|
|
||||||
|
# special handling for spy
|
||||||
|
if length(sp.series_list) == 1
|
||||||
|
series = sp.series_list[1]
|
||||||
|
if series[:seriestype] == :spy
|
||||||
|
push!(plt.o, UnicodePlots.spy(
|
||||||
|
series[:z].surf,
|
||||||
|
width = width,
|
||||||
|
height = height,
|
||||||
|
title = sp[:title],
|
||||||
|
canvas = canvas_type
|
||||||
|
))
|
||||||
|
continue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# # make it a bar canvas if plotting bar
|
# # make it a bar canvas if plotting bar
|
||||||
# if any(series -> series[:seriestype] == :bar, series_list(sp))
|
# if any(series -> series[:seriestype] == :bar, series_list(sp))
|
||||||
|
|||||||
@ -23,21 +23,24 @@ immutable Shape
|
|||||||
# end
|
# end
|
||||||
end
|
end
|
||||||
Shape(verts::AVec) = Shape(unzip(verts)...)
|
Shape(verts::AVec) = Shape(unzip(verts)...)
|
||||||
|
Shape(s::Shape) = deepcopy(s)
|
||||||
|
|
||||||
get_xs(shape::Shape) = shape.x
|
get_xs(shape::Shape) = shape.x
|
||||||
get_ys(shape::Shape) = shape.y
|
get_ys(shape::Shape) = shape.y
|
||||||
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
|
vertices(shape::Shape) = collect(zip(shape.x, shape.y))
|
||||||
|
|
||||||
|
#deprecated
|
||||||
|
@deprecate shape_coords coords
|
||||||
|
|
||||||
function shape_coords(shape::Shape)
|
function coords(shape::Shape)
|
||||||
shape.x, shape.y
|
shape.x, shape.y
|
||||||
end
|
end
|
||||||
|
|
||||||
function shape_coords(shapes::AVec{Shape})
|
function coords(shapes::AVec{Shape})
|
||||||
length(shapes) == 0 && return zeros(0), zeros(0)
|
length(shapes) == 0 && return zeros(0), zeros(0)
|
||||||
xs = map(get_xs, shapes)
|
xs = map(get_xs, shapes)
|
||||||
ys = map(get_ys, shapes)
|
ys = map(get_ys, shapes)
|
||||||
x, y = map(copy, shape_coords(shapes[1]))
|
x, y = map(copy, coords(shapes[1]))
|
||||||
for shape in shapes[2:end]
|
for shape in shapes[2:end]
|
||||||
nanappend!(x, shape.x)
|
nanappend!(x, shape.x)
|
||||||
nanappend!(y, shape.y)
|
nanappend!(y, shape.y)
|
||||||
@ -72,13 +75,13 @@ function makestar(n; offset = -0.5, radius = 1.0)
|
|||||||
z2 = z1 + π / (n)
|
z2 = z1 + π / (n)
|
||||||
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
|
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
|
||||||
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
|
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
|
||||||
Shape(weave(outercircle, innercircle)[1:end-2])
|
Shape(weave(outercircle, innercircle))
|
||||||
end
|
end
|
||||||
|
|
||||||
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
|
"create a shape by picking points around the unit circle. `n` is the number of point/sides, `offset` is the starting angle"
|
||||||
function makeshape(n; offset = -0.5, radius = 1.0)
|
function makeshape(n; offset = -0.5, radius = 1.0)
|
||||||
z = offset * π
|
z = offset * π
|
||||||
Shape(partialcircle(z, z + 2π, n+1, radius)[1:end-1])
|
Shape(partialcircle(z, z + 2π, n+1, radius))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -88,7 +91,7 @@ function makecross(; offset = -0.5, radius = 1.0)
|
|||||||
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
|
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
|
||||||
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
|
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
|
||||||
Shape(weave(outercircle, innercircle,
|
Shape(weave(outercircle, innercircle,
|
||||||
ordering=Vector[outercircle,innercircle,outercircle])[1:end-2])
|
ordering=Vector[outercircle,innercircle,outercircle]))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -110,6 +113,8 @@ const _shape_keys = Symbol[
|
|||||||
:xcross,
|
:xcross,
|
||||||
:utriangle,
|
:utriangle,
|
||||||
:dtriangle,
|
:dtriangle,
|
||||||
|
:rtriangle,
|
||||||
|
:ltriangle,
|
||||||
:pentagon,
|
:pentagon,
|
||||||
:heptagon,
|
:heptagon,
|
||||||
:octagon,
|
:octagon,
|
||||||
@ -127,8 +132,10 @@ const _shapes = KW(
|
|||||||
:circle => makeshape(20),
|
:circle => makeshape(20),
|
||||||
:rect => makeshape(4, offset=-0.25),
|
:rect => makeshape(4, offset=-0.25),
|
||||||
:diamond => makeshape(4),
|
:diamond => makeshape(4),
|
||||||
:utriangle => makeshape(3),
|
:utriangle => makeshape(3, offset=0.5),
|
||||||
:dtriangle => makeshape(3, offset=0.5),
|
:dtriangle => makeshape(3, offset=-0.5),
|
||||||
|
:rtriangle => makeshape(3, offset=0.0),
|
||||||
|
:ltriangle => makeshape(3, offset=1.0),
|
||||||
:pentagon => makeshape(5),
|
:pentagon => makeshape(5),
|
||||||
:hexagon => makeshape(6),
|
:hexagon => makeshape(6),
|
||||||
:heptagon => makeshape(7),
|
:heptagon => makeshape(7),
|
||||||
@ -143,12 +150,14 @@ for n in [4,5,6,7,8]
|
|||||||
_shapes[Symbol("star$n")] = makestar(n)
|
_shapes[Symbol("star$n")] = makestar(n)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Shape(k::Symbol) = deepcopy(_shapes[k])
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
|
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
|
||||||
function center(shape::Shape)
|
function center(shape::Shape)
|
||||||
x, y = shape_coords(shape)
|
x, y = coords(shape)
|
||||||
n = length(x)
|
n = length(x)
|
||||||
A, Cx, Cy = 0.0, 0.0, 0.0
|
A, Cx, Cy = 0.0, 0.0, 0.0
|
||||||
for i=1:n
|
for i=1:n
|
||||||
@ -166,7 +175,7 @@ function center(shape::Shape)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
function Base.scale!(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||||
sx, sy = shape_coords(shape)
|
sx, sy = coords(shape)
|
||||||
cx, cy = c
|
cx, cy = c
|
||||||
for i=1:length(sx)
|
for i=1:length(sx)
|
||||||
sx[i] = (sx[i] - cx) * x + cx
|
sx[i] = (sx[i] - cx) * x + cx
|
||||||
@ -177,11 +186,11 @@ end
|
|||||||
|
|
||||||
function Base.scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
function Base.scale(shape::Shape, x::Real, y::Real = x, c = center(shape))
|
||||||
shapecopy = deepcopy(shape)
|
shapecopy = deepcopy(shape)
|
||||||
scale!(shape, x, y, c)
|
scale!(shapecopy, x, y, c)
|
||||||
end
|
end
|
||||||
|
|
||||||
function translate!(shape::Shape, x::Real, y::Real = x)
|
function translate!(shape::Shape, x::Real, y::Real = x)
|
||||||
sx, sy = shape_coords(shape)
|
sx, sy = coords(shape)
|
||||||
for i=1:length(sx)
|
for i=1:length(sx)
|
||||||
sx[i] += x
|
sx[i] += x
|
||||||
sy[i] += y
|
sy[i] += y
|
||||||
@ -191,7 +200,7 @@ end
|
|||||||
|
|
||||||
function translate(shape::Shape, x::Real, y::Real = x)
|
function translate(shape::Shape, x::Real, y::Real = x)
|
||||||
shapecopy = deepcopy(shape)
|
shapecopy = deepcopy(shape)
|
||||||
translate!(shape, x, y)
|
translate!(shapecopy, x, y)
|
||||||
end
|
end
|
||||||
|
|
||||||
function rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
|
function rotate_x(x::Real, y::Real, Θ::Real, centerx::Real, centery::Real)
|
||||||
@ -208,7 +217,7 @@ function rotate(x::Real, y::Real, θ::Real, c = center(shape))
|
|||||||
end
|
end
|
||||||
|
|
||||||
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
||||||
x, y = shape_coords(shape)
|
x, y = coords(shape)
|
||||||
cx, cy = c
|
cx, cy = c
|
||||||
for i=1:length(x)
|
for i=1:length(x)
|
||||||
xi = rotate_x(x[i], y[i], Θ, cx, cy)
|
xi = rotate_x(x[i], y[i], Θ, cx, cy)
|
||||||
@ -226,7 +235,7 @@ end
|
|||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
immutable Font
|
type Font
|
||||||
family::AbstractString
|
family::AbstractString
|
||||||
pointsize::Int
|
pointsize::Int
|
||||||
halign::Symbol
|
halign::Symbol
|
||||||
@ -283,6 +292,17 @@ function font(args...)
|
|||||||
Font(family, pointsize, halign, valign, rotation, color)
|
Font(family, pointsize, halign, valign, rotation, color)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function scalefontsize(k::Symbol, factor::Number)
|
||||||
|
f = default(k)
|
||||||
|
f.pointsize = round(Int, factor * f.pointsize)
|
||||||
|
default(k, f)
|
||||||
|
end
|
||||||
|
function scalefontsizes(factor::Number)
|
||||||
|
for k in (:titlefont, :guidefont, :tickfont, :legendfont)
|
||||||
|
scalefontsize(k, factor)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
"Wrap a string with font info"
|
"Wrap a string with font info"
|
||||||
immutable PlotText
|
immutable PlotText
|
||||||
str::AbstractString
|
str::AbstractString
|
||||||
@ -296,11 +316,7 @@ function text(str, args...)
|
|||||||
PlotText(string(str), font(args...))
|
PlotText(string(str), font(args...))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Base.length(t::PlotText) = length(t.str)
|
||||||
annotations(::Void) = []
|
|
||||||
annotations(anns::AVec) = anns
|
|
||||||
annotations(anns) = Any[anns]
|
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
@ -314,9 +330,9 @@ immutable Stroke
|
|||||||
end
|
end
|
||||||
|
|
||||||
function stroke(args...; alpha = nothing)
|
function stroke(args...; alpha = nothing)
|
||||||
width = nothing
|
width = 1
|
||||||
color = nothing
|
color = :black
|
||||||
style = nothing
|
style = :solid
|
||||||
|
|
||||||
for arg in args
|
for arg in args
|
||||||
T = typeof(arg)
|
T = typeof(arg)
|
||||||
@ -350,8 +366,8 @@ immutable Brush
|
|||||||
end
|
end
|
||||||
|
|
||||||
function brush(args...; alpha = nothing)
|
function brush(args...; alpha = nothing)
|
||||||
size = nothing
|
size = 1
|
||||||
color = nothing
|
color = :black
|
||||||
|
|
||||||
for arg in args
|
for arg in args
|
||||||
T = typeof(arg)
|
T = typeof(arg)
|
||||||
@ -376,6 +392,109 @@ end
|
|||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
|
type SeriesAnnotations
|
||||||
|
strs::AbstractVector # the labels/names
|
||||||
|
font::Font
|
||||||
|
baseshape::Nullable
|
||||||
|
scalefactor::Tuple
|
||||||
|
end
|
||||||
|
function series_annotations(strs::AbstractVector, args...)
|
||||||
|
fnt = font()
|
||||||
|
shp = Nullable{Any}()
|
||||||
|
scalefactor = (1,1)
|
||||||
|
for arg in args
|
||||||
|
if isa(arg, Shape) || (isa(arg, AbstractVector) && eltype(arg) == Shape)
|
||||||
|
shp = Nullable(arg)
|
||||||
|
elseif isa(arg, Font)
|
||||||
|
fnt = arg
|
||||||
|
elseif isa(arg, Symbol) && haskey(_shapes, arg)
|
||||||
|
shp = _shapes[arg]
|
||||||
|
elseif isa(arg, Number)
|
||||||
|
scalefactor = (arg,arg)
|
||||||
|
elseif is_2tuple(arg)
|
||||||
|
scalefactor = arg
|
||||||
|
else
|
||||||
|
warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
# if scalefactor != 1
|
||||||
|
# for s in get(shp)
|
||||||
|
# scale!(s, scalefactor, scalefactor, (0,0))
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
SeriesAnnotations(strs, fnt, shp, scalefactor)
|
||||||
|
end
|
||||||
|
series_annotations(anns::SeriesAnnotations) = anns
|
||||||
|
series_annotations(::Void) = nothing
|
||||||
|
|
||||||
|
function series_annotations_shapes!(series::Series, scaletype::Symbol = :pixels)
|
||||||
|
anns = series[:series_annotations]
|
||||||
|
# msw,msh = anns.scalefactor
|
||||||
|
# ms = series[:markersize]
|
||||||
|
# msw,msh = if isa(ms, AbstractVector)
|
||||||
|
# 1,1
|
||||||
|
# elseif is_2tuple(ms)
|
||||||
|
# ms
|
||||||
|
# else
|
||||||
|
# ms,ms
|
||||||
|
# end
|
||||||
|
|
||||||
|
# @show msw msh
|
||||||
|
if anns != nothing && !isnull(anns.baseshape)
|
||||||
|
# we use baseshape to overwrite the markershape attribute
|
||||||
|
# with a list of custom shapes for each
|
||||||
|
msw,msh = anns.scalefactor
|
||||||
|
msize = Float64[]
|
||||||
|
shapes = Shape[begin
|
||||||
|
str = cycle(anns.strs,i)
|
||||||
|
|
||||||
|
# get the width and height of the string (in mm)
|
||||||
|
sw, sh = text_size(str, anns.font.pointsize)
|
||||||
|
|
||||||
|
# how much to scale the base shape?
|
||||||
|
# note: it's a rough assumption that the shape fills the unit box [-1,-1,1,1],
|
||||||
|
# so we scale the length-2 shape by 1/2 the total length
|
||||||
|
scalar = (backend() == PyPlotBackend() ? 1.7 : 1.0)
|
||||||
|
xscale = 0.5to_pixels(sw) * scalar
|
||||||
|
yscale = 0.5to_pixels(sh) * scalar
|
||||||
|
|
||||||
|
# we save the size of the larger direction to the markersize list,
|
||||||
|
# and then re-scale a copy of baseshape to match the w/h ratio
|
||||||
|
maxscale = max(xscale, yscale)
|
||||||
|
push!(msize, maxscale)
|
||||||
|
baseshape = cycle(get(anns.baseshape),i)
|
||||||
|
shape = scale(baseshape, msw*xscale/maxscale, msh*yscale/maxscale, (0,0))
|
||||||
|
end for i=1:length(anns.strs)]
|
||||||
|
series[:markershape] = shapes
|
||||||
|
series[:markersize] = msize
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
type EachAnn
|
||||||
|
anns
|
||||||
|
x
|
||||||
|
y
|
||||||
|
end
|
||||||
|
Base.start(ea::EachAnn) = 1
|
||||||
|
Base.done(ea::EachAnn, i) = ea.anns == nothing || isempty(ea.anns.strs) || i > length(ea.y)
|
||||||
|
function Base.next(ea::EachAnn, i)
|
||||||
|
tmp = cycle(ea.anns.strs,i)
|
||||||
|
str,fnt = if isa(tmp, PlotText)
|
||||||
|
tmp.str, tmp.font
|
||||||
|
else
|
||||||
|
tmp, ea.anns.font
|
||||||
|
end
|
||||||
|
((cycle(ea.x,i), cycle(ea.y,i), str, fnt), i+1)
|
||||||
|
end
|
||||||
|
|
||||||
|
annotations(::Void) = []
|
||||||
|
annotations(anns::AVec) = anns
|
||||||
|
annotations(anns) = Any[anns]
|
||||||
|
annotations(sa::SeriesAnnotations) = sa
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
"type which represents z-values for colors and sizes (and anything else that might come up)"
|
"type which represents z-values for colors and sizes (and anything else that might come up)"
|
||||||
immutable ZValues
|
immutable ZValues
|
||||||
values::Vector{Float64}
|
values::Vector{Float64}
|
||||||
@ -452,19 +571,25 @@ Base.eltype{T}(vol::Volume{T}) = T
|
|||||||
# style is :open or :closed (for now)
|
# style is :open or :closed (for now)
|
||||||
immutable Arrow
|
immutable Arrow
|
||||||
style::Symbol
|
style::Symbol
|
||||||
|
side::Symbol # :head (default), :tail, or :both
|
||||||
headlength::Float64
|
headlength::Float64
|
||||||
headwidth::Float64
|
headwidth::Float64
|
||||||
end
|
end
|
||||||
|
|
||||||
function arrow(args...)
|
function arrow(args...)
|
||||||
style = :simple
|
style = :simple
|
||||||
|
side = :head
|
||||||
headlength = 0.3
|
headlength = 0.3
|
||||||
headwidth = 0.3
|
headwidth = 0.3
|
||||||
setlength = false
|
setlength = false
|
||||||
for arg in args
|
for arg in args
|
||||||
T = typeof(arg)
|
T = typeof(arg)
|
||||||
if T == Symbol
|
if T == Symbol
|
||||||
|
if arg in (:head, :tail, :both)
|
||||||
|
side = arg
|
||||||
|
else
|
||||||
style = arg
|
style = arg
|
||||||
|
end
|
||||||
elseif T <: Number
|
elseif T <: Number
|
||||||
# first we apply to both, but if there's more, then only change width after the first number
|
# first we apply to both, but if there's more, then only change width after the first number
|
||||||
headwidth = Float64(arg)
|
headwidth = Float64(arg)
|
||||||
@ -478,7 +603,7 @@ function arrow(args...)
|
|||||||
warn("Skipped arrow arg $arg")
|
warn("Skipped arrow arg $arg")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
Arrow(style, headlength, headwidth)
|
Arrow(style, side, headlength, headwidth)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -523,39 +648,19 @@ end
|
|||||||
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
||||||
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
||||||
|
|
||||||
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
@deprecate curve_points coords
|
||||||
|
|
||||||
|
coords(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||||
|
|
||||||
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
|
# build a BezierCurve which leaves point p vertically upwards and arrives point q vertically upwards.
|
||||||
# may create a loop if necessary. Assumes the view is [0,1]
|
# may create a loop if necessary. Assumes the view is [0,1]
|
||||||
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
|
function directed_curve(args...; kw...)
|
||||||
mn = mean(p, q)
|
error("directed_curve has been moved to PlotRecipes")
|
||||||
diff = q - p
|
|
||||||
|
|
||||||
minx, maxx = minimum(xview), maximum(xview)
|
|
||||||
miny, maxy = minimum(yview), maximum(yview)
|
|
||||||
diffpct = P2(diff[1] / (maxx - minx),
|
|
||||||
diff[2] / (maxy - miny))
|
|
||||||
|
|
||||||
# these points give the initial/final "rise"
|
|
||||||
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
|
|
||||||
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
|
|
||||||
upper_control = p + vertical_offset
|
|
||||||
lower_control = q - vertical_offset
|
|
||||||
|
|
||||||
# try to figure out when to loop around vs just connecting straight
|
|
||||||
# TODO: choose loop direction based on sign of p[1]??
|
|
||||||
# x_close_together = abs(diffpct[1]) <= 0.05
|
|
||||||
p_is_higher = diff[2] <= 0
|
|
||||||
inside_control_points = if p_is_higher
|
|
||||||
# add curve points which will create a loop
|
|
||||||
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
|
|
||||||
inside_offset = P2(0.3 * (maxx - minx), 0)
|
|
||||||
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
|
|
||||||
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
|
|
||||||
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
|
|
||||||
else
|
|
||||||
[]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
BezierCurve([p, upper_control, inside_control_points..., lower_control, q])
|
function extrema_plus_buffer(v, buffmult = 0.2)
|
||||||
|
vmin,vmax = extrema(v)
|
||||||
|
vdiff = vmax-vmin
|
||||||
|
buffer = vdiff * buffmult
|
||||||
|
vmin - buffer, vmax + buffer
|
||||||
end
|
end
|
||||||
|
|||||||
@ -40,7 +40,7 @@ PlotExample("Colors",
|
|||||||
"Access predefined palettes (or build your own with the `colorscheme` method). Line/marker colors are auto-generated from the plot's palette, unless overridden. Set the `z` argument to turn on series gradients.",
|
"Access predefined palettes (or build your own with the `colorscheme` method). Line/marker colors are auto-generated from the plot's palette, unless overridden. Set the `z` argument to turn on series gradients.",
|
||||||
[:(begin
|
[:(begin
|
||||||
y = rand(100)
|
y = rand(100)
|
||||||
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=(0,:auto), α=0.6)
|
plot(0:10:100,rand(11,4),lab="lines",w=3,palette=:grays,fill=0, α=0.6)
|
||||||
scatter!(y, zcolor=abs(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs(y-0.5)+4, lab="grad")
|
scatter!(y, zcolor=abs(y-.5), m=(:heat,0.8,stroke(1,:green)), ms=10*abs(y-0.5)+4, lab="grad")
|
||||||
end)]
|
end)]
|
||||||
),
|
),
|
||||||
|
|||||||
@ -96,6 +96,32 @@ function Base.show(io::IO, bbox::BoundingBox)
|
|||||||
print(io, "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}")
|
print(io, "BBox{l,t,r,b,w,h = $(left(bbox)),$(top(bbox)), $(right(bbox)),$(bottom(bbox)), $(width(bbox)),$(height(bbox))}")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# -----------------------------------------------------------
|
||||||
|
|
||||||
|
# points combined by x/y, pct, and length
|
||||||
|
type MixedMeasures
|
||||||
|
xy::Float64
|
||||||
|
pct::Float64
|
||||||
|
len::AbsoluteLength
|
||||||
|
end
|
||||||
|
|
||||||
|
function resolve_mixed(mix::MixedMeasures, sp::Subplot, letter::Symbol)
|
||||||
|
xy = mix.xy
|
||||||
|
pct = mix.pct
|
||||||
|
if mix.len != 0mm
|
||||||
|
f = (letter == :x ? width : height)
|
||||||
|
totlen = f(plotarea(sp))
|
||||||
|
@show totlen
|
||||||
|
pct += mix.len / totlen
|
||||||
|
end
|
||||||
|
if pct != 0
|
||||||
|
amin, amax = axis_limits(sp[Symbol(letter,:axis)])
|
||||||
|
xy += pct * (amax-amin)
|
||||||
|
end
|
||||||
|
xy
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# -----------------------------------------------------------
|
# -----------------------------------------------------------
|
||||||
# AbstractLayout
|
# AbstractLayout
|
||||||
|
|
||||||
@ -692,9 +718,22 @@ function link_axes!(axes::Axis...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# figure out which subplots to link
|
||||||
|
function link_subplots(a::AbstractArray{AbstractLayout}, axissym::Symbol)
|
||||||
|
subplots = []
|
||||||
|
for l in a
|
||||||
|
if isa(l, Subplot)
|
||||||
|
push!(subplots, l)
|
||||||
|
elseif isa(l, GridLayout) && size(l) == (1,1)
|
||||||
|
push!(subplots, l[1,1])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
subplots
|
||||||
|
end
|
||||||
|
|
||||||
# for some vector or matrix of layouts, filter only the Subplots and link those axes
|
# for some vector or matrix of layouts, filter only the Subplots and link those axes
|
||||||
function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
|
function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
|
||||||
subplots = filter(l -> isa(l, Subplot), a)
|
subplots = link_subplots(a, axissym)
|
||||||
axes = [sp.attr[axissym] for sp in subplots]
|
axes = [sp.attr[axissym] for sp in subplots]
|
||||||
if length(axes) > 0
|
if length(axes) > 0
|
||||||
link_axes!(axes...)
|
link_axes!(axes...)
|
||||||
|
|||||||
@ -52,6 +52,16 @@ function tex(plt::Plot, fn::AbstractString)
|
|||||||
end
|
end
|
||||||
tex(fn::AbstractString) = tex(current(), fn)
|
tex(fn::AbstractString) = tex(current(), fn)
|
||||||
|
|
||||||
|
function html(plt::Plot, fn::AbstractString)
|
||||||
|
fn = addExtension(fn, "html")
|
||||||
|
io = open(fn, "w")
|
||||||
|
_use_remote[] = true
|
||||||
|
show(io, MIME("text/html"), plt)
|
||||||
|
_use_remote[] = false
|
||||||
|
close(io)
|
||||||
|
end
|
||||||
|
html(fn::AbstractString) = html(current(), fn)
|
||||||
|
|
||||||
|
|
||||||
# ----------------------------------------------------------------
|
# ----------------------------------------------------------------
|
||||||
|
|
||||||
@ -63,6 +73,7 @@ const _savemap = Dict(
|
|||||||
"ps" => ps,
|
"ps" => ps,
|
||||||
"eps" => eps,
|
"eps" => eps,
|
||||||
"tex" => tex,
|
"tex" => tex,
|
||||||
|
"html" => html,
|
||||||
)
|
)
|
||||||
|
|
||||||
function getExtension(fn::AbstractString)
|
function getExtension(fn::AbstractString)
|
||||||
@ -111,6 +122,13 @@ savefig(fn::AbstractString) = savefig(current(), fn)
|
|||||||
|
|
||||||
gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
|
gui(plt::Plot = current()) = display(PlotsDisplay(), plt)
|
||||||
|
|
||||||
|
# IJulia only... inline display
|
||||||
|
function inline(plt::Plot = current())
|
||||||
|
isijulia() || error("inline() is IJulia-only")
|
||||||
|
Main.IJulia.clear_output(true)
|
||||||
|
display(Main.IJulia.InlineDisplay(), plt)
|
||||||
|
end
|
||||||
|
|
||||||
function Base.display(::PlotsDisplay, plt::Plot)
|
function Base.display(::PlotsDisplay, plt::Plot)
|
||||||
prepare_output(plt)
|
prepare_output(plt)
|
||||||
_display(plt)
|
_display(plt)
|
||||||
@ -119,6 +137,13 @@ end
|
|||||||
# override the REPL display to open a gui window
|
# override the REPL display to open a gui window
|
||||||
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
|
Base.display(::Base.REPL.REPLDisplay, ::MIME"text/plain", plt::Plot) = gui(plt)
|
||||||
|
|
||||||
|
|
||||||
|
_do_plot_show(plt, showval::Bool) = showval && gui(plt)
|
||||||
|
function _do_plot_show(plt, showval::Symbol)
|
||||||
|
showval == :gui && gui(plt)
|
||||||
|
showval in (:inline,:ijulia) && inline(plt)
|
||||||
|
end
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
|
|
||||||
const _mimeformats = Dict(
|
const _mimeformats = Dict(
|
||||||
@ -172,6 +197,8 @@ for mime in keys(_mimeformats)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
closeall() = closeall(backend())
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------
|
# ---------------------------------------------------------
|
||||||
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
# A backup, if no PNG generation is defined, is to try to make a PDF and use FileIO to convert
|
||||||
|
|||||||
@ -283,7 +283,11 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
|||||||
|
|
||||||
# override subplot/axis args. `sp_attrs` take precendence
|
# override subplot/axis args. `sp_attrs` take precendence
|
||||||
for (idx,sp) in enumerate(plt.subplots)
|
for (idx,sp) in enumerate(plt.subplots)
|
||||||
attr = merge(d, get(sp_attrs, sp, KW()))
|
attr = if !haskey(d, :subplot) || d[:subplot] == idx
|
||||||
|
merge(d, get(sp_attrs, sp, KW()))
|
||||||
|
else
|
||||||
|
get(sp_attrs, sp, KW())
|
||||||
|
end
|
||||||
_update_subplot_args(plt, sp, attr, idx, false)
|
_update_subplot_args(plt, sp, attr, idx, false)
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -333,14 +337,17 @@ function _prepare_annotations(sp::Subplot, d::KW)
|
|||||||
# strip out series annotations (those which are based on series x/y coords)
|
# strip out series annotations (those which are based on series x/y coords)
|
||||||
# and add them to the subplot attr
|
# and add them to the subplot attr
|
||||||
sp_anns = annotations(sp[:annotations])
|
sp_anns = annotations(sp[:annotations])
|
||||||
anns = annotations(pop!(d, :series_annotations, []))
|
# series_anns = annotations(pop!(d, :series_annotations, []))
|
||||||
if length(anns) > 0
|
# if isa(series_anns, SeriesAnnotations)
|
||||||
x, y = d[:x], d[:y]
|
# series_anns.x = d[:x]
|
||||||
nx, ny, na = map(length, (x,y,anns))
|
# series_anns.y = d[:y]
|
||||||
n = max(nx, ny, na)
|
# elseif length(series_anns) > 0
|
||||||
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
|
# x, y = d[:x], d[:y]
|
||||||
end
|
# nx, ny, na = map(length, (x,y,series_anns))
|
||||||
sp.attr[:annotations] = vcat(sp_anns, anns)
|
# n = max(nx, ny, na)
|
||||||
|
# series_anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(series_anns[mod1(i,na)])) for i=1:n]
|
||||||
|
# end
|
||||||
|
# sp.attr[:annotations] = vcat(sp_anns, series_anns)
|
||||||
end
|
end
|
||||||
|
|
||||||
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||||
|
|||||||
27
src/plot.jl
27
src/plot.jl
@ -89,8 +89,16 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
|||||||
plt.o = _create_backend_figure(plt)
|
plt.o = _create_backend_figure(plt)
|
||||||
plt.init = true
|
plt.init = true
|
||||||
|
|
||||||
|
series_attr = KW()
|
||||||
|
for (k,v) in d
|
||||||
|
if haskey(_series_defaults, k)
|
||||||
|
series_attr[k] = pop!(d,k)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# create the layout and initialize the subplots
|
# create the layout and initialize the subplots
|
||||||
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
|
plt.layout, plt.subplots, plt.spmap = build_layout(layout, num_sp, copy(plts))
|
||||||
|
cmdidx = 1
|
||||||
for (idx, sp) in enumerate(plt.subplots)
|
for (idx, sp) in enumerate(plt.subplots)
|
||||||
_initialize_subplot(plt, sp)
|
_initialize_subplot(plt, sp)
|
||||||
serieslist = series_list(sp)
|
serieslist = series_list(sp)
|
||||||
@ -100,8 +108,11 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
|||||||
sp.plt = plt
|
sp.plt = plt
|
||||||
sp.attr[:subplot_index] = idx
|
sp.attr[:subplot_index] = idx
|
||||||
for series in serieslist
|
for series in serieslist
|
||||||
|
merge!(series.d, series_attr)
|
||||||
|
_add_defaults!(series.d, plt, sp, cmdidx)
|
||||||
push!(plt.series_list, series)
|
push!(plt.series_list, series)
|
||||||
_series_added(plt, series)
|
_series_added(plt, series)
|
||||||
|
cmdidx += 1
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -115,9 +126,7 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
|||||||
|
|
||||||
# finish up
|
# finish up
|
||||||
current(plt)
|
current(plt)
|
||||||
if get(d, :show, default(:show))
|
_do_plot_show(plt, get(d, :show, default(:show)))
|
||||||
gui()
|
|
||||||
end
|
|
||||||
plt
|
plt
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -150,6 +159,11 @@ end
|
|||||||
function _plot!(plt::Plot, d::KW, args::Tuple)
|
function _plot!(plt::Plot, d::KW, args::Tuple)
|
||||||
d[:plot_object] = plt
|
d[:plot_object] = plt
|
||||||
|
|
||||||
|
if !isempty(args) && !isdefined(Main, :StatPlots) &&
|
||||||
|
first(split(string(typeof(args[1])), ".")) == "DataFrames"
|
||||||
|
warn("You're trying to plot a DataFrame, but this functionality is provided by StatPlots")
|
||||||
|
end
|
||||||
|
|
||||||
# --------------------------------
|
# --------------------------------
|
||||||
# "USER RECIPES"
|
# "USER RECIPES"
|
||||||
# --------------------------------
|
# --------------------------------
|
||||||
@ -218,9 +232,10 @@ function _plot!(plt::Plot, d::KW, args::Tuple)
|
|||||||
current(plt)
|
current(plt)
|
||||||
|
|
||||||
# do we want to force display?
|
# do we want to force display?
|
||||||
if plt[:show]
|
# if plt[:show]
|
||||||
gui(plt)
|
# gui(plt)
|
||||||
end
|
# end
|
||||||
|
_do_plot_show(plt, plt[:show])
|
||||||
|
|
||||||
plt
|
plt
|
||||||
end
|
end
|
||||||
|
|||||||
298
src/recipes.jl
298
src/recipes.jl
@ -93,57 +93,11 @@ end
|
|||||||
|
|
||||||
# ----------------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
num_series(x::AMat) = size(x,2)
|
num_series(x::AMat) = size(x,2)
|
||||||
num_series(x) = 1
|
num_series(x) = 1
|
||||||
|
|
||||||
|
|
||||||
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T"))
|
RecipesBase.apply_recipe{T}(d::KW, ::Type{T}, plt::Plot) = throw(MethodError("Unmatched plot recipe: $T"))
|
||||||
|
|
||||||
|
|
||||||
# # TODO: remove when StatPlots is ready
|
|
||||||
# if is_installed("DataFrames")
|
|
||||||
# @eval begin
|
|
||||||
# import DataFrames
|
|
||||||
|
|
||||||
# # if it's one symbol, set the guide and return the column
|
|
||||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, sym::Symbol)
|
|
||||||
# get!(d, Symbol(letter * "guide"), string(sym))
|
|
||||||
# collect(df[sym])
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # if it's an array of symbols, set the labels and return a Vector{Any} of columns
|
|
||||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, syms::AbstractArray{Symbol})
|
|
||||||
# get!(d, :label, reshape(syms, 1, length(syms)))
|
|
||||||
# Any[collect(df[s]) for s in syms]
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # for anything else, no-op
|
|
||||||
# function handle_dfs(df::DataFrames.AbstractDataFrame, d::KW, letter, anything)
|
|
||||||
# anything
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # handle grouping by DataFrame column
|
|
||||||
# function extractGroupArgs(group::Symbol, df::DataFrames.AbstractDataFrame, args...)
|
|
||||||
# extractGroupArgs(collect(df[group]))
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # if a DataFrame is the first arg, lets swap symbols out for columns
|
|
||||||
# @recipe function f(df::DataFrames.AbstractDataFrame, args...)
|
|
||||||
# # if any of these attributes are symbols, swap out for the df column
|
|
||||||
# for k in (:fillrange, :line_z, :marker_z, :markersize, :ribbon, :weights, :xerror, :yerror)
|
|
||||||
# if haskey(d, k) && isa(d[k], Symbol)
|
|
||||||
# d[k] = collect(df[d[k]])
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # return a list of new arguments
|
|
||||||
# tuple(Any[handle_dfs(df, d, (i==1 ? "x" : i==2 ? "y" : "z"), arg) for (i,arg) in enumerate(args)]...)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@ -511,6 +465,11 @@ centers(v::AVec) = 0.5 * (v[1:end-1] + v[2:end])
|
|||||||
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
|
xedges, yedges, counts = my_hist_2d(x, y, d[:bins],
|
||||||
normed = d[:normalize],
|
normed = d[:normalize],
|
||||||
weights = d[:weights])
|
weights = d[:weights])
|
||||||
|
for (i,c) in enumerate(counts)
|
||||||
|
if c == 0
|
||||||
|
counts[i] = NaN
|
||||||
|
end
|
||||||
|
end
|
||||||
x := centers(xedges)
|
x := centers(xedges)
|
||||||
y := centers(yedges)
|
y := centers(yedges)
|
||||||
z := Surface(counts)
|
z := Surface(counts)
|
||||||
@ -536,179 +495,6 @@ end
|
|||||||
|
|
||||||
# note: don't add dependencies because this really isn't a drop-in replacement
|
# note: don't add dependencies because this really isn't a drop-in replacement
|
||||||
|
|
||||||
# # TODO: move boxplots and violin plots to StatPlots when it's ready
|
|
||||||
|
|
||||||
# # ---------------------------------------------------------------------------
|
|
||||||
# # Box Plot
|
|
||||||
|
|
||||||
# const _box_halfwidth = 0.4
|
|
||||||
|
|
||||||
# notch_width(q2, q4, N) = 1.58 * (q4-q2)/sqrt(N)
|
|
||||||
|
|
||||||
|
|
||||||
# @recipe function f(::Type{Val{:boxplot}}, x, y, z; notch=false, range=1.5)
|
|
||||||
# xsegs, ysegs = Segments(), Segments()
|
|
||||||
# glabels = sort(collect(unique(x)))
|
|
||||||
# warning = false
|
|
||||||
# outliers_x, outliers_y = zeros(0), zeros(0)
|
|
||||||
# for (i,glabel) in enumerate(glabels)
|
|
||||||
# # filter y
|
|
||||||
# values = y[filter(i -> cycle(x,i) == glabel, 1:length(y))]
|
|
||||||
|
|
||||||
# # compute quantiles
|
|
||||||
# q1,q2,q3,q4,q5 = quantile(values, linspace(0,1,5))
|
|
||||||
|
|
||||||
# # notch
|
|
||||||
# n = notch_width(q2, q4, length(values))
|
|
||||||
|
|
||||||
# # warn on inverted notches?
|
|
||||||
# if notch && !warning && ( (q2>(q3-n)) || (q4<(q3+n)) )
|
|
||||||
# warn("Boxplot's notch went outside hinges. Set notch to false.")
|
|
||||||
# warning = true # Show the warning only one time
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # make the shape
|
|
||||||
# center = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
|
||||||
# hw = d[:bar_width] == nothing ? _box_halfwidth : 0.5cycle(d[:bar_width], i)
|
|
||||||
# l, m, r = center - hw, center, center + hw
|
|
||||||
|
|
||||||
# # internal nodes for notches
|
|
||||||
# L, R = center - 0.5 * hw, center + 0.5 * hw
|
|
||||||
|
|
||||||
# # outliers
|
|
||||||
# if Float64(range) != 0.0 # if the range is 0.0, the whiskers will extend to the data
|
|
||||||
# limit = range*(q4-q2)
|
|
||||||
# inside = Float64[]
|
|
||||||
# for value in values
|
|
||||||
# if (value < (q2 - limit)) || (value > (q4 + limit))
|
|
||||||
# push!(outliers_y, value)
|
|
||||||
# push!(outliers_x, center)
|
|
||||||
# else
|
|
||||||
# push!(inside, value)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
# # change q1 and q5 to show outliers
|
|
||||||
# # using maximum and minimum values inside the limits
|
|
||||||
# q1, q5 = extrema(inside)
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # Box
|
|
||||||
# if notch
|
|
||||||
# push!(xsegs, m, l, r, m, m) # lower T
|
|
||||||
# push!(xsegs, l, l, L, R, r, r, l) # lower box
|
|
||||||
# push!(xsegs, l, l, L, R, r, r, l) # upper box
|
|
||||||
# push!(xsegs, m, l, r, m, m) # upper T
|
|
||||||
|
|
||||||
# push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
|
||||||
# push!(ysegs, q2, q3-n, q3, q3, q3-n, q2, q2) # lower box
|
|
||||||
# push!(ysegs, q4, q3+n, q3, q3, q3+n, q4, q4) # upper box
|
|
||||||
# push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
|
||||||
# else
|
|
||||||
# push!(xsegs, m, l, r, m, m) # lower T
|
|
||||||
# push!(xsegs, l, l, r, r, l) # lower box
|
|
||||||
# push!(xsegs, l, l, r, r, l) # upper box
|
|
||||||
# push!(xsegs, m, l, r, m, m) # upper T
|
|
||||||
|
|
||||||
# push!(ysegs, q1, q1, q1, q1, q2) # lower T
|
|
||||||
# push!(ysegs, q2, q3, q3, q2, q2) # lower box
|
|
||||||
# push!(ysegs, q4, q3, q3, q4, q4) # upper box
|
|
||||||
# push!(ysegs, q5, q5, q5, q5, q4) # upper T
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
# # Outliers
|
|
||||||
# @series begin
|
|
||||||
# seriestype := :scatter
|
|
||||||
# markershape := :circle
|
|
||||||
# markercolor := d[:fillcolor]
|
|
||||||
# markeralpha := d[:fillalpha]
|
|
||||||
# markerstrokecolor := d[:linecolor]
|
|
||||||
# markerstrokealpha := d[:linealpha]
|
|
||||||
# x := outliers_x
|
|
||||||
# y := outliers_y
|
|
||||||
# primary := false
|
|
||||||
# ()
|
|
||||||
# end
|
|
||||||
|
|
||||||
# seriestype := :shape
|
|
||||||
# x := xsegs.pts
|
|
||||||
# y := ysegs.pts
|
|
||||||
# ()
|
|
||||||
# end
|
|
||||||
# @deps boxplot shape scatter
|
|
||||||
|
|
||||||
# # ---------------------------------------------------------------------------
|
|
||||||
# # Violin Plot
|
|
||||||
|
|
||||||
# const _violin_warned = [false]
|
|
||||||
|
|
||||||
# # if the user has KernelDensity installed, use this for violin plots.
|
|
||||||
# # otherwise, just use a histogram
|
|
||||||
# if is_installed("KernelDensity")
|
|
||||||
# @eval import KernelDensity
|
|
||||||
# @eval function violin_coords(y; trim::Bool=false)
|
|
||||||
# kd = KernelDensity.kde(y, npoints = 200)
|
|
||||||
# if trim
|
|
||||||
# xmin, xmax = extrema(y)
|
|
||||||
# inside = Bool[ xmin <= x <= xmax for x in kd.x]
|
|
||||||
# return(kd.density[inside], kd.x[inside])
|
|
||||||
# end
|
|
||||||
# kd.density, kd.x
|
|
||||||
# end
|
|
||||||
# else
|
|
||||||
# @eval function violin_coords(y; trim::Bool=false)
|
|
||||||
# if !_violin_warned[1]
|
|
||||||
# warn("Install the KernelDensity package for best results.")
|
|
||||||
# _violin_warned[1] = true
|
|
||||||
# end
|
|
||||||
# edges, widths = my_hist(y, 10)
|
|
||||||
# centers = 0.5 * (edges[1:end-1] + edges[2:end])
|
|
||||||
# ymin, ymax = extrema(y)
|
|
||||||
# vcat(0.0, widths, 0.0), vcat(ymin, centers, ymax)
|
|
||||||
# end
|
|
||||||
# end
|
|
||||||
|
|
||||||
|
|
||||||
# @recipe function f(::Type{Val{:violin}}, x, y, z; trim=true)
|
|
||||||
# xsegs, ysegs = Segments(), Segments()
|
|
||||||
# glabels = sort(collect(unique(x)))
|
|
||||||
# for glabel in glabels
|
|
||||||
# widths, centers = violin_coords(y[filter(i -> cycle(x,i) == glabel, 1:length(y))], trim=trim)
|
|
||||||
# isempty(widths) && continue
|
|
||||||
|
|
||||||
# # normalize
|
|
||||||
# widths = _box_halfwidth * widths / maximum(widths)
|
|
||||||
|
|
||||||
# # make the violin
|
|
||||||
# xcenter = discrete_value!(d[:subplot][:xaxis], glabel)[1]
|
|
||||||
# xcoords = vcat(widths, -reverse(widths)) + xcenter
|
|
||||||
# ycoords = vcat(centers, reverse(centers))
|
|
||||||
|
|
||||||
# push!(xsegs, xcoords)
|
|
||||||
# push!(ysegs, ycoords)
|
|
||||||
# end
|
|
||||||
|
|
||||||
# seriestype := :shape
|
|
||||||
# x := xsegs.pts
|
|
||||||
# y := ysegs.pts
|
|
||||||
# ()
|
|
||||||
# end
|
|
||||||
# @deps violin shape
|
|
||||||
|
|
||||||
# # ---------------------------------------------------------------------------
|
|
||||||
# # density
|
|
||||||
|
|
||||||
# @recipe function f(::Type{Val{:density}}, x, y, z; trim=false)
|
|
||||||
# newx, newy = violin_coords(y, trim=trim)
|
|
||||||
# if isvertical(d)
|
|
||||||
# newx, newy = newy, newx
|
|
||||||
# end
|
|
||||||
# x := newx
|
|
||||||
# y := newy
|
|
||||||
# seriestype := :path
|
|
||||||
# ()
|
|
||||||
# end
|
|
||||||
# @deps density path
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# contourf - filled contours
|
# contourf - filled contours
|
||||||
@ -740,8 +526,7 @@ end
|
|||||||
|
|
||||||
function error_coords(xorig, yorig, ebar)
|
function error_coords(xorig, yorig, ebar)
|
||||||
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
|
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
|
||||||
x, y = zeros(0), zeros(0)
|
x, y = Array(float_extended_type(xorig), 0), Array(Float64, 0)
|
||||||
|
|
||||||
# for each point, create a line segment from the bottom to the top of the errorbar
|
# for each point, create a line segment from the bottom to the top of the errorbar
|
||||||
for i = 1:max(length(xorig), length(yorig))
|
for i = 1:max(length(xorig), length(yorig))
|
||||||
xi = cycle(xorig, i)
|
xi = cycle(xorig, i)
|
||||||
@ -947,15 +732,52 @@ end
|
|||||||
# series recipe or moved to PlotRecipes
|
# series recipe or moved to PlotRecipes
|
||||||
|
|
||||||
|
|
||||||
"Sparsity plot... heatmap of non-zero values of a matrix"
|
# "Sparsity plot... heatmap of non-zero values of a matrix"
|
||||||
function spy{T<:Real}(z::AMat{T}; kw...)
|
# function spy{T<:Real}(z::AMat{T}; kw...)
|
||||||
mat = map(zi->float(zi!=0), z)'
|
# mat = map(zi->float(zi!=0), z)'
|
||||||
xn, yn = size(mat)
|
# xn, yn = size(mat)
|
||||||
heatmap(mat; leg=false, yflip=true, aspect_ratio=:equal,
|
# heatmap(mat; leg=false, yflip=true, aspect_ratio=:equal,
|
||||||
xlim=(0.5, xn+0.5), ylim=(0.5, yn+0.5),
|
# xlim=(0.5, xn+0.5), ylim=(0.5, yn+0.5),
|
||||||
kw...)
|
# kw...)
|
||||||
|
# end
|
||||||
|
|
||||||
|
# Only allow matrices through, and make it seriestype :spy so the backend can
|
||||||
|
# optionally handle it natively.
|
||||||
|
|
||||||
|
@userplot Spy
|
||||||
|
|
||||||
|
@recipe function f(g::Spy)
|
||||||
|
@assert length(g.args) == 1 && typeof(g.args[1]) <: AbstractMatrix
|
||||||
|
seriestype := :spy
|
||||||
|
mat = g.args[1]
|
||||||
|
n,m = size(mat)
|
||||||
|
Plots.SliceIt, 1:m, 1:n, Surface(mat)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@recipe function f(::Type{Val{:spy}}, x,y,z)
|
||||||
|
yflip := true
|
||||||
|
aspect_ratio := 1
|
||||||
|
rs, cs, zs = findnz(z.surf)
|
||||||
|
xlim := extrema(cs)
|
||||||
|
ylim := extrema(rs)
|
||||||
|
if d[:markershape] == :none
|
||||||
|
markershape := :circle
|
||||||
|
end
|
||||||
|
if d[:markersize] == default(:markersize)
|
||||||
|
markersize := 1
|
||||||
|
end
|
||||||
|
markerstrokewidth := 0
|
||||||
|
marker_z := zs
|
||||||
|
label := ""
|
||||||
|
x := cs
|
||||||
|
y := rs
|
||||||
|
z := nothing
|
||||||
|
seriestype := :scatter
|
||||||
|
()
|
||||||
|
end
|
||||||
|
|
||||||
|
# -------------------------------------------------
|
||||||
|
|
||||||
"Adds a+bx... straight line over the current plot"
|
"Adds a+bx... straight line over the current plot"
|
||||||
function abline!(plt::Plot, a, b; kw...)
|
function abline!(plt::Plot, a, b; kw...)
|
||||||
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
|
plot!(plt, [extrema(plt)...], x -> b + a*x; kw...)
|
||||||
@ -967,14 +789,16 @@ abline!(args...; kw...) = abline!(current(), args...; kw...)
|
|||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
# Dates
|
# Dates
|
||||||
|
|
||||||
@recipe function f{T<:AbstractArray{Date}}(::Type{T}, dts::T)
|
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int,dt), dt -> string(convert(Date,dt)))
|
||||||
date_formatter = dt -> string(convert(Date, dt))
|
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int,dt), dt -> string(convert(DateTime,dt)))
|
||||||
xformatter := date_formatter
|
|
||||||
map(dt->convert(Int,dt), dts)
|
|
||||||
end
|
|
||||||
|
|
||||||
@recipe function f{T<:AbstractArray{DateTime}}(::Type{T}, dts::T)
|
# -------------------------------------------------
|
||||||
date_formatter = dt -> string(convert(DateTime, dt))
|
# Complex Numbers
|
||||||
xformatter := date_formatter
|
|
||||||
map(dt->convert(Int,dt), dts)
|
@userplot ComplexPlot
|
||||||
|
@recipe function f(cp::ComplexPlot)
|
||||||
|
xguide --> "Real Part"
|
||||||
|
yguide --> "Imaginary Part"
|
||||||
|
seriestype --> :scatter
|
||||||
|
real(cp.args[1]), imag(cp.args[1])
|
||||||
end
|
end
|
||||||
|
|||||||
@ -42,8 +42,8 @@ convertToAnyVector(v::Volume, d::KW) = Any[v], nothing
|
|||||||
# # vector of OHLC
|
# # vector of OHLC
|
||||||
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
# convertToAnyVector(v::AVec{OHLC}, d::KW) = Any[v], nothing
|
||||||
|
|
||||||
# dates
|
# # dates
|
||||||
convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
# convertToAnyVector{D<:Union{Date,DateTime}}(dts::AVec{D}, d::KW) = Any[dts], nothing
|
||||||
|
|
||||||
# list of things (maybe other vectors, functions, or something else)
|
# list of things (maybe other vectors, functions, or something else)
|
||||||
function convertToAnyVector(v::AVec, d::KW)
|
function convertToAnyVector(v::AVec, d::KW)
|
||||||
@ -322,18 +322,18 @@ end
|
|||||||
|
|
||||||
@recipe function f(shape::Shape)
|
@recipe function f(shape::Shape)
|
||||||
seriestype --> :shape
|
seriestype --> :shape
|
||||||
shape_coords(shape)
|
coords(shape)
|
||||||
end
|
end
|
||||||
|
|
||||||
@recipe function f(shapes::AVec{Shape})
|
@recipe function f(shapes::AVec{Shape})
|
||||||
seriestype --> :shape
|
seriestype --> :shape
|
||||||
shape_coords(shapes)
|
coords(shapes)
|
||||||
end
|
end
|
||||||
|
|
||||||
@recipe function f(shapes::AMat{Shape})
|
@recipe function f(shapes::AMat{Shape})
|
||||||
seriestype --> :shape
|
seriestype --> :shape
|
||||||
for j in 1:size(shapes,2)
|
for j in 1:size(shapes,2)
|
||||||
@series shape_coords(vec(shapes[:,j]))
|
@series coords(vec(shapes[:,j]))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -1,65 +1,40 @@
|
|||||||
|
|
||||||
const _invisible = RGBA(0,0,0,0)
|
function theme(s::Symbol; kw...)
|
||||||
|
# reset?
|
||||||
const _themes = KW(
|
if s == :none || s == :default
|
||||||
:default => KW(
|
PlotUtils._default_gradient[] = :inferno
|
||||||
:bg => :white,
|
default(;
|
||||||
:bglegend => :match,
|
bg = :white,
|
||||||
:bginside => :match,
|
bglegend = :match,
|
||||||
:bgoutside => :match,
|
bginside = :match,
|
||||||
:fg => :auto,
|
bgoutside = :match,
|
||||||
:fglegend => :match,
|
fg = :auto,
|
||||||
:fggrid => :match,
|
fglegend = :match,
|
||||||
:fgaxis => :match,
|
fggrid = :match,
|
||||||
:fgtext => :match,
|
fgaxis = :match,
|
||||||
:fgborder => :match,
|
fgtext = :match,
|
||||||
:fgguide => :match,
|
fgborder = :match,
|
||||||
|
fgguide = :match,
|
||||||
|
palette = :auto
|
||||||
)
|
)
|
||||||
)
|
return
|
||||||
|
|
||||||
function add_theme(sym::Symbol, theme::KW)
|
|
||||||
_themes[sym] = theme
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# add a new theme, using an existing theme as the base
|
# update the default gradient and other defaults
|
||||||
function add_theme(sym::Symbol;
|
thm = PlotThemes._themes[s]
|
||||||
base = :default, # start with this theme
|
if thm.gradient != nothing
|
||||||
bg = _themes[base][:bg],
|
PlotUtils._default_gradient[] = PlotThemes.gradient_name(s)
|
||||||
bglegend = _themes[base][:bglegend],
|
|
||||||
bginside = _themes[base][:bginside],
|
|
||||||
bgoutside = _themes[base][:bgoutside],
|
|
||||||
fg = _themes[base][:fg],
|
|
||||||
fglegend = _themes[base][:fglegend],
|
|
||||||
fggrid = _themes[base][:fggrid],
|
|
||||||
fgaxis = _themes[base][:fgaxis],
|
|
||||||
fgtext = _themes[base][:fgtext],
|
|
||||||
fgborder = _themes[base][:fgborder],
|
|
||||||
fgguide = _themes[base][:fgguide],
|
|
||||||
kw...)
|
|
||||||
_themes[sym] = merge(KW(
|
|
||||||
:bg => bg,
|
|
||||||
:bglegend => bglegend,
|
|
||||||
:bginside => bginside,
|
|
||||||
:bgoutside => bgoutside,
|
|
||||||
:fg => fg,
|
|
||||||
:fglegend => fglegend,
|
|
||||||
:fggrid => fggrid,
|
|
||||||
:fgaxis => fgaxis,
|
|
||||||
:fgtext => fgtext,
|
|
||||||
:fgborder => fgborder,
|
|
||||||
:fgguide => fgguide,
|
|
||||||
), KW(kw))
|
|
||||||
end
|
end
|
||||||
|
default(;
|
||||||
add_theme(:ggplot2,
|
bg = thm.bg_secondary,
|
||||||
bglegend = :lightgray,
|
bginside = thm.bg_primary,
|
||||||
bginside = :lightgray,
|
fg = thm.lines,
|
||||||
fg = :black,
|
fgtext = thm.text,
|
||||||
fggrid = :white,
|
fgguide = thm.text,
|
||||||
fgborder = _invisible,
|
fglegend = thm.text,
|
||||||
fgaxis = _invisible
|
palette = thm.palette,
|
||||||
|
kw...
|
||||||
)
|
)
|
||||||
|
|
||||||
function set_theme(sym::Symbol)
|
|
||||||
default(; _themes[sym]...)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@deprecate set_theme(s) theme(s)
|
||||||
|
|||||||
@ -90,10 +90,18 @@ end
|
|||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|
||||||
Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
|
Base.getindex(plt::Plot, i::Integer) = plt.subplots[i]
|
||||||
|
Base.length(plt::Plot) = length(plt.subplots)
|
||||||
|
Base.endof(plt::Plot) = length(plt)
|
||||||
|
|
||||||
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
|
Base.getindex(plt::Plot, r::Integer, c::Integer) = plt.layout[r,c]
|
||||||
|
Base.size(plt::Plot) = size(plt.layout)
|
||||||
|
Base.size(plt::Plot, i::Integer) = size(plt.layout)[i]
|
||||||
|
Base.ndims(plt::Plot) = 2
|
||||||
|
|
||||||
# attr(plt::Plot, k::Symbol) = plt.attr[k]
|
# attr(plt::Plot, k::Symbol) = plt.attr[k]
|
||||||
# attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
|
# attr!(plt::Plot, v, k::Symbol) = (plt.attr[k] = v)
|
||||||
|
|
||||||
Base.getindex(sp::Subplot, i::Integer) = series_list(sp)[i]
|
Base.getindex(sp::Subplot, i::Integer) = series_list(sp)[i]
|
||||||
|
Base.endof(sp::Subplot) = length(series_list(sp))
|
||||||
|
|
||||||
# -----------------------------------------------------------------------
|
# -----------------------------------------------------------------------
|
||||||
|
|||||||
41
src/utils.jl
41
src/utils.jl
@ -228,25 +228,34 @@ function Base.next(itr::SegmentsIterator, nextidx::Int)
|
|||||||
istart:iend, i
|
istart:iend, i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Find minimal type that can contain NaN and x
|
||||||
|
# To allow use of NaN separated segments with categorical x axis
|
||||||
|
|
||||||
|
float_extended_type{T}(x::AbstractArray{T}) = Union{T,Float64}
|
||||||
|
float_extended_type{T<:Real}(x::AbstractArray{T}) = Float64
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
nop() = nothing
|
nop() = nothing
|
||||||
notimpl() = error("This has not been implemented yet")
|
notimpl() = error("This has not been implemented yet")
|
||||||
|
|
||||||
Base.cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
isnothing(x::Void) = true
|
||||||
Base.cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
|
isnothing(x) = false
|
||||||
|
|
||||||
Base.cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
|
cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
||||||
Base.cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
|
cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
|
||||||
Base.cycle(v, idx::Int) = v
|
|
||||||
|
|
||||||
Base.cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
|
||||||
Base.cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
|
||||||
Base.cycle(v, indices::AVec{Int}) = fill(v, length(indices))
|
cycle(v, idx::Int) = v
|
||||||
|
|
||||||
Base.cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
|
cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||||
Base.cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
|
cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||||
|
cycle(v, indices::AVec{Int}) = fill(v, length(indices))
|
||||||
|
|
||||||
|
cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
|
||||||
|
cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
|
||||||
|
|
||||||
makevec(v::AVec) = v
|
makevec(v::AVec) = v
|
||||||
makevec{T}(v::T) = T[v]
|
makevec{T}(v::T) = T[v]
|
||||||
@ -460,7 +469,7 @@ ok(tup::Tuple) = ok(tup...)
|
|||||||
# compute one side of a fill range from a ribbon
|
# compute one side of a fill range from a ribbon
|
||||||
function make_fillrange_side(y, rib)
|
function make_fillrange_side(y, rib)
|
||||||
frs = zeros(length(y))
|
frs = zeros(length(y))
|
||||||
for (i, (yi, ri)) in enumerate(zip(y, cycle(rib)))
|
for (i, (yi, ri)) in enumerate(zip(y, Base.cycle(rib)))
|
||||||
frs[i] = yi + ri
|
frs[i] = yi + ri
|
||||||
end
|
end
|
||||||
frs
|
frs
|
||||||
@ -490,6 +499,8 @@ zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
|
|||||||
|
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
|
|
||||||
|
makekw(; kw...) = KW(kw)
|
||||||
|
|
||||||
wraptuple(x::Tuple) = x
|
wraptuple(x::Tuple) = x
|
||||||
wraptuple(x) = (x,)
|
wraptuple(x) = (x,)
|
||||||
|
|
||||||
@ -714,28 +725,28 @@ Base.push!(series::Series, xi, yi, zi) = (push_x!(series,xi); push_y!(series,yi)
|
|||||||
|
|
||||||
# -------------------------------------------------------
|
# -------------------------------------------------------
|
||||||
|
|
||||||
function update!(series::Series; kw...)
|
function attr!(series::Series; kw...)
|
||||||
d = KW(kw)
|
d = KW(kw)
|
||||||
preprocessArgs!(d)
|
preprocessArgs!(d)
|
||||||
for (k,v) in d
|
for (k,v) in d
|
||||||
if haskey(_series_defaults, k)
|
if haskey(_series_defaults, k)
|
||||||
series[k] = v
|
series[k] = v
|
||||||
else
|
else
|
||||||
warn("unused key $k in series update")
|
warn("unused key $k in series attr")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
_series_updated(series[:subplot].plt, series)
|
_series_updated(series[:subplot].plt, series)
|
||||||
series
|
series
|
||||||
end
|
end
|
||||||
|
|
||||||
function update!(sp::Subplot; kw...)
|
function attr!(sp::Subplot; kw...)
|
||||||
d = KW(kw)
|
d = KW(kw)
|
||||||
preprocessArgs!(d)
|
preprocessArgs!(d)
|
||||||
for (k,v) in d
|
for (k,v) in d
|
||||||
if haskey(_subplot_defaults, k)
|
if haskey(_subplot_defaults, k)
|
||||||
sp[k] = v
|
sp[k] = v
|
||||||
else
|
else
|
||||||
warn("unused key $k in subplot update")
|
warn("unused key $k in subplot attr")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
sp
|
sp
|
||||||
|
|||||||
@ -24,7 +24,7 @@ default(size=(500,300))
|
|||||||
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
# TODO: use julia's Condition type and the wait() and notify() functions to initialize a Window, then wait() on a condition that
|
||||||
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
# is referenced in a button press callback (the button clicked callback will call notify() on that condition)
|
||||||
|
|
||||||
const _current_plots_version = v"0.9.4"
|
const _current_plots_version = v"0.9.6"
|
||||||
|
|
||||||
|
|
||||||
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
function image_comparison_tests(pkg::Symbol, idx::Int; debug = false, popup = isinteractive(), sigma = [1,1], eps = 1e-2)
|
||||||
|
|||||||
@ -67,7 +67,7 @@ facts("UnicodePlots") do
|
|||||||
@fact backend() --> Plots.UnicodePlotsBackend()
|
@fact backend() --> Plots.UnicodePlotsBackend()
|
||||||
|
|
||||||
# lets just make sure it runs without error
|
# lets just make sure it runs without error
|
||||||
@fact isa(plot(rand(10)), Plot) --> true
|
@fact isa(plot(rand(10)), Plots.Plot) --> true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ end
|
|||||||
facts("Axes") do
|
facts("Axes") do
|
||||||
p = plot()
|
p = plot()
|
||||||
axis = p.subplots[1][:xaxis]
|
axis = p.subplots[1][:xaxis]
|
||||||
@fact typeof(axis) --> Axis
|
@fact typeof(axis) --> Plots.Axis
|
||||||
@fact Plots.discrete_value!(axis, "HI") --> (0.5, 1)
|
@fact Plots.discrete_value!(axis, "HI") --> (0.5, 1)
|
||||||
@fact Plots.discrete_value!(axis, :yo) --> (1.5, 2)
|
@fact Plots.discrete_value!(axis, :yo) --> (1.5, 2)
|
||||||
@fact extrema(axis) --> (0.5,1.5)
|
@fact extrema(axis) --> (0.5,1.5)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user