Created
June 11, 2019 09:37
-
-
Save SimonDanisch/9fda49e77f5294e1ec532be804bdd8e9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Rhea, Makie | |
# Vbox: left and right-most spines match, heights are distributed | |
# Hbox: top and bottom-most spines match, widths are distributed | |
# grid: rows and columns aligned | |
mutable struct Axis | |
l::variable{Float64} | |
t::variable{Float64} | |
r::variable{Float64} | |
b::variable{Float64} | |
sl::variable{Float64} | |
st::variable{Float64} | |
sr::variable{Float64} | |
sb::variable{Float64} | |
end | |
Axis() = Axis((variable() for f in 1:fieldcount(Axis))...) | |
abstract type Box end | |
const Boxcontent = Union{Box, Axis} | |
struct Vbox <: Box | |
c::Vector{Boxcontent} | |
relheights::Vector{Float64} | |
Vbox(c::Vector{S}, relheights::Union{Vector{T}, Nothing} = nothing) where {S<:Boxcontent, T<:Real}= begin | |
if length(c) <= 1 | |
error("$n elements in Vbox not allowed") | |
end | |
if isnothing(relheights) | |
relheights = [1 for i in 1:length(c)] | |
end | |
if length(relheights) != length(c) | |
error("$(length(relheights)) for $(length(c)) elements") | |
end | |
new(c, convert(Vector{Float64}, relheights)) | |
end | |
end | |
Vbox(n::Int, relheights::Union{Nothing, Vector{T}} = nothing) where {T<:Real} = begin | |
Vbox([Axis() for i in 1:n], relheights) | |
end | |
struct Hbox <: Box | |
c::Vector{Boxcontent} | |
relwidths::Vector{Float64} | |
Hbox(c::Vector{S}, relwidths::Union{Vector{T}, Nothing} = nothing) where {S<:Boxcontent, T<:Real}= begin | |
if length(c) <= 1 | |
error("$n elements in Hbox not allowed") | |
end | |
if isnothing(relwidths) | |
relwidths = [1 for i in 1:length(c)] | |
end | |
if length(relwidths) != length(c) | |
error("$(length(relwidths)) for $(length(c)) elements") | |
end | |
new(c, convert(Vector{Float64}, relwidths)) | |
end | |
end | |
Hbox(n::Int, relwidths::Union{Nothing, Vector{T}} = nothing) where {T<:Real} = begin | |
Hbox([Axis() for i in 1:n], relwidths) | |
end | |
rectangle(l, t, r, b) = FRect2D((l, b), (r-l, t - b)) | |
function axtoshapes(a::Axis) | |
l = value(a.l) | |
r = value(a.r) | |
t = value(a.t) | |
b = value(a.b) | |
sl = l - value(a.sl) | |
sr = r + value(a.sr) | |
st = t + value(a.st) | |
sb = b - value(a.sb) | |
main = rectangle(l, t, r, b) | |
left = rectangle(sl, t, l, b) | |
top = rectangle(l, st, r, t) | |
right = rectangle(r, t, sr, b) | |
bottom = rectangle(l, b, r, sb) | |
[main, left, top, right, bottom] | |
end | |
function plot_axis(a::Axis) | |
c = fill(rand(RGBf0), 5) | |
poly!(axtoshapes(a), color = tuple.(c, [1, 0.5, 0.5, 0.5, 0.5])) | |
end | |
function plot_axis(as::Vector{Axis}) | |
for a in as | |
plot_axis(a) | |
end | |
end | |
function plot_axis(b::Box) | |
for c in b.c | |
plot_axis(c) | |
end | |
end | |
topspine(a::Axis) = a.t | |
topspine(b::Box) = topspine(b.c[1]) # just first element, they will be aligned for hbox | |
bottomspine(a::Axis) = a.b | |
bottomspine(b::Box) = bottomspine(b.c[end]) | |
leftspine(a::Axis) = a.l | |
leftspine(b::Box) = leftspine(b.c[1]) | |
rightspine(a::Axis) = a.r | |
rightspine(b::Box) = rightspine(b.c[end]) | |
axiswidth(a::Axis) = rightspine(a) - leftspine(a) | |
axisheight(a::Axis) = topspine(a) - bottomspine(a) | |
axiswidth(b::Box) = rightspine(b) - leftspine(b) | |
axisheight(b::Box) = topspine(b) - bottomspine(b) | |
width(o::Union{Box, Axis}) = rightedge(o) - leftedge(o) | |
height(o::Union{Box, Axis}) = topedge(o) - bottomedge(o) | |
leftedge(a::Axis) = leftspine(a) - a.sl | |
rightedge(a::Axis) = rightspine(a) + a.sr | |
topedge(a::Axis) = topspine(a) + a.st | |
bottomedge(a::Axis) = bottomspine(a) - a.sb | |
leftmostax(a::Axis) = (a.sl, a) | |
function leftmostax(v::Vbox) | |
val = -Inf | |
candidate = nothing | |
for c in v.c | |
(newvar, newcand) = leftmostax(c) | |
newval = newvar | |
if typeof(newval) <: variable | |
newval = value(newval) | |
end | |
if newval > val | |
candidate = newcand | |
val = newval | |
end | |
end | |
(val, candidate) | |
end | |
function leftmostax(h::Hbox) | |
leftmostax(h.c[1]) | |
end | |
rightmostax(a::Axis) = (a.sr, a) | |
function rightmostax(v::Vbox) | |
val = -Inf | |
candidate = nothing | |
for c in v.c | |
(newvar, newcand) = rightmostax(c) | |
newval = newvar | |
if typeof(newval) <: variable | |
newval = value(newval) | |
end | |
if newval > val | |
candidate = newcand | |
val = newval | |
end | |
end | |
(val, candidate) | |
end | |
function rightmostax(h::Hbox) | |
rightmostax(h.c[end]) | |
end | |
topmostax(a::Axis) = (a.st, a) | |
function topmostax(h::Hbox) | |
val = -Inf | |
candidate = nothing | |
for c in h.c | |
(newvar, newcand) = topmostax(c) | |
newval = newvar | |
if typeof(newval) <: variable | |
newval = value(newval) | |
end | |
if newval > val | |
candidate = newcand | |
val = newval | |
end | |
end | |
(val, candidate) | |
end | |
function topmostax(v::Vbox) | |
topmostax(v.c[1]) | |
end | |
bottommostax(a::Axis) = (a.sb, a) | |
function bottommostax(h::Hbox) | |
val = -Inf | |
candidate = nothing | |
for c in h.c | |
(newvar, newcand) = bottommostax(c) | |
newval = newvar | |
if typeof(newval) <: variable | |
newval = value(newval) | |
end | |
if newval > val | |
candidate = newcand | |
val = newval | |
end | |
end | |
(val, candidate) | |
end | |
function bottommostax(v::Vbox) | |
bottommostax(v.c[end]) | |
end | |
leftedge(b::Vbox) = begin | |
(_, ax) = leftmostax(b) | |
leftedge(ax) | |
end | |
rightedge(b::Vbox) = begin | |
(_, ax) = rightmostax(b) | |
rightedge(ax) | |
end | |
topedge(b::Vbox) = topedge(b.c[1]) | |
bottomedge(b::Vbox) = bottomedge(b.c[end]) | |
leftedge(b::Hbox) = leftedge(b.c[1]) | |
rightedge(b::Hbox) = rightedge(b.c[end]) | |
topedge(b::Hbox) = begin | |
(_, ax) = topmostax(b) | |
topedge(ax) | |
end | |
bottomedge(b::Hbox) = begin | |
(_, ax) = bottommostax(b) | |
bottomedge(ax) | |
end | |
import Rhea.add_constraints | |
function add_constraints(s::simplex_solver, v::Vbox) | |
for c in v.c | |
# go depth first, so that the constraints specifying the | |
# decoration sizes get set up before functions trying | |
# to find the biggest ones | |
add_constraints(s, c) | |
end | |
closegaps = [bottomedge(v.c[i]) == topedge(v.c[i + 1]) for i in 1:length(v.c)-1] | |
relheights = [ | |
axisheight(v.c[i]) * v.relheights[i+1] == axisheight(v.c[i+1]) * v.relheights[i] | |
for i in 1:length(v.c)-1 | |
] | |
leftalignments = [ | |
leftspine(v.c[i]) == leftspine(v.c[i+1]) | |
for i in 1:length(v.c)-1 | |
] | |
rightalignments = [ | |
rightspine(v.c[i]) == rightspine(v.c[i+1]) | |
for i in 1:length(v.c)-1 | |
] | |
add_constraints(s, closegaps) | |
add_constraints(s, relheights) | |
add_constraints(s, leftalignments) | |
add_constraints(s, rightalignments) | |
end | |
function add_constraints(s::simplex_solver, h::Hbox) | |
for c in h.c | |
add_constraints(s, c) | |
end | |
closegaps = [leftedge(h.c[i+1]) == rightedge(h.c[i]) for i in 1:length(h.c)-1] | |
relwidths = [ | |
axiswidth(h.c[i]) * h.relwidths[i+1] == axiswidth(h.c[i+1]) * h.relwidths[i] | |
for i in 1:length(h.c)-1 | |
] | |
topalignments = [ | |
topspine(h.c[i]) == topspine(h.c[i+1]) | |
for i in 1:length(h.c)-1 | |
] | |
bottomalignments = [ | |
bottomspine(h.c[i]) == bottomspine(h.c[i+1]) | |
for i in 1:length(h.c)-1 | |
] | |
add_constraints(s, closegaps) | |
add_constraints(s, relwidths) | |
add_constraints(s, topalignments) | |
add_constraints(s, bottomalignments) | |
end | |
rrange(lo, hi) = rand() * (hi - lo) + lo | |
function add_constraints(s::simplex_solver, a::Axis) | |
constraints = [ | |
leftspine(a) <= rightspine(a), | |
topspine(a) >= bottomspine(a), | |
a.sl == rrange(10, 30), | |
a.sr == rrange(10, 30), | |
a.st == rrange(10, 30), | |
a.sb == rrange(10, 30) | |
] | |
add_constraints(s, constraints) | |
end | |
function test_constraint_layout() | |
h1 = Hbox(3, [1, 2, 1]) | |
v1 = Vbox(2) | |
h2 = Hbox([h1, v1], [2, 1]) | |
v2 = Vbox(2) | |
v3 = Vbox([h2, v2]) | |
v4 = Vbox(3, [2, 3, 4]) | |
h3 = Hbox([v3, v4], [3, 2]) | |
s = simplex_solver() | |
# recursively add all constraints of objects in the top box | |
add_constraints(s, h3) | |
# add specific constraints for plot size and position | |
add_constraints(s, [ | |
width(h3) == 800, | |
height(h3) == 400, | |
leftedge(h3) == 0, | |
bottomedge(h3) == 0 | |
]) | |
s = Scene(scale_plot = false) | |
plot_axis(h3) | |
s | |
end | |
test_constraint_layout() | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment