p5 2.2.0-rc.3 → 2.2.0-rc.4

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 (85) hide show
  1. package/dist/accessibility/color_namer.js +4 -4
  2. package/dist/accessibility/index.js +4 -4
  3. package/dist/app.js +4 -4
  4. package/dist/color/color_conversion.js +4 -4
  5. package/dist/color/index.js +1 -1
  6. package/dist/color/setting.js +1 -1
  7. package/dist/{constants-J9QlHaWW.js → constants-DAarZ_Jd.js} +1 -1
  8. package/dist/core/constants.js +1 -1
  9. package/dist/core/environment.js +1 -1
  10. package/dist/core/filterShaders.js +20 -14
  11. package/dist/core/friendly_errors/fes_core.js +1 -1
  12. package/dist/core/friendly_errors/file_errors.js +1 -1
  13. package/dist/core/friendly_errors/index.js +1 -1
  14. package/dist/core/friendly_errors/param_validator.js +1 -1
  15. package/dist/core/friendly_errors/sketch_verifier.js +1 -1
  16. package/dist/core/helpers.js +1 -1
  17. package/dist/core/init.js +4 -4
  18. package/dist/core/internationalization.js +1 -1
  19. package/dist/core/legacy.js +4 -4
  20. package/dist/core/main.js +4 -4
  21. package/dist/core/p5.Graphics.js +3 -3
  22. package/dist/core/p5.Renderer.js +2 -2
  23. package/dist/core/p5.Renderer2D.js +4 -4
  24. package/dist/core/p5.Renderer3D.js +3 -3
  25. package/dist/core/rendering.js +3 -3
  26. package/dist/dom/dom.js +1 -1
  27. package/dist/dom/index.js +1 -1
  28. package/dist/dom/p5.Element.js +1 -1
  29. package/dist/dom/p5.MediaElement.js +1 -1
  30. package/dist/image/const.js +1 -1
  31. package/dist/image/filterRenderer2D.js +3 -3
  32. package/dist/image/image.js +3 -3
  33. package/dist/image/index.js +3 -3
  34. package/dist/image/loading_displaying.js +3 -3
  35. package/dist/image/p5.Image.js +2 -2
  36. package/dist/io/files.js +3 -3
  37. package/dist/io/index.js +3 -3
  38. package/dist/{main-YIcP6qW5.js → main-CyTEP1c4.js} +3 -3
  39. package/dist/math/Matrices/Matrix.js +1 -1
  40. package/dist/math/Matrices/MatrixNumjs.js +1 -1
  41. package/dist/math/index.js +1 -1
  42. package/dist/math/p5.Matrix.js +1 -1
  43. package/dist/math/p5.Vector.js +1 -1
  44. package/dist/math/trigonometry.js +1 -1
  45. package/dist/{p5.Renderer-CQfJjPpD.js → p5.Renderer-Ds3PCIgb.js} +1 -1
  46. package/dist/{rendering-IbeJmcSe.js → rendering-DPysJ2_4.js} +30 -9
  47. package/dist/shape/2d_primitives.js +1 -1
  48. package/dist/shape/attributes.js +1 -1
  49. package/dist/shape/custom_shapes.js +1 -1
  50. package/dist/shape/index.js +1 -1
  51. package/dist/strands/p5.strands.js +4 -2
  52. package/dist/strands/strands_for.js +1 -1
  53. package/dist/strands/strands_transpiler.js +218 -102
  54. package/dist/type/index.js +2 -2
  55. package/dist/type/p5.Font.js +2 -2
  56. package/dist/type/textCore.js +2 -2
  57. package/dist/webgl/3d_primitives.js +3 -3
  58. package/dist/webgl/GeometryBuilder.js +1 -1
  59. package/dist/webgl/ShapeBuilder.js +1 -1
  60. package/dist/webgl/enums.js +1 -1
  61. package/dist/webgl/index.js +3 -3
  62. package/dist/webgl/interaction.js +1 -1
  63. package/dist/webgl/light.js +3 -3
  64. package/dist/webgl/loading.js +3 -3
  65. package/dist/webgl/material.js +3 -3
  66. package/dist/webgl/p5.Camera.js +3 -3
  67. package/dist/webgl/p5.Framebuffer.js +3 -3
  68. package/dist/webgl/p5.Geometry.js +1 -1
  69. package/dist/webgl/p5.Quat.js +1 -1
  70. package/dist/webgl/p5.RendererGL.js +3 -3
  71. package/dist/webgl/p5.Shader.js +10 -3
  72. package/dist/webgl/p5.Texture.js +3 -3
  73. package/dist/webgl/text.js +3 -3
  74. package/dist/webgl/utils.js +3 -3
  75. package/dist/webgpu/index.js +1 -1
  76. package/dist/webgpu/p5.RendererWebGPU.js +1 -1
  77. package/lib/p5.esm.js +282 -130
  78. package/lib/p5.esm.min.js +1 -1
  79. package/lib/p5.js +282 -130
  80. package/lib/p5.min.js +1 -1
  81. package/lib/p5.webgpu.esm.js +1 -1
  82. package/lib/p5.webgpu.js +1 -1
  83. package/lib/p5.webgpu.min.js +1 -1
  84. package/package.json +1 -1
  85. package/types/p5.d.ts +10 -3
@@ -1,7 +1,7 @@
1
1
  import nj from '@d4c/numjs/build/module/numjs.min.js';
2
2
  import { Vector } from '../p5.Vector.js';
3
3
  import { MatrixInterface } from './MatrixInterface.js';
4
- import '../../constants-J9QlHaWW.js';
4
+ import '../../constants-DAarZ_Jd.js';
5
5
 
6
6
  let isMatrixArray = x => Array.isArray(x);
7
7
  if (typeof Float32Array !== 'undefined') {
@@ -4,7 +4,7 @@ import random from './random.js';
4
4
  import trigonometry from './trigonometry.js';
5
5
  import math$1 from './math.js';
6
6
  import vector from './p5.Vector.js';
7
- import '../constants-J9QlHaWW.js';
7
+ import '../constants-DAarZ_Jd.js';
8
8
 
9
9
  function math(p5){
10
10
  p5.registerAddon(calculation);
@@ -1,6 +1,6 @@
1
1
  import { Matrix } from './Matrices/Matrix.js';
2
2
  import './p5.Vector.js';
3
- import '../constants-J9QlHaWW.js';
3
+ import '../constants-DAarZ_Jd.js';
4
4
  import './Matrices/MatrixInterface.js';
5
5
 
6
6
  /**
@@ -1,4 +1,4 @@
1
- import { q as TWO_PI } from '../constants-J9QlHaWW.js';
1
+ import { q as TWO_PI } from '../constants-DAarZ_Jd.js';
2
2
 
3
3
  /**
4
4
  * @module Math
@@ -1,4 +1,4 @@
1
- import { a5 as RAD_TO_DEG, a6 as DEG_TO_RAD } from '../constants-J9QlHaWW.js';
1
+ import { a5 as RAD_TO_DEG, a6 as DEG_TO_RAD } from '../constants-DAarZ_Jd.js';
2
2
 
3
3
  /**
4
4
  * @module Math
@@ -1,5 +1,5 @@
1
1
  import { C as Color } from './creating_reading-p2iQtNm5.js';
2
- import { a2 as NORMAL, aA as WORD, ax as BASELINE, av as LEFT, v as CENTER, p as CORNER, J as INCLUDE } from './constants-J9QlHaWW.js';
2
+ import { a2 as NORMAL, aA as WORD, ax as BASELINE, av as LEFT, v as CENTER, p as CORNER, J as INCLUDE } from './constants-DAarZ_Jd.js';
3
3
  import Filters from './image/filters.js';
4
4
  import { Vector } from './math/p5.Vector.js';
5
5
  import { Shape } from './shape/custom_shapes.js';
@@ -1,7 +1,7 @@
1
- import { p as CORNER, t as CORNERS, v as CENTER, aB as COVER, aC as CONTAIN, a9 as RIGHT, aw as BOTTOM, B as BLEND, aD as FILL, ad as IMAGE, C as CLAMP, w as ROUND, Y as LINES, X as POINTS, c as TRIANGLES, ab as BLUR, D as DARKEST, L as LIGHTEST, A as ADD, S as SUBTRACT, a as SCREEN, E as EXCLUSION, R as REPLACE, M as MULTIPLY, b as REMOVE, as as BURN, ao as OVERLAY, ap as HARD_LIGHT, aq as SOFT_LIGHT, ar as DODGE, d as UNSIGNED_INT, U as UNSIGNED_BYTE, av as LEFT, ax as BASELINE, ay as TOP, aE as SIMPLE, aF as FULL, q as TWO_PI, Q as OPEN, a2 as NORMAL, V as CLOSE, at as PIE, au as CHORD, h as TEXTURE, P as P2D, g as LINEAR, aa as WEBGL2, N as NEAREST, aG as LINEAR_MIPMAP, f as REPEAT, e as MIRROR, F as FLOAT, ac as WEBGL, H as HALF_FLOAT } from './constants-J9QlHaWW.js';
1
+ import { p as CORNER, t as CORNERS, v as CENTER, aB as COVER, aC as CONTAIN, a9 as RIGHT, aw as BOTTOM, B as BLEND, aD as FILL, ad as IMAGE, C as CLAMP, w as ROUND, Y as LINES, X as POINTS, c as TRIANGLES, ab as BLUR, D as DARKEST, L as LIGHTEST, A as ADD, S as SUBTRACT, a as SCREEN, E as EXCLUSION, R as REPLACE, M as MULTIPLY, b as REMOVE, as as BURN, ao as OVERLAY, ap as HARD_LIGHT, aq as SOFT_LIGHT, ar as DODGE, d as UNSIGNED_INT, U as UNSIGNED_BYTE, av as LEFT, ax as BASELINE, ay as TOP, aE as SIMPLE, aF as FULL, q as TWO_PI, Q as OPEN, a2 as NORMAL, V as CLOSE, at as PIE, au as CHORD, h as TEXTURE, P as P2D, g as LINEAR, aa as WEBGL2, N as NEAREST, aG as LINEAR_MIPMAP, f as REPEAT, e as MIRROR, F as FLOAT, ac as WEBGL, H as HALF_FLOAT } from './constants-DAarZ_Jd.js';
2
2
  import { C as Color, c as creatingReading, h as RGBA, R as RGB } from './creating_reading-p2iQtNm5.js';
3
3
  import { Element } from './dom/p5.Element.js';
4
- import { R as Renderer, I as Image } from './p5.Renderer-CQfJjPpD.js';
4
+ import { R as Renderer, I as Image } from './p5.Renderer-Ds3PCIgb.js';
5
5
  import { MediaElement } from './dom/p5.MediaElement.js';
6
6
  import primitives from './shape/2d_primitives.js';
7
7
  import attributes from './shape/attributes.js';
@@ -9153,6 +9153,27 @@ class Renderer3D extends Renderer {
9153
9153
  }
9154
9154
 
9155
9155
  background(...args) {
9156
+ const a0 = args[0];
9157
+
9158
+ const isImageLike =
9159
+ a0 != null &&
9160
+ typeof a0 === 'object' &&
9161
+ typeof a0.width === 'number' &&
9162
+ typeof a0.height === 'number' &&
9163
+ (a0.canvas != null || a0.elt != null);
9164
+
9165
+ // WEBGL / 3D: support background(image-like)
9166
+ if (isImageLike) {
9167
+ this._pInst.clear();
9168
+ this._pInst.push();
9169
+ this._pInst.resetMatrix();
9170
+ this._pInst.imageMode(CENTER);
9171
+ this._pInst.image(a0, 0, 0, this._pInst.width, this._pInst.height);
9172
+ this._pInst.pop();
9173
+ return;
9174
+ }
9175
+
9176
+ // Default: background(color)
9156
9177
  const _col = this._pInst.color(...args);
9157
9178
  this.clear(..._col._getRGBA());
9158
9179
  }
@@ -10315,14 +10336,14 @@ class Renderer3D extends Renderer {
10315
10336
  _getSphereMapping(img) {
10316
10337
  if (!this.sphereMapping) {
10317
10338
  const p5 = this._pInst;
10318
- this.sphereMapping = this.baseFilterShader().modify(() => {
10319
- const uEnvMap = p5.uniformTexture();
10320
- const uFovY = p5.uniformFloat();
10321
- const uAspect = p5.uniformFloat();
10339
+ this.sphereMapping = this.baseFilterShader().modify(({ p5 }) => {
10340
+ const uEnvMap = p5.uniformTexture('uEnvMap');
10341
+ const uFovY = p5.uniformFloat('uFovY');
10342
+ const uAspect = p5.uniformFloat('uAspect');
10322
10343
  // Hack: we don't have matrix uniforms yet; use three vectors
10323
- const uN1 = p5.uniformVec3();
10324
- const uN2 = p5.uniformVec3();
10325
- const uN3 = p5.uniformVec3();
10344
+ const uN1 = p5.uniformVec3('uN1');
10345
+ const uN2 = p5.uniformVec3('uN2');
10346
+ const uN3 = p5.uniformVec3('uN3');
10326
10347
  p5.getColor((inputs) => {
10327
10348
  const uFovX = uFovY * uAspect;
10328
10349
  const angleY = p5.mix(uFovY/2.0, -uFovY/2.0, inputs.texCoord.y);
@@ -1,4 +1,4 @@
1
- import { p as CORNER, q as TWO_PI, r as HALF_PI, s as PI } from '../constants-J9QlHaWW.js';
1
+ import { p as CORNER, q as TWO_PI, r as HALF_PI, s as PI } from '../constants-DAarZ_Jd.js';
2
2
  import canvas from '../core/helpers.js';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { p as CORNER, t as CORNERS, u as RADIUS, v as CENTER, w as ROUND, x as SQUARE, y as PROJECT, z as BEVEL, I as MITER } from '../constants-J9QlHaWW.js';
1
+ import { p as CORNER, t as CORNERS, u as RADIUS, v as CENTER, w as ROUND, x as SQUARE, y as PROJECT, z as BEVEL, I as MITER } from '../constants-DAarZ_Jd.js';
2
2
 
3
3
  /**
4
4
  * @module Shape
@@ -1,6 +1,6 @@
1
1
  import { C as Color } from '../creating_reading-p2iQtNm5.js';
2
2
  import { Vector } from '../math/p5.Vector.js';
3
- import { J as INCLUDE, K as PATH, O as EMPTY_PATH, Q as OPEN, V as CLOSE, X as POINTS, Y as LINES, c as TRIANGLES, Z as QUADS, _ as TRIANGLE_FAN, T as TRIANGLE_STRIP, $ as QUAD_STRIP, a0 as EXCLUDE, a1 as JOIN } from '../constants-J9QlHaWW.js';
3
+ import { J as INCLUDE, K as PATH, O as EMPTY_PATH, Q as OPEN, V as CLOSE, X as POINTS, Y as LINES, c as TRIANGLES, Z as QUADS, _ as TRIANGLE_FAN, T as TRIANGLE_STRIP, $ as QUAD_STRIP, a0 as EXCLUDE, a1 as JOIN } from '../constants-DAarZ_Jd.js';
4
4
  import 'colorjs.io/fn';
5
5
  import '../color/color_spaces/hsb.js';
6
6
 
@@ -3,7 +3,7 @@ import attributes from './attributes.js';
3
3
  import curves from './curves.js';
4
4
  import vertex from './vertex.js';
5
5
  import customShapes from './custom_shapes.js';
6
- import '../constants-J9QlHaWW.js';
6
+ import '../constants-DAarZ_Jd.js';
7
7
  import '../core/helpers.js';
8
8
  import '../creating_reading-p2iQtNm5.js';
9
9
  import 'colorjs.io/fn';
@@ -76,7 +76,7 @@ function strands(p5, fn) {
76
76
 
77
77
  p5.Shader.prototype.modify = function (shaderModifier, scope = {}) {
78
78
  try {
79
- if (shaderModifier instanceof Function) {
79
+ if (shaderModifier instanceof Function || typeof shaderModifier === 'string') {
80
80
  // Reset the context object every time modify is called;
81
81
  // const backend = glslBackend;
82
82
  initStrandsContext(strandsContext, this._renderer.strandsBackend, {
@@ -93,7 +93,9 @@ function strands(p5, fn) {
93
93
  if (options.parser) {
94
94
  // #7955 Wrap function declaration code in brackets so anonymous functions are not top level statements, which causes an error in acorn when parsing
95
95
  // https://github.com/acornjs/acorn/issues/1385
96
- const sourceString = `(${shaderModifier.toString()})`;
96
+ const sourceString = typeof shaderModifier === 'string'
97
+ ? `(${shaderModifier})`
98
+ : `(${shaderModifier.toString()})`;
97
99
  strandsCallback = transpileStrandsToJS(
98
100
  p5,
99
101
  sourceString,
@@ -360,7 +360,7 @@ class StrandsFor {
360
360
  popBlock(cfg);
361
361
 
362
362
  const loopVarNode = createStrandsNode(phiNode.id, phiNode.dimension, this.strandsContext);
363
- this.bodyResults = this.bodyCb(loopVarNode, phiVars);
363
+ this.bodyResults = this.bodyCb(loopVarNode, phiVars) || {};
364
364
  for (const key in this.bodyResults) {
365
365
  this.bodyResults[key] = this.strandsContext.p5.strandsNode(this.bodyResults[key]);
366
366
  }
@@ -112,41 +112,53 @@ const ASTCallbacks = {
112
112
  VariableDeclarator(node, _state, ancestors) {
113
113
  if (ancestors.some(nodeIsUniform)) { return; }
114
114
  if (nodeIsUniform(node.init)) {
115
- const uniformNameLiteral = {
116
- type: 'Literal',
117
- value: node.id.name
118
- };
119
- node.init.arguments.unshift(uniformNameLiteral);
115
+ // Only inject the variable name if the first argument isn't already a string
116
+ if (node.init.arguments.length === 0 ||
117
+ node.init.arguments[0].type !== 'Literal' ||
118
+ typeof node.init.arguments[0].value !== 'string') {
119
+ const uniformNameLiteral = {
120
+ type: 'Literal',
121
+ value: node.id.name
122
+ };
123
+ node.init.arguments.unshift(uniformNameLiteral);
124
+ }
120
125
  }
121
126
  if (nodeIsVarying(node.init)) {
122
- const varyingNameLiteral = {
123
- type: 'Literal',
124
- value: node.id.name
125
- };
126
- node.init.arguments.unshift(varyingNameLiteral);
127
- _state.varyings[node.id.name] = varyingNameLiteral;
127
+ // Only inject the variable name if the first argument isn't already a string
128
+ if (
129
+ node.init.arguments.length === 0 ||
130
+ node.init.arguments[0].type !== 'Literal' ||
131
+ typeof node.init.arguments[0].value !== 'string'
132
+ ) {
133
+ const varyingNameLiteral = {
134
+ type: 'Literal',
135
+ value: node.id.name
136
+ };
137
+ node.init.arguments.unshift(varyingNameLiteral);
138
+ _state.varyings[node.id.name] = varyingNameLiteral;
139
+ } else {
140
+ // Still track it as a varying even if name wasn't injected
141
+ _state.varyings[node.id.name] = node.init.arguments[0];
142
+ }
128
143
  }
129
144
  },
130
145
  Identifier(node, _state, ancestors) {
131
146
  if (ancestors.some(nodeIsUniform)) { return; }
132
147
  if (_state.varyings[node.name]
133
148
  && !ancestors.some(a => a.type === 'AssignmentExpression' && a.left === node)) {
134
- node.type = 'ExpressionStatement';
135
- node.expression = {
136
- type: 'CallExpression',
137
- callee: {
138
- type: 'MemberExpression',
139
- object: {
140
- type: 'Identifier',
141
- name: node.name
142
- },
143
- property: {
144
- type: 'Identifier',
145
- name: 'getValue'
146
- },
149
+ node.type = 'CallExpression';
150
+ node.callee = {
151
+ type: 'MemberExpression',
152
+ object: {
153
+ type: 'Identifier',
154
+ name: node.name
155
+ },
156
+ property: {
157
+ type: 'Identifier',
158
+ name: 'getValue'
147
159
  },
148
- arguments: [],
149
160
  };
161
+ node.arguments = [];
150
162
  }
151
163
  },
152
164
  // The callbacks for AssignmentExpression and BinaryExpression handle
@@ -209,13 +221,12 @@ const ASTCallbacks = {
209
221
  varyingName = node.left.object.name;
210
222
  }
211
223
  // Check if it's a getValue() call: myVarying.getValue().xyz
212
- else if (node.left.object.type === 'ExpressionStatement' &&
213
- node.left.object.expression?.type === 'CallExpression' &&
214
- node.left.object.expression.callee?.type === 'MemberExpression' &&
215
- node.left.object.expression.callee.property?.name === 'getValue' &&
216
- node.left.object.expression.callee.object?.type === 'Identifier' &&
217
- _state.varyings[node.left.object.expression.callee.object.name]) {
218
- varyingName = node.left.object.expression.callee.object.name;
224
+ else if (node.left.object.type === 'CallExpression' &&
225
+ node.left.object.callee?.type === 'MemberExpression' &&
226
+ node.left.object.callee.property?.name === 'getValue' &&
227
+ node.left.object.callee.object?.type === 'Identifier' &&
228
+ _state.varyings[node.left.object.callee.object.name]) {
229
+ varyingName = node.left.object.callee.object.name;
219
230
  }
220
231
 
221
232
  if (varyingName) {
@@ -276,6 +287,37 @@ const ASTCallbacks = {
276
287
  };
277
288
  node.arguments = [node.right];
278
289
  },
290
+ LogicalExpression(node, _state, ancestors) {
291
+ // Don't convert uniform default values to node methods, as
292
+ // they should be evaluated at runtime, not compiled.
293
+ if (ancestors.some(nodeIsUniform)) { return; }
294
+ // If the left hand side of an expression is one of these types,
295
+ // we should construct a node from it.
296
+ const unsafeTypes = ['Literal', 'ArrayExpression', 'Identifier'];
297
+ if (unsafeTypes.includes(node.left.type)) {
298
+ const leftReplacementNode = {
299
+ type: 'CallExpression',
300
+ callee: {
301
+ type: 'Identifier',
302
+ name: '__p5.strandsNode',
303
+ },
304
+ arguments: [node.left]
305
+ };
306
+ node.left = leftReplacementNode;
307
+ }
308
+ // Replace the logical operator with a call expression
309
+ // in other words a call to BaseNode.or(), .and() etc.
310
+ node.type = 'CallExpression';
311
+ node.callee = {
312
+ type: 'MemberExpression',
313
+ object: node.left,
314
+ property: {
315
+ type: 'Identifier',
316
+ name: replaceBinaryOperator(node.operator),
317
+ },
318
+ };
319
+ node.arguments = [node.right];
320
+ },
279
321
  IfStatement(node, _state, ancestors) {
280
322
  if (ancestors.some(nodeIsUniform)) { return; }
281
323
  // Transform if statement into strandsIf() call
@@ -336,24 +378,56 @@ const ASTCallbacks = {
336
378
  };
337
379
  // Analyze which outer scope variables are assigned in any branch
338
380
  const assignedVars = new Set();
339
- const analyzeBlock = (body) => {
340
- if (body.type !== 'BlockStatement') return;
341
- // First pass: collect variable declarations within this block
342
- const localVars = new Set();
343
- for (const stmt of body.body) {
381
+
382
+ // Helper function to check if a block contains strands control flow calls as immediate children
383
+ const blockContainsStrandsControlFlow = (node) => {
384
+ if (node.type !== 'BlockStatement') return false;
385
+ return node.body.some(stmt => {
386
+ // Check for variable declarations with strands control flow init
344
387
  if (stmt.type === 'VariableDeclaration') {
345
- for (const decl of stmt.declarations) {
346
- if (decl.id.type === 'Identifier') {
347
- localVars.add(decl.id.name);
348
- }
388
+ const match = stmt.declarations.some(decl =>
389
+ decl.init?.type === 'CallExpression' &&
390
+ (
391
+ (
392
+ decl.init?.callee?.type === 'MemberExpression' &&
393
+ decl.init?.callee?.object?.type === 'Identifier' &&
394
+ decl.init?.callee?.object?.name === '__p5' &&
395
+ (decl.init?.callee?.property?.name === 'strandsFor' ||
396
+ decl.init?.callee?.property?.name === 'strandsIf')
397
+ ) ||
398
+ (
399
+ decl.init?.callee?.type === 'Identifier' &&
400
+ (decl.init?.callee?.name === '__p5.strandsFor' ||
401
+ decl.init?.callee?.name === '__p5.strandsIf')
402
+ )
403
+ )
404
+ );
405
+ return match
406
+ }
407
+ return false;
408
+ });
409
+ };
410
+
411
+ const analyzeBranch = (functionBody) => {
412
+ // First pass: collect all variable declarations in the branch
413
+ const localVars = new Set();
414
+ ancestor(functionBody, {
415
+ VariableDeclarator(node, ancestors) {
416
+ // Skip if we're inside a block that contains strands control flow
417
+ if (ancestors.some(blockContainsStrandsControlFlow)) return;
418
+ if (node.id.type === 'Identifier') {
419
+ localVars.add(node.id.name);
349
420
  }
350
421
  }
351
- }
352
- // Second pass: find assignments to non-local variables
353
- for (const stmt of body.body) {
354
- if (stmt.type === 'ExpressionStatement' &&
355
- stmt.expression.type === 'AssignmentExpression') {
356
- const left = stmt.expression.left;
422
+ });
423
+
424
+ // Second pass: find assignments to non-local variables using acorn-walk
425
+ ancestor(functionBody, {
426
+ AssignmentExpression(node, ancestors) {
427
+ // Skip if we're inside a block that contains strands control flow
428
+ if (ancestors.some(blockContainsStrandsControlFlow)) return;
429
+
430
+ const left = node.left;
357
431
  if (left.type === 'Identifier') {
358
432
  // Direct variable assignment: x = value
359
433
  if (!localVars.has(left.name)) {
@@ -361,20 +435,18 @@ const ASTCallbacks = {
361
435
  }
362
436
  } else if (left.type === 'MemberExpression' &&
363
437
  left.object.type === 'Identifier') {
364
- // Property assignment: obj.prop = value
438
+ // Property assignment: obj.prop = value (includes swizzles)
365
439
  if (!localVars.has(left.object.name)) {
366
440
  assignedVars.add(left.object.name);
367
441
  }
368
442
  }
369
- } else if (stmt.type === 'BlockStatement') {
370
- // Recursively analyze nested block statements
371
- analyzeBlock(stmt);
372
443
  }
373
- }
444
+ });
374
445
  };
446
+
375
447
  // Analyze all branches for assignments to outer scope variables
376
- analyzeBlock(thenFunction.body);
377
- analyzeBlock(elseFunction.body);
448
+ analyzeBranch(thenFunction.body);
449
+ analyzeBranch(elseFunction.body);
378
450
  if (assignedVars.size > 0) {
379
451
  // Add copying, reference replacement, and return statements to branch functions
380
452
  const addCopyingAndReturn = (functionBody, varsToReturn) => {
@@ -565,7 +637,7 @@ const ASTCallbacks = {
565
637
 
566
638
  // Transform for statement into strandsFor() call
567
639
  // for (init; test; update) body -> strandsFor(initCb, conditionCb, updateCb, bodyCb, initialVars)
568
-
640
+
569
641
  // Generate unique loop variable name
570
642
  const uniqueLoopVar = `loopVar${loopVarCounter++}`;
571
643
 
@@ -684,46 +756,71 @@ const ASTCallbacks = {
684
756
 
685
757
  // Analyze which outer scope variables are assigned in the loop body
686
758
  const assignedVars = new Set();
687
- const analyzeBlock = (body, parentLocalVars = new Set()) => {
688
- if (body.type !== 'BlockStatement') return;
689
759
 
690
- // First pass: collect variable declarations within this block
691
- const localVars = new Set([...parentLocalVars]);
692
- for (const stmt of body.body) {
760
+ // Helper function to check if a block contains strands control flow calls as immediate children
761
+ const blockContainsStrandsControlFlow = (node) => {
762
+ if (node.type !== 'BlockStatement') return false;
763
+ return node.body.some(stmt => {
764
+ // Check for variable declarations with strands control flow init
693
765
  if (stmt.type === 'VariableDeclaration') {
694
- for (const decl of stmt.declarations) {
695
- if (decl.id.type === 'Identifier') {
696
- localVars.add(decl.id.name);
697
- }
698
- }
766
+ const match = stmt.declarations.some(decl =>
767
+ decl.init?.type === 'CallExpression' &&
768
+ (
769
+ (
770
+ decl.init?.callee?.type === 'MemberExpression' &&
771
+ decl.init?.callee?.object?.type === 'Identifier' &&
772
+ decl.init?.callee?.object?.name === '__p5' &&
773
+ (decl.init?.callee?.property?.name === 'strandsFor' ||
774
+ decl.init?.callee?.property?.name === 'strandsIf')
775
+ ) ||
776
+ (
777
+ decl.init?.callee?.type === 'Identifier' &&
778
+ (decl.init?.callee?.name === '__p5.strandsFor' ||
779
+ decl.init?.callee?.name === '__p5.strandsIf')
780
+ )
781
+ )
782
+ );
783
+ return match
784
+ }
785
+ return false;
786
+ });
787
+ };
788
+
789
+ // First pass: collect all variable declarations in the body
790
+ const localVars = new Set();
791
+ ancestor(bodyFunction.body, {
792
+ VariableDeclarator(node, ancestors) {
793
+ // Skip if we're inside a block that contains strands control flow
794
+ if (ancestors.some(blockContainsStrandsControlFlow)) return;
795
+ if (node.id.type === 'Identifier') {
796
+ localVars.add(node.id.name);
699
797
  }
700
798
  }
799
+ });
701
800
 
702
- // Second pass: find assignments to non-local variables
703
- for (const stmt of body.body) {
704
- if (stmt.type === 'ExpressionStatement' &&
705
- stmt.expression.type === 'AssignmentExpression') {
706
- const left = stmt.expression.left;
707
- if (left.type === 'Identifier') {
708
- // Direct variable assignment: x = value
709
- if (!localVars.has(left.name)) {
710
- assignedVars.add(left.name);
711
- }
712
- } else if (left.type === 'MemberExpression' &&
713
- left.object.type === 'Identifier') {
714
- // Property assignment: obj.prop = value (includes swizzles)
715
- if (!localVars.has(left.object.name)) {
716
- assignedVars.add(left.object.name);
717
- }
801
+ // Second pass: find assignments to non-local variables using acorn-walk
802
+ ancestor(bodyFunction.body, {
803
+ AssignmentExpression(node, ancestors) {
804
+ // Skip if we're inside a block that contains strands control flow
805
+ if (ancestors.some(blockContainsStrandsControlFlow)) {
806
+ return
807
+ }
808
+
809
+ const left = node.left;
810
+ if (left.type === 'Identifier') {
811
+ // Direct variable assignment: x = value
812
+ if (!localVars.has(left.name)) {
813
+ assignedVars.add(left.name);
814
+ }
815
+ } else if (left.type === 'MemberExpression' &&
816
+ left.object.type === 'Identifier') {
817
+ // Property assignment: obj.prop = value (includes swizzles)
818
+ if (!localVars.has(left.object.name)) {
819
+ assignedVars.add(left.object.name);
718
820
  }
719
- } else if (stmt.type === 'BlockStatement') {
720
- // Recursively analyze nested block statements, passing down local vars
721
- analyzeBlock(stmt, localVars);
722
821
  }
723
822
  }
724
- };
725
-
726
- analyzeBlock(bodyFunction.body);
823
+ });
727
824
 
728
825
  if (assignedVars.size > 0) {
729
826
  // Add copying, reference replacement, and return statements similar to if statements
@@ -929,7 +1026,7 @@ const ASTCallbacks = {
929
1026
  // Reset counters at the start of each transpilation
930
1027
  blockVarCounter = 0;
931
1028
  loopVarCounter = 0;
932
-
1029
+
933
1030
  const ast = parse(sourceString, {
934
1031
  ecmaVersion: 2021,
935
1032
  locations: srcLocations
@@ -962,20 +1059,39 @@ const ASTCallbacks = {
962
1059
  recursive(ast, { varyings: {} }, postOrderControlFlowTransform);
963
1060
  const transpiledSource = escodegen.generate(ast);
964
1061
  const scopeKeys = Object.keys(scope);
965
- const internalStrandsCallback = new Function(
966
- // Create a parameter called __p5, not just p5, because users of instance mode
967
- // may pass in a variable called p5 as a scope variable. If we rely on a variable called
968
- // p5, then the scope variable called p5 might accidentally override internal function
969
- // calls to p5 static methods.
970
- '__p5',
971
- ...scopeKeys,
972
- transpiledSource
973
- .slice(
974
- transpiledSource.indexOf('{') + 1,
975
- transpiledSource.lastIndexOf('}')
976
- ).replaceAll(';', '')
977
- );
978
- return () => internalStrandsCallback(p5, ...scopeKeys.map(key => scope[key]));
1062
+ const match = /\(?\s*(?:function)?\s*\(([^)]*)\)\s*(?:=>)?\s*{((?:.|\n)*)}\s*;?\s*\)?/
1063
+ .exec(transpiledSource);
1064
+ if (!match) {
1065
+ console.log(transpiledSource);
1066
+ throw new Error('Could not parse p5.strands function!');
1067
+ }
1068
+ const params = match[1].split(/,\s*/).filter(param => !!param.trim());
1069
+ let paramVals, paramNames;
1070
+ if (params.length > 0) {
1071
+ paramNames = params;
1072
+ paramVals = [scope];
1073
+ } else {
1074
+ paramNames = scopeKeys;
1075
+ paramVals = scopeKeys.map(key => scope[key]);
1076
+ }
1077
+ const body = match[2];
1078
+ try {
1079
+ const internalStrandsCallback = new Function(
1080
+ // Create a parameter called __p5, not just p5, because users of instance mode
1081
+ // may pass in a variable called p5 as a scope variable. If we rely on a variable called
1082
+ // p5, then the scope variable called p5 might accidentally override internal function
1083
+ // calls to p5 static methods.
1084
+ '__p5',
1085
+ ...paramNames,
1086
+ body,
1087
+ );
1088
+ return () => internalStrandsCallback(p5, ...paramVals);
1089
+ } catch (e) {
1090
+ console.error(e);
1091
+ console.log(paramNames);
1092
+ console.log(body);
1093
+ throw new Error('Error transpiling p5.strands callback!');
1094
+ }
979
1095
  }
980
1096
 
981
1097
  export { transpileStrandsToJS };
@@ -1,10 +1,10 @@
1
1
  import textCore from './textCore.js';
2
2
  import font from './p5.Font.js';
3
- import '../p5.Renderer-CQfJjPpD.js';
3
+ import '../p5.Renderer-Ds3PCIgb.js';
4
4
  import '../creating_reading-p2iQtNm5.js';
5
5
  import 'colorjs.io/fn';
6
6
  import '../color/color_spaces/hsb.js';
7
- import '../constants-J9QlHaWW.js';
7
+ import '../constants-DAarZ_Jd.js';
8
8
  import '../image/filters.js';
9
9
  import '../math/p5.Vector.js';
10
10
  import '../shape/custom_shapes.js';
@@ -1,11 +1,11 @@
1
1
  import { textCoreConstants } from './textCore.js';
2
- import { v as CENTER, a9 as RIGHT, u as RADIUS } from '../constants-J9QlHaWW.js';
2
+ import { v as CENTER, a9 as RIGHT, u as RADIUS } from '../constants-DAarZ_Jd.js';
3
3
  import { UnicodeRange } from '@japont/unicode-range';
4
4
  import { unicodeRanges } from './unicodeRanges.js';
5
5
  import { Vector } from '../math/p5.Vector.js';
6
6
  import Typr from './lib/Typr.js';
7
7
  import { createFromCommands } from '@davepagurek/bezier-path';
8
- import '../p5.Renderer-CQfJjPpD.js';
8
+ import '../p5.Renderer-Ds3PCIgb.js';
9
9
  import '../creating_reading-p2iQtNm5.js';
10
10
  import 'colorjs.io/fn';
11
11
  import '../color/color_spaces/hsb.js';
@@ -1,8 +1,8 @@
1
- import { R as Renderer } from '../p5.Renderer-CQfJjPpD.js';
1
+ import { R as Renderer } from '../p5.Renderer-Ds3PCIgb.js';
2
2
  import '../creating_reading-p2iQtNm5.js';
3
3
  import 'colorjs.io/fn';
4
4
  import '../color/color_spaces/hsb.js';
5
- import '../constants-J9QlHaWW.js';
5
+ import '../constants-DAarZ_Jd.js';
6
6
  import '../image/filters.js';
7
7
  import '../math/p5.Vector.js';
8
8
  import '../shape/custom_shapes.js';
@@ -1,5 +1,5 @@
1
- import '../constants-J9QlHaWW.js';
2
- export { p as default } from '../rendering-IbeJmcSe.js';
1
+ import '../constants-DAarZ_Jd.js';
2
+ export { p as default } from '../rendering-DPysJ2_4.js';
3
3
  import '../math/p5.Vector.js';
4
4
  import './p5.Geometry.js';
5
5
  import '../math/p5.Matrix.js';
@@ -10,7 +10,7 @@ import '../color/color_spaces/hsb.js';
10
10
  import '../dom/p5.Element.js';
11
11
  import '../dom/p5.File.js';
12
12
  import '../io/p5.XML.js';
13
- import '../p5.Renderer-CQfJjPpD.js';
13
+ import '../p5.Renderer-Ds3PCIgb.js';
14
14
  import '../image/filters.js';
15
15
  import '../shape/custom_shapes.js';
16
16
  import '../core/States.js';