/**************************************************
This unit is used to collect Analytic Geometry formulars
It includes Line, Line segment and CPolygon
Development by: Frank Shen
Date: 08, 2004
Modification History:
* *** **********************************************/
using System;
namespace GeometryUtility
{
///
///To define a line in the given coordinate system
///and related calculations
///Line Equation:ax+by+c=0
///
// a Line in 2D coordinate system: ax+by+c=0
public class CLine
{
// line: ax+by+c=0;
protected double a;
protected double b;
protected double c;
private void Initialize(double angleInRad, CPoint2D point)
{
// angleInRad should be between 0-Pi
try
{
// if ((angleInRad<0) ||(angleInRad>Math.PI))
if (angleInRad > 2 * Math.PI)
{
string errMsg = "The input line angle" + $" {angleInRad} is wrong. It should be between 0-2*PI.";
InvalidInputGeometryDataException ex = new InvalidInputGeometryDataException(errMsg);
throw ex;
}
if (Math.Abs(angleInRad - Math.PI / 2) < ConstantValue.SmallValue)
{
// vertical line
a = 1;
b = 0;
c = -point.X;
}
else
{
// not vertical line
a = -Math.Tan(angleInRad);
b = 1;
c = (-a * point.X) - (b * point.Y);
}
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine(e.Message + e.StackTrace);
}
}
public CLine(double angleInRad, CPoint2D point)
{
Initialize(angleInRad, point);
}
public CLine(CPoint2D point1, CPoint2D point2)
{
try
{
if (CPoint2D.SamePoints(point1, point2))
{
string errMsg = "The input points are the same";
InvalidInputGeometryDataException ex = new InvalidInputGeometryDataException(errMsg);
throw ex;
}
// Point1 and Point2 are different points:
if (Math.Abs(point1.X - point2.X) < ConstantValue.SmallValue)
{
// vertical line
Initialize(Math.PI / 2, point1);
}
else if (Math.Abs(point1.Y - point2.Y) < ConstantValue.SmallValue)
{
// Horizontal line
Initialize(0, point1);
}
else
{
// normal line
double m = (point2.Y - point1.Y) / (point2.X - point1.X);
double alphaInRad = Math.Atan(m);
Initialize(alphaInRad, point1);
}
}
catch (Exception e)
{
System.Diagnostics.Trace.WriteLine(e.Message + e.StackTrace);
}
}
public CLine(CLine copiedLine)
{
a = copiedLine.a;
b = copiedLine.b;
c = copiedLine.c;
}
/*** calculate the distance from a given point to the line ***/
public double GetDistance(CPoint2D point)
{
double x0 = point.X;
double y0 = point.Y;
double d = Math.Abs(a * x0 + b * y0 + c);
d = d / Math.Sqrt(a * a + b * b);
return d;
}
/*** point(x, y) in the line, based on y, calculate x ***/
public double GetX(double y)
{
// if the line is a horizontal line (a=0), it will return a NaN:
double x;
try
{
if (Math.Abs(a) < ConstantValue.SmallValue)
{
// a=0;
throw new NonValidReturnException();
}
x = -(b * y + c) / a;
}
catch (Exception e)
{
// Horizontal line a=0;
x = double.NaN;
System.Diagnostics.Trace.WriteLine(e.Message + e.StackTrace);
}
return x;
}
/*** point(x, y) in the line, based on x, calculate y ***/
public double GetY(double x)
{
// if the line is a vertical line, it will return a NaN:
double y;
try
{
if (Math.Abs(b) < ConstantValue.SmallValue)
{
throw new NonValidReturnException();
}
y = -(a * x + c) / b;
}
catch (Exception e)
{
y = double.NaN;
System.Diagnostics.Trace.WriteLine(e.Message + e.StackTrace);
}
return y;
}
/*** is it a vertical line:***/
public bool VerticalLine()
{
if (Math.Abs(b - 0) < ConstantValue.SmallValue) return true;
else return false;
}
/*** is it a horizontal line:***/
public bool HorizontalLine()
{
if (Math.Abs(a - 0) < ConstantValue.SmallValue) return true;
else return false;
}
/*** calculate line angle in radian: ***/
public double GetLineAngle()
{
if (Math.Abs(b) < float.Epsilon)
{
return Math.PI / 2;
}
else
{
// b!=0
double tanA = -a / b;
return Math.Atan(tanA);
}
}
public bool Parallel(CLine line)
{
return Math.Abs(a / b - line.a / line.b) < float.Epsilon;
}
/**************************************
Calculate intersection point of two lines
if two lines are parallel, return null
* ************************************/
public CPoint2D IntersecctionWith(CLine line)
{
CPoint2D point = new CPoint2D();
double a1 = a;
double b1 = b;
double c1 = c;
double a2 = line.a;
double b2 = line.b;
double c2 = line.c;
if (!Parallel(line))
{
// not parallen
point.X = (c2 * b1 - c1 * b2) / (a1 * b2 - a2 * b1);
point.Y = (a1 * c2 - c1 * a2) / (a2 * b2 - a1 * b2);
}
return point;
}
}
public class CLineSegment : CLine
{
// line: ax+by+c=0, with start point and end point
// direction from start point ->end point
private CPoint2D _startPoint;
private CPoint2D _endPoint;
public CPoint2D StartPoint => _startPoint;
public CPoint2D EndPoint => _endPoint;
public CLineSegment(CPoint2D startPoint, CPoint2D endPoint)
: base(startPoint, endPoint)
{
_startPoint = startPoint;
_endPoint = endPoint;
}
/*** chagne the line's direction ***/
public void ChangeLineDirection()
{
CPoint2D tempPt;
tempPt = _startPoint;
_startPoint = _endPoint;
_endPoint = tempPt;
}
/*** To calculate the line segment length: ***/
public double GetLineSegmentLength()
{
var d = (_endPoint.X - _startPoint.X) * (_endPoint.X - _startPoint.X);
d += (_endPoint.Y - _startPoint.Y) * (_endPoint.Y - _startPoint.Y);
d = Math.Sqrt(d);
return d;
}
/**********************************************************
Get point location, using windows coordinate system:
y-axes points down.
Return Value:
-1:point at the left of the line (or above the line if the line is horizontal)
0: point in the line segment or in the line segment 's extension
1: point at right of the line (or below the line if the line is horizontal)
***********************************************************/
public int GetPointLocation(CPoint2D point)
{
double Ax, Ay, Bx, By, Cx, Cy;
Bx = _endPoint.X;
By = _endPoint.Y;
Ax = _startPoint.X;
Ay = _startPoint.Y;
Cx = point.X;
Cy = point.Y;
if (HorizontalLine())
{
if (Math.Abs(Ay - Cy) < ConstantValue.SmallValue) // equal
return 0;
else if (Ay > Cy) return -1; // Y Axis points down, point is above the line
else // Ayup
if (_endPoint.Y > _startPoint.Y) ChangeLineDirection();
double L = GetLineSegmentLength();
double s = ((Ay - Cy) * (Bx - Ax) - (Ax - Cx) * (By - Ay)) / (L * L);
// Note: the Y axis is pointing down:
if (Math.Abs(s - 0) < ConstantValue.SmallValue) // s=0
return 0; // point is in the line or line extension
else if (s > 0) return -1; // point is left of line or above the horizontal line
else // s<0
return 1;
}
}
/***Get the minimum x value of the points in the line***/
public double GetXmin()
{
return Math.Min(_startPoint.X, _endPoint.X);
}
/***Get the maximum x value of the points in the line***/
public double GetXmax()
{
return Math.Max(_startPoint.X, _endPoint.X);
}
/***Get the minimum y value of the points in the line***/
public double GetYmin()
{
return Math.Min(_startPoint.Y, _endPoint.Y);
}
/***Get the maximum y value of the points in the line***/
public double GetYmax()
{
return Math.Max(_startPoint.Y, _endPoint.Y);
}
/***Check whether this line is in a longer line***/
public bool InLine(CLineSegment longerLineSegment)
{
bool bInLine = false;
if (_startPoint.InLine(longerLineSegment) && (_endPoint.InLine(longerLineSegment))) bInLine = true;
return bInLine;
}
/************************************************
* Offset the line segment to generate a new line segment
* If the offset direction is along the x-axis or y-axis,
* Parameter is true, other wise it is false
* ***********************************************/
public CLineSegment OffsetLine(double distance, bool rightOrDown)
{
// offset a line with a given distance, generate a new line
// rightOrDown=true means offset to x incress direction,
// if the line is horizontal, offset to y incress direction
CLineSegment line;
CPoint2D newStartPoint = new CPoint2D();
CPoint2D newEndPoint = new CPoint2D();
double alphaInRad = GetLineAngle(); // 0-PI
if (rightOrDown)
{
if (HorizontalLine())
{
// offset to y+ direction
newStartPoint.X = _startPoint.X;
newStartPoint.Y = _startPoint.Y + distance;
newEndPoint.X = _endPoint.X;
newEndPoint.Y = _endPoint.Y + distance;
line = new CLineSegment(newStartPoint, newEndPoint);
}
else
{
// offset to x+ direction
if (Math.Sin(alphaInRad) > 0)
{
newStartPoint.X = _startPoint.X + Math.Abs(distance * Math.Sin(alphaInRad));
newStartPoint.Y = _startPoint.Y - Math.Abs(distance * Math.Cos(alphaInRad));
newEndPoint.X = _endPoint.X + Math.Abs(distance * Math.Sin(alphaInRad));
newEndPoint.Y = _endPoint.Y - Math.Abs(distance * Math.Cos(alphaInRad));
line = new CLineSegment(newStartPoint, newEndPoint);
}
else
{
// sin(FalphaInRad)<0
newStartPoint.X = _startPoint.X + Math.Abs(distance * Math.Sin(alphaInRad));
newStartPoint.Y = _startPoint.Y + Math.Abs(distance * Math.Cos(alphaInRad));
newEndPoint.X = _endPoint.X + Math.Abs(distance * Math.Sin(alphaInRad));
newEndPoint.Y = _endPoint.Y + Math.Abs(distance * Math.Cos(alphaInRad));
line = new CLineSegment(newStartPoint, newEndPoint);
}
}
}
// {rightOrDown}
else
{
// leftOrUp
if (HorizontalLine())
{
// offset to y directin
newStartPoint.X = _startPoint.X;
newStartPoint.Y = _startPoint.Y - distance;
newEndPoint.X = _endPoint.X;
newEndPoint.Y = _endPoint.Y - distance;
line = new CLineSegment(newStartPoint, newEndPoint);
}
else
{
// offset to x directin
if (Math.Sin(alphaInRad) >= 0)
{
newStartPoint.X = _startPoint.X - Math.Abs(distance * Math.Sin(alphaInRad));
newStartPoint.Y = _startPoint.Y + Math.Abs(distance * Math.Cos(alphaInRad));
newEndPoint.X = _endPoint.X - Math.Abs(distance * Math.Sin(alphaInRad));
newEndPoint.Y = _endPoint.Y + Math.Abs(distance * Math.Cos(alphaInRad));
line = new CLineSegment(newStartPoint, newEndPoint);
}
else
{
// sin(FalphaInRad)<0
newStartPoint.X = _startPoint.X - Math.Abs(distance * Math.Sin(alphaInRad));
newStartPoint.Y = _startPoint.Y - Math.Abs(distance * Math.Cos(alphaInRad));
newEndPoint.X = _endPoint.X - Math.Abs(distance * Math.Sin(alphaInRad));
newEndPoint.Y = _endPoint.Y - Math.Abs(distance * Math.Cos(alphaInRad));
line = new CLineSegment(newStartPoint, newEndPoint);
}
}
}
return line;
}
/********************************************************
To check whether 2 lines segments have an intersection
*********************************************************/
public bool IntersectedWith(CLineSegment line)
{
double x1 = _startPoint.X;
double y1 = _startPoint.Y;
double x2 = _endPoint.X;
double y2 = _endPoint.Y;
double x3 = line._startPoint.X;
double y3 = line._startPoint.Y;
double x4 = line._endPoint.X;
double y4 = line._endPoint.Y;
double de = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
// if de<>0 then //lines are not parallel
if (Math.Abs(de - 0) < ConstantValue.SmallValue)
{
// not parallel
double ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / de;
double ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / de;
if ((ub > 0) && (ub < 1)) return true;
else return false;
}
else // lines are parallel
return false;
}
}
}