p5
Version:
[](https://www.npmjs.com/package/p5)
787 lines (719 loc) • 25.9 kB
JavaScript
import { p as p5 } from '../../main-BKN5yFoS.js';
import { translator } from '../internationalization.js';
import '../../constants-BRcElHU3.js';
import '../transform.js';
import '../structure.js';
import '../environment.js';
import '../../math/p5.Vector.js';
import '../../rendering-CvUVN-Vb.js';
import '../../creating_reading-Cr8L2Jnm.js';
import 'colorjs.io/fn';
import '../../color/color_spaces/hsb.js';
import '../../dom/p5.Element.js';
import '../../dom/p5.File.js';
import '../../io/p5.XML.js';
import '../../p5.Renderer-R23xoC7s.js';
import '../../image/filters.js';
import '../../shape/custom_shapes.js';
import '../States.js';
import '../../io/utilities.js';
import 'file-saver';
import '../../dom/p5.MediaElement.js';
import '../../shape/2d_primitives.js';
import '../helpers.js';
import '../../shape/attributes.js';
import '../../shape/curves.js';
import '../../shape/vertex.js';
import '../../color/setting.js';
import 'omggif';
import '../../io/csv.js';
import 'gifenc';
import '../../image/pixels.js';
import '../../webgl/GeometryBuilder.js';
import '../../math/p5.Matrix.js';
import '../../math/Matrices/Matrix.js';
import '../../math/Matrices/MatrixInterface.js';
import '../../webgl/p5.Geometry.js';
import '../../webgl/p5.DataArray.js';
import '../../webgl/p5.Quat.js';
import '../../webgl/p5.RenderBuffer.js';
import '../../webgl/ShapeBuilder.js';
import 'libtess';
import '../../webgl/GeometryBufferCache.js';
import '../../image/const.js';
import '../../math/trigonometry.js';
import '../../image/filterRenderer2D.js';
import 'i18next';
import 'i18next-browser-languagedetector';
/**
* @for p5
* @requires core
*/
// import dataDoc from '../../../docs/parameterData.json';
if (typeof IS_MINIFIED !== 'undefined') {
p5._validateParameters = p5._clearValidateParamsCache = () => {};
} else {
// For speedup over many runs. funcSpecificConstructors[func] only has the
// constructors for types which were seen earlier as args of "func"
// const funcSpecificConstructors = {};
window.addEventListener('load', () => {
// Make a list of all p5 classes to be used for argument validation
// This must be done only when everything has loaded otherwise we get
// an empty array
for (let key of Object.keys(p5)) {
// Get a list of all constructors in p5. They are functions whose names
// start with a capital letter
if (typeof p5[key] === 'function' && key[0] !== key[0].toLowerCase()) {
p5[key];
}
}
});
const argumentTree = {};
// The following two functions are responsible for querying and inserting
// into the argument tree. It stores the types of arguments that each
// function has seen so far. It is used to query if a sequence of
// arguments seen in validate parameters was seen before.
// Lets consider that the following segment of code runs repeatedly, perhaps
// in a loop or in draw()
// color(10, 10, 10);
// color(10, 10);
// color('r', 'g', 'b');
// After the first of run the code segment, the argument tree looks like
// - color
// - number
// - number
// - number
// - seen: true
// - seen: true
// - string
// - string
// - string
// - seen: true
// seen: true signifies that this argument was also seen as the last
// argument in a call. Now in the second run of the sketch, it would traverse
// the existing tree and see seen: true, i.e this sequence was seen
// before and so scoring can be skipped. This also prevents logging multiple
// validation messages for the same thing.
/**
* Query type and return the result as an object
*
* This would be called repeatedly over and over again,
* so it needs to be as optimized for performance as possible
* @method addType
* @private
*/
// const addType = (value, obj, func) => {
// let type = typeof value;
// if (basicTypes[type]) {
// if (constantsReverseMap[value]) {
// // check if the value is a p5 constant and if it is, we would want the
// // value itself to be stored in the tree instead of the type
// obj = obj[value] || (obj[value] = {});
// } else {
// obj = obj[type] || (obj[type] = {});
// }
// } else if (value === null) {
// // typeof null -> "object". don't want that
// obj = obj['null'] || (obj['null'] = {});
// } else {
// // objects which are instances of p5 classes have nameless constructors.
// // native objects have a constructor named "Object". This check
// // differentiates between the two so that we dont waste time finding the
// // p5 class if we just have a native object
// if (value.constructor && value.constructor.name) {
// obj = obj[value.constructor.name] || (obj[value.constructor.name] = {});
// return obj;
// }
// // constructors for types defined in p5 do not have a name property.
// // e.constructor.name gives "". Code in this segment is a workaround for it
// // p5C will only have the name: constructor mapping for types
// // which were already seen as args of "func"
// let p5C = funcSpecificConstructors[func];
// // p5C would contain much fewer items than p5Constructors. if we find our
// // answer in p5C, we don't have to scan through p5Constructors
// if (p5C === undefined) {
// // if there isn't an entry yet for func
// // make an entry of empty object
// p5C = funcSpecificConstructors[func] = {};
// }
// for (let key in p5C) {
// // search on the constructors we have already seen (smaller search space)
// if (value instanceof p5C[key]) {
// obj = obj[key] || (obj[key] = {});
// return obj;
// }
// }
// for (let key in p5Constructors) {
// // if the above search didn't work, search on all p5 constructors
// if (value instanceof p5Constructors[key]) {
// obj = obj[key] || (obj[key] = {});
// // if found, add to known constructors for this function
// p5C[key] = p5Constructors[key];
// return obj;
// }
// }
// // nothing worked, put the type as it is
// obj = obj[type] || (obj[type] = {});
// }
// return obj;
// };
/**
* Build the argument type tree, argumentTree
*
* This would be called repeatedly over and over again,
* so it needs to be as optimized for performance as possible
* @method buildArgTypeCache
* @private
*/
// const buildArgTypeCache = (func, arr) => {
// // get the if an argument tree for current function already exists
// let obj = argumentTree[func];
// if (obj === undefined) {
// // if it doesn't, create an empty tree
// obj = argumentTree[func] = {};
// }
// for (let i = 0, len = arr.length; i < len; ++i) {
// let value = arr[i];
// if (value instanceof Array) {
// // an array is passed as an argument, expand it and get the type of
// // each of its element. We distinguish the start of an array with 'as'
// // or arraystart. This would help distinguish between the arguments
// // (number, number, number) and (number, [number, number])
// obj = obj['as'] || (obj['as'] = {});
// for (let j = 0, lenA = value.length; j < lenA; ++j) {
// obj = addType(value[j], obj, func);
// }
// } else {
// obj = addType(value, obj, func);
// }
// }
// return obj;
// };
/**
* Query data.json
* This is a helper function for validateParameters()
* @method lookupParamDoc
* @private
*/
// const lookupParamDoc = func => {
// // look for the docs in the `data.json` datastructure
// const ichDot = func.lastIndexOf('.');
// const funcName = func.slice(ichDot + 1);
// const funcClass = func.slice(0, ichDot !== -1 ? ichDot : 0) || 'p5';
// const classitems = arrDoc;
// let queryResult = classitems[funcClass][funcName];
// // different JSON structure for funct with multi-format
// const overloads = [];
// if (queryResult.hasOwnProperty('overloads')) {
// // add all the overloads
// for (let i = 0; i < queryResult.overloads.length; i++) {
// overloads.push({ formats: queryResult.overloads[i].params || [] });
// }
// } else {
// // no overloads, just add the main method definition
// overloads.push({ formats: queryResult.params || [] });
// }
// // parse the parameter types for each overload
// const mapConstants = {};
// let maxParams = 0;
// overloads.forEach(overload => {
// const formats = overload.formats;
// // keep a record of the maximum number of arguments
// // this method requires.
// if (maxParams < formats.length) {
// maxParams = formats.length;
// }
// // calculate the minimum number of arguments
// // this overload requires.
// let minParams = formats.length;
// while (minParams > 0 && formats[minParams - 1].optional) {
// minParams--;
// }
// overload.minParams = minParams;
// // loop through each parameter position, and parse its types
// formats.forEach(format => {
// // split this parameter's types
// format.types = format.type.split('|').map(function ct(type) {
// // array
// if (type.slice(-2) === '[]') {
// return {
// name: type,
// array: ct(type.slice(0, -2))
// };
// }
// let lowerType = type.toLowerCase();
// // constant
// if (lowerType === 'constant') {
// let constant;
// if (mapConstants.hasOwnProperty(format.name)) {
// constant = mapConstants[format.name];
// } else {
// // parse possible constant values from description
// const myRe = /either\s+(?:[A-Z0-9_]+\s*,?\s*(?:or)?\s*)+/g;
// const values = {};
// const names = [];
// constant = mapConstants[format.name] = {
// values,
// names
// };
// const myArray = myRe.exec(format.description);
// if (func === 'endShape' && format.name === 'mode') {
// values[constants.CLOSE] = true;
// names.push('CLOSE');
// } else {
// const match = myArray[0];
// const reConst = /[A-Z0-9_]+/g;
// let matchConst;
// while ((matchConst = reConst.exec(match)) !== null) {
// const name = matchConst[0];
// if (name in constants) {
// values[constants[name]] = true;
// names.push(name);
// }
// }
// }
// }
// return {
// name: type,
// builtin: lowerType,
// names: constant.names,
// values: constant.values
// };
// }
// // Handle specific constants in types, e.g. in endShape:
// // @param {CLOSE} [close]
// // Rather than trying to parse the types out of the description, we
// // can use the constant directly from the type
// if (type in constants) {
// return {
// name: type,
// builtin: 'constant',
// names: [type],
// values: { [constants[type]]: true }
// };
// }
// // function
// if (lowerType.slice(0, 'function'.length) === 'function') {
// lowerType = 'function';
// }
// // builtin
// if (builtinTypes.has(lowerType)) {
// return { name: type, builtin: lowerType };
// }
// // find type's prototype
// let t = window;
// const typeParts = type.split('.');
// // special-case 'p5' since it may be non-global
// if (typeParts[0] === 'p5') {
// t = p5;
// typeParts.shift();
// }
// typeParts.forEach(p => {
// t = t && t[p];
// });
// if (t) {
// return { name: type, prototype: t };
// }
// return { name: type, type: lowerType };
// });
// });
// });
// return {
// overloads,
// maxParams
// };
// };
/**
* Checks whether input type is Number
* This is a helper function for validateParameters()
* @method isNumber
* @private
*
* @returns {Boolean} a boolean indicating whether input type is Number
*/
// const isNumber = param => {
// if (isNaN(parseFloat(param))) return false;
// switch (typeof param) {
// case 'number':
// return true;
// case 'string':
// return !isNaN(param);
// default:
// return false;
// }
// };
/**
* Test type for non-object type parameter validation
* @method testParamType
* @private
*/
// const testParamType = (param, type) => {
// const isArray = param instanceof Array;
// let matches = true;
// if (type.array && isArray) {
// for (let i = 0; i < param.length; i++) {
// const error = testParamType(param[i], type.array);
// if (error) return error / 2; // half error for elements
// }
// } else if (type.prototype) {
// matches = param instanceof type.prototype;
// } else if (type.builtin) {
// switch (type.builtin) {
// case 'number':
// matches = isNumber(param);
// break;
// case 'integer':
// matches = isNumber(param) && Number(param) === Math.floor(param);
// break;
// case 'boolean':
// case 'any':
// matches = true;
// break;
// case 'array':
// matches = isArray;
// break;
// case 'string':
// matches = /*typeof param === 'number' ||*/ typeof param === 'string';
// break;
// case 'constant':
// matches = type.values.hasOwnProperty(param);
// break;
// case 'function':
// matches = param instanceof Function;
// break;
// case 'null':
// matches = param === null;
// break;
// }
// } else {
// matches = typeof param === type.t;
// }
// return matches ? 0 : 1;
// };
/**
* Test type for multiple parameters
* @method testParamTypes
* @private
*/
// const testParamTypes = (param, types) => {
// let minScore = 9999;
// for (let i = 0; minScore > 0 && i < types.length; i++) {
// const score = testParamType(param, types[i]);
// if (minScore > score) minScore = score;
// }
// return minScore;
// };
/**
* generate a score (higher is worse) for applying these args to
* this overload.
* @method scoreOverload
* @private
*/
// const scoreOverload = (args, argCount, overload, minScore) => {
// let score = 0;
// const formats = overload.formats;
// const minParams = overload.minParams;
// // check for too few/many args
// // the score is double number of extra/missing args
// if (argCount < minParams) {
// score = (minParams - argCount) * 2;
// } else if (argCount > formats.length) {
// score = (argCount - formats.length) * 2;
// }
// // loop through the formats, adding up the error score for each arg.
// // quit early if the score gets higher than the previous best overload.
// for (let p = 0; score <= minScore && p < formats.length; p++) {
// const arg = args[p];
// const format = formats[p];
// // '== null' checks for 'null' and typeof 'undefined'
// if (arg == null) {
// // handle undefined args
// if (!format.optional || p < minParams || p < argCount) {
// score += 1;
// }
// } else {
// score += testParamTypes(arg, format.types);
// }
// }
// return score;
// };
/**
* Gets a list of errors for this overload
* @method getOverloadErrors
* @private
*/
// const getOverloadErrors = (args, argCount, overload) => {
// const formats = overload.formats;
// const minParams = overload.minParams;
// // check for too few/many args
// if (argCount < minParams) {
// return [
// {
// type: 'TOO_FEW_ARGUMENTS',
// argCount,
// minParams
// }
// ];
// } else if (argCount > formats.length) {
// return [
// {
// type: 'TOO_MANY_ARGUMENTS',
// argCount,
// maxParams: formats.length
// }
// ];
// }
// const errorArray = [];
// for (let p = 0; p < formats.length; p++) {
// const arg = args[p];
// const format = formats[p];
// // '== null' checks for 'null' and typeof 'undefined'
// if (arg == null) {
// // handle undefined args
// if (!format.optional || p < minParams || p < argCount) {
// errorArray.push({
// type: 'EMPTY_VAR',
// position: p,
// format
// });
// }
// } else if (testParamTypes(arg, format.types) > 0) {
// errorArray.push({
// type: 'WRONG_TYPE',
// position: p,
// format,
// arg
// });
// }
// }
// return errorArray;
// };
/**
* a custom error type, used by the mocha
* tests when expecting validation errors
* @method ValidationError
* @private
*/
p5.ValidationError = (name => {
class err extends Error {
constructor(message, func, type) {
super();
this.message = message;
this.func = func;
this.type = type;
if ('captureStackTrace' in Error) Error.captureStackTrace(this, err);
else this.stack = new Error().stack;
}
}
err.prototype.name = name;
return err;
})('ValidationError');
/**
* Prints a friendly msg after parameter validation
* @method _friendlyParamError
* @private
*/
p5._friendlyParamError = function (errorObj, func) {
let message;
let translationObj;
function formatType() {
const format = errorObj.format;
return format.types
.map(type => (type.names ? type.names.join('|') : type.name))
.join('|');
}
switch (errorObj.type) {
case 'EMPTY_VAR': {
translationObj = {
func,
formatType: formatType(),
// It needs to be this way for i18next-extract to work. The comment
// specifies the values that the context can take so that it can
// statically prepare the translation files with them.
/* i18next-extract-mark-context-next-line ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"] */
position: translator('fes.positions.p', {
context: (errorObj.position + 1).toString(),
defaultValue: (errorObj.position + 1).toString()
}),
url: 'https://p5js.org/examples/data-variable-scope.html'
};
break;
}
case 'WRONG_TYPE': {
const arg = errorObj.arg;
const argType =
arg instanceof Array
? 'array'
: arg === null ? 'null' : arg === undefined ? 'undefined' : typeof arg === 'number' && isNaN(arg) ? 'NaN' : arg.name || typeof arg;
translationObj = {
func,
formatType: formatType(),
argType,
/* i18next-extract-mark-context-next-line ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"] */
position: translator('fes.positions.p', {
context: (errorObj.position + 1).toString(),
defaultValue: (errorObj.position + 1).toString()
})
};
break;
}
case 'TOO_FEW_ARGUMENTS': {
translationObj = {
func,
minParams: errorObj.minParams,
argCount: errorObj.argCount
};
break;
}
case 'TOO_MANY_ARGUMENTS': {
translationObj = {
func,
maxParams: errorObj.maxParams,
argCount: errorObj.argCount
};
break;
}
}
if (translationObj) {
try {
// const re = /Function\.validateParameters.*[\r\n].*[\r\n].*\(([^)]*)/;
const myError = new Error();
let parsed = p5._getErrorStackParser().parse(myError);
if (
parsed[3] &&
parsed[3].functionName &&
parsed[3].functionName.includes('.') &&
p5.prototype[parsed[3].functionName.split('.').slice(-1)[0]]
) {
return;
}
if (p5._throwValidationErrors) {
throw new p5.ValidationError(message, func, errorObj.type);
}
// try to extract the location from where the function was called
if (
parsed[3] &&
parsed[3].fileName &&
parsed[3].lineNumber &&
parsed[3].columnNumber
) {
let location = `${parsed[3].fileName}:${parsed[3].lineNumber}:${
parsed[3].columnNumber
}`;
translationObj.location = translator('fes.location', {
location,
// for e.g. get "sketch.js" from "https://example.com/abc/sketch.js"
file: parsed[3].fileName.split('/').slice(-1),
line: parsed[3].lineNumber
});
// tell fesErrorMonitor that we have already given a friendly message
// for this line, so it need not to do the same in case of an error
p5._fesLogCache[location] = true;
}
} catch (err) {
if (err instanceof p5.ValidationError) {
throw err;
}
}
translationObj.context = errorObj.type;
// i18next-extract-mark-context-next-line ["EMPTY_VAR", "TOO_MANY_ARGUMENTS", "TOO_FEW_ARGUMENTS", "WRONG_TYPE"]
message = translator('fes.friendlyParamError.type', translationObj);
p5._friendlyError(`${message}`, func, 3);
}
};
/**
* Clears cache to avoid having multiple FES messages for the same set of
* parameters.
*
* If a function is called with some set of wrong arguments, and then called
* again with the same set of arguments, the messages due to the second call
* will be supressed. If two tests test on the same wrong arguments, the
* second test won't see the validationError. clearing argumentTree solves it
*
* @method _clearValidateParamsCache
* @private
*/
p5._clearValidateParamsCache = function clearValidateParamsCache() {
for (let key of Object.keys(argumentTree)) {
delete argumentTree[key];
}
};
// allowing access to argumentTree for testing
p5._getValidateParamsArgTree = function getValidateParamsArgTree() {
return argumentTree;
};
/**
* Runs parameter validation by matching the input parameters with information
* from `docs/reference/data.json`.
* Generates and prints a friendly error message using key:
* "fes.friendlyParamError.[*]".
*
* @method _validateParameters
* @private
* @param {String} func Name of the function
* @param {Array} args User input arguments
*
* @example:
* const a;
* ellipse(10,10,a,5);
* console output:
* "It looks like ellipse received an empty variable in spot #2."
*
* @example:
* ellipse(10,"foo",5,5);
* console output:
* "ellipse was expecting a number for parameter #1,
* received "foo" instead."
*/
p5._validateParameters = function validateParameters(func, args) {
// NOTE: no-op for later removal
return;
// if (p5.disableFriendlyErrors) {
// return; // skip FES
// }
// // query / build the argument type tree and check if this sequence
// // has already been seen before.
// let obj = buildArgTypeCache(func, args);
// if (obj.seen) {
// return;
// }
// // mark this sequence as seen
// obj.seen = true;
// // lookup the docs in the 'data.json' file
// const docs = docCache[func] || (docCache[func] = lookupParamDoc(func));
// const overloads = docs.overloads;
// let argCount = args.length;
// // the following line ignores trailing undefined arguments, commenting
// // it to resolve https://github.com/processing/p5.js/issues/4571
// // '== null' checks for 'null' and typeof 'undefined'
// // while (argCount > 0 && args[argCount - 1] == null) argCount--;
// // find the overload with the best score
// let minScore = 99999;
// let minOverload;
// for (let i = 0; i < overloads.length; i++) {
// const score = scoreOverload(args, argCount, overloads[i], minScore);
// if (score === 0) {
// return; // done!
// } else if (minScore > score) {
// // this score is better that what we have so far...
// minScore = score;
// minOverload = i;
// }
// }
// // this should _always_ be true here...
// if (minScore > 0) {
// // get the errors for the best overload
// const errorArray = getOverloadErrors(
// args,
// argCount,
// overloads[minOverload]
// );
// // generate err msg
// for (let n = 0; n < errorArray.length; n++) {
// p5._friendlyParamError(errorArray[n], func);
// }
// }
};
p5.prototype._validateParameters = p5.validateParameters;
}
export { p5 as default };