| 1 | // Copyright 2005 Huxtable.com. All rights reserved. |
| 2 | // For more information, see <http://www.jhlabs.com/ip/blurring.html>. |
| 3 | package com.jhlabs.image; |
| 4 | |
| 5 | /** |
| 6 | * A class containing static math methods useful for image processing. |
| 7 | */ |
| 8 | public final class ImageMath |
| 9 | { |
| 10 | |
| 11 | public static final float PI = (float) Math.PI; |
| 12 | public static final float HALF_PI = (float) Math.PI / 2.0f; |
| 13 | public static final float QUARTER_PI = (float) Math.PI / 4.0f; |
| 14 | public static final float TWO_PI = (float) Math.PI * 2.0f; |
| 15 | |
| 16 | // Catmull-Rom splines |
| 17 | private static final float M00 = -0.5f; |
| 18 | private static final float M01 = 1.5f; |
| 19 | private static final float M02 = -1.5f; |
| 20 | private static final float M03 = 0.5f; |
| 21 | private static final float M10 = 1.0f; |
| 22 | private static final float M11 = -2.5f; |
| 23 | private static final float M12 = 2.0f; |
| 24 | private static final float M13 = -0.5f; |
| 25 | private static final float M20 = -0.5f; |
| 26 | private static final float M21 = 0.0f; |
| 27 | private static final float M22 = 0.5f; |
| 28 | private static final float M23 = 0.0f; |
| 29 | private static final float M30 = 0.0f; |
| 30 | private static final float M31 = 1.0f; |
| 31 | private static final float M32 = 0.0f; |
| 32 | private static final float M33 = 0.0f; |
| 33 | |
| 34 | private ImageMath() { } |
| 35 | |
| 36 | /** |
| 37 | * Apply a bias to a number in the unit interval, moving numbers towards 0 or 1 according to |
| 38 | * the bias parameter. |
| 39 | * |
| 40 | * @return the output value |
| 41 | * @param a the number to bias |
| 42 | * @param b the bias parameter. 0.5 means no change, smaller values bias towards 0, larger |
| 43 | * towards 1. |
| 44 | */ |
| 45 | public static float bias(float a, float b) { |
| 46 | return a / ((1.0f / b - 2) * (1.0f - a) + 1); |
| 47 | } |
| 48 | |
| 49 | /** |
| 50 | * Bilinear interpolation of ARGB values. |
| 51 | * |
| 52 | * @return the interpolated value |
| 53 | * @param x the X interpolation parameter 0..1 |
| 54 | * @param y the y interpolation parameter 0..1 |
| 55 | * @param argbs array of four ARGB values in the order NW, NE, SW, SE |
| 56 | */ |
| 57 | public static int bilinearInterpolate(float x, float y, int[] argbs) { |
| 58 | float m0; |
| 59 | float m1; |
| 60 | int a0 = (argbs[0] >> 24) & 0xff; |
| 61 | int r0 = (argbs[0] >> 16) & 0xff; |
| 62 | int g0 = (argbs[0] >> 8) & 0xff; |
| 63 | int b0 = argbs[0] & 0xff; |
| 64 | int a1 = (argbs[1] >> 24) & 0xff; |
| 65 | int r1 = (argbs[1] >> 16) & 0xff; |
| 66 | int g1 = (argbs[1] >> 8) & 0xff; |
| 67 | int b1 = argbs[1] & 0xff; |
| 68 | int a2 = (argbs[2] >> 24) & 0xff; |
| 69 | int r2 = (argbs[2] >> 16) & 0xff; |
| 70 | int g2 = (argbs[2] >> 8) & 0xff; |
| 71 | int b2 = argbs[2] & 0xff; |
| 72 | int a3 = (argbs[3] >> 24) & 0xff; |
| 73 | int r3 = (argbs[3] >> 16) & 0xff; |
| 74 | int g3 = (argbs[3] >> 8) & 0xff; |
| 75 | int b3 = argbs[3] & 0xff; |
| 76 | |
| 77 | float cx = 1.0f - x; |
| 78 | float cy = 1.0f - y; |
| 79 | |
| 80 | m0 = cx * a0 + x * a1; |
| 81 | m1 = cx * a2 + x * a3; |
| 82 | |
| 83 | int a = (int) (cy * m0 + y * m1); |
| 84 | |
| 85 | m0 = cx * r0 + x * r1; |
| 86 | m1 = cx * r2 + x * r3; |
| 87 | |
| 88 | int r = (int) (cy * m0 + y * m1); |
| 89 | |
| 90 | m0 = cx * g0 + x * g1; |
| 91 | m1 = cx * g2 + x * g3; |
| 92 | |
| 93 | int g = (int) (cy * m0 + y * m1); |
| 94 | |
| 95 | m0 = cx * b0 + x * b1; |
| 96 | m1 = cx * b2 + x * b3; |
| 97 | |
| 98 | int b = (int) (cy * m0 + y * m1); |
| 99 | |
| 100 | return (a << 24) | (r << 16) | (g << 8) | b; |
| 101 | } |
| 102 | |
| 103 | /** |
| 104 | * Return the NTSC gray level of an RGB value. |
| 105 | * |
| 106 | * @return the gray level (0-255) |
| 107 | * @param rgb the input pixel |
| 108 | */ |
| 109 | public static int brightnessNTSC(int rgb) { |
| 110 | int r = (rgb >> 16) & 0xff; |
| 111 | int g = (rgb >> 8) & 0xff; |
| 112 | int b = rgb & 0xff; |
| 113 | |
| 114 | return (int) (r * 0.299f + g * 0.587f + b * 0.114f); |
| 115 | } |
| 116 | |
| 117 | /** |
| 118 | * A "circle down" function. Returns 1-y on a unit circle given x. Useful for forming bevels. |
| 119 | * |
| 120 | * @return the output value |
| 121 | * @param x the input parameter in the range 0..1 |
| 122 | */ |
| 123 | public static float circleDown(float x) { |
| 124 | return 1.0f - (float) Math.sqrt(1 - x * x); |
| 125 | } |
| 126 | |
| 127 | /** |
| 128 | * A "circle up" function. Returns y on a unit circle given 1-x. Useful for forming bevels. |
| 129 | * |
| 130 | * @return the output value |
| 131 | * @param x the input parameter in the range 0..1 |
| 132 | */ |
| 133 | public static float circleUp(float x) { |
| 134 | x = 1 - x; |
| 135 | return (float) Math.sqrt(1 - x * x); |
| 136 | } |
| 137 | |
| 138 | /** |
| 139 | * Clamp a value to an interval. |
| 140 | * |
| 141 | * @return the clamped value |
| 142 | * @param x the input parameter |
| 143 | * @param a the lower clamp threshold |
| 144 | * @param b the upper clamp threshold |
| 145 | */ |
| 146 | public static float clamp(float x, float a, float b) { |
| 147 | return (x < a) ? a : (x > b) ? b : x; |
| 148 | } |
| 149 | |
| 150 | /** |
| 151 | * Clamp a value to an interval. |
| 152 | * |
| 153 | * @return the clamped value |
| 154 | * @param x the input parameter |
| 155 | * @param a the lower clamp threshold |
| 156 | * @param b the upper clamp threshold |
| 157 | */ |
| 158 | public static int clamp(int x, int a, int b) { |
| 159 | return (x < a) ? a : (x > b) ? b : x; |
| 160 | } |
| 161 | |
| 162 | public static int clamp(int x) { |
| 163 | return clamp(x, 0, 255); |
| 164 | } |
| 165 | |
| 166 | /** |
| 167 | * Compute a Catmull-Rom spline for RGB values. |
| 168 | * |
| 169 | * @return the spline value |
| 170 | * @param x the input parameter |
| 171 | * @param numKnots the number of knots in the spline |
| 172 | * @param knots the array of knots |
| 173 | */ |
| 174 | public static int colorSpline(float x, int numKnots, int[] knots) { |
| 175 | int span; |
| 176 | int numSpans = numKnots - 3; |
| 177 | float k0; |
| 178 | float k1; |
| 179 | float k2; |
| 180 | float k3; |
| 181 | float c0; |
| 182 | float c1; |
| 183 | float c2; |
| 184 | float c3; |
| 185 | |
| 186 | if (numSpans < 1) { |
| 187 | throw new IllegalArgumentException("Too few knots in spline"); |
| 188 | } |
| 189 | |
| 190 | x = clamp(x, 0, 1) * numSpans; |
| 191 | span = (int) x; |
| 192 | if (span > numKnots - 4) { |
| 193 | span = numKnots - 4; |
| 194 | } |
| 195 | x -= span; |
| 196 | |
| 197 | int v = 0; |
| 198 | |
| 199 | for (int i = 0; i < 4; i++) { |
| 200 | int shift = i * 8; |
| 201 | |
| 202 | k0 = (knots[span] >> shift) & 0xff; |
| 203 | k1 = (knots[span + 1] >> shift) & 0xff; |
| 204 | k2 = (knots[span + 2] >> shift) & 0xff; |
| 205 | k3 = (knots[span + 3] >> shift) & 0xff; |
| 206 | |
| 207 | c3 = M00 * k0 + M01 * k1 + M02 * k2 + M03 * k3; |
| 208 | c2 = M10 * k0 + M11 * k1 + M12 * k2 + M13 * k3; |
| 209 | c1 = M20 * k0 + M21 * k1 + M22 * k2 + M23 * k3; |
| 210 | c0 = M30 * k0 + M31 * k1 + M32 * k2 + M33 * k3; |
| 211 | |
| 212 | int n = (int) (((c3 * x + c2) * x + c1) * x + c0); |
| 213 | |
| 214 | if (n < 0) { |
| 215 | n = 0; |
| 216 | } else if (n > 255) { |
| 217 | n = 255; |
| 218 | } |
| 219 | v |= n << shift; |
| 220 | } |
| 221 | |
| 222 | return v; |
| 223 | } |
| 224 | |
| 225 | /** |
| 226 | * Compute a Catmull-Rom spline for RGB values, but with variable knot spacing. |
| 227 | * |
| 228 | * @return the spline value |
| 229 | * @param x the input parameter |
| 230 | * @param numKnots the number of knots in the spline |
| 231 | * @param xknots the array of knot x values |
| 232 | * @param yknots the array of knot y values |
| 233 | */ |
| 234 | public static int colorSpline(int x, int numKnots, int[] xknots, int[] yknots) { |
| 235 | int span; |
| 236 | int numSpans = numKnots - 3; |
| 237 | float k0; |
| 238 | float k1; |
| 239 | float k2; |
| 240 | float k3; |
| 241 | float c0; |
| 242 | float c1; |
| 243 | float c2; |
| 244 | float c3; |
| 245 | |
| 246 | if (numSpans < 1) { |
| 247 | throw new IllegalArgumentException("Too few knots in spline"); |
| 248 | } |
| 249 | |
| 250 | for (span = 0; span < numSpans; span++) { |
| 251 | if (xknots[span + 1] > x) { |
| 252 | break; |
| 253 | } |
| 254 | } |
| 255 | if (span > numKnots - 3) { |
| 256 | span = numKnots - 3; |
| 257 | } |
| 258 | float t = (float) (x - xknots[span]) / (xknots[span + 1] - xknots[span]); |
| 259 | |
| 260 | span--; |
| 261 | if (span < 0) { |
| 262 | span = 0; |
| 263 | t = 0; |
| 264 | } |
| 265 | |
| 266 | int v = 0; |
| 267 | |
| 268 | for (int i = 0; i < 4; i++) { |
| 269 | int shift = i * 8; |
| 270 | |
| 271 | k0 = (yknots[span] >> shift) & 0xff; |
| 272 | k1 = (yknots[span + 1] >> shift) & 0xff; |
| 273 | k2 = (yknots[span + 2] >> shift) & 0xff; |
| 274 | k3 = (yknots[span + 3] >> shift) & 0xff; |
| 275 | |
| 276 | c3 = M00 * k0 + M01 * k1 + M02 * k2 + M03 * k3; |
| 277 | c2 = M10 * k0 + M11 * k1 + M12 * k2 + M13 * k3; |
| 278 | c1 = M20 * k0 + M21 * k1 + M22 * k2 + M23 * k3; |
| 279 | c0 = M30 * k0 + M31 * k1 + M32 * k2 + M33 * k3; |
| 280 | |
| 281 | int n = (int) (((c3 * t + c2) * t + c1) * t + c0); |
| 282 | |
| 283 | if (n < 0) { |
| 284 | n = 0; |
| 285 | } else if (n > 255) { |
| 286 | n = 255; |
| 287 | } |
| 288 | v |= n << shift; |
| 289 | } |
| 290 | |
| 291 | return v; |
| 292 | } |
| 293 | |
| 294 | /** |
| 295 | * A variant of the gamma function. |
| 296 | * |
| 297 | * @return the output value |
| 298 | * @param a the number to apply gain to |
| 299 | * @param b the gain parameter. 0.5 means no change, smaller values reduce gain, larger values |
| 300 | * increase gain. |
| 301 | */ |
| 302 | public static float gain(float a, float b) { |
| 303 | float result; |
| 304 | float c = (1.0f / b - 2.0f) * (1.0f - 2.0f * a); |
| 305 | |
| 306 | if (a < 0.5) { |
| 307 | result = a / (c + 1.0f); |
| 308 | } else { |
| 309 | result = (c - a) / (c - 1.0f); |
| 310 | } |
| 311 | return result; |
| 312 | } |
| 313 | |
| 314 | /** |
| 315 | * Linear interpolation. |
| 316 | * |
| 317 | * @return the interpolated value |
| 318 | * @param t the interpolation parameter |
| 319 | * @param a the lower interpolation range |
| 320 | * @param b the upper interpolation range |
| 321 | */ |
| 322 | public static float lerp(float t, float a, float b) { |
| 323 | return a + t * (b - a); |
| 324 | } |
| 325 | |
| 326 | /** |
| 327 | * Linear interpolation. |
| 328 | * |
| 329 | * @return the interpolated value |
| 330 | * @param t the interpolation parameter |
| 331 | * @param a the lower interpolation range |
| 332 | * @param b the upper interpolation range |
| 333 | */ |
| 334 | public static int lerp(float t, int a, int b) { |
| 335 | return (int) (a + t * (b - a)); |
| 336 | } |
| 337 | |
| 338 | /** |
| 339 | * Linear interpolation of ARGB values. |
| 340 | * |
| 341 | * @return the interpolated value |
| 342 | * @param t the interpolation parameter |
| 343 | * @param rgb1 the lower interpolation range |
| 344 | * @param rgb2 the upper interpolation range |
| 345 | */ |
| 346 | public static int mixColors(float t, int rgb1, int rgb2) { |
| 347 | int a1 = (rgb1 >> 24) & 0xff; |
| 348 | int r1 = (rgb1 >> 16) & 0xff; |
| 349 | int g1 = (rgb1 >> 8) & 0xff; |
| 350 | int b1 = rgb1 & 0xff; |
| 351 | int a2 = (rgb2 >> 24) & 0xff; |
| 352 | int r2 = (rgb2 >> 16) & 0xff; |
| 353 | int g2 = (rgb2 >> 8) & 0xff; |
| 354 | int b2 = rgb2 & 0xff; |
| 355 | |
| 356 | a1 = lerp(t, a1, a2); |
| 357 | r1 = lerp(t, r1, r2); |
| 358 | g1 = lerp(t, g1, g2); |
| 359 | b1 = lerp(t, b1, b2); |
| 360 | return (a1 << 24) | (r1 << 16) | (g1 << 8) | b1; |
| 361 | } |
| 362 | |
| 363 | /** |
| 364 | * Return a mod b. This differs from the % operator with respect to negative numbers. |
| 365 | * |
| 366 | * @return a mod b |
| 367 | * @param a the dividend |
| 368 | * @param b the divisor |
| 369 | */ |
| 370 | public static double mod(double a, double b) { |
| 371 | int n = (int) (a / b); |
| 372 | |
| 373 | a -= n * b; |
| 374 | if (a < 0) { |
| 375 | return a + b; |
| 376 | } |
| 377 | return a; |
| 378 | } |
| 379 | |
| 380 | /** |
| 381 | * Return a mod b. This differs from the % operator with respect to negative numbers. |
| 382 | * |
| 383 | * @return a mod b |
| 384 | * @param a the dividend |
| 385 | * @param b the divisor |
| 386 | */ |
| 387 | public static float mod(float a, float b) { |
| 388 | int n = (int) (a / b); |
| 389 | |
| 390 | a -= n * b; |
| 391 | if (a < 0) { |
| 392 | return a + b; |
| 393 | } |
| 394 | return a; |
| 395 | } |
| 396 | |
| 397 | /** |
| 398 | * Return a mod b. This differs from the % operator with respect to negative numbers. |
| 399 | * |
| 400 | * @return a mod b |
| 401 | * @param a the dividend |
| 402 | * @param b the divisor |
| 403 | */ |
| 404 | public static int mod(int a, int b) { |
| 405 | int n = a / b; |
| 406 | |
| 407 | a -= n * b; |
| 408 | if (a < 0) { |
| 409 | return a + b; |
| 410 | } |
| 411 | return a; |
| 412 | } |
| 413 | |
| 414 | /** |
| 415 | * The pulse function. Returns 1 between two thresholds, 0 outside. |
| 416 | * |
| 417 | * @return the output value - 0 or 1 |
| 418 | * @param a the lower threshold position |
| 419 | * @param b the upper threshold position |
| 420 | * @param x the input parameter |
| 421 | */ |
| 422 | public static float pulse(float a, float b, float x) { |
| 423 | return (x < a || x >= b) ? 0.0f : 1.0f; |
| 424 | } |
| 425 | |
| 426 | /** |
| 427 | * An implementation of Fant's resampling algorithm. |
| 428 | * |
| 429 | * @param source the source pixels |
| 430 | * @param dest the destination pixels |
| 431 | * @param length the length of the scanline to resample |
| 432 | * @param offset the start offset into the arrays |
| 433 | * @param stride the offset between pixels in consecutive rows |
| 434 | * @param out an array of output positions for each pixel |
| 435 | */ |
| 436 | public static void resample(int[] source, int[] dest, int length, int offset, int stride, float[] out) { |
| 437 | int i; |
| 438 | int j; |
| 439 | float sizfac; |
| 440 | float inSegment; |
| 441 | float outSegment; |
| 442 | int a; |
| 443 | int r; |
| 444 | int g; |
| 445 | int b; |
| 446 | int nextA; |
| 447 | int nextR; |
| 448 | int nextG; |
| 449 | int nextB; |
| 450 | float aSum; |
| 451 | float rSum; |
| 452 | float gSum; |
| 453 | float bSum; |
| 454 | float[] in; |
| 455 | int srcIndex = offset; |
| 456 | int destIndex = offset; |
| 457 | int lastIndex = source.length; |
| 458 | int rgb; |
| 459 | |
| 460 | in = new float[length + 1]; |
| 461 | i = 0; |
| 462 | for (j = 0; j < length; j++) { |
| 463 | while (out[i + 1] < j) { |
| 464 | i++; |
| 465 | } |
| 466 | in[j] = i + (j - out[i]) / (out[i + 1] - out[i]); |
| 467 | } |
| 468 | in[length] = length; |
| 469 | |
| 470 | inSegment = 1.0f; |
| 471 | outSegment = in[1]; |
| 472 | sizfac = outSegment; |
| 473 | aSum = rSum = gSum = bSum = 0.0f; |
| 474 | rgb = source[srcIndex]; |
| 475 | a = (rgb >> 24) & 0xff; |
| 476 | r = (rgb >> 16) & 0xff; |
| 477 | g = (rgb >> 8) & 0xff; |
| 478 | b = rgb & 0xff; |
| 479 | srcIndex += stride; |
| 480 | rgb = source[srcIndex]; |
| 481 | nextA = (rgb >> 24) & 0xff; |
| 482 | nextR = (rgb >> 16) & 0xff; |
| 483 | nextG = (rgb >> 8) & 0xff; |
| 484 | nextB = rgb & 0xff; |
| 485 | srcIndex += stride; |
| 486 | i = 1; |
| 487 | |
| 488 | while (i < length) { |
| 489 | float aIntensity = inSegment * a + (1.0f - inSegment) * nextA; |
| 490 | float rIntensity = inSegment * r + (1.0f - inSegment) * nextR; |
| 491 | float gIntensity = inSegment * g + (1.0f - inSegment) * nextG; |
| 492 | float bIntensity = inSegment * b + (1.0f - inSegment) * nextB; |
| 493 | |
| 494 | if (inSegment < outSegment) { |
| 495 | aSum += (aIntensity * inSegment); |
| 496 | rSum += (rIntensity * inSegment); |
| 497 | gSum += (gIntensity * inSegment); |
| 498 | bSum += (bIntensity * inSegment); |
| 499 | outSegment -= inSegment; |
| 500 | inSegment = 1.0f; |
| 501 | a = nextA; |
| 502 | r = nextR; |
| 503 | g = nextG; |
| 504 | b = nextB; |
| 505 | if (srcIndex < lastIndex) { |
| 506 | rgb = source[srcIndex]; |
| 507 | } |
| 508 | nextA = (rgb >> 24) & 0xff; |
| 509 | nextR = (rgb >> 16) & 0xff; |
| 510 | nextG = (rgb >> 8) & 0xff; |
| 511 | nextB = rgb & 0xff; |
| 512 | srcIndex += stride; |
| 513 | } else { |
| 514 | aSum += (aIntensity * outSegment); |
| 515 | rSum += (rIntensity * outSegment); |
| 516 | gSum += (gIntensity * outSegment); |
| 517 | bSum += (bIntensity * outSegment); |
| 518 | dest[destIndex] = ((int) Math.min(aSum / sizfac, 255) << 24) |
| 519 | | ((int) Math.min(rSum / sizfac, 255) << 16) |
| 520 | | ((int) Math.min(gSum / sizfac, 255) << 8) |
| 521 | | (int) Math.min(bSum / sizfac, 255); |
| 522 | destIndex += stride; |
| 523 | rSum = gSum = bSum = 0.0f; |
| 524 | inSegment -= outSegment; |
| 525 | outSegment = in[i + 1] - in[i]; |
| 526 | sizfac = outSegment; |
| 527 | i++; |
| 528 | } |
| 529 | } |
| 530 | } |
| 531 | |
| 532 | /** |
| 533 | * A smoothed pulse function. A cubic function is used to smooth the step between two |
| 534 | * thresholds. |
| 535 | * |
| 536 | * @return the output value |
| 537 | * @param a1 the lower threshold position for the start of the pulse |
| 538 | * @param a2 the upper threshold position for the start of the pulse |
| 539 | * @param b1 the lower threshold position for the end of the pulse |
| 540 | * @param b2 the upper threshold position for the end of the pulse |
| 541 | * @param x the input parameter |
| 542 | */ |
| 543 | public static float smoothPulse(float a1, float a2, float b1, float b2, float x) { |
| 544 | if (x < a1 || x >= b2) { |
| 545 | return 0; |
| 546 | } |
| 547 | if (x >= a2) { |
| 548 | if (x < b1) { |
| 549 | return 1.0f; |
| 550 | } |
| 551 | x = (x - b1) / (b2 - b1); |
| 552 | return 1.0f - (x * x * (3.0f - 2.0f * x)); |
| 553 | } |
| 554 | x = (x - a1) / (a2 - a1); |
| 555 | return x * x * (3.0f - 2.0f * x); |
| 556 | } |
| 557 | |
| 558 | /** |
| 559 | * A smoothed step function. A cubic function is used to smooth the step between two |
| 560 | * thresholds. |
| 561 | * |
| 562 | * @return the output value |
| 563 | * @param a the lower threshold position |
| 564 | * @param b the upper threshold position |
| 565 | * @param x the input parameter |
| 566 | */ |
| 567 | public static float smoothStep(float a, float b, float x) { |
| 568 | if (x < a) { |
| 569 | return 0; |
| 570 | } |
| 571 | if (x >= b) { |
| 572 | return 1; |
| 573 | } |
| 574 | x = (x - a) / (b - a); |
| 575 | return x * x * (3 - 2 * x); |
| 576 | } |
| 577 | |
| 578 | /** |
| 579 | * Compute a Catmull-Rom spline. |
| 580 | * |
| 581 | * @return the spline value |
| 582 | * @param x the input parameter |
| 583 | * @param numKnots the number of knots in the spline |
| 584 | * @param knots the array of knots |
| 585 | */ |
| 586 | public static float spline(float x, int numKnots, float[] knots) { |
| 587 | int span; |
| 588 | int numSpans = numKnots - 3; |
| 589 | float k0; |
| 590 | float k1; |
| 591 | float k2; |
| 592 | float k3; |
| 593 | float c0; |
| 594 | float c1; |
| 595 | float c2; |
| 596 | float c3; |
| 597 | |
| 598 | if (numSpans < 1) { |
| 599 | throw new IllegalArgumentException("Too few knots in spline"); |
| 600 | } |
| 601 | |
| 602 | x = clamp(x, 0, 1) * numSpans; |
| 603 | span = (int) x; |
| 604 | if (span > numKnots - 4) { |
| 605 | span = numKnots - 4; |
| 606 | } |
| 607 | x -= span; |
| 608 | |
| 609 | k0 = knots[span]; |
| 610 | k1 = knots[span + 1]; |
| 611 | k2 = knots[span + 2]; |
| 612 | k3 = knots[span + 3]; |
| 613 | |
| 614 | c3 = M00 * k0 + M01 * k1 + M02 * k2 + M03 * k3; |
| 615 | c2 = M10 * k0 + M11 * k1 + M12 * k2 + M13 * k3; |
| 616 | c1 = M20 * k0 + M21 * k1 + M22 * k2 + M23 * k3; |
| 617 | c0 = M30 * k0 + M31 * k1 + M32 * k2 + M33 * k3; |
| 618 | |
| 619 | return ((c3 * x + c2) * x + c1) * x + c0; |
| 620 | } |
| 621 | |
| 622 | /** |
| 623 | * Compute a Catmull-Rom spline, but with variable knot spacing. |
| 624 | * |
| 625 | * @return the spline value |
| 626 | * @param x the input parameter |
| 627 | * @param numKnots the number of knots in the spline |
| 628 | * @param xknots the array of knot x values |
| 629 | * @param yknots the array of knot y values |
| 630 | */ |
| 631 | public static float spline(float x, int numKnots, int[] xknots, int[] yknots) { |
| 632 | int span; |
| 633 | int numSpans = numKnots - 3; |
| 634 | float k0; |
| 635 | float k1; |
| 636 | float k2; |
| 637 | float k3; |
| 638 | float c0; |
| 639 | float c1; |
| 640 | float c2; |
| 641 | float c3; |
| 642 | |
| 643 | if (numSpans < 1) { |
| 644 | throw new IllegalArgumentException("Too few knots in spline"); |
| 645 | } |
| 646 | |
| 647 | for (span = 0; span < numSpans; span++) { |
| 648 | if (xknots[span + 1] > x) { |
| 649 | break; |
| 650 | } |
| 651 | } |
| 652 | if (span > numKnots - 3) { |
| 653 | span = numKnots - 3; |
| 654 | } |
| 655 | float t = (x - xknots[span]) / (xknots[span + 1] - xknots[span]); |
| 656 | |
| 657 | span--; |
| 658 | if (span < 0) { |
| 659 | span = 0; |
| 660 | t = 0; |
| 661 | } |
| 662 | |
| 663 | k0 = yknots[span]; |
| 664 | k1 = yknots[span + 1]; |
| 665 | k2 = yknots[span + 2]; |
| 666 | k3 = yknots[span + 3]; |
| 667 | |
| 668 | c3 = M00 * k0 + M01 * k1 + M02 * k2 + M03 * k3; |
| 669 | c2 = M10 * k0 + M11 * k1 + M12 * k2 + M13 * k3; |
| 670 | c1 = M20 * k0 + M21 * k1 + M22 * k2 + M23 * k3; |
| 671 | c0 = M30 * k0 + M31 * k1 + M32 * k2 + M33 * k3; |
| 672 | |
| 673 | return ((c3 * t + c2) * t + c1) * t + c0; |
| 674 | } |
| 675 | |
| 676 | /** |
| 677 | * The step function. Returns 0 below a threshold, 1 above. |
| 678 | * |
| 679 | * @return the output value - 0 or 1 |
| 680 | * @param a the threshold position |
| 681 | * @param x the input parameter |
| 682 | */ |
| 683 | public static float step(float a, float x) { |
| 684 | return (x < a) ? 0.0f : 1.0f; |
| 685 | } |
| 686 | |
| 687 | /** |
| 688 | * The triangle function. Returns a repeating triangle shape in the range 0..1 with wavelength |
| 689 | * 1.0 |
| 690 | * |
| 691 | * @return the output value |
| 692 | * @param x the input parameter |
| 693 | */ |
| 694 | public static float triangle(float x) { |
| 695 | float r = mod(x, 1.0f); |
| 696 | |
| 697 | return 2.0f * (r < 0.5 ? r : 1 - r); |
| 698 | } |
| 699 | } |