forked from hajimehoshi/ebiten
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpath.go
143 lines (126 loc) · 3.79 KB
/
path.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2019 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package vector provides functions for vector graphics rendering.
//
// This package is under experiments and the API might be changed with breaking backward compatibility.
package vector
import (
"image/color"
"math"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/vector/internal/triangulate"
)
var emptyImage *ebiten.Image
func init() {
emptyImage, _ = ebiten.NewImage(1, 1, ebiten.FilterDefault)
emptyImage.Fill(color.White)
}
// Path represents a collection of path segments.
type Path struct {
segs [][]triangulate.Point
cur triangulate.Point
}
// MoveTo skips the current position of the path to the given position (x, y) without adding any strokes.
func (p *Path) MoveTo(x, y float32) {
p.cur = triangulate.Point{X: x, Y: y}
p.segs = append(p.segs, []triangulate.Point{p.cur})
}
// LineTo adds a line segument to the path, which starts from the current position and ends to the given position (x, y).
//
// LineTo updates the current position to (x, y).
func (p *Path) LineTo(x, y float32) {
if len(p.segs) == 0 {
p.segs = append(p.segs, []triangulate.Point{p.cur})
}
p.segs[len(p.segs)-1] = append(p.segs[len(p.segs)-1], triangulate.Point{X: x, Y: y})
p.cur = triangulate.Point{X: x, Y: y}
}
// nseg returns a number of segments based on the given two points (x0, y0) and (x1, y1).
func nseg(x0, y0, x1, y1 float32) int {
distx := x1 - x0
if distx < 0 {
distx = -distx
}
disty := y1 - y0
if disty < 0 {
disty = -disty
}
dist := distx
if dist < disty {
dist = disty
}
return int(math.Ceil(float64(dist)))
}
// QuadTo adds a quadratic Bézier curve to the path.
func (p *Path) QuadTo(cpx, cpy, x, y float32) {
c := p.cur
num := nseg(c.X, c.Y, x, y)
for t := float32(0.0); t <= 1; t += 1.0 / float32(num) {
xf := (1-t)*(1-t)*c.X + 2*t*(1-t)*cpx + t*t*x
yf := (1-t)*(1-t)*c.Y + 2*t*(1-t)*cpy + t*t*y
p.LineTo(xf, yf)
}
}
// CubicTo adds a cubic Bézier curve to the path.
func (p *Path) CubicTo(cp0x, cp0y, cp1x, cp1y, x, y float32) {
c := p.cur
num := nseg(c.X, c.Y, x, y)
for t := float32(0.0); t <= 1; t += 1.0 / float32(num) {
xf := (1-t)*(1-t)*(1-t)*c.X + 3*(1-t)*(1-t)*t*cp0x + 3*(1-t)*t*t*cp1x + t*t*t*x
yf := (1-t)*(1-t)*(1-t)*c.Y + 3*(1-t)*(1-t)*t*cp0y + 3*(1-t)*t*t*cp1y + t*t*t*y
p.LineTo(xf, yf)
}
}
// FillOptions represents options to fill a path.
type FillOptions struct {
// Color is a color to fill with.
Color color.Color
}
// Fill fills the region of the path with the given options op.
func (p *Path) Fill(dst *ebiten.Image, op *FillOptions) {
var vertices []ebiten.Vertex
var indices []uint16
if op.Color == nil {
return
}
r, g, b, a := op.Color.RGBA()
var rf, gf, bf, af float32
if a == 0 {
return
}
rf = float32(r) / float32(a)
gf = float32(g) / float32(a)
bf = float32(b) / float32(a)
af = float32(a) / 0xffff
var base uint16
for _, seg := range p.segs {
for _, pt := range seg {
vertices = append(vertices, ebiten.Vertex{
DstX: pt.X,
DstY: pt.Y,
SrcX: 0,
SrcY: 0,
ColorR: rf,
ColorG: gf,
ColorB: bf,
ColorA: af,
})
}
for _, idx := range triangulate.Triangulate(seg) {
indices = append(indices, idx+base)
}
base += uint16(len(seg))
}
dst.DrawTriangles(vertices, indices, emptyImage, nil)
}