p5 2.2.1 → 2.2.2-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 (94) hide show
  1. package/dist/accessibility/color_namer.js +6 -6
  2. package/dist/accessibility/index.js +6 -6
  3. package/dist/app.js +6 -6
  4. package/dist/color/color_conversion.js +6 -6
  5. package/dist/color/creating_reading.js +1 -1
  6. package/dist/color/index.js +2 -2
  7. package/dist/color/p5.Color.js +1 -1
  8. package/dist/color/setting.js +2 -2
  9. package/dist/{constants-DQyACdzq.js → constants-DAnmXDew.js} +1 -1
  10. package/dist/core/constants.js +1 -1
  11. package/dist/core/environment.js +1 -1
  12. package/dist/core/filterShaders.js +1 -1
  13. package/dist/core/friendly_errors/fes_core.js +1 -1
  14. package/dist/core/friendly_errors/file_errors.js +1 -1
  15. package/dist/core/friendly_errors/index.js +1 -1
  16. package/dist/core/friendly_errors/param_validator.js +1 -1
  17. package/dist/core/friendly_errors/sketch_verifier.js +2 -2
  18. package/dist/core/helpers.js +1 -1
  19. package/dist/core/init.js +6 -6
  20. package/dist/core/internationalization.js +1 -1
  21. package/dist/core/legacy.js +6 -6
  22. package/dist/core/main.js +6 -6
  23. package/dist/core/p5.Graphics.js +4 -4
  24. package/dist/core/p5.Renderer.js +3 -3
  25. package/dist/core/p5.Renderer2D.js +6 -6
  26. package/dist/core/p5.Renderer3D.js +4 -4
  27. package/dist/core/rendering.js +4 -4
  28. package/dist/{creating_reading-ZXzcZEsb.js → creating_reading-C7hu6sg1.js} +2 -2
  29. package/dist/dom/dom.js +2 -2
  30. package/dist/dom/index.js +2 -2
  31. package/dist/dom/p5.Element.js +2 -2
  32. package/dist/dom/p5.MediaElement.js +2 -2
  33. package/dist/image/const.js +1 -1
  34. package/dist/image/filterRenderer2D.js +5 -5
  35. package/dist/image/image.js +4 -4
  36. package/dist/image/index.js +4 -4
  37. package/dist/image/loading_displaying.js +4 -4
  38. package/dist/image/p5.Image.js +3 -3
  39. package/dist/io/files.js +4 -4
  40. package/dist/io/index.js +4 -4
  41. package/dist/{ir_builders-DXNgaB9N.js → ir_builders-w12-GSxu.js} +37 -5
  42. package/dist/{main-DvN69W3f.js → main-E-swj5tF.js} +4 -4
  43. package/dist/math/Matrices/Matrix.js +1 -1
  44. package/dist/math/Matrices/MatrixNumjs.js +1 -1
  45. package/dist/math/index.js +1 -1
  46. package/dist/math/p5.Matrix.js +1 -1
  47. package/dist/math/p5.Vector.js +1 -1
  48. package/dist/math/trigonometry.js +1 -1
  49. package/dist/{p5.Renderer-D-5LdCRz.js → p5.Renderer-DOk9UW65.js} +2 -2
  50. package/dist/{rendering-h9unX5K0.js → rendering-B8V5Zt4k.js} +58 -14
  51. package/dist/shape/2d_primitives.js +1 -1
  52. package/dist/shape/attributes.js +1 -1
  53. package/dist/shape/custom_shapes.js +2 -2
  54. package/dist/shape/index.js +2 -2
  55. package/dist/strands/ir_builders.js +1 -1
  56. package/dist/strands/p5.strands.js +1 -1
  57. package/dist/strands/strands_api.js +91 -31
  58. package/dist/strands/strands_conditionals.js +1 -1
  59. package/dist/strands/strands_for.js +2 -2
  60. package/dist/strands/strands_node.js +1 -1
  61. package/dist/type/index.js +3 -3
  62. package/dist/type/p5.Font.js +3 -3
  63. package/dist/type/textCore.js +3 -3
  64. package/dist/webgl/3d_primitives.js +4 -4
  65. package/dist/webgl/GeometryBuilder.js +1 -1
  66. package/dist/webgl/ShapeBuilder.js +1 -1
  67. package/dist/webgl/enums.js +1 -1
  68. package/dist/webgl/index.js +5 -5
  69. package/dist/webgl/interaction.js +1 -1
  70. package/dist/webgl/light.js +4 -4
  71. package/dist/webgl/loading.js +12 -14
  72. package/dist/webgl/material.js +4 -4
  73. package/dist/webgl/p5.Camera.js +4 -4
  74. package/dist/webgl/p5.Framebuffer.js +4 -4
  75. package/dist/webgl/p5.Geometry.js +1 -1
  76. package/dist/webgl/p5.Quat.js +1 -1
  77. package/dist/webgl/p5.RendererGL.js +5 -5
  78. package/dist/webgl/p5.Texture.js +4 -4
  79. package/dist/webgl/strands_glslBackend.js +1 -1
  80. package/dist/webgl/text.js +5 -5
  81. package/dist/webgl/utils.js +4 -4
  82. package/dist/webgpu/index.js +2 -2
  83. package/dist/webgpu/p5.RendererWebGPU.js +13 -4
  84. package/dist/webgpu/strands_wgslBackend.js +5 -4
  85. package/lib/p5.esm.js +196 -62
  86. package/lib/p5.esm.min.js +1 -1
  87. package/lib/p5.js +196 -62
  88. package/lib/p5.min.js +1 -1
  89. package/lib/p5.webgpu.esm.js +51 -9
  90. package/lib/p5.webgpu.js +51 -9
  91. package/lib/p5.webgpu.min.js +1 -1
  92. package/package.json +1 -1
  93. package/types/global.d.ts +1453 -1383
  94. package/types/p5.d.ts +729 -694
@@ -1,5 +1,5 @@
1
1
  import rendererWebGPU from './p5.RendererWebGPU.js';
2
- import '../constants-DQyACdzq.js';
2
+ import '../constants-DAnmXDew.js';
3
3
  import '../webgl/enums.js';
4
4
  import '../strands/ir_types.js';
5
5
  import './shaders/color.js';
@@ -10,7 +10,7 @@ import './shaders/blit.js';
10
10
  import './strands_wgslBackend.js';
11
11
  import '../strands/ir_dag.js';
12
12
  import '../strands/strands_FES.js';
13
- import '../ir_builders-DXNgaB9N.js';
13
+ import '../ir_builders-w12-GSxu.js';
14
14
  import '../strands/ir_cfg.js';
15
15
  import '../strands/strands_builtins.js';
16
16
  import './shaders/functions/noise3DWGSL.js';
@@ -1,4 +1,4 @@
1
- import { W as WEBGPU, T as TRIANGLE_STRIP, L as LIGHTEST, D as DARKEST, S as SUBTRACT, R as REPLACE, E as EXCLUSION, a as SCREEN, M as MULTIPLY, b as REMOVE, A as ADD, B as BLEND, c as TRIANGLES, U as UNSIGNED_BYTE, F as FLOAT, H as HALF_FLOAT, d as UNSIGNED_INT, e as MIRROR, f as REPEAT, C as CLAMP, g as LINEAR, N as NEAREST } from '../constants-DQyACdzq.js';
1
+ import { W as WEBGPU, T as TRIANGLE_STRIP, L as LIGHTEST, D as DARKEST, S as SUBTRACT, R as REPLACE, E as EXCLUSION, a as SCREEN, M as MULTIPLY, b as REMOVE, A as ADD, B as BLEND, c as TRIANGLES, U as UNSIGNED_BYTE, F as FLOAT, H as HALF_FLOAT, d as UNSIGNED_INT, e as MIRROR, f as REPEAT, C as CLAMP, g as LINEAR, N as NEAREST } from '../constants-DAnmXDew.js';
2
2
  import { getStrokeDefs } from '../webgl/enums.js';
3
3
  import { DataType } from '../strands/ir_types.js';
4
4
  import { colorVertexShader, colorFragmentShader } from './shaders/color.js';
@@ -12,7 +12,7 @@ import { baseFilterVertexShader, baseFilterFragmentShader } from './shaders/filt
12
12
  import { imageLightVertexShader, imageLightDiffusedFragmentShader, imageLightSpecularFragmentShader } from './shaders/imageLight.js';
13
13
  import '../strands/ir_dag.js';
14
14
  import '../strands/strands_FES.js';
15
- import '../ir_builders-DXNgaB9N.js';
15
+ import '../ir_builders-w12-GSxu.js';
16
16
  import '../strands/ir_cfg.js';
17
17
  import '../strands/strands_builtins.js';
18
18
 
@@ -97,6 +97,10 @@ function rendererWebGPU(p5, fn) {
97
97
  this.finalCamera = new Camera(this);
98
98
  this.finalCamera._computeCameraDefaultSettings();
99
99
  this.finalCamera._setDefaultCamera();
100
+
101
+ this.depthFormat = 'depth24plus-stencil8';
102
+ this.depthTexture = null;
103
+ this.depthTextureView = null;
100
104
  }
101
105
 
102
106
  async setupContext() {
@@ -137,7 +141,6 @@ function rendererWebGPU(p5, fn) {
137
141
  });
138
142
 
139
143
  // TODO disablable stencil
140
- this.depthFormat = 'depth24plus-stencil8';
141
144
  this.mainFramebuffer = this.createFramebuffer({ _useCanvasFormat: true });
142
145
  this._updateSize();
143
146
  this._update();
@@ -194,6 +197,7 @@ function rendererWebGPU(p5, fn) {
194
197
  }
195
198
 
196
199
  _updateSize() {
200
+ if (!this.device || !this.depthFormat) return;
197
201
  if (this.depthTexture && this.depthTexture.destroy) {
198
202
  this.flushDraw();
199
203
  const textureToDestroy = this.depthTexture;
@@ -288,6 +292,7 @@ function rendererWebGPU(p5, fn) {
288
292
  }
289
293
 
290
294
  clear(...args) {
295
+ if (!this.device || !this.drawingContext) return;
291
296
  const _r = args[0] || 0;
292
297
  const _g = args[1] || 0;
293
298
  const _b = args[2] || 0;
@@ -354,6 +359,7 @@ function rendererWebGPU(p5, fn) {
354
359
  * occlude anything subsequently drawn.
355
360
  */
356
361
  clearDepth(depth = 1) {
362
+ if (!this.device || !this.depthTextureView) return;
357
363
  this._finishActiveRenderPass();
358
364
  const commandEncoder = this.device.createCommandEncoder();
359
365
 
@@ -1902,7 +1908,7 @@ function rendererWebGPU(p5, fn) {
1902
1908
 
1903
1909
  getNextBindingIndex({ vert, frag }, group = 0) {
1904
1910
  // Get the highest binding index in the specified group and return the next available
1905
- const samplerRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var\s+(\w+)\s*:\s*(texture_2d<f32>|sampler|uniform)/g;
1911
+ const samplerRegex = /@group\((\d+)\)\s*@binding\((\d+)\)\s*var(?:<uniform>)?\s+(\w+)\s*:\s*(texture_2d<f32>|sampler|uniform|\w+)/g;
1906
1912
  let maxBindingIndex = -1;
1907
1913
 
1908
1914
  for (const [src, visibility] of [
@@ -2252,6 +2258,9 @@ function rendererWebGPU(p5, fn) {
2252
2258
  // Inject hook uniforms as a separate struct at a new binding
2253
2259
  let hookUniformFields = '';
2254
2260
  for (const key in shader.hooks.uniforms) {
2261
+ // Skip textures, they don't get added to structs
2262
+ if (key.endsWith(': sampler2D')) continue;
2263
+
2255
2264
  // WGSL format: "name: type"
2256
2265
  hookUniformFields += ` ${key},\n`;
2257
2266
  }
@@ -1,7 +1,7 @@
1
1
  import { BaseType, DataType, NodeTypeToName, NodeType, OpCode, OpCodeToSymbol, isStructType, StatementType, BlockType } from '../strands/ir_types.js';
2
2
  import { getNodeDataFromID, extractNodeTypeInfo } from '../strands/ir_dag.js';
3
3
  import { internalError } from '../strands/strands_FES.js';
4
- import { v as variableNode, c as createStrandsNode, f as functionCallNode } from '../ir_builders-DXNgaB9N.js';
4
+ import { v as variableNode, c as createStrandsNode, f as functionCallNode } from '../ir_builders-w12-GSxu.js';
5
5
  import '../strands/ir_cfg.js';
6
6
  import '../strands/strands_builtins.js';
7
7
 
@@ -225,10 +225,11 @@ const wgslBackend = {
225
225
  return primitiveTypeName;
226
226
  },
227
227
  generateHookUniformKey(name, typeInfo) {
228
- // For sampler2D types, we don't add them to the uniform struct
229
- // Instead, they become separate texture and sampler bindings
228
+ // For sampler2D types, we don't add them to the uniform struct,
229
+ // but we still need them in the shader's hooks object so that
230
+ // they can be set by users.
230
231
  if (typeInfo.baseType === 'sampler2D') {
231
- return null; // Signal that this should not be added to uniform struct
232
+ return `${name}: sampler2D`; // Signal that this should not be added to uniform struct
232
233
  }
233
234
  return `${name}: ${this.getTypeName(typeInfo.baseType, typeInfo.dimension)}`;
234
235
  },
package/lib/p5.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! p5.js v2.2.1 February 11, 2026 */
1
+ /*! p5.js v2.2.2-rc.1 February 19, 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.2.1';
15
+ const VERSION = '2.2.2-rc.1';
16
16
 
17
17
  // GRAPHICS RENDERER
18
18
  /**
@@ -12611,11 +12611,11 @@ function creatingReading(p5, fn){
12611
12611
  * values. 0 is equal to the first color, 0.1 is very near the first color,
12612
12612
  * 0.5 is halfway between the two colors, and so on. Negative numbers are set
12613
12613
  * to 0. Numbers greater than 1 are set to 1. This differs from the behavior of
12614
- * <a href="#/lerp">lerp</a>. It's necessary because numbers outside of the
12614
+ * <a href="#/p5/lerp">lerp</a>. It's necessary because numbers outside of the
12615
12615
  * interval [0, 1] will produce strange and unexpected colors.
12616
12616
  *
12617
12617
  * The way that colors are interpolated depends on the current
12618
- * <a href="#/colorMode">colorMode()</a>.
12618
+ * <a href="#/p5/colorMode">colorMode()</a>.
12619
12619
  *
12620
12620
  * @method lerpColor
12621
12621
  * @param {p5.Color} c1 interpolate from this color.
@@ -61237,6 +61237,40 @@ function material(p5, fn) {
61237
61237
  * }
61238
61238
  * ```
61239
61239
  *
61240
+ * We can use the `noise()` function built into strands to generate a color for each pixel. (Again no need here for underlying content for the filter to operate on.) Again we'll animate by passing in an announced uniform variable `time` with `setUniform()`, each frame.
61241
+ *
61242
+ * ```js example
61243
+ * let myFilter;
61244
+ *
61245
+ * function setup() {
61246
+ * createCanvas(100, 100, WEBGL);
61247
+ * myFilter = buildFilterShader(noiseShaderCallback);
61248
+ * describe('Evolving animated cloud-like noise in cyan and magenta');
61249
+ * }
61250
+ *
61251
+ * function noiseShaderCallback() {
61252
+ * let time = uniformFloat();
61253
+ * filterColor.begin();
61254
+ * let coord = filterColor.texCoord;
61255
+ *
61256
+ * //generate a value roughly between 0 and 1
61257
+ * let noiseVal = noise(coord.x, coord.y, time / 2000);
61258
+ *
61259
+ * let result = mix(
61260
+ * [1, 0, 1, 1], // Magenta
61261
+ * [0, 1, 1, 1], // Cyan
61262
+ * noiseVal
61263
+ * );
61264
+ * filterColor.set(result);
61265
+ * filterColor.end();
61266
+ * }
61267
+ *
61268
+ * function draw() {
61269
+ * myFilter.setUniform("time", millis());
61270
+ * filter(myFilter);
61271
+ * }
61272
+ * ```
61273
+ *
61240
61274
  * Like the `modify()` method on shaders,
61241
61275
  * advanced users can also fill in `filterColor` using <a href="https://developer.mozilla.org/en-US/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders" target="_blank">GLSL</a>
61242
61276
  * instead of JavaScript.
@@ -61251,15 +61285,17 @@ function material(p5, fn) {
61251
61285
  * @beta
61252
61286
  * @submodule p5.strands
61253
61287
  * @param {Function} callback A function building a p5.strands shader.
61288
+ * @param {Object} [scope] An optional scope object passed to .modify().
61254
61289
  * @returns {p5.Shader} The material shader
61255
61290
  */
61256
61291
  /**
61257
61292
  * @method buildFilterShader
61258
61293
  * @param {Object} hooks An object specifying p5.strands hooks in GLSL.
61294
+ * @param {Object} [scope] An optional scope object passed to .modify().
61259
61295
  * @returns {p5.Shader} The material shader
61260
61296
  */
61261
- fn.buildFilterShader = function (callback) {
61262
- return this.baseFilterShader().modify(callback);
61297
+ fn.buildFilterShader = function (callback, scope) {
61298
+ return this.baseFilterShader().modify(callback, scope);
61263
61299
  };
61264
61300
 
61265
61301
  /**
@@ -62079,15 +62115,17 @@ function material(p5, fn) {
62079
62115
  * @submodule p5.strands
62080
62116
  * @beta
62081
62117
  * @param {Function} callback A function building a p5.strands shader.
62118
+ * @param {Object} [scope] An optional scope object passed to .modify().
62082
62119
  * @returns {p5.Shader} The material shader.
62083
62120
  */
62084
62121
  /**
62085
62122
  * @method buildMaterialShader
62086
62123
  * @param {Object} hooks An object specifying p5.strands hooks in GLSL.
62124
+ * @param {Object} [scope] An optional scope object passed to .modify().
62087
62125
  * @returns {p5.Shader} The material shader.
62088
62126
  */
62089
- fn.buildMaterialShader = function (cb) {
62090
- return this.baseMaterialShader().modify(cb);
62127
+ fn.buildMaterialShader = function (cb, scope) {
62128
+ return this.baseMaterialShader().modify(cb, scope);
62091
62129
  };
62092
62130
 
62093
62131
  /**
@@ -62186,7 +62224,7 @@ function material(p5, fn) {
62186
62224
  /**
62187
62225
  * Returns the base shader used for filters.
62188
62226
  *
62189
- * Calling <a href="#/p5/buildMaterialShader">`buildFilterShader(shaderFunction)`</a>
62227
+ * Calling <a href="#/p5/buildFilterShader">`buildFilterShader(shaderFunction)`</a>
62190
62228
  * is equivalent to calling `baseFilterShader().modify(shaderFunction)`.
62191
62229
  *
62192
62230
  * Read <a href="#/p5/buildFilterShader">the `buildFilterShader` reference</a> or
@@ -62295,15 +62333,17 @@ function material(p5, fn) {
62295
62333
  * @submodule p5.strands
62296
62334
  * @beta
62297
62335
  * @param {Function} callback A function building a p5.strands shader.
62336
+ * @param {Object} [scope] An optional scope object passed to .modify().
62298
62337
  * @returns {p5.Shader} The normal shader.
62299
62338
  */
62300
62339
  /**
62301
62340
  * @method buildNormalShader
62302
62341
  * @param {Object} hooks An object specifying p5.strands hooks in GLSL.
62342
+ * @param {Object} [scope] An optional scope object passed to .modify().
62303
62343
  * @returns {p5.Shader} The normal shader.
62304
62344
  */
62305
- fn.buildNormalShader = function (cb) {
62306
- return this.baseNormalShader().modify(cb);
62345
+ fn.buildNormalShader = function (cb, scope) {
62346
+ return this.baseNormalShader().modify(cb, scope);
62307
62347
  };
62308
62348
 
62309
62349
  /**
@@ -62459,15 +62499,17 @@ function material(p5, fn) {
62459
62499
  * @submodule p5.strands
62460
62500
  * @beta
62461
62501
  * @param {Function} callback A function building a p5.strands shader.
62502
+ * @param {Object} [scope] An optional scope object passed to .modify().
62462
62503
  * @returns {p5.Shader} The color shader.
62463
62504
  */
62464
62505
  /**
62465
62506
  * @method buildColorShader
62466
62507
  * @param {Object} hooks An object specifying p5.strands hooks in GLSL.
62508
+ * @param {Object} [scope] An optional scope object passed to .modify().
62467
62509
  * @returns {p5.Shader} The color shader.
62468
62510
  */
62469
- fn.buildColorShader = function (cb) {
62470
- return this.baseColorShader().modify(cb);
62511
+ fn.buildColorShader = function (cb, scope) {
62512
+ return this.baseColorShader().modify(cb, scope);
62471
62513
  };
62472
62514
 
62473
62515
  /**
@@ -62714,15 +62756,17 @@ function material(p5, fn) {
62714
62756
  * @submodule p5.strands
62715
62757
  * @beta
62716
62758
  * @param {Function} callback A function building a p5.strands shader.
62759
+ * @param {Object} [scope] An optional scope object passed to .modify().
62717
62760
  * @returns {p5.Shader} The stroke shader.
62718
62761
  */
62719
62762
  /**
62720
62763
  * @method buildStrokeShader
62721
62764
  * @param {Object} hooks An object specifying p5.strands hooks in GLSL.
62765
+ * @param {Object} [scope] An optional scope object passed to .modify().
62722
62766
  * @returns {p5.Shader} The stroke shader.
62723
62767
  */
62724
- fn.buildStrokeShader = function (cb) {
62725
- return this.baseStrokeShader().modify(cb);
62768
+ fn.buildStrokeShader = function (cb, scope) {
62769
+ return this.baseStrokeShader().modify(cb, scope);
62726
62770
  };
62727
62771
 
62728
62772
  /**
@@ -69201,7 +69245,7 @@ class StrandsNode {
69201
69245
  const baseType = orig?.baseType ?? BaseType.FLOAT;
69202
69246
 
69203
69247
  let newValueID;
69204
- if (value instanceof StrandsNode) {
69248
+ if (value?.isStrandsNode) {
69205
69249
  newValueID = value.id;
69206
69250
  } else {
69207
69251
  const newVal = primitiveConstructorNode(
@@ -69256,7 +69300,7 @@ class StrandsNode {
69256
69300
  const baseType = orig?.baseType ?? BaseType.FLOAT;
69257
69301
 
69258
69302
  let newValueID;
69259
- if (value instanceof StrandsNode) {
69303
+ if (value?.isStrandsNode) {
69260
69304
  newValueID = value.id;
69261
69305
  } else {
69262
69306
  const newVal = primitiveConstructorNode(
@@ -69486,7 +69530,7 @@ function unaryOpNode(strandsContext, nodeOrValue, opCode) {
69486
69530
  const { dag, cfg } = strandsContext;
69487
69531
  let dependsOn;
69488
69532
  let node;
69489
- if (nodeOrValue instanceof StrandsNode) {
69533
+ if (nodeOrValue?.isStrandsNode) {
69490
69534
  node = nodeOrValue;
69491
69535
  } else {
69492
69536
  const { id, dimension } = primitiveConstructorNode(strandsContext, { baseType: BaseType.FLOAT, dimension: null }, nodeOrValue);
@@ -69686,6 +69730,20 @@ function constructTypeFromIDs(strandsContext, typeInfo, strandsNodesArray) {
69686
69730
 
69687
69731
  function primitiveConstructorNode(strandsContext, typeInfo, dependsOn) {
69688
69732
  const cfg = strandsContext.cfg;
69733
+ dependsOn = (Array.isArray(dependsOn) ? dependsOn : [dependsOn])
69734
+ .flat(Infinity)
69735
+ .map(a => {
69736
+ if (
69737
+ a.isStrandsNode &&
69738
+ a.typeInfo().baseType === BaseType.INT &&
69739
+ // TODO: handle ivec inputs instead of just int scalars
69740
+ a.typeInfo().dimension === 1
69741
+ ) {
69742
+ return castToFloat(strandsContext, a);
69743
+ } else {
69744
+ return a;
69745
+ }
69746
+ });
69689
69747
  const { mappedDependencies, inferredTypeInfo } = mapPrimitiveDepsToIDs(strandsContext, typeInfo, dependsOn);
69690
69748
 
69691
69749
  const finalType = {
@@ -69701,6 +69759,24 @@ function primitiveConstructorNode(strandsContext, typeInfo, dependsOn) {
69701
69759
  return { id, dimension: finalType.dimension, components: mappedDependencies };
69702
69760
  }
69703
69761
 
69762
+ function castToFloat(strandsContext, dep) {
69763
+ const { id, dimension } = functionCallNode(
69764
+ strandsContext,
69765
+ strandsContext.backend.getTypeName('float', dep.typeInfo().dimension),
69766
+ [dep],
69767
+ {
69768
+ overloads: [{
69769
+ params: [dep.typeInfo()],
69770
+ returnType: {
69771
+ ...dep.typeInfo(),
69772
+ baseType: BaseType.FLOAT,
69773
+ },
69774
+ }],
69775
+ }
69776
+ );
69777
+ return createStrandsNode(id, dimension, strandsContext);
69778
+ }
69779
+
69704
69780
  function structConstructorNode(strandsContext, structTypeInfo, rawUserArgs) {
69705
69781
  const { cfg, dag } = strandsContext;
69706
69782
  const { properties } = structTypeInfo;
@@ -69920,7 +69996,7 @@ function swizzleTrap(id, dimension, strandsContext, onRebind) {
69920
69996
  // This may not be the most efficient way, as we swizzle each component individually,
69921
69997
  // so that .xyz becomes .x, .y, .z
69922
69998
  let scalars = [];
69923
- if (value instanceof StrandsNode) {
69999
+ if (value?.isStrandsNode) {
69924
70000
  if (value.dimension === 1) {
69925
70001
  scalars = Array(chars.length).fill(value);
69926
70002
  } else if (value.dimension === chars.length) {
@@ -96586,7 +96662,7 @@ const verifierUtils = {
96586
96662
 
96587
96663
  try {
96588
96664
  const ast = parse(code, {
96589
- ecmaVersion: 2021,
96665
+ ecmaVersion: 'latest',
96590
96666
  sourceType: 'module',
96591
96667
  locations: true // This helps us get the line number.
96592
96668
  });
@@ -110019,12 +110095,10 @@ function loading$1(p5, fn){
110019
110095
  const vertString = tokens[vertexTokens[tokenInd]];
110020
110096
  let vertParts = vertString.split('/');
110021
110097
 
110022
- // TODO: Faces can technically use negative numbers to refer to the
110023
- // previous nth vertex. I haven't seen this used in practice, but
110024
- // it might be good to implement this in the future.
110025
-
110026
110098
  for (let i = 0; i < vertParts.length; i++) {
110027
- vertParts[i] = parseInt(vertParts[i]) - 1;
110099
+ let index = parseInt(vertParts[i]);
110100
+ if (index > 0) index -= 1; // OBJ uses 1-based indexing
110101
+ vertParts[i] = index;
110028
110102
  }
110029
110103
 
110030
110104
  if (!usedVerts[vertString]) {
@@ -110033,11 +110107,11 @@ function loading$1(p5, fn){
110033
110107
 
110034
110108
  if (usedVerts[vertString][currentMaterial] === undefined) {
110035
110109
  const vertIndex = model.vertices.length;
110036
- model.vertices.push(loadedVerts.v[vertParts[0]].copy());
110037
- model.uvs.push(loadedVerts.vt[vertParts[1]] ?
110038
- loadedVerts.vt[vertParts[1]].slice() : [0, 0]);
110039
- model.vertexNormals.push(loadedVerts.vn[vertParts[2]] ?
110040
- loadedVerts.vn[vertParts[2]].copy() : new Vector());
110110
+ model.vertices.push(loadedVerts.v.at(vertParts[0]).copy());
110111
+ model.uvs.push(loadedVerts.vt.at(vertParts[1]) ?
110112
+ loadedVerts.vt.at(vertParts[1]).slice() : [0, 0]);
110113
+ model.vertexNormals.push(loadedVerts.vn.at(vertParts[2]) ?
110114
+ loadedVerts.vn.at(vertParts[2]).copy() : new Vector());
110041
110115
 
110042
110116
  usedVerts[vertString][currentMaterial] = vertIndex;
110043
110117
  face.push(vertIndex);
@@ -120917,7 +120991,7 @@ function text(p5, fn) {
120917
120991
 
120918
120992
  if (!p5.Font.hasGlyphData(this.states.textFont)) {
120919
120993
  console.log(
120920
- 'WEBGL: only Opentype (.otf) and Truetype (.ttf) fonts with glyph data are supported'
120994
+ 'WEBGL: only Opentype (.otf) and Truetype (.ttf) fonts with glyph data are supported. Make sure to set the font using textFont() before drawing text.'
120921
120995
  );
120922
120996
  return;
120923
120997
  }
@@ -132149,7 +132223,7 @@ class StrandsFor {
132149
132223
  let initialVar = this.initialCb();
132150
132224
 
132151
132225
  // Convert to StrandsNode if it's not already one
132152
- if (!(initialVar instanceof StrandsNode)) {
132226
+ if (!(initialVar?.isStrandsNode)) {
132153
132227
  const { id, dimension } = primitiveConstructorNode(this.strandsContext, { baseType: BaseType.FLOAT, dimension: 1 }, initialVar);
132154
132228
  initialVar = createStrandsNode(id, dimension, this.strandsContext);
132155
132229
  }
@@ -132308,32 +132382,45 @@ function _getBuiltinGlobalsCache(strandsContext) {
132308
132382
  return strandsContext._builtinGlobals
132309
132383
  }
132310
132384
 
132311
- function getBuiltinGlobalNode(strandsContext, name) {
132312
- const spec = BUILTIN_GLOBAL_SPECS[name];
132313
- if (!spec) return null
132314
-
132385
+ function getOrCreateUniformNode(strandsContext, uniformName, typeInfo, defaultValueFn) {
132315
132386
  const cache = _getBuiltinGlobalsCache(strandsContext);
132316
- const uniformName = `_p5_global_${name}`;
132387
+
132317
132388
  const cached = cache.nodes.get(uniformName);
132318
- if (cached) return cached
132389
+ if (cached) return cached;
132319
132390
 
132320
132391
  if (!cache.uniformsAdded.has(uniformName)) {
132321
132392
  cache.uniformsAdded.add(uniformName);
132322
132393
  strandsContext.uniforms.push({
132323
132394
  name: uniformName,
132324
- typeInfo: spec.typeInfo,
132325
- defaultValue: () => {
132326
- const p5Instance = strandsContext.renderer?._pInst || strandsContext.p5?.instance;
132327
- return p5Instance ? spec.get(p5Instance) : undefined
132328
- },
132395
+ typeInfo,
132396
+ defaultValue: defaultValueFn,
132329
132397
  });
132330
132398
  }
132331
132399
 
132332
- const { id, dimension } = variableNode(strandsContext, spec.typeInfo, uniformName);
132400
+ const { id, dimension } = variableNode(strandsContext, typeInfo, uniformName);
132333
132401
  const node = createStrandsNode(id, dimension, strandsContext);
132334
- node._originalBuiltinName = name;
132335
132402
  cache.nodes.set(uniformName, node);
132336
- return node
132403
+ return node;
132404
+ }
132405
+
132406
+ function getBuiltinGlobalNode(strandsContext, name) {
132407
+ const spec = BUILTIN_GLOBAL_SPECS[name];
132408
+ if (!spec) return null;
132409
+
132410
+ const uniformName = `_p5_global_${name}`;
132411
+ const instance = strandsContext.renderer?._pInst || strandsContext.p5?.instance;
132412
+
132413
+ const node = getOrCreateUniformNode(
132414
+ strandsContext,
132415
+ uniformName,
132416
+ spec.typeInfo,
132417
+ () => {
132418
+ return instance ? spec.get(instance) : undefined;
132419
+ }
132420
+ );
132421
+
132422
+ node._originalBuiltinName = name;
132423
+ return node;
132337
132424
  }
132338
132425
 
132339
132426
  function installBuiltinGlobalAccessors(strandsContext) {
@@ -132409,7 +132496,7 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
132409
132496
  }
132410
132497
 
132411
132498
  // Convert value to a StrandsNode if it isn't already
132412
- const valueNode = value instanceof StrandsNode ? value : p5.strandsNode(value);
132499
+ const valueNode = value?.isStrandsNode ? value : p5.strandsNode(value);
132413
132500
 
132414
132501
  // Create a new CFG block for the early return
132415
132502
  const earlyReturnBlockID = createBasicBlock(cfg, BlockType.DEFAULT);
@@ -132496,6 +132583,7 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
132496
132583
  // Add noise function with backend-agnostic implementation
132497
132584
  const originalNoise = fn.noise;
132498
132585
  const originalNoiseDetail = fn.noiseDetail;
132586
+ const originalMillis = fn.millis;
132499
132587
 
132500
132588
  strandsContext._noiseOctaves = null;
132501
132589
  strandsContext._noiseAmpFalloff = null;
@@ -132558,6 +132646,21 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
132558
132646
  return createStrandsNode(id, dimension, strandsContext);
132559
132647
  };
132560
132648
 
132649
+ fn.millis = function (...args) {
132650
+ if (!strandsContext.active) {
132651
+ return originalMillis.apply(this, args);
132652
+ }
132653
+ const instance = strandsContext.renderer?._pInst || strandsContext.p5?.instance;
132654
+ return getOrCreateUniformNode(
132655
+ strandsContext,
132656
+ '_p5_global_millis',
132657
+ DataType.float1,
132658
+ () => {
132659
+ return instance ? instance.millis() : undefined;
132660
+ }
132661
+ );
132662
+ };
132663
+
132561
132664
  // Next is type constructors and uniform functions.
132562
132665
  // For some of them, we have aliases so that you can write either a more human-readable
132563
132666
  // variant or also one more directly translated from GLSL, or to be more compatible with
@@ -132624,12 +132727,17 @@ function initGlobalStrandsAPI(p5, fn, strandsContext) {
132624
132727
  fn[typeInfo.fnName] = function(...args) {
132625
132728
  if (strandsContext.active) {
132626
132729
  if (args.length === 1 && args[0].dimension && args[0].dimension === typeInfo.dimension) {
132627
- const { id, dimension } = functionCallNode(strandsContext, typeInfo.fnName, args, {
132628
- overloads: [{
132629
- params: [args[0].typeInfo()],
132630
- returnType: typeInfo,
132631
- }]
132632
- });
132730
+ const { id, dimension } = functionCallNode(
132731
+ strandsContext,
132732
+ strandsContext.backend.getTypeName(typeInfo.baseType, typeInfo.dimension),
132733
+ args,
132734
+ {
132735
+ overloads: [{
132736
+ params: [args[0].typeInfo()],
132737
+ returnType: typeInfo,
132738
+ }]
132739
+ }
132740
+ );
132633
132741
  return createStrandsNode(id, dimension, strandsContext);
132634
132742
  } else {
132635
132743
  // For vector types with a single argument, repeat it for each component
@@ -132686,7 +132794,7 @@ function createHookArguments(strandsContext, parameters){
132686
132794
  const oldDependsOn = dag.dependsOn[structNode.id];
132687
132795
  const newDependsOn = [...oldDependsOn];
132688
132796
  let newValueID;
132689
- if (val instanceof StrandsNode) {
132797
+ if (val?.isStrandsNode) {
132690
132798
  newValueID = val.id;
132691
132799
  }
132692
132800
  else {
@@ -132718,7 +132826,7 @@ function createHookArguments(strandsContext, parameters){
132718
132826
  return args;
132719
132827
  }
132720
132828
  function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName) {
132721
- if (!(returned instanceof StrandsNode)) {
132829
+ if (!(returned?.isStrandsNode)) {
132722
132830
  // try {
132723
132831
  const result = primitiveConstructorNode(strandsContext, expectedType, returned);
132724
132832
  return result.id;
@@ -132740,7 +132848,12 @@ function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName
132740
132848
  };
132741
132849
  if (receivedType.dimension !== expectedType.dimension) {
132742
132850
  if (receivedType.dimension !== 1) {
132743
- userError('type error', `You have returned a vector with ${receivedType.dimension} components in ${hookName} when a ${expectedType.baseType + expectedType.dimension} was expected!`);
132851
+ const receivedTypeDisplay = receivedType.baseType + (receivedType.dimension > 1 ? receivedType.dimension : '');
132852
+ const expectedTypeDisplay = expectedType.baseType + expectedType.dimension;
132853
+ userError('type error',
132854
+ `You have returned a ${receivedTypeDisplay} in ${hookName} when a ${expectedTypeDisplay} was expected!\n\n` +
132855
+ `Make sure your hook returns the correct type.`
132856
+ );
132744
132857
  }
132745
132858
  else {
132746
132859
  const result = primitiveConstructorNode(strandsContext, expectedType, returned);
@@ -132827,10 +132940,27 @@ function createShaderHooksFunctions(strandsContext, fn, shader) {
132827
132940
  const handleRetVal = (retNode) => {
132828
132941
  if(isStructType(expectedReturnType)) {
132829
132942
  const expectedStructType = structType(expectedReturnType);
132830
- if (retNode instanceof StrandsNode) {
132943
+ if (retNode?.isStrandsNode) {
132831
132944
  const returnedNode = getNodeDataFromID(strandsContext.dag, retNode.id);
132832
132945
  if (returnedNode.baseType !== expectedStructType.typeName) {
132833
- userError("type error", `You have returned a ${retNode.baseType} from ${hookType.name} when a ${expectedStructType.typeName} was expected.`);
132946
+ const receivedTypeName = returnedNode.baseType || 'undefined';
132947
+ const receivedDim = dag.dimensions[retNode.id];
132948
+ const receivedTypeDisplay = receivedDim > 1 ?
132949
+ `${receivedTypeName}${receivedDim}` : receivedTypeName;
132950
+
132951
+ const expectedProps = expectedStructType.properties
132952
+ .map(p => p.name).join(', ');
132953
+ userError('type error',
132954
+ `You have returned a ${receivedTypeDisplay} from ${hookType.name} when a ${expectedStructType.typeName} was expected.\n\n` +
132955
+ `The ${expectedStructType.typeName} struct has these properties: { ${expectedProps} }\n\n` +
132956
+ `Instead of returning a different type, you should modify and return the ${expectedStructType.typeName} struct that was passed to your hook.\n\n` +
132957
+ `For example:\n` +
132958
+ `${hookType.name}((inputs) => {\n` +
132959
+ ` // Modify properties of inputs\n` +
132960
+ ` inputs.someProperty = ...;\n` +
132961
+ ` return inputs; // Return the modified struct\n` +
132962
+ `})`
132963
+ );
132834
132964
  }
132835
132965
  const newDeps = returnedNode.dependsOn.slice();
132836
132966
  for (let i = 0; i < expectedStructType.properties.length; i++) {
@@ -132849,10 +132979,14 @@ function createShaderHooksFunctions(strandsContext, fn, shader) {
132849
132979
  const propName = expectedProp.name;
132850
132980
  const receivedValue = retNode[propName];
132851
132981
  if (receivedValue === undefined) {
132852
- userError('type error', `You've returned an incomplete struct from ${hookType.name}.\n` +
132853
- `Expected: { ${expectedReturnType.properties.map(p => p.name).join(', ')} }\n` +
132854
- `Received: { ${Object.keys(retNode).join(', ')} }\n` +
132855
- `All of the properties are required!`);
132982
+ const expectedProps = expectedReturnType.properties.map(p => p.name).join(', ');
132983
+ const receivedProps = Object.keys(retNode).join(', ');
132984
+ userError('type error',
132985
+ `You've returned an incomplete ${expectedStructType.typeName} struct from ${hookType.name}.\n\n` +
132986
+ `Expected properties: { ${expectedProps} }\n` +
132987
+ `Received properties: { ${receivedProps} }\n\n` +
132988
+ `All properties are required! Make sure to include all properties in the returned struct.`
132989
+ );
132856
132990
  }
132857
132991
  const expectedTypeInfo = expectedProp.dataType;
132858
132992
  const returnedPropID = enforceReturnTypeMatch(strandsContext, expectedTypeInfo, receivedValue, hookType.name);