namespace Terminal.Gui;
/// Extension methods for (including collections).
public static class StraightLineExtensions
{
///
/// Splits or removes all lines in the such that none cover the given exclusion
/// area.
///
/// Lines to adjust
/// First point to remove from collection
/// The number of sequential points to exclude
/// Orientation of the exclusion line
///
public static IEnumerable Exclude (
this IEnumerable collection,
Point start,
int length,
Orientation orientation
)
{
List toReturn = new ();
if (length == 0)
{
return collection;
}
foreach (StraightLine l in collection)
{
if (l.Length == 0)
{
toReturn.Add (l);
continue;
}
// lines are parallel. For any straight line one axis (x or y) is constant
// e.g. Horizontal lines have constant y
int econstPoint = orientation == Orientation.Horizontal ? start.Y : start.X;
int lconstPoint = l.Orientation == Orientation.Horizontal ? l.Start.Y : l.Start.X;
// For the varying axis what is the max/mins
// i.e. points on horizontal lines vary by x, vertical lines vary by y
int eDiffMin = GetLineStartOnDiffAxis (start, length, orientation);
int eDiffMax = GetLineEndOnDiffAxis (start, length, orientation);
int lDiffMin = GetLineStartOnDiffAxis (l.Start, l.Length, l.Orientation);
int lDiffMax = GetLineEndOnDiffAxis (l.Start, l.Length, l.Orientation);
// line is parallel to exclusion
if (l.Orientation == orientation)
{
// Do the parallel lines share constant plane
if (econstPoint != lconstPoint)
{
// No, so no way they overlap
toReturn.Add (l);
}
else
{
if (lDiffMax < eDiffMin)
{
// Line ends before exclusion starts
toReturn.Add (l);
}
else if (lDiffMin > eDiffMax)
{
// Line starts after exclusion ends
toReturn.Add (l);
}
else
{
//lines overlap!
// Is there a bit we can keep on the left?
if (lDiffMin < eDiffMin)
{
// Create line up to exclusion point
int from = lDiffMin;
int len = eDiffMin - lDiffMin;
if (len > 0)
{
toReturn.Add (CreateLineFromDiff (l, from, len));
}
}
// Is there a bit we can keep on the right?
if (lDiffMax > eDiffMax)
{
// Create line up to exclusion point
int from = eDiffMax + 1;
int len = lDiffMax - eDiffMax;
if (len > 0)
{
// A single line with length 1 and -1 are the same (fills only the single cell)
// They differ only in how they join to other lines (i.e. to create corners)
// Using negative for the later half of the line ensures line joins in a way
// consistent with its pre-snipped state.
if (len == 1)
{
len = -1;
}
toReturn.Add (CreateLineFromDiff (l, from, len));
}
}
}
}
}
else
{
// line is perpendicular to exclusion
// Does the constant plane of the exclusion appear within the differing plane of the line?
if (econstPoint >= lDiffMin && econstPoint <= lDiffMax)
{
// Yes, e.g. Vertical exclusion's x is within xmin/xmax of the horizontal line
// Vice versa must also be true
// for example there is no intersection if the vertical exclusion line does not
// stretch down far enough to reach the line
if (lconstPoint >= eDiffMin && lconstPoint <= eDiffMax)
{
// Perpendicular intersection occurs here
Point intersection = l.Orientation == Orientation.Horizontal
? new Point (econstPoint, lconstPoint)
: new Point (lconstPoint, econstPoint);
// To snip out this single point we will use a recursive call
// snipping 1 length along the orientation of l (i.e. parallel)
toReturn.AddRange (new [] { l }.Exclude (intersection, 1, l.Orientation));
}
else
{
// No intersection
toReturn.Add (l);
}
}
else
{
// Lines do not intersect
toReturn.Add (l);
}
}
}
return toReturn;
}
///
/// Creates a new line which is part of from the point on the varying axis
/// to . Horizontal lines have points that vary by x while vertical
/// lines have points that vary by y
///
/// Line to create sub part from
/// Point on varying axis to start at
/// Length of line to return
/// The new line
private static StraightLine CreateLineFromDiff (StraightLine l, int from, int length)
{
var start = new Point (
l.Orientation == Orientation.Horizontal ? from : l.Start.X,
l.Orientation == Orientation.Horizontal ? l.Start.Y : from
);
return new StraightLine (start, length, l.Orientation, l.Style, l.Attribute);
}
///
///
/// Calculates the single digit point where a line ends on the differing axis i.e. the maximum (controlling for
/// negative lengths).
///
///
/// For lines with this is an x coordinate. For lines that are
/// this is a y coordinate.
///
///
/// Where the line starts
/// Length of the line
/// Orientation of the line
/// The maximum x or y (whichever is differing) point on the line, controlling for negative lengths.
private static int GetLineEndOnDiffAxis (Point start, int length, Orientation orientation)
{
if (length == 0)
{
throw new ArgumentException ("0 length lines are not supported", nameof (length));
}
int sub = length > 0 ? 1 : -1;
if (orientation == Orientation.Vertical)
{
// Points on line differ by y
return Math.Max (start.Y + length - sub, start.Y);
}
// Points on line differ by x
return Math.Max (start.X + length - sub, start.X);
}
///
///
/// Calculates the single digit point where a line starts on the differing axis i.e. the minimum (controlling for
/// negative lengths).
///
///
/// For lines with this is an x coordinate. For lines that are
/// this is a y coordinate.
///
///
/// Where the line starts
/// Length of the line
/// Orientation of the line
/// The minimum x or y (whichever is differing) point on the line, controlling for negative lengths.
private static int GetLineStartOnDiffAxis (Point start, int length, Orientation orientation)
{
if (length == 0)
{
throw new ArgumentException ("0 length lines are not supported", nameof (length));
}
int sub = length > 0 ? 1 : -1;
if (orientation == Orientation.Vertical)
{
// Points on line differ by y
return Math.Min (start.Y + length - sub, start.Y);
}
// Points on line differ by x
return Math.Min (start.X + length - sub, start.X);
}
}