root/trunk/luigi/base.d

Revision 47, 13.9 kB (checked in by baxissimo, 2 years ago)

Just some minor tweaks and convenience additions.

Line 
1 //---------------------------------------------------------------------
2 /*
3  Copyright:
4
5   luigi/base.d -- basic definitions for the 'luigi' user interface library.
6
7   Copyright (C) 2006 William V. Baxter III
8
9   This software is provided 'as-is', without any express or implied
10   warranty.  In no event will the authors be held liable for any
11   damages arising from the use of this software.
12
13   Permission is granted to anyone to use this software for any
14   purpose, including commercial applications, and to alter it and
15   redistribute it freely, subject to the following restrictions:
16
17   1. The origin of this software must not be misrepresented; you must
18      not claim that you wrote the original software. If you use this
19      software in a product, an acknowledgment in the product
20      documentation would be appreciated but is not required.
21
22   2. Altered source versions must be plainly marked as such, and must
23      not be misrepresented as being the original software.
24   3. This notice may not be removed or altered from any source distribution.
25
26   William Baxter wbaxter@gmail.com
27 */
28 module luigi.base;
29
30 import math = std.math;
31 import string = std.string;
32
33 //import std.math : lrint;  // Error: "not implemented"!!
34
35 // Until std.math actually implements it
36 long lrint(float v) {
37     return cast(long)math.floor(v+0.5);
38 }
39
40
41 typedef Exception GUIException;
42
43
44
45 /** Rectangle class.  Size and position are stored in floating point.
46  */
47 struct Rect
48 {
49     // This layout lets you use rect either like
50     //    (r.x, r.y, r.width, r.height) // primary
51     // or like
52     //    (r.x1, r.y1, r.x2, r.y2)  // also allowed with restrictions
53     // or like
54     //    (r.pos, r.size);
55     union {
56         struct { float x,y; }
57         Point pos;
58     }
59     union {
60         struct { float width ,height; }
61         Size size;
62     }
63
64     alias x x1;
65     alias y y1;
66     alias width w;
67     alias height h;
68
69     float x2() { return x+w; }
70     float y2() { return y+h; }
71     float x2(float v) { return w=(v-x); }
72     float y2(float v) { return h=(v-y); }
73
74     Point center() { return Point(x+width/2,y+height/2); }
75     Point minpos() { return Point(x1,y1); }
76     Point maxpos() { return Point(x2,y2); }
77
78     /** Rect constructor */
79     static Rect opCall(float rx = 0, float ry = 0, float rwidth = 0, float rheight = 0) {
80         Rect r;  r.x = rx; r.y = ry; r.w = rwidth; r.h = rheight;
81         return r;
82     }
83     /** Set value using left,top,width,height convention */
84     void  set(float xpos, float ypos, float rwidth, float rheight) {
85         x = xpos; y = ypos; w = rwidth; h = rheight;
86     }
87     /** Set value using left,top,right,bottom convention */
88     void  setLTRB(float leftx, float topy, float rightx, float bottomy) {
89         set(leftx,topy,rightx-leftx,bottomy-topy);
90     }
91     alias setLTRB setMinMax;
92
93     /** Point-inside-rect test */
94     bool contains(float px, float py) {
95         return ( x <= px && px < x2 && y <= py && py < y2 );
96     }
97     bool contains(Point p) {
98         return contains(p.x,p.y);
99     }
100
101     void inset(float v) {
102         if (2*v > w) { x += w/2; w = 0; }
103         else    {         x+=v; w-=2*v; }
104         if (2*v > h) { y += h/2; h = 0; }
105         else {            y+=v; h-=2*v; }
106     }
107     void inflate(float v) {
108         inset(-v);
109     }
110
111     /** Intersection of two rects. */
112     //void intersect(Rect o) {
113     //    // not implemented
114     //}
115
116     /** Grow rect to enclose other rect (union of rects.) */
117     void enclose(Rect o) {
118         enclose(o.pos);
119         enclose(Point(o.x2,o.y2));
120     }
121     /** Grow rect to enclose point p. */
122     void enclose(Point p) {
123         if (!valid) {
124             x = p.x; y=p.y;
125             w = 0; h = 0;
126         }
127         if (p.x < x) {  w = x2-p.x;  x = p.x; }
128         else if (p.x > x2) {  x2 = p.x; }
129         if (p.y < y) {  h = y2-p.y;  y = p.y; }
130         else if (p.y > y2) {  y2 = p.y; }
131     }
132
133
134     bool valid() {
135         return !(math.isnan(x)||math.isnan(y)||
136                  math.isnan(w)||math.isnan(h));
137     }
138
139     char[] toString() {
140         return string.format("Rect(%0.1f,%0.1f,%0.1f,%0.1f)",x,y,width,height);
141     }
142 }
143
144 /** Size is used to describe the width and height of a box.
145     Width is given by size.width, and height by size.height
146     Several synonyms are provided for the .w and .h properties.
147     .width is equivalent to .w or .x.
148     .height is equivalent to .h or .y.
149  */
150 struct Size
151 {
152     float width = 0;
153     float height = 0;
154     alias width x;
155     alias height y;
156     alias width w;
157     alias height h;
158
159     /** Constructor */
160     static Size opCall(float _w, float _h) {
161         Size s; s.width = _w;  s.height = _h; return s;
162     }
163
164     /** Allows Sizes to be added to using s1 += s2. */
165     void opAddAssign(Size o) { w+=o.w; h+=o.h; }
166
167     /** Allows Sizes to be subtracted from using s1 += s2. */
168     void opSubAssign(Size o) { w-=o.w; h-=o.h; }
169
170     /** Allows Sizes to be multiplied by a scalar */
171     void opMulAssign(float d) { w*=d; h*=d; }
172
173     /** Return a printable representation of the Size */
174     char[] toString() {
175         return string.format("(%0.1f,%0.1f)",w,h);
176     }
177
178     /** Return a pointer to the first element */
179     float *ptr() { return &x; }
180 }
181
182 /** Point can be used as an alias for Size. 
183     This is not a typedef so there is no compiler enforcement of the distinction,
184     but you can use Point vs Size as the situation dictates to indicate the
185     intent of the variable, be it a location or a displacement.
186  */
187 alias Size Point;
188
189
190 /** Color represents a standard 4-byte RGBA color value.
191     It is the type used to represent colors througout Luigi.
192     Color components can be accessed via .r, .g, .b, and .a properties.
193     Alternatively, one can use numerical indexing, e.g. c[0],c[1],c[2],c[3].
194  */
195 struct Color
196 {
197     union {
198         struct {
199             ubyte r=0,g=0,b=0,a=255;
200         }
201         ubyte[4] v;
202     }
203
204     /** Constructor using integral values in the range 0--255 */
205     static Color opCall(int r_, int g_, int b_, int a_=255) {
206         Color c; c.set(r_,g_,b_,a_); return c;
207     }
208     /** Constructor using floating point  values in the range 0.0--1.0 */
209     static Color opCall(float r_, float g_, float b_, float a_=1.0) {
210         Color c; c.set(r_,g_,b_,a_); return c;
211     }
212
213     /** Setter using integral values in the range 0--255 */
214     void set(int r_, int g_, int b_, int a_=255) {
215         r=r_, g=g_, b=b_, a=a_;
216     }
217
218     /** Setter using floating point  values in the range 0.0--1.0 */
219     void set(float r_, float g_, float b_, float a_=1.0) {
220         r=lrint(r_*255), g=lrint(g_*255), b=lrint(b_*255), a=lrint(a_*255);
221     }
222
223     /** Linearly interpolate between the two colors.
224         Params:
225             c1 = the first color
226             c2 = the second color
227             t  = the interpolation parameter between 0 and 1
228         Returns: the blended color
229                  If t==0 then the result is c1
230                  If t==1 then the result is c2
231     */
232     static Color lerp(Color c1, Color c2, float t) {
233         Color ret;
234         ret.r = lrint( (1-t)*c1.r + t*c2.r );
235         ret.g = lrint( (1-t)*c1.g + t*c2.g );
236         ret.b = lrint( (1-t)*c1.b + t*c2.b );
237         ret.a = lrint( (1-t)*c1.a + t*c2.a );
238         return ret;
239     }
240
241     /** Allows a color to be indexed with brackets [].
242         R,G,B,A components correspond to indexes 0,1,2,3.
243      */
244     ubyte opIndex(int i) { return v[i]; }
245     ubyte opIndexAssign(int i, ubyte val) { return v[i]=val; }
246
247     /** Return a pointer to the beginning of the 4 bytes that
248      *  make up this color.  Bytes are ordered R,G,B,A.
249      */
250     ubyte* ptr() { return &r; }
251 }
252
253
254 //----------------------------------------------------------------------------
255
256 /**
257  * Returns the largest of the two supplied types, or the first type if
258  * the sizes are identical.  If either type is an object then both must
259  * be objects of either the same type or where one is a base class of
260  * the other.  Interfaces are not supported.  Used by min() and max() templates.
261  */
262 template largestOrConvertible( T, U )
263 {
264     static if( is( T : Object ) || is( U : Object ) )
265     {
266         static assert( is( T : Object ) && is( U : Object ),
267                        "Types incompatible." );
268
269         static if( is(T : U) )
270             alias T largestOrConvertible;
271         else static if( is(U : T) )
272             alias U largestOrConvertible;
273         else static assert( false, "Object types must be related." );
274     }
275     else static if( is( T : Interface ) || is( U : Interface ) )
276         static assert( false, "Interface types not yet supported." );
277     else static if( T.sizeof < U.sizeof )
278         alias U largestOf;  // concrete type, U larger
279     else alias T largestOf; // concrete type, T larger or equal
280 }
281
282 /** Returns the maximum of two values */
283 template max( T, U )
284 {
285     largestOrConvertible!(T,U).largestOf max( T t, U u )
286     {
287         return t > u ? t : u;
288     }
289 }
290
291 /** Returns the minimum of two values */
292 template min( T, U )
293 {
294     largestOrConvertible!(T,U).largestOf min( T t, U u )
295     {
296         return t < u ? t : u;
297     }
298 } 
299 unittest {
300     int ismall=1, ibig=5;
301     byte bsmall=1, bbig=5;
302     float fsmall=1, fbig=5;
303     double dsmall=1, dbig=5;
304     assert(max(ibig,ismall)==ibig);
305     assert(max(ismall,ibig)==ibig);
306     assert(min(ibig,ismall)==ismall);
307     assert(min(ismall,ibig)==ismall);
308
309     assert(max(fbig,fsmall)==fbig);
310     assert(max(fsmall,fbig)==fbig);
311     assert(min(fbig,fsmall)==fsmall);
312     assert(min(fsmall,fbig)==fsmall);
313
314     assert(min(dsmall,fbig)==dsmall);
315     assert(max(dsmall,fbig)==fbig);
316     assert(min(dbig,fsmall)==fsmall);
317     assert(max(dbig,fsmall)==dbig);
318
319     assert( is(typeof(min(dsmall,fbig)) == double) );
320     assert( is(typeof(max(dsmall,fbig)) == double) );
321     assert( is(typeof(min(dbig,fsmall)) == double) );
322     assert( is(typeof(max(dbig,fsmall)) == double) );
323
324     assert( is(typeof(min(bsmall,ibig))==int) );
325     assert( is(typeof(max(bsmall,ibig))==int) );
326     assert( is(typeof(min(bbig,ismall))==int) );
327     assert( is(typeof(max(bbig,ismall))==int) );
328 }
329
330
331
332 //----------------------------------------------------------------------------
333
334
335 /* Some simple array manipulators that should be in the standard library but aren't
336  * Adapted mostly from Cashew.utils
337  */
338
339 const size_t NOT_FOUND = size_t.max ;
340
341
342 /**
343  *  Remove an item from an array by index, and return it.
344  */
345 T drop (T) (inout T[] haystack, size_t index)
346 in {
347   assert(index < haystack.length, ".drop() called with index greater than array length");
348 }
349 body {
350   T      result = haystack[index]     ;
351   size_t max    = haystack.length - 1 ;
352
353   for (; index < max; ++index) {
354     haystack[index] = haystack[index + 1];
355   }
356   haystack.length = max;
357   return result;
358 }
359 unittest {
360   //_begin(r".drop(N)"c);
361   int[] foo = [0, 1, 2, 3, 4, 5] ;
362   int   elm = foo.drop(3U)       ;
363   assert(foo == [0, 1, 2, 4, 5]);
364   assert(elm == 3              );
365   //_end;
366 }
367
368 /**
369  *  Remove an item from an array by value, and return it.
370  *  (modified from Cashew.utils)
371  */
372 T drop_item (T) (inout T[] haystack, T item)
373 body {
374     size_t index = haystack.find_item(item);
375     assert(index!=NOT_FOUND, ".drop_item() called for an item not in the array");
376     return haystack.drop(index);
377 }
378 unittest {
379     //_begin(r".drop(N)"c);
380     int[] foo = [0, 1, 2, 3, 4, 5] ;
381     int   elm = foo.drop_item(3)       ;
382     assert(foo == [0, 1, 2, 4, 5]);
383     assert(elm == 3              );
384     //_end;
385 }
386
387 /**
388  * Remove a range of elements from an array in place. 
389  * It is not an error for the range to be empty or for start to be greater than end.
390  * If so, the array is not modified.
391  * Out of bounds checks performed only in debug mode.
392  * Returns: the array, which (is modified in-place).
393  * Note: This is an O(n) operation.
394  */
395 template drop_range(T)
396 {
397   T[] drop_range( inout T[] arr, int start, int end )
398   {
399     debug if ( start>=arr.length || end > arr.length || start<0 || end<0)
400         throw new Exception(string.format("Attempt to drop range %s,%s from size %s",start,end,arr.length));
401     if (start>=end) return arr;
402     size_t len = end-start;
403     size_t max = arr.length-len;
404     for (; start < max; start++ ) arr[start]=arr[start+len];
405     arr.length= max;
406     return arr;
407   }
408 } 
409
410 /***********************************************************************************
411  * Lookup the index of the first element of an array that satisfies a delegate.
412  * Returns: the index of the value or NOT_FOUND if not found.
413  */
414 size_t find (T) (T[] haystack, bool delegate (T) dg)
415 body {
416     foreach (index, needle; haystack) {
417         if (dg(needle)) {
418             return index;
419         }
420     }
421     return NOT_FOUND;
422 }
423 unittest {
424     //_begin(r".find(Dlg)"c);
425     int[] foo = [1, 2, 4, 8, 16, 32];
426     size_t idx = foo.find((int x) {return x > 10;});
427     assert(idx == 4U);
428     //_end;
429 }
430
431 /***********************************************************************************
432  * Lookup the index of the first element of an array that equals a given value
433  * Returns: the index of the value or NOT_FOUND if not found.
434  */
435 size_t find_item (T) (T[] haystack, T cmp)
436 body {
437   foreach (index, needle; haystack) {
438     if (needle==cmp) {
439       return index;
440     }
441   }
442   return NOT_FOUND;
443 }
444 unittest {
445     //_begin(r".find_item(Dlg)"c);
446     int[] foo = [1, 2, 4, 8, 16, 32];
447     size_t idx = foo.find_item(4);
448     assert(idx == 2U);
449   //_end;
450 }
451
452 /***********************************************************************************
453  * Return whether a particular item is in an array
454  */
455 bool contains(T) (T[] haystack, T cmp)
456 body {
457     return haystack.find_item(cmp) != NOT_FOUND;
458 }
459 unittest {
460     //_begin(r".contains(Dlg)"c);
461     int[] foo = [1, 3, 4, 8, 16, 32];
462     assert(foo.contains(3));
463     assert(!foo.contains(42));
464     //_end;
465 }
Note: See TracBrowser for help on using the browser.