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 }