Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement splineProperty #7471

Merged
merged 1 commit into from
Jan 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -2409,34 +2407,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
Loading