Curved arrow using Makie.jl

Hello,

I would like to draw a curved arrow inside an axis using Makie.jl

More precisely, I have a starting point x0, a final point x1, and I want to draw and arrow which is not straight, but curved in some way.

How can I do it? The arrows function seems to support only straight arrows.

If you dont find a solution with Makie.jl alone, check BezierCurve in Meshes.jl. you can visualize them as described in the docs.

Makie does have a BezierPath function that you can give Bezier commands in the syntax of d - SVG: Scalable Vector Graphics | MDN to. I’m not sure how well supported it is but it is documented. The authors of Makie might be able to give better assistance on their discord that might integrate better with arrows.

Makie.jl has the BezierPath, which should be similar to the BezierCurve you proposed (right?).

Although I can use the lines function together with the BezierPath, I don’t know how to make the head of the arrow, corresponding to the correct direction.

That doesn’t exist yet as a nice recipe, but I have plans for it. A quick mockup without much functionality is this:

@recipe(CurvedArrow) do scene
    Theme(
        color = :black,
    )
end

function Makie.plot!(p::CurvedArrow)
    scene = Makie.get_scene(p)

    points = lift(p, scene.camera.projectionview, p.model, Makie.transform_func(p),
          scene.viewport, p[1], p[2]) do _, _, _, _, p1, p2

        return Makie.project.(Ref(scene), (p1, p2))
    end

    arc = lift(points) do (p1, p2)
        len = Makie.norm(p2 - p1)
        EllipticalArc(
            p1...,
            p2...,
            len/1.3,
            len/1.3,
            0,
            false,
            false,
        )
    end

    path = lift(arc) do arc
        BezierPath([
            MoveTo(points[][1]),
            arc,
        ])
    end

    trimarker = BezierPath([MoveTo(0, 0), LineTo(0.5, -1), LineTo(-0.5, -1), ClosePath()])

    markerangle = lift(arc) do arc
        arc.a2 + pi
    end

    lines!(p, path, space = :pixel, color = p.color)
    scatter!(p, p[2], marker = trimarker, rotation = markerangle, color = p.color)
end

Makie.data_limits(p::CurvedArrow) = Rect3f(Rect2f([p[1][], p[2][]]))
Makie.boundingbox(p::CurvedArrow, space::Symbol = :data) = Makie.apply_transform_and_model(p, Makie.data_limits(p))

f, ax, _ = curvedarrow(Point2f(0, 0), Point2f(1, 1))
curvedarrow!(ax, Point2f(1.1, 1.1), Point2f(1.5, 2))
f

When I make the real one, it will have lots of options, annotation arrows can be pretty tricky to get right

5 Likes

It doesn’t work for me. I have CairoMakie v0.12.5

Hm I did this with GLMakie, would have to try later what’s going on. As a side note, telling me how something doesn’t work is helpful :smile:

It doesn’t display in the jupyter notebook. No errors, but I can’t see anything.

Aha seems some method in CairoMakie is missing to project the EllipticalArc. This works, using some internal makie function to replace the EllipticalArc with curves

path = lift(arc) do arc
        Makie.replace_nonfreetype_commands(
            BezierPath([
                MoveTo(points[][1]),
                arc,
            ])
        )
    end

Thank you very much.

BTW, is it necessary all this code? In my mind, I was wondering on using just lines with a BezierPath and then making the head using a single marker of scatter. The only question is how to get the correct angle of the marker to be the head of the arrow.

The complexity comes exactly from that problem, determining marker angle in screen space, and connection line in screen space. If you make an arc in data space it will not look like an arc if your axes are scaled differently

2 Likes

Is this feature not implemented still?

It would be great to have something kind of like what Plots does, described here.

Being able to add an arrow = true in the lines method for instance.

This thread seems to be about curved arrows, while the linked one on Plots is about straight arrows.
If you just want straight lines with arrows, they are already available in [ANN] MakieExtra.jl – more recipes and tools for Makie plots as arrowlines():


Even with different arrowhead styles (:

2 Likes

Ok, thanks! That’s neat for straight lines!

It would be a nice feature to be able to do it with curved lines in Makie:

using Plots
plot(sin, 0:0.01:7, arrow = true)

Which yields:

1 Like

Actually, it turned out to be basically trivial to support!
I’ve been planning to fix x/yscale handling for arrows anyway, and added multi-point arrows as well while at it (:
Coming in the next MakieExtra version:

4 Likes

Fantastic work! Looking forward to it!