p5 2.3.0 → 2.3.1-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/README.md +3 -11
  2. package/dist/accessibility/color_namer.js +7 -7
  3. package/dist/accessibility/index.js +7 -7
  4. package/dist/app.js +7 -7
  5. package/dist/app.node.js +7 -7
  6. package/dist/color/color_conversion.js +7 -7
  7. package/dist/color/creating_reading.js +1 -1
  8. package/dist/color/index.js +2 -2
  9. package/dist/color/p5.Color.js +1 -1
  10. package/dist/color/setting.js +2 -2
  11. package/dist/{constants-Dv6ZqA6s.js → constants-Bx3_xAam.js} +1 -1
  12. package/dist/core/constants.js +1 -1
  13. package/dist/core/environment.js +1 -1
  14. package/dist/core/filterShaders.js +1 -1
  15. package/dist/core/friendly_errors/fes_core.js +1 -1
  16. package/dist/core/friendly_errors/file_errors.js +1 -1
  17. package/dist/core/friendly_errors/index.js +1 -1
  18. package/dist/core/friendly_errors/param_validator.js +1 -1
  19. package/dist/core/friendly_errors/sketch_verifier.js +1 -1
  20. package/dist/core/helpers.js +1 -1
  21. package/dist/core/init.js +7 -7
  22. package/dist/core/internationalization.js +1 -1
  23. package/dist/core/legacy.js +7 -7
  24. package/dist/core/main.js +7 -7
  25. package/dist/core/p5.Graphics.js +4 -4
  26. package/dist/core/p5.Renderer.js +3 -3
  27. package/dist/core/p5.Renderer2D.js +7 -7
  28. package/dist/core/p5.Renderer3D.js +4 -4
  29. package/dist/core/rendering.js +4 -4
  30. package/dist/{creating_reading-DLkHH80h.js → creating_reading-YUSK33h8.js} +8 -0
  31. package/dist/dom/dom.js +2 -2
  32. package/dist/dom/index.js +2 -2
  33. package/dist/dom/p5.Element.js +2 -2
  34. package/dist/dom/p5.MediaElement.js +2 -2
  35. package/dist/events/pointer.js +15 -0
  36. package/dist/image/const.js +1 -1
  37. package/dist/image/filterRenderer2D.js +6 -6
  38. package/dist/image/image.js +4 -4
  39. package/dist/image/index.js +4 -4
  40. package/dist/image/loading_displaying.js +4 -4
  41. package/dist/image/p5.Image.js +3 -3
  42. package/dist/io/files.js +4 -4
  43. package/dist/io/index.js +4 -4
  44. package/dist/{ir_builders-C8mrXj5z.js → ir_builders-CMXkjMoV.js} +34 -24
  45. package/dist/{main-BR2ALC_6.js → main-BpW7VbDr.js} +39 -208
  46. package/dist/math/Matrices/Matrix.js +1 -1
  47. package/dist/math/Matrices/MatrixNumjs.js +1 -1
  48. package/dist/math/calculation.js +669 -84
  49. package/dist/math/index.js +1 -1
  50. package/dist/math/p5.Matrix.js +1 -1
  51. package/dist/math/p5.Vector.js +9 -1
  52. package/dist/math/patch-vector.js +3 -1
  53. package/dist/math/trigonometry.js +399 -48
  54. package/dist/{p5.Renderer-XOvhEbJn.js → p5.Renderer-op0Y1WO6.js} +2 -2
  55. package/dist/{rendering-C5lX6zKm.js → rendering-SZ71KIDM.js} +24 -9
  56. package/dist/shape/2d_primitives.js +1 -1
  57. package/dist/shape/attributes.js +4 -3
  58. package/dist/shape/custom_shapes.js +205 -3
  59. package/dist/shape/index.js +2 -2
  60. package/dist/strands/ir_builders.js +1 -1
  61. package/dist/strands/p5.strands.js +26 -9
  62. package/dist/strands/strands_FES.js +8 -1
  63. package/dist/strands/strands_api.js +316 -21
  64. package/dist/strands/strands_conditionals.js +1 -1
  65. package/dist/strands/strands_for.js +1 -1
  66. package/dist/strands/strands_node.js +1 -1
  67. package/dist/strands/strands_ternary.js +1 -1
  68. package/dist/{strands_glslBackend-D6G3UyCs.js → strands_glslBackend-DMhOnoGl.js} +2 -2
  69. package/dist/type/index.js +3 -3
  70. package/dist/type/p5.Font.js +13 -4
  71. package/dist/type/textCore.js +3 -3
  72. package/dist/webgl/3d_primitives.js +4 -4
  73. package/dist/webgl/GeometryBuilder.js +1 -1
  74. package/dist/webgl/ShapeBuilder.js +1 -1
  75. package/dist/webgl/enums.js +1 -1
  76. package/dist/webgl/index.js +6 -6
  77. package/dist/webgl/interaction.js +1 -1
  78. package/dist/webgl/light.js +4 -4
  79. package/dist/webgl/loading.js +4 -4
  80. package/dist/webgl/material.js +4 -4
  81. package/dist/webgl/p5.Camera.js +4 -4
  82. package/dist/webgl/p5.Framebuffer.js +4 -4
  83. package/dist/webgl/p5.Geometry.js +1 -1
  84. package/dist/webgl/p5.Quat.js +1 -1
  85. package/dist/webgl/p5.RendererGL.js +6 -6
  86. package/dist/webgl/p5.Shader.js +5 -1
  87. package/dist/webgl/p5.Texture.js +4 -4
  88. package/dist/webgl/strands_glslBackend.js +2 -2
  89. package/dist/webgl/text.js +4 -4
  90. package/dist/webgl/utils.js +4 -4
  91. package/dist/webgpu/index.js +2 -2
  92. package/dist/webgpu/p5.RendererWebGPU.js +2 -2
  93. package/dist/webgpu/strands_wgslBackend.js +2 -2
  94. package/lib/p5.esm.js +1765 -405
  95. package/lib/p5.esm.min.js +1 -1
  96. package/lib/p5.js +1765 -405
  97. package/lib/p5.min.js +1 -1
  98. package/lib/p5.webgpu.esm.js +34 -3
  99. package/lib/p5.webgpu.js +34 -3
  100. package/lib/p5.webgpu.min.js +1 -1
  101. package/package.json +1 -1
  102. package/types/global.d.ts +5539 -2604
  103. package/types/p5.d.ts +2917 -1432
package/lib/p5.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! p5.js v2.3.0 May 28, 2026 */
1
+ /*! p5.js v2.3.1-rc.1 June 24, 2026 */
2
2
  /**
3
3
  * @module Constants
4
4
  * @submodule Constants
@@ -12,7 +12,7 @@ const _PI = Math.PI;
12
12
  * @property {String} VERSION
13
13
  * @final
14
14
  */
15
- const VERSION = '2.3.0';
15
+ const VERSION = '2.3.1-rc.1';
16
16
 
17
17
  // GRAPHICS RENDERER
18
18
  /**
@@ -11357,6 +11357,14 @@ function creatingReading(p5, fn){
11357
11357
  * The version of `color()` with four parameters interprets them as RGBA, HSBA,
11358
11358
  * or HSLA colors, depending on the current `colorMode()`. The last parameter
11359
11359
  * sets the alpha (transparency) value.
11360
+ * In p5.strands shader callbacks, `color()` accepts the same input
11361
+ * formats but returns a `vec4` instead of a `p5.Color` object, with
11362
+ * RGBA components normalized to the 0–1 range. All colors in strands
11363
+ * are RGB-based; `colorMode()` has no effect inside shader callbacks.
11364
+ * Color utility functions such as `red()`, `green()`, `blue()`,
11365
+ * `alpha()`, `hue()`, `saturation()`, `brightness()`, and
11366
+ * `lightness()` also return values in the 0–1 range when used in
11367
+ * strands.
11360
11368
  *
11361
11369
  * @method color
11362
11370
  * @param {Number} gray number specifying value between white and black.
@@ -17530,7 +17538,7 @@ function _checkFileExtension(filename, extension) {
17530
17538
  */
17531
17539
 
17532
17540
 
17533
- class Image {
17541
+ let Image$1 = class Image {
17534
17542
  constructor(width, height) {
17535
17543
  this.width = width;
17536
17544
  this.height = height;
@@ -19313,7 +19321,7 @@ class Image {
19313
19321
  }
19314
19322
  }
19315
19323
  }
19316
- }
19324
+ };
19317
19325
  function encodeAndDownloadGif(pImg, filename) {
19318
19326
  const props = pImg.gifProperties;
19319
19327
 
@@ -19615,9 +19623,9 @@ function image$2(p5, fn){
19615
19623
  * @param {Number} width
19616
19624
  * @param {Number} height
19617
19625
  */
19618
- p5.Image = Image;
19626
+ p5.Image = Image$1;
19619
19627
 
19620
- Image.prototype._friendlyError = p5._friendlyError;
19628
+ Image$1.prototype._friendlyError = p5._friendlyError;
19621
19629
 
19622
19630
  /**
19623
19631
  * The image's width in pixels.
@@ -23465,6 +23473,14 @@ function vector(p5, fn) {
23465
23473
  * @property z
23466
23474
  * @name z
23467
23475
  */
23476
+
23477
+ /**
23478
+ * The dimensions of the vector
23479
+ * @type {Number}
23480
+ * @for p5.Vector
23481
+ * @property dimensions
23482
+ * @name dimensions
23483
+ */
23468
23484
  }
23469
23485
 
23470
23486
  if (typeof p5 !== 'undefined') {
@@ -24012,6 +24028,47 @@ class EllipsePrimitive extends ShapePrimitive {
24012
24028
  }
24013
24029
  }
24014
24030
 
24031
+ class RectPrimitive extends ShapePrimitive {
24032
+ #x;
24033
+ #y;
24034
+ #w;
24035
+ #h;
24036
+ #tl;
24037
+ #tr;
24038
+ #br;
24039
+ #bl;
24040
+ #vertexCapacity = 1;
24041
+
24042
+ constructor(startVertex, x, y, w, h, tl, tr, br, bl) {
24043
+ super(startVertex);
24044
+ this.#x = x;
24045
+ this.#y = y;
24046
+ this.#w = w;
24047
+ this.#h = h;
24048
+ this.#tl = tl;
24049
+ this.#tr = tr;
24050
+ this.#br = br;
24051
+ this.#bl = bl;
24052
+ }
24053
+
24054
+ get x() { return this.#x; }
24055
+ get y() { return this.#y; }
24056
+ get w() { return this.#w; }
24057
+ get h() { return this.#h; }
24058
+ get tl() { return this.#tl; }
24059
+ get tr() { return this.#tr; }
24060
+ get br() { return this.#br; }
24061
+ get bl() { return this.#bl; }
24062
+
24063
+ get vertexCapacity() {
24064
+ return this.#vertexCapacity;
24065
+ }
24066
+
24067
+ accept(visitor) {
24068
+ visitor.visitRectPrimitive(this);
24069
+ }
24070
+ }
24071
+
24015
24072
  // ---- TESSELLATION PRIMITIVES ----
24016
24073
 
24017
24074
  class TriangleFan extends ShapePrimitive {
@@ -24494,6 +24551,42 @@ class Shape {
24494
24551
  return primitive.addToShape(this);
24495
24552
  }
24496
24553
 
24554
+ rectPrimitive(x, y, w, h, tl, tr, br, bl) {
24555
+ const startVertex = this.#createVertex(new Vector(x, y));
24556
+ const primitive = new RectPrimitive(startVertex, x, y, w, h, tl, tr, br, bl);
24557
+ return primitive.addToShape(this);
24558
+ }
24559
+
24560
+ point(x, y) {
24561
+ const v0 = this.#createVertex(new Vector(x, y));
24562
+ const primitive = new Point(v0);
24563
+ return primitive.addToShape(this);
24564
+ }
24565
+
24566
+ line(x1, y1, x2, y2) {
24567
+ const v0 = this.#createVertex(new Vector(x1, y1));
24568
+ const v1 = this.#createVertex(new Vector(x2, y2));
24569
+ const primitive = new Line(v0, v1);
24570
+ return primitive.addToShape(this);
24571
+ }
24572
+
24573
+ triangle(x1, y1, x2, y2, x3, y3) {
24574
+ const v0 = this.#createVertex(new Vector(x1, y1));
24575
+ const v1 = this.#createVertex(new Vector(x2, y2));
24576
+ const v2 = this.#createVertex(new Vector(x3, y3));
24577
+ const primitive = new Triangle(v0, v1, v2);
24578
+ return primitive.addToShape(this);
24579
+ }
24580
+
24581
+ quad(x1, y1, x2, y2, x3, y3, x4, y4) {
24582
+ const v0 = this.#createVertex(new Vector(x1, y1));
24583
+ const v1 = this.#createVertex(new Vector(x2, y2));
24584
+ const v2 = this.#createVertex(new Vector(x3, y3));
24585
+ const v3 = this.#createVertex(new Vector(x4, y4));
24586
+ const primitive = new Quad(v0, v1, v2, v3);
24587
+ return primitive.addToShape(this);
24588
+ }
24589
+
24497
24590
  beginContour(shapeKind = PATH) {
24498
24591
  if (this.at(-1)?.kind === EMPTY_PATH) {
24499
24592
  this.contours.pop();
@@ -24598,6 +24691,9 @@ class PrimitiveVisitor {
24598
24691
  visitEllipsePrimitive(ellipse) {
24599
24692
  throw new Error('Method visitEllipsePrimitive() has not been implemented.');
24600
24693
  }
24694
+ visitRectPrimitive(rect) {
24695
+ throw new Error('Method visitRectPrimitive() has not been implemented.');
24696
+ }
24601
24697
 
24602
24698
  // isolated primitives
24603
24699
  visitPoint(point) {
@@ -24801,6 +24897,62 @@ class PrimitiveToPath2DConverter extends PrimitiveVisitor {
24801
24897
  this.path.moveTo(centerX + radiusX, centerY);
24802
24898
  this.path.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI);
24803
24899
  }
24900
+ visitRectPrimitive(rect) {
24901
+ const x = rect.x;
24902
+ const y = rect.y;
24903
+ const w = rect.w;
24904
+ const h = rect.h;
24905
+ let tl = rect.tl;
24906
+ let tr = rect.tr;
24907
+ let br = rect.br;
24908
+ let bl = rect.bl;
24909
+
24910
+ if (typeof tl === 'undefined') {
24911
+ this.path.rect(x, y, w, h);
24912
+ } else {
24913
+ if (typeof tr === 'undefined') {
24914
+ tr = tl;
24915
+ }
24916
+ if (typeof br === 'undefined') {
24917
+ br = tr;
24918
+ }
24919
+ if (typeof bl === 'undefined') {
24920
+ bl = br;
24921
+ }
24922
+
24923
+ const absW = Math.abs(w);
24924
+ const absH = Math.abs(h);
24925
+ const hw = absW / 2;
24926
+ const hh = absH / 2;
24927
+
24928
+ if (absW < 2 * tl) {
24929
+ tl = hw;
24930
+ }
24931
+ if (absH < 2 * tl) {
24932
+ tl = hh;
24933
+ }
24934
+ if (absW < 2 * tr) {
24935
+ tr = hw;
24936
+ }
24937
+ if (absH < 2 * tr) {
24938
+ tr = hh;
24939
+ }
24940
+ if (absW < 2 * br) {
24941
+ br = hw;
24942
+ }
24943
+ if (absH < 2 * br) {
24944
+ br = hh;
24945
+ }
24946
+ if (absW < 2 * bl) {
24947
+ bl = hw;
24948
+ }
24949
+ if (absH < 2 * bl) {
24950
+ bl = hh;
24951
+ }
24952
+
24953
+ this.path.roundRect(x, y, w, h, [tl, tr, br, bl]);
24954
+ }
24955
+ }
24804
24956
  visitQuadStrip(quadStrip) {
24805
24957
  for (let i = 0; i < quadStrip.vertices.length - 3; i += 2) {
24806
24958
  const v0 = quadStrip.vertices[i];
@@ -24997,6 +25149,72 @@ class PrimitiveToVerticesConverter extends PrimitiveVisitor {
24997
25149
  verts.push(new Vertex(vertexProps));
24998
25150
  }
24999
25151
 
25152
+ this.contours.push(verts);
25153
+ }
25154
+ visitRectPrimitive(rect) {
25155
+ const x = rect.x;
25156
+ const y = rect.y;
25157
+ const w = rect.w;
25158
+ const h = rect.h;
25159
+ let tl = rect.tl;
25160
+ let tr = rect.tr;
25161
+ let br = rect.br;
25162
+ let bl = rect.bl;
25163
+
25164
+ const startVertex = rect.vertices[0];
25165
+ const getVertexProps = (px, py) => {
25166
+ const props = {};
25167
+ for (const [key, value] of Object.entries(startVertex)) {
25168
+ if (key === 'position') continue;
25169
+ props[key] = value;
25170
+ }
25171
+ props.position = new Vector(px, py);
25172
+ return new Vertex(props);
25173
+ };
25174
+
25175
+ const verts = [];
25176
+ if (typeof tl === 'undefined') {
25177
+ verts.push(getVertexProps(x, y));
25178
+ verts.push(getVertexProps(x + w, y));
25179
+ verts.push(getVertexProps(x + w, y + h));
25180
+ verts.push(getVertexProps(x, y + h));
25181
+ verts.push(getVertexProps(x, y));
25182
+ } else {
25183
+ if (typeof tr === 'undefined') tr = tl;
25184
+ if (typeof br === 'undefined') br = tr;
25185
+ if (typeof bl === 'undefined') bl = br;
25186
+
25187
+ const absW = Math.abs(w);
25188
+ const absH = Math.abs(h);
25189
+ const hw = absW / 2;
25190
+ const hh = absH / 2;
25191
+
25192
+ if (absW < 2 * tl) tl = hw;
25193
+ if (absH < 2 * tl) tl = hh;
25194
+ if (absW < 2 * tr) tr = hw;
25195
+ if (absH < 2 * tr) tr = hh;
25196
+ if (absW < 2 * br) br = hw;
25197
+ if (absH < 2 * br) br = hh;
25198
+ if (absW < 2 * bl) bl = hw;
25199
+ if (absH < 2 * bl) bl = hh;
25200
+
25201
+ const addCornerArc = (cx, cy, rx, ry, startAngle, endAngle) => {
25202
+ const perimeter = Math.PI / 2 * (rx + ry) / 2;
25203
+ const numPoints = Math.max(1, Math.ceil(this.curveDetail * perimeter));
25204
+ for (let i = 0; i <= numPoints; i++) {
25205
+ const angle = startAngle + (endAngle - startAngle) * (i / numPoints);
25206
+ verts.push(getVertexProps(cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)));
25207
+ }
25208
+ };
25209
+
25210
+ addCornerArc(x + tl, y + tl, tl, tl, Math.PI, 1.5 * Math.PI);
25211
+ addCornerArc(x + w - tr, y + tr, tr, tr, 1.5 * Math.PI, 2 * Math.PI);
25212
+ addCornerArc(x + w - br, y + h - br, br, br, 0, 0.5 * Math.PI);
25213
+ addCornerArc(x + bl, y + h - bl, bl, bl, 0.5 * Math.PI, Math.PI);
25214
+
25215
+ verts.push(verts[0]);
25216
+ }
25217
+
25000
25218
  this.contours.push(verts);
25001
25219
  }
25002
25220
  }
@@ -26841,7 +27059,7 @@ class Renderer {
26841
27059
  // get(x,y,w,h)
26842
27060
  }
26843
27061
 
26844
- const region = new Image(w*pd, h*pd);
27062
+ const region = new Image$1(w*pd, h*pd);
26845
27063
  region.pixelDensity(pd);
26846
27064
  region.canvas
26847
27065
  .getContext('2d')
@@ -30602,8 +30820,9 @@ function attributes(p5, fn){
30602
30820
  * Sets the width of the stroke used for points, lines, and the outlines of
30603
30821
  * shapes.
30604
30822
  *
30605
- * Note: `strokeWeight()` is affected by transformations, especially calls to
30606
- * <a href="#/p5/scale">scale()</a>.
30823
+ * Note: In 2D mode, `strokeWeight()` is affected by transformations,
30824
+ * especially calls to <a href="#/p5/scale">scale()</a>. It isn't affected by
30825
+ * transformations in WebGL and WebGPU modes.
30607
30826
  *
30608
30827
  * Calling `strokeWeight()` without an argument returns the current stroke weight as a number.
30609
30828
  *
@@ -38983,8 +39202,19 @@ function loadingDisplaying(p5, fn){
38983
39202
 
38984
39203
  } else {
38985
39204
  // Non-GIF Section
38986
- const blob = new Blob([data]);
38987
- const img = await createImageBitmap(blob);
39205
+ const img = await new Promise((resolve, reject) => {
39206
+ const img = new Image();
39207
+ const blob = new Blob([data], { type: contentType });
39208
+ const url = URL.createObjectURL(blob);
39209
+
39210
+ img.onerror = e => reject(e);
39211
+ img.onload = () => {
39212
+ URL.revokeObjectURL(url);
39213
+ resolve(img);
39214
+ };
39215
+
39216
+ img.src = url;
39217
+ });
38988
39218
 
38989
39219
  pImg.width = pImg.canvas.width = img.width;
38990
39220
  pImg.height = pImg.canvas.height = img.height;
@@ -55054,7 +55284,11 @@ class Renderer3D extends Renderer {
55054
55284
  // the next time a shader is used. However, the texture() function
55055
55285
  // works differently and is global p5 state. If the p5 state has
55056
55286
  // been cleared, we also need to clear the value in uSampler to match.
55057
- fillShader.setUniform("uSampler", this.states._tex || empty);
55287
+ this._settingFillUniforms = true;
55288
+ if (this.states._tex || !fillShader._userSetSampler) {
55289
+ fillShader.setUniform("uSampler", this.states._tex || empty);
55290
+ }
55291
+ this._settingFillUniforms = false;
55058
55292
  fillShader.setUniform(
55059
55293
  "uTint",
55060
55294
  this.states.tint?._getRGBA([255, 255, 255, 255]) ?? [255, 255, 255, 255]
@@ -55177,7 +55411,7 @@ class Renderer3D extends Renderer {
55177
55411
  _getEmptyTexture() {
55178
55412
  if (!this._emptyTexture) {
55179
55413
  // a plain white texture RGBA, full alpha, single pixel.
55180
- const im = new Image(1, 1);
55414
+ const im = new Image$1(1, 1);
55181
55415
  im.set(0, 0, 255);
55182
55416
  this._emptyTexture = new Texture(this, im);
55183
55417
  }
@@ -60621,7 +60855,7 @@ class Shader {
60621
60855
  * @returns {String} The GLSL version used by the shader.
60622
60856
  */
60623
60857
  version() {
60624
- const match = /#version (.+)$/.exec(this.vertSrc());
60858
+ const match = /#version (.+)$/m.exec(this.vertSrc());
60625
60859
  if (match) {
60626
60860
  return match[1];
60627
60861
  } else {
@@ -61625,6 +61859,10 @@ class Shader {
61625
61859
  return;
61626
61860
  }
61627
61861
 
61862
+ if (uniformName === 'uSampler' && !this._renderer._settingFillUniforms) {
61863
+ this._userSetSampler = true;
61864
+ }
61865
+
61628
61866
  // In p5.strands-related code, where some of the code may be in
61629
61867
  // p5.webgpu.js instead of the main p5.js build, we generally use
61630
61868
  // duck typing instead of instanceof to avoid accidentally importing
@@ -63936,7 +64174,7 @@ function material(p5, fn) {
63936
64174
  * // Replace alpha in the color with dithering by
63937
64175
  * // randomly setting pixel colors to 0 based on opacity
63938
64176
  * let a = 1;
63939
- * if (noise(pixelInputs.position.xy) > pixelInputs.color.a) {
64177
+ * if (random() > pixelInputs.color.a) {
63940
64178
  * a = 0;
63941
64179
  * }
63942
64180
  * pixelInputs.color.a = a;
@@ -65766,11 +66004,7 @@ function trigonometry(p5, fn){
65766
66004
  * <a href="#/p5/angleMode">angleMode()</a> is `DEGREES`, then values are
65767
66005
  * returned in the range 0 to 180.
65768
66006
  *
65769
- * @method acos
65770
- * @param {Number} value value whose arc cosine is to be returned.
65771
- * @return {Number} arc cosine of the given value.
65772
- *
65773
- * @example
66007
+ * ```js example
65774
66008
  * function setup() {
65775
66009
  * createCanvas(100, 100);
65776
66010
  *
@@ -65788,8 +66022,9 @@ function trigonometry(p5, fn){
65788
66022
  *
65789
66023
  * describe('The numbers 3.142, -1, and 3.142 written on separate rows.');
65790
66024
  * }
66025
+ * ```
65791
66026
  *
65792
- * @example
66027
+ * ```js example
65793
66028
  * function setup() {
65794
66029
  * createCanvas(100, 100);
65795
66030
  *
@@ -65807,6 +66042,52 @@ function trigonometry(p5, fn){
65807
66042
  *
65808
66043
  * describe('The numbers 3.927, -0.707, and 2.356 written on separate rows.');
65809
66044
  * }
66045
+ * ```
66046
+ *
66047
+ * `acos()` can also be used in shaders with p5.strands. The following example
66048
+ * uses `acos()` to create a pulsing color transition on a shape.
66049
+ *
66050
+ * ```js example
66051
+ * let myShader;
66052
+ *
66053
+ * function setup() {
66054
+ * createCanvas(100, 100, WEBGL);
66055
+ * myShader = buildColorShader(shaderCallback);
66056
+ * describe('A sphere that pulses between orange and teal.');
66057
+ * }
66058
+ *
66059
+ * function shaderCallback() {
66060
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
66061
+ * let t = millis() * 0.001;
66062
+ *
66063
+ * // acos(cos(t)) creates a triangle wave that goes from 0 to PI and back.
66064
+ * // Dividing by PI normalizes the result to the 0 to 1 range.
66065
+ * let value = acos(cos(t)) / PI;
66066
+ *
66067
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66068
+ * let orange = [1, 0.5, 0, 1];
66069
+ * let teal = [0, 0.8, 0.8, 1];
66070
+ *
66071
+ * finalColor.begin();
66072
+ *
66073
+ * // mix() blends between orange (when value = 0) and teal (when value = 1).
66074
+ * // acos() creates a pulsing effect by turning smooth oscillation into a triangle wave.
66075
+ * finalColor.set(mix(orange, teal, value));
66076
+ *
66077
+ * finalColor.end();
66078
+ * }
66079
+ *
66080
+ * function draw() {
66081
+ * background(220);
66082
+ * shader(myShader);
66083
+ * noStroke();
66084
+ * sphere(30);
66085
+ * }
66086
+ * ```
66087
+ *
66088
+ * @method acos
66089
+ * @param {Number} value value whose arc cosine is to be returned.
66090
+ * @return {Number} arc cosine of the given value.
65810
66091
  */
65811
66092
  fn.acos = function(ratio) {
65812
66093
  return this._fromRadians(Math.acos(ratio));
@@ -65821,11 +66102,7 @@ function trigonometry(p5, fn){
65821
66102
  * the <a href="#/p5/angleMode">angleMode()</a> is `DEGREES` then values are
65822
66103
  * returned in the range -90 to 90.
65823
66104
  *
65824
- * @method asin
65825
- * @param {Number} value value whose arc sine is to be returned.
65826
- * @return {Number} arc sine of the given value.
65827
- *
65828
- * @example
66105
+ * ```js example
65829
66106
  * function setup() {
65830
66107
  * createCanvas(100, 100);
65831
66108
  *
@@ -65843,8 +66120,9 @@ function trigonometry(p5, fn){
65843
66120
  *
65844
66121
  * describe('The numbers 1.047, 0.866, and 1.047 written on separate rows.');
65845
66122
  * }
66123
+ * ```
65846
66124
  *
65847
- * @example
66125
+ * ```js example
65848
66126
  * function setup() {
65849
66127
  * createCanvas(100, 100);
65850
66128
  *
@@ -65862,6 +66140,52 @@ function trigonometry(p5, fn){
65862
66140
  *
65863
66141
  * describe('The numbers 4.189, -0.866, and -1.047 written on separate rows.');
65864
66142
  * }
66143
+ * ```
66144
+ *
66145
+ * `asin()` can also be used in shaders with p5.strands. The following example
66146
+ * uses `asin()` to create a smooth color transition on a shape.
66147
+ *
66148
+ * ```js example
66149
+ * let myShader;
66150
+ *
66151
+ * function setup() {
66152
+ * createCanvas(100, 100, WEBGL);
66153
+ * myShader = buildColorShader(shaderCallback);
66154
+ * describe('A sphere that smoothly shifts between green and purple.');
66155
+ * }
66156
+ *
66157
+ * function shaderCallback() {
66158
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
66159
+ * let t = millis() * 0.001;
66160
+ *
66161
+ * // asin(sin(t)) returns a value between -PI/2 and PI/2.
66162
+ * // Dividing by PI/2 normalizes to -1 to 1, then adding 1 and multiplying by 0.5
66163
+ * // remaps to the 0 to 1 range.
66164
+ * let value = (asin(sin(t)) / (PI / 2) + 1) * 0.5;
66165
+ *
66166
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66167
+ * let green = [0, 1, 0.5, 1];
66168
+ * let purple = [0.5, 0, 1, 1];
66169
+ *
66170
+ * finalColor.begin();
66171
+ *
66172
+ * // mix() blends between green (when value = 0) and purple (when value = 1).
66173
+ * finalColor.set(mix(green, purple, value));
66174
+ *
66175
+ * finalColor.end();
66176
+ * }
66177
+ *
66178
+ * function draw() {
66179
+ * background(220);
66180
+ * shader(myShader);
66181
+ * noStroke();
66182
+ * sphere(30);
66183
+ * }
66184
+ * ```
66185
+ *
66186
+ * @method asin
66187
+ * @param {Number} value value whose arc sine is to be returned.
66188
+ * @return {Number} arc sine of the given value.
65865
66189
  */
65866
66190
  fn.asin = function(ratio) {
65867
66191
  return this._fromRadians(Math.asin(ratio));
@@ -65876,11 +66200,7 @@ function trigonometry(p5, fn){
65876
66200
  * (about 1.57). If the <a href="#/p5/angleMode">angleMode()</a> is `DEGREES`
65877
66201
  * then values are returned in the range -90 to 90.
65878
66202
  *
65879
- * @method atan
65880
- * @param {Number} value value whose arc tangent is to be returned.
65881
- * @return {Number} arc tangent of the given value.
65882
- *
65883
- * @example
66203
+ * ```js example
65884
66204
  * function setup() {
65885
66205
  * createCanvas(100, 100);
65886
66206
  *
@@ -65898,8 +66218,9 @@ function trigonometry(p5, fn){
65898
66218
  *
65899
66219
  * describe('The numbers 1.047, 1.732, and 1.047 written on separate rows.');
65900
66220
  * }
66221
+ * ```
65901
66222
  *
65902
- * @example
66223
+ * ```js example
65903
66224
  * function setup() {
65904
66225
  * createCanvas(100, 100);
65905
66226
  *
@@ -65917,6 +66238,55 @@ function trigonometry(p5, fn){
65917
66238
  *
65918
66239
  * describe('The numbers 4.189, 1.732, and 1.047 written on separate rows.');
65919
66240
  * }
66241
+ * ```
66242
+ *
66243
+ * `atan()` can also be used in shaders with p5.strands. The following example
66244
+ * uses `atan()` to create a soft color transition on a shape.
66245
+ *
66246
+ * ```js example
66247
+ * let myShader;
66248
+ *
66249
+ * function setup() {
66250
+ * createCanvas(100, 100, WEBGL);
66251
+ * myShader = buildColorShader(shaderCallback);
66252
+ * describe('A sphere that softly shifts between pink and lime.');
66253
+ * }
66254
+ *
66255
+ * function shaderCallback() {
66256
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
66257
+ * // sin() oscillates the input between -5 and 5, so atan() gets both positive and negative values.
66258
+ * let t = sin(millis() * 0.001) * 5;
66259
+ *
66260
+ * // atan(t) returns values between -PI/2 and PI/2.
66261
+ * // Dividing by PI/2 normalizes to -1 to 1, then adding 1 and multiplying by 0.5
66262
+ * // remaps to the 0 to 1 range.
66263
+ * // atan() compresses the wide range of t into a smooth S-curve (soft clipping).
66264
+ * let value = (atan(t) / (PI / 2) + 1) * 0.5;
66265
+ *
66266
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66267
+ * let pink = [1, 0, 0.5, 1];
66268
+ * let lime = [0.5, 1, 0, 1];
66269
+ *
66270
+ * finalColor.begin();
66271
+ *
66272
+ * // mix() blends between pink (when value = 0) and lime (when value = 1).
66273
+ * // atan() creates a soft, eased transition instead of a linear blend.
66274
+ * finalColor.set(mix(pink, lime, value));
66275
+ *
66276
+ * finalColor.end();
66277
+ * }
66278
+ *
66279
+ * function draw() {
66280
+ * background(220);
66281
+ * shader(myShader);
66282
+ * noStroke();
66283
+ * sphere(30);
66284
+ * }
66285
+ * ```
66286
+ *
66287
+ * @method atan
66288
+ * @param {Number} value value whose arc tangent is to be returned.
66289
+ * @return {Number} arc tangent of the given value.
65920
66290
  */
65921
66291
  fn.atan = function(ratio) {
65922
66292
  return this._fromRadians(Math.atan(ratio));
@@ -66000,11 +66370,7 @@ function trigonometry(p5, fn){
66000
66370
  * calculates the cosine of an angle, using radians by default, or according
66001
66371
  * to if <a href="#/p5/angleMode">angleMode()</a> setting (RADIANS or DEGREES).
66002
66372
  *
66003
- * @method cos
66004
- * @param {Number} angle the angle, in radians by default, or according to if <a href="/reference/p5/angleMode/">angleMode()</a> setting (RADIANS or DEGREES).
66005
- * @return {Number} cosine of the angle.
66006
- *
66007
- * @example
66373
+ * ```js example
66008
66374
  * function setup() {
66009
66375
  * createCanvas(100, 100);
66010
66376
  *
@@ -66022,8 +66388,9 @@ function trigonometry(p5, fn){
66022
66388
  * line(50, y, x, y);
66023
66389
  * circle(x, y, 20);
66024
66390
  * }
66391
+ * ```
66025
66392
  *
66026
- * @example
66393
+ * ```js example
66027
66394
  * function setup() {
66028
66395
  * createCanvas(100, 100);
66029
66396
  *
@@ -66040,8 +66407,9 @@ function trigonometry(p5, fn){
66040
66407
  * // Draw the point.
66041
66408
  * point(x, y);
66042
66409
  * }
66410
+ * ```
66043
66411
  *
66044
- * @example
66412
+ * ```js example
66045
66413
  * function setup() {
66046
66414
  * createCanvas(100, 100);
66047
66415
  *
@@ -66058,6 +66426,51 @@ function trigonometry(p5, fn){
66058
66426
  * // Draw the point.
66059
66427
  * point(x, y);
66060
66428
  * }
66429
+ * ```
66430
+ *
66431
+ * `cos()` can also be used in shaders with p5.strands. The following example
66432
+ * uses `cos()` to smoothly oscillate the color of a shape over time.
66433
+ *
66434
+ * ```js example
66435
+ * let myShader;
66436
+ *
66437
+ * function setup() {
66438
+ * createCanvas(100, 100, WEBGL);
66439
+ * myShader = buildColorShader(shaderCallback);
66440
+ * describe('A sphere that fades between yellow and blue.');
66441
+ * }
66442
+ *
66443
+ * function shaderCallback() {
66444
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
66445
+ * let t = millis() * 0.001;
66446
+ *
66447
+ * // cos(t) oscillates between -1 and 1.
66448
+ * // 0.5 + 0.5 * cos(t) remaps this to the 0 to 1 range.
66449
+ * let value = 0.5 + 0.5 * cos(t);
66450
+ *
66451
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66452
+ * let yellow = [1, 1, 0, 1];
66453
+ * let blue = [0, 0, 1, 1];
66454
+ *
66455
+ * finalColor.begin();
66456
+ *
66457
+ * // mix() blends between yellow (when value = 0) and blue (when value = 1).
66458
+ * finalColor.set(mix(yellow, blue, value));
66459
+ *
66460
+ * finalColor.end();
66461
+ * }
66462
+ *
66463
+ * function draw() {
66464
+ * background(220);
66465
+ * shader(myShader);
66466
+ * noStroke();
66467
+ * sphere(30);
66468
+ * }
66469
+ * ```
66470
+ *
66471
+ * @method cos
66472
+ * @param {Number} angle the angle, in radians by default, or according to if <a href="/reference/p5/angleMode/">angleMode()</a> setting (RADIANS or DEGREES).
66473
+ * @return {Number} cosine of the angle.
66061
66474
  */
66062
66475
  fn.cos = function(angle) {
66063
66476
  return Math.cos(this._toRadians(angle));
@@ -66071,11 +66484,7 @@ function trigonometry(p5, fn){
66071
66484
  * calculates the sine of an angle, using radians by default, or according to
66072
66485
  * if <a href="#/p5/angleMode">angleMode()</a> setting (RADIANS or DEGREES).
66073
66486
  *
66074
- * @method sin
66075
- * @param {Number} angle the angle, in radians by default, or according to if <a href="/reference/p5/angleMode/">angleMode()</a> setting (RADIANS or DEGREES).
66076
- * @return {Number} sine of the angle.
66077
- *
66078
- * @example
66487
+ * ```js example
66079
66488
  * function setup() {
66080
66489
  * createCanvas(100, 100);
66081
66490
  *
@@ -66093,8 +66502,9 @@ function trigonometry(p5, fn){
66093
66502
  * line(50, y, x, y);
66094
66503
  * circle(x, y, 20);
66095
66504
  * }
66505
+ * ```
66096
66506
  *
66097
- * @example
66507
+ * ```js example
66098
66508
  * function setup() {
66099
66509
  * createCanvas(100, 100);
66100
66510
  *
@@ -66111,8 +66521,9 @@ function trigonometry(p5, fn){
66111
66521
  * // Draw the point.
66112
66522
  * point(x, y);
66113
66523
  * }
66524
+ * ```
66114
66525
  *
66115
- * @example
66526
+ * ```js example
66116
66527
  * function setup() {
66117
66528
  * createCanvas(100, 100);
66118
66529
  *
@@ -66129,6 +66540,51 @@ function trigonometry(p5, fn){
66129
66540
  * // Draw the point.
66130
66541
  * point(x, y);
66131
66542
  * }
66543
+ * ```
66544
+ *
66545
+ * `sin()` can also be used in shaders with p5.strands. The following example
66546
+ * uses `sin()` to oscillate the color of a shape over time.
66547
+ *
66548
+ * ```js example
66549
+ * let myShader;
66550
+ *
66551
+ * function setup() {
66552
+ * createCanvas(100, 100, WEBGL);
66553
+ * myShader = buildColorShader(shaderCallback);
66554
+ * describe('A sphere that pulses between cyan and magenta.');
66555
+ * }
66556
+ *
66557
+ * function shaderCallback() {
66558
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
66559
+ * let t = millis() * 0.001;
66560
+ *
66561
+ * // sin(t) oscillates between -1 and 1.
66562
+ * // 0.5 + 0.5 * sin(t) remaps this to the 0 to 1 range.
66563
+ * let value = 0.5 + 0.5 * sin(t);
66564
+ *
66565
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66566
+ * let cyan = [0, 1, 1, 1];
66567
+ * let magenta = [1, 0, 1, 1];
66568
+ *
66569
+ * finalColor.begin();
66570
+ *
66571
+ * // mix() blends between cyan (when value = 0) and magenta (when value = 1).
66572
+ * finalColor.set(mix(cyan, magenta, value));
66573
+ *
66574
+ * finalColor.end();
66575
+ * }
66576
+ *
66577
+ * function draw() {
66578
+ * background(220);
66579
+ * shader(myShader);
66580
+ * noStroke();
66581
+ * sphere(30);
66582
+ * }
66583
+ * ```
66584
+ *
66585
+ * @method sin
66586
+ * @param {Number} angle the angle, in radians by default, or according to if <a href="/reference/p5/angleMode/">angleMode()</a> setting (RADIANS or DEGREES).
66587
+ * @return {Number} sine of the angle.
66132
66588
  */
66133
66589
  fn.sin = function(angle) {
66134
66590
  return Math.sin(this._toRadians(angle));
@@ -66143,11 +66599,7 @@ function trigonometry(p5, fn){
66143
66599
  * by default, or according to
66144
66600
  * if <a href="#/p5/angleMode">angleMode()</a> setting (RADIANS or DEGREES).
66145
66601
  *
66146
- * @method tan
66147
- * @param {Number} angle the angle, in radians by default, or according to if <a href="/reference/p5/angleMode/">angleMode()</a> setting (RADIANS or DEGREES).
66148
- * @return {Number} tangent of the angle.
66149
- *
66150
- * @example
66602
+ * ```js example
66151
66603
  * function setup() {
66152
66604
  * createCanvas(100, 100);
66153
66605
  *
@@ -66164,6 +66616,53 @@ function trigonometry(p5, fn){
66164
66616
  * // Draw the point.
66165
66617
  * point(x, y);
66166
66618
  * }
66619
+ * ```
66620
+ *
66621
+ * `tan()` can also be used in shaders with p5.strands. The following example
66622
+ * uses `tan()` to create rapid color transitions on a shape.
66623
+ *
66624
+ * ```js example
66625
+ * let myShader;
66626
+ *
66627
+ * function setup() {
66628
+ * createCanvas(100, 100, WEBGL);
66629
+ * myShader = buildColorShader(shaderCallback);
66630
+ * describe('A sphere with rapidly shifting colors.');
66631
+ * }
66632
+ *
66633
+ * function shaderCallback() {
66634
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.0005 to slow it down.
66635
+ * let t = millis() * 0.0005;
66636
+ *
66637
+ * // tan(t) can grow to very large values (even infinity) at certain angles.
66638
+ * // 0.5 + 0.5 * tan(t) shifts the range but can still go way past 0 or 1.
66639
+ * // min(max(..., 0), 1) clamps the result to the 0 to 1 range.
66640
+ * let value = min(max(0.5 + 0.5 * tan(t), 0), 1);
66641
+ *
66642
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66643
+ * let orange = [1, 0.5, 0, 1];
66644
+ * let blue = [0, 0.5, 1, 1];
66645
+ *
66646
+ * finalColor.begin();
66647
+ *
66648
+ * // mix() blends between orange (when value = 0) and blue (when value = 1).
66649
+ * // tan() creates rapid, dramatic color shifts as it spikes and resets.
66650
+ * finalColor.set(mix(orange, blue, value));
66651
+ *
66652
+ * finalColor.end();
66653
+ * }
66654
+ *
66655
+ * function draw() {
66656
+ * background(220);
66657
+ * shader(myShader);
66658
+ * noStroke();
66659
+ * sphere(30);
66660
+ * }
66661
+ * ```
66662
+ *
66663
+ * @method tan
66664
+ * @param {Number} angle the angle, in radians by default, or according to if <a href="/reference/p5/angleMode/">angleMode()</a> setting (RADIANS or DEGREES).
66665
+ * @return {Number} tangent of the angle.
66167
66666
  */
66168
66667
  fn.tan = function(angle) {
66169
66668
  return Math.tan(this._toRadians(angle));
@@ -66179,11 +66678,7 @@ function trigonometry(p5, fn){
66179
66678
  * quarter of a full rotation. The same angle is 2 &times; &pi; &divide; 4
66180
66679
  * (about 1.57) radians.
66181
66680
  *
66182
- * @method degrees
66183
- * @param {Number} radians radians value to convert to degrees.
66184
- * @return {Number} converted angle.
66185
- *
66186
- * @example
66681
+ * ```js example
66187
66682
  * function setup() {
66188
66683
  * createCanvas(100, 100);
66189
66684
  *
@@ -66198,6 +66693,54 @@ function trigonometry(p5, fn){
66198
66693
  *
66199
66694
  * describe('The text "0.79 rad = 45˚".');
66200
66695
  * }
66696
+ * ```
66697
+ *
66698
+ * `degrees()` can also be used in shaders with p5.strands. The following example
66699
+ * uses `degrees()` to convert a radian value to degrees inside a shader.
66700
+ *
66701
+ * ```js example
66702
+ * let myShader;
66703
+ *
66704
+ * function setup() {
66705
+ * createCanvas(100, 100, WEBGL);
66706
+ * myShader = buildColorShader(shaderCallback);
66707
+ * describe('A sphere that cycles through warm colors.');
66708
+ * }
66709
+ *
66710
+ * function shaderCallback() {
66711
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
66712
+ * let t = millis() * 0.001;
66713
+ *
66714
+ * // degrees() converts the radian value t to degrees.
66715
+ * // (deg % 360) wraps the degrees into a 0-360 range.
66716
+ * // Dividing by 360 normalizes to the 0 to 1 range.
66717
+ * let deg = degrees(t);
66718
+ * let value = (deg % 360) / 360;
66719
+ *
66720
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66721
+ * let red = [1, 0, 0, 1];
66722
+ * let yellow = [1, 1, 0, 1];
66723
+ *
66724
+ * finalColor.begin();
66725
+ *
66726
+ * // mix() blends between red (when value = 0) and yellow (when value = 1).
66727
+ * // degrees() creates a cycling sawtooth pattern as time increases.
66728
+ * finalColor.set(mix(red, yellow, value));
66729
+ *
66730
+ * finalColor.end();
66731
+ * }
66732
+ *
66733
+ * function draw() {
66734
+ * background(220);
66735
+ * shader(myShader);
66736
+ * noStroke();
66737
+ * sphere(30);
66738
+ * }
66739
+ * ```
66740
+ *
66741
+ * @method degrees
66742
+ * @param {Number} radians radians value to convert to degrees.
66743
+ * @return {Number} converted angle.
66201
66744
  */
66202
66745
  fn.degrees = angle => angle * RAD_TO_DEG;
66203
66746
 
@@ -66211,11 +66754,7 @@ function trigonometry(p5, fn){
66211
66754
  * quarter of a full rotation. The same angle is 2 &times; &pi; &divide; 4
66212
66755
  * (about 1.57) radians.
66213
66756
  *
66214
- * @method radians
66215
- * @param {Number} degrees degree value to convert to radians.
66216
- * @return {Number} converted angle.
66217
- *
66218
- * @example
66757
+ * ```js example
66219
66758
  * function setup() {
66220
66759
  * createCanvas(100, 100);
66221
66760
  *
@@ -66230,6 +66769,56 @@ function trigonometry(p5, fn){
66230
66769
  *
66231
66770
  * describe('The text "45˚ = 0.785 rad".');
66232
66771
  * }
66772
+ * ```
66773
+ *
66774
+ * `radians()` can also be used in shaders with p5.strands. The following example
66775
+ * uses `radians()` to convert degrees to radians inside a shader.
66776
+ *
66777
+ * ```js example
66778
+ * let myShader;
66779
+ *
66780
+ * function setup() {
66781
+ * createCanvas(100, 100, WEBGL);
66782
+ * myShader = buildColorShader(shaderCallback);
66783
+ * describe('A sphere that fades between red and white.');
66784
+ * }
66785
+ *
66786
+ * function shaderCallback() {
66787
+ * // shaderCallback runs on the GPU. millis() gives ms since start.
66788
+ * // Multiply by 0.05 and mod 360 to cycle through 0-360 degrees over time.
66789
+ * let deg = (millis() * 0.05) % 360;
66790
+ *
66791
+ * // radians() converts degrees to radians so sin() can use them.
66792
+ * let rad = radians(deg);
66793
+ *
66794
+ * // sin(rad) oscillates between -1 and 1.
66795
+ * // 0.5 + 0.5 * sin(rad) remaps this to the 0 to 1 range.
66796
+ * let value = 0.5 + 0.5 * sin(rad);
66797
+ *
66798
+ * // Each color is [R, G, B, A] with values from 0 to 1.
66799
+ * let red = [1, 0, 0, 1];
66800
+ * let white = [1, 1, 1, 1];
66801
+ *
66802
+ * finalColor.begin();
66803
+ *
66804
+ * // mix() blends between red (when value = 0) and white (when value = 1).
66805
+ * // radians() converts the degree input so sin() can produce smooth oscillation.
66806
+ * finalColor.set(mix(red, white, value));
66807
+ *
66808
+ * finalColor.end();
66809
+ * }
66810
+ *
66811
+ * function draw() {
66812
+ * background(220);
66813
+ * shader(myShader);
66814
+ * noStroke();
66815
+ * sphere(30);
66816
+ * }
66817
+ * ```
66818
+ *
66819
+ * @method radians
66820
+ * @param {Number} degrees degree value to convert to radians.
66821
+ * @return {Number} converted angle.
66233
66822
  */
66234
66823
  fn.radians = angle => angle * DEG_TO_RAD;
66235
66824
 
@@ -67215,7 +67804,7 @@ class Texture {
67215
67804
  _detectSourceType() {
67216
67805
  const obj = this.src;
67217
67806
  this.isFramebufferTexture = obj instanceof FramebufferTexture;
67218
- this.isSrcP5Image = obj instanceof Image;
67807
+ this.isSrcP5Image = obj instanceof Image$1;
67219
67808
  this.isSrcP5Graphics = obj instanceof Graphics;
67220
67809
  this.isSrcP5Renderer = obj instanceof Renderer;
67221
67810
  this.isImageData = typeof ImageData !== 'undefined' && obj instanceof ImageData;
@@ -70158,6 +70747,13 @@ function userError(errorType, errorMessage) {
70158
70747
  throw new Error(prefixedMessage);
70159
70748
  }
70160
70749
 
70750
+ function dimensionMismatchError(declaredDim,actualDim,varName){
70751
+ userError(
70752
+ 'dimension mismatch',
70753
+ `Cannot assign a value of dimension ${actualDim} to \`${varName}\`, which expects dimension ${declaredDim}.`
70754
+ );
70755
+ }
70756
+
70161
70757
  /////////////////////////////////
70162
70758
  // Public functions for strands runtime
70163
70759
  /////////////////////////////////
@@ -70446,6 +71042,16 @@ class StrandsNode {
70446
71042
 
70447
71043
  // For varying variables, we need both assignment generation AND a way to reference by identifier
70448
71044
  if (this._originalIdentifier) {
71045
+ const valueDim = value?.isStrandsNode
71046
+ ? value.dimension
71047
+ : (Array.isArray(value) ? value.length : 1);
71048
+ if (valueDim !== this._originalDimension && valueDim !== 1){
71049
+ dimensionMismatchError(
71050
+ this._originalDimension,
71051
+ valueDim,
71052
+ this._originalIdentifier
71053
+ );
71054
+ }
70449
71055
  // Create a variable node for the target (the varying variable)
70450
71056
  const { id: targetVarID } = variableNode(
70451
71057
  this.strandsContext,
@@ -70498,6 +71104,16 @@ class StrandsNode {
70498
71104
 
70499
71105
  // For varying variables, create swizzle assignment
70500
71106
  if (this._originalIdentifier) {
71107
+ const valueDim = value?.isStrandsNode
71108
+ ? value.dimension
71109
+ : (Array.isArray(value) ? value.length : 1);
71110
+ if (valueDim !== swizzlePattern.length && valueDim !== 1){
71111
+ dimensionMismatchError(
71112
+ swizzlePattern.length,
71113
+ valueDim,
71114
+ `${this._originalIdentifier}.${swizzlePattern}`
71115
+ );
71116
+ }
70501
71117
  // Create a variable node for the target with swizzle
70502
71118
  const { id: targetVarID } = variableNode(
70503
71119
  this.strandsContext,
@@ -71046,38 +71662,24 @@ function castToFloat(strandsContext, dep) {
71046
71662
  return createStrandsNode(id, dimension, strandsContext);
71047
71663
  }
71048
71664
 
71049
- function structConstructorNode(strandsContext, structTypeInfo, rawUserArgs) {
71665
+ function structConstructorNode(strandsContext, structTypeInfo, dependsOn) {
71050
71666
  const { cfg, dag } = strandsContext;
71051
71667
  const { properties } = structTypeInfo;
71052
71668
 
71053
- if (!(rawUserArgs.length === properties.length)) {
71669
+ if (dependsOn.length !== properties.length) {
71054
71670
  userError('type error',
71055
- `You've tried to construct a ${structTypeInfo.typeName} struct with ${rawUserArgs.length} properties, but it expects ${properties.length} properties.\n` +
71671
+ `You've tried to construct a ${structTypeInfo.typeName} struct with ${dependsOn.length} properties, but it expects ${properties.length} properties.\n` +
71056
71672
  `The properties it expects are:\n` +
71057
- `${properties.map(prop => prop.name + ' ' + prop.DataType.baseType + prop.DataType.dimension)}`
71673
+ `${properties.map(prop => `${prop.name}: ${prop.dataType.baseType}${prop.dataType.dimension}`).join(', ')}`
71058
71674
  );
71059
71675
  }
71060
71676
 
71061
- const dependsOn = [];
71062
- for (let i = 0; i < properties.length; i++) {
71063
- const expectedProperty = properties[i];
71064
- const { originalNodeID, mappedDependencies } = mapPrimitiveDepsToIDs(strandsContext, expectedProperty.dataType, rawUserArgs[i]);
71065
- if (originalNodeID) {
71066
- dependsOn.push(originalNodeID);
71067
- }
71068
- else {
71069
- dependsOn.push(
71070
- constructTypeFromIDs(strandsContext, expectedProperty.dataType, mappedDependencies)
71071
- );
71072
- }
71073
- }
71074
-
71075
71677
  const nodeData = createNodeData({
71076
71678
  nodeType: NodeType.OPERATION,
71077
71679
  opCode: OpCode.Nary.CONSTRUCTOR,
71078
71680
  dimension: properties.length,
71079
- baseType: structTypeInfo.typeName ,
71080
- dependsOn
71681
+ baseType: structTypeInfo.typeName,
71682
+ dependsOn,
71081
71683
  });
71082
71684
  const id = getOrCreateNode(dag, nodeData);
71083
71685
  recordInBasicBlock(cfg, cfg.currentBlock, id);
@@ -71274,7 +71876,11 @@ function swizzleTrap(id, dimension, strandsContext, onRebind) {
71274
71876
  scalars.push(createStrandsNode(id, dimension, strandsContext));
71275
71877
  }
71276
71878
  } else {
71277
- userError('type error', `Swizzle assignment: RHS vector does not match LHS vector (need ${chars.length}, got ${value.dimension}).`);
71879
+ dimensionMismatchError(
71880
+ chars.length,
71881
+ value.dimension,
71882
+ `${target._originalIdentifier || 'value'}.${property}`
71883
+ );
71278
71884
  }
71279
71885
  } else if (Array.isArray(value)) {
71280
71886
  const flat = value.flat(Infinity);
@@ -71748,7 +72354,7 @@ const glslBackend = {
71748
72354
  }
71749
72355
  const dag = strandsContext.dag;
71750
72356
  const rootNode = getNodeDataFromID(dag, rootNodeID);
71751
- if (isStructType(returnType)) {
72357
+ if (isStructType(returnType) && rootNode.identifier) {
71752
72358
  const structTypeInfo = returnType;
71753
72359
  for (let i = 0; i < structTypeInfo.properties.length; i++) {
71754
72360
  const prop = structTypeInfo.properties[i];
@@ -72039,7 +72645,7 @@ class FilterRenderer2D {
72039
72645
  _arraysEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b),
72040
72646
  _getEmptyTexture: () => {
72041
72647
  if (!this._emptyTexture) {
72042
- const im = new Image(1, 1);
72648
+ const im = new Image$1(1, 1);
72043
72649
  im.set(0, 0, 255);
72044
72650
  this._emptyTexture = new Texture(this._renderer, im);
72045
72651
  }
@@ -72471,8 +73077,6 @@ class FilterRenderer2D {
72471
73077
  }
72472
73078
  }
72473
73079
 
72474
- const styleEmpty = 'rgba(0,0,0,0)';
72475
-
72476
73080
  class Renderer2D extends Renderer {
72477
73081
  constructor(pInst, w, h, isMainCanvas, elt, attributes = {}) {
72478
73082
  super(pInst, w, h, isMainCanvas);
@@ -72631,7 +73235,7 @@ class Renderer2D extends Renderer {
72631
73235
  }
72632
73236
  this.push();
72633
73237
  this.resetMatrix();
72634
- if (args[0] instanceof Image) {
73238
+ if (args[0] instanceof Image$1) {
72635
73239
  const img = args[0];
72636
73240
  if (args[1] >= 0) {
72637
73241
  // set transparency of background
@@ -72904,10 +73508,12 @@ class Renderer2D extends Renderer {
72904
73508
  ctx.save();
72905
73509
  ctx.clearRect(0, 0, img.canvas.width, img.canvas.height);
72906
73510
 
73511
+ const tint = this.states.tint._getRGBA([255, 255, 255, 255]);
73512
+
72907
73513
  if (
72908
- this.states.tint[0] < 255 ||
72909
- this.states.tint[1] < 255 ||
72910
- this.states.tint[2] < 255
73514
+ tint[0] < 255 ||
73515
+ tint[1] < 255 ||
73516
+ tint[2] < 255
72911
73517
  ) {
72912
73518
  // Color tint: we need to use the multiply blend mode to change the colors.
72913
73519
  // However, the canvas implementation of this destroys the alpha channel of
@@ -72930,16 +73536,16 @@ class Renderer2D extends Renderer {
72930
73536
 
72931
73537
  // Apply color tint
72932
73538
  ctx.globalCompositeOperation = 'multiply';
72933
- ctx.fillStyle = `rgb(${this.states.tint.slice(0, 3).join(', ')})`;
73539
+ ctx.fillStyle = `rgb(${tint.slice(0, 3).join(', ')})`;
72934
73540
  ctx.fillRect(0, 0, img.canvas.width, img.canvas.height);
72935
73541
 
72936
73542
  // Replace the alpha channel with the original alpha * the alpha tint
72937
73543
  ctx.globalCompositeOperation = 'destination-in';
72938
- ctx.globalAlpha = this.states.tint[3] / 255;
73544
+ ctx.globalAlpha = tint[3] / 255;
72939
73545
  ctx.drawImage(img.canvas, 0, 0);
72940
73546
  } else {
72941
73547
  // If we only need to change the alpha, we can skip all the extra work!
72942
- ctx.globalAlpha = this.states.tint[3] / 255;
73548
+ ctx.globalAlpha = tint[3] / 255;
72943
73549
  ctx.drawImage(img.canvas, 0, 0);
72944
73550
  }
72945
73551
 
@@ -73025,7 +73631,7 @@ class Renderer2D extends Renderer {
73025
73631
  // round down to get integer numbers
73026
73632
  x = Math.floor(x);
73027
73633
  y = Math.floor(y);
73028
- if (imgOrCol instanceof Graphics || imgOrCol instanceof Image) {
73634
+ if (imgOrCol instanceof Graphics || imgOrCol instanceof Image$1) {
73029
73635
  this.drawingContext.save();
73030
73636
  this.drawingContext.setTransform(1, 0, 0, 1, 0, 0);
73031
73637
  this.drawingContext.scale(
@@ -73165,96 +73771,32 @@ class Renderer2D extends Renderer {
73165
73771
  }
73166
73772
 
73167
73773
  line(x1, y1, x2, y2) {
73168
- const ctx = this.drawingContext;
73169
- if (!this.states.strokeColor) {
73170
- return this;
73171
- } else if (this._getStroke() === styleEmpty) {
73172
- return this;
73173
- }
73174
- if (this._clipping) {
73175
- const tempPath = new Path2D();
73176
- tempPath.moveTo(x1, y1);
73177
- tempPath.lineTo(x2, y2);
73178
- const currentTransform = this.drawingContext.getTransform();
73179
- const clipBaseTransform = this._clipBaseTransform.inverse();
73180
- const relativeTransform = clipBaseTransform.multiply(currentTransform);
73181
- this.clipPath.addPath(tempPath, relativeTransform);
73182
- return this;
73183
- }
73184
- ctx.beginPath();
73185
- ctx.moveTo(x1, y1);
73186
- ctx.lineTo(x2, y2);
73187
- ctx.stroke();
73774
+ const shape = new p5$2.Shape({ position: new p5$2.Vector(0, 0) });
73775
+ shape.beginShape();
73776
+ shape.line(x1, y1, x2, y2);
73777
+ shape.endShape();
73778
+ this.drawShape(shape);
73188
73779
 
73189
73780
  return this;
73190
73781
  }
73191
73782
 
73192
73783
  point(x, y) {
73193
- const ctx = this.drawingContext;
73194
- if (!this.states.strokeColor) {
73195
- return this;
73196
- } else if (this._getStroke() === styleEmpty) {
73197
- return this;
73198
- }
73199
- const s = this._getStroke();
73200
- const f = this._getFill();
73201
- if (this._clipping) {
73202
- const tempPath = new Path2D();
73203
- const drawingContextWidth = this.drawingContext.lineWidth;
73204
- tempPath.arc(x, y, drawingContextWidth / 2, 0, TWO_PI);
73205
- const currentTransform = this.drawingContext.getTransform();
73206
- const clipBaseTransform = this._clipBaseTransform.inverse();
73207
- const relativeTransform = clipBaseTransform.multiply(currentTransform);
73208
- this.clipPath.addPath(tempPath, relativeTransform);
73209
- return this;
73210
- }
73211
- this._setFill(s);
73212
- ctx.beginPath();
73213
- ctx.arc(x, y, ctx.lineWidth / 2, 0, TWO_PI, false);
73214
- ctx.fill();
73215
- this._setFill(f);
73784
+ const shape = new p5$2.Shape({ position: new p5$2.Vector(0, 0) });
73785
+ shape.beginShape();
73786
+ shape.point(x, y);
73787
+ shape.endShape();
73788
+ this.drawShape(shape);
73216
73789
 
73217
73790
  return this;
73218
73791
  }
73219
73792
 
73220
73793
  quad(x1, y1, x2, y2, x3, y3, x4, y4) {
73221
- const ctx = this.drawingContext;
73222
- const doFill = !!this.states.fillColor,
73223
- doStroke = this.states.strokeColor;
73224
- if (doFill && !doStroke) {
73225
- if (this._getFill() === styleEmpty) {
73226
- return this;
73227
- }
73228
- } else if (!doFill && doStroke) {
73229
- if (this._getStroke() === styleEmpty) {
73230
- return this;
73231
- }
73232
- }
73233
- if (this._clipping) {
73234
- const tempPath = new Path2D();
73235
- tempPath.moveTo(x1, y1);
73236
- tempPath.lineTo(x2, y2);
73237
- tempPath.lineTo(x3, y3);
73238
- tempPath.lineTo(x4, y4);
73239
- tempPath.closePath();
73240
- const currentTransform = this.drawingContext.getTransform();
73241
- const clipBaseTransform = this._clipBaseTransform.inverse();
73242
- const relativeTransform = clipBaseTransform.multiply(currentTransform);
73243
- this.clipPath.addPath(tempPath, relativeTransform);
73244
- return this;
73245
- }
73246
- ctx.beginPath();
73247
- ctx.moveTo(x1, y1);
73248
- ctx.lineTo(x2, y2);
73249
- ctx.lineTo(x3, y3);
73250
- ctx.lineTo(x4, y4);
73251
- ctx.closePath();
73252
- if (doFill) {
73253
- ctx.fill();
73254
- }
73255
- if (doStroke) {
73256
- ctx.stroke();
73257
- }
73794
+ const shape = new p5$2.Shape({ position: new p5$2.Vector(0, 0) });
73795
+ shape.beginShape();
73796
+ shape.quad(x1, y1, x2, y2, x3, y3, x4, y4);
73797
+ shape.endShape();
73798
+ this.drawShape(shape);
73799
+
73258
73800
  return this;
73259
73801
  }
73260
73802
 
@@ -73267,134 +73809,29 @@ class Renderer2D extends Renderer {
73267
73809
  let tr = args[5];
73268
73810
  let br = args[6];
73269
73811
  let bl = args[7];
73270
- const ctx = this.drawingContext;
73271
- const doFill = !!this.states.fillColor,
73272
- doStroke = this.states.strokeColor;
73273
- if (doFill && !doStroke) {
73274
- if (this._getFill() === styleEmpty) {
73275
- return this;
73276
- }
73277
- } else if (!doFill && doStroke) {
73278
- if (this._getStroke() === styleEmpty) {
73279
- return this;
73280
- }
73281
- }
73282
- if (this._clipping) {
73283
- const tempPath = new Path2D();
73284
- if (typeof tl === 'undefined') {
73285
- tempPath.rect(x, y, w, h);
73286
- } else {
73287
- tempPath.roundRect(x, y, w, h, [tl, tr, br, bl]);
73288
- }
73289
- const currentTransform = this.drawingContext.getTransform();
73290
- const clipBaseTransform = this._clipBaseTransform.inverse();
73291
- const relativeTransform = clipBaseTransform.multiply(currentTransform);
73292
- this.clipPath.addPath(tempPath, relativeTransform);
73293
- return this;
73294
- }
73295
- ctx.beginPath();
73296
- if (typeof tl === 'undefined') {
73297
- // No rounded corners
73298
- ctx.rect(x, y, w, h);
73299
- } else {
73300
- // At least one rounded corner
73301
- // Set defaults when not specified
73302
- if (typeof tr === 'undefined') {
73303
- tr = tl;
73304
- }
73305
- if (typeof br === 'undefined') {
73306
- br = tr;
73307
- }
73308
- if (typeof bl === 'undefined') {
73309
- bl = br;
73310
- }
73311
73812
 
73312
- // corner rounding must always be positive
73313
- const absW = Math.abs(w);
73314
- const absH = Math.abs(h);
73315
- const hw = absW / 2;
73316
- const hh = absH / 2;
73317
-
73318
- // Clip radii
73319
- if (absW < 2 * tl) {
73320
- tl = hw;
73321
- }
73322
- if (absH < 2 * tl) {
73323
- tl = hh;
73324
- }
73325
- if (absW < 2 * tr) {
73326
- tr = hw;
73327
- }
73328
- if (absH < 2 * tr) {
73329
- tr = hh;
73330
- }
73331
- if (absW < 2 * br) {
73332
- br = hw;
73333
- }
73334
- if (absH < 2 * br) {
73335
- br = hh;
73336
- }
73337
- if (absW < 2 * bl) {
73338
- bl = hw;
73339
- }
73340
- if (absH < 2 * bl) {
73341
- bl = hh;
73342
- }
73813
+ const shape = new p5$2.Shape({ position: new p5$2.Vector(0, 0) });
73814
+ shape.beginShape();
73815
+ shape.rectPrimitive(x, y, w, h, tl, tr, br, bl);
73816
+ shape.endShape();
73817
+ this.drawShape(shape);
73343
73818
 
73344
- ctx.roundRect(x, y, w, h, [tl, tr, br, bl]);
73345
- }
73346
- if (doFill) {
73347
- ctx.fill();
73348
- }
73349
- if (doStroke) {
73350
- ctx.stroke();
73351
- }
73352
73819
  return this;
73353
73820
  }
73354
73821
 
73355
-
73356
73822
  triangle(args) {
73357
- const ctx = this.drawingContext;
73358
- const doFill = !!this.states.fillColor,
73359
- doStroke = this.states.strokeColor;
73360
73823
  const x1 = args[0],
73361
73824
  y1 = args[1];
73362
73825
  const x2 = args[2],
73363
73826
  y2 = args[3];
73364
73827
  const x3 = args[4],
73365
73828
  y3 = args[5];
73366
- if (doFill && !doStroke) {
73367
- if (this._getFill() === styleEmpty) {
73368
- return this;
73369
- }
73370
- } else if (!doFill && doStroke) {
73371
- if (this._getStroke() === styleEmpty) {
73372
- return this;
73373
- }
73374
- }
73375
- if (this._clipping) {
73376
- const tempPath = new Path2D();
73377
- tempPath.moveTo(x1, y1);
73378
- tempPath.lineTo(x2, y2);
73379
- tempPath.lineTo(x3, y3);
73380
- tempPath.closePath();
73381
- const currentTransform = this.drawingContext.getTransform();
73382
- const clipBaseTransform = this._clipBaseTransform.inverse();
73383
- const relativeTransform = clipBaseTransform.multiply(currentTransform);
73384
- this.clipPath.addPath(tempPath, relativeTransform);
73385
- return this;
73386
- }
73387
- ctx.beginPath();
73388
- ctx.moveTo(x1, y1);
73389
- ctx.lineTo(x2, y2);
73390
- ctx.lineTo(x3, y3);
73391
- ctx.closePath();
73392
- if (doFill) {
73393
- ctx.fill();
73394
- }
73395
- if (doStroke) {
73396
- ctx.stroke();
73397
- }
73829
+
73830
+ const shape = new p5$2.Shape({ position: new p5$2.Vector(0, 0) });
73831
+ shape.beginShape();
73832
+ shape.triangle(x1, y1, x2, y2, x3, y3);
73833
+ shape.endShape();
73834
+ this.drawShape(shape);
73398
73835
 
73399
73836
  return this;
73400
73837
  }
@@ -102727,6 +103164,7 @@ function pointer(p5, fn, lifecycles){
102727
103164
  const events = [
102728
103165
  'pointerdown',
102729
103166
  'pointerup',
103167
+ 'pointercancel',
102730
103168
  'pointermove',
102731
103169
  'dragend',
102732
103170
  'dragover',
@@ -103786,6 +104224,10 @@ function pointer(p5, fn, lifecycles){
103786
104224
  this._activePointers.set(e.pointerId, e);
103787
104225
  this._setMouseButton(e);
103788
104226
 
104227
+ if (this.mouseIsPressed && e.buttons === 0) {
104228
+ this._onpointerup(e);
104229
+ }
104230
+
103789
104231
  if (
103790
104232
  !this.mouseIsPressed &&
103791
104233
  typeof this._customActions.mouseMoved === 'function'
@@ -104112,6 +104554,16 @@ function pointer(p5, fn, lifecycles){
104112
104554
  fn._ondragend = fn._onpointerup;
104113
104555
  fn._ondragover = fn._onpointermove;
104114
104556
 
104557
+ fn._onpointercancel = function(e) {
104558
+ this._activePointers.delete(e.pointerId);
104559
+ this._setMouseButton(e);
104560
+ this._updatePointerCoords(e);
104561
+
104562
+ if (this._activePointers.size === 0) {
104563
+ this.mouseIsPressed = false;
104564
+ }
104565
+ };
104566
+
104115
104567
  /**
104116
104568
  * A function that's called once after a mouse button is pressed and released.
104117
104569
  *
@@ -106344,11 +106796,7 @@ function calculation(p5, fn){
106344
106796
  * -5 and 5 are both five units away from zero, so calling `abs(-5)` and
106345
106797
  * `abs(5)` both return 5. The absolute value of a number is always positive.
106346
106798
  *
106347
- * @method abs
106348
- * @param {Number} n number to compute.
106349
- * @return {Number} absolute value of given number.
106350
- *
106351
- * @example
106799
+ * ```js example
106352
106800
  * function setup() {
106353
106801
  * createCanvas(100, 100);
106354
106802
  *
@@ -106368,6 +106816,55 @@ function calculation(p5, fn){
106368
106816
  * // from the middle.
106369
106817
  * rect(0, 100 - h, 100, h);
106370
106818
  * }
106819
+ * ```
106820
+ *
106821
+ * `abs()` can also be used in shaders with p5.strands. The following example
106822
+ * uses `abs()` to create a mirror effect on the color of a shape.
106823
+ *
106824
+ * ```js example
106825
+ * let myShader;
106826
+ *
106827
+ * function setup() {
106828
+ * createCanvas(100, 100, WEBGL);
106829
+ * myShader = buildColorShader(shaderCallback);
106830
+ * describe('A sphere with colors that fold back like a mirror.');
106831
+ * }
106832
+ *
106833
+ * function shaderCallback() {
106834
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
106835
+ * let t = millis() * 0.001;
106836
+ *
106837
+ * // sin(t) goes between -1 and 1 over time.
106838
+ * let sinVal = sin(t);
106839
+ *
106840
+ * // abs() folds the negative values to positive.
106841
+ * // Now value goes between 0 and 1, creating a mirror effect.
106842
+ * let value = abs(sinVal);
106843
+ *
106844
+ * // Each color is [R, G, B, A] with values from 0 to 1.
106845
+ * let navy = [0.2, 0.2, 0.8, 1];
106846
+ * let coral = [0.8, 0.2, 0.2, 1];
106847
+ *
106848
+ * finalColor.begin();
106849
+ *
106850
+ * // mix() blends between navy (when value = 0) and coral (when value = 1).
106851
+ * finalColor.set(mix(navy, coral, value));
106852
+ *
106853
+ * finalColor.end();
106854
+ * }
106855
+ *
106856
+ * function draw() {
106857
+ * background(220);
106858
+ * shader(myShader);
106859
+ * noStroke();
106860
+ * sphere(30);
106861
+ * }
106862
+ * ```
106863
+ *
106864
+ * @method abs
106865
+ * @param {Number} n number to compute.
106866
+ * @return {Number} absolute value of given number.
106867
+ *
106371
106868
  */
106372
106869
  fn.abs = Math.abs;
106373
106870
 
@@ -106378,11 +106875,7 @@ function calculation(p5, fn){
106378
106875
  * For example, calling `ceil(9.03)` and `ceil(9.97)` both return the value
106379
106876
  * 10.
106380
106877
  *
106381
- * @method ceil
106382
- * @param {Number} n number to round up.
106383
- * @return {Integer} rounded up number.
106384
- *
106385
- * @example
106878
+ * ```js example
106386
106879
  * function setup() {
106387
106880
  * createCanvas(100, 100);
106388
106881
  *
@@ -106407,6 +106900,57 @@ function calculation(p5, fn){
106407
106900
  *
106408
106901
  * describe('Two rectangles. The one on the left is dark red and the one on the right is bright red.');
106409
106902
  * }
106903
+ * ```
106904
+ *
106905
+ * `ceil()` can also be used in shaders with p5.strands. The following example
106906
+ * uses `ceil()` to create a stepped color effect on a shape.
106907
+ *
106908
+ * ```js example
106909
+ * let myShader;
106910
+ *
106911
+ * function setup() {
106912
+ * createCanvas(100, 100, WEBGL);
106913
+ * myShader = buildColorShader(shaderCallback);
106914
+ * describe('A sphere with stepped color bands.');
106915
+ * }
106916
+ *
106917
+ * function shaderCallback() {
106918
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
106919
+ * let t = millis() * 0.001;
106920
+ *
106921
+ * // sin(t) goes between -1 and 1.
106922
+ * // 0.5 + 0.5 * sin(t) remaps this to the 0 to 1 range.
106923
+ * let sinVal = 0.5 + 0.5 * sin(t);
106924
+ *
106925
+ * // Multiply by 4 then ceil to get the next whole number up.
106926
+ * // Divide by 4 to bring the result back to the 0 to 1 range.
106927
+ * // This creates 4 distinct stepped color levels: 0.25, 0.5, 0.75, 1.
106928
+ * let value = ceil(sinVal * 4) / 4;
106929
+ *
106930
+ * // Each color is [R, G, B, A] with values from 0 to 1.
106931
+ * let cyan = [0, 0.5, 1, 1];
106932
+ * let orange = [1, 0.5, 0, 1];
106933
+ *
106934
+ * finalColor.begin();
106935
+ *
106936
+ * // mix() blends between cyan (when value = 0) and orange (when value = 1).
106937
+ * // ceil() creates sharp steps between color levels.
106938
+ * finalColor.set(mix(cyan, orange, value));
106939
+ *
106940
+ * finalColor.end();
106941
+ * }
106942
+ *
106943
+ * function draw() {
106944
+ * background(220);
106945
+ * shader(myShader);
106946
+ * noStroke();
106947
+ * sphere(30);
106948
+ * }
106949
+ * ```
106950
+ *
106951
+ * @method ceil
106952
+ * @param {Number} n number to round up.
106953
+ * @return {Integer} rounded up number.
106410
106954
  */
106411
106955
  fn.ceil = Math.ceil;
106412
106956
 
@@ -106544,11 +107088,7 @@ function calculation(p5, fn){
106544
107088
  * Calculates the value of Euler's number e (2.71828...) raised to the power
106545
107089
  * of a number.
106546
107090
  *
106547
- * @method exp
106548
- * @param {Number} n exponent to raise.
106549
- * @return {Number} e^n
106550
- *
106551
- * @example
107091
+ * ```js example
106552
107092
  * function setup() {
106553
107093
  * createCanvas(100, 100);
106554
107094
  *
@@ -106572,8 +107112,9 @@ function calculation(p5, fn){
106572
107112
  *
106573
107113
  * describe('A series of circles that grow exponentially from top left to bottom right.');
106574
107114
  * }
107115
+ * ```
106575
107116
  *
106576
- * @example
107117
+ * ```js example
106577
107118
  * function setup() {
106578
107119
  * createCanvas(100, 100);
106579
107120
  *
@@ -106594,6 +107135,53 @@ function calculation(p5, fn){
106594
107135
  * // Draw a point.
106595
107136
  * point(x, y);
106596
107137
  * }
107138
+ * ```
107139
+ *
107140
+ * `exp()` can also be used in shaders with p5.strands. The following example
107141
+ * uses `exp()` to create an accelerating color transition on a shape.
107142
+ *
107143
+ * ```js example
107144
+ * let myShader;
107145
+ *
107146
+ * function setup() {
107147
+ * createCanvas(100, 100, WEBGL);
107148
+ * myShader = buildColorShader(shaderCallback);
107149
+ * describe('A sphere that brightens with accelerating speed.');
107150
+ * }
107151
+ *
107152
+ * function shaderCallback() {
107153
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.0005 to slow it.
107154
+ * let t = millis() * 0.0005;
107155
+ *
107156
+ * // exp(t) grows slowly at first, then accelerates (exponential growth).
107157
+ * // Multiply by 0.01 to keep it from growing too fast.
107158
+ * // min(..., 1) caps the value at 1 so it doesn't go past white.
107159
+ * let value = min(exp(t) * 0.01, 1);
107160
+ *
107161
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107162
+ * let darkBlue = [0.1, 0.1, 0.3, 1];
107163
+ * let lightYellow = [1, 1, 0.5, 1];
107164
+ *
107165
+ * finalColor.begin();
107166
+ *
107167
+ * // mix() blends between darkBlue (when value = 0) and lightYellow (when value = 1).
107168
+ * // Because exp() accelerates, the color transition gets faster over time.
107169
+ * finalColor.set(mix(darkBlue, lightYellow, value));
107170
+ *
107171
+ * finalColor.end();
107172
+ * }
107173
+ *
107174
+ * function draw() {
107175
+ * background(220);
107176
+ * shader(myShader);
107177
+ * noStroke();
107178
+ * sphere(30);
107179
+ * }
107180
+ * ```
107181
+ *
107182
+ * @method exp
107183
+ * @param {Number} n exponent to raise.
107184
+ * @return {Number} e^n
106597
107185
  */
106598
107186
  fn.exp = Math.exp;
106599
107187
 
@@ -106601,11 +107189,7 @@ function calculation(p5, fn){
106601
107189
  * Calculates the closest integer value that is less than or equal to the
106602
107190
  * value of a number.
106603
107191
  *
106604
- * @method floor
106605
- * @param {Number} n number to round down.
106606
- * @return {Integer} rounded down number.
106607
- *
106608
- * @example
107192
+ * ```js example
106609
107193
  * function setup() {
106610
107194
  * createCanvas(100, 100);
106611
107195
  *
@@ -106628,6 +107212,57 @@ function calculation(p5, fn){
106628
107212
  *
106629
107213
  * describe('Two rectangles. The one on the left is bright red and the one on the right is black.');
106630
107214
  * }
107215
+ * ```
107216
+ *
107217
+ * `floor()` can also be used in shaders with p5.strands. The following example
107218
+ * uses `floor()` to create banding effects on a shape.
107219
+ *
107220
+ * ```js example
107221
+ * let myShader;
107222
+ *
107223
+ * function setup() {
107224
+ * createCanvas(100, 100, WEBGL);
107225
+ * myShader = buildColorShader(shaderCallback);
107226
+ * describe('A sphere with posterized color bands.');
107227
+ * }
107228
+ *
107229
+ * function shaderCallback() {
107230
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
107231
+ * let t = millis() * 0.001;
107232
+ *
107233
+ * // sin(t) goes between -1 and 1.
107234
+ * // 0.5 + 0.5 * sin(t) remaps this to the 0 to 1 range.
107235
+ * let sinVal = 0.5 + 0.5 * sin(t);
107236
+ *
107237
+ * // Multiply by 4 then floor to get the next whole number down.
107238
+ * // Divide by 4 to bring the result back to the 0 to 1 range.
107239
+ * // This creates 5 distinct stepped color levels: 0, 0.25, 0.5, 0.75, 1.
107240
+ * let value = floor(sinVal * 4) / 4;
107241
+ *
107242
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107243
+ * let darkPurple = [0.2, 0, 0.8, 1];
107244
+ * let brightTeal = [0.2, 1, 0.8, 1];
107245
+ *
107246
+ * finalColor.begin();
107247
+ *
107248
+ * // mix() blends between darkPurple (when value = 0) and brightTeal (when value = 1).
107249
+ * // The floor() creates visible banding/posterization in the color.
107250
+ * finalColor.set(mix(darkPurple, brightTeal, value));
107251
+ *
107252
+ * finalColor.end();
107253
+ * }
107254
+ *
107255
+ * function draw() {
107256
+ * background(220);
107257
+ * shader(myShader);
107258
+ * noStroke();
107259
+ * sphere(30);
107260
+ * }
107261
+ * ```
107262
+ *
107263
+ * @method floor
107264
+ * @param {Number} n number to round down.
107265
+ * @return {Integer} rounded down number.
106631
107266
  */
106632
107267
  fn.floor = Math.floor;
106633
107268
 
@@ -106644,13 +107279,7 @@ function calculation(p5, fn){
106644
107279
  * number outside of the original interval. For example, calling
106645
107280
  * `lerp(0, 10, 1.5)` will return 15.
106646
107281
  *
106647
- * @method lerp
106648
- * @param {Number} start first value.
106649
- * @param {Number} stop second value.
106650
- * @param {Number} amt number.
106651
- * @return {Number} lerped value.
106652
- *
106653
- * @example
107282
+ * ```js example
106654
107283
  * function setup() {
106655
107284
  * createCanvas(100, 100);
106656
107285
  *
@@ -106678,8 +107307,9 @@ function calculation(p5, fn){
106678
107307
  *
106679
107308
  * describe('Five points in a horizontal line. The outer points are black and the inner points are gray.');
106680
107309
  * }
107310
+ * ```
106681
107311
  *
106682
- * @example
107312
+ * ```js example
106683
107313
  * let x = 50;
106684
107314
  * let y = 50;
106685
107315
  * let targetX = 50;
@@ -106709,6 +107339,54 @@ function calculation(p5, fn){
106709
107339
  * x = mouseX;
106710
107340
  * y = mouseY;
106711
107341
  * }
107342
+ * ```
107343
+ *
107344
+ * `lerp()` can also be used in shaders with p5.strands, where it maps to the
107345
+ * <a href="#/p5/mix">mix()</a> function in GLSL. The following example
107346
+ * uses `lerp()` to blend colors on a shape over time.
107347
+ *
107348
+ * ```js example
107349
+ * let myShader;
107350
+ *
107351
+ * function setup() {
107352
+ * createCanvas(100, 100, WEBGL);
107353
+ * myShader = buildColorShader(shaderCallback);
107354
+ * describe('A sphere that blends between teal and coral.');
107355
+ * }
107356
+ *
107357
+ * function shaderCallback() {
107358
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
107359
+ * let t = millis() * 0.001;
107360
+ *
107361
+ * // sin(t) goes between -1 and 1 over time.
107362
+ * // 0.5 + 0.5 * sin(t) remaps this to the 0 to 1 range.
107363
+ * let value = 0.5 + 0.5 * sin(t);
107364
+ *
107365
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107366
+ * let teal = [0, 0.8, 0.8, 1];
107367
+ * let coral = [1, 0.5, 0.3, 1];
107368
+ *
107369
+ * finalColor.begin();
107370
+ *
107371
+ * // lerp() blends teal (when value = 0) and coral (when value = 1).
107372
+ * finalColor.set(lerp(teal, coral, value));
107373
+ *
107374
+ * finalColor.end();
107375
+ * }
107376
+ *
107377
+ * function draw() {
107378
+ * background(220);
107379
+ * shader(myShader);
107380
+ * noStroke();
107381
+ * sphere(30);
107382
+ * }
107383
+ * ```
107384
+ *
107385
+ * @method lerp
107386
+ * @param {Number} start first value.
107387
+ * @param {Number} stop second value.
107388
+ * @param {Number} amt number.
107389
+ * @return {Number} lerped value.
106712
107390
  */
106713
107391
  fn.lerp = function(start, stop, amt) {
106714
107392
  // p5._validateParameters('lerp', arguments);
@@ -106721,11 +107399,7 @@ function calculation(p5, fn){
106721
107399
  * `log()` expects the `n` parameter to be a value greater than 0 because
106722
107400
  * the natural logarithm is defined that way.
106723
107401
  *
106724
- * @method log
106725
- * @param {Number} n number greater than 0.
106726
- * @return {Number} natural logarithm of n.
106727
- *
106728
- * @example
107402
+ * ```js example
106729
107403
  * function setup() {
106730
107404
  * createCanvas(100, 100);
106731
107405
  *
@@ -106741,8 +107415,9 @@ function calculation(p5, fn){
106741
107415
  *
106742
107416
  * describe('Two white circles. The circle at the top-left is small. The circle at the bottom-right is about five times larger.');
106743
107417
  * }
107418
+ * ```
106744
107419
  *
106745
- * @example
107420
+ * ```js example
106746
107421
  * function setup() {
106747
107422
  * createCanvas(100, 100);
106748
107423
  *
@@ -106763,6 +107438,53 @@ function calculation(p5, fn){
106763
107438
  * // Draw a point.
106764
107439
  * point(x, y);
106765
107440
  * }
107441
+ * ```
107442
+ *
107443
+ * `log()` can also be used in shaders with p5.strands. The following example
107444
+ * uses `log()` to create a decelerating color transition on a shape.
107445
+ *
107446
+ * ```js example
107447
+ * let myShader;
107448
+ *
107449
+ * function setup() {
107450
+ * createCanvas(100, 100, WEBGL);
107451
+ * myShader = buildColorShader(shaderCallback);
107452
+ * describe('A sphere that slowly shifts from purple to yellow.');
107453
+ * }
107454
+ *
107455
+ * function shaderCallback() {
107456
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
107457
+ * let t = millis() * 0.001;
107458
+ *
107459
+ * // log(1 + t * 2) grows at a moderate pace over time.
107460
+ * // Multiply by 0.4 to bring it into the 0 to 1 range.
107461
+ * // min(..., 1) caps the value at 1.
107462
+ * let value = min(log(1 + t * 2) * 0.4, 1);
107463
+ *
107464
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107465
+ * let purple = [0.5, 0, 0.5, 1];
107466
+ * let yellow = [1, 1, 0, 1];
107467
+ *
107468
+ * finalColor.begin();
107469
+ *
107470
+ * // mix() blends between purple (when value = 0) and yellow (when value = 1).
107471
+ * // Because log() slows down over time, the color transition decelerates.
107472
+ * finalColor.set(mix(purple, yellow, value));
107473
+ *
107474
+ * finalColor.end();
107475
+ * }
107476
+ *
107477
+ * function draw() {
107478
+ * background(220);
107479
+ * shader(myShader);
107480
+ * noStroke();
107481
+ * sphere(30);
107482
+ * }
107483
+ * ```
107484
+ *
107485
+ * @method log
107486
+ * @param {Number} n number greater than 0.
107487
+ * @return {Number} natural logarithm of n.
106766
107488
  */
106767
107489
  fn.log = Math.log;
106768
107490
 
@@ -106830,16 +107552,7 @@ function calculation(p5, fn){
106830
107552
  * constrains the remapped value to the target range. For example,
106831
107553
  * `map(11, 0, 10, 0, 100, true)` returns 100.
106832
107554
  *
106833
- * @method map
106834
- * @param {Number} value the value to be remapped.
106835
- * @param {Number} start1 lower bound of the value's current range.
106836
- * @param {Number} stop1 upper bound of the value's current range.
106837
- * @param {Number} start2 lower bound of the value's target range.
106838
- * @param {Number} stop2 upper bound of the value's target range.
106839
- * @param {Boolean} [withinBounds] constrain the value to the newly mapped range.
106840
- * @return {Number} remapped number.
106841
- *
106842
- * @example
107555
+ * ```js example
106843
107556
  * function setup() {
106844
107557
  * createCanvas(100, 100);
106845
107558
  *
@@ -106858,8 +107571,9 @@ function calculation(p5, fn){
106858
107571
  * // Draw the bottom line.
106859
107572
  * line(0, 75, 0, x);
106860
107573
  * }
107574
+ * ```
106861
107575
  *
106862
- * @example
107576
+ * ```js example
106863
107577
  * function setup() {
106864
107578
  * createCanvas(100, 100);
106865
107579
  *
@@ -106878,6 +107592,58 @@ function calculation(p5, fn){
106878
107592
  * // Draw the circle.
106879
107593
  * circle(50, 50, 20);
106880
107594
  * }
107595
+ * ```
107596
+ *
107597
+ * `map()` can also be used in shaders with p5.strands. The following example
107598
+ * uses `map()` to remap time values to color in a shader.
107599
+ *
107600
+ * ```js example
107601
+ * let myShader;
107602
+ *
107603
+ * function setup() {
107604
+ * createCanvas(100, 100, WEBGL);
107605
+ * myShader = buildColorShader(shaderCallback);
107606
+ * describe('A sphere that shifts between cyan and orange over time.');
107607
+ * }
107608
+ *
107609
+ * function shaderCallback() {
107610
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
107611
+ * let t = millis() * 0.001;
107612
+ *
107613
+ * // sin(t) goes between -1 and 1 over time.
107614
+ * let sinVal = sin(t);
107615
+ *
107616
+ * // map() remaps this from the range [-1, 1] to the range [0, 1].
107617
+ * let value = map(sinVal, -1, 1, 0, 1);
107618
+ *
107619
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107620
+ * let cyan = [0, 0.5, 1, 1];
107621
+ * let orange = [1, 0.5, 0, 1];
107622
+ *
107623
+ * finalColor.begin();
107624
+ *
107625
+ * // mix() blends between cyan (when value = 0) and orange (when value = 1).
107626
+ * finalColor.set(mix(cyan, orange, value));
107627
+ *
107628
+ * finalColor.end();
107629
+ * }
107630
+ *
107631
+ * function draw() {
107632
+ * background(220);
107633
+ * shader(myShader);
107634
+ * noStroke();
107635
+ * sphere(30);
107636
+ * }
107637
+ * ```
107638
+ *
107639
+ * @method map
107640
+ * @param {Number} value the value to be remapped.
107641
+ * @param {Number} start1 lower bound of the value's current range.
107642
+ * @param {Number} stop1 upper bound of the value's current range.
107643
+ * @param {Number} start2 lower bound of the value's target range.
107644
+ * @param {Number} stop2 upper bound of the value's target range.
107645
+ * @param {Boolean} [withinBounds] constrain the value to the newly mapped range.
107646
+ * @return {Number} remapped number.
106881
107647
  */
106882
107648
  fn.map = function(n, start1, stop1, start2, stop2, withinBounds) {
106883
107649
  // p5._validateParameters('map', arguments);
@@ -106901,12 +107667,7 @@ function calculation(p5, fn){
106901
107667
  * The version of `max()` with two or more parameters interprets them as
106902
107668
  * individual numbers and returns the largest number.
106903
107669
  *
106904
- * @method max
106905
- * @param {Number} n0 first number to compare.
106906
- * @param {Number} n1 second number to compare.
106907
- * @return {Number} maximum number.
106908
- *
106909
- * @example
107670
+ * ```js example
106910
107671
  * function setup() {
106911
107672
  * createCanvas(100, 100);
106912
107673
  *
@@ -106924,8 +107685,9 @@ function calculation(p5, fn){
106924
107685
  *
106925
107686
  * describe('The number 20 written in the middle of a gray square.');
106926
107687
  * }
107688
+ * ```
106927
107689
  *
106928
- * @example
107690
+ * ```js example
106929
107691
  * function setup() {
106930
107692
  * createCanvas(100, 100);
106931
107693
  *
@@ -106946,6 +107708,53 @@ function calculation(p5, fn){
106946
107708
  *
106947
107709
  * describe('The number 20 written in the middle of a gray square.');
106948
107710
  * }
107711
+ * ```
107712
+ *
107713
+ * `max()` can also be used in shaders with p5.strands. The following example
107714
+ * uses `max()` to clamp values in a shader.
107715
+ *
107716
+ * ```js example
107717
+ * let myShader;
107718
+ *
107719
+ * function setup() {
107720
+ * createCanvas(100, 100, WEBGL);
107721
+ * myShader = buildColorShader(shaderCallback);
107722
+ * describe('A sphere that shifts from rose to steelBlue and stops.');
107723
+ * }
107724
+ *
107725
+ * function shaderCallback() {
107726
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
107727
+ * let t = millis() * 0.001;
107728
+ *
107729
+ * // 1 - t * 0.2 decreases steadily over time.
107730
+ * // max(..., 0) ensures the value never goes below 0.
107731
+ * let value = max(1 - t * 0.2, 0);
107732
+ *
107733
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107734
+ * let steelBlue = [0, 0.3, 0.8, 1];
107735
+ * let rose = [1, 0.3, 0.8, 1];
107736
+ *
107737
+ * finalColor.begin();
107738
+ *
107739
+ * // mix() blends between steelBlue (when value = 0) and rose (when value = 1).
107740
+ * // max() clamps the blend so the color stops changing once it reaches steelBlue.
107741
+ * finalColor.set(mix(steelBlue, rose, value));
107742
+ *
107743
+ * finalColor.end();
107744
+ * }
107745
+ *
107746
+ * function draw() {
107747
+ * background(220);
107748
+ * shader(myShader);
107749
+ * noStroke();
107750
+ * sphere(30);
107751
+ * }
107752
+ * ```
107753
+ *
107754
+ * @method max
107755
+ * @param {Number} n0 first number to compare.
107756
+ * @param {Number} n1 second number to compare.
107757
+ * @return {Number} maximum number.
106949
107758
  */
106950
107759
  /**
106951
107760
  * @method max
@@ -106977,12 +107786,7 @@ function calculation(p5, fn){
106977
107786
  * The version of `min()` with two or more parameters interprets them as
106978
107787
  * individual numbers and returns the smallest number.
106979
107788
  *
106980
- * @method min
106981
- * @param {Number} n0 first number to compare.
106982
- * @param {Number} n1 second number to compare.
106983
- * @return {Number} minimum number.
106984
- *
106985
- * @example
107789
+ * ```js example
106986
107790
  * function setup() {
106987
107791
  * createCanvas(100, 100);
106988
107792
  *
@@ -107000,8 +107804,9 @@ function calculation(p5, fn){
107000
107804
  *
107001
107805
  * describe('The number 5 written in the middle of a gray square.');
107002
107806
  * }
107807
+ * ```
107003
107808
  *
107004
- * @example
107809
+ * ```js example
107005
107810
  * function setup() {
107006
107811
  * createCanvas(100, 100);
107007
107812
  *
@@ -107022,6 +107827,53 @@ function calculation(p5, fn){
107022
107827
  *
107023
107828
  * describe('The number 5 written in the middle of a gray square.');
107024
107829
  * }
107830
+ * ```
107831
+ *
107832
+ * `min()` can also be used in shaders with p5.strands. The following example
107833
+ * uses `min()` to clamp values in a shader.
107834
+ *
107835
+ * ```js example
107836
+ * let myShader;
107837
+ *
107838
+ * function setup() {
107839
+ * createCanvas(100, 100, WEBGL);
107840
+ * myShader = buildColorShader(shaderCallback);
107841
+ * describe('A sphere that shifts from red to green and stops.');
107842
+ * }
107843
+ *
107844
+ * function shaderCallback() {
107845
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
107846
+ * let t = millis() * 0.001;
107847
+ *
107848
+ * // t * 0.2 grows steadily over time.
107849
+ * // min(..., 1) caps the value at 1 so it doesn't go past the target color.
107850
+ * let value = min(t * 0.2, 1);
107851
+ *
107852
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107853
+ * let deepRed = [0.8, 0, 0.2, 1];
107854
+ * let yellowGreen = [0.8, 1, 0.2, 1];
107855
+ *
107856
+ * finalColor.begin();
107857
+ *
107858
+ * // mix() blends between deepRed (when value = 0) and yellowGreen (when value = 1).
107859
+ * // min() clamps the blend so the color stops changing once it reaches yellowGreen.
107860
+ * finalColor.set(mix(deepRed, yellowGreen, value));
107861
+ *
107862
+ * finalColor.end();
107863
+ * }
107864
+ *
107865
+ * function draw() {
107866
+ * background(220);
107867
+ * shader(myShader);
107868
+ * noStroke();
107869
+ * sphere(30);
107870
+ * }
107871
+ * ```
107872
+ *
107873
+ * @method min
107874
+ * @param {Number} n0 first number to compare.
107875
+ * @param {Number} n1 second number to compare.
107876
+ * @return {Number} minimum number.
107025
107877
  */
107026
107878
  /**
107027
107879
  * @method min
@@ -107090,12 +107942,7 @@ function calculation(p5, fn){
107090
107942
  * 2 &times; 2 &times; 2. `pow(2, -3)` evaluates 1 &#247;
107091
107943
  * (2 &times; 2 &times; 2).
107092
107944
  *
107093
- * @method pow
107094
- * @param {Number} n base of the exponential expression.
107095
- * @param {Number} e power by which to raise the base.
107096
- * @return {Number} n^e.
107097
- *
107098
- * @example
107945
+ * ```js example
107099
107946
  * function setup() {
107100
107947
  * createCanvas(100, 100);
107101
107948
  *
@@ -107122,6 +107969,52 @@ function calculation(p5, fn){
107122
107969
  *
107123
107970
  * describe('A series of circles that grow exponentially from top left to bottom right.');
107124
107971
  * }
107972
+ * ```
107973
+ *
107974
+ * `pow()` can also be used in shaders with p5.strands. The following example
107975
+ * uses `pow()` to create a gamma curve effect on colors in a shader.
107976
+ *
107977
+ * ```js example
107978
+ * let myShader;
107979
+ *
107980
+ * function setup() {
107981
+ * createCanvas(100, 100, WEBGL);
107982
+ * myShader = buildColorShader(shaderCallback);
107983
+ * describe('A sphere with colors that shift with a power curve.');
107984
+ * }
107985
+ *
107986
+ * function shaderCallback() {
107987
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.0005 to slow it.
107988
+ * let t = millis() * 0.0005;
107989
+ *
107990
+ * // pow(t, 2) squares the time value: it starts slow then accelerates.
107991
+ * let value = pow(t, 2);
107992
+ *
107993
+ * // Each color is [R, G, B, A] with values from 0 to 1.
107994
+ * let deepBlue = [0, 0.1, 0.5, 1];
107995
+ * let gold = [1, 0.8, 0, 1];
107996
+ *
107997
+ * finalColor.begin();
107998
+ *
107999
+ * // mix() blends between deepBlue (when value = 0) and gold (when value = 1).
108000
+ * // Because pow() accelerates, the color transition gets faster over time.
108001
+ * finalColor.set(mix(deepBlue, gold, value));
108002
+ *
108003
+ * finalColor.end();
108004
+ * }
108005
+ *
108006
+ * function draw() {
108007
+ * background(220);
108008
+ * shader(myShader);
108009
+ * noStroke();
108010
+ * sphere(30);
108011
+ * }
108012
+ * ```
108013
+ *
108014
+ * @method pow
108015
+ * @param {Number} n base of the exponential expression.
108016
+ * @param {Number} e power by which to raise the base.
108017
+ * @return {Number} n^e.
107125
108018
  */
107126
108019
  fn.pow = Math.pow;
107127
108020
 
@@ -107134,12 +108027,7 @@ function calculation(p5, fn){
107134
108027
  * decimal places to use when rounding. For example, `round(12.34, 1)` returns
107135
108028
  * 12.3. `decimals` is 0 by default.
107136
108029
  *
107137
- * @method round
107138
- * @param {Number} n number to round.
107139
- * @param {Number} [decimals] number of decimal places to round to, default is 0.
107140
- * @return {Integer} rounded number.
107141
- *
107142
- * @example
108030
+ * ```js example
107143
108031
  * function setup() {
107144
108032
  * createCanvas(100, 100);
107145
108033
  *
@@ -107157,8 +108045,9 @@ function calculation(p5, fn){
107157
108045
  *
107158
108046
  * describe('The number 4 written in middle of the canvas.');
107159
108047
  * }
108048
+ * ```
107160
108049
  *
107161
- * @example
108050
+ * ```js example
107162
108051
  * function setup() {
107163
108052
  * createCanvas(100, 100);
107164
108053
  *
@@ -107176,6 +108065,57 @@ function calculation(p5, fn){
107176
108065
  *
107177
108066
  * describe('The number 12.78 written in middle of canvas.');
107178
108067
  * }
108068
+ * ```
108069
+ *
108070
+ * `round()` can also be used in shaders with p5.strands. The following example
108071
+ * uses `round()` to quantize colors in a shader.
108072
+ *
108073
+ * ```js example
108074
+ * let myShader;
108075
+ *
108076
+ * function setup() {
108077
+ * createCanvas(100, 100, WEBGL);
108078
+ * myShader = buildColorShader(shaderCallback);
108079
+ * describe('A sphere with posterized quantized colors.');
108080
+ * }
108081
+ *
108082
+ * function shaderCallback() {
108083
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
108084
+ * let t = millis() * 0.001;
108085
+ *
108086
+ * // sin(t) goes between -1 and 1.
108087
+ * // 0.5 + 0.5 * sin(t) remaps this to the 0 to 1 range.
108088
+ * let sinVal = 0.5 + 0.5 * sin(t);
108089
+ *
108090
+ * // Multiply by 4 then round to get 5 distinct levels (0, 0.25, 0.5, 0.75, 1).
108091
+ * // Divide by 4 to bring the result back to the 0 to 1 range.
108092
+ * let value = round(sinVal * 4) / 4;
108093
+ *
108094
+ * // Each color is [R, G, B, A] with values from 0 to 1.
108095
+ * let mutedBlue = [0.3, 0.4, 0.7, 1];
108096
+ * let rose = [0.8, 0.3, 0.4, 1];
108097
+ *
108098
+ * finalColor.begin();
108099
+ *
108100
+ * // mix() blends between mutedBlue (when value = 0) and rose (when value = 1).
108101
+ * // The round() creates stepped bands of color like a posterization effect.
108102
+ * finalColor.set(mix(mutedBlue, rose, value));
108103
+ *
108104
+ * finalColor.end();
108105
+ * }
108106
+ *
108107
+ * function draw() {
108108
+ * background(220);
108109
+ * shader(myShader);
108110
+ * noStroke();
108111
+ * sphere(30);
108112
+ * }
108113
+ * ```
108114
+ *
108115
+ * @method round
108116
+ * @param {Number} n number to round.
108117
+ * @param {Number} [decimals] number of decimal places to round to, default is 0.
108118
+ * @return {Integer} rounded number.
107179
108119
  */
107180
108120
  fn.round = function(n, decimals) {
107181
108121
  if (!decimals) {
@@ -107246,11 +108186,7 @@ function calculation(p5, fn){
107246
108186
  * always returns a positive value. `sqrt()` doesn't work with negative arguments
107247
108187
  * such as `sqrt(-9)`.
107248
108188
  *
107249
- * @method sqrt
107250
- * @param {Number} n non-negative number to square root.
107251
- * @return {Number} square root of number.
107252
- *
107253
- * @example
108189
+ * ```js example
107254
108190
  * function setup() {
107255
108191
  * createCanvas(100, 100);
107256
108192
  *
@@ -107266,8 +108202,9 @@ function calculation(p5, fn){
107266
108202
  *
107267
108203
  * describe('Two white circles. The circle at the top-left is small. The circle at the bottom-right is ten times larger.');
107268
108204
  * }
108205
+ * ```
107269
108206
  *
107270
- * @example
108207
+ * ```js example
107271
108208
  * function setup() {
107272
108209
  * createCanvas(100, 100);
107273
108210
  *
@@ -107288,6 +108225,63 @@ function calculation(p5, fn){
107288
108225
  * // Draw the point.
107289
108226
  * point(x, y);
107290
108227
  * }
108228
+ * ```
108229
+ *
108230
+ * `sqrt()` can also be used in shaders with p5.strands. The following example
108231
+ * uses `sqrt()` to create a smooth ease-out curve on color and size.
108232
+ *
108233
+ * ```js example
108234
+ * let myShader;
108235
+ *
108236
+ * function setup() {
108237
+ * createCanvas(100, 100, WEBGL);
108238
+ * myShader = buildColorShader(shaderCallback);
108239
+ * describe('A sphere that grows and shifts from navy to orange with an ease-out curve.');
108240
+ * }
108241
+ *
108242
+ * function shaderCallback() {
108243
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
108244
+ * let t = millis() * 0.001;
108245
+ *
108246
+ * // sin(t) goes between -1 and 1.
108247
+ * // 0.5 + 0.5 * sin(t) remaps this to the 0 to 1 range.
108248
+ * let sinVal = 0.5 + 0.5 * sin(t);
108249
+ *
108250
+ * // sqrt(sinVal) creates an ease-out curve: fast start, slow finish.
108251
+ * // Since sinVal is in [0,1], sqrt() stays in [0,1].
108252
+ * let value = sqrt(sinVal);
108253
+ *
108254
+ * // Each color is [R, G, B, A] with values from 0 to 1.
108255
+ * let navy = [0, 0.1, 0.4, 1];
108256
+ * let brightOrange = [1, 0.6, 0, 1];
108257
+ *
108258
+ * finalColor.begin();
108259
+ *
108260
+ * // mix() blends between navy (when value = 0) and brightOrange (when value = 1).
108261
+ * // The sqrt() ease-out makes the color change fast at first, then slow down.
108262
+ * finalColor.set(mix(navy, brightOrange, value));
108263
+ *
108264
+ * finalColor.end();
108265
+ * }
108266
+ *
108267
+ * function drawShape() {
108268
+ * let t = millis() * 0.001;
108269
+ * let sinVal = 0.5 + 0.5 * sin(t);
108270
+ * let size = 10 + sqrt(sinVal) * 30;
108271
+ * sphere(size);
108272
+ * }
108273
+ *
108274
+ * function draw() {
108275
+ * background(220);
108276
+ * shader(myShader);
108277
+ * noStroke();
108278
+ * drawShape();
108279
+ * }
108280
+ * ```
108281
+ *
108282
+ * @method sqrt
108283
+ * @param {Number} n non-negative number to square root.
108284
+ * @return {Number} square root of number.
107291
108285
  */
107292
108286
  fn.sqrt = Math.sqrt;
107293
108287
 
@@ -107297,11 +108291,7 @@ function calculation(p5, fn){
107297
108291
  * A number's fractional part includes its decimal values. For example,
107298
108292
  * `fract(12.34)` returns 0.34.
107299
108293
  *
107300
- * @method fract
107301
- * @param {Number} n number whose fractional part will be found.
107302
- * @returns {Number} fractional part of n.
107303
- *
107304
- * @example
108294
+ * ```js example
107305
108295
  * function setup() {
107306
108296
  * createCanvas(100, 100);
107307
108297
  *
@@ -107321,6 +108311,53 @@ function calculation(p5, fn){
107321
108311
  *
107322
108312
  * describe('The number 56.78 written above the number 0.78.');
107323
108313
  * }
108314
+ * ```
108315
+ *
108316
+ * `fract()` can also be used in shaders with p5.strands. The following example
108317
+ * uses `fract()` to create repeating patterns in a shader.
108318
+ *
108319
+ * ```js example
108320
+ * let myShader;
108321
+ *
108322
+ * function setup() {
108323
+ * createCanvas(100, 100, WEBGL);
108324
+ * myShader = buildColorShader(shaderCallback);
108325
+ * describe('A sphere with a repeating gradient pattern.');
108326
+ * }
108327
+ *
108328
+ * function shaderCallback() {
108329
+ * // shaderCallback runs on the GPU. millis() gives ms since start; multiply by 0.001 for seconds.
108330
+ * let t = millis() * 0.001;
108331
+ *
108332
+ * // Multiply by 0.5 to slow the animation to half speed.
108333
+ * // fract(t * 0.5) extracts only the decimal part of the number.
108334
+ * // This creates a smooth sawtooth wave that repeats every 2 seconds.
108335
+ * let value = fract(t * 0.5);
108336
+ *
108337
+ * // Each color is [R, G, B, A] with values from 0 to 1.
108338
+ * let cyan = [0, 0.5, 1, 1];
108339
+ * let orange = [1, 0.5, 0, 1];
108340
+ *
108341
+ * finalColor.begin();
108342
+ *
108343
+ * // mix() blends between cyan (when value = 0) and orange (when value = 1).
108344
+ * // Because fract() resets to 0 each cycle, the color loops smoothly.
108345
+ * finalColor.set(mix(cyan, orange, value));
108346
+ *
108347
+ * finalColor.end();
108348
+ * }
108349
+ *
108350
+ * function draw() {
108351
+ * background(220);
108352
+ * shader(myShader);
108353
+ * noStroke();
108354
+ * sphere(30);
108355
+ * }
108356
+ * ```
108357
+ *
108358
+ * @method fract
108359
+ * @param {Number} n number whose fractional part will be found.
108360
+ * @returns {Number} fractional part of n.
107324
108361
  */
107325
108362
  fn.fract = function(toConvert) {
107326
108363
  // p5._validateParameters('fract', arguments);
@@ -108452,6 +109489,8 @@ function _validatedVectorOperation(expectsSoloNumberArgument){
108452
109489
  }
108453
109490
 
108454
109491
  /**
109492
+ * @private
109493
+ * @internal
108455
109494
  * Each of the following decorators validates the data on vector operations.
108456
109495
  * These ensure that the arguments are consistently formatted, and that
108457
109496
  * pre-conditions are met.
@@ -120921,7 +121960,7 @@ class Font {
120921
121960
  * coordinates of the bounding box's bottom-left corner. See
120922
121961
  * <a href="#/p5/textAlign">textAlign()</a> for more ways to align text.
120923
121962
  *
120924
- * The fourth parameter, `options`, is also optional. `font.textToPoints()`
121963
+ * The fourth parameter, `options`, is also optional. `font.textToContours()`
120925
121964
  * expects an object with the following properties:
120926
121965
  *
120927
121966
  * `sampleFactor` is the ratio of the text's path length to the number of
@@ -121004,6 +122043,15 @@ class Font {
121004
122043
  *
121005
122044
  * The generated model (a Geometry object) can be manipulated further—rotated, scaled,
121006
122045
  * or styled with shaders—to create engaging, interactive visual art.
122046
+ *
122047
+ * The `options` parameter is also optional. `font.textToModel()` expects an object
122048
+ * with the following properties:
122049
+ *
122050
+ * `extrude` is the depth to extrude the text. It defaults to 0. A value of 0 produces
122051
+ * flat text; higher values create thicker, 3D models.
122052
+ *
122053
+ * `sampleFactor` is a factor controlling the level of detail for the text contours.
122054
+ * It defaults to 1. Higher values result in smoother curves.
121007
122055
  *
121008
122056
  * @param {String} str The text string to convert into a 3D model.
121009
122057
  * @param {Number} x The x-coordinate for the starting position of the text.
@@ -124886,7 +125934,7 @@ class RendererGL extends Renderer3D {
124886
125934
  }
124887
125935
 
124888
125936
  // Create image from data
124889
- const region = new Image(w * framebuffer.density, h * framebuffer.density);
125937
+ const region = new Image$1(w * framebuffer.density, h * framebuffer.density);
124890
125938
  region.imageData = region.canvas.getContext('2d').createImageData(
124891
125939
  region.width,
124892
125940
  region.height
@@ -134979,19 +136027,116 @@ function installBuiltinGlobalAccessors(strandsContext) {
134979
136027
 
134980
136028
  for (const name of Object.keys(BUILTIN_GLOBAL_SPECS)) {
134981
136029
  const spec = BUILTIN_GLOBAL_SPECS[name];
134982
- Object.defineProperty(window, name, {
134983
- get: () => {
136030
+ const backingKey = `_strands_${name}`;
136031
+
136032
+ // Define on window for global mode only
136033
+ const inst = getRuntimeP5Instance();
136034
+ if (inst?._isGlobal) {
136035
+ Object.defineProperty(window, name, {
136036
+ get: () => {
136037
+ if (strandsContext.active) {
136038
+ return getBuiltinGlobalNode(strandsContext, name);
136039
+ }
136040
+ const inst = getRuntimeP5Instance();
136041
+ return spec.get(inst);
136042
+ },
136043
+ configurable: true,
136044
+ });
136045
+ }
136046
+
136047
+ // Capture original descriptor (held in closure for the getter to delegate to)
136048
+ const originalProtoDesc = Object.getOwnPropertyDescriptor(strandsContext.p5.prototype, name);
136049
+
136050
+ // Define on p5.prototype for instance mode
136051
+ Object.defineProperty(strandsContext.p5.prototype, name, {
136052
+ get: function() {
134984
136053
  if (strandsContext.active) {
134985
136054
  return getBuiltinGlobalNode(strandsContext, name);
134986
136055
  }
134987
- const inst = getRuntimeP5Instance();
134988
- return spec.get(inst);
136056
+ // If our setter stored a value on this instance, return it
136057
+ if (Object.prototype.hasOwnProperty.call(this, backingKey)) {
136058
+ return this[backingKey];
136059
+ }
136060
+ // Delegate to original getter (e.g. width -> this._renderer?.width)
136061
+ if (originalProtoDesc?.get) {
136062
+ return originalProtoDesc.get.call(this);
136063
+ }
136064
+ // Fall back to original value for data properties (like mouseX)
136065
+ return originalProtoDesc?.value;
134989
136066
  },
136067
+ set: function(val) {
136068
+ this[backingKey] = val;
136069
+ },
136070
+ configurable: true,
134990
136071
  });
136072
+
136073
+ // Define on p5.Graphics.prototype for graphics mode
136074
+ const GraphicsProto = strandsContext.p5?.Graphics?.prototype;
136075
+ if (GraphicsProto) {
136076
+ const originalDesc = Object.getOwnPropertyDescriptor(GraphicsProto, name);
136077
+
136078
+ Object.defineProperty(GraphicsProto, name, {
136079
+ get: function() {
136080
+ if (strandsContext.active) {
136081
+ return getBuiltinGlobalNode(strandsContext, name);
136082
+ }
136083
+ // Delegate to original getter if it exists (class-level getters like width, deltaTime)
136084
+ if (originalDesc?.get) {
136085
+ return originalDesc.get.call(this);
136086
+ }
136087
+ return this[backingKey];
136088
+ },
136089
+ set: function(val) {
136090
+ if (originalDesc?.set) {
136091
+ originalDesc.set.call(this, val);
136092
+ } else {
136093
+ this[backingKey] = val;
136094
+ }
136095
+ },
136096
+ configurable: true,
136097
+ });
136098
+ }
134991
136099
  }
134992
136100
  strandsContext._builtinGlobalsAccessorsInstalled = true;
134993
136101
  }
134994
136102
 
136103
+ function installInstanceIndexAccessor(strandsContext) {
136104
+ if (strandsContext._instanceIndexAccessorInstalled) return;
136105
+
136106
+ const getRuntimeP5Instance = () => strandsContext.renderer?._pInst || strandsContext.p5?.instance;
136107
+
136108
+ const instanceIndexGetter = function() {
136109
+ if (strandsContext.active) {
136110
+ const node = variableNode(strandsContext, { baseType: BaseType.INT, dimension: 1 }, strandsContext.backend.instanceIdReference());
136111
+ return createStrandsNode(node.id, node.dimension, strandsContext);
136112
+ }
136113
+ return undefined;
136114
+ };
136115
+
136116
+ const inst = getRuntimeP5Instance();
136117
+ if (inst?._isGlobal) {
136118
+ Object.defineProperty(window, 'instanceIndex', {
136119
+ get: instanceIndexGetter,
136120
+ configurable: true,
136121
+ });
136122
+ }
136123
+
136124
+ Object.defineProperty(strandsContext.p5.prototype, 'instanceIndex', {
136125
+ get: instanceIndexGetter,
136126
+ configurable: true,
136127
+ });
136128
+
136129
+ const GraphicsProto = strandsContext.p5?.Graphics?.prototype;
136130
+ if (GraphicsProto) {
136131
+ Object.defineProperty(GraphicsProto, 'instanceIndex', {
136132
+ get: instanceIndexGetter,
136133
+ configurable: true,
136134
+ });
136135
+ }
136136
+
136137
+ strandsContext._instanceIndexAccessorInstalled = true;
136138
+ }
136139
+
134995
136140
  //////////////////////////////////////////////
134996
136141
  // Prototype mirroring helpers
134997
136142
  //////////////////////////////////////////////
@@ -135165,7 +136310,7 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
135165
136310
  const originalLerp = fn.lerp;
135166
136311
  augmentFn(fn, p5, 'lerp', function (...args) {
135167
136312
  if (strandsContext.active) {
135168
- return fn.mix(...args);
136313
+ return this.mix(...args);
135169
136314
  } else {
135170
136315
  return originalLerp.apply(this, args);
135171
136316
  }
@@ -135188,6 +136333,139 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
135188
136333
  return result;
135189
136334
  });
135190
136335
 
136336
+ const originalColor = fn.color;
136337
+ augmentFn(fn, p5, 'color', function (...args) {
136338
+ if (!strandsContext.active) {
136339
+ return originalColor.apply(this, args);
136340
+ }
136341
+ // Reuse p5's parser - handles hex strings, rgb(), CSS named colors, numerics
136342
+ const c = originalColor.apply(this, args);
136343
+ // _getRGBA() returns [r, g, b, a] normalized to 0-1
136344
+ const rgba = c._getRGBA();
136345
+ const { id, dimension } = primitiveConstructorNode(
136346
+ strandsContext,
136347
+ { baseType: BaseType.FLOAT, dimension: null },
136348
+ rgba
136349
+ );
136350
+ return createStrandsNode(id, dimension, strandsContext);
136351
+ });
136352
+ const originalLerpColor = fn.lerpColor;
136353
+ augmentFn(fn, p5, 'lerpColor', function (...args) {
136354
+ if (!strandsContext.active) {
136355
+ return originalLerpColor.apply(this, args);
136356
+ }
136357
+ // In strands, colors are vec4s - lerpColor maps directly to GLSL mix()
136358
+ return this.mix(...args);
136359
+ });
136360
+ // Component accessors: extract scalar channels from a vec4 color
136361
+ const originalRed = fn.red;
136362
+ augmentFn(fn, p5, 'red', function (...args) {
136363
+ if (!strandsContext.active) {
136364
+ return originalRed.apply(this, args);
136365
+ }
136366
+ return p5.strandsNode(args[0]).x;
136367
+ });
136368
+
136369
+ const originalGreen = fn.green;
136370
+ augmentFn(fn, p5, 'green', function (...args) {
136371
+ if (!strandsContext.active) {
136372
+ return originalGreen.apply(this, args);
136373
+ }
136374
+ return p5.strandsNode(args[0]).y;
136375
+ });
136376
+
136377
+ const originalBlue = fn.blue;
136378
+ augmentFn(fn, p5, 'blue', function (...args) {
136379
+ if (!strandsContext.active) {
136380
+ return originalBlue.apply(this, args);
136381
+ }
136382
+ return p5.strandsNode(args[0]).z;
136383
+ });
136384
+
136385
+ const originalAlpha = fn.alpha;
136386
+ augmentFn(fn, p5, 'alpha', function (...args) {
136387
+ if (!strandsContext.active) {
136388
+ return originalAlpha.apply(this, args);
136389
+ }
136390
+ return p5.strandsNode(args[0]).w;
136391
+ });
136392
+
136393
+ // RGB to HSB conversion based on:
136394
+ // https://en.wikipedia.org/wiki/HSL_and_HSV#From_RGB
136395
+ // Using mix/step to avoid branching, via the compact form from:
136396
+ // http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
136397
+ const _rgb2hsb = (instance, colorNode) => {
136398
+ const r = colorNode.x;
136399
+ const g = colorNode.y;
136400
+ const b = colorNode.z;
136401
+ const K = instance.vec4(0, -1/3, 2/3, -1);
136402
+ const p = instance.mix(
136403
+ instance.vec4(b, g, K.w, K.z),
136404
+ instance.vec4(g, b, K.x, K.y),
136405
+ instance.step(b, g)
136406
+ );
136407
+ const q = instance.mix(
136408
+ instance.vec4(p.x, p.y, p.w, r),
136409
+ instance.vec4(r, p.y, p.z, p.x),
136410
+ instance.step(p.x, r)
136411
+ );
136412
+ const d = q.x.sub(instance.min(q.w, q.y));
136413
+ const e = p5.strandsNode(1e-10);
136414
+ const h = instance.abs(q.z.add(q.w.sub(q.y).div(d.mult(6).add(e))));
136415
+ const s = d.div(q.x.add(e));
136416
+ return instance.vec3(h, s, q.x);
136417
+ };
136418
+
136419
+ const _rgb2hsl = (instance, colorNode) => {
136420
+ const r = colorNode.x;
136421
+ const g = colorNode.y;
136422
+ const b = colorNode.z;
136423
+ const maxC = instance.max(r, instance.max(g, b));
136424
+ const minC = instance.min(r, instance.min(g, b));
136425
+ const l = maxC.add(minC).div(2);
136426
+ const d = maxC.sub(minC);
136427
+ const e = p5.strandsNode(1e-10);
136428
+ const s = instance.mix(
136429
+ p5.strandsNode(0),
136430
+ d.div(p5.strandsNode(1).sub(instance.abs(l.mult(2).sub(1)))),
136431
+ instance.step(e, d)
136432
+ );
136433
+ const h_rg = instance.mod(g.sub(b).div(d.add(e)), p5.strandsNode(6)).div(6);
136434
+ const h_gb = b.sub(r).div(d.add(e)).add(2).div(6);
136435
+ const h_br = r.sub(g).div(d.add(e)).add(4).div(6);
136436
+ const isR = instance.step(maxC.sub(e), r).mult(instance.step(r.sub(e), maxC));
136437
+ const isG = instance.step(maxC.sub(e), g).mult(instance.step(g.sub(e), maxC));
136438
+ const h = instance.mix(instance.mix(h_br, h_gb, isG), h_rg, isR);
136439
+ return instance.vec3(h, s, l);
136440
+ };
136441
+ const originalHue = fn.hue;
136442
+ augmentFn(fn, p5, 'hue', function (...args) {
136443
+ if (!strandsContext.active) return originalHue.apply(this, args);
136444
+ const colorNode = p5.strandsNode(args[0]);
136445
+ return _rgb2hsl(this, this.vec3(colorNode.x, colorNode.y, colorNode.z)).x;
136446
+ });
136447
+
136448
+ const originalSaturation = fn.saturation;
136449
+ augmentFn(fn, p5, 'saturation', function (...args) {
136450
+ if (!strandsContext.active) return originalSaturation.apply(this, args);
136451
+ const colorNode = p5.strandsNode(args[0]);
136452
+ return _rgb2hsl(this, this.vec3(colorNode.x, colorNode.y, colorNode.z)).y;
136453
+ });
136454
+
136455
+ const originalBrightness = fn.brightness;
136456
+ augmentFn(fn, p5, 'brightness', function (...args) {
136457
+ if (!strandsContext.active) return originalBrightness.apply(this, args);
136458
+ const colorNode = p5.strandsNode(args[0]);
136459
+ return _rgb2hsb(this, this.vec3(colorNode.x, colorNode.y, colorNode.z)).z;
136460
+ });
136461
+
136462
+ const originalLightness = fn.lightness;
136463
+ augmentFn(fn, p5, 'lightness', function (...args) {
136464
+ if (!strandsContext.active) return originalLightness.apply(this, args);
136465
+ const colorNode = p5.strandsNode(args[0]);
136466
+ return _rgb2hsl(this, this.vec3(colorNode.x, colorNode.y, colorNode.z)).z;
136467
+ });
136468
+
135191
136469
  augmentFn(fn, p5, 'getTexture', function (...rawArgs) {
135192
136470
  if (strandsContext.active) {
135193
136471
  const { id, dimension } = strandsContext.backend.createGetTextureCall(strandsContext, rawArgs);
@@ -135213,6 +136491,7 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
135213
136491
  const originalNoise = fn.noise;
135214
136492
  const originalNoiseDetail = fn.noiseDetail;
135215
136493
  const originalRandom = fn.random;
136494
+ const originalRandomGaussian=fn.randomGaussian;
135216
136495
  const originalRandomSeed = fn.randomSeed;
135217
136496
  const originalMillis = fn.millis;
135218
136497
 
@@ -135356,6 +136635,20 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
135356
136635
  }
135357
136636
  });
135358
136637
 
136638
+ augmentFn(fn, p5, 'randomGaussian', function(...args){
136639
+ if(!strandsContext.active){
136640
+ return originalRandomGaussian.apply(this, args);
136641
+ }
136642
+ const mean = args.length >= 1 ? args[0] : 0;
136643
+ const stdDev = args.length >= 2 ? args[1] : 1;
136644
+
136645
+ const u1 = this.max(this.random(), 1e-6);
136646
+ const u2 = this.random();
136647
+ const z = this.sqrt(this.log(u1).mult(-2)).mult(this.cos(u2.mult(2*Math.PI)));
136648
+
136649
+ return z.mult(stdDev).add(mean);
136650
+ });
136651
+
135359
136652
  augmentFn(fn, p5, 'millis', function (...args) {
135360
136653
  if (!strandsContext.active) {
135361
136654
  return originalMillis.apply(this, args);
@@ -135547,6 +136840,17 @@ function createHookArguments(strandsContext, parameters){
135547
136840
  return createStrandsNode(propNode.id, propNode.dimension, strandsContext, onRebind);
135548
136841
  },
135549
136842
  set(val) {
136843
+ const valDim = val?.isStrandsNode
136844
+ ? val.dimension
136845
+ : (Array.isArray(val) ? val.length : 1);
136846
+ if( valDim !== propertyType.dataType.dimension && valDim !== 1){
136847
+ dimensionMismatchError(
136848
+ propertyType.dataType.dimension,
136849
+ valDim,
136850
+ `${param.name}.${propertyType.name}`
136851
+ );
136852
+ }
136853
+
135550
136854
  const oldDependsOn = dag.dependsOn[structNode.id];
135551
136855
  const newDependsOn = [...oldDependsOn];
135552
136856
  let newValueID;
@@ -135624,6 +136928,7 @@ function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName
135624
136928
  }
135625
136929
  function createShaderHooksFunctions(strandsContext, fn, shader) {
135626
136930
  installBuiltinGlobalAccessors(strandsContext);
136931
+ installInstanceIndexAccessor(strandsContext);
135627
136932
 
135628
136933
  // Add shader context to hooks before spreading
135629
136934
  const vertexHooksWithContext = Object.fromEntries(
@@ -135656,6 +136961,54 @@ function createShaderHooksFunctions(strandsContext, fn, shader) {
135656
136961
  hook.set = function(result) {
135657
136962
  hook._result = result;
135658
136963
  };
136964
+ hook._active = false;
136965
+
136966
+ const numStructArgs = hookType.parameters.filter(
136967
+ param => param.type && param.type.properties
136968
+ ).length;
136969
+ let argIdx = -1;
136970
+ if (numStructArgs === 1) {
136971
+ argIdx = hookType.parameters.findIndex(
136972
+ param => param.type && param.type.properties
136973
+ );
136974
+ }
136975
+ if (argIdx >= 0) {
136976
+ const structParam = hookType.parameters[argIdx];
136977
+ if (structParam.type.properties) {
136978
+ const nameMatch = /^get([A-Z0-9]\w*)$/.exec(hookType.name);
136979
+ const displayName = nameMatch
136980
+ ? nameMatch[1][0].toLowerCase() + nameMatch[1].slice(1)
136981
+ : hookType.name;
136982
+ for (const prop of structParam.type.properties) {
136983
+ const key = prop.name;
136984
+ Object.defineProperty(hook, key, {
136985
+ get() {
136986
+ if (!this._active) {
136987
+ userError(
136988
+ 'scope error',
136989
+ `It looks like you're trying to access "${displayName}.${key}" outside of its begin()/end() block.\n\n` +
136990
+ `Properties of ${displayName} are only available between ` +
136991
+ `${displayName}.begin() and ${displayName}.end().\n\n` +
136992
+ `To share data between hooks, use sharedVec3() or sharedFloat() ` +
136993
+ `to pass values between them.`
136994
+ );
136995
+ }
136996
+ return this._args[this._argIdx][key];
136997
+ },
136998
+ set(val) {
136999
+ if (!this._active) {
137000
+ userError(
137001
+ 'scope error',
137002
+ `It looks like you're trying to set "${displayName}.${key}" outside of its begin()/end() block.`
137003
+ );
137004
+ }
137005
+ this._args[this._argIdx][key] = val;
137006
+ },
137007
+ enumerable: true,
137008
+ });
137009
+ }
137010
+ }
137011
+ }
135659
137012
 
135660
137013
  let entryBlockID;
135661
137014
  function setupHook() {
@@ -135664,25 +137017,14 @@ function createShaderHooksFunctions(strandsContext, fn, shader) {
135664
137017
  addEdge(cfg, cfg.currentBlock, entryBlockID);
135665
137018
  pushBlock(cfg, entryBlockID);
135666
137019
  const args = createHookArguments(strandsContext, hookType.parameters);
135667
- const numStructArgs = hookType.parameters.filter(param => param.type.properties).length;
135668
- let argIdx = -1;
135669
- if (numStructArgs === 1) {
135670
- argIdx = hookType.parameters.findIndex(param => param.type.properties);
135671
- }
137020
+ hook._active = true;
137021
+ hook._args = args;
137022
+ hook._argIdx = argIdx;
135672
137023
  hook._properties = [];
135673
137024
  for (let i = 0; i < args.length; i++) {
135674
137025
  if (i === argIdx) {
135675
137026
  for (const key of args[argIdx].structProperties || []) {
135676
137027
  hook._properties.push(key);
135677
- Object.defineProperty(hook, key, {
135678
- get() {
135679
- return args[argIdx][key];
135680
- },
135681
- set(val) {
135682
- args[argIdx][key] = val;
135683
- },
135684
- enumerable: true,
135685
- });
135686
137028
  }
135687
137029
  if (hookType.returnType?.typeName === hookType.parameters[argIdx].type.typeName) {
135688
137030
  hook.set(args[argIdx]);
@@ -135695,6 +137037,7 @@ function createShaderHooksFunctions(strandsContext, fn, shader) {
135695
137037
  return args;
135696
137038
  }
135697
137039
  function finishHook() {
137040
+ hook._active = false;
135698
137041
  const userReturned = hook._result;
135699
137042
  strandsContext.activeHook = undefined;
135700
137043
 
@@ -136133,18 +137476,20 @@ if (typeof p5 !== "undefined") {
136133
137476
  */
136134
137477
 
136135
137478
  /**
136136
- * @method instanceID
137479
+ * @property instanceIndex
136137
137480
  * @beta
136138
137481
  * @description
136139
137482
  * Returns the index of the current instance when drawing multiple copies of a
136140
137483
  * shape with <a href="#/p5/model">`model(count)`</a>. The first instance has an
136141
- * ID of `0`, the second has `1`, and so on.
137484
+ * index of `0`, the second has `1`, and so on.
136142
137485
  *
136143
137486
  * This lets each copy of a shape behave differently. For example, you can use
136144
- * the ID to place instances at different positions, give them different colors,
137487
+ * the index to place instances at different positions, give them different colors,
136145
137488
  * or animate them at different speeds.
136146
137489
  *
136147
- * `instanceID()` can only be used inside a p5.strands shader callback.
137490
+ * `instanceIndex` can only be used inside a p5.strands shader callback.
137491
+ *
137492
+ * (Note: `instanceID()` is also available as a function for compatibility.)
136148
137493
  *
136149
137494
  * ```js example
136150
137495
  * let instancesShader;
@@ -136167,7 +137512,7 @@ if (typeof p5 !== "undefined") {
136167
137512
  * // Spread spheres evenly across the canvas based on their index
136168
137513
  * let spacing = width / count;
136169
137514
  * worldInputs.position.x +=
136170
- * (instanceID() - (count - 1) / 2) * spacing;
137515
+ * (instanceIndex - (count - 1) / 2) * spacing;
136171
137516
  * worldInputs.end();
136172
137517
  * }
136173
137518
  *
@@ -136181,7 +137526,7 @@ if (typeof p5 !== "undefined") {
136181
137526
  * }
136182
137527
  * ```
136183
137528
  *
136184
- * If you are using WebGPU mode, a common pattern is to use `instanceID()` to look up data made with
137529
+ * If you are using WebGPU mode, a common pattern is to use `instanceIndex` to look up data made with
136185
137530
  * <a href="#/p5/createStorage">`createStorage()`</a>.
136186
137531
  * This lets you give each instance different properties.
136187
137532
  *
@@ -136224,7 +137569,7 @@ if (typeof p5 !== "undefined") {
136224
137569
  * let itemColor = sharedVec4();
136225
137570
  *
136226
137571
  * worldInputs.begin();
136227
- * let item = data[instanceID()];
137572
+ * let item = data[instanceIndex];
136228
137573
  * itemColor = item.color;
136229
137574
  * worldInputs.position += item.position;
136230
137575
  * worldInputs.end();
@@ -136246,7 +137591,22 @@ if (typeof p5 !== "undefined") {
136246
137591
  * This can be paired with <a href="#/p5/buildComputeShader">`buildComputeShader`</a>
136247
137592
  * to update the data being read.
136248
137593
  *
136249
- * @webgpu
137594
+ * @type {*}
137595
+ */
137596
+
137597
+ /**
137598
+ * @method instanceID
137599
+ * @beta
137600
+ * @deprecated Use <a href="#/p5/instanceIndex">`instanceIndex`</a> instead.
137601
+ * @description
137602
+ * A function alias for <a href="#/p5/instanceIndex">`instanceIndex`</a>, kept for compatibility.
137603
+ * Prefer using <a href="#/p5/instanceIndex">`instanceIndex`</a> directly as a value instead.
137604
+ *
137605
+ * Returns the index of the current instance when drawing multiple copies of a
137606
+ * shape with <a href="#/p5/model">`model(count)`</a>.
137607
+ *
137608
+ * `instanceID()` can only be used inside a p5.strands shader callback.
137609
+ *
136250
137610
  * @returns {*} The index of the current instance.
136251
137611
  */
136252
137612