self intersecting polygon bugfix
This commit is contained in:
parent
421d388f91
commit
d670f964c8
2 changed files with 76 additions and 91 deletions
34
path2d.go
34
path2d.go
|
@ -41,7 +41,7 @@ func (p *Path2D) MoveTo(x, y float64) {
|
||||||
if len(p.p) > 0 && isSamePoint(p.p[len(p.p)-1].pos, vec{x, y}, 0.1) {
|
if len(p.p) > 0 && isSamePoint(p.p[len(p.p)-1].pos, vec{x, y}, 0.1) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
p.p = append(p.p, pathPoint{pos: vec{x, y}, flags: pathMove}) // todo more flags probably
|
p.p = append(p.p, pathPoint{pos: vec{x, y}, flags: pathMove | pathIsConvex})
|
||||||
p.cwSum = 0
|
p.cwSum = 0
|
||||||
p.move = vec{x, y}
|
p.move = vec{x, y}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,7 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
||||||
p.p = append(p.p, pathPoint{pos: vec{x, y}})
|
p.p = append(p.p, pathPoint{pos: vec{x, y}})
|
||||||
newp := &p.p[count]
|
newp := &p.p[count]
|
||||||
|
|
||||||
|
if prev.flags&pathIsConvex > 0 {
|
||||||
px, py := prev.pos[0], prev.pos[1]
|
px, py := prev.pos[0], prev.pos[1]
|
||||||
p.cwSum += (x - px) * (y + py)
|
p.cwSum += (x - px) * (y + py)
|
||||||
cwTotal := p.cwSum
|
cwTotal := p.cwSum
|
||||||
|
@ -73,17 +74,28 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
||||||
if cwTotal <= 0 {
|
if cwTotal <= 0 {
|
||||||
newp.flags |= pathIsClockwise
|
newp.flags |= pathIsClockwise
|
||||||
}
|
}
|
||||||
|
|
||||||
if prev.flags&pathSelfIntersects > 0 {
|
|
||||||
newp.flags |= pathSelfIntersects
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(p.p) < 4 || Performance.AssumeConvex {
|
if len(p.p) < 4 || Performance.AssumeConvex {
|
||||||
newp.flags |= pathIsConvex
|
newp.flags |= pathIsConvex
|
||||||
} else if prev.flags&pathIsConvex > 0 {
|
} else if prev.flags&pathIsConvex > 0 {
|
||||||
|
prev2 := &p.p[count-2]
|
||||||
|
cw := (prev.flags & pathIsClockwise) > 0
|
||||||
|
|
||||||
|
ln := prev.pos.sub(prev2.pos)
|
||||||
|
lo := vec{ln[1], -ln[0]}
|
||||||
|
dot := newp.pos.sub(prev2.pos).dot(lo)
|
||||||
|
|
||||||
|
if (cw && dot <= 0) || (!cw && dot >= 0) {
|
||||||
|
newp.flags |= pathIsConvex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if prev.flags&pathSelfIntersects > 0 {
|
||||||
|
newp.flags |= pathSelfIntersects
|
||||||
|
} else if newp.flags&pathIsConvex == 0 && newp.flags&pathSelfIntersects == 0 && checkSelfIntersection && !Performance.IgnoreSelfIntersections {
|
||||||
cuts := false
|
cuts := false
|
||||||
var cutPoint vec
|
var cutPoint vec
|
||||||
if checkSelfIntersection && !Performance.IgnoreSelfIntersections {
|
|
||||||
b0, b1 := prev.pos, vec{x, y}
|
b0, b1 := prev.pos, vec{x, y}
|
||||||
for i := 1; i < count; i++ {
|
for i := 1; i < count; i++ {
|
||||||
a0, a1 := p.p[i-1].pos, p.p[i].pos
|
a0, a1 := p.p[i-1].pos, p.p[i].pos
|
||||||
|
@ -94,20 +106,8 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if cuts && !isSamePoint(cutPoint, vec{x, y}, samePointTolerance) {
|
if cuts && !isSamePoint(cutPoint, vec{x, y}, samePointTolerance) {
|
||||||
newp.flags |= pathSelfIntersects
|
newp.flags |= pathSelfIntersects
|
||||||
} else {
|
|
||||||
prev2 := &p.p[len(p.p)-3]
|
|
||||||
cw := (newp.flags & pathIsClockwise) > 0
|
|
||||||
|
|
||||||
ln := prev.pos.sub(prev2.pos)
|
|
||||||
lo := vec{ln[1], -ln[0]}
|
|
||||||
dot := newp.pos.sub(prev2.pos).dot(lo)
|
|
||||||
|
|
||||||
if (cw && dot <= 0) || (!cw && dot >= 0) {
|
|
||||||
newp.flags |= pathIsConvex
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,24 +25,6 @@ func pointIsRightOfLine(a, b, p vec) (bool, bool) {
|
||||||
return p[0] > x, dir
|
return p[0] > x, dir
|
||||||
}
|
}
|
||||||
|
|
||||||
func pointIsBelowLine(a, b, p vec) (bool, bool) {
|
|
||||||
if a[0] == b[0] {
|
|
||||||
return false, false
|
|
||||||
}
|
|
||||||
dir := false
|
|
||||||
if a[0] > b[0] {
|
|
||||||
a, b = b, a
|
|
||||||
dir = !dir
|
|
||||||
}
|
|
||||||
if p[0] < a[0] || p[0] > b[0] {
|
|
||||||
return false, false
|
|
||||||
}
|
|
||||||
v := b.sub(a)
|
|
||||||
r := (p[0] - a[0]) / v[0]
|
|
||||||
x := a[1] + r*v[1]
|
|
||||||
return p[1] > x, dir
|
|
||||||
}
|
|
||||||
|
|
||||||
func triangleContainsPoint(a, b, c, p vec) bool {
|
func triangleContainsPoint(a, b, c, p vec) bool {
|
||||||
// if point is outside triangle bounds, return false
|
// if point is outside triangle bounds, return false
|
||||||
if p[0] < a[0] && p[0] < b[0] && p[0] < c[0] {
|
if p[0] < a[0] && p[0] < b[0] && p[0] < c[0] {
|
||||||
|
@ -316,61 +298,66 @@ func setPathLeftRightInside(net *tessNet) {
|
||||||
a1, b1 := net.verts[e1.a], net.verts[e1.b]
|
a1, b1 := net.verts[e1.a], net.verts[e1.b]
|
||||||
diff := b1.pos.sub(a1.pos)
|
diff := b1.pos.sub(a1.pos)
|
||||||
mid := a1.pos.add(diff.mulf(0.5))
|
mid := a1.pos.add(diff.mulf(0.5))
|
||||||
num := 0
|
|
||||||
|
|
||||||
if math.Abs(diff[1]) < math.Abs(diff[0]) {
|
|
||||||
edir := diff[1] > 0
|
|
||||||
|
|
||||||
|
left, right := 0, 0
|
||||||
|
if math.Abs(diff[1]) > math.Abs(diff[0]) {
|
||||||
for j, e2 := range net.edges {
|
for j, e2 := range net.edges {
|
||||||
if i == j {
|
if i == j {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
a2, b2 := net.verts[e2.a], net.verts[e2.b]
|
a2, b2 := net.verts[e2.a].pos, net.verts[e2.b].pos
|
||||||
r, dir := pointIsRightOfLine(a2.pos, b2.pos, mid)
|
if a2[1] == b2[1] {
|
||||||
if !r {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if dir {
|
if a2[1] > b2[1] {
|
||||||
num++
|
a2, b2 = b2, a2
|
||||||
} else {
|
}
|
||||||
num--
|
if mid[1] < a2[1] || mid[1] > b2[1] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v := b2.sub(a2)
|
||||||
|
r := (mid[1] - a2[1]) / v[1]
|
||||||
|
x := a2[0] + r*v[0]
|
||||||
|
if mid[0] > x {
|
||||||
|
left++
|
||||||
|
} else if mid[0] < x {
|
||||||
|
right++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if diff[1] > 0 {
|
||||||
if edir {
|
left, right = right, left
|
||||||
net.edges[i].leftInside = (num - 1) != 0
|
|
||||||
net.edges[i].rightInside = num != 0
|
|
||||||
} else {
|
|
||||||
net.edges[i].leftInside = num != 0
|
|
||||||
net.edges[i].rightInside = (num + 1) != 0
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
edir := diff[0] > 0
|
|
||||||
|
|
||||||
for j, e2 := range net.edges {
|
for j, e2 := range net.edges {
|
||||||
if i == j {
|
if i == j {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
a2, b2 := net.verts[e2.a], net.verts[e2.b]
|
a2, b2 := net.verts[e2.a].pos, net.verts[e2.b].pos
|
||||||
b, dir := pointIsBelowLine(a2.pos, b2.pos, mid)
|
if a2[0] == b2[0] {
|
||||||
if !b {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if dir {
|
if a2[0] > b2[0] {
|
||||||
num++
|
a2, b2 = b2, a2
|
||||||
} else {
|
}
|
||||||
num--
|
if mid[0] < a2[0] || mid[0] > b2[0] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
v := b2.sub(a2)
|
||||||
|
r := (mid[0] - a2[0]) / v[0]
|
||||||
|
y := a2[1] + r*v[1]
|
||||||
|
if mid[1] > y {
|
||||||
|
left++
|
||||||
|
} else if mid[1] < y {
|
||||||
|
right++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if diff[0] < 0 {
|
||||||
|
left, right = right, left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if edir {
|
net.edges[i].leftInside = left%2 == 1
|
||||||
net.edges[i].leftInside = num != 0
|
net.edges[i].rightInside = right%2 == 1
|
||||||
net.edges[i].rightInside = (num - 1) != 0
|
|
||||||
} else {
|
|
||||||
net.edges[i].leftInside = (num + 1) != 0
|
|
||||||
net.edges[i].rightInside = num != 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -409,8 +396,6 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool)
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
// fmt.Println("start", start, from, cur, net.verts[cur], left)
|
|
||||||
|
|
||||||
sp2 = append(sp2, pathPoint{
|
sp2 = append(sp2, pathPoint{
|
||||||
pos: net.verts[cur].pos,
|
pos: net.verts[cur].pos,
|
||||||
flags: pathMove,
|
flags: pathMove,
|
||||||
|
@ -458,13 +443,11 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool)
|
||||||
next = e.a
|
next = e.a
|
||||||
}
|
}
|
||||||
any = true
|
any = true
|
||||||
// fmt.Println("-", e, nextEdge, next)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !any {
|
if !any {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
// fmt.Println(start, from, cur, net.verts[cur], nextEdge, next, net.verts[next])
|
|
||||||
if left {
|
if left {
|
||||||
net.edges[nextEdge].leftInside = false
|
net.edges[nextEdge].leftInside = false
|
||||||
} else {
|
} else {
|
||||||
|
@ -480,10 +463,12 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(sp2) >= 3 {
|
||||||
stop := partFn(sp2)
|
stop := partFn(sp2)
|
||||||
if stop {
|
if stop {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
}
|
||||||
sp2 = sp2[:0]
|
sp2 = sp2[:0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue