Commit 3e98d2f9 authored by Lucas Bourneuf's avatar Lucas Bourneuf

handle node/1 as single node

parent 1cbf87b5
...@@ -9,10 +9,11 @@ from biseau import utils ...@@ -9,10 +9,11 @@ from biseau import utils
RANK_TYPES = {'same', 'min', 'source', 'max', 'sink'} RANK_TYPES = {'same', 'min', 'source', 'max', 'sink'}
DOTABLE_PREDICATES = {'link', 'color', 'shape', 'label', 'annot', 'dot_property', 'obj_property', 'textwrap', 'rank'} DOTABLE_PREDICATES = {'link', 'node', 'color', 'shape', 'label', 'annot', 'dot_property', 'obj_property', 'textwrap', 'rank'}
VisualConfig = namedtuple('VisualConfig', 'arcs, properties, upper_annotations, lower_annotations, global_properties, ranks') VisualConfig = namedtuple('VisualConfig', 'arcs, nodes, properties, upper_annotations, lower_annotations, global_properties, ranks')
""" """
arcs -- iterable of 2-uplet (source's uid, target's uid) arcs -- iterable of 2-uplet (source's uid, target's uid)
nodes -- set of nodes that may or may not be used by arcs
properties -- map uid -> (field -> value) and (uid, uid) -> (field -> value) properties -- map uid -> (field -> value) and (uid, uid) -> (field -> value)
upper_annotations -- map uid -> {field -> value} specialized for annotations upper_annotations -- map uid -> {field -> value} specialized for annotations
lower_annotations -- map uid -> {field -> value} specialized for annotations lower_annotations -- map uid -> {field -> value} specialized for annotations
...@@ -55,7 +56,7 @@ def visual_config_from_asp(asp_models:iter, annotation_sep:str=' ') -> [VisualCo ...@@ -55,7 +56,7 @@ def visual_config_from_asp(asp_models:iter, annotation_sep:str=' ') -> [VisualCo
def visual_config_from_atoms(atoms:dict, base_atoms:dict, def visual_config_from_atoms(atoms:dict, base_atoms:dict,
annotation_sep:str) -> VisualConfig: annotation_sep:str) -> VisualConfig:
arcs = [] arcs, nodes = [], set()
upper_annotations = defaultdict(lambda: defaultdict(set)) upper_annotations = defaultdict(lambda: defaultdict(set))
lower_annotations = defaultdict(lambda: defaultdict(set)) lower_annotations = defaultdict(lambda: defaultdict(set))
properties = defaultdict(lambda: defaultdict(set)) # node -> (property -> {value}) properties = defaultdict(lambda: defaultdict(set)) # node -> (property -> {value})
...@@ -82,6 +83,9 @@ def visual_config_from_atoms(atoms:dict, base_atoms:dict, ...@@ -82,6 +83,9 @@ def visual_config_from_atoms(atoms:dict, base_atoms:dict,
for link in get_atoms_of_predicate('link'): for link in get_atoms_of_predicate('link'):
if len(link) == 2: if len(link) == 2:
arcs.append(tuple(map(get_uid_from_atom, link))) arcs.append(tuple(map(get_uid_from_atom, link)))
for node in get_atoms_of_predicate('node'):
if len(node) == 1:
nodes.add(tuple(map(get_uid_from_atom, node))[0])
for args in get_atoms_of_predicate('textwrap'): for args in get_atoms_of_predicate('textwrap'):
if len(args) == 1: # global value if len(args) == 1: # global value
max_label_width[None] = int(args[0]) max_label_width[None] = int(args[0])
...@@ -163,7 +167,6 @@ def visual_config_from_atoms(atoms:dict, base_atoms:dict, ...@@ -163,7 +167,6 @@ def visual_config_from_atoms(atoms:dict, base_atoms:dict,
# posttreat the data for later use # posttreat the data for later use
arcs = tuple(arcs) arcs = tuple(arcs)
nodes = frozenset(properties.keys())
def treat_texts(texts:iter, node, max_label_width=max_label_width) -> str: def treat_texts(texts:iter, node, max_label_width=max_label_width) -> str:
ret = annotation_sep.join(map(get_uid_from_atom, texts)).strip('"') ret = annotation_sep.join(map(get_uid_from_atom, texts)).strip('"')
text_width = max_label_width.get(node, max_label_width.get(None)) text_width = max_label_width.get(node, max_label_width.get(None))
...@@ -203,6 +206,6 @@ def visual_config_from_atoms(atoms:dict, base_atoms:dict, ...@@ -203,6 +206,6 @@ def visual_config_from_atoms(atoms:dict, base_atoms:dict,
properties[key][field] = treat_texts(properties[key][field], key) properties[key][field] = treat_texts(properties[key][field], key)
return VisualConfig( return VisualConfig(
arcs, dict(properties), dict(upper_annotations), dict(lower_annotations), arcs, frozenset(nodes), dict(properties), dict(upper_annotations), dict(lower_annotations),
dict(global_properties), ranks dict(global_properties), ranks
) )
...@@ -60,7 +60,7 @@ def _from_config(visual_config:VisualConfig) -> [str]: ...@@ -60,7 +60,7 @@ def _from_config(visual_config:VisualConfig) -> [str]:
"""Yield lines of dot's graph describing the given VisualConfig instance. """Yield lines of dot's graph describing the given VisualConfig instance.
""" """
arcs, properties, upper_annotations, lower_annotations, globals_props, ranks = visual_config arcs, nodes, properties, upper_annotations, lower_annotations, globals_props, ranks = visual_config
for object, props in globals_props.items(): for object, props in globals_props.items():
yield '\t{}{};\n'.format(object, _dot_from_properties(props)) yield '\t{}{};\n'.format(object, _dot_from_properties(props))
if 'node' not in globals_props: if 'node' not in globals_props:
...@@ -68,25 +68,34 @@ def _from_config(visual_config:VisualConfig) -> [str]: ...@@ -68,25 +68,34 @@ def _from_config(visual_config:VisualConfig) -> [str]:
if 'edge' not in globals_props: if 'edge' not in globals_props:
yield '\tedge [arrowhead=none labeldistance=1.5 minlen=2]\n' yield '\tedge [arrowhead=none labeldistance=1.5 minlen=2]\n'
treated_nodes = set() # contains nodes already treated treated_nodes = set() # contains nodes already treated
for node in nodes:
if node not in treated_nodes:
treated_nodes.add(node)
yield from _dot_from_node(node, visual_config)
for source, target in arcs: for source, target in arcs:
for node in source, target: for node in source, target:
if node not in treated_nodes: if node not in treated_nodes:
treated_nodes.add(node) treated_nodes.add(node)
node_dot_props = _dot_from_properties(properties.get(node)) yield from _dot_from_node(node, visual_config)
if node_dot_props:
yield '\t{n}{d}\n'.format(n=node, d=node_dot_props)
if lower_annotations.get(node):
yield '\t{n} -> {n} {d}\n'.format(
n=node, d=_dot_from_properties(lower_annotations[node]))
if upper_annotations.get(node):
yield '\t{n} -> {n} {d}\n'.format(
n=node, d=_dot_from_properties(upper_annotations[node]))
yield '\t{}->{}{}\n'.format(source, target, _dot_from_properties(properties.get((source, target)))) yield '\t{}->{}{}\n'.format(source, target, _dot_from_properties(properties.get((source, target))))
# build ranks # build ranks
for ranktype, nodes in ranks.items(): for ranktype, nodes in ranks.items():
yield '\t{{rank={}; {}}}\n'.format(ranktype, ';'.join(nodes)) yield '\t{{rank={}; {}}}\n'.format(ranktype, ';'.join(nodes))
def _dot_from_node(node, visual_config) -> [str]:
"Yield dot content describing given node in given visual_config"
arcs, nodes, properties, upper_annotations, lower_annotations, globals_props, ranks = visual_config
node_dot_props = _dot_from_properties(properties.get(node))
yield '\t{n}{d}\n'.format(n=node, d=node_dot_props)
if lower_annotations.get(node):
yield '\t{n} -> {n} {d}\n'.format(
n=node, d=_dot_from_properties(lower_annotations[node]))
if upper_annotations.get(node):
yield '\t{n} -> {n} {d}\n'.format(
n=node, d=_dot_from_properties(upper_annotations[node]))
def dot_to_png(dot_lines:iter, outfile:str, dotfile:str=DEFAULT_DOT_FILE, def dot_to_png(dot_lines:iter, outfile:str, dotfile:str=DEFAULT_DOT_FILE,
prog:str=DEFAULT_PROG_FILE): prog:str=DEFAULT_PROG_FILE):
"""Write in outfile a png render of the graph described in given dot lines""" """Write in outfile a png render of the graph described in given dot lines"""
......
...@@ -10,3 +10,12 @@ def test_basic_visual_config(): ...@@ -10,3 +10,12 @@ def test_basic_visual_config():
}, ' ') }, ' ')
assert set(config.arcs) == {('a', 'b'), ('c', 'd')} assert set(config.arcs) == {('a', 'b'), ('c', 'd')}
assert config.properties == {'a': {'fillcolor': 'red'}} assert config.properties == {'a': {'fillcolor': 'red'}}
def test_basic_visual_config():
config = asp_to_dot.visual_config_from_atoms({}, {
'link': {('a', 'b'), ('c', 'd')},
'node': {('e',)},
}, ' ')
assert set(config.arcs) == {('a', 'b'), ('c', 'd')}
assert set(config.nodes) == {'e'}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment