1 module isodi.utils;
2 
3 import raylib;
4 import core.stdc.stdlib;
5 
6 import std.string;
7 import std.random;
8 import std.functional;
9 
10 
11 @safe:
12 
13 
14 /// Integer vector.
15 ///
16 /// Note: Internally this might be later translated to a float anyway, so it might not be precise.
17 struct Vector2I {
18 
19     align (4):
20     int x, y;
21 
22     Vector2I opBinary(string op)(Vector2I vec) const => Vector2I(
23         mixin("x" ~ op ~ "vec.x"),
24         mixin("y" ~ op ~ "vec.y"),
25     );
26 
27     /// Get hash of the position.
28     size_t toHash() const nothrow @nogc => y + 0x9e3779b9 + (x << 6) + (x >>> 2);
29     // Taken from https://github.com/dlang/phobos/blob/master/std/typecons.d#L1234
30     // Which in turn takes from https://www.boost.org/doc/libs/1_55_0/doc/html/hash/reference.html#boost.hash_combine
31 
32 }
33 
34 struct RectangleI {
35 
36     align (4):
37     int x, y;
38     int width, height;
39 
40     /// Convert the rectangle to a shader rectangle relative to texture size.
41     Rectangle toShader(Vector2 textureSize) @nogc const => Rectangle(
42         x / textureSize.x,
43         y / textureSize.y,
44         width  / textureSize.x,
45         height / textureSize.y,
46     );
47 
48 }
49 
50 /// Perform a reinterpret cast.
51 ///
52 /// Note: Using `cast()` for reinterpret casts is not legal for values, per spec. DMD does allow that, but LDC segfaults.
53 To reinterpret(To, From)(From from)
54 in {
55     static assert(From.sizeof == To.sizeof, "From and To type size mismatch");
56 }
57 do {
58 
59     union Union {
60         From before;
61         To after;
62     }
63 
64     // Write the value to the union and read as the target
65     return Union(from).after;
66 
67 }
68 
69 /// Make a simple struct out of a static array. Used to apply `.tupleof` on the arrays in DMD 2.099.
70 auto structof(T, size_t size)(T[size] value) {
71 
72     struct StructOf {
73         static foreach (i; 0..size) {
74             mixin(format!"T t%s;"(i));
75         }
76     }
77     return value.reinterpret!StructOf;
78 
79 }
80 
81 /// Multiplying matrices the proper way
82 Matrix mul(Matrix[] ms...) @trusted @nogc nothrow {
83 
84     auto result = ms[0];
85 
86     foreach (m; ms[1..$]) {
87 
88         result = MatrixMultiply(result, m);
89 
90     }
91 
92     return result;
93 
94 }
95 
96 /// Assign a single chunk of values to an array, assuming the array is made up of fixed size chunks.
97 void assignChunk(T)(T[] range, size_t index, T[] values...) @nogc pure {
98 
99     foreach (i, value; values) {
100 
101         range[values.length * index + i] = value;
102 
103     }
104 
105 }
106 
107 ///
108 @system
109 unittest {
110 
111     int[6] foo;
112 
113     foreach (i; 0 .. foo.length/2) {
114 
115         foo[].assignChunk(i, 1, 2);
116 
117     }
118 
119     assert(foo == [1, 2, 1, 2, 1, 2]);
120 
121 }
122 
123 /// Assign a single chunk of values to an array, altering the array first.
124 template assignChunk(alias fun) {
125 
126     void assignChunk(T, U)(T[] range, size_t index, U[] values...) @nogc pure {
127 
128         alias funnier = unaryFun!fun;
129 
130         foreach (i, value; values) {
131 
132             range[values.length * index + i] = funnier(value);
133 
134         }
135 
136     }
137 
138 }
139 
140 ///
141 @system
142 unittest {
143 
144     int[6] foo;
145 
146     foreach (i; 0 .. foo.length/2) {
147 
148         foo[].assignChunk!(a => cast(int) i*2 + a)(i, 1, 2);
149 
150     }
151 
152     assert(foo == [1, 2, 3, 4, 5, 6]);
153 
154 }
155 
156 /// Assign multiple copies of the same value to the array, indexing it by chunks of the same size.
157 void assign(T)(T[] range, size_t index, size_t count, T value) @nogc pure {
158 
159     const start = count * index;
160     range[start .. start + count] = value;
161 
162 }
163 
164 /// Iterate on file ancestors, starting from and including the requested file and ending on the root.
165 struct DeepAncestors {
166 
167     const string path;
168 
169     int opApply(scope int delegate(string) @trusted dg) {
170 
171         auto dir = path[];
172 
173         while (dir.length) {
174 
175             // Remove trailing slashes
176             dir = dir.stripRight("/");
177 
178             // Yield the content
179             auto result = dg(dir);
180             if (result) return result;
181 
182             // Get the position on which the segment ends
183             auto segmentEnd = dir.lastIndexOf("/");
184 
185             // Stop if this is the last segment
186             if (segmentEnd == -1) break;
187 
188             // Remove last path segment
189             dir = dir[0 .. segmentEnd];
190 
191         }
192 
193         // Push empty path
194         return dg("");
195 
196     }
197 
198 }
199 
200 /// Iterate on file ancestors starting from root, ending on and including the file itself.
201 struct Ancestors {
202 
203     const wstring path;
204 
205     int opApply(int delegate(wstring) @trusted dg) {
206 
207         wstring current;
208 
209         auto result = dg(""w);
210         if (result) return result;
211 
212         // Check each value
213         foreach (ch; path) {
214 
215             // Encountered a path separator
216             if (ch == '/' || ch == '\\') {
217 
218                 // Yield the current values
219                 result = dg(current);
220                 if (result) return result;
221                 current ~= "/";
222 
223             }
224 
225             // Add the character
226             else current ~= ch;
227 
228         }
229 
230         // Yield the full path
231         if (current.length && current[$-1] != '/') {
232 
233             result = dg(current);
234             return result;
235 
236         }
237 
238         return 0;
239 
240     }
241 
242 }
243 
244 /// Get a random number in inclusive range.
245 T randomNumber(T, RNG)(T min, T max, ref RNG rng) @trusted
246 in (min <= max, "minimum value must be lesser or equal to max")
247 do {
248 
249     import std.conv;
250 
251     // This is a copy-paste from https://github.com/dlang/phobos/blob/v2.100.0/std/random.d#L2218
252     // but made @nogc
253 
254     auto upperDist = unsigned(max - min) + 1u;
255     assert(upperDist != 0);
256 
257     alias UpperType = typeof(upperDist);
258     static assert(UpperType.min == 0);
259 
260     UpperType offset, rnum, bucketFront;
261     do {
262 
263         rnum = uniform!UpperType(rng);
264         offset = rnum % upperDist;
265         bucketFront = rnum - offset;
266     }
267 
268     // While we're in an unfair bucket...
269     while (bucketFront > (UpperType.max - (upperDist - 1)));
270 
271     return cast(T) (min + offset);
272 
273 }
274 
275 /// Generate a random variant in the given atlas.
276 /// Param:
277 ///     atlasSize = Size of the atlas used.
278 ///     resultSize = Expected size of the result
279 ///     seed = Seed to use
280 /// Returns: Offset of the variant texture in the given atlas.
281 Vector2I randomVariant(Vector2I atlasSize, Vector2I resultSize, ulong seed) @nogc {
282 
283     auto rng = Mt19937_64(seed);
284 
285     // Get the grid dimensions
286     const gridSize = atlasSize / resultSize;
287 
288     // Get the variant number
289     const tileVariantCount = gridSize.x * gridSize.y;
290     assert(tileVariantCount > 0, "Invalid atlasSize or resultSize, all parameters must be positive");
291 
292     const variant = randomNumber(0, tileVariantCount-1, rng);
293 
294     // Get the offset
295     return resultSize * Vector2I(
296         variant % gridSize.x,
297         variant / gridSize.x,
298     );
299 
300 }
301 
302 /// Awful workaround to get writefln in @nogc :D
303 /// Yes, it does allocate in the GC.
304 package debug template writefln(T...) {
305 
306     void writefln(Args...)(Args args) @nogc @system {
307 
308         // We can GC-allocate in writeln, no need to care about that
309         scope auto fundebug = delegate {
310 
311             import io = std.stdio;
312             io.writefln!T(args);
313 
314         };
315         (cast(void delegate() @nogc) fundebug)();
316 
317     }
318 
319 }