egra: prepare agg mini before calling `onPaint()` (and cleanup afterwards)
[iv.d.git] / gxx / geom.d
blob2bff7519a5ee07a9943f7a1aeec82f3d8947ab4e
1 /* coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
2 * Understanding is not required. Only obedience.
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, version 3 of the License ONLY.
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 module iv.gxx.geom /*is aliced*/;
17 private:
20 // ////////////////////////////////////////////////////////////////////////// //
21 public struct GxSize {
22 public:
23 int width, height; ///
25 ///
26 string toString () const @trusted nothrow {
27 if (valid) {
28 import core.stdc.stdio : snprintf;
29 char[128] buf = void;
30 return buf[0..snprintf(buf.ptr, buf.length, "(%dx%d)", width, height)].idup;
31 } else {
32 return "(invalid-size)";
36 pure nothrow @safe @nogc:
37 this() (in auto ref GxSize p) { /*pragma(inline, true);*/ width = p.width; height = p.height; } ///
38 this (int ax, int ay) { /*pragma(inline, true);*/ width = ax; height = ay; } ///
39 @property bool valid () const { pragma(inline, true); return (width >= 0 && height >= 0); } ///
40 @property bool invalid () const { pragma(inline, true); return (width < 0 || height < 0); } ///
41 @property bool empty () const { pragma(inline, true); return (width <= 0 || height <= 0); } /// invalid rects are empty
42 void opAssign() (in auto ref GxSize p) { pragma(inline, true); width = p.width; height = p.height; } ///
43 bool opEquals() (in auto ref GxSize p) const { pragma(inline, true); return (p.width == width && p.height == height); } ///
44 ///
45 int opCmp() (in auto ref GxSize p) const {
46 pragma(inline, true);
47 if (auto d0 = height-p.height) return (d0 < 0 ? -1 : 1);
48 else if (auto d1 = width-p.width) return (d1 < 0 ? -1 : 1);
49 else return 0;
54 // ////////////////////////////////////////////////////////////////////////// //
55 public struct GxPoint {
56 public:
57 int x, y; ///
59 ///
60 string toString () const @trusted nothrow {
61 import core.stdc.stdio : snprintf;
62 char[128] buf = void;
63 return buf[0..snprintf(buf.ptr, buf.length, "(%d,%d)", x, y)].idup;
66 pure nothrow @safe @nogc:
67 this() (in auto ref GxPoint p) { pragma(inline, true); x = p.x; y = p.y; } ///
68 this (int ax, int ay) { pragma(inline, true); x = ax; y = ay; } ///
69 void opAssign() (in auto ref GxPoint p) { pragma(inline, true); x = p.x; y = p.y; } ///
70 bool opEquals() (in auto ref GxPoint p) const { pragma(inline, true); return (p.x == x && p.y == y); } ///
71 ///
72 int opCmp() (in auto ref GxPoint p) const {
73 pragma(inline, true);
74 if (auto d0 = y-p.y) return (d0 < 0 ? -1 : 1);
75 else if (auto d1 = x-p.x) return (d1 < 0 ? -1 : 1);
76 else return 0;
81 // ////////////////////////////////////////////////////////////////////////// //
82 public struct GxRect {
83 public:
84 int x0, y0; ///
85 int width = -1; // <0: invalid rect
86 int height = -1; // <0: invalid rect
88 alias left = x0; ///
89 alias top = y0; ///
90 alias right = x1; ///
91 alias bottom = y1; ///
92 alias x = x0;
93 alias y = y0;
95 ///
96 string toString () const @trusted nothrow {
97 if (valid) {
98 import core.stdc.stdio : snprintf;
99 char[128] buf = void;
100 return buf[0..snprintf(buf.ptr, buf.length, "(%d,%d)-(%d,%d)", x0, y0, x0+width-1, y0+height-1)].idup;
101 } else {
102 return "(invalid-rect)";
106 pure nothrow @safe @nogc:
108 this() (in auto ref GxRect rc) { /*pragma(inline, true);*/ x0 = rc.x0; y0 = rc.y0; width = rc.width; height = rc.height; }
111 this() (in auto ref GxSize sz) { /*pragma(inline, true);*/ x0 = 0; y0 = 0; width = sz.width; height = sz.height; }
114 this (int ax0, int ay0, int awidth, int aheight) {
115 //pragma(inline, true);
116 x0 = ax0;
117 y0 = ay0;
118 width = awidth;
119 height = aheight;
123 this() (in auto ref GxPoint xy0, int awidth, int aheight) {
124 //pragma(inline, true);
125 x0 = xy0.x;
126 y0 = xy0.y;
127 width = awidth;
128 height = aheight;
132 this() (in auto ref GxPoint xy0, in auto ref GxPoint xy1) {
133 //pragma(inline, true);
134 x0 = xy0.x;
135 y0 = xy0.y;
136 width = xy1.x-xy0.x+1;
137 height = xy1.y-xy0.y+1;
140 void opAssign() (in auto ref GxRect rc) { /*pragma(inline, true);*/ x0 = rc.x0; y0 = rc.y0; width = rc.width; height = rc.height; } ///
141 bool opEquals() (in auto ref GxRect rc) const { pragma(inline, true); return (rc.x0 == x0 && rc.y0 == y0 && rc.width == width && rc.height == height); } ///
143 int opCmp() (in auto ref GxRect p) const {
144 if (auto d0 = y0-rc.y0) return (d0 < 0 ? -1 : 1);
145 if (auto d1 = x0-rc.x0) return (d1 < 0 ? -1 : 1);
146 if (auto d2 = width*height-rc.width*rc.height) return (d2 < 0 ? -1 : 1);
147 return 0;
150 @property bool valid () const { pragma(inline, true); return (width >= 0 && height >= 0); } ///
151 @property bool invalid () const { pragma(inline, true); return (width < 0 || height < 0); } ///
152 @property bool empty () const { pragma(inline, true); return (width <= 0 || height <= 0); } /// invalid rects are empty
154 void invalidate () { pragma(inline, true); width = height = -1; } ///
156 @property GxPoint lefttop () const { pragma(inline, true); return GxPoint(x0, y0); } ///
157 @property GxPoint righttop () const { pragma(inline, true); return GxPoint(x0+width-1, y0); } ///
158 @property GxPoint leftbottom () const { pragma(inline, true); return GxPoint(x0, y0+height-1); } ///
159 @property GxPoint rightbottom () const { pragma(inline, true); return GxPoint(x0+width-1, y0+height-1); } ///
161 @property void lefttop() (in auto ref GxPoint v) { pragma(inline, true); x0 = v.x; y0 = v.y; } ///
162 @property void righttop() (in auto ref GxPoint v) { pragma(inline, true); x1 = v.x; y0 = v.y; } ///
163 @property void leftbottom() (in auto ref GxPoint v) { pragma(inline, true); x0 = v.x; y1 = v.y; } ///
164 @property void rightbottom() (in auto ref GxPoint v) { pragma(inline, true); x1 = v.x; y1 = v.y; } ///
166 alias topleft = lefttop; ///
167 alias topright = righttop; ///
168 alias bottomleft = leftbottom; ///
169 alias bottomright = rightbottom; ///
171 @property GxSize size () const { pragma(inline, true); return GxSize(width, height); } ///
172 @property void size() (in auto ref GxSize sz) { pragma(inline, true); width = sz.width; height = sz.height; } ///
174 @property int x1 () const { pragma(inline, true); return (width > 0 ? x0+width-1 : x0-1); } ///
175 @property int y1 () const { pragma(inline, true); return (height > 0 ? y0+height-1 : y0-1); } ///
177 @property void x1 (in int val) { pragma(inline, true); width = val-x0+1; } ///
178 @property void y1 (in int val) { pragma(inline, true); height = val-y0+1; } ///
180 GxPoint translateToGlobal() (in auto ref GxPoint lpt) const {
181 pragma(inline, true);
182 return GxPoint(lpt.x+x0, lpt.y+y0);
185 GxRect translateToGlobal() (in auto ref GxRect lrc) const {
186 pragma(inline, true);
187 return GxRect(lrc.x0+x0, lrc.y0+y0, lrc.width, lrc.height);
191 bool inside() (in auto ref GxPoint p) const {
192 pragma(inline, true);
193 return (width > 0 && height > 0 ? (p.x >= x0 && p.y >= y0 && p.x < x0+width && p.y < y0+height) : false);
196 /// ditto
197 bool inside (in int ax, in int ay) const {
198 pragma(inline, true);
199 return (width > 0 && height > 0 ? (ax >= x0 && ay >= y0 && ax < x0+width && ay < y0+height) : false);
202 /// is `r` inside `this`?
203 bool contains() (in auto ref GxRect r) const {
204 pragma(inline, true);
205 return
206 width > 0 && height > 0 &&
207 r.width > 0 && r.height > 0 &&
208 r.x0 >= x0 && r.y0 >= y0 &&
209 r.x0+r.width <= x0+width && r.y0+r.height <= y0+height;
212 /// is `r` and `this` overlaps?
213 bool overlaps() (in auto ref GxRect r) const {
214 pragma(inline, true);
215 return
216 width > 0 && height > 0 &&
217 r.width > 0 && r.height > 0 &&
218 x0 < r.x0+r.width && r.x0 < x0+width &&
219 y0 < r.y0+r.height && r.y0 < y0+height;
222 /// extend `this` so it will include `r`
223 void include() (in auto ref GxRect r) {
224 pragma(inline, true);
225 if (!r.empty) {
226 if (empty) {
227 x0 = r.x;
228 y0 = r.y;
229 width = r.width;
230 height = r.height;
231 } else {
232 if (r.x < x0) x0 = r.x0;
233 if (r.y < y0) y0 = r.y0;
234 if (r.x1 > x1) x1 = r.x1;
235 if (r.y1 > y1) y1 = r.y1;
240 /// clip `this` so it will not be larger than `r`
241 bool intersect() (in auto ref GxRect r) {
242 if (r.invalid || invalid) { width = height = -1; return false; }
243 if (r.empty || empty) { width = height = 0; return false; }
244 if (r.y1 < y0 || r.x1 < x0 || r.x0 > x1 || r.y0 > y1) { width = height = 0; return false; }
245 // rc is at least partially inside this rect
246 if (x0 < r.x0) x0 = r.x0;
247 if (y0 < r.y0) y0 = r.y0;
248 if (x1 > r.x1) x1 = r.x1;
249 if (y1 > r.y1) y1 = r.y1;
250 assert(!empty); // yeah, always
251 return true;
255 void shrinkBy (int dx, int dy) {
256 pragma(inline, true);
257 if ((dx || dy) && valid) {
258 x0 += dx;
259 y0 += dy;
260 width -= dx*2;
261 height -= dy*2;
265 /// ditto
266 void shrinkBy() (in auto ref GxSize sz) { pragma(inline, true); shrinkBy(sz.width, sz.height); }
269 void growBy (int dx, int dy) {
270 pragma(inline, true);
271 if ((dx || dy) && valid) {
272 x0 -= dx;
273 y0 -= dy;
274 width += dx*2;
275 height += dy*2;
279 /// ditto
280 void growBy() (in auto ref GxSize sz) { pragma(inline, true); growBy(sz.width, sz.height); }
283 void set (int ax0, int ay0, int awidth, int aheight) {
284 pragma(inline, true);
285 x0 = ax0;
286 y0 = ay0;
287 width = awidth;
288 height = aheight;
292 void moveLeftTopBy (int dx, int dy) {
293 pragma(inline, true);
294 x0 += dx;
295 y0 += dy;
296 width -= dx;
297 height -= dy;
300 /// ditto
301 void moveLeftTopBy() (in auto ref GxPoint p) { pragma(inline, true); moveLeftTopBy(p.x, p.y); }
303 alias moveTopLeftBy = moveLeftTopBy; /// ditto
306 void moveRightBottomBy (int dx, int dy) {
307 pragma(inline, true);
308 width += dx;
309 height += dy;
312 /// ditto
313 void moveRightBottomBy() (in auto ref GxPoint p) { pragma(inline, true); moveRightBottomBy(p.x, p.y); }
315 alias moveBottomRightBy = moveRightBottomBy; /// ditto
318 void moveBy (int dx, int dy) {
319 pragma(inline, true);
320 x0 += dx;
321 y0 += dy;
324 /// ditto
325 void moveBy() (in auto ref GxPoint p) { pragma(inline, true); moveBy(p.x, p.y); }
328 void moveTo (int nx, int ny) {
329 pragma(inline, true);
330 x0 = nx;
331 y0 = ny;
334 /// ditto
335 void moveTo() (in auto ref GxPoint p) { pragma(inline, true); moveTo(p.x, p.y); }
338 * clip (x,y,len) stripe to this rect
340 * Params:
341 * x = stripe start (not relative to rect)
342 * y = stripe start (not relative to rect)
343 * len = stripe length
345 * Returns:
346 * x = fixed x (invalid if result is false)
347 * len = fixed length (invalid if result is false)
348 * leftSkip = how much cells skipped at the left side (invalid if result is false)
349 * result = false if stripe is completely clipped out
351 * TODO:
352 * overflows
354 bool clipHStripe (ref int x, int y, ref int len, int* leftSkip=null) const @trusted {
355 if (empty) return false;
356 if (len <= 0 || y < y0 || y >= y0+height || x >= x0+width) return false;
357 if (x < x0) {
358 // left clip
359 if (x+len <= x0) return false;
360 immutable int dx = x0-x;
361 if (leftSkip !is null) *leftSkip = dx;
362 len -= dx;
363 x = x0;
364 assert(len > 0); // yeah, always
366 if (x+len > x0+width) {
367 // right clip
368 len = x0+width-x;
369 assert(len > 0); // yeah, always
371 return true;
375 * clip (x,y,hgt) stripe to this rect
377 * Params:
378 * x = stripe start (not relative to rect)
379 * y = stripe start (not relative to rect)
380 * hgt = stripe length
382 * Returns:
383 * y = fixed y (invalid if result is false)
384 * hgt = fixed length (invalid if result is false)
385 * topSkip = how much cells skipped at the top side (invalid if result is false)
386 * result = false if stripe is completely clipped out
388 * TODO:
389 * overflows
391 bool clipVStripe (int x, ref int y, ref int hgt, int* topSkip=null) const @trusted {
392 if (empty) return false;
393 if (hgt <= 0 || x < x0 || x >= x0+width || y >= y0+height) return false;
394 if (y < y0) {
395 // top clip
396 if (y+hgt <= y0) return false;
397 immutable int dy = y0-y;
398 if (topSkip !is null) *topSkip = dy;
399 hgt -= dy;
400 y = y0;
401 assert(hgt > 0); // yeah, always
403 if (y+hgt > y0+height) {
404 // bottom clip
405 hgt = y0+height-y;
406 assert(hgt > 0); // yeah, always
408 return true;
412 bool clipHVStripes (ref int x, ref int y, ref int wdt, ref int hgt, int* leftSkip=null, int* topSkip=null) const @trusted {
413 if (empty || wdt <= 0 || hgt <= 0) return false;
414 if (y >= y0+height || x >= x0+width) return false;
415 if (x < x0) {
416 // left clip
417 if (x+wdt <= x0) return false;
418 immutable int dx = x0-x;
419 if (leftSkip !is null) *leftSkip = dx;
420 wdt -= dx;
421 x = x0;
422 assert(wdt > 0); // yeah, always
424 if (x+wdt > x0+width) {
425 // right clip
426 wdt = x0+width-x;
427 assert(wdt > 0); // yeah, always
430 if (y < y0) {
431 // top clip
432 if (y+hgt <= y0) return false;
433 immutable int dy = y0-y;
434 if (topSkip !is null) *topSkip = dy;
435 hgt -= dy;
436 y = y0;
437 assert(hgt > 0); // yeah, always
439 if (y+hgt > y0+height) {
440 // bottom clip
441 hgt = y0+height-y;
442 assert(hgt > 0); // yeah, always
445 return true;