Skip to content

Commit 902d2f6

Browse files
authored
Merge branch 'dev-2.0' into transform/Clip
2 parents 5a49130 + d287796 commit 902d2f6

104 files changed

Lines changed: 482 additions & 181 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/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
@@ -205,8 +205,18 @@ export function initGlobalStrandsAPI(p5, fn, strandsContext) {
205205
const originalp5Fn = fn[typeInfo.fnName];
206206
fn[typeInfo.fnName] = function(...args) {
207207
if (strandsContext.active) {
208-
const { id, dimension } = build.primitiveConstructorNode(strandsContext, typeInfo, args);
209-
return createStrandsNode(id, dimension, strandsContext);
208+
if (args.length === 1 && args[0].dimension && args[0].dimension === typeInfo.dimension) {
209+
const { id, dimension } = build.functionCallNode(strandsContext, typeInfo.fnName, args, {
210+
overloads: [{
211+
params: [args[0].typeInfo()],
212+
returnType: typeInfo,
213+
}]
214+
});
215+
return createStrandsNode(id, dimension, strandsContext);
216+
} else {
217+
const { id, dimension } = build.primitiveConstructorNode(strandsContext, typeInfo, args);
218+
return createStrandsNode(id, dimension, strandsContext);
219+
}
210220
} else if (originalp5Fn) {
211221
return originalp5Fn.apply(this, args);
212222
} 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: 4 additions & 16 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

0 commit comments

Comments
 (0)