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

Streamline Graphviz Edge/Vertex label output #32

Merged
merged 8 commits into from
May 31, 2013
Merged
Show file tree
Hide file tree
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ you spot any mistakes.

* BC break: `Graph::createVertices()` now returns an array of vertices instead of the chainable `Graph` (#19)
* Feature: `Graph::createVertices()` now also accepts an array of vertex IDs (#19)
* Fix: Various issues with `Vertex`/`Edge` layout attributes (#32)

## 0.5.0 (2013-05-07)

Expand Down
143 changes: 80 additions & 63 deletions lib/Fhaculty/Graph/GraphViz.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Fhaculty\Graph\Algorithm\Groups;
use Fhaculty\Graph\Exception\UnexpectedValueException;
use Fhaculty\Graph\Exception\InvalidArgumentException;
use Fhaculty\Graph\Edge\Base as Edge;
use \stdClass;

class GraphViz
Expand Down Expand Up @@ -34,6 +35,14 @@ class GraphViz
*/
private $executable = 'dot';

/**
* string to use as indentation for dot output
*
* @var string
* @see GraphViz::createScript()
*/
private $formatIndent = ' ';

const DELAY_OPEN = 2.0;

const EOL = PHP_EOL;
Expand Down Expand Up @@ -273,13 +282,13 @@ public function createScript()
// add global attributes
$layout = $this->graph->getLayout();
if ($layout) {
$script .= ' graph ' . $this->escapeAttributes($layout) . self::EOL;
$script .= $this->formatIndent . 'graph ' . $this->escapeAttributes($layout) . self::EOL;
}
if ($this->layoutVertex) {
$script .= ' node ' . $this->escapeAttributes($this->layoutVertex) . self::EOL;
$script .= $this->formatIndent . 'node ' . $this->escapeAttributes($this->layoutVertex) . self::EOL;
}
if ($this->layoutEdge) {
$script .= ' edge ' . $this->escapeAttributes($this->layoutEdge) . self::EOL;
$script .= $this->formatIndent . 'edge ' . $this->escapeAttributes($this->layoutEdge) . self::EOL;
}

$alg = new Groups($this->graph);
Expand All @@ -288,25 +297,15 @@ public function createScript()

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 .= ' subgraph cluster_' . $gid++ . ' {' . self::EOL .
' label = ' . $this->escape($group) . self::EOL;
$script .= $this->formatIndent . 'subgraph cluster_' . $gid++ . ' {' . self::EOL .
$indent . 'label = ' . $this->escape($group) . self::EOL;
foreach($alg->getVerticesGroup($group) as $vid => $vertex) {
$layout = $vertex->getLayout();

$balance = $vertex->getBalance();
if($balance !== NULL){
if($balance > 0){
$balance = '+' . $balance;
}
if(!isset($layout['label'])){
$layout['label'] = $vid;
}
$layout['label'] .= ' (' . $balance . ')';
}
$layout = $this->getLayoutVertex($vertex);

$script .= ' ' . $this->escapeId($vid);
$script .= $indent . $this->escapeId($vid);
if($layout){
$script .= ' ' . $this->escapeAttributes($layout);
}
Expand All @@ -318,21 +317,10 @@ public function createScript()
// 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 = $vertex->getLayout();

$balance = $vertex->getBalance();
if($balance !== NULL){
if($balance > 0){
$balance = '+' . $balance;
}
if(!isset($layout['label'])){
$layout['label'] = $vid;
}
$layout['label'] .= ' (' . $balance . ')';
}
$layout = $this->getLayoutVertex($vertex);

if($vertex->isIsolated() || $layout){
$script .= ' ' . $this->escapeId($vid);
$script .= $this->formatIndent . $this->escapeId($vid);
if($layout){
$script .= ' ' . $this->escapeAttributes($layout);
}
Expand All @@ -349,43 +337,16 @@ public function createScript()
$currentStartVertex = $both[0];
$currentTargetVertex = $both[1];

$script .= ' ' . $this->escapeId($currentStartVertex->getId()) . $edgeop . $this->escapeId($currentTargetVertex->getId());

$attrs = $currentEdge->getLayout();
$script .= $this->formatIndent . $this->escapeId($currentStartVertex->getId()) . $edgeop . $this->escapeId($currentTargetVertex->getId());

// use flow/capacity/weight as edge label
$label = NULL;
$layout = $this->getLayoutEdge($currentEdge);

$flow = $currentEdge->getFlow();
$capacity = $currentEdge->getCapacity();
// flow is set
if ($flow !== NULL) {
// NULL capacity = infinite capacity
$label = $flow . '/' . ($capacity === NULL ? '∞' : $capacity);
// capacity set, but not flow (assume zero flow)
} elseif ($capacity !== NULL) {
$label = '0/' . $capacity;
}

$weight = $currentEdge->getWeight();
// weight is set
if ($weight !== NULL) {
if ($label === NULL) {
$label = $weight;
} else {
$label .= '/' . $weight;
}
}

if ($label !== NULL) {
$attrs['label'] = $label;
}
// this edge also points to the opposite direction => this is actually an undirected edge
if ($directed && $currentEdge->isConnection($currentTargetVertex, $currentStartVertex)) {
$attrs['dir'] = 'none';
$layout['dir'] = 'none';
}
if ($attrs) {
$script .= ' ' . $this->escapeAttributes($attrs);
if ($layout) {
$script .= ' ' . $this->escapeAttributes($layout);
}

$script .= self::EOL;
Expand Down Expand Up @@ -457,4 +418,60 @@ public static function raw($string)
{
return (object) array('string' => $string);
}

protected function getLayoutVertex(Vertex $vertex)
{
$layout = $vertex->getLayout();

$balance = $vertex->getBalance();
if($balance !== NULL){
if($balance > 0){
$balance = '+' . $balance;
}
if(!isset($layout['label'])){
$layout['label'] = $vertex->getId();
}
$layout['label'] .= ' (' . $balance . ')';
}

return $layout;
}

protected function getLayoutEdge(Edge $edge)
{
$layout = $edge->getLayout();

// use flow/capacity/weight as edge label
$label = NULL;

$flow = $edge->getFlow();
$capacity = $edge->getCapacity();
// flow is set
if ($flow !== NULL) {
// NULL capacity = infinite capacity
$label = $flow . '/' . ($capacity === NULL ? '∞' : $capacity);
// capacity set, but not flow (assume zero flow)
} elseif ($capacity !== NULL) {
$label = '0/' . $capacity;
}

$weight = $edge->getWeight();
// weight is set
if ($weight !== NULL) {
if ($label === NULL) {
$label = $weight;
} else {
$label .= '/' . $weight;
}
}

if ($label !== NULL) {
if (isset($layout['label'])) {
$layout['label'] .= ' ' . $label;
} else {
$layout['label'] = $label;
}
}
return $layout;
}
}
Loading