-
-
Notifications
You must be signed in to change notification settings - Fork 73
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Support nested vertex groups (subgraphs) #36
Changes from 6 commits
be85afb
19ce548
190eeba
7a0d0fd
1ca74f0
986643f
8dee7da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -299,33 +299,35 @@ public function createScript() | |
$gid = 0; | ||
$indent = str_repeat($this->formatIndent, 2); | ||
// put each group of vertices in a separate subgraph cluster | ||
foreach ($alg->getGroups() as $group) { | ||
$script .= $this->formatIndent . 'subgraph cluster_' . $gid++ . ' {' . self::EOL . | ||
$indent . 'label = ' . $this->escape($group) . self::EOL; | ||
foreach($alg->getVerticesGroup($group) as $vid => $vertex) { | ||
$layout = $this->getLayoutVertex($vertex); | ||
|
||
$script .= $indent . $this->escapeId($vid); | ||
if($layout){ | ||
$script .= ' ' . $this->escapeAttributes($layout); | ||
} | ||
$script .= self::EOL; | ||
$groups = $alg->getGroups(); | ||
$tree = array(); | ||
foreach ($groups as $group) { | ||
$fragments = explode(":", $group); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should probably add a configuration option to change the separation character. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would need an alien GraphViz setting (that is not in http://www.graphviz.org/content/attrs) for group separator as Graphviz.php produced a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Currently, there's no way to set |
||
$pointer = &$tree; | ||
while (count($fragments)) { | ||
$key = array_shift($fragments); | ||
if (!isset($pointer[$key])) { | ||
$pointer[$key] = array(); | ||
} | ||
$script .= ' }' . self::EOL; | ||
$pointer = &$pointer[$key]; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Really interesting concept! 👍 Perhaps this should be moved to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you mean? The Cluster concept? That would indeed be nice. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, perhaps a |
||
} | ||
} else { | ||
// explicitly add all isolated vertices (vertices with no edges) and vertices with special layout set | ||
// other vertices wil be added automatically due to below edge definitions | ||
foreach ($this->graph->getVertices() as $vid => $vertex){ | ||
$layout = $this->getLayoutVertex($vertex); | ||
|
||
if($vertex->isIsolated() || $layout){ | ||
$script .= $this->formatIndent . $this->escapeId($vid); | ||
if($layout){ | ||
$script .= ' ' . $this->escapeAttributes($layout); | ||
} | ||
$script .= self::EOL; | ||
foreach( $tree as $group => $subtree) { | ||
$this->printCluster($group, $subtree, $alg, $script); | ||
} | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose we still need this block.. Otherwise the vertices end up being listed twice: once in their subgraph and once in the main graph. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nope. Vertices must be listed at full after the clustering which only have their IDs listed. But this is WIP as I have to add a real layout test still :( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd love to see some insight onto why this is a must. Perhaps this will be clear once we have those layout tests 👍 If possible I'd like the output to be as concise as possible and not contain any duplication. |
||
|
||
// explicitly add all isolated vertices (vertices with no edges) and vertices with special layout set | ||
// other vertices wil be added automatically due to below edge definitions | ||
foreach ($this->graph->getVertices() as $vid => $vertex){ | ||
$layout = $this->getLayoutVertex($vertex); | ||
|
||
if($vertex->isIsolated() || $layout){ | ||
$script .= $this->formatIndent . $this->escapeId($vid); | ||
if($layout){ | ||
$script .= ' ' . $this->escapeAttributes($layout); | ||
} | ||
$script .= self::EOL; | ||
} | ||
} | ||
|
||
|
@@ -356,6 +358,24 @@ public function createScript() | |
return $script; | ||
} | ||
|
||
protected function printCluster($group, $tree, $alg, &$script, $depth = 1) { | ||
if ($group == "_NULL_") { | ||
return; | ||
} | ||
$script .= str_repeat($this->formatIndent, $depth) . 'subgraph cluster_' . str_replace(":", "_", $group) . ' {' . self::EOL; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
IMO however, it can be removed completely. The old way was to use just incrementing numbers in order to avoid clashes. The actual group name is added as a subgraph label anyway, so I don't think the real subgraph name is even needed? Any thoughts? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point about clashes. But is a_1 somehow related to cluster_a_1? We do have (or at least should add) a constaint on group IDs There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous constraint was forcing it to be an integer 😉 So whatever we can come up with, we've already improved that situation. If possible however, I'd like to not enforce any constraints. As far as I can tell this should not be an issue: The subgraph name will only be used in the dot file and will not be shown in the final image file, so it should probably be okay to use an arbitrary/random identifier and make sure to add the real group ID as a subgraph label (which is already done one line below). |
||
$script .= str_repeat($this->formatIndent, $depth + 1) . 'label = ' . $this->escape($group) . self::EOL; | ||
foreach($alg->getVerticesGroup($group) as $vid => $vertex) { | ||
$script .= str_repeat($this->formatIndent, $depth + 1) . $this->escape($vid); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation should probably not be evaluated for every single line :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. True ... will fix that later on. |
||
$script .= self::EOL; | ||
} | ||
if (!empty($tree)) { | ||
foreach($tree as $sub_group => $sub_tree) { | ||
$this->printCluster($group . ':' . $sub_group, $sub_tree, $alg, $script, $depth + 1); | ||
} | ||
} | ||
$script .= str_repeat($this->formatIndent, $depth) . '}' . self::EOL; | ||
} | ||
|
||
/** | ||
* escape given id string and wrap in quotes if needed | ||
* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
<?php | ||
|
||
use Fhaculty\Graph\Algorithm\Groups as Groups; | ||
use Fhaculty\Graph\Graph; | ||
|
||
class GroupTest extends TestCase | ||
{ | ||
public function testGroupsTwo() | ||
{ | ||
$graph = new Graph(); | ||
$graph->createVertex('no group'); | ||
$graph->createVertex('group 0')->setGroup(0); | ||
|
||
$alg = new Groups($graph); | ||
$this->assertEquals(2, $alg->getNumberOfGroups(), 'Empty group must differ from 0'); | ||
|
||
} | ||
|
||
public function testGroupsOneAndTwo() | ||
{ | ||
|
||
$graph = new Graph(); | ||
$graph->createVertex('group 1')->setGroup(1); | ||
$graph->createVertex('group 2.1')->setGroup(2); | ||
$graph->createVertex('group 2.2')->setGroup(2); | ||
|
||
$alg = new Groups($graph); | ||
$this->assertEquals(2, $alg->getNumberOfGroups()); | ||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// Graph has a name (tooltip in SVG output) | ||
graph Faculty { | ||
// cluster_ is a special prefix making it a box with a label | ||
subgraph cluster_graph { | ||
label = "<<package>>\nGraph" | ||
|
||
graph_php [ | ||
label = "Graph.php" | ||
] | ||
|
||
// Note the absence of cluster_ making their groupness hard to tell | ||
subgraph algorithm { | ||
label = "<<package>>\nAlgorithm" | ||
subgraph cluster_maxflow { | ||
label = "<<package>>\nMaxFlow" | ||
EdmondsKarp | ||
} | ||
AlgorithmX -- AlgorithmY | ||
} | ||
subgraph cluster_edge { | ||
label = "<<package>>\nEdge" | ||
P | ||
Q | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
graph G { | ||
subgraph cluster_A { | ||
label = "A" | ||
"n1" | ||
} | ||
subgraph cluster_B { | ||
label = "B" | ||
"n21" | ||
subgraph cluster_B_A { | ||
label = "B:A" | ||
"n211" | ||
"n212" | ||
subgraph cluster_B_A_A { | ||
label = "B:A:A" | ||
"n2111" | ||
} | ||
} | ||
} | ||
"n1" | ||
"n21" | ||
"n211" | ||
"n212" | ||
"n2111" | ||
"n3" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
graph G { | ||
subgraph cluster_A { | ||
label = "A" | ||
"n1" | ||
} | ||
subgraph cluster_B { | ||
label = "B" | ||
"n2" | ||
subgraph cluster_B_A { | ||
label = "B:A" | ||
"n21" | ||
"n22" | ||
} | ||
} | ||
"n1" | ||
"n2" | ||
"n21" | ||
"n22" | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
This directory is used to generate output from some fixtures for visual inspection. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
graph G { | ||
subgraph cluster_A { | ||
label = "A" | ||
"n1" | ||
} | ||
subgraph cluster_B { | ||
label = "B" | ||
"n21" | ||
subgraph cluster_B_A { | ||
label = "B:A" | ||
"n211" | ||
"n212" | ||
subgraph cluster_B_A_A { | ||
label = "B:A:A" | ||
"n2111" | ||
} | ||
} | ||
} | ||
"n1" | ||
"n21" | ||
"n211" | ||
"n212" | ||
"n2111" | ||
"n3" | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor nitpick regarding code-style:
Also, if the default group is
null
, it should come out as such (if at all possible). We also have to make sure to handle the case when a group with the literal name"_NULL_"
does already exist. We should not merge them with the vertices with no group at all (null
).There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When having no group the vertex is not part of any group :)
_NULL_
is arbitrary but the group IDs must follow a patternA:B:C
that is using a:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a user, I'd expect to receive:
I guess we may a different understanding here?
[edit] fixed invalid format for var_dump()