Skip to content

Commit d7b33df

Browse files
Copilotphilstopford
andcommitted
Fix makeContour adaptive radius for medium-length edges
Co-authored-by: philstopford <1983851+philstopford@users.noreply.github.com>
1 parent 8f1b62b commit d7b33df

File tree

2 files changed

+125
-8
lines changed

2 files changed

+125
-8
lines changed

Common/shapeEngine/contourGen.cs

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public static PathD makeContour(PathD original_path, double concaveRadius, doubl
8888
radius = concaveRadius;
8989

9090
PathD current_corner = ProcessCorner(startLine, endLine, radius, angularResolution, edgeResolution,
91-
SamplingMode.ByMaxSegmentLength);
91+
shortEdgeLength, SamplingMode.ByMaxSegmentLength);
9292
processed.Add(current_corner);
9393
}
9494

@@ -164,7 +164,7 @@ static int[] CategorizeCorners(PathD path_, double short_edge_length)
164164
}
165165

166166
static PathD ProcessCorner(PathD startLine, PathD endLine, double radius, double angular_resolution,
167-
double edge_resolution, SamplingMode mode = SamplingMode.ByMaxAngle)
167+
double edge_resolution, double shortEdgeThreshold, SamplingMode mode = SamplingMode.ByMaxAngle)
168168
{
169169
PointD startLineStart = startLine[0];
170170
PointD startLineEnd = startLine[1];
@@ -176,14 +176,19 @@ static PathD ProcessCorner(PathD startLine, PathD endLine, double radius, double
176176
PointD endLength = Helper.Minus(endLineEnd, endLineStart);
177177
PointD endDir = Helper.Normalized(endLength);
178178

179-
double start_radius = radius;
180-
double half_edge_length = Math.Abs(Math.Sqrt(startLength.x * startLength.x + startLength.y * startLength.y));
181-
if (start_radius > half_edge_length) start_radius = half_edge_length;
179+
// Compute actual edge lengths (distance from corner to edge midpoint)
180+
double startEdgeLength = Math.Abs(Math.Sqrt(startLength.x * startLength.x + startLength.y * startLength.y));
181+
double endEdgeLength = Math.Abs(Math.Sqrt(endLength.x * endLength.x + endLength.y * endLength.y));
182+
183+
// Apply adaptive radius limiting based on edge classification
184+
double adaptiveRadius = ComputeAdaptiveRadius(startEdgeLength, endEdgeLength, radius, shortEdgeThreshold);
185+
186+
double start_radius = adaptiveRadius;
187+
if (start_radius > startEdgeLength) start_radius = startEdgeLength;
182188
PointD curveStartPoint = Helper.Add(startLineStart, Helper.Mult(startDir, start_radius));
183189

184-
double end_radius = radius;
185-
half_edge_length = Math.Abs(Math.Sqrt(endLength.x * endLength.x + endLength.y * endLength.y));
186-
if (end_radius > half_edge_length) end_radius = half_edge_length;
190+
double end_radius = adaptiveRadius;
191+
if (end_radius > endEdgeLength) end_radius = endEdgeLength;
187192
PointD curveEndPoint = Helper.Add(endLineStart, Helper.Mult(endDir, end_radius));
188193

189194
double dx = curveEndPoint.x - curveStartPoint.x;
@@ -217,6 +222,33 @@ static PathD ProcessCorner(PathD startLine, PathD endLine, double radius, double
217222
return samples;
218223
}
219224

225+
/// <summary>
226+
/// Computes an adaptive radius that respects edge length constraints.
227+
/// For short edges, applies more restrictive limiting. For normal edges,
228+
/// allows larger radii while still preventing geometric issues.
229+
/// </summary>
230+
static double ComputeAdaptiveRadius(double startEdgeLength, double endEdgeLength,
231+
double desiredRadius, double shortEdgeThreshold)
232+
{
233+
// Check if either edge qualifies as "short"
234+
bool hasShortEdge = (startEdgeLength <= shortEdgeThreshold) || (endEdgeLength <= shortEdgeThreshold);
235+
236+
if (hasShortEdge)
237+
{
238+
// For short edges, limit radius to a fraction of the shortest edge
239+
double minEdgeLength = Math.Min(startEdgeLength, endEdgeLength);
240+
double maxAllowableRadius = minEdgeLength * 0.4; // Use 40% of edge length
241+
return Math.Min(desiredRadius, maxAllowableRadius);
242+
}
243+
244+
// For normal edges, use desired radius with standard half-edge limiting
245+
double halfStartEdge = startEdgeLength * 0.5;
246+
double halfEndEdge = endEdgeLength * 0.5;
247+
double maxStandardRadius = Math.Min(halfStartEdge, halfEndEdge);
248+
249+
return Math.Min(desiredRadius, maxStandardRadius);
250+
}
251+
220252
static PathD AssembleWithEasing(
221253
PathsD processedCorners,
222254
int[] corner_types,

UnitTests/PrototypingTests.cs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -751,5 +751,90 @@ public void TestPointNormalization()
751751
}
752752

753753
#endregion
754+
755+
#region Contour Generation Tests
756+
757+
[Test]
758+
public void TestMakeContour_10UnitEdge_VerifyAdaptiveRadiusFix()
759+
{
760+
// Arrange: Create the exact polygon from the problem statement
761+
var original_path = new Clipper2Lib.PathD()
762+
{
763+
new Clipper2Lib.PointD(0, 0),
764+
new Clipper2Lib.PointD(0, 60),
765+
new Clipper2Lib.PointD(20, 60),
766+
new Clipper2Lib.PointD(20, 10),
767+
new Clipper2Lib.PointD(60, 10),
768+
new Clipper2Lib.PointD(60, 0),
769+
new Clipper2Lib.PointD(0, 0) // Close the path
770+
};
771+
772+
double concaveRadius = 30;
773+
double convexRadius = 30;
774+
double edgeResolution = 1;
775+
double angularResolution = 1;
776+
double shortEdgeLength = 0.01;
777+
double maxShortEdgeLength = 0.01;
778+
779+
// Act
780+
var result = shapeEngine.contourGen.makeContour(original_path, concaveRadius, convexRadius,
781+
edgeResolution, angularResolution, shortEdgeLength, maxShortEdgeLength);
782+
783+
// Assert: The result should have smooth curves applied
784+
Assert.That(result.Count, Is.GreaterThan(7), "Result should have more points than input due to curve sampling");
785+
786+
// The fix ensures that normal 10-unit edges get appropriate treatment
787+
// The edge from (60,10) to (60,0) is 10 units, much longer than shortEdgeLength=0.01
788+
// With the adaptive radius fix, this should receive proper Bezier curve treatment
789+
790+
// Verify the result has a reasonable number of points (not too few, not too many)
791+
Console.WriteLine($"Result points: {result.Count}");
792+
Assert.That(result.Count, Is.InRange(30, 150),
793+
"Result should have a reasonable number of curve points indicating proper radius application");
794+
}
795+
796+
[Test]
797+
public void TestMakeContour_18UnitEdge_ShouldWorkCorrectly()
798+
{
799+
// Arrange: Create a polygon where the problematic edge is 18 units (reported to work)
800+
var original_path = new Clipper2Lib.PathD()
801+
{
802+
new Clipper2Lib.PointD(0, 0),
803+
new Clipper2Lib.PointD(0, 60),
804+
new Clipper2Lib.PointD(20, 60),
805+
new Clipper2Lib.PointD(20, 18), // Changed from 10 to 18
806+
new Clipper2Lib.PointD(60, 18), // Changed from 10 to 18
807+
new Clipper2Lib.PointD(60, 0),
808+
new Clipper2Lib.PointD(0, 0)
809+
};
810+
811+
double concaveRadius = 30;
812+
double convexRadius = 30;
813+
double edgeResolution = 1;
814+
double angularResolution = 1;
815+
double shortEdgeLength = 0.01;
816+
double maxShortEdgeLength = 0.01;
817+
818+
// Act
819+
var result = shapeEngine.contourGen.makeContour(original_path, concaveRadius, convexRadius,
820+
edgeResolution, angularResolution, shortEdgeLength, maxShortEdgeLength);
821+
822+
// Assert: This should work correctly according to the problem statement
823+
Assert.That(result.Count, Is.GreaterThan(7), "Result should have curved points");
824+
825+
// The 18-unit edge should get proper rounding
826+
var pointsNearCorner = new List<Clipper2Lib.PointD>();
827+
foreach (var point in result)
828+
{
829+
if (Math.Abs(point.x - 60) < 35 && Math.Abs(point.y - 0) < 35)
830+
{
831+
pointsNearCorner.Add(point);
832+
}
833+
}
834+
835+
Assert.That(pointsNearCorner.Count, Is.GreaterThan(0), "Should have points near the corner");
836+
}
837+
838+
#endregion
754839
}
755840
}

0 commit comments

Comments
 (0)