/// <summary> /// Generates a new feature from the buffer of this feature. The DataRow of /// the new feature will be null. /// </summary> /// <param name="self">This feature</param> /// <param name="distance">The double distance</param> /// <param name="endCapStyle">The end cap style to use</param> /// <returns>An IFeature representing the output from the buffer operation</returns> public static IFeature Buffer(this IFeature self, double distance, EndCapStyle endCapStyle) { IGeometry g = self.Geometry.Buffer(distance, new BufferParameters { EndCapStyle = endCapStyle }); return(new Feature(g)); }
/// <summary> /// Creates a set of parameters with the /// given parameter values. /// </summary> /// <param name="quadrantSegments"> the number of quadrant segments to use</param> /// <param name="endCapStyle"> the end cap style to use</param> /// <param name="joinStyle"> the join style to use</param> /// <param name="mitreLimit"> the mitre limit to use</param> public BufferParameters(int quadrantSegments, EndCapStyle endCapStyle, JoinStyle joinStyle, double mitreLimit) : this(quadrantSegments, endCapStyle) { JoinStyle = joinStyle; MitreLimit = mitreLimit; }
private void ParseArgs(Object[] args) { _argCount = args.Length; _distance = Double.Parse((String)args[0]); if (_argCount >= 2) { _quadSegments = Int32.Parse((String)args[1]); } if (_argCount >= 3) { _endCapStyle = (EndCapStyle)Int32.Parse((String)args[2]); } }
private static EndType Convert(EndCapStyle style) { switch (style) { case EndCapStyle.Round: return(EndType.etOpenRound); case EndCapStyle.Square: return(EndType.etOpenSquare); case EndCapStyle.Butt: default: return(EndType.etOpenButt); } }
private void ParseArgs(object[] args) { _argCount = args.Length; _distance = double.Parse((string)args[0]); if (_argCount >= 2) { _quadSegments = int.Parse((string)args[1]); } if (_argCount >= 3) { _endCapStyle = (EndCapStyle)int.Parse((string)args[2]); } }
/// <summary> /// Generates a buffer, but also adds the newly created feature to the specified output featureset. /// This will also compare the field names of the input featureset with the destination featureset. /// If a column name exists in both places, it will copy those values to the destination featureset. /// </summary> /// <param name="self">The feature to calcualate the buffer for.</param> /// <param name="distance">The double distance to use for calculating the buffer</param> /// <param name="endCapStyle">The end cap style to use</param> /// <param name="destinationFeatureset">The output featureset to add this feature to and use /// as a reference for determining which data columns to copy.</param> /// <returns>The IFeature that represents the buffer feature.</returns> public static IFeature Buffer(this IFeature self, double distance, EndCapStyle endCapStyle, IFeatureSet destinationFeatureset) { IFeature f = Buffer(self, distance, endCapStyle); destinationFeatureset.Features.Add(f); foreach (DataColumn dc in destinationFeatureset.DataTable.Columns) { if (self.DataRow[dc.ColumnName] != null) { f.DataRow[dc.ColumnName] = self.DataRow[dc.ColumnName]; } } return(f); }
public void Dump(XmlWriter writer, SwfTagCode shapeType) { writer.WriteStartElement("line-style"); writer.WriteAttributeString("width", Width.ToString()); if (shapeType == SwfTagCode.DefineMorphShape) { writer.WriteAttributeString("end-width", EndWidth.ToString()); writer.WriteAttributeString("color", Color.ToHtmlHex()); writer.WriteAttributeString("end-color", EndColor.ToHtmlHex()); } else if (shapeType == SwfTagCode.DefineShape4 || shapeType == SwfTagCode.DefineMorphShape2) { bool isMorph = shapeType == SwfTagCode.DefineMorphShape2; if (isMorph) { writer.WriteAttributeString("end-width", EndWidth.ToString()); } writer.WriteAttributeString("start-cap", StartCapStyle.ToString()); writer.WriteAttributeString("end-cap", EndCapStyle.ToString()); writer.WriteAttributeString("flags", Flags.ToString()); if (JoinStyle == SwfJoinStyle.Miter) { writer.WriteAttributeString("miter-limit", MiterLimit.ToString()); } if (Fill != null) { Fill.Dump(writer, shapeType); } else { writer.WriteAttributeString("color", Color.ToHtmlHex()); if (isMorph) { writer.WriteAttributeString("end-color", EndColor.ToHtmlHex()); } } } else { writer.WriteAttributeString("color", Color.ToHtmlHex(shapeType == SwfTagCode.DefineShape3)); } writer.WriteEndElement(); }
/// <summary> /// Creates a set of parameters with the /// given quadrantSegments and endCapStyle values. /// </summary> /// <param name="quadrantSegments"> the number of quadrant segments to use</param> /// <param name="endCapStyle"> the end cap style to use</param> public BufferParameters(int quadrantSegments, EndCapStyle endCapStyle) : this(quadrantSegments) { EndCapStyle = endCapStyle; }
/// <summary> /// Computes a buffer region around this <c>Geometry</c> having the given width. /// The buffer of a Geometry is the Minkowski sum or difference of the geometry /// with a disc of radius <c>Abs(distance)</c>. /// </summary> /// <remarks> /// <para>The end cap style specifies the buffer geometry that will be /// created at the ends of linestrings. The styles provided are: /// <ul> /// <li><see cref="EndCapStyle.Round" /> - (default) a semi-circle</li> /// <li><see cref="EndCapStyle.Flat" /> - a straight line perpendicular to the end segment</li> /// <li><see cref="EndCapStyle.Square" /> - a half-square</li> /// </ul></para> /// <para>The buffer operation always returns a polygonal result. The negative or /// zero-distance buffer of lines and points is always an empty <see cref="IPolygonal"/>.</para> /// </remarks> /// <param name="distance"> /// The width of the buffer, interpreted according to the /// <c>PrecisionModel</c> of the <c>Geometry</c>. /// </param> /// <param name="endCapStyle">Cap Style to use for compute buffer.</param> /// <returns> /// a polygonal geometry representing the buffer region (which may be empty) /// </returns> /// <exception cref="TopologyException">If a robustness error occurs</exception> /// <seealso cref="Buffer(double)"/> /// <seealso cref="Buffer(double, IBufferParameters)"/> /// <seealso cref="Buffer(double, int)"/> /// <seealso cref="Buffer(double, int, EndCapStyle)"/> public IGeometry Buffer(double distance, EndCapStyle endCapStyle) { return BufferOp.Buffer(this, distance, BufferParameters.DefaultQuadrantSegments, (BufferStyle)endCapStyle); }
/// <summary> /// Generates a new feature from the buffer of this feature. The DataRow of /// the new feature will be null. /// </summary> /// <param name="self">This feature</param> /// <param name="distance">The double distance</param> /// <param name="quadrantSegments">The number of segments to use to approximate a quadrant of a circle</param> /// <param name="endCapStyle">The end cap style to use</param> /// <returns>An IFeature representing the output from the buffer operation</returns> public static IFeature Buffer(this IFeature self, double distance, int quadrantSegments, EndCapStyle endCapStyle) { IGeometry g = self.Geometry.Buffer(distance, quadrantSegments, endCapStyle); return(new Feature(g)); }
/// <summary> /// Computes a buffer region around this <c>Geometry</c> having the given /// width and with a specified number of segments used to approximate curves. /// The buffer of a Geometry is the Minkowski sum of the Geometry with /// a disc of radius <c>distance</c>. Curves in the buffer polygon are /// approximated with line segments. This method allows specifying the /// accuracy of that approximation. /// </summary> /// <remarks><para>Mathematically-exact buffer area boundaries can contain circular arcs. /// To represent these arcs using linear geometry they must be approximated with line segments. /// The <c>quadrantSegments</c> argument allows controlling the accuracy of /// the approximation by specifying the number of line segments used to /// represent a quadrant of a circle</para> /// <para>The end cap style specifies the buffer geometry that will be /// created at the ends of linestrings. The styles provided are: /// <ul> /// <li><see cref="EndCapStyle.Round" /> - (default) a semi-circle</li> /// <li><see cref="EndCapStyle.Flat" /> - a straight line perpendicular to the end segment</li> /// <li><see cref="EndCapStyle.Square" /> - a half-square</li> /// </ul></para> /// <para>The buffer operation always returns a polygonal result. The negative or /// zero-distance buffer of lines and points is always an empty <see cref="IPolygonal"/>. /// This is also the result for the buffers of degenerate (zero-area) polygons. /// </para> /// </remarks> /// <param name="distance"> /// The width of the buffer, interpreted according to the /// <c>PrecisionModel</c> of the <c>Geometry</c>. /// </param> /// <param name="quadrantSegments">The number of segments to use to approximate a quadrant of a circle.</param> /// <param name="endCapStyle">Cap Style to use for compute buffer.</param> /// <returns> /// a polygonal geometry representing the buffer region (which may be empty) /// </returns> /// <exception cref="TopologyException">If a robustness error occurs</exception> /// <seealso cref="Buffer(double)"/> /// <seealso cref="Buffer(double, EndCapStyle)"/> /// <seealso cref="Buffer(double, IBufferParameters)"/> /// <seealso cref="Buffer(double, int)"/> public IGeometry Buffer(double distance, int quadrantSegments, EndCapStyle endCapStyle) { return BufferOp.Buffer(this, distance, quadrantSegments, (BufferStyle)endCapStyle); }
private void ParseArgs(Object[] args) { _argCount = args.Length; _distance = Double.Parse((String)args[0]); if (_argCount >= 2) _quadSegments = Int32.Parse((String)args[1]); if (_argCount >= 3) _endCapStyle = (EndCapStyle)Int32.Parse((String)args[2]); }
/// <summary> /// Generates an outline of the path. /// </summary> /// <param name="path">The path to outline</param> /// <param name="width">The outline width.</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, JointStyle jointStyle, EndCapStyle endCapStyle) { ClipperOffset offset = new(MiterOffsetDelta); offset.AddPath(path, jointStyle, endCapStyle); return(offset.Execute(width)); }
private static void OutputStarOutlineDashed(int points, float inner = 10, float outer = 20, float width = 5, JointStyle jointStyle = JointStyle.Miter, EndCapStyle cap = EndCapStyle.Butt) { // center the shape outerRadii + 10 px away from edges float offset = outer + 10; Star star = new Star(offset, offset, points, inner, outer); var outline = Outliner.GenerateOutline(star, width, new float[] { 3, 3 }, false, jointStyle, cap); outline.SaveImage("Stars", $"StarOutlineDashed_{points}_{jointStyle}_{cap}.png"); }
/// <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> /// Generates a solid outline of the path. /// </summary> /// <param name="path">the path to outline</param> /// <param name="width">The final width outline</param> /// <param name="jointStyle">The style to render the joints.</param> /// <param name="endCapStyle">The style to render the end caps of open paths (ignored on closed paths).</param> /// <returns>A new path representing the outline.</returns> public static IPath GenerateOutline(this IPath path, float width, JointStyle jointStyle = JointStyle.Square, EndCapStyle endCapStyle = EndCapStyle.Square) { var offset = new ClipperOffset() { MiterLimit = MiterOffsetDelta }; JoinType style = Convert(jointStyle); EndType openEndCapStyle = Convert(endCapStyle); // Pattern can be applied to the path by cutting it into segments IEnumerable <ISimplePath> paths = path.Flatten(); foreach (ISimplePath p in paths) { IReadOnlyList <PointF> vectors = p.Points; var points = new List <IntPoint>(vectors.Count); foreach (Vector2 v in vectors) { points.Add(new IntPoint(v.X * ScalingFactor, v.Y * ScalingFactor)); } EndType type = p.IsClosed ? EndType.etClosedLine : openEndCapStyle; offset.AddPath(points, style, type); } return(ExecuteOutliner(width, offset)); }
/// <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)); }
/// <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="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, JointStyle jointStyle, EndCapStyle endCapStyle) => GenerateOutline(path, width, pattern, false, jointStyle, endCapStyle);