mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
Compare commits
3 Commits
cdb430b2a5
...
fc37cdedeb
Author | SHA1 | Date | |
---|---|---|---|
|
fc37cdedeb | ||
|
309c886c2e | ||
|
68380e506e |
147
vector/path.go
147
vector/path.go
@ -65,11 +65,18 @@ type subpath struct {
|
||||
closed bool
|
||||
}
|
||||
|
||||
func (s *subpath) pointCount() int {
|
||||
// reset resets the subpath.
|
||||
// reset doesn't release the allocated memory so that the memory can be reused.
|
||||
func (s *subpath) reset() {
|
||||
s.points = s.points[:0]
|
||||
s.closed = false
|
||||
}
|
||||
|
||||
func (s subpath) pointCount() int {
|
||||
return len(s.points)
|
||||
}
|
||||
|
||||
func (s *subpath) lastPoint() point {
|
||||
func (s subpath) lastPoint() point {
|
||||
return s.points[len(s.points)-1]
|
||||
}
|
||||
|
||||
@ -78,10 +85,12 @@ func (s *subpath) appendPoint(pt point) {
|
||||
panic("vector: a closed subpathment cannot append a new point")
|
||||
}
|
||||
|
||||
// Do not add a too close point to the last point.
|
||||
// This can cause unexpected rendering results.
|
||||
if lp := s.lastPoint(); abs(lp.x-pt.x) < 1e-2 && abs(lp.y-pt.y) < 1e-2 {
|
||||
return
|
||||
if len(s.points) > 0 {
|
||||
// Do not add a too close point to the last point.
|
||||
// This can cause unexpected rendering results.
|
||||
if lp := s.lastPoint(); abs(lp.x-pt.x) < 1e-2 && abs(lp.y-pt.y) < 1e-2 {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
s.points = append(s.points, pt)
|
||||
@ -100,13 +109,30 @@ func (s *subpath) close() {
|
||||
type Path struct {
|
||||
ops []op
|
||||
|
||||
subpaths []*subpath
|
||||
subpaths []subpath
|
||||
}
|
||||
|
||||
func (p *Path) ensureSubpaths() []*subpath {
|
||||
// TODO: Probably it is better to avoid returning a slice since allocation is heavy.
|
||||
// What about walkSubpaths(func(*subpath))?
|
||||
// reset resets the path.
|
||||
// reset doesn't release the allocated memory so that the memory can be reused.
|
||||
func (p *Path) reset() {
|
||||
p.ops = p.ops[:0]
|
||||
p.subpaths = p.subpaths[:0]
|
||||
}
|
||||
|
||||
func (p *Path) appendNewSubpath(pt point) {
|
||||
if cap(p.subpaths) > len(p.subpaths) {
|
||||
// Reuse the last subpath since the last subpath might have an already allocated slice.
|
||||
p.subpaths = p.subpaths[:len(p.subpaths)+1]
|
||||
p.subpaths[len(p.subpaths)-1].reset()
|
||||
p.subpaths[len(p.subpaths)-1].appendPoint(pt)
|
||||
return
|
||||
}
|
||||
p.subpaths = append(p.subpaths, subpath{
|
||||
points: []point{pt},
|
||||
})
|
||||
}
|
||||
|
||||
func (p *Path) ensureSubpaths() []subpath {
|
||||
if len(p.subpaths) > 0 || len(p.ops) == 0 {
|
||||
return p.subpaths
|
||||
}
|
||||
@ -115,9 +141,7 @@ func (p *Path) ensureSubpaths() []*subpath {
|
||||
for _, op := range p.ops {
|
||||
switch op.typ {
|
||||
case opTypeMoveTo:
|
||||
p.subpaths = append(p.subpaths, &subpath{
|
||||
points: []point{op.p1},
|
||||
})
|
||||
p.appendNewSubpath(op.p1)
|
||||
cur = op.p1
|
||||
case opTypeLineTo:
|
||||
p.lineTo(op.p1)
|
||||
@ -192,9 +216,7 @@ func (p *Path) Close() {
|
||||
|
||||
func (p *Path) lineTo(pt point) {
|
||||
if len(p.subpaths) == 0 || p.subpaths[len(p.subpaths)-1].closed {
|
||||
p.subpaths = append(p.subpaths, &subpath{
|
||||
points: []point{pt},
|
||||
})
|
||||
p.appendNewSubpath(pt)
|
||||
return
|
||||
}
|
||||
p.subpaths[len(p.subpaths)-1].appendPoint(pt)
|
||||
@ -457,8 +479,7 @@ func (p *Path) close() {
|
||||
if len(p.subpaths) == 0 {
|
||||
return
|
||||
}
|
||||
subpath := p.subpaths[len(p.subpaths)-1]
|
||||
subpath.close()
|
||||
p.subpaths[len(p.subpaths)-1].close()
|
||||
}
|
||||
|
||||
// AppendVerticesAndIndicesForFilling appends vertices and indices to fill this path and returns them.
|
||||
@ -558,6 +579,7 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic
|
||||
}
|
||||
|
||||
var rects [][4]point
|
||||
var tmpPath Path
|
||||
for _, subpath := range p.ensureSubpaths() {
|
||||
if subpath.pointCount() < 2 {
|
||||
continue
|
||||
@ -643,46 +665,49 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic
|
||||
delta := math.Pi - da
|
||||
exceed := float32(math.Abs(1/math.Sin(float64(delta/2)))) > op.MiterLimit
|
||||
|
||||
var quad Path
|
||||
quad.MoveTo(c.x, c.y)
|
||||
// Quadrilateral
|
||||
tmpPath.reset()
|
||||
tmpPath.MoveTo(c.x, c.y)
|
||||
if da < math.Pi {
|
||||
quad.LineTo(rect[1].x, rect[1].y)
|
||||
tmpPath.LineTo(rect[1].x, rect[1].y)
|
||||
if !exceed {
|
||||
pt := crossingPointForTwoLines(rect[0], rect[1], nextRect[0], nextRect[1])
|
||||
quad.LineTo(pt.x, pt.y)
|
||||
tmpPath.LineTo(pt.x, pt.y)
|
||||
}
|
||||
quad.LineTo(nextRect[0].x, nextRect[0].y)
|
||||
tmpPath.LineTo(nextRect[0].x, nextRect[0].y)
|
||||
} else {
|
||||
quad.LineTo(rect[3].x, rect[3].y)
|
||||
tmpPath.LineTo(rect[3].x, rect[3].y)
|
||||
if !exceed {
|
||||
pt := crossingPointForTwoLines(rect[2], rect[3], nextRect[2], nextRect[3])
|
||||
quad.LineTo(pt.x, pt.y)
|
||||
tmpPath.LineTo(pt.x, pt.y)
|
||||
}
|
||||
quad.LineTo(nextRect[2].x, nextRect[2].y)
|
||||
tmpPath.LineTo(nextRect[2].x, nextRect[2].y)
|
||||
}
|
||||
vertices, indices = quad.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
|
||||
case LineJoinBevel:
|
||||
var tri Path
|
||||
tri.MoveTo(c.x, c.y)
|
||||
// Triangle
|
||||
tmpPath.reset()
|
||||
tmpPath.MoveTo(c.x, c.y)
|
||||
if da < math.Pi {
|
||||
tri.LineTo(rect[1].x, rect[1].y)
|
||||
tri.LineTo(nextRect[0].x, nextRect[0].y)
|
||||
tmpPath.LineTo(rect[1].x, rect[1].y)
|
||||
tmpPath.LineTo(nextRect[0].x, nextRect[0].y)
|
||||
} else {
|
||||
tri.LineTo(rect[3].x, rect[3].y)
|
||||
tri.LineTo(nextRect[2].x, nextRect[2].y)
|
||||
tmpPath.LineTo(rect[3].x, rect[3].y)
|
||||
tmpPath.LineTo(nextRect[2].x, nextRect[2].y)
|
||||
}
|
||||
vertices, indices = tri.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
|
||||
case LineJoinRound:
|
||||
var arc Path
|
||||
arc.MoveTo(c.x, c.y)
|
||||
// Arc
|
||||
tmpPath.reset()
|
||||
tmpPath.MoveTo(c.x, c.y)
|
||||
if da < math.Pi {
|
||||
arc.Arc(c.x, c.y, op.Width/2, a0, a1, Clockwise)
|
||||
tmpPath.Arc(c.x, c.y, op.Width/2, a0, a1, Clockwise)
|
||||
} else {
|
||||
arc.Arc(c.x, c.y, op.Width/2, a0+math.Pi, a1+math.Pi, CounterClockwise)
|
||||
tmpPath.Arc(c.x, c.y, op.Width/2, a0+math.Pi, a1+math.Pi, CounterClockwise)
|
||||
}
|
||||
vertices, indices = arc.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
}
|
||||
}
|
||||
|
||||
@ -707,10 +732,11 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic
|
||||
y: (startR[0].y + startR[2].y) / 2,
|
||||
}
|
||||
a := float32(math.Atan2(float64(startR[0].y-startR[2].y), float64(startR[0].x-startR[2].x)))
|
||||
var arc Path
|
||||
arc.MoveTo(startR[0].x, startR[0].y)
|
||||
arc.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, CounterClockwise)
|
||||
vertices, indices = arc.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
// Arc
|
||||
tmpPath.reset()
|
||||
tmpPath.MoveTo(startR[0].x, startR[0].y)
|
||||
tmpPath.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, CounterClockwise)
|
||||
vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
}
|
||||
{
|
||||
c := point{
|
||||
@ -718,10 +744,11 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic
|
||||
y: (endR[1].y + endR[3].y) / 2,
|
||||
}
|
||||
a := float32(math.Atan2(float64(endR[1].y-endR[3].y), float64(endR[1].x-endR[3].x)))
|
||||
var arc Path
|
||||
arc.MoveTo(endR[1].x, endR[1].y)
|
||||
arc.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, Clockwise)
|
||||
vertices, indices = arc.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
// Arc
|
||||
tmpPath.reset()
|
||||
tmpPath.MoveTo(endR[1].x, endR[1].y)
|
||||
tmpPath.Arc(c.x, c.y, op.Width/2, a, a+math.Pi, Clockwise)
|
||||
vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
}
|
||||
|
||||
case LineCapSquare:
|
||||
@ -731,24 +758,26 @@ func (p *Path) AppendVerticesAndIndicesForStroke(vertices []ebiten.Vertex, indic
|
||||
s, c := math.Sincos(a)
|
||||
dx, dy := float32(c)*op.Width/2, float32(s)*op.Width/2
|
||||
|
||||
var quad Path
|
||||
quad.MoveTo(startR[0].x, startR[0].y)
|
||||
quad.LineTo(startR[0].x+dx, startR[0].y+dy)
|
||||
quad.LineTo(startR[2].x+dx, startR[2].y+dy)
|
||||
quad.LineTo(startR[2].x, startR[2].y)
|
||||
vertices, indices = quad.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
// Quadrilateral
|
||||
tmpPath.reset()
|
||||
tmpPath.MoveTo(startR[0].x, startR[0].y)
|
||||
tmpPath.LineTo(startR[0].x+dx, startR[0].y+dy)
|
||||
tmpPath.LineTo(startR[2].x+dx, startR[2].y+dy)
|
||||
tmpPath.LineTo(startR[2].x, startR[2].y)
|
||||
vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
}
|
||||
{
|
||||
a := math.Atan2(float64(endR[1].y-endR[0].y), float64(endR[1].x-endR[0].x))
|
||||
s, c := math.Sincos(a)
|
||||
dx, dy := float32(c)*op.Width/2, float32(s)*op.Width/2
|
||||
|
||||
var quad Path
|
||||
quad.MoveTo(endR[1].x, endR[1].y)
|
||||
quad.LineTo(endR[1].x+dx, endR[1].y+dy)
|
||||
quad.LineTo(endR[3].x+dx, endR[3].y+dy)
|
||||
quad.LineTo(endR[3].x, endR[3].y)
|
||||
vertices, indices = quad.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
// Quadrilateral
|
||||
tmpPath.reset()
|
||||
tmpPath.MoveTo(endR[1].x, endR[1].y)
|
||||
tmpPath.LineTo(endR[1].x+dx, endR[1].y+dy)
|
||||
tmpPath.LineTo(endR[3].x+dx, endR[3].y+dy)
|
||||
tmpPath.LineTo(endR[3].x, endR[3].y)
|
||||
vertices, indices = tmpPath.AppendVerticesAndIndicesForFilling(vertices, indices)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user