Created
November 26, 2015 04:22
-
-
Save gpduck/3367596eb7f1518dbae7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function New-Node { | |
[OutputType("Whatsupduck.Powershell.GraphViz.Node")] | |
param( | |
[Parameter(Mandatory=$true)] | |
[ValidateNotNullOrEmpty()] | |
[String]$Name, | |
[Parameter(Mandatory=$false)] | |
[ValidateNotNullOrEmpty()] | |
[String]$Label, | |
[Parameter(Mandatory=$false)] | |
[Whatsupduck.Powershell.GraphViz.Shapes]$Shape, | |
[Parameter(Mandatory=$false)] | |
[ValidateRange(0,[Double]::MaxValue)] | |
[Double]$Height, | |
[Parameter(Mandatory=$false)] | |
[ValidateRange(0,[Double]::MaxValue)] | |
[Double]$Width | |
) | |
$node = New-Object -TypeName Whatsupduck.Powershell.GraphViz.Node | |
$node.Name = $Name | |
$node | Get-Member -MemberType Property | Select-Object -ExpandProperty Name | %{ | |
if($PSBoundParameters.ContainsKey($_)) { | |
$node."$_" = $PSBoundParameters[$_] | |
} | |
} | |
Write-Output $node | |
} | |
function New-Graph { | |
[OutputType("Whatsupduck.Powershell.GraphViz.Graph")] | |
param( | |
[Parameter(Mandatory=$false)] | |
[ValidateNotNullOrEmpty()] | |
[String]$Name, | |
[Parameter(Mandatory=$false)] | |
[switch]$Directed, | |
[String]$Label, | |
[String]$Color, | |
[Whatsupduck.Powershell.GraphViz.RankDir]$RankDir, | |
[String]$Style, | |
[Switch]$Concentrate | |
) | |
if($Directed) { | |
$Graph = New-Object -TypeName Whatsupduck.Powershell.GraphViz.DirectedGraph | |
} else { | |
$Graph = New-Object -TypeName Whatsupduck.Powershell.GraphViz.UndirectedGraph | |
} | |
$PSBoundParameters.Keys | ?{$_ -ne "Directed"} | %{ | |
$KeyName = $_ | |
$KeyValue = $PSBoundParameters[$KeyName] | |
$Graph."$KeyName" = $KeyValue | |
} | |
Write-Output $Graph | |
} | |
<# | |
.PARAMETER Shape | |
The shape for the node. The default is ellipse. | |
See http://www.graphviz.org/doc/info/shapes.html for a visual depiction of all shapes. | |
#> | |
function Add-Node { | |
[OutputType("Whatsupduck.Powershell.GraphViz.Graph")] | |
param( | |
[Parameter(Mandatory=$true)] | |
[ValidateNotNullOrEmpty()] | |
[String]$Name, | |
[Parameter(Mandatory=$false)] | |
[ValidateNotNullOrEmpty()] | |
[String]$Label, | |
[Parameter(Mandatory=$false)] | |
[Whatsupduck.Powershell.GraphViz.Shapes]$Shape, | |
[Parameter(Mandatory=$false)] | |
[ValidateRange(0,[Double]::MaxValue)] | |
[Double]$Height, | |
[Parameter(Mandatory=$false)] | |
[ValidateRange(0,[Double]::MaxValue)] | |
[Double]$Width, | |
[Parameter(Mandatory=$true,ValueFromPipeline=$true)] | |
[Whatsupduck.Powershell.GraphViz.Graph]$Graph | |
) | |
begin { | |
$NewNodeParams = @{} | |
$PSBoundParameters.Keys | ?{$_ -ne "Graph" } | %{ | |
$NewNodeParams.Add($_, $PSBoundParameters[$_]) | |
} | |
} | |
process { | |
$Graph.AddNode( (New-Node @NewNodeParams)) | |
Write-Output $Graph | |
} | |
} | |
function New-Edge { | |
[OutputType("Whatsupduck.Powershell.GraphViz.Edge")] | |
param( | |
[Parameter(Mandatory=$true,ParameterSetName="DirectedEdge")] | |
[ValidateNotNullOrEmpty()] | |
[String]$SourceName, | |
[Parameter(Mandatory=$true,ParameterSetName="DirectedEdge")] | |
[ValidateNotNullOrEmpty()] | |
[String]$TargetName, | |
[Parameter(Mandatory=$true,ParameterSetName="UndirectedEdge")] | |
[ValidateCount(2,[Int32]::MaxValue)] | |
[String[]]$Names, | |
[Parameter(Mandatory=$false,ParameterSetName="DirectedEdge")] | |
[Parameter(Mandatory=$false,ParameterSetName="UnirectedEdge")] | |
[ValidateNotNullOrEmpty()] | |
[String]$Label | |
) | |
switch ($PSCmdlet.ParameterSetName) { | |
"DirectedEdge" { | |
$Edge = New-Object -TypeName Whatsupduck.Powershell.GraphViz.DirectedEdge | |
$Edge.SourceName = $SourceName | |
$Edge.TargetName = $TargetName | |
if($Label) { | |
$Edge.Label = $Label | |
} | |
Write-Output $Edge | |
} | |
"UndirectedEdge" { | |
$PreviousName = $null | |
$Names | %{ | |
if($PreviousName) { | |
$Edge = New-Object -TypeName Whatsupduck.Powershell.GraphViz.UndirectedEdge | |
$Edge.SourceName = $PreviousName | |
$Edge.TargetName = $_ | |
if($Label) { | |
$Edge.Label = $Label | |
} | |
Write-Output $Edge | |
} | |
$PreviousName = $_ | |
} | |
} | |
default { | |
throw "Unknown parameter set name" | |
} | |
} | |
} | |
function Add-Edge { | |
[OutputType("Whatsupduck.Powershell.GraphViz.Graph")] | |
param( | |
[Parameter(Mandatory=$true,ParameterSetName="DirectedEdge")] | |
[ValidateNotNullOrEmpty()] | |
[String]$SourceName, | |
[Parameter(Mandatory=$true,ParameterSetName="DirectedEdge")] | |
[ValidateNotNullOrEmpty()] | |
[String]$TargetName, | |
[Parameter(Mandatory=$true,ParameterSetName="UndirectedEdge")] | |
[ValidateCount(2,[Int32]::MaxValue)] | |
[String[]]$Names, | |
[Parameter(Mandatory=$false,ParameterSetName="DirectedEdge")] | |
[Parameter(Mandatory=$false,ParameterSetName="UnirectedEdge")] | |
[ValidateNotNullOrEmpty()] | |
[String]$Label, | |
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="DirectedEdge")] | |
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="UndirectedEdge")] | |
[ValidateNotNullOrEmpty()] | |
[Whatsupduck.Powershell.GraphViz.Graph]$Graph | |
) | |
begin { | |
$EdgeParameters = @{} | |
$PSBoundParameters.Keys | ?{$_ -ne "Graph"} | %{ | |
$EdgeParameters.Add($_, $PSBoundParameters[$_]) | |
} | |
} | |
process { | |
if($Graph -is [Whatsupduck.Powershell.GraphViz.UndirectedGraph] -and $PSCmdlet.ParameterSetName -eq "DirectedEdge") { | |
Write-Error "Unable to add a directed edge ($SourceName -> $TargetName) to an undirected graph $(Graph.Name)" | |
} else { | |
New-Edge @EdgeParameters | %{ | |
$Graph.AddEdge($_) | |
} | |
Write-Output $Graph | |
} | |
} | |
} | |
function ConvertTo-Dot { | |
[OutputType("System.String")] | |
param( | |
[Parameter(Mandatory=$true,ValueFromPipeline=$true)] | |
[ValidateNotNullOrEmpty()] | |
[Whatsupduck.Powershell.GraphViz.Graph]$Graph | |
) | |
process { | |
Write-Output $Graph.ToDot(); | |
} | |
} | |
function Out-GraphViz { | |
param( | |
[Parameter(Mandatory=$true,ParameterSetName="FromDot")] | |
[Parameter(Mandatory=$true,ParameterSetName="FromGraph")] | |
[ValidateNotNullOrEmpty()] | |
[String]$Path, | |
[Parameter(Mandatory=$true,ParameterSetName="FromDot")] | |
[ValidateSet("dot","neato","circo","fdp","osage","sfdp","twopi")] | |
[String]$LayoutEngine, | |
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="FromDot")] | |
[ValidateNotNullOrEmpty()] | |
[String]$Dot, | |
[Parameter(Mandatory=$true,ValueFromPipeline=$true,ParameterSetName="FromGraph")] | |
[ValidateNotNullOrEmpty()] | |
[Whatsupduck.Powershell.GraphViz.Graph]$Graph, | |
[Parameter(Mandatory=$false,ParameterSetName="FromDot")] | |
[Parameter(Mandatory=$false,ParameterSetName="FromGraph")] | |
[switch]$Show | |
) | |
begin { | |
if($Env:PROCESSOR_ARCHITECTURE -eq "amd64") { | |
$GraphVizRegistryPath = "HKLM:\Software\Wow6432Node\AT&T Research Labs\Graphviz*" | |
} else { | |
$GraphVizRegistryPath = "HKLM:\Software\AT&T Research Labs\Graphviz*" | |
} | |
$GraphVizRegistry = Get-Item $GraphVizRegistryPath -ErrorAction SilentlyContinue | |
if($GraphVizRegistry) { | |
$GraphVizInstallPath = $GraphVizRegistry.GetValue("InstallPath") | |
} | |
if( !(Test-Path (Join-Path $GraphVizInstallPath "bin\dot.exe")) ) { | |
throw "Unable to locate GraphViz installation folder" | |
} | |
} | |
process { | |
switch ($PSCmdlet.ParameterSetName) { | |
"FromDot" { | |
#do nothing special | |
} | |
"FromGraph" { | |
$Dot = $Graph.ToDot() | |
$LayoutEngine = $Graph.LayoutEngine | |
} | |
default { | |
throw "Unknown Parameter Set Name" | |
} | |
} | |
$Exe = Join-Path $GraphVizInstallPath ("bin\{0}.exe" -f $LayoutEngine) | |
if( !(Test-Path $Exe)) { | |
throw "Layout engine $LayoutEngine not found at $Exe" | |
} | |
$Dot | &$EXE -Tpng -o $Path | |
if($Show) { | |
Invoke-Item $Path | |
} | |
} | |
} | |
$ObjectsSource = @" | |
using System.Collections.Generic; | |
using System.Text; | |
using System; | |
namespace Whatsupduck.Powershell.GraphViz { | |
public enum RankDir { | |
LR, | |
RT, | |
TB, | |
BT | |
} | |
public abstract class Graph { | |
protected LinkedList<Node> nodeList; | |
protected LinkedList<Edge> edgeList; | |
protected LinkedList<Graph> graphList; | |
protected Dictionary<string, string> properties; | |
public String Name { get; set; } | |
public String Label { | |
get { | |
return properties["label"]; | |
} | |
set { | |
properties["label"] = value; | |
} | |
} | |
public String Color { | |
get { | |
return properties["color"]; | |
} | |
set { | |
properties["color"] = value; | |
} | |
} | |
public bool Concentrate { | |
get { | |
if(properties.ContainsKey("concentrate")) { | |
return bool.Parse(properties["concentrate"]); | |
} else { | |
return false; | |
} | |
} | |
set { | |
properties["concentrate"] = value.ToString(); | |
} | |
} | |
public RankDir RankDir { | |
get { | |
return (RankDir)Enum.Parse(typeof(RankDir), properties["rankdir"]); | |
} | |
set { | |
properties["rankdir"] = Enum.GetName(typeof(RankDir), value); | |
} | |
} | |
public String Style { | |
get { return properties["style"]; } | |
set { properties["style"] = value; } | |
} | |
public abstract String LayoutEngine { get; } | |
public bool IsSubGraph { get; set; } | |
public bool IsCompound { get; set; } | |
public ICollection<Node> Nodes { | |
get { | |
return nodeList; | |
} | |
} | |
public ICollection<Edge> Edges { | |
get { | |
return edgeList; | |
} | |
} | |
public ICollection<Graph> Subgraphs { | |
get { | |
return graphList; | |
} | |
} | |
public Graph() { | |
this.Name = ""; | |
this.nodeList = new LinkedList<Node>(); | |
this.edgeList = new LinkedList<Edge>(); | |
this.graphList = new LinkedList<Graph>(); | |
this.properties = new Dictionary<string,string>(); | |
this.IsSubGraph = false; | |
this.IsCompound = false; | |
this.RankDir = RankDir.LR; | |
} | |
public void AddNode(Node newNode) { | |
nodeList.AddLast(newNode); | |
} | |
public void AddEdge(Edge newEdge) { | |
edgeList.AddLast(newEdge); | |
} | |
public void AddGraph(Graph newGraph) { | |
if(!newGraph.IsSubGraph) { | |
newGraph.IsSubGraph = true; | |
} | |
graphList.AddLast(newGraph); | |
this.IsCompound = true; | |
} | |
public abstract String ToDot(); | |
public String ToDot(string GraphType) { | |
StringBuilder sb = new StringBuilder(); | |
if(this.IsSubGraph) { | |
sb.Append("subgraph "); | |
} else { | |
sb.Append(GraphType + " "); | |
} | |
sb.Append("\"" + this.Name + "\""); | |
sb.AppendLine(" {"); | |
foreach(string name in this.properties.Keys) { | |
string value = this.properties[name]; | |
sb.AppendLine(name + "=\"" + value + "\""); | |
} | |
if(this.IsCompound) { | |
sb.AppendLine("compound=true;"); | |
} | |
foreach(Graph g in graphList) { | |
sb.Append(g.ToDot()); | |
} | |
foreach(Node n in nodeList) { | |
sb.Append(n.ToString()); | |
sb.AppendLine(";"); | |
} | |
foreach(Edge e in edgeList) { | |
sb.Append(e.ToString()); | |
sb.AppendLine(";"); | |
} | |
sb.AppendLine("}"); | |
return sb.ToString(); | |
} | |
} | |
public class DirectedGraph : Graph { | |
public override String ToDot() { | |
return base.ToDot("digraph"); | |
} | |
public override String LayoutEngine { get { return "dot"; } } | |
} | |
public class UndirectedGraph : Graph { | |
public override String ToDot() { | |
return base.ToDot("graph"); | |
} | |
public override String LayoutEngine { get { return "neato"; } } | |
} | |
// Full list http://www.graphviz.org/doc/info/shapes.html | |
// I left out these redundent shapes: none, rect | |
public enum Shapes { box, polygon, ellipse, oval, circle, point, egg, triangle, plaintext, diamond, trapezium, parallelogram, house, pentagon, hexagon, septagon, octagon, doublecircle, doubleoctagon, tripleoctagon, invtriangle, invtrapezium, invhouse, Mdiamond, Msquare, Mcircle, rectangle, square, note, tab, folder, box3d, component, promoter, cds, terminator, utr, primersite, restrictionsite, fivepoverhang, threepoverhang, noverhang, assembly, signature, insulator, ribosite, rnastab, proteasesite, proteinstab, rpromoter, rarrow, larrow, lpromoter }; | |
public class Node { | |
public String Name { get; set; } | |
private String _label; | |
public String Label { | |
get { | |
if(String.IsNullOrEmpty(_label)) { | |
return Name; | |
} else { | |
return _label; | |
} | |
} | |
set { | |
_label = value; | |
} | |
} | |
public Double Width { get; set; } | |
public Double Height { get; set; } | |
public Shapes Shape { get; set; } | |
public int Sides { get; set; } | |
public Node() { | |
Name = ""; | |
Label = ""; | |
//These are the defaults values for DOT | |
Width = .75; | |
Height = .5; | |
Shape = Shapes.ellipse; | |
Sides = 4; | |
} | |
public override String ToString() { | |
return String.Format("\"{0}\" [ label=\"{1}\", shape={2}]", Name, Label, Shape); | |
} | |
} | |
public abstract class Edge { | |
public String SourceName { get; set; } | |
public String TargetName { get; set; } | |
public String Label { get; set; } | |
public Edge() { | |
SourceName = ""; | |
TargetName = ""; | |
Label = ""; | |
} | |
public Edge(Node sourceNode, Node targetNode, String label) { | |
this.SourceName = sourceNode.Name; | |
this.TargetName = targetNode.Name; | |
this.Label = label; | |
} | |
public abstract override String ToString(); | |
} | |
public class DirectedEdge : Edge { | |
public override String ToString() { | |
return String.Format("\"{0}\" -> \"{1}\" [ label=\"{2}\"]", SourceName, TargetName, Label); | |
} | |
} | |
public class UndirectedEdge : Edge { | |
public override String ToString() { | |
return String.Format("\"{0}\" -- \"{1}\" [ label=\"{2}\"]", SourceName, TargetName, Label); | |
} | |
} | |
} | |
"@ | |
Add-Type -TypeDefinition $ObjectsSource |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment