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 = TopologyRecipe.Create("expanded", mode);
var context = new VisualRenderContext(
new VisualLayoutOptions(new VisualSize(960, 600)), theme, mode,
new VisualFrame("Service request topology", "Groups, status, details and routed links", showLegend: true, 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.Topology).ToHtmlPage());
/// Creates a service topology or an atlas of the available node shapes.
public static class TopologyRecipe {
/// Creates the diagram model for an expanded, compact or shape-atlas view.
/// Use wide, expanded or compact for the service topology, or shapes for the shape atlas.
/// The rendering mode; diagram colors are resolved when the model is prepared.
public static TopologyChart Create(string variant = "wide", VisualThemeMode mode = VisualThemeMode.Light) {
if (variant is not ("wide" or "expanded" or "compact" or "shapes"))
throw new ArgumentOutOfRangeException(nameof(variant), variant, "Use wide, expanded, compact or shapes.");
var atlas = variant == "shapes";
var chart = TopologyChart.Create();
chart.Id = atlas ? "shape-atlas" : "service-topology";
chart.DefaultRenderOptions = new TopologyRenderOptions {
FitContentToViewport = true,
WrapNodeLabels = true,
IncludeLegend = true,
IncludeGroupStatusDots = true
};
if (atlas) {
var shapes = Enum.GetValues();
for (var index = 0; index < shapes.Length; index++) {
chart.Nodes.Add(new TopologyNode {
Id = "shape-" + index,
Label = shapes[index].ToString(),
X = 24 + index % 5 * 160,
Y = 24 + index / 5 * 120,
Width = 132,
Height = 86,
Shape = shapes[index],
PreserveDisplayModeSize = true,
ShowStatusBadge = false
});
}
return chart;
}
chart.Groups.Add(new TopologyGroup {
Id = "primary", Label = "Primary region", X = 0, Y = 0, Width = 340, Height = 300,
Status = TopologyHealthStatus.Healthy
});
chart.Groups.Add(new TopologyGroup {
Id = "secondary", Label = "Secondary region", X = 390, Y = 0, Width = 340, Height = 300,
Status = TopologyHealthStatus.Warning
});
var source = new TopologyNode {
Id = "client", Label = "Client service", Subtitle = "Requests", X = 30, Y = 65, Width = 240, Height = 85,
Kind = TopologyNodeKind.Server, GroupId = "primary", Status = TopologyHealthStatus.Healthy
};
source.Details.Add(new TopologyNodeDetail {
Label = "State", Value = "Available", Status = TopologyHealthStatus.Healthy
});
chart.Nodes.Add(source);
chart.Nodes.Add(new TopologyNode {
Id = "cache", Label = "Cache", X = 70, Y = 210, Width = 150, Height = 65,
Shape = TopologyNodeShape.Cylinder, GroupId = "primary"
});
chart.Nodes.Add(new TopologyNode {
Id = "worker", Label = "Processing service", X = 430, Y = 65, Width = 240, Height = 85,
Kind = TopologyNodeKind.Server, GroupId = "secondary", Status = TopologyHealthStatus.Warning
});
chart.Nodes.Add(new TopologyNode {
Id = "store", Label = "Result store", X = 470, Y = 210, Width = 150, Height = 65,
Shape = TopologyNodeShape.Cylinder, GroupId = "secondary"
});
chart.Edges.Add(new TopologyEdge {
Id = "request", SourceNodeId = "client", TargetNodeId = "worker", Label = "Requests", SecondaryLabel = "Active",
Routing = TopologyEdgeRouting.ObstacleAvoidingOrthogonal, Direction = VisualLinkDirection.Forward
});
chart.Edges.Add(new TopologyEdge {
Id = "cache-read", SourceNodeId = "client", TargetNodeId = "cache", Label = "Lookup", Routing = TopologyEdgeRouting.Curved,
LineStyle = TopologyEdgeLineStyle.Dashed, Direction = VisualLinkDirection.Bidirectional
});
chart.Edges.Add(new TopologyEdge {
Id = "persist", SourceNodeId = "worker", TargetNodeId = "store", Label = "Persist",
Routing = TopologyEdgeRouting.Orthogonal
});
chart.Legend = TopologyLegend.Create()
.AddStatus("Available", TopologyHealthStatus.Healthy)
.AddStatus("Review", TopologyHealthStatus.Warning)
.AddNodeKind("Service", TopologyNodeKind.Server)
.AddEdgeKind("Dependency", TopologyEdgeKind.Dependency, lineStyle: TopologyEdgeLineStyle.Dashed);
return chart;
}
}