diff --git a/D2/ArrowheadOptions.cs b/D2/ArrowheadOptions.cs index 14239ee..1b0c314 100644 --- a/D2/ArrowheadOptions.cs +++ b/D2/ArrowheadOptions.cs @@ -1,6 +1,8 @@ -using System.Text; +using System; +using System.Text; using D2.Enums; using D2.Extensions; +using static D2.Constants; namespace D2 { @@ -9,31 +11,31 @@ public class ArrowheadOptions private ArrowheadType? _type { get; } private string _label { get; } private bool _fill { get; } - + public ArrowheadOptions(ArrowheadType type, string label = "", bool fill = true) { _type = type; _label = label; _fill = fill; - } - + } + public override string ToString() { var sb = new StringBuilder(); - + if (!string.IsNullOrWhiteSpace(_label)) sb.Append($"{_label} "); if (!_type.HasValue) return sb.ToString(); - - sb.Append("{"); - + + sb.Append(OPEN_CONTAINER); + sb.Append($" shape: {_type.Value.ToCatalog()};"); // TODO: Determine if fill is default for given arrowhead type - sb.Append($" style.filled: {_fill.ToString().ToLower()}"); - - sb.Append(" }"); + sb.Append($" style.filled: {_fill.ToString().ToLower()} "); + + sb.Append(CLOSE_CONTAINER); return sb.ToString(); } } -} \ No newline at end of file +} diff --git a/D2/Connection.cs b/D2/Connection.cs index b0cb4ba..3d27e67 100644 --- a/D2/Connection.cs +++ b/D2/Connection.cs @@ -12,11 +12,11 @@ public class Connection : IRenderable private string _from { get; } private string _to { get; } private string? _label { get; } - + private ConnectionType _type { get; } private ArrowheadOptions? _fromArrowhead { get; } private ArrowheadOptions? _toArrowhead { get; } - + public Connection(Guid from, Guid to) : this(from.ToString(), to.ToString(), string.Empty, null) { } public Connection(Shape from, Shape to) : this(from.Key, to.Key, string.Empty, null) { } public Connection(string from, string to) : this(from, to, string.Empty, null) { } @@ -43,32 +43,32 @@ public Connection(string from, _fromArrowhead = fromArrowhead; _toArrowhead = toArrowhead; } - + public override string ToString() { var sb = new StringBuilder($"{_from} {_type.CatalogName()} {_to}"); - + var hasProperties = _fromArrowhead != null || _toArrowhead != null; var hasLabel = !string.IsNullOrEmpty(_label); if (hasProperties || hasLabel) sb.Append(":"); - + if (hasLabel) sb.Append($" {_label}"); - + if (!hasProperties) return sb.ToString(); - sb.Append($" {OPEN_CONTAINER}"); + sb.Append($" {OPEN_CONTAINER}{Environment.NewLine}"); if (_fromArrowhead != null) sb.AppendLine($"{TAB}source-arrowhead: {_fromArrowhead}"); - + if (_toArrowhead != null) sb.AppendLine($"{TAB}target-arrowhead: {_toArrowhead}"); - + sb.Append(CLOSE_CONTAINER); - + return sb.ToString(); } } -} \ No newline at end of file +} diff --git a/D2/Constants.cs b/D2/Constants.cs index b7198c2..786530f 100644 --- a/D2/Constants.cs +++ b/D2/Constants.cs @@ -4,9 +4,8 @@ namespace D2 { public static class Constants { - - internal static readonly string OPEN_CONTAINER = $"{{{Environment.NewLine}"; + internal static readonly string OPEN_CONTAINER = "{"; internal static readonly string CLOSE_CONTAINER = "}"; - internal static readonly string TAB = " "; // TODO: Make this configurable + internal static readonly string TAB = " "; // TODO: Make this configurable } -} \ No newline at end of file +} diff --git a/D2/Diagram.cs b/D2/Diagram.cs index d047b81..0e75c70 100644 --- a/D2/Diagram.cs +++ b/D2/Diagram.cs @@ -11,28 +11,82 @@ public class Diagram : IRenderable private readonly List _renderables = new List(); public static Diagram Create() => new Diagram(); - + public Diagram Add(IRenderable renderable) { _renderables.Add(renderable); return this; } - + public Diagram Add(IEnumerable renderable) { _renderables.AddRange(renderable); return this; } + public Diagram Connect(Shape from, Shape to, string label = "", ArrowheadOptions? fromArrowhead = null, ArrowheadOptions? toArrowhead = null) + { + _renderables.Add(new Connection(from.Key, to.Key, label, fromArrowhead, toArrowhead)); + return this; + } + public Diagram CreateShape(string key, string label, ShapeType type) { _renderables.Add(new Shape(key, label, type)); return this; } - - public Diagram CreateConnection(string from, string to, string label = "", ArrowheadOptions? arrowhead = null) + + public Diagram CreateShape(string key, string label) + { + _renderables.Add(new Shape(key, label)); + return this; + } + + public Diagram CreateShape(string key, ShapeType type) + { + _renderables.Add(new Shape(key, type)); + return this; + } + + public Diagram CreateMarkdownShape(string key, string label) + { + _renderables.Add(new Markdown(key, label)); + return this; + } + + public Diagram CreateMarkdownShape(string key, string label, ShapeType type) + { + _renderables.Add(new Markdown(key, label, type)); + return this; + } + + public Diagram CreateLatexShape(string key, string label) + { + _renderables.Add(new LateX(key, label)); + return this; + } + + public Diagram CreateLatexShape(string key, string label, ShapeType type) + { + _renderables.Add(new LateX(key, label, type)); + return this; + } + + public Diagram CreateDirectionalConnection(string from, string to, string label = "", ArrowheadOptions? toArrowhead = null) + { + _renderables.Add(new Connection(from, to, label, null, toArrowhead)); + return this; + } + + public Diagram CreateBidirectionalConnection(string from, string to, string label = "", ArrowheadOptions? fromArrowhead = null, ArrowheadOptions? toArrowhead = null) + { + _renderables.Add(new Connection(from, to, label, ConnectionType.Bidirectional, fromArrowhead, toArrowhead)); + return this; + } + + public Diagram CreateConnection(string from, string to, string label = "") { - _renderables.Add(new Connection(from, to, label, arrowhead)); + _renderables.Add(new Connection(from, to, label, ConnectionType.From)); return this; } @@ -41,4 +95,4 @@ public override string ToString() return string.Join(Environment.NewLine, _renderables); } } -} \ No newline at end of file +} diff --git a/D2/Shape.cs b/D2/Shape.cs index 45973a4..0131576 100644 --- a/D2/Shape.cs +++ b/D2/Shape.cs @@ -15,33 +15,41 @@ public class Shape : IRenderable private string _label { get; } private ShapeType? _type { get; set; } private Dictionary _attributes { get; } + private Dictionary _styleAttributes { get; } private IEnumerable _children { get; } - + public string Key => _key; public string Label => _label; - - public Shape(string label) : this(label, string.Empty, null) { } + + public Shape(string label) : this(label, string.Empty) { } public Shape(string label, ShapeType? type) : this(label, string.Empty, type) { } - + public Shape(string key, string label, ShapeType? type = null, IEnumerable? children = null) { if (string.IsNullOrEmpty(key)) throw new ArgumentException("Key cannot be null or empty"); - + _key = key; _label = label; _type = type; + _children = children ?? Enumerable.Empty(); _attributes = new Dictionary(); - _children = children ?? Array.Empty(); + _styleAttributes = new Dictionary(); } - + public Shape WithAttribute(string key, string value) { _attributes[key] = value; return this; } - + + public Shape WithStyle(string key, string value) + { + _styleAttributes[key] = value; + return this; + } + public Shape WithIcon(string icon) { _attributes["icon"] = icon; @@ -54,26 +62,38 @@ public Shape AsIcon(string icon) _type = ShapeType.Image; return this; } - + public override string ToString() { var sb = new StringBuilder(_key); - + var hasProperties = _type.HasValue || _attributes.Count > 0 || _children.Any(); var hasLabel = !string.IsNullOrWhiteSpace(_label); - if (hasProperties || hasLabel) + var hasStyle = _styleAttributes.Count > 0; + if (hasProperties || hasLabel || hasStyle) sb.Append(":"); - + if (hasLabel) sb.Append($" {_label}"); - if (!hasProperties) return sb.ToString(); - - sb.Append($" {OPEN_CONTAINER}"); - + if (!hasProperties && !hasStyle) + return sb.ToString(); + + sb.Append($" {OPEN_CONTAINER}{Environment.NewLine}"); + if (_type.HasValue) sb.AppendLine($"{TAB}shape: {_type.Value.CatalogName()}"); + if (hasStyle) + { + sb.AppendLine($"{TAB}style: {OPEN_CONTAINER}"); + foreach (var styleAttribute in _styleAttributes) + { + sb.Append($"{TAB}{TAB}{styleAttribute.Key}: {styleAttribute.Value}"); + } + sb.AppendLine($"{Environment.NewLine}{TAB}{CLOSE_CONTAINER}"); + } + if (_attributes.Count > 0) { foreach (var attribute in _attributes) @@ -81,7 +101,7 @@ public override string ToString() sb.AppendLine($"{TAB}{attribute.Key}: {attribute.Value}"); } } - + if (_children.Any()) { foreach (var child in _children) @@ -91,10 +111,10 @@ public override string ToString() sb.AppendLine($"{TAB}{indentedChildString}"); } } - + sb.Append(CLOSE_CONTAINER); return sb.ToString(); } } -} \ No newline at end of file +} diff --git a/Examples/Program.cs b/Examples/Program.cs index e4f8fa6..53a9589 100644 --- a/Examples/Program.cs +++ b/Examples/Program.cs @@ -1,16 +1,43 @@ using D2; using D2.Enums; +using D2.Writers; var A = new Shape("A", "Starting Point"); var B = new Shape("B", "Ending Point"); var C = new Shape("C", "Middle Point", ShapeType.Hexagon); +A.WithStyle("fill", "green"); +B.WithStyle("fill", "red"); +C.WithStyle("fill", "blue"); + var basicTemplate = Diagram.Create() .Add(A) .Add(B) .Add(C) - .Add(new Connection(A, C)) - .Add(new Connection(C, B, "Hello World!")); + .Connect(A, C) + .Connect(C, B); // Each component overrides ToString() to return the D2 code for that instance -Console.WriteLine(basicTemplate); \ No newline at end of file +Console.WriteLine("Basic Template:"); +Console.WriteLine(basicTemplate); + +var styledTemplate = Diagram.Create() + .CreateShape("D", type: ShapeType.Circle) + .CreateShape("E", "Styled Shape", ShapeType.Hexagon) + .CreateShape("F", "Styled Diamond Shape", ShapeType.Diamond) + .CreateDirectionalConnection("D", "E", "Styled Connection", new ArrowheadOptions(ArrowheadType.CrowsFootManyRequired, "Filled Triangle")) + .CreateBidirectionalConnection("E", "F", "Bidirectional Connection", + new ArrowheadOptions(ArrowheadType.CrowsFootManyRequired), + new ArrowheadOptions(ArrowheadType.CrowsFootManyRequired)) + .CreateConnection("F", "D") + .CreateMarkdownShape("G", @" # I can do headers + + - lists + - lists + + And other normal markdown stuff") + .CreateLatexShape("H", @"\\lim_{h \\rightarrow 0 } \\frac{f(x+h)-f(x)}{h}") + .CreateBidirectionalConnection("G", "H"); + +Console.WriteLine("Styled Template:"); +Console.WriteLine(styledTemplate);