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 }