1 /**
2 * Module for graphics.
3 *
4 * Authors:
5 *   Jacob Jensen
6 * License:
7 *   https://github.com/PoisonEngine/poison-ui/blob/master/LICENSE
8 */
9 module poison.ui.graphics;
10 
11 import dsfml.graphics : RectangleShape;
12 import dsfml.system : Vector2f;
13 
14 import poison.ui.paint;
15 import poison.core : Size, Point;
16 import poison.ui.picture;
17 import poison.ui.fonts;
18 
19 /// Original space struct for graphical elements.
20 private struct OriginalSpace(TPosition,TSize) {
21   /// The original x position.
22   TPosition x;
23 
24   /// The original y position.
25   TPosition y;
26 
27   /// The original width.
28   TSize width;
29 
30   /// The original height.
31   TSize height;
32 }
33 
34 /// A sub rect of overflow calculation results.
35 private struct SubRect {
36   /// The x coordinate.
37   float x;
38 
39   /// The y coordinate.
40   float y;
41 
42   /// The width.
43   float width;
44 
45   /// The height.
46   float height;
47 
48   /// The overflow top.
49   float overflowTop;
50 
51   /// The overflow right.
52   float overflowRight;
53 
54   /// The overflow bottom.
55   float overflowBottom;
56 
57   /// The overflow left.
58   float overflowLeft;
59 }
60 
61 /// Graphics wrapper for components.
62 class Graphics {
63   private:
64   /// The background paint.
65   Paint _backgroundPaint;
66 
67   /// The foreground paint.
68   Paint _foregroundPaint;
69 
70   /// The low-level background rectangle.
71   RectangleShape _backgroundRect;
72 
73   /// The original background rectangle's space.
74   OriginalSpace!(float,float) _originalBackgroundRect;
75 
76   /// The background picture.
77   Picture _backgroundPicture;
78 
79   /// The original background ´picture's space.
80   OriginalSpace!(ptrdiff_t,int) _originalBackgroundPicture;
81 
82   /// The size.
83   Size _size;
84 
85   /// The position.
86   Point _position;
87 
88   /// The font.
89   Font _font;
90 
91   /// The font size.
92   uint _fontSize;
93 
94   /// Boolean determining whether the background rectangle is displayable or not.
95   bool _displayableBackgroundRect;
96 
97   /// Boolean determining whether the background picture is displayable or not.
98   bool _displayableBackgroundPicture;
99 
100   /// Boolean determining whether the graphics has a size.
101   bool _hasSize;
102 
103   /// Boolean determining whether the graphics has a position.
104   bool _hasPosition;
105 
106   public:
107   final:
108   /// Creates a new graphics wrapper.
109   this() {
110     _backgroundPaint = transparent;
111     _foregroundPaint = transparent;
112 
113     _size = new Size(0,0);
114     _position = new Point(0,0);
115 
116     _fontSize = 13;
117     _displayableBackgroundRect = true;
118     _displayableBackgroundPicture = true;
119     _originalBackgroundRect = OriginalSpace!(float,float)(0,0,0,0);
120     _originalBackgroundPicture = OriginalSpace!(ptrdiff_t, int)(0,0,0,0);
121   }
122 
123   @property {
124     /// Gets the background paint.
125     Paint backgroundPaint() { return _backgroundPaint; }
126 
127     /// Sets the background paint.
128     void backgroundPaint(Paint newPaint) {
129       _backgroundPaint = newPaint;
130 
131       if (_backgroundRect) {
132         _backgroundRect.fillColor = _backgroundPaint.sfmlColor;
133       }
134     }
135 
136     /// Gets the foreground paint.
137     Paint foregroundPaint() { return _foregroundPaint; }
138 
139     /// Sets the foreground paint.
140     void foregroundPaint(Paint newPaint) {
141       _foregroundPaint = newPaint;
142     }
143 
144     /// Gets the low-level background rectangle.
145     RectangleShape backgroundRect() { return _backgroundRect; }
146 
147     /// Gets the background picture.
148     Picture backgroundPicture() { return _backgroundPicture; }
149 
150     /// Sets the background picture.
151     void backgroundPicture(Picture newBackgroundPicture) {
152       _backgroundPicture = newBackgroundPicture;
153 
154       if (_backgroundPicture) {
155         _backgroundPicture.position = _position;
156 
157         _originalBackgroundPicture.x = _position.x;
158         _originalBackgroundPicture.y = _position.y;
159 
160         _originalBackgroundPicture.width = _size.width;
161         _originalBackgroundPicture.height = _size.height;
162       }
163     }
164 
165     /// Gets the font.
166     Font font() { return _font; }
167 
168     /// Sets the font.
169     void font(Font newFont) {
170       _font = newFont;
171     }
172 
173     /// Gets the font size.
174     uint fontSize() { return _fontSize; }
175 
176     /// Sets the font size.
177     void fontSize(uint newFontSize) {
178       _fontSize = newFontSize;
179     }
180   }
181 
182   package(poison):
183   @property {
184     /// Sets the position of the graphics.
185     void position(Point newPosition) {
186       _position = newPosition;
187 
188       if (_backgroundRect) {
189         _backgroundRect.position = Vector2f(cast(float)_position.x, cast(float)_position.y);
190         _originalBackgroundRect.x = _backgroundRect.position.x;
191         _originalBackgroundRect.y = _backgroundRect.position.y;
192       }
193 
194       if (_backgroundPicture) {
195         _backgroundPicture.position = _position;
196         _originalBackgroundPicture.x = cast(int)_position.x;
197         _originalBackgroundPicture.y = cast(int)_position.y;
198       }
199     }
200 
201     /// Sets the size of the graphics.
202     void size(Size newSize) {
203       _size = newSize;
204 
205       _backgroundRect = new RectangleShape(Vector2f(cast(float)_size.width, cast(float)_size.height));
206       backgroundPaint = _backgroundPaint;
207       position = _position;
208 
209       _originalBackgroundRect.width = _backgroundRect.size.x;
210       _originalBackgroundRect.height = _backgroundRect.size.y;
211 
212       _originalBackgroundPicture.width = cast(int)_size.width;
213       _originalBackgroundPicture.height = cast(int)_size.height;
214     }
215   }
216 
217   package(poison):
218   @property {
219     /// Gets a boolean determining whether the background rectangle is displayable.
220     bool displayableBackgroundRect() { return _displayableBackgroundRect; }
221 
222     /// Gets a boolean determining whether the background picture is displayable.
223     bool displayableBackgroundPicture() { return _displayableBackgroundPicture; }
224 
225     /// Gets the size of the graphics. Use hasSize before using this.
226     Size size() { return _size; }
227 
228     /// Gets the position of the graphics. Use hasPosition before using this.
229     Point position() { return _position; }
230 
231     /// Gets a boolean determining whether the graphics has a size or not.
232     bool hasSize() { return _hasSize; }
233 
234     /// Sets a boolean determining whether the graphics has a size or not.
235     void hasSize(bool setHasSize) {
236       _hasSize = setHasSize;
237     }
238 
239     /// Gets a boolean determining whether the graphics has a position or not.
240     bool hasPosition() { return _hasPosition; }
241 
242     /// Sets a boolean determining whether the graphics has a position or not.
243     void hasPosition(bool setHasPosition) {
244       _hasPosition = setHasPosition;
245     }
246   }
247 
248   /**
249   * Finalizes paint inputs for the graphics.
250   * Params:
251   *   position =  The position of the paint input.
252   *   size =      The size of the paint input.
253   *   paint =     The paint.
254   */
255   void finalizePaint(Point position, Size size, Paint paint) {
256     if (!_backgroundPicture) {
257       _backgroundPicture = new Picture(size, transparent);
258     }
259 
260     _backgroundPicture.draw(position, size, paint);
261   }
262 
263   /**
264   * Finalizes vertical gradient inputs for the graphics.
265   * Params:
266   *   position =  The position of the graident.
267   *   size =      The size of the gradient.
268   *   fromPaint = The paint to draw the gradient from.
269   *   toPaint =   The paint to draw the gradient to.
270   */
271   void finalizeGradientVertical(Point position, Size size, Paint fromPaint, Paint toPaint) {
272     if (!_backgroundPicture) {
273       _backgroundPicture = new Picture(size, transparent);
274     }
275 
276     _backgroundPicture.gradientVertical(position, size, fromPaint, toPaint);
277   }
278 
279   /**
280   * Finalizes horizontal gradient inputs for the graphics.
281   * Params:
282   *   position =  The position of the graident.
283   *   size =      The size of the gradient.
284   *   fromPaint = The paint to draw the gradient from.
285   *   toPaint =   The paint to draw the gradient to.
286   */
287   void finalizeGradientHorizontal(Point position, Size size, Paint fromPaint, Paint toPaint) {
288     if (!_backgroundPicture) {
289       _backgroundPicture = new Picture(size, transparent);
290     }
291 
292     _backgroundPicture.gradientHorizontal(position, size, fromPaint, toPaint);
293   }
294 
295   /**
296   * Renders the sub rectangles for the graphics elements.
297   * Params:
298   *   position =  The position to be relative to.
299   *   size =      The size to be relative to.
300   */
301   void renderSub(Point position, Size size) {
302     import dsfml.graphics : IntRect;
303     import dsfml.system : Vector2f;
304 
305     SubRect calculateOverflow(float targetX, float targetY, float targetWidth, float targetHeight) {
306       import std.math : fmax;
307 
308       float overflowTop = fmax(0, (cast(float)position.y - targetY));
309       float overflowRight = fmax(0, (targetX + targetWidth) - (cast(float)position.x + cast(float)size.width));
310       float overflowBottom = fmax(0, (targetY + targetHeight) - (cast(float)position.y + cast(float)size.height));
311       float overflowLeft = fmax(0, (cast(float)position.x - targetX));
312 
313       targetY += overflowTop;
314       targetHeight -= overflowTop;
315       targetWidth -= overflowRight;
316       targetHeight -= overflowBottom;
317       targetX += overflowLeft;
318       targetWidth -= overflowLeft;
319 
320       return SubRect(targetX, targetY, targetWidth, targetHeight, overflowTop, overflowRight, overflowBottom, overflowLeft);
321     }
322 
323     bool intersect(float width, float height, float x, float y) {
324       return(x < cast(float)position.x + cast(float)size.width) &&
325         (cast(float)position.x < (x + width)) &&
326         (y < cast(float)position.y + cast(float)size.height) &&
327         (cast(float)position.y < y + height);
328      }
329 
330     if (_backgroundRect) {
331       _backgroundRect.size = Vector2f(_originalBackgroundRect.width, _originalBackgroundRect.height);
332       _backgroundRect.position = Vector2f(_originalBackgroundRect.x, _originalBackgroundRect.y);
333 
334       auto rectSize = _backgroundRect.size;
335       auto rectPosition = _backgroundRect.position;
336 
337       _displayableBackgroundRect = intersect(rectSize.x, rectSize.y, rectPosition.x, rectPosition.y);
338 
339       if (_displayableBackgroundRect) {
340         auto overflow = calculateOverflow(rectPosition.x, rectPosition.y, rectSize.x, rectSize.y);
341 
342         _backgroundRect.size = Vector2f(overflow.width, overflow.height);
343         _backgroundRect.position = Vector2f(overflow.x, overflow.y);
344       }
345     }
346 
347     if (_backgroundPicture) {
348       _displayableBackgroundPicture = intersect(cast(float)_originalBackgroundPicture.x, cast(float)_originalBackgroundPicture.y, cast(float)_originalBackgroundPicture.width, cast(float)_originalBackgroundPicture.height);
349 
350       if (_displayableBackgroundPicture) {
351         auto backgroundSprite = _backgroundPicture.backgroundSprite;
352         auto drawingSprite = _backgroundPicture.drawingSprite;
353 
354         if (backgroundSprite) {
355           backgroundSprite.position = new Point(_originalBackgroundPicture.x, _originalBackgroundPicture.y);
356           backgroundSprite.textureRect = IntRect(0, 0, cast(int)_originalBackgroundPicture.width, cast(int)_originalBackgroundPicture.height);
357         }
358 
359         if (drawingSprite) {
360           drawingSprite.position = new Point(_originalBackgroundPicture.x, _originalBackgroundPicture.y);
361           drawingSprite.textureRect = IntRect(0, 0, cast(int)_originalBackgroundPicture.width, cast(int)_originalBackgroundPicture.height);
362         }
363 
364         auto overflow = calculateOverflow(cast(float)_originalBackgroundPicture.x, cast(float)_originalBackgroundPicture.y, cast(float)_originalBackgroundPicture.width, cast(float)_originalBackgroundPicture.height);
365 
366         if (backgroundSprite) {
367           backgroundSprite.textureRect = IntRect(cast(int)overflow.overflowLeft, cast(int)overflow.overflowTop, cast(int)overflow.width, cast(int)overflow.height);
368           backgroundSprite.position = new Point(cast(ptrdiff_t)overflow.x, cast(ptrdiff_t)overflow.y);
369         }
370 
371         if (drawingSprite) {
372           drawingSprite.textureRect = IntRect(cast(int)overflow.overflowLeft, cast(int)overflow.overflowTop, cast(int)overflow.width, cast(int)overflow.height);
373           drawingSprite.position = new Point(cast(ptrdiff_t)overflow.x, cast(ptrdiff_t)overflow.y);
374         }
375       }
376     }
377   }
378 }