Centralize utility functions, add legend-angle to plotly, pgfplotsx

This commit is contained in:
David Gustavsson 2021-02-28 23:40:06 +01:00
parent 2cee039dbc
commit 6724a3a2fe
5 changed files with 77 additions and 43 deletions

View File

@ -211,6 +211,7 @@ include("output.jl")
include("ijulia.jl") include("ijulia.jl")
include("fileio.jl") include("fileio.jl")
include("init.jl") include("init.jl")
include("legend.jl")
include("backends/plotly.jl") include("backends/plotly.jl")
include("backends/gr.jl") include("backends/gr.jl")

View File

@ -1116,52 +1116,24 @@ function gr_legend_pos(v::Tuple{S,T}, viewport_plotarea) where {S<:Real, T<:Real
end end
function gr_legend_pos(theta::Real, leg, viewport_plotarea; axisclearance=nothing) function gr_legend_pos(theta::Real, leg, viewport_plotarea; axisclearance=nothing)
xmean = +(viewport_plotarea[1:2]...)/2 xcenter = +(viewport_plotarea[1:2]...)/2
ymean = +(viewport_plotarea[3:4]...)/2 ycenter = +(viewport_plotarea[3:4]...)/2
(s,c) = sincosd(theta)
if isnothing(axisclearance) if isnothing(axisclearance)
# Inner # Inner
# rectangle relative to midpoint where the anchor can legally be # rectangle where the anchor can legally be
rect = viewport_plotarea .+ [ xmin = viewport_plotarea[1] + leg.xoffset + leg.leftw
- xmean + leg.xoffset + leg.leftw, xmax = viewport_plotarea[2] - leg.xoffset - leg.rightw - leg.textw
- xmean - leg.xoffset - leg.rightw - leg.textw, ymin = viewport_plotarea[3] + leg.yoffset + leg.h
- ymean + leg.yoffset + leg.h, ymax = viewport_plotarea[4] - leg.yoffset - leg.dy
- ymean - leg.yoffset - leg.dy,
]
x = c < 0 ? rect[1]/c : rect[2]/c
y = s < 0 ? rect[3]/s : rect[4]/s
A = min(x,y) # Biggest A that places (Acos(theta),Asin(theta)) inside rectangle
else else
# Outer # Outer
# rectangle relative to midpoint where the anchor is forbidden xmin = viewport_plotarea[1] - leg.xoffset - leg.rightw - leg.textw - axisclearance[1]
rect = viewport_plotarea .+ [ xmax = viewport_plotarea[2] + leg.xoffset + leg.leftw + axisclearance[2]
- xmean - leg.xoffset - leg.rightw - leg.textw - axisclearance[1], ymin = viewport_plotarea[3] - leg.yoffset - leg.dy - axisclearance[3]
- xmean + leg.xoffset + leg.leftw + axisclearance[2], ymax = viewport_plotarea[4] + leg.yoffset + leg.h + axisclearance[4]
- ymean - leg.yoffset - leg.dy - axisclearance[3],
- ymean + leg.yoffset + leg.h + axisclearance[4],
]
t = tand(theta)
if c < 0
if t < rect[4]/rect[1]
A = rect[4]/s
elseif t < rect[3]/rect[1]
A = rect[1]/c
else
A = rect[3]/s
end
else
if t < rect[3]/rect[2]
A = rect[3]/s
elseif t <rect[4]/rect[2]
A = rect[2]/c
else
A = rect[4]/s
end
end
end end
return legend_pos_from_angle(theta,xmin,xcenter,xmax,ymin,ycenter,ymax)
return xmean + A*c, ymean + A*s
end end
function gr_get_legend_geometry(viewport_plotarea, sp) function gr_get_legend_geometry(viewport_plotarea, sp)

View File

@ -769,8 +769,26 @@ pgfx_get_legend_pos(k) = get(
Symbol(k), Symbol(k),
("at" => string((1.02, 1)), "anchor" => "north west"), ("at" => string((1.02, 1)), "anchor" => "north west"),
) )
pgfx_get_legend_pos(t::Tuple) = ("at" => "{$(string(t))}", "anchor" => "north west") pgfx_get_legend_pos(t::Tuple{S,T}) where {S<:Real,T<:Real} = ("at" => "{$(string(t))}", "anchor" => "north west")
pgfx_get_legend_pos(nt::NamedTuple) = ("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor)) pgfx_get_legend_pos(nt::NamedTuple) = ("at" => "{$(string(nt.at))}", "anchor" => string(nt.anchor))
pgfx_get_legend_pos(theta::Real) = pgfx_get_legend_pos((theta,:inner))
function pgfx_get_legend_pos(v::Tuple{S,Symbol}) where S <: Real
(s,c) = sincosd(v[1])
anchors = [
"north west" "north" "north east";
"west" "center" "east";
"south west" "south" "south east";
]
if v[2] === :inner
rect = (0.07,0.5,1.0,0.07,0.52,1.0)
anchor = anchors[legend_anchor_index(s),legend_anchor_index(c)]
else
rect = (-0.15,0.5,1.05,-0.15,0.52,1.1)
anchor = anchors[4-legend_anchor_index(s),4-legend_anchor_index(c)]
end
return ("at"=>"$(string(legend_pos_from_angle(v[1],rect...)))", "anchor"=>anchor)
end
pgfx_get_colorbar_pos(s) = pgfx_get_colorbar_pos(s) =
get((left = " left", bottom = " horizontal", top = " horizontal"), s, "") get((left = " left", bottom = " horizontal", top = " horizontal"), s, "")

View File

@ -384,6 +384,25 @@ end
plotly_legend_pos(v::Tuple{S,T}) where {S<:Real, T<:Real} = (coords=v, xanchor="left", yanchor="top") plotly_legend_pos(v::Tuple{S,T}) where {S<:Real, T<:Real} = (coords=v, xanchor="left", yanchor="top")
plotly_legend_pos(theta::Real) = plotly_legend_pos((theta, :inner))
function plotly_legend_pos(v::Tuple{S,Symbol}) where S<:Real
(s,c) = sincosd(v[1])
xanchors = ["left", "center", "right"]
yanchors = ["bottom", "middle", "top"]
if v[2] === :inner
rect = (0.07,0.5,1.0,0.07,0.52,1.0)
xanchor = xanchors[legend_anchor_index(c)]
yanchor = yanchors[legend_anchor_index(s)]
else
rect = (-0.15,0.5,1.05,-0.15,0.52,1.1)
xanchor = xanchors[4-legend_anchor_index(c)]
yanchor = yanchors[4-legend_anchor_index(s)]
end
return (coords=legend_pos_from_angle(v[1],rect...), xanchor=xanchor, yanchor=yanchor)
end
function plotly_layout_json(plt::Plot) function plotly_layout_json(plt::Plot)
JSON.json(plotly_layout(plt), 4) JSON.json(plotly_layout(plt), 4)
@ -595,9 +614,9 @@ function plotly_series(plt::Plot, series::Series)
elseif st == :mesh3d elseif st == :mesh3d
plotattributes_out[:type] = "mesh3d" plotattributes_out[:type] = "mesh3d"
plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z
if series[:connections] !== nothing if series[:connections] !== nothing
if typeof(series[:connections]) <: Tuple{Array,Array,Array} if typeof(series[:connections]) <: Tuple{Array,Array,Array}
i,j,k = series[:connections] i,j,k = series[:connections]
if !(length(i) == length(j) == length(k)) if !(length(i) == length(j) == length(k))
throw(ArgumentError("Argument connections must consist of equally sized arrays.")) throw(ArgumentError("Argument connections must consist of equally sized arrays."))

24
src/legend.jl Normal file
View File

@ -0,0 +1,24 @@
"""
```julia
legend_pos_from_angle(theta, xmin, xcenter, xmax, ymin, ycenter, ymax, inout)
```
Return `(x,y)` at an angle `theta` degrees from
`(xcenter,ycenter)` on a rectangle defined by (`xmin`,
`xmax`, `ymin`, `ymax`).
"""
function legend_pos_from_angle(theta, xmin, xcenter, xmax, ymin, ycenter, ymax)
(s,c) = sincosd(theta)
x = c < 0 ? (xmin-xcenter)/c : (xmax-xcenter)/c
y = s < 0 ? (ymin-ycenter)/s : (ymax-ycenter)/s
A = min(x,y)
return (xcenter + A*c, ycenter + A*s)
end
"""
Split continuous range `[-1,1]` into an integer `[1,2,3]`
"""
function legend_anchor_index(x)
return ceil(Integer,2//3*(x+1))
end