op/clip: add bounds expansion after move

After moving the pen, the next action should update the bounds for the start position.

References: https://todo.sr.ht/~eliasnaur/gio/451
Signed-off-by: Walter Werner SCHNEIDER <contact@schnwalter.eu>
This commit is contained in:
Walter Werner SCHNEIDER
2025-07-15 18:40:31 +03:00
committed by Elias Naur
parent ba82ae46d0
commit 0c145b3815
2 changed files with 112 additions and 3 deletions
+6 -3
View File
@@ -214,8 +214,9 @@ func (p *Path) LineTo(to f32.Point) {
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Line(p.pen, to))
p.pen = to
p.expand(p.pen)
p.expand(to)
p.pen = to
}
func (p *Path) cmd(data []byte, c scene.Command) {
@@ -263,9 +264,10 @@ func (p *Path) QuadTo(ctrl, to f32.Point) {
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Quad(p.pen, ctrl, to))
p.pen = to
p.expand(p.pen)
p.expand(ctrl)
p.expand(to)
p.pen = to
}
// ArcTo adds an elliptical arc to the path. The implied ellipse is defined
@@ -307,10 +309,11 @@ func (p *Path) CubeTo(ctrl0, ctrl1, to f32.Point) {
bo := binary.LittleEndian
bo.PutUint32(data[0:], uint32(p.contour))
p.cmd(data[4:], scene.Cubic(p.pen, ctrl0, ctrl1, to))
p.pen = to
p.expand(p.pen)
p.expand(ctrl0)
p.expand(ctrl1)
p.expand(to)
p.pen = to
}
// Close closes the path contour.
+106
View File
@@ -0,0 +1,106 @@
// SPDX-License-Identifier: Unlicense OR MIT
package clip
import (
"gioui.org/f32"
"gioui.org/op"
"math"
"testing"
)
func TestPath_MoveTo_LineTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
endPoint := f32.Pt(64, 64)
p.MoveTo(startPoint)
p.LineTo(endPoint)
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_QuadTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
midPoint := f32.Pt(60, 60)
p.MoveTo(startPoint)
p.QuadTo(midPoint.Sub(f32.Pt(-4, 0)), midPoint.Sub(f32.Pt(0, -4)))
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_ArcTo(t *testing.T) {
// We need a tolerance here because of rounding errors.
tolerance := f32.Pt(1, 1)
var ops op.Ops
p := Path{}
p.Begin(&ops)
arcStartPoint := f32.Pt(48, 32)
arcCenterPoint := f32.Pt(48, 48)
p.MoveTo(arcStartPoint)
p.ArcTo(arcCenterPoint, arcCenterPoint, math.Pi*2)
pathSpec := p.End()
minPoint := f32.Pt(32, 32).Sub(tolerance).Round()
maxPoint := f32.Pt(64, 64).Add(tolerance).Round()
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min.X < minPoint.X || pathSpec.bounds.Min.Y < minPoint.Y {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max.X > maxPoint.X || pathSpec.bounds.Max.Y > maxPoint.Y {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}
func TestPath_MoveTo_CubeTo(t *testing.T) {
var ops op.Ops
p := Path{}
p.Begin(&ops)
startPoint := f32.Pt(32, 32)
midPoint := f32.Pt(48, 48)
endPoint := f32.Pt(64, 64)
p.MoveTo(startPoint)
p.CubeTo(midPoint.Sub(f32.Pt(-4, 0)), midPoint.Sub(f32.Pt(0, -4)), endPoint)
pathSpec := p.End()
minPoint := f32.Pt(32, 32)
maxPoint := f32.Pt(64, 64)
if pathSpec.bounds.Min == pathSpec.bounds.Max {
t.Errorf("zero path")
}
if pathSpec.bounds.Min != minPoint.Round() {
t.Errorf("pathSpec.bounds.Min = %v, want %v", pathSpec.bounds.Min, minPoint)
}
if pathSpec.bounds.Max != maxPoint.Round() {
t.Errorf("pathSpec.bounds.Max = %v, want %v", pathSpec.bounds.Max, maxPoint)
}
}