diff --git a/Terminal.Gui/Core/Graphs/StraightLineCanvas.cs b/Terminal.Gui/Core/Graphs/StraightLineCanvas.cs
new file mode 100644
index 000000000..ba67537eb
--- /dev/null
+++ b/Terminal.Gui/Core/Graphs/StraightLineCanvas.cs
@@ -0,0 +1,300 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Terminal.Gui.Graphs {
+
+
+ ///
+ /// Facilitates box drawing and line intersection detection
+ /// and rendering.
+ ///
+ public class StraightLineCanvas {
+
+ private List lines = new List ();
+ private ConsoleDriver driver;
+
+ public StraightLineCanvas (ConsoleDriver driver)
+ {
+ this.driver = driver;
+ }
+
+ ///
+ /// Add a new line to the canvas starting at .
+ /// Use positive for Right and negative for Left
+ /// when is .
+ /// Use positive for Down and negative for Up
+ /// when is .
+ ///
+ /// Starting point.
+ /// Length of line. 0 for a dot.
+ /// Positive for Down/Right. Negative for Up/Left.
+ /// Direction of the line.
+ public void AddLine (Point from, int length, Orientation orientation, BorderStyle style)
+ {
+ lines.Add (new StraightLine (from, length, orientation, style));
+ }
+ ///
+ /// Evaluate all currently defined lines that lie within
+ /// and generate a 'bitmap' that
+ /// shows what characters (if any) should be rendered at each
+ /// point so that all lines connect up correctly with appropriate
+ /// intersection symbols.
+ ///
+ ///
+ ///
+ /// Map as 2D array where first index is rows and second is column
+ public Rune? [,] GenerateImage (Rect inArea)
+ {
+ Rune? [,] canvas = new Rune? [inArea.Height, inArea.Width];
+
+ // walk through each pixel of the bitmap
+ for (int y = 0; y < inArea.Height; y++) {
+ for (int x = 0; x < inArea.Width; x++) {
+
+ var intersects = lines
+ .Select (l => l.Intersects (x, y))
+ .Where(i=>i != null)
+ .ToArray();
+
+ // TODO: use Driver and LineStyle to map
+ canvas [x, y] = GetRuneForIntersects (intersects);
+
+ }
+ }
+
+ return canvas;
+ }
+
+ private Rune? GetRuneForIntersects (IntersectionDefinition[] intersects)
+ {
+ if (!intersects.Any ())
+ return null;
+
+ // TODO: merge these intersection types to give correct rune
+
+ return '.';
+ }
+
+ class IntersectionDefinition {
+ ///
+ /// The point at which the intersection happens
+ ///
+ public Point Point { get; }
+
+ ///
+ /// Defines how position relates
+ /// to .
+ ///
+ public IntersectionType Type { get; }
+
+ ///
+ /// The line that intersects
+ ///
+ public StraightLine Line { get; }
+
+ public IntersectionDefinition (Point point, IntersectionType type, StraightLine line)
+ {
+ Point = point;
+ Type = type;
+ Line = line;
+ }
+ }
+
+ ///
+ /// The type of Rune that we will use before considering
+ /// double width, curved borders etc
+ ///
+ enum IntersectionRuneType
+ {
+ None,
+ Dot,
+ ULCorner,
+ URCorner,
+ LLCorner,
+ LRCorner,
+ UpperT,
+ LowerT,
+ RightT,
+ LeftT,
+ Crosshair,
+ }
+
+ enum IntersectionType {
+ ///
+ /// There is no intersection
+ ///
+ None,
+
+ ///
+ /// A line passes directly over this point traveling along
+ /// the horizontal axis
+ ///
+ PassOverHorizontal,
+
+ ///
+ /// A line passes directly over this point traveling along
+ /// the vertical axis
+ ///
+ PassOverVertical,
+
+ ///
+ /// A line starts at this point and is traveling up
+ ///
+ StartUp,
+
+ ///
+ /// A line starts at this point and is traveling right
+ ///
+ StartRight,
+
+ ///
+ /// A line starts at this point and is traveling down
+ ///
+ StartDown,
+
+ ///
+ /// A line starts at this point and is traveling left
+ ///
+ StartLeft,
+
+ ///
+ /// A line exists at this point who has 0 length
+ ///
+ Dot
+ }
+
+ class StraightLine {
+ public Point Start { get; }
+ public int Length { get; }
+ public Orientation Orientation { get; }
+ public BorderStyle Style { get; }
+
+ public StraightLine (Point start, int length, Orientation orientation, BorderStyle style)
+ {
+ this.Start = start;
+ this.Length = length;
+ this.Orientation = orientation;
+ this.Style = style;
+ }
+
+ internal IntersectionDefinition Intersects (int x, int y)
+ {
+ if (IsDot ()) {
+ if (StartsAt (x, y)) {
+ return new IntersectionDefinition (Start, IntersectionType.Dot, this);
+ } else {
+ return null;
+ }
+ }
+
+ switch (Orientation) {
+ case Orientation.Horizontal: return IntersectsHorizontally (x, y);
+ case Orientation.Vertical: return IntersectsVertically (x, y);
+ default: throw new ArgumentOutOfRangeException (nameof (Orientation));
+ }
+
+ }
+
+ private IntersectionDefinition IntersectsHorizontally (int x, int y)
+ {
+ if (Start.Y != y) {
+ return null;
+ } else {
+ if (StartsAt (x, y)) {
+
+ return new IntersectionDefinition (
+ Start,
+ Length < 0 ? IntersectionType.StartLeft : IntersectionType.StartRight,
+ this
+ );
+
+ }
+
+ if (EndsAt (x, y)) {
+
+ return new IntersectionDefinition (
+ Start,
+ Length < 0 ? IntersectionType.StartRight : IntersectionType.StartLeft,
+ this
+ );
+
+ } else {
+ var xmin = Math.Min (Start.X, Start.X + Length);
+ var xmax = Math.Max (Start.X, Start.X + Length);
+
+ if (xmin < x && xmax > x) {
+ return new IntersectionDefinition (
+ new Point (x, y),
+ IntersectionType.PassOverHorizontal,
+ this
+ );
+ }
+ }
+
+ return null;
+ }
+ }
+
+ private IntersectionDefinition IntersectsVertically (int x, int y)
+ {
+ if (Start.X != x) {
+ return null;
+ } else {
+ if (StartsAt (x, y)) {
+
+ return new IntersectionDefinition (
+ Start,
+ Length < 0 ? IntersectionType.StartUp : IntersectionType.StartDown,
+ this
+ );
+
+ }
+
+ if (EndsAt (x, y)) {
+
+ return new IntersectionDefinition (
+ Start,
+ Length < 0 ? IntersectionType.StartDown : IntersectionType.StartUp,
+ this
+ );
+
+ } else {
+ var ymin = Math.Min (Start.Y, Start.Y + Length);
+ var ymax = Math.Max (Start.Y, Start.Y + Length);
+
+ if (ymin < y && ymax > y) {
+ return new IntersectionDefinition (
+ new Point (x, y),
+ IntersectionType.PassOverVertical,
+ this
+ );
+ }
+ }
+
+ return null;
+ }
+ }
+
+ private bool EndsAt (int x, int y)
+ {
+ if (Orientation == Orientation.Horizontal) {
+ return Start.X + Length == x && Start.Y == y;
+ }
+
+ return Start.X == x && Start.Y + Length == y;
+ }
+
+ private bool StartsAt (int x, int y)
+ {
+ return Start.X == x && Start.Y == y;
+ }
+
+ private bool IsDot ()
+ {
+ return Length == 0;
+ }
+ }
+ }
+}