/// <summary> /// /// </summary> /// <param name="shape"></param> /// <param name="rect"></param> /// <param name="selection"></param> /// <param name="builder"></param> /// <param name="treshold"></param> /// <param name="dx"></param> /// <param name="dy"></param> /// <returns></returns> private static bool HitTest( BaseShape shape, Rect2 rect, Vector2[] selection, ImmutableHashSet<BaseShape>.Builder builder, double treshold, double dx, double dy) { if (shape is XPoint) { if (GetPointBounds(shape as XPoint, treshold, dx, dy).IntersectsWith(rect)) { if (builder != null) { builder.Add(shape); } else { return true; } } return false; } else if (shape is XLine) { var line = shape as XLine; if (GetPointBounds(line.Start, treshold, dx, dy).IntersectsWith(rect) || GetPointBounds(line.End, treshold, dx, dy).IntersectsWith(rect) || MathHelpers.LineIntersectsWithRect(rect, new Point2(line.Start.X, line.Start.Y), new Point2(line.End.X, line.End.Y))) { if (builder != null) { builder.Add(line); return false; } else { return true; } } return false; } else if (shape is XEllipse) { if (GetEllipseBounds(shape as XEllipse, dx, dy).IntersectsWith(rect)) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } else if (shape is XRectangle) { if (GetRectangleBounds(shape as XRectangle, dx, dy).IntersectsWith(rect)) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } else if (shape is XArc) { if (GetArcBounds(shape as XArc, dx, dy).IntersectsWith(rect)) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } else if (shape is XBezier) { if (ConvexHullBounds.Overlap(selection, ConvexHullBounds.GetVertices(shape as XBezier, dx, dy))) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } else if (shape is XQBezier) { if (ConvexHullBounds.Overlap(selection, ConvexHullBounds.GetVertices(shape as XQBezier, dx, dy))) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } else if (shape is XText) { if (GetTextBounds(shape as XText, dx, dy).IntersectsWith(rect)) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } else if (shape is XImage) { if (GetImageBounds(shape as XImage, dx, dy).IntersectsWith(rect)) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } else if (shape is XPath) { if ((shape as XPath).Geometry != null) { var points = (shape as XPath).GetAllPoints(); if (ConvexHullBounds.Overlap(selection, ConvexHullBounds.GetVertices(points, dx, dy))) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } } return false; } else if (shape is XGroup) { if (HitTest((shape as XGroup).Shapes.Reverse(), rect, selection, null, treshold, dx, dy) == true) { if (builder != null) { builder.Add(shape); return false; } else { return true; } } return false; } return false; }
/// <summary> /// /// </summary> /// <param name="shapes"></param> /// <param name="rect"></param> /// <param name="selection"></param> /// <param name="builder"></param> /// <param name="treshold"></param> /// <param name="dx"></param> /// <param name="dy"></param> /// <returns></returns> private static bool HitTest( IEnumerable<BaseShape> shapes, Rect2 rect, Vector2[] selection, ImmutableHashSet<BaseShape>.Builder builder, double treshold, double dx, double dy) { foreach (var shape in shapes) { var result = HitTest(shape, rect, selection, builder, treshold, dx, dy); if (result == true) { return true; } } return false; }
/// <summary> /// /// </summary> /// <param name="gfx"></param> /// <param name="stroke"></param> /// <param name="rect"></param> /// <param name="offsetX"></param> /// <param name="offsetY"></param> /// <param name="cellWidth"></param> /// <param name="cellHeight"></param> /// <param name="isStroked"></param> private void DrawGridInternal( Graphics gfx, Pen stroke, ref Rect2 rect, double offsetX, double offsetY, double cellWidth, double cellHeight, bool isStroked) { double ox = rect.X; double oy = rect.Y; double sx = ox + offsetX; double sy = oy + offsetY; double ex = ox + rect.Width; double ey = oy + rect.Height; for (double x = sx; x < ex; x += cellWidth) { var p0 = new PointF( _scaleToPage(x), _scaleToPage(oy)); var p1 = new PointF( _scaleToPage(x), _scaleToPage(ey)); DrawLineInternal(gfx, stroke, isStroked, ref p0, ref p1); } for (double y = sy; y < ey; y += cellHeight) { var p0 = new PointF( _scaleToPage(ox), _scaleToPage(y)); var p1 = new PointF( _scaleToPage(ex), _scaleToPage(y)); DrawLineInternal(gfx, stroke, isStroked, ref p0, ref p1); } }
/// <summary> /// /// </summary> /// <param name="container"></param> /// <param name="rect"></param> /// <param name="treshold"></param> /// <returns></returns> public static ImmutableHashSet<BaseShape> HitTest(Container container, Rect2 rect, double treshold) { var builder = ImmutableHashSet.CreateBuilder<BaseShape>(); var selection = new Vector2[] { new Vector2(rect.X, rect.Y), new Vector2(rect.X + rect.Width, rect.Y), new Vector2(rect.X + rect.Width, rect.Y + rect.Height), new Vector2(rect.X, rect.Y + rect.Height) }; HitTest(container.CurrentLayer.Shapes.Reverse(), rect, selection, builder, treshold, 0, 0); return builder.ToImmutableHashSet(); }
/// <summary> /// /// </summary> /// <param name="gfx"></param> /// <param name="brush"></param> /// <param name="pen"></param> /// <param name="isStroked"></param> /// <param name="isFilled"></param> /// <param name="rect"></param> private static void DrawRectangleInternal( Graphics gfx, Brush brush, Pen pen, bool isStroked, bool isFilled, ref Rect2 rect) { if (isFilled) { gfx.FillRectangle( brush, (float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height); } if (isStroked) { gfx.DrawRectangle( pen, (float)rect.X, (float)rect.Y, (float)rect.Width, (float)rect.Height); } }
/// <summary> /// /// </summary> /// <param name="gfx"></param> /// <param name="line"></param> /// <param name="dx"></param> /// <param name="dy"></param> /// <param name="db"></param> /// <param name="r"></param> public void Draw(object gfx, XLine line, double dx, double dy, ImmutableArray<ShapeProperty> db, Record r) { var _gfx = gfx as Graphics; Brush fillLine = ToSolidBrush(line.Style.Fill); Pen strokeLine = ToPen(line.Style, _scaleToPage); Brush fillStartArrow = ToSolidBrush(line.Style.StartArrowStyle.Fill); Pen strokeStartArrow = ToPen(line.Style.StartArrowStyle, _scaleToPage); Brush fillEndArrow = ToSolidBrush(line.Style.EndArrowStyle.Fill); Pen strokeEndArrow = ToPen(line.Style.EndArrowStyle, _scaleToPage); double _x1 = line.Start.X + dx; double _y1 = line.Start.Y + dy; double _x2 = line.End.X + dx; double _y2 = line.End.Y + dy; XLine.SetMaxLength(line, ref _x1, ref _y1, ref _x2, ref _y2); float x1 = _scaleToPage(_x1); float y1 = _scaleToPage(_y1); float x2 = _scaleToPage(_x2); float y2 = _scaleToPage(_y2); var sas = line.Style.StartArrowStyle; var eas = line.Style.EndArrowStyle; float a1 = (float)(Math.Atan2(y1 - y2, x1 - x2) * 180.0 / Math.PI); float a2 = (float)(Math.Atan2(y2 - y1, x2 - x1) * 180.0 / Math.PI); var t1 = new Matrix(); var c1 = new PointF(x1, y1); t1.RotateAt(a1, c1); var t2 = new Matrix(); var c2 = new PointF(x2, y2); t2.RotateAt(a2, c2); PointF pt1; PointF pt2; double radiusX1 = sas.RadiusX; double radiusY1 = sas.RadiusY; double sizeX1 = 2.0 * radiusX1; double sizeY1 = 2.0 * radiusY1; switch (sas.ArrowType) { default: case ArrowType.None: { pt1 = new PointF(x1, y1); } break; case ArrowType.Rectangle: { var pts = new PointF[] { new PointF(x1 - (float)sizeX1, y1) }; t1.TransformPoints(pts); pt1 = pts[0]; var rect = new Rect2(x1 - sizeX1, y1 - radiusY1, sizeX1, sizeY1); var gs = _gfx.Save(); _gfx.MultiplyTransform(t1); DrawRectangleInternal(_gfx, fillStartArrow, strokeStartArrow, sas.IsStroked, sas.IsFilled, ref rect); _gfx.Restore(gs); } break; case ArrowType.Ellipse: { var pts = new PointF[] { new PointF(x1 - (float)sizeX1, y1) }; t1.TransformPoints(pts); pt1 = pts[0]; var gs = _gfx.Save(); _gfx.MultiplyTransform(t1); var rect = new Rect2(x1 - sizeX1, y1 - radiusY1, sizeX1, sizeY1); DrawEllipseInternal(_gfx, fillStartArrow, strokeStartArrow, sas.IsStroked, sas.IsFilled, ref rect); _gfx.Restore(gs); } break; case ArrowType.Arrow: { var pts = new PointF[] { new PointF(x1, y1), new PointF(x1 - (float)sizeX1, y1 + (float)sizeY1), new PointF(x1, y1), new PointF(x1 - (float)sizeX1, y1 - (float)sizeY1), new PointF(x1, y1) }; t1.TransformPoints(pts); pt1 = pts[0]; var p11 = pts[1]; var p21 = pts[2]; var p12 = pts[3]; var p22 = pts[4]; DrawLineInternal(_gfx, strokeStartArrow, sas.IsStroked, ref p11, ref p21); DrawLineInternal(_gfx, strokeStartArrow, sas.IsStroked, ref p12, ref p22); } break; } double radiusX2 = eas.RadiusX; double radiusY2 = eas.RadiusY; double sizeX2 = 2.0 * radiusX2; double sizeY2 = 2.0 * radiusY2; switch (eas.ArrowType) { default: case ArrowType.None: { pt2 = new PointF(x2, y2); } break; case ArrowType.Rectangle: { var pts = new PointF[] { new PointF(x2 - (float)sizeX2, y2) }; t2.TransformPoints(pts); pt2 = pts[0]; var rect = new Rect2(x2 - sizeX2, y2 - radiusY2, sizeX2, sizeY2); var gs = _gfx.Save(); _gfx.MultiplyTransform(t2); DrawRectangleInternal(_gfx, fillEndArrow, strokeEndArrow, eas.IsStroked, eas.IsFilled, ref rect); _gfx.Restore(gs); } break; case ArrowType.Ellipse: { var pts = new PointF[] { new PointF(x2 - (float)sizeX2, y2) }; t2.TransformPoints(pts); pt2 = pts[0]; var gs = _gfx.Save(); _gfx.MultiplyTransform(t2); var rect = new Rect2(x2 - sizeX2, y2 - radiusY2, sizeX2, sizeY2); DrawEllipseInternal(_gfx, fillEndArrow, strokeEndArrow, eas.IsStroked, eas.IsFilled, ref rect); _gfx.Restore(gs); } break; case ArrowType.Arrow: { var pts = new PointF[] { new PointF(x2, y2), new PointF(x2 - (float)sizeX2, y2 + (float)sizeY2), new PointF(x2, y2), new PointF(x2 - (float)sizeX2, y2 - (float)sizeY2), new PointF(x2, y2) }; t2.TransformPoints(pts); pt2 = pts[0]; var p11 = pts[1]; var p21 = pts[2]; var p12 = pts[3]; var p22 = pts[4]; DrawLineInternal(_gfx, strokeEndArrow, eas.IsStroked, ref p11, ref p21); DrawLineInternal(_gfx, strokeEndArrow, eas.IsStroked, ref p12, ref p22); } break; } _gfx.DrawLine(strokeLine, pt1, pt2); fillLine.Dispose(); strokeLine.Dispose(); fillStartArrow.Dispose(); strokeStartArrow.Dispose(); fillEndArrow.Dispose(); strokeEndArrow.Dispose(); }
/// <summary> /// /// </summary> /// <param name="rect"></param> /// <returns></returns> public bool IntersectsWith(Rect2 rect) { return (rect.Left <= Right) && (rect.Right >= Left) && (rect.Top <= Bottom) && (rect.Bottom >= Top); }
/// <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; }