From d4128982a761c272dda96217e77c86991cdc9c68 Mon Sep 17 00:00:00 2001 From: jkrumbiegel <22495855+jkrumbiegel@users.noreply.github.com> Date: Sun, 25 Apr 2021 21:25:08 +0200 Subject: [PATCH] jk/text offset (#703) --- docs/src/plotting_functions/text.md | 25 +++++++++++++++++ src/interfaces.jl | 1 + src/layouting/layouting.jl | 40 ++++++++++++++++++++------- test/ReferenceTests/src/tests/text.jl | 39 +++++++++++++++++++++++++- 4 files changed, 94 insertions(+), 11 deletions(-) diff --git a/docs/src/plotting_functions/text.md b/docs/src/plotting_functions/text.md index ce3574196..41d3151f5 100644 --- a/docs/src/plotting_functions/text.md +++ b/docs/src/plotting_functions/text.md @@ -101,4 +101,29 @@ for (p, al) in zip(points[7:9], (:left, :center, :right)) end scene +``` + +The offset attribute can be used to shift text away from its position. +This is especially useful with `space = :screen`, for example to place text together with barplots. +You can specify the end of the barplots in data coordinates, and then offset the text a little bit to the left. + +```@example +using CairoMakie +CairoMakie.activate!() # hide +AbstractPlotting.inline!(true) # hide + +f = Figure(resolution = (800, 600)) + +horsepower = [52, 78, 80, 112, 140] +cars = ["Kia", "Mini", "Honda", "Mercedes", "Ferrari"] + +ax = Axis(f[1, 1], xlabel = "horse power") +tightlimits!(ax, Left()) +hideydecorations!(ax) + +barplot!(horsepower, direction = :x) +text!(cars, position = Point.(horsepower, 1:5), align = (:right, :center), + offset = (-20, 0), color = :white) + +f ``` \ No newline at end of file diff --git a/src/interfaces.jl b/src/interfaces.jl index 8d8a96100..d265477bc 100644 --- a/src/interfaces.jl +++ b/src/interfaces.jl @@ -275,6 +275,7 @@ $(ATTRIBUTES) justification = automatic, lineheight = 1.0, space = :screen, # or :data + offset = Point2f0(0, 0), _glyphlayout = nothing, ) end diff --git a/src/layouting/layouting.jl b/src/layouting/layouting.jl index c85d36a07..f18cbc8e8 100644 --- a/src/layouting/layouting.jl +++ b/src/layouting/layouting.jl @@ -231,14 +231,17 @@ function glyph_positions(str::AbstractString, font_per_char, fontscale_px, halig reduce(vcat, map(line -> [l.hadvance for l in line], lineinfos))) end -function preprojected_glyph_arrays(string::String, position::VecTypes, glyphlayout::AbstractPlotting.Glyphlayout, font, textsize, space::Symbol, projview, resolution) +function preprojected_glyph_arrays(string::String, position::VecTypes, glyphlayout::AbstractPlotting.Glyphlayout, font, textsize, space::Symbol, projview, resolution, offset::VecTypes, transfunc) + + offset = to_ndim(Point3f0, offset, 0) + pos3f0 = to_ndim(Point3f0, position, 0) atlas = get_texture_atlas() if space == :data - positions = glyphlayout.origins .+ to_ndim(Point3f0, position, 0) + positions = apply_transform(transfunc, [pos3f0 + offset + o for o in glyphlayout.origins]) elseif space == :screen - projected = AbstractPlotting.project(projview, resolution, to_ndim(Point3f0, position, 0)) - positions = glyphlayout.origins .+ to_ndim(Point3f0, projected, 0) + projected = AbstractPlotting.project(projview, resolution, apply_transform(transfunc, pos3f0)) + positions = [to_ndim(Point3f0, projected, 0) + offset + o for o in glyphlayout.origins] else error("Unknown space $space, only :data or :screen allowed") end @@ -255,19 +258,36 @@ function preprojected_glyph_arrays(string::String, position::VecTypes, glyphlayo return positions, offsets, uv, scales end -function preprojected_glyph_arrays(strings::AbstractVector{<:String}, positions::AbstractVector, glyphlayouts::Vector, font, textsize, space::Symbol, projview, resolution) + +function preprojected_glyph_arrays(strings::AbstractVector{<:String}, positions::AbstractVector, glyphlayouts::Vector, font, textsize, space::Symbol, projview, resolution, offset, transfunc) + + if offset isa VecTypes + offset = [to_ndim(Point3f0, offset, 0)] + end megastring = join(strings, "") if space == :data - allpos = map(positions, glyphlayouts) do pos, glyphlayout::AbstractPlotting.Glyphlayout + allpos = broadcast(positions, glyphlayouts, offset) do pos, glyphlayout::AbstractPlotting.Glyphlayout, offs p = to_ndim(Point3f0, pos, 0) - [p + o for o in glyphlayout.origins] + apply_transform( + transfunc, + [p .+ to_ndim(Point3f0, offs, 0) .+ o for o in glyphlayout.origins] + ) end elseif space == :screen - allpos = map(positions, glyphlayouts) do pos, glyphlayout::AbstractPlotting.Glyphlayout - projected = to_ndim(Point3f0, AbstractPlotting.project(projview, resolution, to_ndim(Point3f0, pos, 0)), 0) - return [projected .+ o for o in glyphlayout.origins] + allpos = broadcast(positions, glyphlayouts, offset) do pos, glyphlayout::AbstractPlotting.Glyphlayout, offs + projected = to_ndim( + Point3f0, + AbstractPlotting.project( + projview, + resolution, + apply_transform(transfunc, to_ndim(Point3f0, pos, 0)) + ), + 0) + + return [projected .+ to_ndim(Point3f0, offs, 0) + o + for o in glyphlayout.origins] end else error("Unknown space $space, only :data or :screen allowed") diff --git a/test/ReferenceTests/src/tests/text.jl b/test/ReferenceTests/src/tests/text.jl index c468bfb6f..1467f5874 100644 --- a/test/ReferenceTests/src/tests/text.jl +++ b/test/ReferenceTests/src/tests/text.jl @@ -199,7 +199,7 @@ end end -@cell "3D screenspace annotaitons" begin +@cell "3D screenspace annotations" begin positions = RNG.rand(Point3f0, 10) fig, ax, p = meshscatter(positions, color=:white) text!( @@ -211,3 +211,40 @@ end overdraw=false) fig end + + +@cell "Text offset" begin + f = Figure(resolution = (1000, 1000)) + barplot(f[1, 1], 3:5) + text!("bar 1", position = (1, 3), offset = (0, 10), align = (:center, :baseline)) + text!(["bar 2", "bar 3"], position = [(2, 4), (3, 5)], + offset = [(0, -10), (0, -20)], + align = (:center, :top), color = :white) + + scatter(f[1, 2], Point2f0(0, 0)) + text!("hello", position = (0, 0), offset = (40, 0), align = (:left, :center)) + text!("hello", position = (0, 0), offset = (40, 0), align = (:left, :center), + rotation = -pi/4) + text!("hello", position = (0, 0), offset = (40, 0), align = (:left, :center), + rotation = pi/4) + + scatter(f[2, 1], Point2f0[(0, 0), (10, 0), (20, 10)]) + text!("ABC", space = :data, offset = (0, 0), color = (:red, 0.3), align = (:left, :baseline)) + text!("ABC", space = :data, offset = (10, 0), color = (:green, 0.3), align = (:left, :baseline)) + text!("ABC", space = :data, offset = (20, 10), color = (:blue, 0.3), align = (:left, :baseline)) + + LScene(f[2, 2], scenekw = (show_axis = false,)) + scatter!(Point3f0[(0, 0, 0), (2, 2, 2)]) + text!("hello", position = Point3f0(1, 1, 1), offset = (10, 10)) + + f +end + + +@cell "Log10 text" begin + barplot([1, 10, 100], fillto = 0.1, axis = (yscale = log10,)) + text!(["bar 1", "bar 2", "bar 3"], position = [(1, 1), (2, 10), (3, 100)], + offset = (0, -10), color = :white, align = (:center, :top)) + tightlimits!(current_axis(), Bottom()) + current_figure() +end \ No newline at end of file