diff --git a/examples/vector/main.go b/examples/vector/main.go index 79e9c2eca..058a54335 100644 --- a/examples/vector/main.go +++ b/examples/vector/main.go @@ -98,6 +98,34 @@ func drawEbitenText(screen *ebiten.Image) { path.Fill(screen, color.White) } +func drawEbitenLogo(screen *ebiten.Image, x, y int) { + const unit = 16 + + var path vector.Path + xf, yf := float32(x), float32(y) + + path.MoveTo(xf, yf+4*unit) + path.LineTo(xf, yf+6*unit) + path.LineTo(xf+2*unit, yf+6*unit) + path.LineTo(xf+2*unit, yf+5*unit) + path.LineTo(xf+3*unit, yf+5*unit) + path.LineTo(xf+3*unit, yf+4*unit) + path.LineTo(xf+4*unit, yf+4*unit) + path.LineTo(xf+4*unit, yf+2*unit) + path.LineTo(xf+6*unit, yf+2*unit) + path.LineTo(xf+6*unit, yf+1*unit) + path.LineTo(xf+5*unit, yf+1*unit) + path.LineTo(xf+5*unit, yf) + path.LineTo(xf+4*unit, yf) + path.LineTo(xf+4*unit, yf+2*unit) + path.LineTo(xf+2*unit, yf+2*unit) + path.LineTo(xf+2*unit, yf+3*unit) + path.LineTo(xf+unit, yf+3*unit) + path.LineTo(xf+unit, yf+4*unit) + + path.Fill(screen, color.RGBA{0xdb, 0x56, 0x20, 0xff}) +} + var counter = 0 func update(screen *ebiten.Image) error { @@ -107,6 +135,7 @@ func update(screen *ebiten.Image) error { } drawEbitenText(screen) + drawEbitenLogo(screen, 20, 80) return nil } diff --git a/vector/internal/math/triangulate.go b/vector/internal/math/triangulate.go index 3cc6fa161..5b954ab7b 100644 --- a/vector/internal/math/triangulate.go +++ b/vector/internal/math/triangulate.go @@ -45,34 +45,9 @@ func Triangulate(pts []Point) []uint16 { var currentIndices []uint16 // Remove duplicated points -dup: for i := range pts { - for j := 0; j < i; j++ { - if pts[i] == pts[j] { - continue dup - } - } currentIndices = append(currentIndices, uint16(i)) } - if len(currentIndices) < 3 { - return nil - } - - // Determine the direction of the polygon from the upper-left point. - var upperLeft int - for ci, i := range currentIndices { - if pts[upperLeft].X < pts[i].X { - upperLeft = int(ci) - } - if pts[upperLeft].X == pts[i].X && pts[upperLeft].Y < pts[i].Y { - upperLeft = int(ci) - } - } - i0, i1, i2 := adjacentIndices(currentIndices, upperLeft) - pt0 := pts[i0] - pt1 := pts[i1] - pt2 := pts[i2] - clockwise := triangleCross(pt0, pt1, pt2) < 0 var indices []uint16 @@ -81,25 +56,43 @@ dup: for len(currentIndices) >= 3 { // Calculate cross-products and remove unneeded vertices. cs := make([]float32, len(currentIndices)) - idx := -1 + idxToRemove := -1 + + // Determine the direction of the polygon from the upper-left point. + var upperLeft int for i := range currentIndices { + next := (i + 1) % len(currentIndices) + if pts[currentIndices[i]] == pts[currentIndices[next]] { + idxToRemove = next + break + } + i0, i1, i2 := adjacentIndices(currentIndices, i) pt0 := pts[i0] pt1 := pts[i1] pt2 := pts[i2] c := triangleCross(pt0, pt1, pt2) if c == 0 { - idx = i + idxToRemove = i break } cs[i] = c + + if pts[currentIndices[upperLeft]].X > pts[currentIndices[i]].X { + upperLeft = i + } else if pts[currentIndices[upperLeft]].X == pts[currentIndices[i]].X && + pts[currentIndices[upperLeft]].Y > pts[currentIndices[i]].Y { + upperLeft = i + } } - if idx != -1 { - currentIndices = append(currentIndices[:idx], currentIndices[idx+1:]...) + if idxToRemove != -1 { + currentIndices = append(currentIndices[:idxToRemove], currentIndices[idxToRemove+1:]...) continue } - idx = -1 + clockwise := cs[upperLeft] < 0 + + idx := -1 index: for i := range currentIndices { i0, i1, i2 := adjacentIndices(currentIndices, i) @@ -119,6 +112,10 @@ dup: if l := len(currentIndices); j == (i+l-1)%l || j == i || j == (i+1)%l { continue } + jj := currentIndices[j] + if pts[i0] == pts[jj] || pts[i1] == pts[jj] || pts[i2] == pts[jj] { + continue + } if InTriangle(pts[currentIndices[j]], pt0, pt1, pt2) { // If the triangle includes another point, the triangle is not an ear. continue index diff --git a/vector/internal/math/triangulate_test.go b/vector/internal/math/triangulate_test.go index e4815981b..554fc426a 100644 --- a/vector/internal/math/triangulate_test.go +++ b/vector/internal/math/triangulate_test.go @@ -215,6 +215,61 @@ func TestTriangulate(t *testing.T) { }, Out: []uint16{8, 0, 1, 1, 2, 4, 1, 4, 5, 8, 1, 5, 8, 5, 7}, }, + { + In: []Point{ + {0, 0}, + {0, 1}, + {1, 1}, + {1, 0}, + {2, 0}, + }, + Out: []uint16{3, 0, 1, 3, 1, 2}, + }, + { + In: []Point{ + {2, 0}, + {0, 0}, + {1, 0}, + {1, 1}, + {2, 1}, + }, + Out: []uint16{4, 0, 2, 4, 2, 3}, + }, + { + In: []Point{ + {2, 0}, + {2, 1}, + {1, 1}, + {1, 0}, + {0, 0}, + }, + Out: []uint16{3, 0, 1, 3, 1, 2}, + }, + { + // Butterfly + In: []Point{ + {0, 0}, + {0, 2}, + {1, 1}, + {2, 2}, + {2, 0}, + {1, 1}, + }, + Out: []uint16{5, 0, 1, 5, 3, 4}, + }, + { + In: []Point{ + {0, 6}, + {0, 9}, + {6, 6}, + {6, 3}, + {9, 3}, + {8, 0}, + {6, 0}, + {6, 3}, + }, + Out: []uint16{7, 0, 1, 7, 1, 2, 7, 4, 5, 7, 5, 6}, + }, } for _, tc := range tests { got := Triangulate(tc.In)