egra: agg mini clipping bugfix; some fixes in widget rendering (buttons, etc.)
[iv.d.git] / sdpy / rect.d
blob6ac522ba5068b2119e4cda45908d3ed471ec8efa
1 /*
2 * Pixel Graphics Library
3 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
4 * Understanding is not required. Only obedience.
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, version 3 of the License ONLY.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module iv.sdpy.rect /*is aliced*/;
19 import iv.alice;
22 // ////////////////////////////////////////////////////////////////////////// //
23 struct Rect {
24 int x = 0, y = 0;
25 int width = -1; // <0: invalid rect
26 int height = -1; // <0: invalid rect
28 string toString () const @safe pure {
29 import std.string : format;
30 return (valid ? "(%s,%s)-(%s,%s)".format(x, y, x+width-1, y+height-1) : "(invalid-rect)");
33 nothrow @safe @nogc:
34 // default constructor: (x, y, w, h)
35 this (int ax, int ay, int awidth, int aheight) { static if (__VERSION__ > 2067) pragma(inline, true); set(ax, ay, awidth, aheight); }
37 @property pure const {
38 // note that killed rects are always equal
39 bool opEquals() (in auto ref Rect rc) {
40 static if (__VERSION__ > 2067) pragma(inline, true);
41 return (valid ? (x0 == rc.x0 && y0 == rc.y0 && width == rc.width && height == rc.height) : !rc.valid);
44 bool valid () { static if (__VERSION__ > 2067) pragma(inline, true); return (width >= 0 && height >= 0); }
45 bool empty () { static if (__VERSION__ > 2067) pragma(inline, true); return (width <= 0 || height <= 0); } // invalid rects are empty
47 int x0 () { static if (__VERSION__ > 2067) pragma(inline, true); return x; }
48 int y0 () { static if (__VERSION__ > 2067) pragma(inline, true); return y; }
49 int x1 () { static if (__VERSION__ > 2067) pragma(inline, true); return (width > 0 ? x+width-1 : x-1); }
50 int y1 () { static if (__VERSION__ > 2067) pragma(inline, true); return (height > 0 ? y+height-1 : y-1); }
53 @property {
54 void x0 (in int val) { static if (__VERSION__ > 2067) pragma(inline, true); width = x+width-val; x = val; }
55 void y0 (in int val) { static if (__VERSION__ > 2067) pragma(inline, true); height = y+height-val; y = val; }
56 void x1 (in int val) { static if (__VERSION__ > 2067) pragma(inline, true); width = val-x+1; }
57 void y1 (in int val) { static if (__VERSION__ > 2067) pragma(inline, true); height = val-y+1; }
60 void kill () { static if (__VERSION__ > 2067) pragma(inline, true); width = height = -1; }
62 alias left = x0;
63 alias top = y0;
64 alias right = x1;
65 alias bottom = y1;
67 bool contains (in int ax, in int ay) const pure { static if (__VERSION__ > 2067) pragma(inline, true); return (empty ? false : (ax >= x && ay >= y && ax < x+width && ay < y+height)); }
69 // is `this` contains `rc`?
70 bool contains() (in auto ref Rect rc) const pure {
71 static if (__VERSION__ > 2067) pragma(inline, true);
72 return
73 !this.empty && !rc.empty &&
74 rc.x >= this.x && rc.y >= this.y &&
75 rc.x1 <= this.x1 && rc.y1 <= this.y1;
78 // is `this` inside `rc`?
79 bool inside() (in auto ref Rect rc) const pure {
80 static if (__VERSION__ > 2067) pragma(inline, true);
81 return
82 !rc.empty && !this.empty &&
83 this.x >= rc.x && this.y >= rc.y &&
84 this.x1 <= rc.x1 && this.y1 <= rc.y1;
87 // is `r` and `this` overlaps?
88 bool overlap() (in auto ref Rect r) const pure {
89 static if (__VERSION__ > 2067) pragma(inline, true);
90 return
91 !empty && !r.empty &&
92 x <= r.x1 && r.x <= x1 && y <= r.y1 && r.y <= y1;
93 //!(x > r.x1 || r.x > x1 || y > r.y1 || r.y > y1);
96 // extend `this` so it will include `r`
97 void include() (in auto ref Rect r) {
98 static if (__VERSION__ > 2067) pragma(inline, true);
99 if (!r.empty) {
100 if (empty) {
101 x = r.x;
102 y = r.y;
103 width = r.width;
104 height = r.height;
105 } else {
106 if (r.x < x) x = r.x;
107 if (r.y < y) y = r.y;
108 if (r.x1 > x1) x1 = r.x1;
109 if (r.y1 > y1) y1 = r.y1;
114 void set (int ax, int ay, int awidth, int aheight) {
115 static if (__VERSION__ > 2067) pragma(inline, true);
116 x = ax;
117 y = ay;
118 width = awidth;
119 height = aheight;
122 void moveX0Y0By (int dx, int dy) {
123 static if (__VERSION__ > 2067) pragma(inline, true);
124 x += dx;
125 y += dy;
126 width -= dx;
127 height -= dy;
130 void moveX1Y1By (int dx, int dy) {
131 static if (__VERSION__ > 2067) pragma(inline, true);
132 width += dx;
133 height += dy;
136 void moveBy (int dx, int dy) {
137 static if (__VERSION__ > 2067) pragma(inline, true);
138 x += dx;
139 y += dy;
143 * clip (x,y,len) stripe to this rect
145 * Params:
146 * x = stripe start (not relative to rect)
147 * y = stripe start (not relative to rect)
148 * len = stripe length
150 * Returns:
151 * x = fixed x
152 * len = fixed length
153 * leftSkip = how much cells skipped at the left side
154 * result = false if stripe is completely clipped out
156 * TODO:
157 * overflows
159 bool clipStripe (ref int x, int y, ref int len, int* leftSkip=null) @trusted {
160 int dummy;
161 if (leftSkip is null) leftSkip = &dummy;
162 *leftSkip = 0;
163 if (empty) return false;
164 if (len <= 0 || y < this.y || y >= this.y+height || x >= this.x+width) return false;
165 if (x < this.x) {
166 // left clip
167 if (x+len <= this.x) return false;
168 len -= (*leftSkip = this.x-x);
169 x = this.x;
171 if (x+len >= this.x+width) {
172 // right clip
173 len = this.x+width-x;
174 assert(len > 0); // yeah, always
176 return true;
180 * clip this rect by another rect. both rects has same origin.
182 * Params:
183 * rc = rect to clip by
185 * Returns:
186 * result = false if rect is completely clipped out (and is killed)
188 bool clipByRect() (in auto ref Rect rc) {
189 // alas, dmd cannot inline function with more than one assignment
190 //static if (__VERSION__ > 2067) pragma(inline, true);
191 if (this.empty || rc.empty || this.y1 < rc.y0 || this.x1 < rc.x0 || this.x0 > rc.x1 || this.y0 > rc.y1) {
192 this.kill();
193 return false;
194 } else {
195 // this is at least partially inside rc rect
196 if (this.x0 < rc.x0) this.x0 = rc.x0; // clip left
197 if (this.y0 < rc.y0) this.y0 = rc.y0; // clip top
198 if (this.x1 > rc.x1) this.x1 = rc.x1; // clip right
199 if (this.y1 > rc.y1) this.y1 = rc.y1; // clip bottom
200 assert(!this.empty); // yeah, always
201 return true;