diff --git a/src/core/constants.js b/src/core/constants.js index 0885ab3e48..6c36301c70 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -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 @@ -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 @@ -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 diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index bcc69ed086..e5ba194d63 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -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, @@ -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 @@ -77,7 +89,6 @@ class Renderer { this._clipping = false; this._clipInvert = false; - this._curveTightness = 0; this._currentShape = undefined; // Lazily generate current shape } @@ -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(); } @@ -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() { diff --git a/src/shape/curves.js b/src/shape/curves.js index 93a19c1d38..370011152e 100644 --- a/src/shape/curves.js +++ b/src/shape/curves.js @@ -765,61 +765,6 @@ function curves(p5, fn){ return this; }; - /** - * Adjusts the way curve() and - * splineVertex() 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 - *
- * - * // 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(); - * } - * - *
- */ - fn.curveTightness = function(t) { - // p5._validateParameters('curveTightness', arguments); - this._renderer._curveTightness = t; - return this; - }; - /** * Calculates coordinates along a spline curve using interpolation. * @@ -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, @@ -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, diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 84724486c4..58465a2298 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -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; @@ -296,7 +298,7 @@ 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 @@ -304,7 +306,7 @@ class SplineSegment extends Segment { // 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]; @@ -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); @@ -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(); @@ -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)); } @@ -410,7 +412,7 @@ class SplineSegment extends Segment { } close() { - this._splineEnds = constants.JOIN; + this._splineProperties.ends = constants.JOIN; } } @@ -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( @@ -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; } /* @@ -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; @@ -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]); @@ -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) { @@ -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); }; /** diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index b6b90984af..b6ab38c9c3 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -407,8 +407,6 @@ class RendererGL extends Renderer { this.filterLayerTemp = undefined; this.defaultFilterShaders = {}; - this._curveTightness = 6; - this.fontInfos = {}; this._curShader = undefined; @@ -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) { diff --git a/test/unit/core/curves.js b/test/unit/core/curves.js index d389e0fb9d..4840e273b1 100644 --- a/test/unit/core/curves.js +++ b/test/unit/core/curves.js @@ -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); }); diff --git a/test/unit/visual/cases/shapes.js b/test/unit/visual/cases/shapes.js index 61f8c0d3d9..8e7835a604 100644 --- a/test/unit/visual/cases/shapes.js +++ b/test/unit/visual/cases/shapes.js @@ -128,7 +128,7 @@ visualSuite('Shape drawing', function() { visualTest('Drawing with curves with hidden ends', function(p5, screenshot) { setup(p5); p5.beginShape(); - p5.splineEnds(p5.EXCLUDE); + p5.splineProperty('ends', p5.EXCLUDE); p5.splineVertex(10, 10); p5.splineVertex(15, 40); p5.splineVertex(40, 35); @@ -152,7 +152,7 @@ visualSuite('Shape drawing', function() { visualTest('Drawing with curves with tightness', function(p5, screenshot) { setup(p5); - p5.curveTightness(0.5); + p5.splineProperty('tightness', -1); p5.beginShape(); p5.splineVertex(10, 10); p5.splineVertex(15, 40); @@ -166,7 +166,7 @@ visualSuite('Shape drawing', function() { visualTest('Drawing closed curve loops', function(p5, screenshot) { setup(p5); p5.beginShape(); - p5.splineEnds(p5.EXCLUDE); + p5.splineProperty('ends', p5.EXCLUDE); p5.splineVertex(10, 10); p5.splineVertex(15, 40); p5.splineVertex(40, 35); diff --git a/test/unit/visual/cases/typography.js b/test/unit/visual/cases/typography.js index 3bf1332344..df0450f0b2 100644 --- a/test/unit/visual/cases/typography.js +++ b/test/unit/visual/cases/typography.js @@ -543,4 +543,4 @@ visualSuite("Typography", function () { screenshot(); }); }); -}); +}, { shiftThreshold: 3 }); diff --git a/test/unit/visual/screenshots/Shape drawing/2D mode/Drawing with curves with tightness/000.png b/test/unit/visual/screenshots/Shape drawing/2D mode/Drawing with curves with tightness/000.png index 593867d9c8..36ee10117a 100644 Binary files a/test/unit/visual/screenshots/Shape drawing/2D mode/Drawing with curves with tightness/000.png and b/test/unit/visual/screenshots/Shape drawing/2D mode/Drawing with curves with tightness/000.png differ diff --git a/test/unit/visual/screenshots/Shape drawing/WebGL mode/Drawing with curves with tightness/000.png b/test/unit/visual/screenshots/Shape drawing/WebGL mode/Drawing with curves with tightness/000.png index 8d123f745f..e9b6d00541 100644 Binary files a/test/unit/visual/screenshots/Shape drawing/WebGL mode/Drawing with curves with tightness/000.png and b/test/unit/visual/screenshots/Shape drawing/WebGL mode/Drawing with curves with tightness/000.png differ diff --git a/test/unit/visual/visualTest.js b/test/unit/visual/visualTest.js index 4e77574c6c..068e8c7247 100644 --- a/test/unit/visual/visualTest.js +++ b/test/unit/visual/visualTest.js @@ -8,11 +8,6 @@ const { readFile, writeFile } = server.commands // based on antialiasing. const COLOR_THRESHOLD = 25; -// By how many pixels can the snapshot shift? This is -// often useful to accommodate different text rendering -// across environments. -const SHIFT_THRESHOLD = 3; - // The max side length to shrink test images down to before // comparing, for performance. const MAX_SIDE = 50; @@ -39,6 +34,11 @@ function escapeName(name) { let namePrefix = ''; +// By how many pixels can the snapshot shift? This is +// often useful to accommodate different text rendering +// across environments. +let shiftThreshold = 2; + /** * A helper to define a category of visual tests. * @@ -51,7 +51,7 @@ let namePrefix = ''; export function visualSuite( name, callback, - { focus = false, skip = false } = {} + { focus = false, skip = false, shiftThreshold: newShiftThreshold } = {} ) { let suiteFn = describe; if (focus) { @@ -61,11 +61,16 @@ export function visualSuite( suiteFn = suiteFn.skip; } suiteFn(name, () => { + let lastShiftThreshold let lastPrefix; let lastDeviceRatio = window.devicePixelRatio; beforeAll(() => { lastPrefix = namePrefix; namePrefix += escapeName(name) + '/'; + lastShiftThreshold = shiftThreshold; + if (newShiftThreshold !== undefined) { + shiftThreshold = newShiftThreshold + } // Force everything to be 1x window.devicePixelRatio = 1; @@ -76,6 +81,7 @@ export function visualSuite( afterAll(() => { namePrefix = lastPrefix; window.devicePixelRatio = lastDeviceRatio; + shiftThreshold = lastShiftThreshold; }); }); } @@ -109,7 +115,7 @@ export async function checkMatch(actual, expected, p5) { cnv.image(actual, 0, 0); cnv.blendMode(DIFFERENCE); cnv.image(expectedWithBg, 0, 0); - for (let i = 0; i < SHIFT_THRESHOLD; i++) { + for (let i = 0; i < shiftThreshold; i++) { cnv.filter(ERODE, false); } const diff = cnv.get();