Skip to content

feat(text): Allow mixed per-axis space like (:data, :relative)#5612

Open
jkrumbiegel wants to merge 6 commits into
masterfrom
jk/mixed-spaces
Open

feat(text): Allow mixed per-axis space like (:data, :relative)#5612
jkrumbiegel wants to merge 6 commits into
masterfrom
jk/mixed-spaces

Conversation

@jkrumbiegel
Copy link
Copy Markdown
Member

Summary

space can now be a tuple of symbols, with each entry applying to a single axis (axes not covered default to :data). The natural use case is annotating vlines / hlines where one coordinate is meaningful in data space and the other is most easily expressed as a fraction of the viewport (or a pixel offset). Mixed-space plots only contribute to axis autolimits on their :data axes, so a (:data, :relative) scatter dot at relative y=0.95 doesn't blow up the y limits.

The implementation builds a per-axis combined projection matrix from each axis's individual matrix (taking the diagonal entry only), so it's exact for orthographic, axis-aligned cameras (the standard 2D Axis) and ignores rotation/perspective coupling. The same path is used for both text and scatter (and any other plot whose positions go through register_camera_matrix! / register_positions_projected!).

Example

using Random
Random.seed!(2)
n = 200
y = cumsum(randn(n))
y .-= (minimum(y) + maximum(y)) / 2

f = Figure()
ax = Axis(f[1, 1], xgridvisible = false, ygridvisible = false)
lines!(ax, 1:n, y)

events = [(40, "Event A"), (110, "Event B"), (165, "Event C")]
for (x, name) in events
    vlines!(ax, [x]; color = (:black, 0.4))
    text!(ax, [Point2(x, 1)];
        text = [name],
        align = (:right, :top),
        rotation = π / 2,
        offset = (0, -4),
        space = (:data, :relative),
    )
end

for (yv, label, color) in (
        (0, "zero", :crimson),
        (minimum(y), "min = $(round(minimum(y); digits = 2))", :seagreen),
    )
    hlines!(ax, [yv]; color = color)
    text!(ax, [Point2(4, yv)];
        text = [label], color = color,
        align = (:left, :bottom),
        space = (:pixel, :data),
    )
end

f

mixed_doc.png

Closes #3038. Partial progress on #4407 (covers the (data, relative) style from the request, doesn't cover arithmetic composition like data(123) + pixel(3)).

Checks

  • Added @testset "mixed-space data_limits" in Makie/test/boundingboxes.jl — asserts which axes contribute to data_limits for scatter and text under (:data, :relative), (:relative, :data), (:data, :pixel), plus homogeneous-tuple corner cases (12 assertions).
  • Added @reference_test "mixed-space annotations" in ReferenceTests/src/tests/text.jl covering (:data, :relative), (:relative, :data), (:data, :pixel) with text + scatter on top of vline / hline.
  • Added a "Mixed per-axis space" section to docs/src/reference/plots/text.md with the example shown above.
  • Manually verified that homogeneous-space plots, Axis3, and space = :relative-only plots are unchanged.

`space` may now be a tuple of symbols, with each entry applying to a
single axis (axes not covered default to `:data`). This is the natural
way to annotate `vlines`/`hlines`: use `(:data, :relative)` for a
vertical line label or `(:pixel, :data)` for a horizontal line label
without polluting the axis autolimits.

The combiner takes the diagonal of each axis's projection matrix, so it
is exact for orthographic axis-aligned cameras (the typical 2D `Axis`)
and ignores rotation/perspective coupling.

Closes #3038. Partial progress on #4407.
@github-project-automation github-project-automation Bot moved this to Work in progress in PR review May 1, 2026
@jkrumbiegel jkrumbiegel requested a review from ffreyer May 1, 2026 10:27
@MakieBot
Copy link
Copy Markdown
Collaborator

MakieBot commented May 1, 2026

Benchmark Results

SHA: 5d031bcaca3eec67b82f6ea40dba60cdaf6b2b2a

Warning

These results are subject to substantial noise because GitHub's CI runs on shared machines that are not ideally suited for benchmarking.

GLMakie
CairoMakie
WGLMakie

@ffreyer
Copy link
Copy Markdown
Collaborator

ffreyer commented May 1, 2026

I want to rework this. Specifically pull in and generalize the changes I made in #5556, which get rid of this weird CameraMatrixCallback workaround and instead just use map!()'s that get replaced dynamically.

And then:

  • stop space from defaulting in _register_common_camera_matrices!(). That should only be called when space is dynamic, i.e. exists
  • make tuple spaces dynamically changeable (to other tuple spaces and Symbol spaces)
  • probably allow markerspace to be a tuple space (I don't think that is really allowed atm?)
  • allow space to be a tuple space without markerspace existing, i.e. allow projection, view, projectionview to be mixed too. (As far as I can tell it only currently handles preprojection right now?)
  • allow input and output spaces in register_camera_matrix!() and register_projected_positions!() to be tuple spaces

@jkrumbiegel
Copy link
Copy Markdown
Member Author

jkrumbiegel commented May 1, 2026

Okay, sounds good. You can just continue this one if you want. I don't know enough about the internals anyway.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Work in progress

Development

Successfully merging this pull request may close these issues.

Add text to lines and spans

3 participants