Skip to content

Commit 9cfef5a

Browse files
Merge branch 'dev-2.0' into dev-2.0
2 parents 0ce4e89 + 23b90ec commit 9cfef5a

File tree

182 files changed

+3007
-3703
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

182 files changed

+3007
-3703
lines changed

docs/parameterData.json

+2,177-3,161
Large diffs are not rendered by default.

src/color/creating_reading.js

+55
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,61 @@ function creatingReading(p5, fn){
15271527
// p5._validateParameters('lerpColor', arguments);
15281528
return c1.lerp(c2, amt, this._renderer.states.colorMode);
15291529
};
1530+
1531+
/**
1532+
* Blends multiple colors to find a color between them.
1533+
*
1534+
* The `amt` parameter specifies the amount to interpolate between the color
1535+
* stops which are colors at each `amt` value "location" with `amt` values
1536+
* that are between 2 color stops interpolating between them based on its relative
1537+
* distance to both.
1538+
*
1539+
* The way that colors are interpolated depends on the current
1540+
* <a href="#/colorMode">colorMode()</a>.
1541+
*
1542+
* @method paletteLerp
1543+
* @param {[p5.Color|String|Number|Number[], Number][]} colors_stops color stops to interpolate from
1544+
* @param {Number} amt number to use to interpolate relative to color stops
1545+
* @return {p5.Color} interpolated color.
1546+
*
1547+
* @example
1548+
* <div>
1549+
* <code>
1550+
* function setup() {
1551+
* createCanvas(400, 400);
1552+
* }
1553+
*
1554+
* function draw() {
1555+
* // The background goes from white to red to green to blue fill
1556+
* background(paletteLerp([
1557+
* ['white', 0],
1558+
* ['red', 0.05],
1559+
* ['green', 0.25],
1560+
* ['blue', 1]
1561+
* ], millis() / 10000 % 1));
1562+
* }
1563+
* </code>
1564+
* </div>
1565+
*/
1566+
fn.paletteLerp = function(color_stops, amt) {
1567+
const first_color_stop = color_stops[0];
1568+
if (amt < first_color_stop[1])
1569+
return this.color(first_color_stop[0]);
1570+
1571+
for (let i = 1; i < color_stops.length; i++) {
1572+
const color_stop = color_stops[i];
1573+
if (amt < color_stop[1]) {
1574+
const prev_color_stop = color_stops[i - 1];
1575+
return this.lerpColor(
1576+
this.color(prev_color_stop[0]),
1577+
this.color(color_stop[0]),
1578+
(amt - prev_color_stop[1]) / (color_stop[1] - prev_color_stop[1])
1579+
);
1580+
}
1581+
}
1582+
1583+
return this.color(color_stops[color_stops.length - 1][0]);
1584+
};
15301585
}
15311586

15321587
export default creatingReading;

src/core/constants.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1359,7 +1359,7 @@ export const FLOAT = 'float';
13591359
export const HALF_FLOAT = 'half-float';
13601360

13611361
/**
1362-
* The `splineEnds` mode where splines curve through
1362+
* The `splineProperty('ends')` mode where splines curve through
13631363
* their first and last points.
13641364
* @typedef {unique symbol} INCLUDE
13651365
* @property {INCLUDE} INCLUDE
@@ -1368,7 +1368,7 @@ export const HALF_FLOAT = 'half-float';
13681368
export const INCLUDE = Symbol('include');
13691369

13701370
/**
1371-
* The `splineEnds` mode where the first and last points in a spline
1371+
* The `splineProperty('ends')` mode where the first and last points in a spline
13721372
* affect the direction of the curve, but are not rendered.
13731373
* @typedef {unique symbol} EXCLUDE
13741374
* @property {EXCLUDE} EXCLUDE
@@ -1377,7 +1377,7 @@ export const INCLUDE = Symbol('include');
13771377
export const EXCLUDE = Symbol('exclude');
13781378

13791379
/**
1380-
* The `splineEnds` mode where the spline loops back to its first point.
1380+
* The `splineProperty('ends')` mode where the spline loops back to its first point.
13811381
* Only used internally.
13821382
* @typedef {unique symbol} JOIN
13831383
* @property {JOIN} JOIN

src/core/friendly_errors/param_validator.js

+39-26
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,13 @@ function validateParams(p5, fn, lifecycles) {
1919
// const p5Constructors = {};
2020
// NOTE: This is a tempt fix for unit test but is not correct
2121
// Attaced constructors are `undefined`
22-
const p5Constructors = Object.keys(dataDoc).reduce((acc, val) => {
23-
if (val !== 'p5') {
24-
const className = val.substring(3);
25-
acc[className] = p5[className];
22+
const p5Constructors = Object.keys(p5).reduce((acc, val) => {
23+
if (
24+
val.match(/^[A-Z]/) && // Starts with a capital
25+
!val.match(/^[A-Z][A-Z0-9]*$/) && // Is not an all caps constant
26+
p5[val] instanceof Function // Is a function
27+
) {
28+
acc[val] = p5[val];
2629
}
2730
return acc;
2831
}, {});
@@ -125,18 +128,6 @@ function validateParams(p5, fn, lifecycles) {
125128
* @returns {z.ZodSchema} Zod schema
126129
*/
127130
fn.generateZodSchemasForFunc = function (func) {
128-
// A special case for `p5.Color.paletteLerp`, which has an unusual and
129-
// complicated function signature not shared by any other function in p5.
130-
if (func === 'p5.Color.paletteLerp') {
131-
return z.tuple([
132-
z.array(z.tuple([
133-
z.instanceof(p5.Color),
134-
z.number()
135-
])),
136-
z.number()
137-
]);
138-
}
139-
140131
const { funcName, funcClass } = extractFuncNameAndClass(func);
141132
let funcInfo = dataDoc[funcClass][funcName];
142133

@@ -149,18 +140,27 @@ function validateParams(p5, fn, lifecycles) {
149140

150141
// Returns a schema for a single type, i.e. z.boolean() for `boolean`.
151142
const generateTypeSchema = type => {
143+
if (!type) return z.any();
144+
152145
const isArray = type.endsWith('[]');
153146
const baseType = isArray ? type.slice(0, -2) : type;
154147

155148
let typeSchema;
156149

157-
// Type only contains uppercase letters and underscores -> type is a
158-
// constant. Note that because we're ultimately interested in the value of
150+
// Check for constants. Note that because we're ultimately interested in the value of
159151
// the constant, mapping constants to their values via `constantsMap` is
160152
// necessary.
161-
if (/^[A-Z_]+$/.test(baseType)) {
153+
if (baseType in constantsMap) {
162154
typeSchema = z.literal(constantsMap[baseType]);
163155
}
156+
// Some more constants are attached directly to p5.prototype, e.g. by addons:
157+
else if (baseType.match(/^[A-Z][A-Z0-9]*$/) && baseType in fn) {
158+
typeSchema = z.literal(fn[baseType]);
159+
}
160+
// Function types
161+
else if (baseType.startsWith('function')) {
162+
typeSchema = z.function();
163+
}
164164
// All p5 objects start with `p5` in the documentation, i.e. `p5.Camera`.
165165
else if (baseType.startsWith('p5')) {
166166
const className = baseType.substring(baseType.indexOf('.') + 1);
@@ -169,6 +169,19 @@ function validateParams(p5, fn, lifecycles) {
169169
// For primitive types and web API objects.
170170
else if (schemaMap[baseType]) {
171171
typeSchema = schemaMap[baseType];
172+
}
173+
// Tuple types
174+
else if (baseType.startsWith('[') && baseType.endsWith(']')) {
175+
typeSchema = z.tuple(
176+
baseType
177+
.slice(1, -1)
178+
.split(/, */g)
179+
.map(entry => generateTypeSchema(entry))
180+
);
181+
}
182+
// JavaScript classes, e.g. Request
183+
else if (baseType.match(/^[A-Z]/) && baseType in window) {
184+
typeSchema = z.instanceof(window[baseType]);
172185
} else {
173186
throw new Error(`Unsupported type '${type}' in parameter validation. Please report this issue.`);
174187
}
@@ -179,8 +192,8 @@ function validateParams(p5, fn, lifecycles) {
179192
// Generate a schema for a single parameter. In the case where a parameter can
180193
// be of multiple types, `generateTypeSchema` is called for each type.
181194
const generateParamSchema = param => {
182-
const isOptional = param.endsWith('?');
183-
param = param.replace(/\?$/, '');
195+
const isOptional = param?.endsWith('?');
196+
param = param?.replace(/\?$/, '');
184197

185198
let schema;
186199

@@ -193,7 +206,7 @@ function validateParams(p5, fn, lifecycles) {
193206
// our constants sometimes have numeric or non-primitive values.
194207
// 2) In some cases, the type can be constants or strings, making z.enum()
195208
// insufficient for the use case.
196-
if (param.includes('|')) {
209+
if (param?.includes('|')) {
197210
const types = param.split('|');
198211
schema = z.union(types
199212
.map(t => generateTypeSchema(t))
@@ -218,11 +231,11 @@ function validateParams(p5, fn, lifecycles) {
218231
// combinations that are valid for all numbers of parameters.
219232
const generateOverloadCombinations = params => {
220233
// No optional parameters, return the original parameter list right away.
221-
if (!params.some(p => p.endsWith('?'))) {
234+
if (!params.some(p => p?.endsWith('?'))) {
222235
return [params];
223236
}
224237

225-
const requiredParamsCount = params.filter(p => !p.endsWith('?')).length;
238+
const requiredParamsCount = params.filter(p => p === null || !p.endsWith('?')).length;
226239
const result = [];
227240

228241
for (let i = requiredParamsCount; i <= params.length; i++) {
@@ -332,7 +345,7 @@ function validateParams(p5, fn, lifecycles) {
332345
/**
333346
* Prints a friendly error message after parameter validation, if validation
334347
* has failed.
335-
*
348+
*
336349
* @method _friendlyParamError
337350
* @private
338351
* @param {z.ZodError} zodErrorObj - The Zod error object containing validation errors.
@@ -423,7 +436,7 @@ function validateParams(p5, fn, lifecycles) {
423436

424437
// Generates a link to the documentation based on the given function name.
425438
// TODO: Check if the link is reachable before appending it to the error
426-
// message.
439+
// message.
427440
const generateDocumentationLink = (func) => {
428441
const { funcName, funcClass } = extractFuncNameAndClass(func);
429442
const p5BaseUrl = 'https://p5js.org/reference';

0 commit comments

Comments
 (0)