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 nudsfml.graphics.vertexarray;
50 
51 import nudsfml.graphics.vertex;
52 import nudsfml.graphics.primitivetype;
53 import nudsfml.graphics.rect;
54 import nudsfml.graphics.drawable;
55 import nudsfml.graphics.rendertarget;
56 import nudsfml.graphics.renderstates;
57 
58 import nudsfml.system.vector2;
59 
60 /**
61  * Define a set of one or more 2D primitives.
62  */
63 class VertexArray : Drawable {
64     /**
65      * The type of primitive to draw.
66      *
67      * Can be any of the following:
68      * - Points
69      * - Lines
70      * - Triangles
71      * - Quads
72      *
73      * The default primitive type is Points.
74      */
75     PrimitiveType primitiveType;
76     private Vertex[] Vertices;
77 
78     /**
79      * Default constructor
80      *
81      * Creates an empty vertex array.
82      */
83     this() {
84     }
85 
86     /**
87      * Construct the vertex array with a type and an initial number of vertices
88      *
89      * Params:
90      *  type        = Type of primitives
91      *  vertexCount = Initial number of vertices in the array
92      */
93     this(PrimitiveType type, uint vertexCount = 0) {
94         primitiveType = type;
95         Vertices = new Vertex[vertexCount];
96     }
97 
98     private this(PrimitiveType type, Vertex[] vertices) {
99         primitiveType = type;
100         Vertices = vertices;
101     }
102 
103     /// Destructor.
104     ~this() {
105         import nudsfml.system.config;
106         mixin(destructorOutput);
107     }
108 
109     /**
110      * Compute the bounding rectangle of the vertex array.
111      *
112      * This function returns the axis-aligned rectangle that contains all the
113      * vertices of the array.
114      *
115      * Returns: Bounding rectangle of the vertex array.
116      */
117     FloatRect getBounds() const {
118         if (Vertices.length>0) {
119             float left = Vertices[0].position.x;
120             float top = Vertices[0].position.y;
121             float right = Vertices[0].position.x;
122             float bottom = Vertices[0].position.y;
123 
124             for (size_t i = 1; i < Vertices.length; ++i) {
125                 Vector2f position = Vertices[i].position;
126 
127                 // Update left and right
128                 if (position.x < left)
129                     left = position.x;
130                 if (position.x > right)
131                     right = position.x;
132 
133                 // Update top and bottom
134                 if (position.y <= top)
135                     top = position.y;
136                 if (position.y > bottom)
137                     bottom = position.y;
138             }
139             return FloatRect(left, top, right - left, bottom - top);
140         } else {
141             return FloatRect(0,0,0,0);
142         }
143     }
144 
145     /**
146      * Return the vertex count.
147      *
148      * Returns: Number of vertices in the array
149      */
150     uint getVertexCount() const {
151         import std.algorithm;
152         return cast(uint)min(uint.max, Vertices.length);
153     }
154 
155     /**
156      * Add a vertex to the array.
157      *
158      * Params:
159      * 		newVertex = Vertex to add.
160      */
161     void append(Vertex newVertex) {
162         Vertices ~= newVertex;
163     }
164 
165     /**
166      * Clear the vertex array.
167      *
168      * This function removes all the vertices from the array. It doesn't
169      * deallocate the corresponding memory, so that adding new vertices after
170      * clearing doesn't involve reallocating all the memory.
171      */
172     void clear() {
173         Vertices.length = 0;
174     }
175 
176     /**
177      * Draw the object to a render target.
178      *
179      * Params:
180      *  	renderTarget = Render target to draw to
181      *  	renderStates = Current render states
182      */
183     override void draw(RenderTarget renderTarget, RenderStates renderStates) {
184         if(Vertices.length != 0) {
185             renderTarget.draw(Vertices, primitiveType,renderStates);
186         }
187     }
188 
189     /**
190      * Resize the vertex array.
191      *
192      * If vertexCount is greater than the current size, the previous vertices
193      * are kept and new (default-constructed) vertices are added. If vertexCount
194      * is less than the current size, existing vertices are removed from the
195      * array.
196      *
197      * Params:
198      * 		vertexCount	= New size of the array (number of vertices).
199      */
200     void resize(uint vertexCount) {
201         Vertices.length = vertexCount;
202     }
203 
204     /**
205      * Get a read-write access to a vertex by its index
206      *
207      * This function doesn't check index, it must be in range
208      * [0, getVertexCount() - 1]. The behavior is undefined otherwise.
209      *
210      * Params:
211      *  index = Index of the vertex to get
212      *
213      * Returns: Reference to the index-th vertex.
214      */
215     ref Vertex opIndex(size_t index) {
216         return Vertices[index];
217     }
218 
219     //TODO: const ref Vertex opIndex(size_t) const, perhaps?
220 }
221 
222 unittest
223 {
224     version(DSFML_Unittest_Graphics)
225     {
226         import std.stdio;
227         import nudsfml.graphics.texture;
228         import nudsfml.graphics.rendertexture;
229         import nudsfml.graphics.color;
230 
231         writeln("Unit test for VertexArray");
232 
233         auto texture = new Texture();
234 
235         assert(texture.loadFromFile("data/lain.png"));
236 
237         auto dimensions = FloatRect(0,0,texture.getSize().x,texture.getSize().y);
238 
239         auto vertexArray = new VertexArray(PrimitiveType.Quads, 0);
240 
241         //Creates a vertex array at position (0,0) the width and height of the loaded texture
242         vertexArray.append(Vertex(Vector2f(dimensions.left,dimensions.top), Color.Blue, Vector2f(dimensions.left,dimensions.top)));
243         vertexArray.append(Vertex(Vector2f(dimensions.left,dimensions.height), Color.Blue, Vector2f(dimensions.left,dimensions.height)));
244         vertexArray.append(Vertex(Vector2f(dimensions.width,dimensions.height), Color.Blue, Vector2f(dimensions.width,dimensions.height)));
245         vertexArray.append(Vertex(Vector2f(dimensions.width,dimensions.top), Color.Blue, Vector2f(dimensions.width,dimensions.top)));
246 
247         auto renderStates = RenderStates(texture);
248 
249         auto renderTexture = new RenderTexture();
250 
251         renderTexture.create(100,100);
252 
253         renderTexture.clear();
254 
255         //draw the VertexArray with the texture we loaded
256         renderTexture.draw(vertexArray, renderStates);
257 
258         renderTexture.display();
259 
260         writeln();
261     }
262 }