/************************************************** 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; } } }