fixed a bug with self intersecting polygons, added a test

This commit is contained in:
Thomas Friedel 2019-01-11 11:01:12 +01:00
parent 908e88d374
commit 3800d8519c
3 changed files with 49 additions and 13 deletions

View file

@ -456,3 +456,16 @@ func TestConvex(t *testing.T) {
cv.Fill() cv.Fill()
}) })
} }
func TestConvexSelfIntersecting(t *testing.T) {
run(t, func(cv *canvas.Canvas) {
cv.SetFillStyle("#F00")
cv.BeginPath()
cv.MoveTo(33.7, 31.5)
cv.LineTo(35.4, 55.0)
cv.LineTo(53.0, 33.5)
cv.LineTo(56.4, 62.4)
cv.ClosePath()
cv.Fill()
})
}

View file

@ -26,6 +26,7 @@ const (
pathIsRect pathIsRect
pathIsConvex pathIsConvex
pathIsClockwise pathIsClockwise
pathSelfIntersects
) )
// BeginPath clears the current path and starts a new one // BeginPath clears the current path and starts a new one
@ -53,19 +54,20 @@ func (cv *Canvas) MoveTo(x, y float64) {
// LineTo adds a line to the end of the path // LineTo adds a line to the end of the path
func (cv *Canvas) LineTo(x, y float64) { func (cv *Canvas) LineTo(x, y float64) {
if len(cv.path.p) > 0 && isSamePoint(cv.path.p[len(cv.path.p)-1].tf, cv.tf(vec{x, y}), 0.1) { count := len(cv.path.p)
if count > 0 && isSamePoint(cv.path.p[len(cv.path.p)-1].tf, cv.tf(vec{x, y}), 0.1) {
return return
} }
if len(cv.path.p) == 0 { if count == 0 {
cv.path.p = append(cv.path.p, pathPoint{pos: vec{x, y}, tf: cv.tf(vec{x, y}), flags: pathMove}) cv.path.p = append(cv.path.p, pathPoint{pos: vec{x, y}, tf: cv.tf(vec{x, y}), flags: pathMove})
return return
} }
prev := &cv.path.p[len(cv.path.p)-1] prev := &cv.path.p[count-1]
tf := cv.tf(vec{x, y}) tf := cv.tf(vec{x, y})
prev.next = tf prev.next = tf
prev.flags |= pathAttach prev.flags |= pathAttach
cv.path.p = append(cv.path.p, pathPoint{pos: vec{x, y}, tf: tf}) cv.path.p = append(cv.path.p, pathPoint{pos: vec{x, y}, tf: tf})
newp := &cv.path.p[len(cv.path.p)-1] newp := &cv.path.p[count]
px, py := prev.pos[0], prev.pos[1] px, py := prev.pos[0], prev.pos[1]
cv.path.cwSum += (x - px) * (y + py) cv.path.cwSum += (x - px) * (y + py)
@ -75,9 +77,26 @@ func (cv *Canvas) LineTo(x, y float64) {
newp.flags |= pathIsClockwise newp.flags |= pathIsClockwise
} }
if prev.flags&pathSelfIntersects > 0 {
newp.flags |= pathSelfIntersects
}
if len(cv.path.p) < 4 { if len(cv.path.p) < 4 {
newp.flags |= pathIsConvex newp.flags |= pathIsConvex
} else if prev.flags&pathIsConvex > 0 { } else if prev.flags&pathIsConvex > 0 {
cuts := false
b0, b1 := prev.pos, vec{x, y}
for i := 1; i < count; i++ {
a0, a1 := cv.path.p[i-1].pos, cv.path.p[i].pos
_, r1, r2 := lineIntersection(a0, a1, b0, b1)
if r1 > 0 && r1 < 1 && r2 > 0 && r2 < 1 {
cuts = true
break
}
}
if cuts {
newp.flags |= pathSelfIntersects
} else {
prev2 := &cv.path.p[len(cv.path.p)-3] prev2 := &cv.path.p[len(cv.path.p)-3]
cw := (newp.flags & pathIsClockwise) > 0 cw := (newp.flags & pathIsClockwise) > 0
@ -90,6 +109,7 @@ func (cv *Canvas) LineTo(x, y float64) {
} }
} }
} }
}
// Arc adds a circle segment to the end of the path. x/y is the center, radius // Arc adds a circle segment to the end of the path. x/y is the center, radius
// is the radius, startAngle and endAngle are angles in radians, anticlockwise // is the radius, startAngle and endAngle are angles in radians, anticlockwise
@ -623,7 +643,8 @@ func (cv *Canvas) Fill() {
} }
func (cv *Canvas) appendSubPathTriangles(tris []float32, path []pathPoint) []float32 { func (cv *Canvas) appendSubPathTriangles(tris []float32, path []pathPoint) []float32 {
if path[len(path)-1].flags&pathIsConvex != 0 { last := path[len(path)-1]
if last.flags&pathIsConvex != 0 {
p0, p1 := path[0].tf, path[1].tf p0, p1 := path[0].tf, path[1].tf
last := len(path) last := len(path)
for i := 2; i < last; i++ { for i := 2; i < last; i++ {
@ -631,9 +652,11 @@ func (cv *Canvas) appendSubPathTriangles(tris []float32, path []pathPoint) []flo
tris = append(tris, float32(p0[0]), float32(p0[1]), float32(p1[0]), float32(p1[1]), float32(p2[0]), float32(p2[1])) tris = append(tris, float32(p0[0]), float32(p0[1]), float32(p1[0]), float32(p1[1]), float32(p2[0]), float32(p2[1]))
p1 = p2 p1 = p2
} }
} else { } else if last.flags&pathSelfIntersects != 0 {
path = cv.cutIntersections(path) path = cv.cutIntersections(path)
tris = triangulatePath(path, tris) tris = triangulatePath(path, tris)
} else {
tris = triangulatePath(path, tris)
} }
return tris return tris
} }

BIN
testdata/ConvexSelfIntersecting.png vendored Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B