/// <summary> /// Draws the specified <see cref="PolygonGrid"/> to the specified <see /// cref="StreamGeometry"/>, with optimization to remove duplicate lines.</summary> /// <param name="grid"> /// The <see cref="PolygonGrid"/> to draw.</param> /// <param name="context"> /// The <see cref="StreamGeometryContext"/> that receives <paramref name="grid"/>.</param> /// <param name="offset"> /// The offset by which to shift <paramref name="grid"/>.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="grid"/> or <paramref name="context"/> is a null reference.</exception> /// <remarks><para> /// <b>DrawOptimized</b> shifts the <see cref="RegularPolygon.Vertices"/> of each <see /// cref="PolygonGrid.Element"/> in the specified <paramref name="grid"/> by the /// corresponding <see cref="PolygonGrid.GridToDisplay"/> result plus the specified /// <paramref name="offset"/>. /// </para><para> /// <b>DrawOptimized</b> skips any <see cref="RegularPolygon"/> edges that coincide with an /// edge drawn for a previous <see cref="PolygonGrid.Element"/>. This optimization results /// in a total number of lines drawn to the specified <paramref name="context"/> that is /// about half that of the unoptimized <see cref="Draw"/> method. /// </para><para> /// There are two disadvantages. First, <b>DrawOptimized</b> requires more time and memory /// than <see cref="Draw"/> due to the additional vertex comparisons. Second, most polygons /// are not represented by closed figures within the specified <paramref name="context"/>, /// and so the area covered by the specified <paramref name="grid"/> is never considered /// filled for hit-testing, rendering, and clipping.</para></remarks> public static void DrawOptimized(this PolygonGrid grid, StreamGeometryContext context, PointD offset) { HashSet <LineI> lines = new HashSet <LineI>(); double epsilon = grid.Element.Length / 4.0; PointD[] vertices = grid.Element.Vertices; int width = grid.Size.Width, height = grid.Size.Height; for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { PointD element = grid.GridToDisplay(x, y) + offset; // get & remember first element vertex PointD start = vertices[0] + element; PointD elementStart = start; bool lineSkipped = true; for (int i = 1; i <= vertices.Length; i++) { // close figure by returning to first vertex PointD next = (i == vertices.Length ? elementStart : vertices[i] + element); // reduce line to unique hash coordinates LineI line = new LineI( (int)(start.X / epsilon), (int)(start.Y / epsilon), (int)(next.X / epsilon), (int)(next.Y / epsilon)); // skip lines that were drawn (in either direction) if (lines.Contains(line) || lines.Contains(new LineI(line.End, line.Start))) { lineSkipped = true; } else { // restart figure after skipping lines if (lineSkipped) { context.BeginFigure(start.ToWpfPoint(), false, false); lineSkipped = false; } // draw & remember current line context.LineTo(next.ToWpfPoint(), true, true); lines.Add(line); } // prepare for next vertex start = next; } } } }
/// <overloads> /// Draws the specified <see cref="RegularPolygon"/> or <see cref="PolygonGrid"/> to the /// specified <see cref="StreamGeometry"/>.</overloads> /// <summary> /// Draws the specified <see cref="RegularPolygon"/> to the specified <see /// cref="StreamGeometry"/>.</summary> /// <param name="polygon"> /// The <see cref="RegularPolygon"/> to draw.</param> /// <param name="context"> /// The <see cref="StreamGeometryContext"/> that receives <paramref name="polygon"/>. /// </param> /// <param name="offset"> /// The offset by which to shift <paramref name="polygon"/>.</param> /// <param name="isFilled"> /// <c>true</c> to use the area covered by <paramref name="polygon"/> for hit-testing, /// rendering, and clipping; otherwise, <c>false</c>.</param> /// <exception cref="ArgumentNullException"> /// <paramref name="polygon"/> or <paramref name="context"/> is a null reference. /// </exception> /// <remarks> /// <b>Draw</b> issues one <see cref="StreamGeometryContext.BeginFigure"/> call with the /// specified <paramref name="isFilled"/> parameter for the first <see /// cref="RegularPolygon.Vertices"/> element of the specified <paramref name="polygon"/>, /// and then one <see cref="StreamGeometryContext.LineTo"/> call for each remaining element. /// All coordinates are shifted by the specified <paramref name="offset"/>.</remarks> public static void Draw(this RegularPolygon polygon, StreamGeometryContext context, PointD offset, bool isFilled) { PointD[] vertices = polygon.Vertices; PointD start = vertices[0] + offset; context.BeginFigure(start.ToWpfPoint(), isFilled, true); for (int i = 1; i < vertices.Length; i++) { PointD next = vertices[i] + offset; context.LineTo(next.ToWpfPoint(), true, true); } }
/// <summary> /// Converts the specified <see cref="RegularPolygon"/> to a closed <see /// cref="PathFigure"/>, shifted by a specified offset.</summary> /// <param name="polygon"> /// The <see cref="RegularPolygon"/> to convert.</param> /// <param name="offset"> /// The offset by which to shift the <see cref="PathFigure"/>.</param> /// <returns> /// A closed and frozen <see cref="PathFigure"/> containing all <see /// cref="RegularPolygon.Vertices"/> of the specified <paramref name="polygon"/>, shifted by /// the specified <paramref name="offset"/>.</returns> /// <exception cref="ArgumentNullException"> /// <paramref name="polygon"/> is a null reference.</exception> public static PathFigure ToFigure(this RegularPolygon polygon, PointD offset) { PointD[] vertices = polygon.Vertices; PathFigure figure = new PathFigure(); figure.StartPoint = (vertices[0] + offset).ToWpfPoint(); for (int i = 1; i < vertices.Length; i++) { PointD next = vertices[i] + offset; figure.Segments.Add(new LineSegment(next.ToWpfPoint(), true)); } figure.IsClosed = true; figure.Freeze(); return(figure); }