diff --git a/gpu/internal/rendertest/clip_test.go b/gpu/internal/rendertest/clip_test.go index 4275a7cf..3a147312 100644 --- a/gpu/internal/rendertest/clip_test.go +++ b/gpu/internal/rendertest/clip_test.go @@ -207,6 +207,32 @@ func TestStrokedPathCoincidentControlPoint(t *testing.T) { }) } +func TestStrokedPathBalloon(t *testing.T) { + run(t, func(o *op.Ops) { + // This shape is based on the one drawn by the Bubble function in + // github.com/llgcode/draw2d/samples/geometry/geometry.go. + p := new(clip.Path) + p.Begin(o) + p.MoveTo(f32.Pt(42.69375, 10.5)) + p.CubeTo(f32.Pt(42.69375, 10.5), f32.Pt(14.85, 10.5), f32.Pt(14.85, 31.5)) + p.CubeTo(f32.Pt(14.85, 31.5), f32.Pt(14.85, 52.5), f32.Pt(28.771875, 52.5)) + p.CubeTo(f32.Pt(28.771875, 52.5), f32.Pt(28.771875, 63.7), f32.Pt(17.634375, 66.5)) + p.CubeTo(f32.Pt(17.634375, 66.5), f32.Pt(34.340626, 63.7), f32.Pt(37.125, 52.5)) + p.CubeTo(f32.Pt(37.125, 52.5), f32.Pt(70.5375, 52.5), f32.Pt(70.5375, 31.5)) + p.CubeTo(f32.Pt(70.5375, 31.5), f32.Pt(70.5375, 10.5), f32.Pt(42.69375, 10.5)) + cl := clip.Stroke{ + Path: p.End(), + Width: 2.83, + }.Op().Push(o) + paint.Fill(o, black) + cl.Pop() + }, func(r result) { + r.expect(0, 0, transparent) + r.expect(70, 52, colornames.Black) + r.expect(70, 90, transparent) + }) +} + func TestPathReuse(t *testing.T) { run(t, func(o *op.Ops) { var path clip.Path diff --git a/gpu/internal/rendertest/refs/TestStrokedPathBalloon.png b/gpu/internal/rendertest/refs/TestStrokedPathBalloon.png new file mode 100644 index 00000000..a9a57b28 Binary files /dev/null and b/gpu/internal/rendertest/refs/TestStrokedPathBalloon.png differ diff --git a/internal/stroke/stroke.go b/internal/stroke/stroke.go index 47db6cad..741e56a1 100644 --- a/internal/stroke/stroke.go +++ b/internal/stroke/stroke.go @@ -510,14 +510,18 @@ func strokePathRoundJoin(rhs, lhs *StrokeQuads, hw float32, pivot, n0, n1 f32.Po // Path bends to the right, ie. CW (or 180 degree turn). c := pivot.Sub(lhs.pen()) angle := -math.Acos(float64(cosPt(n0, n1))) - lhs.arc(c, c, float32(angle)) + if !math.IsNaN(angle) { + lhs.arc(c, c, float32(angle)) + } lhs.lineTo(lp) // Add a line to accommodate for rounding errors. rhs.lineTo(rp) default: // Path bends to the left, ie. CCW. angle := math.Acos(float64(cosPt(n0, n1))) c := pivot.Sub(rhs.pen()) - rhs.arc(c, c, float32(angle)) + if !math.IsNaN(angle) { + rhs.arc(c, c, float32(angle)) + } rhs.lineTo(rp) // Add a line to accommodate for rounding errors. lhs.lineTo(lp) }