diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index 161d2c366c..2fedb173fb 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -19,6 +19,7 @@ import { import { ColorSpace, to, + toGamut, serialize, parse, range, @@ -127,7 +128,7 @@ class Color { }); this._cachedMode = mode; this._cachedColor = to(this._cachedColor, this._cachedColor.spaceId); - } catch { + } catch (err) { // TODO: Invalid color string throw new Error('Invalid color string'); } @@ -304,6 +305,11 @@ class Color { }); } + // Will do conversion in-Gamut as out of Gamut conversion is only really useful for futher conversions + #toColorMode(mode) { + return new Color(this._color, mode); + } + // Get raw coordinates of underlying library, can differ between libraries get _array() { return this._getRGBA(); diff --git a/src/core/filterShaders.js b/src/core/filterShaders.js index 2dcfdd6097..9c265c3c33 100644 --- a/src/core/filterShaders.js +++ b/src/core/filterShaders.js @@ -101,7 +101,7 @@ export function makeFilterShader(renderer, operation, p5) { const maxSamples = 64.0; let numSamples = p5.floor(radius * 7.0); - if (p5.mod(numSamples, 2) === 0.0) { + if (p5.mod(numSamples, 2) == 0.0) { numSamples++; } @@ -162,7 +162,7 @@ export function makeFilterShader(renderer, operation, p5) { for (let x = -1; x <= 1; x++) { for (let y = -1; y <= 1; y++) { - if (x !== 0 || y !== 0) { + if (x != 0 || y != 0) { const offset = p5.vec2(x, y) * inputs.texelSize; const neighborColor = p5.getTexture( canvasContent, @@ -198,7 +198,7 @@ export function makeFilterShader(renderer, operation, p5) { for (let x = -1; x <= 1; x++) { for (let y = -1; y <= 1; y++) { - if (x !== 0 || y !== 0) { + if (x != 0 || y != 0) { const offset = p5.vec2(x, y) * inputs.texelSize; const neighborColor = p5.getTexture( canvasContent, diff --git a/src/core/main.js b/src/core/main.js index 7d00494fa2..31126e659b 100644 --- a/src/core/main.js +++ b/src/core/main.js @@ -400,7 +400,7 @@ class p5 { for (const p in p5.prototype) { try { delete window[p]; - } catch { + } catch (x) { window[p] = undefined; } } @@ -408,7 +408,7 @@ class p5 { if (this.hasOwnProperty(p2)) { try { delete window[p2]; - } catch { + } catch (x) { window[p2] = undefined; } } diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index cdaab0905a..50c22dcd8b 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -447,5 +447,25 @@ function renderer(p5, fn) { p5.Renderer = Renderer; } +/** + * Helper fxn to measure ascent and descent. + * Adapted from http://stackoverflow.com/a/25355178 + * @private + */ +function calculateOffset(object) { + let currentLeft = 0, + currentTop = 0; + if (object.offsetParent) { + do { + currentLeft += object.offsetLeft; + currentTop += object.offsetTop; + } while ((object = object.offsetParent)); + } else { + currentLeft += object.offsetLeft; + currentTop += object.offsetTop; + } + return [currentLeft, currentTop]; +} + export default renderer; export { Renderer }; diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index e8e92eca97..bd7ab167fa 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -11,6 +11,8 @@ import { Matrix } from '../math/p5.Matrix'; import { PrimitiveToPath2DConverter } from '../shape/custom_shapes'; import { DefaultFill, textCoreConstants } from '../type/textCore'; +const styleEmpty = 'rgba(0,0,0,0)'; + class Renderer2D extends Renderer { constructor(pInst, w, h, isMainCanvas, elt, attributes = {}) { super(pInst, w, h, isMainCanvas); @@ -149,7 +151,7 @@ class Renderer2D extends Renderer { for (const savedKey in props) { try { this.drawingContext[savedKey] = props[savedKey]; - } catch { + } catch (err) { // ignore read-only property errors } } diff --git a/src/core/p5.Renderer3D.js b/src/core/p5.Renderer3D.js index 047188820d..e59f60fb60 100644 --- a/src/core/p5.Renderer3D.js +++ b/src/core/p5.Renderer3D.js @@ -437,6 +437,13 @@ export class Renderer3D extends Renderer { } } + remove() { + this.wrappedElt.remove(); + this.wrappedElt = null; + this.canvas = null; + this.elt = null; + } + ////////////////////////////////////////////// // Geometry Building ////////////////////////////////////////////// @@ -1344,7 +1351,7 @@ export class Renderer3D extends Renderer { for (const savedKey in props) { try { this.drawingContext[savedKey] = props[savedKey]; - } catch { + } catch (err) { // ignore read-only property errors } } @@ -1927,7 +1934,7 @@ export class Renderer3D extends Renderer { throw Error('_yAlignOffset: height is required'); } - let { textLeading, textBaseline, textSize } = this.states; + let { textLeading, textBaseline, textSize, textFont } = this.states; let yOff = 0, numLines = dataArr.length; let totalHeight = @@ -2169,10 +2176,6 @@ export class Renderer3D extends Renderer { if (this._textCanvas) { this._textCanvas.parentElement.removeChild(this._textCanvas); } - this.wrappedElt.remove(); - this.wrappedElt = null; - this.canvas = null; - this.elt = null; super.remove(); } } diff --git a/src/dom/p5.MediaElement.js b/src/dom/p5.MediaElement.js index 63a683913c..019fcbf8e3 100644 --- a/src/dom/p5.MediaElement.js +++ b/src/dom/p5.MediaElement.js @@ -5,7 +5,7 @@ import { Element } from './p5.Element'; // import { friendlyAutoplayError } from '../friendly_errors/fes_core'; -import { FES } from '../friendly_errors/fes'; +import { FES, TL } from '../friendly_errors/fes'; /** * @typedef {'video'} VIDEO @@ -934,7 +934,7 @@ class MediaElement extends Element { try { audioContext = obj.context; mainOutput = audioContext.destination; - } catch { + } catch (e) { throw 'connect() is meant to be used with Web Audio API or p5.sound.js'; } } @@ -1639,7 +1639,7 @@ function media(p5, fn) { } else { domElement.src = window.URL.createObjectURL(stream); } - } catch { + } catch (err) { domElement.src = stream; } }) diff --git a/src/friendly_errors/param_validator.js b/src/friendly_errors/param_validator.js index d29a1cd91a..0f8a08c39f 100644 --- a/src/friendly_errors/param_validator.js +++ b/src/friendly_errors/param_validator.js @@ -555,8 +555,6 @@ function validateParams(p5, fn, lifecycles) { message = FES.log`Expected ${match[1]} at the ${position} parameter in ${func + '()'}.`; break; } - // Unrecognized custom errors fall through to the default logging below. - // falls through } default: { console.log('Zod error object', currentError); @@ -566,7 +564,7 @@ function validateParams(p5, fn, lifecycles) { if (isVersionError) { FES.log`${message}`(); } else { - const [, stacktrace] = processStack( + const [_null, stacktrace] = processStack( null, errorStackParser.parse(Error()).slice(3) ); @@ -626,7 +624,7 @@ function validateParams(p5, fn, lifecycles) { success: true, data: funcSchemas.parse(args) }; - } catch { + } catch (error) { const closestSchema = findClosestSchema(funcSchemas, args); const zodError = closestSchema.safeParse(args).error; const errorMessage = friendlyParamError(zodError, func, args); diff --git a/src/friendly_errors/stacktrace.js b/src/friendly_errors/stacktrace.js index 015e429705..bf92d00831 100644 --- a/src/friendly_errors/stacktrace.js +++ b/src/friendly_errors/stacktrace.js @@ -325,6 +325,12 @@ export const processStack = (error, stacktrace) => { // from user's code if (friendlyStack.length === 0) return [true, null]; + // get the function just above the topmost frame in the friendlyStack. + // i.e the name of the library function called from user's code + const func = stacktrace[friendlyStack[0].frameIndex - 1].functionName + .split('.') + .slice(-1)[0]; + // Try and get the location (line no.) from the top element of the stack let locationObj; if ( @@ -345,8 +351,6 @@ export const processStack = (error, stacktrace) => { } // Library error - // `func` below is the name of the library function called from user's code, - // i.e. stacktrace[friendlyStack[0].frameIndex - 1].functionName. // const message = TL.tl`${locationObj ? TL.tl`[${locationObj.file}, line ${locationObj.line}]` : ''} An error with message "${error.message}" occurred inside the p5js library when ${func} was called. If not stated otherwise, it might be an issue with the arguments passed to ${func}.`; // p5._friendlyError( // message, diff --git a/src/image/p5.Image.js b/src/image/p5.Image.js index 4823ad8bf4..423d7f1cf8 100644 --- a/src/image/p5.Image.js +++ b/src/image/p5.Image.js @@ -48,7 +48,13 @@ class Image { if (typeof density !== 'undefined') { // Setter: set the density and handle resize if (density <= 0) { - // TODO: report an INVALID_VALUE param error through the FES here. + const errorObj = { + type: 'INVALID_VALUE', + format: { types: ['Number'] }, + position: 1 + }; + + // p5._friendlyParamError(errorObj, 'pixelDensity'); // Default to 1 in case of an invalid value density = 1; diff --git a/src/io/csv.js b/src/io/csv.js index 9dda273e8b..bf279d60f7 100644 --- a/src/io/csv.js +++ b/src/io/csv.js @@ -233,5 +233,5 @@ function inferType(value) { } function escapeRegExp(str) { - return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&'); + return str.replace(/[-\[\]/\{}\()\*+\?.\\^\$|]/g, '\\$&'); } diff --git a/src/io/files.js b/src/io/files.js index bbbb9d5d52..8dce5b6839 100644 --- a/src/io/files.js +++ b/src/io/files.js @@ -1146,7 +1146,6 @@ function files(p5, fn) { case 'xml': // NOTE: still need to normalize type handling/mapping // datatype = 'xml'; - // falls through case 'txt': default: datatype = 'text'; @@ -2120,6 +2119,17 @@ function files(p5, fn) { // The following line is CC BY SA 3 by user Fregante https://stackoverflow.com/a/23522755 return /^((?!chrome|android).)*safari/i.test(navigator.userAgent); }; + + /** + * Helper function, a callback for download that deletes + * an invisible anchor element from the DOM once the file + * has been automatically downloaded. + * + * @private + */ + function destroyClickedElement(event) { + document.body.removeChild(event.target); + } } export default files; diff --git a/src/math/Matrices/Matrix.js b/src/math/Matrices/Matrix.js index 91d3ecc263..6a09f7bede 100644 --- a/src/math/Matrices/Matrix.js +++ b/src/math/Matrices/Matrix.js @@ -2005,9 +2005,6 @@ export class Matrix extends MatrixInterface { * @return {Number} Determinant of our 4×4 matrix * @private */ - // Kept private until the determinant API is made public; see the skipped - // 'Determinant' tests in test/unit/math/p5.Matrix.js. - // oxlint-disable-next-line no-unused-private-class-members #determinant4x4() { if (this.#sqDimention !== 4) { throw new Error( diff --git a/src/math/Matrices/MatrixInterface.js b/src/math/Matrices/MatrixInterface.js index 8c33b09be6..8f428e219a 100644 --- a/src/math/Matrices/MatrixInterface.js +++ b/src/math/Matrices/MatrixInterface.js @@ -5,6 +5,8 @@ if (typeof Float32Array !== 'undefined') { isMatrixArray = x => Array.isArray(x) || x instanceof Float32Array; } export class MatrixInterface { + // Private field to store the matrix + #matrix = null; constructor(...args) { if (this.constructor === MatrixInterface) { throw new Error("Class is of abstract type and can't be instantiated"); diff --git a/src/math/Matrices/MatrixNumjs.js b/src/math/Matrices/MatrixNumjs.js index 374f7f7142..1e719ffea5 100644 --- a/src/math/Matrices/MatrixNumjs.js +++ b/src/math/Matrices/MatrixNumjs.js @@ -10,8 +10,10 @@ import { MatrixInterface } from './MatrixInterface'; * Reference/Global_Objects/SIMD */ +let GLMAT_ARRAY_TYPE = Array; let isMatrixArray = x => Array.isArray(x); if (typeof Float32Array !== 'undefined') { + GLMAT_ARRAY_TYPE = Float32Array; isMatrixArray = x => Array.isArray(x) || x instanceof Float32Array; } @@ -129,6 +131,7 @@ export class MatrixNumjs extends MatrixInterface { * @return {MatrixNumjs} the copy of the MatrixNumjs object */ get() { + let temp = new MatrixNumjs(this.mat4); return new MatrixNumjs(this.mat4); } @@ -519,6 +522,7 @@ export class MatrixNumjs extends MatrixInterface { x = x[0]; // must be last } this._mat4 = this._mat4.flatten(); + const vect = nj.array([x, y, z, 1]); this._mat4.set(0, x * this._mat4.get(0)); this._mat4.set(1, x * this._mat4.get(1)); this._mat4.set(2, x * this._mat4.get(2)); @@ -801,11 +805,12 @@ export class MatrixNumjs extends MatrixInterface { * @chainable */ mult3x3(multMatrix) { + let _src; let tempMatrix = multMatrix; if (multMatrix === this || multMatrix === this._mat3) { // mat3; // only need to allocate in this rare case } else if (multMatrix instanceof MatrixNumjs) { - // tempMatrix already holds the matrix we need + _src = multMatrix.mat3; } else if (isMatrixArray(multMatrix)) { multMatrix._mat3 = nj.array(arguments); } else if (arguments.length === 9) { diff --git a/src/strands/ir_dag.js b/src/strands/ir_dag.js index 1ac64ae3e6..31dbb474f4 100644 --- a/src/strands/ir_dag.js +++ b/src/strands/ir_dag.js @@ -2,6 +2,7 @@ import { NodeTypeRequiredFields, NodeTypeToName, BasePriority, + StatementType, BaseType } from './ir_types'; import * as FES from './strands_FES'; @@ -148,6 +149,11 @@ function createNode(graph, node) { return id; } +function getNodeKey(node) { + const key = JSON.stringify(node); + return key; +} + function validateNode(node) { const nodeType = node.nodeType; const requiredFields = NodeTypeRequiredFields[nodeType]; diff --git a/src/strands/ir_types.js b/src/strands/ir_types.js index 51a7ba447c..65a17769b1 100644 --- a/src/strands/ir_types.js +++ b/src/strands/ir_types.js @@ -282,8 +282,8 @@ export const ConstantFolding = { [OpCode.Binary.MULTIPLY]: (a, b) => a * b, [OpCode.Binary.DIVIDE]: (a, b) => a / b, [OpCode.Binary.MODULO]: (a, b) => a % b, - [OpCode.Binary.EQUAL]: (a, b) => a === b, - [OpCode.Binary.NOT_EQUAL]: (a, b) => a !== b, + [OpCode.Binary.EQUAL]: (a, b) => a == b, + [OpCode.Binary.NOT_EQUAL]: (a, b) => a != b, [OpCode.Binary.GREATER_THAN]: (a, b) => a > b, [OpCode.Binary.GREATER_EQUAL]: (a, b) => a >= b, [OpCode.Binary.LESS_THAN]: (a, b) => a < b, diff --git a/src/strands/strands_api.js b/src/strands/strands_api.js index 2e085dfe33..da61d5290a 100644 --- a/src/strands/strands_api.js +++ b/src/strands/strands_api.js @@ -5,7 +5,9 @@ import { DataType, BaseType, structType, + TypeInfoFromGLSLName, isStructType, + OpCode, StatementType, NodeType, HOOK_PARAM_PREFIX diff --git a/src/strands/strands_codegen.js b/src/strands/strands_codegen.js index 0e1577f299..df450c29e9 100644 --- a/src/strands/strands_codegen.js +++ b/src/strands/strands_codegen.js @@ -1,5 +1,11 @@ import { sortCFG } from './ir_cfg'; -import { structType } from './ir_types'; +import * as DAG from './ir_dag'; +import { + NodeType, + StatementType, + structType, + TypeInfoFromGLSLName +} from './ir_types'; export function generateShaderCode(strandsContext) { const { diff --git a/src/strands/strands_conditionals.js b/src/strands/strands_conditionals.js index 4e4f571652..cccd5af144 100644 --- a/src/strands/strands_conditionals.js +++ b/src/strands/strands_conditionals.js @@ -1,7 +1,7 @@ import * as CFG from './ir_cfg'; import * as DAG from './ir_dag'; import { BlockType, NodeType } from './ir_types'; -import { createStrandsNode } from './strands_node'; +import { StrandsNode, createStrandsNode } from './strands_node'; import { createPhiNode } from './strands_phi_utils'; export class StrandsConditional { constructor(strandsContext, condition, branchCallback) { diff --git a/src/strands/strands_for.js b/src/strands/strands_for.js index 11aea9f0ae..d035395201 100644 --- a/src/strands/strands_for.js +++ b/src/strands/strands_for.js @@ -7,7 +7,7 @@ import { StatementType, OpCode } from './ir_types'; -import { createStrandsNode } from './strands_node'; +import { StrandsNode, createStrandsNode } from './strands_node'; import { primitiveConstructorNode } from './ir_builders'; import { createPhiNode } from './strands_phi_utils'; diff --git a/src/strands/strands_transpiler.js b/src/strands/strands_transpiler.js index a22b670e1d..95d23765c8 100644 --- a/src/strands/strands_transpiler.js +++ b/src/strands/strands_transpiler.js @@ -1910,6 +1910,26 @@ function transformHelperFunctionEarlyReturns(ast, names) { * This staged approach ensures correct ordering and avoids transformation conflicts. */ +// Wraps each callback with a uniform context guard, eliminating the need +// to repeat the early-return check at the top of every handler. +function makeGuardedCallbacks(callbacks) { + const guarded = {}; + for (const [name, fn] of Object.entries(callbacks)) { + guarded[name] = (node, state, ancestors) => { + if ( + ancestors.some( + a => + nodeIsUniform(a) || + nodeIsUniformCallbackFn(a, state.uniformCallbackNames) + ) + ) + return; + return fn(node, state, ancestors); + }; + } + return guarded; +} + function runNonControlFlowPass( ast, uniformCallbackNames, diff --git a/src/type/p5.Font.js b/src/type/p5.Font.js index 8a5ead0cab..d93352b763 100644 --- a/src/type/p5.Font.js +++ b/src/type/p5.Font.js @@ -843,7 +843,7 @@ export class Font { } _position(renderer, lines, bounds, width, height) { - let { textAlign, textLeading } = renderer.states; + let { textAlign, textLeading, textSize } = renderer.states; let metrics = this._measureTextDefault(renderer, 'X'); let ascent = metrics.fontBoundingBoxAscent; @@ -1078,7 +1078,8 @@ function createFontFace(name, path, descriptors, rawFont) { if ((rawFont?.fvar?.length ?? 0) > 0) { descriptors = descriptors || {}; - for (const [tag, minVal, , maxVal] of rawFont.fvar[0]) { + for (const [tag, minVal, defaultVal, maxVal, flags, name] of rawFont + .fvar[0]) { if (tag === 'wght') { descriptors.weight = `${minVal} ${maxVal}`; } else if (tag === 'wdth') { @@ -1445,7 +1446,7 @@ function font(p5, fn) { let info; try { info = await fetch(path, { method: 'HEAD' }); - } catch { + } catch (e) { // Sometimes files fail when requested with HEAD. Fallback to a // regular GET. It loads more data, but at least then it's cached // for the likely case when we have to fetch the whole thing. @@ -1496,7 +1497,7 @@ function font(p5, fn) { } fontData = await fn.parseFontData(url); } - } catch {} + } catch (_e) {} return create(this, name, src, fontDescriptors, fontData); }, loadWithoutData: () => create(this, name, src, fontDescriptors) @@ -1576,7 +1577,7 @@ function font(p5, fn) { // create a FontFace object and pass it to the p5.Font constructor pfont = await create(this, name, path, descriptors, fontData); - } catch { + } catch (err) { // failed to parse the font, load it as a simple FontFace let ident = name || diff --git a/src/type/textCore.js b/src/type/textCore.js index a72854c2c6..72bb835e6f 100644 --- a/src/type/textCore.js +++ b/src/type/textCore.js @@ -23,7 +23,7 @@ function textCore(p5, fn) { const LinebreakRe = /\r?\n/g; const CommaDelimRe = /,\s+/; const QuotedRe = /^".*"$/; - const SpecialCharRe = /\P{ASCII}/u; // Non-ascii + const SpecialCharRe = /[^\x00-\x7F]/; // Non-ascii const TabsRe = /\t/g; const FontVariationSettings = 'fontVariationSettings'; @@ -1810,6 +1810,14 @@ function textCore(p5, fn) { } } + if (0 && opts?.ignoreRectMode) { + // draw bounds for debugging + let ss = context.strokeStyle; + context.strokeStyle = 'green'; + context.strokeRect(bounds.x, bounds.y, bounds.w, bounds.h); + context.strokeStyle = ss; + } + context.textBaseline = setBaseline; // restore baseline return { bounds, lines }; @@ -1858,7 +1866,7 @@ function textCore(p5, fn) { if (this.textCanvas().style[opt] !== value) { // fails on precision for floating points, also quotes and spaces - if (debug) + if (0) console.warn( `Unable to set '${opt}' property` + // FES? ' on canvas.style. It may not be supported. Expected "' + @@ -1901,8 +1909,28 @@ function textCore(p5, fn) { if (this.states.fontWeight !== val) this.textWeight(val); return val; case 'wdth': - // TODO: map the numeric 'wdth' axis onto the allowed font-stretch - // keywords (ultra-condensed ... ultra-expanded) by nearest value. + if (0) { + // attempt to map font-stretch to allowed keywords + const FontStretchMap = { + 'ultra-condensed': 50, + 'extra-condensed': 62.5, + condensed: 75, + 'semi-condensed': 87.5, + normal: 100, + 'semi-expanded': 112.5, + expanded: 125, + 'extra-expanded': 150, + 'ultra-expanded': 200 + }; + let values = Object.values(FontStretchMap); + const indexArr = values.map(function (k) { + return Math.abs(k - val); + }); + const min = Math.min.apply(Math, indexArr); + let idx = indexArr.indexOf(min); + let stretch = Object.keys(FontStretchMap)[idx]; + this.states.setValue('fontStretch', stretch); + } break; case 'ital': if (debug) diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 332a16073b..5c6c270745 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -1961,7 +1961,6 @@ function primitives3D(p5, fn) { this.bezierVertex(x3, y3, z3); this.bezierVertex(x4, y4, z4); this.endShape(); - this.bezierOrder(prevOrder); }; // pretier-ignore diff --git a/src/webgl/loading.js b/src/webgl/loading.js index 51bb504cc4..1c6271c499 100755 --- a/src/webgl/loading.js +++ b/src/webgl/loading.js @@ -13,7 +13,7 @@ async function fileExists(url) { try { const response = await fetch(url, { method: 'HEAD' }); return response.ok; - } catch { + } catch (error) { return false; } } @@ -645,7 +645,7 @@ function loading(p5, fn) { const parsedMaterials = await Promise.all(parsedMaterialPromises); const materials = Object.assign({}, ...parsedMaterials); return materials; - } catch { + } catch (error) { return {}; } } @@ -756,6 +756,7 @@ function loading(p5, fn) { // material per kept face, aligned with model.faces, for bucketing later const faceMaterials = []; let hasColoredVertices = false; + let hasColorlessVertices = false; for (let line = 0; line < lines.length; ++line) { // Each line is a separate object (vertex, face, vertex normal, etc) // For each line, split it into tokens on whitespace. The first token @@ -833,6 +834,7 @@ function loading(p5, fn) { model.vertexColors.push(materialDiffuseColor[2]); model.vertexColors.push(1); } else { + hasColorlessVertices = true; model.vertexColors.push(-1, -1, -1, -1); } } else { diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 57d1b9726d..e344a4a467 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -13,9 +13,11 @@ import { Renderer3D } from '../core/p5.Renderer3D'; import { getStrokeDefs } from './enums'; import { Shader } from './p5.Shader'; import { MipmapTexture } from './p5.Texture'; +import { Framebuffer } from './p5.Framebuffer'; import { RGB, RGBA } from '../color/creating_reading'; import { Image } from '../image/p5.Image'; import { glslBackend } from './strands_glslBackend'; +import { TypeInfoFromGLSLName } from '../strands/ir_types.js'; import { getShaderHookTypes } from './shaderHookUtils'; import filterBaseVert from './shaders/filters/base.vert'; @@ -278,7 +280,7 @@ class RendererGL extends Renderer3D { geometry.lineVertices.length / 3, count ); - } catch { + } catch (e) { console.log( '🌸 p5.js says: Instancing is only supported in WebGL2 mode' ); @@ -320,7 +322,7 @@ class RendererGL extends Renderer3D { 0, count ); - } catch { + } catch (e) { console.log( '🌸 p5.js says: Instancing is only supported in WebGL2 mode' ); @@ -340,7 +342,7 @@ class RendererGL extends Renderer3D { } else { try { gl.drawArraysInstanced(glMode, 0, geometry.vertices.length, count); - } catch { + } catch (e) { console.log( '🌸 p5.js says: Instancing is only supported in WebGL2 mode' ); diff --git a/src/webgl/strands_glslBackend.js b/src/webgl/strands_glslBackend.js index 8fd3ccaa81..44f9ebf7c1 100644 --- a/src/webgl/strands_glslBackend.js +++ b/src/webgl/strands_glslBackend.js @@ -353,7 +353,7 @@ export const glslBackend = { } return node.identifier; - case NodeType.OPERATION: { + case NodeType.OPERATION: const useParantheses = node.usedBy.length > 0; if (node.opCode === OpCode.Nary.CONSTRUCTOR) { // TODO: differentiate casts and constructors for more efficient codegen. @@ -460,10 +460,6 @@ export const glslBackend = { const sym = OpCodeToSymbol[node.opCode]; return `${sym}${val}`; } - return FES.internalError( - `Operation with opCode ${node.opCode} is not supported in expressions` - ); - } case NodeType.PHI: // Phi nodes represent conditional merging of values // If this phi node has an identifier (like varying variables), use that @@ -486,14 +482,19 @@ export const glslBackend = { ); } else { throw new Error(`No valid inputs for node`); + // Fallback: create a default value + const typeName = this.getTypeName(node.baseType, node.dimension); + if (node.dimension === 1) { + return node.baseType === BaseType.FLOAT ? '0.0' : '0'; + } else { + return `${typeName}(0.0)`; + } } } case NodeType.ASSIGNMENT: - return FES.internalError( - `ASSIGNMENT nodes should not be used as expressions` - ); + FES.internalError(`ASSIGNMENT nodes should not be used as expressions`); default: - return FES.internalError( + FES.internalError( `${NodeTypeToName[node.nodeType]} code generation not implemented yet` ); } diff --git a/src/webgl/text.js b/src/webgl/text.js index 6aa530f67e..f10c8e8ea1 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -87,7 +87,7 @@ function text(p5, fn) { try { // create a new image imageData = new ImageData(this.width, this.height); - } catch { + } catch (err) { // for browsers that don't support ImageData constructors (ie IE11) // create an ImageData using the old method let canvas = document.getElementsByTagName('canvas')[0]; diff --git a/src/webgl/utils.js b/src/webgl/utils.js index f8d17679be..0d29e143e4 100644 --- a/src/webgl/utils.js +++ b/src/webgl/utils.js @@ -1,4 +1,5 @@ import * as constants from '../core/constants'; +import { INSTANCE_ID_VARYING_NAME } from '../strands/ir_types'; import { Texture } from './p5.Texture'; /** diff --git a/src/webgpu/p5.RendererWebGPU.js b/src/webgpu/p5.RendererWebGPU.js index a55f8e7253..e2990e79a4 100644 --- a/src/webgpu/p5.RendererWebGPU.js +++ b/src/webgpu/p5.RendererWebGPU.js @@ -6,7 +6,7 @@ import * as constants from '../core/constants'; import { getStrokeDefs } from '../webgl/enums'; -import { DataType } from '../strands/ir_types.js'; +import { DataType, INSTANCE_ID_VARYING_NAME } from '../strands/ir_types.js'; import { colorVertexShader, colorFragmentShader } from './shaders/color'; import { lineVertexShader, lineFragmentShader } from './shaders/line'; @@ -2424,7 +2424,7 @@ function rendererWebGPU(p5, fn) { }; while ((match = elementRegex.exec(structBody)) !== null) { - const [, location, name, type] = match; + const [_, location, name, type] = match; const { size, align, pack, packInPlace, baseType } = baseAlignAndSize(type); offset = Math.ceil(offset / align) * align; @@ -2481,7 +2481,7 @@ function rendererWebGPU(p5, fn) { ? shader.computeSrc() : shader.vertSrc(); while ((match = uniformVarRegex.exec(src)) !== null) { - const [, groupNum, binding, varName, structType] = match; + const [_, groupNum, binding, varName, structType] = match; const bindingIndex = parseInt(binding); const uniforms = this._parseStruct(src, structType); @@ -2547,7 +2547,7 @@ function rendererWebGPU(p5, fn) { let match; while ((match = samplerRegex.exec(src)) !== null) { - const [, group, binding, name, type] = match; + const [_, group, binding, name, type] = match; const groupIndex = parseInt(group); const bindingIndex = parseInt(binding); // Skip struct uniform bindings which we've already parsed @@ -2582,7 +2582,7 @@ function rendererWebGPU(p5, fn) { // Parse storage buffers while ((match = storageRegex.exec(src)) !== null) { - const [, group, binding, accessMode, name, elementType] = match; + const [_, group, binding, accessMode, name, elementType] = match; const groupIndex = parseInt(group); const bindingIndex = parseInt(binding); @@ -2634,10 +2634,10 @@ function rendererWebGPU(p5, fn) { if (frag) sources.push([frag, GPUShaderStage.FRAGMENT]); if (compute) sources.push([compute, GPUShaderStage.COMPUTE]); - for (const [src] of sources) { + for (const [src, visibility] of sources) { let match; while ((match = bindingRegex.exec(src)) !== null) { - const [, groupIndex, bindingIndex] = match; + const [_, groupIndex, bindingIndex] = match; if (parseInt(groupIndex) === group) { maxBindingIndex = Math.max(maxBindingIndex, parseInt(bindingIndex)); } @@ -3259,7 +3259,7 @@ ${hookUniformFields}} // Handle instanceID varying for fragment access if (shader.hooks.instanceIDVarying) { - const { declaration, source, interpolation } = + const { name, declaration, source, interpolation } = shader.hooks.instanceIDVarying; const nextLocIndex = this._getNextAvailableLocation( preMain, @@ -3268,7 +3268,7 @@ ${hookUniformFields}} const interpAttr = interpolation ? ` @interpolate(${interpolation})` : ''; - const [varName] = declaration.split(':').map(s => s.trim()); + const [varName, varType] = declaration.split(':').map(s => s.trim()); const structMember = `@location(${nextLocIndex})${interpAttr} ${declaration},`; if (shaderType === 'vertex') { @@ -3318,7 +3318,7 @@ ${hookUniformFields}} } for (const hookDef in shader.hooks.helpers) { const [hookType, hookName] = hookDef.split(' '); - const [, params, body] = /^(\([^)]*\))((?:.|\n)*)$/.exec( + const [_, params, body] = /^(\([^)]*\))((?:.|\n)*)$/.exec( shader.hooks.helpers[hookDef] ); if (hookType === 'void') { @@ -3337,7 +3337,7 @@ ${hookUniformFields}} shader.hooks.modified[shaderType][hookDef] ? 'true' : 'false' };\n`; - let [, params, body] = /^(\([^)]*\))((?:.|\n)*)$/.exec( + let [_, params, body] = /^(\([^)]*\))((?:.|\n)*)$/.exec( shader.hooks[shaderType][hookDef] ); diff --git a/src/webgpu/strands_wgslBackend.js b/src/webgpu/strands_wgslBackend.js index 6159af6965..2d193f172f 100644 --- a/src/webgpu/strands_wgslBackend.js +++ b/src/webgpu/strands_wgslBackend.js @@ -526,7 +526,7 @@ export const wgslBackend = { } else { return node.value; } - case NodeType.VARIABLE: { + case NodeType.VARIABLE: // Track shared variable usage context if ( generationContext.shaderContext && @@ -567,8 +567,7 @@ export const wgslBackend = { } return node.identifier; - } - case NodeType.OPERATION: { + case NodeType.OPERATION: const useParantheses = node.usedBy.length > 0; if (node.opCode === OpCode.Nary.CONSTRUCTOR) { // TODO: differentiate casts and constructors for more efficient codegen. @@ -707,10 +706,6 @@ export const wgslBackend = { const sym = OpCodeToSymbol[node.opCode]; return `${sym}${val}`; } - return FES.internalError( - `Operation with opCode ${node.opCode} is not supported in expressions` - ); - } case NodeType.PHI: // Phi nodes represent conditional merging of values // If this phi node has an identifier (like varying variables), use that @@ -736,11 +731,9 @@ export const wgslBackend = { } } case NodeType.ASSIGNMENT: - return FES.internalError( - `ASSIGNMENT nodes should not be used as expressions` - ); + FES.internalError(`ASSIGNMENT nodes should not be used as expressions`); default: - return FES.internalError( + FES.internalError( `${NodeTypeToName[node.nodeType]} code generation not implemented yet` ); } diff --git a/test/unit/accessibility/outputs.js b/test/unit/accessibility/outputs.js index e4edbd5ad8..3da3291c6a 100644 --- a/test/unit/accessibility/outputs.js +++ b/test/unit/accessibility/outputs.js @@ -5,6 +5,8 @@ import p5 from '../../../src/app.js'; // TODO: Is it possible to test this without a runtime? suite('outputs', function () { + let myID = 'myCanvasID'; + beforeAll(function () { outputs(mockP5, mockP5Prototype); textOutput(mockP5, mockP5Prototype); diff --git a/test/unit/core/sketch_overrides.js b/test/unit/core/sketch_overrides.js index 410b5da7c8..44a045f6a1 100644 --- a/test/unit/core/sketch_overrides.js +++ b/test/unit/core/sketch_overrides.js @@ -1,6 +1,16 @@ import { verifierUtils } from '../../../src/friendly_errors/sketch_verifier.js'; suite('Sketch Verifier', function () { + const mockP5 = { + _validateParameters: vi.fn(), + Color: function () {}, + Vector: function () {}, + prototype: { + rect: function () {}, + ellipse: function () {} + } + }; + afterEach(() => { vi.restoreAllMocks(); vi.unstubAllGlobals(); diff --git a/test/unit/dom/dom.js b/test/unit/dom/dom.js index ba865573d6..4469096e84 100644 --- a/test/unit/dom/dom.js +++ b/test/unit/dom/dom.js @@ -947,14 +947,11 @@ suite('DOM', function () { }); const emptyCallback = () => {}; - // Commented out along with its only callers: the file-input tests further - // down in this suite. Restore this together with them. - // - // const createDummyFile = filename => { - // return new File(['testFileBlob'], filename, { - // type: 'text/plain' - // }); - // }; + const createDummyFile = filename => { + return new File(['testFileBlob'], filename, { + type: 'text/plain' + }); + }; test('should be a function', function () { assert.isFunction(mockP5Prototype.createFileInput); diff --git a/test/unit/image/loading.js b/test/unit/image/loading.js index 4e7c5cafa0..4b53cbb469 100644 --- a/test/unit/image/loading.js +++ b/test/unit/image/loading.js @@ -5,36 +5,32 @@ import image from '../../../src/image/p5.Image'; import p5 from '../../../src/app.js'; import { vi } from 'vitest'; -// Commented out along with its only callers: the 'should draw image with -// defaults' and 'should draw cropped image' tests further down in this suite, -// both of which are parked behind TODOs. Restore this together with them. -// -// /** -// * Expects an image file and a p5 instance with an image file loaded and drawn -// * and checks that they are exactly the same. Sends result to the callback. -// */ -// var testImageRender = function (file, sketch) { -// sketch.loadPixels(); -// var p = sketch.pixels; -// var ctx = sketch; -// -// sketch.clear(); -// -// return new Promise(function (resolve, reject) { -// sketch.loadImage(file, resolve, reject); -// }).then(function (img) { -// ctx.image(img, 0, 0); -// -// ctx.loadPixels(); -// var n = 0; -// for (var i = 0; i < p.length; i++) { -// var diff = Math.abs(p[i] - ctx.pixels[i]); -// n += diff; -// } -// var same = n === 0 && ctx.pixels.length === p.length; -// return same; -// }); -// }; +/** + * Expects an image file and a p5 instance with an image file loaded and drawn + * and checks that they are exactly the same. Sends result to the callback. + */ +var testImageRender = function (file, sketch) { + sketch.loadPixels(); + var p = sketch.pixels; + var ctx = sketch; + + sketch.clear(); + + return new Promise(function (resolve, reject) { + sketch.loadImage(file, resolve, reject); + }).then(function (img) { + ctx.image(img, 0, 0); + + ctx.loadPixels(); + var n = 0; + for (var i = 0; i < p.length; i++) { + var diff = Math.abs(p[i] - ctx.pixels[i]); + n += diff; + } + var same = n === 0 && ctx.pixels.length === p.length; + return same; + }); +}; suite('loading images', function () { const imagePath = '/test/unit/assets/cat.jpg'; diff --git a/test/unit/visual/cases/webgl.js b/test/unit/visual/cases/webgl.js index acc58c9d29..8aa084fd49 100644 --- a/test/unit/visual/cases/webgl.js +++ b/test/unit/visual/cases/webgl.js @@ -1506,7 +1506,7 @@ visualSuite('WebGL', function () { p5.baseMaterialShader().modify(() => { undefined.someMethod(); // This will throw an error }); - } catch {} + } catch (e) {} p5.background('red'); p5.circle(p5.noise(0), p5.noise(0), 20); screenshot(); diff --git a/test/unit/visual/cases/webgpu.js b/test/unit/visual/cases/webgpu.js index 9da7a01bc9..87dbf1a8c8 100644 --- a/test/unit/visual/cases/webgpu.js +++ b/test/unit/visual/cases/webgpu.js @@ -1,3 +1,4 @@ +import { vi } from 'vitest'; import p5 from '../../../../src/app'; import { visualSuite, visualTest } from '../visualTest'; import rendererWebGPU from '../../../../src/webgpu/p5.RendererWebGPU'; diff --git a/test/unit/visual/visualTest.js b/test/unit/visual/visualTest.js index 36ed4e1110..31181d8217 100644 --- a/test/unit/visual/visualTest.js +++ b/test/unit/visual/visualTest.js @@ -1,8 +1,14 @@ import p5 from '../../../src/app.js'; import { server } from 'vitest/browser'; +import { THRESHOLD, DIFFERENCE, ERODE } from '../../../src/core/constants.js'; const { readFile, writeFile } = server.commands; import pixelmatch from 'pixelmatch'; +// By how much can each color channel value (0-255) differ before +// we call it a mismatch? This should be large enough to not trigger +// based on antialiasing. +const COLOR_THRESHOLD = 25; + // The max side length to shrink test images down to before // comparing, for performance. const MAX_SIDE = 50; diff --git a/test/unit/webgl/p5.Framebuffer.js b/test/unit/webgl/p5.Framebuffer.js index 0353f833e6..f29ee150c2 100644 --- a/test/unit/webgl/p5.Framebuffer.js +++ b/test/unit/webgl/p5.Framebuffer.js @@ -496,7 +496,7 @@ suite('p5.Framebuffer', function () { }); test('get() creates a p5.Image matching the source pixel density', function () { - myp5.createCanvas(20, 20, myp5.WEBGL); + const mainCanvas = myp5.createCanvas(20, 20, myp5.WEBGL); myp5.pixelDensity(2); const fbo = myp5.createFramebuffer(); fbo.draw(() => { diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index ae93791d6f..bcd04e97e7 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -2207,7 +2207,7 @@ void main() { }); test('works normally for <50k vertices', function () { - myp5.createCanvas(10, 10, myp5.WEBGL); + const renderer = myp5.createCanvas(10, 10, myp5.WEBGL); const confirmSpy = vi.spyOn(window, 'confirm').mockReturnValue(false); myp5.beginShape(); @@ -2226,7 +2226,7 @@ void main() { suite('color interpolation', function () { test('strokes should interpolate colors between vertices', function () { - myp5.createCanvas(512, 4, myp5.WEBGL); + const renderer = myp5.createCanvas(512, 4, myp5.WEBGL); // far left color: (242, 236, 40) // far right color: (42, 36, 240) @@ -3094,13 +3094,13 @@ void main() { }); test('Maintains stencil test state across draw cycles when user enabled', function () { + let drawCalled = false; + myp5.createCanvas(50, 50, myp5.WEBGL); - // NOTE: redraw() is async and isn't awaited here, so this override runs - // after the assertions below rather than before them. The stencil state - // assertions are what this test actually verifies. const originalDraw = myp5.draw; myp5.draw = function () { + drawCalled = true; if (originalDraw) originalDraw.call(myp5); }; diff --git a/test/unit/webgl/p5.Shader.js b/test/unit/webgl/p5.Shader.js index 06791151af..93ef6be721 100644 --- a/test/unit/webgl/p5.Shader.js +++ b/test/unit/webgl/p5.Shader.js @@ -1930,9 +1930,6 @@ suite('p5.Shader', function () { const testShader = myp5.baseFilterShader().modify( () => { - // The constant comparisons below are the subject of this test: they - // exercise how p5.strands transpiles boolean intermediate variables. - /* oxlint-disable no-constant-binary-expression */ myp5.getColor((inputs, canvasContent) => { let value = 1; let condition = 1 > 2; @@ -1947,7 +1944,6 @@ suite('p5.Shader', function () { return [0.4, 0, 0, 1]; }); - /* oxlint-enable no-constant-binary-expression */ }, { myp5 } ); @@ -1966,9 +1962,6 @@ suite('p5.Shader', function () { const testShader = myp5.baseFilterShader().modify( () => { - // The constant comparisons below are the subject of this test: they - // exercise how p5.strands transpiles boolean intermediate variables. - /* oxlint-disable no-constant-binary-expression */ const conditionMet = () => { let condition = 1 > 2; let value = 1; @@ -1977,7 +1970,6 @@ suite('p5.Shader', function () { } return !condition; }; - /* oxlint-enable no-constant-binary-expression */ myp5.getColor((inputs, canvasContent) => { if (conditionMet()) { return [1, 0, 0, 1]; @@ -2545,7 +2537,7 @@ suite('p5.Shader', function () { for (let xOff = -1; xOff <= 1; xOff++) { for (let yOff = -1; yOff <= 1; yOff++) { - if (xOff !== 0 || yOff !== 0) { + if (xOff != 0 || yOff != 0) { aliveNeighbours += 0.1; } } @@ -2972,8 +2964,7 @@ suite('p5.Shader', function () { test('simple vector multiplication in filter shader', () => { myp5.createCanvas(50, 50, myp5.WEBGL); - // Compiling the shader without throwing is what this test checks. - myp5.baseFilterShader().modify( + const testShader = myp5.baseFilterShader().modify( () => { myp5.getColor((inputs, canvasContent) => { // Test simple scalar * vector operation @@ -3600,8 +3591,6 @@ suite('p5.Shader', function () { expect(() => { myp5.baseMaterialShader().modify( () => { - // The shared variable is consumed by the p5.strands transpiler, not by JS. - /* oxlint-disable-next-line no-unused-vars */ let worldPosX = myp5.sharedVec3(); myp5.getWorldInputs(inputs => { worldPosX = inputs.position.x; // scalar → vec3, valid broadcast @@ -3619,8 +3608,6 @@ suite('p5.Shader', function () { expect(() => { myp5.baseMaterialShader().modify( () => { - // The shared variable is consumed by the p5.strands transpiler, not by JS. - /* oxlint-disable-next-line no-unused-vars */ let myVec = myp5.sharedVec3(); myp5.getWorldInputs(inputs => { myVec = inputs.position.xy; // vec2 → vec3 mismatch @@ -3655,8 +3642,6 @@ suite('p5.Shader', function () { expect(() => { myp5.baseMaterialShader().modify( () => { - // The shared variable is consumed by the p5.strands transpiler, not by JS. - /* oxlint-disable-next-line no-unused-vars */ let myVec = myp5.sharedVec3(); myp5.getWorldInputs(inputs => { myVec = inputs.position; // vec3 → vec3, OK @@ -3698,7 +3683,7 @@ suite('p5.Shader', function () { }, { myp5 } ); - } catch { + } catch (e) { /* expected */ } @@ -3725,7 +3710,7 @@ suite('p5.Shader', function () { }, { myp5 } ); - } catch { + } catch (e) { /* expected */ } @@ -3751,7 +3736,7 @@ suite('p5.Shader', function () { }, { myp5 } ); - } catch { + } catch (e) { /* expected */ } @@ -3781,7 +3766,7 @@ suite('p5.Shader', function () { }, { myp5 } ); - } catch { + } catch (e) { /* expected */ } @@ -3845,13 +3830,11 @@ suite('p5.Shader', function () { () => { myp5.getWorldInputs.begin(); myp5.getWorldInputs.end(); - // Reading `.position` outside the hook scope is what should error. - /* oxlint-disable-next-line no-unused-vars */ const pos = myp5.getWorldInputs.position; }, { myp5 } ); - } catch { + } catch (e) { /* expected */ } diff --git a/test/unit/webgl/p5.Texture.js b/test/unit/webgl/p5.Texture.js index a3c53da6fc..7c3d79bc36 100644 --- a/test/unit/webgl/p5.Texture.js +++ b/test/unit/webgl/p5.Texture.js @@ -217,8 +217,8 @@ suite('p5.Texture', function () { }); test('Set global wrap mode to clamp', function () { myp5.textureWrap(myp5.CLAMP); - myp5._renderer.getTexture(texImg1); - myp5._renderer.getTexture(texImg2); + var tex1 = myp5._renderer.getTexture(texImg1); + var tex2 = myp5._renderer.getTexture(texImg2); expect(texParamSpy).toHaveBeenCalledWith( myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, @@ -242,8 +242,8 @@ suite('p5.Texture', function () { }); test('Set global wrap mode to repeat', function () { myp5.textureWrap(myp5.REPEAT); - myp5._renderer.getTexture(texImg1); - myp5._renderer.getTexture(texImg2); + var tex1 = myp5._renderer.getTexture(texImg1); + var tex2 = myp5._renderer.getTexture(texImg2); expect(texParamSpy).toHaveBeenCalledWith( myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, @@ -267,8 +267,8 @@ suite('p5.Texture', function () { }); test('Set global wrap mode to mirror', function () { myp5.textureWrap(myp5.MIRROR); - myp5._renderer.getTexture(texImg1); - myp5._renderer.getTexture(texImg2); + var tex1 = myp5._renderer.getTexture(texImg1); + var tex2 = myp5._renderer.getTexture(texImg2); expect(texParamSpy).toHaveBeenCalledWith( myp5._renderer.GL.TEXTURE_2D, myp5._renderer.GL.TEXTURE_WRAP_S, diff --git a/test/unit/webgpu/p5.Shader.js b/test/unit/webgpu/p5.Shader.js index 4003152397..5c20a6b199 100644 --- a/test/unit/webgpu/p5.Shader.js +++ b/test/unit/webgpu/p5.Shader.js @@ -691,9 +691,6 @@ suite('WebGPU p5.Shader', function () { const testShader = myp5.baseFilterShader().modify( () => { - // The constant comparisons below are the subject of this test: they - // exercise how p5.strands transpiles boolean intermediate variables. - /* oxlint-disable no-constant-binary-expression */ myp5.getColor((inputs, canvasContent) => { let value = 1; let condition = 1 > 2; @@ -708,7 +705,6 @@ suite('WebGPU p5.Shader', function () { return [0.4, 0, 0, 1]; }); - /* oxlint-enable no-constant-binary-expression */ }, { myp5 } ); @@ -727,9 +723,6 @@ suite('WebGPU p5.Shader', function () { const testShader = myp5.baseFilterShader().modify( () => { - // The constant comparisons below are the subject of this test: they - // exercise how p5.strands transpiles boolean intermediate variables. - /* oxlint-disable no-constant-binary-expression */ const conditionMet = () => { let condition = 1 > 2; let value = 1; @@ -738,7 +731,6 @@ suite('WebGPU p5.Shader', function () { } return !condition; }; - /* oxlint-enable no-constant-binary-expression */ myp5.getColor((inputs, canvasContent) => { if (conditionMet()) { return [1, 0, 0, 1]; @@ -1624,8 +1616,7 @@ suite('WebGPU p5.Shader', function () { test('simple vector multiplication in filter shader', async () => { await myp5.createCanvas(50, 50, myp5.WEBGPU); - // Compiling the shader without throwing is what this test checks. - myp5.baseFilterShader().modify( + const testShader = myp5.baseFilterShader().modify( () => { myp5.getColor((inputs, canvasContent) => { // Test simple scalar * vector operation @@ -1888,12 +1879,9 @@ suite('WebGPU p5.Shader', function () { () => { const buf = myp5.uniformStorage(); const id = myp5.index.x; - if (id === 0) { + if (id == 0) { buf[0] = 1.0; return; - // The statement after the early return is the subject of this - // test: p5.strands must not emit it. - /* oxlint-disable-next-line no-unreachable */ buf[0] = 2.0; // Should not execute } }, @@ -2077,8 +2065,6 @@ suite('WebGPU p5.Shader', function () { expect(() => { myp5.baseMaterialShader().modify( () => { - // The shared variable is consumed by the p5.strands transpiler, not by JS. - /* oxlint-disable-next-line no-unused-vars */ let worldPosX = myp5.sharedVec3(); myp5.getWorldInputs(inputs => { worldPosX = inputs.position.x; // scalar → vec3, valid broadcast @@ -2096,8 +2082,6 @@ suite('WebGPU p5.Shader', function () { expect(() => { myp5.baseMaterialShader().modify( () => { - // The shared variable is consumed by the p5.strands transpiler, not by JS. - /* oxlint-disable-next-line no-unused-vars */ let myVec = myp5.sharedVec3(); myp5.getWorldInputs(inputs => { myVec = inputs.position.xy; // vec2 → vec3 mismatch @@ -2132,8 +2116,6 @@ suite('WebGPU p5.Shader', function () { expect(() => { myp5.baseMaterialShader().modify( () => { - // The shared variable is consumed by the p5.strands transpiler, not by JS. - /* oxlint-disable-next-line no-unused-vars */ let myVec = myp5.sharedVec3(); myp5.getWorldInputs(inputs => { myVec = inputs.position; // vec3 → vec3, OK diff --git a/utils/contributors-png.js b/utils/contributors-png.js index 40d8b02997..2686f1685f 100644 --- a/utils/contributors-png.js +++ b/utils/contributors-png.js @@ -23,7 +23,7 @@ async function loadAvatar(url) { const buffer = Buffer.from(await res.arrayBuffer()); return await loadImage(buffer); - } catch { + } catch (err) { return null; } } diff --git a/utils/contributors-png.mjs b/utils/contributors-png.mjs index 1258021d8a..ec13c50c8f 100644 --- a/utils/contributors-png.mjs +++ b/utils/contributors-png.mjs @@ -22,7 +22,7 @@ async function loadAvatar(url) { if (!res.ok) throw new Error(`HTTP ${res.status}`); const buffer = Buffer.from(await res.arrayBuffer()); return await loadImage(buffer); - } catch { + } catch (err) { return null; } }