#region Setup
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;
var mode = VisualThemeMode.Dark;
var theme = VisualTheme.Graphite();
var model = PyramidPresentationRecipe.Create("reversed", mode);
var context = new VisualRenderContext(
new VisualLayoutOptions(new VisualSize(800, 560)), theme, mode,
new VisualFrame("Independent allocation captions and borders", "Area encoding retains source order with separate category and value placement", showLegend: null, showSurface: false));
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());
#endregion
/// Builds triangular allocation partitions with independent category/value captions and source-owned borders.
public static class PyramidPresentationRecipe {
/// Creates the full allocation example. Exact documentation typography needs Carlito regular/bold installed or registered; other hosts use normal font fallback.
public static Chart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) {
if (variant is not ("wide" or "compact" or "horizontal" or "compact-horizontal" or "reversed" or "compact-reversed")) throw new ArgumentOutOfRangeException(nameof(variant));
var compact = variant.StartsWith("compact", StringComparison.Ordinal);
var horizontal = variant.EndsWith("horizontal", StringComparison.Ordinal);
var reversed = variant.EndsWith("reversed", StringComparison.Ordinal);
var chart = Chart.Create().WithSize(compact ? 390 : 800, compact ? 500 : 560)
.WithTheme(VisualTheme.Graphite(), mode).WithTitle("Allocation by service")
.WithSubtitle("Partition area encodes values; caption placement and borders preserve source order")
.WithLegend(false).WithDataLabels().WithXLabels("Services", "Platform", "Support", "Unassigned");
var series = new ChartSeries(new ChartSeriesId("allocation"), "Allocation", ChartSeriesKind.Pyramid,
new[] { new ChartPoint(1, 50), new ChartPoint(2, 30), new ChartPoint(3, 20), new ChartPoint(4, 0) })
.WithObservationIds(new[] { new ChartObservationId("services"), new ChartObservationId("platform"), new ChartObservationId("support"), new ChartObservationId("unassigned") })
.WithPointColor(0, "#357BA4").WithPointColor(1, "#C18B3D").WithPointColor(2, "#4D947D")
.WithPointFillPattern(1, ChartFillPattern.DiagonalForward).WithPointStrokeColor(1, ChartColor.FromHex("#8D3E69"));
chart.AddSeries(series).ConfigurePyramid(options => {
options.ValueEncoding = ChartPyramidValueEncoding.Area; options.Reversed = reversed;
options.Orientation = horizontal ? ChartOrientation.Horizontal : ChartOrientation.Vertical;
options.LabelContent = ChartStageLabelContent.Separate;
options.CategoryLabel.Style.FontSize = 12; options.ValueLabel.Style.FontSize = 13;
options.PartitionOutline = new ChartStrokeStyle(width: 2, dash: new[] { 6d, 2d });
options.PartitionOutlineDashOffset = -1;
});
return chart.WithValueFormat(ChartValueFormat.Number("0", CultureInfo.InvariantCulture));
}
}