added quiver plot/arg; fixed gadfly shapes to allow vector of shapes
This commit is contained in:
parent
9bfcb64542
commit
2fc973245d
@ -70,6 +70,8 @@ export
|
||||
boxplot!,
|
||||
violin,
|
||||
violin!,
|
||||
quiver,
|
||||
quiver!,
|
||||
|
||||
title!,
|
||||
xlabel!,
|
||||
@ -196,6 +198,8 @@ boxplot(args...; kw...) = plot(args...; kw..., linetype = :box)
|
||||
boxplot!(args...; kw...) = plot!(args...; kw..., linetype = :box)
|
||||
violin(args...; kw...) = plot(args...; kw..., linetype = :violin)
|
||||
violin!(args...; kw...) = plot!(args...; kw..., linetype = :violin)
|
||||
quiver(args...; kw...) = plot(args...; kw..., linetype = :quiver)
|
||||
quiver!(args...; kw...) = plot!(args...; kw..., linetype = :quiver)
|
||||
|
||||
|
||||
title!(s::AbstractString; kw...) = plot!(; title = s, kw...)
|
||||
|
||||
@ -11,7 +11,7 @@ const _3dTypes = [:path3d, :scatter3d, :surface, :wireframe]
|
||||
const _allTypes = vcat([
|
||||
:none, :line, :path, :steppre, :steppost, :sticks, :scatter,
|
||||
:heatmap, :hexbin, :hist, :hist2d, :hist3d, :density, :bar, :hline, :vline, :ohlc,
|
||||
:contour, :pie, :shape, :box, :violin
|
||||
:contour, :pie, :shape, :box, :violin, :quiver
|
||||
], _3dTypes)
|
||||
@compat const _typeAliases = KW(
|
||||
:n => :none,
|
||||
@ -39,6 +39,8 @@ const _allTypes = vcat([
|
||||
:poly => :shape,
|
||||
:polygon => :shape,
|
||||
:boxplot => :box,
|
||||
:velocity => :quiver,
|
||||
:gradient => :quiver,
|
||||
)
|
||||
|
||||
like_histogram(linetype::Symbol) = linetype in (:hist, :density)
|
||||
@ -148,6 +150,7 @@ _seriesDefaults[:orientation] = :vertical
|
||||
_seriesDefaults[:xerror] = nothing
|
||||
_seriesDefaults[:yerror] = nothing
|
||||
_seriesDefaults[:ribbon] = nothing
|
||||
_seriesDefaults[:quiver] = nothing
|
||||
|
||||
|
||||
const _plotDefaults = KW()
|
||||
@ -333,6 +336,9 @@ end
|
||||
:xerrorbar => :xerror,
|
||||
:yerr => :yerror,
|
||||
:yerrorbar => :yerror,
|
||||
:velocity => :quiver,
|
||||
:quiver2d => :quiver,
|
||||
:gradient => :quiver,
|
||||
)
|
||||
|
||||
# add all pluralized forms to the _keyAliases dict
|
||||
|
||||
@ -132,10 +132,16 @@ end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
get_shape(sym::Symbol) = _shapes[sym]
|
||||
get_shape(shape::Shape) = shape
|
||||
|
||||
# extract the underlying ShapeGeometry object(s)
|
||||
getMarkerGeom(shape::Shape) = gadflyshape(shape)
|
||||
getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
||||
getMarkerGeom(shapes::AVec) = map(getMarkerGeom, shapes)
|
||||
getMarkerGeom(shapes::AVec) = gadflyshape(map(get_shape, shapes))
|
||||
getMarkerGeom(other) = gadflyshape(get_shape(other))
|
||||
|
||||
# getMarkerGeom(shape::Shape) = gadflyshape(shape)
|
||||
# getMarkerGeom(shape::Symbol) = gadflyshape(_shapes[shape])
|
||||
# getMarkerGeom(shapes::AVec) = gadflyshape(map(gadflyshape, shapes)) # map(getMarkerGeom, shapes)
|
||||
function getMarkerGeom(d::KW)
|
||||
if d[:linetype] == :shape
|
||||
Gadfly.Geom.polygon(fill = true, preserve_order = true)
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
|
||||
|
||||
# Geometry which displays arbitrary shapes at given (x, y) positions.
|
||||
# note: vertices is a list of shapes
|
||||
immutable ShapeGeometry <: Gadfly.GeometryElement
|
||||
vertices::AbstractVector{@compat(Tuple{Float64,Float64})}
|
||||
vertices::AbstractVector #{Tuple{Float64,Float64}}
|
||||
tag::Symbol
|
||||
|
||||
function ShapeGeometry(shape; tag::Symbol=Gadfly.Geom.empty_tag)
|
||||
@ -66,24 +67,27 @@ function Gadfly.render(geom::ShapeGeometry, theme::Gadfly.Theme, aes::Gadfly.Aes
|
||||
end
|
||||
|
||||
function gadflyshape(sv::Shape)
|
||||
ShapeGeometry(sv.vertices)
|
||||
ShapeGeometry(Any[sv.vertices])
|
||||
end
|
||||
|
||||
function gadflyshape(sv::AVec{Shape})
|
||||
ShapeGeometry(Any[s.vertices for s in sv])
|
||||
end
|
||||
|
||||
|
||||
# create a Compose context given a ShapeGeometry and the xs/ys/sizes
|
||||
function make_polygon(geom::ShapeGeometry, xs::AbstractArray, ys::AbstractArray, rs::AbstractArray)
|
||||
n = max(length(xs), length(ys), length(rs))
|
||||
T = @compat(Tuple{Compose.Measure, Compose.Measure})
|
||||
T = Tuple{Compose.Measure, Compose.Measure}
|
||||
polys = Array(Vector{T}, n)
|
||||
for i in 1:n
|
||||
x = Compose.x_measure(xs[mod1(i, length(xs))])
|
||||
y = Compose.y_measure(ys[mod1(i, length(ys))])
|
||||
r = rs[mod1(i, length(rs))]
|
||||
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in geom.vertices]
|
||||
polys[i] = T[(x + r * sx, y + r * sy) for (sx,sy) in get_mod(geom.vertices, i)]
|
||||
end
|
||||
Gadfly.polygon(polys, geom.tag)
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@ -80,11 +80,12 @@ supportedArgs(::GadflyBackend) = [
|
||||
:xerror,
|
||||
:yerror,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
]
|
||||
supportedAxes(::GadflyBackend) = [:auto, :left]
|
||||
supportedTypes(::GadflyBackend) = [:none, :line, :path, :steppre, :steppost, :sticks,
|
||||
:scatter, :hist2d, :hexbin, :hist, :bar, :box, :violin,
|
||||
:scatter, :hist2d, :hexbin, :hist, :bar, :box, :violin, :quiver,
|
||||
:hline, :vline, :contour, :shape]
|
||||
supportedStyles(::GadflyBackend) = [:auto, :solid, :dash, :dot, :dashdot, :dashdotdot]
|
||||
supportedMarkers(::GadflyBackend) = vcat(_allMarkers, Shape)
|
||||
@ -171,11 +172,12 @@ supportedArgs(::PyPlotBackend) = [
|
||||
:xerror,
|
||||
:yerror,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
]
|
||||
supportedAxes(::PyPlotBackend) = _allAxes
|
||||
supportedTypes(::PyPlotBackend) = [:none, :line, :path, :steppre, :steppost, #:sticks,
|
||||
:scatter, :hist2d, :hexbin, :hist, :density, :bar, :box, :violin,
|
||||
:scatter, :hist2d, :hexbin, :hist, :density, :bar, :box, :violin, :quiver,
|
||||
:hline, :vline, :contour, :path3d, :scatter3d, :surface, :wireframe, :heatmap]
|
||||
supportedStyles(::PyPlotBackend) = [:auto, :solid, :dash, :dot, :dashdot]
|
||||
# supportedMarkers(::PyPlotBackend) = [:none, :auto, :rect, :ellipse, :diamond, :utriangle, :dtriangle, :cross, :xcross, :star5, :hexagon]
|
||||
@ -249,6 +251,7 @@ supportedArgs(::GRBackend) = [
|
||||
:xerror,
|
||||
:yerror,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
]
|
||||
supportedAxes(::GRBackend) = _allAxes
|
||||
@ -575,6 +578,7 @@ supportedArgs(::PlotlyBackend) = [
|
||||
:xerror,
|
||||
:yerror,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
]
|
||||
supportedAxes(::PlotlyBackend) = [:auto, :left]
|
||||
@ -649,6 +653,7 @@ supportedArgs(::PlotlyJSBackend) = [
|
||||
:xerror,
|
||||
:yerror,
|
||||
:ribbon,
|
||||
:quiver,
|
||||
:orientation,
|
||||
]
|
||||
supportedAxes(::PlotlyJSBackend) = [:auto, :left]
|
||||
|
||||
@ -9,6 +9,13 @@ export
|
||||
typealias P2 FixedSizeArrays.Vec{2,Float64}
|
||||
typealias P3 FixedSizeArrays.Vec{3,Float64}
|
||||
|
||||
nanpush!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P2}, b) = (push!(a, P2(NaN,NaN)); append!(a, b))
|
||||
nanpush!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); push!(a, b))
|
||||
nanappend!(a::AbstractVector{P3}, b) = (push!(a, P3(NaN,NaN,NaN)); append!(a, b))
|
||||
compute_angle(v::P2) = (angle = atan2(v[2], v[1]); angle < 0 ? 2π - angle : angle)
|
||||
|
||||
# -------------------------------------------------------------
|
||||
|
||||
immutable Shape
|
||||
vertices::AVec
|
||||
@ -98,6 +105,14 @@ function makecross(; offset = -0.5, radius = 1.0)
|
||||
end
|
||||
|
||||
|
||||
from_polar(angle, dist) = P2(dist*cos(angle), dist*sin(angle))
|
||||
|
||||
function makearrowhead(angle; h = 2.0, w = 0.4)
|
||||
tip = from_polar(angle, h)
|
||||
Shape(P2[(0,0), from_polar(angle - 0.5π, w) - tip,
|
||||
from_polar(angle + 0.5π, w) - tip, (0,0)])
|
||||
end
|
||||
|
||||
const _shapes = KW(
|
||||
:ellipse => makeshape(20),
|
||||
:rect => makeshape(4, offset=-0.25),
|
||||
|
||||
128
src/recipes.jl
128
src/recipes.jl
@ -147,12 +147,18 @@ function error_style!(d::KW)
|
||||
d[:label] = ""
|
||||
end
|
||||
|
||||
# if we're passed a tuple of vectors, convert to a vector of tuples
|
||||
function error_zipit(ebar)
|
||||
if istuple(ebar)
|
||||
collect(zip(ebar...))
|
||||
else
|
||||
ebar
|
||||
end
|
||||
end
|
||||
|
||||
function error_coords(xorig, yorig, ebar)
|
||||
# init empty x/y, and zip errors if passed Tuple{Vector,Vector}
|
||||
x, y = zeros(0), zeros(0)
|
||||
if istuple(ebar)
|
||||
ebar = collect(zip(ebar...))
|
||||
end
|
||||
|
||||
# for each point, create a line segment from the bottom to the top of the errorbar
|
||||
for i = 1:max(length(xorig), length(yorig))
|
||||
@ -177,19 +183,131 @@ end
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:yerror}})
|
||||
error_style!(d)
|
||||
d[:markershape] = :hline
|
||||
d[:x], d[:y] = error_coords(d[:x], d[:y], d[:yerror])
|
||||
d[:x], d[:y] = error_coords(d[:x], d[:y], error_zipit(d[:yerror]))
|
||||
KW[d]
|
||||
end
|
||||
|
||||
function apply_series_recipe(d::KW, ::Type{Val{:xerror}})
|
||||
error_style!(d)
|
||||
d[:markershape] = :vline
|
||||
d[:y], d[:x] = error_coords(d[:y], d[:x], d[:xerror])
|
||||
d[:y], d[:x] = error_coords(d[:y], d[:x], error_zipit(d[:xerror]))
|
||||
KW[d]
|
||||
end
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# quiver
|
||||
|
||||
# function apply_series_recipe(d::KW, ::Type{Val{:quiver}})
|
||||
# d[:label] = ""
|
||||
# d[:linetype] = :scatter
|
||||
#
|
||||
# # create a second series to draw the arrow shaft
|
||||
# dpath = copy(d)
|
||||
# error_style!(dpath)
|
||||
# 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}})
|
||||
d[:label] = ""
|
||||
d[:linetype] = :shape
|
||||
|
||||
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
|
||||
pts = P2[]
|
||||
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
|
||||
elseif isa(vi,Function)
|
||||
vi(xi, yi)
|
||||
else
|
||||
error("unexpected vi type $(typeof(vi)) for quiver: $vi")
|
||||
end
|
||||
v = P2(vx, vy)
|
||||
|
||||
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
|
||||
|
||||
ppv = p+v
|
||||
nanappend!(pts, P2[p, ppv-U1, ppv-U1+U2, ppv, ppv-U1-U2, ppv-U1])
|
||||
end
|
||||
|
||||
d[:x], d[:y] = Plots.unzip(pts)
|
||||
KW[d]
|
||||
end
|
||||
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@ -161,11 +161,20 @@ function build_series_args(plt::AbstractPlot, kw::KW) #, idxfilter)
|
||||
|
||||
# handle ribbons
|
||||
if get(d, :ribbon, nothing) != nothing
|
||||
# append!(ret, apply_series_recipe(copy(d), Val{:ribbon}))
|
||||
rib = d[:ribbon]
|
||||
d[:fillrange] = (d[:y] - rib, d[:y] + rib)
|
||||
end
|
||||
|
||||
# handle quiver plots
|
||||
if lt == :quiver
|
||||
d[:linetype] = lt = :path
|
||||
d[:linewidth] = 0
|
||||
end
|
||||
if get(d, :quiver, nothing) != nothing
|
||||
append!(ret, apply_series_recipe(copy(d), Val{:quiver}))
|
||||
end
|
||||
|
||||
|
||||
|
||||
# now that we've processed a given series... optionally split into
|
||||
# multiple dicts through a recipe (for example, a box plot is split into component
|
||||
|
||||
@ -240,6 +240,7 @@ Base.merge(a::AbstractVector, b::AbstractVector) = sort(unique(vcat(a,b)))
|
||||
nanpush!(a::AbstractVector, b) = (push!(a, NaN); push!(a, b))
|
||||
nanappend!(a::AbstractVector, b) = (push!(a, NaN); append!(a, b))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------
|
||||
|
||||
wraptuple(x::@compat(Tuple)) = x
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user