-
-
Notifications
You must be signed in to change notification settings - Fork 344
Refactor arrows #4925
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Refactor arrows #4925
Conversation
Benchmark ResultsSHA: 1a4721cd302829521cb6138b274c51d69776e1d1 Warning These results are subject to substantial noise because GitHub's CI runs on shared machines that are not ideally suited for benchmarking. |
First prototype for 2d arrows: begin
ps = [Point2f(cos(a), sin(a)) for a in range(0, 2pi, length=21)[1:end-1]]
f,a,p = Makie.arrows2d(
ps, 0.25 .* ps, color = (:blue, 0.5), align = :tip,
tail = Point2f[(0, 0.5), (1, 0), (1, 1)], taillength = 8
)
Makie.arrows2d!(
ps, 0.25 .* ps, color = (:red, 0.5), align = :tail
)
scatter!(ps, marker = Circle, color = :transparent, strokewidth = 1)
f
end I feel like this is suffering under fxaa quite a bit and the arrows aren't even as small as the the current docs examples... |
3D arrows, requires JuliaGeometry/GeometryBasics.jl#257 r = Rect3f(-1,-1,-1,2,2,2)
ps = Makie.coordinates(r)
zdirs = [Vec3f(0,0, z) for (x,y,z) in ps]
f = Figure()
a = LScene(f[1,1])
p = Makie.arrows3d!(
ps, 0.5 .* ps, color = :blue, align = :tip,
tail = Makie.Cone(Point3f(0,0,1), Point3f(0,0,0), 0.5f0), taillength = 0.06
)
Makie.arrows3d!(
ps, -zdirs, color = :red, align = :tail
)
linesegments!(r)
linesegments!(Rect3f(Point3f(-0.5), Point3f(1)))
f |
f = Figure(size = (1000, 500))
# 2D
ps = [Point2f(cos(a), sin(a)) for a in range(0, 2pi, length=21)[1:end-1]]
a = Axis(f[1,1], aspect = DataAspect())
p = Makie.arrows2d!(
ps, [ps[2:end]..., ps[1]], color = (:blue, 0.5),
align = :center, lengthscale = 0.5, argmode = :endpoint,
tail = Point2f[(0, 0.5), (1, 0), (1, 1)], taillength = 8
)
Makie.arrows2d!(
ps, ps, color = eachindex(ps), align = :tail,
colormap = :rainbow, lengthscale = 0.5
)
scatter!(ps, marker = Circle, color = :transparent, strokewidth = 1)
# 3D
ps = [Point3f(cos(a), sin(a), 0) for a in range(0, 2pi, length=21)[1:end-1]]
a = Axis3(f[1,2], aspect = :data)
p = Makie.arrows3d!(
ps, [ps[2:end]..., ps[1]], color = (:blue, 0.5),
align = :center, lengthscale = 0.5, argmode = :endpoint,
tail = Makie.Cone(Point3f(0,0,1), Point3f(0,0,0), 0.5f0), taillength = 0.4
)
Makie.arrows3d!(
ps, [Vec3f(0,0,1) for _ in ps], color = eachindex(ps), align = :tip,
colormap = :rainbow, lengthscale = 0.5
)
scatter!(ps, marker = Circle, color = :transparent, strokewidth = 1)
f |
Not sure if we can consider this a (big) bug fix. It's certainly visually breaking. Potentially breaking changes for
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think visually breaking with good warnings for how to fix things is good enough to release this.
We could also still make a breaking release, to be on the safe side.
Description
Arrows has a bunch of issues which I want to fix in this pr. I'm starting this with just some planning so there is more time for people to interject if I'm going down the wrong track.
First of all, I'm planning to split up arrows into 2D and 3D because they are pretty different in implementation and sometimes in their attributes. E.g. a tipwidth would be a tipradius, quality and lighting attributes are 3D only, etc. I plan to add some glue code to make
arrows
still work and if things end up more similar than I imagine I may merge them again.Conversions
Related Issues:
linesegments
with arrow, orarrows
method that takes a pair of points #2612arrows(points, directions; kwargs...)
doesn't work (CairoMakie backend) #3247, Provide a method to plot a sample (or calibration) arrow #3531 (single point + direction fails)Arrows should rely on
PointBased()
conversions and flatten matrices for convenience. That should make plotting a single arrow using a single position and direction possible (similar to how Scatter accepts a Point).For #2612 we could interpret the second argument as directions if they are
Vec
s or as endpoints if they arePoint
s but I think that's a bit too specific. It also doesn't work with argument groups, i.e. if you pass (x, y, u, v). I'm currently thinking an attributearg_mode = :directions / :endpoints
would be better.Attributes
Issues:
These just need to be connected.
Marker
Issues:
arrowtail
does nothing? #1200 (double headed arrows)Both of these are more complex.
To fix #1688 naively, we would have to calculate the markerspace position where the triangle marker ends and the line should begin. This is complicated with generic markers because they typically are less than
markersize
units large. It will also leave some overlap or gap in GL backends because anti-aliasing happens before lines and scatter are composed. The full solution to this would be to either extend lines/scatter to natively support arrows (similar tomarker = Rect
orlinecap = :round
), or to generate a mesh for each arrow and render them all with poly. Option 1 is more performant, higher quality, more restrictive and requires backend changes that Simon isn't a fan of, so I'll go with Option 2.To fix #1200 we'll need to consider arrows as three parts - a tail, a shaft and a tip. This is important for the main course:
Alignment
Issues:
PyPlot also reminded me that with a static pixel size for the arrow/head we can't draw arrows shorter than that size. I.e. at some point the arrow needs to scale as whole, rather than just shortening the shaft/tail.
2D
With #1200 in mind, I think our arrow should have 3 parts, each with a length and width:
From these user attributes we then derive effectively the same set of internal attributes: (all in markerspace)
target_length = lengthscale * norm(direction)
(orlengthscale * norm(endpoint - startpoints)
)shaftlength = automatic
, calculate an internal_shaftlength = max(minshaftlength, target_length - taillength - tiplength)
. Otherwise_shaftlength = shaftlength
marker_length = taillength + _shaftlength + tiplength
target_length / marker_length
This should allow you to:
minshaftlength
ifshaftlength = automatic
shaftlength
is a valuetail
The tail, shaft and tip should probably be individually settable markers/meshes/point vectors. I will probably repeat the interface from #4879 here, where you either define a shape on a 0..1 x 0..1 range and it gets scaled internally, or you define a callback function that constructs it from the calculated scales.
With respect to
align
I'm planning to deprecate duplicate names and just have :tail, :center and :tip. I'm also planning to allow a float for fractional alignment.I haven't really thought aboutJust do the obvious andnormalize
in 2D yet, but maybe it would make sense to have it settarget_length = taillength + (min)shaftlength + tiplength
. That would force all arrows to be the user defined size. Or maybe it should just not be there, and something likescale = false
should have that effect.normalize(directions)
3D
I think the scaling and alignment handling will generalize to 3D. Though I think it would be good to rename
width
toradius
. Since 3D doesn't use pixel space we don't need to build a mesh for every arrow and can instead just apply scaling to a persistent mesh throughmeshscatter
. In the callback case that will need to be rebuild.I'm planning to move the mesh generation bits, i.e.
_mantle
and_circle
to GeometryBasics with this pr. They don't really belong here, and having GeometryPrimitives likeCircle3D
,Cone
andCylinderShell
(Tube? OpenCylinder?) sounds generally useful.tl;dr goals:
:tail, :center, :tip
or a 0..1 floatminshaftlength
, otherwise scale only shaftarg_mode = :directions / :endpoints
to allow defining arrows based on two pointsPR State
arrows(points, directions; kwargs...)
doesn't work (CairoMakie backend) #3247 (StaticVectors)arrowtail
does nothing? #1200align
option (need help, draft) #2884TODO:
maxshaftlength
markerscale = automatic
(from, to)
arg modeType of change
Delete options that do not apply:
Checklist