Skip to content

Commit ba76e66

Browse files
committed
Factor out common helper
1 parent 8c6f47d commit ba76e66

2 files changed

Lines changed: 97 additions & 109 deletions

File tree

src/strands/strands_transpiler.js

Lines changed: 75 additions & 109 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,79 @@ function buildPropertyPath(memberExpr) {
118118
return parts.join('.');
119119
}
120120

121+
// Replace all references to original variables with temp variables
122+
// and wrap literal assignments in strandsNode calls
123+
function replaceReferences(node, tempVarMap) {
124+
const internalReplaceReferences = (node) => {
125+
if (!node || typeof node !== 'object') return;
126+
127+
// Check if this MemberExpression matches a tracked property path
128+
if (node.type === 'MemberExpression') {
129+
const propName = node.property.name || node.property.value;
130+
if (isSwizzle(propName)) {
131+
// For swizzles, only replace the object part, keep the swizzle
132+
internalReplaceReferences(node.object);
133+
return;
134+
}
135+
const propertyPath = buildPropertyPath(node);
136+
if (propertyPath && tempVarMap.has(propertyPath)) {
137+
// Replace entire member expression with temp variable
138+
Object.assign(node, {
139+
type: 'Identifier',
140+
name: tempVarMap.get(propertyPath)
141+
});
142+
return; // Don't recurse into replaced node
143+
}
144+
}
145+
146+
// Handle simple identifier replacements
147+
if (node.type === 'Identifier' && tempVarMap.has(node.name)) {
148+
node.name = tempVarMap.get(node.name);
149+
}
150+
151+
// Handle literal assignments to temp variables
152+
if (node.type === 'AssignmentExpression') {
153+
let leftPath = null;
154+
if (node.left.type === 'Identifier') {
155+
leftPath = node.left.name;
156+
} else if (node.left.type === 'MemberExpression') {
157+
leftPath = buildPropertyPath(node.left);
158+
}
159+
160+
if (leftPath && tempVarMap.has(leftPath) &&
161+
(node.right.type === 'Literal' || node.right.type === 'ArrayExpression')) {
162+
// Wrap the right hand side in a strandsNode call to make sure
163+
// it's not just a literal and has a type
164+
node.right = {
165+
type: 'CallExpression',
166+
callee: {
167+
type: 'Identifier',
168+
name: '__p5.strandsNode'
169+
},
170+
arguments: [node.right]
171+
};
172+
}
173+
}
174+
175+
// Recursively process all properties
176+
for (const key in node) {
177+
if (node.hasOwnProperty(key) && key !== 'parent') {
178+
// Don't recurse into property names of non-computed member expressions
179+
if (node.type === 'MemberExpression' && key === 'property' && !node.computed) {
180+
continue;
181+
}
182+
if (Array.isArray(node[key])) {
183+
node[key].forEach(internalReplaceReferences);
184+
} else if (typeof node[key] === 'object') {
185+
internalReplaceReferences(node[key]);
186+
}
187+
}
188+
}
189+
};
190+
191+
internalReplaceReferences(node);
192+
}
193+
121194
const ASTCallbacks = {
122195
UnaryExpression(node, _state, ancestors) {
123196
if (ancestors.some(nodeIsUniform)) { return; }
@@ -538,76 +611,8 @@ const ASTCallbacks = {
538611
kind: 'let'
539612
});
540613
}
541-
// Replace all references to original variables with temp variables
542-
// and wrap literal assignments in strandsNode calls
543-
const replaceReferences = (node) => {
544-
if (!node || typeof node !== 'object') return;
545-
546-
// Check if this MemberExpression matches a tracked property path
547-
if (node.type === 'MemberExpression') {
548-
const propName = node.property.name || node.property.value;
549-
if (isSwizzle(propName)) {
550-
// For swizzles, only replace the object part, keep the swizzle
551-
replaceReferences(node.object);
552-
return;
553-
}
554-
const propertyPath = buildPropertyPath(node);
555-
if (propertyPath && tempVarMap.has(propertyPath)) {
556-
// Replace entire member expression with temp variable
557-
Object.assign(node, {
558-
type: 'Identifier',
559-
name: tempVarMap.get(propertyPath)
560-
});
561-
return; // Don't recurse into replaced node
562-
}
563-
}
564-
565-
// Handle simple identifier replacements
566-
if (node.type === 'Identifier' && tempVarMap.has(node.name)) {
567-
node.name = tempVarMap.get(node.name);
568-
}
569-
570-
// Handle literal assignments to temp variables
571-
if (node.type === 'AssignmentExpression') {
572-
let leftPath = null;
573-
if (node.left.type === 'Identifier') {
574-
leftPath = node.left.name;
575-
} else if (node.left.type === 'MemberExpression') {
576-
leftPath = buildPropertyPath(node.left);
577-
}
578-
579-
if (leftPath && tempVarMap.has(leftPath) &&
580-
(node.right.type === 'Literal' || node.right.type === 'ArrayExpression')) {
581-
// Wrap the right hand side in a strandsNode call to make sure
582-
// it's not just a literal and has a type
583-
node.right = {
584-
type: 'CallExpression',
585-
callee: {
586-
type: 'Identifier',
587-
name: '__p5.strandsNode'
588-
},
589-
arguments: [node.right]
590-
};
591-
}
592-
}
593-
594-
// Recursively process all properties
595-
for (const key in node) {
596-
if (node.hasOwnProperty(key) && key !== 'parent') {
597-
// Don't recurse into property names of non-computed member expressions
598-
if (node.type === 'MemberExpression' && key === 'property' && !node.computed) {
599-
continue;
600-
}
601-
if (Array.isArray(node[key])) {
602-
node[key].forEach(replaceReferences);
603-
} else if (typeof node[key] === 'object') {
604-
replaceReferences(node[key]);
605-
}
606-
}
607-
}
608-
};
609614
// Apply reference replacement to all statements
610-
functionBody.body.forEach(replaceReferences);
615+
functionBody.body.forEach(node => replaceReferences(node, tempVarMap));
611616
// Insert copy statements at the beginning
612617
functionBody.body.unshift(...copyStatements);
613618
// Add return statement with flat object using property paths as keys
@@ -971,46 +976,7 @@ const ASTCallbacks = {
971976
});
972977
}
973978

974-
// Replace references to original variables with temp variables
975-
const replaceReferences = (node) => {
976-
if (!node || typeof node !== 'object') return;
977-
978-
if (node.type === 'MemberExpression') {
979-
const propName = node.property.name || node.property.value;
980-
if (isSwizzle(propName)) {
981-
replaceReferences(node.object);
982-
return;
983-
}
984-
const propertyPath = buildPropertyPath(node);
985-
if (propertyPath && tempVarMap.has(propertyPath)) {
986-
Object.assign(node, {
987-
type: 'Identifier',
988-
name: tempVarMap.get(propertyPath)
989-
});
990-
return;
991-
}
992-
}
993-
994-
if (node.type === 'Identifier' && tempVarMap.has(node.name)) {
995-
node.name = tempVarMap.get(node.name);
996-
}
997-
998-
for (const key in node) {
999-
if (node.hasOwnProperty(key) && key !== 'parent') {
1000-
// Don't recurse into property names of non-computed member expressions
1001-
if (node.type === 'MemberExpression' && key === 'property' && !node.computed) {
1002-
continue;
1003-
}
1004-
if (Array.isArray(node[key])) {
1005-
node[key].forEach(replaceReferences);
1006-
} else if (typeof node[key] === 'object') {
1007-
replaceReferences(node[key]);
1008-
}
1009-
}
1010-
}
1011-
};
1012-
1013-
functionBody.body.forEach(replaceReferences);
979+
functionBody.body.forEach(node => replaceReferences(node, tempVarMap));
1014980
functionBody.body.unshift(...copyStatements);
1015981

1016982
// Add return statement with flat object using property paths as keys

test/unit/webgl/p5.Shader.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,28 @@ suite('p5.Shader', function() {
497497
assert.approximately(pixelColor[1], 255, 5); // Green channel should be 255
498498
assert.approximately(pixelColor[2], 255, 5); // Blue channel should be 255
499499
});
500+
test('handle simple if statement with condition that is not a swizzle', () => {
501+
myp5.createCanvas(50, 50, myp5.WEBGL);
502+
const testShader = myp5.baseMaterialShader().modify(() => {
503+
const x = myp5.uniformFloat(() => 1.0); // true condition
504+
myp5.getPixelInputs(inputs => {
505+
let color = myp5.float(0.5); // initial gray
506+
if (x > 0.5) {
507+
color = myp5.float(1.0); // set to white in if branch
508+
}
509+
inputs.color = [color, color, color, 1.0];
510+
return inputs;
511+
});
512+
}, { myp5 });
513+
myp5.noStroke();
514+
myp5.shader(testShader);
515+
myp5.plane(myp5.width, myp5.height);
516+
// Check that the center pixel is white (condition was true)
517+
const pixelColor = myp5.get(25, 25);
518+
assert.approximately(pixelColor[0], 255, 5); // Red channel should be 255 (white)
519+
assert.approximately(pixelColor[1], 255, 5); // Green channel should be 255
520+
assert.approximately(pixelColor[2], 255, 5); // Blue channel should be 255
521+
});
500522
test('handle simple if statement with simpler assignment', () => {
501523
myp5.createCanvas(50, 50, myp5.WEBGL);
502524
const testShader = myp5.baseMaterialShader().modify(() => {

0 commit comments

Comments
 (0)