Skip to content
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

Improve subgraph support #23

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 75 additions & 33 deletions src/GraphViz.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Graphp\GraphViz;

use Fhaculty\Graph\Set\Vertices;
use Graphp\Algorithms\Directed;
use Graphp\Algorithms\Groups;
use Graphp\Algorithms\Degree;
Expand Down Expand Up @@ -212,6 +213,57 @@ public function createImageFile(Graph $graph)
return $tmp . '.' . $this->format;
}

/**
* @param Vertex $vertex
* @return bool
*/
protected function vertexRequiresScript(Vertex $vertex)
{
$graph = $vertex->getGraph();
$degree = new Degree($graph);
$layout = $this->getLayoutVertex($vertex);
// If isolated (no edges) or has layout attributes, vertex cannot be fully defined
// via related edge definitions
return $layout || $degree->isVertexIsolated($vertex);
}

/**
* @param Vertex $vertex
* @param string $indent
* @return string
*/
protected function createVertexScript(Vertex $vertex, $indent = '')
{
$vid = $vertex->getId();
$script = $indent . $this->escapeId($vid);
$layout = $this->getLayoutVertex($vertex);
if($layout){
$script .= ' ' . $this->escapeAttributes($layout);
}
$script .= self::EOL;
return $script;
}

/**
* @param $id
* @param Vertices $vertices
* @param string $indent
* @return string
*/
protected function createSubgraphScript($id, Vertices $vertices, $indent = '')
{
$script = $this->formatIndent . 'subgraph cluster_' . $id . ' {' . self::EOL .
$indent . 'label = ' . $this->escape($id) . self::EOL;

foreach($vertices->getMap() as $vid => $vertex) {
if($this->vertexRequiresScript($vertex)){
$script .= $this->createVertexScript($vertex, $indent);
}
}
$script .= ' }' . self::EOL;
return $script;
}

/**
* create graphviz script representing this graph
*
Expand Down Expand Up @@ -243,49 +295,39 @@ public function createScript(Graph $graph)
}
}

// Handle grouped vertices
$alg = new Groups($graph);
// only append group number to vertex label if there are at least 2 different groups
$showGroups = ($alg->getNumberOfGroups() > 1);
$groups = $alg->getGroups();
$showGroups = count($groups) > 1;
$indent = str_repeat($this->formatIndent, 2);
foreach ($alg->getGroups() as $group) {
if (!$showGroups || $group === null) { // TODO: Remove if(!$showGroups) condition when Vertex::group is made nullable
continue; // Group is not set, skip
}
$vertices = $alg->getVerticesGroup((string)$group); // getGroups returns array_keys()
$script .= $this->createSubgraphScript($group, $vertices, $indent);
}

// Handle ungrouped vertices
// TODO: Remove if($showGroups) branch when Vertex::group is made nullable
if ($showGroups) {
$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)->getMap() as $vid => $vertex) {
$layout = $this->getLayoutVertex($vertex);

$script .= $indent . $this->escapeId($vid);
if($layout){
$script .= ' ' . $this->escapeAttributes($layout);
}
$script .= self::EOL;
}
$script .= ' }' . self::EOL;
}
$group = end($groups);
$vertices = $graph->getVertices()->getVerticesMatch(function (Vertex $vertex) use ($group) {
return $vertex->getGroup() === null;
});
} else {
$alg = new Degree($graph);

// 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 ($graph->getVertices()->getMap() as $vid => $vertex){
$layout = $this->getLayoutVertex($vertex);

if($layout || $alg->isVertexIsolated($vertex)){
$script .= $this->formatIndent . $this->escapeId($vid);
if($layout){
$script .= ' ' . $this->escapeAttributes($layout);
}
$script .= self::EOL;
}
$vertices = $graph->getVertices();
}
foreach ($vertices as $vertex) {
if ($this->vertexRequiresScript($vertex)) {
$script .= $this->createVertexScript($vertex);
}
}

$edgeop = $directed ? ' -> ' : ' -- ';

// add all edges as directed edges
// TODO: Sort edges originating in subgraphs into relevant subgraphs definitions
foreach ($graph->getEdges() as $currentEdge) {
$both = $currentEdge->getVertices()->getVector();
$currentStartVertex = $both[0];
Expand Down