From 63339235b7b8b3d14904b0fa67728e17103215f2 Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Sat, 28 Jan 2017 20:01:59 +0100
Subject: [PATCH] rework Triangles API and adapt it

---
 batch.go       | 12 ++++++--
 canvas.go      | 21 +++++--------
 gltriangles.go | 55 +++++++++++++++++++++-------------
 graphics.go    | 81 ++++++++++++++++++++++----------------------------
 interface.go   | 49 ++++++++++++++++++------------
 window.go      |  2 +-
 6 files changed, 116 insertions(+), 104 deletions(-)

diff --git a/batch.go b/batch.go
index b7c2ecc..72aa476 100644
--- a/batch.go
+++ b/batch.go
@@ -35,7 +35,8 @@ func NewBatch(pic *Picture, container Triangles) *Batch {
 
 // Clear removes all objects from the Batch.
 func (b *Batch) Clear() {
-	b.cont.Update(&TrianglesData{})
+	b.cont.SetLen(0)
+	b.cont.Dirty()
 }
 
 // Draw draws all objects that are currently in the Batch onto another Target.
@@ -45,10 +46,11 @@ func (b *Batch) Draw(t Target) {
 }
 
 // MakeTriangles returns a specialized copy of the provided Triangles, that draws onto this Batch.
-func (b *Batch) MakeTriangles(t Triangles) Triangles {
+func (b *Batch) MakeTriangles(t Triangles) TargetTriangles {
 	return &batchTriangles{
 		Triangles: t.Copy(),
 		trans:     t.Copy(),
+		data:      MakeTrianglesData(t.Len()),
 		batch:     b,
 	}
 }
@@ -101,5 +103,9 @@ func (bt *batchTriangles) Draw() {
 		}
 	}
 	bt.trans.Update(&bt.data)
-	bt.batch.cont.Append(bt.trans)
+
+	cont := bt.batch.cont
+	cont.SetLen(cont.Len() + bt.trans.Len())
+	cont.Slice(cont.Len()-bt.trans.Len(), cont.Len()).Update(bt.trans)
+	cont.Dirty()
 }
diff --git a/canvas.go b/canvas.go
index e082c18..c472283 100644
--- a/canvas.go
+++ b/canvas.go
@@ -129,11 +129,11 @@ func (c *Canvas) Draw(t Target) {
 }
 
 // MakeTriangles returns Triangles that draw onto this Canvas.
-func (c *Canvas) MakeTriangles(t Triangles) Triangles {
-	tpcs := NewGLTriangles(c.s, t).(trianglesPositionColorTexture)
+func (c *Canvas) MakeTriangles(t Triangles) TargetTriangles {
+	gt := NewGLTriangles(c.s, t).(*glTriangles)
 	return &canvasTriangles{
-		c: c,
-		trianglesPositionColorTexture: tpcs,
+		c:           c,
+		glTriangles: gt,
 	}
 }
 
@@ -170,16 +170,9 @@ func (c *Canvas) SetMaskColor(col color.Color) {
 	c.col = mgl32.Vec4{r, g, b, a}
 }
 
-type trianglesPositionColorTexture interface {
-	Triangles
-	Position(i int) Vec
-	Color(i int) NRGBA
-	Texture(i int) Vec
-}
-
 type canvasTriangles struct {
 	c *Canvas
-	trianglesPositionColorTexture
+	*glTriangles
 }
 
 func (ct *canvasTriangles) Draw() {
@@ -199,10 +192,10 @@ func (ct *canvasTriangles) Draw() {
 
 		if pic != nil {
 			pic.Texture().Begin()
-			ct.trianglesPositionColorTexture.Draw()
+			ct.glTriangles.Draw()
 			pic.Texture().End()
 		} else {
-			ct.trianglesPositionColorTexture.Draw()
+			ct.glTriangles.Draw()
 		}
 
 		ct.c.s.End()
diff --git a/gltriangles.go b/gltriangles.go
index 1e55e4a..ca022fa 100644
--- a/gltriangles.go
+++ b/gltriangles.go
@@ -1,6 +1,8 @@
 package pixel
 
 import (
+	"fmt"
+
 	"github.com/faiface/mainthread"
 	"github.com/faiface/pixel/pixelgl"
 )
@@ -13,7 +15,7 @@ import (
 // Draw method simply draws the underlying pixelgl.VertexSlice. It needs to be called in the main
 // thread manually. Also, you need to take care of additional Target initialization or setting of
 // uniform attributes.
-func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles {
+func NewGLTriangles(shader *pixelgl.Shader, t Triangles) TargetTriangles {
 	var gt *glTriangles
 	mainthread.Call(func() {
 		gt = &glTriangles{
@@ -21,6 +23,7 @@ func NewGLTriangles(shader *pixelgl.Shader, t Triangles) Triangles {
 			shader: shader,
 		}
 	})
+	gt.SetLen(t.Len())
 	gt.Update(t)
 	return gt
 }
@@ -35,13 +38,7 @@ func (gt *glTriangles) Len() int {
 	return len(gt.data) / gt.vs.Stride()
 }
 
-func (gt *glTriangles) Draw() {
-	gt.vs.Begin()
-	gt.vs.Draw()
-	gt.vs.End()
-}
-
-func (gt *glTriangles) resize(len int) {
+func (gt *glTriangles) SetLen(len int) {
 	if len > gt.Len() {
 		needAppend := len - gt.Len()
 		for i := 0; i < needAppend; i++ {
@@ -57,10 +54,24 @@ func (gt *glTriangles) resize(len int) {
 	}
 }
 
-func (gt *glTriangles) updateData(offset int, t Triangles) {
+func (gt *glTriangles) Slice(i, j int) Triangles {
+	return &glTriangles{
+		vs:     gt.vs.Slice(i, j),
+		data:   gt.data[i*gt.vs.Stride() : j*gt.vs.Stride()],
+		shader: gt.shader,
+	}
+}
+
+func (gt *glTriangles) updateData(t Triangles) {
+	// glTriangles short path
+	if t, ok := t.(*glTriangles); ok {
+		copy(gt.data, t.data)
+		return
+	}
+
 	// TrianglesData short path
 	if t, ok := t.(*TrianglesData); ok {
-		for i := offset; i < offset+t.Len(); i++ {
+		for i := 0; i < gt.Len(); i++ {
 			var (
 				px, py = (*t)[i].Position.XY()
 				col    = (*t)[i].Color
@@ -79,14 +90,14 @@ func (gt *glTriangles) updateData(offset int, t Triangles) {
 	}
 
 	if t, ok := t.(TrianglesPosition); ok {
-		for i := offset; i < offset+t.Len(); i++ {
+		for i := 0; i < gt.Len(); i++ {
 			px, py := t.Position(i).XY()
 			gt.data[i*gt.vs.Stride()+0] = float32(px)
 			gt.data[i*gt.vs.Stride()+1] = float32(py)
 		}
 	}
 	if t, ok := t.(TrianglesColor); ok {
-		for i := offset; i < offset+t.Len(); i++ {
+		for i := 0; i < gt.Len(); i++ {
 			col := t.Color(i)
 			gt.data[i*gt.vs.Stride()+2] = float32(col.R)
 			gt.data[i*gt.vs.Stride()+3] = float32(col.G)
@@ -95,7 +106,7 @@ func (gt *glTriangles) updateData(offset int, t Triangles) {
 		}
 	}
 	if t, ok := t.(TrianglesTexture); ok {
-		for i := offset; i < offset+t.Len(); i++ {
+		for i := 0; i < gt.Len(); i++ {
 			tx, ty := t.Texture(i).XY()
 			gt.data[i*gt.vs.Stride()+6] = float32(tx)
 			gt.data[i*gt.vs.Stride()+7] = float32(ty)
@@ -120,14 +131,10 @@ func (gt *glTriangles) submitData() {
 }
 
 func (gt *glTriangles) Update(t Triangles) {
-	gt.resize(t.Len())
-	gt.updateData(0, t)
-	gt.submitData()
-}
-
-func (gt *glTriangles) Append(t Triangles) {
-	gt.resize(gt.Len() + t.Len())
-	gt.updateData(gt.Len()-t.Len(), t)
+	if gt.Len() != t.Len() {
+		panic(fmt.Errorf("%T.Update: invalid triangles len", gt))
+	}
+	gt.updateData(t)
 	gt.submitData()
 }
 
@@ -135,6 +142,12 @@ func (gt *glTriangles) Copy() Triangles {
 	return NewGLTriangles(gt.shader, gt)
 }
 
+func (gt *glTriangles) Draw() {
+	gt.vs.Begin()
+	gt.vs.Draw()
+	gt.vs.End()
+}
+
 func (gt *glTriangles) Position(i int) Vec {
 	px := gt.data[i*gt.vs.Stride()+0]
 	py := gt.data[i*gt.vs.Stride()+1]
diff --git a/graphics.go b/graphics.go
index eceaad8..637ea4e 100644
--- a/graphics.go
+++ b/graphics.go
@@ -13,6 +13,21 @@ type TrianglesData []struct {
 	Texture  Vec
 }
 
+// MakeTrianglesData creates TrianglesData of length len initialized with default property values.
+//
+// Prefer this function to make(TrianglesData, len), because make zeros them, while this function
+// does a correct intialization.
+func MakeTrianglesData(len int) TrianglesData {
+	td := TrianglesData{}
+	td.SetLen(len)
+	return td
+}
+
+// Len returns the number of vertices in TrianglesData.
+func (td *TrianglesData) Len() int {
+	return len(*td)
+}
+
 // SetLen resizes TrianglesData to len, while keeping the original content.
 //
 // If len is greater than TrianglesData's current length, the new data is filled with default
@@ -33,36 +48,32 @@ func (td *TrianglesData) SetLen(len int) {
 	}
 }
 
-// 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))
+// Slice returns a sub-Triangles of this TrianglesData.
+func (td *TrianglesData) Slice(i, j int) Triangles {
+	s := TrianglesData((*td)[i:j])
+	return &s
 }
 
-func (td *TrianglesData) updateData(offset int, t Triangles) {
+func (td *TrianglesData) updateData(t Triangles) {
 	// fast path optimization
 	if t, ok := t.(*TrianglesData); ok {
-		copy((*td)[offset:], *t)
+		copy(*td, *t)
 		return
 	}
 
 	// slow path manual copy
 	if t, ok := t.(TrianglesPosition); ok {
-		for i := offset; i < len(*td); i++ {
+		for i := range *td {
 			(*td)[i].Position = t.Position(i)
 		}
 	}
 	if t, ok := t.(TrianglesColor); ok {
-		for i := offset; i < len(*td); i++ {
+		for i := range *td {
 			(*td)[i].Color = t.Color(i)
 		}
 	}
 	if t, ok := t.(TrianglesTexture); ok {
-		for i := offset; i < len(*td); i++ {
+		for i := range *td {
 			(*td)[i].Texture = t.Texture(i)
 		}
 	}
@@ -72,19 +83,16 @@ func (td *TrianglesData) updateData(offset int, t Triangles) {
 //
 // TrianglesPosition, TrianglesColor and TrianglesTexture are supported.
 func (td *TrianglesData) Update(t Triangles) {
-	td.SetLen(t.Len())
-	td.updateData(0, t)
-}
-
-// Append adds supplied Triangles to the end of the TrianglesData.
-func (td *TrianglesData) Append(t Triangles) {
-	td.SetLen(td.Len() + t.Len())
-	td.updateData(td.Len()-t.Len(), t)
+	if td.Len() != t.Len() {
+		panic(fmt.Errorf("%T.Update: invalid triangles length", td))
+	}
+	td.updateData(t)
 }
 
 // Copy returns an exact independent copy of this TrianglesData.
 func (td *TrianglesData) Copy() Triangles {
-	copyTd := make(TrianglesData, td.Len())
+	copyTd := TrianglesData{}
+	copyTd.SetLen(td.Len())
 	copyTd.Update(td)
 	return &copyTd
 }
@@ -111,7 +119,7 @@ func (td *TrianglesData) Texture(i int) Vec {
 type TrianglesDrawer struct {
 	Triangles
 
-	tris  map[Target]Triangles
+	tris  map[Target]TargetTriangles
 	dirty bool
 }
 
@@ -129,7 +137,7 @@ func (td *TrianglesDrawer) flush() {
 // 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.tris = make(map[Target]TargetTriangles)
 	}
 
 	td.flush()
@@ -142,27 +150,8 @@ func (td *TrianglesDrawer) Draw(target Target) {
 	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)
-}
-
-// Dirty marks the underlying container as changed (dirty). If you, despite all warnings, updated
-// the underlying container in a way different from td.Update or td.Append, call Dirty and
-// everything will be fine :)
+// Dirty marks the underlying container as changed (dirty). Always call this when you change the
+// underlying Triangles (by Update, SetLen, etc.).
 func (td *TrianglesDrawer) Dirty() {
 	td.dirty = true
 }
@@ -232,7 +221,7 @@ type Polygon struct {
 // counter-clock-wise order, it doesn't matter. They should however form a convex polygon.
 func NewPolygon(c color.Color, points ...Vec) *Polygon {
 	p := &Polygon{
-		data: make(TrianglesData, len(points)),
+		data: TrianglesData{},
 	}
 	p.td = TrianglesDrawer{Triangles: &p.data}
 	p.SetColor(c)
diff --git a/interface.go b/interface.go
index 27c4f0e..3c3cb09 100644
--- a/interface.go
+++ b/interface.go
@@ -11,13 +11,13 @@ import "image/color"
 type Target interface {
 	// MakeTriangles generates a specialized copy of the provided Triangles.
 	//
-	// When calling Draw method on the returned Triangles, the Triangles will be drawn onto the
-	// target that generated them.
+	// When calling Draw method on the returned TargetTriangles, the TargetTriangles will be
+	// drawn onto the Target that generated them.
 	//
-	// Note, that not every Target has to recognize all possible types of triangles. Some may
+	// Note, that not every Target has to recognize all possible types of Triangles. Some may
 	// only recognize TrianglesPosition and TrianglesColor and ignore all other properties (if
-	// present) when making new Triangles. This varies from Target to Target.
-	MakeTriangles(Triangles) Triangles
+	// present) when making new TargetTriangles. This varies from Target to Target.
+	MakeTriangles(Triangles) TargetTriangles
 
 	// These are the most basic Target "adjustment" methods.
 	SetPicture(*Picture)
@@ -32,33 +32,39 @@ type Triangles interface {
 	// divided by 3.
 	Len() int
 
-	// Draw draws Triangles onto an associated Target (if any).
+	// SetLen resizes Triangles to len vertices. If Triangles B were obtained by calling Slice
+	// method on Triangles A, the relationship between A and B is undefined after calling SetLen
+	// on either one of them.
+	SetLen(len int)
+
+	// Slice returns a sub-Triangles of this Triangles, covering vertices in range [i, j).
 	//
-	// Note, that this method does not have to be implemented, however it is always implemented
-	// for Triangles generated by a Target.
-	Draw()
+	// If Triangles B were obtained by calling Slice(4, 9) on Triangles A, then A and B must
+	// share the same underlying data. Modifying B must change the contents of A in range
+	// [4, 9). The vertex with index 0 at B is the vertex with index 4 in A, and so on.
+	//
+	// Returned Triangles must have the same underlying type.
+	Slice(i, j int) Triangles
 
 	// Update copies vertex properties from the supplied Triangles into this Triangles.
 	//
 	// Properies not supported by these Triangles should be ignored. Properties not supported by
 	// the supplied Triangles should be left untouched.
 	//
-	// If these Triangles and supplied Triangles have different lengths, these Triangles should
-	// be resized.
+	// The two Triangles need to have the same Len.
 	Update(Triangles)
 
-	// Append adds supplied Triangles to the end of these Triangles.
-	//
-	// Behavior regarding unsupported properties should be same as with Update.
-	Append(Triangles)
-
 	// Copy creates an exact independent copy of this Triangles (with the same underlying type).
 	Copy() Triangles
 }
 
-// Drawer is something that can be drawn onto any Target.
-type Drawer interface {
-	Draw(Target)
+// TargetTriangles are Triangles generated by a Target with MakeTriangles method. They can be drawn
+// onto that Target.
+type TargetTriangles interface {
+	Triangles
+
+	// Draw draws Triangles onto an associated Target.
+	Draw()
 }
 
 // TrianglesPosition specifies Triangles with Position property.
@@ -86,3 +92,8 @@ type TrianglesTexture interface {
 	Triangles
 	Texture(i int) Vec
 }
+
+// Drawer is something that can be drawn onto any Target.
+type Drawer interface {
+	Draw(Target)
+}
diff --git a/window.go b/window.go
index fff9c48..24c2aee 100644
--- a/window.go
+++ b/window.go
@@ -369,7 +369,7 @@ func (w *Window) end() {
 // Window.
 //
 // Window supports TrianglesPosition, TrianglesColor and TrianglesTexture.
-func (w *Window) MakeTriangles(t Triangles) Triangles {
+func (w *Window) MakeTriangles(t Triangles) TargetTriangles {
 	return w.canvas.MakeTriangles(t)
 }
 
-- 
GitLab