~eliasnaur/gio-patches

gio: widget/material: use clip.Path.Arc to draw loader v1 PROPOSED

Sebastien Binet: 1
 widget/material: use clip.Path.Arc to draw loader

 1 files changed, 16 insertions(+), 57 deletions(-)
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
On Thursday, August 27, 2020 3:26 PM, Elias Naur <mail@eliasnaur.com> wrote:
Next
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/12135/mbox | git am -3
Learn more about email & git
View this thread in the archives

[PATCH gio] widget/material: use clip.Path.Arc to draw loader Export this patch

Signed-off-by: Sebastien Binet <s@sbinet.org>
---
 widget/material/loader.go | 73 +++++++++------------------------------
 1 file changed, 16 insertions(+), 57 deletions(-)

diff --git a/widget/material/loader.go b/widget/material/loader.go
index bec82b7..75ba0f1 100644
--- a/widget/material/loader.go
+++ b/widget/material/loader.go
@@ -60,73 +60,32 @@ func (l LoaderStyle) Layout(gtx layout.Context) layout.Dimensions {
func clipLoader(ops *op.Ops, startAngle, endAngle, radius float64) {
	const thickness = .25

	outer := float32(radius)
	inner := float32(radius) * (1. - thickness)
	var (
		outer = float32(radius)
		delta = float32(endAngle - startAngle)

	var p clip.Path
	p.Begin(ops)

	vy, vx := math.Sincos(startAngle)

	start := f32.Pt(float32(vx), float32(vy))

	// Use quadratic beziér curves to approximate a circle arc and
	// minimize the error by capping the length of each curve segment.

	nsegments := math.Round(20 * math.Pi / (endAngle - startAngle))
		vy, vx = math.Sincos(startAngle)

	θ := (endAngle - startAngle) / nsegments
		pen    = f32.Pt(float32(vx), float32(vy)).Mul(outer)
		center = f32.Pt(0, 0).Sub(pen)

	// To avoid a math.Sincos for every segment, compute a clockwise
	// rotation matrix once and apply for each segment.
	//
	// [ cos θ -sin θ]
	// [sin θ cos θ]
	sinθ64, cosθ64 := math.Sincos(θ)
	sinθ, cosθ := float32(sinθ64), float32(cosθ64)
	rotate := func(clockwise float32, p f32.Point) f32.Point {
		return f32.Point{
			X: p.X*cosθ - p.Y*clockwise*sinθ,
			Y: p.X*clockwise*sinθ + p.Y*cosθ,
		}
	}

	// Compute 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 θ
	//
	b := (cosθ - 1.) / sinθ

	control := func(clockwise float32, S f32.Point) f32.Point {
		tangent := f32.Pt(-S.Y, S.X)
		return S.Add(tangent.Mul(b * -clockwise))
	}
		p clip.Path
	)

	pen := start.Mul(outer)
	p.Begin(ops)
	p.Move(pen)

	end := start
	arc := func(clockwise float32, radius float32) {
		for i := 0; i < int(nsegments); i++ {
			ctrl := control(clockwise, end)
			end = rotate(clockwise, end)
			p.Quad(ctrl.Mul(radius).Sub(pen), end.Mul(radius).Sub(pen))
			pen = end.Mul(radius)
		}
	}
	// Outer arc, clockwise.
	arc(+1, outer)
	// Outer arc.
	p.Arc(center, center, delta)

	// Arc cap.
	cap := end.Mul(inner)
	pen = p.Pos()
	cap := pen.Mul(1 - thickness)
	p.Line(cap.Sub(pen))
	pen = cap

	// Inner arc, counter-clockwise.
	arc(-1, inner)
	// Inner arc.
	center = f32.Pt(0, 0).Sub(p.Pos())
	p.Arc(center, center, -delta)

	// Second arc cap automatically completed by End.
	p.End().Add(ops)
-- 
2.28.0
Thanks. Nice simplification.

Elias