1 module des.il.util;
2 
3 import std.algorithm;
4 import std.exception;
5 import std.range;
6 import std.conv : to;
7 
8 import des.util.stdext.algorithm;
9 import des.util.testsuite;
10 
11 import des.math.linear.vector;
12 import des.math.util.accessstring;
13 
14 import des.il.region;
15 
16 ///
17 class ImageException : Exception
18 {
19     ///
20     this( string msg, string file=__FILE__, size_t line=__LINE__ ) @safe pure nothrow
21     { super( msg, file, line ); }
22 }
23 
24 void imEnforce(string file=__FILE__, size_t line=__LINE__)( bool val, lazy string msg )
25 { enforce( val, new ImageException( msg, file, line ) ); }
26 
27 alias coord_t = ptrdiff_t;
28 alias CrdVector(size_t N) = Vector!(N,coord_t);
29 alias CrdRegion(size_t N) = Region!(N,coord_t);
30 alias CrdVectorD = CrdVector!0;
31 alias CrdRegionD = CrdRegion!0;
32 
33 /++ checks all components
34  Returns:
35  true if all is positive
36  +/
37 bool isAllCompPositive(V)( in V v )
38     if( is( typeof( v[0] ) ) && isNumeric!(typeof(v[0])) )
39 {
40     foreach( e; v ) if( e < 0 ) return false;
41     return true;
42 }
43 
44 ///
45 unittest
46 {
47     assert(  isAllCompPositive( [1,2,3] ) );
48     assert(  isAllCompPositive( vec3( 1,2,3 ) ) );
49     assert(  isAllCompPositive( CrdVector!3( 1,2,3 ) ) );
50     assert( !isAllCompPositive( [-1,2,3] ) );
51 }
52 
53 ///
54 coord_t[] redimSize(A)( size_t K, size_t N, in A[] size ) pure
55 if( isIntegral!A )
56 in
57 {
58     assert( K >= N );
59     assert( isAllCompPositive( size ) );
60 }
61 body
62 {
63     auto ret = new coord_t[](K);
64     ret[] = 1;
65     foreach( i; 0 .. min( N, size.length ) )
66         ret[i] = size[i];
67     if( size.length > N )
68         ret[N-1] *= reduce!((r,v)=>r*=v)( 1, size[N..$] );
69     return ret;
70 }
71 
72 ///
73 unittest
74 {
75     assertEq( [1,2,3], redimSize( 3, 3, [1,2,3] ) );
76     assertEq( [1,6,1], redimSize( 3, 2, [1,2,3] ) );
77     assertEq( [5,6,1], redimSize( 3, 2, [5,3,2] ) );
78     assertEq( [5,6], redimSize( 2, 2, [5,3,2] ) );
79     assertEq( [30,1,1,1], redimSize( 4, 1, [5,3,2] ) );
80 }
81 
82 ///
83 coord_t[] redimSize(A)( size_t N, in A[] size ) pure
84 if( isIntegral!A )
85 in { assert( isAllCompPositive( size ) ); }
86 body { return redimSize( N, N, size ); }
87 
88 ///
89 unittest
90 {
91     assertEq( [1,2,3], redimSize( 3, [1,2,3] ) );
92     assertEq( [1,2,3,1,1], redimSize( 5, [1,2,3] ) );
93     assertEq( [1,6], redimSize( 2, [1,2,3] ) );
94     assertEq( [6], redimSize( 1, [1,2,3] ) );
95     assertEq( [1,1,1,1], redimSize( 4, cast(int[])[] ) );
96 }
97 
98 /++ get index of element in 1-dim array by N-dim coordinate
99 
100  Params:
101  size = N-dim array of sizes by each dimension
102  crd = N-dim array of coordinates in N-dim space
103 
104  Returns:
105  index in 1-dim array
106  +/
107 coord_t getIndex(A,B)( in A[] size, in B[] crd ) pure
108 if( isIntegral!A && isIntegral!B )
109 in
110 {
111     assert( size.length == crd.length, "array length mismatch" );
112     assert( isAllCompPositive( size ), "negative size" );
113     assert( isAllCompPositive( crd ), "negative coordinate" );
114     assert( all!"a[0]>a[1]"( zip( size, crd ) ), "range violation" );
115 }
116 body
117 {
118     size_t ret;
119     foreach( i; 0 .. size.length )
120     {
121         size_t cm = 1UL;
122         foreach( j; 0 .. i ) cm *= size[j];
123         ret += crd[i] * cm;
124     }
125     return ret;
126 }
127 
128 ///
129 unittest
130 {
131     assertEq( getIndex( [3,3], [1,1] ), 4 );
132     assertEq( getIndex( [4,3], [1,1] ), 5 );
133     assertEq( getIndex( [3,3,3], [1,1,1] ), 13 );
134 }
135 
136 /++ get coordinate in N-dim space by N-dim size and line index
137 
138  Params:
139  size = N-dim array of sizes by each dimension
140  index = index in 1-dim array
141 
142  Retrurns:
143  N-dim array of coordinates in N-dim space
144  +/
145 size_t[] getCoord(A)( in A[] size, size_t index ) pure
146 if( isIntegral!A )
147 in
148 {
149     assert( isAllCompPositive( size ), "negative size" );
150     auto maxindex = new A[]( size.length );
151     foreach( i, ref mi; maxindex ) mi = size[i] - 1;
152     assert( index <= getIndex( size, maxindex ), "range violation" );
153 }
154 body
155 {
156     size_t buf = index;
157     auto ret = new size_t[]( size.length );
158     foreach_reverse( i; 0 .. size.length )
159     {
160         auto vol = reduce!((a,b)=>(a*=b))(1U,size[0..i]);
161         ret[i] = buf / vol;
162         buf = buf % vol;
163     }
164     return ret;
165 }
166 
167 ///
168 unittest
169 {
170     assertEq( getCoord( [3,3,3], 13 ), [1,1,1] );
171     auto size = CrdVector!4( 10, 20, 30, 40 );
172     auto crd = CrdVector!4( 3, 5, 8, 10 );
173     assertEq( crd, getCoord( size, getIndex( size, crd ) ) );
174 }
175 
176 /// get line index in origin array by layer line index and layer number
177 size_t getOrigIndexByLayerCoord(A)( in A[] size, size_t dimNo,
178                                     size_t layerIndex, size_t layerNo ) pure
179 if( isIntegral!A )
180 in
181 {
182     assert( isAllCompPositive( size ), "negative size" );
183     assert( dimNo < size.length );
184 }
185 body
186 {
187     auto layerCrd = getCoord( cut( size, dimNo ), layerIndex );
188     return getIndex( size, paste( layerCrd, dimNo, layerNo ) );
189 }
190 
191 unittest
192 {
193     assertEq( getOrigIndexByLayerCoord( [3,3,3], 2, 4, 1 ), 13 );
194 }
195 
196 T[] cut(T)( in T[] arr, size_t N ) pure
197 in{ assert( N < arr.length ); } body
198 { return arr[0..N].dup ~ ( arr.length-1 == N ? [] : arr[N+1..$] ); }
199 
200 unittest
201 {
202     assertEq( [1,2,3,4].cut(0), [2,3,4] );
203     assertEq( [1,2,3,4].cut(2), [1,2,4] );
204     assertEq( [1,2,3,4].cut(3), [1,2,3] );
205 }
206 
207 T[] paste(T)( in T[] arr, size_t N, T value ) pure
208 in{ assert( N <= arr.length ); } body
209 { return arr[0..N].dup ~ value ~ ( arr.length == N ? [] : arr[N..$] ); }
210 
211 unittest
212 {
213     assertEq( [1,2,3,4].paste(0,8), [8,1,2,3,4] );
214     assertEq( [1,2,3,4].paste(3,8), [1,2,3,8,4] );
215     assertEq( [1,2,3,4].paste(4,8), [1,2,3,4,8] );
216 }
217 
218 unittest
219 {
220     auto orig = [1,2,3,4];
221     assertEq( orig.paste(0,666).cut(0), orig );
222     assertEq( orig.paste(1,666).cut(1), orig );
223     assertEq( orig.paste(2,666).cut(2), orig );
224     assertEq( orig.paste(3,666).cut(3), orig );
225 }