GraphicalEngine/Show-Graph.ps1
Function Show-Graph { [cmdletbinding()] Param( [int[]] $Datapoints, [String] $GraphTitle, [ValidateScript({ if( $_ -le 5 ){ Throw "Can not set XAxisStep less than or equals to 5" } else { return $true } })] [Int] $XAxisStep = 10, [Int] $YAxisStep = 10, [Int] $yAxisMaxRows = 10, [ValidateSet("Bar","Scatter","Line")] [String] $Type = 'Bar', [Hashtable] $ColorMap ) Function Format-YAxisValue { param([int]$Value) if ($Value -ge 1000000) { return "{0:0.0}M" -f ($Value / 1000000) } elseif ($Value -ge 1000) { return "{0:0.0}k" -f ($Value / 1000) } else { return $Value.ToString() } } # graph boundary marks $TopLeft = [char]9484 # ┌ $BottomLeft = [char]9492 # └ $TopRight = [char]9488 # ┐ $BottomRight = [char]9496 # ┘ $VerticalEdge = [char]9474 # │ $TopEdge = $BottomEdge = [char]9472 # ─ # Calculate Max, Min and Range of Y axis $NumOfDatapoints = $Datapoints.Count $Metric = $Datapoints | Measure-Object -Maximum -Minimum $EndofRange = $Metric.Maximum + ($YAxisStep - $Metric.Maximum % $YAxisStep) $StartOfRange = $Metric.Minimum - ($Metric.Minimum % $YAxisStep) $difference = $EndofRange - $StartOfRange $NumOfRows = $difference/($YAxisStep) # Calculate maximum Y value width for formatting $MaxYValueWidth = 1 for ($i = 1; $i -le $NumOfRows; $i++) { $YValue = $StartOfRange + $i * $YAxisStep $FormattedLength = (Format-YAxisValue -Value $YValue).Length if ($FormattedLength -gt $MaxYValueWidth) { $MaxYValueWidth = $FormattedLength } } # Cap number of rows to yAxisMaxRows for better readability if ( $NumOfRows -gt $yAxisMaxRows ) { $NumOfRows = $yAxisMaxRows } # Create a 2D Array to save datapoints in a 2D format switch( $Type ){ 'Bar' { $Array = Get-BarPlot -Datapoints $Datapoints -Step $YAxisStep -StartOfRange $StartOfRange -EndofRange $EndofRange -yAxisMaxRows $yAxisMaxRows } 'Scatter' { $Array = Get-ScatterPlot -Datapoints $Datapoints -Step $YAxisStep -StartOfRange $StartOfRange -EndofRange $EndofRange -yAxisMaxRows $yAxisMaxRows } 'Line' { $Array = Get-LinePlot -Datapoints $Datapoints -Step $YAxisStep -StartOfRange $StartOfRange -EndofRange $EndofRange -yAxisMaxRows $yAxisMaxRows } } # Preparing the step markings on the X-Axis (reversed for time series) $Increment = $XAxisStep $XAxisText = " " * ($MaxYValueWidth + 4) $XAxis = " " * ($MaxYValueWidth + 3) + [char]9492 # └ For ( $Index = 1; $Index -le $NumOfDatapoints; $Index++ ) { if ( [math]::floor($Index/$XAxisStep) ){ $TimeValue = $NumOfDatapoints - $Index + 1 $XAxisText += $TimeValue.tostring().PadLeft($Increment) $XAxis += ([char]9516).ToString() $XAxisStep += $Increment } else { $XAxis += [Char]9472 # ─ } } # calculate boundaries of the graph $TopBoundaryLength = [Math]::Max(0, $XAxis.Length - $GraphTitle.Length) $BottomBoundaryLength = $XAxis.Length + 2 # draw top boundary Write-Host ([string]::Concat($TopLeft," ",$GraphTitle," ",$([string]$TopEdge * $TopBoundaryLength),$TopRight)) Write-Host ([String]::Concat($VerticalEdge,$(" "*$($XAxis.length+2)),$VerticalEdge)) # extra line to add space between top-boundary and the graph # draw the graph For( $i = $NumOfRows; $i -gt 0; $i-- ) { $Row = '' For( $j = 0; $j -lt $NumOfDatapoints; $j++ ) { $Cell = $Array[$i,$j] if( [String]::IsNullOrWhiteSpace($Cell) ){ if( $AddHorizontalLines ) { $String = [Char]9472 # ─ } else { $String = ' ' } } else { $String = $Cell } $Row = [string]::Concat($Row, $String) } $CurrentYValue = $StartOfRange + $i * $YAxisStep $FormattedYValue = Format-YAxisValue -Value $CurrentYValue If( $ColorMap ) { $Keys = $ColorMap.Keys | Sort-Object $LowerBound = $StartOfRange $Map = @() $Map += For( $k = 0; $k -lt $Keys.count; $k++ ) { [PSCustomObject]@{ LowerBound = $LowerBound UpperBound = $Keys[$k] Color = $ColorMap[$Keys[$k]] } $LowerBound = $Keys[$k] + 1 } $Color = $Map.ForEach({ if( $CurrentYValue -ge $_.LowerBound -and $CurrentYValue -le $_.UpperBound ) { $_.Color } }) # if out of bounds, use the last color if ( [String]::IsNullOrEmpty($Color) ) { $Map = $Map | sort-object -Property UpperBound -Descending $Color = $Map[0].Color } Write-Graph ' ' $FormattedYValue $Row $Color 'DarkYellow' $MaxYValueWidth } else { THROW "ColorMap is not defined. Please provide a valid ColorMap hashtable." } } # draw bottom boundary $XAxisText +=" "*([Math]::Max(0, $XAxis.Length-$XAxisText.Length)) # to match x-axis label length with x-axis length Write-Host([String]::Concat($VerticalEdge,$XAxis," ",$VerticalEdge)) # Prints X-Axis horizontal line Write-Host([string]::Concat($VerticalEdge,$XAxisText," ",$VerticalEdge)) # Prints X-Axis step labels # bottom boundary Write-Host([string]::Concat($BottomLeft,$([string]$BottomEdge * $BottomBoundaryLength),$BottomRight)) } |