Skip to content

Commit

Permalink
Add GraphViz renderer.
Browse files Browse the repository at this point in the history
  • Loading branch information
drupol committed Dec 9, 2018
1 parent fd67e47 commit 0727216
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 0 deletions.
51 changes: 51 additions & 0 deletions spec/drupol/phptree/tests/TestGraphVizSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php

declare(strict_types = 1);

namespace spec\drupol\phptree\tests;

use drupol\phptree\Node\ValueNode;
use drupol\phptree\tests\TestGraphViz;
use drupol\phptree\Visitor\BreadthFirstVisitor;
use PhpSpec\ObjectBehavior;

class TestGraphVizSpec extends ObjectBehavior
{
public function it_is_initializable()
{
$visitor = new BreadthFirstVisitor();

$this->beConstructedWith($visitor);

$this->shouldHaveType(TestGraphViz::class);
}

public function it_can_create_a_graph()
{
$this->beConstructedWith(new BreadthFirstVisitor());

$tree = new ValueNode('root', 2);

$nodes = [];
foreach (\range('A', 'F') as $letter) {
$nodes[] = new ValueNode($letter, 2);
}

$tree->add(...$nodes);

$result = <<< EOF
digraph G {
"root" -> "A"
"root" -> "B"
"A" -> "C"
"A" -> "D"
"B" -> "E"
"B" -> "F"
}
EOF;

$this
->render($tree)
->shouldReturn($result . PHP_EOL);
}
}
22 changes: 22 additions & 0 deletions spec/src/TestGraphViz.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?php

declare(strict_types = 1);

namespace drupol\phptree\tests;

use drupol\phptree\Node\NodeInterface;
use drupol\phptree\Render\GraphViz;

/**
* Class TestGraphViz
*/
class TestGraphViz extends GraphViz
{
/**
* {@inheritdoc}
*/
protected function hash(NodeInterface $node)
{
return $node->getValue();
}
}
88 changes: 88 additions & 0 deletions src/Render/GraphViz.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
<?php

declare(strict_types = 1);

namespace drupol\phptree\Render;

use drupol\phptree\Node\NodeInterface;
use drupol\phptree\Visitor\VisitorInterface;
use Fhaculty\Graph\Graph;

/**
* Class GraphViz
*/
class GraphViz implements RendererInterface
{
/**
* @var \drupol\phptree\Visitor\VisitorInterface
*/
private $visitor;

/**
* @var \Fhaculty\Graph\Graph
*/
private $graph;

/**
* @var array
*/
private $nodes;

/**
* @var \Graphp\GraphViz\GraphViz
*/
private $graphviz;

/**
* GraphViz constructor.
*
* @param \drupol\phptree\Visitor\VisitorInterface $visitor
*/
public function __construct(VisitorInterface $visitor)
{
$this->visitor = $visitor;
$this->graph = new Graph();
$this->graphviz = new \Graphp\GraphViz\GraphViz();
$this->nodes = [];
}

/**
* @param \drupol\phptree\Node\NodeInterface $node
*
* @return string
*/
public function render(NodeInterface $node): string
{
foreach ($this->visitor->traverse($node) as $child) {
$hash_parent = $this->hash($child);

if (!isset($this->nodes[$hash_parent])) {
$this->nodes[$hash_parent] = $this->graph->createVertex($hash_parent);
}

if (null === $parent = $child->getParent()) {
continue;
}

$hash = $this->hash($parent);

if (!isset($this->nodes[$hash])) {
$this->nodes[$hash] = $this->graph->createVertex($hash);
}

$this->nodes[$hash]->createEdgeTo($this->nodes[$hash_parent]);
}

return $this->graphviz->createScript($this->graph);
}

/**
* @param \drupol\phptree\Node\NodeInterface $node
*
* @return int
*/
protected function hash(NodeInterface $node)
{
return (int) \spl_object_hash($node);
}
}
18 changes: 18 additions & 0 deletions src/Render/RendererInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

declare(strict_types = 1);

namespace drupol\phptree\Render;

use drupol\phptree\Node\NodeInterface;

/**
* Interface RendererInterface
*/
interface RendererInterface
{
/**
* @param \drupol\phptree\Node\NodeInterface $node
*/
public function render(NodeInterface $node);
}

0 comments on commit 0727216

Please sign in to comment.