|
| 1 | +## Depth First Search |
| 2 | + |
| 3 | +### Points covered |
| 4 | +1. Graph creation using `LightGraphs.jl` |
| 5 | +2. Demonstrate additional utility functions |
| 6 | + |
| 7 | +### Graph creation |
| 8 | + |
| 9 | +In the previous example, it was shown how a graph can be created and animated for a simple graph data type. To simplify a lot of details one can provide a known graph type i.e. from JuliaGraphs. The candidate types for this are: |
| 10 | +* `SimpleGraph` |
| 11 | +* `SimpleDiGraph` |
| 12 | +* `SimpleWeightedGraph` |
| 13 | +* `SimpleWeightedDiGraph` |
| 14 | + |
| 15 | +Here I cover the case when it is represented using `SimpleGraph` from the LightGraphs package. |
| 16 | + |
| 17 | +```julia |
| 18 | +using LightGraphs |
| 19 | +g = SimpleGraph(6) |
| 20 | +add_edge!(g, 1, 2) |
| 21 | +add_edge!(g, 1, 3) |
| 22 | +add_edge!(g, 2, 4) |
| 23 | +add_edge!(g, 3, 5) |
| 24 | +add_edge!(g, 4, 6) |
| 25 | + |
| 26 | +ag, nodes, edges = create_graph(g, 300, 300; layout=:spring, mode=:static) |
| 27 | +``` |
| 28 | +`ag` - A Javis object storing some useful meta-data corresponding to the graph. Only the translate operation is supported on it as of now. |
| 29 | + |
| 30 | +`nodes` - list of references to node objects in the order of node id |
| 31 | + |
| 32 | +`edges` - list of references to edge objects in the order of creation |
| 33 | + |
| 34 | +The `layout` defines how the nodes shall be arranged on the canvas. The `mode` argument determines whether the graph is to be considered a static or a dynamic graph. |
| 35 | + |
| 36 | +#### Static graphs |
| 37 | +* The layout computation is done only once when the entire graph is known. |
| 38 | +* Updates to nodes/edge properties in the input graph are not animated i.e. the final graph state is used for the animation |
| 39 | + |
| 40 | +#### Dynamic graphs |
| 41 | +* Addition of each new node leads to recomputation of new layout |
| 42 | +* Group animations of nodes/edges are separated across frames using a predefined ordering |
| 43 | +* Updates to edges and nodes through properties are animated with the help of action transitions leading to visualisation for time evolving graphs |
| 44 | + |
| 45 | +*Note - Dynamic layout will be much more computationally intensive than static layout* |
| 46 | + |
| 47 | +**Adding or removing nodes and edges** |
| 48 | + |
| 49 | +New nodes and edges can be added to the canvas or existing nodes deleted after the graph creation. |
| 50 | +```julia |
| 51 | +removeNode!(ag, 2) |
| 52 | +addNode!(ag, 7) |
| 53 | +addEdge!(ag, 1, 7) |
| 54 | +``` |
| 55 | +or |
| 56 | +```julia |
| 57 | +addEdge!(ag, 1, 7, 20) |
| 58 | +``` |
| 59 | +The last argument is the edge weight. This mimics the `add_edge!` function provided by the LightGraph interface, where the `weight` parameter is valid if the internal graph type is a `SimpleWeightedGraph`. |
| 60 | + |
| 61 | + |
| 62 | +### Visualisation and demo utility functions |
| 63 | + |
| 64 | +Rendering the video at this stage would just draw and animate a plain graph based on the underlying `LightGraph` type. It picks up reasonable defaults for these animations for e.g. if the graph is of type `SimpleWeightedGraph` the edge weights are simply centered on the lines drawn. |
| 65 | + |
| 66 | +```julia |
| 67 | +current=1 |
| 68 | +dst=6 |
| 69 | +path=[] |
| 70 | +visited=[false for i in 1:nv(g)] |
| 71 | +num_visited=0 |
| 72 | +``` |
| 73 | + |
| 74 | +The `path` variable stores the path to the destination node. The additional variable `num_visited` is needed to organize the animation of the call to the utility functions one after another. Once a relative way to define action frames across different objects is available this variable won't be necessary. |
| 75 | + |
| 76 | +```julia |
| 77 | +function dfs_and_animate(node, path) |
| 78 | + if node==dst |
| 79 | + highlightNode(GFrames(20+num_visited*10, 100), ag, current, :border_color, "red") |
| 80 | + return |
| 81 | + end |
| 82 | + # Highlight the current node |
| 83 | + highlightNode(GFrames(20+num_visited*10, 100), ag, current, :border_color, "yellow") |
| 84 | + visited[current]=true |
| 85 | + num_visited+=1 |
| 86 | + push!(path, current) |
| 87 | + # Change node color when highlighting effect ends |
| 88 | + changeNodeProperty!(@Frames(prev_end(), stop=parent_end()), ag, current, :color, "blue") |
| 89 | + for nb in neighbors(g, current) |
| 90 | + if visited[nb] |
| 91 | + continue |
| 92 | + end |
| 93 | + highlightEdge(GFrames(20+num_visited*10, 100), ag, current, nb, :color, "green") |
| 94 | + num_visited+=1 |
| 95 | + dfs_and_animate(nb, path) |
| 96 | + end |
| 97 | + # Highlight again to indicate return to parent node |
| 98 | + highlightNode(GFrames(20+num_visited*10, 100), ag, current, :border_color, "yellow") |
| 99 | + num_visited+=1 |
| 100 | + pop!(path) |
| 101 | +end |
| 102 | + |
| 103 | +dfs_and_animate(1, path) |
| 104 | +``` |
| 105 | + |
| 106 | +Note - Even though the drawing property `:color` was not explicitly defined by passing it to a drawing function, these are provided as defaults for every node and edges in the case the graph is created using `create_graph`. |
| 107 | + |
| 108 | +The default drawing properties provided are - |
| 109 | +* `fill_color` - for nodes |
| 110 | +* `border_color` - for nodes |
| 111 | +* `radius` - for nodes |
| 112 | +* `color` - for edges |
| 113 | +* `weights` - this property is only available for edges in weighted graphs |
| 114 | +* `opacity`, `scale` & `line_width` - defined in [example 1](example1.md) |
| 115 | + |
| 116 | +If frame management becomes an issue, it is possible to skip it completely and let Javis handle the frame management, again through the use of reasonable defaults. For example, skipping frames in `highlightNode` schedules the node highlighting after the end of the previous animation specified on the graph. This animation could be any of the graph utility functions with the exception of `changeNodeProperty` and `changeEdgeProperty` for which the starting frame is used as reference. To avail of this default option, the `default_keyframes=true` option needs to be passed in `create_graph`. |
| 117 | + |
| 118 | +Most utility functions like `changeNodeProperty` or `highlightNode` or `animate_*` use Javis actions underneath in the implementations which makes it easy to arrange them via frames. |
| 119 | + |
| 120 | +**Using `animate_neighbors`** |
| 121 | + |
| 122 | +Animating neighboring nodes and edges can be simplified by using the `animate_neighbors` utlity function |
| 123 | + |
| 124 | +```julia |
| 125 | +animate_neighbors(ag, 1; animate_node_on=(:fill_color, "green"), animate_edge_on=(:color, "red")) |
| 126 | +``` |
| 127 | +This internally makes a call to `changeNodeProperty` and `changeEdgeProperty`. If the keyword arguments are not provided, simple switching highlighting is used for the animation. |
| 128 | + |
| 129 | +**Using `animate_path`** |
| 130 | + |
| 131 | +After the traversal has been done and the shortest path found, show the path by animating it. |
| 132 | + |
| 133 | +```julia |
| 134 | +# change back the graph to its original form |
| 135 | +changeNodeProperty!(ag, (node)->true, :fill_color, "white") |
| 136 | +changeEdgeProperty!(ag, (nodes...)->true, :color, "black") |
| 137 | +animate_path(ag, path; animate_node_on=(:fill_color, "green"), animate_edge_on=(:color, "red")) |
| 138 | + |
| 139 | +render(video; pathname="tutorial_1.gif") |
| 140 | +``` |
| 141 | + |
| 142 | +In the default case, when nothing is specified about how to animate nodes/edges in a path simple on/off highlighting is used on the universal property `:opacity`. |
0 commit comments