Skip to content
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

arrow_shift = :end works poorly with larger edge width #171

Closed
fredcallaway opened this issue Jan 25, 2024 · 6 comments
Closed

arrow_shift = :end works poorly with larger edge width #171

fredcallaway opened this issue Jan 25, 2024 · 6 comments

Comments

@fredcallaway
Copy link
Contributor

Here's an example with edge width 4.

0125-11073728-problem

Automatically handling this is probably too much to ask. But perhaps we could have an argument that allowed us to fine-tune the positions starting with arrow_shift = :end? I think the correct way to handle this would be to simply move the end point of the arrow by some fixed pixel value, (I don't think it should scale with the radius of the destination node).

@hexaeder
Copy link
Collaborator

Do you mean the problem that the arrow is not really pointy anymore? Unfortunately that cannot be fixed by moving the arrowhead, as the underlying thick edge is drawn all the way to the center of the node position (as can be seen by choosing a transparent node Color, really ugly!). So we'd need to define new endpoints for the "edge lines", which do not coincide with the node position...

@fredcallaway
Copy link
Contributor Author

If you move the arrows, they're not actually pointy, but it looks like a pointy is just behind the state, which to my eye is aesthetically fine.

But I agree that the best fix would be to move the endpoints for the edge lines. Shouldn't it be pretty straightforward to move the endpoint to wherever the arrow ends up when using arrow_shift = :end? I'd be happy to work on a PR if you're open to it.

@fredcallaway
Copy link
Contributor Author

While I'm at it, maybe we should set the default endpoint to the edge of circle to allow for transparent nodes?

@hexaeder
Copy link
Collaborator

Yes, internally the :end option actually updates the arrow_shift observable to move the arrow along the line where 0 is the position of the src and 1 the position of the destination node (see update_arrow_shift function). The same observable could be used to calculate the line's endpoint in case of arrow_shift==:end.

While I'm at it, maybe we should set the default endpoint to the edge of circle to allow for transparent nodes?

That would be great! Unfortunatley, curvy lines, potential 3d plots, different node shapes and different canvases (dataspace vs. pixelspace) make it quite complex to calculate the intersection between node "surface" and edge path in general. @hdavid16 put quite some effort into the :end option for which he needed to calculate the same thing. He might have some insights on that!

@fredcallaway
Copy link
Contributor Author

OK so it sounds like there's interest in a PR. I will first look into whether it's possible to straightforwardly apply the existing code for arrow positioning to also position the end of the line. I'll test curvy and straight lines, but I think I'll restrict myself to 2d (also true for the current :end option I think).

@hdavid16
Copy link
Collaborator

The main code that calculates the edge of the destination node to land the arrowhead on its surface is:

"""
update_arrow_shift(g, gp, edge_paths::Vector{<:AbstractPath{PT}}, to_px) where {PT}
Checks `arrow_shift` attr so that `arrow_shift = :end` gets transformed so that the arrowhead for that edge
lands on the surface of the destination node.
"""
function update_arrow_shift(g, gp, edge_paths::Vector{<:AbstractPath{PT}}, to_px, node_markers, node_sizes, shift) where {PT}
arrow_shift = Vector{Float32}(undef, ne(g))
for (i,e) in enumerate(edges(g))
t = getattr(shift, i, 0.5)
if t === :end
j = dst(e)
p0 = getattr(gp.node_pos, j)
node_marker = getattr(node_markers, j)
node_size = getattr(node_sizes, j)
arrow_marker = getattr(gp.arrow_marker, i)
arrow_size = getattr(gp.arrow_size, i)
d = distance_between_markers(node_marker, node_size, arrow_marker, arrow_size)
p1 = point_near_dst(edge_paths[i], p0, d, to_px)
t = inverse_interpolate(edge_paths[i], p1)
if isnan(t)
@warn """
Shifting arrowheads to destination nodes failed.
This can happen when the markers are inadequately scaled (e.g., when zooming out too far).
Arrow shift has been reset to 0.5.
"""
t = 0.5
end
end
arrow_shift[i] = t
end
return arrow_shift
end

You should be able to calculate the new endpoint for the edge in a similar fashion. However, note that this get's complicated with non-circular nodes: #115

hexaeder added a commit that referenced this issue Mar 21, 2024
better edges when arrow_shift = :end (fixes #171)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants