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 }