Newer
Older
// TrianglesData specifies a list of Triangles vertices with three common properties: Position,
// Color and Texture.
type TrianglesData []struct {
Position Vec
Color NRGBA
// Len returns the number of vertices in TrianglesData.
func (td *TrianglesData) Len() int {
return len(*td)
// Draw is unimplemented for TrianglesData and panics.
func (td *TrianglesData) Draw() {
panic(fmt.Errorf("%T.Draw: invalid operation", td))
func (td *TrianglesData) resize(len int) {
if len > td.Len() {
needAppend := len - td.Len()
for i := 0; i < needAppend; i++ {
*td = append(*td, struct {
Position Vec
Color NRGBA
Texture Vec
}{V(0, 0), NRGBA{1, 1, 1, 1}, V(-1, -1)})
if len < td.Len() {
*td = (*td)[:len]
func (td *TrianglesData) updateData(offset int, t Triangles) {
// fast path optimization
if t, ok := t.(*TrianglesData); ok {
// slow path manual copy
if t, ok := t.(TrianglesPosition); ok {
for i := offset; i < len(*td); i++ {
(*td)[i].Position = t.Position(i)
}
if t, ok := t.(TrianglesColor); ok {
for i := offset; i < len(*td); i++ {
(*td)[i].Color = t.Color(i)
if t, ok := t.(TrianglesTexture); ok {
for i := offset; i < len(*td); i++ {
(*td)[i].Texture = t.Texture(i)
// Update copies vertex properties from the supplied Triangles into this TrianglesData.
//
// TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
func (td *TrianglesData) Update(t Triangles) {
td.resize(t.Len())
td.updateData(0, t)
}
// Append adds supplied Triangles to the end of the TrianglesData.
func (td *TrianglesData) Append(t Triangles) {
td.resize(td.Len() + t.Len())
td.updateData(td.Len()-t.Len(), t)
// Copy returns an exact independent copy of this TrianglesData.
func (td *TrianglesData) Copy() Triangles {
copyTd := make(TrianglesData, td.Len())
copyTd.Update(td)
return ©Td
}
// Position returns the position property of i-th vertex.
func (td *TrianglesData) Position(i int) Vec {
return (*td)[i].Position
// Color returns the color property of i-th vertex.
return (*td)[i].Color
// Texture returns the texture property of i-th vertex.
func (td *TrianglesData) Texture(i int) Vec {
return (*td)[i].Texture
// TrianglesDrawer is a helper type that wraps Triangles and turns them into a Drawer.
// It does so by creating a separate Triangles instance for each Target. The instances are
// correctly updated alongside the wrapped Triangles.
type TrianglesDrawer struct {
Triangles
tris map[Target]Triangles
dirty bool
func (td *TrianglesDrawer) flush() {
if !td.dirty {
return
td.dirty = false
for _, t := range td.tris {
t.Update(td.Triangles)
// Draw draws the wrapped Triangles onto the provided Target.
func (td *TrianglesDrawer) Draw(target Target) {
if td.tris == nil {
td.tris = make(map[Target]Triangles)
}
tri := td.tris[target]
if tri == nil {
tri = target.MakeTriangles(td.Triangles)
td.tris[target] = tri
}
tri.Draw()
// Update updates the wrapped Triangles with the supplied Triangles.
//
// Call only this method to update the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Update(t Triangles) {
td.dirty = true
td.Triangles.Update(t)
// Append appends the supplied Triangles to the wrapped Triangles.
//
// Call only this method to append to the wrapped Triangles, otherwise the TrianglesDrawer will not
// work correctly.
func (td *TrianglesDrawer) Append(t Triangles) {
td.dirty = true
td.Triangles.Append(t)
}
// Sprite is a picture that can be drawn onto a Target. To change the position/rotation/scale of
// the Sprite, use Target's SetTransform method.
type Sprite struct {
}
// NewSprite creates a Sprite with the supplied Picture. The dimensions of the returned Sprite match
// the dimensions of the Picture.
func NewSprite(pic *Picture) *Sprite {
{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 0)},
{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)},
{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)},
{Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 1)},
}
s := &Sprite{
}
s.SetPicture(pic)
return s
}
// SetPicture changes the Picture of the Sprite and resizes it accordingly.
func (s *Sprite) SetPicture(pic *Picture) {
oldPic := s.pic
s.pic = pic
if oldPic.Bounds().Size == pic.Bounds().Size {
return
}
w, h := pic.Bounds().Size.XY()
(*s.data)[0].Position = V(0, 0)
(*s.data)[2].Position = V(w, h)
(*s.data)[1].Position = V(w, 0)
(*s.data)[3].Position = V(0, 0)
(*s.data)[4].Position = V(w, h)
(*s.data)[5].Position = V(0, h)
}
// Picture returns the current Picture of the Sprite.
func (s *Sprite) Picture() *Picture {
return s.pic
}
// Draw draws the Sprite onto the provided Target.
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
func (s *Sprite) Draw(t Target) {
t.SetPicture(s.pic)
s.td.Draw(t)
}
// Polygon is a convex polygon shape filled with a single color.
type Polygon struct {
td TrianglesDrawer
data *TrianglesData
col NRGBA
}
// NewPolygon creates a Polygon with specified color and points. Points can be in clock-wise or
// counter-clock-wise order, it doesn't matter. They should however form a convex polygon.
func NewPolygon(c color.Color, points ...Vec) *Polygon {
data := make(TrianglesData, len(points))
p := &Polygon{
td: TrianglesDrawer{Triangles: &data},
data: &data,
}
p.SetColor(c)
p.SetPoints(points...)
return p
}
// SetColor changes the color of the Polygon.
//
// If the Polygon is very large, this method might end up being too expensive. Consider using
// a color mask on a Target, in such a case.
func (p *Polygon) SetColor(c color.Color) {
p.col = NRGBAModel.Convert(c).(NRGBA)
for i := range *p.data {
(*p.data)[i].Color = p.col
}
// dirty stuff, need to update manually
p.td.dirty = true
}
// Color returns the current color of the Polygon.
func (p *Polygon) Color() NRGBA {
return p.col
}
// SetPoints sets the points of the Polygon. The number of points might differ from the original
// count.
//
// This method is more effective, than creating a new Polygon with the given points.
//
// However, it is less expensive than using a transform on a Target.
func (p *Polygon) SetPoints(points ...Vec) {
p.data.resize(len(points))
for i, pt := range points {
(*p.data)[i].Position = pt
(*p.data)[i].Color = p.col
(*p.data)[i].Texture = V(-1, -1)
}
// dirty stuff
p.td.dirty = true
}
// Points returns a slice of points of the Polygon in the order they where supplied.
func (p *Polygon) Points() []Vec {
points := make([]Vec, p.data.Len())
for i := range *p.data {
points[i] = (*p.data)[i].Position
}
return points
}
// Draw draws the Polygon onto the Target.
func (p *Polygon) Draw(t Target) {
t.SetPicture(nil)
p.td.Draw(t)