working on layouts

This commit is contained in:
Thomas Breloff 2016-05-17 21:24:34 -04:00
parent 0d237028e7
commit 33d9664df5
4 changed files with 69 additions and 29 deletions

View File

@ -249,22 +249,21 @@ drawax(ax) = ax[:draw](renderer(ax[:get_figure]()))
# BoundingBox(left, bottom, right, top)
# end
function py_bbox_fig(obj)
fl, fr, fb, ft = fig[:get_window_extent]()
get_extents(obj) = obj[:get_window_extent]()[:get_points]()
function py_bbox_fig(fig)
fl, fr, fb, ft = get_extents(fig)
BoundingBox(0px, 0px, (fr-fl)*px, (ft-fb)*px)
end
py_bbox_fig(plt::Plot) = py_bbox_fig(plt.o)
# compute a bounding box (with origin top-left), however pyplot gives coords with origin bottom-left
function py_bbox(obj)
fl, fr, fb, ft = py_bbox_fig(obj[:get_figure]())
l, r, b, t = obj[:get_window_extent]()[:get_points]()
fl, fr, fb, ft = get_extents(obj[:get_figure]())
l, r, b, t = get_extents(obj)
BoundingBox(l*px, (ft-t)*px, (r-l)*px, (t-b)*px)
# BoundingBox(l*px, (t*px, (r-l)*px, (t-b)*px)
end
# bbox_from_pyplot(obj) =
py_bbox_fig(plt::Plot) = py_bbox_fig(plt.o)
function py_bbox_ticks(ax, letter)
# fig = ax[:get_figure]()
@ -301,10 +300,26 @@ end
# the most extreme guide/axis in each direction. Can pass method (left,top,right,bottom)
# and aggregation (minimum or maximum) into a method to do this.
min_padding_left(layout::Subplot{PyPlotBackend}) = 0mm
min_padding_top(layout::Subplot{PyPlotBackend}) = 0mm
min_padding_right(layout::Subplot{PyPlotBackend}) = 0mm
min_padding_bottom(layout::Subplot{PyPlotBackend}) = 0mm
min_padding_left(layout::Subplot{PyPlotBackend}) = compute_min_padding(layout, left, 1)
min_padding_top(layout::Subplot{PyPlotBackend}) = compute_min_padding(layout, top, -1)
min_padding_right(layout::Subplot{PyPlotBackend}) = compute_min_padding(layout, right, -1)
min_padding_bottom(layout::Subplot{PyPlotBackend}) = compute_min_padding(layout, bottom, 1)
# loop over the guides and axes and compute how far they "stick out" from the plot area,
# so that we know the minimum padding we need to avoid cropping and overlapping text.
# `func` is one of (left,top,right,bottom), and we multiply by 1 or -1 depending on direction
function compute_min_padding(sp::Subplot{PyPlotBackend}, func::Function, mult::Number)
ax = sp.o
plotbb = py_bbox(ax)
padding = 0mm
for bb in (py_bbox_axis(ax, "x"),
py_bbox_axis(ax, "y"),
py_bbox_title(ax))
diff = func(plotbb) - func(bb)
padding = max(padding, mult * diff)
end
padding
end
# xaxis_height(sp::Subplot{PyPlotBackend}) = height(py_bbox_axis(sp.o,"x"))
# yaxis_width(sp::Subplot{PyPlotBackend}) = width(py_bbox_axis(sp.o,"y"))
@ -336,9 +351,10 @@ min_padding_bottom(layout::Subplot{PyPlotBackend}) = 0mm
function update_position!(sp::Subplot{PyPlotBackend})
ax = sp.o
figw, figh = size(py_bbox(ax[:get_figure]()))
figw, figh = size(py_bbox_fig(sp.plt))
plot_bb = plotarea_bbox(sp)
# plot_bb = plotarea_bbox(sp)
plot_bb = sp.plotarea
@show sp.bbox plot_bb
# l = float(left(plot_bb) / px) / figw
# b = float(bottom(plot_bb) / px) / figh

View File

@ -57,6 +57,7 @@ function plot(args...; kw...)
plt.layout, plt.subplots, plt.spmap = build_layout(plt.plotargs)
for sp in plt.subplots
# update the subplot/axis args from inputs, then pass to backend to init further
sp.plt = plt
_update_subplot_args(plt, sp, d)
_initialize_subplot(plt, sp)
end

View File

@ -127,6 +127,7 @@ function update_child_bboxes!(layout::GridLayout)
minpad_top = map(min_padding_top, layout.grid)
minpad_right = map(min_padding_right, layout.grid)
minpad_bottom = map(min_padding_bottom, layout.grid)
@show minpad_left minpad_top minpad_right minpad_bottom
# get the max horizontal (left and right) padding over columns,
# and max vertical (bottom and top) padding over rows
@ -135,42 +136,48 @@ function update_child_bboxes!(layout::GridLayout)
pad_top = maximum(minpad_top, 2)
pad_right = maximum(minpad_right, 1)
pad_bottom = maximum(minpad_bottom, 2)
@show pad_left pad_top pad_right pad_bottom
# scale this up to the total padding in each direction
total_pad_horizontal = (pad_left + pad_right) * nc
total_pad_vertical = (pad_top + pad_bottom) * nr
total_pad_horizontal = (pad_left + pad_right) .* nc
total_pad_vertical = (pad_top + pad_bottom) .* nr
@show total_pad_horizontal total_pad_vertical
# now we can compute the total plot area in each direction
total_plotarea_horizontal = width(layout) - total_pad_horizontal
total_plotarea_vertical = height(layout) - total_pad_vertical
@show total_plotarea_horizontal total_plotarea_vertical
# normalize widths/heights so they sum to 1
layout.widths ./ sum(layout.widths)
layout.heights ./ sum(layout.heights)
denom_w = sum(layout.widths)
denom_h = sum(layout.heights)
@show denom_w, denom_h
# we have all the data we need... lets compute the plot areas
for r=1:nr, c=1:nc
child = layout[r,c]
# get the top-left corner of this child
child_left = (c == 1 ? 0mm : right(layout[r, c-1])
child_top = (r == 1 ? 0mm : top(layout[r-1, c]))
child_left = (c == 1 ? 0mm : right(layout[r, c-1].bbox))
child_top = (r == 1 ? 0mm : top(layout[r-1, c].bbox))
# compute plot area
plotarea_left = child_left + pad_left[c]
plotarea_top = child_top + pad_top[r]
plotarea_width = total_pad_horizontal * layout.widths[c]
plotarea_height = total_pad_vertical * layout.heights[r]
child.plotarea = BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height)
plotarea_width = total_plotarea_horizontal[c] * layout.widths[c] / denom_w
plotarea_height = total_plotarea_vertical[r] * layout.heights[r] / denom_h
child_plotarea = BoundingBox(plotarea_left, plotarea_top, plotarea_width, plotarea_height)
# compute child bbox
child_width = pad_left[c] + plotarea_width + pad_right[c]
child_height = pad_top[r] + plotarea_height + pad_bottom[r]
child.bbox = BoundingBox(child_left, child_top, child_width, child_height)
child_bbox = BoundingBox(child_left, child_top, child_width, child_height)
@show (r,c) child_plotarea child_bbox
# the bounding boxes are currently relative to the parent, but we need them relative to the canvas
child.plotarea = crop(layout.bbox, child.plotarea)
child.bbox = crop(layout.bbox, child.plotarea)
plotarea!(child, crop(layout.bbox, child_plotarea))
bbox!(child, crop(layout.bbox, child_bbox))
@show child_plotarea child_bbox
# recursively update the child's children
update_child_bboxes!(child)

View File

@ -47,6 +47,15 @@ typealias BBox Measures.Absolute2DBox
const px = AbsoluteLength(0.254)
const pct = Length{:pct, Float64}(1.0)
Base.(:.*)(m::Measure, n::Number) = m * n
Base.(:.*)(n::Number, m::Measure) = m * n
Base.(:-)(m::Measure, a::AbstractArray) = map(ai -> m - ai, a)
Base.(:-)(a::AbstractArray, m::Measure) = map(ai -> ai - m, a)
Base.zero(::Type{typeof(mm)}) = 0mm
Base.one(::Type{typeof(mm)}) = 1mm
Base.typemin(::typeof(mm)) = -Inf*mm
Base.typemax(::typeof(mm)) = Inf*mm
Base.(:+)(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 + m2.value))
Base.(:+)(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (1 + m1.value))
Base.(:-)(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * (1 - m2.value))
@ -56,6 +65,12 @@ Base.(:*)(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.v
Base.(:/)(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
Base.(:/)(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
Base.zero(::Type{typeof(pct)}) = 0pct
Base.one(::Type{typeof(pct)}) = 1pct
Base.typemin(::typeof(pct)) = 0pct
Base.typemax(::typeof(pct)) = 1pct
const defaultbox = BoundingBox(0mm, 0mm, 0mm, 0mm)
# left(bbox::BoundingBox) = bbox.left
@ -120,6 +135,7 @@ abstract AbstractLayout
width(layout::AbstractLayout) = width(layout.bbox)
height(layout::AbstractLayout) = height(layout.bbox)
plotarea!(layout::AbstractLayout, bbox::BoundingBox) = nothing
# -----------------------------------------------------------
@ -151,12 +167,12 @@ type Subplot{T<:AbstractBackend} <: AbstractLayout
end
function Subplot{T<:AbstractBackend}(::T; parent = RootLayout())
Subplot{T}(parent, defaultbox, KW(), nothing)
Subplot{T}(parent, defaultbox, defaultbox, KW(), nothing, nothing)
end
# -----------------------------------------------------------
plotarea!(sp::Subplot, bbox::BoundingBox) = (sp.plotarea = bbox)
# TODO: i'll want a plotarea! method to set the plotarea only if the field exists
# -----------------------------------------------------------
# nested, gridded layout with optional size percentages
type GridLayout <: AbstractLayout