Skip to content

Commit

Permalink
Cleanup align handling (#3191)
Browse files Browse the repository at this point in the history
* cleanup/unify align conversions

* adjust error message handling

* bring back old error

* fix typo

* simplify error testing

* add angle2align
  • Loading branch information
ffreyer authored Aug 31, 2023
1 parent 3414ed2 commit e4e50d1
Show file tree
Hide file tree
Showing 8 changed files with 117 additions and 91 deletions.
16 changes: 2 additions & 14 deletions src/basic_recipes/barplot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -162,22 +162,10 @@ function stack_grouped_from_to(i_stack, y, grp)
end

function calculate_bar_label_align(label_align, label_rotation::Real, in_y_direction::Bool, flip::Bool)
make_align(a::VecTypes{2, <:Real}) = Vec2f(a)
make_align(a::NTuple{2, Symbol}) = to_align(a)
make_align(a) = error("`label_align` needs to be of type NTuple{2, <:Real}, not of type $(typeof(a))")
if label_align == automatic
if flip
label_rotation += π
end
if !in_y_direction
label_rotation += π/2
end
s, c = sincos(label_rotation)
scale = 1 / max(abs(s), abs(c))
align = Vec2f(0.5 - 0.5scale * s, 0.5 - 0.5scale * c)
return align
return angle2align(-label_rotation - !flip * pi + in_y_direction * pi/2)
else
return make_align(label_align)
return to_align(label_align, "Failed to convert `label_align` $label_align.")
end
end

Expand Down
77 changes: 74 additions & 3 deletions src/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -999,10 +999,81 @@ convert_attribute(c::Tuple{<: Number, <: Number, <: Number}, ::key"position") =
convert_attribute(c::VecTypes{N}, ::key"position") where N = Point{N, Float32}(c)

"""
Text align, e.g.:
to_align(align[, error_prefix])
Converts the given align to a `Vec2f`. Can convert `VecTypes{2}` and two
component `Tuple`s with `Real` and `Symbol` elements.
To specify a custom error message you can add an `error_prefix` or use
`halign2num(value, error_msg)` and `valign2num(value, error_msg)` respectively.
"""
to_align(x::Tuple) = Vec2f(halign2num(x[1]), valign2num(x[2]))
to_align(x::VecTypes{2, <:Real}) = Vec2f(x)

function to_align(v, error_prefix::String)
try
return to_align(v)
catch
error(error_prefix)
end
end

"""
halign2num(align[, error_msg])
Attempts to convert a horizontal align to a Float32 and errors with `error_msg`
if it fails to do so.
"""
halign2num(v::Real, error_msg = "") = Float32(v)
function halign2num(v::Symbol, error_msg = "Invalid halign $v. Valid values are <:Real, :left, :center and :right.")
if v === :left
return 0.0f0
elseif v === :center
return 0.5f0
elseif v === :right
return 1.0f0
else
error(error_msg)
end
end
function halign2num(v, error_msg = "Invalid halign $v. Valid values are <:Real, :left, :center and :right.")
error(error_msg)
end

"""
valign2num(align[, error_msg])
Attempts to convert a vertical align to a Float32 and errors with `error_msg`
if it fails to do so.
"""
valign2num(v::Real, error_msg = "") = Float32(v)
function valign2num(v::Symbol, error_msg = "Invalid valign $v. Valid values are <:Real, :bottom, :top, and :center.")
if v === :top
return 1f0
elseif v === :bottom
return 0f0
elseif v === :center
return 0.5f0
else
error(error_msg)
end
end
function valign2num(v, error_msg = "Invalid valign $v. Valid values are <:Real, :bottom, :top, and :center.")
error(error_msg)
end

"""
to_align(x::Tuple{Symbol, Symbol}) = Vec2f(alignment2num.(x))
to_align(x::Vec2f) = x
angle2align(angle::Real)
Converts a given angle to an alignment by projecting the resulting direction on
a unit square and scaling the result to a 0..1 range appropriate for alignments.
"""
function angle2align(angle::Real)
s, c = sincos(angle)
scale = 1 / max(abs(s), abs(c))
return Vec2f(0.5scale * c + 0.5, 0.5scale * s + 0.5)
end


const FONT_CACHE = Dict{String, NativeFont}()
const FONT_CACHE_LOCK = Base.ReentrantLock()
Expand Down
40 changes: 3 additions & 37 deletions src/layouting/layouting.jl
Original file line number Diff line number Diff line change
Expand Up @@ -179,14 +179,8 @@ function glyph_collection(
else
0.5f0
end
elseif justification === :left
0.0f0
elseif justification === :right
1.0f0
elseif justification === :center
0.5f0
else
Float32(justification)
halign2num(justification, "Invalid justification $justification. Valid values are <:Real, :left, :center and :right.")
end

xs_justified = map(xs, width_differences) do xsgroup, wd
Expand All @@ -203,17 +197,7 @@ function glyph_collection(
ys = cumsum([0.0; -lineheights[2:end]])

# compute x values after left/center/right alignment
halign = if halign isa Number
Float32(halign)
elseif halign === :left
0.0f0
elseif halign === :center
0.5f0
elseif halign === :right
1.0f0
else
error("Invalid halign $halign. Valid values are <:Number, :left, :center and :right.")
end
halign = halign2num(halign)
xs_aligned = [xsgroup .- halign * maxwidth for xsgroup in xs_justified]

# for y alignment, we need the largest ascender of the first line
Expand All @@ -233,17 +217,7 @@ function glyph_collection(
ys_aligned = if valign === :baseline
ys .- first_line_ascender .+ overall_height .+ last_line_descender
else
va = if valign isa Number
Float32(valign)
elseif valign === :top
1f0
elseif valign === :bottom
0f0
elseif valign === :center
0.5f0
else
error("Invalid valign $valign. Valid values are <:Number, :bottom, :baseline, :top, and :center.")
end
va = valign2num(valign, "Invalid valign $valign. Valid values are <:Number, :bottom, :baseline, :top, and :center.")
ys .- first_line_ascender .+ (1 - va) .* overall_height
end

Expand Down Expand Up @@ -287,14 +261,6 @@ function padded_vcat(arrs::AbstractVector{T}, fillvalue) where T <: AbstractVect
arr
end

function alignment2num(x::Symbol)
(x === :center) && return 0.5f0
(x in (:left, :bottom)) && return 0.0f0
(x in (:right, :top)) && return 1.0f0
return 0.0f0 # 0 default, or better to error?
end


# Backend data

_offset_to_vec(o::VecTypes) = to_ndim(Vec3f, o, 0)
Expand Down
11 changes: 2 additions & 9 deletions src/makielayout/blocks/axis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -439,15 +439,8 @@ function initialize_block!(ax::Axis; palette = nothing)
ignore_equal_values=true) do a,
titlegap, align, xaxisposition, xaxisprotrusion

x = if align === :center
a.origin[1] + a.widths[1] / 2
elseif align === :left
a.origin[1]
elseif align === :right
a.origin[1] + a.widths[1]
else
error("Title align $align not supported.")
end
align_factor = halign2num(align, "Horizontal title align $align not supported.")
x = a.origin[1] + align_factor * a.widths[1]

yoffset = top(a) + titlegap + (xaxisposition === :top ? xaxisprotrusion : 0f0)

Expand Down
13 changes: 3 additions & 10 deletions src/makielayout/blocks/axis3d.jl
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,8 @@ function initialize_block!(ax::Axis3)

titlepos = lift(scene, scene.px_area, ax.titlegap, ax.titlealign) do a, titlegap, align

x = if align === :center
a.origin[1] + a.widths[1] / 2
elseif align === :left
a.origin[1]
elseif align === :right
a.origin[1] + a.widths[1]
else
error("Title align $align not supported.")
end
align_factor = halign2num(align, "Horizontal title align $align not supported.")
x = a.origin[1] + align_factor * a.widths[1]

yoffset = top(a) + titlegap

Expand Down Expand Up @@ -1122,7 +1115,7 @@ function attribute_examples(::Type{Axis3})
name = "`zreversed` on and off",
code = """
using FileIO
fig = Figure()
brain = load(assetpath("brain.stl"))
Expand Down
19 changes: 4 additions & 15 deletions src/makielayout/blocks/polaraxis.jl
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,7 @@ function draw_axis!(po::PolarAxis, axis_radius)
end

thetatick_align[] = map(_thetatickvalues) do angle
s, c = sincos(dir * (angle + theta_0))
scale = 1 / max(abs(s), abs(c)) # point on ellipse -> point on bbox
Point2f(0.5 - 0.5scale * c, 0.5 - 0.5scale * s)
return angle2align(dir * (angle + theta_0) + pi)
end

# transform px_pad to radial pad
Expand Down Expand Up @@ -257,9 +255,7 @@ function draw_axis!(po::PolarAxis, axis_radius)
strokecolor = rstrokecolor,
visible = po.rticklabelsvisible,
align = map(po.direction, po.theta_0, po.rtickangle) do dir, theta_0, angle
s, c = sincos(dir * (angle + theta_0))
scale = 1 / max(abs(s), abs(c)) # point on ellipse -> point on bbox
Point2f(0.5 - 0.5scale * c, 0.5 - 0.5scale * s)
return angle2align(dir * (angle + theta_0) + pi)
end
)

Expand Down Expand Up @@ -341,15 +337,8 @@ end
function calculate_polar_title_position(area, titlegap, align)
w, h = area.widths

x::Float32 = if align === :center
area.origin[1] + w / 2
elseif align === :left
area.origin[1]
elseif align === :right
area.origin[1] + w
else
error("Title align $align not supported.")
end
align_factor = halign2num(align, "Horizontal title align $align not supported.")
x::Float32 = area.origin[1] + align_factor * w

# local subtitlespace::Float32 = if ax.subtitlevisible[] && !iswhitespace(ax.subtitle[])
# boundingbox(subtitlet).widths[2] + subtitlegap
Expand Down
5 changes: 2 additions & 3 deletions test/barplot_labels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,8 @@

@testset "error" begin
input = 0.0, false, false
for align in ("center", 0.5, ("center", "center"), (0.5, :center))
msg = "`label_align` needs to be of type NTuple{2, <:Real}, not of type $(typeof(align))"
@test_throws ErrorException(msg) Makie.calculate_bar_label_align(align, input...)
for align in ("center", 0.5, ("center", "center"))
@test_throws ErrorException Makie.calculate_bar_label_align(align, input...)
end
end

Expand Down
27 changes: 27 additions & 0 deletions test/conversions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -355,4 +355,31 @@ end

@test isapprox(cs, zs, rtol = 1e-6)
end
end

@testset "align conversions" begin
for (val, halign) in zip((0f0, 0.5f0, 1f0), (:left, :center, :right))
@test Makie.halign2num(halign) == val
end
@test_throws ErrorException Makie.halign2num(:bottom)
@test_throws ErrorException Makie.halign2num("center")
@test Makie.halign2num(0.73) == 0.73f0

for (val, valign) in zip((0f0, 0.5f0, 1f0), (:bottom, :center, :top))
@test Makie.valign2num(valign) == val
end
@test_throws ErrorException Makie.valign2num(:right)
@test_throws ErrorException Makie.valign2num("center")
@test Makie.valign2num(0.23) == 0.23f0

@test Makie.to_align((:center, :bottom)) == Vec2f(0.5, 0.0)
@test Makie.to_align((:right, 0.3)) == Vec2f(1.0, 0.3)

for angle in 4pi .* rand(10)
s, c = sincos(angle)
@test Makie.angle2align(angle) Vec2f(0.5c, 0.5s) ./ max(abs(s), abs(c)) .+ Vec2f(0.5)
end
# sanity checks
@test isapprox(Makie.angle2align(pi/4), Vec2f(1, 1), atol = 1e-12)
@test isapprox(Makie.angle2align(5pi/4), Vec2f(0, 0), atol = 1e-12)
end

0 comments on commit e4e50d1

Please sign in to comment.