Skip to content
This repository has been archived by the owner on Jul 13, 2021. It is now read-only.

text in screen space #430

Merged
merged 47 commits into from
Mar 24, 2021
Merged

text in screen space #430

merged 47 commits into from
Mar 24, 2021

Conversation

jkrumbiegel
Copy link
Member

This adds the capability to draw text in screen space instead of data space. For that to work, the backends need to read the space = :data or :screen attribute and draw offset the glyphs from the text position either in screen or in data space.

That means: text position is always in data space, but how the glyphs are drawn relative to that point depends on the keyword. In most plots, people will want to use space = :screen because that leaves the text undistorted by axis projections.

Right now, the annotations recipe doesn't work with space = :screen because it concatenates all its text parts into one big vector, and layouts them all in data space already. This means the backend couldn't separate text positions and glyph offsets anymore. This needs to be changed so that the text parts of annotations don't have glyph offsets baked in.

There also needs to be made a decision how boundingbox and data_limits should react to the two spaces. The behavior can stay how it is for space = :data, but for space = :screen it needs to be different.

It makes sense that the data limits of a text or annotations is just the boundingbox enclosing all specifed data positions. In the case of normal text, that's just one point, in the case of normal annotations, it's as many points as there are text parts.

For boundingbox it depends on how one interprets what this function does, whether it's always screen relative or not.

@jkrumbiegel
Copy link
Member Author

Ok I've thought a bit more about this, and I think we should change the text behavior a bit, extend it with screen space projection, and also remove annotations.

Let's think about the different scenarios that a user might want.

Multiple strings at multiple positions

This needs an iterable of strings for the text part, and an iterable of positions with one position for each string. This is super common for labeling multiple data points in a plot with words. The positioning of the glyphs is done by the text layouting algorithm, and the glyphs can be placed / scaled either in :screen or :data space. If text had an option to accept an array of strings, we wouldn't need annotations anymore. Annotations right now calculate an array of positions for every glyph in all given strings, and an accompanying big string. This is useless for situations where :screen space is used, because the positions vector is meant to contain positions in data space, not in a mix of data space plus an offset in screen space.

Example

text!(scene, strings, positions)
# or alternatively, to be able to change the number of words
text!(scene, zip(strings, positions))

grafik

One string at one position

This is how text works now, it's just the single string variant of the previous method. The text input would be a string and the positions input would be one point or equivalent. This would be used internally by LText, for titles, x and y labels, etc.

Example

text!(scene, string, position)

grafik

One string at multiple positions (per char)

I think this is quite the rare use case because the user has to compute sensible glyph positions, which is complicated. I can only imagine one might use this for text along a curve or something like that. Right now this is what powers annotations, but as I wrote above, I think this is not needed. Glyph positioning should be mostly left to the default layout algorithm.

Example

text!(scene, string, glyph_positions)

grafik

Summary

What I would suggest:

  • remove annotations
  • enable iterable of strings / iterable of positions for text
  • enable screen space for all three variants and make it the default
  • decide how boundingboxes should work for each variant
  • implement in GLMakie / CairoMakie

src/basic_recipes/basic_recipes.jl Outdated Show resolved Hide resolved
if x.space[] == :data
return boundingbox(x)
elseif x.space[] == :screen
if x.position[] isa Union{StaticArrays.StaticArray, Tuple{Real, Real}, GeometryBasics.Point}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tuple{Real, Real}

should we make this NTuple{2, Real}, NTuple{3, Real}?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no it's actually annoying if things don't work if you give a position = (1, 2.3)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, makes sense. We should probably generalize to the 3D case as well (Tuple{Real, Real, Real}).

@@ -121,7 +121,7 @@ function atomic_limits(x::Text{<: Tuple{Arg1}}) where Arg1
end
else
if x.position[] isa Union{StaticArrays.StaticArray, Tuple{Real, Real}, GeometryBasics.Point}
bb = FRect3D(x.position[])
bb = frect3d(x.position[])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should try to not to introduce these kind of secondary, abstractplotting specific APIs. I know I'm guilty of this a lot... And I learned, that it's very annoying to clean up later and explain to other contributors.
If there's something wrong with FRect3D we should fix it in GeometryBasics - or introduce a proper boundingbox function ;) But not just introduce a new function, with almost the same name, which (probably?) behaves slightly different.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree. I think it's not very clear what the API is for these objects and I can often not find what I need quickly enough. So I write these helper functions instead of stopping to make a PR somewhere else.

What I also find super annoying is that multiplication of rectangles, addition with points, and practical things like that are either not defined or have such strong type restrictions that I can't use them without doing a lot of guard rail code.

Plus, that I can't do array_of_points .+ point because a point behaves as a small array. That's super impractical..

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, we should fix that: https://github.com/JuliaGeometry/GeometryBasics.jl/blob/master/src/rectangles.jl#L271

The types look pretty unrestricted?

Plus, that I can't do array_of_points .+ point because a point behaves as a small array. That's super impractical..

Yes, totally agree... I think I'm ready to have that fixed as well^^

@jkrumbiegel jkrumbiegel marked this pull request as ready for review March 24, 2021 13:20
@jkrumbiegel jkrumbiegel merged commit 53d8039 into master Mar 24, 2021
@jkrumbiegel jkrumbiegel deleted the jk/text-screen-space branch March 24, 2021 13:20
@SimonDanisch SimonDanisch changed the title WIP: text in screen space text in screen space Mar 24, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants