diff --git a/.github/workflows/Release.yml b/.github/workflows/Release.yml
index 79a0a0b..6496cc2 100644
--- a/.github/workflows/Release.yml
+++ b/.github/workflows/Release.yml
@@ -2,7 +2,7 @@ name: Publish PowerShell Module
on:
release:
- types: [ published ]
+ types: [published]
jobs:
publish-to-psgallery:
@@ -23,12 +23,14 @@ jobs:
- name: Build the library for MacOS Arm64
run: dotnet publish ./Sources -c Release -r osx-arm64
- name: Copy the library for MacOS Arm64
- run: copy ./Sources/bin/Release/netstandard2.0/osx-arm64/publish/*.*
+ run:
+ copy ./Sources/bin/Release/netstandard2.0/osx-arm64/publish/*.*
./AsBuiltReport.Chart/Src/Assemblies/Core/mac-osx/osx-arm64
- name: Build the library for Linux
run: dotnet publish ./Sources -c Release -r linux-x64
- name: Copy the library for Linux
- run: copy ./Sources/bin/Release/netstandard2.0/linux-x64/publish/*.*
+ run:
+ copy ./Sources/bin/Release/netstandard2.0/linux-x64/publish/*.*
./AsBuiltReport.Chart/Src/Assemblies/Core/linux-x64
- name: Build the library for Windows
run: dotnet publish ./Sources -c Release -r win-x64
@@ -54,30 +56,13 @@ jobs:
shell: pwsh
run: |
Publish-Module -Path .\AsBuiltReport.Chart\ -NuGetApiKey ${{ secrets.PSGALLERY_API_KEY }} -Verbose
- # tweet:
- # needs: publish-to-psgallery
- # runs-on: ubuntu-latest
- # steps:
- # - uses: Eomm/why-don-t-you-tweet@v2
- # # We don't want to tweet if the repository is not a public one
- # if: ${{ !github.event.repository.private }}
- # with:
- # # GitHub event payload
- # # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release
- # tweet-message: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #AsBuiltReport #PowerShell"
- # env:
- # TWITTER_CONSUMER_API_KEY: ${{ secrets.TWITTER_CONSUMER_API_KEY }}
- # TWITTER_CONSUMER_API_SECRET: ${{ secrets.TWITTER_CONSUMER_API_SECRET }}
- # TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
- # TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
- # bsky-post:
- # needs: publish-to-psgallery
- # runs-on: ubuntu-latest
- # steps:
- # - uses: zentered/bluesky-post-action@v0.3.0
- # with:
- # post: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #AsBuiltReport #PowerShell"
- # env:
- # BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }}
- # BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }}
-
+ bsky-post:
+ needs: publish-to-psgallery
+ runs-on: ubuntu-latest
+ steps:
+ - uses: zentered/bluesky-post-action@v0.4.0
+ with:
+ post: "[New Release] ${{ github.event.repository.name }} ${{ github.event.release.tag_name }}! Check out what's new! ${{ github.event.release.html_url }} #AsBuiltReport #PowerShell"
+ env:
+ BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }}
+ BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }}
diff --git a/AsBuiltReport.Chart/AsBuiltReport.Chart.psd1 b/AsBuiltReport.Chart/AsBuiltReport.Chart.psd1
index f37ef90..4fa00be 100644
--- a/AsBuiltReport.Chart/AsBuiltReport.Chart.psd1
+++ b/AsBuiltReport.Chart/AsBuiltReport.Chart.psd1
@@ -12,7 +12,7 @@
RootModule = 'AsBuiltReport.Chart.psm1'
# Version number of this module.
- ModuleVersion = '0.3.2'
+ ModuleVersion = '0.3.3'
# Supported PSEditions
# CompatiblePSEditions = @()
@@ -69,7 +69,7 @@
# NestedModules = @()
# Functions to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no functions to export.
- FunctionsToExport = 'New-PieChart', 'New-BarChart', 'New-StackedBarChart', 'New-SignalChart', 'New-SingleStackedBarChart'
+ FunctionsToExport = 'New-PieChart', 'New-BarChart', 'New-StackedBarChart', 'New-SignalChart', 'New-SingleStackedBarChart', 'New-DonutChart', 'New-RadarChart'
# Cmdlets to export from this module, for best performance, do not use wildcards and do not delete the entry, use an empty array if there are no cmdlets to export.
# CmdletsToExport = '*'
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 82ca8ca..ddc3341 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## [0.3.3] - Unreleased
+
+### Added
+
+- Add support for Donut from Slices chart: [Donut from Slices](https://scottplot.net/cookbook/5/Pie/PieDonut/)
+ - Add pester test to validate the functionality of the New-DonutChart cmdlet
+ - Add example 12/13 to document on how to use the New-DonutChart cmdlet
+- Add radar chart support: implement New-RadarChart cmdlet and associated classes
+
+### Changed
+
+- Update module v0.3.3
+- Update SkiaSharp .NET dependency to v3.119.4
+- Update HarfBuzzSharp .NET dependency to v8.3.1.5
+
## [0.3.2] - 2026-05-05
### Added
diff --git a/Examples/Example12.ps1 b/Examples/Example12.ps1
new file mode 100644
index 0000000..25b5a1f
--- /dev/null
+++ b/Examples/Example12.ps1
@@ -0,0 +1,61 @@
+<#
+ .SYNOPSIS
+ Example 01 - Basic Donut Chart
+
+ .DESCRIPTION
+ This example demonstrates how to create a basic Donut Chart using the AsBuiltReport.Chart module.
+ The chart displays a simple breakdown of VM power states across a vSphere environment.
+#>
+
+[CmdletBinding()]
+param (
+ [System.IO.DirectoryInfo] $Path = (Get-Location).Path,
+ [string] $Format = 'png'
+)
+
+<#
+ Starting with PowerShell v3, modules are auto-imported when needed. Importing the module here
+ ensures clarity and avoids ambiguity.
+#>
+
+# Import-Module AsBuiltReport.Chart -Force -Verbose:$false
+
+<#
+ Since the chart output is a file, specify the output folder path using $OutputFolderPath.
+#>
+
+$OutputFolderPath = Resolve-Path $Path
+
+<#
+ Define the data to be displayed in the chart.
+ In a real-world scenario these values would come from your infrastructure query.
+#>
+
+$ChartTitle = 'User Type Breakdown'
+$Values = @(800, 80, 200)
+$Labels = @('Members', 'Guests', 'Disabled')
+
+<#
+ The New-DonutChart cmdlet generates the Donut Chart image.
+
+ -Title : Sets the chart title displayed at the top of the image.
+ -Values : Array of numeric values, one per slice.
+ -Labels : Array of label strings corresponding to each value.
+ -Format : Output file format (e.g. png, jpg, svg).
+ -OutputFolderPath : Directory where the generated chart file will be saved.
+ -Width : Width of the chart image in pixels.
+ -Height : Height of the chart image in pixels.
+ -ColorPalette : Predefined color palette (e.g. Category20, Pastel).
+ -Filename : Name of the output file (without extension).
+#>
+
+New-DonutChart `
+ -Title $ChartTitle `
+ -Values $Values `
+ -Labels $Labels `
+ -Format $Format `
+ -OutputFolderPath $OutputFolderPath `
+ -Width 600 `
+ -Height 400 `
+ -ColorPalette Category20 `
+ -Filename 'Example12-DonutChart'
diff --git a/Examples/Example13.ps1 b/Examples/Example13.ps1
new file mode 100644
index 0000000..908cc57
--- /dev/null
+++ b/Examples/Example13.ps1
@@ -0,0 +1,98 @@
+<#
+ .SYNOPSIS
+ Example 02 - Donut Chart with Legend, Custom Colors and Border
+
+ .DESCRIPTION
+ This example demonstrates how to create a Donut Chart with additional visual options, including:
+ - An enabled legend with custom alignment and orientation
+ - A custom hex color palette
+ - A chart border
+ - Adjusted title and label font sizes
+ - Custom chart dimensions
+
+ The chart displays a server operating system distribution report.
+#>
+
+[CmdletBinding()]
+param (
+ [System.IO.DirectoryInfo] $Path = (Get-Location).Path,
+ [string] $Format = 'png'
+)
+
+<#
+ Starting with PowerShell v3, modules are auto-imported when needed. Importing the module here
+ ensures clarity and avoids ambiguity.
+#>
+
+# Import-Module AsBuiltReport.Chart -Force -Verbose:$false
+
+<#
+ Since the chart output is a file, specify the output folder path using $OutputFolderPath.
+#>
+
+$OutputFolderPath = Resolve-Path $Path
+
+<#
+ Define the data to be displayed in the chart.
+ In a real-world scenario these values would come from your infrastructure query.
+#>
+
+$ChartTitle = 'Server OS Distribution'
+$Values = @(85, 60, 30, 15)
+$Labels = @('Windows Server 2022', 'Windows Server 2019', 'RHEL 9', 'Ubuntu 22.04')
+
+<#
+ A custom hex color palette can be used to match corporate branding or improve readability.
+ Each color corresponds to a slice in the order they appear in $Values.
+#>
+
+$CustomColors = @('#0078D4', '#00B7C3', '#E74C3C', '#F39C12')
+
+<#
+ The New-DonutChart cmdlet generates the Donut Chart image.
+
+ -Title : Sets the chart title.
+ -TitleFontSize : Sets the font size of the title in points.
+ -TitleFontBold : Renders the title in bold.
+ -Values : Array of numeric values, one per slice.
+ -Labels : Array of label strings corresponding to each value.
+ -LabelFontSize : Sets the font size of the slice labels.
+ -LabelDistance : Controls how far labels are placed from the chart center (0.5-0.9).
+ -EnableLegend : Enables the legend on the chart.
+ -LegendAlignment : Positions the legend (e.g. UpperRight, LowerCenter).
+ -LegendOrientation : Sets legend layout direction (Vertical or Horizontal).
+ -LegendFontSize : Sets the legend text font size in points.
+ -EnableChartBorder : Draws a border around the chart area.
+ -ChartBorderColor : Sets the border color.
+ -ChartBorderSize : Sets the border thickness in pixels.
+ -EnableCustomColorPalette : Enables use of the custom color palette.
+ -CustomColorPalette : Array of hex color strings for each slice.
+ -Width : Output image width in pixels.
+ -Height : Output image height in pixels.
+ -Format : Output file format (e.g. png, jpg, svg).
+ -OutputFolderPath : Directory where the generated chart file will be saved.
+ -Filename : Name of the output file (without extension).
+#>
+
+New-DonutChart `
+ -Title $ChartTitle `
+ -TitleFontSize 18 `
+ -TitleFontBold `
+ -Values $Values `
+ -Labels $Labels `
+ -LabelFontSize 13 `
+ -LabelDistance 0.7 `
+ -EnableLegend `
+ -LegendAlignment UpperRight `
+ -LegendOrientation Vertical `
+ -LegendFontSize 12 `
+ -EnableChartBorder `
+ -ChartBorderColor Black `
+ -ChartBorderSize 2 `
+ -EnableCustomColorPalette `
+ -CustomColorPalette $CustomColors `
+ -Width 600 `
+ -Height 400 `
+ -Format $Format `
+ -OutputFolderPath $OutputFolderPath `
+ -Filename 'Example13-DonutChart-Advanced'
diff --git a/Sources/AsBuiltReportChart.csproj b/Sources/AsBuiltReportChart.csproj
index 5a0269a..94f1aa2 100644
--- a/Sources/AsBuiltReportChart.csproj
+++ b/Sources/AsBuiltReportChart.csproj
@@ -3,15 +3,15 @@
netstandard2.0
- 0.3.2
+ 0.3.3
-
+
-
-
+
+
diff --git a/Sources/Chart.cs b/Sources/Chart.cs
index 4c5834d..fc574c4 100644
--- a/Sources/Chart.cs
+++ b/Sources/Chart.cs
@@ -33,7 +33,7 @@ internal partial class Chart
public static string LabelXAxis { get; set; } = "Values";
// this set the distance of the labels from the chart center (Pie Chart)
- internal static double _labelDistance = 0.6;
+ internal static double _labelDistance;
public static double LabelDistance
{
get { return _labelDistance; }
@@ -50,6 +50,24 @@ public static double LabelDistance
}
}
+ // this set the distance of the labels from the chart center (Pie Chart)
+ internal static double _spokesLength;
+ public static double SpokesLength
+ {
+ get { return _spokesLength; }
+ set
+ {
+ if (value >= 5 && value <= 50)
+ {
+ _spokesLength = value;
+ }
+ else
+ {
+ throw new ArgumentException("Error: SpokesLength value range must be from 5 to 50.");
+ }
+ }
+ }
+
// this set the orientation chart area (Bar Chart)
public static Orientations AreaOrientation { get; set; } = Orientations.Vertical;
@@ -71,6 +89,27 @@ public static double AreaExplodeFraction
}
}
+ // Center text for Donut Chart
+ public static string DonutCenterText { get; set; }
+
+ // this set the distance of the chart area elements (Donut Chart)
+ internal static double _donutFraction;
+ public static double DonutFraction
+ {
+ get { return _donutFraction; }
+ set
+ {
+ if (value >= 0.0 && value <= 0.5)
+ {
+ _donutFraction = value;
+ }
+ else
+ {
+ throw new ArgumentException("Error: DonutFraction value range must be from 0.0 to 0.5.");
+ }
+ }
+ }
+
// Legend setting (Pie Chart)
public static bool EnableLegend { get; set; }
@@ -87,6 +126,9 @@ public static double AreaExplodeFraction
public static Alignments LegendAlignment { get; set; } = Alignments.LowerRight;
+ // Hide Values
+ public static bool HideValues { get; set; }
+
// Chart border settings (All Charts)
public static bool EnableChartBorder { get; set; }
public static BorderStyles ChartBorderStyle { get; set; }
@@ -439,6 +481,8 @@ internal static void Reset()
WatermarkFontSize = 24;
WatermarkColor = BasicColors.Gray;
_watermarkOpacity = 0.3;
+ DonutFraction = 0.5;
+ HideValues = false;
}
public static string GenerateToken(Byte length)
diff --git a/Sources/DonutChart.cs b/Sources/DonutChart.cs
new file mode 100644
index 0000000..a6464ac
--- /dev/null
+++ b/Sources/DonutChart.cs
@@ -0,0 +1,132 @@
+using ScottPlot;
+using System;
+using System.IO;
+using System.Collections.Generic;
+namespace AsBuiltReportChart
+{
+ internal class Donut : Chart
+ {
+ public Donut() { }
+ public object Chart(double[] values, string[] labels, string filename = "output", int width = 400, int height = 300)
+ {
+ if (values.Length == labels.Length)
+ {
+ Plot myPlot = new Plot();
+
+ if (EnableCustomColorPalette)
+ {
+
+ if (_customColorPalette != null && _customColorPalette.Length > 0)
+ {
+ myPlot.Add.Palette = colorPalette = new ScottPlot.Palettes.Custom(_customColorPalette);
+ }
+ else
+ {
+ throw new InvalidOperationException("CustomColorPalette is empty. Please provide valid color values.");
+ }
+
+ }
+ else
+ {
+ // Set ScottPlot native color palette
+ if (colorPalette != null)
+ {
+ myPlot.Add.Palette = colorPalette;
+ }
+ }
+
+ List slices = new List();
+ for (int i = 0; i < values.Length; i++)
+ {
+ slices.Add(new PieSlice()
+ {
+ Value = values[i],
+ LabelText = HideValues ? null : values[i].ToString(),
+ LabelFontSize = LabelFontSize,
+ LabelFontColor = GetDrawingColor(LabelFontColor),
+ LabelBold = LabelBold,
+ LabelFontName = FontName,
+ LegendText = EnableLegend ? labels[i] : null,
+ FillColor = colorPalette.GetColor(i)
+ });
+ }
+
+ var pie = myPlot.Add.Pie(slices);
+ pie.DonutFraction = DonutFraction;
+
+ // hide unnecessary plot components
+ myPlot.Axes.Frameless();
+ myPlot.HideGrid();
+
+ if (EnableLegend)
+ {
+ // Legend Font Properties
+ myPlot.Legend.FontName = FontName;
+ myPlot.Legend.FontSize = LegendFontSize;
+ myPlot.Legend.FontColor = GetDrawingColor(LegendFontColor);
+
+ // Legend box Style Properties
+ myPlot.Legend.OutlineColor = GetDrawingColor(LegendBorderColor);
+ myPlot.Legend.OutlineWidth = LegendBorderSize;
+
+ myPlot.Legend.OutlinePattern = LegendBorderStyleMap[LegendBorderStyle];
+
+ myPlot.Legend.Orientation = LegendOrientationMap[LegendOrientation];
+
+ myPlot.Legend.Alignment = LegendAlignmentMap[LegendAlignment];
+ }
+
+ if (EnableChartBorder)
+ {
+ myPlot.FigureBorder = new LineStyle()
+ {
+ Color = GetDrawingColor(ChartBorderColor),
+ Width = ChartBorderSize,
+ Pattern = ChartBorderStyleMap[ChartBorderStyle],
+ };
+ }
+
+ // Set title properties
+ if (Title != null)
+ {
+ myPlot.Title(Title);
+ myPlot.Axes.Title.Label.FontSize = TitleFontSize;
+ myPlot.Axes.Title.Label.ForeColor = GetDrawingColor(TitleFontColor);
+ myPlot.Axes.Title.Label.Bold = TitleFontBold;
+ myPlot.Axes.Title.Label.FontName = FontName;
+ }
+
+ // Set margins settings
+ myPlot.Axes.Margins(left: AxesMarginsLeft, right: AxesMarginsRight, bottom: AxesMarginsDown, top: AxesMarginsTop);
+
+ // Set background colors
+ if (FigureBackgroundColor.HasValue)
+ {
+ myPlot.FigureBackground.Color = GetDrawingColor(FigureBackgroundColor.Value);
+ }
+ if (DataBackgroundColor.HasValue)
+ {
+ myPlot.DataBackground.Color = GetDrawingColor(DataBackgroundColor.Value);
+ }
+
+ // Set label distance from the center of the donut slices
+ pie.SliceLabelDistance = _labelDistance;
+
+ // Apply watermark if enabled
+ ApplyWatermark(myPlot);
+
+ // Set filetpath to save
+ string Filepath = _outputFolderPath ?? Directory.GetCurrentDirectory();
+
+ // Set filename
+ return SaveInFormat(myPlot, width, height, Filepath, filename, Format);
+ }
+ else
+ {
+ throw new ArgumentException("Error: Values and labels must be equal.");
+ }
+ }
+ }
+}
+
+
diff --git a/Sources/PieChart.cs b/Sources/PieChart.cs
index 12127bb..e2e493d 100644
--- a/Sources/PieChart.cs
+++ b/Sources/PieChart.cs
@@ -14,7 +14,6 @@ public object Chart(double[] values, string[] labels, string filename = "output"
if (EnableCustomColorPalette)
{
-
if (_customColorPalette != null && _customColorPalette.Length > 0)
{
myPlot.Add.Palette = colorPalette = new ScottPlot.Palettes.Custom(_customColorPalette);
@@ -23,7 +22,6 @@ public object Chart(double[] values, string[] labels, string filename = "output"
{
throw new InvalidOperationException("CustomColorPalette is empty. Please provide valid color values.");
}
-
}
else
{
@@ -36,7 +34,6 @@ public object Chart(double[] values, string[] labels, string filename = "output"
var pie = myPlot.Add.Pie(values);
pie.ExplodeFraction = _areaExplodeFraction;
- pie.SliceLabelDistance = _labelDistance;
// set each slice value to its label
for (var i = 0; i < pie.Slices.Count; i++)
@@ -108,6 +105,9 @@ public object Chart(double[] values, string[] labels, string filename = "output"
myPlot.DataBackground.Color = GetDrawingColor(DataBackgroundColor.Value);
}
+ // Set the distance of the chart area elements (Pie Chart)
+ pie.SliceLabelDistance = _labelDistance;
+
// Apply watermark if enabled
ApplyWatermark(myPlot);
diff --git a/Sources/PowerShell/BarChartPwsh.cs b/Sources/PowerShell/BarChartPwsh.cs
index 262f0a0..a819429 100644
--- a/Sources/PowerShell/BarChartPwsh.cs
+++ b/Sources/PowerShell/BarChartPwsh.cs
@@ -161,6 +161,7 @@ public class NewBarChartCommand : Cmdlet
// Set OutputFolderPath
[Parameter(Mandatory = false, HelpMessage = "Output folder path where the chart will be saved.")]
+ [ValidatePath()]
public string OutputFolderPath { get; set; } = Directory.GetCurrentDirectory();
protected override void ProcessRecord()
diff --git a/Sources/PowerShell/DonutChartPwsh.cs b/Sources/PowerShell/DonutChartPwsh.cs
new file mode 100644
index 0000000..8daedc5
--- /dev/null
+++ b/Sources/PowerShell/DonutChartPwsh.cs
@@ -0,0 +1,271 @@
+using System;
+using System.IO;
+using System.Management.Automation;
+
+namespace AsBuiltReportChart.PowerShell
+{
+ [Cmdlet(VerbsCommon.New, "DonutChart")]
+ public class NewDonutChartCommand : Cmdlet
+ {
+ // Declare the parameters for the cmdlet.
+ [Parameter(Mandatory = false, HelpMessage = "Output filename for the chart. Defaults to a randomly generated 8-character token.")]
+ public string Filename { get; set; } = Chart.GenerateToken(8);
+
+ [Parameter(Mandatory = true, HelpMessage = "Array of numeric values to display in the donut chart.")]
+ public double[] Values { get; set; }
+
+ [Parameter(Mandatory = true, HelpMessage = "Array of labels corresponding to each donut slice.")]
+ public string[] Labels { get; set; }
+
+ [Parameter(Mandatory = true, HelpMessage = "Title text to display on the chart.")]
+ public string Title { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to make the title font bold.")]
+ public SwitchParameter TitleFontBold { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Font size for the title. Defaults to 14.")]
+ public int TitleFontSize { get; set; } = 14;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font color for the title.")]
+ public BasicColors TitleFontColor { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to enable the legend on the chart.")]
+ public SwitchParameter EnableLegend { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Orientation of the legend (Vertical or Horizontal). Defaults to Vertical.")]
+ public Enums.Orientations LegendOrientation { get; set; } = Enums.Orientations.Vertical;
+
+ [Parameter(Mandatory = false, HelpMessage = "Alignment of the legend on the chart. Defaults to UpperRight.")]
+ public Enums.Alignments LegendAlignment { get; set; } = Enums.Alignments.UpperRight;
+
+ [Parameter(Mandatory = true, HelpMessage = "Output format for the chart (e.g., PNG, JPG, SVG).")]
+ public Formats Format { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Fraction to explode donut slices (0.0 to 0.5).")]
+ [ValidateSet("0.0", "0.1", "0.2", "0.3", "0.4", "0.5")]
+ public double DonutFraction { get; set; } = 0.5;
+
+ [Parameter(Mandatory = false, HelpMessage = "Color of the chart border.")]
+ public BasicColors ChartBorderColor { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Size of the chart border in pixels. Defaults to 1.")]
+ public int ChartBorderSize { get; set; } = 1;
+
+ [Parameter(Mandatory = false, HelpMessage = "Style of the chart border.")]
+ public Enums.BorderStyles ChartBorderStyle { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to enable the chart border.")]
+ public SwitchParameter EnableChartBorder { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Font size for the legend. Defaults to 14.")]
+ public int LegendFontSize { get; set; } = 14;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font color for the legend. Defaults to Black.")]
+ public BasicColors LegendFontColor { get; set; } = BasicColors.Black;
+
+ [Parameter(Mandatory = false, HelpMessage = "Border style for the legend.")]
+ public Enums.BorderStyles LegendBorderStyle { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Border size for the legend in pixels. Defaults to 1.")]
+ public int LegendBorderSize { get; set; } = 1;
+
+ [Parameter(Mandatory = false, HelpMessage = "Border color for the legend. Defaults to Black.")]
+ public BasicColors LegendBorderColor { get; set; } = BasicColors.Black;
+
+ [Parameter(Mandatory = false, HelpMessage = "Color palette for the chart. Defaults to Category10.")]
+ public Enums.ColorPalettes ColorPalette { get; set; } = Enums.ColorPalettes.Category10;
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to enable custom color palette.")]
+ public SwitchParameter EnableCustomColorPalette { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Invert the custom color palette.")]
+ public SwitchParameter InvertCustomColorPalette { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Array of custom hex color values for the chart.")]
+ public string[] CustomColorPalette { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Font name to use for all text. Defaults to Arial.")]
+ public string FontName { get; set; } = "Arial";
+
+ // Label Font settings
+ [Parameter(Mandatory = false, HelpMessage = "Font size for chart labels. Defaults to 14.")]
+ public int LabelFontSize { get; set; } = 14;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font color for labels. Defaults to Black.")]
+ public BasicColors LabelFontColor { get; set; } = BasicColors.Black;
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to make label font bold.")]
+ public SwitchParameter LabelBold { get; set; }
+
+ // this set the distance of the labels from the chart center (Donut Chart)
+ [Parameter(Mandatory = false, HelpMessage = "Distance of labels from the chart center (0.5 to 0.9). Defaults to 0.6.")]
+ [ValidateSet("0.5", "0.6", "0.7", "0.8", "0.9")]
+ public double LabelDistance { get; set; } = 0.7;
+
+ // this set the area axes margins (Bar Chart)
+ [Parameter(Mandatory = false, HelpMessage = "Top margin for the chart area. Defaults to 0.2.")]
+ public double AxesMarginsTop { get; set; } = 0.2;
+
+ [Parameter(Mandatory = false, HelpMessage = "Bottom margin for the chart area. Defaults to 0.05.")]
+ public double AxesMarginsDown { get; set; } = 0.05;
+
+ [Parameter(Mandatory = false, HelpMessage = "Left margin for the chart area. Defaults to 0.05.")]
+ public double AxesMarginsLeft { get; set; } = 0.05;
+
+ [Parameter(Mandatory = false, HelpMessage = "Right margin for the chart area. Defaults to 0.05.")]
+ public double AxesMarginsRight { get; set; } = 0.05;
+
+ [Parameter(Mandatory = false, HelpMessage = "Background color of the entire figure (canvas).")]
+ public BasicColors? FigureBackgroundColor { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Background color of the data area inside the axes.")]
+ public BasicColors? DataBackgroundColor { get; set; }
+
+ // Watermark settings
+ [Parameter(Mandatory = false, HelpMessage = "Enable a watermark on the chart.")]
+ public SwitchParameter EnableWatermark { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Text to display as the watermark. Defaults to 'Confidential'.")]
+ public string WatermarkText { get; set; } = "Confidential";
+
+ [Parameter(Mandatory = false, HelpMessage = "Alignment of the watermark text. Defaults to 'MiddleCenter'.")]
+ public Enums.Alignments WatermarkAlignment { get; set; } = Enums.Alignments.MiddleCenter;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font name for the watermark text.")]
+ public string WatermarkFontName { get; set; } = "Arial";
+
+ [Parameter(Mandatory = false, HelpMessage = "Font size for the watermark text in points. Defaults to 24.")]
+ public int WatermarkFontSize { get; set; } = 24;
+
+ [Parameter(Mandatory = false, HelpMessage = "Color of the watermark text.")]
+ public BasicColors WatermarkColor { get; set; } = BasicColors.Gray;
+
+ [Parameter(Mandatory = false, HelpMessage = "Opacity of the watermark (0.0 fully transparent to 1.0 fully opaque). Defaults to 0.3.")]
+ public double WatermarkOpacity { get; set; } = 0.3;
+
+ [Parameter(Mandatory = false, HelpMessage = "Rotation angle of the watermark text in degrees. Defaults to 0.")]
+ public float WatermarkRotation { get; set; } = 0;
+
+ // Set chart Size WxH
+ [Parameter(Mandatory = false, HelpMessage = "Width of the chart in pixels. Defaults to 400.")]
+ public int Width { get; set; } = 400;
+
+ [Parameter(Mandatory = false, HelpMessage = "Height of the chart in pixels. Defaults to 300.")]
+ public int Height { get; set; } = 300;
+
+ // Set OutputFolderPath
+ [Parameter(Mandatory = false, HelpMessage = "Directory path where the chart file will be saved. Defaults to current directory.")]
+ [ValidatePath()]
+ public string OutputFolderPath { get; set; } = Directory.GetCurrentDirectory();
+
+ // Switch to hide values on the chart
+ [Parameter(Mandatory = false, HelpMessage = "Switch to hide values on the chart.")]
+ public SwitchParameter HideValues { get; set; }
+
+ protected override void ProcessRecord()
+ {
+ Chart.Reset();
+ if (Values != null && Labels != null)
+ {
+ if (EnableLegend)
+ {
+ Chart.EnableLegend = EnableLegend;
+ // Legend box settings
+ Chart.LegendOrientation = LegendOrientation;
+ Chart.LegendAlignment = LegendAlignment;
+
+ // Legend font settings
+ Chart.LegendFontSize = LegendFontSize;
+ Chart.LegendFontColor = LegendFontColor;
+ // Legend border settings
+ Chart.LegendBorderStyle = LegendBorderStyle;
+ Chart.LegendBorderSize = LegendBorderSize;
+ Chart.LegendBorderColor = LegendBorderColor;
+ }
+
+ if (HideValues)
+ {
+ Chart.HideValues = HideValues;
+ }
+
+ Chart.DonutFraction = DonutFraction;
+ if (EnableChartBorder)
+ {
+ // Chart area settings
+ Chart.EnableChartBorder = EnableChartBorder;
+ Chart.ChartBorderColor = ChartBorderColor;
+ Chart.ChartBorderSize = ChartBorderSize;
+ Chart.ChartBorderStyle = ChartBorderStyle;
+ }
+ // Color palette settings
+ if (EnableCustomColorPalette)
+ {
+ if (CustomColorPalette != null && CustomColorPalette.Length > 0)
+ {
+ // Set ScottPlot custom color palette
+ Chart.EnableCustomColorPalette = EnableCustomColorPalette;
+ Chart.InvertCustomColorPalette = InvertCustomColorPalette;
+ Chart.CustomColorPalette = CustomColorPalette;
+ }
+ else
+ {
+ throw new InvalidOperationException("EnableCustomColorPalette requires CustomColorPalette to be set.");
+ }
+ }
+ else
+ {
+ Chart.ColorPalette = ColorPalette;
+ }
+
+ // this set the area axes margins (Bar Chart)
+ Chart.AxesMarginsTop = AxesMarginsTop;
+ Chart.AxesMarginsDown = AxesMarginsDown;
+ Chart.AxesMarginsLeft = AxesMarginsLeft;
+ Chart.AxesMarginsRight = AxesMarginsRight;
+
+ // Title settings
+ if (Title != null)
+ {
+ Chart.Title = Title;
+ Chart.TitleFontBold = TitleFontBold;
+ Chart.TitleFontSize = TitleFontSize;
+ Chart.TitleFontColor = TitleFontColor;
+ }
+
+ // This set the distance of the labels from the chart center (Donut Chart)
+ Chart.LabelDistance = LabelDistance;
+
+ // Font Settings
+ Chart.FontName = FontName;
+ Chart.LabelFontSize = LabelFontSize;
+ Chart.LabelFontColor = LabelFontColor;
+ Chart.LabelBold = LabelBold;
+
+ // Background color settings
+ Chart.FigureBackgroundColor = FigureBackgroundColor;
+ Chart.DataBackgroundColor = DataBackgroundColor;
+
+ // Watermark settings
+ Chart.EnableWatermark = EnableWatermark;
+ Chart.WatermarkText = WatermarkText;
+ Chart.WatermarkAlignment = WatermarkAlignment;
+ Chart.WatermarkFontName = WatermarkFontName;
+ Chart.WatermarkFontSize = WatermarkFontSize;
+ Chart.WatermarkColor = WatermarkColor;
+ Chart.WatermarkOpacity = WatermarkOpacity;
+ Chart.WatermarkRotation = WatermarkRotation;
+
+ // Set file directory save path
+ Chart.OutputFolderPath = OutputFolderPath;
+
+ Chart.Format = Format;
+ Donut myDonut = new Donut();
+ WriteObject(myDonut.Chart(Values, Labels, Filename, Width, Height));
+ }
+ else
+ {
+ WriteObject("Please provide both Values and Labels parameters.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/PowerShell/RadarChartPwsh.cs b/Sources/PowerShell/RadarChartPwsh.cs
new file mode 100644
index 0000000..a9a362e
--- /dev/null
+++ b/Sources/PowerShell/RadarChartPwsh.cs
@@ -0,0 +1,269 @@
+using System;
+using System.IO;
+using System.Collections.Generic;
+using System.Management.Automation;
+
+namespace AsBuiltReportChart.PowerShell
+{
+ [Cmdlet(VerbsCommon.New, "RadarChart")]
+ public class NewRadarChartCommand : Cmdlet
+ {
+ // Declare the parameters for the cmdlet.
+ [Parameter(Mandatory = false, HelpMessage = "Output filename for the chart. Defaults to a randomly generated 8-character token.")]
+ public string Filename { get; set; } = Chart.GenerateToken(8);
+
+ [Parameter(Mandatory = true, HelpMessage = "List of arrays containing numeric values for each radar series.")]
+ public List Values { get; set; }
+
+ [Parameter(Mandatory = true, HelpMessage = "Array of labels corresponding to each radar series.")]
+ public string[] LegendLabels { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Array of labels corresponding to each radar series.")]
+ public string[] SpokeLabels { get; set; }
+
+ [Parameter(Mandatory = true, HelpMessage = "Title text to display on the chart.")]
+ public string Title { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to make the title font bold.")]
+ public SwitchParameter TitleFontBold { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Font size for the title. Defaults to 14.")]
+ public int TitleFontSize { get; set; } = 14;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font color for the title.")]
+ public BasicColors TitleFontColor { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to enable the legend on the chart.")]
+ public SwitchParameter EnableLegend { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Orientation of the legend (Vertical or Horizontal). Defaults to Vertical.")]
+ public Enums.Orientations LegendOrientation { get; set; } = Enums.Orientations.Vertical;
+
+ [Parameter(Mandatory = false, HelpMessage = "Alignment of the legend on the chart. Defaults to UpperRight.")]
+ public Enums.Alignments LegendAlignment { get; set; } = Enums.Alignments.UpperRight;
+
+ [Parameter(Mandatory = true, HelpMessage = "Output format for the chart (e.g., PNG, JPG, SVG).")]
+ public Formats Format { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Color of the chart border.")]
+ public BasicColors ChartBorderColor { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Size of the chart border in pixels. Defaults to 1.")]
+ public int ChartBorderSize { get; set; } = 1;
+
+ [Parameter(Mandatory = false, HelpMessage = "Style of the chart border.")]
+ public Enums.BorderStyles ChartBorderStyle { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to enable the chart border.")]
+ public SwitchParameter EnableChartBorder { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Font size for the legend. Defaults to 14.")]
+ public int LegendFontSize { get; set; } = 14;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font color for the legend. Defaults to Black.")]
+ public BasicColors LegendFontColor { get; set; } = BasicColors.Black;
+
+ [Parameter(Mandatory = false, HelpMessage = "Border style for the legend.")]
+ public Enums.BorderStyles LegendBorderStyle { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Border size for the legend in pixels. Defaults to 1.")]
+ public int LegendBorderSize { get; set; } = 1;
+
+ [Parameter(Mandatory = false, HelpMessage = "Border color for the legend. Defaults to Black.")]
+ public BasicColors LegendBorderColor { get; set; } = BasicColors.Black;
+
+ [Parameter(Mandatory = false, HelpMessage = "Color palette for the chart. Defaults to Category10.")]
+ public Enums.ColorPalettes ColorPalette { get; set; } = Enums.ColorPalettes.Category10;
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to enable custom color palette.")]
+ public SwitchParameter EnableCustomColorPalette { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Invert the custom color palette.")]
+ public SwitchParameter InvertCustomColorPalette { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Array of custom hex color values for the chart.")]
+ public string[] CustomColorPalette { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Font name to use for all text. Defaults to Arial.")]
+ public string FontName { get; set; } = "Arial";
+
+ // Label Font settings
+ [Parameter(Mandatory = false, HelpMessage = "Font size for chart labels. Defaults to 14.")]
+ public int LabelFontSize { get; set; } = 14;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font color for labels. Defaults to Black.")]
+ public BasicColors LabelFontColor { get; set; } = BasicColors.Black;
+
+ [Parameter(Mandatory = false, HelpMessage = "Switch to make label font bold.")]
+ public SwitchParameter LabelBold { get; set; }
+
+ // this set the distance of the labels from the chart center (Radar Chart)
+ [Parameter(Mandatory = false, HelpMessage = "Distance of labels from the chart center (10 to 50). Defaults to 10.")]
+ public double SpokesLength { get; set; } = 10;
+
+ // this set the area axes margins (Bar Chart)
+ [Parameter(Mandatory = false, HelpMessage = "Top margin for the chart area. Defaults to 0.2.")]
+ public double AxesMarginsTop { get; set; } = 0.2;
+
+ [Parameter(Mandatory = false, HelpMessage = "Bottom margin for the chart area. Defaults to 0.05.")]
+ public double AxesMarginsDown { get; set; } = 0.05;
+
+ [Parameter(Mandatory = false, HelpMessage = "Left margin for the chart area. Defaults to 0.05.")]
+ public double AxesMarginsLeft { get; set; } = 0.05;
+
+ [Parameter(Mandatory = false, HelpMessage = "Right margin for the chart area. Defaults to 0.05.")]
+ public double AxesMarginsRight { get; set; } = 0.05;
+
+ [Parameter(Mandatory = false, HelpMessage = "Background color of the entire figure (canvas).")]
+ public BasicColors? FigureBackgroundColor { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Background color of the data area inside the axes.")]
+ public BasicColors? DataBackgroundColor { get; set; }
+
+ // Watermark settings
+ [Parameter(Mandatory = false, HelpMessage = "Enable a watermark on the chart.")]
+ public SwitchParameter EnableWatermark { get; set; }
+
+ [Parameter(Mandatory = false, HelpMessage = "Text to display as the watermark. Defaults to 'Confidential'.")]
+ public string WatermarkText { get; set; } = "Confidential";
+
+ [Parameter(Mandatory = false, HelpMessage = "Alignment of the watermark text. Defaults to 'MiddleCenter'.")]
+ public Enums.Alignments WatermarkAlignment { get; set; } = Enums.Alignments.MiddleCenter;
+
+ [Parameter(Mandatory = false, HelpMessage = "Font name for the watermark text.")]
+ public string WatermarkFontName { get; set; } = "Arial";
+
+ [Parameter(Mandatory = false, HelpMessage = "Font size for the watermark text in points. Defaults to 24.")]
+ public int WatermarkFontSize { get; set; } = 24;
+
+ [Parameter(Mandatory = false, HelpMessage = "Color of the watermark text.")]
+ public BasicColors WatermarkColor { get; set; } = BasicColors.Gray;
+
+ [Parameter(Mandatory = false, HelpMessage = "Opacity of the watermark (0.0 fully transparent to 1.0 fully opaque). Defaults to 0.3.")]
+ public double WatermarkOpacity { get; set; } = 0.3;
+
+ [Parameter(Mandatory = false, HelpMessage = "Rotation angle of the watermark text in degrees. Defaults to 0.")]
+ public float WatermarkRotation { get; set; } = 0;
+
+ // Set chart Size WxH
+ [Parameter(Mandatory = false, HelpMessage = "Width of the chart in pixels. Defaults to 400.")]
+ public int Width { get; set; } = 400;
+
+ [Parameter(Mandatory = false, HelpMessage = "Height of the chart in pixels. Defaults to 300.")]
+ public int Height { get; set; } = 300;
+
+ // Set OutputFolderPath
+ [Parameter(Mandatory = false, HelpMessage = "Directory path where the chart file will be saved. Defaults to current directory.")]
+ [ValidatePath()]
+ public string OutputFolderPath { get; set; } = Directory.GetCurrentDirectory();
+
+ protected override void ProcessRecord()
+ {
+ Chart.Reset();
+ if (Values != null && LegendLabels != null)
+ {
+ if (EnableLegend)
+ {
+ Chart.EnableLegend = EnableLegend;
+ // Legend box settings
+ Chart.LegendOrientation = LegendOrientation;
+ Chart.LegendAlignment = LegendAlignment;
+
+ // Legend font settings
+ Chart.LegendFontSize = LegendFontSize;
+ Chart.LegendFontColor = LegendFontColor;
+ // Legend border settings
+ Chart.LegendBorderStyle = LegendBorderStyle;
+ Chart.LegendBorderSize = LegendBorderSize;
+ Chart.LegendBorderColor = LegendBorderColor;
+ }
+
+ if (EnableChartBorder)
+ {
+ // Chart area settings
+ Chart.EnableChartBorder = EnableChartBorder;
+ Chart.ChartBorderColor = ChartBorderColor;
+ Chart.ChartBorderSize = ChartBorderSize;
+ Chart.ChartBorderStyle = ChartBorderStyle;
+ }
+ // Color palette settings
+ if (EnableCustomColorPalette)
+ {
+ if (CustomColorPalette != null && CustomColorPalette.Length > 0)
+ {
+ // Set ScottPlot custom color palette
+ Chart.EnableCustomColorPalette = EnableCustomColorPalette;
+ Chart.InvertCustomColorPalette = InvertCustomColorPalette;
+ Chart.CustomColorPalette = CustomColorPalette;
+ }
+ else
+ {
+ throw new InvalidOperationException("EnableCustomColorPalette requires CustomColorPalette to be set.");
+ }
+ }
+ else
+ {
+ Chart.ColorPalette = ColorPalette;
+ }
+
+ // this set the area axes margins (Bar Chart)
+ Chart.AxesMarginsTop = AxesMarginsTop;
+ Chart.AxesMarginsDown = AxesMarginsDown;
+ Chart.AxesMarginsLeft = AxesMarginsLeft;
+ Chart.AxesMarginsRight = AxesMarginsRight;
+
+ // Title settings
+ if (Title != null)
+ {
+ Chart.Title = Title;
+ Chart.TitleFontBold = TitleFontBold;
+ Chart.TitleFontSize = TitleFontSize;
+ Chart.TitleFontColor = TitleFontColor;
+ }
+
+ // This set the distance of the labels from the chart center (Radar Chart)
+ Chart.SpokesLength = SpokesLength;
+
+ // Font Settings
+ Chart.FontName = FontName;
+ Chart.LabelFontSize = LabelFontSize;
+ Chart.LabelFontColor = LabelFontColor;
+ Chart.LabelBold = LabelBold;
+
+ // Background color settings
+ Chart.FigureBackgroundColor = FigureBackgroundColor;
+ Chart.DataBackgroundColor = DataBackgroundColor;
+
+ // Watermark settings
+ Chart.EnableWatermark = EnableWatermark;
+ Chart.WatermarkText = WatermarkText;
+ Chart.WatermarkAlignment = WatermarkAlignment;
+ Chart.WatermarkFontName = WatermarkFontName;
+ Chart.WatermarkFontSize = WatermarkFontSize;
+ Chart.WatermarkColor = WatermarkColor;
+ Chart.WatermarkOpacity = WatermarkOpacity;
+ Chart.WatermarkRotation = WatermarkRotation;
+
+ // Set file directory save path
+ Chart.OutputFolderPath = OutputFolderPath;
+
+ Chart.Format = Format;
+
+ Radar myRadar = new Radar();
+
+ if (SpokeLabels != null && SpokeLabels.Length > 0)
+ {
+ WriteObject(myRadar.Chart(Values, LegendLabels, SpokeLabels, Filename, Width, Height));
+ }
+ else
+ {
+ WriteObject(myRadar.Chart(Values, LegendLabels, Filename, Width, Height));
+ }
+ }
+ else
+ {
+ WriteObject("Please provide both Values and LegendLabels parameters.");
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Sources/PowerShell/SignalChartPwsh.cs b/Sources/PowerShell/SignalChartPwsh.cs
index 78c70bf..2231b5c 100644
--- a/Sources/PowerShell/SignalChartPwsh.cs
+++ b/Sources/PowerShell/SignalChartPwsh.cs
@@ -170,6 +170,7 @@ public class NewSignalChartCommand : Cmdlet
// Set OutputFolderPath
[Parameter(Mandatory = false, HelpMessage = "Output folder path where the chart will be saved.")]
+ [ValidatePath()]
public string OutputFolderPath { get; set; } = Directory.GetCurrentDirectory();
protected override void ProcessRecord()
diff --git a/Sources/PowerShell/SingleStackedBarChartPwsh.cs b/Sources/PowerShell/SingleStackedBarChartPwsh.cs
index fed9e70..04ded83 100644
--- a/Sources/PowerShell/SingleStackedBarChartPwsh.cs
+++ b/Sources/PowerShell/SingleStackedBarChartPwsh.cs
@@ -167,6 +167,7 @@ public class NewSingleStackedBarChartCommand : Cmdlet
// Output folder
[Parameter(Mandatory = false, HelpMessage = "Set the output folder path for the chart file.")]
+ [ValidatePath()]
public string OutputFolderPath { get; set; } = Directory.GetCurrentDirectory();
protected override void ProcessRecord()
diff --git a/Sources/PowerShell/StackedBarChartPwsh.cs b/Sources/PowerShell/StackedBarChartPwsh.cs
index 94c5ca3..0aa39a5 100644
--- a/Sources/PowerShell/StackedBarChartPwsh.cs
+++ b/Sources/PowerShell/StackedBarChartPwsh.cs
@@ -165,6 +165,7 @@ public class NewStackedBarChartCommand : Cmdlet
// Set OutputFolderPath
[Parameter(Mandatory = false, HelpMessage = "Set the output folder path for the chart file.")]
+ [ValidatePath()]
public string OutputFolderPath { get; set; } = Directory.GetCurrentDirectory();
protected override void ProcessRecord()
diff --git a/Sources/RadarChart.cs b/Sources/RadarChart.cs
new file mode 100644
index 0000000..86c6af1
--- /dev/null
+++ b/Sources/RadarChart.cs
@@ -0,0 +1,251 @@
+using ScottPlot;
+using System;
+using System.IO;
+using System.Collections.Generic;
+namespace AsBuiltReportChart
+{
+ internal class Radar : Chart
+ {
+ public Radar() { }
+ public object Chart(List values, string[] labels, string filename = "output", int width = 400, int height = 300)
+ {
+ if (values.Count == labels.Length)
+ {
+ Plot myPlot = new Plot();
+
+ if (EnableCustomColorPalette)
+ {
+ if (_customColorPalette != null && _customColorPalette.Length > 0)
+ {
+ myPlot.Add.Palette = colorPalette = new ScottPlot.Palettes.Custom(_customColorPalette);
+ }
+ else
+ {
+ throw new InvalidOperationException("CustomColorPalette is empty. Please provide valid color values.");
+ }
+ }
+ else
+ {
+ // Set ScottPlot native color palette
+ if (colorPalette != null)
+ {
+ myPlot.Add.Palette = colorPalette;
+ }
+ }
+
+ var radar = myPlot.Add.Radar(values);
+
+ // set each slice value to its label
+ if (EnableLegend)
+ {
+ for (var i = 0; i < radar.Series.Count; i++)
+ {
+ radar.Series[i].LegendText = labels[i];
+ }
+ }
+
+ // hide unnecessary plot components
+ myPlot.Axes.Frameless();
+ myPlot.HideGrid();
+
+ if (EnableLegend)
+ {
+ // Legend Font Properties
+ myPlot.Legend.FontName = FontName;
+ myPlot.Legend.FontSize = LegendFontSize;
+ myPlot.Legend.FontColor = GetDrawingColor(LegendFontColor);
+
+ // Legend box Style Properties
+ myPlot.Legend.OutlineColor = GetDrawingColor(LegendBorderColor);
+ myPlot.Legend.OutlineWidth = LegendBorderSize;
+
+ myPlot.Legend.OutlinePattern = LegendBorderStyleMap[LegendBorderStyle];
+
+ myPlot.Legend.Orientation = LegendOrientationMap[LegendOrientation];
+
+ myPlot.Legend.Alignment = LegendAlignmentMap[LegendAlignment];
+ }
+
+ if (EnableChartBorder)
+ {
+ myPlot.FigureBorder = new LineStyle()
+ {
+ Color = GetDrawingColor(ChartBorderColor),
+ Width = ChartBorderSize,
+ Pattern = ChartBorderStyleMap[ChartBorderStyle],
+ };
+ }
+
+ // Set title properties
+ if (Title != null)
+ {
+ myPlot.Title(Title);
+ myPlot.Axes.Title.Label.FontSize = TitleFontSize;
+ myPlot.Axes.Title.Label.ForeColor = GetDrawingColor(TitleFontColor);
+ myPlot.Axes.Title.Label.Bold = TitleFontBold;
+ myPlot.Axes.Title.Label.FontName = FontName;
+ }
+
+ // Set margins settings
+ myPlot.Axes.Margins(left: AxesMarginsLeft, right: AxesMarginsRight, bottom: AxesMarginsDown, top: AxesMarginsTop);
+
+ // Set background colors
+ if (FigureBackgroundColor.HasValue)
+ {
+ myPlot.FigureBackground.Color = GetDrawingColor(FigureBackgroundColor.Value);
+ }
+ if (DataBackgroundColor.HasValue)
+ {
+ myPlot.DataBackground.Color = GetDrawingColor(DataBackgroundColor.Value);
+ }
+
+ // Set the distance of the chart area elements (Radar Chart)
+ // radar.SliceLabelDistance = _labelDistance;
+
+ // Apply watermark if enabled
+ ApplyWatermark(myPlot);
+
+ // Set filetpath to save
+ string Filepath = _outputFolderPath ?? Directory.GetCurrentDirectory();
+
+ // Set filename
+ return SaveInFormat(myPlot, width, height, Filepath, filename, Format);
+ }
+ else
+ {
+ throw new ArgumentException("Error: Values and labels must be equal.");
+ }
+ }
+ public object Chart(List values, string[] labels, string[] categoryNames, string filename = "output", int width = 400, int height = 300)
+ {
+ if (values.Count == labels.Length)
+ {
+ Plot myPlot = new Plot();
+
+ if (EnableCustomColorPalette)
+ {
+ if (_customColorPalette != null && _customColorPalette.Length > 0)
+ {
+ myPlot.Add.Palette = colorPalette = new ScottPlot.Palettes.Custom(_customColorPalette);
+ }
+ else
+ {
+ throw new InvalidOperationException("CustomColorPalette is empty. Please provide valid color values.");
+ }
+ }
+ else
+ {
+ // Set ScottPlot native color palette
+ if (colorPalette != null)
+ {
+ myPlot.Add.Palette = colorPalette;
+ }
+ }
+
+ var radar = myPlot.Add.Radar(values);
+
+ // set each slice value to its label
+ if (EnableLegend)
+ {
+ for (var i = 0; i < radar.Series.Count; i++)
+ {
+ radar.Series[i].LegendText = labels[i];
+ }
+ }
+
+ if (categoryNames != null)
+ {
+ if (categoryNames.Length > 0 && categoryNames.Length == values[0].Length)
+ {
+ radar.PolarAxis.SetSpokes(categoryNames, length: SpokesLength);
+ // set each PolarAxis value to its label
+ for (var i = 0; i < radar.PolarAxis.Spokes.Count; i++)
+ {
+ radar.PolarAxis.Spokes[i].LabelStyle.FontSize = LabelFontSize;
+ radar.PolarAxis.Spokes[i].LabelStyle.ForeColor = GetDrawingColor(LabelFontColor);
+ radar.PolarAxis.Spokes[i].LabelStyle.Bold = LabelBold;
+ radar.PolarAxis.Spokes[i].LabelStyle.FontName = FontName;
+ }
+ }
+ else
+ {
+ throw new ArgumentException("Error: SpokeLabels must be provided and its length must match the number of values in each series.");
+ }
+ }
+
+ // hide unnecessary plot components
+ myPlot.Axes.Frameless();
+ myPlot.HideGrid();
+
+ if (EnableLegend)
+ {
+ // Legend Font Properties
+ myPlot.Legend.FontName = FontName;
+ myPlot.Legend.FontSize = LegendFontSize;
+ myPlot.Legend.FontColor = GetDrawingColor(LegendFontColor);
+
+ // Legend box Style Properties
+ myPlot.Legend.OutlineColor = GetDrawingColor(LegendBorderColor);
+ myPlot.Legend.OutlineWidth = LegendBorderSize;
+
+ myPlot.Legend.OutlinePattern = LegendBorderStyleMap[LegendBorderStyle];
+
+ myPlot.Legend.Orientation = LegendOrientationMap[LegendOrientation];
+
+ myPlot.Legend.Alignment = LegendAlignmentMap[LegendAlignment];
+ }
+
+ if (EnableChartBorder)
+ {
+ myPlot.FigureBorder = new LineStyle()
+ {
+ Color = GetDrawingColor(ChartBorderColor),
+ Width = ChartBorderSize,
+ Pattern = ChartBorderStyleMap[ChartBorderStyle],
+ };
+ }
+
+ // Set title properties
+ if (Title != null)
+ {
+ myPlot.Title(Title);
+ myPlot.Axes.Title.Label.FontSize = TitleFontSize;
+ myPlot.Axes.Title.Label.ForeColor = GetDrawingColor(TitleFontColor);
+ myPlot.Axes.Title.Label.Bold = TitleFontBold;
+ myPlot.Axes.Title.Label.FontName = FontName;
+ }
+
+ // Set margins settings
+ myPlot.Axes.Margins(left: AxesMarginsLeft, right: AxesMarginsRight, bottom: AxesMarginsDown, top: AxesMarginsTop);
+
+ // Set background colors
+ if (FigureBackgroundColor.HasValue)
+ {
+ myPlot.FigureBackground.Color = GetDrawingColor(FigureBackgroundColor.Value);
+ }
+ if (DataBackgroundColor.HasValue)
+ {
+ myPlot.DataBackground.Color = GetDrawingColor(DataBackgroundColor.Value);
+ }
+
+ // Set the distance of the chart area elements (Radar Chart)
+ // radar.SliceLabelDistance = _labelDistance;
+
+ // Apply watermark if enabled
+ ApplyWatermark(myPlot);
+
+ // Set filetpath to save
+ string Filepath = _outputFolderPath ?? Directory.GetCurrentDirectory();
+
+ // Set filename
+ return SaveInFormat(myPlot, width, height, Filepath, filename, Format);
+ }
+ else
+ {
+ throw new ArgumentException("Error: Values and labels must be equal.");
+ }
+ }
+ }
+}
+
+
diff --git a/Tests/AsBuiltReport.Chart.Functions.Tests.ps1 b/Tests/AsBuiltReport.Chart.Functions.Tests.ps1
index 4d44cc3..faba0c6 100644
--- a/Tests/AsBuiltReport.Chart.Functions.Tests.ps1
+++ b/Tests/AsBuiltReport.Chart.Functions.Tests.ps1
@@ -41,6 +41,23 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
}
}
+ Context 'New-DonutChart' {
+ It 'Should run without error with sample input' {
+ { New-DonutChart -Title 'Test' -Values @(1, 2) -Labels @('A', 'B') -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
+ }
+ It 'Should return a file path as output' {
+ $result = New-DonutChart -Title 'Test' -Values @(1, 2) -Labels @('A', 'B') -Format 'png' -OutputFolderPath $TestDrive
+ $result | Should -BeOfType 'System.IO.FileSystemInfo'
+ Test-Path $result | Should -BeTrue
+ }
+ It 'Should throw error for missing mandatory parameters' {
+ { New-DonutChart } | Should -Throw
+ }
+ It 'Should throw error for mismatched Values and Labels' {
+ { New-DonutChart -Title 'Test' -Values @(1, 2) -Labels @('A') -Format 'png' -OutputFolderPath $TestDrive } | Should -Throw -ExpectedMessage "Error: Values and labels must be equal."
+ }
+ }
+
Context 'New-BarChart' {
It 'Should run without error with sample input' {
{ New-BarChart -Title 'Test' -Values @(1, 2) -Labels @('A', 'B') -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
@@ -77,10 +94,10 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
{ New-StackedBarChart -Title 'Test' -Values @(@(1, 2), @(3, 4)) -Labels @('A', 'B') -LegendCategories @('X') -Format 'png' -OutputFolderPath $TestDrive } | Should -Throw -ExpectedMessage "Error: Each set of values must have the same length as category names."
}
It 'Should run without error with a single element (single-bar chart)' {
- { New-StackedBarChart -Title 'Test' -Values @(,[double[]]@(1, 2)) -Labels @('A') -LegendCategories @('X', 'Y') -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
+ { New-StackedBarChart -Title 'Test' -Values @(, [double[]]@(1, 2)) -Labels @('A') -LegendCategories @('X', 'Y') -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
It 'Should return a file path as output for a single element' {
- $result = New-StackedBarChart -Title 'Test' -Values @(,[double[]]@(1, 2)) -Labels @('A') -LegendCategories @('X', 'Y') -Format 'png' -OutputFolderPath $TestDrive
+ $result = New-StackedBarChart -Title 'Test' -Values @(, [double[]]@(1, 2)) -Labels @('A') -LegendCategories @('X', 'Y') -Format 'png' -OutputFolderPath $TestDrive
$result | Should -BeOfType 'System.IO.FileSystemInfo'
Test-Path $result | Should -BeTrue
}
@@ -126,10 +143,10 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
Context 'New-SignalChart' {
It 'Should run without error with a single signal line' {
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
It 'Should return a file path as output' {
- $result = New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive
+ $result = New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive
$result | Should -BeOfType 'System.IO.FileSystemInfo'
Test-Path $result | Should -BeTrue
}
@@ -138,15 +155,15 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
}
It 'Should run without error with DateTime ticks' {
$xOffset = (Get-Date '2024-01-01').ToOADate()
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3)) -XOffset $xOffset -Period 1.0 -DateTimeTicksBottom -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3)) -XOffset $xOffset -Period 1.0 -DateTimeTicksBottom -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
It 'Should run without error in scatter mode with explicit X values' {
- $xValues = @(,[double[]]@(1.0, 2.0, 3.0, 4.0))
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -ScatterXValues $xValues -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
+ $xValues = @(, [double[]]@(1.0, 2.0, 3.0, 4.0))
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -ScatterXValues $xValues -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
It 'Should return a file path as output in scatter mode' {
- $xValues = @(,[double[]]@(1.0, 2.0, 3.0, 4.0))
- $result = New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -ScatterXValues $xValues -Format 'png' -OutputFolderPath $TestDrive
+ $xValues = @(, [double[]]@(1.0, 2.0, 3.0, 4.0))
+ $result = New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -ScatterXValues $xValues -Format 'png' -OutputFolderPath $TestDrive
$result | Should -BeOfType 'System.IO.FileSystemInfo'
Test-Path $result | Should -BeTrue
}
@@ -154,7 +171,7 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
$startDate = (Get-Date '2024-01-01').ToOADate()
{
$xArr = [double[]]@($startDate, ($startDate + 1.0), ($startDate + 2.0))
- New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3)) -ScatterXValues @(,$xArr) -DateTimeTicksBottom -Format 'png' -OutputFolderPath $TestDrive
+ New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3)) -ScatterXValues @(, $xArr) -DateTimeTicksBottom -Format 'png' -OutputFolderPath $TestDrive
} | Should -Not -Throw
}
It 'Should run without error in scatter mode with multiple lines' {
@@ -162,12 +179,12 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
{ New-SignalChart -Title 'Test' -Values @(@(1, 2, 3), @(4, 5, 6)) -ScatterXValues $xValues -Labels @('Line1', 'Line2') -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
It 'Should throw error when ScatterXValues count does not match Values count' {
- $xValues = @(,[double[]]@(1.0, 2.0, 3.0))
+ $xValues = @(, [double[]]@(1.0, 2.0, 3.0))
{ New-SignalChart -Title 'Test' -Values @(@(1, 2, 3), @(4, 5, 6)) -ScatterXValues $xValues -Format 'png' -OutputFolderPath $TestDrive } | Should -Throw -ExpectedMessage "Error: XValues and Values must have the same number of arrays."
}
It 'Should throw error when XValues elements count does not match Values elements count' {
- $xValues = @(,[double[]]@(1.0, 2.0))
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3)) -ScatterXValues $xValues -Format 'png' -OutputFolderPath $TestDrive } | Should -Throw -ExpectedMessage "Error: XValues and Values at index 0 must have the same number of elements."
+ $xValues = @(, [double[]]@(1.0, 2.0))
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3)) -ScatterXValues $xValues -Format 'png' -OutputFolderPath $TestDrive } | Should -Throw -ExpectedMessage "Error: XValues and Values at index 0 must have the same number of elements."
}
It 'Should throw error for missing mandatory parameters' {
{ New-SignalChart } | Should -Throw
@@ -277,18 +294,18 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
Context 'New-SignalChart with watermark' {
It 'Should run without error with watermark enabled using defaults' {
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark } | Should -Not -Throw
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark } | Should -Not -Throw
}
It 'Should return a file when watermark is enabled' {
- $result = New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark
+ $result = New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark
$result | Should -BeOfType 'System.IO.FileSystemInfo'
Test-Path $result | Should -BeTrue
}
It 'Should run without error with custom watermark color and opacity' {
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark -WatermarkColor Green -WatermarkOpacity 0.4 } | Should -Not -Throw
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark -WatermarkColor Green -WatermarkOpacity 0.4 } | Should -Not -Throw
}
It 'Should run without error without watermark (disabled by default)' {
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3, 4)) -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
}
}
@@ -332,8 +349,8 @@ Describe 'AsBuiltReport.Chart Exported Functions' {
{ New-BarChart -Title 'Test' -Values @(1, 2) -Labels @('A', 'B') -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
It 'EnableWatermark should not persist from one New-SignalChart call to the next' {
- New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark | Out-Null
- { New-SignalChart -Title 'Test' -Values @(,[double[]]@(1, 2, 3)) -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
+ New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3)) -Format 'png' -OutputFolderPath $TestDrive -EnableWatermark | Out-Null
+ { New-SignalChart -Title 'Test' -Values @(, [double[]]@(1, 2, 3)) -Format 'png' -OutputFolderPath $TestDrive } | Should -Not -Throw
}
}
}
diff --git a/Todo.md b/Todo.md
index 8b13789..d6cc011 100644
--- a/Todo.md
+++ b/Todo.md
@@ -1 +1,2 @@
-
+[Done] Add radar chart support: [Radar Chart](https://scottplot.net/cookbook/5/Radar/)
+[] Calculate the spoke length based on the maximum value in the dataset to ensure the radar chart is properly scaled.
\ No newline at end of file