Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 75 additions & 99 deletions Terminal.Gui/Core/Graphs/LineCanvas.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ namespace Terminal.Gui.Graphs {
/// </summary>
public class LineCanvas {


private List<StraightLine> lines = new List<StraightLine> ();

Dictionary<IntersectionRuneType, IntersectionRuneResolver> runeResolvers = new Dictionary<IntersectionRuneType, IntersectionRuneResolver> {
Dictionary<IntersectionRuneType, IntersectionRuneResolver> runeResolvers = new Dictionary<IntersectionRuneType, IntersectionRuneResolver> {
{IntersectionRuneType.ULCorner,new ULIntersectionRuneResolver()},
{IntersectionRuneType.URCorner,new URIntersectionRuneResolver()},
{IntersectionRuneType.LLCorner,new LLIntersectionRuneResolver()},
Expand Down Expand Up @@ -48,67 +48,49 @@ public void AddLine (Point from, int length, Orientation orientation, BorderStyl
}
/// <summary>
/// Evaluate all currently defined lines that lie within
/// <paramref name="inArea"/> and generate a 'bitmap' that
/// <paramref name="inArea"/> and map that
/// shows what characters (if any) should be rendered at each
/// point so that all lines connect up correctly with appropriate
/// intersection symbols.
/// <returns></returns>
/// </summary>
/// <param name="inArea"></param>
/// <returns>Map as 2D array where first index is rows and second is column</returns>
public Rune? [,] GenerateImage (Rect inArea)
/// <returns>Mapping of all the points within <paramref name="inArea"/> to
/// line or intersection runes which should be drawn there.</returns>
public Dictionary<Point,Rune> GenerateImage (Rect inArea)
{
Rune? [,] canvas = new Rune? [inArea.Height, inArea.Width];
var map = new Dictionary<Point,Rune>();

// walk through each pixel of the bitmap
for (int y = 0; y < inArea.Height; y++) {
for (int x = 0; x < inArea.Width; x++) {
for (int y = inArea.Y; y < inArea.Height; y++) {
for (int x = inArea.X; 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 [y, x] = GetRuneForIntersects (Application.Driver, intersects);

}
}

return canvas;
}
var rune = GetRuneForIntersects (Application.Driver, intersects);

/// <summary>
/// Draws all the lines that lie within the <paramref name="bounds"/> onto
/// the <paramref name="view"/> client area. This method should be called from
/// <see cref="View.Redraw(Rect)"/>.
/// </summary>
/// <param name="view"></param>
/// <param name="bounds"></param>
public void Draw (View view, Rect bounds)
{
var runes = GenerateImage (bounds);

for (int y = bounds.Y; y < bounds.Height; y++) {
for (int x = bounds.X; x < bounds.Width; x++) {
var rune = runes [y, x];

if (rune.HasValue) {
view.AddRune (x, y, rune.Value);
if(rune != null)
{
map.Add(new Point(x,y),rune.Value);
}
}
}

return map;
}

private abstract class IntersectionRuneResolver
{
private abstract class IntersectionRuneResolver {
readonly Rune round;
readonly Rune doubleH;
readonly Rune doubleV;
readonly Rune doubleBoth;
readonly Rune normal;

public IntersectionRuneResolver(Rune round, Rune doubleH, Rune doubleV, Rune doubleBoth, Rune normal)
public IntersectionRuneResolver (Rune round, Rune doubleH, Rune doubleV, Rune doubleBoth, Rune normal)
{
this.round = round;
this.doubleH = doubleH;
Expand All @@ -121,93 +103,87 @@ public IntersectionRuneResolver(Rune round, Rune doubleH, Rune doubleV, Rune dou
{
var useRounded = intersects.Any (i => i.Line.Style == BorderStyle.Rounded && i.Line.Length != 0);

bool doubleHorizontal = intersects.Any(l=>l.Line.Orientation == Orientation.Horizontal && l.Line.Style == BorderStyle.Double);
bool doubleVertical = intersects.Any(l=>l.Line.Orientation == Orientation.Vertical && l.Line.Style == BorderStyle.Double);
bool doubleHorizontal = intersects.Any (l => l.Line.Orientation == Orientation.Horizontal && l.Line.Style == BorderStyle.Double);
bool doubleVertical = intersects.Any (l => l.Line.Orientation == Orientation.Vertical && l.Line.Style == BorderStyle.Double);


if(doubleHorizontal)
{
return doubleVertical ? doubleBoth : doubleH;
if (doubleHorizontal) {
return doubleVertical ? doubleBoth : doubleH;
}

if(doubleVertical)
{

if (doubleVertical) {
return doubleV;
}

return useRounded ? round : normal;
}
}

private class ULIntersectionRuneResolver : IntersectionRuneResolver
{
public ULIntersectionRuneResolver() :
base('╭','╒','╓','╔','┌')
private class ULIntersectionRuneResolver : IntersectionRuneResolver {
public ULIntersectionRuneResolver () :
base ('╭', '╒', '╓', '╔', '┌')
{

}
}
private class URIntersectionRuneResolver : IntersectionRuneResolver
{
private class URIntersectionRuneResolver : IntersectionRuneResolver {

public URIntersectionRuneResolver() :
base('╮','╕','╖','╗','┐')
public URIntersectionRuneResolver () :
base ('╮', '╕', '╖', '╗', '┐')
{

}
}
private class LLIntersectionRuneResolver : IntersectionRuneResolver
{
private class LLIntersectionRuneResolver : IntersectionRuneResolver {

public LLIntersectionRuneResolver() :
base('╰','╘','╙','╚','└')
public LLIntersectionRuneResolver () :
base ('╰', '╘', '╙', '╚', '└')
{

}
}
private class LRIntersectionRuneResolver : IntersectionRuneResolver
{
public LRIntersectionRuneResolver() :
base('╯','╛','╜','╝','┘')
private class LRIntersectionRuneResolver : IntersectionRuneResolver {
public LRIntersectionRuneResolver () :
base ('╯', '╛', '╜', '╝', '┘')
{

}
}

private class TopTeeIntersectionRuneResolver : IntersectionRuneResolver
{
public TopTeeIntersectionRuneResolver():
base('┬','╤','╥','╦','┬'){
}
private class TopTeeIntersectionRuneResolver : IntersectionRuneResolver {
public TopTeeIntersectionRuneResolver () :
base ('┬', '╤', '╥', '╦', '┬')
{

}
}
private class LeftTeeIntersectionRuneResolver : IntersectionRuneResolver
{
public LeftTeeIntersectionRuneResolver():
base('├','╞','╟','╠','├'){
}
private class LeftTeeIntersectionRuneResolver : IntersectionRuneResolver {
public LeftTeeIntersectionRuneResolver () :
base ('├', '╞', '╟', '╠', '├')
{

}
}
private class RightTeeIntersectionRuneResolver : IntersectionRuneResolver
{
public RightTeeIntersectionRuneResolver():
base('┤','╡','╢','╣','┤'){
}
private class RightTeeIntersectionRuneResolver : IntersectionRuneResolver {
public RightTeeIntersectionRuneResolver () :
base ('┤', '╡', '╢', '╣', '┤')
{

}
}
private class BottomTeeIntersectionRuneResolver : IntersectionRuneResolver
{
public BottomTeeIntersectionRuneResolver():
base('┴','╧','╨','╩','┴'){
}
private class BottomTeeIntersectionRuneResolver : IntersectionRuneResolver {
public BottomTeeIntersectionRuneResolver () :
base ('┴', '╧', '╨', '╩', '┴')
{

}
}
private class CrosshairIntersectionRuneResolver : IntersectionRuneResolver
{
public CrosshairIntersectionRuneResolver():
base('┼','╪','╫','╬','┼'){
}
private class CrosshairIntersectionRuneResolver : IntersectionRuneResolver {
public CrosshairIntersectionRuneResolver () :
base ('┼', '╪', '╫', '╬', '┼')
{

}
}

private Rune? GetRuneForIntersects (ConsoleDriver driver, IntersectionDefinition [] intersects)
Expand All @@ -217,7 +193,7 @@ public CrosshairIntersectionRuneResolver():

var runeType = GetRuneTypeForIntersects (intersects);

if(runeResolvers.ContainsKey (runeType)) {
if (runeResolvers.ContainsKey (runeType)) {
return runeResolvers [runeType].GetRuneForIntersects (driver, intersects);
}

Expand All @@ -228,13 +204,13 @@ public CrosshairIntersectionRuneResolver():
// TODO: maybe make these resolvers to for simplicity?
// or for dotted lines later on or that kind of thing?
switch (runeType) {
case IntersectionRuneType.None:
case IntersectionRuneType.None:
return null;
case IntersectionRuneType.Dot:
case IntersectionRuneType.Dot:
return (Rune)'.';
case IntersectionRuneType.HLine:
case IntersectionRuneType.HLine:
return useDouble ? driver.HDLine : driver.HLine;
case IntersectionRuneType.VLine:
case IntersectionRuneType.VLine:
return useDouble ? driver.VDLine : driver.VLine;
default: throw new Exception ("Could not find resolver or switch case for " + nameof (runeType) + ":" + runeType);
}
Expand All @@ -243,7 +219,7 @@ public CrosshairIntersectionRuneResolver():

private IntersectionRuneType GetRuneTypeForIntersects (IntersectionDefinition [] intersects)
{
if(intersects.All(i=>i.Line.Length == 0)) {
if (intersects.All (i => i.Line.Length == 0)) {
return IntersectionRuneType.Dot;
}

Expand Down
15 changes: 13 additions & 2 deletions UICatalog/Scenarios/LineDrawing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,12 @@ public override void Redraw (Rect bounds)
base.Redraw (bounds);

Driver.SetAttribute (new Terminal.Gui.Attribute (Color.DarkGray, ColorScheme.Normal.Background));
grid.Draw (this, bounds);


foreach(var p in grid.GenerateImage(bounds))
{
this.AddRune(p.Key.X,p.Key.Y,p.Value);
}

foreach (var swatch in swatches) {
Driver.SetAttribute (new Terminal.Gui.Attribute (swatch.Value, ColorScheme.Normal.Background));
Expand Down Expand Up @@ -151,7 +156,13 @@ public override void Redraw (Rect bounds)
foreach (var kvp in colorLayers) {

Driver.SetAttribute (new Terminal.Gui.Attribute (kvp.Key, ColorScheme.Normal.Background));
canvases [kvp.Value].Draw (this, bounds);

var canvas = canvases [kvp.Value];

foreach(var p in canvas.GenerateImage(bounds))
{
this.AddRune(p.Key.X,p.Key.Y,p.Value);
}
}
}
public override bool OnMouseEvent (MouseEvent mouseEvent)
Expand Down
Loading