arrows with updated quiver recipe; only pyplot so far

This commit is contained in:
Thomas Breloff 2016-05-10 13:40:25 -04:00
parent 93a35d8fdc
commit 678dde710b
7 changed files with 139 additions and 68 deletions

View File

@ -114,6 +114,7 @@ export
brush, brush,
Surface, Surface,
OHLC, OHLC,
arrow,
colorscheme, colorscheme,
ColorScheme, ColorScheme,

View File

@ -150,10 +150,12 @@ _seriesDefaults[:xerror] = nothing
_seriesDefaults[:yerror] = nothing _seriesDefaults[:yerror] = nothing
_seriesDefaults[:ribbon] = nothing _seriesDefaults[:ribbon] = nothing
_seriesDefaults[:quiver] = nothing _seriesDefaults[:quiver] = nothing
_seriesDefaults[:arrow] = nothing # allows for adding arrows to line/path... call `arrow(args...)`
_seriesDefaults[:normalize] = false # do we want a normalized histogram? _seriesDefaults[:normalize] = false # do we want a normalized histogram?
_seriesDefaults[:weights] = nothing # optional weights for histograms (1D and 2D) _seriesDefaults[:weights] = nothing # optional weights for histograms (1D and 2D)
_seriesDefaults[:contours] = false # add contours to 3d surface and wireframe plots _seriesDefaults[:contours] = false # add contours to 3d surface and wireframe plots
_seriesDefaults[:match_dimensions] = false # do rows match x (true) or y (false) for heatmap/image/spy? see issue 196 _seriesDefaults[: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!
const _plotDefaults = KW() const _plotDefaults = KW()
@ -343,6 +345,7 @@ add_aliases(:yerror, :yerr, :yerrorbar, :err, :errorbar)
add_aliases(:quiver, :velocity, :quiver2d, :gradient) add_aliases(:quiver, :velocity, :quiver2d, :gradient)
add_aliases(:normalize, :norm, :normed, :normalized) add_aliases(:normalize, :norm, :normed, :normalized)
add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio) add_aliases(:aspect_ratio, :aspectratio, :axis_ratio, :axisratio, :ratio)
add_aliases(:match_dimensions, :transpose, :transpose_z)
# add all pluralized forms to the _keyAliases dict # add all pluralized forms to the _keyAliases dict
@ -464,6 +467,9 @@ function processLineArg(d::KW, arg)
arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color)) arg.color == nothing || (d[:fillcolor] = arg.color == :auto ? :auto : colorscheme(arg.color))
arg.alpha == nothing || (d[:fillalpha] = arg.alpha) arg.alpha == nothing || (d[:fillalpha] = arg.alpha)
elseif typeof(arg) <: Arrow || arg in (:arrow, :arrows)
d[:arrow] = arg
# linealpha # linealpha
elseif allAlphas(arg) elseif allAlphas(arg)
d[:linealpha] = arg d[:linealpha] = arg

View File

@ -29,7 +29,7 @@ supportedArgs(::PyPlotBackend) = [
:grid, :legend, :colorbar, :grid, :legend, :colorbar,
:marker_z, :levels, :marker_z, :levels,
:xerror, :yerror, :xerror, :yerror,
:ribbon, :quiver, :ribbon, :quiver, :arrow,
:orientation, :orientation,
:overwrite_figure, :overwrite_figure,
:polar, :polar,
@ -151,7 +151,7 @@ function buildPyPlotPath(x, y)
for i=1:n for i=1:n
mat[i,1] = x[i] mat[i,1] = x[i]
mat[i,2] = y[i] mat[i,2] = y[i]
nan = !isfinite(x[i]) || !isfinite(y[i]) nan = !ok(x[i], y[i])
codes[i] = if nan codes[i] = if nan
_path_CLOSEPOLY _path_CLOSEPOLY
else else
@ -415,9 +415,46 @@ function _add_series(pkg::PyPlotBackend, plt::Plot, d::KW)
color = pylinecolor(d), color = pylinecolor(d),
linewidth = d[:linewidth], linewidth = d[:linewidth],
linestyle = getPyPlotLineStyle(lt, d[:linestyle]), linestyle = getPyPlotLineStyle(lt, d[:linestyle]),
solid_capstyle = "round",
dash_capstyle = "round",
drawstyle = getPyPlotStepStyle(lt) drawstyle = getPyPlotStepStyle(lt)
)[1] )[1]
push!(handles, handle) push!(handles, handle)
if d[:arrow] != nothing
if !is3d(d) # TODO: handle 3d later
n = length(x)
a = d[:arrow]
@assert typeof(a) == Arrow
arrowprops = KW(
# :arrowstyle => (a.style == :open ? "->" : (a.style == :closed ? "-|>" : string(a.style))),
:arrowstyle => "simple,head_length=$(a.headlength),head_width=$(a.headwidth)",
# :arrowstyle => "simple",
:shrinkA => 0,
:shrinkB => 0,
:edgecolor => pylinecolor(d),
:facecolor => pylinecolor(d),
:linewidth => d[:linewidth],
:linestyle => getPyPlotLineStyle(lt, d[:linestyle]),
# :head_length => a.headlength,
# :head_width => a.headwidth,
)
for i=2:n
xystart = (x[i-1], y[i-1])
xyend = (x[i], y[i])
if ok(xystart) && ok(xyend)
if i==n || !ok(x[i+1], y[i+1])
# add the arrow from xystart to xyend
ax[:annotate]("",
xytext = (0.001xystart[1] + 0.999xyend[1], 0.001xystart[2] + 0.999xyend[2]),
xy = xyend,
arrowprops = arrowprops
)
end
end
end
end
end
end end
end end

View File

@ -398,6 +398,38 @@ type OHLC{T<:Real}
close::T close::T
end end
# -----------------------------------------------------------------------
# style is :open or :closed (for now)
immutable Arrow
style::Symbol
headlength::Float64
headwidth::Float64
end
function arrow(args...)
style = :simple
headlength = 0.3
headwidth = 0.2
for arg in args
T = typeof(arg)
if T == Symbol
style = arg
elseif T <: Number
headlength = headwidth = Float64(arg)
elseif T <: Tuple && length(arg) == 2
headlength, headwidth = Float64(arg[1]), Float64(arg[2])
else
warn("Skipped arrow arg $arg")
end
end
Arrow(style, headlength, headwidth)
end
# -----------------------------------------------------------------------
# @require FixedSizeArrays begin # @require FixedSizeArrays begin

View File

@ -320,69 +320,46 @@ end
# quiver # quiver
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}}) # function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
# d[:label] = "" function quiver_using_arrows(d::KW)
# d[:linetype] = :scatter d[:label] = ""
# d[:linetype] = :path
# # create a second series to draw the arrow shaft if !isa(d[:arrow], Arrow)
# dpath = copy(d) d[:arrow] = arrow()
# error_style!(dpath) end
# dpath[:markershape] = :none
#
# velocity = error_zipit(d[:quiver])
# xorig, yorig = d[:x], d[:y]
#
# # for each point, we create an arrow of velocity vi, translated to the x/y coordinates
# # x, y = zeros(0), zeros(0)
# paths = P2[]
# arrows = P2[]
# arrowshapes = Shape[]
# for i = 1:max(length(xorig), length(yorig))
#
# # get the starting position
# xi = get_mod(xorig, i)
# yi = get_mod(yorig, i)
# p = P2(xi, yi)
#
# # get the velocity
# vi = get_mod(velocity, i)
# vx, vy = if istuple(vi)
# first(vi), last(vi)
# elseif isscalar(vi)
# vi, vi
# else
# error("unexpected vi type $(typeof(vi)) for quiver: $vi")
# end
# v = P2(vx, vy)
#
# nanappend!(paths, [p, p+v])
# push!(arrows, p+v)
# push!(arrowshapes, makearrowhead(compute_angle(v)))
#
# # # dist = sqrt(vx^2 + vy^2)
# # dist = norm(v)
# # arrow_h = 0.1dist # height of arrowhead
# # arrow_w = 0.5arrow_h # halfwidth of arrowhead
# # U1 = v ./ dist # vector of arrowhead height
# # U2 = P2(-U1[2], U1[1]) # vector of arrowhead halfwidth
# # U1 *= arrow_h
# # U2 *= arrow_w
# #
# # append!(pts, P2(xi, yi) .+ P2[(0,0), v-U1, v-U1+U2, v, v-U1-U2, v-U1, (NaN,NaN)])
# # # a1 = v - arrow_h * U1 + arrow_w * U2
# # # a2 = v - arrow_h * U1 - arrow_w * U2
# # # nanappend!(x, xi + [0.0, vx, a1[1], a2[1], vx])
# # # nanappend!(y, yi + [0.0, vy, a1[2], a2[2], vy])
# end
#
# # d[:x], d[:y] = Plots.unzip(pts)
# dpath[:x], dpath[:y] = Plots.unzip(paths)
# d[:x], d[:y] = Plots.unzip(arrows)
# d[:markershape] = arrowshapes
#
# KW[dpath, d]
# end
function apply_series_recipe(d::KW, ::Type{Val{:quiver}}) velocity = error_zipit(d[:quiver])
xorig, yorig = d[:x], d[:y]
# for each point, we create an arrow of velocity vi, translated to the x/y coordinates
x, y = zeros(0), zeros(0)
for i = 1:max(length(xorig), length(yorig))
# get the starting position
xi = get_mod(xorig, i)
yi = get_mod(yorig, i)
# get the velocity
vi = get_mod(velocity, i)
vx, vy = if istuple(vi)
first(vi), last(vi)
elseif isscalar(vi)
vi, vi
elseif isa(vi,Function)
vi(xi, yi)
else
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
end
# add the points
nanappend!(x, [xi, xi+vx, NaN])
nanappend!(y, [yi, yi+vy, NaN])
end
d[:x], d[:y] = x, y
KW[d]
end
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
function quiver_using_hack(d::KW)
d[:label] = "" d[:label] = ""
d[:linetype] = :shape d[:linetype] = :shape
@ -427,6 +404,13 @@ function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
KW[d] KW[d]
end end
function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
if :arrow in supportedArgs()
quiver_using_arrows(d)
else
quiver_using_hack(d)
end
end
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

View File

@ -184,12 +184,18 @@ function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
end end
# handle quiver plots # handle quiver plots
if lt == :quiver # either a series of velocity vectors are passed in (`:quiver` keyword),
d[:linetype] = lt = :path # or we just add arrows to the path
d[:linewidth] = 0
end # if lt == :quiver
# d[:linetype] = lt = :path
# d[:linewidth] = 0
# end
if get(d, :quiver, nothing) != nothing if get(d, :quiver, nothing) != nothing
append!(ret, apply_series_recipe(copy(d), Val{:quiver})) append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
elseif lt == :quiver
d[:linetype] = lt = :path
d[:arrow] = arrow()
end end
# now that we've processed a given series... optionally split into # now that we've processed a given series... optionally split into

View File

@ -291,6 +291,11 @@ function transpose_z(d::KW, z, transpose_on_match::Bool = true)
end end
end end
function ok(x::Number, y::Number, z::Number = 0)
isfinite(x) && isfinite(y) && isfinite(z)
end
ok(tup::Tuple) = ok(tup...)
# --------------------------------------------------------------- # ---------------------------------------------------------------