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