//| //| Harmonia - windowless (a.k.a portable) GUI framework //| //| History: Harmonia, Phobos and Deimos were children of Ares (Mars) //| //| Author: Andrew Fedoniouk @ Terra Informatica Software //| module harmonia.gx.geometry; import harmonia.tools; struct size { int x, y; static size opCall( int x, int y ) { size p; p.x = x; p.y = y; return p; } size opAdd(int i) { return opCall(x + i, y + i); } size opAdd_r(int i) { return opCall(x + i, y + i); } size opAdd(size s) { return opCall(x + s.x, y + s.y); } // size = size - int; size = int - size; size = size - size size opSub(int i) { return opCall(x - i, y - i); } size opSub_r(int i) { return opCall(i - x, i - y); } size opSub(size s) { return opCall(x - s.x, y - s.y); } // size = size * int; size = int * size; size = size * size size opMul(int i) { return opCall(x * i, y * i); } size opMul_r(int i) { return opCall(x * i, y * i); } size opMul(size s) { return opCall(x * s.x, y * s.y); } // size = size / int; size = int / size; size = size / size; // handling division on zero is a caller responsibility size opDiv(int i) { return opCall(x / i, y / i); } size opDiv_r(int i) { return opCall(i / x, i / y); } size opDiv(size s) { return opCall(x / s.x, y / s.y); } size opNeg() { return opCall(-x, -y); } size opAddAssign(int i) { return x += i, y += i, *this; } size opAddAssign(size s) { return x += s.x, y += s.y, *this; } size opSubAssign(int i) { return x -= i, y -= i, *this; } size opSubAssign(size s) { return x -= s.x, y -= s.y, *this; } size opMulAssign(int i) { return x *= i, y *= i, *this; } size opMulAssign(size s) { return x *= s.x, y *= s.y, *this; } size opDivAssign(int i) { return x /= i, y /= i, *this; } size opDivAssign(size s) { return x /= s.x, y /= s.y, *this; } } /* point primitive. integer 2d coordinate */ struct point { int x,y; // fake ctor (structs do not have ctors in D) static point opCall( int x, int y ) { point p; p.x = x; p.y = y; return p; } // trivial set void set( int x, int y ) { this.x = x; this.y = y; } // geometry arithmetics // point = point + int; point = int + point; point = point + point point opAdd(int i) { return opCall(x + i, y + i); } point opAdd_r(int i) { return opCall(x + i, y + i); } point opAdd(point p) { return opCall(x + p.x, y + p.y); } point opAdd(size s) { return opCall(x + s.x, y + s.y); } // point = point - int; point = int - point; size = point - point point opSub(int i) { return opCall(x - i, y - i); } point opSub_r(int i) { return opCall(i - x, i - y); } point opSub(point p) { return opCall(x - p.x, y - p.y); } point opSub(size s) { return opCall(x - s.x, y - s.y); } // point = point * int; point = int * point; point = point * point point opMul(int i) { return opCall(x * i, y * i); } point opMul_r(int i) { return opCall(x * i, y * i); } point opMul(point p) { return opCall(x * p.x, y * p.y); } point opMul(size s) { return opCall(x * s.x, y * s.y); } // point = point / int; point = int / point; point = point / point; // handling division on zero is a caller responsibility point opDiv(int i) { return opCall(x / i, y / i); } point opDiv_r(int i) { return opCall(i / x, i / y); } point opDiv(point p) { return opCall(x / p.x, y / p.y); } point opDiv(size s) { return opCall(x / s.x, y / s.y); } point opNeg() { return opCall(-x, -y); } point opAddAssign(int i) { return x += i, y += i, *this; } point opAddAssign(point p) { return x += p.x, y += p.y, *this; } point opAddAssign(size s) { return x += s.x, y += s.y, *this; } point opSubAssign(int i) { return x -= i, y -= i, *this; } point opSubAssign(point p) { return x -= p.x, y -= p.y, *this; } point opSubAssign(size s) { return x -= s.x, y -= s.y, *this; } point opMulAssign(int i) { return x *= i, y *= i, *this; } point opMulAssign(point p) { return x *= p.x, y *= p.y, *this; } point opMulAssign(size s) { return x *= s.x, y *= s.y, *this; } point opDivAssign(int i) { return x /= i, y /= i, *this; } point opDivAssign(point p) { return x /= p.x, y /= p.y, *this; } point opDivAssign(size s) { return x /= s.x, y /= s.y, *this; } } /** * diapason * please pay attention that both l and h belong to the range - [l,h] */ struct range { int l,h; // low and high static range opCall( int low, int high ) { range r; r.l = low; r.h = high; return r; } bool isEmpty() { return (l > h); } int length() { return (l <= h)? h - l + 1 : 0; } bool overlap(range r) { return (max!(int)(l,r.l)) <= (min!(int)(h,r.h)); } bool opEquals(range b) { return (l == b.l) && (h == b.h); } // check if 'i' is inside the range bool contains(int i) { return (i >= l) && (i <= h); } // intersection of two ranges r3 = r1 & r2; range opAnd(range r) { return opCall( max!(int)(l,r.l), min!(int)(h,r.h)); } // union of two ranges r3 = r1 | r2; range opOr(range r) { return opCall( min!(int)(l,r.l), max!(int)(h,r.h)); } range opAddAssign(int i) { return l += i, h += i, *this; } range opSubAssign(int i) { return l -= i, h -= i, *this; } // inplace intersection r1 &= r2; range opAndAssign(range a) { return l = max!(int)(l,a.l), h = min!(int)(h,a.h), *this; } // inplace union r1 |= r2; range opOrAssign(range b) { if(isEmpty()) *this = b; else if(!b.isEmpty()) { l = min!(int)(l,b.l); h = max!(int)(h,b.h); } return *this; } static range empty() { range r; r.l = 0; r.h = -1; return r; } }; /** * rectangle primitive */ struct rect { point origin, corner; // origin and corner belong to rectangle. sic! // face "constructors" // constructs a rectangle with the given x, y, width and height. static rect opCall(int x1, int y1, int x2, int y2) { rect r; r.origin.x = x1; r.origin.y = y1; r.corner.x = x2; r.corner.y = y2; return r; } static rect opCall(point org, point cor) { rect r; r.origin = org; r.corner = cor; return r; } static rect opCall(point org, size dimension) { rect r; r.origin = org; r.corner = org + dimension - 1; return r; } static rect opCall(size dimension) { return opCall( point(0,0) , dimension ); } static rect opCall(range x, range y) { rect r; r.origin.x = x.l; r.origin.y = y.l; r.corner.x = x.h; r.corner.y = y.h; return r; } // projection of the rect on axis range x() { return range(origin.x,corner.x); } range y() { return range(origin.y,corner.y); } range x(range rn) { origin.x = rn.l; corner.x = rn.h; return x(); } range y(range rn) { origin.y = rn.l; corner.y = rn.h; return y(); } int left() { return origin.x; } int right() { return corner.x; } int top() { return origin.y; } int bottom() { return corner.y; } int width() { return corner.x - origin.x + 1; } int height() { return corner.y - origin.y + 1; } int width(int w) { corner.x = origin.x + w - 1; return width(); } int height(int h) { corner.y = origin.y + h - 1; return height(); } bool contains(point p) { return x().contains(p.x) && y().contains(p.y); } // contains in full bool contains(rect r) { return (r.origin.x >= origin.x) && (r.origin.y >= origin.y) && (r.corner.x <= corner.x) && (r.corner.y <= corner.y); } bool overlap(rect r) { return x().overlap(r.x()) && y().overlap(r.y()); } // in- or de- flate rect // rect rc; rc >>= 2 - deflate all sides on 2 rect opShlAssign /*<<=*/(int a) { origin += a; corner -= a; return *this; } rect opShrAssign /*>>=*/(int a) { origin -= a; corner += a; return *this; } rect opShlAssign /*<<=*/(size a) { origin += a.x; corner -= a.x; return *this; } rect opShrAssign /*>>=*/(size a) { origin -= a.y; corner += a.y; return *this; } rect opShlAssign /*<<=*/(rect a) { origin += a.origin; corner -= a.corner; return *this; } rect opShrAssign /*>>=*/(rect a) { origin -= a.origin; corner += a.corner; return *this; } rect opShl /*<<=*/(int a) { rect r = *this; r <<= a; return r; } rect opShr /*>>=*/(int a) { rect r = *this; r >>= a; return r; } rect opShl /*<<=*/(size a) { rect r = *this; r <<= a; return r; } rect opShr /*>>=*/(size a) { rect r = *this; r >>= a; return r; } rect opShl /*<<=*/(rect a) { rect r = *this; r <<= a; return r; } rect opShr /*>>=*/(rect a) { rect r = *this; r >>= a; return r; } // shift position rect opAddAssign /*+= */(int a) { origin += a; corner += a; return *this; } rect opSubAssign /*-= */(int a) { origin -= a; corner -= a; return *this; } rect opAddAssign /*+= */(point a) { origin += a; corner += a; return *this; } rect opSubAssign /*-= */(point a) { origin -= a; corner -= a; return *this; } rect opAddAssign /*+= */(size a) { origin += a; corner += a; return *this; } rect opSubAssign /*-= */(size a) { origin -= a; corner -= a; return *this; } rect opAdd /* r + p*/ (point a) { return opCall(origin + a, corner + a); } rect opAdd /* r + s*/ (size a) { return opCall(origin + a, corner + a); } //intersection of two rects is a rect iself rect opAnd(rect r) { return opCall( x() & r.x(), y() & r.y()); } //union (well sort of) of two rects is a rect outlining them rect opOr(rect r) { return opCall( x() | r.x(), y() | r.y()); } // is it empty? bool isEmpty() { return x().isEmpty() || y().isEmpty(); }; static rect empty() { rect r; r.clear(); return r; } void set(point o, size s) { origin = o; corner = o + s - 1; } // opCalls this rect empty - having width or height less than or equal to zero void clear() { origin.x = origin.y = 0; corner.x = corner.y = -1; } // returns point of rectangle. for WHICH values see numbers on num keypad. point pointOf(int which) { switch(which) { case 7: return origin; case 3: return corner; case 1: return point(origin.x,corner.y); case 9: return point(corner.x,origin.y); case 8: return point((corner.x + origin.x)/2,origin.y); case 2: return point((corner.x + origin.x)/2,corner.y); case 4: return point(origin.x,(corner.y + origin.y)/2); case 6: return point(corner.x,(corner.y + origin.y)/2); case 5: return point((corner.x + origin.x)/2,(corner.y + origin.y)/2); default: assert(false); } return point(0x00000BAD,0x00000BAD); } // sets point of rectangle. for WHICH values see numbers on num keypad. void pointOf(int which, point v) { switch(which) { case 7: origin = v; break; case 3: corner = v; break; case 1: origin.x = v.x; corner.y = v.y; break; case 9: corner.x = v.x; origin.y = v.y; break; case 8: origin.y = v.y; break; case 2: corner.y = v.y; break; case 4: origin.x = v.x; break; case 6: corner.x = v.x; break; case 5: break; // NOTHING! default: assert(false); } } // position attribute point pos() { return origin; } point pos(point pos) { corner.x = pos.x + corner.x - origin.x; corner.y = pos.y + corner.y - origin.y; return origin = pos; } // dimension attribute size dim() { return size(width(),height()); } size dim(size sz) { corner = origin + sz - 1; return size(width(),height()); } // move this rect to fit inside the border outline in full void inscribe(rect border) { point newpos = origin; size sz = dim; size bsz = border.dim; // check the dimension first if( sz.x > bsz.x ) corner.x = origin.x + bsz.x - 1; if( sz.y > bsz.y ) corner.y = origin.y + bsz.y - 1; //move if( origin.x < border.origin.x ) newpos.x = border.origin.x; else if ( corner.x > border.corner.x ) newpos.x = border.corner.x - (corner.x - origin.x); if( origin.y < border.origin.y ) newpos.y = border.origin.y; else if ( corner.y > border.corner.y ) newpos.y = border.corner.y - (corner.y - origin.y); pos(newpos); } } // test for overlaping of two rects bool overlap(rect r1, rect r2) { return r1.x().overlap(r2.x()) && r1.y().overlap(r2.y()); }