Skip to content

Commit

Permalink
Bar label alignment (#3177)
Browse files Browse the repository at this point in the history
* Improve Barplot Label Alignment

* Barplot Label Alignment tests and NEWS.md

* Refactor calculate_bar_label_align with simpler code

---------

Co-authored-by: Matthijs Cox <matthijs.cox@asml.com>
Co-authored-by: Simon <sdanisch@protonmail.com>
  • Loading branch information
3 people committed Aug 29, 2023
1 parent 1e3190a commit fc95b21
Show file tree
Hide file tree
Showing 4 changed files with 93 additions and 10 deletions.
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## master

- Improved Barplot Label Alignment [#3160](https://github.com/MakieOrg/Makie.jl/issues/3160).
- Fixed regression in determining axis limits [#3179](https://github.com/MakieOrg/Makie.jl/pull/3179)
- Added a theme `theme_latexfonts` that uses the latex font family as default fonts [#3147](https://github.com/MakieOrg/Makie.jl/pull/3147).

Expand Down
48 changes: 38 additions & 10 deletions src/basic_recipes/barplot.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ $(ATTRIBUTES)
label_font = theme(scene, :font),
label_size = theme(scene, :fontsize),
label_formatter = bar_label_formatter,
label_align = automatic,
transparency = false
)
end
Expand Down Expand Up @@ -160,7 +161,28 @@ function stack_grouped_from_to(i_stack, y, grp)
(from = from, to = to)
end

function text_attributes(values, in_y_direction, flip_labels_at, color_over_background, color_over_bar, label_offset)
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
else
return make_align(label_align)
end
end

function text_attributes(values, in_y_direction, flip_labels_at, color_over_background, color_over_bar,
label_offset, label_rotation, label_align)
aligns = Vec2f[]
offsets = Vec2f[]
text_colors = RGBAf[]
Expand All @@ -178,22 +200,27 @@ function text_attributes(values, in_y_direction, flip_labels_at, color_over_back
end

for (i, k) in enumerate(values)
# Plot text inside bar
if flip(k)
push!(aligns, swap(0.5, 1.0))

isflipped = flip(k)

push!(aligns, calculate_bar_label_align(label_align, label_rotation, in_y_direction, isflipped))

if isflipped
# plot text inside bar
push!(offsets, swap(0, -label_offset))
push!(text_colors, geti(color_over_bar, i))
else
# plot text next to bar
push!(aligns, swap(0.5, 0.0))
push!(offsets, swap(0, label_offset))
push!(text_colors, geti(color_over_background, i))
end
end
return aligns, offsets, text_colors
end

function barplot_labels(xpositions, ypositions, bar_labels, in_y_direction, flip_labels_at, color_over_background, color_over_bar, label_formatter, label_offset)
function barplot_labels(xpositions, ypositions, bar_labels, in_y_direction, flip_labels_at,
color_over_background, color_over_bar, label_formatter, label_offset, label_rotation,
label_align)
if bar_labels isa Symbol && bar_labels in (:x, :y)
bar_labels = map(xpositions, ypositions) do x, y
if bar_labels === :x
Expand All @@ -205,7 +232,8 @@ function barplot_labels(xpositions, ypositions, bar_labels, in_y_direction, flip
end
if bar_labels isa AbstractVector
if length(bar_labels) == length(xpositions)
attributes = text_attributes(ypositions, in_y_direction, flip_labels_at, color_over_background, color_over_bar, label_offset)
attributes = text_attributes(ypositions, in_y_direction, flip_labels_at, color_over_background,
color_over_bar, label_offset, label_rotation, label_align)
label_pos = map(xpositions, ypositions, bar_labels) do x, y, l
return (string(l), in_y_direction ? Point2f(x, y) : Point2f(y, x))
end
Expand All @@ -226,7 +254,7 @@ function Makie.plot!(p::BarPlot)
label_colors = Observable(RGBAf[])
function calculate_bars(xy, fillto, offset, transformation, width, dodge, n_dodge, gap, dodge_gap, stack,
dir, bar_labels, flip_labels_at, label_color, color_over_background,
color_over_bar, label_formatter, label_offset)
color_over_bar, label_formatter, label_offset, label_rotation, label_align)

in_y_direction = get((y=true, x=false), dir) do
error("Invalid direction $dir. Options are :x and :y.")
Expand Down Expand Up @@ -276,7 +304,7 @@ function Makie.plot!(p::BarPlot)
obar = color_over_bar === automatic ? label_color : color_over_bar
label_args = barplot_labels(x̂, y, bar_labels, in_y_direction,
flip_labels_at, to_color(oback), to_color(obar),
label_formatter, label_offset)
label_formatter, label_offset, label_rotation, label_align)
labels[], label_aligns[], label_offsets[], label_colors[] = label_args
end

Expand All @@ -285,7 +313,7 @@ function Makie.plot!(p::BarPlot)

bars = lift(calculate_bars, p, p[1], p.fillto, p.offset, p.transformation.transform_func, p.width, p.dodge, p.n_dodge, p.gap,
p.dodge_gap, p.stack, p.direction, p.bar_labels, p.flip_labels_at,
p.label_color, p.color_over_background, p.color_over_bar, p.label_formatter, p.label_offset)
p.label_color, p.color_over_background, p.color_over_bar, p.label_formatter, p.label_offset, p.label_rotation, p.label_align)

poly!(
p, bars, color = p.color, colormap = p.colormap, colorscale = p.colorscale, colorrange = p.colorrange,
Expand Down
53 changes: 53 additions & 0 deletions test/barplot_labels.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
@testset "Barplot label align" begin
@testset "automatic" begin
# for more info see https://github.com/MakieOrg/Makie.jl/issues/3160
# below is the best square angles behavior for bar labels

al = Makie.automatic

y_dir, flip = false, false
@test Makie.calculate_bar_label_align(al, 0.0, y_dir, flip) Vec2f(0.0, 0.5)
@test Makie.calculate_bar_label_align(al, π, y_dir, flip) Vec2f(1.0, 0.5)
@test Makie.calculate_bar_label_align(al, π/2, y_dir, flip) Vec2f(0.5, 1.0)
@test Makie.calculate_bar_label_align(al, -π/2, y_dir, flip) Vec2f(0.5, 0.0)

y_dir, flip = true, false
@test Makie.calculate_bar_label_align(al, 0.0, y_dir, flip) Vec2f(0.5, 0.0)
@test Makie.calculate_bar_label_align(al, π, y_dir, flip) Vec2f(0.5, 1.0)
@test Makie.calculate_bar_label_align(al, π/2, y_dir, flip) Vec2f(0.0, 0.5)
@test Makie.calculate_bar_label_align(al, -π/2, y_dir, flip) Vec2f(1.0, 0.5)

y_dir, flip = false, true
@test Makie.calculate_bar_label_align(al, 0.0, y_dir, flip) Vec2f(1.0, 0.5)
@test Makie.calculate_bar_label_align(al, π, y_dir, flip) Vec2f(0.0, 0.5)
@test Makie.calculate_bar_label_align(al, π/2, y_dir, flip) Vec2f(0.5, 0.0)
@test Makie.calculate_bar_label_align(al, -π/2, y_dir, flip) Vec2f(0.5, 1.0)

y_dir, flip = true, true
@test Makie.calculate_bar_label_align(al, 0.0, y_dir, flip) Vec2f(0.5, 1.0)
@test Makie.calculate_bar_label_align(al, π, y_dir, flip) Vec2f(0.5, 0.0)
@test Makie.calculate_bar_label_align(al, π/2, y_dir, flip) Vec2f(1.0, 0.5)
@test Makie.calculate_bar_label_align(al, -π/2, y_dir, flip) Vec2f(0.0, 0.5)
end

@testset "manual" begin
input = 0.0, false, false
for align in (Vec2f(1.0, 0.5), Point2f(1.0, 0.5), (1.0, 0.5), (1, 0), (1.0, 0))
@test Makie.calculate_bar_label_align(align, input...) Vec2f(align)
end
end

@testset "symbols" begin
input = 0.0, false, false
@test Makie.calculate_bar_label_align((:center, :center), input...) Makie.calculate_bar_label_align((0.5, 0.5), input...)
end

@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...)
end
end

end
1 change: 1 addition & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ using Makie: volume
include("text.jl")
include("boundingboxes.jl")
include("ray_casting.jl")
include("barplot_labels.jl")
end

0 comments on commit fc95b21

Please sign in to comment.