public void EndGlyph() { GlyphRenderData renderData = default; // has the glyoh been rendedered already???? if (this.raterizationRequired) { IPath path = this.builder.Build(); if (this.renderFill) { renderData.FillMap = this.Render(path); } if (this.renderOutline) { if (this.Pen.StrokePattern.Length == 0) { path = path.GenerateOutline(this.Pen.StrokeWidth); } else { path = path.GenerateOutline(this.Pen.StrokeWidth, this.Pen.StrokePattern); } renderData.OutlineMap = this.Render(path); } this.glyphData[this.currentGlyphRenderParams] = renderData; } else { renderData = this.glyphData[this.currentGlyphRenderParams]; } if (this.renderFill) { this.FillOperations.Add(new DrawingOperation { Location = this.currentRenderPosition, Map = renderData.FillMap }); } if (this.renderOutline) { this.OutlineOperations.Add(new DrawingOperation { Location = this.currentRenderPosition, Map = renderData.OutlineMap }); } }
private void RenderShapeToCanvas(SvgGraphicsElement svgGraphicsElement, IPath path) { var matrix = CalulateUpdatedMatrix(svgGraphicsElement); var brush = svgGraphicsElement.CreateFillPaintServer()?.Accept(BrushGenerator <TPixel> .Instance); IBrush strokFill = null; IPath outline = null; if (svgGraphicsElement.StrokeWidth > 0) { strokFill = svgGraphicsElement.CreateStrokePaintServer()?.Accept(BrushGenerator <TPixel> .Instance); if (strokFill != null) { var pattern = svgGraphicsElement.Style.StrokeDashArray.Value?.Select(X => X.Value).ToArray(); var joint = svgGraphicsElement.Style.StrokeLineJoin.AsJointStyle(); var end = svgGraphicsElement.Style.StrokeLineCap.AsEndCapStyle(); if (pattern == null || pattern.Length == 0) { outline = path.GenerateOutline(svgGraphicsElement.StrokeWidth, joint, end); } else { outline = path.GenerateOutline(svgGraphicsElement.StrokeWidth, pattern, false, joint, end); } } } var shapeOptions = new ShapeOptions { IntersectionRule = IntersectionRule.Nonzero }; var shapeGraphicsOptions = new DrawingOptions() { ShapeOptions = shapeOptions }; if (brush != null) { image.Fill(shapeGraphicsOptions, brush, path.Transform(matrix)); } if (outline != null && strokFill != null) { image.Fill(shapeGraphicsOptions, strokFill, outline.Transform(matrix)); } }
public void ClippedTriangleGapInIntersections() { Polygon simplePath = new Polygon(new LinearLineSegment( new PointF(10, 10), new PointF(200, 150), new PointF(50, 300))); Polygon hole1 = new Polygon(new LinearLineSegment( new PointF(37, 85), new PointF(93, 85), new PointF(65, 137))); IPath clippedPath = simplePath.Clip(hole1); IPath outline = clippedPath.GenerateOutline(5, new[] { 1f }); PointF[] buffer = new PointF[20]; PointF start = new PointF(outline.Bounds.Left - 1, 102); PointF end = new PointF(outline.Bounds.Right + 1, 102); int matches = outline.FindIntersections(start, end, buffer, 0); int maxIndex = buffer.Select((x, i) => new { x, i }).Where(x => x.x.X > 0).Select(x => x.i).Last(); Assert.Equal(matches - 1, maxIndex); }
public void ClippedTriangle() { var simplePath = new Polygon(new LinearLineSegment( new PointF(10, 10), new PointF(200, 150), new PointF(50, 300))); var hole1 = new Polygon(new LinearLineSegment( new PointF(37, 85), new PointF(93, 85), new PointF(65, 137))); IPath clippedPath = simplePath.Clip(hole1); IPath outline = clippedPath.GenerateOutline(5, new[] { 1f }); Assert.False(outline.Contains(new PointF(74, 97))); }
/// <summary> /// Generates a outline of the path with alternating on and off segments based on the pattern. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="pattern">The pattern made of multiples of the width.</param> /// <param name="startOff">Weather the first item in the pattern is on or off.</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, float[] pattern, bool startOff) { if (pattern == null || pattern.Length < 2) { return(path.GenerateOutline(width)); } IEnumerable <ISimplePath> paths = path.Flatten(); ClipperOffset offset = new ClipperOffset(); List <IntPoint> buffer = new List <IntPoint>(3); foreach (ISimplePath p in paths) { bool online = !startOff; float targetLength = pattern[0] * width; int patternPos = 0; // Create a new list of points representing the new outline int pCount = p.Points.Count; if (!p.IsClosed) { pCount--; } int i = 0; Vector2 currentPoint = p.Points[0]; while (i < pCount) { int next = (i + 1) % p.Points.Count; Vector2 targetPoint = p.Points[next]; float distToNext = Vector2.Distance(currentPoint, targetPoint); if (distToNext > targetLength) { // find a point between the 2 float t = targetLength / distToNext; Vector2 point = (currentPoint * (1 - t)) + (targetPoint * t); buffer.Add(currentPoint.ToPoint()); buffer.Add(point.ToPoint()); // we now inset a line joining if (online) { offset.AddPath(buffer, JoinType.jtSquare, EndType.etOpenButt); } online = !online; buffer.Clear(); currentPoint = point; // next length patternPos = (patternPos + 1) % pattern.Length; targetLength = pattern[patternPos] * width; } else if (distToNext <= targetLength) { buffer.Add(currentPoint.ToPoint()); currentPoint = targetPoint; i++; targetLength -= distToNext; } } if (buffer.Count > 0) { if (p.IsClosed) { buffer.Add(p.Points.First().ToPoint()); } else { buffer.Add(p.Points.Last().ToPoint()); } if (online) { offset.AddPath(buffer, JoinType.jtSquare, EndType.etOpenButt); } online = !online; buffer.Clear(); patternPos = (patternPos + 1) % pattern.Length; targetLength = pattern[patternPos] * width; } } return(ExecuteOutliner(width, offset)); }
/// <summary> /// Generates a outline of the path with alternating on and off segments based on the pattern. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="pattern">The pattern made of multiples of the width.</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, float[] pattern) { return(path.GenerateOutline(width, pattern, false)); }
/// <summary> /// Initializes a new instance of the <see cref="ShapePath"/> class. /// </summary> /// <param name="shape">The shape.</param> /// <param name="pen">The pen to apply to the shape.</param> public ShapePath(IPath shape, IPen pen) : base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern)) { }
/// <summary> /// Generates a outline of the path with alternating on and off segments based on the pattern. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="pattern">The pattern made of multiples of the width.</param> /// <param name="startOff">Weather the first item in the pattern is on or off.</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, float[] pattern, bool startOff) => path.GenerateOutline(width, new ReadOnlySpan <float>(pattern), startOff);
/// <summary> /// Generates a outline of the path with alternating on and off segments based on the pattern. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="pattern">The pattern made of multiples of the width.</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan <float> pattern) { return(path.GenerateOutline(width, pattern, false)); }
/// <summary> /// Generates a outline of the path with alternating on and off segments based on the pattern. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="pattern">The pattern made of multiples of the width.</param> /// <param name="startOff">Weather the first item in the pattern is on or off.</param> /// <param name="jointStyle">The style to render the joints.</param> /// <param name="patternSectionCapStyle">The style to render between sections of the specified pattern.</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan <float> pattern, bool startOff, JointStyle jointStyle = JointStyle.Square, EndCapStyle patternSectionCapStyle = EndCapStyle.Butt) { if (pattern.Length < 2) { return(path.GenerateOutline(width, jointStyle: jointStyle)); } JoinType style = Convert(jointStyle); EndType patternSectionCap = Convert(patternSectionCapStyle); IEnumerable <ISimplePath> paths = path.Flatten(); var offset = new ClipperOffset() { MiterLimit = MiterOffsetDelta }; var buffer = new List <IntPoint>(3); foreach (ISimplePath p in paths) { bool online = !startOff; float targetLength = pattern[0] * width; int patternPos = 0; // Create a new list of points representing the new outline int pCount = p.Points.Count; if (!p.IsClosed) { pCount--; } int i = 0; Vector2 currentPoint = p.Points[0]; while (i < pCount) { int next = (i + 1) % p.Points.Count; Vector2 targetPoint = p.Points[next]; float distToNext = Vector2.Distance(currentPoint, targetPoint); if (distToNext > targetLength) { // find a point between the 2 float t = targetLength / distToNext; Vector2 point = (currentPoint * (1 - t)) + (targetPoint * t); buffer.Add(currentPoint.ToPoint()); buffer.Add(point.ToPoint()); // we now inset a line joining if (online) { offset.AddPath(buffer, style, patternSectionCap); } online = !online; buffer.Clear(); currentPoint = point; // next length patternPos = (patternPos + 1) % pattern.Length; targetLength = pattern[patternPos] * width; } else if (distToNext <= targetLength) { buffer.Add(currentPoint.ToPoint()); currentPoint = targetPoint; i++; targetLength -= distToNext; } } if (buffer.Count > 0) { if (p.IsClosed) { buffer.Add(p.Points.First().ToPoint()); } else { buffer.Add(p.Points.Last().ToPoint()); } if (online) { offset.AddPath(buffer, style, patternSectionCap); } online = !online; buffer.Clear(); patternPos = (patternPos + 1) % pattern.Length; targetLength = pattern[patternPos] * width; } } return(ExecuteOutliner(width, offset)); }
/// <summary> /// Initializes a new instance of the <see cref="ShapePath"/> class. /// </summary> /// <param name="shape">The shape.</param> /// <param name="pen">The pen to apply to the shape.</param> // SixLabors.shape willbe moving to a Span/ReadOnlySpan based API shortly use ToArray for now. public ShapePath(IPath shape, Pens.IPen pen) : base(shape.GenerateOutline(pen.StrokeWidth, pen.StrokePattern.ToArray())) { }
/// <summary> /// Generates an outline of the path with alternating on and off segments based on the pattern. /// </summary> /// <param name="path">The path to outline</param> /// <param name="width">The outline width.</param> /// <param name="pattern">The pattern made of multiples of the width.</param> /// <param name="startOff">Whether the first item in the pattern is on or off.</param> /// <param name="jointStyle">The style to apply to the joints.</param> /// <param name="endCapStyle">The style to apply to the end caps.</param> /// <returns>A new <see cref="IPath"/> representing the outline.</returns> /// <exception cref="ClipperException">Thrown when an offset cannot be calculated.</exception> public static IPath GenerateOutline(this IPath path, float width, ReadOnlySpan <float> pattern, bool startOff, JointStyle jointStyle, EndCapStyle endCapStyle) { if (pattern.Length < 2) { return(path.GenerateOutline(width, jointStyle, endCapStyle)); } IEnumerable <ISimplePath> paths = path.Flatten(); ClipperOffset offset = new(MiterOffsetDelta); List <PointF> buffer = new(); foreach (ISimplePath p in paths) { bool online = !startOff; float targetLength = pattern[0] * width; int patternPos = 0; ReadOnlySpan <PointF> points = p.Points.Span; // Create a new list of points representing the new outline int pCount = points.Length; if (!p.IsClosed) { pCount--; } int i = 0; Vector2 currentPoint = points[0]; while (i < pCount) { int next = (i + 1) % points.Length; Vector2 targetPoint = points[next]; float distToNext = Vector2.Distance(currentPoint, targetPoint); if (distToNext > targetLength) { // find a point between the 2 float t = targetLength / distToNext; Vector2 point = (currentPoint * (1 - t)) + (targetPoint * t); buffer.Add(currentPoint); buffer.Add(point); // we now inset a line joining if (online) { offset.AddPath(new ReadOnlySpan <PointF>(buffer.ToArray()), jointStyle, endCapStyle); } online = !online; buffer.Clear(); currentPoint = point; // next length patternPos = (patternPos + 1) % pattern.Length; targetLength = pattern[patternPos] * width; } else if (distToNext <= targetLength) { buffer.Add(currentPoint); currentPoint = targetPoint; i++; targetLength -= distToNext; } } if (buffer.Count > 0) { if (p.IsClosed) { buffer.Add(points[0]); } else { buffer.Add(points[points.Length - 1]); } if (online) { offset.AddPath(new ReadOnlySpan <PointF>(buffer.ToArray()), jointStyle, endCapStyle); } online = !online; buffer.Clear(); patternPos = (patternPos + 1) % pattern.Length; targetLength = pattern[patternPos] * width; } } return(offset.Execute(width)); }