Skip to content

Commit bd37517

Browse files
committed
Update with demo
1 parent 692508a commit bd37517

File tree

8 files changed

+106
-35
lines changed

8 files changed

+106
-35
lines changed

examples/graph_animations/demo.jl

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
using Javis
2+
3+
function edge(; p1, p2, kwargs...)
4+
sethue("black")
5+
line(p1, p2, :stroke)
6+
end
7+
8+
function node(p=O, color="black")
9+
sethue(color)
10+
circle(p, 10, :fill)
11+
return p
12+
end
13+
14+
function ground(args...)
15+
background("white")
16+
sethue("black")
17+
end
18+
19+
video = Video(400, 400)
20+
Background(1:150, ground)
21+
22+
g = @Object 1:150 JGraph(true, 100, 100) O
23+
24+
adjacency_list = [[2, 3, 4, 5, 6],
25+
[7, 8],
26+
[7, 8],
27+
[],[],[],[],[]]
28+
for i in 1:length(adjacency_list)
29+
@Graph g i*10:150 GraphVertex(i, (args...; kwargs...)->node()) O
30+
end
31+
32+
for i in 1:length(adjacency_list)
33+
for j in adjacency_list[i]
34+
@Graph g 10+i*15:150 GraphEdge(i, j, (args...; kwargs...)->edge(; kwargs...)) O
35+
end
36+
end
37+
38+
render(video; pathname="demo.gif")

examples/graph_animations/example0.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ The demo serves to list the features that I am going to implement for graph anim
2424

2525
### Examples
2626

27+
0. [Demo](demo.jl)
2728
1. [Graph Traversal](example1.md)
2829
2. [Depth First Search](example2.md)
2930
2. [Shortest Path](example3.md)

src/Javis.jl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,7 +371,12 @@ function draw_object(object, video, frame, origin_matrix)
371371
cs = get_current_setting()
372372
!cs.show_object && return
373373

374-
res = object.func(video, object, frame; collect(object.change_keywords)...)
374+
if object.meta !== nothing && :opts in fieldnames(typeof(object.meta))
375+
res = object.func(video, object, frame; collect(object.change_keywords)..., collect(object.meta.opts)...)
376+
else
377+
res = object.func(video, object, frame; collect(object.change_keywords)...)
378+
end
379+
375380
current_global_matrix = cairotojuliamatrix(getmatrix())
376381
# obtain current matrix without the initial matrix part
377382
current_matrix = inv(origin_matrix) * current_global_matrix

src/structs/GraphEdge.jl

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ render(video; pathname="graph_node.gif")
2525
struct GraphEdge
2626
from_node::Int
2727
to_node::Int
28-
draw::Function
2928
animate_on::Symbol
3029
property_style_map::Dict{Any,Symbol}
30+
opts::Dict{Symbol, Any}
3131
end
3232

3333
"""
@@ -53,11 +53,29 @@ Create a graph edge with additional drawing function and options.
5353
- `:weights`: only possible for weighted graphs i.e. `SimpleWeightedGraphs`.
5454
- `properties_to_style_map::Dict{Any,Symbol}`: A mapping to of how edge attributes map to edge drawing styles.
5555
"""
56+
GraphEdge(from_node::Int, to_node::Int, draw::Union{Vector{Function}, Function}; kwargs...) =
57+
GraphEdge(CURRENT_GRAPH[1], from_node, to_node, draw; kwargs...)
58+
59+
GraphEdge(graph::AbstractObject, from_node::Int, to_node::Int, draw::Vector{Function}; kwargs...) =
60+
GraphEdge(graph, from_node, to_node, compile_draw_funcs(draw); kwargs...)
61+
5662
function GraphEdge(
5763
graph::AbstractObject,
5864
from_node::Int,
5965
to_node::Int,
6066
draw::Function;
6167
animate_on::Symbol = :opacity,
62-
property_style_map::Dict{Any,Symbol} = Dict(),
63-
) end
68+
property_style_map::Dict{Any,Symbol} = Dict{Any,Symbol}(),
69+
)
70+
g = graph.meta
71+
if !(typeof(g) <: JGraph)
72+
throw(ErrorException("Cannot define edge since $(typeof(graph)) is not a `JGraph`"))
73+
end
74+
if get_prop(g.adjacency_list, from_node, to_node) !== nothing
75+
@warn "Edge $(from_node)=>$(to_node) is already created on canvas. Recreating it will leave orphan edges in the animation. To undo, call `rem_edge!`"
76+
end
77+
add_edge!(g.adjacency_list.graph, from_node, to_node)
78+
set_prop!(g.adjacency_list, from_node, to_node, length(g.ordering)+1)
79+
graph_edge = GraphEdge(from_node, to_node, animate_on, property_style_map, Dict{Symbol, Any}())
80+
return draw, graph_edge
81+
end

src/structs/GraphVertex.jl

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct GraphVertex
2525
node::Int
2626
animate_on::Symbol
2727
property_style_map::Dict{Any,Symbol}
28+
opts::Dict{Symbol, Any}
2829
end
2930

3031
"""
@@ -56,7 +57,7 @@ GraphVertex(node::Int, draw::Union{Vector{Function}, Function}; kwargs...) =
5657
GraphVertex(CURRENT_GRAPH[1], node, draw; kwargs...)
5758

5859
GraphVertex(graph::AbstractObject, node::Int, draw::Vector{Function}; kwargs...) =
59-
GraphVertex(graph::AbstractObject, node::Int, compile_draw_funcs(draw); kwargs...)
60+
GraphVertex(graph, node, compile_draw_funcs(draw); kwargs...)
6061

6162
function GraphVertex(
6263
graph::AbstractObject,
@@ -71,7 +72,7 @@ function GraphVertex(
7172
end
7273
if g.mode == :static
7374
if get_prop(g.adjacency_list, node) !== nothing
74-
@warn "Node $(node) is already created on canvas. Recreating it will leave orphan node objects in the animation. To undo, call `rem_edge!`"
75+
@warn "Node $(node) is already created on canvas. Recreating it will leave orphan node objects in the animation. To undo, call `rem_node!`"
7576
end
7677
if g.layout != :none
7778
draw_fn = (args...; position=O, kwargs...) -> begin Luxor.translate(position); draw(args...; position=position, kwargs...) end
@@ -80,7 +81,7 @@ function GraphVertex(
8081
end
8182
add_vertex!(g.adjacency_list.graph)
8283
set_prop!(g.adjacency_list, nv(g.adjacency_list), length(g.ordering)+1)
83-
graph_vertex = GraphVertex(node, animate_on, property_style_map)
84+
graph_vertex = GraphVertex(node, animate_on, property_style_map, Dict{Symbol, Any}())
8485
return draw_fn, graph_vertex
8586
elseif g.mode == :dynamic
8687
end

src/structs/JGraph.jl

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -166,36 +166,43 @@ function _global_property_limits(video, object, frames; kwargs...)
166166
end
167167

168168
function _global_layout(video, object, frame; kwargs...)
169-
if frame != 1
170-
return
171-
end
172169
g = object.meta
173-
layout_x = []
174-
layout_y = []
175-
if g.layout == :none
176-
return
177-
elseif g.layout == :spring
178-
# Check due to some errors in calling spring_layout with an empty graph
179-
if nv(g.adjacency_list.graph) > 0
180-
layout_x, layout_y = spring_layout(g.adjacency_list.graph)
181-
end
182-
elseif g.layout == :spectral
183-
# Check special property layout_weight is defined on edges and collect weights
184-
edge_ordering_positions = edge_props(g.adjacency_list.graph)
185-
weights = map(
186-
(idx) -> get(g.ordering[idx].change_keywords, :layout_weight, 1),
187-
edge_ordering_positions,
188-
)
189-
if nv(g.adjacency_list.graph) > 0
190-
layout_x, layout_y = spectral_layout(g.adjacency_list, weights)
170+
if frame == first(get_frames(object))
171+
layout_x = []
172+
layout_y = []
173+
if g.layout == :none
174+
return
175+
elseif g.layout == :spring
176+
# Check due to some errors in calling spring_layout with an empty graph
177+
if nv(g.adjacency_list.graph) > 0
178+
layout_x, layout_y = spring_layout(g.adjacency_list.graph)
179+
end
180+
elseif g.layout == :spectral
181+
# Check special property layout_weight is defined on edges and collect weights
182+
edge_ordering_positions = edge_props(g.adjacency_list.graph)
183+
weights = map(
184+
(idx) -> get(g.ordering[idx].change_keywords, :layout_weight, 1),
185+
edge_ordering_positions,
186+
)
187+
if nv(g.adjacency_list.graph) > 0
188+
layout_x, layout_y = spectral_layout(g.adjacency_list, weights)
189+
end
191190
end
191+
# Normalize coordinates between -0.5 and 0.5
192+
coords = map((p) -> Point(p), collect(zip(layout_x, layout_y))) .- [(0.5, 0.5)]
193+
# Scale to graph dimensions
194+
coords = coords .* [(g.width, g.height)]
195+
object.opts[:layout] = coords
192196
end
193-
# Normalize coordinates between -0.5 and 0.5
194-
coords = map((p) -> Point(p), collect(zip(layout_x, layout_y))) .- [(0.5, 0.5)]
195-
# Scale to graph dimensions
196-
coords = coords .* [(g.width, g.height)]
197197
# Now assign positions back to all nodes
198198
for (idx, p) in enumerate(node_props(g.adjacency_list))
199-
g.ordering[p].change_keywords[:position] = coords[idx]
199+
g.ordering[p].meta.opts[:position] = object.opts[:layout][idx]
200+
end
201+
# Define keyword arguments for edges defining endpoint position
202+
for (_, p) in enumerate(edge_props(g.adjacency_list))
203+
from_node = get_prop(g.adjacency_list, g.ordering[p].meta.from_node)
204+
to_node = get_prop(g.adjacency_list, g.ordering[p].meta.to_node)
205+
g.ordering[p].meta.opts[:p1] = g.ordering[from_node].meta.opts[:position]
206+
g.ordering[p].meta.opts[:p2] = g.ordering[to_node].meta.opts[:position]
200207
end
201208
end

src/structs/Object.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ macro Graph(g, frames, node, start_pos, kwargs...)
133133
object_kwargs = [esc(i) for i in kwargs]
134134
return quote
135135
CURRENT_GRAPH[1]=$(esc(g))
136-
push!(CURRENT_GRAPH[1].meta.ordering, Object($frames, $(esc(node))..., $start_pos; $(object_kwargs...)))[length(CURRENT_GRAPH[1].meta.ordering)]
136+
_frames=$(esc(frames))
137+
push!(CURRENT_GRAPH[1].meta.ordering, Object(_frames, $(esc(node))..., $start_pos; $(object_kwargs...)))[length(CURRENT_GRAPH[1].meta.ordering)]
137138
end
138139
end
139140

src/structs/WeightedGraph.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,5 @@ end
127127
edge_props(wg::WeightedGraph)
128128
"""
129129
function edge_props(wg::WeightedGraph)
130-
map((e) -> wg.edge_w[Tuple(e)], edges(wg))
130+
map((e) -> wg.edge_w[Pair(e)], edges(wg))
131131
end

0 commit comments

Comments
 (0)