working on layouts
This commit is contained in:
parent
0d237028e7
commit
33d9664df5
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
22
src/types.jl
22
src/types.jl
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user