Plots.jl: arrows style in quiver()?

In Plots.jl,

quiver(x, y, gradient=(u, v), ...)

produces arrows with head sizes being independent of gradient magnitude, like this:
arrows
The vector in the bottom-left corner has a small norm, and its head looks disproportionally big. Does anybody know how to make arrow heads proportional to vector norms (smaller vector → smaller head), and overall how to style the arrows?
A side question: documentation for Plots.jl is rather scarce, so I wonder whether there is some interface which allows to explore options available, without digging into the source?

1 Like

For the real question - yes there is functionality for it, and I think it’s broken and there’s an issue for it.
For the second question - yes, you can go to this table Overview · Plots and look, but there’s also a programmatic interface to it:

julia> plotattr("arrow")
arrow {nothing (no arrows), Bool (if true, default arrows), Arrow object, or arg(s) that could be style or head length/widths}
arrows

Defines arrowheads that should be displayed at the end of path line segments (just before a NaN and the last non-NaN point).  Used in quiverplot, streamplot, or similar.
Series attribute,  default: nothing
3 Likes

@mkborregaard, thank you for the link, the description of arrow attribute says:
“… or arg(s) that could be style or head length/widths”
So is it possible to bind these lengths/widths to data, and how to do this? I’ve written

quiver(..., arrow=arrow(0.01, 0.01))

to no avail, just to test whether at least constant scaling works, but it doesn’t (indeed something is broken).
I looked up the source, and struct Arrow contains headlength and headwidth as Float64 fields, so I can’t really understand how to use these if I need to vary head size from vector to vector in the data.
Are you aware of any other library which achieves this goal? I was thinking to go with old faithful Compose.jl, but it is not able to build on my system due to missing pangocairo or something like that.

1 Like

TBH I think it’s something that Tom (who wrote Plots) stopped halfway through implementing and it’s just that nobody took it up. @dpsanders has requested this functionality several times. I guess it would be simple enough to do. This should just be a weakness of Plots, I’d bet that any of the well-featured plotting packages have this option.

6 Likes

I hate to bump an old thread, but has there been any progress on this in the past couple years?

A simple user defined function arrow0! using Plots.jl, for displaying arrows with head sizes proportionally to their lengths (no bells and whistles):

using Plots; gr(legend=false)

# as: arrow head size 0-1 (fraction of arrow length)
# la: arrow alpha transparency 0-1
function arrow0!(x, y, u, v; as=0.07, lc=:black, la=1)
    nuv = sqrt(u^2 + v^2)
    v1, v2 = [u;v] / nuv,  [-v;u] / nuv
    v4 = (3*v1 + v2)/3.1623  # sqrt(10) to get unit vector
    v5 = v4 - 2*(v4'*v2)*v2
    v4, v5 = as*nuv*v4, as*nuv*v5
    plot!([x,x+u], [y,y+v], lc=lc,la=la)
    plot!([x+u,x+u-v5[1]], [y+v,y+v-v5[2]], lc=lc, la=la)
    plot!([x+u,x+u-v4[1]], [y+v,y+v-v4[2]], lc=lc, la=la)
end

# Define some input points and arrows
N = 12;
x, y = 1 .+ 2* rand(N), 1 .+ 2*rand(N);  # points
r, θ = rand(N), LinRange(0,2π,N)
u, v = r .* cos.(θ), r .* sin.(θ)    # arrows 

# plot points and arrows with 10% head sizes
scatter(x, y, mc=:red, ms=2.5, ratio=1, ma=0.5)
for (x,y,u,v) in zip(x,y,u,v)
    display(arrow0!(x, y, u, v; as=0.1, lc=:blue, la=1))
end

arrow_heads_with_variable_size

8 Likes

This is great, thank you. I ended up making something pretty similar where I defined a custom marker using Shape() and used rotation matrices inside a call to scatter!().

1 Like

@maxkapur, the rotation matrices were avoided by adding known unit orthogonal vectors to make the arrow heads turn.
PS: this was further simplified in the revised version above

1 Like