trying out measures
This commit is contained in:
parent
2ac17afe30
commit
606229a08f
@ -241,26 +241,33 @@ drawax(ax) = ax[:draw](renderer(ax[:get_figure]()))
|
||||
# # merge a list of bounding boxes together to become the area that surrounds them all
|
||||
# bbox_union(bboxes) = pytransforms.Bbox[:union](bboxes)
|
||||
|
||||
function py_bbox_pct(obj)
|
||||
pybbox_pixels = obj[:get_window_extent]()
|
||||
fig = obj[:get_figure]()
|
||||
pybbox_pct = pybbox_pixels[:inverse_transformed](fig[:transFigure])
|
||||
left, right, bottom, top = pybbox_pct[:get_points]()
|
||||
BoundingBox(left, bottom, right, top)
|
||||
# function py_bbox_pct(obj)
|
||||
# pybbox_pixels = obj[:get_window_extent]()
|
||||
# fig = obj[:get_figure]()
|
||||
# pybbox_pct = pybbox_pixels[:inverse_transformed](fig[:transFigure])
|
||||
# left, right, bottom, top = pybbox_pct[:get_points]()
|
||||
# BoundingBox(left, bottom, right, top)
|
||||
# end
|
||||
|
||||
function py_bbox(obj)
|
||||
l, r, b, t = obj[:get_window_extent]()[:get_points]()
|
||||
BoundingBox(l*px, b*px, (r-l)*px, (t-b)*px)
|
||||
end
|
||||
|
||||
# bbox_from_pyplot(obj) =
|
||||
|
||||
py_bbox_fig(plt::Plot) = py_bbox(plt.o)
|
||||
|
||||
function py_bbox_ticks(ax, letter)
|
||||
# fig = ax[:get_figure]()
|
||||
# @show fig
|
||||
labels = ax[symbol("get_"*letter*"ticklabels")]()
|
||||
# @show labels
|
||||
# bboxes = []
|
||||
bbox_union = BoundingBox()
|
||||
bbox_union = defaultbox
|
||||
for lab in labels
|
||||
# @show lab,lab[:get_text]()
|
||||
bbox = py_bbox_pct(lab)
|
||||
bbox = py_bbox(lab)
|
||||
bbox_union = bbox_union + bbox
|
||||
# @show bbox_union
|
||||
end
|
||||
@ -269,7 +276,7 @@ end
|
||||
|
||||
function py_bbox_axislabel(ax, letter)
|
||||
pyaxis_label = ax[symbol("get_"*letter*"axis")]()[:label]
|
||||
py_bbox_pct(pyaxis_label)
|
||||
py_bbox(pyaxis_label)
|
||||
end
|
||||
|
||||
# get a bounding box for the whole axis
|
||||
@ -279,7 +286,7 @@ end
|
||||
|
||||
# get a bounding box for the title area
|
||||
function py_bbox_title(ax)
|
||||
py_bbox_pct(ax[:title])
|
||||
py_bbox(ax[:title])
|
||||
end
|
||||
|
||||
xaxis_height(sp::Subplot{PyPlotBackend}) = height(py_bbox_axis(sp.o,"x"))
|
||||
@ -293,26 +300,37 @@ title_height(sp::Subplot{PyPlotBackend}) = height(py_bbox_title(sp.o))
|
||||
# cache the plotarea bbox while we're doing that (need to add plotarea field to Subplot)
|
||||
function plotarea_bbox(sp::Subplot{PyPlotBackend})
|
||||
ax = sp.o
|
||||
plot_bb = py_bbox_pct(ax)
|
||||
plot_bb = py_bbox(ax)
|
||||
xbb = py_bbox_axis(ax, "x")
|
||||
ybb = py_bbox_axis(ax, "y")
|
||||
titbb = py_bbox_title(ax)
|
||||
items = [xbb, ybb, titbb]
|
||||
# TODO: add in margin/padding from sp.attr
|
||||
leftpad = max(0, left(plot_bb) - minimum(map(left, items)))
|
||||
bottompad = max(0, bottom(plot_bb) - minimum(map(bottom, items)))
|
||||
rightpad = max(0, maximum(map(right, items)) - right(plot_bb))
|
||||
toppad = max(0, maximum(map(top, items)) - top(plot_bb))
|
||||
# crop(bbox(sp), BoundingBox(yaxis_width(sp), xaxis_height(sp), 1, 1 - title_height(sp)))
|
||||
crop(bbox(sp), BoundingBox(leftpad, bottompad, 1 - rightpad, 1 - toppad))
|
||||
leftpad = max(0mm, left(plot_bb) - minimum(map(left, items)))
|
||||
bottompad = max(0mm, bottom(plot_bb) - minimum(map(bottom, items)))
|
||||
rightpad = max(0mm, maximum(map(right, items)) - right(plot_bb))
|
||||
toppad = max(0mm, maximum(map(top, items)) - top(plot_bb))
|
||||
crop(bbox(sp), BoundingBox(leftpad, bottompad,
|
||||
width(sp) - rightpad - leftpad,
|
||||
height(sp) - toppad - bottompad))
|
||||
end
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
function update_position!(sp::Subplot{PyPlotBackend})
|
||||
ax = sp.o
|
||||
bb = plotarea_bbox(sp)
|
||||
ax[:set_position]([f(bb) for f in (left, bottom, width, height)])
|
||||
figw, figh = size(py_bbox(ax[:get_figure]()))
|
||||
|
||||
plot_bb = plotarea_bbox(sp)
|
||||
@show sp.bbox plot_bb
|
||||
# l = float(left(plot_bb) / px) / figw
|
||||
# b = float(bottom(plot_bb) / px) / figh
|
||||
# w = float(width(plot_bb) / px) / figw
|
||||
mms = Float64[f(plot_bb).value for f in (left, bottom, width, height)]
|
||||
@show mms
|
||||
pcts = mms ./ Float64[figw.value, figh.value, figw.value, figh.value]
|
||||
@show pcts
|
||||
ax[:set_position](pcts)
|
||||
end
|
||||
|
||||
# each backend should set up the subplot here
|
||||
@ -1297,7 +1315,8 @@ function finalizePlot(plt::Plot{PyPlotBackend})
|
||||
ax[:title][:set_color](getPyPlotColor(plt.plotargs[:titlefont].color))
|
||||
end
|
||||
drawfig(plt.o)
|
||||
update_bboxes!(plt.layout)
|
||||
plt.layout.bbox = py_bbox_fig(plt)
|
||||
upadte_child_bboxes!(plt.layout)
|
||||
update_position!(plt.layout)
|
||||
PyPlot.draw()
|
||||
end
|
||||
|
||||
@ -1,39 +1,5 @@
|
||||
|
||||
|
||||
left(bbox::BoundingBox) = bbox.left
|
||||
bottom(bbox::BoundingBox) = bbox.bottom
|
||||
right(bbox::BoundingBox) = bbox.right
|
||||
top(bbox::BoundingBox) = bbox.top
|
||||
width(bbox::BoundingBox) = bbox.right - bbox.left
|
||||
height(bbox::BoundingBox) = bbox.top - bbox.bottom
|
||||
|
||||
Base.size(bbox::BoundingBox) = (width(bbox), height(bbox))
|
||||
|
||||
# union together bounding boxes
|
||||
function Base.(:+)(bb1::BoundingBox, bb2::BoundingBox)
|
||||
# empty boxes don't change the union
|
||||
if width(bb1) <= 0 || height(bb1) <= 0
|
||||
return bb2
|
||||
elseif width(bb2) <= 0 || height(bb2) <= 0
|
||||
return bb1
|
||||
end
|
||||
BoundingBox(
|
||||
min(bb1.left, bb2.left),
|
||||
min(bb1.bottom, bb2.bottom),
|
||||
max(bb1.right, bb2.right),
|
||||
max(bb1.top, bb2.top)
|
||||
)
|
||||
end
|
||||
|
||||
# this creates a bounding box in the parent's scope, where the child bounding box
|
||||
# is relative to the parent
|
||||
function crop(parent::BoundingBox, child::BoundingBox)
|
||||
l = left(parent) + width(parent) * left(child)
|
||||
w = width(parent) * width(child)
|
||||
b = bottom(parent) + height(parent) * bottom(child)
|
||||
h = height(parent) * height(child)
|
||||
BoundingBox(l, b, l+w, b+h)
|
||||
end
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ -48,8 +14,8 @@ parent_bbox(layout::AbstractLayout) = bbox(parent(layout))
|
||||
# this is a calculation of the percentage of free space available in the canvas
|
||||
# after accounting for the size of guides and axes
|
||||
free_size(layout::AbstractLayout) = (free_width(layout), free_height(layout))
|
||||
free_width(layout::AbstractLayout) = 1.0 - used_width(layout)
|
||||
free_height(layout::AbstractLayout) = 1.0 - used_height(layout)
|
||||
free_width(layout::AbstractLayout) = width(layout.bbox) - used_width(layout)
|
||||
free_height(layout::AbstractLayout) = height(layout.bbox) - used_height(layout)
|
||||
|
||||
used_size(layout::AbstractLayout) = (used_width(layout), used_height(layout))
|
||||
|
||||
@ -59,7 +25,7 @@ used_size(layout::AbstractLayout) = (used_width(layout), used_height(layout))
|
||||
# end
|
||||
|
||||
update_position!(layout::AbstractLayout) = nothing
|
||||
update_bboxes!(layout::AbstractLayout) = nothing
|
||||
upadte_child_bboxes!(layout::AbstractLayout) = nothing
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
@ -67,15 +33,15 @@ Base.size(layout::EmptyLayout) = (0,0)
|
||||
Base.length(layout::EmptyLayout) = 0
|
||||
Base.getindex(layout::EmptyLayout, r::Int, c::Int) = nothing
|
||||
|
||||
used_width(layout::EmptyLayout) = 0.0
|
||||
used_height(layout::EmptyLayout) = 0.0
|
||||
used_width(layout::EmptyLayout) = 0pct
|
||||
used_height(layout::EmptyLayout) = 0pct
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
Base.parent(::RootLayout) = nothing
|
||||
parent_bbox(::RootLayout) = BoundingBox(0,0,1,1)
|
||||
bbox(::RootLayout) = BoundingBox(0,0,1,1)
|
||||
parent_bbox(::RootLayout) = defaultbox
|
||||
bbox(::RootLayout) = defaultbox
|
||||
|
||||
# Base.size(layout::RootLayout) = (1,1)
|
||||
# Base.length(layout::RootLayout) = 1
|
||||
@ -100,7 +66,10 @@ used_height(sp::Subplot) = xaxis_height(sp) + title_height(sp)
|
||||
# bounding box (relative to canvas) for plot area
|
||||
# note: we assume the x axis is on the left, and y axis is on the bottom
|
||||
function plotarea_bbox(sp::Subplot)
|
||||
crop(bbox(sp), BoundingBox(yaxis_width(sp), xaxis_height(sp), 1, 1 - title_height(sp)))
|
||||
xh = xaxis_height(sp)
|
||||
yw = yaxis_width(sp)
|
||||
crop(bbox(sp), BoundingBox(yw, xh, width(sp) - yw,
|
||||
height(sp) - xh - title_height(sp)))
|
||||
end
|
||||
|
||||
# NOTE: this is unnecessary I think as it is the same as bbox(::Subplot)
|
||||
@ -117,16 +86,21 @@ function Base.setindex!(layout::GridLayout, v, r::Int, c::Int)
|
||||
end
|
||||
|
||||
function used_width(layout::GridLayout)
|
||||
w = 0.0
|
||||
w = 0mm
|
||||
nr,nc = size(layout)
|
||||
for c=1:nc
|
||||
@show w
|
||||
w += maximum([used_width(layout[r,c]) for r=1:nr])
|
||||
for r=1:nr
|
||||
@show used_width(layout[r,c])
|
||||
end
|
||||
@show w
|
||||
end
|
||||
w
|
||||
end
|
||||
|
||||
function used_height(layout::GridLayout)
|
||||
h = 0.0
|
||||
h = 0mm
|
||||
nr,nc = size(layout)
|
||||
for r=1:nr
|
||||
h += maximum([used_height(layout[r,c]) for c=1:nc])
|
||||
@ -140,39 +114,46 @@ update_position!(layout::GridLayout) = map(update_position!, layout.grid)
|
||||
# up the tree, then assigning bounding boxes according to height/width percentages
|
||||
# note: this should be called after all axis objects are updated to re-compute the
|
||||
# bounding boxes for the layout tree
|
||||
function update_bboxes!(layout::GridLayout) #, parent_bbox::BoundingBox = BoundingBox(0,0,1,1))
|
||||
function upadte_child_bboxes!(layout::GridLayout) #, parent_bbox::BoundingBox = defaultbox)
|
||||
# initialize the free space (per child!)
|
||||
nr, nc = size(layout)
|
||||
freew, freeh = free_size(layout)
|
||||
@show freew, freeh
|
||||
freew /= sum(layout.widths)
|
||||
freeh /= sum(layout.heights)
|
||||
@show freew, freeh
|
||||
|
||||
# TODO: this should really track used/free space for each row/column so that we can align plot areas properly
|
||||
|
||||
# l, b = 0.0, 0.0
|
||||
rights = zeros(nc)
|
||||
bottoms = ones(nr)
|
||||
rights = Measure[0mm for i=1:nc] #zeros(nc) .* pct
|
||||
bottoms = Measure[1pct for i=1:nr] # ones(nr) .* pct
|
||||
for r=1:nr, c=1:nc
|
||||
# compute the child's bounding box relative to the parent
|
||||
child = layout[r,c]
|
||||
usedw, usedh = used_size(child)
|
||||
@show r,c, usedw, usedh
|
||||
|
||||
left = (c == 1 ? 0 : rights[c-1])
|
||||
top = (r == 1 ? 1 : bottoms[r-1])
|
||||
plot_l = (c == 1 ? 0mm : rights[c-1])
|
||||
plot_t = (r == 1 ? height(layout) : bottoms[r-1])
|
||||
# bottom = (r == 1 ? 0 : bottoms[r-1])
|
||||
right = left + usedw + freew * layout.widths[c]
|
||||
bottom = top - usedh - freeh * layout.heights[r]
|
||||
# top = bottom + usedh + freeh * layout.heights[r]
|
||||
child_bbox = BoundingBox(left, bottom, right, top)
|
||||
plot_w = freew * layout.widths[c]
|
||||
plot_h = freeh * layout.heights[r]
|
||||
right = plot_l + usedw + plot_w
|
||||
bottom = plot_t - usedh - plot_h
|
||||
# plot_t = bottom + usedh + freeh * layout.heights[r]
|
||||
child_bbox = BoundingBox(plot_l, bottom, plot_w, plot_h)
|
||||
@show child_bbox
|
||||
|
||||
rights[c] = right
|
||||
bottoms[r] = bottom
|
||||
|
||||
# then compute the bounding box relative to the canvas, and cache it in the child object
|
||||
bbox!(child, crop(bbox(layout), child_bbox))
|
||||
@show child.bbox
|
||||
|
||||
# now recursively update the child
|
||||
update_bboxes!(child)
|
||||
upadte_child_bboxes!(child)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
112
src/types.jl
112
src/types.jl
@ -35,20 +35,100 @@ end
|
||||
# Layouts
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# wraps bounding box coords (percent of parent area)
|
||||
# NOTE: (0,0) is the bottom-left, and (1,1) is the top-right!
|
||||
immutable BoundingBox
|
||||
left::Float64
|
||||
bottom::Float64
|
||||
right::Float64
|
||||
top::Float64
|
||||
# # wraps bounding box coords (percent of parent area)
|
||||
# # NOTE: (0,0) is the bottom-left, and (1,1) is the top-right!
|
||||
# immutable BoundingBox
|
||||
# left::Float64
|
||||
# bottom::Float64
|
||||
# right::Float64
|
||||
# top::Float64
|
||||
# end
|
||||
# BoundingBox() = BoundingBox(0,0,0,0)
|
||||
|
||||
import Measures
|
||||
import Measures: Length, AbsoluteLength, Measure, BoundingBox, mm, cm, inch, pt, width, height
|
||||
export BBox, BoundingBox, mm, cm, inch, pt, px, pct
|
||||
|
||||
typealias BBox Measures.Absolute2DBox
|
||||
|
||||
# allow pixels and percentages
|
||||
const px = AbsoluteLength(0.254)
|
||||
const pct = Length{:pct, Float64}(1.0)
|
||||
|
||||
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))
|
||||
Base.(:-)(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * (m1.value - 1))
|
||||
Base.(:*)(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value * m2.value)
|
||||
Base.(:*)(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value * m1.value)
|
||||
Base.(:/)(m1::AbsoluteLength, m2::Length{:pct}) = AbsoluteLength(m1.value / m2.value)
|
||||
Base.(:/)(m1::Length{:pct}, m2::AbsoluteLength) = AbsoluteLength(m2.value / m1.value)
|
||||
|
||||
const defaultbox = BoundingBox(0mm, 0mm, 20mm, 20mm)
|
||||
|
||||
# left(bbox::BoundingBox) = bbox.left
|
||||
# bottom(bbox::BoundingBox) = bbox.bottom
|
||||
# right(bbox::BoundingBox) = bbox.right
|
||||
# top(bbox::BoundingBox) = bbox.top
|
||||
# width(bbox::BoundingBox) = bbox.right - bbox.left
|
||||
# height(bbox::BoundingBox) = bbox.top - bbox.bottom
|
||||
|
||||
|
||||
|
||||
left(bbox::BoundingBox) = bbox.x0[1]
|
||||
bottom(bbox::BoundingBox) = bbox.x0[2]
|
||||
right(bbox::BoundingBox) = left(bbox) + width(bbox)
|
||||
top(bbox::BoundingBox) = bottom(bbox) + height(bbox)
|
||||
Base.size(bbox::BoundingBox) = (width(bbox), height(bbox))
|
||||
|
||||
# Base.(:*){T,N}(m1::Length{T,N}, m2::Length{T,N}) = Length{T,N}(m1.value * m2.value)
|
||||
ispositive(m::Measure) = m.value > 0
|
||||
|
||||
# union together bounding boxes
|
||||
function Base.(:+)(bb1::BoundingBox, bb2::BoundingBox)
|
||||
# empty boxes don't change the union
|
||||
ispositive(width(bb1)) || return bb2
|
||||
ispositive(height(bb1)) || return bb2
|
||||
ispositive(width(bb2)) || return bb1
|
||||
ispositive(height(bb2)) || return bb1
|
||||
|
||||
# if width(bb1) <= 0mm || height(bb1) <= 0mm
|
||||
# return bb2
|
||||
# elseif width(bb2) <= 0mm || height(bb2) <= 0mm
|
||||
# return bb1
|
||||
# end
|
||||
l = min(left(bb1), left(bb2))
|
||||
b = min(bottom(bb1), bottom(bb2))
|
||||
r = max(right(bb1), right(bb2))
|
||||
t = max(top(bb1), top(bb2))
|
||||
BoundingBox(l, b, r-l, t-b)
|
||||
end
|
||||
|
||||
# this creates a bounding box in the parent's scope, where the child bounding box
|
||||
# is relative to the parent
|
||||
function crop(parent::BoundingBox, child::BoundingBox)
|
||||
# l = left(parent) + width(parent) * left(child)
|
||||
# b = bottom(parent) + height(parent) * bottom(child)
|
||||
# w = width(parent) * width(child)
|
||||
# h = height(parent) * height(child)
|
||||
l = left(parent) + left(child)
|
||||
b = bottom(parent) + bottom(child)
|
||||
w = width(child)
|
||||
h = height(child)
|
||||
BoundingBox(l, b, w, h)
|
||||
end
|
||||
|
||||
function Base.show(io::IO, bbox::BoundingBox)
|
||||
print(io, "BBox{l,b,r,t,w,h = $(left(bbox)), $(bottom(bbox)), $(right(bbox)), $(top(bbox)), $(width(bbox)), $(height(bbox))}")
|
||||
end
|
||||
BoundingBox() = BoundingBox(0,0,0,0)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
abstract AbstractLayout
|
||||
|
||||
width(layout::AbstractLayout) = width(layout.bbox)
|
||||
height(layout::AbstractLayout) = height(layout.bbox)
|
||||
|
||||
# -----------------------------------------------------------
|
||||
|
||||
# contains blank space
|
||||
@ -56,7 +136,7 @@ type EmptyLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
bbox::BoundingBox
|
||||
end
|
||||
EmptyLayout(parent = RootLayout()) = EmptyLayout(parent, BoundingBox(0,0,1,1))
|
||||
EmptyLayout(parent = RootLayout()) = EmptyLayout(parent, defaultbox)
|
||||
|
||||
# this is the parent of the top-level layout
|
||||
immutable RootLayout <: AbstractLayout
|
||||
@ -77,7 +157,7 @@ type Subplot{T<:AbstractBackend} <: AbstractLayout
|
||||
end
|
||||
|
||||
function Subplot{T<:AbstractBackend}(::T; parent = RootLayout())
|
||||
Subplot{T}(parent, BoundingBox(0,0,1,1), KW(), nothing)
|
||||
Subplot{T}(parent, defaultbox, KW(), nothing)
|
||||
end
|
||||
|
||||
# -----------------------------------------------------------
|
||||
@ -87,8 +167,8 @@ type GridLayout <: AbstractLayout
|
||||
parent::AbstractLayout
|
||||
bbox::BoundingBox
|
||||
grid::Matrix{AbstractLayout} # Nested layouts. Each position is a AbstractLayout, which allows for arbitrary recursion
|
||||
widths::Vector{Float64}
|
||||
heights::Vector{Float64}
|
||||
widths::Vector{Measure}
|
||||
heights::Vector{Measure}
|
||||
attr::KW
|
||||
end
|
||||
|
||||
@ -100,10 +180,12 @@ function GridLayout(dims...;
|
||||
grid = Matrix{AbstractLayout}(dims...)
|
||||
layout = GridLayout(
|
||||
parent,
|
||||
BoundingBox(0,0,1,1),
|
||||
defaultbox,
|
||||
grid,
|
||||
convert(Vector{Float64}, widths),
|
||||
convert(Vector{Float64}, heights),
|
||||
Measure[w*pct for w in widths],
|
||||
Measure[h*pct for h in heights],
|
||||
# convert(Vector{Float64}, widths),
|
||||
# convert(Vector{Float64}, heights),
|
||||
KW(kw))
|
||||
fill!(grid, EmptyLayout(layout))
|
||||
layout
|
||||
|
||||
@ -3,6 +3,7 @@ julia 0.4
|
||||
RecipesBase
|
||||
Colors
|
||||
Reexport
|
||||
Measures
|
||||
FactCheck
|
||||
Cairo
|
||||
Gadfly
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user