diff --git a/path2d.go b/path2d.go index 0c3efab..e256c3e 100644 --- a/path2d.go +++ b/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) { 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.move = vec{x, y} } @@ -66,48 +66,48 @@ func (p *Path2D) lineTo(x, y float64, checkSelfIntersection bool) { p.p = append(p.p, pathPoint{pos: vec{x, y}}) newp := &p.p[count] - px, py := prev.pos[0], prev.pos[1] - p.cwSum += (x - px) * (y + py) - cwTotal := p.cwSum - cwTotal += (p.move[0] - x) * (p.move[1] + y) - if cwTotal <= 0 { - newp.flags |= pathIsClockwise - } - - if prev.flags&pathSelfIntersects > 0 { - newp.flags |= pathSelfIntersects + if prev.flags&pathIsConvex > 0 { + px, py := prev.pos[0], prev.pos[1] + p.cwSum += (x - px) * (y + py) + cwTotal := p.cwSum + cwTotal += (p.move[0] - x) * (p.move[1] + y) + if cwTotal <= 0 { + newp.flags |= pathIsClockwise + } } if len(p.p) < 4 || Performance.AssumeConvex { newp.flags |= pathIsConvex } 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 var cutPoint vec - if checkSelfIntersection && !Performance.IgnoreSelfIntersections { - b0, b1 := prev.pos, vec{x, y} - for i := 1; i < count; i++ { - a0, a1 := p.p[i-1].pos, p.p[i].pos - var r1, r2 float64 - cutPoint, r1, r2 = lineIntersection(a0, a1, b0, b1) - if r1 > 0 && r1 < 1 && r2 > 0 && r2 < 1 { - cuts = true - break - } + b0, b1 := prev.pos, vec{x, y} + for i := 1; i < count; i++ { + a0, a1 := p.p[i-1].pos, p.p[i].pos + var r1, r2 float64 + cutPoint, r1, r2 = lineIntersection(a0, a1, b0, b1) + if r1 > 0 && r1 < 1 && r2 > 0 && r2 < 1 { + cuts = true + break } } if cuts && !isSamePoint(cutPoint, vec{x, y}, samePointTolerance) { 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 - } } } } diff --git a/triangulation.go b/triangulation.go index ba20a9c..40e8ae3 100644 --- a/triangulation.go +++ b/triangulation.go @@ -25,24 +25,6 @@ func pointIsRightOfLine(a, b, p vec) (bool, bool) { 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 { // if point is outside triangle bounds, return false 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] diff := b1.pos.sub(a1.pos) 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 { if i == j { continue } - a2, b2 := net.verts[e2.a], net.verts[e2.b] - r, dir := pointIsRightOfLine(a2.pos, b2.pos, mid) - if !r { + a2, b2 := net.verts[e2.a].pos, net.verts[e2.b].pos + if a2[1] == b2[1] { continue } - if dir { - num++ - } else { - num-- + if a2[1] > b2[1] { + a2, b2 = b2, a2 + } + 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 edir { - 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 + if diff[1] > 0 { + left, right = right, left } } else { - edir := diff[0] > 0 - for j, e2 := range net.edges { if i == j { continue } - a2, b2 := net.verts[e2.a], net.verts[e2.b] - b, dir := pointIsBelowLine(a2.pos, b2.pos, mid) - if !b { + a2, b2 := net.verts[e2.a].pos, net.verts[e2.b].pos + if a2[0] == b2[0] { continue } - if dir { - num++ - } else { - num-- + if a2[0] > b2[0] { + a2, b2 = b2, a2 + } + 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 edir { - net.edges[i].leftInside = num != 0 - net.edges[i].rightInside = (num - 1) != 0 - } else { - net.edges[i].leftInside = (num + 1) != 0 - net.edges[i].rightInside = num != 0 + if diff[0] < 0 { + left, right = right, left } } + + net.edges[i].leftInside = left%2 == 1 + net.edges[i].rightInside = right%2 == 1 } } @@ -409,8 +396,6 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool) break } - // fmt.Println("start", start, from, cur, net.verts[cur], left) - sp2 = append(sp2, pathPoint{ pos: net.verts[cur].pos, flags: pathMove, @@ -458,13 +443,11 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool) next = e.a } any = true - // fmt.Println("-", e, nextEdge, next) } } if !any { break } - // fmt.Println(start, from, cur, net.verts[cur], nextEdge, next, net.verts[next]) if left { net.edges[nextEdge].leftInside = false } else { @@ -480,9 +463,11 @@ func selfIntersectingPathParts(p []pathPoint, partFn func(sp []pathPoint) bool) } } - stop := partFn(sp2) - if stop { - return true + if len(sp2) >= 3 { + stop := partFn(sp2) + if stop { + return true + } } sp2 = sp2[:0] }