diff --git a/src/Plots.jl b/src/Plots.jl index 50b4205c..5378fb73 100644 --- a/src/Plots.jl +++ b/src/Plots.jl @@ -211,6 +211,7 @@ include("output.jl") include("ijulia.jl") include("fileio.jl") include("init.jl") +include("legend.jl") include("backends/plotly.jl") include("backends/gr.jl") diff --git a/src/backends/gr.jl b/src/backends/gr.jl index 29ac071c..3e89ac90 100644 --- a/src/backends/gr.jl +++ b/src/backends/gr.jl @@ -1116,52 +1116,24 @@ function gr_legend_pos(v::Tuple{S,T}, viewport_plotarea) where {S<:Real, T<:Real end function gr_legend_pos(theta::Real, leg, viewport_plotarea; axisclearance=nothing) - xmean = +(viewport_plotarea[1:2]...)/2 - ymean = +(viewport_plotarea[3:4]...)/2 - (s,c) = sincosd(theta) + xcenter = +(viewport_plotarea[1:2]...)/2 + ycenter = +(viewport_plotarea[3:4]...)/2 if isnothing(axisclearance) # Inner - # rectangle relative to midpoint where the anchor can legally be - rect = viewport_plotarea .+ [ - - xmean + leg.xoffset + leg.leftw, - - xmean - leg.xoffset - leg.rightw - leg.textw, - - ymean + leg.yoffset + leg.h, - - 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 + # rectangle where the anchor can legally be + xmin = viewport_plotarea[1] + leg.xoffset + leg.leftw + xmax = viewport_plotarea[2] - leg.xoffset - leg.rightw - leg.textw + ymin = viewport_plotarea[3] + leg.yoffset + leg.h + ymax = viewport_plotarea[4] - leg.yoffset - leg.dy else # Outer - # rectangle relative to midpoint where the anchor is forbidden - rect = viewport_plotarea .+ [ - - xmean - leg.xoffset - leg.rightw - leg.textw - axisclearance[1], - - xmean + leg.xoffset + leg.leftw + axisclearance[2], - - 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 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(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) = get((left = " left", bottom = " horizontal", top = " horizontal"), s, "") diff --git a/src/backends/plotly.jl b/src/backends/plotly.jl index ce2b704c..3b6d2794 100644 --- a/src/backends/plotly.jl +++ b/src/backends/plotly.jl @@ -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(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) JSON.json(plotly_layout(plt), 4) @@ -595,9 +614,9 @@ function plotly_series(plt::Plot, series::Series) elseif st == :mesh3d plotattributes_out[:type] = "mesh3d" plotattributes_out[:x], plotattributes_out[:y], plotattributes_out[:z] = x, y, z - + 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] if !(length(i) == length(j) == length(k)) throw(ArgumentError("Argument connections must consist of equally sized arrays.")) diff --git a/src/legend.jl b/src/legend.jl new file mode 100644 index 00000000..7037bc14 --- /dev/null +++ b/src/legend.jl @@ -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