Skip to content

Commit 3cb0a74

Browse files
authored
Merge branch 'dev-2.0' into dev-2.0-noiseDetail
2 parents 14d7857 + ce3037d commit 3cb0a74

110 files changed

Lines changed: 558 additions & 186 deletions

File tree

Some content is hidden

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

contributor_docs/es/steward_guidelines.md

Lines changed: 92 additions & 91 deletions
Large diffs are not rendered by default.

src/core/environment.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,10 +319,11 @@ function environment(p5, fn, lifecycles){
319319
cursor = type;
320320
} else if (typeof type === 'string') {
321321
let coords = '';
322-
if (x && y && (typeof x === 'number' && typeof y === 'number')) {
322+
if (typeof x === 'number') { // fix for #8323
323+
y = typeof y === 'number' ? y : 0;
323324
// Note that x and y values must be unit-less positive integers < 32
324325
// https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
325-
coords = `${x} ${y}`;
326+
coords = `${Math.max(x, 0)} ${Math.max(y, 0)}`;
326327
}
327328
if (
328329
type.substring(0, 7) === 'http://' ||

src/shape/2d_primitives.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,10 @@ function primitives(p5, fn){
760760
* createCanvas(100, 100);
761761
*
762762
* background(200);
763-
*
763+
*
764+
* // Making point to 5 pixels
765+
* strokeWeight(5);
766+
*
764767
* // Top-left.
765768
* point(30, 20);
766769
*
@@ -786,6 +789,9 @@ function primitives(p5, fn){
786789
* createCanvas(100, 100);
787790
*
788791
* background(200);
792+
*
793+
* // Making point to 5 pixels.
794+
* strokeWeight(5);
789795
*
790796
* // Top-left.
791797
* point(30, 20);
@@ -816,6 +822,9 @@ function primitives(p5, fn){
816822
* createCanvas(100, 100);
817823
*
818824
* background(200);
825+
*
826+
* // Making point to 5 pixels.
827+
* strokeWeight(5);
819828
*
820829
* // Top-left.
821830
* let a = createVector(30, 20);

src/shape/custom_shapes.js

Lines changed: 105 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2286,18 +2286,114 @@ function customShapes(p5, fn) {
22862286
};
22872287

22882288
/**
2289-
* Get or set multiple spline properties at once.
2290-
*
2291-
* Similar to <a href="#/p5/splineProperty">splineProperty()</a>:
2292-
* `splineProperty('tightness', t)` is the same as
2293-
* `splineProperties({'tightness': t})`
2289+
* Sets multiple properties for spline curves at once.
2290+
*
2291+
* `splineProperties()` accepts an object with key-value pairs to configure
2292+
* how spline curves are drawn. This is a convenient way to set multiple
2293+
* spline properties with a single function call, rather than calling
2294+
* <a href="#/p5/splineProperty">splineProperty()</a> multiple times.
2295+
*
2296+
* The properties object can include:
2297+
* - `tightness`: A number that controls how tightly the curve fits to the
2298+
* vertex points. The default value is 0. Positive values make the curve
2299+
* tighter (straighter), while negative values make it looser. Values
2300+
* between -5 and 5 work best.
2301+
* - `ends`: Controls whether to draw the end segments of the spline. Set to
2302+
* `EXCLUDE` to skip drawing the segments between the first and second
2303+
* points and between the second-to-last and last points. This is useful
2304+
* when you want to use the first and last points as control points only.
2305+
*
2306+
* `splineProperties()` affects curves drawn with
2307+
* <a href="#/p5/splineVertex">splineVertex()</a> within
2308+
* <a href="#/p5/beginShape">beginShape()</a> and
2309+
* <a href="#/p5/endShape">endShape()</a>, as well as curves drawn with
2310+
* <a href="#/p5/spline">spline()</a>. The properties remain active until
2311+
* changed by another call to `splineProperties()` or
2312+
* <a href="#/p5/splineProperty">splineProperty()</a>.
22942313
*
22952314
* @method splineProperties
2296-
* @param {Object} properties An object containing key-value pairs to set.
2297-
*/
2298-
/**
2315+
* @param {Object} values an object containing spline property key-value pairs
2316+
* @chainable
2317+
*
2318+
* @example
2319+
* <div>
2320+
* <code>
2321+
* function setup() {
2322+
* createCanvas(100, 100);
2323+
* background(220);
2324+
*
2325+
* // Set spline tightness using splineProperties
2326+
* splineProperties({
2327+
* tightness: 0.5
2328+
* });
2329+
*
2330+
* // Draw a spline curve
2331+
* noFill();
2332+
* stroke(0);
2333+
* strokeWeight(2);
2334+
*
2335+
* beginShape();
2336+
* splineVertex(20, 80);
2337+
* splineVertex(30, 30);
2338+
* splineVertex(70, 30);
2339+
* splineVertex(80, 80);
2340+
* endShape();
2341+
*
2342+
* // Show vertex points
2343+
* fill(255, 0, 0);
2344+
* noStroke();
2345+
* circle(20, 80, 6);
2346+
* circle(30, 30, 6);
2347+
* circle(70, 30, 6);
2348+
* circle(80, 80, 6);
2349+
*
2350+
* describe('A smooth curved line with tightness 0.5 connecting four red points.');
2351+
* }
2352+
* </code>
2353+
* </div>
2354+
*
2355+
* @example
2356+
* <div>
2357+
* <code>
2358+
* function setup() {
2359+
* createCanvas(100, 100);
2360+
* background(220);
2361+
*
2362+
* // Exclude end segments - first and last points become control points
2363+
* splineProperties({
2364+
* tightness: 0,
2365+
* ends: EXCLUDE
2366+
* });
2367+
*
2368+
* // Draw curve only between middle points
2369+
* noFill();
2370+
* stroke(0);
2371+
* strokeWeight(2);
2372+
*
2373+
* beginShape();
2374+
* splineVertex(10, 50); // Control point (affects curve but not drawn to)
2375+
* splineVertex(30, 20); // Start of visible curve
2376+
* splineVertex(70, 80); // End of visible curve
2377+
* splineVertex(90, 50); // Control point (affects curve but not drawn to)
2378+
* endShape();
2379+
*
2380+
* // Show all points
2381+
* fill(200, 0, 0);
2382+
* noStroke();
2383+
* circle(10, 50, 6); // Control point
2384+
* circle(90, 50, 6); // Control point
2385+
*
2386+
* fill(0, 0, 255);
2387+
* circle(30, 20, 6); // Visible curve point
2388+
* circle(70, 80, 6); // Visible curve point
2389+
*
2390+
* describe('A curved line between two blue points, with red control points at the ends.');
2391+
* }
2392+
* </code>
2393+
* </div>
2394+
*
22992395
* @method splineProperties
2300-
* @returns {Object} The current spline properties.
2396+
* @return {Object}
23012397
*/
23022398
fn.splineProperties = function(values) {
23032399
return this._renderer.splineProperties(values);

src/strands/p5.strands.js

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -65,39 +65,41 @@ function strands(p5, fn) {
6565

6666
p5.Shader.prototype.modify = function(shaderModifier, scope = {}) {
6767
if (shaderModifier instanceof Function) {
68-
// Reset the context object every time modify is called;
69-
// const backend = glslBackend;
70-
initStrandsContext(strandsContext, glslBackend, { active: true });
71-
createShaderHooksFunctions(strandsContext, fn, this);
72-
// TODO: expose this, is internal for debugging for now.
73-
const options = { parser: true, srcLocations: false };
68+
try {
69+
// Reset the context object every time modify is called;
70+
// const backend = glslBackend;
71+
initStrandsContext(strandsContext, glslBackend, { active: true });
72+
createShaderHooksFunctions(strandsContext, fn, this);
73+
// TODO: expose this, is internal for debugging for now.
74+
const options = { parser: true, srcLocations: false };
7475

75-
// 1. Transpile from strands DSL to JS
76-
let strandsCallback;
77-
if (options.parser) {
78-
// #7955 Wrap function declaration code in brackets so anonymous functions are not top level statements, which causes an error in acorn when parsing
79-
// https://github.com/acornjs/acorn/issues/1385
80-
const sourceString = `(${shaderModifier.toString()})`;
81-
strandsCallback = transpileStrandsToJS(p5, sourceString, options.srcLocations, scope);
82-
} else {
83-
strandsCallback = shaderModifier;
84-
}
85-
86-
// 2. Build the IR from JavaScript API
87-
const globalScope = createBasicBlock(strandsContext.cfg, BlockType.GLOBAL);
88-
pushBlock(strandsContext.cfg, globalScope);
89-
strandsCallback();
90-
popBlock(strandsContext.cfg);
76+
// 1. Transpile from strands DSL to JS
77+
let strandsCallback;
78+
if (options.parser) {
79+
// #7955 Wrap function declaration code in brackets so anonymous functions are not top level statements, which causes an error in acorn when parsing
80+
// https://github.com/acornjs/acorn/issues/1385
81+
const sourceString = `(${shaderModifier.toString()})`;
82+
strandsCallback = transpileStrandsToJS(p5, sourceString, options.srcLocations, scope);
83+
} else {
84+
strandsCallback = shaderModifier;
85+
}
9186

92-
// 3. Generate shader code hooks object from the IR
93-
// .......
94-
const hooksObject = generateShaderCode(strandsContext);
87+
// 2. Build the IR from JavaScript API
88+
const globalScope = createBasicBlock(strandsContext.cfg, BlockType.GLOBAL);
89+
pushBlock(strandsContext.cfg, globalScope);
90+
strandsCallback();
91+
popBlock(strandsContext.cfg);
9592

96-
// Reset the strands runtime context
97-
deinitStrandsContext(strandsContext);
93+
// 3. Generate shader code hooks object from the IR
94+
// .......
95+
const hooksObject = generateShaderCode(strandsContext);
9896

99-
// Call modify with the generated hooks object
100-
return oldModify.call(this, hooksObject);
97+
// Call modify with the generated hooks object
98+
return oldModify.call(this, hooksObject);
99+
} finally {
100+
// Reset the strands runtime context
101+
deinitStrandsContext(strandsContext);
102+
}
101103
}
102104
else {
103105
return oldModify.call(this, shaderModifier)

src/strands/strands_api.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,8 +230,18 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
230230
const originalp5Fn = fn[typeInfo.fnName];
231231
fn[typeInfo.fnName] = function(...args) {
232232
if (strandsContext.active) {
233-
const { id, dimension } = build.primitiveConstructorNode(strandsContext, typeInfo, args);
234-
return createStrandsNode(id, dimension, strandsContext);
233+
if (args.length === 1 && args[0].dimension && args[0].dimension === typeInfo.dimension) {
234+
const { id, dimension } = build.functionCallNode(strandsContext, typeInfo.fnName, args, {
235+
overloads: [{
236+
params: [args[0].typeInfo()],
237+
returnType: typeInfo,
238+
}]
239+
});
240+
return createStrandsNode(id, dimension, strandsContext);
241+
} else {
242+
const { id, dimension } = build.primitiveConstructorNode(strandsContext, typeInfo, args);
243+
return createStrandsNode(id, dimension, strandsContext);
244+
}
235245
} else if (originalp5Fn) {
236246
return originalp5Fn.apply(this, args);
237247
} else {

src/strands/strands_node.js

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,21 @@ export class StrandsNode {
1313
const nodeData = getNodeDataFromID(dag, this.id);
1414
if (nodeData && nodeData.identifier) {
1515
this._originalIdentifier = nodeData.identifier;
16+
}
17+
if (nodeData) {
1618
this._originalBaseType = nodeData.baseType;
1719
this._originalDimension = nodeData.dimension;
1820
}
1921
}
2022
copy() {
2123
return createStrandsNode(this.id, this.dimension, this.strandsContext);
2224
}
25+
typeInfo() {
26+
return {
27+
baseType: this._originalBaseType || BaseType.FLOAT,
28+
dimension: this.dimension
29+
};
30+
}
2331
bridge(value) {
2432
const { dag, cfg } = this.strandsContext;
2533
const orig = getNodeDataFromID(dag, this.id);
@@ -30,8 +38,8 @@ export class StrandsNode {
3038
newValueID = value.id;
3139
} else {
3240
const newVal = primitiveConstructorNode(
33-
this.strandsContext,
34-
{ baseType, dimension: this.dimension },
41+
this.strandsContext,
42+
{ baseType, dimension: this.dimension },
3543
value
3644
);
3745
newValueID = newVal.id;
@@ -85,8 +93,8 @@ export class StrandsNode {
8593
newValueID = value.id;
8694
} else {
8795
const newVal = primitiveConstructorNode(
88-
this.strandsContext,
89-
{ baseType, dimension: this.dimension },
96+
this.strandsContext,
97+
{ baseType, dimension: this.dimension },
9098
value
9199
);
92100
newValueID = newVal.id;
@@ -159,4 +167,4 @@ export function createStrandsNode(id, dimension, strandsContext, onRebind) {
159167
new StrandsNode(id, dimension, strandsContext),
160168
swizzleTrap(id, dimension, strandsContext, onRebind)
161169
);
162-
}
170+
}

src/type/p5.Font.js

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -737,25 +737,14 @@ export class Font {
737737

738738
/////////////////////////////// HELPERS ////////////////////////////////
739739

740-
_verticalAlign(size) {
741-
const { sCapHeight } = this.data?.['OS/2'] || {};
742-
const { unitsPerEm = 1000 } = this.data?.head || {};
743-
const { ascender = 0, descender = 0 } = this.data?.hhea || {};
744-
const current = ascender / 2;
745-
const target = (sCapHeight || (ascender + descender)) / 2;
746-
const offset = target - current;
747-
return offset * size / unitsPerEm;
748-
}
749-
750740
/*
751741
Returns an array of line objects, each containing { text, x, y, glyphs: [ {g, path} ] }
752742
*/
753743
_lineateAndPathify(str, x, y, width, height, options = {}) {
754744

755745
let renderer = options?.graphics?._renderer || this._pInst._renderer;
756-
757-
// save the baseline
758-
let setBaseline = renderer.drawingContext.textBaseline;
746+
renderer.push();
747+
renderer.textFont(this);
759748

760749
// lineate and compute bounds for the text
761750
let { lines, bounds } = renderer._computeBounds
@@ -772,8 +761,7 @@ export class Font {
772761
const axs = this._currentAxes(renderer);
773762
let pathsForLine = lines.map(l => this._lineToGlyphs(l, { scale, axs }));
774763

775-
// restore the baseline
776-
renderer.drawingContext.textBaseline = setBaseline;
764+
renderer.pop();
777765

778766
return pathsForLine;
779767
}
@@ -857,7 +845,7 @@ export class Font {
857845

858846
_position(renderer, lines, bounds, width, height) {
859847

860-
let { textAlign, textLeading } = renderer.states;
848+
let { textAlign, textLeading, textSize } = renderer.states;
861849
let metrics = this._measureTextDefault(renderer, 'X');
862850
let ascent = metrics.fontBoundingBoxAscent;
863851

@@ -1451,7 +1439,8 @@ function font(p5, fn) {
14511439
* <code>
14521440
* // Some other forms of loading fonts:
14531441
* loadFont("https://fonts.googleapis.com/css2?family=Bricolage+Grotesque:opsz,wght@12..96,200..800&display=swap");
1454-
* loadFont(`@font-face { font-family: "Bricolage Grotesque", serif; font-optical-sizing: auto; font-weight: 400; font-style: normal; font-variation-settings: "wdth" 100; }`);
1442+
*
1443+
* loadFont('@font-face { font-family: "Bricolage Grotesque", serif; font-optical-sizing: auto; font-weight: 400; font-style: normal; font-variation-settings: "wdth" 100; }');
14551444
* </code>
14561445
* </div>
14571446
*/
@@ -1480,7 +1469,15 @@ function font(p5, fn) {
14801469
let isCSS = path.includes('@font-face');
14811470

14821471
if (!isCSS) {
1483-
const info = await fetch(path, { method: 'HEAD' });
1472+
let info;
1473+
try {
1474+
info = await fetch(path, { method: 'HEAD' });
1475+
} catch (e) {
1476+
// Sometimes files fail when requested with HEAD. Fallback to a
1477+
// regular GET. It loads more data, but at least then it's cached
1478+
// for the likely case when we have to fetch the whole thing.
1479+
info = await fetch(path);
1480+
}
14841481
const isCSSFile = info.headers.get('content-type')?.startsWith('text/css');
14851482
if (isCSSFile) {
14861483
isCSS = true;

0 commit comments

Comments
 (0)