using System;
using System.Collections.Generic;
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)
{
var toReturn = new List ();
if (length == 0) {
return collection;
}
foreach (var 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
var 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;
}
///
/// 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));
}
var 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);
}
///
/// 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));
}
var 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);
}
///
/// 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);
}
}
}