Closed AllanCameron closed 2 years ago
Nice work Alan! These look good to me: the bezier solution seems nice for underdefined paths and the spline solution seems nice for overdefined paths.
But I know from the blogpost I recently mentioned that people dislike it if their text intersects the path. It might therefore be nice if, for example, the radius of the bezier curve doesn't exceed the first true offset distance. I think we also mentioned some convex/concave hull-type of thing to specifically mitigate text/path intersections, but I hadn't the time to explore the options in this sense.
Sorry I just recalled something. If this is primarily for the sf
variant due to it having no access to smoothing stats, is this something we could do before makeContent
?
I really think we need a general smoother for text. The blog post you linked was a good example - I think that most paths from real-world examples will need text smoothing. Line plots are generally either geometric or noisy, with only a relatively small subset being smooth.
I'm pretty sure if I was an end-user coming across this package, I would expect it to be easy to smooth the text label on an arbitrary path without running gam
or similar - at most setting some smoothing parameter.
Yes, I agree with you. My point wasn't about whether we need it, it seems very useful, my question was about whether we'd need to repeat the smoothing every time the window is resized.
Oh, I see what you mean. I can't think of any reason why it couldn't be done beforehand, which would obviously be preferable from the performance point of view.
The latest commit includes a version of text smoothing that works fairly well. It requires a copy of the smoothed x, y co-ordinates to be passed along with the original path into the makeContent functions. The smoothing algorithms try to keep track of the mapping between the original and the smoothed path to get the line gaps right. At the moment, this works fairly well, but the "noisy smoother" isn't quite perfect in its ability to map when the smoothing is high, However, the mechanism is now in place and the algorithm is located in a single small function that can be tweaked as needed.
Current behaviour is as follows:
library(geomtextpath)
#> Loading required package: ggplot2
df <- data.frame(x = 1:5, y = c(1, 3, 3, 1, 5))
p <- ggplot(df, aes(x, y, label = "A reasonably long text label"))
p + geom_textpath(size = 5, hjust = 0.24)
p + geom_textpath(size = 5, hjust = 0.24, text_smoothing = 90)
p + geom_textpath(size = 5, hjust = 0.24, vjust = -1.5)
p + geom_textpath(size = 5, hjust = 0.24, vjust = -1.5, text_smoothing = 90)
p + geom_labelpath(size = 5, hjust = 0.24, vjust = -1.5)
p + geom_labelpath(size = 5, hjust = 0.24, vjust = -1.5, text_smoothing = 90)
The noisy path smoother also works fairly well, and I have moved the economics example to a brief new section in the readme to demonstrate.
I have also put it to practical use to improve the look of the geom_textsf
and richtext examples.
I will keep this issue open until I am happier with the algorithm, and you've had a chance to review the changes @teunbrand
Created on 2021-12-31 by the reprex package (v2.0.1)
This looks pretty good already! Nice work Alan!
At the moment, this works fairly well, but the "noisy smoother" isn't quite perfect
Without smoothing, the point where the path is cut isn't very intuitive either (notice the 'e' intersecting the path for example).
library(geomtextpath)
#> Loading required package: ggplot2
ggplot(economics, aes(date, unemploy)) +
geom_textpath(label = "Decline", vjust = 1)
With some smoothing, it already looks much better.
ggplot(economics, aes(date, unemploy)) +
geom_textpath(label = "Decline", vjust = 1, text_smoothing = 50)
Created on 2022-01-02 by the reprex package (v2.0.1)
I'm wondering whether padding = unit(0.15, "inch")
isn't setting too wide a margin. Do you think we should set the default somewhat smaller? I think using unit(5, "pt")
or unit(0.05, "inch")
tends to look better in most cases.
Do you think we should set the default somewhat smaller?
I'm happy to have a smaller gap - it does look a bit large to me, especially at default text sizes
Before attempting to introduce automatic text smoothing, we need some idea of when and how to smooth a path. There are two situations I can think of where adhering too closely to a path brings ugly results.
One is where the line is overly geometric, producing corners that makes the text appear to separate:
I have written an algorithm that attempts to smooth such corners using quadratic Bezier curves. It looks like this:
You will notice that it needs a
radius
parameter. It is possible that setting it to a reasonable value internally could avoid extra parameter passing to the grob.The other type of problematic path is the "noisy" path, as in the
economics
example. There are several ways to handle this (including the rolling mean), but I have again come up with another algorithm that smooths by finding the centre of mass of regularly spaced chunks of the path (say 50) then creating splines to join the dots:This function also takes a parameter,
samples
which is just the number of points on which the path is sampled. However, this seems to work pretty well with 50 - 100 samples for noisy paths, and again we might not need to expose this parameter.The latest commit includes these functions, both of which return a two-column x, y matrix.
There may be problems that you see with these approaches, or other problematic paths I haven't considered, so let me know what you think before I look at applying these in the makeContent code.
Created on 2021-12-22 by the reprex package (v2.0.1)