Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ StrokeOptions options = new()
LineJoin = LineJoin.Round,
LineCap = LineCap.Round,
MiterLimit = 4,
InnerMiterLimit = 1.01,
ArcDetailScale = 1
};

Expand Down
30 changes: 0 additions & 30 deletions src/PolygonClipper/InnerJoin.cs

This file was deleted.

71 changes: 6 additions & 65 deletions src/PolygonClipper/PolygonStroker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ public sealed class PolygonStroker
private const double Pi = Math.PI;
private const double PiMul2 = Math.PI * 2D;

// The inner miter limit used to clamp joins on acute interior angles.
private const double InnerMiterLimit = 1.01D;

// Keep at most 2 warm instances per option-set (one active shape and one spare)
// to reduce churn without retaining many rarely reused configurations.
private const int MaxPooledStrokersPerOptions = 2;
Expand Down Expand Up @@ -79,10 +82,8 @@ public PolygonStroker(StrokeOptions options)
ArgumentNullException.ThrowIfNull(options);
this.NormalizeOutput = options.NormalizeOutput;
this.LineJoin = options.LineJoin;
this.InnerJoin = options.InnerJoin;
this.LineCap = options.LineCap;
this.MiterLimit = options.MiterLimit;
this.InnerMiterLimit = options.InnerMiterLimit;
this.ArcDetailScale = options.ArcDetailScale;
}

Expand Down Expand Up @@ -131,34 +132,26 @@ public StrokeOptionsKey(StrokeOptions options)
{
this.NormalizeOutput = options.NormalizeOutput;
this.LineJoin = options.LineJoin;
this.InnerJoin = options.InnerJoin;
this.LineCap = options.LineCap;
this.MiterLimit = options.MiterLimit;
this.InnerMiterLimit = options.InnerMiterLimit;
this.ArcDetailScale = options.ArcDetailScale;
}

public bool NormalizeOutput { get; }

public LineJoin LineJoin { get; }

public InnerJoin InnerJoin { get; }

public LineCap LineCap { get; }

public double MiterLimit { get; }

public double InnerMiterLimit { get; }

public double ArcDetailScale { get; }

public bool Equals(StrokeOptionsKey other)
=> this.NormalizeOutput == other.NormalizeOutput &&
this.LineJoin == other.LineJoin &&
this.InnerJoin == other.InnerJoin &&
this.LineCap == other.LineCap &&
this.MiterLimit == other.MiterLimit &&
this.InnerMiterLimit == other.InnerMiterLimit &&
this.ArcDetailScale == other.ArcDetailScale;

public override bool Equals(object? obj) => obj is StrokeOptionsKey other && this.Equals(other);
Expand All @@ -167,10 +160,8 @@ public override int GetHashCode()
=> HashCode.Combine(
this.NormalizeOutput,
this.LineJoin,
this.InnerJoin,
this.LineCap,
this.MiterLimit,
this.InnerMiterLimit,
this.ArcDetailScale);
}

Expand Down Expand Up @@ -266,11 +257,6 @@ public Polygon Stroke(Polygon polygon, double width)
/// </summary>
public double MiterLimit { get; }

/// <summary>
/// Gets the inner miter limit used to clamp joins on acute interior angles.
/// </summary>
public double InnerMiterLimit { get; }

/// <summary>
/// Gets the tessellation detail scale used for round joins and round caps.
/// Higher values produce more vertices and smoother curves.
Expand All @@ -287,11 +273,6 @@ public Polygon Stroke(Polygon polygon, double width)
/// </summary>
public LineCap LineCap { get; }

/// <summary>
/// Gets the join style used for sharp interior angles.
/// </summary>
public InnerJoin InnerJoin { get; }

/// <summary>
/// Gets a value indicating whether generated contours should be normalized by resolving
/// self-intersections and overlaps.
Expand Down Expand Up @@ -1123,52 +1104,12 @@ private void CalcJoin(ref StrokeVertexDistance v0, ref StrokeVertexDistance v1,
if (Math.Abs(cp) > double.Epsilon && (cp > 0D) == (strokeWidth > 0D))
{
double limit = Math.Min(len1, len2) / widthAbs;
if (limit < this.InnerMiterLimit)
if (limit < InnerMiterLimit)
{
limit = this.InnerMiterLimit;
limit = InnerMiterLimit;
}

switch (this.InnerJoin)
{
default:
// Bevel-like fallback for inner corners.
this.AddPoint(v1.X + dx1, v1.Y - dy1);
this.AddPoint(v1.X + dx2, v1.Y - dy2);
break;

case InnerJoin.Miter:
this.CalcMiter(ref v0, ref v1, ref v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0D);
break;

case InnerJoin.Jag:
case InnerJoin.Round:
// If offsets are close enough, miter produces cleaner inner-corner output.
Vertex offset1 = new(dx1, dy1);
Vertex offset2 = new(dx2, dy2);
double offsetDeltaSquared = Vertex.DistanceSquared(offset1, offset2);
if (offsetDeltaSquared < len1 * len1 && offsetDeltaSquared < len2 * len2)
{
this.CalcMiter(ref v0, ref v1, ref v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0D);
}
else if (this.InnerJoin == InnerJoin.Jag)
{
// Jagged inner join inserts center vertex to preserve cusp.
this.AddPoint(v1.X + dx1, v1.Y - dy1);
this.AddPoint(v1.X, v1.Y);
this.AddPoint(v1.X + dx2, v1.Y - dy2);
}
else
{
// Rounded inner join bridges via arc passing through the corner.
this.AddPoint(v1.X + dx1, v1.Y - dy1);
this.AddPoint(v1.X, v1.Y);
this.CalcArc(v1.X, v1.Y, dx2, -dy2, dx1, -dy1);
this.AddPoint(v1.X, v1.Y);
this.AddPoint(v1.X + dx2, v1.Y - dy2);
}

break;
}
this.CalcMiter(ref v0, ref v1, ref v2, dx1, dy1, dx2, dy2, LineJoin.MiterRevert, limit, 0D);
}
else
{
Expand Down
18 changes: 2 additions & 16 deletions src/PolygonClipper/StrokeOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ public sealed class StrokeOptions : IEquatable<StrokeOptions?>
/// </summary>
public double MiterLimit { get; set; } = 4D;

/// <summary>
/// Gets or sets the inner miter limit used to clamp joins on acute interior angles.
/// </summary>
public double InnerMiterLimit { get; set; } = 1.01D;

/// <summary>
/// Gets or sets the tessellation detail scale for round joins and round caps.
/// Higher values produce more vertices (smoother curves, more work).
Expand All @@ -45,11 +40,6 @@ public sealed class StrokeOptions : IEquatable<StrokeOptions?>
/// </summary>
public LineCap LineCap { get; set; } = LineCap.Butt;

/// <summary>
/// Gets or sets the join style used for sharp interior angles.
/// </summary>
public InnerJoin InnerJoin { get; set; } = InnerJoin.Miter;

/// <inheritdoc/>
public override bool Equals(object? obj) => this.Equals(obj as StrokeOptions);

Expand All @@ -58,20 +48,16 @@ public bool Equals(StrokeOptions? other)
=> other is not null &&
this.NormalizeOutput == other.NormalizeOutput &&
this.MiterLimit == other.MiterLimit &&
this.InnerMiterLimit == other.InnerMiterLimit &&
this.ArcDetailScale == other.ArcDetailScale &&
this.LineJoin == other.LineJoin &&
this.LineCap == other.LineCap &&
this.InnerJoin == other.InnerJoin;
this.LineCap == other.LineCap;

/// <inheritdoc/>
public override int GetHashCode()
=> HashCode.Combine(
this.NormalizeOutput,
this.MiterLimit,
this.InnerMiterLimit,
this.ArcDetailScale,
this.LineJoin,
this.LineCap,
this.InnerJoin);
this.LineCap);
}
5 changes: 2 additions & 3 deletions tests/PolygonClipper.Tests/PolygonStrokerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,7 @@ public void Stroke_OpenPolylineWithThreeVertices_IsNotForcedClosed()
StrokeOptions options = new()
{
LineCap = LineCap.Butt,
LineJoin = LineJoin.Miter,
InnerJoin = InnerJoin.Miter
LineJoin = LineJoin.Miter
};

Polygon open =
Expand Down Expand Up @@ -138,7 +137,7 @@ public void ProcessPolygonAndClip_FigureNinePath_ProducesValidStroke()
AssertStrokeCoversInputCenterline(input, actual, samplesPerSegment: 3);
}

[Theory (Skip = "For profiling only.")]
[Theory(Skip = "For profiling only.")]
[InlineData(101)]
[InlineData(301)]
[InlineData(1001)]
Expand Down
Loading