diff --git a/preview/index.html b/preview/index.html index 18f9d06e37..d0f3b329ae 100644 --- a/preview/index.html +++ b/preview/index.html @@ -20,14 +20,27 @@ import p5 from '../src/app.js'; const sketch = function (p) { + let fbo; p.setup = async function () { - p.createCanvas(400, 400); + p.createCanvas(400, 400, p.WEBGL); + fbo = p.createFramebuffer() }; p.draw = function () { + p.push(); p.background(200); + fbo.begin() + p.background('blue') p.strokeWeight(10); + p.push() + p.stroke('red') p.line(-100, -100, 100, 100); + p.pop() + p.translate(200, 200) + p.line(-100, -100, 100, 100); + fbo.end() + p.image(fbo, 0, 0) + p.pop(); }; }; diff --git a/src/color/creating_reading.js b/src/color/creating_reading.js index 026cff0154..4d73073524 100644 --- a/src/color/creating_reading.js +++ b/src/color/creating_reading.js @@ -52,15 +52,13 @@ function creatingReading(p5, fn){ [OKLAB]: [100, [-125, 125], [-125, 125], 1], [OKLCH]: [100, 150, 360, 1], clone: function(){ - let ret = {}; - for(const key in this){ - if(typeof this[key] !== 'function'){ - ret[key] = structuredClone(this[key]); - }else{ - ret[key] = this[key]; + const cloned = { ...this }; + for (const key in cloned) { + if (cloned[key] instanceof Array) { + cloned[key] = [...cloned[key]]; } } - return ret; + return cloned; } }; diff --git a/src/color/p5.Color.js b/src/color/p5.Color.js index 2a2a3824ac..d9f7bb95ff 100644 --- a/src/color/p5.Color.js +++ b/src/color/p5.Color.js @@ -33,6 +33,8 @@ import HSBSpace from './color_spaces/hsb.js'; const map = (n, start1, stop1, start2, stop2) => ((n - start1) / (stop1 - start1) * (stop2 - start2) + start2); +const serializationMap = {}; + class Color { // Reference to underlying color object depending on implementation // Not meant to be used publicly unless the implementation is known for sure @@ -242,10 +244,16 @@ class Color { * */ toString(format) { - // NOTE: memoize - return serialize(this._color, { - format - }); + const key = `${this._color.space.id}-${this._color.coords.join(",")}-${this._color.alpha}-${format}`; + let colorString = serializationMap[key]; + + if(!colorString){ + colorString = serialize(this._color, { + format + }); + serializationMap[key] = colorString; + } + return colorString; } /** diff --git a/src/color/setting.js b/src/color/setting.js index 1a233ca9bb..0028aebe43 100644 --- a/src/color/setting.js +++ b/src/color/setting.js @@ -964,9 +964,10 @@ function setting(p5, fn){ ].includes(mode) ) { // Set color mode. - this._renderer.states.colorMode = mode; + this._renderer.states.setValue('colorMode', mode); // Set color maxes. + this._renderer.states.setValue('colorMaxes', this._renderer.states.colorMaxes.clone()); const maxes = this._renderer.states.colorMaxes[mode]; if (arguments.length === 2) { maxes[0] = max1; // Red @@ -1335,7 +1336,7 @@ function setting(p5, fn){ * */ fn.noStroke = function() { - this._renderer.states.strokeColor = null; + this._renderer.states.setValue('strokeColor', null); return this; }; diff --git a/src/core/States.js b/src/core/States.js new file mode 100644 index 0000000000..a70a6300c2 --- /dev/null +++ b/src/core/States.js @@ -0,0 +1,33 @@ +export class States { + #modified = {}; + + constructor(initialState) { + for (const key in initialState) { + this[key] = initialState[key]; + } + } + + setValue(key, value) { + if (!(key in this.#modified)) { + this.#modified[key] = this[key]; + } + this[key] = value; + } + + getDiff() { + const diff = this.#modified; + this.#modified = {}; + return diff; + } + + getModified() { + return this.#modified; + } + + applyDiff(prevModified) { + for (const key in this.#modified) { + this[key] = this.#modified[key]; + } + this.#modified = prevModified; + } +} diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index e5ba194d63..ea4e875014 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -9,6 +9,7 @@ import * as constants from '../core/constants'; import { Image } from '../image/p5.Image'; import { Vector } from '../math/p5.Vector'; import { Shape } from '../shape/custom_shapes'; +import { States } from './States'; class ClonableObject { constructor(obj = {}) { @@ -70,15 +71,7 @@ class Renderer { } // Renderer state machine - this.states = Object.assign({}, Renderer.states); - // Clone properties that support it - for (const key in this.states) { - if (this.states[key] instanceof Array) { - this.states[key] = this.states[key].slice(); - } else if (this.states[key] && this.states[key].clone instanceof Function) { - this.states[key] = this.states[key].clone(); - } - } + this.states = new States(Renderer.states); this.states.strokeColor = new Color([0, 0, 0]); this.states.fillColor = new Color([255, 255, 255]); @@ -122,33 +115,25 @@ class Renderer { // and push it into the push pop stack push() { this._pushPopDepth++; - const currentStates = Object.assign({}, this.states); - // Clone properties that support it - for (const key in currentStates) { - if (currentStates[key] instanceof Array) { - currentStates[key] = currentStates[key].slice(); - } else if (currentStates[key] && currentStates[key].clone instanceof Function) { - currentStates[key] = currentStates[key].clone(); - } - } - this._pushPopStack.push(currentStates); - return currentStates; + this._pushPopStack.push(this.states.getDiff()); } // Pop the previous states out of the push pop stack and // assign it back to the current state pop() { this._pushPopDepth--; - Object.assign(this.states, this._pushPopStack.pop()); - this.updateShapeVertexProperties(); - this.updateShapeProperties(); + const diff = this._pushPopStack.pop() || {}; + const modified = this.states.getModified(); + this.states.applyDiff(diff); + this.updateShapeVertexProperties(modified); + this.updateShapeProperties(modified); } bezierOrder(order) { if (order === undefined) { return this.states.bezierOrder; } else { - this.states.bezierOrder = order; + this.states.setValue('bezierOrder', order); this.updateShapeProperties(); } } @@ -165,6 +150,7 @@ class Renderer { if (value === undefined) { return this.states.splineProperties[key]; } else { + this.states.setValue('splineProperties', this.states.splineProperties.clone()); this.states.splineProperties[key] = value; } this.updateShapeProperties(); @@ -182,7 +168,7 @@ class Renderer { if (d === undefined) { return this.states.curveDetail; } else { - this.states.curveDetail = d; + this.states.setValue('curveDetail', d); } } @@ -277,31 +263,31 @@ class Renderer { } fill(...args) { - this.states.fillSet = true; - this.states.fillColor = this._pInst.color(...args); + this.states.setValue('fillSet', true); + this.states.setValue('fillColor', this._pInst.color(...args)); this.updateShapeVertexProperties(); } noFill() { - this.states.fillColor = null; + this.states.setValue('fillColor', null); } strokeWeight(w) { if (w === undefined) { return this.states.strokeWeight; } else { - this.states.strokeWeight = w; + this.states.setValue('strokeWeight', w); } } stroke(...args) { - this.states.strokeSet = true; - this.states.strokeColor = this._pInst.color(...args); + this.states.setValue('strokeSet', true); + this.states.setValue('strokeColor', this._pInst.color(...args)); this.updateShapeVertexProperties(); } noStroke() { - this.states.strokeColor = null; + this.states.setValue('strokeColor', null); } getCommonVertexProperties() { @@ -314,25 +300,31 @@ class Renderer { } } - updateShapeProperties() { - this.currentShape.bezierOrder(this.states.bezierOrder); - this.currentShape.splineProperty('ends', this.states.splineProperties.ends); - this.currentShape.splineProperty('tightness', this.states.splineProperties.tightness); + updateShapeProperties(modified) { + if (!modified || modified.bezierOrder || modified.splineProperties) { + const shape = this.currentShape; + shape.bezierOrder(this.states.bezierOrder); + shape.splineProperty('ends', this.states.splineProperties.ends); + shape.splineProperty('tightness', this.states.splineProperties.tightness); + } } - updateShapeVertexProperties() { + updateShapeVertexProperties(modified) { const props = this.getCommonVertexProperties(); - for (const key in props) { - this.currentShape[key](props[key]); + if (!modified || Object.keys(modified).some((k) => k in props)) { + const shape = this.currentShape; + for (const key in props) { + shape[key](props[key]); + } } } textSize(s) { if (typeof s === 'number') { - this.states.textSize = s; + this.states.setValue('textSize', s); if (!this.states.leadingSet) { // only use a default value if not previously set (#5181) - this.states.textLeading = s * constants._DEFAULT_LEADMULT; + this.states.setValue('textLeading', s * constants._DEFAULT_LEADMULT); } return this._applyTextProperties(); } @@ -342,8 +334,8 @@ class Renderer { textLeading (l) { if (typeof l === 'number') { - this.states.leadingSet = true; - this.states.textLeading = l; + this.states.setValue('leadingSet', true); + this.states.setValue('textLeading', l); return this._pInst; } @@ -358,7 +350,7 @@ class Renderer { s === constants.BOLD || s === constants.BOLDITALIC ) { - this.states.fontStyle = s; + this.states.setValue('fontStyle', s); } return this._applyTextProperties(); @@ -383,10 +375,10 @@ class Renderer { textAlign (h, v) { if (typeof h !== 'undefined') { - this.states.textAlign = h; + this.states.setValue('textAlign', h); if (typeof v !== 'undefined') { - this.states.textBaseline = v; + this.states.setValue('textBaseline', v); } return this._applyTextProperties(); @@ -399,7 +391,7 @@ class Renderer { } textWrap (wrapStyle) { - this.states.textWrap = wrapStyle; + this.states.setValue('textWrap', wrapStyle); return this.states.textWrap; } @@ -647,8 +639,8 @@ class Renderer { _updateTextMetrics() { if (this._isOpenType()) { - this.states.textAscent = this.states.textFont._textAscent(); - this.states.textDescent = this.states.textFont._textDescent(); + this.states.setValue('textAscent', this.states.textFont._textAscent()); + this.states.setValue('textDescent', this.states.textFont._textDescent()); return this; } @@ -684,8 +676,8 @@ class Renderer { document.body.removeChild(container); - this.states.textAscent = ascent; - this.states.textDescent = descent; + this.states.setValue('textAscent', ascent); + this.states.setValue('textDescent', descent); return this; } diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 84b37034f7..311d5d12fc 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -119,7 +119,8 @@ class Renderer2D extends Renderer { } _applyDefaults() { - this._cachedFillStyle = this._cachedStrokeStyle = undefined; + this.states.setValue('_cachedFillStyle', undefined); + this.states.setValue('_cachedStrokeStyle', undefined); this._cachedBlendMode = constants.BLEND; this._setFill(constants._DEFAULT_FILL); this._setStroke(constants._DEFAULT_STROKE); @@ -163,7 +164,7 @@ class Renderer2D extends Renderer { ////////////////////////////////////////////// background(...args) { - this.drawingContext.save(); + this.push(); this.resetMatrix(); if (args[0] instanceof Image) { @@ -176,7 +177,6 @@ class Renderer2D extends Renderer { this._pInst.image(args[0], 0, 0, this.width, this.height); } } else { - const curFill = this._getFill(); // create background rect const color = this._pInst.color(...args); @@ -193,14 +193,12 @@ class Renderer2D extends Renderer { } this.drawingContext.fillRect(0, 0, this.width, this.height); - // reset fill - this._setFill(curFill); if (this._isErasing) { this._pInst.erase(); } } - this.drawingContext.restore(); + this.pop(); } clear() { @@ -235,12 +233,12 @@ class Renderer2D extends Renderer { erase(opacityFill, opacityStroke) { if (!this._isErasing) { // cache the fill style - this._cachedFillStyle = this.drawingContext.fillStyle; + this.states.setValue('_cachedFillStyle', this.drawingContext.fillStyle); const newFill = this._pInst.color(255, opacityFill).toString(); this.drawingContext.fillStyle = newFill; // cache the stroke style - this._cachedStrokeStyle = this.drawingContext.strokeStyle; + this.states.setValue('_cachedStrokeStyle', this.drawingContext.strokeStyle); const newStroke = this._pInst.color(255, opacityStroke).toString(); this.drawingContext.strokeStyle = newStroke; @@ -255,8 +253,8 @@ class Renderer2D extends Renderer { noErase() { if (this._isErasing) { - this.drawingContext.fillStyle = this._cachedFillStyle; - this.drawingContext.strokeStyle = this._cachedStrokeStyle; + this.drawingContext.fillStyle = this.states._cachedFillStyle; + this.drawingContext.strokeStyle = this.states._cachedStrokeStyle; this.blendMode(this._cachedBlendMode); this._isErasing = false; @@ -282,12 +280,12 @@ class Renderer2D extends Renderer { super.beginClip(options); // cache the fill style - this._cachedFillStyle = this.drawingContext.fillStyle; + this.states.setValue('_cachedFillStyle', this.drawingContext.fillStyle); const newFill = this._pInst.color(255, 0).toString(); this.drawingContext.fillStyle = newFill; // cache the stroke style - this._cachedStrokeStyle = this.drawingContext.strokeStyle; + this.states.setValue('_cachedStrokeStyle', this.drawingContext.strokeStyle); const newStroke = this._pInst.color(255, 0).toString(); this.drawingContext.strokeStyle = newStroke; @@ -330,8 +328,8 @@ class Renderer2D extends Renderer { super.endClip(); - this.drawingContext.fillStyle = this._cachedFillStyle; - this.drawingContext.strokeStyle = this._cachedStrokeStyle; + this.drawingContext.fillStyle = this.states._cachedFillStyle; + this.drawingContext.strokeStyle = this.states._cachedStrokeStyle; this.blendMode(this._cachedBlendMode); } @@ -945,30 +943,30 @@ class Renderer2D extends Renderer { } _getFill() { - if (!this._cachedFillStyle) { - this._cachedFillStyle = this.drawingContext.fillStyle; + if (!this.states._cachedFillStyle) { + this.states.setValue('_cachedFillStyle', this.drawingContext.fillStyle); } - return this._cachedFillStyle; + return this.states._cachedFillStyle; } _setFill(fillStyle) { - if (fillStyle !== this._cachedFillStyle) { + if (fillStyle !== this.states._cachedFillStyle) { this.drawingContext.fillStyle = fillStyle; - this._cachedFillStyle = fillStyle; + this.states.setValue('_cachedFillStyle', fillStyle); } } _getStroke() { - if (!this._cachedStrokeStyle) { - this._cachedStrokeStyle = this.drawingContext.strokeStyle; + if (!this.states._cachedStrokeStyle) { + this.states.setValue('_cachedStrokeStyle', this.drawingContext.strokeStyle); } - return this._cachedStrokeStyle; + return this.states._cachedStrokeStyle; } _setStroke(strokeStyle) { - if (strokeStyle !== this._cachedStrokeStyle) { + if (strokeStyle !== this.states._cachedStrokeStyle) { this.drawingContext.strokeStyle = strokeStyle; - this._cachedStrokeStyle = strokeStyle; + this.states.setValue('_cachedStrokeStyle', strokeStyle); } } @@ -1104,14 +1102,14 @@ class Renderer2D extends Renderer { let font; const p = this._pInst; - this.states.textAscent = null; - this.states.textDescent = null; + this.states.setValue('textAscent', null); + this.states.setValue('textDescent', null); font = this.states.textFont; if (this._isOpenType()) { font = this.states.textFont.font.familyName; - this.states.textStyle = this._textFont.font.styleName; + this.states.setValue('textStyle', this._textFont.font.styleName); } let fontNameString = font || 'sans-serif'; @@ -1155,9 +1153,6 @@ class Renderer2D extends Renderer { // class' pop method pop(style) { this.drawingContext.restore(); - // Re-cache the fill / stroke state - this._cachedFillStyle = this.drawingContext.fillStyle; - this._cachedStrokeStyle = this.drawingContext.strokeStyle; super.pop(style); } diff --git a/src/image/filterRenderer2D.js b/src/image/filterRenderer2D.js index 6a3f194466..8d4ac5c274 100644 --- a/src/image/filterRenderer2D.js +++ b/src/image/filterRenderer2D.js @@ -184,8 +184,8 @@ class FilterRenderer2D { this._shader.setUniform('radius', Math.max(1, this.filterParameter)); this._shader.setUniform('filterParameter', this.filterParameter); - this.pInst.states.rectMode = constants.CORNER; - this.pInst.states.imageMode = constants.CORNER; + this.pInst.states.setValue('rectMode', constants.CORNER); + this.pInst.states.setValue('imageMode', constants.CORNER); this.pInst.blendMode(constants.BLEND); this.pInst.resetMatrix(); diff --git a/src/image/loading_displaying.js b/src/image/loading_displaying.js index 677b768464..8b4399129c 100644 --- a/src/image/loading_displaying.js +++ b/src/image/loading_displaying.js @@ -1291,7 +1291,7 @@ function loadingDisplaying(p5, fn){ fn.tint = function(...args) { // p5._validateParameters('tint', args); const c = this.color(...args); - this._renderer.states.tint = c._getRGBA([255, 255, 255, 255]); + this._renderer.states.setValue('tint', c._getRGBA([255, 255, 255, 255])); }; /** @@ -1330,7 +1330,7 @@ function loadingDisplaying(p5, fn){ * */ fn.noTint = function() { - this._renderer.states.tint = null; + this._renderer.states.setValue('tint', null); }; /** @@ -1449,7 +1449,7 @@ function loadingDisplaying(p5, fn){ m === constants.CORNERS || m === constants.CENTER ) { - this._renderer.states.imageMode = m; + this._renderer.states.setValue('imageMode', m); } }; } diff --git a/src/math/Matrices/Matrix.js b/src/math/Matrices/Matrix.js index 58ee51fd26..3711d87f51 100644 --- a/src/math/Matrices/Matrix.js +++ b/src/math/Matrices/Matrix.js @@ -17,55 +17,55 @@ if (typeof Float32Array !== "undefined") { } /** * The `Matrix` class represents a mathematical matrix and provides various methods for matrix operations. - * + * * This class extends the `MatrixInterface` and includes methods for creating, manipulating, and performing * operations on matrices. It supports both 3x3 and 4x4 matrices, as well as general NxN matrices. - * + * * @class * @extends MatrixInterface - * + * * @example * // Creating a 3x3 matrix from an array * const matrix = new Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]); - * + * * // Creating a 4x4 identity matrix * const identityMatrix = new Matrix(4); - * + * * // Adding two matrices * const matrix1 = new Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]); * const matrix2 = new Matrix([9, 8, 7, 6, 5, 4, 3, 2, 1]); * matrix1.add(matrix2); // matrix1 is now [10, 10, 10, 10, 10, 10, 10, 10, 10] - * + * * // Setting an element in the matrix * matrix.setElement(0, 10); // matrix is now [10, 2, 3, 4, 5, 6, 7, 8, 9] - * + * * // Resetting the matrix to an identity matrix * matrix.reset(); - * + * * // Getting the diagonal elements of the matrix * const diagonal = matrix.diagonal(); // [1, 1, 1] - * + * * // Transposing the matrix * matrix.transpose(); - * + * * // Multiplying two matrices * matrix1.mult(matrix2); - * + * * // Inverting the matrix * matrix.invert(); - * + * * // Scaling the matrix * matrix.scale(2, 2, 2); - * + * * // Rotating the matrix around an axis * matrix.rotate4x4(Math.PI / 4, 1, 0, 0); - * + * * // Applying a perspective transformation * matrix.perspective(Math.PI / 4, 1, 0.1, 100); - * + * * // Applying an orthographic transformation * matrix.ortho(-1, 1, -1, 1, 0.1, 100); - * + * * // Multiplying a vector by the matrix * const vector = new Vector(1, 2, 3); * const result = matrix.multiplyPoint(vector); @@ -81,7 +81,7 @@ export class Matrix extends MatrixInterface { if (isMatrixArray(args[0]) && isPerfectSquare(args[0])) { const sqDimention = Math.sqrt(Array.from(args[0]).length); this.#sqDimention = sqDimention; - this.matrix = Array.from(args[0]); + this.matrix = GLMAT_ARRAY_TYPE.from(args[0]); } else if (typeof args[0] === "number") { this.#sqDimention = Number(args[0]); this.matrix = this.#createIdentityMatrix(args[0]); @@ -91,10 +91,10 @@ export class Matrix extends MatrixInterface { /** * Getter for a 3x3 matrix. - * - * This method returns the matrix if its dimensions are 3x3. + * + * This method returns the matrix if its dimensions are 3x3. * If the matrix is not 3x3, it returns `undefined`. - * + * * @returns {Array|undefined} The 3x3 matrix or `undefined` if the matrix is not 3x3. */ get mat3() { @@ -107,10 +107,10 @@ export class Matrix extends MatrixInterface { /** * Getter for a 4x4 matrix. - * + * * This method returns the matrix if its dimensions are 4x4. * If the matrix is not 4x4, it returns `undefined`. - * + * * @returns {Array|undefined} The 4x4 matrix or `undefined` if the matrix is not 4x4. */ get mat4() { @@ -123,11 +123,11 @@ export class Matrix extends MatrixInterface { /** * Adds the corresponding elements of the given matrix to this matrix. - * + * * @param {Matrix} matrix - The matrix to add to this matrix. It must have the same dimensions as this matrix. * @returns {Matrix} The resulting matrix after addition. * @throws {Error} If the matrices do not have the same dimensions. - * + * * @example * const matrix1 = new Matrix([1, 2, 3]); * const matrix2 = new Matrix([4, 5, 6]); @@ -146,7 +146,7 @@ export class Matrix extends MatrixInterface { /** * Sets the value of a specific element in the matrix. * - * @param {number} index - The position in the matrix where the value should be set. + * @param {number} index - The position in the matrix where the value should be set. * Must be a non-negative integer less than the length of the matrix. * @param {*} value - The new value to be assigned to the specified position in the matrix. * @returns {Matrix} The current instance of the Matrix, allowing for method chaining. @@ -165,10 +165,10 @@ export class Matrix extends MatrixInterface { /** * Resets the current matrix to an identity matrix. - * + * * This method replaces the current matrix with an identity matrix of the same dimensions. * An identity matrix is a square matrix with ones on the main diagonal and zeros elsewhere. - * + * * @returns {Matrix} The current instance of the Matrix class, allowing for method chaining. */ reset() { @@ -192,11 +192,11 @@ export class Matrix extends MatrixInterface { * @chainable */ set(inMatrix) { - let refArray = Array.from([...arguments]); + let refArray = GLMAT_ARRAY_TYPE.from([...arguments]); if (inMatrix instanceof Matrix) { - refArray = inMatrix.matrix; + refArray = GLMAT_ARRAY_TYPE.from(inMatrix.matrix); } else if (isMatrixArray(inMatrix)) { - refArray = inMatrix; + refArray = GLMAT_ARRAY_TYPE.from(inMatrix); } if (refArray.length !== this.matrix.length) { p5._friendlyError( @@ -205,7 +205,7 @@ export class Matrix extends MatrixInterface { ); return this; } - this.matrix = [...refArray]; + this.matrix = refArray; return this; } @@ -283,16 +283,16 @@ export class Matrix extends MatrixInterface { return new Vector(...rowVector); } - + /** * Transposes the given matrix `a` based on the square dimension of the matrix. - * + * * This method rearranges the elements of the matrix such that the rows become columns * and the columns become rows. It handles matrices of different dimensions (4x4, 3x3, NxN) * by delegating to specific transpose methods for each case. - * + * * @param {Array} a - The matrix to be transposed. It should be a 2D array where each sub-array represents a row. * @returns {Array} - The transposed matrix. */ @@ -310,17 +310,17 @@ export class Matrix extends MatrixInterface { } } - + /** * Multiplies the current matrix with another matrix or matrix-like array. - * + * * This method supports several types of input: * - Another Matrix instance * - A matrix-like array (must be a perfect square, e.g., 4x4 or 3x3) * - Multiple arguments that form a perfect square matrix - * + * * If the input is the same as the current matrix, a copy is made to avoid modifying the original matrix. - * + * * @param {Matrix|Array|...number} multMatrix - The matrix or matrix-like array to multiply with. * @returns {Matrix|undefined} The resulting matrix after multiplication, or undefined if the input is invalid. * @chainable @@ -368,10 +368,10 @@ export class Matrix extends MatrixInterface { /** * Inverts a given matrix. - * + * * This method inverts a matrix based on its dimensions. Currently, it supports * 3x3 and 4x4 matrices. If the matrix dimension is greater than 4, an error is thrown. - * + * * @param {Array} a - The matrix to be inverted. It should be a 2D array representing the matrix. * @returns {Array} - The inverted matrix. * @throws {Error} - Throws an error if the matrix dimension is greater than 4. @@ -687,7 +687,7 @@ export class Matrix extends MatrixInterface { * Rotates the matrix around the Z-axis by a given angle. * * @param {number} a - The angle in radians to rotate the matrix by. - * + * * This method modifies the current matrix to apply a rotation transformation * around the Z-axis. The rotation is performed in a 4x4 matrix context, which * is commonly used in 3D graphics to handle transformations. @@ -880,12 +880,12 @@ export class Matrix extends MatrixInterface { /** * Multiplies the current 4x4 matrix with another 4x4 matrix. * This method updates the current matrix with the result of the multiplication. - * + * * @private * @param {number[]} _src - A 16-element array representing the 4x4 matrix to multiply with. - * + * * @returns {this} The current instance with the updated matrix. - * + * * @example * // Assuming `matrix` is an instance of the Matrix class * const srcMatrix = [ @@ -1010,7 +1010,7 @@ export class Matrix extends MatrixInterface { /** * Transposes a square matrix in place. * This method swaps the rows and columns of the matrix, effectively flipping it over its diagonal. - * + * * @private * @returns {Matrix} The current instance of the Matrix, with the transposed values. */ diff --git a/src/shape/attributes.js b/src/shape/attributes.js index 317d510245..77ea6e0a6e 100644 --- a/src/shape/attributes.js +++ b/src/shape/attributes.js @@ -92,7 +92,7 @@ function attributes(p5, fn){ m === constants.RADIUS || m === constants.CENTER ) { - this._renderer.states.ellipseMode = m; + this._renderer.states.setValue('ellipseMode', m); } return this; }; @@ -295,7 +295,7 @@ function attributes(p5, fn){ m === constants.RADIUS || m === constants.CENTER ) { - this._renderer.states.rectMode = m; + this._renderer.states.setValue('rectMode', m); } return this; // return current rectMode ? }; diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index cb53761d19..ef4104336f 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -609,7 +609,7 @@ class Shape { } serializeToArray(val) { - if (val === null) { + if (val === null || val === undefined) { return []; } if (val instanceof Number) { return [val]; diff --git a/src/type/textCore.js b/src/type/textCore.js index 99aa3f5225..4bf8abe502 100644 --- a/src/type/textCore.js +++ b/src/type/textCore.js @@ -199,19 +199,19 @@ function textCore(p5, fn) { return this.textDrawingContext().measureText('_').fontBoundingBoxDescent; }; - + // setters/getters for text properties ////////////////////////// Renderer.prototype.textAlign = function (h, v) { // the setter if (typeof h !== 'undefined') { - this.states.textAlign = h; + this.states.setValue('textAlign', h); if (typeof v !== 'undefined') { if (v === fn.CENTER) { v = fn._CTX_MIDDLE; } - this.states.textBaseline = v; + this.states.setValue('textBaseline', v); } return this._applyTextProperties(); } @@ -267,7 +267,7 @@ function textCore(p5, fn) { } // update font properties in this.states - this.states.textFont = { font, family, size }; + this.states.setValue('textFont', { font, family, size }); // convert/update the size in this.states if (typeof size !== 'undefined') { @@ -298,17 +298,14 @@ function textCore(p5, fn) { if (debug) console.log(' this.states.' + prop + '="' + style[prop] + '"'); }); - if (debug) console.log(' this.states.textFont="' + style.fontFamily + '"'); - if (debug) console.log(' this.states.textSize="' + style.fontSize + '"'); - return { family: style.fontFamily, size: style.fontSize }; } Renderer.prototype.textLeading = function (leading) { // the setter if (typeof leading === 'number') { - this.states.leadingSet = true; - this.states.textLeading = leading; + this.states.setValue('leadingSet', true); + this.states.setValue('textLeading', leading); return this._applyTextProperties(); } // the getter @@ -318,7 +315,7 @@ function textCore(p5, fn) { Renderer.prototype.textWeight = function (weight) { // the setter if (typeof weight === 'number') { - this.states.fontWeight = weight; + this.states.setValue('fontWeight', weight); this._applyTextProperties(); this._setCanvasStyleProperty('font-variation-settings', `"wght" ${weight}`); return; @@ -345,7 +342,7 @@ function textCore(p5, fn) { // the setter if (typeof style !== 'undefined') { - this.states.fontStyle = style; + this.states.setValue('fontStyle', style); return this._applyTextProperties(); } // the getter @@ -355,7 +352,7 @@ function textCore(p5, fn) { Renderer.prototype.textWrap = function (wrapStyle) { if (wrapStyle === fn.WORD || wrapStyle === fn.CHAR) { - this.states.textWrap = wrapStyle; + this.states.setValue('textWrap', wrapStyle); // no need to apply text properties here as not a context property return this._pInst; } @@ -365,7 +362,7 @@ function textCore(p5, fn) { Renderer.prototype.textDirection = function (direction) { if (typeof direction !== 'undefined') { - this.states.direction = direction; + this.states.setValue('direction', direction); return this._applyTextProperties(); } return this.states.direction; @@ -621,7 +618,7 @@ function textCore(p5, fn) { const min = Math.min.apply(Math, indexArr) let idx = indexArr.indexOf(min); let stretch = Object.keys(FontStretchMap)[idx]; - this.states.fontStretch = stretch; + this.states.setValue('fontStretch', stretch); } break; case 'ital': @@ -933,11 +930,11 @@ function textCore(p5, fn) { // set it in `this.states` if its been changed if (this.states.textSize !== theSize) { - this.states.textSize = theSize; + this.states.setValue('textSize', theSize); // handle leading here, if not set otherwise if (!this.states.leadingSet) { - this.states.textLeading = this.states.textSize * LeadingScale; + this.states.setValue('textLeading', this.states.textSize * LeadingScale); } return true; // size was changed } diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 1c880d87d0..377021ad03 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -1712,11 +1712,11 @@ function primitives3D(p5, fn){ x1, y1, 0, 1 // the resulting origin ]).mult(this.states.uModelMatrix); - this.states.uModelMatrix = mult; + this.states.setValue('uModelMatrix', mult); this._drawGeometry(this.geometryBufferCache.getGeometryByID(gid)); } finally { - this.states.uModelMatrix = uModelMatrix; + this.states.setValue('uModelMatrix', uModelMatrix); } return this; @@ -1839,7 +1839,8 @@ function primitives3D(p5, fn){ this.geometryBufferCache.ensureCached(arcGeom); } - const uModelMatrix = this.states.uModelMatrix.copy(); + const uModelMatrix = this.states.uModelMatrix; + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); try { this.states.uModelMatrix.translate([x, y, 0]); @@ -1847,7 +1848,7 @@ function primitives3D(p5, fn){ this._drawGeometry(this.geometryBufferCache.getGeometryByID(gid)); } finally { - this.states.uModelMatrix = uModelMatrix; + this.states.setValue('uModelMatrix', uModelMatrix); } return this; @@ -1900,14 +1901,15 @@ function primitives3D(p5, fn){ // opposite corners at (0,0) & (1,1). // // before rendering, this square is scaled & moved to the required location. - const uModelMatrix = this.states.uModelMatrix.copy(); + const uModelMatrix = this.states.uModelMatrix; + this.states.setValue('uModelMatrix', this.states.uModelMatrix.copy()); try { this.states.uModelMatrix.translate([x, y, 0]); this.states.uModelMatrix.scale(width, height, 1); this._drawGeometry(this.geometryBufferCache.getGeometryByID(gid)); } finally { - this.states.uModelMatrix = uModelMatrix; + this.states.setValue('uModelMatrix', uModelMatrix); } } else { // Use Immediate mode to round the rectangle corner, @@ -1949,7 +1951,7 @@ function primitives3D(p5, fn){ let y2 = d; const prevMode = this.states.textureMode; - this.states.textureMode = constants.NORMAL; + this.states.setValue('textureMode', constants.NORMAL); const prevOrder = this.bezierOrder(); this.bezierOrder(2); this.beginShape(); @@ -1984,7 +1986,7 @@ function primitives3D(p5, fn){ } this.endShape(constants.CLOSE); - this.states.textureMode = prevMode; + this.states.setValue('textureMode', prevMode); this.bezierOrder(prevOrder); } return this; @@ -2184,10 +2186,10 @@ function primitives3D(p5, fn){ this.push(); this.noLights(); - this.states.strokeColor = null;; + this.states.setValue('strokeColor', null); this.texture(img); - this.states.textureMode = constants.NORMAL; + this.states.setValue('textureMode', constants.NORMAL); let u0 = 0; if (sx <= img.width) { diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index 5cb4862168..83d2fb90de 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -12,7 +12,7 @@ class GeometryBuilder { this.renderer = renderer; renderer._pInst.push(); this.identityMatrix = new Matrix(4); - renderer.states.uModelMatrix = new Matrix(4); + renderer.states.setValue('uModelMatrix', new Matrix(4)); this.geometry = new Geometry(undefined, undefined, undefined, this.renderer); this.geometry.gid = `_p5_GeometryBuilder_${GeometryBuilder.nextGeometryId}`; GeometryBuilder.nextGeometryId++; diff --git a/src/webgl/interaction.js b/src/webgl/interaction.js index 1b257ce6fd..60af792069 100644 --- a/src/webgl/interaction.js +++ b/src/webgl/interaction.js @@ -364,6 +364,7 @@ function interaction(p5, fn){ 10, -this._renderer.zoomVelocity ); // modify uPMatrix + this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone()); this._renderer.states.uPMatrix.mat4[0] = cam.projMatrix.mat4[0]; this._renderer.states.uPMatrix.mat4[5] = cam.projMatrix.mat4[5]; } @@ -814,6 +815,7 @@ function interaction(p5, fn){ this._renderer.states.curStrokeColor[1] * 255, this._renderer.states.curStrokeColor[2] * 255 ); + this._renderer.states.setValue('uModelMatrix', this._renderer.states.uModelMatrix.clone()); this._renderer.states.uModelMatrix.reset(); // Lines along X axis @@ -861,6 +863,7 @@ function interaction(p5, fn){ return function() { this.push(); + this._renderer.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); this._renderer.states.uModelMatrix.reset(); // X axis diff --git a/src/webgl/light.js b/src/webgl/light.js index ba125d3784..7a4c52fd9c 100644 --- a/src/webgl/light.js +++ b/src/webgl/light.js @@ -1457,23 +1457,24 @@ function light(p5, fn){ RendererGL.prototype.ambientLight = function(v1, v2, v3, a) { const color = this._pInst.color(...arguments); + this.states.setValue('ambientLightColors', [...this.states.ambientLightColors]); this.states.ambientLightColors.push( color._array[0], color._array[1], color._array[2] ); - this.states.enableLighting = true; + this.states.setValue('enableLighting', true); } RendererGL.prototype.specularColor = function(v1, v2, v3) { const color = this._pInst.color(...arguments); - this.states.specularColors = [ + this.states.setValue('specularColors', [ color._array[0], color._array[1], color._array[2] - ]; + ]); } RendererGL.prototype.directionalLight = function(v1, v2, v3, x, y, z) { @@ -1498,19 +1499,23 @@ function light(p5, fn){ // normalize direction const l = Math.sqrt(_x * _x + _y * _y + _z * _z); + this.states.setValue('directionalLightDirections', [...this.states.directionalLightDirections]); this.states.directionalLightDirections.push(_x / l, _y / l, _z / l); + this.states.setValue('directionalLightDiffuseColors', [...this.states.directionalLightDiffuseColors]); this.states.directionalLightDiffuseColors.push( color._array[0], color._array[1], color._array[2] ); + + this.states.setValue('directionalLightSpecularColors', [...this.states.directionalLightSpecularColors]); Array.prototype.push.apply( this.states.directionalLightSpecularColors, this.states.specularColors ); - this.states.enableLighting = true; + this.states.setValue('enableLighting', true); } RendererGL.prototype.pointLight = function(v1, v2, v3, x, y, z) { @@ -1533,25 +1538,30 @@ function light(p5, fn){ _z = v.z; } + this.states.setValue('pointLightPositions', [...this.states.pointLightPositions]); this.states.pointLightPositions.push(_x, _y, _z); + + this.states.setValue('pointLightDiffuseColors', [...this.states.pointLightDiffuseColors]); this.states.pointLightDiffuseColors.push( color._array[0], color._array[1], color._array[2] ); + + this.states.setValue('pointLightSpecularColors', [...this.states.pointLightSpecularColors]); Array.prototype.push.apply( this.states.pointLightSpecularColors, this.states.specularColors ); - this.states.enableLighting = true; + this.states.setValue('enableLighting', true); } RendererGL.prototype.imageLight = function(img) { // activeImageLight property is checked by _setFillUniforms // for sending uniforms to the fillshader - this.states.activeImageLight = img; - this.states.enableLighting = true; + this.states.setValue('activeImageLight', img); + this.states.setValue('enableLighting', true); } RendererGL.prototype.lights = function() { @@ -1596,9 +1606,9 @@ function light(p5, fn){ ); } - this.states.constantAttenuation = constantAttenuation; - this.states.linearAttenuation = linearAttenuation; - this.states.quadraticAttenuation = quadraticAttenuation; + this.states.setValue('constantAttenuation', constantAttenuation); + this.states.setValue('linearAttenuation', linearAttenuation); + this.states.setValue('quadraticAttenuation', quadraticAttenuation); } RendererGL.prototype.spotLight = function( @@ -1770,23 +1780,23 @@ function light(p5, fn){ ); return; } - this.states.spotLightDiffuseColors = [ + this.states.setValue('spotLightDiffuseColors', [ color._array[0], color._array[1], color._array[2] - ]; + ]); - this.states.spotLightSpecularColors = [ + this.states.setValue('spotLightSpecularColors', [ ...this.states.specularColors - ]; + ]); - this.states.spotLightPositions = [position.x, position.y, position.z]; + this.states.setValue('spotLightPositions', [position.x, position.y, position.z]); direction.normalize(); - this.states.spotLightDirections = [ + this.states.setValue('spotLightDirections', [ direction.x, direction.y, direction.z - ]; + ]); if (angle === undefined) { angle = Math.PI / 3; @@ -1802,39 +1812,39 @@ function light(p5, fn){ } angle = this._pInst._toRadians(angle); - this.states.spotLightAngle = [Math.cos(angle)]; - this.states.spotLightConc = [concentration]; + this.states.setValue('spotLightAngle', [Math.cos(angle)]); + this.states.setValue('spotLightConc', [concentration]); - this.states.enableLighting = true; + this.states.setValue('enableLighting', true); } RendererGL.prototype.noLights = function() { - this.states.activeImageLight = null; - this.states.enableLighting = false; - - this.states.ambientLightColors.length = 0; - this.states.specularColors = [1, 1, 1]; - - this.states.directionalLightDirections.length = 0; - this.states.directionalLightDiffuseColors.length = 0; - this.states.directionalLightSpecularColors.length = 0; - - this.states.pointLightPositions.length = 0; - this.states.pointLightDiffuseColors.length = 0; - this.states.pointLightSpecularColors.length = 0; - - this.states.spotLightPositions.length = 0; - this.states.spotLightDirections.length = 0; - this.states.spotLightDiffuseColors.length = 0; - this.states.spotLightSpecularColors.length = 0; - this.states.spotLightAngle.length = 0; - this.states.spotLightConc.length = 0; - - this.states.constantAttenuation = 1; - this.states.linearAttenuation = 0; - this.states.quadraticAttenuation = 0; - this.states._useShininess = 1; - this.states._useMetalness = 0; + this.states.setValue('activeImageLight', null); + this.states.setValue('enableLighting', false); + + this.states.setValue('ambientLightColors', []); + this.states.setValue('specularColors', [1, 1, 1]); + + this.states.setValue('directionalLightDirections', []); + this.states.setValue('directionalLightDiffuseColors', []); + this.states.setValue('directionalLightSpecularColors', []); + + this.states.setValue('pointLightPositions', []); + this.states.setValue('pointLightDiffuseColors', []); + this.states.setValue('pointLightSpecularColors', []); + + this.states.setValue('spotLightPositions', []); + this.states.setValue('spotLightDirections', []); + this.states.setValue('spotLightDiffuseColors', []); + this.states.setValue('spotLightSpecularColors', []); + this.states.setValue('spotLightAngle', []); + this.states.setValue('spotLightConc', []); + + this.states.setValue('constantAttenuation', 1); + this.states.setValue('linearAttenuation', 0); + this.states.setValue('quadraticAttenuation', 0); + this.states.setValue('_useShininess', 1); + this.states.setValue('_useMetalness', 0); } } diff --git a/src/webgl/material.js b/src/webgl/material.js index cd9b444e06..b0770cbc78 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -2521,7 +2521,7 @@ function material(p5, fn){ `You tried to set ${mode} textureMode only supports IMAGE & NORMAL ` ); } else { - this._renderer.states.textureMode = mode; + this._renderer.states.setValue('textureMode', mode); } }; @@ -2800,8 +2800,8 @@ function material(p5, fn){ * */ fn.textureWrap = function (wrapX, wrapY = wrapX) { - this._renderer.states.textureWrapX = wrapX; - this._renderer.states.textureWrapY = wrapY; + this._renderer.states.setValue('textureWrapX', wrapX); + this._renderer.states.setValue('textureWrapY', wrapY); for (const texture of this._renderer.textures.values()) { texture.setWrapMode(wrapX, wrapY); @@ -3078,11 +3078,11 @@ function material(p5, fn){ // p5._validateParameters('ambientMaterial', arguments); const color = fn.color.apply(this, arguments); - this._renderer.states._hasSetAmbient = true; - this._renderer.states.curAmbientColor = color._array; - this._renderer.states._useNormalMaterial = false; - this._renderer.states.enableLighting = true; - this._renderer.states.fillColor = true; + this._renderer.states.setValue('_hasSetAmbient', true); + this._renderer.states.setValue('curAmbientColor', color._array); + this._renderer.states.setValue('_useNormalMaterial', false); + this._renderer.states.setValue('enableLighting', true); + this._renderer.states.setValue('fillColor', true); return this; }; @@ -3174,10 +3174,10 @@ function material(p5, fn){ // p5._validateParameters('emissiveMaterial', arguments); const color = fn.color.apply(this, arguments); - this._renderer.states.curEmissiveColor = color._array; - this._renderer.states._useEmissiveMaterial = true; - this._renderer.states._useNormalMaterial = false; - this._renderer.states.enableLighting = true; + this._renderer.states.setValue('curEmissiveColor', color._array); + this._renderer.states.setValue('_useEmissiveMaterial', true); + this._renderer.states.setValue('_useNormalMaterial', false); + this._renderer.states.setValue('enableLighting', true); return this; }; @@ -3429,10 +3429,10 @@ function material(p5, fn){ // p5._validateParameters('specularMaterial', arguments); const color = fn.color.apply(this, arguments); - this._renderer.states.curSpecularColor = color._array; - this._renderer.states._useSpecularMaterial = true; - this._renderer.states._useNormalMaterial = false; - this._renderer.states.enableLighting = true; + this._renderer.states.setValue('curSpecularColor', color._array); + this._renderer.states.setValue('_useSpecularMaterial', true); + this._renderer.states.setValue('_useNormalMaterial', false); + this._renderer.states.setValue('enableLighting', true); return this; }; @@ -3749,45 +3749,45 @@ function material(p5, fn){ RendererGL.prototype.shader = function(s) { // Always set the shader as a fill shader - this.states.userFillShader = s; - this.states._useNormalMaterial = false; + this.states.setValue('userFillShader', s); + this.states.setValue('_useNormalMaterial', false); s.ensureCompiledOnContext(this); s.setDefaultUniforms(); } RendererGL.prototype.strokeShader = function(s) { - this.states.userStrokeShader = s; + this.states.setValue('userStrokeShader', s); s.ensureCompiledOnContext(this); s.setDefaultUniforms(); } RendererGL.prototype.imageShader = function(s) { - this.states.userImageShader = s; + this.states.setValue('userImageShader', s); s.ensureCompiledOnContext(this); s.setDefaultUniforms(); } RendererGL.prototype.resetShader = function() { - this.states.userFillShader = null; - this.states.userStrokeShader = null; - this.states.userImageShader = null; + this.states.setValue('userFillShader', null); + this.states.setValue('userStrokeShader', null); + this.states.setValue('userImageShader', null); } RendererGL.prototype.texture = function(tex) { - this.states.drawMode = constants.TEXTURE; - this.states._useNormalMaterial = false; - this.states._tex = tex; - this.states.fillColor = new Color([1, 1, 1]); + this.states.setValue('drawMode', constants.TEXTURE); + this.states.setValue('_useNormalMaterial', false); + this.states.setValue('_tex', tex); + this.states.setValue('fillColor', new Color([1, 1, 1])); }; RendererGL.prototype.normalMaterial = function(...args) { - this.states.drawMode = constants.FILL; - this.states._useSpecularMaterial = false; - this.states._useEmissiveMaterial = false; - this.states._useNormalMaterial = true; - this.states.curFillColor = [1, 1, 1, 1]; - this.states.fillColor = new Color([1, 1, 1]); - this.states.strokeColor = null; + this.states.setValue('drawMode', constants.FILL); + this.states.setValue('_useSpecularMaterial', false); + this.states.setValue('_useEmissiveMaterial', false); + this.states.setValue('_useNormalMaterial', true); + this.states.setValue('curFillColor', [1, 1, 1, 1]); + this.states.setValue('fillColor', new Color([1, 1, 1])); + this.states.setValue('strokeColor', null); } // RendererGL.prototype.ambientMaterial = function(v1, v2, v3) { @@ -3803,12 +3803,12 @@ function material(p5, fn){ if (shine < 1) { shine = 1; } - this.states._useShininess = shine; + this.states.setValue('_useShininess', shine); } RendererGL.prototype.metalness = function(metallic) { const metalMix = 1 - Math.exp(-metallic / 100); - this.states._useMetalness = metalMix; + this.states.setValue('_useMetalness', metalMix); } } diff --git a/src/webgl/p5.Camera.js b/src/webgl/p5.Camera.js index aa0a41e40c..aa35fba5b4 100644 --- a/src/webgl/p5.Camera.js +++ b/src/webgl/p5.Camera.js @@ -1195,6 +1195,7 @@ class Camera { /* eslint-enable indent */ if (this._isActive()) { + this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone()); this._renderer.states.uPMatrix.set(this.projMatrix); } } @@ -1382,6 +1383,7 @@ class Camera { tx, ty, tz, 1); /* eslint-enable indent */ if (this._isActive()) { + this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone()); this._renderer.states.uPMatrix.set(this.projMatrix); } this.cameraType = 'custom'; @@ -1520,6 +1522,7 @@ class Camera { /* eslint-enable indent */ if (this._isActive()) { + this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone()); this._renderer.states.uPMatrix.set(this.projMatrix); } @@ -2094,6 +2097,7 @@ class Camera { this.cameraMatrix.translate([tx, ty, tz]); if (this._isActive()) { + this._renderer.states.setValue('uViewMatrix', this._renderer.states.uViewMatrix.clone()); this._renderer.states.uViewMatrix.set(this.cameraMatrix); } return this; @@ -2419,6 +2423,9 @@ class Camera { this.projMatrix = cam.projMatrix.copy(); if (this._isActive()) { + this._renderer.states.setValue('uModelMatrix', this._renderer.states.uModelMatrix.clone()); + this._renderer.states.setValue('uViewMatrix', this._renderer.states.uViewMatrix.clone()); + this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone()); this._renderer.states.uModelMatrix.reset(); this._renderer.states.uViewMatrix.set(this.cameraMatrix); this._renderer.states.uPMatrix.set(this.projMatrix); @@ -2524,6 +2531,7 @@ class Camera { ); // If the camera is active, make uPMatrix reflect changes in projMatrix. if (this._isActive()) { + this._renderer.states.setValue('uPMatrix', this._renderer.states.uPMatrix.clone()); this._renderer.states.uPMatrix.mat4 = this.projMatrix.mat4.slice(); } } @@ -3911,10 +3919,12 @@ function camera(p5, fn){ } RendererGL.prototype.setCamera = function(cam) { - this.states.curCamera = cam; + this.states.setValue('curCamera', cam); // set the projection matrix (which is not normally updated each frame) + this.states.setValue('uPMatrix', this.states.uPMatrix.clone()); this.states.uPMatrix.set(cam.projMatrix); + this.states.setValue('uViewMatrix', this.states.uViewMatrix.clone()); this.states.uViewMatrix.set(cam.cameraMatrix); } } diff --git a/src/webgl/p5.Framebuffer.js b/src/webgl/p5.Framebuffer.js index 7d46af86e8..2155ac75bf 100644 --- a/src/webgl/p5.Framebuffer.js +++ b/src/webgl/p5.Framebuffer.js @@ -129,7 +129,7 @@ class Framebuffer { const prevCam = this.renderer.states.curCamera; this.defaultCamera = this.createCamera(); this.filterCamera = this.createCamera(); - this.renderer.states.curCamera = prevCam; + this.renderer.states.setValue('curCamera', prevCam); this.draw(() => this.renderer.clear()); } @@ -905,7 +905,6 @@ class Framebuffer { const cam = new FramebufferCamera(this); cam._computeCameraDefaultSettings(); cam._setDefaultCamera(); - this.renderer.states.curCamera = cam; return cam; } @@ -1067,9 +1066,11 @@ class Framebuffer { // RendererGL.reset() does, but this does not try to clear any buffers; // it only sets the camera. // this.renderer.setCamera(this.defaultCamera); - this.renderer.states.curCamera = this.defaultCamera; + this.renderer.states.setValue('curCamera', this.defaultCamera); // set the projection matrix (which is not normally updated each frame) + this.renderer.states.setValue('uPMatrix', this.renderer.states.uPMatrix.clone()); this.renderer.states.uPMatrix.set(this.defaultCamera.projMatrix); + this.renderer.states.setValue('uViewMatrix', this.renderer.states.uViewMatrix.clone()); this.renderer.states.uViewMatrix.set(this.defaultCamera.cameraMatrix); this.renderer.resetMatrix(); @@ -1557,10 +1558,10 @@ class Framebuffer { this.begin(); this.renderer.push(); // this.renderer.imageMode(constants.CENTER); - this.renderer.states.imageMode = constants.CORNER; + this.renderer.states.setValue('imageMode', constants.CORNER); this.renderer.setCamera(this.filterCamera); this.renderer.resetMatrix(); - this.renderer.states.strokeColor = null; + this.renderer.states.setValue('strokeColor', null); this.renderer.clear(); this.renderer._drawingFilter = true; this.renderer.image( diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index c845a1effb..f41f39cdf0 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -181,6 +181,8 @@ class RendererGL extends Renderer { this.states.uPMatrix = new Matrix(4); this.states.curCamera = new Camera(this); + this.states.uPMatrix.set(this.states.curCamera.projMatrix); + this.states.uViewMatrix.set(this.states.curCamera.cameraMatrix); this.states.enableLighting = false; this.states.ambientLightColors = []; @@ -286,9 +288,9 @@ class RendererGL extends Renderer { this._drawingFilter = false; this._drawingImage = false; - this.states.specularShader = undefined; + this.specularShader = undefined; this.sphereMapping = undefined; - this.states.diffusedShader = undefined; + this.diffusedShader = undefined; this._defaultLightShader = undefined; this._defaultImmediateModeShader = undefined; this._defaultNormalShader = undefined; @@ -500,7 +502,7 @@ class RendererGL extends Renderer { if (d === undefined) { return this.states.curveDetail; } else { - this.states.curveDetail = d; + this.states.setValue('curveDetail', d); } } @@ -578,9 +580,9 @@ class RendererGL extends Renderer { normal(xorv, y, z) { if (xorv instanceof Vector) { - this.states._currentNormal = xorv; + this.states.setValue('_currentNormal', xorv); } else { - this.states._currentNormal = new Vector(xorv, y, z); + this.states.setValue('_currentNormal', new Vector(xorv, y, z)); } this.updateShapeVertexProperties(); } @@ -634,7 +636,8 @@ class RendererGL extends Renderer { } _drawGeometryScaled(model, scaleX, scaleY, scaleZ) { - let originalModelMatrix = this.states.uModelMatrix.copy(); + let originalModelMatrix = this.states.uModelMatrix; + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); try { this.states.uModelMatrix.scale(scaleX, scaleY, scaleZ); @@ -644,7 +647,7 @@ class RendererGL extends Renderer { this._drawGeometry(model); } } finally { - this.states.uModelMatrix = originalModelMatrix; + this.states.setValue('uModelMatrix', originalModelMatrix); } } @@ -977,33 +980,35 @@ class RendererGL extends Renderer { _update() { // reset model view and apply initial camera transform // (containing only look at info; no projection). + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); this.states.uModelMatrix.reset(); + this.states.setValue('uViewMatrix', this.states.uViewMatrix.clone()); this.states.uViewMatrix.set(this.states.curCamera.cameraMatrix); // reset light data for new frame. - this.states.ambientLightColors.length = 0; - this.states.specularColors = [1, 1, 1]; + this.states.setValue('ambientLightColors', []); + this.states.setValue('specularColors', [1, 1, 1]); - this.states.directionalLightDirections.length = 0; - this.states.directionalLightDiffuseColors.length = 0; - this.states.directionalLightSpecularColors.length = 0; + this.states.setValue('directionalLightDirections', []); + this.states.setValue('directionalLightDiffuseColors', []); + this.states.setValue('directionalLightSpecularColors', []); - this.states.pointLightPositions.length = 0; - this.states.pointLightDiffuseColors.length = 0; - this.states.pointLightSpecularColors.length = 0; + this.states.setValue('pointLightPositions', []); + this.states.setValue('pointLightDiffuseColors', []); + this.states.setValue('pointLightSpecularColors', []); - this.states.spotLightPositions.length = 0; - this.states.spotLightDirections.length = 0; - this.states.spotLightDiffuseColors.length = 0; - this.states.spotLightSpecularColors.length = 0; - this.states.spotLightAngle.length = 0; - this.states.spotLightConc.length = 0; + this.states.setValue('spotLightPositions', []); + this.states.setValue('spotLightDirections', []); + this.states.setValue('spotLightDiffuseColors', []); + this.states.setValue('spotLightSpecularColors', []); + this.states.setValue('spotLightAngle', []); + this.states.setValue('spotLightConc', []); - this.states.enableLighting = false; + this.states.setValue('enableLighting', false); //reset tint value for new frame - this.states.tint = [255, 255, 255, 255]; + this.states.setValue('tint', [255, 255, 255, 255]); //Clear depth every frame this.GL.clearStencil(0); @@ -1065,10 +1070,10 @@ class RendererGL extends Renderer { //see material.js for more info on color blending in webgl // const color = fn.color.apply(this._pInst, arguments); const color = this.states.fillColor; - this.states.curFillColor = color._array; - this.states.drawMode = constants.FILL; - this.states._useNormalMaterial = false; - this.states._tex = null; + this.states.setValue('curFillColor', color._array); + this.states.setValue('drawMode', constants.FILL); + this.states.setValue('_useNormalMaterial', false); + this.states.setValue('_tex', null); } /** @@ -1103,10 +1108,11 @@ class RendererGL extends Renderer { stroke(...args) { super.stroke(...args); // const color = fn.color.apply(this._pInst, arguments); - this.states.curStrokeColor = this.states.strokeColor._array; + this.states.setValue('curStrokeColor', this.states.strokeColor._array); } getCommonVertexProperties() { + if (!this.states) debugger return { ...super.getCommonVertexProperties(), stroke: this.states.strokeColor, @@ -1173,11 +1179,11 @@ class RendererGL extends Renderer { filterShaderFrags[operation], ); } - this.states.filterShader = this.defaultFilterShaders[operation]; + this.states.setValue('filterShader', this.defaultFilterShaders[operation]); } // use custom user-supplied shader else { - this.states.filterShader = args[0]; + this.states.setValue('filterShader', args[0]); } // Setting the target to the framebuffer when applying a filter to a framebuffer. @@ -1202,7 +1208,7 @@ class RendererGL extends Renderer { this.matchSize(tmp, target); // setup this.push(); - this.states.strokeColor = null; + this.states.setValue('strokeColor', null); this.blendMode(constants.BLEND); // draw main to temp buffer @@ -1242,7 +1248,7 @@ class RendererGL extends Renderer { // every other non-blur shader uses single pass else { fbo.draw(() => { - this.states.strokeColor = null; + this.states.setValue('strokeColor', null); this.blendMode(constants.BLEND); this.shader(this.states.filterShader); this.states.filterShader.setUniform("tex0", target); @@ -1260,10 +1266,10 @@ class RendererGL extends Renderer { } // draw fbo contents onto main renderer. this.push(); - this.states.strokeColor = null; + this.states.setValue('strokeColor', null); this.clear(); this.push(); - this.states.imageMode = constants.CORNER; + this.states.setValue('imageMode', constants.CORNER); this.blendMode(constants.BLEND); target.filterCamera._resize(); this.setCamera(target.filterCamera); @@ -1309,7 +1315,7 @@ class RendererGL extends Renderer { mode === constants.MULTIPLY || mode === constants.REMOVE ) - this.states.curBlendMode = mode; + this.states.setValue('curBlendMode', mode); else if ( mode === constants.BURN || mode === constants.OVERLAY || @@ -1329,19 +1335,19 @@ class RendererGL extends Renderer { this._isErasing = true; this.blendMode(constants.REMOVE); this._cachedFillStyle = this.states.curFillColor.slice(); - this.states.curFillColor = [1, 1, 1, opacityFill / 255]; + this.states.setValue('curFillColor', [1, 1, 1, opacityFill / 255]); this._cachedStrokeStyle = this.states.curStrokeColor.slice(); - this.states.curStrokeColor = [1, 1, 1, opacityStroke / 255]; + this.states.setValue('curStrokeColor', [1, 1, 1, opacityStroke / 255]); } } noErase() { if (this._isErasing) { // Restore colors - this.states.curFillColor = this._cachedFillStyle.slice(); - this.states.curStrokeColor = this._cachedStrokeStyle.slice(); + this.states.setValue('curFillColor', this._cachedFillStyle.slice()); + this.states.setValue('curStrokeColor', this._cachedStrokeStyle.slice()); // Restore blend mode - this.states.curBlendMode = this.preEraseBlend; + this.states.setValue('curBlendMode', this.preEraseBlend); this.blendMode(this.preEraseBlend); // Ensure that _applyBlendMode() sets preEraseBlend back to the original blend mode this._isErasing = false; @@ -1468,7 +1474,7 @@ class RendererGL extends Renderer { this.push(); this.resetMatrix(); this.clear(); - this.states.imageMode = constants.CORNER; + this.states.setValue('imageMode', constants.CORNER); this.image( fbo, 0, @@ -1621,6 +1627,7 @@ class RendererGL extends Renderer { } applyMatrix(a, b, c, d, e, f) { + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); if (arguments.length === 16) { // this.states.uModelMatrix.apply(arguments); Matrix.prototype.apply.apply(this.states.uModelMatrix, arguments); @@ -1661,6 +1668,7 @@ class RendererGL extends Renderer { y = x.y; x = x.x; } + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); this.states.uModelMatrix.translate([x, y, z]); return this; } @@ -1674,6 +1682,7 @@ class RendererGL extends Renderer { * @chainable */ scale(x, y, z) { + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); this.states.uModelMatrix.scale(x, y, z); return this; } @@ -1682,6 +1691,7 @@ class RendererGL extends Renderer { if (typeof axis === "undefined") { return this.rotateZ(rad); } + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); Matrix.prototype.rotate4x4.apply(this.states.uModelMatrix, arguments); return this; } @@ -1724,7 +1734,9 @@ class RendererGL extends Renderer { } } resetMatrix() { + this.states.setValue('uModelMatrix', this.states.uModelMatrix.clone()); this.states.uModelMatrix.reset(); + this.states.setValue('uViewMatrix', this.states.uViewMatrix.clone()); this.states.uViewMatrix.set(this.states.curCamera.cameraMatrix); return this; } @@ -2100,16 +2112,16 @@ class RendererGL extends Renderer { }); // create framebuffer is like making a new sketch, all functions on main // sketch it would be available on framebuffer - if (!this.states.diffusedShader) { - this.states.diffusedShader = this._pInst.createShader( + if (!this.diffusedShader) { + this.diffusedShader = this._pInst.createShader( defaultShaders.imageLightVert, defaultShaders.imageLightDiffusedFrag, ); } newFramebuffer.draw(() => { - this.shader(this.states.diffusedShader); - this.states.diffusedShader.setUniform("environmentMap", input); - this.states.strokeColor = null; + this.shader(this.diffusedShader); + this.diffusedShader.setUniform("environmentMap", input); + this.states.setValue('strokeColor', null); this.noLights(); this.plane(width, height); }); @@ -2142,8 +2154,8 @@ class RendererGL extends Renderer { density: 1, }); let count = Math.log(size) / Math.log(2); - if (!this.states.specularShader) { - this.states.specularShader = this._pInst.createShader( + if (!this.specularShader) { + this.specularShader = this._pInst.createShader( defaultShaders.imageLightVert, defaultShaders.imageLightSpecularFrag, ); @@ -2158,11 +2170,11 @@ class RendererGL extends Renderer { let currCount = Math.log(w) / Math.log(2); let roughness = 1 - currCount / count; framebuffer.draw(() => { - this.shader(this.states.specularShader); + this.shader(this.specularShader); this.clear(); - this.states.specularShader.setUniform("environmentMap", input); - this.states.specularShader.setUniform("roughness", roughness); - this.states.strokeColor = null; + this.specularShader.setUniform("environmentMap", input); + this.specularShader.setUniform("roughness", roughness); + this.states.setValue('strokeColor', null); this.noLights(); this.plane(w, w); }); @@ -2623,9 +2635,7 @@ function rendererGL(p5, fn) { } } - this.push(); this._renderer._resetContext(); - this.pop(); if (this._renderer.states.curCamera) { this._renderer.states.curCamera._renderer = this._renderer; diff --git a/src/webgl/text.js b/src/webgl/text.js index a9cec5fa1f..d2c95b889e 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -669,8 +669,8 @@ function text(p5, fn){ const doStroke = this.states.strokeColor; const drawMode = this.states.drawMode; - this.states.strokeColor = null; - this.states.drawMode = constants.TEXTURE; + this.states.setValue('strokeColor', null); + this.states.setValue('drawMode', constants.TEXTURE); // get the cached FontInfo object const { font } = this.states.textFont; @@ -766,8 +766,8 @@ function text(p5, fn){ // clean up sh.unbindShader(); - this.states.strokeColor = doStroke; - this.states.drawMode = drawMode; + this.states.setValue('strokeColor', doStroke); + this.states.setValue('drawMode', drawMode); gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); this.pop(); diff --git a/test/js/mocks.js b/test/js/mocks.js index 80a4cd68f8..5de4e4a1ec 100644 --- a/test/js/mocks.js +++ b/test/js/mocks.js @@ -1,6 +1,7 @@ import { vi } from 'vitest'; import { http, HttpResponse, passthrough } from 'msw'; import { setupWorker } from 'msw/browser'; +import { States } from '../../src/core/States'; // HTTP requests mocks const httpMocks = [ @@ -18,7 +19,7 @@ const httpMocks = [ export const httpMock = setupWorker(...httpMocks); // p5.js module mocks -const rendererStates = {}; +const rendererStates = new States({}); export const mockP5 = vi.fn(); Object.assign(mockP5, { _validateParameters: vi.fn(), diff --git a/test/unit/color/setting.js b/test/unit/color/setting.js index 12a44f195d..53e86d1174 100644 --- a/test/unit/color/setting.js +++ b/test/unit/color/setting.js @@ -4,6 +4,7 @@ import { vi } from 'vitest'; import { mockP5, mockP5Prototype } from '../../js/mocks'; import creatingReading from '../../../src/color/creating_reading'; import setting from '../../../src/color/setting'; +import { States } from '../../../src/core/States'; // NOTE: Require ESM compatible libtess suite('color/Setting', function() { @@ -57,14 +58,18 @@ suite('color/Setting', function() { beforeEach(() => { mockP5Prototype._renderer = { erase: vi.fn(), - states: { + states: new States({ colorMode: 'rgb', colorMaxes: { rgb: [255, 255, 255, 255], hsb: [360, 100, 100, 1], - hsl: [360, 100, 100, 1] + hsl: [360, 100, 100, 1], + clone: function() { + // Quick incorrect implementation for this test + return this; + } } - } + }) } }); diff --git a/test/unit/core/curves.js b/test/unit/core/curves.js index 4840e273b1..dbad66940d 100644 --- a/test/unit/core/curves.js +++ b/test/unit/core/curves.js @@ -1,14 +1,15 @@ import { mockP5, mockP5Prototype } from '../../js/mocks'; import curves from '../../../src/shape/curves'; +import { States } from '../../../src/core/States'; suite('Curves', function() { beforeAll(function() { mockP5Prototype._renderer = { - states: { + states: new States({ splineProperties: { tightness: 0 } - } + }) }; curves(mockP5, mockP5Prototype); }); diff --git a/test/unit/math/p5.Matrix.js b/test/unit/math/p5.Matrix.js index 51e684db18..88b31d5b27 100644 --- a/test/unit/math/p5.Matrix.js +++ b/test/unit/math/p5.Matrix.js @@ -3,11 +3,11 @@ import p5 from "../../../src/app.js"; const toArray = (typedArray) => Array.from(typedArray); /* eslint-disable indent */ -var mat4 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; +var mat4 = Float32Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); -var other = [1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16]; +var other = Float32Array.from([1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16]); -var mat3 = [1, 2, 3, 4, 5, 6, 7, 8, 9]; +var mat3 = Float32Array.from([1, 2, 3, 4, 5, 6, 7, 8, 9]); /* eslint-enable indent */ suite("p5.Matrix", function () { @@ -49,7 +49,7 @@ suite("p5.Matrix", function () { var m = new p5.Matrix(mat3); assert.instanceOf(m, p5.Matrix); assert.isUndefined(m.mat4); - assert.deepEqual([].slice.call(m.mat3), mat3); + assert.deepEqual(m.mat3, mat3); }); test("identity()", function () { @@ -93,13 +93,13 @@ suite("p5.Matrix", function () { test("array", function () { var m = new p5.Matrix(4); m.set(mat4); - assert.deepEqual([].slice.call(m.mat4), mat4); + assert.deepEqual(m.mat4, mat4); }); test("arguments", function () { var m = new p5.Matrix(4); m.set(1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6); - expect(m.mat4).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]); + expect(Array.from(m.mat4)).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6]); }); }); @@ -118,7 +118,7 @@ suite("p5.Matrix", function () { const clone = original.clone(); expect(clone).not.toBe(original); - expect(toArray(clone.mat3)).toEqual(original.mat3); + expect(clone.mat3).toEqual(original.mat3); }); it("should clone an identity matrix correctly", () => { @@ -262,31 +262,31 @@ suite("p5.Matrix", function () { suite("rotate", function () { /* eslint-disable max-len */ - var rm = [ + var rm = Float32Array.from([ 1.433447866601989, 2.5241247073503885, 3.6148015480987885, 4.7054783888471885, 6.460371405020393, 7.054586073938033, 7.648800742855675, 8.243015411773316, 7.950398010346969, 9.157598472697025, 10.36479893504708, 11.571999397397136, 13, 14, 15, 16, - ]; + ]); /* eslint-enable max-len */ test("p5.Vector", function () { var m = new p5.Matrix(mat4.slice()); var v = myp5.createVector(2, 3, 5); m.rotate4x4(45 * myp5.DEG_TO_RAD, v); - assert.deepEqual([].slice.call(m.mat4), rm); + assert.deepEqual(m.mat4, rm); }); test("array", function () { var m = new p5.Matrix(mat4.slice()); m.rotate4x4(45 * myp5.DEG_TO_RAD, [2, 3, 5]); - assert.deepEqual([].slice.call(m.mat4), rm); + assert.deepEqual(m.mat4, rm); }); test("arguments", function () { var m = new p5.Matrix(mat4.slice()); m.rotate4x4(45 * myp5.DEG_TO_RAD, 2, 3, 5); - assert.deepEqual([].slice.call(m.mat4), rm); + assert.deepEqual([].slice.call(m.mat4), Array.from(rm)); }); }); @@ -323,19 +323,19 @@ suite("p5.Matrix", function () { const multMatrix = new p5.Matrix([1, 1, 1, 0, 1, 1, 1, 0, 1]); // When taking a matrix as an argument m.mult(multMatrix); - expect(m.mat3).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]); + expect(toArray(m.mat3)).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]); }); - + test("mult a 3x3 matrix with array as argument", function () { const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]); m.mult([1, 1, 1, 0, 1, 1, 1, 0, 1]) - expect(m.mat3).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]); + expect(toArray(m.mat3)).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]); }); test("mult a 3x3 matrix with arguments non array", function () { const m = new p5.Matrix([1, 2, 3, 4, 5, 6, 7, 8, 9]); m.mult(1, 1, 1, 0, 1, 1, 1, 0, 1) - expect(m.mat3).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]); + expect(toArray(m.mat3)).toEqual([ 4, 3, 6, 10, 9, 15, 16, 15, 24 ]); }); test("column() and row()", function () { @@ -396,7 +396,7 @@ suite("p5.Matrix", function () { mat.transpose(mat); - expect(mat.mat4).toEqual([ + expect(toArray(mat.mat4)).toEqual([ 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16, ]); }); @@ -408,7 +408,7 @@ suite("p5.Matrix", function () { mat.transpose([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]); - expect(mat.mat4).toEqual([ + expect(toArray(mat.mat4)).toEqual([ 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15, 4, 8, 12, 16, ]); }); @@ -455,7 +455,7 @@ suite("p5.Matrix", function () { const invertedMatrix = matrix.invert(matrix); - expect(invertedMatrix.mat4).toEqual([ + expect(toArray(invertedMatrix.mat4)).toEqual([ 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, ]); }); @@ -477,7 +477,7 @@ suite("p5.Matrix", function () { const invertedMatrix = matrix.invert(matrix); - expect(invertedMatrix.mat4).toEqual([ + expect(toArray(invertedMatrix.mat4)).toEqual([ 0, 0, 0, 1, 0, 0, 1, -1, 0, 1, 1, -2, 1, -1, -2, 2, ]); }); @@ -488,7 +488,7 @@ suite("p5.Matrix", function () { const matrix = new p5.Matrix([1, 2, 3, 0, 1, 4, 5, 6, 0]); const invertedMatrix = matrix.invert(); - expect(invertedMatrix.mat3).toEqual([-24, 18, 5, 20, -15, -4, -5, 4, 1]); + expect(toArray(invertedMatrix.mat3)).toEqual([-24, 18, 5, 20, -15, -4, -5, 4, 1]); }); it("should return null for a non-invertible 3x3 matrix", () => { @@ -502,7 +502,7 @@ suite("p5.Matrix", function () { const matrix = new p5.Matrix([1, 0, 0, 0, 1, 0, 0, 0, 1]); const invertedMatrix = matrix.invert(); - expect(invertedMatrix.mat3).toEqual([1, 0, 0, 0, 1, 0, 0, 0, 1]); + expect(toArray(invertedMatrix.mat3)).toEqual([1, 0, 0, 0, 1, 0, 0, 0, 1]); }); }); @@ -513,7 +513,7 @@ suite("p5.Matrix", function () { ]); const invertedMatrix = matrix.setElement(2, 0); - expect(invertedMatrix.mat4).toEqual([ + expect(toArray(invertedMatrix.mat4)).toEqual([ 1, 2, 0, 5, 0, 1, 4, 5, 5, 6, 0, 5, 5, 6, 0, 5, ]); }); @@ -522,7 +522,7 @@ suite("p5.Matrix", function () { const matrix = new p5.Matrix([1, 2, 3, 0, 1, 4, 5, 6, 0]); const invertedMatrix = matrix.setElement(2, 0); - expect(invertedMatrix.mat3).toEqual([1, 2, 0, 0, 1, 4, 5, 6, 0]); + expect(toArray(invertedMatrix.mat3)).toEqual([1, 2, 0, 0, 1, 4, 5, 6, 0]); }); }); }); diff --git a/test/unit/visual/cases/typography.js b/test/unit/visual/cases/typography.js index 63cd0f9452..46fbab5777 100644 --- a/test/unit/visual/cases/typography.js +++ b/test/unit/visual/cases/typography.js @@ -279,7 +279,7 @@ visualSuite("Typography", function () { p5.createCanvas(150, 100, mode === 'webgl' ? p5.WEBGL : undefined); if (mode === 'webgl') p5.translate(-p5.width/2, -p5.height/2); - p5.textSize(20); + p5.textSize(19); p5.textWrap(p5.CHAR); const font = await p5.loadFont( '/unit/assets/Inconsolata-Bold.ttf' diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/000.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/000.png index 8c532567f2..972bc839ab 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/000.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/001.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/001.png index 8c532567f2..ee7d540a28 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/001.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/001.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/002.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/002.png index 8c532567f2..d5ece8e022 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/002.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/002.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/003.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/003.png index aa31ca421c..de9ed8e90d 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/003.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/003.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/004.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/004.png index aa31ca421c..a722b39ab3 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/004.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/004.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/005.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/005.png index aa31ca421c..5e0a9ebfad 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/005.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/005.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/006.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/006.png index 24c31bf9f0..7d20ef1af4 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/006.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/006.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/007.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/007.png index 24c31bf9f0..f0ba0feadf 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/007.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/007.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/008.png b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/008.png index 24c31bf9f0..009a33e472 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/008.png and b/test/unit/visual/screenshots/Typography/textAlign/2d mode/all alignments with multi-lines and wrap char/008.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/000.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/000.png index a85c0d7e3f..150c342587 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/000.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/000.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/001.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/001.png index a85c0d7e3f..b61a7788c9 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/001.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/001.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/002.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/002.png index a85c0d7e3f..a353f6a37f 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/002.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/002.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/003.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/003.png index d3986bad49..9012e58b30 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/003.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/003.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/004.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/004.png index d3986bad49..3e9fb83840 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/004.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/004.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/005.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/005.png index d3986bad49..f493557a75 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/005.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/005.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/006.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/006.png index 75490d5dad..128c72195b 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/006.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/006.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/007.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/007.png index 75490d5dad..2c4f829b64 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/007.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/007.png differ diff --git a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/008.png b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/008.png index 75490d5dad..9239857a40 100644 Binary files a/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/008.png and b/test/unit/visual/screenshots/Typography/textAlign/webgl mode/all alignments with multi-lines and wrap char/008.png differ