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 | } |