Skip to content

Commit 5fb8ff6

Browse files
committed
Fix some bugs in p5.strands type aliasing
1 parent fbad474 commit 5fb8ff6

File tree

7 files changed

+238
-50
lines changed

7 files changed

+238
-50
lines changed

docs/parameterData.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3168,6 +3168,13 @@
31683168
]
31693169
]
31703170
},
3171+
"contrast": {
3172+
"overloads": [
3173+
[
3174+
"Color"
3175+
]
3176+
]
3177+
},
31713178
"setRed": {
31723179
"overloads": [
31733180
[

src/strands/strands_api.js

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -139,22 +139,31 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
139139
});
140140
return createStrandsNode(id, dimension, strandsContext);
141141
};
142-
// Next is type constructors and uniform functions
142+
143+
// Next is type constructors and uniform functions.
144+
// For some of them, we have aliases so that you can write either a more human-readable
145+
// variant or also one more directly translated from GLSL, or to be more compatible with
146+
// APIs we documented at the release of 2.x and have to continue supporting.
143147
for (const type in DataType) {
144148
if (type === BaseType.DEFER) {
145149
continue;
146150
}
147151
const typeInfo = DataType[type];
152+
const typeAliases = [];
148153
let pascalTypeName;
149154
if (/^[ib]vec/.test(typeInfo.fnName)) {
150155
pascalTypeName = typeInfo.fnName
151-
.slice(0, 2).toUpperCase()
152-
+ typeInfo.fnName
153-
.slice(2)
154-
.toLowerCase();
156+
.slice(0, 2).toUpperCase()
157+
+ typeInfo.fnName
158+
.slice(2)
159+
.toLowerCase();
160+
typeAliases.push(pascalTypeName.replace('Vec', 'Vector'));
155161
} else {
156162
pascalTypeName = typeInfo.fnName.charAt(0).toUpperCase()
157-
+ typeInfo.fnName.slice(1).toLowerCase();
163+
+ typeInfo.fnName.slice(1);
164+
if (pascalTypeName === 'Sampler2D') {
165+
typeAliases.push('Texture')
166+
}
158167
}
159168
fn[`uniform${pascalTypeName}`] = function(name, defaultValue) {
160169
const { id, dimension } = build.variableNode(strandsContext, typeInfo, name);
@@ -183,11 +192,12 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
183192

184193
// Alias varying* as shared* for backward compatibility
185194
fn[`varying${pascalTypeName}`] = fn[`shared${pascalTypeName}`];
186-
if (pascalTypeName.startsWith('Vec')) {
195+
for (const typeAlias of typeAliases) {
187196
// For compatibility, also alias uniformVec2 as uniformVector2, what we initially
188197
// documented these as
189-
fn[`uniform${pascalTypeName.replace('Vec', 'Vector')}`] = fn[`uniform${pascalTypeName}`];
190-
fn[`varying${pascalTypeName.replace('Vec', 'Vector')}`] = fn[`varying${pascalTypeName}`];
198+
fn[`uniform${typeAlias}`] = fn[`uniform${pascalTypeName}`];
199+
fn[`varying${typeAlias}`] = fn[`varying${pascalTypeName}`];
200+
fn[`shared${typeAlias}`] = fn[`shared${pascalTypeName}`];
191201
}
192202
const originalp5Fn = fn[typeInfo.fnName];
193203
fn[typeInfo.fnName] = function(...args) {

test/types/strands.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Based on https://beta.p5js.org/tutorials/intro-to-p5-strands/
2+
3+
import '../../types/global'
4+
5+
let starShader: p5.Shader;
6+
let starStrokeShader: p5.Shader;
7+
let stars: p5.Geometry;
8+
let originalImage: p5.Framebuffer;
9+
let pixelateShader: p5.Shader;
10+
let fresnelShader: p5.Shader;
11+
let bloomShader: p5.Shader;
12+
13+
function fresnelShaderCallback() {
14+
const fresnelPower = uniformFloat(2);
15+
const fresnelBias = uniformFloat(-0.1);
16+
const fresnelScale = uniformFloat(2);
17+
18+
getCameraInputs((inputs) => {
19+
let n = normalize(inputs.normal);
20+
let v = normalize(-inputs.position);
21+
let base = 1.0 - dot(n, v);
22+
let fresnel = fresnelScale * pow(base, fresnelPower) + fresnelBias;
23+
let col = mix([0, 0, 0], [1, .5, .7], fresnel);
24+
inputs.color = [col, 1];
25+
return inputs;
26+
});
27+
}
28+
29+
function starShaderCallback() {
30+
const time = uniformFloat(() => millis());
31+
const skyRadius = uniformFloat(250);
32+
33+
function rand2(st) {
34+
return fract(sin(dot(st, [12.9898, 78.233])) * 43758.5453123);
35+
}
36+
37+
function semiSphere() {
38+
let id = instanceID();
39+
let theta = rand2([id, 0.1234]) * TWO_PI + time / 100000;
40+
let phi = rand2([id, 3.321]) * PI + time / 50000;
41+
42+
let r = skyRadius;
43+
r *= sin(phi);
44+
let x = r * sin(phi) * cos(theta);
45+
let y = r * 1.5 * cos(phi);
46+
let z = r * sin(phi) * sin(theta);
47+
return [x, y, z];
48+
}
49+
50+
getWorldInputs((inputs) => {
51+
inputs.position += semiSphere();
52+
return inputs;
53+
});
54+
55+
getObjectInputs((inputs) => {
56+
let size = 1 + 0.5 * sin(time * 0.002 + instanceID());
57+
inputs.position *= size;
58+
return inputs;
59+
});
60+
}
61+
62+
function pixelateShaderCallback() {
63+
const pixelCountX = uniformFloat(()=> 280);
64+
65+
getColor((inputs, canvasContent) => {
66+
const aspectRatio = inputs.canvasSize.x / inputs.canvasSize.y;
67+
const pixelSize = [pixelCountX, pixelCountX / aspectRatio];
68+
69+
let coord = inputs.texCoord;
70+
// @ts-ignore
71+
coord = floor(coord * pixelSize) / pixelSize;
72+
73+
let col = getTexture(canvasContent, coord);
74+
return col//[coord, 0, 1];
75+
});
76+
}
77+
78+
function bloomShaderCallback() {
79+
const preBlur = uniformSampler2D(() => originalImage);
80+
81+
getColor((input, canvasContent) => {
82+
const blurredCol = getTexture(canvasContent, input.texCoord);
83+
const originalCol = getTexture(preBlur, input.texCoord);
84+
85+
const intensity = max(originalCol, 0.1) * 12.2;
86+
87+
const bloom = originalCol + blurredCol * intensity;
88+
return [bloom.rgb, 1];
89+
});
90+
}
91+
92+
async function setup(){
93+
createCanvas(800, 600, WEBGL);
94+
pixelDensity(1);
95+
stars = buildGeometry(() => sphere(8, 4, 2))
96+
originalImage = createFramebuffer();
97+
98+
starShader = baseMaterialShader().modify(starShaderCallback);
99+
starStrokeShader = baseStrokeShader().modify(starShaderCallback)
100+
fresnelShader = baseColorShader().modify(fresnelShaderCallback);
101+
bloomShader = baseFilterShader().modify(bloomShaderCallback);
102+
pixelateShader = baseFilterShader().modify(pixelateShaderCallback);
103+
}
104+
105+
function draw(){
106+
originalImage.begin();
107+
background(0);
108+
orbitControl();
109+
110+
push()
111+
strokeWeight(2)
112+
stroke(255,0,0)
113+
rotateX(PI/2 + millis() * 0.0005);
114+
fill(255,100, 150)
115+
strokeShader(starStrokeShader)
116+
shader(starShader);
117+
model(stars, 1000);
118+
pop()
119+
120+
push()
121+
shader(fresnelShader)
122+
noStroke()
123+
sphere(90);
124+
filter(pixelateShader);
125+
pop()
126+
127+
originalImage.end();
128+
129+
imageMode(CENTER)
130+
image(originalImage, 0, 0)
131+
132+
filter(BLUR, 15)
133+
filter(bloomShader);
134+
}

test/unit/visual/cases/webgl.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -679,7 +679,6 @@ visualSuite('WebGL', function() {
679679
p5.createCanvas(50, 50, p5.WEBGL);
680680
const tex = await p5.loadImage('/unit/assets/cat.jpg');
681681
p5.texture(tex);
682-
p5.texture(tex);
683682
p5.rect(-20, -20, 40, 40);
684683
screenshot();
685684
});
@@ -688,9 +687,25 @@ visualSuite('WebGL', function() {
688687
p5.createCanvas(50, 50, p5.WEBGL);
689688
const tex = await p5.loadImage('/unit/assets/cat.jpg');
690689
p5.texture(tex);
691-
p5.texture(tex);
692690
p5.rect(-20, -20, 40, 40, 10);
693691
screenshot();
694692
});
695693
});
694+
695+
visualSuite('textures in p5.strands', async () => {
696+
visualTest('uniformTexture() works', async (p5, screenshot) => {
697+
p5.createCanvas(50, 50, p5.WEBGL);
698+
const tex = await p5.loadImage('/unit/assets/cat.jpg');
699+
const shader = p5.baseMaterialShader().modify(() => {
700+
const texUniform = p5.uniformTexture(() => tex)
701+
p5.getPixelInputs((inputs) => {
702+
inputs.color = p5.getTexture(texUniform, inputs.texCoord);
703+
return inputs;
704+
});
705+
}, { p5, tex });
706+
p5.shader(shader);
707+
p5.rect(-20, -20, 40, 40);
708+
screenshot();
709+
});
710+
});
696711
});
5.17 KB
Loading
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"numScreenshots": 1
3+
}

utils/typescript.mjs

Lines changed: 58 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,27 @@ allRawData.forEach(entry => {
3636

3737
// Process strands functions to extract p5 methods
3838
function processStrandsFunctions() {
39-
const strandsMethods = [];
39+
const strandsMethods = [
40+
{
41+
name: 'instanceID',
42+
overloads: [{
43+
params: [],
44+
return: {
45+
type: { type: 'NameExpression', name: 'any' } // Return 'any' for strands nodes
46+
}
47+
}],
48+
description: `Returns the ID when drawing many instances`,
49+
static: false
50+
},
51+
{
52+
name: 'discard',
53+
overloads: [{
54+
params: [],
55+
}],
56+
description: `Discards the current pixel`,
57+
static: false
58+
},
59+
];
4060

4161
// Add ALL GLSL builtin functions (both isp5Function: true and false)
4262
for (const [functionName, overloads] of Object.entries(builtInGLSLFunctions)) {
@@ -61,62 +81,61 @@ function processStrandsFunctions() {
6181
}
6282

6383
// Add uniform functions: uniformFloat, uniformVec2, etc.
64-
const uniformMethods = [];
84+
const typeMethods = [];
6585
for (const type in DataType) {
6686
if (type === 'defer') {
6787
continue;
6888
}
6989

7090
const typeInfo = DataType[type];
7191
let pascalTypeName;
92+
const typeAliases = [];
7293

7394
if (/^[ib]vec/.test(typeInfo.fnName)) {
7495
pascalTypeName = typeInfo.fnName
7596
.slice(0, 2).toUpperCase()
7697
+ typeInfo.fnName
7798
.slice(2)
7899
.toLowerCase();
100+
typeAliases.push(pascalTypeName.replace('Vec', 'Vector'));
79101
} else {
80102
pascalTypeName = typeInfo.fnName.charAt(0).toUpperCase()
81-
+ typeInfo.fnName.slice(1).toLowerCase();
103+
+ typeInfo.fnName.slice(1);
104+
if (pascalTypeName === 'Sampler2D') {
105+
typeAliases.push('Texture')
106+
}
82107
}
83108

84-
const uniformMethodName = `uniform${pascalTypeName}`;
85-
const uniformMethod = {
86-
name: uniformMethodName,
87-
overloads: [{
88-
params: [
89-
{
90-
name: 'name',
91-
type: { type: 'NameExpression', name: 'String' },
92-
optional: false
93-
},
94-
{
95-
name: 'defaultValue',
96-
type: { type: 'NameExpression', name: 'any' },
97-
optional: true
109+
typeMethods.push(...[pascalTypeName, ...typeAliases].flatMap((typeName) => [
110+
{
111+
name: `uniform${typeName}`,
112+
overloads: [{
113+
params: [
114+
{
115+
name: 'defaultValue',
116+
type: { type: 'NameExpression', name: 'any' },
117+
optional: true
118+
}
119+
],
120+
return: {
121+
type: { type: 'NameExpression', name: 'any' }
98122
}
99-
],
100-
return: {
101-
type: { type: 'NameExpression', name: 'any' }
102-
}
103-
}],
104-
description: `Create a ${pascalTypeName} uniform variable`,
105-
static: false
106-
};
107-
108-
uniformMethods.push(uniformMethod);
109-
110-
// Add Vector aliases for Vec types
111-
if (pascalTypeName.startsWith('Vec')) {
112-
const vectorMethodName = `uniform${pascalTypeName.replace('Vec', 'Vector')}`;
113-
const vectorMethod = {
114-
...uniformMethod,
115-
name: vectorMethodName,
116-
description: `Create a ${pascalTypeName.replace('Vec', 'Vector')} uniform variable`
117-
};
118-
uniformMethods.push(vectorMethod);
119-
}
123+
}],
124+
description: `Create a ${pascalTypeName} uniform variable`,
125+
static: false
126+
},
127+
...['varying', 'shared'].map((prefix) => ({
128+
name: `${prefix}${typeName}`,
129+
overloads: [{
130+
params: [],
131+
return: {
132+
type: { type: 'NameExpression', name: 'any' }
133+
}
134+
}],
135+
description: `Create a shared ${pascalTypeName} to pass data between hooks`,
136+
static: false
137+
}))
138+
]));
120139
}
121140

122141
// Add type casting functions (DataType constructor functions)
@@ -148,7 +167,7 @@ function processStrandsFunctions() {
148167
typeCastingMethods.push(castingMethod);
149168
}
150169

151-
return [...strandsMethods, ...uniformMethods, ...typeCastingMethods];
170+
return [...strandsMethods, ...typeMethods, ...typeCastingMethods];
152171
}
153172

154173
// TypeScript-specific type conversion from raw type objects

0 commit comments

Comments
 (0)