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