From b4396f23173bcc222537b403ca88e2a980174352 Mon Sep 17 00:00:00 2001 From: "Frank R. Haugen" Date: Sun, 28 Apr 2024 03:46:30 +0200 Subject: [PATCH] Implement Mermaid charting and graph functionalities This commit introduces the ability to generate Mermaid diagrams using C# language. It incorporates new classes such as XyChart, Timeline, and Flowchart. Furthermore, it implements tests for these features to guarantee their correctness. Noticeably, it supports customizing different properties of graphs, such as the shape of nodes in a flowchart, and the direction of links. Useful additions include an 'IMermaidable' interface and numerous helper classes like Node, Link, Subgraph, and Series. --- Frank.Mermaid.Tests/FlowchartTests.cs | 104 ++++++++++++++++++ Frank.Mermaid.Tests/TimelineTests.cs | 39 +++++++ Frank.Mermaid.Tests/UnitTest1.cs | 9 -- Frank.Mermaid/Class1.cs | 5 - Frank.Mermaid/Flowchart/Direction.cs | 10 ++ .../Flowchart/DirectionExtensions.cs | 17 +++ Frank.Mermaid/Flowchart/Flowchart.cs | 55 +++++++++ Frank.Mermaid/Flowchart/Line.cs | 50 +++++++++ Frank.Mermaid/Flowchart/LineStyle.cs | 11 ++ Frank.Mermaid/Flowchart/Link.cs | 69 ++++++++++++ Frank.Mermaid/Flowchart/LinkType.cs | 9 ++ Frank.Mermaid/Flowchart/Node.cs | 46 ++++++++ Frank.Mermaid/Flowchart/Shape.cs | 12 ++ Frank.Mermaid/Flowchart/Subgraph.cs | 67 +++++++++++ Frank.Mermaid/Frank.Mermaid.csproj | 4 + Frank.Mermaid/IMermaidable.cs | 27 +++++ Frank.Mermaid/Timeline/Event.cs | 45 ++++++++ Frank.Mermaid/Timeline/Section.cs | 35 ++++++ Frank.Mermaid/Timeline/TimePeriod.cs | 15 +++ Frank.Mermaid/Timeline/Timeline.cs | 45 ++++++++ Frank.Mermaid/XyChart/Axis.cs | 23 ++++ Frank.Mermaid/XyChart/Point.cs | 20 ++++ Frank.Mermaid/XyChart/Series.cs | 28 +++++ Frank.Mermaid/XyChart/XyChart.cs | 38 +++++++ 24 files changed, 769 insertions(+), 14 deletions(-) create mode 100644 Frank.Mermaid.Tests/FlowchartTests.cs create mode 100644 Frank.Mermaid.Tests/TimelineTests.cs delete mode 100644 Frank.Mermaid.Tests/UnitTest1.cs delete mode 100644 Frank.Mermaid/Class1.cs create mode 100644 Frank.Mermaid/Flowchart/Direction.cs create mode 100644 Frank.Mermaid/Flowchart/DirectionExtensions.cs create mode 100644 Frank.Mermaid/Flowchart/Flowchart.cs create mode 100644 Frank.Mermaid/Flowchart/Line.cs create mode 100644 Frank.Mermaid/Flowchart/LineStyle.cs create mode 100644 Frank.Mermaid/Flowchart/Link.cs create mode 100644 Frank.Mermaid/Flowchart/LinkType.cs create mode 100644 Frank.Mermaid/Flowchart/Node.cs create mode 100644 Frank.Mermaid/Flowchart/Shape.cs create mode 100644 Frank.Mermaid/Flowchart/Subgraph.cs create mode 100644 Frank.Mermaid/IMermaidable.cs create mode 100644 Frank.Mermaid/Timeline/Event.cs create mode 100644 Frank.Mermaid/Timeline/Section.cs create mode 100644 Frank.Mermaid/Timeline/TimePeriod.cs create mode 100644 Frank.Mermaid/Timeline/Timeline.cs create mode 100644 Frank.Mermaid/XyChart/Axis.cs create mode 100644 Frank.Mermaid/XyChart/Point.cs create mode 100644 Frank.Mermaid/XyChart/Series.cs create mode 100644 Frank.Mermaid/XyChart/XyChart.cs diff --git a/Frank.Mermaid.Tests/FlowchartTests.cs b/Frank.Mermaid.Tests/FlowchartTests.cs new file mode 100644 index 0000000..3186970 --- /dev/null +++ b/Frank.Mermaid.Tests/FlowchartTests.cs @@ -0,0 +1,104 @@ +using Frank.Mermaid.Flowchart; +using Frank.Mermaid.Timeline; +using Xunit.Abstractions; + +namespace Frank.Mermaid.Tests; + +public class FlowchartTests +{ + private readonly ITestOutputHelper _outputHelper; + + public FlowchartTests(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + } + + [Fact] + public void Test1() + { + var flowchart = new Flowchart.Flowchart(); + var node1 = new Node("Node 1"); + var node2 = new Node("Node 2"); + flowchart.AddNode(node1); + flowchart.AddNode(node2); + + var link = new Link(node1, node2); + flowchart.AddLink(link); + + var subgraph = new Subgraph("Subgraph 1", Direction.TopToBottom); + var subgraphNode1 = new Node("Subgraph Node 1"); + var subgraphNode2 = new Node("Subgraph Node 2"); + subgraph.AddNode(subgraphNode1); + subgraph.AddNode(subgraphNode2); + + var subgraphLink = new Link(subgraphNode1, subgraphNode2, "Subgraph Link 1"); + subgraph.AddLink(subgraphLink); + + var link2 = new Link(subgraph, node2, "Subgraph Link to Node 2"); + flowchart.AddLink(link2); + + flowchart.AddSubgraph(subgraph); + + var writer = flowchart.ToMermaidSyntax(); + var result = writer.ToString(); + + _outputHelper.WriteLine(result); + } + + [Fact] + public void Test2() + { + /* + Testcase: + flowchart LR + subgraph subgraph1 + direction TB + top1[top] --> bottom1[bottom] + end + subgraph subgraph2 + direction TB + top2[top] --> bottom2[bottom] + end + %% ^ These subgraphs are identical, except for the links to them: + + %% Link *to* subgraph1: subgraph1 direction is maintained + outside --> subgraph1 + %% Link *within* subgraph2: + %% subgraph2 inherits the direction of the top-level graph (LR) + outside ---> top2 + */ + + var flowchart = new Flowchart.Flowchart(); + + var subgraph1 = new Subgraph("subgraph1", Direction.TopToBottom); + var top1 = new Node("top"); + var bottom1 = new Node("bottom"); + subgraph1.AddNode(top1); + subgraph1.AddNode(bottom1); + var link1 = new Link(top1, bottom1); + subgraph1.AddLink(link1); + flowchart.AddSubgraph(subgraph1); + + var subgraph2 = new Subgraph("subgraph2", Direction.TopToBottom); + var top2 = new Node("top"); + var bottom2 = new Node("bottom"); + subgraph2.AddNode(top2); + subgraph2.AddNode(bottom2); + var link2 = new Link(top2, bottom2); + subgraph2.AddLink(link2); + flowchart.AddSubgraph(subgraph2); + + var outside = new Node("outside"); + flowchart.AddNode(outside); + var link3 = new Link(outside, subgraph1); + flowchart.AddLink(link3); + + var link4 = new Link(outside, top2); + flowchart.AddLink(link4); + + var writer = flowchart.ToMermaidSyntax(); + var result = writer.ToString(); + + _outputHelper.WriteLine(result); + } +} \ No newline at end of file diff --git a/Frank.Mermaid.Tests/TimelineTests.cs b/Frank.Mermaid.Tests/TimelineTests.cs new file mode 100644 index 0000000..ade07dd --- /dev/null +++ b/Frank.Mermaid.Tests/TimelineTests.cs @@ -0,0 +1,39 @@ +using Frank.Mermaid.Timeline; +using Xunit.Abstractions; + +namespace Frank.Mermaid.Tests; + +public class TimelineTests +{ + private readonly ITestOutputHelper _outputHelper; + + public TimelineTests(ITestOutputHelper outputHelper) + { + _outputHelper = outputHelper; + } + + [Fact] + public void Test1() + { + var timeline = new Timeline.Timeline("My Timeline"); + var section1 = new Section("Section 1"); + section1.AddEvent(new Event("Event 1", new DateTime(2022, 1, 1))); + section1.AddEvent(new Event("Event 2", new DateTime(2022, 1, 2))); + timeline.AddSection(section1); + + var section2 = new Section("Section 2"); + section2.AddEvent(new Event("Event 3", new DateTime(2022, 1, 3))); + section2.AddEvent(new Event("Event 4", new DateTime(2022, 1, 4))); + timeline.AddSection(section2); + + var section3 = new Section("Section 3"); + section3.AddEvent(new Event("Event 5", new DateTime(2022, 1, 5))); + section3.AddEvent(new Event("Event 6", new DateTime(2022, 1, 6))); + timeline.AddSection(section3); + + var writer = timeline.ToMermaidSyntax(); + var result = writer.ToString(); + + _outputHelper.WriteLine(result); + } +} \ No newline at end of file diff --git a/Frank.Mermaid.Tests/UnitTest1.cs b/Frank.Mermaid.Tests/UnitTest1.cs deleted file mode 100644 index e25f9f7..0000000 --- a/Frank.Mermaid.Tests/UnitTest1.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Frank.Mermaid.Tests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - } -} \ No newline at end of file diff --git a/Frank.Mermaid/Class1.cs b/Frank.Mermaid/Class1.cs deleted file mode 100644 index e3c68ad..0000000 --- a/Frank.Mermaid/Class1.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace Frank.Mermaid; - -public class Class1 -{ -} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/Direction.cs b/Frank.Mermaid/Flowchart/Direction.cs new file mode 100644 index 0000000..8bcf14e --- /dev/null +++ b/Frank.Mermaid/Flowchart/Direction.cs @@ -0,0 +1,10 @@ +namespace Frank.Mermaid.Flowchart; + +public enum Direction +{ + TopToBottom, + TopDown, + BottomToTop, + RightToLeft, + LeftToRight +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/DirectionExtensions.cs b/Frank.Mermaid/Flowchart/DirectionExtensions.cs new file mode 100644 index 0000000..d161d13 --- /dev/null +++ b/Frank.Mermaid/Flowchart/DirectionExtensions.cs @@ -0,0 +1,17 @@ +namespace Frank.Mermaid.Flowchart; + +public static class DirectionExtensions +{ + public static string ToMermaidSyntax(this Direction direction) + { + return direction switch + { + Direction.TopToBottom => "TB", + Direction.TopDown => "TD", + Direction.BottomToTop => "BT", + Direction.RightToLeft => "RL", + Direction.LeftToRight => "LR", + _ => throw new ArgumentOutOfRangeException(nameof(direction), direction, null) + }; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/Flowchart.cs b/Frank.Mermaid/Flowchart/Flowchart.cs new file mode 100644 index 0000000..d8b130d --- /dev/null +++ b/Frank.Mermaid/Flowchart/Flowchart.cs @@ -0,0 +1,55 @@ +using CodegenCS; + +namespace Frank.Mermaid.Flowchart; + +public class Flowchart : IMermaidable +{ + private readonly List _nodes = new(); + private readonly List _links = new(); + private readonly List _subgraphs = new(); + private readonly Direction _direction; + + public Flowchart(Direction direction = Direction.TopToBottom) + { + _direction = direction; + } + + public void AddSubgraph(Subgraph subgraph) => _subgraphs.Add(subgraph); + public void AddSubgraphs(IEnumerable subgraphs) => _subgraphs.AddRange(subgraphs); + + public void AddNode(Node node) => _nodes.Add(node); + public void AddNodes(IEnumerable nodes) => _nodes.AddRange(nodes); + + public void AddLink(Link link) => _links.Add(link); + public void AddLinks(IEnumerable links) => _links.AddRange(links); + + /// + public Guid Id { get; } = Guid.NewGuid(); + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("flowchart {0}", _direction.ToMermaidSyntax()); + writer.IncreaseIndent(); + + foreach (var node in _nodes) + { + writer.Write(node.ToMermaidSyntax()); + } + + foreach (var link in _links) + { + writer.Write(link.ToMermaidSyntax()); + } + + foreach (var subgraph in _subgraphs) + { + writer.Write(subgraph.ToMermaidSyntax()); + } + + writer.DecreaseIndent(); + + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/Line.cs b/Frank.Mermaid/Flowchart/Line.cs new file mode 100644 index 0000000..36904b7 --- /dev/null +++ b/Frank.Mermaid/Flowchart/Line.cs @@ -0,0 +1,50 @@ +using CodegenCS; + +namespace Frank.Mermaid.Flowchart; + +public class Line : IMermaidable +{ + private readonly LineStyle _lineStyle; + private readonly int _lineWidth; + + public Line(LineStyle lineStyle, int lineWidth = 1) + { + _lineStyle = lineStyle; + _lineWidth = lineWidth; + } + + /// + public Guid Id { get; } = Guid.NewGuid(); + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + var arrow = _lineStyle.ToString().Contains("Arrow") ? ">" : ""; + + switch (_lineStyle) + { + case LineStyle.Normal: + case LineStyle.NormalWithArrow: + writer.Write(new string('-', _lineWidth)); + break; + case LineStyle.Thick: + case LineStyle.ThickWithArrow: + writer.Write(new string('=', _lineWidth)); + break; + case LineStyle.Dotted: + case LineStyle.DottedWithArrow: + for (var i = 0; i < _lineWidth; i++) + { + writer.Write(i == _lineWidth - 1 ? "." : ".-"); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + + writer.Write(arrow); + + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/LineStyle.cs b/Frank.Mermaid/Flowchart/LineStyle.cs new file mode 100644 index 0000000..5c26137 --- /dev/null +++ b/Frank.Mermaid/Flowchart/LineStyle.cs @@ -0,0 +1,11 @@ +namespace Frank.Mermaid.Flowchart; + +public enum LineStyle +{ + Normal, + NormalWithArrow, + Thick, + ThickWithArrow, + Dotted, + DottedWithArrow +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/Link.cs b/Frank.Mermaid/Flowchart/Link.cs new file mode 100644 index 0000000..5ea76c5 --- /dev/null +++ b/Frank.Mermaid/Flowchart/Link.cs @@ -0,0 +1,69 @@ +using CodegenCS; + +namespace Frank.Mermaid.Flowchart; + +public class Link : IMermaidable +{ + public Link(string source, string target, string? label = null) + { + Source = source; + Target = target; + Label = label; + } + + public string? Label { get; } + + public string Target { get; } + + public string Source { get; } + + public Line Line { get; private set; } = new Line(LineStyle.Normal, 3); + + public Link(IMermaidable source, IMermaidable target, string? label = null) + { + Source = GetIdentifier(source); + Target = GetIdentifier(target); + Label = label; + } + + private string? GetIdentifier(IMermaidable source) + { + if (source is Subgraph subgraph) + { + return subgraph.Label; + } + + return source.Id.ToString("N"); + } + + public void SetLineStyle(Line line) + { + Line = line; + } + + /// + public Guid Id { get; } = Guid.NewGuid(); + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + + writer.Write("{0} {1}{2} {3}", Source, Line.ToMermaidSyntax(), GetLabel(), Target); + + writer.WriteLine(); + return writer; + } + + private string GetLabel() => !string.IsNullOrWhiteSpace(Label) ? $"|{Label}|" : string.Empty; + + /* + Length 1 2 3 + Normal --- ---- ----- + Normal with arrow --> ---> ----> + Thick === ==== ===== + Thick with arrow ==> ===> ====> + Dotted -.- -..- -...- + Dotted with arrow -.-> -..-> -...-> + */ +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/LinkType.cs b/Frank.Mermaid/Flowchart/LinkType.cs new file mode 100644 index 0000000..fb11519 --- /dev/null +++ b/Frank.Mermaid/Flowchart/LinkType.cs @@ -0,0 +1,9 @@ +namespace Frank.Mermaid.Flowchart; + +public enum LinkType +{ + Default, + Dotted, + Thick, + DottedThick +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/Node.cs b/Frank.Mermaid/Flowchart/Node.cs new file mode 100644 index 0000000..ad618f5 --- /dev/null +++ b/Frank.Mermaid/Flowchart/Node.cs @@ -0,0 +1,46 @@ +using CodegenCS; + +namespace Frank.Mermaid.Flowchart; + +public class Node : IMermaidable +{ + public Node(Guid id, string label, Shape shape) + { + Id = id; + Label = label; + Shape = shape; + } + + public Node(string label) : this(Guid.NewGuid(), label, Shape.Rectangle) + { + } + + public Node(string label, Shape shape) : this(Guid.NewGuid(), label, shape) + { + } + + /// + public Guid Id { get; } + public string Label { get; } + public Shape Shape { get; } + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var label = Shape switch + { + Shape.Circle => $"(({Label}))", + Shape.Subroutine => $"[[{Label}]]", + Shape.Rounded => $"({Label})", + Shape.Hexagon => $"{{{{{Label}}}}}", + Shape.Database => $"[({Label})]", + Shape.Rectangle => $"[{Label}]", // Default case + _ => $"[{Label}]" // Default case + }; + + var writer = new CodegenTextWriter(); + writer.WriteLine("{0}{1}", this.GetId(), label); + return writer; + } + +} diff --git a/Frank.Mermaid/Flowchart/Shape.cs b/Frank.Mermaid/Flowchart/Shape.cs new file mode 100644 index 0000000..c486187 --- /dev/null +++ b/Frank.Mermaid/Flowchart/Shape.cs @@ -0,0 +1,12 @@ +namespace Frank.Mermaid.Flowchart; + +public enum Shape +{ + Rectangle, + Rounded, + Circle, + Subroutine, + Database, + Diamond, + Hexagon +} \ No newline at end of file diff --git a/Frank.Mermaid/Flowchart/Subgraph.cs b/Frank.Mermaid/Flowchart/Subgraph.cs new file mode 100644 index 0000000..767911e --- /dev/null +++ b/Frank.Mermaid/Flowchart/Subgraph.cs @@ -0,0 +1,67 @@ +using CodegenCS; + +namespace Frank.Mermaid.Flowchart; +// Define the line styles using an enumeration + +public class Subgraph : IMermaidable +{ + public Subgraph(string label, Direction direction) + { + Label = label; + Direction = direction; + } + + public void AddNode(Node node) => Nodes.Add(node); + public void AddNodes(IEnumerable nodes) => Nodes.AddRange(nodes); + + public void AddLink(Link link) => Links.Add(link); + public void AddLinks(IEnumerable links) => Links.AddRange(links); + + public void AddSubgraph(Subgraph subgraph) => _subgraphs.Add(subgraph); + public void AddSubgraphs(IEnumerable subgraphs) => _subgraphs.AddRange(subgraphs); + + /// + public Guid Id { get; } = Guid.NewGuid(); + + public string Label { get; } + + public Direction Direction { get; } + + public List Nodes { get; } = new(); + + private List Links { get; } = new(); + + + + private readonly List _subgraphs = new(); + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("subgraph {0}", Label); + writer.IncreaseIndent(); + + writer.WriteLine("direction {0}", Direction.ToMermaidSyntax()); + + foreach (var node in Nodes) + { + writer.Write(node.ToMermaidSyntax()); + } + + foreach (var link in Links) + { + writer.Write(link.ToMermaidSyntax()); + } + + foreach (var subgraph in _subgraphs) + { + writer.Write(subgraph.ToMermaidSyntax()); + } + + + writer.DecreaseIndent(); + writer.WriteLine("end"); + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/Frank.Mermaid.csproj b/Frank.Mermaid/Frank.Mermaid.csproj index 2b8a13c..3233c00 100644 --- a/Frank.Mermaid/Frank.Mermaid.csproj +++ b/Frank.Mermaid/Frank.Mermaid.csproj @@ -3,4 +3,8 @@ + + + + diff --git a/Frank.Mermaid/IMermaidable.cs b/Frank.Mermaid/IMermaidable.cs new file mode 100644 index 0000000..be3b816 --- /dev/null +++ b/Frank.Mermaid/IMermaidable.cs @@ -0,0 +1,27 @@ +using CodegenCS; + +namespace Frank.Mermaid; + +public interface IMermaidable +{ + /// + /// The unique identifier of the object. + /// + public Guid Id { get; } + + /// + /// Returns a string representation of the object in Mermaid syntax. + /// + /// a string representation of the object in Mermaid syntax + public ICodegenTextWriter ToMermaidSyntax(); +} + +public static class MermaidableExtensions +{ + /// + /// Returns the unique identifier of the object as a string without dashes. + /// + /// + /// + public static string GetId(this IMermaidable source) => source.Id.ToString("N"); +} \ No newline at end of file diff --git a/Frank.Mermaid/Timeline/Event.cs b/Frank.Mermaid/Timeline/Event.cs new file mode 100644 index 0000000..55c76e4 --- /dev/null +++ b/Frank.Mermaid/Timeline/Event.cs @@ -0,0 +1,45 @@ +using CodegenCS; + +namespace Frank.Mermaid.Timeline; + +public class Event : IMermaidable +{ + private readonly string _title; + private readonly DateTime _date; + public readonly TimePeriod TimePeriod; + + public Event(string title, DateTime date, TimePeriod timePeriod = TimePeriod.Day) + { + _title = title; + _date = date; + TimePeriod = timePeriod; + } + + /// + public Guid Id { get; } = Guid.NewGuid(); + + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.Write("{0} : {1}", GetPeriodString(_date), _title); + return writer; + } + + private string GetPeriodString(DateTime dateTime) + { + return TimePeriod switch + { + TimePeriod.Year => dateTime.ToString("yyyy"), + TimePeriod.Month => dateTime.ToString("yyyy-MM"), + TimePeriod.Day => dateTime.ToString("yyyy-MM-dd"), + TimePeriod.Hour => dateTime.ToString("yyyy-MM-dd HH"), + TimePeriod.Minute => dateTime.ToString("yyyy-MM-dd HH:mm"), + TimePeriod.Second => dateTime.ToString("yyyy-MM-dd HH:mm:ss"), + TimePeriod.Millisecond => dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff"), + TimePeriod.Microsecond => dateTime.ToString("yyyy-MM-dd HH:mm:ss.ffffff"), + TimePeriod.Nanosecond => dateTime.ToString("yyyy-MM-dd HH:mm:ss.fffffff"), + TimePeriod.Tick => dateTime.ToString("yyyy-MM-dd HH:mm:ss.fffffffff"), + _ => throw new ArgumentOutOfRangeException() + }; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/Timeline/Section.cs b/Frank.Mermaid/Timeline/Section.cs new file mode 100644 index 0000000..f0b59fb --- /dev/null +++ b/Frank.Mermaid/Timeline/Section.cs @@ -0,0 +1,35 @@ +using CodegenCS; + +namespace Frank.Mermaid.Timeline; + +public class Section : IMermaidable +{ + private readonly string _title; + private readonly List _events = new(); + + public Section(string title) + { + _title = title; + } + + public void AddEvent(Event @event) => _events.Add(@event); + + public void AddEvents(IEnumerable events) => _events.AddRange(events); + + /// + public Guid Id { get; } = Guid.NewGuid(); + + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("section {0}", _title); + + writer.IncreaseIndent(); + foreach (var @event in _events) + { + writer.WriteLine(@event.ToMermaidSyntax()); + } + + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/Timeline/TimePeriod.cs b/Frank.Mermaid/Timeline/TimePeriod.cs new file mode 100644 index 0000000..017c466 --- /dev/null +++ b/Frank.Mermaid/Timeline/TimePeriod.cs @@ -0,0 +1,15 @@ +namespace Frank.Mermaid.Timeline; + +public enum TimePeriod +{ + Year, + Month, + Day, + Hour, + Minute, + Second, + Millisecond, + Microsecond, + Nanosecond, + Tick +} \ No newline at end of file diff --git a/Frank.Mermaid/Timeline/Timeline.cs b/Frank.Mermaid/Timeline/Timeline.cs new file mode 100644 index 0000000..46692bc --- /dev/null +++ b/Frank.Mermaid/Timeline/Timeline.cs @@ -0,0 +1,45 @@ +using CodegenCS; + +namespace Frank.Mermaid.Timeline; + +public class Timeline(string title) : IMermaidable +{ + private readonly List
_sections = new(); + private readonly List _events = new(); + + public void AddSection(Section section) => _sections.Add(section); + public void AddSections(IEnumerable
sections) => _sections.AddRange(sections); + + public void AddEvent(Event @event) => _events.Add(@event); + public void AddEvents(IEnumerable events) => _events.AddRange(events); + + /// + public Guid Id { get; } = Guid.NewGuid(); + + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("timeline"); + writer.IncreaseIndent(); + writer.WriteLine("title {0}", title); + + if (_sections.Any()) + { + foreach (var section in _sections) + { + writer.Write(section.ToMermaidSyntax()); + } + + writer.DecreaseIndent(); + } + else + { + foreach (var @event in _events) + { + writer.Write(@event.ToMermaidSyntax()); + } + } + + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/XyChart/Axis.cs b/Frank.Mermaid/XyChart/Axis.cs new file mode 100644 index 0000000..7bf0fcd --- /dev/null +++ b/Frank.Mermaid/XyChart/Axis.cs @@ -0,0 +1,23 @@ +using CodegenCS; + +namespace Frank.Mermaid.XyChart; + +public class Axis : IMermaidable +{ + public string Title { get; } + public bool Logarithmic { get; } + + /// + public Guid Id { get; } + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("axis {0}", Title); + writer.IncreaseIndent(); + writer.WriteLine("log {0}", Logarithmic.ToString().ToLower()); + writer.DecreaseIndent(); + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/XyChart/Point.cs b/Frank.Mermaid/XyChart/Point.cs new file mode 100644 index 0000000..9ea3d86 --- /dev/null +++ b/Frank.Mermaid/XyChart/Point.cs @@ -0,0 +1,20 @@ +using CodegenCS; + +namespace Frank.Mermaid.XyChart; + +public class Point : IMermaidable +{ + public double X { get; } + public double Y { get; } + + /// + public Guid Id { get; } = Guid.NewGuid(); + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("{0} {1}", X, Y); + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/XyChart/Series.cs b/Frank.Mermaid/XyChart/Series.cs new file mode 100644 index 0000000..bbca8ea --- /dev/null +++ b/Frank.Mermaid/XyChart/Series.cs @@ -0,0 +1,28 @@ +using CodegenCS; + +namespace Frank.Mermaid.XyChart; + +public class Series : IMermaidable +{ + public string Name { get; } + public List Points { get; } + + /// + public Guid Id { get; } + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("series {0}", Name); + writer.IncreaseIndent(); + foreach (var point in Points) + { + writer.WriteLine(point.ToMermaidSyntax()); + } + + writer.DecreaseIndent(); + + return writer; + } +} \ No newline at end of file diff --git a/Frank.Mermaid/XyChart/XyChart.cs b/Frank.Mermaid/XyChart/XyChart.cs new file mode 100644 index 0000000..45eb44d --- /dev/null +++ b/Frank.Mermaid/XyChart/XyChart.cs @@ -0,0 +1,38 @@ +using CodegenCS; + +namespace Frank.Mermaid.XyChart; + +/// +/// Represents an XY chart. +/// +/// Beta +public class XyChart : IMermaidable +{ + public string Title { get; } + public Axis XAxis { get; } + public Axis YAxis { get; } + public List Series { get; } + + /// + public Guid Id { get; } + + /// + public ICodegenTextWriter ToMermaidSyntax() + { + var writer = new CodegenTextWriter(); + writer.WriteLine("xyChart {0}", Title); + writer.IncreaseIndent(); + writer.WriteLine("xAxis"); + writer.WriteLine(XAxis.ToMermaidSyntax()); + writer.WriteLine("yAxis"); + writer.WriteLine(YAxis.ToMermaidSyntax()); + foreach (var series in Series) + { + writer.WriteLine(series.ToMermaidSyntax()); + } + + writer.DecreaseIndent(); + + return writer; + } +} \ No newline at end of file