Skip to content

Commit

Permalink
Merge pull request #7471 from processing/splineProperty
Browse files Browse the repository at this point in the history
Implement splineProperty
  • Loading branch information
davepagurek authored Jan 20, 2025
2 parents 92da914 + e8e2c28 commit fd39484
Show file tree
Hide file tree
Showing 11 changed files with 73 additions and 135 deletions.
6 changes: 3 additions & 3 deletions src/core/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -1347,7 +1347,7 @@ export const FLOAT = 'float';
export const HALF_FLOAT = 'half-float';

/**
* The `splineEnds` mode where splines curve through
* The `splineProperty('ends')` mode where splines curve through
* their first and last points.
* @typedef {unique symbol} INCLUDE
* @property {INCLUDE} INCLUDE
Expand All @@ -1356,7 +1356,7 @@ export const HALF_FLOAT = 'half-float';
export const INCLUDE = Symbol('include');

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

/**
* The `splineEnds` mode where the spline loops back to its first point.
* The `splineProperty('ends')` mode where the spline loops back to its first point.
* Only used internally.
* @typedef {unique symbol} JOIN
* @property {JOIN} JOIN
Expand Down
26 changes: 19 additions & 7 deletions src/core/p5.Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,18 @@ import { Image } from '../image/p5.Image';
import { Vector } from '../math/p5.Vector';
import { Shape } from '../shape/custom_shapes';

class ClonableObject {
constructor(obj = {}) {
for (const key in obj) {
this[key] = obj[key];
}
}

clone() {
return new ClonableObject(this);
}
};

class Renderer {
static states = {
strokeColor: null,
Expand All @@ -30,7 +42,7 @@ class Renderer {
textAlign: constants.LEFT,
textBaseline: constants.BASELINE,
bezierOrder: 3,
splineEnds: constants.INCLUDE,
splineProperties: new ClonableObject({ ends: constants.INCLUDE, tightness: 0 }),
textWrap: constants.WORD,

// added v2.0
Expand Down Expand Up @@ -77,7 +89,6 @@ class Renderer {

this._clipping = false;
this._clipInvert = false;
this._curveTightness = 0;

this._currentShape = undefined; // Lazily generate current shape
}
Expand Down Expand Up @@ -150,11 +161,11 @@ class Renderer {
this.currentShape.bezierVertex(position, textureCoordinates);
}

splineEnds(mode) {
if (mode === undefined) {
return this.states.splineEnds;
splineProperty(key, value) {
if (value === undefined) {
return this.states.splineProperties[key];
} else {
this.states.splineEnds = mode;
this.states.splineProperties[key] = value;
}
this.updateShapeProperties();
}
Expand Down Expand Up @@ -305,7 +316,8 @@ class Renderer {

updateShapeProperties() {
this.currentShape.bezierOrder(this.states.bezierOrder);
this.currentShape.splineEnds(this.states.splineEnds);
this.currentShape.splineProperty('ends', this.states.splineProperties.ends);
this.currentShape.splineProperty('tightness', this.states.splineProperties.tightness);
}

updateShapeVertexProperties() {
Expand Down
59 changes: 2 additions & 57 deletions src/shape/curves.js
Original file line number Diff line number Diff line change
Expand Up @@ -765,61 +765,6 @@ function curves(p5, fn){
return this;
};

/**
* Adjusts the way <a href="#/p5/curve">curve()</a> and
* <a href="#/p5/curveVertex">splineVertex()</a> draw.
*
* Spline curves are like cables that are attached to a set of points.
* `curveTightness()` adjusts how tightly the cable is attached to the points.
*
* The parameter, `tightness`, determines how the curve fits to the vertex
* points. By default, `tightness` is set to 0. Setting tightness to 1,
* as in `curveTightness(1)`, connects the curve's points using straight
* lines. Values in the range from –5 to 5 deform curves while leaving them
* recognizable.
*
* @method curveTightness
* @param {Number} amount amount of tightness.
* @chainable
*
* @example
* <div>
* <code>
* // Move the mouse left and right to see the curve change.
*
* function setup() {
* createCanvas(100, 100);
*
* describe('A black curve forms a sideways U shape. The curve deforms as the user moves the mouse from left to right');
* }
*
* function draw() {
* background(200);
*
* // Set the curve's tightness using the mouse.
* let t = map(mouseX, 0, 100, -5, 5, true);
* curveTightness(t);
*
* // Draw the curve.
* noFill();
* beginShape();
* splineVertex(10, 26);
* splineVertex(10, 26);
* splineVertex(83, 24);
* splineVertex(83, 61);
* splineVertex(25, 65);
* splineVertex(25, 65);
* endShape();
* }
* </code>
* </div>
*/
fn.curveTightness = function(t) {
// p5._validateParameters('curveTightness', arguments);
this._renderer._curveTightness = t;
return this;
};

/**
* Calculates coordinates along a spline curve using interpolation.
*
Expand Down Expand Up @@ -934,7 +879,7 @@ function curves(p5, fn){
*/
fn.curvePoint = function(a, b, c, d, t) {
// p5._validateParameters('curvePoint', arguments);
const s = this._renderer._curveTightness,
const s = this._renderer.states.splineProperties.tightness,
t3 = t * t * t,
t2 = t * t,
f1 = (s - 1) / 2 * t3 + (1 - s) * t2 + (s - 1) / 2 * t,
Expand Down Expand Up @@ -1051,7 +996,7 @@ function curves(p5, fn){
fn.curveTangent = function(a, b, c, d, t) {
// p5._validateParameters('curveTangent', arguments);

const s = this._renderer._curveTightness,
const s = this._renderer.states.splineProperties.tightness,
tt3 = t * t * 3,
t2 = t * 2,
f1 = (s - 1) / 2 * tt3 + (1 - s) * t2 + (s - 1) / 2,
Expand Down
53 changes: 27 additions & 26 deletions src/shape/custom_shapes.js
Original file line number Diff line number Diff line change
Expand Up @@ -280,8 +280,10 @@ to interpolated endpoints (a breaking change)
*/
class SplineSegment extends Segment {
#vertexCapacity = Infinity;
_splineEnds = constants.INCLUDE;
_splineTightness = 0;
_splineProperties = {
ends: constants.INCLUDE,
tightness: 0
};

get vertexCapacity() {
return this.#vertexCapacity;
Expand All @@ -296,15 +298,15 @@ class SplineSegment extends Segment {
}

get canOverrideAnchor() {
return this._splineEnds === constants.EXCLUDE;
return this._splineProperties.ends === constants.EXCLUDE;
}

// assuming for now that the first interpolated vertex is always
// the second vertex passed to splineVertex()
// if this spline segment doesn't follow another segment,
// the first vertex is in an anchor
get _firstInterpolatedVertex() {
if (this._splineEnds === constants.EXCLUDE) {
if (this._splineProperties.ends === constants.EXCLUDE) {
return this._comesAfterSegment ?
this.vertices[1] :
this.vertices[0];
Expand All @@ -328,10 +330,10 @@ class SplineSegment extends Segment {
// doesn't line up with end of last segment
addToShape(shape) {
const added = super.addToShape(shape);
this._splineEnds = shape._splineEnds;
this._splineTightness = shape._splineTightness;
this._splineProperties.ends = shape._splineProperties.ends;
this._splineProperties.tightness = shape._splineProperties.tightness;

if (this._splineEnds !== constants.EXCLUDE) return added;
if (this._splineProperties.ends !== constants.EXCLUDE) return added;

let verticesPushed = !this._belongsToShape;
let lastPrimitive = shape.at(-1, -1);
Expand Down Expand Up @@ -367,9 +369,9 @@ class SplineSegment extends Segment {

// override method on base class
getEndVertex() {
if (this._splineEnds === constants.INCLUDE) {
if (this._splineProperties.ends === constants.INCLUDE) {
return super.getEndVertex();
} else if (this._splineEnds === constants.EXCLUDE) {
} else if (this._splineProperties.ends === constants.EXCLUDE) {
return this.vertices.at(-2);
} else {
return this.getStartVertex();
Expand All @@ -389,10 +391,10 @@ class SplineSegment extends Segment {
}

const prevVertex = this.getStartVertex();
if (this._splineEnds === constants.INCLUDE) {
if (this._splineProperties.ends === constants.INCLUDE) {
points.unshift(prevVertex);
points.push(this.vertices.at(-1));
} else if (this._splineEnds === constants.JOIN) {
} else if (this._splineProperties.ends === constants.JOIN) {
points.unshift(this.vertices.at(-1), prevVertex);
points.push(prevVertex, this.vertices.at(0));
}
Expand All @@ -410,7 +412,7 @@ class SplineSegment extends Segment {
}

close() {
this._splineEnds = constants.JOIN;
this._splineProperties.ends = constants.JOIN;
}
}

Expand Down Expand Up @@ -581,10 +583,12 @@ class Shape {
#initialVertexProperties;
#primitiveShapeCreators;
#bezierOrder = 3;
_splineTightness = 0;
kind = null;
contours = [];
_splineEnds = constants.INCLUDE;
_splineProperties = {
tightness: 0,
ends: constants.INCLUDE
};
userVertexProperties = null;

constructor(
Expand Down Expand Up @@ -828,12 +832,8 @@ class Shape {
this.#bezierOrder = order;
}

splineEnds(mode) {
this._splineEnds = mode;
}

splineTightness(tightness) {
this._splineTightness = tightness;
splineProperty(key, value) {
this._splineProperties[key] = value;
}

/*
Expand Down Expand Up @@ -1076,7 +1076,7 @@ class PrimitiveToPath2DConverter extends PrimitiveVisitor {
const shape = splineSegment._shape;

if (
splineSegment._splineEnds === constants.EXCLUDE &&
splineSegment._splineProperties.ends === constants.EXCLUDE &&
!splineSegment._comesAfterSegment
) {
let startVertex = splineSegment._firstInterpolatedVertex;
Expand All @@ -1088,7 +1088,7 @@ class PrimitiveToPath2DConverter extends PrimitiveVisitor {
);
let bezierArrays = shape.catmullRomToBezier(
arrayVertices,
splineSegment._splineTightness
splineSegment._splineProperties.tightness
).map(arr => arr.map(vertArr => shape.arrayToVertex(vertArr)));
for (const array of bezierArrays) {
const points = array.flatMap(vert => [vert.position.x, vert.position.y]);
Expand Down Expand Up @@ -1217,7 +1217,7 @@ class PrimitiveToVerticesConverter extends PrimitiveVisitor {
);
let bezierArrays = shape.catmullRomToBezier(
arrayVertices,
splineSegment._splineTightness
splineSegment._splineProperties.tightness
);
let startVertex = shape.vertexToArray(splineSegment._firstInterpolatedVertex);
for (const array of bezierArrays) {
Expand Down Expand Up @@ -1596,10 +1596,11 @@ function customShapes(p5, fn) {

/**
* TODO: documentation
* @param {SHOW|HIDE} mode
* @param {String} key
* @param value
*/
fn.splineEnds = function(mode) {
return this._renderer.splineEnds(mode);
fn.splineProperty = function(key, value) {
return this._renderer.splineProperty(key, value);
};

/**
Expand Down
30 changes: 0 additions & 30 deletions src/webgl/p5.RendererGL.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,8 +407,6 @@ class RendererGL extends Renderer {
this.filterLayerTemp = undefined;
this.defaultFilterShaders = {};

this._curveTightness = 6;

this.fontInfos = {};

this._curShader = undefined;
Expand Down Expand Up @@ -2424,34 +2422,6 @@ class RendererGL extends Renderer {
_vToNArray(arr) {
return arr.flatMap((item) => [item.x, item.y, item.z]);
}

// function to calculate BezierVertex Coefficients
_bezierCoefficients(t) {
const t2 = t * t;
const t3 = t2 * t;
const mt = 1 - t;
const mt2 = mt * mt;
const mt3 = mt2 * mt;
return [mt3, 3 * mt2 * t, 3 * mt * t2, t3];
}

// function to calculate QuadraticVertex Coefficients
_quadraticCoefficients(t) {
const t2 = t * t;
const mt = 1 - t;
const mt2 = mt * mt;
return [mt2, 2 * mt * t, t2];
}

// function to convert Bezier coordinates to Catmull Rom Splines
_bezierToCatmull(w) {
const p1 = w[1];
const p2 = w[1] + (w[2] - w[0]) / this._curveTightness;
const p3 = w[2] - (w[3] - w[1]) / this._curveTightness;
const p4 = w[2];
const p = [p1, p2, p3, p4];
return p;
}
}

function rendererGL(p5, fn) {
Expand Down
6 changes: 5 additions & 1 deletion test/unit/core/curves.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ import curves from '../../../src/shape/curves';
suite('Curves', function() {
beforeAll(function() {
mockP5Prototype._renderer = {
_curveTightness: 0
states: {
splineProperties: {
tightness: 0
}
}
};
curves(mockP5, mockP5Prototype);
});
Expand Down
Loading

0 comments on commit fd39484

Please sign in to comment.