diff --git a/op/clip/clip.go b/op/clip/clip.go index 14e348e2..57a65890 100644 --- a/op/clip/clip.go +++ b/op/clip/clip.go @@ -294,12 +294,15 @@ func (p *Path) arc(alpha float64, c f32.Point, rx, ry, beg, delta float64) { // Cube records a cubic Bézier from the pen through // two control points ending in to. func (p *Path) Cube(ctrl0, ctrl1, to f32.Point) { - if ctrl0 == (f32.Point{}) && ctrl1 == (f32.Point{}) && to == (f32.Point{}) { + p.CubeTo(p.pen.Add(ctrl0), p.pen.Add(ctrl1), p.pen.Add(to)) +} + +// CubeTo records a cubic Bézier from the pen through +// two control points ending in to, with absolute coordinates. +func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) { + if ctrl0 == p.pen && ctrl1 == p.pen && to == p.pen { return } - ctrl0 = ctrl0.Add(p.pen) - ctrl1 = ctrl1.Add(p.pen) - to = to.Add(p.pen) data := p.ops.Write(scene.CommandSize + 4) bo := binary.LittleEndian bo.PutUint32(data[0:], uint32(p.contour)) diff --git a/op/clip/shapes.go b/op/clip/shapes.go index 068ca526..f420814e 100644 --- a/op/clip/shapes.go +++ b/op/clip/shapes.go @@ -4,6 +4,7 @@ package clip import ( "image" + "math" "gioui.org/f32" "gioui.org/op" @@ -101,6 +102,55 @@ func (rr Border) Add(ops *op.Ops) { rr.Op(ops).Add(ops) } +// Circle represents the clip area of a circle. +type Circle struct { + Center f32.Point + Radius float32 +} + +// Op returns the op for the circle. +func (c Circle) Op(ops *op.Ops) Op { + return Outline{Path: c.path(ops)}.Op() +} + +// path returns the path spec for the circle. +func (c Circle) path(ops *op.Ops) PathSpec { + var p Path + p.Begin(ops) + + center := c.Center + r := c.Radius + + // https://pomax.github.io/bezierinfo/#circles_cubic. + const q = 4 * (math.Sqrt2 - 1) / 3 // 4*(sqrt(2)-1)/3 + + curve := r * q + top := f32.Point{X: center.X, Y: center.Y - r} + + p.MoveTo(top) + p.CubeTo( + f32.Point{X: center.X + curve, Y: center.Y - r}, + f32.Point{X: center.X + r, Y: center.Y - curve}, + f32.Point{X: center.X + r, Y: center.Y}, + ) + p.CubeTo( + f32.Point{X: center.X + r, Y: center.Y + curve}, + f32.Point{X: center.X + curve, Y: center.Y + r}, + f32.Point{X: center.X, Y: center.Y + r}, + ) + p.CubeTo( + f32.Point{X: center.X - curve, Y: center.Y + r}, + f32.Point{X: center.X - r, Y: center.Y + curve}, + f32.Point{X: center.X - r, Y: center.Y}, + ) + p.CubeTo( + f32.Point{X: center.X - r, Y: center.Y - curve}, + f32.Point{X: center.X - curve, Y: center.Y - r}, + top, + ) + return p.End() +} + // roundRect adds the outline of a rectangle with rounded corners to a // path. func roundRect(p *Path, size f32.Point, se, sw, nw, ne float32) {