473 lines
No EOL
16 KiB
C#
473 lines
No EOL
16 KiB
C#
/**************************************************
|
|
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
|
|
{
|
|
/// <summary>
|
|
///To define a line in the given coordinate system
|
|
///and related calculations
|
|
///Line Equation:ax+by+c=0
|
|
///</summary>
|
|
|
|
// 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 // Ay<Cy
|
|
return 1; // Y Axis points down, point is below the line
|
|
}
|
|
else
|
|
{
|
|
// Not a horizontal line
|
|
// make the line direction bottom->up
|
|
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;
|
|
}
|
|
}
|
|
} |