Skip to content

Commit

Permalink
Merge pull request #174 from fredcallaway/master
Browse files Browse the repository at this point in the history
better edges when arrow_shift = :end (fixes #171)
  • Loading branch information
hexaeder authored Mar 21, 2024
2 parents be54be1 + d1cd53b commit 956dd2a
Show file tree
Hide file tree
Showing 56 changed files with 80 additions and 51 deletions.
Binary file modified assets/plots.jl-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-07.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/plots.jl-17.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-01.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-02.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-03.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-04.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-05.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-06.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-07.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-17.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-18.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-19.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/reftests.jl-20.png
Binary file modified assets/reftests.jl-21.png
Binary file modified assets/reftests.jl-22.png
Binary file modified assets/reftests.jl-23.png
Binary file modified assets/reftests.jl-24.png
Binary file modified assets/reftests.jl-25.png
Binary file modified assets/reftests.jl-26.png
Binary file modified assets/reftests.jl-27.png
Binary file modified assets/reftests.jl-28.png
Binary file modified assets/reftests.jl-29.png
Binary file modified assets/reftests.jl-30.png
Binary file modified assets/reftests.jl-31.png
Binary file modified assets/reftests.jl-32.png
Binary file modified assets/reftests.jl-33.png
Binary file modified assets/reftests.jl-34.png
Binary file modified assets/reftests.jl-35.png
Binary file modified assets/reftests.jl-36.png
Binary file modified assets/reftests.jl-37.png
Binary file modified assets/reftests.jl-38.png
Binary file modified assets/reftests.jl-39.png
Binary file modified assets/reftests.jl-40.png
Binary file modified assets/reftests.jl-41.png
Binary file modified assets/reftests.jl-42.png
Binary file modified assets/reftests.jl-43.png
Binary file modified assets/syntaxtree.jl-01.png
Binary file modified assets/syntaxtree.jl-02.png
1 change: 1 addition & 0 deletions docs/examples/plots.jl
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ add_edge!(g, 1, 1) #add self loop
f, ax, p = graphplot(g,
layout = [(0,0)],
waypoints = [[(1,-1),(1,1),(-1,1),(-1,-1)]])
hidedecorations!(ax); hidespines!(ax); ax.aspect = DataAspect()
@save_reference f #hide

#=
Expand Down
23 changes: 23 additions & 0 deletions docs/examples/reftests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,12 @@ g = complete_digraph(3)
elabels = repr.(edges(g))
nlabels = repr.(1:nv(g))
fig, ax, p = graphplot(g; elabels, nlabels, elabels_fontsize=10)
hidedecorations!(ax)
@save_reference fig
#
limits!(ax, ax.finallimits[]) # freeze the limits
p[:node_pos][] = Point2f.([(1., -.5), (-1.,0.), (-1.,-1.)])
hidedecorations!(ax)
@save_reference fig

# ## Change of the underlying graph
Expand Down Expand Up @@ -115,28 +117,33 @@ graphplot(fig[1,1],
edge_attr = (; linestyle = [:dot, :dash]),
edge_plottype = :beziersegments,
)
hidedecorations!(current_axis())

graphplot(fig[1,2],
DiGraph([Edge(1 => 2), Edge(2 => 1)]),
edge_attr = (; linestyle = [:dot, :dash]),
edge_plottype = :beziersegments,
)
hidedecorations!(current_axis())

graphplot(fig[2,1],
DiGraph([Edge(1 => 2), Edge(2 => 3), Edge(3=>4), Edge(4=>1)]),
edge_attr = (; linestyle = Linestyle([0.5, 1.0, 1.5, 2.5])),
edge_plottype = :beziersegments,
)
hidedecorations!(current_axis())
@save_reference fig

# ## Self loop with waypoints
g1 = SimpleDiGraph(1)
add_edge!(g1, 1, 1) #add self loop
fig, ax, p = graphplot(g1, layout = [(0,0)], waypoints = [[(1,-1),(1,1),(-1,1),(-1,-1)]])
hidedecorations!(ax)
@save_reference fig

# ## Shift arrows to nodes
fig, ax, p=graphplot(SimpleDiGraph(ones(2,2)),node_size=50,arrow_size=20,curve_distance=0.5,arrow_shift=:end)
hidedecorations!(ax)
@save_reference fig

# ### update shifts
Expand All @@ -147,6 +154,7 @@ add_edge!(g, 1, 1); add_edge!(g, 1, 2); add_edge!(g, 2, 1); add_edge!(g, 2, 3);
fig, ax, p = graphplot(g; arrow_shift=:end,
node_size=[20 for _ in 1:nv(g)],
arrow_size=[20 for _ in 1:ne(g)])
hidedecorations!(ax)
@save_reference fig

p.node_size[][1] = 40
Expand Down Expand Up @@ -175,16 +183,19 @@ fig, ax, p = graphplot(g; arrow_shift=:end, layout=SquareGrid(cols=2),
arrow_attr=(color=:blue,),
edge_color=:red)
xlims!(-.5,1.5); ylims!(-3.5,.5)
hidedecorations!(ax)
@save_reference fig

# ## Inner node labels

fig, ax, p = graphplot(cycle_digraph(3), ilabels=[1, L"\sum_{i=1}^n \alpha^i", "a label"], node_marker=Circle)
hidedecorations!(ax)
@save_reference fig

# Interact with `arrow_shift=:end`

fig, ax, p = graphplot(cycle_digraph(3), ilabels=[1, L"\sum_{i=1}^n \alpha^i", "a label"], node_marker=Circle, arrow_shift=:end)
hidedecorations!(ax)
@save_reference fig

# Update observables
Expand All @@ -204,6 +215,7 @@ gc = circular_ladder_graph(5);
ons = Observable(30);
onf = Observable(30);
fig,ax,p = graphplot(gc; nlabels=repr.(vertices(gc)), node_size=ons, nlabels_fontsize=onf)
hidedecorations!(ax)
@save_reference fig

# Change node size
Expand All @@ -219,6 +231,7 @@ onf[] = 10; # check changes
onf = Observable(Dict(1=>30, 10=>30));
ons = Observable(Dict(1=>30, 10=>30));
fig,ax,p = graphplot(gc; nlabels=repr.(vertices(gc)), node_size=ons, nlabels_fontsize=onf)
hidedecorations!(ax)
@save_reference fig

# Change label font size
Expand All @@ -233,6 +246,7 @@ onf[] = Dict(7=>30); # check changes
ons = Observable(DefaultDict(70, 1=>30, 10=>30));
onf = Observable(DefaultDict(70, 1=>30, 10=>30));
fig,ax,p = graphplot(gc; nlabels=repr.(vertices(gc)), node_size=ons, nlabels_fontsize=onf)
hidedecorations!(ax)
@save_reference fig

# Change node size
Expand All @@ -248,42 +262,51 @@ onf[] = DefaultDict(20, 10=>70); # check changes
# First with a normal `Dict`
gc = circular_ladder_graph(5);
fig,ax,p = graphplot(gc, nlabels=Dict(1=>"One", 2 => "Two"))
hidedecorations!(ax)
@save_reference fig

# And also with a `DefaultDict`
fig,ax,p = graphplot(gc, nlabels=DefaultDict("Unknown", 1=>"One", 2 => "Two"))
hidedecorations!(ax)
@save_reference fig

# ## Use Dict{Edge} for edge arguments
fig,ax,p = graphplot(gc, edge_color=Dict(Edge(7,8)=>:blue))
hidedecorations!(ax)
@save_reference fig

# try out also the DefaultDict
fig,ax,p = graphplot(gc, edge_color=DefaultDict(:green, Edge(7,8)=>:blue))
hidedecorations!(ax)
@save_reference fig

# Of course you can still use integers labeling
ind = findfirst(==(Edge(7,8)) , collect(edges(gc)))
fig,ax,p = graphplot(gc, edge_color=DefaultDict(:green, ind=>:blue))
hidedecorations!(ax)
@save_reference fig

# The same can be done with all enumerations of edge arguments
fig,ax,p = graphplot(gc, elabels=DefaultDict("Unknown", Edge(1,2)=>"1-2", Edge(7,8) => "7-8"))
hidedecorations!(ax)
@save_reference fig

# directed and undirected graphs are handled appropriately.
# For example for directed graphs
gcd = SimpleDiGraph(gc)
fig,ax,p = graphplot(gcd, elabels=DefaultDict("Unknown", Edge(8,7)=>"8-7", Edge(2,7) => "2-7"), nlabels=repr.(vertices(gcd)))
hidedecorations!(ax)
@save_reference fig

# and non-directed graphs
fig,ax,p = graphplot(gc, elabels=DefaultDict("Unknown", Edge(8,7)=>"8-7", Edge(2,7) => "2-7"), nlabels=repr.(vertices(gc)))
hidedecorations!(ax)
@save_reference fig

# Test edge-specific updates
ec = Observable(Dict(Edge(8,7)=>:blue))
fig,ax,p = graphplot(gc, edge_color=ec, nlabels=repr.(vertices(gc)))
hidedecorations!(ax)
@save_reference fig

# update `Observable`
Expand Down
11 changes: 11 additions & 0 deletions src/beziercurves.jl
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,17 @@ ptype(::Union{AbstractPath{PT}, Type{<:AbstractPath{PT}}}) where {PT} = PT
straighten(l::Line) = l
straighten(p::BezierPath) = Line(interpolate(p,0.0), interpolate(p,1.0))

adjust_endpoint(l::Line, p) = Line(l.p0, p)
adjust_endpoint(::MoveTo, p) = MoveTo(p)
adjust_endpoint(::LineTo, p) = LineTo(p)
adjust_endpoint(c::CurveTo, p) = CurveTo(c.c1, c.c2, p)

function adjust_endpoint(path::BezierPath, p)
commands = copy(path.commands)
commands[end] = adjust_endpoint(commands[end], p)
BezierPath(commands)
end

####
#### Helper functions to work with bezier paths
####
Expand Down
62 changes: 40 additions & 22 deletions src/recipes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -285,36 +285,48 @@ function Makie.plot!(gp::GraphPlot)
$(gp.node_strokewidth)
end

# compute initial edge paths; will be adjusted later if arrow_shift = :end
# create array of pathes triggered by node_pos changes
# in case of a graph change the node_position will change anyway
gp[:edge_paths] = lift(node_pos, gp.selfedge_size,
init_edge_paths = lift(node_pos, gp.selfedge_size,
gp.selfedge_direction, gp.selfedge_width, gp.curve_distance_usage, gp.curve_distance) do pos, s, d, w, cdu, cd
find_edge_paths(graph[], gp.attributes, pos)
end
edge_paths = gp[:edge_paths]

# plot edges
edge_plot = edgeplot!(gp, edge_paths;
color=prep_edge_attributes(gp.edge_color, graph, dfth.edge_color),
linewidth=prep_edge_attributes(gp.edge_width, graph, dfth.edge_width),
gp.edge_attr...)

# plot arrow heads
arrow_shift_m = lift(edge_paths, to_px, gp.arrow_shift, node_marker_m, node_size_m, gp.arrow_size) do paths, tpx, shift, nmarker, nsize, asize
arrow_shift_m = lift(init_edge_paths, to_px, gp.arrow_shift, node_marker_m, node_size_m, gp.arrow_size) do paths, tpx, shift, nmarker, nsize, asize
update_arrow_shift(graph[], gp, paths, tpx, node_marker_m, node_size_m, shift)
end
arrow_pos = @lift if !isempty(edge_paths[])
broadcast(interpolate, edge_paths[], $arrow_shift_m)
arrow_pos = @lift if !isempty(init_edge_paths[])
broadcast(interpolate, init_edge_paths[], $arrow_shift_m)
else # if no edges return (empty) vector of points, broadcast yields Vector{Any} which can't be plotted
Vector{eltype(node_pos[])}()
end
arrow_rot = @lift if !isempty(edge_paths[])
Billboard(broadcast($to_angle, edge_paths[], $arrow_pos, $(arrow_shift_m)))
arrow_rot = @lift if !isempty(init_edge_paths[])
Billboard(broadcast($to_angle, init_edge_paths[], $arrow_pos, $(arrow_shift_m)))
else
Billboard(Float32[])
end

# update edge paths to line up with arrow heads if arrow_shift = :end
edge_paths = gp[:edge_paths] = @lift map($init_edge_paths, $arrow_pos, eachindex($arrow_pos)) do ep, ap, i
if getattr($(gp.arrow_shift), i) == :end
adjust_endpoint(ep, ap)
else
ep
end
end

# actually plot edges
edge_plot = gp[:edge_plot] = edgeplot!(gp, edge_paths;
plottype=gp[:edge_plottype][],
color=prep_edge_attributes(gp.edge_color, graph, dfth.edge_color),
linewidth=prep_edge_attributes(gp.edge_width, graph, dfth.edge_width),
gp.edge_attr...)

# arrow plots
arrow_show_m = @lift $(gp.arrow_show) === automatic ? Graphs.is_directed($graph) : $(gp.arrow_show)
arrow_heads = scatter!(gp,
arrow_plot = gp[:arrow_plot] = scatter!(gp,
arrow_pos;
marker = prep_edge_attributes(gp.arrow_marker, graph, dfth.arrow_marker),
markersize = prep_edge_attributes(gp.arrow_size, graph, dfth.arrow_size),
Expand All @@ -325,8 +337,9 @@ function Makie.plot!(gp::GraphPlot)
visible = arrow_show_m,
gp.arrow_attr...)


# plot vertices
vertex_plot = scatter!(gp, node_pos;
vertex_plot = gp[:node_plot] = scatter!(gp, node_pos;
color=prep_vertex_attributes(node_color_m, graph, scatter_theme.color),
marker=prep_vertex_attributes(node_marker_m, graph, scatter_theme.marker),
markersize=prep_vertex_attributes(node_size_m, graph, scatter_theme.markersize),
Expand All @@ -349,7 +362,7 @@ function Makie.plot!(gp::GraphPlot)
$(gp.nlabels_distance) .* align_to_dir($(gp.nlabels_align))
end

nlabels_plot = text!(gp, positions;
nlabels_plot = gp[:nlabels_plot] = text!(gp, positions;
text=prep_vertex_attributes(gp.nlabels, graph, Observable("")),
align=prep_vertex_attributes(gp.nlabels_align, graph, dfth.nlabels_align),
color=prep_vertex_attributes(gp.nlabels_color, graph, dfth.nlabels_color),
Expand Down Expand Up @@ -398,7 +411,7 @@ function Makie.plot!(gp::GraphPlot)
offsets = map(p -> Point(-p.data[2], p.data[1])/norm(p), tangent_px)
offsets .= elabels_distance_offset(graph[], gp.attributes) .* offsets
end
elabels_plot = text!(gp, positions;
elabels_plot = gp[:elabels_plot] = text!(gp, positions;
text=prep_edge_attributes(gp.elabels, graph, Observable("")),
rotation=rotation,
offset=offsets,
Expand Down Expand Up @@ -477,7 +490,7 @@ function find_edge_paths(g, attr, pos::AbstractVector{PT}) where {PT}

if !isnothing(waypoints) && !isempty(waypoints) #there are waypoints
radius = getattr(attr.waypoint_radius, i, nothing)
if radius === nothing || radius === :spline
if radius === nothing || radius === :spline
paths[i] = Path(p1, waypoints..., p2; tangents, tfactor)
elseif radius isa Real
paths[i] = Path(radius, p1, waypoints..., p2)
Expand Down Expand Up @@ -631,13 +644,16 @@ draw using bezier segments.
All attributes are passed down to the actual recipe.
"""
@recipe(EdgePlot, paths) do scene
Attributes()
Attributes(plottype=automatic)
end

function Makie.plot!(p::EdgePlot)
N = length(p[:paths][])

if eltype(p[:paths][]) <: Line
plottype = pop!(p.attributes, :plottype)[]
alllines = eltype(p[:paths][]) <: Line

if alllines && plottype != :beziersegments
PT = ptype(eltype(p[:paths][]))
segs = Observable(Vector{PT}(undef, 2*N))

Expand All @@ -647,8 +663,10 @@ function Makie.plot!(p::EdgePlot)
end

linesegments!(p, segs; p.attributes...)
else
elseif plottype != :linesegments
beziersegments!(p, p[:paths]; p.attributes...)
else
error("Impossible combination of plottype=$plottype and alllines=$alllines.")
end

return p
Expand Down Expand Up @@ -776,7 +794,7 @@ function update_arrow_shift(g, gp, edge_paths::Vector{<:AbstractPath{PT}}, to_px
end
arrow_shift[i] = t
end

return arrow_shift
end

Expand Down
34 changes: 5 additions & 29 deletions src/utils.jl
Original file line number Diff line number Diff line change
@@ -1,42 +1,18 @@
export get_edge_plot, get_arrow_plot, get_node_plot, get_nlabel_plot, get_elabel_plot

"Get the `EdgePlot` subplot from a `GraphPlot`."
function get_edge_plot(gp::GraphPlot)
p = gp.plots[1]
@assert p isa EdgePlot
return p
end
get_edge_plot(gp::GraphPlot) = gp.edge_plot[]

"Get the scatter plot of the arrow heads from a `GraphPlot`."
function get_arrow_plot(gp::GraphPlot)
p = gp.plots[2]
@assert p isa Scatter
return p
end
get_arrow_plot(gp::GraphPlot) = gp.arrow_plot[]

"Get the scatter plot of the nodes from a `GraphPlot`."
function get_node_plot(gp::GraphPlot)
p = gp.plots[3]
@assert p isa Scatter
@assert p[1][] == gp[:node_pos][]
return p
end
get_node_plot(gp::GraphPlot) = gp.node_plot[]

"Get the text plot of the node labels from a `GraphPlot`."
get_nlabel_plot(gp::GraphPlot) = _get_label_plot(gp, gp.nlabels[])
get_nlabel_plot(gp::GraphPlot) = hasproperty(gp, :nlabels_plot) ? gp.nlabels_plot[] : nothing
"Get the text plot of the edge labels from a `GraphPlot`."
get_elabel_plot(gp::GraphPlot) = _get_label_plot(gp, gp.elabels[])

function _get_label_plot(gp::GraphPlot, labels)
ps = filter(p -> p isa Makie.Text && p[:text][] == labels, gp.plots)
if isempty(ps)
return nothing
elseif length(ps) == 1
return ps[1]
else
error("Could not determine plot $ps")
end
end
get_elabel_plot(gp::GraphPlot) = hasproperty(gp, :elabels_plot) ? gp.elabels_plot[] : nothing

"""
getedgekeys(gr::G, edgedat::D) where {G<:AbstractGraph, K<:AbstractEdge, D<:AbstractDict{K}, IsDirected{G}}
Expand Down

0 comments on commit 956dd2a

Please sign in to comment.