using System;
namespace Terminal.Gui {
// TODO: Add events that notify when StraightLine changes to enable dynamic layout
///
/// A line between two points on a horizontal or vertical
/// and a given style/color.
///
public class StraightLine {
///
/// Gets or sets where the line begins.
///
public Point Start { get; set; }
///
/// Gets or sets the length of the line.
///
public int Length { get; set; }
///
/// Gets or sets the orientation (horizontal or vertical) of the line.
///
public Orientation Orientation { get; set; }
///
/// Gets or sets the line style of the line (e.g. dotted, double).
///
public LineStyle Style { get; set; }
///
/// Gets or sets the color of the line.
///
public Attribute? Attribute { get; set; }
///
/// Creates a new instance of the class.
///
///
///
///
///
///
public StraightLine (Point start, int length, Orientation orientation, LineStyle style, Attribute? attribute = default)
{
this.Start = start;
this.Length = length;
this.Orientation = orientation;
this.Style = style;
this.Attribute = attribute;
}
internal IntersectionDefinition Intersects (int x, int y)
{
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,
GetTypeByLength (IntersectionType.StartLeft, IntersectionType.PassOverHorizontal, 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,
GetTypeByLength (IntersectionType.StartUp, IntersectionType.PassOverVertical, 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 IntersectionType GetTypeByLength (IntersectionType typeWhenNegative, IntersectionType typeWhenZero, IntersectionType typeWhenPositive)
{
if (Length == 0) {
return typeWhenZero;
}
return Length < 0 ? typeWhenNegative : typeWhenPositive;
}
private bool EndsAt (int x, int y)
{
var sub = (Length == 0) ? 0 : (Length > 0) ? 1 : -1;
if (Orientation == Orientation.Horizontal) {
return Start.X + Length - sub == x && Start.Y == y;
}
return Start.X == x && Start.Y + Length - sub == y;
}
private bool StartsAt (int x, int y)
{
return Start.X == x && Start.Y == y;
}
///
/// Gets the rectangle that describes the bounds of the canvas. Location is the coordinates of the
/// line that is furthest left/top and Size is defined by the line that extends the furthest
/// right/bottom.
///
internal Rect Bounds {
get {
// 0 and 1/-1 Length means a size (width or height) of 1
var size = Math.Max (1, Math.Abs (Length));
// How much to offset x or y to get the start of the line
var offset = Math.Abs (Length < 0 ? Length + 1 : 0);
var x = Start.X - (Orientation == Orientation.Horizontal ? offset : 0);
var y = Start.Y - (Orientation == Orientation.Vertical ? offset : 0);
var width = Orientation == Orientation.Horizontal ? size : 1;
var height = Orientation == Orientation.Vertical ? size : 1;
return new Rect (x, y, width, height);
}
}
///
/// Formats the Line as a string in (Start.X,Start.Y,Length,Orientation) notation.
///
public override string ToString ()
{
return $"({Start.X},{Start.Y},{Length},{Orientation})";
}
}
}