Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 436ab89d2d | |||
| 74c2db4909 | |||
| e12c3f332e | |||
| 34a64db91d | |||
| e2a4a1e2ea | |||
| 22f12316ef | |||
| e24acece49 | |||
| dd7462bd3f | |||
| da7e0cba3e | |||
| 50219aa872 | |||
| 000660b43a | |||
| fbeaa59f18 | |||
| 578e601368 | |||
| 247fe27f03 | |||
| ee2e807cc2 | |||
| c4a880c870 | |||
| 4eb6fac881 | |||
| 0ab37f49fa | |||
| 376ee02041 | |||
| c779dc6577 | |||
| ad71e81338 | |||
| 9f2ba09fc4 | |||
| 1e6cee2c5d | |||
| 4dc742489d | |||
| d751b985ac | |||
| 8a58190cce | |||
| 612ca15fc0 | |||
| 2200b76054 | |||
| 08771395b8 | |||
| e3c06cad57 | |||
| 4dfadeaf15 | |||
| af1896dc36 | |||
| 350ffdee25 | |||
| 281b92c262 | |||
| 75f1cf8cbb | |||
| bf0f44773f | |||
| dfb89ef7f5 | |||
| 5ef7126a3a | |||
| c9835a53e3 | |||
| 5ea48e6fcd | |||
| 1d1a703e3c | |||
| 8110fec4ef | |||
| 67ecf4e350 | |||
| e8ed0a3034 | |||
| add962c8ea | |||
| 9bed393210 | |||
| 3aac2b2895 | |||
| aa6814a6d3 | |||
| d291582ad9 | |||
| ebc591747a | |||
| 456602b79f | |||
| b1835a91af | |||
| 8c6c81e8d0 | |||
| 874e94218c |
+1
-2
@@ -2,10 +2,9 @@
|
||||
language: julia
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
# - osx
|
||||
julia:
|
||||
- 0.5
|
||||
- nightly
|
||||
matrix:
|
||||
allow_failures:
|
||||
- julia: nightly
|
||||
|
||||
+26
-37
@@ -1,6 +1,4 @@
|
||||
|
||||
__precompile__()
|
||||
|
||||
module Plots
|
||||
|
||||
using Reexport
|
||||
@@ -12,18 +10,10 @@ using Base.Meta
|
||||
import Showoff
|
||||
|
||||
export
|
||||
AbstractPlot,
|
||||
Plot,
|
||||
Subplot,
|
||||
AbstractLayout,
|
||||
GridLayout,
|
||||
grid,
|
||||
EmptyLayout,
|
||||
bbox,
|
||||
plotarea,
|
||||
@layout,
|
||||
AVec,
|
||||
AMat,
|
||||
KW,
|
||||
|
||||
wrap,
|
||||
@@ -31,7 +21,7 @@ export
|
||||
|
||||
plot,
|
||||
plot!,
|
||||
update!,
|
||||
attr!,
|
||||
|
||||
current,
|
||||
default,
|
||||
@@ -79,7 +69,6 @@ export
|
||||
Shape,
|
||||
text,
|
||||
font,
|
||||
Axis,
|
||||
stroke,
|
||||
brush,
|
||||
Surface,
|
||||
@@ -91,6 +80,7 @@ export
|
||||
Animation,
|
||||
frame,
|
||||
gif,
|
||||
animate,
|
||||
@animate,
|
||||
@gif,
|
||||
|
||||
@@ -105,9 +95,7 @@ export
|
||||
center,
|
||||
P2,
|
||||
P3,
|
||||
BezierCurve,
|
||||
curve_points,
|
||||
directed_curve
|
||||
BezierCurve
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
@@ -201,28 +189,29 @@ yflip!(flip::Bool = true; kw...) = plot!(; yflip = flip
|
||||
xaxis!(args...; kw...) = plot!(; xaxis = args, kw...)
|
||||
yaxis!(args...; kw...) = plot!(; yaxis = args, kw...)
|
||||
|
||||
title!(plt::Plot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||
xlabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
||||
ylabel!(plt::Plot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
||||
xlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
|
||||
ylims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
|
||||
zlims!{T<:Real,S<:Real}(plt::Plot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
|
||||
xlims!(plt::Plot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
||||
ylims!(plt::Plot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
||||
zlims!(plt::Plot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
||||
xticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
|
||||
yticks!{T<:Real}(plt::Plot, ticks::AVec{T}; kw...) = plot!(plt; yticks = ticks, kw...)
|
||||
xticks!{T<:Real,S<:AbstractString}(plt::Plot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; xticks = (ticks,labels), kw...)
|
||||
yticks!{T<:Real,S<:AbstractString}(plt::Plot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
|
||||
annotate!(plt::Plot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
annotate!{T<:Tuple}(plt::Plot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
xflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
||||
yflip!(plt::Plot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
||||
xaxis!(plt::Plot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
||||
yaxis!(plt::Plot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||
|
||||
let PlotOrSubplot = Union{Plot, Subplot}
|
||||
title!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; title = s, kw...)
|
||||
xlabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; xlabel = s, kw...)
|
||||
ylabel!(plt::PlotOrSubplot, s::AbstractString; kw...) = plot!(plt; ylabel = s, kw...)
|
||||
xlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; xlims = lims, kw...)
|
||||
ylims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; ylims = lims, kw...)
|
||||
zlims!{T<:Real,S<:Real}(plt::PlotOrSubplot, lims::Tuple{T,S}; kw...) = plot!(plt; zlims = lims, kw...)
|
||||
xlims!(plt::PlotOrSubplot, xmin::Real, xmax::Real; kw...) = plot!(plt; xlims = (xmin,xmax), kw...)
|
||||
ylims!(plt::PlotOrSubplot, ymin::Real, ymax::Real; kw...) = plot!(plt; ylims = (ymin,ymax), kw...)
|
||||
zlims!(plt::PlotOrSubplot, zmin::Real, zmax::Real; kw...) = plot!(plt; zlims = (zmin,zmax), kw...)
|
||||
xticks!{T<:Real}(plt::PlotOrSubplot, ticks::AVec{T}; kw...) = plot!(plt; xticks = ticks, kw...)
|
||||
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...)
|
||||
yticks!{T<:Real,S<:AbstractString}(plt::PlotOrSubplot,
|
||||
ticks::AVec{T}, labels::AVec{S}; kw...) = plot!(plt; yticks = (ticks,labels), kw...)
|
||||
annotate!(plt::PlotOrSubplot, anns...; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
annotate!{T<:Tuple}(plt::PlotOrSubplot, anns::AVec{T}; kw...) = plot!(plt; annotation = anns, kw...)
|
||||
xflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; xflip = flip, kw...)
|
||||
yflip!(plt::PlotOrSubplot, flip::Bool = true; kw...) = plot!(plt; yflip = flip, kw...)
|
||||
xaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; xaxis = args, kw...)
|
||||
yaxis!(plt::PlotOrSubplot, args...; kw...) = plot!(plt; yaxis = args, kw...)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------
|
||||
|
||||
+28
-1
@@ -16,6 +16,33 @@ function frame{P<:AbstractPlot}(anim::Animation, plt::P=current())
|
||||
push!(anim.frames, filename)
|
||||
end
|
||||
|
||||
giffn() = (isijulia() ? "tmp.gif" : tempname()*".gif")
|
||||
|
||||
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,7 +51,7 @@ immutable AnimatedGif
|
||||
filename::String
|
||||
end
|
||||
|
||||
function gif(anim::Animation, fn = (isijulia() ? "tmp.gif" : tempname()*".gif"); fps::Integer = 20, loop::Integer = 0)
|
||||
function gif(anim::Animation, fn = giffn(); fps::Integer = 20, loop::Integer = 0)
|
||||
fn = abspath(fn)
|
||||
|
||||
try
|
||||
|
||||
+1
-1
@@ -19,7 +19,7 @@ const _arg_desc = KW(
|
||||
:markersize => "Number or AbstractVector. Size (radius pixels) of the markers.",
|
||||
:markerstrokestyle => "Symbol. Style of the marker stroke (border). Choose from $(_allStyles)",
|
||||
: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.",
|
||||
: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?",
|
||||
|
||||
+27
-6
@@ -123,6 +123,16 @@ const _markerAliases = Dict{Symbol,Symbol}(
|
||||
:dtri => :dtriangle,
|
||||
:downtri => :dtriangle,
|
||||
:downtriangle => :dtriangle,
|
||||
:> => :rtriangle,
|
||||
:rt => :rtriangle,
|
||||
:rtri => :rtriangle,
|
||||
:righttri => :rtriangle,
|
||||
:righttriangle => :rtriangle,
|
||||
:< => :ltriangle,
|
||||
:lt => :ltriangle,
|
||||
:ltri => :ltriangle,
|
||||
:lighttri => :ltriangle,
|
||||
:lighttriangle => :ltriangle,
|
||||
# :+ => :cross,
|
||||
:plus => :cross,
|
||||
# :x => :xcross,
|
||||
@@ -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
|
||||
# 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?
|
||||
: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
|
||||
# one logical series to be broken up (path and markers, for example)
|
||||
:hover => nothing, # text to display when hovering over the data points
|
||||
@@ -388,7 +398,7 @@ add_aliases(:foreground_color_guide, :fg_guide, :fgguide, :fgcolor_guide, :fg_co
|
||||
# alphas
|
||||
add_aliases(:seriesalpha, :alpha, :α, :opacity)
|
||||
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(:fillalpha, :fa, :falpha, :fα, :fillopacity, :fopacity)
|
||||
|
||||
@@ -440,7 +450,7 @@ add_aliases(:match_dimensions, :transpose, :transpose_z)
|
||||
add_aliases(:subplot, :sp, :subplt, :splt)
|
||||
add_aliases(:projection, :proj)
|
||||
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(:orientation, :direction, :dir)
|
||||
add_aliases(:inset_subplots, :inset, :floating)
|
||||
@@ -692,6 +702,11 @@ function preprocessArgs!(d::KW)
|
||||
end
|
||||
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
|
||||
|
||||
if haskey(d, :arrow)
|
||||
@@ -1029,8 +1044,14 @@ end
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
function _update_subplot_periphery(sp::Subplot, anns::AVec)
|
||||
# extend annotations
|
||||
sp.attr[:annotations] = vcat(anns, sp[:annotations])
|
||||
# extend annotations, and ensure we always have a (x,y,PlotText) tuple
|
||||
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
|
||||
sp.attr[:legend] = convertLegendValue(sp.attr[:legend])
|
||||
@@ -1094,7 +1115,7 @@ function _update_axis(axis::Axis, d_in::KW, letter::Symbol, subplot_index::Int)
|
||||
end
|
||||
|
||||
# update the axis
|
||||
update!(axis, args...; kw...)
|
||||
attr!(axis, args...; kw...)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
+5
-2
@@ -29,7 +29,7 @@ function Axis(sp::Subplot, letter::Symbol, args...; kw...)
|
||||
d[:discrete_values] = []
|
||||
|
||||
# update the defaults
|
||||
update!(Axis([sp], d), args...; kw...)
|
||||
attr!(Axis([sp], d), args...; kw...)
|
||||
end
|
||||
|
||||
function get_axis(sp::Subplot, letter::Symbol)
|
||||
@@ -83,7 +83,7 @@ function process_axis_arg!(d::KW, arg, letter = "")
|
||||
end
|
||||
|
||||
# update an Axis object with magic args and keywords
|
||||
function update!(axis::Axis, args...; kw...)
|
||||
function attr!(axis::Axis, args...; kw...)
|
||||
# first process args
|
||||
d = axis.d
|
||||
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)
|
||||
amax = amin + 1.0
|
||||
end
|
||||
if !isfinite(amin) && !isfinite(amax)
|
||||
amin, amax = 0.0, 1.0
|
||||
end
|
||||
if should_widen
|
||||
widen(amin, amax)
|
||||
else
|
||||
|
||||
+28
-8
@@ -51,6 +51,21 @@ _before_layout_calcs(plt::Plot) = nothing
|
||||
title_padding(sp::Subplot) = sp[:title] == "" ? 0mm : sp[:titlefont].pointsize * pt
|
||||
guide_padding(axis::Axis) = axis[:guide] == "" ? 0mm : axis[:guidefont].pointsize * pt
|
||||
|
||||
"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
|
||||
function tick_padding(axis::Axis)
|
||||
ticks = get_ticks(axis)
|
||||
@@ -58,19 +73,24 @@ function tick_padding(axis::Axis)
|
||||
0mm
|
||||
else
|
||||
vals, labs = ticks
|
||||
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
|
||||
isempty(labs) && return 0mm
|
||||
# ptsz = axis[:tickfont].pointsize * pt
|
||||
longest_label = maximum(length(lab) for lab in labs)
|
||||
labelwidth = 0.8longest_label * ptsz
|
||||
|
||||
# generalize by "rotating" y labels
|
||||
rot = axis[:rotation] + (axis[:letter] == :y ? 90 : 0)
|
||||
|
||||
# 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
|
||||
# # we need to compute the size of the ticks generically
|
||||
# # this means computing the bounding box and then getting the width/height
|
||||
# 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
|
||||
|
||||
|
||||
+456
-192
File diff suppressed because it is too large
Load Diff
+53
-20
@@ -30,6 +30,7 @@ const _gr_attr = merge_with_base_supported([
|
||||
:normalize, :weights,
|
||||
:inset_subplots,
|
||||
:bar_width,
|
||||
:arrow,
|
||||
])
|
||||
const _gr_seriestype = [
|
||||
: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
|
||||
# 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
|
||||
n = length(x)
|
||||
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 istart > 0 && iend > 0
|
||||
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
|
||||
break
|
||||
end
|
||||
@@ -263,11 +270,13 @@ end
|
||||
|
||||
# draw ONE 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)
|
||||
xi, yi = GR.wctondc(xi, yi)
|
||||
GR.fillarea(xi + sx * 0.0015msize,
|
||||
yi + sy * 0.0015msize)
|
||||
ms_ndc_x, ms_ndc_y = gr_pixels_to_ndc(msize, msize)
|
||||
GR.fillarea(xi .+ sx .* ms_ndc_x,
|
||||
yi .+ sy .* ms_ndc_y)
|
||||
GR.selntran(1)
|
||||
end
|
||||
|
||||
@@ -281,10 +290,11 @@ end
|
||||
|
||||
# draw the markers, one at a time
|
||||
function gr_draw_markers(series::Series, x, y, msize, mz)
|
||||
shape = series[:markershape]
|
||||
if shape != :none
|
||||
shapes = series[:markershape]
|
||||
if shapes != :none
|
||||
for i=1:length(x)
|
||||
msi = cycle(msize, i)
|
||||
shape = cycle(shapes, i)
|
||||
cfunc = isa(shape, Shape) ? gr_set_fillcolor : gr_set_markercolor
|
||||
cfuncind = isa(shape, Shape) ? GR.setfillcolorind : GR.setmarkercolorind
|
||||
|
||||
@@ -364,6 +374,9 @@ end
|
||||
# values are [xmin, xmax, ymin, ymax]. they range [0,1].
|
||||
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)
|
||||
viewport = zeros(4)
|
||||
viewport[1] = viewport_canvas[2] * (left(bb) / w)
|
||||
@@ -418,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_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
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------------------
|
||||
|
||||
@@ -445,6 +465,7 @@ function gr_display(plt::Plot)
|
||||
# compute the viewport_canvas, normalized to the larger dimension
|
||||
viewport_canvas = Float64[0,1,0,1]
|
||||
w, h = plt[:size]
|
||||
gr_plot_size[:] = [w, h]
|
||||
if w > h
|
||||
ratio = float(h) / w
|
||||
msize = display_width_ratio * w
|
||||
@@ -542,16 +563,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# set the scale flags and window
|
||||
xmin, xmax, ymin, ymax = data_lims
|
||||
scale = 0
|
||||
scaleop = 0
|
||||
xtick, ytick = 1, 1
|
||||
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 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)
|
||||
yaxis[:scale] == :log10 && (scale |= GR.OPTION_Y_LOG)
|
||||
xaxis[:flip] && (scale |= GR.OPTION_FLIP_X)
|
||||
yaxis[:flip] && (scale |= GR.OPTION_FLIP_Y)
|
||||
if scale & GR.OPTION_X_LOG == 0
|
||||
xaxis[:scale] == :log10 && (scaleop |= GR.OPTION_X_LOG)
|
||||
yaxis[:scale] == :log10 && (scaleop |= GR.OPTION_Y_LOG)
|
||||
xaxis[:flip] && (scaleop |= GR.OPTION_FLIP_X)
|
||||
yaxis[:flip] && (scaleop |= GR.OPTION_FLIP_Y)
|
||||
if scaleop & GR.OPTION_X_LOG == 0
|
||||
majorx = 1 #5
|
||||
xtick = GR.tick(xmin, xmax) / majorx
|
||||
else
|
||||
@@ -559,7 +580,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
xtick = 2 # scientific notation
|
||||
majorx = 2 # no minor grid lines
|
||||
end
|
||||
if scale & GR.OPTION_Y_LOG == 0
|
||||
if scaleop & GR.OPTION_Y_LOG == 0
|
||||
majory = 1 #5
|
||||
ytick = GR.tick(ymin, ymax) / majory
|
||||
else
|
||||
@@ -570,7 +591,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
|
||||
# NOTE: setwindow sets the "data coordinate" limits of the current "viewport"
|
||||
GR.setwindow(xmin, xmax, ymin, ymax)
|
||||
GR.setscale(scale)
|
||||
GR.setscale(scaleop)
|
||||
end
|
||||
|
||||
# draw the axes
|
||||
@@ -730,12 +751,16 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
x, y, z = series[:x], series[:y], series[:z]
|
||||
frng = series[:fillrange]
|
||||
|
||||
# add custom frame shapes to markershape?
|
||||
series_annotations_shapes!(series)
|
||||
# -------------------------------------------------------
|
||||
|
||||
# recompute data
|
||||
if typeof(z) <: Surface
|
||||
if st == :heatmap
|
||||
expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
|
||||
expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
|
||||
end
|
||||
# if st == :heatmap
|
||||
# expand_extrema!(sp[:xaxis], (x[1]-0.5*(x[2]-x[1]), x[end]+0.5*(x[end]-x[end-1])))
|
||||
# expand_extrema!(sp[:yaxis], (y[1]-0.5*(y[2]-y[1]), y[end]+0.5*(y[end]-y[end-1])))
|
||||
# end
|
||||
z = vec(transpose_z(series, z.surf, false))
|
||||
elseif ispolar(sp)
|
||||
if frng != nothing
|
||||
@@ -764,7 +789,8 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
# draw the line(s)
|
||||
if st == :path
|
||||
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
|
||||
|
||||
@@ -828,7 +854,7 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
round(Int, blue(c) * 255) << 16 +
|
||||
round(Int, green(c) * 255) << 8 +
|
||||
round(Int, red(c) * 255) ), colors)
|
||||
GR.drawimage(xmin, xmax, ymin, ymax, length(x), length(y), rgba)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, length(x), length(y), rgba)
|
||||
cmap && gr_colorbar(sp)
|
||||
|
||||
elseif st in (:path3d, :scatter3d)
|
||||
@@ -929,6 +955,13 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
|
||||
GR.drawimage(xmin, xmax, ymax, ymin, w, h, rgba)
|
||||
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()
|
||||
end
|
||||
|
||||
|
||||
+13
-1
@@ -304,6 +304,18 @@ function plotly_layout(plt::Plot)
|
||||
# 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
|
||||
# for sargs in seriesargs
|
||||
# a = sargs[:arrow]
|
||||
@@ -363,7 +375,7 @@ function plotly_close_shapes(x, y)
|
||||
xs, ys = nansplit(x), nansplit(y)
|
||||
for i=1:length(xs)
|
||||
shape = Shape(xs[i], ys[i])
|
||||
xs[i], ys[i] = shape_coords(shape)
|
||||
xs[i], ys[i] = coords(shape)
|
||||
end
|
||||
nanvcat(xs), nanvcat(ys)
|
||||
end
|
||||
|
||||
@@ -40,7 +40,11 @@ end
|
||||
|
||||
|
||||
function _create_backend_figure(plt::Plot{PlotlyJSBackend})
|
||||
PlotlyJS.plot()
|
||||
if !isplotnull() && plt[:overwrite_figure] && isa(current().o, PlotlyJS.SyncPlot)
|
||||
PlotlyJS.SyncPlot(PlotlyJS.Plot(), current().o.view)
|
||||
else
|
||||
PlotlyJS.plot()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -59,7 +63,7 @@ function _series_updated(plt::Plot{PlotlyJSBackend}, series::Series)
|
||||
kw = KW(xsym => (series.d[:x],), ysym => (series.d[:y],))
|
||||
z = series[:z]
|
||||
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
|
||||
PlotlyJS.restyle!(
|
||||
plt.o,
|
||||
@@ -97,3 +101,10 @@ _show(io::IO, ::MIME"image/eps", plt::Plot{PlotlyJSBackend}) = plotlyjs_save_hac
|
||||
function _display(plt::Plot{PlotlyJSBackend})
|
||||
display(plt.o)
|
||||
end
|
||||
|
||||
|
||||
function closeall(::PlotlyJSBackend)
|
||||
if !isplotnull() && isa(current().o, PlotlyJS.SyncPlot)
|
||||
close(current().o)
|
||||
end
|
||||
end
|
||||
|
||||
+50
-21
@@ -142,7 +142,7 @@ function py_linestyle(seriestype::Symbol, linestyle::Symbol)
|
||||
end
|
||||
|
||||
function py_marker(marker::Shape)
|
||||
x, y = shape_coords(marker)
|
||||
x, y = coords(marker)
|
||||
n = length(x)
|
||||
mat = zeros(n+1,2)
|
||||
for i=1:n
|
||||
@@ -445,6 +445,9 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
error("Only numbers and vectors are supported with levels keyword")
|
||||
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
|
||||
|
||||
# line plot
|
||||
@@ -560,16 +563,46 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
else
|
||||
xyargs
|
||||
end
|
||||
handle = ax[:scatter](xyargs...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(series[:markershape]),
|
||||
s = py_dpi_scale(plt, series[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(series),
|
||||
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
|
||||
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...;
|
||||
label = series[:label],
|
||||
zorder = series[:series_plotindex] + 0.5,
|
||||
marker = py_marker(series[:markershape]),
|
||||
s = py_dpi_scale(plt, series[:markersize] .^ 2),
|
||||
edgecolors = py_markerstrokecolor(series),
|
||||
linewidths = py_dpi_scale(plt, series[:markerstrokewidth]),
|
||||
extrakw...
|
||||
)
|
||||
push!(handles, handle)
|
||||
end
|
||||
end
|
||||
|
||||
if st == :hexbin
|
||||
@@ -770,16 +803,6 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
push!(handle, ax[:add_patch](patches))
|
||||
end
|
||||
end
|
||||
# path = py_path(x, y)
|
||||
# patches = pypatches.pymember("PathPatch")(path;
|
||||
# label = series[:label],
|
||||
# zorder = series[:series_plotindex],
|
||||
# edgecolor = py_linecolor(series),
|
||||
# facecolor = py_fillcolor(series),
|
||||
# linewidth = py_dpi_scale(plt, series[:linewidth]),
|
||||
# fill = true
|
||||
# )
|
||||
# handle = ax[:add_patch](patches)
|
||||
push!(handles, handle)
|
||||
end
|
||||
|
||||
@@ -849,6 +872,12 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
||||
)
|
||||
push!(handles, handle)
|
||||
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
|
||||
|
||||
# --------------------------------------------------------------------------
|
||||
|
||||
+169
-64
@@ -23,21 +23,24 @@ immutable Shape
|
||||
# end
|
||||
end
|
||||
Shape(verts::AVec) = Shape(unzip(verts)...)
|
||||
Shape(s::Shape) = deepcopy(s)
|
||||
|
||||
get_xs(shape::Shape) = shape.x
|
||||
get_ys(shape::Shape) = 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
|
||||
end
|
||||
|
||||
function shape_coords(shapes::AVec{Shape})
|
||||
function coords(shapes::AVec{Shape})
|
||||
length(shapes) == 0 && return zeros(0), zeros(0)
|
||||
xs = map(get_xs, 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]
|
||||
nanappend!(x, shape.x)
|
||||
nanappend!(y, shape.y)
|
||||
@@ -72,13 +75,13 @@ function makestar(n; offset = -0.5, radius = 1.0)
|
||||
z2 = z1 + π / (n)
|
||||
outercircle = partialcircle(z1, z1 + 2π, n+1, radius)
|
||||
innercircle = partialcircle(z2, z2 + 2π, n+1, 0.4radius)
|
||||
Shape(weave(outercircle, innercircle)[1:end-2])
|
||||
Shape(weave(outercircle, innercircle))
|
||||
end
|
||||
|
||||
"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)
|
||||
z = offset * π
|
||||
Shape(partialcircle(z, z + 2π, n+1, radius)[1:end-1])
|
||||
Shape(partialcircle(z, z + 2π, n+1, radius))
|
||||
end
|
||||
|
||||
|
||||
@@ -88,7 +91,7 @@ function makecross(; offset = -0.5, radius = 1.0)
|
||||
outercircle = partialcircle(z1, z1 + 2π, 9, radius)
|
||||
innercircle = partialcircle(z2, z2 + 2π, 5, 0.5radius)
|
||||
Shape(weave(outercircle, innercircle,
|
||||
ordering=Vector[outercircle,innercircle,outercircle])[1:end-2])
|
||||
ordering=Vector[outercircle,innercircle,outercircle]))
|
||||
end
|
||||
|
||||
|
||||
@@ -110,6 +113,8 @@ const _shape_keys = Symbol[
|
||||
:xcross,
|
||||
:utriangle,
|
||||
:dtriangle,
|
||||
:rtriangle,
|
||||
:ltriangle,
|
||||
:pentagon,
|
||||
:heptagon,
|
||||
:octagon,
|
||||
@@ -127,8 +132,10 @@ const _shapes = KW(
|
||||
:circle => makeshape(20),
|
||||
:rect => makeshape(4, offset=-0.25),
|
||||
:diamond => makeshape(4),
|
||||
:utriangle => makeshape(3),
|
||||
:dtriangle => makeshape(3, offset=0.5),
|
||||
:utriangle => 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),
|
||||
:hexagon => makeshape(6),
|
||||
:heptagon => makeshape(7),
|
||||
@@ -143,12 +150,14 @@ for n in [4,5,6,7,8]
|
||||
_shapes[Symbol("star$n")] = makestar(n)
|
||||
end
|
||||
|
||||
Shape(k::Symbol) = deepcopy(_shapes[k])
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
||||
# uses the centroid calculation from https://en.wikipedia.org/wiki/Centroid#Centroid_of_polygon
|
||||
function center(shape::Shape)
|
||||
x, y = shape_coords(shape)
|
||||
x, y = coords(shape)
|
||||
n = length(x)
|
||||
A, Cx, Cy = 0.0, 0.0, 0.0
|
||||
for i=1:n
|
||||
@@ -166,7 +175,7 @@ function center(shape::Shape)
|
||||
end
|
||||
|
||||
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
|
||||
for i=1:length(sx)
|
||||
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))
|
||||
shapecopy = deepcopy(shape)
|
||||
scale!(shape, x, y, c)
|
||||
scale!(shapecopy, x, y, c)
|
||||
end
|
||||
|
||||
function translate!(shape::Shape, x::Real, y::Real = x)
|
||||
sx, sy = shape_coords(shape)
|
||||
sx, sy = coords(shape)
|
||||
for i=1:length(sx)
|
||||
sx[i] += x
|
||||
sy[i] += y
|
||||
@@ -191,7 +200,7 @@ end
|
||||
|
||||
function translate(shape::Shape, x::Real, y::Real = x)
|
||||
shapecopy = deepcopy(shape)
|
||||
translate!(shape, x, y)
|
||||
translate!(shapecopy, x, y)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function rotate!(shape::Shape, Θ::Real, c = center(shape))
|
||||
x, y = shape_coords(shape)
|
||||
x, y = coords(shape)
|
||||
cx, cy = c
|
||||
for i=1:length(x)
|
||||
xi = rotate_x(x[i], y[i], Θ, cx, cy)
|
||||
@@ -226,7 +235,7 @@ end
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
|
||||
immutable Font
|
||||
type Font
|
||||
family::AbstractString
|
||||
pointsize::Int
|
||||
halign::Symbol
|
||||
@@ -283,6 +292,17 @@ function font(args...)
|
||||
Font(family, pointsize, halign, valign, rotation, color)
|
||||
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"
|
||||
immutable PlotText
|
||||
str::AbstractString
|
||||
@@ -296,11 +316,7 @@ function text(str, args...)
|
||||
PlotText(string(str), font(args...))
|
||||
end
|
||||
|
||||
|
||||
annotations(::Void) = []
|
||||
annotations(anns::AVec) = anns
|
||||
annotations(anns) = Any[anns]
|
||||
|
||||
Base.length(t::PlotText) = length(t.str)
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
@@ -314,9 +330,9 @@ immutable Stroke
|
||||
end
|
||||
|
||||
function stroke(args...; alpha = nothing)
|
||||
width = nothing
|
||||
color = nothing
|
||||
style = nothing
|
||||
width = 1
|
||||
color = :black
|
||||
style = :solid
|
||||
|
||||
for arg in args
|
||||
T = typeof(arg)
|
||||
@@ -350,8 +366,8 @@ immutable Brush
|
||||
end
|
||||
|
||||
function brush(args...; alpha = nothing)
|
||||
size = nothing
|
||||
color = nothing
|
||||
size = 1
|
||||
color = :black
|
||||
|
||||
for arg in args
|
||||
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)"
|
||||
immutable ZValues
|
||||
values::Vector{Float64}
|
||||
@@ -452,19 +571,25 @@ Base.eltype{T}(vol::Volume{T}) = T
|
||||
# style is :open or :closed (for now)
|
||||
immutable Arrow
|
||||
style::Symbol
|
||||
side::Symbol # :head (default), :tail, or :both
|
||||
headlength::Float64
|
||||
headwidth::Float64
|
||||
end
|
||||
|
||||
function arrow(args...)
|
||||
style = :simple
|
||||
side = :head
|
||||
headlength = 0.3
|
||||
headwidth = 0.3
|
||||
setlength = false
|
||||
for arg in args
|
||||
T = typeof(arg)
|
||||
if T == Symbol
|
||||
style = arg
|
||||
if arg in (:head, :tail, :both)
|
||||
side = arg
|
||||
else
|
||||
style = arg
|
||||
end
|
||||
elseif T <: Number
|
||||
# first we apply to both, but if there's more, then only change width after the first number
|
||||
headwidth = Float64(arg)
|
||||
@@ -478,7 +603,7 @@ function arrow(args...)
|
||||
warn("Skipped arrow arg $arg")
|
||||
end
|
||||
end
|
||||
Arrow(style, headlength, headwidth)
|
||||
Arrow(style, side, headlength, headwidth)
|
||||
end
|
||||
|
||||
|
||||
@@ -508,54 +633,34 @@ end
|
||||
# -----------------------------------------------------------------------
|
||||
|
||||
type BezierCurve{T <: FixedSizeArrays.Vec}
|
||||
control_points::Vector{T}
|
||||
control_points::Vector{T}
|
||||
end
|
||||
|
||||
function (bc::BezierCurve)(t::Real)
|
||||
p = zero(P2)
|
||||
n = length(bc.control_points)-1
|
||||
for i in 0:n
|
||||
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
|
||||
end
|
||||
p
|
||||
p = zero(P2)
|
||||
n = length(bc.control_points)-1
|
||||
for i in 0:n
|
||||
p += bc.control_points[i+1] * binomial(n, i) * (1-t)^(n-i) * t^i
|
||||
end
|
||||
p
|
||||
end
|
||||
|
||||
Base.mean(x::Real, y::Real) = 0.5*(x+y)
|
||||
Base.mean{N,T<:Real}(ps::FixedSizeArrays.Vec{N,T}...) = sum(ps) / length(ps)
|
||||
|
||||
curve_points(curve::BezierCurve, n::Integer = 30; range = [0,1]) = map(curve, linspace(range..., n))
|
||||
@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.
|
||||
# may create a loop if necessary. Assumes the view is [0,1]
|
||||
function directed_curve(p::P2, q::P2; xview = 0:1, yview = 0:1)
|
||||
mn = mean(p, q)
|
||||
diff = q - p
|
||||
|
||||
minx, maxx = minimum(xview), maximum(xview)
|
||||
miny, maxy = minimum(yview), maximum(yview)
|
||||
diffpct = P2(diff[1] / (maxx - minx),
|
||||
diff[2] / (maxy - miny))
|
||||
|
||||
# these points give the initial/final "rise"
|
||||
# vertical_offset = P2(0, (maxy - miny) * max(0.03, min(abs(0.5diffpct[2]), 1.0)))
|
||||
vertical_offset = P2(0, max(0.15, 0.5norm(diff)))
|
||||
upper_control = p + vertical_offset
|
||||
lower_control = q - vertical_offset
|
||||
|
||||
# try to figure out when to loop around vs just connecting straight
|
||||
# TODO: choose loop direction based on sign of p[1]??
|
||||
# x_close_together = abs(diffpct[1]) <= 0.05
|
||||
p_is_higher = diff[2] <= 0
|
||||
inside_control_points = if p_is_higher
|
||||
# add curve points which will create a loop
|
||||
sgn = mn[1] < 0.5 * (maxx + minx) ? -1 : 1
|
||||
inside_offset = P2(0.3 * (maxx - minx), 0)
|
||||
additional_offset = P2(sgn * diff[1], 0) # make it even loopier
|
||||
[upper_control + sgn * (inside_offset + max(0, additional_offset)),
|
||||
lower_control + sgn * (inside_offset + max(0, -additional_offset))]
|
||||
else
|
||||
[]
|
||||
function directed_curve(args...; kw...)
|
||||
error("directed_curve has been moved to PlotRecipes")
|
||||
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
|
||||
|
||||
+1
-1
@@ -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.",
|
||||
[:(begin
|
||||
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")
|
||||
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))}")
|
||||
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
|
||||
|
||||
|
||||
+16
-9
@@ -283,7 +283,11 @@ function _subplot_setup(plt::Plot, d::KW, kw_list::Vector{KW})
|
||||
|
||||
# override subplot/axis args. `sp_attrs` take precendence
|
||||
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)
|
||||
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)
|
||||
# and add them to the subplot attr
|
||||
sp_anns = annotations(sp[:annotations])
|
||||
anns = annotations(pop!(d, :series_annotations, []))
|
||||
if length(anns) > 0
|
||||
x, y = d[:x], d[:y]
|
||||
nx, ny, na = map(length, (x,y,anns))
|
||||
n = max(nx, ny, na)
|
||||
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
|
||||
end
|
||||
sp.attr[:annotations] = vcat(sp_anns, anns)
|
||||
# series_anns = annotations(pop!(d, :series_annotations, []))
|
||||
# if isa(series_anns, SeriesAnnotations)
|
||||
# series_anns.x = d[:x]
|
||||
# series_anns.y = d[:y]
|
||||
# elseif length(series_anns) > 0
|
||||
# x, y = d[:x], d[:y]
|
||||
# nx, ny, na = map(length, (x,y,series_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
|
||||
|
||||
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)
|
||||
|
||||
@@ -792,3 +792,14 @@ abline!(args...; kw...) = abline!(current(), args...; kw...)
|
||||
|
||||
@recipe f(::Type{Date}, dt::Date) = (dt -> convert(Int,dt), dt -> string(convert(Date,dt)))
|
||||
@recipe f(::Type{DateTime}, dt::DateTime) = (dt -> convert(Int,dt), dt -> string(convert(DateTime,dt)))
|
||||
|
||||
# -------------------------------------------------
|
||||
# Complex Numbers
|
||||
|
||||
@userplot ComplexPlot
|
||||
@recipe function f(cp::ComplexPlot)
|
||||
xguide --> "Real Part"
|
||||
yguide --> "Imaginary Part"
|
||||
linetype --> :scatter
|
||||
real(cp.args[1]), imag(cp.args[1])
|
||||
end
|
||||
|
||||
+3
-3
@@ -322,18 +322,18 @@ end
|
||||
|
||||
@recipe function f(shape::Shape)
|
||||
seriestype --> :shape
|
||||
shape_coords(shape)
|
||||
coords(shape)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AVec{Shape})
|
||||
seriestype --> :shape
|
||||
shape_coords(shapes)
|
||||
coords(shapes)
|
||||
end
|
||||
|
||||
@recipe function f(shapes::AMat{Shape})
|
||||
seriestype --> :shape
|
||||
for j in 1:size(shapes,2)
|
||||
@series shape_coords(vec(shapes[:,j]))
|
||||
@series coords(vec(shapes[:,j]))
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
+1
-1
@@ -23,7 +23,7 @@ function theme(s::Symbol; kw...)
|
||||
# update the default gradient and other defaults
|
||||
thm = PlotThemes._themes[s]
|
||||
if thm.gradient != nothing
|
||||
PlotUtils._default_gradient[] = s
|
||||
PlotUtils._default_gradient[] = PlotThemes.gradient_name(s)
|
||||
end
|
||||
default(;
|
||||
bg = thm.bg_secondary,
|
||||
|
||||
+20
-15
@@ -234,19 +234,22 @@ end
|
||||
nop() = nothing
|
||||
notimpl() = error("This has not been implemented yet")
|
||||
|
||||
Base.cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
||||
Base.cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
|
||||
isnothing(x::Void) = true
|
||||
isnothing(x) = false
|
||||
|
||||
Base.cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
|
||||
Base.cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
|
||||
Base.cycle(v, idx::Int) = v
|
||||
cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
|
||||
cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
|
||||
|
||||
Base.cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
Base.cycle(v::AMat, indices::AVec{Int}) = map(i -> cycle(v,i), indices)
|
||||
Base.cycle(v, indices::AVec{Int}) = fill(v, length(indices))
|
||||
cycle(v::AVec, idx::Int) = v[mod1(idx, length(v))]
|
||||
cycle(v::AMat, idx::Int) = size(v,1) == 1 ? v[1, mod1(idx, size(v,2))] : v[:, mod1(idx, size(v,2))]
|
||||
cycle(v, idx::Int) = v
|
||||
|
||||
Base.cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
|
||||
Base.cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, indices)
|
||||
cycle(v::AVec, indices::AVec{Int}) = map(i -> cycle(v,i), 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{T}(v::T) = T[v]
|
||||
@@ -460,7 +463,7 @@ ok(tup::Tuple) = ok(tup...)
|
||||
# compute one side of a fill range from a ribbon
|
||||
function make_fillrange_side(y, rib)
|
||||
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
|
||||
end
|
||||
frs
|
||||
@@ -490,6 +493,8 @@ zlims(sp_idx::Int = 1) = zlims(current(), sp_idx)
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
makekw(; kw...) = KW(kw)
|
||||
|
||||
wraptuple(x::Tuple) = x
|
||||
wraptuple(x) = (x,)
|
||||
|
||||
@@ -714,28 +719,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)
|
||||
preprocessArgs!(d)
|
||||
for (k,v) in d
|
||||
if haskey(_series_defaults, k)
|
||||
series[k] = v
|
||||
else
|
||||
warn("unused key $k in series update")
|
||||
warn("unused key $k in series attr")
|
||||
end
|
||||
end
|
||||
_series_updated(series[:subplot].plt, series)
|
||||
series
|
||||
end
|
||||
|
||||
function update!(sp::Subplot; kw...)
|
||||
function attr!(sp::Subplot; kw...)
|
||||
d = KW(kw)
|
||||
preprocessArgs!(d)
|
||||
for (k,v) in d
|
||||
if haskey(_subplot_defaults, k)
|
||||
sp[k] = v
|
||||
else
|
||||
warn("unused key $k in subplot update")
|
||||
warn("unused key $k in subplot attr")
|
||||
end
|
||||
end
|
||||
sp
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ facts("UnicodePlots") do
|
||||
@fact backend() --> Plots.UnicodePlotsBackend()
|
||||
|
||||
# lets just make sure it runs without error
|
||||
@fact isa(plot(rand(10)), Plot) --> true
|
||||
@fact isa(plot(rand(10)), Plots.Plot) --> true
|
||||
end
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ end
|
||||
facts("Axes") do
|
||||
p = plot()
|
||||
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, :yo) --> (1.5, 2)
|
||||
@fact extrema(axis) --> (0.5,1.5)
|
||||
|
||||
Reference in New Issue
Block a user