1 module des.il.region; 2 3 import std.algorithm; 4 import std..string; 5 import std.traits; 6 import std.typetuple; 7 import des.math.linear.vector; 8 import des.util.testsuite; 9 import std.exception; 10 11 import des.il.util; 12 13 /// rectangle region of space 14 struct Region(size_t N,T) if( isNumeric!T ) 15 { 16 /// 17 alias Vector!(N,T) vec_t; 18 19 /// 20 alias Region!(N,T) self_t; 21 22 /// 23 vec_t pos, size; 24 25 static if( N == 0 ) 26 { 27 invariant 28 { 29 enforce( pos.length == size.length, "pos and size dimension mismatch" ); 30 } 31 } 32 33 /// 34 pure this(size_t Z,K)( in Region!(Z,K) e ) 35 if( (Z==0||N==0||Z==N) ) 36 { 37 pos = vec_t( e.pos ); 38 size = vec_t( e.size ); 39 } 40 41 /// 42 pure this(E...)( in E ext ) 43 //if( is( typeof( Vector!(N*2,T)(ext) ) ) ) 44 { 45 auto vr = Vector!(N*2,T)(ext); 46 static if( N == 0 ) 47 enforce( vr.length % 2 == 0, "wrong size of input" ); 48 pos = vec_t( vr.data[0..$/2] ); 49 size = vec_t( vr.data[$/2..$] ); 50 } 51 52 /// 53 pure static self_t fromSize(E...)( in E vals ) 54 { 55 auto size = Vector!(N,T)(vals); 56 auto pos = size; 57 foreach( ref v; pos.data ) v = 0; 58 return self_t( pos, size ); 59 } 60 61 pure @property 62 { 63 /// 64 vec_t lim() const { return pos + size; } 65 /// 66 vec_t lim( in vec_t nl ) 67 { 68 size = nl - pos; 69 return vec_t( nl ); 70 } 71 72 /// 73 size_t dims() const 74 { 75 static if( N == 0 ) return pos.length; 76 else return N; 77 } 78 79 static if( N == 0 ) 80 { 81 size_t dims( size_t ndim ) 82 { 83 pos.length = ndim; 84 size.length = ndim; 85 return ndim; 86 } 87 } 88 89 /// multiplication of size components 90 T volume() const 91 { 92 T ret = 1; 93 foreach( s; size ) ret *= s; 94 return ret; 95 } 96 97 /// returns false if any of size components is zero 98 bool hasVolume() const 99 { 100 foreach( s; size ) 101 if( s == 0 ) 102 return false; 103 return true; 104 } 105 } 106 107 /// 108 bool contains(size_t Z,E)( in Vector!(Z,E) pnt ) pure const 109 if( (Z==0||N==0||Z==N) && is(typeof(E.init>T.init)) ) 110 { 111 static if( N==0 || Z==0 ) 112 enforce( pnt.length == dims, "dimension mismatch" ); 113 114 auto l = lim; 115 116 foreach( i; 0 .. dims ) 117 if( pnt[i] < pos[i] || pnt[i] >= l[i] ) 118 return false; 119 120 return true; 121 } 122 123 /// 124 bool contains(size_t Z,E)( in Region!(Z,E) reg ) pure const 125 if( (Z==0||N==0||Z==N) && is(typeof(E.init>T.init)) ) 126 { 127 static if( N==0 || Z==0 ) 128 enforce( reg.pos.length == dims, "dimension mismatch" ); 129 130 return contains( reg.pos ) && contains( reg.lim ); 131 } 132 133 /// 134 bool opBinaryRight(string op, size_t Z, E)( in Vector!(Z,E) pnt ) const 135 if( (Z==0||N==0||Z==N) && op == "in" && is(typeof(E.init>T.init)) ) 136 { return contains( pnt ); } 137 138 static if( N>0 ) 139 { 140 /// 141 bool contains(Args...)( Args args ) pure const 142 if( allSatisfy!( isNumeric, Args ) && Args.length == N ) 143 { return contains( vec_t( args ) ); } 144 } 145 146 /// 147 bool opBinaryRight(string op, size_t Z, E)( in Region!(Z,E) reg ) const 148 if( (Z==0||N==0||Z==N) && op == "in" && is(typeof(E.init>T.init)) ) 149 { return contains( reg ); } 150 151 /// logic and 152 auto overlap(size_t Z, E)( in Region!(Z,E) reg ) const 153 if( (Z==0||N==0||Z==N) ) 154 { 155 static if( N==0 || Z==0 ) 156 enforce( reg.pos.length == dims, "dimension mismatch" ); 157 158 vec_t r1, r2; 159 160 static if( N==0 ) 161 { 162 r1.length = dims; 163 r2.length = dims; 164 } 165 166 auto lll = lim; 167 auto reg_lim = reg.lim; 168 169 foreach( i; 0 .. dims ) 170 { 171 r1[i] = cast(T)( min( max( pos[i], reg.pos[i] ), lll[i] ) ); 172 r2[i] = cast(T)( max( min( lll[i], reg_lim[i] ), pos[i] ) ); 173 } 174 175 return self_t( r1, r2 - r1 ); 176 } 177 178 /// 179 auto overlapLocal(size_t Z, E)( in Region!(Z,E) reg ) const 180 if( (Z==0||N==0||Z==N) ) 181 { 182 static if( N==0 || Z==0 ) 183 enforce( reg.pos.length == dims, "dimension mismatch" ); 184 185 auto buf = overlap( self_t( vec_t(reg.pos) + pos, reg.size ) ); 186 return self_t( buf.pos - pos, buf.size ); 187 } 188 189 /// 190 auto expand(size_t Z, E)( in Region!(Z,E) reg ) const 191 if( (Z==0||N==0||Z==N) ) 192 { 193 static if( N==0 || Z==0 ) 194 enforce( reg.pos.length == dims, "dimension mismatch" ); 195 196 vec_t r1, r2; 197 198 static if( N==0 ) 199 { 200 r1.length = dims; 201 r2.length = dims; 202 } 203 204 auto self_lim = lim; 205 auto reg_lim = reg.lim; 206 207 foreach( i; 0 .. dims ) 208 { 209 r1[i] = min( pos[i], reg.pos[i], self_lim[i], reg_lim[i] ); 210 r2[i] = max( pos[i], reg.pos[i], self_lim[i], reg_lim[i] ); 211 } 212 213 return self_t( r1, r2 - r1 ); 214 } 215 216 /// 217 auto expand(size_t Z, E)( in Vector!(Z,E) pnt ) const 218 if( (Z==0||N==0||Z==N) ) 219 { 220 static if( N==0 || Z==0 ) 221 enforce( pnt.length == dims, "dimension mismatch" ); 222 223 vec_t r1, r2; 224 225 static if( N==0 ) 226 { 227 r1.length = dims; 228 r2.length = dims; 229 } 230 231 auto self_lim = lim; 232 233 foreach( i; 0 .. dims ) 234 { 235 r1[i] = min( pos[i], self_lim[i], pnt[i] ); 236 r2[i] = max( pos[i], self_lim[i], pnt[i] ); 237 } 238 239 return self_t( r1, r2 - r1 ); 240 } 241 } 242 243 /// 244 alias Region!(1,float) fRegion1; 245 /// 246 alias Region!(2,float) fRegion2; 247 /// 248 alias Region!(3,float) fRegion3; 249 250 /// 251 alias Region!(1,int) iRegion1; 252 /// 253 alias Region!(2,int) iRegion2; 254 /// 255 alias Region!(3,int) iRegion3; 256 257 unittest 258 { 259 auto a = fRegion1( 1, 5 ); 260 assert( a.contains(2) ); 261 assert( !a.contains(8) ); 262 assert( a.lim[0] == 6 ); 263 auto b = fRegion1( 2, 3 ); 264 assert( b in a ); 265 } 266 267 /// 268 unittest 269 { 270 auto a = fRegion1(1,5); 271 auto b = fRegion1(2,5); 272 assert( a.overlap(b) == b.overlap(a) ); 273 assert( a.overlap(b) == fRegion1(2,4) ); 274 275 assert( a.overlapLocal(b) == fRegion1(2,3) ); 276 } 277 278 /// 279 unittest 280 { 281 auto a = fRegion1(1,2); 282 auto b = fRegion1(4,2); 283 assert( a.expand(b) == fRegion1(1,5) ); 284 } 285 286 unittest 287 { 288 auto a = fRegion3( vec3(0,0,0), vec3(1,1,1) ); 289 //assert( vec3(.5,.2,.8) in a ); 290 assert( a.opBinaryRight!"in"( vec3(.5,.2,.8) ) ); 291 assert( a == a.expand( vec3(.2,.3,.4) ) ); 292 assert( a != a.expand( vec3(1.2,.3,.4) ) ); 293 assert( fRegion3( vec3(0,0,0), vec3(1.2,1,1) ) == 294 a.expand( vec3(1.2,.3,.4) ) ); 295 } 296 297 /// 298 unittest 299 { 300 alias Region!(5,float) MSR; // MultiSpaceRegtion 301 alias MSR.vec_t msrvec; 302 auto a = MSR( msrvec(1,0,3,4,3), msrvec(3,2,4,8,4) ); 303 assert( msrvec(2,1,4,5,5) in a ); 304 } 305 306 /// 307 unittest 308 { 309 auto a = fRegion2( vec2(1,1), vec2(2,2) ); 310 assert( a.contains(2,2) ); 311 } 312 313 /// 314 unittest 315 { 316 alias NReg = Region!(0,float); 317 auto r1 = NReg( 1,2,3,4 ); 318 assertEq( r1.dims, 2 ); 319 assertEq( r1.pos.data, [1,2] ); 320 assertEq( r1.size.data, [3,4] ); 321 322 assert( vec2(2,3) in r1 ); 323 324 auto r2 = NReg( 1,2 ); 325 assertEq( r2.dims, 1 ); 326 assertEq( r2.pos.data, [1] ); 327 assertEq( r2.size.data, [2] ); 328 329 assert( Vector!(0,float)(1.4) in r2 ); 330 r2.dims = 3; 331 r2.pos = vec3(1,2,3); 332 r2.size = vec3(1,2,3); 333 //mustExcept({ r2.size = vec2(1,2); }); // uncatcable exception from invariant 334 r2 = NReg( vec2(1,2), vec2(3,2) ); 335 assert( vec2(2,3) in r2 ); 336 }