using System.Collections.Generic;
public static void Main()
RectGroup rNew = new RectGroup();
rNew.Location = new Point(10,80);
rNew.Size = new Size(40, 15);
rNew.CssClass = "State EnabledState";
RectGroup rWithdrawn = new RectGroup();
rWithdrawn.Location = new Point(10,115);
rWithdrawn.Size = new Size(40, 15);
rWithdrawn.Text = "Withdrawn";
rWithdrawn.CssClass = "State EnabledState";
RectGroup rAccepted = new RectGroup();
rAccepted.Location = new Point(80,45);
rAccepted.Size = new Size(40, 15);
rAccepted.Text = "Accepted";
rAccepted.CssClass = "State EnabledState";
RectGroup rClosed = new RectGroup();
rClosed.Location = new Point(150,45);
rClosed.Size = new Size(40, 15);
rClosed.CssClass = "State EnabledState";
RectGroup rReopened = new RectGroup();
rReopened.Location = new Point(200,150);
rReopened.Size = new Size(40, 15);
rReopened.Text = "Reopened";
rReopened.CssClass = "State EnabledState";
RectGroup rRejected = new RectGroup();
rRejected.Location = new Point(64,115);
rRejected.Size = new Size(40, 15);
rRejected.Text = "Rejected";
rRejected.CssClass = "State EnabledState";
RectGroup rScheduled = new RectGroup();
rScheduled.Location = new Point(150,80);
rScheduled.Size = new Size(40, 15);
rScheduled.Text = "Scheduled";
rScheduled.CssClass = "State EnabledState";
List<Arrow> arrows = new List<Arrow>();
arrows.Add(Arrow.SimpleArrow(rNew.Anchors["B2"], rWithdrawn.Anchors["T2"]));
arrows.Add(Arrow.SimpleArrow(rWithdrawn.Anchors["T4"], rNew.Anchors["B4"]));
arrows.Add(Arrow.SimpleArrow(rNew.NorthAnchor, rAccepted.WestAnchor, new Point(rNew.NorthAnchor.X, rAccepted.WestAnchor.Y)));
arrows.Add(Arrow.ArrowWithMiddleElbow(rNew.R2, rRejected.T2));
arrows.Add(Arrow.SimpleArrow(rAccepted.EastAnchor, rClosed.WestAnchor));
arrows.Add(Arrow.SimpleArrow(rAccepted.B4, rScheduled.WestAnchor, new Point(rAccepted.B4.X, rScheduled.WestAnchor.Y)));
arrows.Add(Arrow.SimpleArrow(rAccepted.B2, rRejected.T4));
arrows.Add(Arrow.SimpleArrow(rAccepted.NorthAnchor, rReopened.EastAnchor, new Point(rAccepted.NorthAnchor.X, rAccepted.NorthAnchor.Y - 10), new Point(rReopened.EastAnchor.X + 10, rAccepted.NorthAnchor.Y - 10), new Point(rReopened.EastAnchor.X + 10, rReopened.EastAnchor.Y)));
arrows.Add(Arrow.ArrowWithMiddleElbow(rClosed.R1, rReopened.T5));
arrows.Add(Arrow.ArrowWithMiddleElbow(rScheduled.R1, rReopened.T2));
arrows.Add(Arrow.SimpleArrow(rScheduled.NorthAnchor, rClosed.SouthAnchor));
arrows.Add(Arrow.SimpleArrow(rScheduled.SouthAnchor, rRejected.EastAnchor, new Point(rScheduled.SouthAnchor.X, rRejected.EastAnchor.Y)));
arrows.Add(Arrow.SimpleArrow(rRejected.B4, rReopened.L1, new Point(rRejected.B4.X, rReopened.L1.Y)));
arrows.Add(Arrow.SimpleArrow(rReopened.L3, rRejected.B2, new Point(rRejected.B2.X, rReopened.L3.Y)));
arrows.Add(Arrow.ArrowWithMiddleElbow(rReopened.T4, rClosed.R3));
arrows.Add(Arrow.ArrowWithMiddleElbow(rReopened.T1, rScheduled.R3));
Console.WriteLine(rNew.ToSvg());
Console.WriteLine(rWithdrawn.ToSvg());
Console.WriteLine(rAccepted.ToSvg());
Console.WriteLine(rClosed.ToSvg());
Console.WriteLine(rReopened.ToSvg());
Console.WriteLine(rRejected.ToSvg());
Console.WriteLine(rScheduled.ToSvg());
Console.WriteLine(a.ToSvg());
public Size Size { get; set; }
public Point Location { get; set; }
public string Text { get; set; }
private Point TextLocation
public string CssClass { get; set; }
public string Id { get; set; }
string result = $@"<g transform='translate({Location.X},{Location.Y})'><rect class='{CssClass}' id='{Id}' width='{Size.Width}' height='{Size.Height}'/>" +
$@"<text x='{TextLocation.X}' y='{TextLocation.Y}' font-family='Verdana' font-size='6' fill='black'>{Text}</text></g>";
List<Color> cols = new List<Color> { Color.Black } ;
foreach(var x in Anchors)
result += $"<circle cx='{x.Value.Location.X}' cy='{x.Value.Location.Y}' r='0.2' stroke='{cols[i].Name}' fill='{cols[i].Name}' />";
i++; if(i >= cols.Count) i = 0;
public bool DebugMode { get; } = false;
public Anchor NorthAnchor {
get { return Anchor.TopLineAnchor(new Point(MidWidth, Top-5)); }
public Anchor SouthAnchor {
get { return Anchor.BottomLineAnchor(new Point(MidWidth, Bottom+5)); }
public Anchor WestAnchor {
get { return Anchor.LeftLineAnchor(new Point(Left-5, MidHeight)); }
public Anchor EastAnchor {
get { return Anchor.RightLineAnchor(new Point(Right+5, MidHeight)); }
private double QuarterWidth => Size.Width / 4;
private double QuarterHeight => Size.Height / 4;
public Dictionary<string, Anchor> Anchors {
Dictionary<string, Anchor> list = new Dictionary<string, Anchor>();
list.Add("T1", Anchor.TopLineAnchor(Location.X + Size.Width*0.15, Top-5));
list.Add("T2", Anchor.TopLineAnchor(Location.X + Size.Width*0.30, Top-5));
list.Add("T3", NorthAnchor);
list.Add("T4", Anchor.TopLineAnchor(Location.X + Size.Width*0.70, Top-5));
list.Add("T5", Anchor.TopLineAnchor(Location.X + Size.Width*0.85, Top-5));
list.Add("B1", Anchor.BottomLineAnchor(Location.X + Size.Width*0.15, Bottom+5));
list.Add("B2", Anchor.BottomLineAnchor(Location.X + Size.Width*0.30, Bottom+5));
list.Add("B3", SouthAnchor);
list.Add("B4", Anchor.BottomLineAnchor(Location.X + Size.Width*0.70, Bottom+5));
list.Add("B5", Anchor.BottomLineAnchor(Location.X + Size.Width*0.85, Bottom+5));
list.Add("L1", Anchor.LeftLineAnchor(Left - 5, Location.Y + Size.Height * 0.25));
list.Add("L2", WestAnchor);
list.Add("L3", Anchor.LeftLineAnchor(Left - 5, Location.Y + Size.Height * 0.75));
list.Add("R1", Anchor.LeftLineAnchor(Right + 5, Location.Y + Size.Height * 0.25));
list.Add("R2", EastAnchor);
list.Add("R3", Anchor.LeftLineAnchor(Right + 5, Location.Y + Size.Height * 0.75));
public Anchor T1 => Anchors["T1"];
public Anchor T2 => Anchors["T2"];
public Anchor T3 => Anchors["T3"];
public Anchor T4 => Anchors["T4"];
public Anchor T5 => Anchors["T5"];
public Anchor B1 => Anchors["B1"];
public Anchor B2 => Anchors["B2"];
public Anchor B3 => Anchors["B3"];
public Anchor B4 => Anchors["B4"];
public Anchor B5 => Anchors["B5"];
public Anchor L1 => Anchors["L1"];
public Anchor L2 => Anchors["L2"];
public Anchor L3 => Anchors["L3"];
public Anchor R1 => Anchors["R1"];
public Anchor R2 => Anchors["R2"];
public Anchor R3 => Anchors["R3"];
private double Left { get { return Location.X; } }
private double Right { get { return Location.X + Size.Width; } }
private double Top { get { return Location.Y; } }
private double Bottom { get { return Location.Y + Size.Height; } }
private double MidWidth { get { return Location.X + Size.Width / 2; } }
private double MidHeight { get { return Location.Y + Size.Height / 2; } }
public static Anchor TopLineAnchor(double x, double y)
return TopLineAnchor(new Point(x, y));
public static Anchor TopLineAnchor(Point location)
return new Anchor { Location = location, IsTopLine = true };
public static Anchor BottomLineAnchor(double x, double y)
return BottomLineAnchor(new Point(x, y));
public static Anchor BottomLineAnchor(Point location)
return new Anchor { Location = location, IsBottomLine = true };
public static Anchor LeftLineAnchor(Point location)
return new Anchor { Location = location, IsLeftLine = true };
public static Anchor LeftLineAnchor(double x, double y)
return LeftLineAnchor(new Point(x, y));
public static Anchor RightLineAnchor(Point location)
return new Anchor { Location = location, IsRightLine = true };
public static Anchor RightLineAnchor(double x, double y)
return RightLineAnchor(new Point(x, y));
public Point Location { get; set; }
public bool IsTopLine { get; set; }
public bool IsBottomLine { get; set; }
public bool IsLeftLine { get; set; }
public bool IsRightLine { get; set; }
public double X => Location.X;
public double Y => Location.Y;
public static Arrow ArrowWithMiddleElbow(Anchor Start, Anchor End, bool preferLineAbove = true)
double xmax = Start.Location.X;
double xmin = End.Location.X;
if(End.Location.X > xmax) { xmax = End.Location.X; xmin = Start.Location.X; }
double ymax = Start.Location.Y;
double ymin = End.Location.Y;
if(End.Location.Y > ymax) { ymax = End.Location.Y; ymin = Start.Location.Y; }
if(preferLineAbove) midpoint = new Point(xmax, ymin);
else midpoint = new Point(xmin, ymax);
return new Arrow { OrderedPoints = new List<Point> { Start.Location, midpoint, End.Location } };
public static Arrow SimpleArrow(Anchor Start, Anchor End, params Point[] elbows)
List<Point> op = new List<Point>();
if(elbows != null) op.AddRange(elbows);
return new Arrow { OrderedPoints = op };
public List<Point> OrderedPoints { get; set; }
public string Label { get; set; }
private Point TextLocation
foreach(var p in OrderedPoints)
pointList += p.X + "," + p.Y + " ";
return $@"<path d='M{pointList}' marker-end='url(#pointer)' marker-start='' />";
public Point(double x, double y) { X = x; Y = y; }
public double X { get; set; }
public double Y { get; set; }
public Size(double width, double height) { Width = width; Height = height; }
public double Width { get; set; }
public double Height { get; set; }