~eliasnaur/gio-patches

gio: op/clip: smoother implementation of Arc for large angles v1 PROPOSED

Sebastien Binet: 1
 op/clip: smoother implementation of Arc for large angles

 3 files changed, 17 insertions(+), 37 deletions(-)
#290108 openbsd.yml success
#290109 freebsd.yml success
#290110 apple.yml failed
#290129 linux.yml success
Export patchset (mbox)
How do I use this?

Copy & paste the following snippet into your terminal to import this patchset into git:

curl -s https://lists.sr.ht/~eliasnaur/gio-patches/patches/12198/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH gio] op/clip: smoother implementation of Arc for large angles Export this patch

Signed-off-by: Sebastien Binet <s@sbinet.org>
---
 internal/rendertest/clip_test.go          |   1 +
 internal/rendertest/refs/TestPaintArc.png | Bin 1888 -> 2221 bytes
 op/clip/clip.go                           |  53 +++++++---------------
 3 files changed, 17 insertions(+), 37 deletions(-)

diff --git a/internal/rendertest/clip_test.go b/internal/rendertest/clip_test.go
index 9debf7f..b996d81 100644
--- a/internal/rendertest/clip_test.go
+++ b/internal/rendertest/clip_test.go
@@ -61,6 +61,7 @@ func TestPaintArc(t *testing.T) {
		p.Line(f32.Pt(0, 25))
		p.Arc(f32.Pt(-10, 5), f32.Pt(10, 15), -math.Pi)
		p.Line(f32.Pt(0, 25))
		p.Arc(f32.Pt(10, 10), f32.Pt(10, 10), 2*math.Pi)
		p.Line(f32.Pt(-10, 0))
		p.Arc(f32.Pt(-10, 0), f32.Pt(-40, 0), -math.Pi)
		p.Line(f32.Pt(-10, 0))
diff --git a/internal/rendertest/refs/TestPaintArc.png b/internal/rendertest/refs/TestPaintArc.png
index d900cc32eabbfa73473c95f256bcab07d25d5aa3..8ffb300e67dfc4fc2c52db1d8aac5971684a94d2 100644
GIT binary patch
delta 2210
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

literal 1888
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

diff --git a/op/clip/clip.go b/op/clip/clip.go
index 7547852..955a6e0 100644
--- a/op/clip/clip.go
+++ b/op/clip/clip.go
@@ -194,16 +194,9 @@ func arcFrom(f1, f2, p f32.Point) (c f32.Point, rx, ry, start, alpha float64) {
// An electronic version may be found at:
//  http://spaceroots.org/documents/ellipse/elliptical-arc.pdf
func (p *Path) arc(alpha float64, c f32.Point, rx, ry, beg, delta float64) {
	const n = 16
	var (
		n = math.Round(20 * math.Pi / math.Abs(delta))
		θ = delta / n

		sinθ64, cosθ64 = math.Sincos(θ)
		sinθ, cosθ     = float32(sinθ64), float32(cosθ64)
		b              = (cosθ - 1) / sinθ
	)

	var (
		θ   = delta / n
		ref f32.Affine2D // transform from absolute frame to ellipse-based one
		rot f32.Affine2D // rotation matrix for each segment
		inv f32.Affine2D // transform from ellipse-based frame to absolute one
@@ -215,43 +208,29 @@ func (p *Path) arc(alpha float64, c f32.Point, rx, ry, beg, delta float64) {
		Y: float32(1 / ry),
	})
	inv = ref.Invert()
	rot = rot.Rotate(f32.Point{}, float32(θ))
	rot = rot.Rotate(f32.Point{}, float32(0.5*θ))

	// Instead of invoking math.Sincos for every segment, compute a rotation
	// matrix once and apply for each segment.
	// Before applying the rotation matrix rot, transform the coordinates
	// to a frame centered to the ellipse (and warped into a unit circle), then rotate.
	// Finally, transform back into the original frame.
	//
	// Also compute the control point C according to
	// https://pomax.github.io/bezierinfo/#circles.
	// If S is the starting point, S' is the orthogonal
	// tangent, θ is clockwise:
	//
	// C = S + b*S', b = (cos θ - 1)/sin θ
	//
	// We apply the same original <-> ellipse frame transformation to the
	// control point as well.
	rotate := func(p f32.Point) (end, ctl f32.Point) {
	step := func(p f32.Point) f32.Point {
		q := ref.Transform(p)
		t := f32.Pt(-q.Y, q.X)

		end = rot.Transform(q)
		ctl = q.Add(t.Mul(b))

		end = inv.Transform(end)
		ctl = inv.Transform(ctl)

		return end, ctl
		q = rot.Transform(q)
		q = inv.Transform(q)
		return q
	}

	var (
		ctl f32.Point
		end = p.pen
	)
	for i := 0; i < int(n); i++ {
		end, ctl = rotate(p.pen)
		p.quadTo(ctl, end)
	for i := 0; i < n; i++ {
		p0 := p.pen
		p1 := step(p0)
		p2 := step(p1)
		ctl := f32.Pt(
			2*p1.X-0.5*(p0.X+p2.X),
			2*p1.Y-0.5*(p0.Y+p2.Y),
		)
		p.quadTo(ctl, p2)
	}
}

-- 
2.28.0
builds.sr.ht
gio/patches: FAILED in 5m58s

[op/clip: smoother implementation of Arc for large angles][0] from [Sebastien Binet][1]

[0]: https://lists.sr.ht/~eliasnaur/gio-patches/patches/12198
[1]: mailto:s@sbinet.org

✗ #290110 FAILED  gio/patches/apple.yml   https://builds.sr.ht/~eliasnaur/job/290110
✗ #290111 FAILED  gio/patches/linux.yml   https://builds.sr.ht/~eliasnaur/job/290111
✓ #290108 SUCCESS gio/patches/openbsd.yml https://builds.sr.ht/~eliasnaur/job/290108
✓ #290109 SUCCESS gio/patches/freebsd.yml https://builds.sr.ht/~eliasnaur/job/290109
Thank you, merged.

Elias