Skip to content

Commit

Permalink
Optional topologically sorted graph.
Browse files Browse the repository at this point in the history
When enabling this option (e.g., in the graphviewer), the references
are arranged according to the topological ordering from the model
(that is also used for ordering the witnesses in the edition).

Note that this works by introducing additional, invisible edges, so
you shouldn’t use the graph or dot file produced using this method for
anything else than visualization purposes.
  • Loading branch information
thvitt committed Jul 19, 2020
1 parent 192c8ab commit cd640db
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/graphviewer/graphviewer.py
Expand Up @@ -31,7 +31,6 @@
class NoNodes(ValueError):
pass


def prepare_agraph():
node_str = request.values.get('nodes')
model = request.values.get('model', 'default')
Expand All @@ -52,6 +51,7 @@ def prepare_agraph():
tred = request.values.get('tred', False)
nohl = request.values.get('nohl', False)
syn = request.values.get('syn', False)
order = request.values.get('order', False)
if nodes:
g = info.subgraph(*nodes, context=context, abs_dates=abs_dates, paths=extra, keep_timeline=True,
paths_without_timeline=paths_wo_timeline,
Expand All @@ -72,8 +72,12 @@ def prepare_agraph():
flash('Cannot produce DAG – subgraph is not acyclic!?', 'error')
g = simplify_timeline(g)
g.add_nodes_from(nodes)
if order:
g = info.order_graph(g)
agraph = write_dot(g, target=None, highlight=None if nohl else nodes, edge_labels=not no_edge_labels)
agraph.graph_attr['basename'] = ",".join([str(node.filename.stem if hasattr(node, 'filename') else node) for node in nodes])
if order:
agraph.graph_attr['ranksep'] = '0.2'
return agraph
else:
raise NoNodes('No nodes in graph')
Expand Down
1 change: 1 addition & 0 deletions src/graphviewer/templates/form.html
Expand Up @@ -97,6 +97,7 @@ <h3>Kantenauswahl und -gestaltung:</h3>
<p><input type="checkbox" name="syn" id="syn" {% if syn %}checked{% endif %}> <label for="syn">Kanten für »ungefähr gleichzeitig«</label> </p>
<p><input type="checkbox" name="tred" id="tred" {% if tred %}checked{% endif %}> <label for="tred">Transitive Reduktion</label> </p>
<p><input type="checkbox" name="no_edge_labels" id="no_edge_labels" {% if no_edge_labels %}checked{% endif %}> <label for="no_edge_labels">keine Kantenbeschriftung</label> </p>
<p><input type="checkbox" name="order" id="order" {% if order %}checked{% endif %}> <label for="order">Topologische Sortierung</label></p>
</form>

{% endblock %}
Expand Down
25 changes: 25 additions & 0 deletions src/macrogen/graph.py
Expand Up @@ -19,6 +19,7 @@
from dataclasses import dataclass

from macrogen.graphutils import is_orphan, find_reachable_by_edge
from more_itertools import windowed

from .graphutils import mark_edges_to_delete, remove_edges, in_path, first
from .bibliography import BiblSource
Expand Down Expand Up @@ -738,6 +739,29 @@ def subgraph(self, *nodes: Node, context: bool = True, path_to: Iterable[Node] =
return subgraph


def order_graph(self, graph: nx.MultiDiGraph) -> nx.MultiDiGraph:
"""
This returns a version of the given graph that will be ordered left to right by sort order when layed out
using dot.
"""
# noinspection PyTypeChecker -- graph.copy() will return the correct type
result: nx.MultiDiGraph = graph.copy()
ref_order = [ref for ref in self.order_refs() if ref in result.nodes]
for u, v, k, attr in result.edges(keys=True, data=True):
attr['weight'] = 0

for u, v in windowed(ref_order, 2):
if result.has_edge(u, v, 0):
result[u][v][0]['weight'] = 1
else:
result.add_edge(u, v, penwidth=0, dir='none', weight=1)
return result






def macrogenesis_graphs() -> MacrogenesisInfo:
warn("macrogenesis_graphs() is deprecated, instantiate MacrogenesisInfo directly instead", DeprecationWarning)
return MacrogenesisInfo()
Expand Down Expand Up @@ -1030,3 +1054,4 @@ def stat(self): # TODO better name
involved_cycles=len(self.involved_cycles),
removed_source_count=len(self.removed_sources)
)

0 comments on commit cd640db

Please sign in to comment.