Skip to content

Commit 082953a

Browse files
authored
Merge branch 'dev-2.0' into fix-lerpcolor-reference-links
2 parents 9f69881 + 33369be commit 082953a

File tree

22 files changed

+577
-57
lines changed

22 files changed

+577
-57
lines changed

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
"test/**/*.js": "eslint",
2121
"utils/**/*.{js,mjs}": "eslint"
2222
},
23-
"version": "2.2.1-rc.0",
23+
"version": "2.2.2-rc.0",
2424
"dependencies": {
2525
"@davepagurek/bezier-path": "^0.0.7",
2626
"@japont/unicode-range": "^1.0.0",

src/core/friendly_errors/sketch_verifier.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ export const verifierUtils = {
8080

8181
try {
8282
const ast = parse(code, {
83-
ecmaVersion: 2021,
83+
ecmaVersion: 'latest',
8484
sourceType: 'module',
8585
locations: true // This helps us get the line number.
8686
});

src/strands/ir_builders.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ export function unaryOpNode(strandsContext, nodeOrValue, opCode) {
4343
const { dag, cfg } = strandsContext;
4444
let dependsOn;
4545
let node;
46-
if (nodeOrValue instanceof StrandsNode) {
46+
if (nodeOrValue?.isStrandsNode) {
4747
node = nodeOrValue;
4848
} else {
4949
const { id, dimension } = primitiveConstructorNode(strandsContext, { baseType: BaseType.FLOAT, dimension: null }, nodeOrValue);
@@ -257,6 +257,20 @@ export function constructTypeFromIDs(strandsContext, typeInfo, strandsNodesArray
257257

258258
export function primitiveConstructorNode(strandsContext, typeInfo, dependsOn) {
259259
const cfg = strandsContext.cfg;
260+
dependsOn = (Array.isArray(dependsOn) ? dependsOn : [dependsOn])
261+
.flat(Infinity)
262+
.map(a => {
263+
if (
264+
a.isStrandsNode &&
265+
a.typeInfo().baseType === BaseType.INT &&
266+
// TODO: handle ivec inputs instead of just int scalars
267+
a.typeInfo().dimension === 1
268+
) {
269+
return castToFloat(strandsContext, a);
270+
} else {
271+
return a;
272+
}
273+
});
260274
const { mappedDependencies, inferredTypeInfo } = mapPrimitiveDepsToIDs(strandsContext, typeInfo, dependsOn);
261275

262276
const finalType = {
@@ -272,6 +286,24 @@ export function primitiveConstructorNode(strandsContext, typeInfo, dependsOn) {
272286
return { id, dimension: finalType.dimension, components: mappedDependencies };
273287
}
274288

289+
export function castToFloat(strandsContext, dep) {
290+
const { id, dimension } = functionCallNode(
291+
strandsContext,
292+
strandsContext.backend.getTypeName('float', dep.typeInfo().dimension),
293+
[dep],
294+
{
295+
overloads: [{
296+
params: [dep.typeInfo()],
297+
returnType: {
298+
...dep.typeInfo(),
299+
baseType: BaseType.FLOAT,
300+
},
301+
}],
302+
}
303+
);
304+
return createStrandsNode(id, dimension, strandsContext);
305+
}
306+
275307
export function structConstructorNode(strandsContext, structTypeInfo, rawUserArgs) {
276308
const { cfg, dag } = strandsContext;
277309
const { identifer, properties } = structTypeInfo;
@@ -491,7 +523,7 @@ export function swizzleTrap(id, dimension, strandsContext, onRebind) {
491523
// This may not be the most efficient way, as we swizzle each component individually,
492524
// so that .xyz becomes .x, .y, .z
493525
let scalars = [];
494-
if (value instanceof StrandsNode) {
526+
if (value?.isStrandsNode) {
495527
if (value.dimension === 1) {
496528
scalars = Array(chars.length).fill(value);
497529
} else if (value.dimension === chars.length) {

src/strands/strands_api.js

Lines changed: 48 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function _getBuiltinGlobalsCache(strandsContext) {
5656
function getBuiltinGlobalNode(strandsContext, name) {
5757
const spec = BUILTIN_GLOBAL_SPECS[name]
5858
if (!spec) return null
59-
59+
6060
const cache = _getBuiltinGlobalsCache(strandsContext)
6161
const uniformName = `_p5_global_${name}`
6262
const cached = cache.nodes.get(uniformName)
@@ -154,7 +154,7 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
154154
}
155155

156156
// Convert value to a StrandsNode if it isn't already
157-
const valueNode = value instanceof StrandsNode ? value : p5.strandsNode(value);
157+
const valueNode = value?.isStrandsNode ? value : p5.strandsNode(value);
158158

159159
// Create a new CFG block for the early return
160160
const earlyReturnBlockID = CFG.createBasicBlock(cfg, BlockType.DEFAULT);
@@ -369,12 +369,17 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
369369
fn[typeInfo.fnName] = function(...args) {
370370
if (strandsContext.active) {
371371
if (args.length === 1 && args[0].dimension && args[0].dimension === typeInfo.dimension) {
372-
const { id, dimension } = build.functionCallNode(strandsContext, typeInfo.fnName, args, {
373-
overloads: [{
374-
params: [args[0].typeInfo()],
375-
returnType: typeInfo,
376-
}]
377-
});
372+
const { id, dimension } = build.functionCallNode(
373+
strandsContext,
374+
strandsContext.backend.getTypeName(typeInfo.baseType, typeInfo.dimension),
375+
args,
376+
{
377+
overloads: [{
378+
params: [args[0].typeInfo()],
379+
returnType: typeInfo,
380+
}]
381+
}
382+
);
378383
return createStrandsNode(id, dimension, strandsContext);
379384
} else {
380385
// For vector types with a single argument, repeat it for each component
@@ -431,7 +436,7 @@ function createHookArguments(strandsContext, parameters){
431436
const oldDependsOn = dag.dependsOn[structNode.id];
432437
const newDependsOn = [...oldDependsOn];
433438
let newValueID;
434-
if (val instanceof StrandsNode) {
439+
if (val?.isStrandsNode) {
435440
newValueID = val.id;
436441
}
437442
else {
@@ -463,7 +468,7 @@ function createHookArguments(strandsContext, parameters){
463468
return args;
464469
}
465470
function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName) {
466-
if (!(returned instanceof StrandsNode)) {
471+
if (!(returned?.isStrandsNode)) {
467472
// try {
468473
const result = build.primitiveConstructorNode(strandsContext, expectedType, returned);
469474
return result.id;
@@ -485,7 +490,12 @@ function enforceReturnTypeMatch(strandsContext, expectedType, returned, hookName
485490
}
486491
if (receivedType.dimension !== expectedType.dimension) {
487492
if (receivedType.dimension !== 1) {
488-
FES.userError('type error', `You have returned a vector with ${receivedType.dimension} components in ${hookName} when a ${expectedType.baseType + expectedType.dimension} was expected!`);
493+
const receivedTypeDisplay = receivedType.baseType + (receivedType.dimension > 1 ? receivedType.dimension : '');
494+
const expectedTypeDisplay = expectedType.baseType + expectedType.dimension;
495+
FES.userError('type error',
496+
`You have returned a ${receivedTypeDisplay} in ${hookName} when a ${expectedTypeDisplay} was expected!\n\n` +
497+
`Make sure your hook returns the correct type.`
498+
);
489499
}
490500
else {
491501
const result = build.primitiveConstructorNode(strandsContext, expectedType, returned);
@@ -573,10 +583,27 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) {
573583
const handleRetVal = (retNode) => {
574584
if(isStructType(expectedReturnType)) {
575585
const expectedStructType = structType(expectedReturnType);
576-
if (retNode instanceof StrandsNode) {
586+
if (retNode?.isStrandsNode) {
577587
const returnedNode = getNodeDataFromID(strandsContext.dag, retNode.id);
578588
if (returnedNode.baseType !== expectedStructType.typeName) {
579-
FES.userError("type error", `You have returned a ${retNode.baseType} from ${hookType.name} when a ${expectedStructType.typeName} was expected.`);
589+
const receivedTypeName = returnedNode.baseType || 'undefined';
590+
const receivedDim = dag.dimensions[retNode.id];
591+
const receivedTypeDisplay = receivedDim > 1 ?
592+
`${receivedTypeName}${receivedDim}` : receivedTypeName;
593+
594+
const expectedProps = expectedStructType.properties
595+
.map(p => p.name).join(', ');
596+
FES.userError('type error',
597+
`You have returned a ${receivedTypeDisplay} from ${hookType.name} when a ${expectedStructType.typeName} was expected.\n\n` +
598+
`The ${expectedStructType.typeName} struct has these properties: { ${expectedProps} }\n\n` +
599+
`Instead of returning a different type, you should modify and return the ${expectedStructType.typeName} struct that was passed to your hook.\n\n` +
600+
`For example:\n` +
601+
`${hookType.name}((inputs) => {\n` +
602+
` // Modify properties of inputs\n` +
603+
` inputs.someProperty = ...;\n` +
604+
` return inputs; // Return the modified struct\n` +
605+
`})`
606+
);
580607
}
581608
const newDeps = returnedNode.dependsOn.slice();
582609
for (let i = 0; i < expectedStructType.properties.length; i++) {
@@ -595,10 +622,14 @@ export function createShaderHooksFunctions(strandsContext, fn, shader) {
595622
const propName = expectedProp.name;
596623
const receivedValue = retNode[propName];
597624
if (receivedValue === undefined) {
598-
FES.userError('type error', `You've returned an incomplete struct from ${hookType.name}.\n` +
599-
`Expected: { ${expectedReturnType.properties.map(p => p.name).join(', ')} }\n` +
600-
`Received: { ${Object.keys(retNode).join(', ')} }\n` +
601-
`All of the properties are required!`);
625+
const expectedProps = expectedReturnType.properties.map(p => p.name).join(', ');
626+
const receivedProps = Object.keys(retNode).join(', ');
627+
FES.userError('type error',
628+
`You've returned an incomplete ${expectedStructType.typeName} struct from ${hookType.name}.\n\n` +
629+
`Expected properties: { ${expectedProps} }\n` +
630+
`Received properties: { ${receivedProps} }\n\n` +
631+
`All properties are required! Make sure to include all properties in the returned struct.`
632+
);
602633
}
603634
const expectedTypeInfo = expectedProp.dataType;
604635
const returnedPropID = enforceReturnTypeMatch(strandsContext, expectedTypeInfo, receivedValue, hookType.name);

src/strands/strands_for.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ export class StrandsFor {
309309
let initialVar = this.initialCb();
310310

311311
// Convert to StrandsNode if it's not already one
312-
if (!(initialVar instanceof StrandsNode)) {
312+
if (!(initialVar?.isStrandsNode)) {
313313
const { id, dimension } = primitiveConstructorNode(this.strandsContext, { baseType: BaseType.FLOAT, dimension: 1 }, initialVar);
314314
initialVar = createStrandsNode(id, dimension, this.strandsContext);
315315
}

src/strands/strands_node.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export class StrandsNode {
4040
const baseType = orig?.baseType ?? BaseType.FLOAT;
4141

4242
let newValueID;
43-
if (value instanceof StrandsNode) {
43+
if (value?.isStrandsNode) {
4444
newValueID = value.id;
4545
} else {
4646
const newVal = primitiveConstructorNode(
@@ -95,7 +95,7 @@ export class StrandsNode {
9595
const baseType = orig?.baseType ?? BaseType.FLOAT;
9696

9797
let newValueID;
98-
if (value instanceof StrandsNode) {
98+
if (value?.isStrandsNode) {
9999
newValueID = value.id;
100100
} else {
101101
const newVal = primitiveConstructorNode(

src/webgl/loading.js

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -593,12 +593,10 @@ function loading(p5, fn){
593593
const vertString = tokens[vertexTokens[tokenInd]];
594594
let vertParts = vertString.split('/');
595595

596-
// TODO: Faces can technically use negative numbers to refer to the
597-
// previous nth vertex. I haven't seen this used in practice, but
598-
// it might be good to implement this in the future.
599-
600596
for (let i = 0; i < vertParts.length; i++) {
601-
vertParts[i] = parseInt(vertParts[i]) - 1;
597+
let index = parseInt(vertParts[i]);
598+
if (index > 0) index -= 1; // OBJ uses 1-based indexing
599+
vertParts[i] = index;
602600
}
603601

604602
if (!usedVerts[vertString]) {
@@ -607,11 +605,11 @@ function loading(p5, fn){
607605

608606
if (usedVerts[vertString][currentMaterial] === undefined) {
609607
const vertIndex = model.vertices.length;
610-
model.vertices.push(loadedVerts.v[vertParts[0]].copy());
611-
model.uvs.push(loadedVerts.vt[vertParts[1]] ?
612-
loadedVerts.vt[vertParts[1]].slice() : [0, 0]);
613-
model.vertexNormals.push(loadedVerts.vn[vertParts[2]] ?
614-
loadedVerts.vn[vertParts[2]].copy() : new Vector());
608+
model.vertices.push(loadedVerts.v.at(vertParts[0]).copy());
609+
model.uvs.push(loadedVerts.vt.at(vertParts[1]) ?
610+
loadedVerts.vt.at(vertParts[1]).slice() : [0, 0]);
611+
model.vertexNormals.push(loadedVerts.vn.at(vertParts[2]) ?
612+
loadedVerts.vn.at(vertParts[2]).copy() : new Vector());
615613

616614
usedVerts[vertString][currentMaterial] = vertIndex;
617615
face.push(vertIndex);

0 commit comments

Comments
 (0)