smarter cleanup of dicts; layout fixes: split out update_min_padding and added minimum_perimeter logic; check for nonempty axes when linking; add subplot processing and linking when combining plots
This commit is contained in:
parent
d7098f77c2
commit
8007d0dd6a
80
src/args.jl
80
src/args.jl
@ -896,78 +896,75 @@ end
|
||||
|
||||
# update attr from an input dictionary
|
||||
function _update_plot_args(plt::Plot, d_in::KW)
|
||||
pargs = plt.attr
|
||||
for (k,v) in _plot_defaults
|
||||
slice_arg!(d_in, pargs, k, v)
|
||||
slice_arg!(d_in, plt.attr, k, v)
|
||||
end
|
||||
|
||||
# handle colors
|
||||
bg = convertColor(pargs[:background_color])
|
||||
fg = pargs[:foreground_color]
|
||||
bg = convertColor(plt.attr[:background_color])
|
||||
fg = plt.attr[:foreground_color]
|
||||
if fg == :auto
|
||||
fg = isdark(bg) ? colorant"white" : colorant"black"
|
||||
end
|
||||
pargs[:background_color] = bg
|
||||
pargs[:foreground_color] = convertColor(fg)
|
||||
# color_or_match!(pargs, :background_color_outside, bg)
|
||||
color_or_nothing!(pargs, :background_color_outside)
|
||||
plt.attr[:background_color] = bg
|
||||
plt.attr[:foreground_color] = convertColor(fg)
|
||||
# color_or_match!(plt.attr, :background_color_outside, bg)
|
||||
color_or_nothing!(plt.attr, :background_color_outside)
|
||||
end
|
||||
|
||||
|
||||
# update a subplots args and axes
|
||||
function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::Integer; remove_pair = true)
|
||||
pargs = plt.attr
|
||||
spargs = sp.attr
|
||||
anns = pop!(sp.attr, :annotations, [])
|
||||
|
||||
# grab those args which apply to this subplot
|
||||
for (k,v) in _subplot_defaults
|
||||
slice_arg!(d_in, spargs, k, v, subplot_index, remove_pair = remove_pair)
|
||||
slice_arg!(d_in, sp.attr, k, v, subplot_index, remove_pair = remove_pair)
|
||||
end
|
||||
|
||||
# extend annotations
|
||||
sp.attr[:annotations] = vcat(anns, sp[:annotations])
|
||||
|
||||
# handle legend/colorbar
|
||||
spargs[:legend] = convertLegendValue(spargs[:legend])
|
||||
spargs[:colorbar] = convertLegendValue(spargs[:colorbar])
|
||||
if spargs[:colorbar] == :legend
|
||||
spargs[:colorbar] = spargs[:legend]
|
||||
sp.attr[:legend] = convertLegendValue(sp.attr[:legend])
|
||||
sp.attr[:colorbar] = convertLegendValue(sp.attr[:colorbar])
|
||||
if sp.attr[:colorbar] == :legend
|
||||
sp.attr[:colorbar] = sp.attr[:legend]
|
||||
end
|
||||
|
||||
# background colors
|
||||
# bg = color_or_match!(spargs, :background_color_subplot, pargs[:background_color])
|
||||
color_or_nothing!(spargs, :background_color_subplot)
|
||||
bg = sp[:background_color_subplot]
|
||||
spargs[:color_palette] = get_color_palette(spargs[:color_palette], bg, 30)
|
||||
# color_or_match!(spargs, :background_color_legend, bg)
|
||||
color_or_nothing!(spargs, :background_color_legend)
|
||||
# color_or_match!(spargs, :background_color_inside, bg)
|
||||
color_or_nothing!(spargs, :background_color_inside)
|
||||
# bg = color_or_match!(sp.attr, :background_color_subplot, plt.attr[:background_color])
|
||||
color_or_nothing!(sp.attr, :background_color_subplot)
|
||||
bg = convertColor(sp[:background_color_subplot])
|
||||
sp.attr[:color_palette] = get_color_palette(sp.attr[:color_palette], bg, 30)
|
||||
# color_or_match!(sp.attr, :background_color_legend, bg)
|
||||
color_or_nothing!(sp.attr, :background_color_legend)
|
||||
# color_or_match!(sp.attr, :background_color_inside, bg)
|
||||
color_or_nothing!(sp.attr, :background_color_inside)
|
||||
|
||||
# foreground colors
|
||||
# fg = color_or_match!(spargs, :foreground_color_subplot, pargs[:foreground_color])
|
||||
color_or_nothing!(spargs, :foreground_color_subplot)
|
||||
# color_or_match!(spargs, :foreground_color_legend, fg)
|
||||
color_or_nothing!(spargs, :foreground_color_legend)
|
||||
# color_or_match!(spargs, :foreground_color_grid, fg)
|
||||
color_or_nothing!(spargs, :foreground_color_grid)
|
||||
# color_or_match!(spargs, :foreground_color_title, fg)
|
||||
color_or_nothing!(spargs, :foreground_color_title)
|
||||
# fg = color_or_match!(sp.attr, :foreground_color_subplot, plt.attr[:foreground_color])
|
||||
color_or_nothing!(sp.attr, :foreground_color_subplot)
|
||||
# color_or_match!(sp.attr, :foreground_color_legend, fg)
|
||||
color_or_nothing!(sp.attr, :foreground_color_legend)
|
||||
# color_or_match!(sp.attr, :foreground_color_grid, fg)
|
||||
color_or_nothing!(sp.attr, :foreground_color_grid)
|
||||
# color_or_match!(sp.attr, :foreground_color_title, fg)
|
||||
color_or_nothing!(sp.attr, :foreground_color_title)
|
||||
|
||||
# for k in (:left_margin, :top_margin, :right_margin, :bottom_margin)
|
||||
# if spargs[k] == :match
|
||||
# spargs[k] = spargs[:margin]
|
||||
# if sp.attr[k] == :match
|
||||
# sp.attr[k] = sp.attr[:margin]
|
||||
# end
|
||||
# end
|
||||
|
||||
for letter in (:x, :y, :z)
|
||||
# get (maybe initialize) the axis
|
||||
axissym = Symbol(letter, :axis)
|
||||
axis = if haskey(spargs, axissym)
|
||||
spargs[axissym]
|
||||
axis = if haskey(sp.attr, axissym)
|
||||
sp.attr[axissym]
|
||||
else
|
||||
spargs[axissym] = Axis(sp, letter)
|
||||
sp.attr[axissym] = Axis(sp, letter)
|
||||
end
|
||||
|
||||
# grab magic args (for example `xaxis = (:flip, :log)`)
|
||||
@ -987,7 +984,8 @@ function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::I
|
||||
# then get those args that were passed with a leading letter: `xlabel = "X"`
|
||||
lk = Symbol(letter, k)
|
||||
if haskey(d_in, lk)
|
||||
kw[k] = slice_arg(pop!(d_in, lk), subplot_index)
|
||||
# kw[k] = slice_arg(pop!(d_in, lk), subplot_index)
|
||||
kw[k] = slice_arg(d_in[lk], subplot_index)
|
||||
end
|
||||
end
|
||||
|
||||
@ -1007,10 +1005,10 @@ function _update_subplot_args(plt::Plot, sp::Subplot, d_in::KW, subplot_index::I
|
||||
# TODO: need to handle linking here?
|
||||
end
|
||||
|
||||
# now we can get rid of the axis keys without a letter
|
||||
for k in keys(_axis_defaults)
|
||||
delete!(d_in, k)
|
||||
end
|
||||
# # now we can get rid of the axis keys without a letter
|
||||
# for k in keys(_axis_defaults)
|
||||
# delete!(d_in, k)
|
||||
# end
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -97,19 +97,12 @@ bbox!(layout::AbstractLayout, bb::BoundingBox) = (layout.bbox = bb)
|
||||
Base.parent(layout::AbstractLayout) = layout.parent
|
||||
parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
|
||||
|
||||
# NOTE: these should be implemented for subplots in each backend!
|
||||
# they represent the minimum size of the axes and guides
|
||||
min_padding_left(layout::AbstractLayout) = 0mm
|
||||
min_padding_top(layout::AbstractLayout) = 0mm
|
||||
min_padding_right(layout::AbstractLayout) = 0mm
|
||||
min_padding_bottom(layout::AbstractLayout) = 0mm
|
||||
# padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout)
|
||||
# padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout)
|
||||
# padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
|
||||
|
||||
padding_w(layout::AbstractLayout) = left_padding(layout) + right_padding(layout)
|
||||
padding_h(layout::AbstractLayout) = bottom_padding(layout) + top_padding(layout)
|
||||
padding(layout::AbstractLayout) = (padding_w(layout), padding_h(layout))
|
||||
|
||||
_update_position!(layout::AbstractLayout) = nothing
|
||||
update_child_bboxes!(layout::AbstractLayout) = nothing
|
||||
update_position!(layout::AbstractLayout) = nothing
|
||||
update_child_bboxes!(layout::AbstractLayout, minimum_perimeter = [0mm,0mm,0mm,0mm]) = nothing
|
||||
|
||||
width(layout::AbstractLayout) = width(layout.bbox)
|
||||
height(layout::AbstractLayout) = height(layout.bbox)
|
||||
@ -204,6 +197,12 @@ rightpad(layout::GridLayout) = layout.minpad[3]
|
||||
bottompad(layout::GridLayout) = layout.minpad[4]
|
||||
|
||||
|
||||
# here's how this works... first we recursively "update the minimum padding" (which
|
||||
# means to calculate the minimum size needed from the edge of the subplot to plot area)
|
||||
# for the whole layout tree. then we can compute the "padding borders" of this
|
||||
# layout as the biggest padding of the children on the perimeter. then we need to
|
||||
# recursively pass those borders back down the tree, one side at a time, but ONLY
|
||||
# to those perimeter children.
|
||||
|
||||
# leftpad, toppad, rightpad, bottompad
|
||||
function _update_min_padding!(layout::GridLayout)
|
||||
@ -217,10 +216,11 @@ function _update_min_padding!(layout::GridLayout)
|
||||
end
|
||||
|
||||
|
||||
function _update_position!(layout::GridLayout)
|
||||
map(_update_position!, layout.grid)
|
||||
function update_position!(layout::GridLayout)
|
||||
map(update_position!, layout.grid)
|
||||
end
|
||||
|
||||
# some lengths are fixed... we have to split up the free space among the list v
|
||||
function recompute_lengths(v)
|
||||
# dump(v)
|
||||
tot = 0pct
|
||||
@ -242,11 +242,11 @@ function recompute_lengths(v)
|
||||
end
|
||||
|
||||
# recursively compute the bounding boxes for the layout and plotarea (relative to canvas!)
|
||||
function update_child_bboxes!(layout::GridLayout)
|
||||
function update_child_bboxes!(layout::GridLayout, minimum_perimeter = [0mm,0mm,0mm,0mm])
|
||||
nr, nc = size(layout)
|
||||
|
||||
# create a matrix for each minimum padding direction
|
||||
_update_min_padding!(layout)
|
||||
# # create a matrix for each minimum padding direction
|
||||
# _update_min_padding!(layout)
|
||||
|
||||
minpad_left = map(leftpad, layout.grid)
|
||||
minpad_top = map(toppad, layout.grid)
|
||||
@ -263,6 +263,12 @@ function update_child_bboxes!(layout::GridLayout)
|
||||
pad_bottom = maximum(minpad_bottom, 2)
|
||||
# @show pad_left pad_top pad_right pad_bottom
|
||||
|
||||
# make sure the perimeter match the parent
|
||||
pad_left[1] = max(pad_left[1], minimum_perimeter[1])
|
||||
pad_top[1] = max(pad_top[1], minimum_perimeter[2])
|
||||
pad_right[end] = max(pad_right[end], minimum_perimeter[3])
|
||||
pad_bottom[end] = max(pad_bottom[end], minimum_perimeter[4])
|
||||
|
||||
# scale this up to the total padding in each direction
|
||||
total_pad_horizontal = sum(pad_left + pad_right)
|
||||
total_pad_vertical = sum(pad_top + pad_bottom)
|
||||
@ -303,8 +309,17 @@ function update_child_bboxes!(layout::GridLayout)
|
||||
child_height = pad_top[r] + plotarea_height + pad_bottom[r]
|
||||
bbox!(child, BoundingBox(child_left, child_top, child_width, child_height))
|
||||
|
||||
# this is the minimum perimeter as decided by this child's parent, so that
|
||||
# all children on this border have the same value
|
||||
min_child_perimeter = [
|
||||
c == 1 ? layout.minpad[1] : 0mm,
|
||||
r == 1 ? layout.minpad[2] : 0mm,
|
||||
c == nc ? layout.minpad[3] : 0mm,
|
||||
r == nr ? layout.minpad[4] : 0mm
|
||||
]
|
||||
|
||||
# recursively update the child's children
|
||||
update_child_bboxes!(child)
|
||||
update_child_bboxes!(child, min_child_perimeter)
|
||||
end
|
||||
end
|
||||
|
||||
@ -376,40 +391,21 @@ end
|
||||
|
||||
layout_args(huh) = error("unhandled layout type $(typeof(huh)): $huh")
|
||||
|
||||
# # pass the layout arg through
|
||||
# function build_layout(d::KW)
|
||||
# build_layout(get(d, :layout, default(:layout)))
|
||||
# end
|
||||
#
|
||||
# function build_layout(n::Integer)
|
||||
# nr, nc = compute_gridsize(n, -1, -1)
|
||||
# build_layout(GridLayout(nr, nc), n)
|
||||
# end
|
||||
#
|
||||
# function build_layout{I<:Integer}(sztup::NTuple{2,I})
|
||||
# nr, nc = sztup
|
||||
# build_layout(GridLayout(nr, nc))
|
||||
# end
|
||||
#
|
||||
# function build_layout{I<:Integer}(sztup::NTuple{3,I})
|
||||
# n, nr, nc = sztup
|
||||
# nr, nc = compute_gridsize(n, nr, nc)
|
||||
# build_layout(GridLayout(nr, nc), n)
|
||||
# end
|
||||
#
|
||||
# # compute number of subplots
|
||||
# function build_layout(layout::GridLayout)
|
||||
# # recursively get the size of the grid
|
||||
# n = calc_num_subplots(layout)
|
||||
# build_layout(layout, n)
|
||||
# end
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
|
||||
function build_layout(args...)
|
||||
layout, n = layout_args(args...)
|
||||
build_layout(layout, n)
|
||||
end
|
||||
|
||||
# n is the number of subplots
|
||||
# # just a single subplot
|
||||
# function build_layout(sp::Subplot, n::Integer)
|
||||
# sp, Subplot[sp], SubplotMap(gensym() => sp)
|
||||
# end
|
||||
|
||||
# n is the number of subplots... build a grid and initialize the inner subplots recursively
|
||||
function build_layout(layout::GridLayout, n::Integer)
|
||||
nr, nc = size(layout)
|
||||
subplots = Subplot[]
|
||||
@ -644,7 +640,9 @@ end
|
||||
function link_axes!(a::AbstractArray{AbstractLayout}, axissym::Symbol)
|
||||
subplots = filter(l -> isa(l, Subplot), a)
|
||||
axes = [sp.attr[axissym] for sp in subplots]
|
||||
link_axes!(axes...)
|
||||
if length(axes) > 0
|
||||
link_axes!(axes...)
|
||||
end
|
||||
end
|
||||
|
||||
# don't do anything for most layout types
|
||||
|
||||
48
src/plot.jl
48
src/plot.jl
@ -102,6 +102,25 @@ function plot(plt1::Plot, plts_tail::Plot...; kw...)
|
||||
end
|
||||
end
|
||||
|
||||
# just in case the backend needs to set up the plot (make it current or something)
|
||||
_prepare_plot_object(plt)
|
||||
|
||||
# first apply any args for the subplots
|
||||
for (idx,sp) in enumerate(plt.subplots)
|
||||
_update_subplot_args(plt, sp, d, idx, remove_pair = false)
|
||||
end
|
||||
|
||||
# # now we can get rid of the axis keys without a letter
|
||||
# for k in keys(_axis_defaults)
|
||||
# delete!(d, k)
|
||||
# for letter in (:x,:y,:z)
|
||||
# delete!(d, Symbol(letter,k))
|
||||
# end
|
||||
# end
|
||||
|
||||
# do we need to link any axes together?
|
||||
link_axes!(plt.layout, plt[:link])
|
||||
|
||||
# finish up
|
||||
current(plt)
|
||||
if get(d, :show, default(:show))
|
||||
@ -305,17 +324,17 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
end
|
||||
end
|
||||
|
||||
# merge in anything meant for plot/subplot
|
||||
# merge in anything meant for plot/subplot/axis
|
||||
for kw in kw_list
|
||||
for (k,v) in kw
|
||||
for defdict in (_plot_defaults, _subplot_defaults)
|
||||
for defdict in (_plot_defaults,
|
||||
_subplot_defaults,
|
||||
_axis_defaults,
|
||||
_axis_defaults_byletter)
|
||||
if haskey(defdict, k)
|
||||
d[k] = pop!(kw, k)
|
||||
end
|
||||
end
|
||||
# if haskey(_plot_defaults, k) || haskey(_subplot_defaults, k)
|
||||
# d[k] = v
|
||||
# end
|
||||
end
|
||||
end
|
||||
|
||||
@ -351,9 +370,6 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
# this is it folks!
|
||||
# TODO: we probably shouldn't use i for tracking series index, but rather explicitly track it in recipes
|
||||
for (i,kw) in enumerate(kw_list)
|
||||
# if !(get(kw, :seriestype, :none) in (:xerror, :yerror))
|
||||
# plt.n += 1
|
||||
# end
|
||||
command_idx = kw[:series_plotindex] - kw_list[1][:series_plotindex] + 1
|
||||
|
||||
# get the Subplot object to which the series belongs
|
||||
@ -393,15 +409,6 @@ function _plot!(plt::Plot, d::KW, args...)
|
||||
_apply_series_recipe(plt, kw)
|
||||
end
|
||||
|
||||
# # everything is processed, time to compute the layout bounding boxes
|
||||
# _before_layout_calcs(plt)
|
||||
# w, h = plt.attr[:size]
|
||||
# plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
|
||||
# update_child_bboxes!(plt.layout)
|
||||
#
|
||||
# # TODO just need to pass plt... and we should do all non-series updates here
|
||||
# _update_plot_object(plt)
|
||||
|
||||
current(plt)
|
||||
|
||||
# note: lets ignore the show param and effectively use the semicolon at the end of the REPL statement
|
||||
@ -429,8 +436,15 @@ function prepare_output(plt::Plot)
|
||||
|
||||
w, h = plt.attr[:size]
|
||||
plt.layout.bbox = BoundingBox(0mm, 0mm, w*px, h*px)
|
||||
|
||||
# One pass down and back up the tree to compute the minimum padding
|
||||
# of the children on the perimeter. This is an backend callback.
|
||||
_update_min_padding!(plt.layout)
|
||||
|
||||
# now another pass down, to update the bounding boxes
|
||||
update_child_bboxes!(plt.layout)
|
||||
|
||||
# the backend callback, to reposition subplots, etc
|
||||
_update_plot_object(plt)
|
||||
end
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user