GraphMakie
This is the Documentation for GraphMakie.
This Package consists of two parts: a plot recipe for graphs types from Graphs.jl and some helper functions to add interactions to those plots.
There are also plot examples and interaction examples pages.
The graphplot
Recipe
GraphMakie.graphplot
โ Functiongraphplot(graph::AbstractGraph)
graphplot!(ax, graph::AbstractGraph)
Creates a plot of the network graph
. Consists of multiple steps:
- Layout the nodes: see
layout
attribute. The node position is accessible from outside the plot objectp
as an observable usingp[:node_pos]
. - plot edges as
edgeplot
-plot - if
arrow_show
plot arrowheads asscatter
-plot - plot nodes as
scatter
-plot - if
nlabels!=nothing
plot node labels astext
-plot - if
elabels!=nothing
plot edge labels astext
-plot
The main attributes for the subplots are exposed as attributes for graphplot
. Additional attributes for the scatter
, edgeplot
and text
plots can be provided as a named tuples to node_attr
, edge_attr
, nlabels_attr
and elabels_attr
.
Most of the arguments can be either given as a vector of length of the edges/nodes or as a single value. One might run into errors when changing the underlying graph and therefore changing the number of Edges/Nodes.
Attributes
Main attributes
layout=Spring()
: functionAbstractGraph->Vector{Point}
orVector{Point}
determines the base layoutnode_color=automatic
: Defaults toscatter_theme.color
in absence ofilabels
.node_size=automatic
: Defaults toscatter_theme.markersize
in absence ofilabels
. Otherwise choses node size based onilabels
size.node_marker=automatic
: Defaults toscatter_theme.marker
in absence ofilabels
.node_strokewidth=automatic
Defaults toscatter_theme.strokewidth
in absence ofilabels
.node_attr=(;)
: List of kw arguments which gets passed to thescatter
commandedge_color=lineseg_theme.color
: Color for edges.edge_width=lineseg_theme.linewidth
: Pass a vector with 2 width per edge to get pointy edges.edge_attr=(;)
: List of kw arguments which gets passed to thelinesegments
commandarrow_show=Makie.automatic
:Bool
, indicate edge directions with arrowheads? Defaults toGraphs.is_directed(graph)
.arrow_marker='โค'
arrow_size=scatter_theme.markersize
: Size of arrowheads.arrow_shift=0.5
: Shift arrow position from source (0) to dest (1) node. Ifarrow_shift=:end
, the arrowhead will be placed on the surface of the destination node (assuming the destination node is circular).arrow_attr=(;)
: List of kw arguments which gets passed to thescatter
command
Node labels
The position of each label is determined by the node position plus an offset in data space.
nlabels=nothing
:Vector{String}
with label for each nodenlabels_align=(:left, :bottom)
: Anchor of text field.nlabels_distance=0.0
: Pixel distance from node in direction of align.nlabels_color=labels_theme.color
nlabels_offset=nothing
:Point
orVector{Point}
(in data space)nlabels_fontsize=labels_theme.fontsize
nlabels_attr=(;)
: List of kw arguments which gets passed to thetext
command
Inner node labels
Put labels inside the marker. If labels are provided, change default attributes to node_marker=Circle
, node_strokewidth=1
and node_color=:gray80
. The node_size
will match size of the ilabels
.
ilabels=nothing
:Vector
with label for each nodeilabels_color=labels_theme.color
ilabels_fontsize=labels_theme.fontsize
ilabels_attr=(;)
: List of kw arguments which gets passed to thetext
command
Edge labels
The base position of each label is determined by src + shift*(dst-src)
. The additional distance
parameter is given in pixels and shifts the text away from the edge.
elabels=nothing
:Vector{String}
with label for each edgeelabels_align=(:center, :center)
: Anchor of text field.elabels_side = :left
: Side of the edge to put the edge label textelabels_distance=Makie.automatic
: Pixel distance of anchor to edge. The direction is decided based onelabels_side
elabels_shift=0.5
: Position between src and dst of edge.elabels_rotation=Makie.automatic
: Angle of text per label. Ifnothing
this will be determined by the edge angle. Ifautomatic
it will also point upwards making it easy to read.elabels_offset=nothing
: Additional offset in data spaceelabels_color=labels_theme.color
elabels_fontsize=labels_theme.fontsize
elabels_attr=(;)
: List of kw arguments which gets passed to thetext
command
Curvy edges & self edges/loops
edge_plottype=Makie.automatic()
: Eitherautomatic
,:linesegments
or:beziersegments
.:beziersegments
are much slower for big graphs!
Self edges / loops:
selfedge_size=Makie.automatic()
: Size of selfloop (dict/vector possible).selfedge_direction=Makie.automatic()
: Direction of center of the selfloop asPoint2
(dict/vector possible).selfedge_width=Makie.automatic()
: Opening of selfloop in rad (dict/vector possible).- Note: If valid waypoints are provided for selfloops, the selfedge attributes above will be ignored.
High level interface for curvy edges:
curve_distance=0.1
:Specify a distance of the (now curved) line to the straight line in data space. Can be single value, array or dict. User provided
tangents
orwaypoints
will overrule this property.curve_distance_usage=Makie.automatic()
:If
Makie.automatic()
, only plot double edges in a curvy way. Other options aretrue
andfalse
.
Tangents interface for curvy edges:
tangents=nothing
:Specify a pair of tangent vectors per edge (for src and dst). If
nothing
(or edge idx not in dict) draw a straight line.tfactor=0.6
:Factor is used to calculate the bezier waypoints from the (normalized) tangents. Higher factor means bigger radius. Can be tuple per edge to specify different factor for src and dst.
Note: Tangents are ignored on selfloops if no waypoints are provided.
Waypoints along edges:
waypoints=nothing
Specify waypoints for edges. This parameter should be given as a vector or dict. Waypoints will be crossed using natural cubic splines. The waypoints may or may not include the src/dst positions.
waypoint_radius=nothing
If the attribute
waypoint_radius
isnothing
or:spline
the waypoints will be crossed using natural cubic spline interpolation. If number (dict/vector possible), the waypoints won't be reached, instead they will be connected with straight lines which bend in the given radius around the waypoints.
Passing arguments
The recipe can handle a range of argument types. For all the arguments that support a collection of configurations per element, you pass-in a Vector
. However you can also pass in a Dict
or a DefaultDict
to only specify a configuration for a specific element of interest, while the rest get the default value. The keys
of the Dict
ionaries are the Int
index of the element or the Edge
when reasonable. See some demonstration on Changes of node and label sizes, Dict and DefaultDict and Use Dict{Edge} for edge arguments.
Network Layouts
The layout algorithms are provided by NetworkLayout.jl
. See the docs for a list of available layouts.
A layout has to be a function f(g::AbstractGraph) -> pos::Vector{Point}
. You can also provide your own layouts or use other packages like LayeredLayouts.jl
for DAG (see also the Dependency Graph of a Package example).
using LayeredLayouts
function mylayout(g::SimpleGraph)
xs, ys, _ = solve_positions(Zarate(), g)
return Point.(zip(xs, ys))
end
Predefined Interactions
GraphMakie.jl
provides some pre-built interactions to enable drag&drop of nodes and edges as well as highlight on hover.
To try them all use the following code in a GLMakie
environment.
using GLMakie
using GraphMakie
using Graphs
g = wheel_graph(10)
f, ax, p = graphplot(g, edge_width=[3 for i in 1:ne(g)],
node_size=[10 for i in 1:nv(g)])
deregister_interaction!(ax, :rectanglezoom)
register_interaction!(ax, :nhover, NodeHoverHighlight(p))
register_interaction!(ax, :ehover, EdgeHoverHighlight(p))
register_interaction!(ax, :ndrag, NodeDrag(p))
register_interaction!(ax, :edrag, EdgeDrag(p))
GraphMakie.NodeHoverHighlight
โ FunctionNodeHoverHeighlight(p::GraphPlot, factor=2)
Magnifies the node_size
of node under cursor by factor
.
Example
julia> g = wheel_graph(10)
julia> f, ax, p = graphplot(g, node_size = [20 for i in 1:nv(g)])
julia> register_interaction!(ax, :nodehover, NodeHoverHighlight(p))
GraphMakie.EdgeHoverHighlight
โ FunctionEdgeHoverHeighlight(p::GraphPlot, factor=2)
Magnifies the edge_width
of edge under cursor by factor
. If arrow_size isa Vector{<:Real}
it also magnifies the arrow scatter.
Example
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, edge_width = [3 for i in 1:ne(g)],
arrow_size=[10 for i in 1:ne(g)])
julia> register_interaction!(ax, :nodehover, EdgeHoverHighlight(p))
GraphMakie.NodeDrag
โ FunctionNodeDrag(p::GraphPlot)
Allows drag and drop of Nodes. Please deregister the :rectanglezoom
interaction.
Example
julia> g = wheel_graph(10)
julia> f, ax, p = graphplot(g, node_size = [10 for i in 1:nv(g)])
julia> deregister_interaction!(ax, :rectanglezoom)
julia> register_interaction!(ax, :nodehover, NodeHoverHighlight(p))
julia> register_interaction!(ax, :nodedrag, NodeDrag(p))
GraphMakie.EdgeDrag
โ FunctionEdgeDrag(p::GraphPlot)
Allows drag and drop of Edges. Please deregister the :rectanglezoom
interaction.
Example
julia> g = wheel_graph(10)
julia> f, ax, p = graphplot(g, edge_width = [3 for i in 1:ne(g)])
julia> deregister_interaction!(ax, :rectanglezoom)
julia> register_interaction!(ax, :edgehover, EdgeHoverHighlight(p))
julia> register_interaction!(ax, :edgedrag, EdgeDrag(p))
Interaction Interface
GraphMakie.jl
provides some helper functions to register interactions to your graph plot. There are special interaction types for hovering, clicking and dragging nodes and edges. For more information on the axis interaction please consult the Makie.jl
docs.
The general idea is to create some handler type, provide some action function and register it as an interaction with the axes.
Click Interactions
GraphMakie.NodeClickHandler
โ FunctionNodeClickHandler(fun)
Initializes ClickHandler
for nodes. Calls function
fun(idx, event, axis)
on left-click events where idx
is the node index.
Example
julia> using Makie.Colors
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, node_size=30, node_color=[colorant"red" for i in 1:nv(g)])
julia> function action(idx, event, axis)
p.node_color[][idx] = rand(RGB)
p.node_color[] = p.node_color[]
end
julia> register_interaction!(ax, :nodeclick, NodeClickHandler(action))
GraphMakie.EdgeClickHandler
โ FunctionEdgeClickHandler(fun)
Initializes ClickHandler
for edges. Calls function
fun(idx, event, axis)
on left-click events where idx
is the edge index.
Example
julia> using Makie.Colors
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, edge_width=4, edge_color=[colorant"black" for i in 1:ne(g)])
julia> function action(idx, event, axis)
p.edge_color[][idx] = rand(RGB)
p.edge_color[] = p.edge_color[]
end
julia> register_interaction!(ax, :edgeclick, EdgeClickHandler(action))
Hover Interactions
GraphMakie.NodeHoverHandler
โ FunctionNodeHoverHandler(fun)
Initializes HoverHandler
for nodes. Calls function
fun(hoverstate, idx, event, axis)
with hoverstate=true
on hover and false
at the end of hover. idx
is the node index.
Example
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, node_size = [20 for i in 1:nv(g)])
julia> function action(state, idx, event, axis)
p.node_size[][idx] = state ? 40 : 20
p.node_size[] = p.node_size[] #trigger observable
end
julia> register_interaction!(ax, :nodehover, NodeHoverHandler(action))
GraphMakie.EdgeHoverHandler
โ FunctionEdgeHoverHandler(fun)
Initializes HoverHandler
for edges. Calls function
fun(hoverstate, idx, event, axis)
with hoverstate=true
on hover and false
at the end of hover. idx
is the edge index.
Example
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, edge_width = [3.0 for i in 1:ne(g)])
julia> function action(state, idx, event, axis)
p.edge_width[][idx] = state ? 6.0 : 3.0
p.edge_width[] = p.edge_width[] #trigger observable
end
julia> register_interaction!(ax, :edgehover, EdgeHoverHandler(action))
Drag Interactions
GraphMakie.NodeDragHandler
โ FunctionNodeDragHandler(fun)
Initializes DragHandler
for Nodes. Calls function
fun(dragstate, idx, event, axis)
where dragstate=true
during the drag and false
at the end of the drag, the last time fun
is triggered. idx
is the node index.
Example
julia> g = wheel_digraph(10)
julia> f, ax, p = graphplot(g, node_size=20)
julia> deregister_interaction!(ax, :rectanglezoom)
julia> function action(state, idx, event, axis)
p[:node_pos][][idx] = event.data
p[:node_pos][] = p[:node_pos][]
end
julia> register_interaction!(ax, :nodedrag, NodeDragHandler(action))
GraphMakie.EdgeDragHandler
โ FunctionEdgeDragHandler(fun)
Initializes DragHandler
for Edges. Calls function
fun(dragstate, idx, event, axis)
where dragstate=true
during the drag and false
at the end of the drag, the last time fun
is triggered. idx
is the edge index.
See EdgeDrag
for a concrete implementation. ```