added sorting of font contours for correct triangulation
This commit is contained in:
parent
c4d3130770
commit
e37f4f5565
2 changed files with 157 additions and 18 deletions
106
earcut.go
106
earcut.go
|
@ -799,3 +799,109 @@ func (ec *earcut) removeNode(p *node) {
|
||||||
p.nextZ.prevZ = p.prevZ
|
p.nextZ.prevZ = p.prevZ
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sortFontContours takes the contours of a font glyph
|
||||||
|
// and checks whether each contour is the outside or a
|
||||||
|
// hole, and returns an array that is sorted so that
|
||||||
|
// it contains an index of an outer contour followed by
|
||||||
|
// any number of indices of hole contours followed by
|
||||||
|
// a terminating -1
|
||||||
|
func sortFontContours(contours [][]backendbase.Vec) []int {
|
||||||
|
type cut struct {
|
||||||
|
idx int
|
||||||
|
count int
|
||||||
|
}
|
||||||
|
type info struct {
|
||||||
|
cuts []cut
|
||||||
|
cutTotal int
|
||||||
|
outer bool
|
||||||
|
}
|
||||||
|
|
||||||
|
cutBuf := make([]cut, len(contours)*len(contours))
|
||||||
|
cinf := make([]info, len(contours))
|
||||||
|
for i := range contours {
|
||||||
|
cinf[i].cuts = cutBuf[i*len(contours) : i*len(contours)]
|
||||||
|
}
|
||||||
|
|
||||||
|
// go through each contour, pick one point on it, and
|
||||||
|
// project that point to the right. count the number of
|
||||||
|
// other contours that it cuts
|
||||||
|
for i, p1 := range contours {
|
||||||
|
pt := p1[0]
|
||||||
|
for j, p2 := range contours {
|
||||||
|
if i == j {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for k := range p2 {
|
||||||
|
a, b := p2[k], p2[(k+1)%len(p2)]
|
||||||
|
if a == b {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
minY := math.Min(a[1], b[1])
|
||||||
|
maxY := math.Max(a[1], b[1])
|
||||||
|
|
||||||
|
if pt[1] <= minY || pt[1] > maxY {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
r := (pt[1] - a[1]) / (b[1] - a[1])
|
||||||
|
x := (b[0]-a[0])*r + a[0]
|
||||||
|
if x <= pt[0] {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for l := range cinf[i].cuts {
|
||||||
|
if cinf[i].cuts[l].idx == j {
|
||||||
|
cinf[i].cuts[l].count++
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
cinf[i].cuts = append(cinf[i].cuts, cut{idx: j, count: 1})
|
||||||
|
}
|
||||||
|
cinf[i].cutTotal++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// any contour with an even number of cuts is outer,
|
||||||
|
// odd number of cuts means it is a hole
|
||||||
|
for i := range cinf {
|
||||||
|
cinf[i].outer = cinf[i].cutTotal%2 == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// go through them again, pick any outer contour, then
|
||||||
|
// find any hole where the first outer contour it cuts
|
||||||
|
// an odd number of times is the picked contour and add
|
||||||
|
// it to the list of its holes
|
||||||
|
result := make([]int, 0, len(contours)*2)
|
||||||
|
for i := range cinf {
|
||||||
|
if !cinf[i].outer {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
result = append(result, i)
|
||||||
|
|
||||||
|
for j := range cinf {
|
||||||
|
if cinf[j].outer {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, cut := range cinf[j].cuts {
|
||||||
|
if cut.count%2 == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cut.idx == i {
|
||||||
|
result = append(result, j)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, -1)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
53
text.go
53
text.go
|
@ -596,7 +596,7 @@ func (cv *Canvas) runeTris(rn rune) []backendbase.Vec {
|
||||||
var gb truetype.GlyphBuf
|
var gb truetype.GlyphBuf
|
||||||
gb.Load(cv.state.font.font, baseFontSize, idx, font.HintingFull)
|
gb.Load(cv.state.font.font, baseFontSize, idx, font.HintingFull)
|
||||||
|
|
||||||
polyWithHoles := make([][]backendbase.Vec, 0, len(gb.Ends))
|
contours := make([][]backendbase.Vec, 0, len(gb.Ends))
|
||||||
|
|
||||||
from := 0
|
from := 0
|
||||||
for _, to := range gb.Ends {
|
for _, to := range gb.Ends {
|
||||||
|
@ -608,7 +608,7 @@ func (cv *Canvas) runeTris(rn rune) []backendbase.Vec {
|
||||||
others = ps[1:]
|
others = ps[1:]
|
||||||
} else {
|
} else {
|
||||||
last := ps[len(ps)-1]
|
last := ps[len(ps)-1]
|
||||||
if ps[len(ps)-1].Flags&0x01 != 0 {
|
if last.Flags&0x01 != 0 {
|
||||||
start = last
|
start = last
|
||||||
others = ps[:len(ps)-1]
|
others = ps[:len(ps)-1]
|
||||||
} else {
|
} else {
|
||||||
|
@ -652,30 +652,63 @@ func (cv *Canvas) runeTris(rn rune) []backendbase.Vec {
|
||||||
}
|
}
|
||||||
path.ClosePath()
|
path.ClosePath()
|
||||||
|
|
||||||
poly := make([]backendbase.Vec, len(path.p))
|
contour := make([]backendbase.Vec, len(path.p))
|
||||||
for i, pt := range path.p {
|
for i, pt := range path.p {
|
||||||
poly[i] = pt.pos
|
contour[i] = pt.pos
|
||||||
}
|
}
|
||||||
|
|
||||||
polyWithHoles = append(polyWithHoles, poly)
|
contours = append(contours, contour)
|
||||||
|
|
||||||
from = to
|
from = to
|
||||||
}
|
}
|
||||||
|
|
||||||
|
idxs := sortFontContours(contours)
|
||||||
|
sortedContours := make([][]backendbase.Vec, 0, len(idxs))
|
||||||
|
trisList := make([][]backendbase.Vec, 0, len(contours))
|
||||||
|
|
||||||
|
for i := 0; i < len(idxs); {
|
||||||
|
var j int
|
||||||
|
for j = i; j < len(idxs); j++ {
|
||||||
|
if idxs[j] == -1 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sortedContours = sortedContours[:j-i]
|
||||||
|
for k, idx := range idxs[i:j] {
|
||||||
|
sortedContours[k] = contours[idx]
|
||||||
|
}
|
||||||
|
|
||||||
var ec earcut
|
var ec earcut
|
||||||
ec.run(polyWithHoles)
|
ec.run(sortedContours)
|
||||||
|
|
||||||
tris := make([]backendbase.Vec, len(ec.indices))
|
tris := make([]backendbase.Vec, len(ec.indices))
|
||||||
for i, idx := range ec.indices {
|
for i, idx := range ec.indices {
|
||||||
pidx := 0
|
pidx := 0
|
||||||
poly := polyWithHoles[pidx]
|
poly := sortedContours[pidx]
|
||||||
for idx >= len(poly) {
|
for idx >= len(poly) {
|
||||||
idx -= len(poly)
|
idx -= len(poly)
|
||||||
pidx++
|
pidx++
|
||||||
poly = polyWithHoles[pidx]
|
poly = sortedContours[pidx]
|
||||||
}
|
}
|
||||||
tris[i] = poly[idx]
|
tris[i] = poly[idx]
|
||||||
}
|
}
|
||||||
|
trisList = append(trisList, tris)
|
||||||
|
|
||||||
|
i = j + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
count := 0
|
||||||
|
for _, tris := range trisList {
|
||||||
|
count += len(tris)
|
||||||
|
}
|
||||||
|
|
||||||
|
allTris := make([]backendbase.Vec, count)
|
||||||
|
pos := 0
|
||||||
|
for _, tris := range trisList {
|
||||||
|
copy(allTris[pos:], tris)
|
||||||
|
pos += len(tris)
|
||||||
|
}
|
||||||
|
|
||||||
cache, ok := cv.fontTriCache[cv.state.font]
|
cache, ok := cv.fontTriCache[cv.state.font]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
@ -683,9 +716,9 @@ func (cv *Canvas) runeTris(rn rune) []backendbase.Vec {
|
||||||
cv.fontTriCache[cv.state.font] = cache
|
cv.fontTriCache[cv.state.font] = cache
|
||||||
}
|
}
|
||||||
cache.lastUsed = time.Now()
|
cache.lastUsed = time.Now()
|
||||||
cache.cache[idx] = tris
|
cache.cache[idx] = allTris
|
||||||
|
|
||||||
return tris
|
return allTris
|
||||||
}
|
}
|
||||||
|
|
||||||
// TextMetrics is the result of a MeasureText call
|
// TextMetrics is the result of a MeasureText call
|
||||||
|
|
Loading…
Reference in a new issue