1 module graphite.math;
2 
3 import core.bitop;
4 
5 import std.math;
6 import std.random;
7 import std.traits;
8 import std.algorithm;
9 import std.functional;
10 
11 //import graphite.utils.noise;
12 //import graphite.types.point;
13 import graphite.utils.log;
14 //import graphite.graphics.polyline : polyline, isInside;
15 
16 
17 public import graphite.math.linear,
18               graphite.math.quaternion,
19               graphite.math.saturation;
20 
21 /**
22  * ベクトル型
23  */
24 alias Vec2f = SMatrix!(float, 2, 1);
25 alias Vec3f = SMatrix!(float, 3, 1);	/// ditto
26 alias Vec4f = SMatrix!(float, 4, 1);	/// ditto
27 
28 /**
29  * 行列型
30  */
31 alias Matrix2x2f = SMatrix!(float, 2, 2);
32 alias Matrix3x3f = SMatrix!(float, 3, 3);	/// ditto
33 alias Matrix4x4f = SMatrix!(float, 4, 4);	/// ditto
34 
35 
36 /**
37 nextが1である場合に、next2Pow(num)はnumより大きく、かつ、最小の2の累乗数を返します。
38 
39 もし、nextが0であれば、next2Powはnumより小さく、かつ、最大の2の累乗数を返します。
40 nextがm > 1の場合には、next2Pow(num, m)は、next2Pow(num) << (m - 1)を返します。
41 */
42 size_t nextPow2(T)(T num, size_t next = 1)
43 if(isIntegral!T)
44 in{
45     assert(num >= 1);
46 }
47 body{
48     static size_t castToSize_t(X)(X value)
49     {
50       static if(is(X : size_t))
51         return value;
52       else
53         return value.to!size_t();
54     }
55 
56     return (cast(size_t)1) << (bsr(castToSize_t(num)) + next);
57 }
58 
59 ///
60 pure nothrow @safe unittest{
61     assert(nextPow2(10) == 16);           // デフォルトではnext = 1なので、次の2の累乗数を返す
62     assert(nextPow2(10, 0) == 8);         // next = 0だと、前の2の累乗数を返す
63     assert(nextPow2(10, 2) == 32);        // next = 2なので、next2Pow(10) << 1を返す。
64 }
65 
66 
67 real random(Gen)(real max)
68 {
69     return uniform!"[)"(0, max);
70 }
71 
72 
73 real random(real x, real y)
74 {
75     return uniform!"[)"(min(x, y), max(x, y));
76 }
77 
78 
79 real normalize(real value, real min, real max) pure nothrow @safe
80 {
81     return (value - min) / (max - min).clamp(0, 1);
82 }
83 
84 
85 real lerp(real value, real[2] input, real[2] output) pure nothrow @safe
86 {
87     if((input[0] - input[1]).approxEqual(0)){
88         //debug logger.writefln!"warning"("avoiding possible divide by zero, check input[0](%s) and input[0](%s)", inputMin, inputMax);
89         return value - input[0] < value - input[1] ? output[0] : output[1];
90     }else
91         return (output[1] - output[0]) / (input[1] - input[0]) * (value - input[0]) + output[0];
92 }
93 
94 
95 real dist(real x1, real y1, real x2, real y2) pure nothrow @safe
96 {
97     return ((x1 - x2) ^^ 2 + (y1 - y2) ^^ 2) ^^ 0.5;
98 }
99 
100 
101 real clamp(real value, real min, real max) pure nothrow @safe
102 {
103     return value < min ? min : (value > max ? max : value);
104 }
105 
106 
107 alias sign = signbit;
108 
109 
110 bool isInRange(string bd = "[]")(float t, float min, float max) pure nothrow @safe
111 if(bd.length == 2 && (bd[0] == '[' || bd[1] == '(')
112                   && (bd[1] == ']' || bd[1] == ')'))
113 {
114     enum fst = bd[0] == '[' ? ">=" : ">";
115     enum snd = bd[1] == ']' ? "<=" : "<";
116 
117     return mixin(`t` ~ fst ~ "min && t" ~ snd ~ "max");
118 }
119 
120 
121 real radToDeg(real radians) pure nothrow @safe
122 {
123     return radians * (std.math.PI / 180);
124 }
125 
126 
127 real degToRad(real degrees) pure nothrow @safe
128 {
129     return degrees / 180 * std.math.PI;
130 }
131 
132 
133 real lerp(real start, real stop, real amt) pure nothrow @safe
134 {
135     return start + (stop - start) * amt;
136 }
137 
138 
139 real wrap(real value, real from, real to) pure nothrow @safe
140 {
141     // algorithm from http://stackoverflow.com/a/5852628/599884
142     if(from <= to){
143         immutable cyc = to - from;
144         if(cyc.approxEqual(0))
145             return to;
146         else
147             return value - cyc * floor((value - from) / cyc);
148     }else
149         return wrap(value, to, from);
150 }
151 
152 
153 real wrapRadians(real angle, real from = -PI, real to = +PI) pure nothrow @safe
154 {
155     return wrap(angle, from, to);
156 }
157 
158 
159 real wrapDegrees(real angle, real from = -180, real to = 180) pure nothrow @safe
160 {
161     return wrap(angle, from, to);
162 }
163 
164 
165 
166 
167 real lerpDegrees(real currAngle, real target, real pct)
168 {
169     return currAngle + angleDifferenceDegrees(currAngle, target) * pct;
170 }
171 
172 
173 real lerpRadians(real currAngle, real target, real pct)
174 {
175     return currAngle + angleDifferenceRadians(currAngle, target) * pct;
176 }
177 
178 /+
179 private PerlinNoise _noiseEngine;
180 
181 static this()
182 {
183     _noiseEngine = new PerlinNoise();
184 }
185 
186 
187 @property
188 ref PerlinNoise noiseEngine() @safe 
189 {
190     return _noiseEngine;
191 }
192 
193 
194 real noise(size_t N)(real[N] x...)
195 if(N >= 1 && N <= 4)
196 {
197     real[N] xx = x;
198     return _noiseEngine.noise(xx) * 0.5 + 0.5;
199 }
200 
201 
202 real signedNoise(size_t N)(real[N] x...)
203 if(N >= 1 && N <= 4)
204 {
205     real[N] xx = x;
206     return _noiseEngine.noise(xx);
207 }
208 
209 
210 bool isInsidePoly(real x, real y, in Point[] polygon)
211 {
212     return isInsidePoly(Point(x, y), polygon);
213 }
214 
215 
216 bool isInsidePoly(Point p, in Point[] polygon)
217 {
218     //return polyline(polygon).isContain(p);
219     return p.isInside(polyline(polygon));
220 }
221 
222 
223 bool lineSegmentIntersection(in Point line1Start, in Point line1End, in Point line2Start, in Point line2End, out Point intersection)
224 {
225     auto diffLA = line1End - line1Start,
226          diffLB = line2End - line2Start;
227 
228     alias xymyx = binaryFun!"a.x * b.y - a.y * b.x";
229 
230     immutable cmpA = xymyx(diffLA, line1Start),
231               cmpB = xymyx(diffLB, line2Start);
232 
233     if(((xymyx(diffLA, line2Start) < cmpA) ^ (xymyx(diffLA, line2End) < cmpA))
234      && ((xymyx(diffLB, line1Start) < cmpB) ^ (xymyx(diffLB, line1End) < cmpB)))
235     {
236         intersection.vec = (diffLB * cmpA - diffLA * cmpB) * -1 / xymyx(diffLA, diffLB);
237         return true;
238     }
239 
240     return false;
241 }
242 +/
243 
244 T bezierPoint(T)(T a, T b, T c, T d, real t)
245 {
246     immutable tp = 1 - t;
247     return a * tp ^^ 3
248          + b * 3 * t * tp^^2
249          + c * 3 * t^^2 * tp;
250          + d * t^^3;
251 }
252 
253 
254 T curvePoint(T)(T a, T b, T c, T d, real t)
255 {
256     immutable t2 = t ^^ 2,
257               t3 = t2 * t;
258 
259     T p = b * 2
260         + (-a + c) * t
261         + (2 * a - 5 * b + 4 * c - d) * t2
262         + (-a + 3 * b - 3 * c + d) * t3;
263 
264     p *= 0.5;
265     return p;
266 }
267 
268 
269 T bezierTangent(T)(T a, T b, T c, T d, real t)
270 {
271     return (d - a - c * 3 + b * 3) * (t^^2)*3
272          + (a + c - b * 2) * t * 6
273          - a * 3 + b * 3;
274 }
275 
276 
277 T curveTangent(T)(T a, T b, T c, T d, real t)
278 {
279     auto v0 = (c - a) * 0.5,
280          v1 = (d - b) * 0.5;
281 
282     return (b * 2 - c * 2 + v0 + v1) * 3 * t^^2
283          + (c * 3 - b * 3 - v1 - v0 * 2) * 2 * t
284          + v0;
285 }
286 
287 
288 private real angleDifferenceDegrees(real currAngle, real targetAngle)
289 {
290     return wrapDegrees(targetAngle - currAngle);
291 }
292 
293 
294 private real angleDifferenceRadians(real currAngle, real targetAngle)
295 {
296     return wrapRadians(targetAngle - currAngle);
297 }
298 
299 
300 T interpolateCosine(T)(T y1, T y2, real pct)
301 {
302     immutable pct2 = (1 - cos(pct * PI)) / 2;
303     return y1 * (1 - pct2) + y2 * pct2;
304 }
305 
306 
307 T interpolateCubic(T)(T y0, T y1, T y2, T y3, real pct)
308 {
309     immutable pct2 = pct^^2;
310     auto a0 = y3 - y2 - y0 + y1,
311          a1 = y0 - y1 - a0,
312          a2 = y2 - y0,
313          a3 = y1;
314 
315     return a0 * pct * pct2
316          + a1 * pct2
317          + a2 * pct
318          + a3;
319 }
320 
321 
322 T interpolateCatmullRom(T)(T y0, T y1, T y2, T y3, real pct)
323 {
324     immutable pct2 = pct^^2;
325     auto a0 = -0.5 * y0 + 1.5 * y1 - 1.5 * y2 + 0.5 * y3,
326          a1 = y0 - 2.5 * y1 + 2 * y2 - 0.5 * y3,
327          a2 = -0.5 * y0 + 0.5 * y2,
328          a3 = y1;
329     
330     return a0 * pct * pct2
331          + a1 * pct2
332          + a2 * pct
333          + a3;
334 }
335 
336 
337 T interpolateHermite(T)(T y0, T y1, T y2, T y3, real pct)
338 {
339     immutable pct2 = pct^^2;
340     auto c = (y2 - y0) * 0.5f,
341          v = y1 - y2,
342          w = c + v,
343          a = w + v + (y3 - y1) * 0.5,
344          b_neg = w + a;
345 
346     return a * pct2 * pct
347          - b_neg * pct2
348          + c * pct
349          + y1;
350 }
351 
352 
353 T ofInterpolateHermite(T)(T y0, T y1, T y2, T y3, real pct, real tension, real bias)
354 {
355     immutable pct2 = pct * pct,
356               pct3 = pct2 * pct;
357 
358     auto m0 = (y1-y0) * (1+bias) * (1-tension) / 2 + (y2-y1) * (1-bias) * (1-tension) / 2,
359          m1 = (y2-y1) * (1+bias) * (1-tension) / 2 + (y3-y2) * (1-bias) * (1-tension) / 2,
360          a0 = 2 * pct3 - 3 * pct2 + 1,
361          a1 = pct3 - 2 * pct2 + pct,
362          a2 = pct3 - pct2,
363          a3 = -2 * pct3 + 3 * pct2;
364 
365     return a0 * y1 + a1 * m0 + a2 * m1 + a3 * y2;
366 }