1 /*
2  * DSFML - The Simple and Fast Multimedia Library for D
3  *
4  * Copyright (c) 2013 - 2018 Jeremy DeHaan (dehaan.jeremiah@gmail.com)
5  *
6  * This software is provided 'as-is', without any express or implied warranty.
7  * In no event will the authors be held liable for any damages arising from the
8  * use of this software.
9  *
10  * Permission is granted to anyone to use this software for any purpose,
11  * including commercial applications, and to alter it and redistribute it
12  * freely, subject to the following restrictions:
13  *
14  * 1. The origin of this software must not be misrepresented; you must not claim
15  * that you wrote the original software. If you use this software in a product,
16  * an acknowledgment in the product documentation would be appreciated but is
17  * not required.
18  *
19  * 2. Altered source versions must be plainly marked as such, and must not be
20  * misrepresented as being the original software.
21  *
22  * 3. This notice may not be removed or altered from any source distribution
23  *
24  *
25  * DSFML is based on SFML (Copyright Laurent Gomila)
26  */
27 
28 /**
29  * $(U VertexArray) is a very simple wrapper around a dynamic array of vertices
30  *and a primitives type.
31  *
32  * It inherits $(DRAWABLE_LINK), but unlike other drawables it is not
33  * transformable.
34  *
35  * Example:
36  * ---
37  * VertexArray lines(PrimitiveType.LineStrip, 4);
38  * lines[0].position = Vector2f(10, 0);
39  * lines[1].position = Vector2f(20, 0);
40  * lines[2].position = Vector2f(30, 5);
41  * lines[3].position = Vector2f(40, 2);
42  *
43  * window.draw(lines);
44  * ---
45  *
46  * See_Also:
47  * $(VERTEX_LINK)
48  */
49 module dsfml.graphics.vertexarray;
50 
51 import dsfml.graphics.vertex;
52 import dsfml.graphics.primitivetype;
53 import dsfml.graphics.rect;
54 import dsfml.graphics.drawable;
55 import dsfml.graphics.rendertarget;
56 import dsfml.graphics.renderstates;
57 
58 import dsfml.system.vector2;
59 
60 /**
61  * Define a set of one or more 2D primitives.
62  */
63 class VertexArray : Drawable
64 {
65     /**
66      * The type of primitive to draw.
67      *
68      * Can be any of the following:
69      * - Points
70      * - Lines
71      * - Triangles
72      * - Quads
73      *
74      * The default primitive type is Points.
75      */
76     PrimitiveType primitiveType;
77     private Vertex[] Vertices;
78 
79     /**
80      * Default constructor
81      *
82      * Creates an empty vertex array.
83      */
84     this()
85     {
86     }
87 
88     /**
89      * Construct the vertex array with a type and an initial number of vertices
90      *
91      * Params:
92      *  type        = Type of primitives
93      *  vertexCount = Initial number of vertices in the array
94      */
95     this(PrimitiveType type, uint vertexCount = 0)
96     {
97         primitiveType = type;
98         Vertices = new Vertex[vertexCount];
99     }
100 
101     private this(PrimitiveType type, Vertex[] vertices)
102     {
103         primitiveType = type;
104         Vertices = vertices;
105     }
106 
107     /// Destructor.
108     ~this()
109     {
110         import dsfml.system.config;
111         mixin(destructorOutput);
112     }
113 
114     /**
115      * Compute the bounding rectangle of the vertex array.
116      *
117      * This function returns the axis-aligned rectangle that contains all the
118      * vertices of the array.
119      *
120      * Returns: Bounding rectangle of the vertex array.
121      */
122     FloatRect getBounds() const
123     {
124         if (Vertices.length>0)
125         {
126             float left = Vertices[0].position.x;
127             float top = Vertices[0].position.y;
128             float right = Vertices[0].position.x;
129             float bottom = Vertices[0].position.y;
130 
131             for (size_t i = 1; i < Vertices.length; ++i)
132             {
133                 Vector2f position = Vertices[i].position;
134 
135                 // Update left and right
136                 if (position.x < left)
137                     left = position.x;
138                 else if (position.x > right)
139                     right = position.x;
140 
141                 // Update top and bottom
142                 if (position.y < top)
143                     top = position.y;
144                 else if (position.y > bottom)
145                     bottom = position.y;
146             }
147 
148             return FloatRect(left, top, right - left, bottom - top);
149         }
150         else
151         {
152             return FloatRect(0,0,0,0);
153         }
154     }
155 
156     /**
157      * Return the vertex count.
158      *
159      * Returns: Number of vertices in the array
160      */
161     uint getVertexCount() const
162     {
163         import std.algorithm;
164         return cast(uint)min(uint.max, Vertices.length);
165     }
166 
167     /**
168      * Add a vertex to the array.
169      *
170      * Params:
171      * 		newVertex = Vertex to add.
172      */
173     void append(Vertex newVertex)
174     {
175         Vertices ~= newVertex;
176     }
177 
178     /**
179      * Clear the vertex array.
180      *
181      * This function removes all the vertices from the array. It doesn't
182      * deallocate the corresponding memory, so that adding new vertices after
183      * clearing doesn't involve reallocating all the memory.
184      */
185     void clear()
186     {
187         Vertices.length = 0;
188     }
189 
190     /**
191      * Draw the object to a render target.
192      *
193      * Params:
194      *  	renderTarget = Render target to draw to
195      *  	renderStates = Current render states
196      */
197     override void draw(RenderTarget renderTarget, RenderStates renderStates)
198     {
199         if(Vertices.length != 0)
200         {
201             renderTarget.draw(Vertices, primitiveType,renderStates);
202         }
203     }
204 
205     /**
206      * Resize the vertex array.
207      *
208      * If vertexCount is greater than the current size, the previous vertices
209      * are kept and new (default-constructed) vertices are added. If vertexCount
210      * is less than the current size, existing vertices are removed from the
211      * array.
212      *
213      * Params:
214      * 		vertexCount	= New size of the array (number of vertices).
215      */
216     void resize(uint vertexCount)
217     {
218         Vertices.length = vertexCount;
219     }
220 
221     /**
222      * Get a read-write access to a vertex by its index
223      *
224      * This function doesn't check index, it must be in range
225      * [0, getVertexCount() - 1]. The behavior is undefined otherwise.
226      *
227      * Params:
228      *  index = Index of the vertex to get
229      *
230      * Returns: Reference to the index-th vertex.
231      */
232     ref Vertex opIndex(size_t index)
233     {
234         return Vertices[index];
235     }
236 
237     //TODO: const ref Vertex opIndex(size_t) const, perhaps?
238 }
239 
240 unittest
241 {
242     version(DSFML_Unittest_Graphics)
243     {
244         import std.stdio;
245         import dsfml.graphics.texture;
246         import dsfml.graphics.rendertexture;
247         import dsfml.graphics.color;
248 
249         writeln("Unit test for VertexArray");
250 
251         auto texture = new Texture();
252 
253         assert(texture.loadFromFile("res/TestImage.png"));
254 
255         auto dimensions = FloatRect(0,0,texture.getSize().x,texture.getSize().y);
256 
257         auto vertexArray = new VertexArray(PrimitiveType.Quads, 0);
258 
259         //Creates a vertex array at position (0,0) the width and height of the loaded texture
260         vertexArray.append(Vertex(Vector2f(dimensions.left,dimensions.top), Color.Blue, Vector2f(dimensions.left,dimensions.top)));
261         vertexArray.append(Vertex(Vector2f(dimensions.left,dimensions.height), Color.Blue, Vector2f(dimensions.left,dimensions.height)));
262         vertexArray.append(Vertex(Vector2f(dimensions.width,dimensions.height), Color.Blue, Vector2f(dimensions.width,dimensions.height)));
263         vertexArray.append(Vertex(Vector2f(dimensions.width,dimensions.top), Color.Blue, Vector2f(dimensions.width,dimensions.top)));
264 
265         auto renderStates = RenderStates(texture);
266 
267         auto renderTexture = new RenderTexture();
268 
269         renderTexture.create(100,100);
270 
271         renderTexture.clear();
272 
273         //draw the VertexArray with the texture we loaded
274         renderTexture.draw(vertexArray, renderStates);
275 
276         renderTexture.display();
277 
278         writeln();
279     }
280 }