/// <summary> /// /// </summary> /// <param name="line1Start"></param> /// <param name="line1End"></param> /// <param name="line2Start"></param> /// <param name="line2End"></param> /// <returns></returns> public static double AngleLineSegments( Point2 line1Start, Point2 line1End, Point2 line2Start, Point2 line2End) { double angle1 = Math.Atan2(line1Start.Y - line1End.Y, line1Start.X - line1End.X); double angle2 = Math.Atan2(line2Start.Y - line2End.Y, line2Start.X - line2End.X); double result = (angle2 - angle1) * 180.0 / Math.PI; if (result < 0) result += 360.0; return result; }
/// <summary> /// /// </summary> /// <param name="centerPoint"></param> /// <param name="angleInDegrees"></param> /// <returns></returns> public Point2 RotateAt( Point2 centerPoint, double angleInDegrees) { double angleInRadians = angleInDegrees * (Math.PI / 180.0); double cosTheta = Math.Cos(angleInRadians); double sinTheta = Math.Sin(angleInRadians); return new Point2 { X = (cosTheta * (this.X - centerPoint.X) - sinTheta * (this.Y - centerPoint.Y) + centerPoint.X), Y = (sinTheta * (this.X - centerPoint.X) + cosTheta * (this.Y - centerPoint.Y) + centerPoint.Y) }; }
/// <summary> /// /// </summary> /// <param name="point"></param> /// <returns></returns> public double Distance(Point2 point) { double dx = this.X - point.X; double dy = this.Y - point.Y; return Math.Sqrt(dx * dx + dy * dy); }
/// <summary> /// /// </summary> /// <param name="tl"></param> /// <param name="br"></param> /// <param name="dx"></param> /// <param name="dy"></param> /// <returns></returns> public static Rect2 Create( Point2 tl, Point2 br, double dx = 0.0, double dy = 0.0) { return Rect2.Create(tl.X, tl.Y, br.X, br.Y, dx, dy); }
/// <summary> /// /// </summary> /// <param name="rect"></param> /// <param name="p1"></param> /// <param name="p2"></param> /// <param name="onlySegment"></param> /// <returns></returns> public static IList<Point2> FindEllipseSegmentIntersections( Rect2 rect, Point2 p1, Point2 p2, bool onlySegment) { if ((rect.Width == 0) || (rect.Height == 0) || ((p1.X == p2.X) && (p1.Y == p2.Y))) return new Point2[] { }; if (rect.Width < 0) { rect.X = rect.Right; rect.Width = -rect.Width; } if (rect.Height < 0) { rect.Y = rect.Bottom; rect.Height = -rect.Height; } double cx = rect.Left + rect.Width / 2.0; double cy = rect.Top + rect.Height / 2.0; rect.X -= cx; rect.Y -= cy; p1.X -= cx; p1.Y -= cy; p2.X -= cx; p2.Y -= cy; double a = rect.Width / 2.0; double b = rect.Height / 2.0; double A = (p2.X - p1.X) * (p2.X - p1.X) / a / a + (p2.Y - p1.Y) * (p2.Y - p1.Y) / b / b; double B = 2 * p1.X * (p2.X - p1.X) / a / a + 2 * p1.Y * (p2.Y - p1.Y) / b / b; double C = p1.X * p1.X / a / a + p1.Y * p1.Y / b / b - 1; var solutions = new List<double>(); double discriminant = B * B - 4 * A * C; if (discriminant == 0) { solutions.Add(-B / 2 / A); } else if (discriminant > 0) { solutions.Add((-B + Math.Sqrt(discriminant)) / 2 / A); solutions.Add((-B - Math.Sqrt(discriminant)) / 2 / A); } var points = new List<Point2>(); foreach (var t in solutions) { if (!onlySegment || ((t >= 0f) && (t <= 1f))) { double x = p1.X + (p2.X - p1.X) * t + cx; double y = p1.Y + (p2.Y - p1.Y) * t + cy; points.Add(Point2.Create(x, y)); } } return points; }
/// <summary> /// Check if line intersects with rectangle using Liang-Barsky line clipping algorithm. /// </summary> /// <param name="rect"></param> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns>True if line intersects with rectangle.</returns> public static bool LineIntersectsWithRect(Rect2 rect, Point2 p0, Point2 p1) { double left = rect.Left; double right = rect.Right; double bottom = rect.Bottom; double top = rect.Top; double x0 = p0.X; double y0 = p0.Y; double x1 = p1.X; double y1 = p1.Y; double t0 = 0.0; double t1 = 1.0; double dx = x1 - x0; double dy = y1 - y0; double p = 0.0, q = 0.0, r; for (int edge = 0; edge < 4; edge++) { if (edge == 0) { p = -dx; q = -(left - x0); } if (edge == 1) { p = dx; q = (right - x0); } if (edge == 2) { p = dy; q = (bottom - y0); } if (edge == 3) { p = -dy; q = -(top - y0); } r = q / p; if (p == 0.0 && q < 0.0) { return false; } if (p < 0.0) { if (r > t1) { return false; } else if (r > t0) { t0 = r; } } else if (p > 0.0) { if (r < t0) { return false; } else if (r < t1) { t1 = r; } } } // to calulate clipped line position // x0clip = x0 + t0 * dx; // y0clip = y0 + t0 * dy; // x1clip = x0 + t1 * dx; // y1clip = y0 + t1 * dy; return true; }
/// <summary> /// /// </summary> /// <param name="rect"></param> /// <param name="p1"></param> /// <param name="p2"></param> /// <param name="onlySegment"></param> /// <returns></returns> public static IList <Point2> FindEllipseSegmentIntersections( Rect2 rect, Point2 p1, Point2 p2, bool onlySegment) { if ((rect.Width == 0) || (rect.Height == 0) || ((p1.X == p2.X) && (p1.Y == p2.Y))) { return new Point2[] { } } ; if (rect.Width < 0) { rect.X = rect.Right; rect.Width = -rect.Width; } if (rect.Height < 0) { rect.Y = rect.Bottom; rect.Height = -rect.Height; } double cx = rect.Left + rect.Width / 2.0; double cy = rect.Top + rect.Height / 2.0; rect.X -= cx; rect.Y -= cy; p1.X -= cx; p1.Y -= cy; p2.X -= cx; p2.Y -= cy; double a = rect.Width / 2.0; double b = rect.Height / 2.0; double A = (p2.X - p1.X) * (p2.X - p1.X) / a / a + (p2.Y - p1.Y) * (p2.Y - p1.Y) / b / b; double B = 2 * p1.X * (p2.X - p1.X) / a / a + 2 * p1.Y * (p2.Y - p1.Y) / b / b; double C = p1.X * p1.X / a / a + p1.Y * p1.Y / b / b - 1; var solutions = new List <double>(); double discriminant = B * B - 4 * A * C; if (discriminant == 0) { solutions.Add(-B / 2 / A); } else if (discriminant > 0) { solutions.Add((-B + Math.Sqrt(discriminant)) / 2 / A); solutions.Add((-B - Math.Sqrt(discriminant)) / 2 / A); } var points = new List <Point2>(); foreach (var t in solutions) { if (!onlySegment || ((t >= 0f) && (t <= 1f))) { double x = p1.X + (p2.X - p1.X) * t + cx; double y = p1.Y + (p2.Y - p1.Y) * t + cy; points.Add(Point2.Create(x, y)); } } return(points); }
/// <summary> /// Check if line intersects with rectangle using Liang-Barsky line clipping algorithm. /// </summary> /// <param name="rect"></param> /// <param name="p0"></param> /// <param name="p1"></param> /// <returns>True if line intersects with rectangle.</returns> public static bool LineIntersectsWithRect(Rect2 rect, Point2 p0, Point2 p1) { double left = rect.Left; double right = rect.Right; double bottom = rect.Bottom; double top = rect.Top; double x0 = p0.X; double y0 = p0.Y; double x1 = p1.X; double y1 = p1.Y; double t0 = 0.0; double t1 = 1.0; double dx = x1 - x0; double dy = y1 - y0; double p = 0.0, q = 0.0, r; for (int edge = 0; edge < 4; edge++) { if (edge == 0) { p = -dx; q = -(left - x0); } if (edge == 1) { p = dx; q = (right - x0); } if (edge == 2) { p = dy; q = (bottom - y0); } if (edge == 3) { p = -dy; q = -(top - y0); } r = q / p; if (p == 0.0 && q < 0.0) { return(false); } if (p < 0.0) { if (r > t1) { return(false); } else if (r > t0) { t0 = r; } } else if (p > 0.0) { if (r < t0) { return(false); } else if (r < t1) { t1 = r; } } } // to calulate clipped line position // x0clip = x0 + t0 * dx; // y0clip = y0 + t0 * dy; // x1clip = x0 + t1 * dx; // y1clip = y0 + t1 * dy; return(true); }
/// <summary> /// /// </summary> /// <param name="arc"></param> /// <param name="dx"></param> /// <param name="dy"></param> /// <returns></returns> public static WpfArc FromXArc(XArc arc, double dx, double dy) { var p1 = Point2.Create(arc.Point1.X + dx, arc.Point1.Y + dy); var p2 = Point2.Create(arc.Point2.X + dx, arc.Point2.Y + dy); var p3 = Point2.Create(arc.Point3.X + dx, arc.Point3.Y + dy); var p4 = Point2.Create(arc.Point4.X + dx, arc.Point4.Y + dy); var rect = Rect2.Create(p1, p2); var center = Point2.Create(rect.X + rect.Width / 2.0, rect.Y + rect.Height / 2.0); double offsetX = center.X - rect.X; double offsetY = center.Y - rect.Y; double minLenght = Math.Max(offsetX, offsetY); double length1 = center.Distance(p3); double p3x = p3.X + (p3.X - center.X) / length1 * minLenght; double p3y = p3.Y + (p3.Y - center.Y) / length1 * minLenght; double length2 = center.Distance(p4); double p4x = p4.X + (p4.X - center.X) / length2 * minLenght; double p4y = p4.Y + (p4.Y - center.Y) / length2 * minLenght; p3.X = p3x; p3.Y = p3y; p4.X = p4x; p4.Y = p4y; var p3i = MathHelpers.FindEllipseSegmentIntersections(rect, center, p3, true); var p4i = MathHelpers.FindEllipseSegmentIntersections(rect, center, p4, true); Point2 start; Point2 end; if (p3i.Count == 1) { start = p3i.FirstOrDefault(); } else { start = Point2.Create(p3.X, p3.Y); } if (p4i.Count == 1) { end = p4i.FirstOrDefault(); } else { end = Point2.Create(p4.X, p4.Y); } double angle = MathHelpers.AngleLineSegments(center, start, center, end); bool isLargeArc = angle > 180.0; double helperLenght = 60.0; double lengthStart = center.Distance(start); double p3hx = start.X + (start.X - center.X) / lengthStart * helperLenght; double p3hy = start.Y + (start.Y - center.Y) / lengthStart * helperLenght; double lengthEnd = center.Distance(end); double p4hx = end.X + (end.X - center.X) / lengthEnd * helperLenght; double p4hy = end.Y + (end.Y - center.Y) / lengthEnd * helperLenght; p3.X = p3hx; p3.Y = p3hy; p4.X = p4hx; p4.Y = p4hy; return(new WpfArc() { P1 = p1, P2 = p2, P3 = p3, P4 = p4, Rect = rect, Center = center, Start = start, End = end, Radius = Size2.Create(offsetX, offsetY), IsLargeArc = isLargeArc, Angle = angle }); }
/// <summary> /// /// </summary> /// <param name="tl"></param> /// <param name="br"></param> /// <param name="dx"></param> /// <param name="dy"></param> /// <returns></returns> public static Rect2 Create( Point2 tl, Point2 br, double dx = 0.0, double dy = 0.0) { return(Rect2.Create(tl.X, tl.Y, br.X, br.Y, dx, dy)); }