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 = 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), 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.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; } }