using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using ChartForgeX.Core; using ChartForgeX.Primitives; using ChartForgeX.Rendering; using ChartForgeX.Themes; using ChartForgeX.Topology; using ChartForgeX.Typography; using ChartForgeX.VisualArtifacts; using ChartForgeX.VisualBlocks; // Optional exact gallery typography; copy the gallery's fonts directory beside this program. FontRegistry.Register("CFX Proof Carlito", "fonts/Carlito-Regular.ttf", 400); FontRegistry.Register("CFX Proof Carlito", "fonts/Carlito-Bold.ttf", 700); var mode = VisualThemeMode.Dark; var theme = VisualTheme.Graphite(); var model = RadialColumnRecipe.Create("wide", mode); var context = new VisualRenderContext( new VisualLayoutOptions(new VisualSize(800, 440)), theme, mode, new VisualFrame("Regional service workload", "Explore the data, then download the chart", showLegend: null, showSurface: false), FontSpec.FromFamily("CFX Proof Carlito")); var prepared = model.Prepare(context); File.WriteAllText("visual.svg", prepared.ToSvg()); File.WriteAllBytes("visual.png", prepared.ToPng()); File.WriteAllText("visual.html", prepared.ToArtifact("example", VisualArtifactKind.Chart).ToHtmlPage()); /// Regional requests and follow-ups encoded as numeric radial columns. public static class RadialColumnRecipe { /// Creates radial columns with optional normalized stacking or explicit axis-caption density. public static Chart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) { if (variant is not ("wide" or "compact" or "sparse" or "options" or "compact-options" or "axis-labels" or "compact-axis-labels" or "all-labels" or "compact-all-labels")) throw new ArgumentOutOfRangeException(nameof(variant)); if (variant is "all-labels" or "compact-all-labels") return AllLabels(); var requests = new ChartSeries(new ChartSeriesId("requests"), "Requests", ChartSeriesKind.RadialColumn, Points(1200, 950, 680, 1050)); var followUps = new ChartSeries(new ChartSeriesId("follow-ups"), "Follow-ups", ChartSeriesKind.RadialColumn, Points(320, 410, 260, 360)); var chart = Chart.Create().WithXLabels("North", "South", "East", "West") .WithRadialGeometry(new ChartRadialGeometryOptions(-90, 180, .22, .22, .15)) .AddSeries(requests).AddSeries(followUps).WithDataLabels().WithDataLabelPlacement(ChartDataLabelPlacement.Inside) .WithValueFormat(ChartValueFormat.Number("0.##", System.Globalization.CultureInfo.InvariantCulture)); if (variant is "options" or "compact-options") { requests.WithStackGroup("work").WithNormalization(100); followUps.WithStackGroup("work").WithNormalization(100).WithPointFillPattern(1, ChartFillPattern.DiagonalForward); chart.WithRadialGeometry(new ChartRadialGeometryOptions(-90, 180, .22, .22, .15, cornerRadius: 6)) .WithYAxisBounds(0, 100).WithXAxis("Region").WithYAxis("Share of regional workload (%)") .ConfigureXAxis(axis => axis.LabelAngle = -30).ConfigureYAxis(axis => axis.LabelAngle = 30); } else { chart.WithYAxisBounds(0, 1500); } if (variant is "axis-labels" or "compact-axis-labels") { chart.WithXAxis("Region").WithYAxis("Requests") .ConfigureXAxis(axis => { axis.LabelOrientation = ChartAxisLabelOrientation.Parallel; axis.LabelAngle = 10; axis.LabelDensity = ChartLabelDensity.Relaxed; }) .ConfigureYAxis(axis => { axis.LabelOrientation = ChartAxisLabelOrientation.Perpendicular; axis.LabelDensity = ChartLabelDensity.Dense; }); } return chart; } private static Chart AllLabels() { var requests = new ChartSeries(new ChartSeriesId("requests"), "Requests", ChartSeriesKind.RadialColumn, Enumerable.Range(1, 20).Select(index => new ChartPoint(index, 500 + index * 20))); var chart = Chart.Create().WithXLabels(Enumerable.Range(1, 20).Select(index => "Region " + index + " request volume").ToArray()) .WithRadialGeometry(new ChartRadialGeometryOptions(-75, 195, .2, .25, .1)) .WithYAxisBounds(0, 1500).WithDataLabels(false).WithXAxis("All region captions; overlaps are intentional") .ConfigureXAxis(axis => axis.LabelDensity = ChartLabelDensity.All).AddSeries(requests) .WithValueFormat(ChartValueFormat.Number("0.##", System.Globalization.CultureInfo.InvariantCulture)); chart.Options.YAxis.Visible = false; return chart; } private static ChartPoint[] Points(params double[] values) => values.Select((value, index) => new ChartPoint(index + 1, value)).ToArray(); }