← Gallery

Combination chart

Checks over the reporting period

Explore the example, download the output or use the C# source in your own project.

Preview
Checks over the reporting periodChart with 3 data series: Observed, Expected, Capacity.Checks over the reporting periodOne prepared scene for SVG and native PNGObservedExpectedCapacityDay 1Day 2Day 3Day 4Day 5Day 6Day 70204060
Checks over the reporting period
Checks over the reporting periodChart with 3 data series: Observed, Expected, Capacity.Checks over the reporting periodOne prepared scene for SVG and native PNGObservedExpectedCapacityDay 1Day 2Day 3Day 4Day 5Day 6Day 70204060
Checks over the reporting period
360 × 360 pixels

Create this example

Create a console project, add the ChartForgeX package, and replace Program.cs with this source. Run it to write SVG, PNG and HTML with the default system fonts.

For the same typography as the gallery, download the gallery-font recipe and copy the gallery’s fonts directory beside your program.

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.Light;
var theme = VisualTheme.Graphite();
var model = ComparisonRecipe.Create("compact", mode);
var context = new VisualRenderContext(
    new VisualLayoutOptions(new VisualSize(360, 360)), theme, mode,
    new VisualFrame("Checks over the reporting period", "One prepared scene for SVG and native PNG", 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());

/// <summary>Line, area and bar observations share categories, including explicit missing and empty states.</summary>
public static class ComparisonRecipe {
    public static Chart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) {
        var count = variant == "empty" ? 0 : variant == "dense" ? 24 : 7;
        var names = variant == "wrapped-legend"
            ? new[] { "Observed checks across all locations", "Expected coverage of monitored services", "Capacity available for the next assessment" }
            : new[] { "Observed", "Expected", "Capacity" };
        var observed = ChartSeries.CreateLine(new ChartSeriesId("observed"), names[0], Points(count, variant, 0));
        var expected = ChartSeries.CreateArea(new ChartSeriesId("expected"), names[1], Points(count, variant, 1));
        var capacity = ChartSeries.CreateBar(new ChartSeriesId("capacity"), names[2], Points(count, variant, 2, false));
        if (variant is "explicit-status" or "explicit-series") {
            var colors = VisualTheme.Graphite().Resolve(mode);
            var selected = variant == "explicit-status"
                ? new[] { colors.Status.Pass.Fill, colors.Status.Medium.Fill, colors.Status.Critical.Fill }
                : colors.Palette.Take(3).ToArray();
            observed.WithColor(selected[0]); expected.WithColor(selected[1]); capacity.WithColor(selected[2]);
        }
        return Chart.Create().WithXLabels(Enumerable.Range(1, count).Select(index => "Day " + index).ToArray())
            .AddSeries(observed).AddSeries(expected).AddSeries(capacity);
    }

    private static IEnumerable<ChartPoint> Points(int count, string variant, int series, bool allowBreaks = true) {
        for (var index = 0; index < count; index++) {
            if (variant == "missing" && (index == 2 || index == 3)) continue;
            var value = variant == "zero" ? 0 : 20 + series * 14 + index * (series + 3) % 23;
            yield return new ChartPoint(index + 1, value, allowBreaks && variant == "missing" && index == 4);
        }
    }
}
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 = ComparisonRecipe.Create("compact", mode);
var context = new VisualRenderContext(
    new VisualLayoutOptions(new VisualSize(360, 360)), theme, mode,
    new VisualFrame("Checks over the reporting period", "One prepared scene for SVG and native PNG", 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());

/// <summary>Line, area and bar observations share categories, including explicit missing and empty states.</summary>
public static class ComparisonRecipe {
    public static Chart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) {
        var count = variant == "empty" ? 0 : variant == "dense" ? 24 : 7;
        var names = variant == "wrapped-legend"
            ? new[] { "Observed checks across all locations", "Expected coverage of monitored services", "Capacity available for the next assessment" }
            : new[] { "Observed", "Expected", "Capacity" };
        var observed = ChartSeries.CreateLine(new ChartSeriesId("observed"), names[0], Points(count, variant, 0));
        var expected = ChartSeries.CreateArea(new ChartSeriesId("expected"), names[1], Points(count, variant, 1));
        var capacity = ChartSeries.CreateBar(new ChartSeriesId("capacity"), names[2], Points(count, variant, 2, false));
        if (variant is "explicit-status" or "explicit-series") {
            var colors = VisualTheme.Graphite().Resolve(mode);
            var selected = variant == "explicit-status"
                ? new[] { colors.Status.Pass.Fill, colors.Status.Medium.Fill, colors.Status.Critical.Fill }
                : colors.Palette.Take(3).ToArray();
            observed.WithColor(selected[0]); expected.WithColor(selected[1]); capacity.WithColor(selected[2]);
        }
        return Chart.Create().WithXLabels(Enumerable.Range(1, count).Select(index => "Day " + index).ToArray())
            .AddSeries(observed).AddSeries(expected).AddSeries(capacity);
    }

    private static IEnumerable<ChartPoint> Points(int count, string variant, int series, bool allowBreaks = true) {
        for (var index = 0; index < count; index++) {
            if (variant == "missing" && (index == 2 || index == 3)) continue;
            var value = variant == "zero" ? 0 : 20 + series * 14 + index * (series + 3) % 23;
            yield return new ChartPoint(index + 1, value, allowBreaks && variant == "missing" && index == 4);
        }
    }
}