package pixel import ( "fmt" "image/color" ) // TrianglesData specifies a list of Triangles vertices with three common properties: Position, // Color and Texture. type TrianglesData []struct { Position Vec Color NRGBA Texture Vec } // 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)) } // Update copies vertex properties from the supplied Triangles into this TrianglesData. // // TrianglesPosition, TrianglesColor and TrianglesTexture are supported. func (td *TrianglesData) Update(t Triangles) { if t.Len() > td.Len() { *td = append(*td, make(TrianglesData, t.Len()-td.Len())...) } if t.Len() < td.Len() { *td = (*td)[:t.Len()] } // fast path optimization if t, ok := t.(*TrianglesData); ok { copy(*td, *t) return } // slow path manual copy if t, ok := t.(TrianglesPosition); ok { for i := range *td { (*td)[i].Position = t.Position(i) } } if t, ok := t.(TrianglesColor); ok { for i := range *td { (*td)[i].Color = NRGBAModel.Convert(t.Color(i)).(NRGBA) } } if t, ok := t.(TrianglesTexture); ok { for i := range *td { (*td)[i].Texture = t.Texture(i) } } } // Append adds supplied Triangles to the end of the TrianglesData. func (td *TrianglesData) Append(t Triangles) { newTd := make(TrianglesData, t.Len()) newTd.Update(t) *td = append(*td, newTd...) } // 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. func (td *TrianglesData) Color(i int) NRGBA { 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) } td.flush() 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, positioned somewhere, with an optional mask color. type Sprite struct { td TrianglesDrawer pic *Picture transform []Transform maskColor color.Color } // 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 { s := &Sprite{ td: TrianglesDrawer{Triangles: &TrianglesData{}}, } s.SetPicture(pic) return s } // SetPicture changes the Picture of the Sprite and resizes it accordingly. func (s *Sprite) SetPicture(pic *Picture) { w, h := pic.Bounds().Size.XY() s.td.Update(&TrianglesData{ {Position: V(0, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 0)}, {Position: V(w, 0), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 0)}, {Position: V(w, h), 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(w, h), Color: NRGBA{1, 1, 1, 1}, Texture: V(1, 1)}, {Position: V(0, h), Color: NRGBA{1, 1, 1, 1}, Texture: V(0, 1)}, }) s.pic = pic } // Picture returns the current Picture of the Sprite. func (s *Sprite) Picture() *Picture { return s.pic } // SetTransform sets a chain of Transforms that will be applied to this Sprite in reverse order. func (s *Sprite) SetTransform(t ...Transform) { s.transform = t } // Transform returns the current chain of Transforms that this Sprite is transformed by. func (s *Sprite) Transform() []Transform { return s.transform } // SetMaskColor changes the mask color of the Sprite. func (s *Sprite) SetMaskColor(c color.Color) { s.maskColor = c } // MaskColor returns the current mask color of the Sprite. func (s *Sprite) MaskColor() color.Color { return s.maskColor } // Draw draws the Sprite onto the provided Target. func (s *Sprite) Draw(target Target) { target.SetPicture(s.pic) target.SetTransform(s.transform...) target.SetMaskColor(s.maskColor) s.td.Draw(target) }