Base.cycle to cycle; text_size; animate; MixedMeasures; SeriesAnnotations

This commit is contained in:
Thomas Breloff 2016-11-03 13:29:20 -04:00
parent 281b92c262
commit 350ffdee25
8 changed files with 161 additions and 34 deletions

View File

@ -89,6 +89,7 @@ export
Animation,
frame,
gif,
animate,
@animate,
@gif,

View File

@ -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); 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

View File

@ -51,6 +51,20 @@ _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
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)
@ -59,19 +73,23 @@ function tick_padding(axis::Axis)
else
vals, labs = ticks
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
# 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

View File

@ -1021,14 +1021,18 @@ function gr_display(sp::Subplot{GRBackend}, w, h, viewport_canvas)
end
end
for ann in sp[:annotations]
x, y, val = ann
x, y = if is3d(sp)
# GR.wc3towc(x, y, z)
if isa(ann, SeriesAnnotation)
# TODO handle series annotations
else
GR.wctondc(x, y)
x, y, val = ann
x, y = if is3d(sp)
# GR.wc3towc(x, y, z)
else
GR.wctondc(x, y)
end
gr_set_font(val.font)
gr_text(x, y, val.str)
end
gr_set_font(val.font)
gr_text(x, y, val.str)
end
GR.restorestate()
end

View File

@ -308,10 +308,6 @@ function text(str, args...)
end
annotations(::Void) = []
annotations(anns::AVec) = anns
annotations(anns) = Any[anns]
# -----------------------------------------------------------------------
@ -387,6 +383,47 @@ end
# -----------------------------------------------------------------------
type SeriesAnnotations
strs::AbstractVector # the labels/names
font::Font
shape::Nullable{Shape}
shapefill::Brush
shapestroke::Stroke
bboxes::Vector{BBox}
end
function SeriesAnnotations(strs::AbstractVector, args...)
fnt = font()
shp = Nullable{Shape}()
br = brush(:steelblue)
stk = stroke(:black, 1)
for arg in args
if isa(arg, Shape)
shp = Nullable{Shape}(arg)
elseif isa(arg, Brush)
brush = arg
elseif isa(arg, Stroke)
stk = arg
elseif isa(arg, Font)
fnt = arg
elseif isa(arg, Symbol) && haskey(_shapes, arg)
shape = _shapes[arg]
else
warn("Unused SeriesAnnotations arg: $arg ($(typeof(arg)))")
end
end
# note: x/y coords are added later
SeriesAnnotations(strs, fnt, shp, br, stk, BBox[])
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}

View File

@ -96,6 +96,31 @@ 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))
pct += mix.len / totlen
end
if pct != 0
amin, amax = axis_limits(sp[Symbol(letter,:axis)])
xy += pct * (amax-amin)
end
xy
end
# -----------------------------------------------------------
# AbstractLayout

View File

@ -333,14 +333,29 @@ 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
series_anns = annotations(pop!(d, :series_annotations, []))
if isa(series_anns, SeriesAnnotations)
# x, y = d[:x], d[:y]
# nx, ny, na = map(length, (x,y,series_anns.strs))
# n = max(nx, ny, na)
# for i=1:n
# str = cycle(series_anns.strs,i)
# xi = cycle(x,i)
# yi = cycle(y,i)
# mwidth, mheight = text_size(str, series_anns.font.pointsize)
# xsz = measure_to_data(sp[:xaxis], mwidth)
# ysz = measure_to_data(sp[:yaxis], mheight)
error()
elseif length(series_anns) > 0
x, y = d[:x], d[:y]
nx, ny, na = map(length, (x,y,anns))
nx, ny, na = map(length, (x,y,series_anns))
n = max(nx, ny, na)
anns = [(x[mod1(i,nx)], y[mod1(i,ny)], text(anns[mod1(i,na)])) for i=1:n]
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, anns)
sp.attr[:annotations] = vcat(sp_anns, series_anns)
# sp[:series_annotations] = series_anns
# sp[:annotations] = sp_anns
end
function _expand_subplot_extrema(sp::Subplot, d::KW, st::Symbol)

View File

@ -234,19 +234,19 @@ 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
cycle(wrapper::InputWrapper, idx::Int) = wrapper.obj
cycle(wrapper::InputWrapper, idx::AVec{Int}) = wrapper.obj
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(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(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, 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))
Base.cycle(grad::ColorGradient, idx::Int) = cycle(grad.colors, idx)
Base.cycle(grad::ColorGradient, indices::AVec{Int}) = cycle(grad.colors, 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]