Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion src/core/friendly_errors/param_validator.js
Original file line number Diff line number Diff line change
Expand Up @@ -573,7 +573,10 @@ function validateParams(p5, fn, lifecycles) {
lifecycles.presetup = function(){
loadP5Constructors();

if(p5.disableParameterValidator !== true){
if(
p5.disableParameterValidator !== true &&
p5.disableFriendlyErrors !== true
){
const excludes = ['validate'];
for(const f in this){
if(!excludes.includes(f) && !f.startsWith('_') && typeof this[f] === 'function'){
Expand Down
126 changes: 109 additions & 17 deletions src/core/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,28 +78,120 @@ class p5 {
this._updateWindowSize();

const bindGlobal = property => {
Object.defineProperty(window, property, {
configurable: true,
enumerable: true,
get: () => {
if(typeof this[property] === 'function'){
return this[property].bind(this);
}else{
return this[property];
}
},
set: newValue => {
if (property === 'constructor') return;

// Common setter for all property types
const createSetter = () => newValue => {
Object.defineProperty(window, property, {
configurable: true,
enumerable: true,
value: newValue,
writable: true
});
if (!p5.disableFriendlyErrors) {
console.log(`You just changed the value of "${property}", which was a p5 global value. This could cause problems later if you're not careful.`);
}
};

// Check if this property has a getter on the instance or prototype
const instanceDescriptor = Object.getOwnPropertyDescriptor(this, property);
const prototypeDescriptor = Object.getOwnPropertyDescriptor(p5.prototype, property);
const hasGetter = (instanceDescriptor && instanceDescriptor.get) ||
(prototypeDescriptor && prototypeDescriptor.get);

// Only check if it's a function if it doesn't have a getter
// to avoid actually evaluating getters before things like the
// renderer are fully constructed
let isPrototypeFunction = false;
let isConstant = false;
let constantValue;

if (!hasGetter) {
const prototypeValue = p5.prototype[property];
isPrototypeFunction = typeof prototypeValue === 'function';

// Check if this is a true constant from the constants module
if (!isPrototypeFunction && constants[property] !== undefined) {
isConstant = true;
constantValue = prototypeValue;
}
}

if (isPrototypeFunction) {
// For regular functions, cache the bound function
const boundFunction = p5.prototype[property].bind(this);
if (p5.disableFriendlyErrors) {
Object.defineProperty(window, property, {
configurable: true,
enumerable: true,
value: newValue,
writable: true
value: boundFunction,
});
} else {
Object.defineProperty(window, property, {
configurable: true,
enumerable: true,
get() {
return boundFunction;
},
set: createSetter()
});
if (!p5.disableFriendlyErrors) {
console.log(`You just changed the value of "${property}", which was a p5 global value. This could cause problems later if you're not careful.`);
}
}
});
} else if (isConstant) {
// For constants, cache the value directly
if (p5.disableFriendlyErrors) {
Object.defineProperty(window, property, {
configurable: true,
enumerable: true,
value: constantValue,
});
} else {
Object.defineProperty(window, property, {
configurable: true,
enumerable: true,
get() {
return constantValue;
},
set: createSetter()
});
}
} else if (hasGetter || !isPrototypeFunction) {
// For properties with getters or non-function properties, use lazy optimization
// On first access, determine the type and optimize subsequent accesses
let lastFunction = null;
let boundFunction = null;
let isFunction = null; // null = unknown, true = function, false = not function

Object.defineProperty(window, property, {
configurable: true,
enumerable: true,
get: () => {
const currentValue = this[property];

if (isFunction === null) {
// First access - determine type and optimize
isFunction = typeof currentValue === 'function';
if (isFunction) {
lastFunction = currentValue;
boundFunction = currentValue.bind(this);
return boundFunction;
} else {
return currentValue;
}
} else if (isFunction) {
// Optimized function path - only rebind if function changed
if (currentValue !== lastFunction) {
lastFunction = currentValue;
boundFunction = currentValue.bind(this);
}
return boundFunction;
} else {
// Optimized non-function path
return currentValue;
}
},
set: createSetter()
});
}
};
// If the user has created a global setup or draw function,
// assume "global" mode and make everything global (i.e. on the window)
Expand Down
6 changes: 3 additions & 3 deletions src/math/Matrices/Matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Vector } from '../p5.Vector';
import { MatrixInterface } from './MatrixInterface';

const isPerfectSquare = arr => {
const sqDimention = Math.sqrt(Array.from(arr).length);
const sqDimention = Math.sqrt(arr.length);
if (sqDimention % 1 !== 0) {
throw new Error('Array length must be a perfect square.');
}
Expand All @@ -29,7 +29,7 @@ export class Matrix extends MatrixInterface {
// This is default behavior when object
// instantiated using createMatrix()
if (isMatrixArray(args[0]) && isPerfectSquare(args[0])) {
const sqDimention = Math.sqrt(Array.from(args[0]).length);
const sqDimention = Math.sqrt(args[0].length);
this.#sqDimention = sqDimention;
this.matrix = GLMAT_ARRAY_TYPE.from(args[0]);
} else if (typeof args[0] === 'number') {
Expand Down Expand Up @@ -568,7 +568,7 @@ export class Matrix extends MatrixInterface {
_src = multMatrix.matrix;
} else if (isMatrixArray(multMatrix) && isPerfectSquare(multMatrix)) {
_src = multMatrix;
} else if (isPerfectSquare(arguments)) {
} else if (isPerfectSquare(Array.from(arguments))) {
_src = Array.from(arguments);
} else {
return; // nothing to do.
Expand Down
5 changes: 3 additions & 2 deletions src/math/Matrices/MatrixInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export class MatrixInterface {
if (this.constructor === MatrixInterface) {
throw new Error("Class is of abstract type and can't be instantiated");
}
const methods = [
// TODO: don't check this at runtime but still at compile time
/*const methods = [
'add',
'setElement',
'reset',
Expand Down Expand Up @@ -48,6 +49,6 @@ export class MatrixInterface {
if (this[method] === undefined) {
throw new Error(`${method}() method must be implemented`);
}
});
});*/
}
}
30 changes: 15 additions & 15 deletions src/math/p5.Vector.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,12 @@ class Vector {
// This is how it comes in with createVector()
// This check if the first argument is a function
constructor(...args) {
let values = args.map(arg => arg || 0);
let values = args; // .map(arg => arg || 0);
if (typeof args[0] === 'function') {
this.isPInst = true;
this._fromRadians = args[0];
this._toRadians = args[1];
values = args.slice(2).map(arg => arg || 0);
values = args.slice(2); // .map(arg => arg || 0);
}
let dimensions = values.length; // TODO: make default 3 if no arguments
if (dimensions === 0) {
Expand Down Expand Up @@ -272,7 +272,7 @@ class Vector {
* </div>
*/
toString() {
return `[${this.values.join(', ')}]`;
return `[${this._values.join(', ')}]`;
}

/**
Expand Down Expand Up @@ -334,13 +334,13 @@ class Vector {
*/
set(...args) {
if (args[0] instanceof Vector) {
this.values = args[0].values.slice();
this._values = args[0].values.slice();
} else if (Array.isArray(args[0])) {
this.values = args[0].map(arg => arg || 0);
this._values = args[0].map(arg => arg || 0);
} else {
this.values = args.map(arg => arg || 0);
this._values = args.map(arg => arg || 0);
}
this.dimensions = this.values.length;
this.dimensions = this._values.length;
return this;
}

Expand Down Expand Up @@ -374,9 +374,9 @@ class Vector {
*/
copy() {
if (this.isPInst) {
return new Vector(this._fromRadians, this._toRadians, ...this.values);
return new Vector(this._fromRadians, this._toRadians, ...this._values);
} else {
return new Vector(...this.values);
return new Vector(...this._values);
}
}

Expand Down Expand Up @@ -521,7 +521,7 @@ class Vector {
args = args[0];
}
args.forEach((value, index) => {
this.values[index] = (this.values[index] || 0) + (value || 0);
this._values[index] = (this._values[index] || 0) + (value || 0);
});
return this;
}
Expand Down Expand Up @@ -833,15 +833,15 @@ class Vector {
sub(...args) {
if (args[0] instanceof Vector) {
args[0].values.forEach((value, index) => {
this.values[index] -= value || 0;
this._values[index] -= value || 0;
});
} else if (Array.isArray(args[0])) {
args[0].forEach((value, index) => {
this.values[index] -= value || 0;
this._values[index] -= value || 0;
});
} else {
args.forEach((value, index) => {
this.values[index] -= value || 0;
this._values[index] -= value || 0;
});
}
return this;
Expand Down Expand Up @@ -1044,7 +1044,7 @@ class Vector {
mult(...args) {
if (args.length === 1 && args[0] instanceof Vector) {
const v = args[0];
const maxLen = Math.min(this.values.length, v.values.length);
const maxLen = Math.min(this._values.length, v.values.length);
for (let i = 0; i < maxLen; i++) {
if (Number.isFinite(v.values[i]) && typeof v.values[i] === 'number') {
this._values[i] *= v.values[i];
Expand All @@ -1058,7 +1058,7 @@ class Vector {
}
} else if (args.length === 1 && Array.isArray(args[0])) {
const arr = args[0];
const maxLen = Math.min(this.values.length, arr.length);
const maxLen = Math.min(this._values.length, arr.length);
for (let i = 0; i < maxLen; i++) {
if (Number.isFinite(arr[i]) && typeof arr[i] === 'number') {
this._values[i] *= arr[i];
Expand Down
Loading