iter_segments and curve series type
This commit is contained in:
parent
2bd67f3519
commit
38804898c5
@ -109,6 +109,7 @@ export
|
|||||||
chorddiagram,
|
chorddiagram,
|
||||||
|
|
||||||
test_examples,
|
test_examples,
|
||||||
|
iter_segments,
|
||||||
|
|
||||||
translate,
|
translate,
|
||||||
translate!,
|
translate!,
|
||||||
@ -183,6 +184,7 @@ end
|
|||||||
@shorthands boxplot
|
@shorthands boxplot
|
||||||
@shorthands violin
|
@shorthands violin
|
||||||
@shorthands quiver
|
@shorthands quiver
|
||||||
|
@shorthands curves
|
||||||
|
|
||||||
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
pie(args...; kw...) = plot(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||||
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
pie!(args...; kw...) = plot!(args...; kw..., seriestype = :pie, aspect_ratio = :equal, grid=false, xticks=nothing, yticks=nothing)
|
||||||
|
|||||||
14
src/args.jl
14
src/args.jl
@ -11,6 +11,16 @@ function add_aliases(sym::Symbol, aliases::Symbol...)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function add_non_underscore_aliases!(aliases::KW)
|
||||||
|
for (k,v) in aliases
|
||||||
|
s = string(k)
|
||||||
|
if '_' in s
|
||||||
|
aliases[Symbol(replace(s, "_", ""))] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
const _allAxes = [:auto, :left, :right]
|
const _allAxes = [:auto, :left, :right]
|
||||||
@ -61,8 +71,12 @@ const _allTypes = vcat([
|
|||||||
:imagesc => :image,
|
:imagesc => :image,
|
||||||
:hist => :histogram,
|
:hist => :histogram,
|
||||||
:hist2d => :histogram2d,
|
:hist2d => :histogram2d,
|
||||||
|
:bezier => :curves,
|
||||||
|
:bezier_curves => :curves,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
add_non_underscore_aliases!(_typeAliases)
|
||||||
|
|
||||||
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
|
like_histogram(seriestype::Symbol) = seriestype in (:histogram, :density)
|
||||||
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
|
like_line(seriestype::Symbol) = seriestype in (:line, :path, :steppre, :steppost)
|
||||||
like_surface(seriestype::Symbol) = seriestype in (:contour, :contour3d, :heatmap, :surface, :wireframe, :image)
|
like_surface(seriestype::Symbol) = seriestype in (:contour, :contour3d, :heatmap, :surface, :wireframe, :image)
|
||||||
|
|||||||
@ -444,6 +444,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
lc = pyart3d.Line3DCollection(segments; kw...)
|
lc = pyart3d.Line3DCollection(segments; kw...)
|
||||||
lc[:set_array](d[:line_z])
|
lc[:set_array](d[:line_z])
|
||||||
ax[:add_collection3d](lc, zs=z) #, zdir='y')
|
ax[:add_collection3d](lc, zs=z) #, zdir='y')
|
||||||
|
lc
|
||||||
else
|
else
|
||||||
for i=1:n
|
for i=1:n
|
||||||
segments[i] = [(cycle(x,i), cycle(y,i)), (cycle(x,i+1), cycle(y,i+1))]
|
segments[i] = [(cycle(x,i), cycle(y,i)), (cycle(x,i+1), cycle(y,i+1))]
|
||||||
@ -451,6 +452,7 @@ function py_add_series(plt::Plot{PyPlotBackend}, series::Series)
|
|||||||
lc = pycollections.LineCollection(segments; kw...)
|
lc = pycollections.LineCollection(segments; kw...)
|
||||||
lc[:set_array](d[:line_z])
|
lc[:set_array](d[:line_z])
|
||||||
ax[:add_collection](lc)
|
ax[:add_collection](lc)
|
||||||
|
lc
|
||||||
end
|
end
|
||||||
push!(handles, handle)
|
push!(handles, handle)
|
||||||
needs_colorbar = true
|
needs_colorbar = true
|
||||||
|
|||||||
@ -172,6 +172,11 @@ function _apply_series_recipe(plt::Plot, d::KW)
|
|||||||
sp_idx = get_subplot_index(plt, sp)
|
sp_idx = get_subplot_index(plt, sp)
|
||||||
_update_subplot_args(plt, sp, d, sp_idx)
|
_update_subplot_args(plt, sp, d, sp_idx)
|
||||||
|
|
||||||
|
# do we want to override the series type?
|
||||||
|
if !is3d(st) && d[:z] != nothing && (size(d[:x]) == size(d[:y]) == size(d[:z]))
|
||||||
|
st = d[:seriestype] = (st == :scatter ? :scatter3d : :path3d)
|
||||||
|
end
|
||||||
|
|
||||||
# change to a 3d projection for this subplot?
|
# change to a 3d projection for this subplot?
|
||||||
if is3d(st)
|
if is3d(st)
|
||||||
sp.attr[:projection] = "3d"
|
sp.attr[:projection] = "3d"
|
||||||
|
|||||||
@ -338,6 +338,73 @@ end
|
|||||||
@deps sticks path scatter
|
@deps sticks path scatter
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# bezier curves
|
||||||
|
|
||||||
|
# get the value of the curve point at position t
|
||||||
|
function bezier_value(pts::AVec, t::Real)
|
||||||
|
val = 0.0
|
||||||
|
n = length(pts)-1
|
||||||
|
for (i,p) in enumerate(pts)
|
||||||
|
val += p * binomial(n, i-1) * (1-t)^(n-i+1) * t^(i-1)
|
||||||
|
end
|
||||||
|
val
|
||||||
|
end
|
||||||
|
|
||||||
|
# create segmented bezier curves in place of line segments
|
||||||
|
@recipe function f(::Type{Val{:curves}}, x, y, z)
|
||||||
|
args = z != nothing ? (x,y,z) : (x,y)
|
||||||
|
newx, newy = zeros(0), zeros(0)
|
||||||
|
fr = d[:fillrange]
|
||||||
|
newfr = fr != nothing ? zeros(0) : nothing
|
||||||
|
newz = z != nothing ? zeros(0) : nothing
|
||||||
|
lz = d[:line_z]
|
||||||
|
newlz = lz != nothing ? zeros(0) : nothing
|
||||||
|
|
||||||
|
# for each line segment (point series with no NaNs), convert it into a bezier curve
|
||||||
|
# where the points are the control points of the curve
|
||||||
|
for rng in iter_segments(args...)
|
||||||
|
length(rng) < 2 && continue
|
||||||
|
ts = linspace(0, 1, pop!(d, :npoints, 20))
|
||||||
|
nanappend!(newx, map(t -> bezier_value(cycle(x,rng), t), ts))
|
||||||
|
nanappend!(newy, map(t -> bezier_value(cycle(y,rng), t), ts))
|
||||||
|
if z != nothing
|
||||||
|
nanappend!(newz, map(t -> bezier_value(cycle(z,rng), t), ts))
|
||||||
|
end
|
||||||
|
if fr != nothing
|
||||||
|
nanappend!(newfr, map(t -> bezier_value(cycle(fr,rng), t), ts))
|
||||||
|
end
|
||||||
|
if lz != nothing
|
||||||
|
lzrng = cycle(lz, rng) # the line_z's for this segment
|
||||||
|
# @show lzrng, sizeof(lzrng) map(t -> 1+floor(Int, t * (length(rng)-1)), ts)
|
||||||
|
# choose the line_z value of the control point just before this t
|
||||||
|
push!(newlz, 0.0)
|
||||||
|
append!(newlz, map(t -> lzrng[1+floor(Int, t * (length(rng)-1))], ts))
|
||||||
|
# lzrng = vcat()
|
||||||
|
# nanappend!(newlz, #map(t -> bezier_value(cycle(lz,rng), t), ts))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
x := newx
|
||||||
|
y := newy
|
||||||
|
if z == nothing
|
||||||
|
seriestype := :path
|
||||||
|
else
|
||||||
|
seriestype := :path3d
|
||||||
|
z := newz
|
||||||
|
end
|
||||||
|
if fr != nothing
|
||||||
|
fillrange := newfr
|
||||||
|
end
|
||||||
|
if lz != nothing
|
||||||
|
line_z := newlz
|
||||||
|
linecolor := (isa(d[:linecolor], ColorGradient) ? d[:linecolor] : default_gradient())
|
||||||
|
end
|
||||||
|
# Plots.DD(d)
|
||||||
|
()
|
||||||
|
end
|
||||||
|
@deps curves path
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
# create a bar plot as a filled step function
|
# create a bar plot as a filled step function
|
||||||
|
|||||||
@ -292,22 +292,22 @@ end
|
|||||||
# # 3d line or scatter
|
# # 3d line or scatter
|
||||||
|
|
||||||
@recipe function f(x::AVec, y::AVec, z::AVec)
|
@recipe function f(x::AVec, y::AVec, z::AVec)
|
||||||
st = get(d, :seriestype, :none)
|
# st = get(d, :seriestype, :none)
|
||||||
if st == :scatter
|
# if st == :scatter
|
||||||
d[:seriestype] = :scatter3d
|
# d[:seriestype] = :scatter3d
|
||||||
elseif !is3d(st)
|
# elseif !is3d(st)
|
||||||
d[:seriestype] = :path3d
|
# d[:seriestype] = :path3d
|
||||||
end
|
# end
|
||||||
SliceIt, x, y, z
|
SliceIt, x, y, z
|
||||||
end
|
end
|
||||||
|
|
||||||
@recipe function f(x::AMat, y::AMat, z::AMat)
|
@recipe function f(x::AMat, y::AMat, z::AMat)
|
||||||
st = get(d, :seriestype, :none)
|
# st = get(d, :seriestype, :none)
|
||||||
if size(x) == size(y) == size(z)
|
# if size(x) == size(y) == size(z)
|
||||||
if !is3d(st)
|
# if !is3d(st)
|
||||||
seriestype := :path3d
|
# seriestype := :path3d
|
||||||
end
|
# end
|
||||||
end
|
# end
|
||||||
SliceIt, x, y, z
|
SliceIt, x, y, z
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
51
src/utils.jl
51
src/utils.jl
@ -136,6 +136,53 @@ function imageHack(d::KW)
|
|||||||
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
|
d[:z], d[:fillcolor] = replace_image_with_heatmap(d[:z].surf)
|
||||||
end
|
end
|
||||||
# ---------------------------------------------------------------
|
# ---------------------------------------------------------------
|
||||||
|
|
||||||
|
# -----------------------------------------------------
|
||||||
|
# helper to manage NaN-separated segments
|
||||||
|
|
||||||
|
type SegmentsIterator
|
||||||
|
args::Tuple
|
||||||
|
nextidx::Int
|
||||||
|
n::Int
|
||||||
|
end
|
||||||
|
function iter_segments(args...)
|
||||||
|
tup = Plots.wraptuple(args)
|
||||||
|
n = maximum(map(length, tup))
|
||||||
|
SegmentsIterator(tup, 0, n)
|
||||||
|
end
|
||||||
|
|
||||||
|
# helpers to figure out if there are NaN values in a list of array types
|
||||||
|
anynan(i::Int, args...) = any(a -> isnan(cycle(a,i)), args)
|
||||||
|
anynan(istart::Int, iend::Int, args...) = any(i -> anynan(i, args...), istart:iend)
|
||||||
|
allnan(istart::Int, iend::Int, args...) = all(i -> anynan(i, args...), istart:iend)
|
||||||
|
|
||||||
|
Base.start(itr::SegmentsIterator) = (itr.nextidx = 1) #resets
|
||||||
|
Base.done(itr::SegmentsIterator, unused::Int) = itr.nextidx > itr.n
|
||||||
|
function Base.next(itr::SegmentsIterator, unused::Int)
|
||||||
|
i = istart = iend = itr.nextidx
|
||||||
|
|
||||||
|
# find the next NaN, and iend is the one before
|
||||||
|
while i <= itr.n + 1
|
||||||
|
if i > itr.n || anynan(i, itr.args...)
|
||||||
|
# done... array end or found NaN
|
||||||
|
iend = i-1
|
||||||
|
break
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
# find the next non-NaN, and set itr.nextidx
|
||||||
|
while i <= itr.n
|
||||||
|
if !anynan(i, itr.args...)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
i += 1
|
||||||
|
end
|
||||||
|
|
||||||
|
itr.nextidx = i
|
||||||
|
istart:iend, 0
|
||||||
|
end
|
||||||
|
|
||||||
# ------------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@ -146,6 +193,10 @@ 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::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
|
Base.cycle(v, idx::Int) = v
|
||||||
|
|
||||||
|
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, idx::AVec{Int}) = v
|
||||||
|
|
||||||
makevec(v::AVec) = v
|
makevec(v::AVec) = v
|
||||||
makevec{T}(v::T) = T[v]
|
makevec{T}(v::T) = T[v]
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user