KSeExpr 6.0.0.0
ExprBuiltins.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2011-2019 Disney Enterprises, Inc.
2// SPDX-License-Identifier: LicenseRef-Apache-2.0
3// SPDX-FileCopyrightText: 2020 L. E. Segovia <amy@amyspark.me>
4// SPDX-License-Identifier: GPL-3.0-or-later
5
6#include <cassert>
7#if !defined(_USE_MATH_DEFINES)
8#define _USE_MATH_DEFINES
9#endif
10#include <cmath>
11#include <cstdlib>
12#include <limits>
13#include <algorithm>
14#include <cfloat>
15#include <random>
16
17#include "ExprFunc.h"
18#include "ExprNode.h"
19#include "Vec.h"
20#include "Curve.h"
21#include "ExprBuiltins.h"
22#include "Noise.h"
23#include "Interpreter.h"
25namespace KSeExpr {
26
27static const char* fabs_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float abs(float x)\nabsolute value of x");
28
29// angle conversion functions
30static const char *deg_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float deg(float angle)\nradians to degrees");
31static const char *rad_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float rad(float angle)\ndegrees to radians");
32// trig in degrees
33static const char* cosd_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float cosd(float angle)\ncosine in degrees");
34static const char* sind_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float sind(float angle)\nsine in degrees");
35static const char* tand_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float tand(float angle)\ntangent in degrees");
36static const char* acosd_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float acosd(float value)\narc cosine in degrees");
37static const char* asind_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float asind(float value)\narc sine in degrees");
38static const char* atand_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float atand(float value)\narc tangent in degrees");
39static const char* atan2d_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float atan2d(float y,float x)\narc tangent in degrees of y/x between -180 and 180");
40// trig in radians
41static const char* cos_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float cos(float angle)\ncosine in radians");
42static const char* sin_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float sin(float angle)\nsine in radians");
43static const char* tan_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float tan(float angle)\ntangent in radians");
44static const char* acos_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float acos(float value)\narc cosine in radians");
45static const char* asin_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float asin(float value)\narc sine in radians");
46static const char* atan_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float atan(float value)\narc tangent in radians");
47static const char* atan2_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float atan2(float y,float x)\narc tangent in radians of y/x between -PI and PI");
48// hyperbolic trig
49static const char* cosh_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float cosh(float angle)\nhyperbolic cosine in radians");
50static const char* sinh_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float sinh(float angle)\nhyperbolic sine in radians");
51static const char* tanh_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float tanh(float angle)\nhyperbolic tangent in radians");
52static const char* acosh_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float acosh(float value)\nhyperbolic arc cosine in radians");
53static const char* asinh_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float asinh(float value)\nhyperbolic arc sine in radians");
54static const char* atanh_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float atanh(float value)\nhyperbolic arc tangent in radians");
55// clamping/rounding
56static const char* clamp_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float clamp(float x,float lo,float hi)\nconstrain x to range [lo,hi]");
57static const char* round_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float round(float x)\nnearest integer to x");
58static const char* max_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float max(float a,float b)\ngreater of a and b");
59static const char* min_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float min(float a,float b)\nlesser of a and b");
60static const char* trunc_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float trunc(float a)\nnearest integer towards zero");
61static const char* floor_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float floor(float a)\nnext lower integer");
62static const char* ceil_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float ceil(float a)\nnext higher integer");
63// misc math
64static const char* invert_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float invert(float a)\nDefined as 1-x");
65static const char* cbrt_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float cbrt(float x)\ncube root");
66static const char* sqrt_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float sqrt(float x)\nsquare root");
67static const char* exp_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float exp(float x)\nE raised to the x power");
68static const char* pow_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float pow(float x, float y)\nx to the y power, also available as ^");
69static const char* log_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float log(float x)\nNatural logarithm");
70static const char* log10_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float log10(float x)\nBase 10 logarithm");
71static const char* fmod_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float fmod(float x,float y)\nremainder of x/y (also available as % operator)");
72static const char* turbulence_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
73 "float turbulence(vector v,int octaves=6,float lacunarity=2,float gain=.5)\nAbsolute value of each noise term is "
74 "taken. This gives billowy appearance");
75static const char* cturbulence_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
76 "color cturbulence(vector v,int octaves=6,float lacunarity=2,float gain=.5)\nAbsolute value of each noise term is "
77 "taken. This gives billowy appearance");
78static const char* vturbulence_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
79 "vector vturbulence(vector v,int octaves=6,float lacunarity=2,float gain=.5)\nAbsolute value of each noise term is "
80 "taken. This gives billowy appearance");
81
82double compress(double x, double lo, double hi)
83{
84 return (hi - lo) * x + lo;
85}
86static const char *compress_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float compress(float x,float lo,float hi)\nRemaps x in [0,1] to [lo,hi]");
87
88double expand(double x, double lo, double hi)
89{
90 if (lo == hi)
91 return x < lo ? 0 : 1;
92 return (x - lo) / (hi - lo);
93}
94static const char* expand_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float expand(float x,float lo,float hi)\nRemaps x in [lo,hi] to [0,1]");
95
96double fit(double x, double a1, double b1, double a2, double b2)
97{
98 return (x * (b2 - a2) - a1 * b2 + b1 * a2) / (b1 - a1);
99}
100static const char* fit_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float fit(float x,float a1,float b1,float a2,float b2)\nLinearly remaps x in [a1,b1] to [a2,b2]");
101
102double gamma(double x, double g)
103{
104 return pow(x, 1 / g);
105}
106static const char *gamma_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float gamma(float x, float g)\nGamma correction of x with gamma factor g");
107
108double bias(double x, double b)
109{
110 static double C = 1 / log(0.5);
111 return pow(x, log(b) * C);
112}
113static const char* bias_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
114 "float bias(float x, float g)\nVariation of gamma where values less than 0.5 pull the curve down\nand values "
115 "greater than 0.5 pull the curve up\npow(x,log(b)/log(0.5))");
116
117double contrast(double x, double c)
118{
119 if (x < 0.5)
120 return 0.5 * bias(1 - c, 2 * x);
121 else
122 return 1 - 0.5 * bias(1 - c, 2 - 2 * x);
123}
124static const char* contrast_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
125 "float contrast(float x, float c)\nAdjust the contrast.&nbsp; For c from 0 to 0.5, the contrast is decreased.&nbsp; "
126 "For c &gt; 0.5, the contrast is increased.");
127
128double boxstep(double x, double a)
129{
130 return x < a ? 0.0 : 1.0;
131}
132static const char *boxstep_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "float boxstep(float x,float a)\n if x < a then 0 otherwise 1");
133
134double linearstep(double x, double a, double b)
135{
136 if (a < b) {
137 return x < a ? 0 : (x > b ? 1 : (x - a) / (b - a));
138 } else if (a > b) {
139 return 1 - (x < b ? 0 : (x > a ? 1 : (x - b) / (a - b)));
140 }
141 return boxstep(x, a);
142}
143static const char* linearstep_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
144 "float linearstep(float x, float a, float b)\n if x &lt; a then 0, if x &gt; b then 1, and\nx transitions linearly "
145 "when a &lt; x &lt; b");
146
147double smoothstep(double x, double a, double b)
148{
149 if (a < b) {
150 if (x < a)
151 return 0;
152 if (x >= b)
153 return 1;
154 x = (x - a) / (b - a);
155 } else if (a > b) {
156 if (x <= b)
157 return 1;
158 if (x > a)
159 return 0;
160 x = 1 - (x - b) / (a - b);
161 } else
162 return boxstep(x, a);
163 return x * x * (3 - 2 * x);
164}
165static const char* smoothstep_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
166 "float smoothstep(float x,float a,float b)\n if x &lt; a then 0, if x &gt; b then 1, and\nx transitions smoothly "
167 "(cubic) when a &lt; x &lt; b");
168
169double gaussstep(double x, double a, double b)
170{
171 if (a < b) {
172 if (x < a)
173 return 0;
174 if (x >= b)
175 return 1;
176 x = 1 - (x - a) / (b - a);
177 } else if (a > b) {
178 if (x <= b)
179 return 1;
180 if (x > a)
181 return 0;
182 x = (x - b) / (a - b);
183 } else
184 return boxstep(x, a);
185 return pow(2, -8 * x * x);
186}
187static const char* gaussstep_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
188 "float gasussstep(float x,float a,float b)\n if x &lt; a then 0, if x &gt; b then 1, and\nx transitions smoothly "
189 "(exponentially) when a &lt; x &lt; b");
190
191double remap(double x, double source, double range, double falloff, double interp)
192{
193 range = fabs(range);
195
196 if (falloff == 0)
197 return fabs(x - source) < range;
198
199 double a = NAN;
200 double b = NAN;
201 if (x > source) {
202 a = source + range;
203 b = a + falloff;
204 } else {
205 a = source - range;
206 b = a - falloff;
207 }
208
209 switch (int(interp)) {
210 case 0:
211 return linearstep(x, b, a);
212 case 1:
213 return smoothstep(x, b, a);
214 default:
215 return gaussstep(x, b, a);
216 }
217}
218static const char* remap_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
219 "remap(float x, float source, float range, float falloff, float interp)\n"
220 "General remapping function.\n"
221 "When x is within +/- <i>range</i> of source, the result is one.\n"
222 "The result falls to zero beyond that range over <i>falloff</i> distance.\n"
223 "The falloff shape is controlled by <i>interp</i>. Numeric values\n"
224 "or named constants may be used:\n"
225 "&nbsp;&nbsp;&nbsp;&nbsp;int <b>linear</b> = 0\n"
226 "&nbsp;&nbsp;&nbsp;&nbsp;int <b>smooth</b> = 1\n"
227 "&nbsp;&nbsp;&nbsp;&nbsp;int <b>gaussian</b> = 2\n");
228
229double mix(double x, double y, double alpha)
230{
231 return x * (1 - alpha) + y * alpha;
232}
233static const char *mix_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "mix(float a,float b,float alpha)\nBlend of a and b according to alpha.");
234
235Vec3d hsiAdjust(const Vec3d &rgb, double h, double s, double i)
236{
238 hsl[0] += h * (1.0 / 360);
239 hsl[1] *= s;
240 return hsltorgb(hsl) * i;
241}
242
243Vec3d hsi(int n, const Vec3d *args)
244{
245 if (n < 4)
246 return 0.0;
247
248 double h = args[1][0];
249 double s = args[2][0];
250 double i = args[3][0];
251 if (n >= 5) {
252 // apply mask
253 double m = args[4][0];
254 h *= m;
255 s = (s - 1) * m + 1;
256 i = (i - 1) * m + 1;
257 }
258 return hsiAdjust(args[0], h, s, i);
259}
260static const char* hsi_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
261 "color hsi(color x, float h, float s, float i, float map=1)\n"
262 "The hsi function shifts the hue by h\n"
263 "(in degrees) and scales the saturation and intensity by s and i\n"
264 "respectively.&nbsp; A map may be supplied which will control the shift\n"
265 "- the full shift will happen when the map is one and no shift will\n"
266 "happen when the map is zero.&nbsp; The shift will be scaled back for\n"
267 "values between zero and one.");
268
269Vec3d midhsi(int n, const Vec3d *args)
270{
271 if (n < 4)
272 return 0.0;
273
274 double h = args[1][0];
275 double s = args[2][0];
276 double i = args[3][0];
277 if (n >= 5) {
278 // apply mask
279 double m = args[4][0];
280 // remap from [0..1] to [-1..1]
281 m = m * 2 - 1;
282 // add falloff (if specified)
283 double falloff = 1;
284 double interp = 0;
285 if (n >= 6)
286 falloff = args[5][0];
287 if (n >= 7)
288 interp = args[6][0];
289 if (m < 0)
290 m = -remap(-m, 1, 0, falloff, interp);
291 else
292 m = remap(m, 1, 0, falloff, interp);
293
294 // scale hsi values according to mask (both directions)
295 h *= m;
296 float absm = fabs(static_cast<float>(m));
297 s = s * absm + 1 - absm;
298 i = i * absm + 1 - absm;
299 if (m < 0) {
300 s = 1 / s;
301 i = 1 / i;
302 }
303 }
304 return hsiAdjust(args[0], h, s, i);
305}
306static const char* midhsi_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
307 "color midhsi(color x, float h, float s, float i, float map, float falloff=1, int interp=0)\n"
308 "The midhsi function is just like the hsi function except that\n"
309 "the control map is centered around the mid point (value of 0.5)\n"
310 "and can scale the shift in both directions.");
311
313{
314 // RGB to HSL color space conversion
315 // This is based on Foley, Van Dam (2nd ed; p. 595)
316 // but extended to allow rgb values outside of 0..1
317 double H = NAN;
318 double S = NAN;
319 double L = NAN;
320 double R = rgb[0];
321 double G = rgb[1];
322 double B = rgb[2];
323 double x = R < G ? (R < B ? R : B) : (G < B ? G : B); // min(R,G,B)
324 double y = R > G ? (R > B ? R : B) : (G > B ? G : B); // max(R,G,B)
325
326 // compute lightness = avg of min and max rgb vals
327 double sum = x + y;
328 double diff = y - x;
329 L = sum / 2;
330 if (diff < 1e-6) // achromatic
331 return {0, 0, L};
332
333 // compute saturation
334 if (L <= .5) {
335 if (x < 0)
336 S = 1 - x;
337 else
338 S = diff / sum;
339 } else {
340 if (y > 1)
341 S = y;
342 else
343 S = diff / (2 - sum);
344 }
345
346 // compute hue
347 if (R == y)
348 H = (G - B) / diff;
349 else if (G == y)
350 H = (B - R) / diff + 2;
351 else
352 H = (R - G) / diff + 4;
353 H *= 1 / 6.;
354 H -= floor(H); // make sure hue is in range 0..1
355
356 return {H, S, L};
357}
358static const char* rgbtohsl_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
359 "color rgbtohsl(color rgb)\n"
360 "RGB to HSL color space conversion.\n"
361 "HSL is Hue, Saturation, Lightness (all in range [0..1] )\n"
362 "These functions have also been extended to support rgb and hsl values\n"
363 "outside of the range [0..1] in a reasonable way.&nbsp; For any rgb or\n"
364 "hsl value (except for negative s values), the conversion is\n"
365 "well-defined and reversible.");
366
367static double hslvalue(double x, double y, double H)
368{
369 H -= floor(H); // make sure hue is in range 0..1
370
371 if (H < 1 / 6.)
372 return x + (y - x) * H * 6;
373 else if (H < 3 / 6.)
374 return y;
375 else if (H < 4 / 6.)
376 return x + (y - x) * (4 / 6. - H) * 6;
377 else
378 return x;
379}
380
382{
383 // HSL to RGB color space conversion
384 // This is based on Foley, Van Dam (2nd ed; p. 596)
385 // but extended to allow rgb values outside of 0..1
386 double y = NAN;
387 double H = hsl[0];
388 double S = hsl[1];
389 double L = hsl[2];
390 if (S <= 0) // achromatic
391 return {L, L, L};
392
393 // find min/max rgb values
394 if (L < 0.5) {
395 if (S > 1)
396 y = 2 * L + S - 1;
397 else
398 y = L + L * S;
399 } else {
400 if (S > 1)
401 y = S;
402 else
403 y = L + S - L * S;
404 }
405 double x = 2 * L - y;
406
407 // reconstruct rgb from min,max,hue
408 double R = hslvalue(x, y, H + (1 / 3.));
409 double G = hslvalue(x, y, H);
410 double B = hslvalue(x, y, H - (1 / 3.));
411 return {R, G, B};
412}
413static const char* hsltorgb_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
414 "color hsltorgb(color hsl)\n"
415 "HSL to RGB color space conversion.\n"
416 "HSL is Hue, Saturation, Lightness (all in range [0..1] )\n"
417 "These functions have also been extended to support rgb and hsl values\n"
418 "outside of the range [0..1] in a reasonable way.&nbsp; For any rgb or\n"
419 "hsl value (except for negative s values), the conversion is\n"
420 "well-defined and reversible.");
421
422static Vec3d saturate(const Vec3d &Cin, double amt)
423{
424 const Vec3d lum(.2126, .7152, .0722); // rec709 luminance
425 Vec3d result = Vec3d(Cin.dot(lum) * (1 - amt)) + Cin * amt;
426 if (result[0] < 0)
427 result[0] = 0;
428 if (result[1] < 0)
429 result[1] = 0;
430 if (result[2] < 0)
431 result[2] = 0;
432 return result;
433}
434
435Vec3d saturate(int n, const Vec3d *args)
436{
437 if (n < 2)
438 return 0.0;
439 return saturate(args[0], args[1][0]);
440}
441static const char* saturate_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
442 "color saturate(color val, float amt)\n"
443 "Scale saturation of color by amt.\n"
444 "The color is scaled around the rec709 luminance value,\n"
445 "and negative results are clamped at zero.\n");
446
448{
449 // The default seed of the Mersenne Twister is as predictable as 0 - amyspark
450
451 struct Data : public ExprFuncNode::Data { // NOLINT cert-msc32-c
452 std::mt19937 gen;
453 std::uniform_real_distribution<> dis;
454 };
455
457 {
458 bool valid = true;
459 for (auto i = 0; i < node->numChildren(); i++)
460 valid &= node->checkArg(i, ExprType().FP(1).Varying(), envBuilder);
461 return valid ? ExprType().FP(1).Varying() : ExprType().Error();
462 }
463
465 {
466 auto *data = new Data();
467 auto a = 0.0;
468 auto b = 1.0;
469
470 if (args.nargs() >= 1) {
471 a = args.inFp<1>(0)[0];
472 }
473 if (args.nargs() >= 2) {
474 b = args.inFp<1>(1)[0];
475 }
476
477 if (args.nargs() >= 3) {
478 data->gen = std::mt19937(args.inFp<1>(2)[0]);
479 } else {
480 data->gen = std::mt19937(0); // NOLINT cert-msc32-c
481 }
482
483 data->dis = std::uniform_real_distribution<>(a, b);
484 return data;
485 }
486
487 void eval(ArgHandle args) override
488 {
489 auto *data = dynamic_cast<RandFuncX::Data *>(args.data);
490 args.outFp = data->dis(data->gen);
491 }
492
493public:
496 {
497 } // Thread Safe
499static const char *rand_docstring = QT_TRANSLATE_NOOP("builtin",
500 "float rand ( [float min, float max], [float seed] )\n"
501 "Random number between [min, max] (or [0, 1] if unspecified).\n"
502 "If a seed is supplied, it will be used in addition to the internal seeds and may be used to create multiple distinct generators.");
503
504double hash(int n, double* args)
505{
506 // combine args into a single seed
507 uint32_t seed = 0;
508 for (int i = 0; i < n; i++) {
509 // make irrational to generate fraction and combine xor into 32 bits
510 int exp = 0;
511 double frac = frexp(args[i] * double(M_E * M_PI), &exp);
513
514 // blend with seed (constants from Numerical Recipes, attrib. from Knuth)
515 static const uint32_t M = 1664525;
516 static const uint32_t C = 1013904223;
517 seed = seed * M + s + C;
518 }
519
520 // tempering (from Matsumoto)
521 seed ^= (seed >> 11);
522 seed ^= (seed << 7) & 0x9d2c5680UL;
523 seed ^= (seed << 15) & 0xefc60000UL;
524 seed ^= (seed >> 18);
525
526 // permute
527 static std::array<uint8_t, 256> p{
528 148, 201, 203, 34, 85, 225, 163, 200, 174, 137, 51, 24, 19, 252, 107, 173, 110, 251, 149, 69, 180, 152,
529 141, 132, 22, 20, 147, 219, 37, 46, 154, 114, 59, 49, 155, 161, 239, 77, 47, 10, 70, 227, 53, 235,
530 30, 188, 143, 73, 88, 193, 214, 194, 18, 120, 176, 36, 212, 84, 211, 142, 167, 57, 153, 71, 159, 151,
531 126, 115, 229, 124, 172, 101, 79, 183, 32, 38, 68, 11, 67, 109, 221, 3, 4, 61, 122, 94, 72, 117,
532 12, 240, 199, 76, 118, 5, 48, 197, 128, 62, 119, 89, 14, 45, 226, 195, 80, 50, 40, 192, 60, 65,
533 166, 106, 90, 215, 213, 232, 250, 207, 104, 52, 182, 29, 157, 103, 242, 97, 111, 17, 8, 175, 254, 108,
534 208, 224, 191, 112, 105, 187, 43, 56, 185, 243, 196, 156, 246, 249, 184, 7, 135, 6, 158, 82, 130, 234,
535 206, 255, 160, 236, 171, 230, 42, 98, 54, 74, 209, 205, 33, 177, 15, 138, 178, 44, 116, 96, 140, 253,
536 233, 125, 21, 133, 136, 86, 245, 58, 23, 1, 75, 165, 92, 217, 39, 0, 218, 91, 179, 55, 238, 170,
537 134, 83, 25, 189, 216, 100, 129, 150, 241, 210, 123, 99, 2, 164, 16, 220, 121, 139, 168, 64, 190, 9,
538 31, 228, 95, 247, 244, 81, 102, 145, 204, 146, 26, 87, 113, 198, 181, 127, 237, 169, 28, 93, 27, 41,
539 231, 248, 78, 162, 13, 186, 63, 66, 131, 202, 35, 144, 222, 223};
540 union {
541 uint32_t i;
542 std::array<uint8_t, 4> c;
543 } u1 {}, u2 {};
544 u1.i = seed;
545 u2.c[3] = p[u1.c[0]];
546 u2.c[2] = p[(u1.c[1] + u2.c[3]) & 0xff];
547 u2.c[1] = p[(u1.c[2] + u2.c[2]) & 0xff];
548 u2.c[0] = p[(u1.c[3] + u2.c[1]) & 0xff];
549
550 // scale to [0.0 .. 1.0]
551 return u2.i * (1.0 / UINT32_MAX);
552}
553static const char* hash_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
554 "float hash(float seed1,[float seed2, ...])\n"
555 "Like rand, but with no internal seeds. Any number of seeds may be given\n"
556 "and the result will be a random function based on all the seeds.");
557
558double noise(int n, const Vec3d *args)
559{
560 if (n < 1)
561 return 0;
562 if (n == 1) {
563 // 1 arg = vector arg
564 double result = NAN;
565 std::array<double, 3> p {args[0][0], args[0][1], args[0][2]};
566 Noise<3, 1>(p.data(), &result);
567 return .5 * result + .5;
568 }
569 // scalar args
570 if (n > 4)
571 n = 4;
572 std::array<double, 4> p {};
573 for (int i = 0; i < n; i++)
574 p[i] = args[i][0];
575 double result = NAN;
576 switch (n) {
577 case 1:
578 Noise<1, 1>(p.data(), &result);
579 break;
580 case 2:
581 Noise<2, 1>(p.data(), &result);
582 break;
583 case 3:
584 Noise<3, 1>(p.data(), &result);
585 break;
586 case 4:
587 Noise<4, 1>(p.data(), &result);
588 break;
589 default:
590
591 result = 0;
592 break;
593 }
594 return .5 * result + .5;
595}
596static const char* noise_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
597 "float noise ( vector v )\n"
598 "float noise ( float x, float y )\n"
599 "float noise ( float x, float y, float z )\n"
600 "float noise ( float x, float y, float z, float w )\n"
601 "Original perlin noise at location (C2 interpolant)");
602
603double snoise(const Vec3d &p)
604{
605 double result = NAN;
606 std::array<double, 3> args {p[0], p[1], p[2]};
607 Noise<3, 1>(args.data(), &result);
608 return result;
609}
610static const char* snoise_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
611 "float snoise ( vector v)\n"
612 "signed noise w/ range -1 to 1 formed with original perlin noise at location (C2 interpolant)");
613
615{
617 std::array<double, 3> args {p[0], p[1], p[2]};
618 Noise<3, 3>(args.data(), &result[0]);
619 return result;
620}
621static const char* vnoise_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
622 "vector vnoise ( vector v)\n"
623 "vector noise formed with original perlin noise at location (C2 interpolant)");
624
626{
627 return .5 * vnoise(p) + Vec3d(.5);
628}
629static const char* cnoise_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
630 "color cnoise ( vector v)\n"
631 "color noise formed with original perlin noise at location (C2 interpolant)");
632
633double snoise4(int, const Vec3d *args)
634{
635 double result = NAN;
636 std::array<double, 4> procargs {args[0][0], args[0][1], args[0][2], args[1][0]};
637 Noise<4, 1>(procargs.data(), &result);
638 return result;
639}
640static const char* snoise4_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
641 "float snoise4 ( vector v,float t)\n"
642 "4D signed noise w/ range -1 to 1 formed with original perlin noise at location (C2 interpolant)");
643
644Vec3d vnoise4(int, const Vec3d *args)
645{
647 std::array<double, 4> procargs {args[0][0], args[0][1], args[0][2], args[1][0]};
648 Noise<4, 3>(procargs.data(), &result[0]);
649 return result;
650}
651static const char* vnoise4_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
652 "vector vnoise4 ( vector v,float t)\n"
653 "4D vector noise formed with original perlin noise at location (C2 interpolant)");
654
655Vec3d cnoise4(int n, const Vec3d *args)
656{
657 return .5 * vnoise4(n, args) + Vec3d(.5);
658}
659static const char* cnoise4_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
660 "color cnoise4 ( vector v,float t)\n"
661 "4D color noise formed with original perlin noise at location (C2 interpolant)");
662
663double turbulence(int n, const Vec3d *args)
664{
665 // args: octaves, lacunarity, gain
666 int octaves = 6;
667 double lacunarity = 2;
668 double gain = 0.5;
669 Vec3d p = 0.0;
670
671 switch (n) {
672 case 4:
673 gain = args[3][0];
674 /* fall through */
675 case 3:
676 lacunarity = args[2][0];
677 /* fall through */
678 case 2:
679 octaves = int(clamp(args[1][0], 1, 8));
680 /* fall through */
681 case 1:
682 p = args[0];
683 }
684
685 double result = 0;
686 std::array<double, 3> P {p[0], p[1], p[2]};
688 return .5 * result + .5;
689}
690
691Vec3d vturbulence(int n, const Vec3d *args)
692{
693 // args: octaves, lacunarity, gain
694 int octaves = 6;
695 double lacunarity = 2;
696 double gain = 0.5;
697 Vec3d p = 0.0;
698
699 switch (n) {
700 case 4:
701 gain = args[3][0];
702 /* fall through */
703 case 3:
704 lacunarity = args[2][0];
705 /* fall through */
706 case 2:
707 octaves = int(clamp(args[1][0], 1, 8));
708 /* fall through */
709 case 1:
710 p = args[0];
711 }
712
714 std::array<double, 3> P {p[0], p[1], p[2]};
715 FBM<3, 3, true>(P.data(), &result[0], octaves, lacunarity, gain);
716 return result;
717}
718
719Vec3d cturbulence(int n, const Vec3d *args)
720{
721 return vturbulence(n, args) * .5 + Vec3d(.5);
722}
723
724double fbm(int n, const Vec3d *args)
725{
726 // args: octaves, lacunarity, gain
727 int octaves = 6;
728 double lacunarity = 2;
729 double gain = 0.5;
730 Vec3d p = 0.0;
731
732 switch (n) {
733 case 4:
734 gain = args[3][0];
735 /* fall through */
736 case 3:
737 lacunarity = args[2][0];
738 /* fall through */
739 case 2:
740 octaves = int(clamp(args[1][0], 1, 8));
741 /* fall through */
742 case 1:
743 p = args[0];
744 }
745
746 double result = 0.0;
747 std::array<double, 3> P {p[0], p[1], p[2]};
749 return .5 * result + .5;
750}
751static const char* fbm_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
752 "float fbm(vector v,int octaves=6,float lacunarity=2,float gain=.5)\n"
753 "fbm (Fractal Brownian Motion) is a multi-frequency noise function. \n"
754 "The base frequency is the same as the \"noise\" function. The total "
755 "number of frequencies is controlled by octaves. The lacunarity is the "
756 "spacing between the frequencies - a value of 2 means each octave is "
757 "twice the previous frequency. The gain controls how much each "
758 "frequency is scaled relative to the previous frequency.");
759
760Vec3d vfbm(int n, const Vec3d *args)
761{
762 // args: octaves, lacunarity, gain
763 int octaves = 6;
764 double lacunarity = 2;
765 double gain = 0.5;
766 Vec3d p = 0.0;
767
768 switch (n) {
769 case 4:
770 gain = args[3][0];
771 /* fall through */
772 case 3:
773 lacunarity = args[2][0];
774 /* fall through */
775 case 2:
776 octaves = int(clamp(args[1][0], 1, 8));
777 /* fall through */
778 case 1:
779 p = args[0];
780 }
781
782 Vec3d result = 0.0;
783 std::array<double, 3> P {p[0], p[1], p[2]};
785 return result;
786}
787static const char* vfbm_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "vector vfbm(vector vint octaves=6,float lacunarity=2,float gain=.5)");
788
789double fbm4(int n, const Vec3d *args)
790{
791 // args: octaves, lacunarity, gain
792 int octaves = 6;
793 double lacunarity = 2;
794 double gain = 0.5;
795 Vec3d p = 0.0;
796 float time = 0.0;
797
798 switch (n) {
799 case 5:
800 gain = args[4][0];
801 /* fall through */
802 case 4:
803 lacunarity = args[3][0];
804 /* fall through */
805 case 3:
806 octaves = int(clamp(args[2][0], 1, 8));
807 /* fall through */
808 case 2:
809 time = static_cast<float>(args[1][0]);
810 /* fall through */
811 case 1:
812 p = args[0];
813 }
814
815 double result = 0.0;
816 std::array<double, 4> P {p[0], p[1], p[2], time};
818 return .5 * result + .5;
819}
820static const char* fbm4_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
821 "float fbm4(vector v,float time,int octaves=6,float lacunarity=2,float gain=.5)\n"
822 "fbm (Fractal Brownian Motion) is a multi-frequency noise function. \n"
823 "The base frequency is the same as the \"noise\" function. The total \n"
824 "number of frequencies is controlled by octaves. The lacunarity is the \n"
825 "spacing between the frequencies - a value of 2 means each octave is \n"
826 "twice the previous frequency. The gain controls how much each \n"
827 "frequency is scaled relative to the previous frequency.");
828
829Vec3d vfbm4(int n, const Vec3d *args)
830{
831 // args: octaves, lacunarity, gain
832 int octaves = 6;
833 double lacunarity = 2;
834 double gain = 0.5;
835 Vec3d p = 0.0;
836 float time = 0.0;
837
838 switch (n) {
839 case 5:
840 gain = args[4][0];
841 /* fall through */
842 case 4:
843 lacunarity = args[3][0];
844 /* fall through */
845 case 3:
846 octaves = int(clamp(args[2][0], 1, 8));
847 /* fall through */
848 case 2:
849 time = static_cast<float>(args[1][0]);
850 /* fall through */
851 case 1:
852 p = args[0];
853 }
854
855 Vec3d result = 0.0;
856 std::array<double, 4> P {p[0], p[1], p[2], time};
858 return result;
859}
860static const char* vfbm4_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "vector vfbm4(vector v,float time,int octaves=6,float lacunarity=2,float gain=.5)");
861
862Vec3d cfbm(int n, const Vec3d *args)
863{
864 return vfbm(n, args) * .5 + Vec3d(.5);
865}
866static const char *cfbm_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "color cfbm(vector vint octaves=6,float lacunarity=2,float gain=.5)");
867
868Vec3d cfbm4(int n, const Vec3d *args)
869{
870 return vfbm4(n, args) * .5 + Vec3d(.5);
871}
872static const char *cfbm4_docstring = QT_TRANSLATE_NOOP_UTF8("builtin", "color cfbm4(vector v,float time,int octaves=6,float lacunarity=2,float gain=.5)");
873
874double cellnoise(const Vec3d &p)
875{
876 double result = NAN;
877 std::array<double, 3> args {p[0], p[1], p[2]};
878 CellNoise<3, 1>(args.data(), &result);
879 return result;
880}
881static const char* cellnoise_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
882 "float cellnoise(vector v)\n"
883 "cellnoise generates a field of constant colored cubes based on the integer location.\n"
884 "This is the same as the prman cellnoise function.");
885
887{
889 std::array<double, 3> args {p[0], p[1], p[2]};
890 CellNoise<3, 3>(args.data(), &result[0]);
891 return result;
892}
893static const char* ccellnoise_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
894 "color cellnoise(vector v)\n"
895 "cellnoise generates a field of constant colored cubes based on the integer location.\n"
896 "This is the same as the prman cellnoise function.");
897
898double pnoise(const Vec3d &p, const Vec3d &period)
899{
900 double result = NAN;
901 std::array<double, 3> args {p[0], p[1], p[2]};
902 std::array<int, 3> pargs {std::max((int)1, (int)period[0]), std::max((int)1, (int)period[1]), std::max((int)1, (int)period[2])};
903 PNoise<3, 1>(args.data(), pargs.data(), &result);
904 return result;
905}
906static const char* pnoise_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
907 "float pnoise ( vector v, vector period )\n"
908 "periodic noise");
910 std::array<Vec3d, 27> points;
912 double jitter {-1};
913 VoronoiPointData() = default;
914};
915
916static Vec3d *voronoi_points(VoronoiPointData &data, const Vec3d &cell, double jitter)
917{
918 if (cell == data.cell && jitter == data.jitter)
919 return data.points.data();
920 data.cell = cell;
921 data.jitter = jitter;
922
923 int n = 0;
924 for (int i = -1; i <= 1; i++) {
925 for (int j = -1; j <= 1; j++) {
926 for (int k = -1; k <= 1; k++, n++) {
927 Vec3d testcell = cell + Vec3d(i, j, k);
928 data.points[n] = testcell + jitter * (ccellnoise(testcell) - Vec3d(.5));
929 }
930 }
931 }
932 return data.points.data();
933}
934
935static void voronoi_f1_3d(VoronoiPointData &data, const Vec3d &p, double jitter, double &f1, Vec3d &pos1)
936{
937 // from Advanced Renderman, page 257
938 Vec3d thiscell(floor(p[0]) + 0.5, floor(p[1]) + 0.5, floor(p[2]) + 0.5);
939
940 f1 = 1000;
941 Vec3d *pos = voronoi_points(data, thiscell, jitter);
942 Vec3d *end = pos + 27;
943
944 for (; pos != end; pos++) {
945 Vec3d offset = *pos - p;
946 double dist = offset.dot(offset);
947 if (dist < f1) {
948 f1 = dist;
949 pos1 = *pos;
950 }
951 }
952 f1 = sqrt(f1);
953}
954
955static void voronoi_f1f2_3d(VoronoiPointData &data, const Vec3d &p, double jitter, double &f1, Vec3d &pos1, double &f2, Vec3d &pos2)
956{
957 // from Advanced Renderman, page 258
958 Vec3d thiscell(floor(p[0]) + 0.5, floor(p[1]) + 0.5, floor(p[2]) + 0.5);
959 f1 = f2 = 1000;
960 Vec3d *pos = voronoi_points(data, thiscell, jitter);
961 Vec3d *end = pos + 27;
962
963 for (; pos != end; pos++) {
964 Vec3d offset = *pos - p;
965 double dist = offset.dot(offset);
966 if (dist < f1) {
967 f2 = f1;
968 pos2 = pos1;
969 f1 = dist;
970 pos1 = *pos;
971 } else if (dist < f2) {
972 f2 = dist;
973 pos2 = *pos;
974 }
975 }
976 f1 = sqrt(f1);
977 f2 = sqrt(f2);
978}
979
980Vec3d voronoiFn(VoronoiPointData &data, int n, const Vec3d *args)
981{
982 // args = p, type, jitter,
983 // fbmScale, fbmOctaves, fbmLacunarity, fbmGain
984 Vec3d p;
985 int type = 1;
986 double jitter = 0.5;
987 double fbmScale = 0;
988 double fbmOctaves = 4;
989 double fbmLacunarity = 2;
990 double fbmGain = 0.5;
991 switch (n) {
992 case 7:
993 fbmGain = args[6][0];
994 /* fall through */
995 case 6:
996 fbmLacunarity = args[5][0];
997 /* fall through */
998 case 5:
999 fbmOctaves = args[4][0];
1000 /* fall through */
1001 case 4:
1002 fbmScale = args[3][0];
1003 /* fall through */
1004 case 3:
1005 jitter = clamp(args[2][0], 1e-3, 1);
1006 /* fall through */
1007 case 2:
1008 type = int(args[1][0]);
1009 /* fall through */
1010 case 1:
1011 p = args[0];
1012 }
1013
1014 if (fbmScale > 0) {
1015 std::array<Vec3d, 4> fbmArgs;
1016 fbmArgs[0] = 2 * p;
1017 fbmArgs[1] = fbmOctaves;
1019 fbmArgs[3] = fbmGain;
1020 p += fbmScale * vfbm(4, fbmArgs.data());
1021 }
1022
1023 double f1 = NAN;
1024 double f2 = NAN;
1025 Vec3d pos1;
1026 Vec3d pos2;
1027 if (type >= 3)
1028 voronoi_f1f2_3d(data, p, jitter, f1, pos1, f2, pos2);
1029 else
1030 voronoi_f1_3d(data, p, jitter, f1, pos1);
1031
1032 switch (type) {
1033 case 1:
1034 pos1[0] += 10;
1035 return cellnoise(pos1);
1036 case 2:
1037 return f1;
1038 case 3:
1039 return f2;
1040 case 4:
1041 return f2 - f1;
1042 case 5: {
1043 float scalefactor = static_cast<float>((pos2 - pos1).length() / ((pos1 - p).length() + (pos2 - p).length()));
1044 return smoothstep(f2 - f1, 0, 0.1 * scalefactor);
1045 }
1046 }
1047
1048 return 0.0;
1049}
1050const static char* voronoi_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1051 "float voronoi(vector v, int type=1,float jitter=0.5, float fbmScale=0, int fbmOctaves=4,float fbmLacunarity=2, "
1052 "float fbmGain=.5)\n"
1053 "voronoi is a cellular noise pattern. It is a jittered variant of cellnoise.");
1054
1055Vec3d cvoronoiFn(VoronoiPointData &data, int n, const Vec3d *args)
1056{
1057 // args = p, type, jitter,
1058 // fbmScale, fbmOctaves, fbmLacunarity, fbmGain
1059 Vec3d p;
1060 int type = 1;
1061 double jitter = 0.5;
1062 double fbmScale = 0;
1063 double fbmOctaves = 4;
1064 double fbmLacunarity = 2;
1065 double fbmGain = 0.5;
1066 switch (n) {
1067 case 7:
1068 fbmGain = args[6][0];
1069 /* fall through */
1070 case 6:
1071 fbmLacunarity = args[5][0];
1072 /* fall through */
1073 case 5:
1074 fbmOctaves = args[4][0];
1075 /* fall through */
1076 case 4:
1077 fbmScale = args[3][0];
1078 /* fall through */
1079 case 3:
1080 jitter = clamp(args[2][0], 1e-3, 1);
1081 /* fall through */
1082 case 2:
1083 type = int(args[1][0]);
1084 /* fall through */
1085 case 1:
1086 p = args[0];
1087 }
1088
1089 if (fbmScale > 0) {
1090 std::array<Vec3d, 4> fbmArgs;
1091 fbmArgs[0] = 2 * p;
1092 fbmArgs[1] = fbmOctaves;
1094 fbmArgs[3] = fbmGain;
1095 p += fbmScale * vfbm(4, fbmArgs.data());
1096 }
1097
1098 double f1 = NAN;
1099 double f2 = NAN;
1100 Vec3d pos1;
1101 Vec3d pos2;
1102 if (type >= 3)
1103 voronoi_f1f2_3d(data, p, jitter, f1, pos1, f2, pos2);
1104 else
1105 voronoi_f1_3d(data, p, jitter, f1, pos1);
1106
1107 Vec3d color = ccellnoise(pos1);
1108 switch (type) {
1109 case 1:
1110 pos1[0] += 10;
1111 return color;
1112 case 2:
1113 return f1 * color;
1114 case 3:
1115 return f2 * color;
1116 case 4:
1117 return (f2 - f1) * color;
1118 case 5: {
1119 float scalefactor = static_cast<float>((pos2 - pos1).length() / ((pos1 - p).length() + (pos2 - p).length()));
1120 return smoothstep(f2 - f1, 0, 0.1 * scalefactor) * color;
1121 }
1122 }
1123
1124 return 0.0;
1125}
1126const static char* cvoronoi_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1127 "color cvoronoi(vector v, int type=1,float jitter=0.5, float fbmScale=0, int fbmOctaves=4,float fbmLacunarity=2, "
1128 "float fbmGain=.5)\n"
1129 "returns color in cellular pattern. It is a jittered variant of cellnoise.");
1130
1131Vec3d pvoronoiFn(VoronoiPointData &data, int n, const Vec3d *args)
1132{
1133 // args = p, jitter,
1134 // fbmScale, fbmOctaves, fbmLacunarity, fbmGain
1135 Vec3d p;
1136 double jitter = 0.5;
1137 double fbmScale = 0;
1138 double fbmOctaves = 4;
1139 double fbmLacunarity = 2;
1140 double fbmGain = 0.5;
1141 switch (n) {
1142 case 6:
1143 fbmGain = args[5][0];
1144 /* fall through */
1145 case 5:
1146 fbmLacunarity = args[4][0];
1147 /* fall through */
1148 case 4:
1149 fbmOctaves = args[3][0];
1150 /* fall through */
1151 case 3:
1152 fbmScale = args[2][0];
1153 /* fall through */
1154 case 2:
1155 jitter = clamp(args[1][0], 1e-3, 1);
1156 /* fall through */
1157 case 1:
1158 p = args[0];
1159 }
1160
1161 if (fbmScale > 0) {
1162 std::array<Vec3d, 4> fbmArgs;
1163 fbmArgs[0] = 2 * p;
1164 fbmArgs[1] = fbmOctaves;
1166 fbmArgs[3] = fbmGain;
1167 p += fbmScale * vfbm(4, fbmArgs.data());
1168 }
1169
1170 double f1 = NAN;
1171 Vec3d pos1;
1172 voronoi_f1_3d(data, p, jitter, f1, pos1);
1173 return pos1;
1174}
1175const static char* pvoronoi_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1176 "color pvoronoi(vector v, int type=1,float jitter=0.5, float fbmScale=0, int fbmOctaves=4,float fbmLacunarity=2, "
1177 "float fbmGain=.5)\n"
1178 "returns center of voronoi cell.");
1179
1181{
1182public:
1183 using VoronoiFunc = Vec3d(VoronoiPointData &, int, const Vec3d *);
1189
1191 {
1192 // check number of arguments
1193 int nargs = node->numChildren();
1194 if (nargs < 1 || nargs > 7) {
1196 return ExprType().Error();
1197 }
1198
1199 bool valid = true;
1200 valid &= node->checkArg(0, ExprType().FP(3).Varying(), envBuilder);
1201 for (int i = 1; i < nargs; i++)
1202 valid &= node->checkArg(i, ExprType().FP(1).Constant(), envBuilder);
1203 return valid ? ExprType().FP(3).Varying() : ExprType().Error();
1204 }
1205
1207 {
1208 return new VoronoiPointData();
1209 }
1210
1211 void eval(ArgHandle args) override
1212 {
1213 auto *data = dynamic_cast<VoronoiPointData *>(args.data);
1214 int nargs = args.nargs();
1215 auto sevArgs = std::vector<Vec3d>(nargs);
1216
1217 for (int i = 0; i < nargs; i++)
1218 for (int j = 0; j < 3; j++)
1219 sevArgs[i][j] = args.inFp<3>(i)[j];
1220
1221 Vec3d result = _vfunc(*data, nargs, sevArgs.data());
1222 double *out = &args.outFp;
1223 for (int i = 0; i < 3; i++)
1224 out[i] = result[i];
1225 }
1226
1227private:
1230
1231double dist(const Vec3d &a, const Vec3d &b)
1232{
1233 double x = a[0] - b[0];
1234 double y = a[1] - b[1];
1235 double z = a[2] - b[2];
1236 return sqrt(x * x + y * y + z * z);
1237}
1238static const char* dist_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1239 "float dist(vector a, vector b)\n"
1240 "distance between two points");
1241
1242double length(const Vec3d &v)
1243{
1244 return sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
1245}
1246static const char* length_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1247 "float length(vector v)\n"
1248 "length of vector");
1249
1250double hypot(double x, double y)
1251{
1252 return sqrt(x * x + y * y);
1253}
1254static const char* hypot_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1255 "float hypot(vector v)\n"
1256 "length of 2d vector [x,y]");
1257
1258double dot(const Vec3d &a, const Vec3d &b)
1259{
1260 return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
1261}
1262static const char* dot_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1263 "float dot(vector a,vector b)\n"
1264 "vector dot product");
1265
1267{
1268 double len = length(a);
1269 if (len == 0)
1270 return 0.0;
1271 else
1272 return a / len;
1273}
1274static const char* norm_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1275 "vector norm(vector v)\n"
1276 "vector scaled to unit length");
1277
1278Vec3d cross(const Vec3d &a, const Vec3d &b)
1279{
1280 return {a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]};
1281}
1282static const char* cross_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1283 "vector cross(vector a,vector b)\n"
1284 "vector cross product");
1285
1286double angle(const Vec3d &a, const Vec3d &b)
1287{
1288 double len = length(a) * length(b);
1289 if (len == 0)
1290 return 0;
1291 return acos(dot(a, b) / len);
1292}
1293static const char* angle_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1294 "float angle(vector a,vector b)\n"
1295 "angle between two vectors (in radians)");
1296
1297Vec3d ortho(const Vec3d &a, const Vec3d &b)
1298{
1299 return norm(cross(a, b));
1300}
1301static const char* ortho_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1302 "vector angle(vector a,vector b)\n"
1303 "normalized vector orthogonal to a and b scaled to unit length");
1304
1305Vec3d rotate(int n, const Vec3d *args)
1306{
1307 if (n != 3)
1308 return 0.0;
1309 const Vec3d &P = args[0];
1310 const Vec3d &axis = args[1];
1311 auto angle = static_cast<float>(args[2][0]);
1312 double len = axis.length();
1313 if (len == 0.0)
1314 return P;
1315 return P.rotateBy(axis / len, angle);
1316}
1317static const char* rotate_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1318 "vector rotate(vector v,vector axis,float angle)\n"
1319 "rotates v around axis by given angle (in radians)");
1320
1321Vec3d up(const Vec3d &P, const Vec3d &upvec)
1322{
1323 // rotate vec so y-axis points to upvec
1324 Vec3d yAxis(0, 1, 0);
1325 return P.rotateBy(ortho(upvec, yAxis), angle(upvec, yAxis));
1326}
1327static const char* up_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1328 "vector up(vector P,vector upvec)\n"
1329 "rotates v such that the Y axis points in the given up direction");
1330
1331double cycle(double index, double loRange, double hiRange)
1332{
1333 int lo = int(loRange);
1334 int hi = int(hiRange);
1335 int range = hi - lo + 1;
1336 if (range <= 0)
1337 return lo;
1338 int result = int(index) % range;
1339 if (result < 0)
1340 result += range;
1341 return lo + result;
1342}
1343static const char* cycle_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1344 "int cycle(int index, int loRange, int hiRange )\n"
1345 "Cycles through values between loRange and hiRange based on supplied index.\n"
1346 "This is an offset \"mod\" function. "
1347 "The result is computed as ``loRange + value % (hiRange-loRange+1)``.");
1348
1349double pick(int n, double *params)
1350{
1351 if (n < 3)
1352 return 0;
1353 double index = hash(1, &params[0]);
1354 int loRange = int(params[1]);
1355 int hiRange = int(params[2]);
1356 int range = hiRange - loRange + 1;
1357 if (range <= 0)
1358 return loRange;
1359 int numWeights = n - 3;
1360 if (numWeights > range)
1361 numWeights = range;
1362
1363 // build cutoff points based on weights
1364 // note: n is user-controlled; replaced with vector() - amyspark
1365 auto cutoffs = std::vector<double>(range);
1366 auto weights = std::vector<double>(range);
1367 double total = 0;
1368 for (int i = 0; i < range; i++) {
1369 double weight = i < numWeights ? params[i + 3] : 1;
1370 total += weight;
1371 cutoffs[i] = total;
1372 weights[i] = weight;
1373 }
1374
1375 if (total == 0)
1376 return loRange;
1377
1378 // scale value from [0..1] to [0..total] range
1379 index *= total;
1380
1381 // bsearch cutoff table to find index that spans value
1382 int lo = 0;
1383 int hi = range - 1;
1384 while (lo < hi) {
1385 int m = (lo + hi) / 2;
1386 if (index <= cutoffs[m])
1387 hi = m;
1388 else
1389 lo = m + 1;
1390 }
1391
1392 // skip zero-length intervals
1393 if (weights[lo] == 0) {
1394 if (lo > 0 && cutoffs[lo] > 0) // scan backward if possible
1395 while (--lo > 0 && weights[lo] == 0)
1396 ;
1397 else if (lo < range - 1) // else scan forward if possible
1398 while (++lo < range - 1 && weights[lo] == 0)
1399 ;
1400 }
1401
1402 // add offset and return result
1403 return loRange + lo;
1404}
1405static const char* pick_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1406 "int pick(float index, int loRange, int hiRange, [float weights, ...] )\n"
1407 "Picks values randomly between loRange and hiRange based on supplied index (which is\n"
1408 "automatically hashed).&nbsp; The values will be distributed according\n"
1409 "to the supplied weights.&nbsp; Any weights not supplied are assumed to\n"
1410 "be 1.0.");
1411
1412double swatch(int n, double *params)
1413{
1414 return choose(n, params);
1415}
1416static const char* swatch_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1417 "color swatch(float index, color choice0, color choice1, color choice2, [...])\n"
1418 "Chooses one of the supplied color choices based on the index (assumed to be in range [0..1]).");
1419
1420double choose(int n, double *params)
1421{
1422 if (n < 3)
1423 return 0;
1424 double key = params[0];
1425 // NaN protection
1426 if (key != key)
1427 return 0;
1428 int nvals = n - 1;
1429 return params[1 + int(clamp(key * nvals, 0, nvals - 1))];
1430}
1431static const char* choose_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1432 "float choose(float index,float choice1, float choice2, [...])\n"
1433 "Chooses one of the supplied choices based on the index (assumed to be in range [0, 1]).");
1434
1435double wchoose(int n, double *params)
1436{
1437 if (n < 5)
1438 return 0;
1439 double key = params[0];
1440 // NaN protection
1441 if (key != key)
1442 return 0;
1443 int nvals = (n - 1) / 2; // nweights = nvals
1444
1445 // build cutoff points based on weights
1446 // note: n is user-controlled; replaced with vector() - amyspark
1447 auto cutoffs = std::vector<double>(nvals);
1448 auto weights = std::vector<double>(nvals);
1449 double total = 0;
1450 for (int i = 0; i < nvals; i++) {
1451 double weight = params[i * 2 + 2];
1452 total += weight;
1453 cutoffs[i] = total;
1454 weights[i] = weight;
1455 }
1456
1457 if (total == 0)
1458 return params[1];
1459
1460 // scale value from [0..1] to [0..total] range
1461 key *= total;
1462
1463 // bsearch cutoff table to find index that spans value
1464 int lo = 0;
1465 int hi = nvals - 1;
1466 while (lo < hi) {
1467 int m = (lo + hi) / 2;
1468 if (key <= cutoffs[m])
1469 hi = m;
1470 else
1471 lo = m + 1;
1472 }
1473
1474 // skip zero-length intervals
1475 if (weights[lo] == 0) {
1476 if (lo > 0 && cutoffs[lo] > 0) // scan backward if possible
1477 while (--lo > 0 && weights[lo] == 0)
1478 ;
1479 else if (lo < nvals - 1) // else scan forward if possible
1480 while (++lo < nvals - 1 && weights[lo] == 0)
1481 ;
1482 }
1483
1484 // return corresponding value
1485 return params[lo * 2 + 1];
1486}
1487static const char* wchoose_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1488 "float wchoose(float index,float choice1, float weight1, float choice2, float weight2, [...] )\n"
1489 "Chooses one of the supplied choices based on the index (assumed to be in range [0..1]).\n"
1490 "The values will be distributed according to the supplied weights.");
1491
1492double spline(int n, double *params)
1493{
1494 if (n < 5)
1495 return 0;
1496 double u = clamp(params[0], 0, 1);
1497 if (u == 0)
1498 return params[2];
1499 if (u == 1)
1500 return params[n - 2];
1501 int nsegs = n - 4;
1502 double seg = NAN;
1503 u = modf(u * nsegs, &seg);
1504 double *p = &params[int(seg) + 1];
1505 double u2 = u * u;
1506 double u3 = u2 * u;
1507 return 0.5 * (p[0] * (-u3 + 2 * u2 - u) + p[1] * (3 * u3 - 5 * u2 + 2) + p[2] * (-3 * u3 + 4 * u2 + u) + p[3] * (u3 - u2));
1508}
1509static const char* spline_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1510 "float spline(float param,float y1,float y2,float y3,float y4,[...])\n\n"
1511 "Interpolates a set of values to the parameter specified where y1, ..., yn are\n"
1512 "distributed evenly from [0..1]");
1513
1514template<class T> struct CurveData : public ExprFuncNode::Data {
1516 CurveData() = default;
1517 ~CurveData() override = default;
1518 CurveData(const CurveData &) = default;
1519 CurveData &operator=(const CurveData &) = default;
1523
1525{
1526public:
1529 {
1530 }
1531
1533 {
1534 // check number of arguments
1535 int nargs = node->numChildren();
1536 if ((nargs - 1) % 3) {
1538 return ExprType().Error();
1539 }
1540
1541 bool valid = true;
1542 valid &= node->checkArg(0, ExprType().FP(1).Varying(), envBuilder);
1543 for (int i = 1; i < nargs && valid; i += 3) {
1544 valid &= node->checkArg(i, ExprType().FP(1).Constant(), envBuilder);
1545 valid &= node->checkArg(i + 1, ExprType().FP(1).Constant(), envBuilder);
1546 valid &= node->checkArg(i + 2, ExprType().FP(1).Constant(), envBuilder);
1547 if (valid) {
1548 const auto *value = dynamic_cast<ExprNumNode *>(node->child(i + 2));
1549 if (!value) {
1550 node->addError(ErrorCode::Unknown, {QT_TRANSLATE_NOOP_UTF8("builtin", "Unable to validate the interpolant type")});
1551 return ExprType().Error().Varying();
1552 }
1553 else if (!Curve<Vec3d>::interpTypeValid(static_cast<Curve<Vec3d>::InterpType>((int)value->value()))){
1554 node->addError(ErrorCode::Unknown, {QT_TRANSLATE_NOOP_UTF8("builtin", "Invalid interpolant type")});
1555 return ExprType().Error().Varying();
1556 }
1557 }
1558 }
1559 return valid ? ExprType().FP(1).Varying() : ExprType().Error();
1560 }
1561
1563 {
1564 auto *data = new CurveData<double>;
1565 for (int i = 1; i < args.nargs() - 2; i += 3) {
1566 double pos = args.inFp<1>(i)[0];
1567 double val = args.inFp<1>(i + 1)[0];
1568 double interpDouble = args.inFp<1>(i + 2)[0];
1569 int interpInt = (int)interpDouble;
1572 assert(false && "ExprFuncNode ERROR: invalid interpolant type!");
1573 }
1574 data->curve.addPoint(pos, val, interpolant);
1575 }
1576 data->curve.preparePoints();
1577 return data;
1578 }
1579
1580 void eval(ArgHandle args) override
1581 {
1582 auto *data = dynamic_cast<CurveData<double> *>(args.data);
1583 double param = args.inFp<1>(0)[0];
1584 args.outFp = data->curve.getValue(param);
1585 }
1586
1588static const char* curve_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1589 "float curve(float param,float pos0,float val0,int interp0,float pos1,float val1,int interp1,[...])\n\n"
1590 "Interpolates a 1D ramp defined by control points at 'param'. Control points are specified \n"
1591 "by triples of parameters pos_i, val_i, and interp_i. Interpolation codes are \n"
1592 "0 - none, 1 - linear, 2 - smooth, 3 - spline, \n"
1593 "4 - monotone (non oscillating spline)");
1594
1596{
1598 {
1599 // check number of arguments
1600 int nargs = node->numChildren();
1601 if ((nargs - 1) % 3) {
1602 node->addError(ErrorCode::WrongNumberOfArgumentsMultiple3Plus1);
1603 return ExprType().Error().Varying();
1604 }
1605
1606 bool valid = true;
1607 valid &= node->checkArg(0, ExprType().FP(1).Varying(), envBuilder);
1608 for (int i = 1; i < nargs && valid; i += 3) {
1609 valid &= node->checkArg(i, ExprType().FP(1).Constant(), envBuilder);
1610 valid &= node->checkArg(i + 1, ExprType().FP(3).Constant(), envBuilder);
1611 valid &= node->checkArg(i + 2, ExprType().FP(1).Constant(), envBuilder);
1612 if (valid) {
1613 const auto *value = dynamic_cast<ExprNumNode *>(node->child(i + 2));
1614 if (!value) {
1615 node->addError(ErrorCode::Unknown, {QT_TRANSLATE_NOOP_UTF8("builtin", "Unable to validate the interpolant type")});
1616 return ExprType().Error().Varying();
1617 } else if (!Curve<Vec3d>::interpTypeValid(static_cast<Curve<Vec3d>::InterpType>((int)value->value()))) {
1618 node->addError(ErrorCode::Unknown, {QT_TRANSLATE_NOOP_UTF8("builtin", "Invalid interpolant type")});
1619 return ExprType().Error().Varying();
1620 }
1621 }
1622 }
1623 return valid ? ExprType().FP(3).Varying() : ExprType().Error();
1624 }
1625
1627 {
1628 auto *data = new CurveData<Vec3d>;
1629 for (int i = 1; i < args.nargs() - 2; i += 3) {
1630 double pos = args.inFp<1>(i)[0];
1631 Vec3dRef val(&args.inFp<3>(i + 1)[0]);
1632 double interpDouble = args.inFp<1>(i + 2)[0];
1633 int interpInt = (int)interpDouble;
1636 // TODO: fix error checking!
1637 }
1638 data->curve.addPoint(pos, val, interpolant);
1639 }
1640 data->curve.preparePoints();
1641 return data;
1642 }
1643
1644 void eval(ArgHandle args) override
1645 {
1646 auto *data = dynamic_cast<CurveData<Vec3d> *>(args.data);
1647 double param = args.inFp<1>(0)[0];
1648 Vec3d result = data->curve.getValue(param);
1649 double *out = &args.outFp;
1650 for (int k = 0; k < 3; k++)
1651 out[k] = result[k];
1652 }
1653
1654public:
1657 {
1658 } // Thread Safe
1660static const char* ccurve_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1661 "color curve(float param,float pos0,color val0,int interp0,float pos1,color val1,int interp1,[...])\n\n"
1662 "Interpolates color ramp given by control points at 'param'. Control points are specified \n"
1663 "by triples of parameters pos_i, val_i, and interp_i. Interpolation codes are \n"
1664 "0 - none, 1 - linear, 2 - smooth, 3 - spline, \n"
1665 "4 - monotone (non oscillating spline)");
1666
1668{
1669 struct Data : public ExprFuncNode::Data {
1670 using func = void (*)(double *, double *);
1671 Data(func fIn, int dim)
1672 : f(fIn)
1673 , dim(dim)
1674 {
1675 }
1677 int dim;
1678 };
1679
1681 {
1682 bool valid = true;
1683 valid &= node->checkArg(0, ExprType().String().Constant(), envBuilder);
1684 std::string varName = node->getStrArg(0);
1685 auto *varNode = new ExprVarNode(node->expr(), varName.c_str());
1687 if (varType.isValid()) {
1688 node->removeLastChild(); // remove the useless default argument from the arugment list
1689 node->removeLastChild(); // remove the useless default argument from the arugment list
1690 node->addChild(varNode);
1691 } else {
1692 delete varNode;
1693 node->swapChildren(0, 1); // move the default argument in the beginning
1694 varType = node->child(0)->prep(wantScalar, envBuilder);
1695 node->removeLastChild(); // remove the useless string argument
1696 }
1697 return varType.isValid() ? varType : ExprType().Error();
1698 }
1699
1701 {
1702 return new Data(node->type().isFP() ? getTemplatizedOp<Assign, Data::func>(node->type().dim()) : nullptr, node->type().dim());
1703 }
1704
1705 template<int d> struct Assign {
1706 static void f(double *out, double *in)
1707 {
1708 for (int k = 0; k < d; k++)
1709 out[k] = in[k];
1710 }
1711 };
1712
1713 void eval(ArgHandle args) override
1714 {
1715 Data *data = dynamic_cast<Data *>(args.data);
1716 assert(data);
1717 double *out = &args.outFp;
1718 // for(int i=0;i<data->dim;i++) std::cerr<<" "<<args.inFp<1>(0)[i];
1719 // std::cerr<<std::endl;
1720 if (data->f)
1721 data->f(out, &args.inFp<1>(0)[0]);
1722 else
1723 throw std::runtime_error("getVar does not support non FP types right now got type");
1724 }
1725
1726public:
1729 {
1730 } // Thread Safe
1732static const char* getVar_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1733 "getVar(string varName,vector defaultValue)\n"
1734 "return value of varName if variable exists, otherwise return defaultValue");
1735
1737{
1738 struct Data : public ExprFuncNode::Data {
1739 std::vector<std::pair<int, int>> ranges;
1740 std::string format;
1741 };
1742
1743public:
1745 {
1746 int nargs = node->numChildren();
1747 if (nargs < 1) {
1748 node->addError(ErrorCode::WrongNumberOfArguments1Plus);
1749 return ExprType().Error();
1750 }
1751
1752 bool valid = true;
1753 valid &= node->checkArg(0, ExprType().String().Constant(), envBuilder);
1754
1755 int items = 0;
1756 int searchStart = 0;
1757 auto format = node->getStrArg(0);
1758 while (true) {
1759 std::size_t percentStart = format.find('%', searchStart);
1760 if (percentStart == std::string::npos)
1761 break;
1762 if (percentStart + 1 == format.length()) {
1764 return ExprType().Error();
1765 } else if (format[percentStart + 1] == '%') {
1766 searchStart = static_cast<int>(percentStart + 2);
1767 continue;
1768 } else if (format[percentStart + 1] == 'v' || format[percentStart + 1] == 'f') {
1769 items++;
1770 if (items >= node->numChildren()) {
1771 // TODO: test here, checkArg should not fail
1773 return ExprType().Error();
1774 } else {
1775 valid &= (node->checkArg(items, ExprType().FP(1), envBuilder) || node->checkArg(items, ExprType().FP(3), envBuilder));
1776 searchStart = static_cast<int>(percentStart + 2);
1777 }
1778 } else {
1779 node->addError(InvalidFormatString);
1780 return ExprType().Error();
1781 }
1782 }
1783
1784 if (!valid) {
1785 node->addError(ExpectedFloatOrFloat3);
1786 return ExprType().Error();
1787 } else if (items != nargs - 1) {
1789 return ExprType().Error();
1790 }
1791
1792 return ExprType().FP(1).Constant();
1793 }
1794
1796 {
1797 // parse format string
1798 unsigned int bakeStart = 0;
1799 int searchStart = 0;
1800 int needed = 0;
1801 Data *data = new Data;
1802 data->format = args.inStr(0);
1803 std::string &format = data->format;
1804 std::vector<std::pair<int, int>> &ranges = data->ranges;
1805
1806 int items = 0;
1807 while (true) {
1808 std::size_t percentStart = format.find('%', searchStart);
1809 if (percentStart == std::string::npos)
1810 break;
1811 if (percentStart + 1 == format.length()) {
1812 // node->addError("Unexpected end of format string");
1813 delete data;
1814 assert(false);
1815 } else if (format[percentStart + 1] == '%') {
1816 searchStart = static_cast<int>(percentStart + 2);
1817 continue;
1818 } else if (format[percentStart + 1] == 'v' || format[percentStart + 1] == 'f') {
1819 char c = format[percentStart + 1];
1820 int code = (c == 'v') ? -1 : -2;
1821 needed++;
1822 if (bakeStart != percentStart)
1823 ranges.emplace_back(bakeStart, static_cast<int>(percentStart));
1824 ranges.emplace_back(code, code);
1825 items++;
1826 searchStart = static_cast<int>(percentStart + 2);
1828 } else {
1829 // node->addError("Invalid format string, only %v is allowed");
1830 delete data;
1831 // TODO: check that this is correct
1832 // return ExprType().Error().Varying();
1833 // return false;
1834 assert(false);
1835 }
1836 }
1837 if (bakeStart != format.length())
1838 ranges.emplace_back(bakeStart, static_cast<int>(format.length()));
1839
1840 if (items != args.nargs() - 1) {
1841 // node->addError("Wrong number of arguments for format string");
1842 delete data;
1843 // TODO: check that this is correct
1844 // return ExprType().Error().Varying();
1845 // return false;
1846 assert(false);
1847 }
1848
1849 return data;
1850 }
1851
1852 void eval(ArgHandle args) override
1853 {
1854 Data *data = dynamic_cast<Data *>(args.data);
1855 int item = 1;
1856 for (unsigned int i = 0; i < data->ranges.size(); i++) {
1857 const std::pair<int, int> &range = data->ranges[i];
1858 if (range.first == -2) {
1859 std::cerr << args.inFp<1>(item)[0];
1860 item++;
1861 } else if (range.first == -1) {
1862 std::cerr << "[" << args.inFp<3>(item)[0] << "," << args.inFp<3>(item)[1] << "," << args.inFp<3>(item)[2] << "]";
1863 item++;
1864 } else {
1865 std::cerr << data->format.substr(range.first, range.second - range.first);
1866 }
1867 }
1868 std::cerr << std::endl;
1869
1870 args.outFp = 0;
1871 }
1872
1875 {
1876 } // not thread safe
1877
1879static const char* printf_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1880 "float printf(string format,[vec0, vec1, ...])\n"
1881 "Prints out a string to STDOUT, Format parameters allowed are \"%v\" and \"%f\".\n"
1882 "Return parameter is empty, but must be assigned to a variable.");
1883
1885{
1886 struct StringData : public KSeExpr::ExprFuncNode::Data, public std::string {
1887 };
1888
1889 // Format specifier categories for SPrintFuncX
1890 std::string _intSpec {"diouxXc"};
1891 std::string _doubleSpec {"eEfFgGaA"};
1892 std::string _strSpec {"s"};
1893
1894public:
1897 {
1898 } // not thread safe
1899
1901 {
1902 int nargs = node->numChildren();
1903 if (nargs < 1) {
1904 node->addError(ErrorCode::WrongNumberOfArguments1Plus);
1905 return ExprType().Error().Constant();
1906 }
1907 if (!node->checkArg(0, ExprType().String().Constant(), envBuilder)) {
1908 node->addError(ErrorCode::FirstArgumentNotString);
1909 return ExprType().Error().Constant();
1910 }
1911
1912 const std::string &format = dynamic_cast<const ExprStrNode *>(node->child(0))->str();
1913
1914 static const std::string strSpec("s");
1915 size_t searchStart = 0;
1916 size_t exprArg = 1;
1917 while (true) {
1918 const size_t specStart = format.find('%', searchStart);
1919 if (specStart == std::string::npos)
1920 break;
1921 if (specStart + 1 == format.length()) {
1922 node->addError(ErrorCode::IncompleteFormatSpecifier);
1923 return ExprType().Error().Constant();
1924 }
1925 if (format[specStart + 1] == '%') {
1926 searchStart = specStart + 2; // Skip "%%"
1927 continue;
1928 }
1929
1930 const size_t specEnd = format.find_first_of(std::string(_intSpec).append(_doubleSpec).append(_strSpec), specStart);
1931 if (specEnd == std::string::npos) {
1932 node->addError(ErrorCode::IncompleteFormatSpecifier);
1933 return ExprType().Error().Constant();
1934 }
1935 if (_strSpec.find(format[specEnd]) != std::string::npos) {
1936 if (!node->checkArg(exprArg, ExprType().String(), envBuilder)) {
1937 return ExprType().Error().Constant();
1938 }
1939 } else {
1940 if (!node->checkArg(exprArg, ExprType().FP(1), envBuilder)) {
1941 return ExprType().Error().Constant();
1942 }
1943 }
1944 ++exprArg;
1945 searchStart = specEnd + 1;
1946 };
1947 return ExprType().String().Constant();
1948 }
1949
1951 {
1952 return new StringData();
1953 }
1954
1955 void eval(ArgHandle args) override
1956 {
1957 auto result = *dynamic_cast<StringData *>(args.data);
1958 result.assign(args.inStr(0));
1959
1960 std::array<char, 255> fragment {};
1961 size_t searchStart = 0;
1962 size_t exprArg = 1;
1963 while (true) {
1964 const size_t specStart = result.find('%', searchStart);
1965 if (specStart == std::string::npos)
1966 break;
1967 if (result[specStart + 1] == '%') {
1968 result.erase(specStart, 1);
1969 searchStart = specStart + 1;
1970 continue;
1971 }
1972
1973 const size_t specEnd = result.find_first_of(std::string(_intSpec).append(_doubleSpec).append(_strSpec), specStart);
1974 const std::string &spec = result.substr(specStart, specEnd - specStart + 1);
1975 int fragLen = -1;
1976 if (std::string::npos != _intSpec.find(result[specEnd]))
1977 fragLen = snprintf(fragment.data(), 255, spec.c_str(), int(args.inFp<1>(exprArg++)[0]));
1978 else if (std::string::npos != _doubleSpec.find(result[specEnd]))
1979 fragLen = snprintf(fragment.data(), 255, spec.c_str(), args.inFp<1>(exprArg++)[0]);
1980 else if (std::string::npos != _strSpec.find(result[specEnd]))
1981 fragLen = snprintf(fragment.data(), 255, spec.c_str(), args.inStr(exprArg++));
1982 assert(fragLen >= 0);
1983
1984 result.replace(specStart, spec.size(), fragment.data());
1985 searchStart += fragLen + 1;
1986 };
1987
1988 args.outStr = const_cast<char *>(result.c_str());
1989 }
1990
1992static const char* sprintf_docstring = QT_TRANSLATE_NOOP_UTF8("builtin",
1993 "sprintf(string format, [double|string, double|string, ...])\n"
1994 "Returns a string formatted from the given values. See 'man sprintf' for format details.");
1995
1996#if 0
1997
1998class TestFunc:public ExprFuncSimple
1999{
2000 struct MyData:public ExprFuncNode::Data
2001 {
2002 float foo;
2003 MyData(float foo)
2004 :foo(foo)
2005 {}
2006 };
2007public:
2008 TestFunc()
2009 :ExprFuncSimple(true)
2010 {}
2011 virtual ExprType prep(ExprFuncNode* node,bool scalarWanted,ExprVarEnvBuilder& envBuilder) const
2012 {
2013 bool valid=true;
2014 valid &= node->checkArg(0,ExprType().FP(3).Varying(),envBuilder);
2015 valid &= node->checkArg(1,ExprType().FP(1).Constant(),envBuilder);
2016 return valid ?ExprType().FP(3).Varying():ExprType().Error();
2017 }
2018 virtual ExprFuncNode::Data* evalConstant(ArgHandle args) const
2019 {
2020 //std::cerr<<"evalling const "<<args.inFp<1>(1)<<std::endl;
2021 return new MyData(args.inFp<1>(1)[0]);
2022 }
2023 virtual void eval(ArgHandle args)
2024 {
2025 MyData* data=static_cast<MyData*>(args.data);
2026
2027 Vec<double,3,true>(&args.outFp)=args.inFp<3>(0)+Vec<double,3,false>(data->foo);
2028 }
2029} testfunc;
2030static const char* testfunc_docstring="fdsA";
2031
2032#endif
2033
2035// functions from math.h (global namespace)
2036//#define FUNC(func) define(#func, ExprFunc(::func))
2037#define FUNCADOC(name, func) define3(name, ExprFunc(::func), func##_docstring)
2038#define FUNCDOC(func) define3(#func, ExprFunc(::func), func##_docstring)
2039 FUNCADOC("abs", fabs);
2040 FUNCDOC(acos);
2041 FUNCDOC(asin);
2042 FUNCDOC(atan);
2043 FUNCDOC(atan2);
2044 FUNCDOC(ceil);
2045 FUNCDOC(cos);
2046 FUNCDOC(cosh);
2047 FUNCDOC(exp);
2048 FUNCDOC(floor);
2049 FUNCDOC(fmod);
2050 FUNCDOC(log);
2051 FUNCDOC(log10);
2052 FUNCDOC(pow);
2053 FUNCDOC(sin);
2054 FUNCDOC(sinh);
2055 FUNCDOC(sqrt);
2056 FUNCDOC(tan);
2057 FUNCDOC(tanh);
2058 FUNCDOC(cbrt);
2059 FUNCDOC(asinh);
2060 FUNCDOC(acosh);
2061 FUNCDOC(atanh);
2062 FUNCDOC(trunc);
2063// local functions (KSeExpr namespace)
2064//#undef FUNC
2065#undef FUNCDOC
2066//#define FUNC(func) define(#func, ExprFunc(KSeExpr::func))
2067//#define FUNCN(func, min, max) define(#func, ExprFunc(KSeExpr::func, min, max))
2068#define FUNCDOC(func) define3(#func, ExprFunc(KSeExpr::func), func##_docstring)
2069#define FUNCNDOC(func, min, max) define3(#func, ExprFunc(KSeExpr::func, min, max), func##_docstring)
2070
2071 // trig
2072 FUNCDOC(deg);
2073 FUNCDOC(rad);
2074 FUNCDOC(cosd);
2075 FUNCDOC(sind);
2076 FUNCDOC(tand);
2077 FUNCDOC(acosd);
2078 FUNCDOC(asind);
2079 FUNCDOC(atand);
2080 FUNCDOC(atan2d);
2081
2082 // clamping
2083 FUNCDOC(clamp);
2084 FUNCDOC(round);
2085 FUNCDOC(max);
2086 FUNCDOC(min);
2087
2088 // blending / remapping
2089 FUNCDOC(invert);
2091 FUNCDOC(expand);
2092 FUNCDOC(fit);
2093 FUNCDOC(gamma);
2094 FUNCDOC(bias);
2100 FUNCDOC(remap);
2101 FUNCDOC(mix);
2102 FUNCNDOC(hsi, 4, 5);
2103 FUNCNDOC(midhsi, 5, 7);
2106 FUNCNDOC(saturate, 2, 2);
2107
2108 // noise
2109 FUNCNDOC(rand, 0, 3);
2110 FUNCNDOC(hash, 1, -1);
2111 FUNCNDOC(noise, 1, 4);
2112 FUNCDOC(snoise);
2113 FUNCDOC(vnoise);
2114 FUNCDOC(cnoise);
2115 FUNCNDOC(snoise4, 2, 2);
2116 FUNCNDOC(vnoise4, 2, 2);
2117 FUNCNDOC(cnoise4, 2, 2);
2118 FUNCNDOC(turbulence, 1, 4);
2119 FUNCNDOC(vturbulence, 1, 4);
2120 FUNCNDOC(cturbulence, 1, 4);
2121 FUNCNDOC(fbm, 1, 4);
2122 FUNCNDOC(vfbm, 1, 4);
2123 FUNCNDOC(cfbm, 1, 4);
2126 FUNCDOC(pnoise);
2127 FUNCNDOC(fbm4, 2, 5);
2128 FUNCNDOC(vfbm4, 2, 5);
2129 FUNCNDOC(cfbm4, 2, 5);
2130
2131 // vectors
2132 FUNCDOC(dist);
2133 FUNCDOC(length);
2134 FUNCDOC(hypot);
2135 FUNCDOC(dot);
2136 FUNCDOC(norm);
2137 FUNCDOC(cross);
2138 FUNCDOC(angle);
2139 FUNCDOC(ortho);
2140 FUNCNDOC(rotate, 3, 3);
2141 FUNCDOC(up);
2142
2143 // variations
2144 FUNCDOC(cycle);
2145 FUNCNDOC(pick, 3, -1);
2146 FUNCNDOC(choose, 3, -1);
2147 FUNCNDOC(wchoose, 4, -1);
2148 FUNCNDOC(swatch, 3, -1);
2149 FUNCNDOC(spline, 5, -1);
2150
2151 // FuncX interface
2152 // noise
2153 FUNCNDOC(voronoi, 1, 7);
2154 FUNCNDOC(cvoronoi, 1, 7);
2155 FUNCNDOC(pvoronoi, 1, 6);
2156 // variations
2157 FUNCNDOC(curve, 1, -1);
2158 FUNCNDOC(ccurve, 1, -1);
2159 FUNCNDOC(getVar, 2, 2);
2160 FUNCNDOC(printf, 1, -1);
2161 // FUNCNDOC(testfunc,2,2);
2162
2163 FUNCNDOC(sprintf, 1, -1);
2164}
2165} // namespace KSeExpr
ExprType prep(ExprFuncNode *node, bool, ExprVarEnvBuilder &envBuilder) const override
#define FUNCDOC(func)
void eval(ArgHandle args) override
ExprFuncNode::Data * evalConstant(const ExprFuncNode *, ArgHandle) const override
#define FUNCNDOC(func, min, max)
#define FUNCADOC(name, func)
#define QT_TRANSLATE_NOOP(scope, x)
#define QT_TRANSLATE_NOOP_UTF8(scope, x)
static constexpr std::array< int, 514 > p
Definition NoiseTables.h:10
ExprFuncNode::Data * evalConstant(const ExprFuncNode *, ArgHandle args) const override
void eval(ArgHandle args) override
ExprType prep(ExprFuncNode *node, bool, ExprVarEnvBuilder &envBuilder) const override
CachedVoronoiFunc(VoronoiFunc *vfunc) noexcept
ExprType prep(ExprFuncNode *node, bool, ExprVarEnvBuilder &envBuilder) const override
Vec3d(VoronoiPointData &, int, const Vec3d *) VoronoiFunc
void eval(ArgHandle args) override
ExprFuncNode::Data * evalConstant(const ExprFuncNode *, ArgHandle) const override
ExprFuncNode::Data * evalConstant(const ExprFuncNode *, ArgHandle args) const override
ExprType prep(ExprFuncNode *node, bool, ExprVarEnvBuilder &envBuilder) const override
void eval(ArgHandle args) override
Interpolation curve class for double->double and double->Vec3D.
Definition Curve.h:27
InterpType
Supported interpolation types.
Definition Curve.h:32
Node that calls a function.
Definition ExprNode.h:654
ExprFuncNode::Data * data
Definition ExprFuncX.h:91
Vec< double, d, true > inFp(int i)
Definition ExprFuncX.h:77
void(*)(const char *, const ExprFunc &, const char *) Define3
Definition ExprFunc.h:50
void(*)(const char *, const ExprFunc &) Define
Definition ExprFunc.h:49
Node that stores a numeric constant.
Definition ExprNode.h:610
Node that stores a string.
Definition ExprNode.h:632
ExprType & FP(int d)
Mutate this into a floating point type of dimension d.
Definition ExprType.h:97
ExprType & Constant()
Mutate this into a constant lifetime.
Definition ExprType.h:122
ExprType & Varying()
Mutate this into a varying lifetime.
Definition ExprType.h:134
ExprType & String()
Mutate this into a string type.
Definition ExprType.h:104
ExprType & Error()
Mutate this into an error type.
Definition ExprType.h:111
Variable scope builder is used by the type checking and code gen to track visiblity of variables and ...
Definition ExprEnv.h:181
Node that references a variable.
Definition ExprNode.h:572
ExprType prep(ExprFuncNode *node, bool wantScalar, ExprVarEnvBuilder &envBuilder) const override
ExprFuncNode::Data * evalConstant(const ExprFuncNode *node, ArgHandle) const override
void eval(ArgHandle args) override
void eval(ArgHandle args) override
ExprType prep(ExprFuncNode *node, bool, ExprVarEnvBuilder &envBuilder) const override
ExprFuncNode::Data * evalConstant(const ExprFuncNode *, ArgHandle args) const override
ExprFuncNode::Data * evalConstant(const ExprFuncNode *, ArgHandle args) const override
void eval(ArgHandle args) override
ExprType prep(ExprFuncNode *node, bool, ExprVarEnvBuilder &envBuilder) const override
void eval(ArgHandle args) override
ExprFuncNode::Data * evalConstant(const ExprFuncNode *, ArgHandle) const override
ExprType prep(ExprFuncNode *node, bool, ExprVarEnvBuilder &envBuilder) const override
T dot(const Vec< T, d, refother > &o) const
Definition Vec.h:292
T_VEC_VALUE rotateBy(const Vec< T, 3, refother > &axis, T angle) const
Definition Vec.h:330
T length() const
Euclidean (2) norm.
Definition Vec.h:153
double gamma(double x, double g)
Vec3d cvoronoiFn(VoronoiPointData &data, int n, const Vec3d *args)
static const char * pnoise_docstring
static const char * invert_docstring
static const char * atand_docstring
static const char * vfbm4_docstring
Vec3d pvoronoiFn(VoronoiPointData &data, int n, const Vec3d *args)
double max(double x, double y)
static const char * trunc_docstring
static const char * ceil_docstring
static const char * remap_docstring
static const char * vnoise_docstring
double deg(double angle)
double atan2d(double y, double x)
double gaussstep(double x, double a, double b)
double fit(double x, double a1, double b1, double a2, double b2)
double mix(double x, double y, double alpha)
double asind(double x)
static const char * voronoi_docstring
static const char * mix_docstring
static const char * fmod_docstring
static const char * cos_docstring
static const char * cross_docstring
double dist(const Vec3d &a, const Vec3d &b)
static const char * pick_docstring
KSeExpr::SPrintFuncX sprintf
static const char * smoothstep_docstring
Vec3d vturbulence(int n, const Vec3d *args)
Vec3d vnoise4(int, const Vec3d *args)
static const char * hypot_docstring
static void voronoi_f1f2_3d(VoronoiPointData &data, const Vec3d &p, double jitter, double &f1, Vec3d &pos1, double &f2, Vec3d &pos2)
static const char * hsi_docstring
static const char * expand_docstring
static const char * noise_docstring
static const char * atan2d_docstring
Vec3d rgbtohsl(const Vec3d &rgb)
double dot(const Vec3d &a, const Vec3d &b)
double fbm4(int n, const Vec3d *args)
static const char * acos_docstring
static const char * hash_docstring
static const char * cycle_docstring
static const char * cfbm4_docstring
Vec3d cfbm(int n, const Vec3d *args)
double bias(double x, double b)
static const char * cellnoise_docstring
KSeExpr::PrintFuncX printf
double pnoise(const Vec3d &p, const Vec3d &period)
static const char * vfbm_docstring
static const char * rad_docstring
Vec3d vfbm(int n, const Vec3d *args)
static const char * asind_docstring
Vec3d cnoise4(int n, const Vec3d *args)
static const char * turbulence_docstring
static const char * compress_docstring
double contrast(double x, double c)
static const char * fit_docstring
double hypot(double x, double y)
static double hslvalue(double x, double y, double H)
double snoise4(int, const Vec3d *args)
double noise(int n, const Vec3d *args)
static const char * acosh_docstring
static const char * rotate_docstring
static const char * pvoronoi_docstring
static const char * atanh_docstring
Vec3d cturbulence(int n, const Vec3d *args)
static const char * cturbulence_docstring
Vec3d norm(const Vec3d &a)
double tand(double x)
static const char * dist_docstring
static void voronoi_f1_3d(VoronoiPointData &data, const Vec3d &p, double jitter, double &f1, Vec3d &pos1)
static const char * max_docstring
KSeExpr::CurveData voronoi
static const char * vturbulence_docstring
KSeExpr::GetVar getVar
double length(const Vec3d &v)
double fbm(int n, const Vec3d *args)
static const char * printf_docstring
Vec3d hsiAdjust(const Vec3d &rgb, double h, double s, double i)
static const char * min_docstring
static const char * atan2_docstring
static const char * gamma_docstring
double invert(double x)
double angle(const Vec3d &a, const Vec3d &b)
static const char * midhsi_docstring
Vec3d up(const Vec3d &P, const Vec3d &upvec)
static const char * wchoose_docstring
double linearstep(double x, double a, double b)
double round(double x)
static const char * log10_docstring
static const char * floor_docstring
static const char * boxstep_docstring
static const char * cvoronoi_docstring
Vec3d voronoiFn(VoronoiPointData &data, int n, const Vec3d *args)
static const char * cbrt_docstring
static const char * choose_docstring
double cycle(double index, double loRange, double hiRange)
static const char * cosd_docstring
double boxstep(double x, double a)
static const char * spline_docstring
static const char * hsltorgb_docstring
double expand(double x, double lo, double hi)
static const char * ccellnoise_docstring
static const char * acosd_docstring
double smoothstep(double x, double a, double b)
static const char * dot_docstring
Vec3d cross(const Vec3d &a, const Vec3d &b)
static Vec3d * voronoi_points(VoronoiPointData &data, const Vec3d &cell, double jitter)
static const char * curve_docstring
static const char * sin_docstring
static const char * ortho_docstring
double remap(double x, double source, double range, double falloff, double interp)
static const char * sqrt_docstring
static const char * sprintf_docstring
static const char * fbm4_docstring
double atand(double x)
static const char * log_docstring
double hash(int n, double *args)
double spline(int n, double *params)
static const char * tan_docstring
static const char * angle_docstring
static const char * atan_docstring
static const char * saturate_docstring
double turbulence(int n, const Vec3d *args)
double min(double x, double y)
static const char * snoise4_docstring
static const char * pow_docstring
double snoise(const Vec3d &p)
static const char * clamp_docstring
static const char * cfbm_docstring
static const char * swatch_docstring
static const char * norm_docstring
KSeExpr::CCurveFuncX ccurve
@ InvalidFormatString
Invalid format string, only v or f is allowed.
Definition ErrorCode.h:59
@ WrongNumberOfArgumentsMultiple3Plus1
"Wrong number of arguments, should be multiple of 3 plus 1"
Definition ErrorCode.h:26
@ WrongNumberOfArguments
"Wrong number of arguments, should be 1 to 7"
Definition ErrorCode.h:24
@ ExpectedFloatOrFloat3
"Expected float or FP[3]"
Definition ErrorCode.h:20
@ Unknown
Unknown error (message = %1)
Definition ErrorCode.h:64
@ UnexpectedEndOfFormatString
Unexpected end of format string.
Definition ErrorCode.h:57
@ WrongNumberOfArgumentsForFormatString
Wrong number of arguments for format string.
Definition ErrorCode.h:61
static const char * fabs_docstring
Vec3d vfbm4(int n, const Vec3d *args)
Vec3d hsltorgb(const Vec3d &hsl)
static const char * contrast_docstring
static const char * ccurve_docstring
static const char * tand_docstring
static const char * linearstep_docstring
static const char * cosh_docstring
static const char * length_docstring
Vec3d rotate(int n, const Vec3d *args)
KSeExpr::CachedVoronoiFunc ExprFuncSimple pvoronoi(pvoronoiFn)
Vec3d cfbm4(int n, const Vec3d *args)
static const char * asinh_docstring
KSeExpr::RandFuncX rand
double clamp(double x, double lo, double hi)
static Vec3d saturate(const Vec3d &Cin, double amt)
Vec3d vnoise(const Vec3d &p)
static const char * fbm_docstring
static const char * rand_docstring
Vec3d hsi(int n, const Vec3d *args)
static const char * sinh_docstring
double cosd(double x)
static const char * vnoise4_docstring
KSeExpr::CachedVoronoiFunc ExprFuncSimple cvoronoi(cvoronoiFn)
void defineBuiltins(ExprFunc::Define, ExprFunc::Define3 define3)
double choose(int n, double *params)
double pick(int n, double *params)
static const char * rgbtohsl_docstring
double rad(double angle)
static const char * snoise_docstring
static const char * cnoise_docstring
double swatch(int n, double *params)
static const char * sind_docstring
Vec< double, 3, false > Vec3d
Definition Vec.h:352
double acosd(double x)
static const char * gaussstep_docstring
double compress(double x, double lo, double hi)
Vec3d ortho(const Vec3d &a, const Vec3d &b)
static const char * deg_docstring
static const char * tanh_docstring
static const char * up_docstring
Vec3d midhsi(int n, const Vec3d *args)
double sind(double x)
double wchoose(int n, double *params)
double cellnoise(const Vec3d &p)
static const char * exp_docstring
Vec3d ccellnoise(const Vec3d &p)
static const char * getVar_docstring
Vec3d cnoise(const Vec3d &p)
static const char * asin_docstring
static const char * cnoise4_docstring
static const char * bias_docstring
static const char * round_docstring
CurveData & operator=(const CurveData &)=default
CurveData(CurveData &&) noexcept=default
~CurveData() override=default
CurveData(const CurveData &)=default
base class for custom instance data
Definition ExprNode.h:723
static void f(double *out, double *in)
Data(func fIn, int dim)
std::vector< std::pair< int, int > > ranges
std::uniform_real_distribution dis
std::array< Vec3d, 27 > points