@@ -114,6 +114,42 @@ function installBuiltinGlobalAccessors(strandsContext) {
114114 strandsContext . _builtinGlobalsAccessorsInstalled = true
115115}
116116
117+ //////////////////////////////////////////////
118+ // Prototype mirroring helpers
119+ //////////////////////////////////////////////
120+
121+ /*
122+ * Permanently augment both p5.prototype (fn) and p5.Graphics.prototype
123+ * with a strands function. Overwrites unconditionally - strands wrappers
124+ * are the correct dual mode implementation.
125+ */
126+ function augmentFn ( fn , p5 , name , value ) {
127+ fn [ name ] = value ;
128+ const GraphicsProto = p5 ?. Graphics ?. prototype ;
129+ if ( GraphicsProto ) {
130+ GraphicsProto [ name ] = value ;
131+ }
132+ }
133+
134+ /*
135+ * Temporarily augment window, p5.prototype (fn), and p5.Graphics.prototype
136+ * with a hook function. Saves previous values into strandsContext override
137+ * stores so deinitStrandsContext can restore them.
138+ */
139+ function augmentFnTemporary ( fn , strandsContext , name , value ) {
140+ strandsContext . windowOverrides [ name ] = window [ name ] ;
141+ strandsContext . fnOverrides [ name ] = fn [ name ] ;
142+ window [ name ] = value ;
143+ fn [ name ] = value ;
144+ const GraphicsProto = strandsContext . p5 ?. Graphics ?. prototype ;
145+ if ( GraphicsProto ) {
146+ strandsContext . graphicsOverrides [ name ] = Object . prototype . hasOwnProperty . call ( GraphicsProto , name )
147+ ? GraphicsProto [ name ]
148+ : undefined ;
149+ GraphicsProto [ name ] = value ;
150+ }
151+ }
152+
117153//////////////////////////////////////////////
118154// User nodes
119155//////////////////////////////////////////////
@@ -137,27 +173,27 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
137173 //////////////////////////////////////////////
138174 // Unique Functions
139175 //////////////////////////////////////////////
140- fn . discard = function ( ) {
176+ augmentFn ( fn , p5 , 'discard' , function ( ) {
141177 build . statementNode ( strandsContext , StatementType . DISCARD ) ;
142- }
143- fn . break = function ( ) {
178+ } ) ;
179+ augmentFn ( fn , p5 , 'break' , function ( ) {
144180 build . statementNode ( strandsContext , StatementType . BREAK ) ;
145- } ;
181+ } ) ;
146182 p5 . break = fn . break ;
147- fn . instanceID = function ( ) {
183+ augmentFn ( fn , p5 , 'instanceID' , function ( ) {
148184 const node = build . variableNode ( strandsContext , { baseType : BaseType . INT , dimension : 1 } , strandsContext . backend . instanceIdReference ( ) ) ;
149185 return createStrandsNode ( node . id , node . dimension , strandsContext ) ;
150- }
186+ } ) ;
151187 // Internal methods use p5 static methods; user-facing methods use fn.
152188 // Some methods need to be used by both.
153189 p5 . strandsIf = function ( conditionNode , ifBody ) {
154190 return new StrandsConditional ( strandsContext , conditionNode , ifBody ) ;
155191 }
156- fn . strandsIf = p5 . strandsIf ;
192+ augmentFn ( fn , p5 , 'strandsIf' , p5 . strandsIf ) ;
157193 p5 . strandsFor = function ( initialCb , conditionCb , updateCb , bodyCb , initialVars ) {
158194 return new StrandsFor ( strandsContext , initialCb , conditionCb , updateCb , bodyCb , initialVars ) . build ( ) ;
159195 } ;
160- fn . strandsFor = p5 . strandsFor ;
196+ augmentFn ( fn , p5 , 'strandsFor' , p5 . strandsFor ) ;
161197 p5 . strandsEarlyReturn = function ( value ) {
162198 const { dag, cfg } = strandsContext ;
163199
@@ -190,7 +226,7 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
190226
191227 return valueNode ;
192228 } ;
193- fn . strandsEarlyReturn = p5 . strandsEarlyReturn ;
229+ augmentFn ( fn , p5 , 'strandsEarlyReturn' , p5 . strandsEarlyReturn ) ;
194230 p5 . strandsNode = function ( ...args ) {
195231 if ( args . length === 1 && args [ 0 ] instanceof StrandsNode ) {
196232 return args [ 0 ] ;
@@ -221,16 +257,16 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
221257 const isp5Function = overrides [ 0 ] . isp5Function ;
222258 if ( isp5Function ) {
223259 const originalFn = fn [ functionName ] ;
224- fn [ functionName ] = function ( ...args ) {
260+ augmentFn ( fn , p5 , functionName , function ( ...args ) {
225261 if ( strandsContext . active ) {
226262 const { id, dimension } = build . functionCallNode ( strandsContext , functionName , args ) ;
227263 return createStrandsNode ( id , dimension , strandsContext ) ;
228264 } else {
229265 return originalFn . apply ( this , args ) ;
230266 }
231- }
267+ } ) ;
232268 } else {
233- fn [ functionName ] = function ( ...args ) {
269+ augmentFn ( fn , p5 , functionName , function ( ...args ) {
234270 if ( strandsContext . active ) {
235271 const { id, dimension } = build . functionCallNode ( strandsContext , functionName , args ) ;
236272 return createStrandsNode ( id , dimension , strandsContext ) ;
@@ -239,11 +275,11 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
239275 `It looks like you've called ${ functionName } outside of a shader's modify() function.`
240276 )
241277 }
242- }
278+ } ) ;
243279 }
244280 }
245281
246- fn . getTexture = function ( ...rawArgs ) {
282+ augmentFn ( fn , p5 , 'getTexture' , function ( ...rawArgs ) {
247283 if ( strandsContext . active ) {
248284 const { id, dimension } = strandsContext . backend . createGetTextureCall ( strandsContext , rawArgs ) ;
249285 return createStrandsNode ( id , dimension , strandsContext ) ;
@@ -252,17 +288,17 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
252288 `It looks like you've called getTexture outside of a shader's modify() function.`
253289 )
254290 }
255- }
291+ } ) ;
256292
257293 // Add texture function as alias for getTexture with p5 fallback
258294 const originalTexture = fn . texture ;
259- fn . texture = function ( ...args ) {
295+ augmentFn ( fn , p5 , 'texture' , function ( ...args ) {
260296 if ( strandsContext . active ) {
261297 return this . getTexture ( ...args ) ;
262298 } else {
263299 return originalTexture . apply ( this , args ) ;
264300 }
265- }
301+ } ) ;
266302
267303 // Add noise function with backend-agnostic implementation
268304 const originalNoise = fn . noise ;
@@ -272,16 +308,16 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
272308 strandsContext . _noiseOctaves = null ;
273309 strandsContext . _noiseAmpFalloff = null ;
274310
275- fn . noiseDetail = function ( lod , falloff = 0.5 ) {
311+ augmentFn ( fn , p5 , 'noiseDetail' , function ( lod , falloff = 0.5 ) {
276312 if ( ! strandsContext . active ) {
277313 return originalNoiseDetail . apply ( this , arguments ) ;
278314 }
279315
280316 strandsContext . _noiseOctaves = lod ;
281317 strandsContext . _noiseAmpFalloff = falloff ;
282- } ;
318+ } ) ;
283319
284- fn . noise = function ( ...args ) {
320+ augmentFn ( fn , p5 , 'noise' , function ( ...args ) {
285321 if ( ! strandsContext . active ) {
286322 return originalNoise . apply ( this , args ) ; // fallback to regular p5.js noise
287323 }
@@ -328,9 +364,9 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
328364 } ]
329365 } ) ;
330366 return createStrandsNode ( id , dimension , strandsContext ) ;
331- } ;
367+ } ) ;
332368
333- fn . millis = function ( ...args ) {
369+ augmentFn ( fn , p5 , 'millis' , function ( ...args ) {
334370 if ( ! strandsContext . active ) {
335371 return originalMillis . apply ( this , args ) ;
336372 }
@@ -343,7 +379,7 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
343379 return instance ? instance . millis ( ) : undefined ;
344380 }
345381 ) ;
346- } ;
382+ } ) ;
347383
348384 // Next is type constructors and uniform functions.
349385 // For some of them, we have aliases so that you can write either a more human-readable
@@ -372,13 +408,13 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
372408 typeAliases . push ( pascalTypeName . replace ( 'Vec' , 'Vector' ) ) ;
373409 }
374410 }
375- fn [ `uniform${ pascalTypeName } ` ] = function ( name , defaultValue ) {
411+ augmentFn ( fn , p5 , `uniform${ pascalTypeName } ` , function ( name , defaultValue ) {
376412 const { id, dimension } = build . variableNode ( strandsContext , typeInfo , name ) ;
377413 strandsContext . uniforms . push ( { name, typeInfo, defaultValue } ) ;
378414 return createStrandsNode ( id , dimension , strandsContext ) ;
379- } ;
415+ } ) ;
380416 // Shared variables with smart context detection
381- fn [ `shared${ pascalTypeName } ` ] = function ( name ) {
417+ augmentFn ( fn , p5 , `shared${ pascalTypeName } ` , function ( name ) {
382418 const { id, dimension } = build . variableNode ( strandsContext , typeInfo , name ) ;
383419
384420 // Initialize shared variables tracking if not present
@@ -395,20 +431,20 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
395431 } ) ;
396432
397433 return createStrandsNode ( id , dimension , strandsContext ) ;
398- } ;
434+ } ) ;
399435
400436 // Alias varying* as shared* for backward compatibility
401- fn [ `varying${ pascalTypeName } ` ] = fn [ `shared${ pascalTypeName } ` ] ;
437+ augmentFn ( fn , p5 , `varying${ pascalTypeName } ` , fn [ `shared${ pascalTypeName } ` ] ) ;
402438
403439 for ( const typeAlias of typeAliases ) {
404440 // For compatibility, also alias uniformVec2 as uniformVector2, what we initially
405441 // documented these as
406- fn [ `uniform${ typeAlias } ` ] = fn [ `uniform${ pascalTypeName } ` ] ;
407- fn [ `varying${ typeAlias } ` ] = fn [ `varying${ pascalTypeName } ` ] ;
408- fn [ `shared${ typeAlias } ` ] = fn [ `shared${ pascalTypeName } ` ] ;
442+ augmentFn ( fn , p5 , `uniform${ typeAlias } ` , fn [ `uniform${ pascalTypeName } ` ] ) ;
443+ augmentFn ( fn , p5 , `varying${ typeAlias } ` , fn [ `varying${ pascalTypeName } ` ] ) ;
444+ augmentFn ( fn , p5 , `shared${ typeAlias } ` , fn [ `shared${ pascalTypeName } ` ] ) ;
409445 }
410446 const originalp5Fn = fn [ typeInfo . fnName ] ;
411- fn [ typeInfo . fnName ] = function ( ...args ) {
447+ augmentFn ( fn , p5 , typeInfo . fnName , function ( ...args ) {
412448 if ( strandsContext . active ) {
413449 if ( args . length === 1 && args [ 0 ] . dimension && args [ 0 ] . dimension === typeInfo . dimension ) {
414450 const { id, dimension } = build . functionCallNode (
@@ -440,7 +476,7 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
440476 `It looks like you've called ${ typeInfo . fnName } outside of a shader's modify() function.`
441477 ) ;
442478 }
443- }
479+ } ) ;
444480 }
445481}
446482//////////////////////////////////////////////
@@ -723,10 +759,7 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) {
723759 }
724760
725761 for ( const name of aliases ) {
726- strandsContext . windowOverrides [ name ] = window [ name ] ;
727- strandsContext . fnOverrides [ name ] = fn [ name ] ;
728- window [ name ] = hook ;
729- fn [ name ] = hook ;
762+ augmentFnTemporary ( fn , strandsContext , name , hook ) ;
730763 }
731764 hook . earlyReturns = [ ] ;
732765 }
0 commit comments