diff --git a/widget/material/loader.go b/widget/material/loader.go index bec82b77..75ba0f16 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) + + vy, vx = math.Sincos(startAngle) + + pen = f32.Pt(float32(vx), float32(vy)).Mul(outer) + center = f32.Pt(0, 0).Sub(pen) + + p clip.Path + ) - 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)) - - θ := (endAngle - startAngle) / nsegments - - // 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)) - } - - pen := start.Mul(outer) 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)