0

Is there a possibility to add the arrow head at the end of a geom_segment instead of having the arrow head pointing to the end of a geom_segment in ggplot2?


An example:

  • The upper arrow is what you get naturally: the arrow head points to the end of the geom_segment (or: the arrow heads points to x = 1);
  • The bottom arrow is what I want: the arrow head should start at the end of the geom_segment and pointing a bit further (or: the arrow head starts at x = 1).

enter image description here


What I did so far:

The code that produced the plot above is manually tweaked and only works for a given plot size:

ggplot() + 
  geom_segment(aes(x = 0, y = 1, xend = 1, yend = 1), arrow = arrow()) + 
  geom_segment(aes(x = 0, y = 0, xend = 1.05, yend = 0), arrow = arrow()) +
  ylim(-1, 2)

With another plot size, the code does produce something different that is not what I want:

enter image description here

Therefore I'm on the search for an option inside arrow() (I did not find something useful, as ends = "last" is the default value and produces the same plots as shown above..) or an alternative geom_. Inside arrow().

Any hints?


The comments from @mnm do not do what I am searching for:

ggplot() + 
  geom_segment(aes(x = 0, y = 1, xend = 1, yend = 1), arrow = arrow(ends = "last")) + 
  geom_segment(aes(x = 0, y = 0, xend = 1.01, yend = 0), arrow = arrow(ends = "last")) +
  ylim(-1, 2)

enter image description here

E_net4
  • 27,810
  • 13
  • 101
  • 139
symbolrush
  • 7,123
  • 1
  • 39
  • 67
  • Its difficult for me to understand what you want, perhaps you might want to edit the question. To what I understood was the following, **you want the bottom arow to begin from the end of upper arrow**.. if this is correct, then have you tried replacing the `x=0` in the bottom `geom_segment()` with `x=1`? something like, `ggplot() + geom_segment(aes(x = 0, y = 1, xend = 1, yend = 1), arrow = arrow()) + geom_segment(aes(x = 1, y = 0, xend = 1.05, yend = 0), arrow = arrow()) + ylim(-1, 2)` – mnm Apr 24 '19 at 13:18
  • @mnm: Thx, I want the **arrow head** to start where the line ends. The arrow head should start at `x = 1, y = 0`. See also my edit. Does this make things clearer? – symbolrush Apr 24 '19 at 13:23
  • how about `xend = 1.01` in the bottom `geom_segment()`? – mnm Apr 24 '19 at 13:30
  • @mnm: That's what I did in my made up example. The thing is that it does not fit when a) the `xlim` or `ylim` changes or b) the plot size changes. Therefore I'm on the search for an option inside `arrow()` or an alternative `geom_` – symbolrush Apr 24 '19 at 13:38
  • I think this is what you need, `ggplot() + geom_segment(aes(x = 0, y = 1, xend = 1, yend = 1), arrow = arrow()) + geom_segment(aes(x = 1, y = 0, xend = 1.01, yend = 0), arrow = arrow(ends = "last"))` Thanks for the hint on `arrow()`. I checked it signature and found the option, `ends=last()`.. this is independent of `xlim()` or `ylim()` – mnm Apr 24 '19 at 13:57
  • @mnm: Thx for your comment. I also thought that `ends = "last"` is what I want, but sadly it's not. `ends = "last"` is the default (and therefore also what I have in my example in the question). `ends = "last"` only means the **arrow head** *points to the end* of the `geom_segment`. When plotting your code I get the same behaviour as with my code.. Or do I miss something? – symbolrush Apr 24 '19 at 14:01
  • NO you do not have `ends="last"` in your question!! Nor do you have `xend = 1.01` in your question, which by now should have been so that others who see this question are not mislead.. I'm down-voting the question because it occurs to me that (a) your unsure of what you need (b) you will not accept what was missing in your code rather "claim" it as your own thought.. and you keep re-editing your question based on other's input. – mnm Apr 24 '19 at 14:14
  • @mnm: Thx for your help so far. – symbolrush Apr 24 '19 at 14:21
  • 1
    @mnm I don't think editing the question for clarity based on feedback is a bad thing... And `ends = "last"` is the default option in `arrow()`, so regardless whether it has been spelt out in OP's code, it's implicitly present when the code is run. This question is in line with what OP has been asking in other questions for a while now. I'd say there's coherent intent & they know what they want, even if I don't necessarily support it as the best way to visualise data. – Z.Lin Apr 24 '19 at 14:30

1 Answers1

2

I don't know a way to do this natively with geom_segment, but you could make a function to help with this. It modifies the ending coordinates to extend the length as much as the arrow_length term specifies.

geom_segment_extra <- function(x, y, xend, yend, arrow_length) {
  angle = atan2(yend - y, xend - x)  # Trig reminder from https://stackoverflow.com/a/34617867/6851825
  xend = xend + arrow_length * cos(angle)
  yend = yend + arrow_length * sin(angle)

  list(    # ggplot accepts list objects as new layers
    geom_segment(aes(x = x, y = y, xend = xend, yend = yend), arrow = arrow()) 
  )
}

Then we can use it. Note how the first geom_segment_extra just repeats the normal segment appearance, while adding arrow_length = 0.1 extends it by 0.1 unit:

ggplot() +
  geom_segment(aes(x = 0, y = 1, xend = 1, yend = 1), arrow = arrow()) + 
  geom_segment_extra(0, 0.9, 1, 0.9, arrow_length = 0) +
  geom_segment_extra(0, 0.8, 1, 0.8, arrow_length = 0.1) +
  scale_x_continuous(breaks = 0.2*0:100)

enter image description here

Jon Spring
  • 55,165
  • 4
  • 35
  • 53