From 62567a92de5a4526dc0efdbd17edaa590b75e38a Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 8 Nov 2024 20:11:32 -0700 Subject: [PATCH 01/42] commit message --- src/app.js | 4 ++++ src/shape/custom_shapes.js | 32 ++++++++++++++++++++++++++++++++ src/shape/index.js | 5 +++++ 3 files changed, 41 insertions(+) create mode 100644 src/shape/custom_shapes.js create mode 100644 src/shape/index.js diff --git a/src/app.js b/src/app.js index a0f677bfb8..19d2329242 100644 --- a/src/app.js +++ b/src/app.js @@ -22,6 +22,10 @@ import './core/shape/attributes'; import './core/shape/curves'; import './core/shape/vertex'; +// shapes +import customShapes from './shape'; +customShapes(p5); + //accessibility import accessibility from './accessibility'; accessibility(p5); diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js new file mode 100644 index 0000000000..fac21ddd9d --- /dev/null +++ b/src/shape/custom_shapes.js @@ -0,0 +1,32 @@ +/** + * @module Shape + * @submodule Custom Shapes + * @for p5 + * @requires core + * @requires constants + */ + +// declare MyClass + +function customShapes(p5, fn) { + + // ---- FUNCTIONS ---- + + // documentation here + + // fn.myFunction = function() { + // this.background('yellow'); // call an existing p5 function + // }; + + // ---- CLASSES ---- + + // documentation here + + // p5.MyClass = MyClass; +} + +export default customShapes; + +if (typeof p5 !== 'undefined') { + customShapes(p5, p5.prototype); +} \ No newline at end of file diff --git a/src/shape/index.js b/src/shape/index.js new file mode 100644 index 0000000000..b0930f9214 --- /dev/null +++ b/src/shape/index.js @@ -0,0 +1,5 @@ +import customShapes from './custom_shapes.js'; + +export default function(p5){ + p5.registerAddon(customShapes); +} From e38b3b98ea9f4d3a8e62975a21d271aea5ba5eb7 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Mon, 11 Nov 2024 23:03:32 -0700 Subject: [PATCH 02/42] Set up files for custom shape refactor --- src/shape/custom_shapes.js | 405 ++++++++++++++++++++++++++++++++++++- 1 file changed, 397 insertions(+), 8 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index fac21ddd9d..1b950d3ecd 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -6,26 +6,415 @@ * @requires constants */ -// declare MyClass +// uncomment the following once you need it (otherwise VS Code complains): +// import * as constants from '../core/constants'; + +// ---- GENERAL CLASSES ---- + +class Shape { + constructor() { + + } +} + +class Contour { + constructor() { + + } +} + +// abstract class +class ShapePrimitive { + constructor() { + + } +} + +class Vertex { + constructor() { + + } +} + +// ---- PATH PRIMITIVES ---- + +class Anchor { + constructor() { + + } +} + +// abstract class +class Segment extends ShapePrimitive { + constructor() { + + } +} + +class LineSegment extends Segment { + constructor() { + + } +} + +class BezierSegment extends Segment { + constructor() { + + } +} + +// consider type and end modes -- see #6766) +// may want to use separate classes, but maybe not +class SplineSegment extends Segment { + constructor() { + + } +} + +// ---- ISOLATED PRIMITIVES ---- + +class Point extends ShapePrimitive { + constructor() { + + } +} + +class Line extends ShapePrimitive { + constructor() { + + } +} + +class Triangle extends ShapePrimitive { + constructor() { + + } +} + +class Quad extends ShapePrimitive { + constructor() { + + } +} + +// ---- TESSELLATION PRIMITIVES ---- + +class TriangleFan extends ShapePrimitive { + constructor() { + + } +} + +class TriangleStrip extends ShapePrimitive { + constructor() { + + } +} + +class QuadStrip extends ShapePrimitive { + constructor() { + + } +} + +// ---- PRIMITIVE VISITORS ---- + +// abstract class +class PrimitiveVisitor { + constructor() { + + } +} + +// using this instead of PrimitiveToContext2DConverter for now +class PrimitiveToPath2DConverter extends PrimitiveVisitor { + constructor() { + + } +} + +class PrimitiveToVerticesConverter extends PrimitiveVisitor { + constructor() { + + } +} + +class PointAtLengthGetter extends PrimitiveVisitor { + constructor() { + + } +} function customShapes(p5, fn) { + // ---- GENERAL CLASSES ---- + + /** + * @private + * A class responsible for... + */ + + p5.Shape = Shape; + + /** + * @private + * A class responsible for... + */ + + p5.Contour = Contour; + + /** + * @private + * A class responsible for... + */ + + p5.ShapePrimitive = ShapePrimitive; + + /** + * @private + * A class responsible for... + */ + + p5.Vertex = Vertex; + + // ---- PATH PRIMITIVES ---- + + /** + * @private + * A class responsible for... + */ + + p5.Anchor = Anchor; + + /** + * @private + * A class responsible for... + */ + + p5.Segment = Segment; + + /** + * @private + * A class responsible for... + */ + + p5.LineSegment = LineSegment; + + /** + * @private + * A class responsible for... + */ + + p5.BezierSegment = BezierSegment; + + /** + * @private + * A class responsible for... + */ + + p5.SplineSegment = SplineSegment; + + // ---- ISOLATED PRIMITIVES ---- + + /** + * @private + * A class responsible for... + */ + + p5.Point = Point; + + /** + * @private + * A class responsible for... + */ + + p5.Line = Line; + + /** + * @private + * A class responsible for... + */ + + p5.Triangle = Triangle; + + /** + * @private + * A class responsible for... + */ + + p5.Quad = Quad; + + // ---- TESSELLATION PRIMITIVES ---- + + /** + * @private + * A class responsible for... + */ + + p5.TriangleFan = TriangleFan; + + /** + * @private + * A class responsible for... + */ + + p5.TriangleStrip = TriangleStrip; + + /** + * @private + * A class responsible for... + */ + + p5.QuadStrip = QuadStrip; + + // ---- PRIMITIVE VISITORS ---- + + /** + * @private + * A class responsible for... + */ + + p5.PrimitiveVisitor = PrimitiveVisitor; + + /** + * @private + * A class responsible for... + */ + + p5.PrimitiveToPath2DConverter = PrimitiveToPath2DConverter; + + /** + * @private + * A class responsible for... + */ + + p5.PrimitiveToVerticesConverter = PrimitiveToVerticesConverter; + + /** + * @private + * A class responsible for... + */ + + p5.PointAtLengthGetter = PointAtLengthGetter; // ---- FUNCTIONS ---- + + // Note: Code is commented out for now, to avoid conflicts with the existing implementation. + + /** + * Top-line description + * + * More details... + */ + + // fn.beginContour = function() { + // // example of how to call an existing p5 function: + // // this.background('yellow'); + // }; - // documentation here + /** + * Top-line description + * + * More details... + */ + + // fn.beginShape = function() { + + // }; + + /** + * Top-line description + * + * More details... + */ + + // fn.bezierVertex = function() { + + // }; + + /** + * Top-line description + * + * More details... + */ + + // fn.curveVertex = function() { + + // }; + + /** + * Top-line description + * + * More details... + */ + + // fn.endContour = function() { + + // }; + + /** + * Top-line description + * + * More details... + */ + + // fn.endShape = function() { + + // }; + + /** + * Top-line description + * + * More details... + */ + + // fn.vertex = function() { + + // }; + + /** + * Top-line description + * + * More details... + */ + + // fn.normal = function() { - // fn.myFunction = function() { - // this.background('yellow'); // call an existing p5 function - // }; + // }; - // ---- CLASSES ---- + /** + * Top-line description + * + * More details... + */ - // documentation here + // fn.vertexProperty = function() { - // p5.MyClass = MyClass; + // }; } export default customShapes; +export { + Shape, + Contour, + ShapePrimitive, + Vertex, + Anchor, + Segment, + LineSegment, + BezierSegment, + SplineSegment, + Point, + Line, + Triangle, + Quad, + TriangleFan, + TriangleStrip, + QuadStrip, + PrimitiveVisitor, + PrimitiveToPath2DConverter, + PrimitiveToVerticesConverter, + PointAtLengthGetter +}; if (typeof p5 !== 'undefined') { customShapes(p5, p5.prototype); From b7787846dc3c776d7144ab1965b2090dbc94d938 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Tue, 12 Nov 2024 05:16:03 -0700 Subject: [PATCH 03/42] Add inline documentation for Shape class --- src/shape/custom_shapes.js | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 1b950d3ecd..c9179dd5cf 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -150,7 +150,28 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class to describe a custom shape made with + * beginShape()/endShape(). + * + * Every `Shape` has a `kind`. The kind takes any value that + * can be passed to beginShape(): + * + * - `PATH` + * - `POINTS` + * - `LINES` + * - `TRIANGLES` + * - `QUADS` + * - `TRIANGLE_FAN` + * - `TRIANGLE_STRIP` + * - `QUAD_STRIP` + * + * All `Shapes` consist of `contours`, which can be thought of as + * subshapes (shapes inside another shape). + * + * @class p5.Shape + * @constructor + * @param {Constant} [kind] either PATH, POINTS, LINES, TRIANGLES, QUADS, TRIANGLE_FAN, + * TRIANGLE_STRIP, or QUAD_STRIP. */ p5.Shape = Shape; From 19e8c48d524d3300f31bb3d13fa94ea5f292cb1d Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Tue, 12 Nov 2024 05:50:10 -0700 Subject: [PATCH 04/42] Fix documentation of default parameter for Shape constructor --- src/shape/custom_shapes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index c9179dd5cf..38c3ce0c0b 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -170,8 +170,8 @@ function customShapes(p5, fn) { * * @class p5.Shape * @constructor - * @param {Constant} [kind] either PATH, POINTS, LINES, TRIANGLES, QUADS, TRIANGLE_FAN, - * TRIANGLE_STRIP, or QUAD_STRIP. + * @param {(PATH|POINTS|LINES|TRIANGLES|QUADS|TRIANGLE_FAN|TRIANGLE_STRIP|QUAD_STRIP)} [kind=PATH] either + * PATH, POINTS, LINES, TRIANGLES, QUADS, TRIANGLE_FAN, TRIANGLE_STRIP, or QUAD_STRIP. */ p5.Shape = Shape; From 8eb74e652654cdb54db7ff26491e5eee9e2aab8b Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Wed, 13 Nov 2024 04:35:35 -0700 Subject: [PATCH 05/42] Update inline documentation for p5.Shape --- src/shape/custom_shapes.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 38c3ce0c0b..fdcdacdaa0 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -165,9 +165,12 @@ function customShapes(p5, fn) { * - `TRIANGLE_STRIP` * - `QUAD_STRIP` * - * All `Shapes` consist of `contours`, which can be thought of as + * A `Shape` of any kind consists of `contours`, which can be thought of as * subshapes (shapes inside another shape). * + * To construct a `Shape` called `myShape`, methods such as `myShape.beginShape()`, + * `myShape.vertex()`, and `myShape.endShape()` may be called. + * * @class p5.Shape * @constructor * @param {(PATH|POINTS|LINES|TRIANGLES|QUADS|TRIANGLE_FAN|TRIANGLE_STRIP|QUAD_STRIP)} [kind=PATH] either From f0fc9086e648f25438384bbab55a7bc045dd3dd7 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Thu, 14 Nov 2024 03:10:57 -0700 Subject: [PATCH 06/42] Document p5.Vertex --- src/shape/custom_shapes.js | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index fdcdacdaa0..2ca4c6ed3e 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -195,7 +195,41 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class to describe a vertex (a point on a shape), in 2D or 3D. + * + * Vertices are the basic building blocks of all `p5.Shape` objects, including + * shapes made with vertex(), arcVertex(), + * bezierVertex(), and splineVertex(). + * + * Like a point on an object in the real world, a vertex has different properties. These include coordinate + * properties `position`, `textureCoordinates`, and `normal`, as well as color properties `fill` and `stroke`. + * + * A vertex called `myVertex` with position coordinates `(2, 3, 5)` and a green stroke may be created like this: + * + *

+     * let myVertex = new p5.Vertex({
+     *   position: createVector(2, 3, 5),
+     *   stroke: color('green')
+     * });
+     * 
+ * + * Note: + * - Coordinate properties are `p5.Vector` objects. + * - Color properties are `p5.Color` objects. + * + * Properties may be specified in all the ways supported by `createVector()` + * and `color()`. For example, a vertex position can be set with two coordinates, as in `createVector(2, 3)`. + * The position is then a `p5.Vector` object with coordinates `(2, 3, 0)`. + * + * In general, the vertices that make up a shape contain the same data, in the same format, regardless of + * how the shape is drawn. Like an artist who decides to ignore color or depth, a renderer can decide + * which of the vertex data to use. This data may be be reformatted using methods of `p5.Vector` and + * `p5.Color`. For easier use with certain renderers, vertices also have a `toArray()` method that converts + * a full vertex object to a one-dimensional array. + * + * @class p5.Vertex + * @constructor + * @param {Object} [properties] vertex properties. */ p5.Vertex = Vertex; From 1a05d51f1e09854c1cb207d76e191a889886d69b Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Thu, 14 Nov 2024 15:17:46 -0700 Subject: [PATCH 07/42] Fix code snippet in p5.Vertex documentation --- src/shape/custom_shapes.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 2ca4c6ed3e..71cd0f117f 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -206,12 +206,12 @@ function customShapes(p5, fn) { * * A vertex called `myVertex` with position coordinates `(2, 3, 5)` and a green stroke may be created like this: * - *

+     * ```js
      * let myVertex = new p5.Vertex({
      *   position: createVector(2, 3, 5),
      *   stroke: color('green')
      * });
-     * 
+ * ``` * * Note: * - Coordinate properties are `p5.Vector` objects. From 45591b08e22af87c31f46a61d53136eaba97e833 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 15 Nov 2024 01:41:12 -0700 Subject: [PATCH 08/42] Revise p5.Vertex documentation and add todo comment about a new implementation --- src/shape/custom_shapes.js | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 71cd0f117f..cb604f322f 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -30,6 +30,34 @@ class ShapePrimitive { } } +/* +TODO: + +If we can rename the p5.Vector method `array()` as `toArray()` for clarity and +consistency with `toString()`, and if we can make the private p5.Color method for +converting to an array public, with the name `toArray()`, then we'll be able to +make Vertex more flexible. + +We can modify the Vertex constructor so that a vertex has only the data it needs, by dynamically +generating properties based on a required properties object: + +``` +class Vertex { + constructor(properties) { + for (const [key, value] of Object.entries(properties)) { + this[key] = value; + } + } + + toArray() { + //convert to 1D array + } +} +``` +Any property names or values may be used, but values that are objects must have +a `toArray()` method. +*/ + class Vertex { constructor() { @@ -218,13 +246,10 @@ function customShapes(p5, fn) { * - Color properties are `p5.Color` objects. * * Properties may be specified in all the ways supported by `createVector()` - * and `color()`. For example, a vertex position can be set with two coordinates, as in `createVector(2, 3)`. - * The position is then a `p5.Vector` object with coordinates `(2, 3, 0)`. + * and `color()`. For example, a vertex position can be set with two coordinates, + * as in `createVector(2, 3)`. * - * In general, the vertices that make up a shape contain the same data, in the same format, regardless of - * how the shape is drawn. Like an artist who decides to ignore color or depth, a renderer can decide - * which of the vertex data to use. This data may be be reformatted using methods of `p5.Vector` and - * `p5.Color`. For easier use with certain renderers, vertices also have a `toArray()` method that converts + * Vertices also have a `toArray()` method that converts * a full vertex object to a one-dimensional array. * * @class p5.Vertex From 37c2db4cd9edef5682840a8783f0670919f7bc66 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 15 Nov 2024 03:26:23 -0700 Subject: [PATCH 09/42] Move TODO comments out of the code (and into project timeline) --- src/shape/custom_shapes.js | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index cb604f322f..cb23a0c40d 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -30,34 +30,6 @@ class ShapePrimitive { } } -/* -TODO: - -If we can rename the p5.Vector method `array()` as `toArray()` for clarity and -consistency with `toString()`, and if we can make the private p5.Color method for -converting to an array public, with the name `toArray()`, then we'll be able to -make Vertex more flexible. - -We can modify the Vertex constructor so that a vertex has only the data it needs, by dynamically -generating properties based on a required properties object: - -``` -class Vertex { - constructor(properties) { - for (const [key, value] of Object.entries(properties)) { - this[key] = value; - } - } - - toArray() { - //convert to 1D array - } -} -``` -Any property names or values may be used, but values that are objects must have -a `toArray()` method. -*/ - class Vertex { constructor() { From 9663eda60383f3e870b18318151c8e2e353d3e05 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 15 Nov 2024 04:52:07 -0700 Subject: [PATCH 10/42] Document p5.Contour --- src/shape/custom_shapes.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index cb23a0c40d..97ad05b559 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -11,9 +11,9 @@ // ---- GENERAL CLASSES ---- -class Shape { +class Shape { constructor() { - + } } @@ -181,7 +181,35 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class to describe a contour made with + * beginContour()/endContour(). + * + * Contours are subshapes: they're made inside of shapes created with + * beginShape()/endShape(). + * For example, a contour may be used to create a hole in a shape. + * + * Contours can have any `kind` that a shape can have: + * + * - `PATH` + * - `POINTS` + * - `LINES` + * - `TRIANGLES` + * - `QUADS` + * - `TRIANGLE_FAN` + * - `TRIANGLE_STRIP` + * - `QUAD_STRIP` + * + * By default, a contour has the same kind as the shape that contains it, + * but different kinds of contours can be made inside the same shape. + * + * A `Contour` of any kind consists of `primitives`, which are the most basic + * shapes that can be drawn. For example, if a contour is a hexagon, then + * it's made from six line-segment primitives. + * + * @class p5.Contour + * @constructor + * @param {(PATH|POINTS|LINES|TRIANGLES|QUADS|TRIANGLE_FAN|TRIANGLE_STRIP|QUAD_STRIP)} [kind=PATH] either + * PATH, POINTS, LINES, TRIANGLES, QUADS, TRIANGLE_FAN, TRIANGLE_STRIP, or QUAD_STRIP. */ p5.Contour = Contour; From 5cd71a2b3cf30a6fdc94b718a3bf82fa770e1ea3 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 15 Nov 2024 06:18:32 -0700 Subject: [PATCH 11/42] Document p5.ShapePrimitive (and revise some other documentation). --- src/shape/custom_shapes.js | 39 ++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 97ad05b559..5de4134c60 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -150,8 +150,7 @@ function customShapes(p5, fn) { /** * @private - * A class to describe a custom shape made with - * beginShape()/endShape(). + * A class to describe a custom shape made with `beginShape()`/`endShape()`. * * Every `Shape` has a `kind`. The kind takes any value that * can be passed to beginShape(): @@ -181,12 +180,12 @@ function customShapes(p5, fn) { /** * @private - * A class to describe a contour made with - * beginContour()/endContour(). + * A class to describe a contour made with `beginContour()`/`endContour()`. * * Contours are subshapes: they're made inside of shapes created with * beginShape()/endShape(). - * For example, a contour may be used to create a hole in a shape. + * For example, a contour may be used to create a hole in a shape. Multiple contours + * may be used to create multiple holes. * * Contours can have any `kind` that a shape can have: * @@ -199,8 +198,8 @@ function customShapes(p5, fn) { * - `TRIANGLE_STRIP` * - `QUAD_STRIP` * - * By default, a contour has the same kind as the shape that contains it, - * but different kinds of contours can be made inside the same shape. + * By default, a contour has the same kind as the shape that contains it, but this + * may be changed by passing a different `kind` to beginContour(). * * A `Contour` of any kind consists of `primitives`, which are the most basic * shapes that can be drawn. For example, if a contour is a hexagon, then @@ -216,7 +215,31 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A base class to describe a shape primitive (a basic shape drawn with + * `beginShape()`/`endShape()`). + * + * Shape primitives are the most basic shapes that can be drawn with + * beginShape()/endShape(): + * + * - segment primitives: line segments, bezier segments, spline segments, and arc segments + * - isolated primitives: points, lines, triangles, and quads + * - tessellation primitives: triangle fans, triangle strips, and quad strips + * + * More complex shapes may be created by combining many primitives, possibly of different kinds. + * + * In a similar way, every shape primitive is built from one or more vertices. + * For example, a point consists of a single vertex, while a triangle consists of three vertices. + * + * Each primitive can add itself to a shape, with an `addToShape()` method. + * + * It can also accept visitor objects with an `accept()` method. When a primitive accepts a visitor, + * it gives the visitor access to its vertex data. For example, one visitor to a segment might turn + * the data into 2D drawing instructions. Another might find a point at a given distance + * along the segment. + * + * @class p5.ShapePrimitive + * @constructor + * @param {p5.Vertex} vertex the first vertex to include in the primitive. */ p5.ShapePrimitive = ShapePrimitive; From 84a0fde58b5685c62cf3073f182cb9ed360dfbb9 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 15 Nov 2024 06:34:53 -0700 Subject: [PATCH 12/42] Revise docs for clarity. --- src/shape/custom_shapes.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 5de4134c60..d3c094ad87 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -182,10 +182,10 @@ function customShapes(p5, fn) { * @private * A class to describe a contour made with `beginContour()`/`endContour()`. * - * Contours are subshapes: they're made inside of shapes created with - * beginShape()/endShape(). - * For example, a contour may be used to create a hole in a shape. Multiple contours - * may be used to create multiple holes. + * Contours may be thought of as shapes inside of other shapes. + * For example, a contour may be used to create a hole in a shape that is created + * with beginShape()/endShape(). + * Multiple contours may be used to create multiple holes. * * Contours can have any `kind` that a shape can have: * @@ -225,7 +225,8 @@ function customShapes(p5, fn) { * - isolated primitives: points, lines, triangles, and quads * - tessellation primitives: triangle fans, triangle strips, and quad strips * - * More complex shapes may be created by combining many primitives, possibly of different kinds. + * More complex shapes may be created by combining many primitives, possibly of different kinds, + * into a single shape. * * In a similar way, every shape primitive is built from one or more vertices. * For example, a point consists of a single vertex, while a triangle consists of three vertices. From 0b117cc84bfd914c959e56dbaa9b68aa07a9a491 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Thu, 21 Nov 2024 22:48:21 -0700 Subject: [PATCH 13/42] Add TODO comment about Vertex interface --- src/shape/custom_shapes.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index d3c094ad87..db337cb614 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -34,6 +34,10 @@ class Vertex { constructor() { } + + // TODO: make sure name of array conversion method is + // consistent with any modifications to the names of corresponding + // properties of p5.Vector and p5.Color } // ---- PATH PRIMITIVES ---- From 2fcc2b08c72fbeabbfed4883396908a78cd8a93b Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 22 Nov 2024 01:52:46 -0700 Subject: [PATCH 14/42] Describe custom vertex properties in Shape docs --- src/shape/custom_shapes.js | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index db337cb614..4e52804608 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -169,15 +169,33 @@ function customShapes(p5, fn) { * - `QUAD_STRIP` * * A `Shape` of any kind consists of `contours`, which can be thought of as - * subshapes (shapes inside another shape). + * subshapes (shapes inside another shape). Each `contour` is built from + * basic shapes called primitives, and each primitive consists of one or more vertices. * - * To construct a `Shape` called `myShape`, methods such as `myShape.beginShape()`, - * `myShape.vertex()`, and `myShape.endShape()` may be called. + * For example, a square can be made from a single path contour with four line segment + * primitives. Each line segment contains a vertex that indicates its endpoint. A square + * with a circular hole in it contains the circle in a separate contour. + * + * By default, each vertex only has a position, but a shape's vertices may have other + * properties such as texture coordinates, a normal vector, a fill color, and a stroke color. + * The properties every vertex should have may be customized by passing `vertexProperties` to + * `createShape()`. + * + * Once a shape is created and given a name like `myShape`, it can be built up with + * methods such as `myShape.beginShape()`, `myShape.vertex()`, and `myShape.endShape()`. + * + * Vertex functions such as `vertex()` or `bezierVertex()` are used to set the `position` + * property of vertices, as well as the `textureCoordinates` property if applicable. Those + * properties only apply to a single vertex. + * + * If `vertexProperties` includes other properties, they are each set by a method of the + * same name. For example, if vertices in `myShape` have a `fill`, then that is set with + * `myShape.fill()`. In the same way that a fill() may be applied + * to one or more shapes, `myShape.fill()` may be applied to one or more vertices. * * @class p5.Shape * @constructor - * @param {(PATH|POINTS|LINES|TRIANGLES|QUADS|TRIANGLE_FAN|TRIANGLE_STRIP|QUAD_STRIP)} [kind=PATH] either - * PATH, POINTS, LINES, TRIANGLES, QUADS, TRIANGLE_FAN, TRIANGLE_STRIP, or QUAD_STRIP. + * @param {Object} [vertexProperties={position: createVector(0, 0)}] vertex properties and their initial values. */ p5.Shape = Shape; From d8625910926e929305e81a1587c2564771bba257 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 22 Nov 2024 02:07:03 -0700 Subject: [PATCH 15/42] Revise p5.Contour docs based on new design --- src/shape/custom_shapes.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 4e52804608..9e03b3e9f5 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -207,7 +207,7 @@ function customShapes(p5, fn) { * Contours may be thought of as shapes inside of other shapes. * For example, a contour may be used to create a hole in a shape that is created * with beginShape()/endShape(). - * Multiple contours may be used to create multiple holes. + * Multiple contours may be included inside a single shape. * * Contours can have any `kind` that a shape can have: * @@ -229,8 +229,6 @@ function customShapes(p5, fn) { * * @class p5.Contour * @constructor - * @param {(PATH|POINTS|LINES|TRIANGLES|QUADS|TRIANGLE_FAN|TRIANGLE_STRIP|QUAD_STRIP)} [kind=PATH] either - * PATH, POINTS, LINES, TRIANGLES, QUADS, TRIANGLE_FAN, TRIANGLE_STRIP, or QUAD_STRIP. */ p5.Contour = Contour; From 8ea67c58d45f582ebccd8105221bb8ee5473452d Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 22 Nov 2024 02:10:32 -0700 Subject: [PATCH 16/42] Remove @constructor tags (YUIDoc syntax) --- src/shape/custom_shapes.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 9e03b3e9f5..68de0564bd 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -194,7 +194,6 @@ function customShapes(p5, fn) { * to one or more shapes, `myShape.fill()` may be applied to one or more vertices. * * @class p5.Shape - * @constructor * @param {Object} [vertexProperties={position: createVector(0, 0)}] vertex properties and their initial values. */ @@ -228,7 +227,6 @@ function customShapes(p5, fn) { * it's made from six line-segment primitives. * * @class p5.Contour - * @constructor */ p5.Contour = Contour; @@ -259,7 +257,6 @@ function customShapes(p5, fn) { * along the segment. * * @class p5.ShapePrimitive - * @constructor * @param {p5.Vertex} vertex the first vertex to include in the primitive. */ @@ -297,7 +294,6 @@ function customShapes(p5, fn) { * a full vertex object to a one-dimensional array. * * @class p5.Vertex - * @constructor * @param {Object} [properties] vertex properties. */ From 1443b53420972fd0e73cb313e3a1f1bb2fd75b3f Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 22 Nov 2024 02:20:55 -0700 Subject: [PATCH 17/42] Fix typo in Shape docs --- src/shape/custom_shapes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 68de0564bd..bf4de3d5de 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -172,7 +172,7 @@ function customShapes(p5, fn) { * subshapes (shapes inside another shape). Each `contour` is built from * basic shapes called primitives, and each primitive consists of one or more vertices. * - * For example, a square can be made from a single path contour with four line segment + * For example, a square can be made from a single path contour with four line-segment * primitives. Each line segment contains a vertex that indicates its endpoint. A square * with a circular hole in it contains the circle in a separate contour. * From 78940ff72d27879c69c6b8bf4b3cb1505a350526 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 22 Nov 2024 02:45:39 -0700 Subject: [PATCH 18/42] Update p5.ShapePrimitive docs with new design --- src/shape/custom_shapes.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index bf4de3d5de..3cd5bb7878 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -247,9 +247,12 @@ function customShapes(p5, fn) { * into a single shape. * * In a similar way, every shape primitive is built from one or more vertices. - * For example, a point consists of a single vertex, while a triangle consists of three vertices. + * For example, a point consists of a single vertex, while a triangle consists of three vertices. + * Each type of shape primitive has a `vertexCapacity`, which may be `Infinity` (for example, a + * spline may consist of any number of vertices). A primitive's `vertexCount` is the number of + * vertices it currently contains. * - * Each primitive can add itself to a shape, with an `addToShape()` method. + * Each primitive can add itself to a shape with an `addToShape()` method. * * It can also accept visitor objects with an `accept()` method. When a primitive accepts a visitor, * it gives the visitor access to its vertex data. For example, one visitor to a segment might turn @@ -257,7 +260,7 @@ function customShapes(p5, fn) { * along the segment. * * @class p5.ShapePrimitive - * @param {p5.Vertex} vertex the first vertex to include in the primitive. + * @param {...p5.Vertex} vertices the vertices to include in the primitive. */ p5.ShapePrimitive = ShapePrimitive; From 4dac39eaff08a9de31df130f8e37be39766c4141 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 22 Nov 2024 03:23:19 -0700 Subject: [PATCH 19/42] Update p5.Vertex docs to reflect new design --- src/shape/custom_shapes.js | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 3cd5bb7878..fd5ee92f76 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -273,10 +273,12 @@ function customShapes(p5, fn) { * shapes made with vertex(), arcVertex(), * bezierVertex(), and splineVertex(). * - * Like a point on an object in the real world, a vertex has different properties. These include coordinate - * properties `position`, `textureCoordinates`, and `normal`, as well as color properties `fill` and `stroke`. + * Like a point on an object in the real world, a vertex may have different properties. + * These may include coordinate properties such as `position`, `textureCoordinates`, and `normal`, + * color properties such as `fill` and `stroke`, and more. * - * A vertex called `myVertex` with position coordinates `(2, 3, 5)` and a green stroke may be created like this: + * A vertex called `myVertex` with position coordinates `(2, 3, 5)` and a green stroke may be created + * like this: * * ```js * let myVertex = new p5.Vertex({ @@ -285,16 +287,14 @@ function customShapes(p5, fn) { * }); * ``` * - * Note: - * - Coordinate properties are `p5.Vector` objects. - * - Color properties are `p5.Color` objects. + * Any property names may be used. Property values may be any + * JavaScript primitive, any + * object literal, + * or any object with an `array` property. * - * Properties may be specified in all the ways supported by `createVector()` - * and `color()`. For example, a vertex position can be set with two coordinates, - * as in `createVector(2, 3)`. - * - * Vertices also have a `toArray()` method that converts - * a full vertex object to a one-dimensional array. + * For example, if a position is stored as a `p5.Vector` object and a stroke is stored as a `p5.Color` object, + * then the `array` properties of those objects will be used by the vertex's own `array` property, which provides + * all the vertex data in a single array. * * @class p5.Vertex * @param {Object} [properties] vertex properties. From 567ff259f18607e5d1adbeaf1a857d768b20890e Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Fri, 22 Nov 2024 03:27:47 -0700 Subject: [PATCH 20/42] Make p5.Vertex and p5.Shape constructors consistent --- src/shape/custom_shapes.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index fd5ee92f76..18348c8114 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -297,7 +297,7 @@ function customShapes(p5, fn) { * all the vertex data in a single array. * * @class p5.Vertex - * @param {Object} [properties] vertex properties. + * @param {Object} [properties={position: createVector(0, 0)}] vertex properties. */ p5.Vertex = Vertex; From 727ff60866ec2cfde73304dcc080790b72d2cbf2 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Mon, 18 Nov 2024 16:44:59 -0500 Subject: [PATCH 21/42] Move fill and stroke into the base renderer --- src/color/setting.js | 4 +- src/core/p5.Renderer.js | 23 ++++++--- src/core/p5.Renderer2D.js | 74 ++++++++++++++-------------- src/shape/2d_primitives.js | 14 +++--- src/shape/curves.js | 4 +- src/shape/vertex.js | 2 +- src/typography/loading_displaying.js | 2 +- src/typography/p5.Font.js | 4 +- src/webgl/3d_primitives.js | 16 +++--- src/webgl/GeometryBuilder.js | 6 +-- src/webgl/ShapeBuilder.js | 4 +- src/webgl/material.js | 8 +-- src/webgl/p5.Framebuffer.js | 2 +- src/webgl/p5.RendererGL.js | 26 +++++----- src/webgl/text.js | 8 +-- test/unit/webgl/p5.RendererGL.js | 10 ++-- 16 files changed, 108 insertions(+), 99 deletions(-) diff --git a/src/color/setting.js b/src/color/setting.js index 3c7a68c003..31f08db1f8 100644 --- a/src/color/setting.js +++ b/src/color/setting.js @@ -1269,7 +1269,7 @@ function setting(p5, fn){ * */ fn.noFill = function() { - this._renderer.states.doFill = false; + this._renderer.noFill(); return this; }; @@ -1325,7 +1325,7 @@ function setting(p5, fn){ * */ fn.noStroke = function() { - this._renderer.states.doStroke = false; + this._renderer.states.strokeColor = false; return this; }; diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 09eebec1bd..d4d13bb75f 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -4,6 +4,7 @@ * @for p5 */ +import { Color } from '../color/p5.Color'; import * as constants from '../core/constants'; import { Image } from '../image/p5.Image'; @@ -25,9 +26,9 @@ class Renderer { // Renderer state machine this.states = { - doStroke: true, + strokeColor: new Color([0, 0, 0]), strokeSet: false, - doFill: true, + fillColor: new Color([255, 255, 255]), fillSet: false, tint: null, imageMode: constants.CORNER, @@ -153,14 +154,22 @@ class Renderer { } - fill() { + fill(...args) { this.states.fillSet = true; - this.states.doFill = true; + this.states.fillColor = this._pInst.color(...args); } - stroke() { + noFill() { + this.states.fillColor = null; + } + + stroke(...args) { this.states.strokeSet = true; - this.states.doStroke = true; + this.states.strokeColor = this._pInst.color(...args); + } + + noStroke() { + this.states.strokeColor = null; } textSize(s) { @@ -254,7 +263,7 @@ class Renderer { // fix for #5785 (top of bounding box) let finalMinHeight = y; - if (!(this.states.doFill || this.states.doStroke)) { + if (!(this.states.fillColor || this.states.strokeColor)) { return; } diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 75cf1b953d..2cd1e27eb5 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -201,7 +201,7 @@ class Renderer2D extends Renderer { fill(...args) { super.fill(...args); - const color = this._pInst.color(...args); + const color = this.states.fillColor; this._setFill(color.toString()); //accessible Outputs @@ -212,7 +212,7 @@ class Renderer2D extends Renderer { stroke(...args) { super.stroke(...args); - const color = this._pInst.color(...args); + const color = this.states.strokeColor; this._setStroke(color.toString()); //accessible Outputs @@ -672,7 +672,7 @@ class Renderer2D extends Renderer { } // Fill curves - if (this.states.doFill) { + if (this.states.fillColor) { if (!this._clipping) ctx.beginPath(); curves.forEach((curve, index) => { if (index === 0) { @@ -692,7 +692,7 @@ class Renderer2D extends Renderer { } // Stroke curves - if (this.states.doStroke) { + if (this.states.strokeColor) { if (!this._clipping) ctx.beginPath(); curves.forEach((curve, index) => { if (index === 0) { @@ -717,8 +717,8 @@ class Renderer2D extends Renderer { ellipse(args) { const ctx = this.drawingContext; - const doFill = this.states.doFill, - doStroke = this.states.doStroke; + const doFill = !!this.states.fillColor, + doStroke = this.states.strokeColor; const x = parseFloat(args[0]), y = parseFloat(args[1]), w = parseFloat(args[2]), @@ -750,7 +750,7 @@ class Renderer2D extends Renderer { line(x1, y1, x2, y2) { const ctx = this.drawingContext; - if (!this.states.doStroke) { + if (!this.states.strokeColor) { return this; } else if (this._getStroke() === styleEmpty) { return this; @@ -764,7 +764,7 @@ class Renderer2D extends Renderer { point(x, y) { const ctx = this.drawingContext; - if (!this.states.doStroke) { + if (!this.states.strokeColor) { return this; } else if (this._getStroke() === styleEmpty) { return this; @@ -785,8 +785,8 @@ class Renderer2D extends Renderer { quad(x1, y1, x2, y2, x3, y3, x4, y4) { const ctx = this.drawingContext; - const doFill = this.states.doFill, - doStroke = this.states.doStroke; + const doFill = !!this.states.fillColor, + doStroke = this.states.strokeColor; if (doFill && !doStroke) { if (this._getFill() === styleEmpty) { return this; @@ -821,8 +821,8 @@ class Renderer2D extends Renderer { let br = args[6]; let bl = args[7]; const ctx = this.drawingContext; - const doFill = this.states.doFill, - doStroke = this.states.doStroke; + const doFill = !!this.states.fillColor, + doStroke = this.states.strokeColor; if (doFill && !doStroke) { if (this._getFill() === styleEmpty) { return this; @@ -891,10 +891,10 @@ class Renderer2D extends Renderer { ctx.arcTo(x, y, x + w, y, tl); ctx.closePath(); } - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { ctx.fill(); } - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { ctx.stroke(); } return this; @@ -903,8 +903,8 @@ class Renderer2D extends Renderer { triangle(args) { const ctx = this.drawingContext; - const doFill = this.states.doFill, - doStroke = this.states.doStroke; + const doFill = !!this.states.fillColor, + doStroke = this.states.strokeColor; const x1 = args[0], y1 = args[1]; const x2 = args[2], @@ -945,7 +945,7 @@ class Renderer2D extends Renderer { if (vertices.length === 0) { return this; } - if (!this.states.doStroke && !this.states.doFill) { + if (!this.states.strokeColor && !this.states.fillColor) { return this; } const closeShape = mode === constants.CLOSE; @@ -1039,7 +1039,7 @@ class Renderer2D extends Renderer { if (shapeKind === constants.POINTS) { for (i = 0; i < numVerts; i++) { v = vertices[i]; - if (this.states.doStroke) { + if (this.states.strokeColor) { this._pInst.stroke(v[6]); } this._pInst.point(v[0], v[1]); @@ -1047,7 +1047,7 @@ class Renderer2D extends Renderer { } else if (shapeKind === constants.LINES) { for (i = 0; i + 1 < numVerts; i += 2) { v = vertices[i]; - if (this.states.doStroke) { + if (this.states.strokeColor) { this._pInst.stroke(vertices[i + 1][6]); } this._pInst.line(v[0], v[1], vertices[i + 1][0], vertices[i + 1][1]); @@ -1060,11 +1060,11 @@ class Renderer2D extends Renderer { this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]); this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); this.drawingContext.closePath(); - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { this._pInst.fill(vertices[i + 2][5]); this.drawingContext.fill(); } - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { this._pInst.stroke(vertices[i + 2][6]); this.drawingContext.stroke(); } @@ -1075,18 +1075,18 @@ class Renderer2D extends Renderer { if (!this._clipping) this.drawingContext.beginPath(); this.drawingContext.moveTo(vertices[i + 1][0], vertices[i + 1][1]); this.drawingContext.lineTo(v[0], v[1]); - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { this._pInst.stroke(vertices[i + 1][6]); } - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { this._pInst.fill(vertices[i + 1][5]); } if (i + 2 < numVerts) { this.drawingContext.lineTo(vertices[i + 2][0], vertices[i + 2][1]); - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { this._pInst.stroke(vertices[i + 2][6]); } - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { this._pInst.fill(vertices[i + 2][5]); } } @@ -1106,15 +1106,15 @@ class Renderer2D extends Renderer { // If the next colour is going to be different, stroke / fill now if (i < numVerts - 1) { if ( - (this.states.doFill && v[5] !== vertices[i + 1][5]) || - (this.states.doStroke && v[6] !== vertices[i + 1][6]) + (this.states.fillColor && v[5] !== vertices[i + 1][5]) || + (this.states.strokeColor && v[6] !== vertices[i + 1][6]) ) { - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { this._pInst.fill(v[5]); this.drawingContext.fill(); this._pInst.fill(vertices[i + 1][5]); } - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { this._pInst.stroke(v[6]); this.drawingContext.stroke(); this._pInst.stroke(vertices[i + 1][6]); @@ -1135,10 +1135,10 @@ class Renderer2D extends Renderer { this.drawingContext.lineTo(vertices[i + j][0], vertices[i + j][1]); } this.drawingContext.lineTo(v[0], v[1]); - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { this._pInst.fill(vertices[i + 3][5]); } - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { this._pInst.stroke(vertices[i + 3][6]); } this._doFillStrokeClose(closeShape); @@ -1156,10 +1156,10 @@ class Renderer2D extends Renderer { vertices[i + 1][0], vertices[i + 1][1]); this.drawingContext.lineTo( vertices[i + 3][0], vertices[i + 3][1]); - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { this._pInst.fill(vertices[i + 3][5]); } - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { this._pInst.stroke(vertices[i + 3][6]); } } else { @@ -1290,10 +1290,10 @@ class Renderer2D extends Renderer { if (closeShape) { this.drawingContext.closePath(); } - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { this.drawingContext.fill(); } - if (!this._clipping && this.states.doStroke) { + if (!this._clipping && this.states.strokeColor) { this.drawingContext.stroke(); } } @@ -1352,11 +1352,11 @@ class Renderer2D extends Renderer { // a system/browser font // no stroke unless specified by user - if (this.states.doStroke && this.states.strokeSet) { + if (this.states.strokeColor && this.states.strokeSet) { this.drawingContext.strokeText(line, x, y); } - if (!this._clipping && this.states.doFill) { + if (!this._clipping && this.states.fillColor) { // if fill hasn't been set by user, use default text fill if (!this.states.fillSet) { this._setFill(constants._DEFAULT_TEXT_FILL); diff --git a/src/shape/2d_primitives.js b/src/shape/2d_primitives.js index 6e4f551322..3f1e0a3506 100644 --- a/src/shape/2d_primitives.js +++ b/src/shape/2d_primitives.js @@ -313,7 +313,7 @@ function primitives(p5, fn){ // if the current stroke and fill settings wouldn't result in something // visible, exit immediately - if (!this._renderer.states.doStroke && !this._renderer.states.doFill) { + if (!this._renderer.states.strokeColor && !this._renderer.states.fillColor) { return this; } @@ -540,7 +540,7 @@ function primitives(p5, fn){ fn._renderEllipse = function(x, y, w, h, detailX) { // if the current stroke and fill settings wouldn't result in something // visible, exit immediately - if (!this._renderer.states.doStroke && !this._renderer.states.doFill) { + if (!this._renderer.states.strokeColor && !this._renderer.states.fillColor) { return this; } @@ -712,7 +712,7 @@ function primitives(p5, fn){ fn.line = function(...args) { p5._validateParameters('line', args); - if (this._renderer.states.doStroke) { + if (this._renderer.states.strokeColor) { this._renderer.line(...args); } @@ -896,7 +896,7 @@ function primitives(p5, fn){ fn.point = function(...args) { p5._validateParameters('point', args); - if (this._renderer.states.doStroke) { + if (this._renderer.states.strokeColor) { if (args.length === 1 && args[0] instanceof p5.Vector) { this._renderer.point.call( this._renderer, @@ -1057,7 +1057,7 @@ function primitives(p5, fn){ fn.quad = function(...args) { p5._validateParameters('quad', args); - if (this._renderer.states.doStroke || this._renderer.states.doFill) { + if (this._renderer.states.strokeColor || this._renderer.states.fillColor) { if (this._renderer.isP3D && args.length < 12) { // if 3D and we weren't passed 12 args, assume Z is 0 this._renderer.quad.call( @@ -1334,7 +1334,7 @@ function primitives(p5, fn){ // internal method to have renderer draw a rectangle fn._renderRect = function() { - if (this._renderer.states.doStroke || this._renderer.states.doFill) { + if (this._renderer.states.strokeColor || this._renderer.states.fillColor) { // duplicate width for height in case only 3 arguments is provided if (arguments.length === 3) { arguments[3] = arguments[2]; @@ -1433,7 +1433,7 @@ function primitives(p5, fn){ fn.triangle = function(...args) { p5._validateParameters('triangle', args); - if (this._renderer.states.doStroke || this._renderer.states.doFill) { + if (this._renderer.states.strokeColor || this._renderer.states.fillColor) { this._renderer.triangle(args); } diff --git a/src/shape/curves.js b/src/shape/curves.js index 949f60e8ec..54874bafd9 100644 --- a/src/shape/curves.js +++ b/src/shape/curves.js @@ -205,7 +205,7 @@ function curves(p5, fn){ // if the current stroke and fill settings wouldn't result in something // visible, exit immediately - if (!this._renderer.states.doStroke && !this._renderer.states.doFill) { + if (!this._renderer.states.strokeColor && !this._renderer.states.fillColor) { return this; } @@ -758,7 +758,7 @@ function curves(p5, fn){ fn.curve = function(...args) { p5._validateParameters('curve', args); - if (this._renderer.states.doStroke) { + if (this._renderer.states.strokeColor) { this._renderer.curve(...args); } diff --git a/src/shape/vertex.js b/src/shape/vertex.js index 73afcc1653..bc22d7d886 100644 --- a/src/shape/vertex.js +++ b/src/shape/vertex.js @@ -1529,7 +1529,7 @@ function vertex(p5, fn){ if (vertices.length === 0) { return this; } - if (!this._renderer.states.doStroke && !this._renderer.states.doFill) { + if (!this._renderer.states.strokeColor && !this._renderer.states.fillColor) { return this; } diff --git a/src/typography/loading_displaying.js b/src/typography/loading_displaying.js index 47e7fc5812..b7b22ea43f 100644 --- a/src/typography/loading_displaying.js +++ b/src/typography/loading_displaying.js @@ -325,7 +325,7 @@ p5.prototype.loadFont = async function(path, onSuccess, onError) { */ p5.prototype.text = function(str, x, y, maxWidth, maxHeight) { p5._validateParameters('text', arguments); - return !(this._renderer.states.doFill || this._renderer.states.doStroke) + return !(this._renderer.states.fillColor || this._renderer.states.strokeColor) ? this : this._renderer.text(...arguments); }; diff --git a/src/typography/p5.Font.js b/src/typography/p5.Font.js index a91712e2ed..43b98796ba 100644 --- a/src/typography/p5.Font.js +++ b/src/typography/p5.Font.js @@ -538,11 +538,11 @@ p5.Font = class Font { } // only draw stroke if manually set by user - if (pg.states.doStroke && pg.states.strokeSet && !pg._clipping) { + if (pg.states.strokeColor && pg.states.strokeSet && !pg._clipping) { ctx.stroke(); } - if (pg.states.doFill && !pg._clipping) { + if (pg.states.fillColor && !pg._clipping) { // if fill hasn't been set by user, use default-text-fill if (!pg.states.fillSet) { pg._setFill(constants._DEFAULT_TEXT_FILL); diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 869b7ab240..c9106c2e69 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -2250,7 +2250,7 @@ function primitives3D(p5, fn){ if (detail <= 50) { arcGeom._edgesToVertices(arcGeom); - } else if (this.states.doStroke) { + } else if (this.states.strokeColor) { console.log( `Cannot apply a stroke to an ${shape} with more than 50 detail` ); @@ -3179,7 +3179,7 @@ function primitives3D(p5, fn){ this.push(); this.noLights(); - this.states.doStroke = false;; + this.states.strokeColor = false;; this.texture(img); this.states.textureMode = constants.NORMAL; @@ -3366,7 +3366,7 @@ function primitives3D(p5, fn){ planeGeom.computeFaces().computeNormals(); if (detailX <= 1 && detailY <= 1) { planeGeom._makeTriangleEdges()._edgesToVertices(); - } else if (this.states.doStroke) { + } else if (this.states.strokeColor) { console.log( 'Cannot draw stroke on plane objects with more' + ' than 1 detailX or 1 detailY' @@ -3446,7 +3446,7 @@ function primitives3D(p5, fn){ boxGeom.computeNormals(); if (detailX <= 4 && detailY <= 4) { boxGeom._edgesToVertices(); - } else if (this.states.doStroke) { + } else if (this.states.strokeColor) { console.log( 'Cannot draw stroke on box objects with more' + ' than 4 detailX or 4 detailY' @@ -3502,7 +3502,7 @@ function primitives3D(p5, fn){ ellipsoidGeom.computeFaces(); if (detailX <= 24 && detailY <= 24) { ellipsoidGeom._makeTriangleEdges()._edgesToVertices(); - } else if (this.states.doStroke) { + } else if (this.states.strokeColor) { console.log( 'Cannot draw stroke on ellipsoids with more' + ' than 24 detailX or 24 detailY' @@ -3539,7 +3539,7 @@ function primitives3D(p5, fn){ // normals are computed in call to _truncatedCone if (detailX <= 24 && detailY <= 16) { cylinderGeom._makeTriangleEdges()._edgesToVertices(); - } else if (this.states.doStroke) { + } else if (this.states.strokeColor) { console.log( 'Cannot draw stroke on cylinder objects with more' + ' than 24 detailX or 16 detailY' @@ -3565,7 +3565,7 @@ function primitives3D(p5, fn){ _truncatedCone.call(coneGeom, 1, 0, 1, detailX, detailY, cap, false); if (detailX <= 24 && detailY <= 16) { coneGeom._makeTriangleEdges()._edgesToVertices(); - } else if (this.states.doStroke) { + } else if (this.states.strokeColor) { console.log( 'Cannot draw stroke on cone objects with more' + ' than 24 detailX or 16 detailY' @@ -3628,7 +3628,7 @@ function primitives3D(p5, fn){ torusGeom.computeFaces(); if (detailX <= 24 && detailY <= 16) { torusGeom._makeTriangleEdges()._edgesToVertices(); - } else if (this.states.doStroke) { + } else if (this.states.strokeColor) { console.log( 'Cannot draw strokes on torus object with more' + ' than 24 detailX or 16 detailY' diff --git a/src/webgl/GeometryBuilder.js b/src/webgl/GeometryBuilder.js index a5691db31e..8bb3c272c5 100644 --- a/src/webgl/GeometryBuilder.js +++ b/src/webgl/GeometryBuilder.js @@ -87,12 +87,12 @@ class GeometryBuilder { this.geometry.vertexProperty(propName, data, size); } - if (this.renderer.states.doFill) { + if (this.renderer.states.fillColor) { this.geometry.faces.push( ...input.faces.map(f => f.map(idx => idx + startIdx)) ); } - if (this.renderer.states.doStroke) { + if (this.renderer.states.strokeColor) { this.geometry.edges.push( ...input.edges.map(edge => edge.map(idx => idx + startIdx)) ); @@ -111,7 +111,7 @@ class GeometryBuilder { addImmediate(geometry, shapeMode) { const faces = []; - if (this.renderer.states.doFill) { + if (this.renderer.states.fillColor) { if ( shapeMode === constants.TRIANGLE_STRIP || shapeMode === constants.QUAD_STRIP diff --git a/src/webgl/ShapeBuilder.js b/src/webgl/ShapeBuilder.js index 258f8c07e8..02e5318ff1 100644 --- a/src/webgl/ShapeBuilder.js +++ b/src/webgl/ShapeBuilder.js @@ -260,7 +260,7 @@ export class ShapeBuilder { _processVertices(mode) { if (this.geometry.vertices.length === 0) return; - const calculateStroke = this.renderer.states.doStroke; + const calculateStroke = this.renderer.states.strokeColor; const shouldClose = mode === constants.CLOSE; if (calculateStroke) { this.geometry.edges = this._calculateEdges( @@ -280,7 +280,7 @@ export class ShapeBuilder { const hasContour = this.contourIndices.length > 0; // We tesselate when drawing curves or convex shapes const shouldTess = - this.renderer.states.doFill && + this.renderer.states.fillColor && ( this.isBezier || this.isQuadratic || diff --git a/src/webgl/material.js b/src/webgl/material.js index 288d83f822..a535d5392c 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -2928,7 +2928,7 @@ function material(p5, fn){ this._renderer.states.curAmbientColor = color._array; this._renderer.states._useNormalMaterial = false; this._renderer.states.enableLighting = true; - this._renderer.states.doFill = true; + this._renderer.states.fillColor = true; return this; }; @@ -3625,7 +3625,7 @@ function material(p5, fn){ this.states.drawMode = constants.TEXTURE; this.states._useNormalMaterial = false; this.states._tex = tex; - this.states.doFill = true; + this.states.fillColor = true; }; RendererGL.prototype.normalMaterial = function(...args) { @@ -3634,8 +3634,8 @@ function material(p5, fn){ this.states._useEmissiveMaterial = false; this.states._useNormalMaterial = true; this.states.curFillColor = [1, 1, 1, 1]; - this.states.doFill = true; - this.states.doStroke = false; + this.states.fillColor = true; + this.states.strokeColor = false; } // RendererGL.prototype.ambientMaterial = function(v1, v2, v3) { diff --git a/src/webgl/p5.Framebuffer.js b/src/webgl/p5.Framebuffer.js index 9d78813469..45da02c255 100644 --- a/src/webgl/p5.Framebuffer.js +++ b/src/webgl/p5.Framebuffer.js @@ -1559,7 +1559,7 @@ class Framebuffer { this.renderer.states.imageMode = constants.CORNER; this.renderer.setCamera(this.filterCamera); this.renderer.resetMatrix(); - this.renderer.states.doStroke = false; + this.renderer.states.strokeColor = false; this.renderer.clear(); this.renderer._drawingFilter = true; this.renderer.image( diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 93f9f14f53..cd195dcda1 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -468,7 +468,7 @@ class RendererGL extends Renderer { this.shapeBuilder.geometry, this.shapeBuilder.shapeMode ); - } else if (this.states.doFill || this.states.doStroke) { + } else if (this.states.fillColor || this.states.strokeColor) { this._drawGeometry( this.shapeBuilder.geometry, { mode: this.shapeBuilder.shapeMode, count } @@ -509,14 +509,14 @@ class RendererGL extends Renderer { } if ( - this.states.doFill && + this.states.fillColor && geometry.vertices.length >= 3 && ![constants.LINES, constants.POINTS].includes(mode) ) { this._drawFills(geometry, { mode, count }); } - if (this.states.doStroke && geometry.lineVertices.length >= 1) { + if (this.states.strokeColor && geometry.lineVertices.length >= 1) { this._drawStrokes(geometry, { count }); } @@ -967,7 +967,7 @@ class RendererGL extends Renderer { super.fill(...args); //see material.js for more info on color blending in webgl // const color = fn.color.apply(this._pInst, arguments); - const color = this._pInst.color(...args); + const color = this.states.fillColor; this.states.curFillColor = color._array; this.states.drawMode = constants.FILL; this.states._useNormalMaterial = false; @@ -1006,8 +1006,7 @@ class RendererGL extends Renderer { stroke(...args) { super.stroke(...args); // const color = fn.color.apply(this._pInst, arguments); - const color = this._pInst.color(...args); - this.states.curStrokeColor = color._array; + this.states.curStrokeColor = this.states.strokeColor._array; } strokeCap(cap) { @@ -1097,7 +1096,7 @@ class RendererGL extends Renderer { this.matchSize(tmp, target); // setup this.push(); - this.states.doStroke = false; + this.states.strokeColor = false; this.blendMode(constants.BLEND); // draw main to temp buffer @@ -1131,7 +1130,7 @@ class RendererGL extends Renderer { // every other non-blur shader uses single pass else { fbo.draw(() => { - this.states.doStroke = false; + this.states.strokeColor = false; this.blendMode(constants.BLEND); this.shader(this.states.filterShader); this.states.filterShader.setUniform('tex0', target); @@ -1147,7 +1146,7 @@ class RendererGL extends Renderer { } // draw fbo contents onto main renderer. this.push(); - this.states.doStroke = false; + this.states.strokeColor = false; this.clear(); this.push(); this.states.imageMode = constants.CORNER; @@ -1260,8 +1259,8 @@ class RendererGL extends Renderer { this.push(); this.resetShader(); - if (this.states.doFill) this.fill(0, 0); - if (this.states.doStroke) this.stroke(0, 0); + if (this.states.fillColor) this.fill(0, 0); + if (this.states.strokeColor) this.stroke(0, 0); } endClip() { @@ -2038,7 +2037,8 @@ class RendererGL extends Renderer { newFramebuffer.draw(() => { this.shader(this.states.diffusedShader); this.states.diffusedShader.setUniform('environmentMap', input); - this.states.doStroke = false; + this.states.strokeColor = false; + this.rectMode(constants.CENTER); this.noLights(); this.plane(width, height); }); @@ -2089,7 +2089,7 @@ class RendererGL extends Renderer { this.clear(); this.states.specularShader.setUniform('environmentMap', input); this.states.specularShader.setUniform('roughness', roughness); - this.states.doStroke = false; + this.states.strokeColor = false; this.noLights(); this.plane(w, w); }); diff --git a/src/webgl/text.js b/src/webgl/text.js index b8355f0c9a..d3d69e8b23 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -642,7 +642,7 @@ function text(p5, fn){ ); return; } - if (y >= maxY || !this.states.doFill) { + if (y >= maxY || !this.states.fillColor) { return; // don't render lines beyond our maxY position } @@ -656,10 +656,10 @@ function text(p5, fn){ p.push(); // fix to #803 // remember this state, so it can be restored later - const doStroke = this.states.doStroke; + const doStroke = this.states.strokeColor; const drawMode = this.states.drawMode; - this.states.doStroke = false; + this.states.strokeColor = false; this.states.drawMode = constants.TEXTURE; // get the cached FontInfo object @@ -752,7 +752,7 @@ function text(p5, fn){ // clean up sh.unbindShader(); - this.states.doStroke = doStroke; + this.states.strokeColor = doStroke; this.states.drawMode = drawMode; gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, true); diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 931aa0ef24..0eb5891170 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -141,21 +141,21 @@ suite('p5.RendererGL', function() { test('check activate and deactivating fill and stroke', function() { myp5.noStroke(); assert( - !myp5._renderer.states.doStroke, + !myp5._renderer.states.strokeColor, 'stroke shader still active after noStroke()' ); - assert.isTrue( - myp5._renderer.states.doFill, + assert( + !myp5._renderer.states.doFill, 'fill shader deactivated by noStroke()' ); myp5.stroke(0); myp5.noFill(); assert( - myp5._renderer.states.doStroke, + !!myp5._renderer.states.strokeColor, 'stroke shader not active after stroke()' ); assert.isTrue( - !myp5._renderer.states.doFill, + !myp5._renderer.states.fillColor, 'fill shader still active after noFill()' ); }); From c7561727c4a5f6b6d81d3ed0fcf032180953f1e0 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Mon, 18 Nov 2024 16:55:18 -0500 Subject: [PATCH 22/42] Add vertex state getter --- src/core/p5.Renderer.js | 7 +++++++ src/webgl/p5.RendererGL.js | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index d4d13bb75f..08f825a4f0 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -172,6 +172,13 @@ class Renderer { this.states.strokeColor = null; } + vertexProperties() { + return { + stroke: this.states.strokeColor, + fill: this.states.fillColor, + } + } + textSize(s) { if (typeof s === 'number') { this.states.textSize = s; diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index cd195dcda1..efc815d184 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -1009,6 +1009,13 @@ class RendererGL extends Renderer { this.states.curStrokeColor = this.states.strokeColor._array; } + vertexProperties() { + return { + ...super.vertexProperties(), + normal: this.states._currentNormal, + } + } + strokeCap(cap) { this.curStrokeCap = cap; } From b4e43e513dc53e8e2acae7645f49ad54d26cefba Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Fri, 22 Nov 2024 10:33:31 -0500 Subject: [PATCH 23/42] Fix usage of false instead of null --- src/color/setting.js | 2 +- src/webgl/3d_primitives.js | 2 +- src/webgl/material.js | 2 +- src/webgl/p5.Framebuffer.js | 2 +- src/webgl/p5.RendererGL.js | 11 +++++------ src/webgl/text.js | 2 +- 6 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/color/setting.js b/src/color/setting.js index 31f08db1f8..8103aae975 100644 --- a/src/color/setting.js +++ b/src/color/setting.js @@ -1325,7 +1325,7 @@ function setting(p5, fn){ * */ fn.noStroke = function() { - this._renderer.states.strokeColor = false; + this._renderer.states.strokeColor = null; return this; }; diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index c9106c2e69..9b71bc569a 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -3179,7 +3179,7 @@ function primitives3D(p5, fn){ this.push(); this.noLights(); - this.states.strokeColor = false;; + this.states.strokeColor = null;; this.texture(img); this.states.textureMode = constants.NORMAL; diff --git a/src/webgl/material.js b/src/webgl/material.js index a535d5392c..fa70667de6 100644 --- a/src/webgl/material.js +++ b/src/webgl/material.js @@ -3635,7 +3635,7 @@ function material(p5, fn){ this.states._useNormalMaterial = true; this.states.curFillColor = [1, 1, 1, 1]; this.states.fillColor = true; - this.states.strokeColor = false; + this.states.strokeColor = null; } // RendererGL.prototype.ambientMaterial = function(v1, v2, v3) { diff --git a/src/webgl/p5.Framebuffer.js b/src/webgl/p5.Framebuffer.js index 45da02c255..2a5ced5f7a 100644 --- a/src/webgl/p5.Framebuffer.js +++ b/src/webgl/p5.Framebuffer.js @@ -1559,7 +1559,7 @@ class Framebuffer { this.renderer.states.imageMode = constants.CORNER; this.renderer.setCamera(this.filterCamera); this.renderer.resetMatrix(); - this.renderer.states.strokeColor = false; + this.renderer.states.strokeColor = null; this.renderer.clear(); this.renderer._drawingFilter = true; this.renderer.image( diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index efc815d184..7b8e3cb6cd 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -1103,7 +1103,7 @@ class RendererGL extends Renderer { this.matchSize(tmp, target); // setup this.push(); - this.states.strokeColor = false; + this.states.strokeColor = null; this.blendMode(constants.BLEND); // draw main to temp buffer @@ -1137,7 +1137,7 @@ class RendererGL extends Renderer { // every other non-blur shader uses single pass else { fbo.draw(() => { - this.states.strokeColor = false; + this.states.strokeColor = null; this.blendMode(constants.BLEND); this.shader(this.states.filterShader); this.states.filterShader.setUniform('tex0', target); @@ -1153,7 +1153,7 @@ class RendererGL extends Renderer { } // draw fbo contents onto main renderer. this.push(); - this.states.strokeColor = false; + this.states.strokeColor = null; this.clear(); this.push(); this.states.imageMode = constants.CORNER; @@ -2044,8 +2044,7 @@ class RendererGL extends Renderer { newFramebuffer.draw(() => { this.shader(this.states.diffusedShader); this.states.diffusedShader.setUniform('environmentMap', input); - this.states.strokeColor = false; - this.rectMode(constants.CENTER); + this.states.strokeColor = null; this.noLights(); this.plane(width, height); }); @@ -2096,7 +2095,7 @@ class RendererGL extends Renderer { this.clear(); this.states.specularShader.setUniform('environmentMap', input); this.states.specularShader.setUniform('roughness', roughness); - this.states.strokeColor = false; + this.states.strokeColor = null; this.noLights(); this.plane(w, w); }); diff --git a/src/webgl/text.js b/src/webgl/text.js index d3d69e8b23..1438b24cce 100644 --- a/src/webgl/text.js +++ b/src/webgl/text.js @@ -659,7 +659,7 @@ function text(p5, fn){ const doStroke = this.states.strokeColor; const drawMode = this.states.drawMode; - this.states.strokeColor = false; + this.states.strokeColor = null; this.states.drawMode = constants.TEXTURE; // get the cached FontInfo object From 64e319bd601de0d02424dc26d483f79b59d653e1 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Fri, 22 Nov 2024 11:16:25 -0500 Subject: [PATCH 24/42] Add properties to current shape --- src/core/p5.Renderer.js | 10 ++ src/shape/custom_shapes.js | 182 +++++++++++++++++++------------------ src/webgl/p5.RendererGL.js | 2 + 3 files changed, 106 insertions(+), 88 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 08f825a4f0..797b85bb22 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -7,6 +7,7 @@ import { Color } from '../color/p5.Color'; import * as constants from '../core/constants'; import { Image } from '../image/p5.Image'; +import { Shape } from '../shape/custom_shapes'; class Renderer { constructor(pInst, w, h, isMainCanvas) { @@ -50,6 +51,8 @@ class Renderer { this._clipping = false; this._clipInvert = false; this._curveTightness = 0; + + this.currentShape = new Shape(); } remove() { @@ -92,6 +95,7 @@ class Renderer { pop() { this._pushPopDepth--; Object.assign(this.states, this._pushPopStack.pop()); + this.updateShapeVertexProperties(); } beginClip(options = {}) { @@ -157,6 +161,7 @@ class Renderer { fill(...args) { this.states.fillSet = true; this.states.fillColor = this._pInst.color(...args); + this.updateShapeVertexProperties(); } noFill() { @@ -166,6 +171,7 @@ class Renderer { stroke(...args) { this.states.strokeSet = true; this.states.strokeColor = this._pInst.color(...args); + this.updateShapeVertexProperties(); } noStroke() { @@ -179,6 +185,10 @@ class Renderer { } } + updateShapeVertexProperties() { + this.currentShape.vertexProperties = this.vertexProperties(); + } + textSize(s) { if (typeof s === 'number') { this.states.textSize = s; diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 18348c8114..7bba519fc8 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -11,28 +11,34 @@ // ---- GENERAL CLASSES ---- -class Shape { +class Shape { + vertexProperties = {}; + constructor() { - + + } + + reset() { + // TODO: remove existing vertices } } class Contour { constructor() { - + } } // abstract class class ShapePrimitive { constructor() { - + } } class Vertex { constructor() { - + } // TODO: make sure name of array conversion method is @@ -44,26 +50,26 @@ class Vertex { class Anchor { constructor() { - + } } // abstract class class Segment extends ShapePrimitive { constructor() { - + } } class LineSegment extends Segment { constructor() { - + } } class BezierSegment extends Segment { constructor() { - + } } @@ -71,7 +77,7 @@ class BezierSegment extends Segment { // may want to use separate classes, but maybe not class SplineSegment extends Segment { constructor() { - + } } @@ -79,25 +85,25 @@ class SplineSegment extends Segment { class Point extends ShapePrimitive { constructor() { - + } } class Line extends ShapePrimitive { constructor() { - + } } class Triangle extends ShapePrimitive { constructor() { - + } } class Quad extends ShapePrimitive { constructor() { - + } } @@ -105,19 +111,19 @@ class Quad extends ShapePrimitive { class TriangleFan extends ShapePrimitive { constructor() { - + } } class TriangleStrip extends ShapePrimitive { constructor() { - + } } class QuadStrip extends ShapePrimitive { constructor() { - + } } @@ -126,26 +132,26 @@ class QuadStrip extends ShapePrimitive { // abstract class class PrimitiveVisitor { constructor() { - + } } // using this instead of PrimitiveToContext2DConverter for now class PrimitiveToPath2DConverter extends PrimitiveVisitor { constructor() { - + } } class PrimitiveToVerticesConverter extends PrimitiveVisitor { constructor() { - + } } class PointAtLengthGetter extends PrimitiveVisitor { constructor() { - + } } @@ -155,10 +161,10 @@ function customShapes(p5, fn) { /** * @private * A class to describe a custom shape made with `beginShape()`/`endShape()`. - * + * * Every `Shape` has a `kind`. The kind takes any value that * can be passed to beginShape(): - * + * * - `PATH` * - `POINTS` * - `LINES` @@ -167,32 +173,32 @@ function customShapes(p5, fn) { * - `TRIANGLE_FAN` * - `TRIANGLE_STRIP` * - `QUAD_STRIP` - * + * * A `Shape` of any kind consists of `contours`, which can be thought of as * subshapes (shapes inside another shape). Each `contour` is built from * basic shapes called primitives, and each primitive consists of one or more vertices. - * - * For example, a square can be made from a single path contour with four line-segment - * primitives. Each line segment contains a vertex that indicates its endpoint. A square + * + * For example, a square can be made from a single path contour with four line-segment + * primitives. Each line segment contains a vertex that indicates its endpoint. A square * with a circular hole in it contains the circle in a separate contour. - * - * By default, each vertex only has a position, but a shape's vertices may have other - * properties such as texture coordinates, a normal vector, a fill color, and a stroke color. - * The properties every vertex should have may be customized by passing `vertexProperties` to + * + * By default, each vertex only has a position, but a shape's vertices may have other + * properties such as texture coordinates, a normal vector, a fill color, and a stroke color. + * The properties every vertex should have may be customized by passing `vertexProperties` to * `createShape()`. - * + * * Once a shape is created and given a name like `myShape`, it can be built up with * methods such as `myShape.beginShape()`, `myShape.vertex()`, and `myShape.endShape()`. - * + * * Vertex functions such as `vertex()` or `bezierVertex()` are used to set the `position` * property of vertices, as well as the `textureCoordinates` property if applicable. Those * properties only apply to a single vertex. - * - * If `vertexProperties` includes other properties, they are each set by a method of the - * same name. For example, if vertices in `myShape` have a `fill`, then that is set with + * + * If `vertexProperties` includes other properties, they are each set by a method of the + * same name. For example, if vertices in `myShape` have a `fill`, then that is set with * `myShape.fill()`. In the same way that a fill() may be applied * to one or more shapes, `myShape.fill()` may be applied to one or more vertices. - * + * * @class p5.Shape * @param {Object} [vertexProperties={position: createVector(0, 0)}] vertex properties and their initial values. */ @@ -202,14 +208,14 @@ function customShapes(p5, fn) { /** * @private * A class to describe a contour made with `beginContour()`/`endContour()`. - * + * * Contours may be thought of as shapes inside of other shapes. * For example, a contour may be used to create a hole in a shape that is created - * with beginShape()/endShape(). + * with beginShape()/endShape(). * Multiple contours may be included inside a single shape. - * + * * Contours can have any `kind` that a shape can have: - * + * * - `PATH` * - `POINTS` * - `LINES` @@ -218,14 +224,14 @@ function customShapes(p5, fn) { * - `TRIANGLE_FAN` * - `TRIANGLE_STRIP` * - `QUAD_STRIP` - * - * By default, a contour has the same kind as the shape that contains it, but this + * + * By default, a contour has the same kind as the shape that contains it, but this * may be changed by passing a different `kind` to beginContour(). - * + * * A `Contour` of any kind consists of `primitives`, which are the most basic * shapes that can be drawn. For example, if a contour is a hexagon, then * it's made from six line-segment primitives. - * + * * @class p5.Contour */ @@ -233,32 +239,32 @@ function customShapes(p5, fn) { /** * @private - * A base class to describe a shape primitive (a basic shape drawn with + * A base class to describe a shape primitive (a basic shape drawn with * `beginShape()`/`endShape()`). - * - * Shape primitives are the most basic shapes that can be drawn with + * + * Shape primitives are the most basic shapes that can be drawn with * beginShape()/endShape(): - * - * - segment primitives: line segments, bezier segments, spline segments, and arc segments + * + * - segment primitives: line segments, bezier segments, spline segments, and arc segments * - isolated primitives: points, lines, triangles, and quads * - tessellation primitives: triangle fans, triangle strips, and quad strips - * + * * More complex shapes may be created by combining many primitives, possibly of different kinds, * into a single shape. - * - * In a similar way, every shape primitive is built from one or more vertices. + * + * In a similar way, every shape primitive is built from one or more vertices. * For example, a point consists of a single vertex, while a triangle consists of three vertices. - * Each type of shape primitive has a `vertexCapacity`, which may be `Infinity` (for example, a - * spline may consist of any number of vertices). A primitive's `vertexCount` is the number of + * Each type of shape primitive has a `vertexCapacity`, which may be `Infinity` (for example, a + * spline may consist of any number of vertices). A primitive's `vertexCount` is the number of * vertices it currently contains. - * + * * Each primitive can add itself to a shape with an `addToShape()` method. - * - * It can also accept visitor objects with an `accept()` method. When a primitive accepts a visitor, - * it gives the visitor access to its vertex data. For example, one visitor to a segment might turn - * the data into 2D drawing instructions. Another might find a point at a given distance + * + * It can also accept visitor objects with an `accept()` method. When a primitive accepts a visitor, + * it gives the visitor access to its vertex data. For example, one visitor to a segment might turn + * the data into 2D drawing instructions. Another might find a point at a given distance * along the segment. - * + * * @class p5.ShapePrimitive * @param {...p5.Vertex} vertices the vertices to include in the primitive. */ @@ -268,34 +274,34 @@ function customShapes(p5, fn) { /** * @private * A class to describe a vertex (a point on a shape), in 2D or 3D. - * + * * Vertices are the basic building blocks of all `p5.Shape` objects, including * shapes made with vertex(), arcVertex(), * bezierVertex(), and splineVertex(). - * - * Like a point on an object in the real world, a vertex may have different properties. - * These may include coordinate properties such as `position`, `textureCoordinates`, and `normal`, + * + * Like a point on an object in the real world, a vertex may have different properties. + * These may include coordinate properties such as `position`, `textureCoordinates`, and `normal`, * color properties such as `fill` and `stroke`, and more. - * - * A vertex called `myVertex` with position coordinates `(2, 3, 5)` and a green stroke may be created + * + * A vertex called `myVertex` with position coordinates `(2, 3, 5)` and a green stroke may be created * like this: - * + * * ```js * let myVertex = new p5.Vertex({ * position: createVector(2, 3, 5), * stroke: color('green') * }); * ``` - * - * Any property names may be used. Property values may be any - * JavaScript primitive, any - * object literal, - * or any object with an `array` property. - * + * + * Any property names may be used. Property values may be any + * JavaScript primitive, any + * object literal, + * or any object with an `array` property. + * * For example, if a position is stored as a `p5.Vector` object and a stroke is stored as a `p5.Color` object, * then the `array` properties of those objects will be used by the vertex's own `array` property, which provides * all the vertex data in a single array. - * + * * @class p5.Vertex * @param {Object} [properties={position: createVector(0, 0)}] vertex properties. */ @@ -306,7 +312,7 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class responsible for... */ p5.Anchor = Anchor; @@ -320,7 +326,7 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class responsible for... */ p5.LineSegment = LineSegment; @@ -334,7 +340,7 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class responsible for... */ p5.SplineSegment = SplineSegment; @@ -343,14 +349,14 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class responsible for... */ p5.Point = Point; /** * @private - * A class responsible for... + * A class responsible for... */ p5.Line = Line; @@ -364,7 +370,7 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class responsible for... */ p5.Quad = Quad; @@ -373,14 +379,14 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class responsible for... */ p5.TriangleFan = TriangleFan; /** * @private - * A class responsible for... + * A class responsible for... */ p5.TriangleStrip = TriangleStrip; @@ -396,7 +402,7 @@ function customShapes(p5, fn) { /** * @private - * A class responsible for... + * A class responsible for... */ p5.PrimitiveVisitor = PrimitiveVisitor; @@ -421,11 +427,11 @@ function customShapes(p5, fn) { */ p5.PointAtLengthGetter = PointAtLengthGetter; - + // ---- FUNCTIONS ---- // Note: Code is commented out for now, to avoid conflicts with the existing implementation. - + /** * Top-line description * @@ -436,7 +442,7 @@ function customShapes(p5, fn) { // // example of how to call an existing p5 function: // // this.background('yellow'); // }; - + /** * Top-line description * @@ -544,4 +550,4 @@ export { if (typeof p5 !== 'undefined') { customShapes(p5, p5.prototype); -} \ No newline at end of file +} diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 7b8e3cb6cd..5bce7d3720 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -442,6 +442,7 @@ class RendererGL extends Renderer { ////////////////////////////////////////////// beginShape(...args) { + this.currentShape.reset(); this.shapeBuilder.beginShape(...args); } @@ -494,6 +495,7 @@ class RendererGL extends Renderer { } else { this.states._currentNormal = new Vector(xorv, y, z); } + this.updateShapeVertexProperties(); } ////////////////////////////////////////////// From 290f5c7b5f6bc1cb0d244969985c919af5eba187 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Fri, 22 Nov 2024 19:42:50 -0500 Subject: [PATCH 25/42] Update vertexProperties to make setters --- src/core/p5.Renderer.js | 7 +++++-- src/shape/custom_shapes.js | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 797b85bb22..166e661906 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -52,7 +52,7 @@ class Renderer { this._clipInvert = false; this._curveTightness = 0; - this.currentShape = new Shape(); + this.currentShape = new Shape(this.vertexProperties()); } remove() { @@ -186,7 +186,10 @@ class Renderer { } updateShapeVertexProperties() { - this.currentShape.vertexProperties = this.vertexProperties(); + const props = this.vertexProperties(); + for (const key in props) { + this.currentShape[key](props[key]); + } } textSize(s) { diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 7bba519fc8..c27d8ff6d9 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -12,10 +12,18 @@ // ---- GENERAL CLASSES ---- class Shape { - vertexProperties = {}; - - constructor() { - + vertexProperties; + + constructor(vertexProperties) { + this.vertexProperties = vertexProperties; + + for (const key in this.vertexProperties) { + if (key !== 'position' && key !== 'textureCoordinates') { + this[key] = function(value) { + this.vertexProperties[key] = value; + } + } + } } reset() { From cd156bf1bba32c99b4c2610cead893a9d136f104 Mon Sep 17 00:00:00 2001 From: Dave Pagurek Date: Fri, 22 Nov 2024 20:34:44 -0500 Subject: [PATCH 26/42] Refactor old vertex() into legacyVertex() and start making new vertex() --- src/core/p5.Renderer.js | 39 +++++ src/core/p5.Renderer2D.js | 6 +- src/shape/custom_shapes.js | 24 ++- src/shape/vertex.js | 95 ++++++----- src/webgl/3d_primitives.js | 65 +++---- src/webgl/p5.RendererGL.js | 11 +- test/unit/webgl/p5.RendererGL.js | 284 +++++++++++++++---------------- 7 files changed, 303 insertions(+), 221 deletions(-) diff --git a/src/core/p5.Renderer.js b/src/core/p5.Renderer.js index 166e661906..0139e0272a 100644 --- a/src/core/p5.Renderer.js +++ b/src/core/p5.Renderer.js @@ -7,6 +7,7 @@ import { Color } from '../color/p5.Color'; import * as constants from '../core/constants'; import { Image } from '../image/p5.Image'; +import { Vector } from '../math/p5.Vector'; import { Shape } from '../shape/custom_shapes'; class Renderer { @@ -98,6 +99,44 @@ class Renderer { this.updateShapeVertexProperties(); } + beginShape(...args) { + this.currentShape.reset(); + this.currentShape.beginShape(...args); + } + + endShape(...args) { + this.currentShape.endShape(...args); + this.drawShape(this.currentShape); + } + + drawShape(shape) { + throw new Error('Unimplemented') + } + + vertex(x, y) { + let z, u, v; + + // default to (x, y) mode: all other arguments assumed to be 0. + z = u = v = 0; + + if (arguments.length === 3) { + // (x, y, z) mode: (u, v) assumed to be 0. + z = arguments[2]; + } else if (arguments.length === 4) { + // (x, y, u, v) mode: z assumed to be 0. + u = arguments[2]; + v = arguments[3]; + } else if (arguments.length === 5) { + // (x, y, z, u, v) mode + z = arguments[2]; + u = arguments[3]; + v = arguments[4]; + } + const position = new Vector(x, y, z); + const textureCoordinates = new Vector(u, v); + this.currentShape.vertex(position, textureCoordinates); + } + beginClip(options = {}) { if (this._clipping) { throw new Error("It looks like you're trying to clip while already in the middle of clipping. Did you forget to endClip()?"); diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js index 2cd1e27eb5..3e820ec12e 100644 --- a/src/core/p5.Renderer2D.js +++ b/src/core/p5.Renderer2D.js @@ -252,6 +252,10 @@ class Renderer2D extends Renderer { } } + drawShape(shape) { + // TODO + } + beginClip(options = {}) { super.beginClip(options); @@ -933,7 +937,7 @@ class Renderer2D extends Renderer { } } - endShape( + legacyEndShape( mode, vertices, isCurve, diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index c27d8ff6d9..e63ecb664d 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -7,7 +7,7 @@ */ // uncomment the following once you need it (otherwise VS Code complains): -// import * as constants from '../core/constants'; +import * as constants from '../core/constants'; // ---- GENERAL CLASSES ---- @@ -29,6 +29,28 @@ class Shape { reset() { // TODO: remove existing vertices } + + vertex(position, textureCoordinates) { + // Add the current position and texture coordiantes to the existing state + let vertex = this.createVertex({ ...this.vertexProperties, position, textureCoordinates }); + // TODO + // primitiveShapeCreator = primitiveShapeCreators.get(['vertex', this.kind]); + // primitiveShape = primitiveShapeCreator(vertex); + // primitiveShape.addToShape(this); + } + + createVertex(properties) { + // TODO + // return vertex; + } + + beginShape(shapeKind) { + // TODO + } + + endShape(closeMode = constants.OPEN) { + // TODO + } } class Contour { diff --git a/src/shape/vertex.js b/src/shape/vertex.js index bc22d7d886..0067e760b9 100644 --- a/src/shape/vertex.js +++ b/src/shape/vertex.js @@ -518,9 +518,10 @@ function vertex(p5, fn){ */ fn.beginShape = function(kind) { p5._validateParameters('beginShape', arguments); - if (this._renderer.isP3D) { - this._renderer.beginShape(...arguments); - } else { + this._renderer.beginShape(...arguments); + + // TODO remove this once shape implementation is complete + if (!this._renderer.isP3D) { if ( kind === constants.POINTS || kind === constants.LINES || @@ -1512,8 +1513,11 @@ function vertex(p5, fn){ count = 1; } + this._renderer.endShape(mode, count); + + // TODO remove once shape refactor is complete if (this._renderer.isP3D) { - this._renderer.endShape( + this._renderer.legacyEndShape( mode, isCurve, isBezier, @@ -1540,7 +1544,7 @@ function vertex(p5, fn){ vertices.push(vertices[0]); } - this._renderer.endShape( + this._renderer.legacyEndShape( mode, vertices, isCurve, @@ -2036,8 +2040,11 @@ function vertex(p5, fn){ * @chainable */ fn.vertex = function(x, y, moveTo, u, v) { + this._renderer.vertex(...arguments); + + // TODO remove after shape refactor if (this._renderer.isP3D) { - this._renderer.vertex(...arguments); + this._renderer.legacyVertex(...arguments); } else { const vert = []; vert.isVert = true; @@ -2255,27 +2262,27 @@ function vertex(p5, fn){ }; /** Sets the shader's vertex property or attribute variables. - * + * * An vertex property or vertex attribute is a variable belonging to a vertex in a shader. p5.js provides some * default properties, such as `aPosition`, `aNormal`, `aVertexColor`, etc. These are - * set using vertex(), normal() + * set using vertex(), normal() * and fill() respectively. Custom properties can also - * be defined within beginShape() and + * be defined within beginShape() and * endShape(). - * + * * The first parameter, `propertyName`, is a string with the property's name. * This is the same variable name which should be declared in the shader, such as * `in vec3 aProperty`, similar to .`setUniform()`. - * - * The second parameter, `data`, is the value assigned to the shader variable. This - * value will be applied to subsequent vertices created with + * + * The second parameter, `data`, is the value assigned to the shader variable. This + * value will be applied to subsequent vertices created with * vertex(). It can be a Number or an array of numbers, * and in the shader program the type can be declared according to the WebGL * specification. Common types include `float`, `vec2`, `vec3`, `vec4` or matrices. - * - * See also the vertexProperty() method on + * + * See also the vertexProperty() method on * Geometry objects. - * + * * @example *
* @@ -2283,40 +2290,40 @@ function vertex(p5, fn){ * precision mediump float; * uniform mat4 uModelViewMatrix; * uniform mat4 uProjectionMatrix; - * + * * in vec3 aPosition; * in vec2 aOffset; - * + * * void main(){ * vec4 positionVec4 = vec4(aPosition.xyz, 1.0); - * positionVec4.xy += aOffset; + * positionVec4.xy += aOffset; * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4; * } * `; - * + * * const fragSrc = `#version 300 es * precision mediump float; - * out vec4 outColor; + * out vec4 outColor; * void main(){ - * outColor = vec4(0.0, 1.0, 1.0, 1.0); + * outColor = vec4(0.0, 1.0, 1.0, 1.0); * } * `; - * + * * function setup(){ * createCanvas(100, 100, WEBGL); * * // Create and use the custom shader. * const myShader = createShader(vertSrc, fragSrc); * shader(myShader); - * + * * describe('A wobbly, cyan circle on a gray background.'); * } - * + * * function draw(){ * // Set the styles * background(125); * noStroke(); - * + * * // Draw the circle. * beginShape(); * for (let i = 0; i < 30; i++){ @@ -2326,7 +2333,7 @@ function vertex(p5, fn){ * // Apply some noise to the coordinates. * const xOff = 10 * noise(x + millis()/1000) - 5; * const yOff = 10 * noise(y + millis()/1000) - 5; - * + * * // Apply these noise values to the following vertex. * vertexProperty('aOffset', [xOff, yOff]); * vertex(x, y); @@ -2335,26 +2342,26 @@ function vertex(p5, fn){ * } * *
- * + * *
* * let myShader; * const cols = 10; * const rows = 10; * const cellSize = 6; - * + * * const vertSrc = `#version 300 es * precision mediump float; * uniform mat4 uProjectionMatrix; * uniform mat4 uModelViewMatrix; - * + * * in vec3 aPosition; * in vec3 aNormal; * in vec3 aVertexColor; * in float aDistance; - * + * * out vec3 vVertexColor; - * + * * void main(){ * vec4 positionVec4 = vec4(aPosition, 1.0); * positionVec4.xyz += aDistance * aNormal * 2.0;; @@ -2362,49 +2369,49 @@ function vertex(p5, fn){ * gl_Position = uProjectionMatrix * uModelViewMatrix * positionVec4; * } * `; - * + * * const fragSrc = `#version 300 es * precision mediump float; - * + * * in vec3 vVertexColor; * out vec4 outColor; - * + * * void main(){ * outColor = vec4(vVertexColor, 1.0); * } * `; - * + * * function setup(){ * createCanvas(100, 100, WEBGL); - * + * * // Create and apply the custom shader. * myShader = createShader(vertSrc, fragSrc); * shader(myShader); * noStroke(); * describe('A blue grid, which moves away from the mouse position, on a gray background.'); * } - * + * * function draw(){ * background(200); - * + * * // Draw the grid in the middle of the screen. * translate(-cols*cellSize/2, -rows*cellSize/2); * beginShape(QUADS); * for (let i = 0; i < cols; i++) { * for (let j = 0; j < rows; j++) { - * + * * // Calculate the cell position. * let x = i * cellSize; * let y = j * cellSize; - * + * * fill(j/rows*255, j/cols*255, 255); - * + * * // Calculate the distance from the corner of each cell to the mouse. * let distance = dist(x1,y1, mouseX, mouseY); - * + * * // Send the distance to the shader. * vertexProperty('aDistance', min(distance, 100)); - * + * * vertex(x, y); * vertex(x + cellSize, y); * vertex(x + cellSize, y + cellSize); diff --git a/src/webgl/3d_primitives.js b/src/webgl/3d_primitives.js index 9b71bc569a..5f9f56a2a9 100644 --- a/src/webgl/3d_primitives.js +++ b/src/webgl/3d_primitives.js @@ -2369,30 +2369,31 @@ function primitives3D(p5, fn){ let x2 = c; let y2 = d; + // TODO shapes refactor this.beginShape(); if (tr !== 0) { - this.vertex(x2 - tr, y1); + this.legacyVertex(x2 - tr, y1); this.quadraticVertex(x2, y1, x2, y1 + tr); } else { - this.vertex(x2, y1); + this.legacyVertex(x2, y1); } if (br !== 0) { - this.vertex(x2, y2 - br); + this.legacyVertex(x2, y2 - br); this.quadraticVertex(x2, y2, x2 - br, y2); } else { - this.vertex(x2, y2); + this.legacyVertex(x2, y2); } if (bl !== 0) { - this.vertex(x1 + bl, y2); + this.legacyVertex(x1 + bl, y2); this.quadraticVertex(x1, y2, x1, y2 - bl); } else { - this.vertex(x1, y2); + this.legacyVertex(x1, y2); } if (tl !== 0) { - this.vertex(x1, y1 + tl); + this.legacyVertex(x1, y1 + tl); this.quadraticVertex(x1, y1, x1 + tl, y1); } else { - this.vertex(x1, y1); + this.legacyVertex(x1, y1); } this.shapeBuilder.geometry.uvs.length = 0; @@ -2402,7 +2403,7 @@ function primitives3D(p5, fn){ this.shapeBuilder.geometry.uvs.push(u, v); } - this.endShape(constants.CLOSE); + this.legacyEndShape(constants.CLOSE); } return this; }; @@ -2496,19 +2497,20 @@ function primitives3D(p5, fn){ z1 = z2 = z3 = z4 = 0; } const bezierDetail = this._pInst._bezierDetail || 20; //value of Bezier detail + // TODO shapes refactor this.beginShape(); for (let i = 0; i <= bezierDetail; i++) { const c1 = Math.pow(1 - i / bezierDetail, 3); const c2 = 3 * (i / bezierDetail) * Math.pow(1 - i / bezierDetail, 2); const c3 = 3 * Math.pow(i / bezierDetail, 2) * (1 - i / bezierDetail); const c4 = Math.pow(i / bezierDetail, 3); - this.vertex( + this.legacyVertex( x1 * c1 + x2 * c2 + x3 * c3 + x4 * c4, y1 * c1 + y2 * c2 + y3 * c3 + y4 * c4, z1 * c1 + z2 * c2 + z3 * c3 + z4 * c4 ); } - this.endShape(); + this.legacyEndShape(); return this; }; @@ -2537,6 +2539,7 @@ function primitives3D(p5, fn){ z1 = z2 = z3 = z4 = 0; } const curveDetail = this._pInst._curveDetail; + // TODO shapes refactor this.beginShape(); for (let i = 0; i <= curveDetail; i++) { const c1 = Math.pow(i / curveDetail, 3) * 0.5; @@ -2558,9 +2561,9 @@ function primitives3D(p5, fn){ c2 * (2 * z1 - 5 * z2 + 4 * z3 - z4) + c3 * (-z1 + z3) + c4 * (2 * z2); - this.vertex(vx, vy, vz); + this.legacyVertex(vx, vy, vz); } - this.endShape(); + this.legacyEndShape(); return this; }; @@ -2595,15 +2598,16 @@ function primitives3D(p5, fn){ */ RendererGL.prototype.line = function(...args) { if (args.length === 6) { + // TODO shapes refactor this.beginShape(constants.LINES); - this.vertex(args[0], args[1], args[2]); - this.vertex(args[3], args[4], args[5]); - this.endShape(); + this.legacyVertex(args[0], args[1], args[2]); + this.legacyVertex(args[3], args[4], args[5]); + this.legacyEndShape(); } else if (args.length === 4) { this.beginShape(constants.LINES); - this.vertex(args[0], args[1], 0); - this.vertex(args[2], args[3], 0); - this.endShape(); + this.legacyVertex(args[0], args[1], 0); + this.legacyVertex(args[2], args[3], 0); + this.legacyEndShape(); } return this; }; @@ -2739,7 +2743,7 @@ function primitives3D(p5, fn){ } prop.setCurrentData(newValues); } - this.vertex(_x, _y); + this.legacyVertex(_x, _y); } // so that we leave currentColor with the last value the user set it to this.states.curFillColor = fillColors[3]; @@ -2816,7 +2820,7 @@ function primitives3D(p5, fn){ } prop.setCurrentData(newValues); } - this.vertex(_x, _y, _z); + this.legacyVertex(_x, _y, _z); } // so that we leave currentColor with the last value the user set it to this.states.curFillColor = fillColors[3]; @@ -2955,7 +2959,7 @@ function primitives3D(p5, fn){ } prop.setCurrentData(newValues); } - this.vertex(_x, _y); + this.legacyVertex(_x, _y); } // so that we leave currentColor with the last value the user set it to @@ -3026,7 +3030,7 @@ function primitives3D(p5, fn){ } prop.setCurrentData(newValues); } - this.vertex(_x, _y, _z); + this.legacyVertex(_x, _y, _z); } // so that we leave currentColor with the last value the user set it to @@ -3106,7 +3110,7 @@ function primitives3D(p5, fn){ w_y[1] * this._lookUpTableBezier[i][1] + w_y[2] * this._lookUpTableBezier[i][2] + w_y[3] * this._lookUpTableBezier[i][3]; - this.vertex(_x, _y); + this.legacyVertex(_x, _y); } for (i = 0; i < argLength; i++) { this.shapeBuilder._curveVertex.shift(); @@ -3152,7 +3156,7 @@ function primitives3D(p5, fn){ w_z[1] * this._lookUpTableBezier[i][1] + w_z[2] * this._lookUpTableBezier[i][2] + w_z[3] * this._lookUpTableBezier[i][3]; - this.vertex(_x, _y, _z); + this.legacyVertex(_x, _y, _z); } for (i = 0; i < argLength; i++) { this.shapeBuilder._curveVertex.shift(); @@ -3205,12 +3209,13 @@ function primitives3D(p5, fn){ } this._drawingImage = true; + // TODO shape refactor this.beginShape(); - this.vertex(dx, dy, 0, u0, v0); - this.vertex(dx + dWidth, dy, 0, u1, v0); - this.vertex(dx + dWidth, dy + dHeight, 0, u1, v1); - this.vertex(dx, dy + dHeight, 0, u0, v1); - this.endShape(constants.CLOSE); + this.legacyVertex(dx, dy, 0, u0, v0); + this.legacyVertex(dx + dWidth, dy, 0, u1, v0); + this.legacyVertex(dx + dWidth, dy + dHeight, 0, u1, v1); + this.legacyVertex(dx, dy + dHeight, 0, u0, v1); + this.legacyEndShape(constants.CLOSE); this._drawingImage = false; this.pop(); diff --git a/src/webgl/p5.RendererGL.js b/src/webgl/p5.RendererGL.js index 5bce7d3720..2cdaf0a181 100644 --- a/src/webgl/p5.RendererGL.js +++ b/src/webgl/p5.RendererGL.js @@ -442,11 +442,16 @@ class RendererGL extends Renderer { ////////////////////////////////////////////// beginShape(...args) { - this.currentShape.reset(); + super.beginShape(...args); + // TODO remove when shape refactor is complete this.shapeBuilder.beginShape(...args); } - endShape( + drawShape(shape) { + // TODO + } + + legacyEndShape( mode, isCurve, isBezier, @@ -481,7 +486,7 @@ class RendererGL extends Renderer { this.shapeBuilder.beginContour(...args); } - vertex(...args) { + legacyVertex(...args) { this.shapeBuilder.vertex(...args); } diff --git a/test/unit/webgl/p5.RendererGL.js b/test/unit/webgl/p5.RendererGL.js index 0eb5891170..ab2136f6de 100644 --- a/test/unit/webgl/p5.RendererGL.js +++ b/test/unit/webgl/p5.RendererGL.js @@ -1465,33 +1465,33 @@ suite('p5.RendererGL', function() { test('QUADS mode converts into triangles', function() { var renderer = myp5.createCanvas(10, 10, myp5.WEBGL); myp5.textureMode(myp5.NORMAL); - renderer.beginShape(myp5.QUADS); - renderer.fill(255, 0, 0); - renderer.normal(0, 1, 2); - renderer.vertex(0, 0, 0, 0, 0); - renderer.fill(0, 255, 0); - renderer.normal(3, 4, 5); - renderer.vertex(0, 1, 1, 0, 1); - renderer.fill(0, 0, 255); - renderer.normal(6, 7, 8); - renderer.vertex(1, 0, 2, 1, 0); - renderer.fill(255, 0, 255); - renderer.normal(9, 10, 11); - renderer.vertex(1, 1, 3, 1, 1); - - renderer.fill(255, 0, 0); - renderer.normal(12, 13, 14); - renderer.vertex(2, 0, 4, 0, 0); - renderer.fill(0, 255, 0); - renderer.normal(15, 16, 17); - renderer.vertex(2, 1, 5, 0, 1); - renderer.fill(0, 0, 255); - renderer.normal(18, 19, 20); - renderer.vertex(3, 0, 6, 1, 0); - renderer.fill(255, 0, 255); - renderer.normal(21, 22, 23); - renderer.vertex(3, 1, 7, 1, 1); - renderer.endShape(); + myp5.beginShape(myp5.QUADS); + myp5.fill(255, 0, 0); + myp5.normal(0, 1, 2); + myp5.vertex(0, 0, 0, 0, 0); + myp5.fill(0, 255, 0); + myp5.normal(3, 4, 5); + myp5.vertex(0, 1, 1, 0, 1); + myp5.fill(0, 0, 255); + myp5.normal(6, 7, 8); + myp5.vertex(1, 0, 2, 1, 0); + myp5.fill(255, 0, 255); + myp5.normal(9, 10, 11); + myp5.vertex(1, 1, 3, 1, 1); + + myp5.fill(255, 0, 0); + myp5.normal(12, 13, 14); + myp5.vertex(2, 0, 4, 0, 0); + myp5.fill(0, 255, 0); + myp5.normal(15, 16, 17); + myp5.vertex(2, 1, 5, 0, 1); + myp5.fill(0, 0, 255); + myp5.normal(18, 19, 20); + myp5.vertex(3, 0, 6, 1, 0); + myp5.fill(255, 0, 255); + myp5.normal(21, 22, 23); + myp5.vertex(3, 1, 7, 1, 1); + myp5.endShape(); const expectedVerts = [ [0, 0, 0], @@ -1591,33 +1591,33 @@ suite('p5.RendererGL', function() { test('QUADS mode makes edges for quad outlines', function() { var renderer = myp5.createCanvas(10, 10, myp5.WEBGL); - renderer.beginShape(myp5.QUADS); - renderer.vertex(0, 0); - renderer.vertex(0, 1); - renderer.vertex(1, 0); - renderer.vertex(1, 1); - - renderer.vertex(2, 0); - renderer.vertex(2, 1); - renderer.vertex(3, 0); - renderer.vertex(3, 1); - renderer.endShape(); + myp5.beginShape(myp5.QUADS); + myp5.vertex(0, 0); + myp5.vertex(0, 1); + myp5.vertex(1, 0); + myp5.vertex(1, 1); + + myp5.vertex(2, 0); + myp5.vertex(2, 1); + myp5.vertex(3, 0); + myp5.vertex(3, 1); + myp5.endShape(); assert.equal(renderer.shapeBuilder.geometry.edges.length, 8); }); test('QUAD_STRIP mode makes edges for strip outlines', function() { var renderer = myp5.createCanvas(10, 10, myp5.WEBGL); - renderer.beginShape(myp5.QUAD_STRIP); - renderer.vertex(0, 0); - renderer.vertex(0, 1); - renderer.vertex(1, 0); - renderer.vertex(1, 1); - renderer.vertex(2, 0); - renderer.vertex(2, 1); - renderer.vertex(3, 0); - renderer.vertex(3, 1); - renderer.endShape(); + myp5.beginShape(myp5.QUAD_STRIP); + myp5.vertex(0, 0); + myp5.vertex(0, 1); + myp5.vertex(1, 0); + myp5.vertex(1, 1); + myp5.vertex(2, 0); + myp5.vertex(2, 1); + myp5.vertex(3, 0); + myp5.vertex(3, 1); + myp5.endShape(); // Two full quads (2 * 4) plus two edges connecting them assert.equal(renderer.shapeBuilder.geometry.edges.length, 10); @@ -1630,13 +1630,13 @@ suite('p5.RendererGL', function() { // x--x--x // \ | / // x - renderer.beginShape(myp5.TRIANGLE_FAN); - renderer.vertex(0, 0); - renderer.vertex(0, -5); - renderer.vertex(5, 0); - renderer.vertex(0, 5); - renderer.vertex(-5, 0); - renderer.endShape(); + myp5.beginShape(myp5.TRIANGLE_FAN); + myp5.vertex(0, 0); + myp5.vertex(0, -5); + myp5.vertex(5, 0); + myp5.vertex(0, 5); + myp5.vertex(-5, 0); + myp5.endShape(); assert.equal(renderer.shapeBuilder.geometry.edges.length, 7); }); @@ -1645,24 +1645,24 @@ suite('p5.RendererGL', function() { var renderer = myp5.createCanvas(10, 10, myp5.WEBGL); myp5.textureMode(myp5.NORMAL); - renderer.beginShape(myp5.TESS); - renderer.fill(255, 255, 255); - renderer.normal(-1, -1, 1); - renderer.vertexProperty('aCustom', [1, 1, 1]) - renderer.vertex(-10, -10, 0, 0); - renderer.fill(255, 0, 0); - renderer.normal(1, -1, 1); - renderer.vertexProperty('aCustom', [1, 0, 0]) - renderer.vertex(10, -10, 1, 0); - renderer.fill(0, 255, 0); - renderer.normal(1, 1, 1); - renderer.vertexProperty('aCustom', [0, 1, 0]) - renderer.vertex(10, 10, 1, 1); - renderer.fill(0, 0, 255); - renderer.normal(-1, 1, 1); - renderer.vertexProperty('aCustom', [0, 0, 1]) - renderer.vertex(-10, 10, 0, 1); - renderer.endShape(myp5.CLOSE); + myp5.beginShape(myp5.TESS); + myp5.fill(255, 255, 255); + myp5.normal(-1, -1, 1); + myp5.vertexProperty('aCustom', [1, 1, 1]) + myp5.vertex(-10, -10, 0, 0); + myp5.fill(255, 0, 0); + myp5.normal(1, -1, 1); + myp5.vertexProperty('aCustom', [1, 0, 0]) + myp5.vertex(10, -10, 1, 0); + myp5.fill(0, 255, 0); + myp5.normal(1, 1, 1); + myp5.vertexProperty('aCustom', [0, 1, 0]) + myp5.vertex(10, 10, 1, 1); + myp5.fill(0, 0, 255); + myp5.normal(-1, 1, 1); + myp5.vertexProperty('aCustom', [0, 0, 1]) + myp5.vertex(-10, 10, 0, 1); + myp5.endShape(myp5.CLOSE); assert.equal(renderer.shapeBuilder.geometry.vertices.length, 6); assert.deepEqual( @@ -1748,17 +1748,17 @@ suite('p5.RendererGL', function() { var renderer = myp5.createCanvas(10, 10, myp5.WEBGL); myp5.textureMode(myp5.NORMAL); - renderer.beginShape(myp5.TESS); + myp5.beginShape(myp5.TESS); myp5.noFill(); - renderer.stroke(255, 255, 255); - renderer.vertex(-10, -10, 0, 0); - renderer.stroke(255, 0, 0); - renderer.vertex(10, -10, 1, 0); - renderer.stroke(0, 255, 0); - renderer.vertex(10, 10, 1, 1); - renderer.stroke(0, 0, 255); - renderer.vertex(-10, 10, 0, 1); - renderer.endShape(myp5.CLOSE); + myp5.stroke(255, 255, 255); + myp5.vertex(-10, -10, 0, 0); + myp5.stroke(255, 0, 0); + myp5.vertex(10, -10, 1, 0); + myp5.stroke(0, 255, 0); + myp5.vertex(10, 10, 1, 1); + myp5.stroke(0, 0, 255); + myp5.vertex(-10, 10, 0, 1); + myp5.endShape(myp5.CLOSE); // Vertex colors are not run through tessy assert.deepEqual(renderer.shapeBuilder.geometry.vertexStrokeColors, [ @@ -1775,13 +1775,13 @@ suite('p5.RendererGL', function() { myp5.textureMode(myp5.IMAGE); myp5.texture(texture); - renderer.beginShape(myp5.TESS); + myp5.beginShape(myp5.TESS); myp5.noFill(); - renderer.vertex(-10, -10, 0, 0); - renderer.vertex(10, -10, 25, 0); - renderer.vertex(10, 10, 25, 25); - renderer.vertex(-10, 10, 0, 25); - renderer.endShape(myp5.CLOSE); + myp5.vertex(-10, -10, 0, 0); + myp5.vertex(10, -10, 25, 0); + myp5.vertex(10, 10, 25, 25); + myp5.vertex(-10, 10, 0, 25); + myp5.endShape(myp5.CLOSE); // UVs are correctly translated through tessy assert.deepEqual(renderer.shapeBuilder.geometry.uvs, [ @@ -1806,20 +1806,20 @@ suite('p5.RendererGL', function() { // // Tessy will add a vertex in the middle myp5.textureMode(myp5.NORMAL); - renderer.beginShape(myp5.TESS); - renderer.fill(255, 255, 255); - renderer.normal(-1, -1, 1); - renderer.vertex(-10, -10, 0, 0); - renderer.fill(0, 255, 0); - renderer.normal(1, 1, 1); - renderer.vertex(10, 10, 1, 1); - renderer.fill(255, 0, 0); - renderer.normal(1, -1, 1); - renderer.vertex(10, -10, 1, 0); - renderer.fill(0, 0, 255); - renderer.normal(-1, 1, 1); - renderer.vertex(-10, 10, 0, 1); - renderer.endShape(myp5.CLOSE); + myp5.beginShape(myp5.TESS); + myp5.fill(255, 255, 255); + myp5.normal(-1, -1, 1); + myp5.vertex(-10, -10, 0, 0); + myp5.fill(0, 255, 0); + myp5.normal(1, 1, 1); + myp5.vertex(10, 10, 1, 1); + myp5.fill(255, 0, 0); + myp5.normal(1, -1, 1); + myp5.vertex(10, -10, 1, 0); + myp5.fill(0, 0, 255); + myp5.normal(-1, 1, 1); + myp5.vertex(-10, 10, 0, 1); + myp5.endShape(myp5.CLOSE); assert.equal(renderer.shapeBuilder.geometry.vertices.length, 6); assert.deepEqual( @@ -1896,12 +1896,12 @@ suite('p5.RendererGL', function() { var renderer = myp5.createCanvas(10, 10, myp5.WEBGL); myp5.textureMode(myp5.NORMAL); - renderer.beginShape(myp5.TESS); - renderer.vertex(-10, 0, -10); - renderer.vertex(10, 0, -10); - renderer.vertex(10, 0, 10); - renderer.vertex(-10, 0, 10); - renderer.endShape(myp5.CLOSE); + myp5.beginShape(myp5.TESS); + myp5.vertex(-10, 0, -10); + myp5.vertex(10, 0, -10); + myp5.vertex(10, 0, 10); + myp5.vertex(-10, 0, 10); + myp5.endShape(myp5.CLOSE); assert.equal(renderer.shapeBuilder.geometry.vertices.length, 6); assert.deepEqual( @@ -1939,13 +1939,13 @@ suite('p5.RendererGL', function() { // far right color: (42, 36, 240) // expected middle color: (142, 136, 140) - renderer.strokeWeight(4); - renderer.beginShape(); - renderer.stroke(242, 236, 40); - renderer.vertex(-256, 0); - renderer.stroke(42, 36, 240); - renderer.vertex(256, 0); - renderer.endShape(); + myp5.strokeWeight(4); + myp5.beginShape(); + myp5.stroke(242, 236, 40); + myp5.vertex(-256, 0); + myp5.stroke(42, 36, 240); + myp5.vertex(256, 0); + myp5.endShape(); assert.deepEqual(myp5.get(0, 2), [242, 236, 40, 255]); assert.deepEqual(myp5.get(256, 2), [142, 136, 140, 255]); @@ -1959,12 +1959,12 @@ suite('p5.RendererGL', function() { // end color: (255, 0, 0) // Intermediate values are expected to be approximately half the value. - renderer.beginShape(); - renderer.fill(255); - renderer.vertex(-128, -128); - renderer.fill(255, 0, 0); - renderer.bezierVertex(128, -128, 128, 128, -128, 128); - renderer.endShape(); + myp5.beginShape(); + myp5.fill(255); + myp5.vertex(-128, -128); + myp5.fill(255, 0, 0); + myp5.bezierVertex(128, -128, 128, 128, -128, 128); + myp5.endShape(); assert.deepEqual(myp5.get(128, 127), [255, 129, 129, 255]); }); @@ -1976,14 +1976,14 @@ suite('p5.RendererGL', function() { // end color: (255, 0, 0) // Intermediate values are expected to be approximately half the value. - renderer.strokeWeight(5); - renderer.beginShape(); + myp5.strokeWeight(5); + myp5.beginShape(); myp5.noFill(); - renderer.stroke(255); - renderer.vertex(-128, -128); - renderer.stroke(255, 0, 0); - renderer.bezierVertex(128, -128, 128, 128, -128, 128); - renderer.endShape(); + myp5.stroke(255); + myp5.vertex(-128, -128); + myp5.stroke(255, 0, 0); + myp5.bezierVertex(128, -128, 128, 128, -128, 128); + myp5.endShape(); assert.arrayApproximately(myp5.get(190, 127), [255, 128, 128, 255], 10); }); @@ -1995,12 +1995,12 @@ suite('p5.RendererGL', function() { // end color: (255, 0, 0) // Intermediate values are expected to be approximately half the value. - renderer.beginShape(); - renderer.fill(255); - renderer.vertex(-128, -128); - renderer.fill(255, 0, 0); - renderer.quadraticVertex(256, 0, -128, 128); - renderer.endShape(); + myp5.beginShape(); + myp5.fill(255); + myp5.vertex(-128, -128); + myp5.fill(255, 0, 0); + myp5.quadraticVertex(256, 0, -128, 128); + myp5.endShape(); assert.arrayApproximately(myp5.get(128, 127), [255, 128, 128, 255], 10); }); @@ -2012,14 +2012,14 @@ suite('p5.RendererGL', function() { // end color: (255, 0, 0) // Intermediate values are expected to be approximately half the value. - renderer.strokeWeight(5); - renderer.beginShape(); + myp5.strokeWeight(5); + myp5.beginShape(); myp5.noFill(); - renderer.stroke(255); - renderer.vertex(-128, -128); - renderer.stroke(255, 0, 0); - renderer.quadraticVertex(256, 0, -128, 128); - renderer.endShape(); + myp5.stroke(255); + myp5.vertex(-128, -128); + myp5.stroke(255, 0, 0); + myp5.quadraticVertex(256, 0, -128, 128); + myp5.endShape(); assert.deepEqual(myp5.get(190, 127), [255, 128, 128, 255]); }); From bc252e372ed2b7b5171d01b2409fc8e90bdfc932 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 01:43:20 -0700 Subject: [PATCH 27/42] Implement Vertex constructor --- src/shape/custom_shapes.js | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index e63ecb664d..de0106ab56 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -67,8 +67,18 @@ class ShapePrimitive { } class Vertex { - constructor() { - + constructor(properties) { + for (const [key, value] of Object.entries(properties)) { + this[key] = value; + } + } + + get array() { + // convert to 1D array + // call `toArray()` if value is an object with a toArray() method + // handle primitive values separately + // maybe handle object literals too, with Object.values()? + // probably don’t need anything else for now? } // TODO: make sure name of array conversion method is From 0c61aaf9c2ad366a789b30f659443ecbd87305b7 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 02:32:15 -0700 Subject: [PATCH 28/42] Implement ShapePrimitive, add some doc tags --- src/shape/custom_shapes.js | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index de0106ab56..3548d37a56 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -59,10 +59,27 @@ class Contour { } } -// abstract class class ShapePrimitive { constructor() { + if (this.constructor === ShapePrimitive) { + throw new Error("ShapePrimitive is an abstract class: it cannot be instantiated."); + } + } + + get vertexCount() { + throw new Error("Getter vertexCount must be implemented."); + } + + get vertexCapacity() { + throw new Error("Getter vertexCapacity must be implemented."); + } + + accept(visitor) { + throw new Error("Method accept() must be implemented."); + } + addToShape(shape) { + throw new Error("Method addToShape() must be implemented."); } } @@ -306,7 +323,7 @@ function customShapes(p5, fn) { * along the segment. * * @class p5.ShapePrimitive - * @param {...p5.Vertex} vertices the vertices to include in the primitive. + * @abstract */ p5.ShapePrimitive = ShapePrimitive; @@ -353,6 +370,9 @@ function customShapes(p5, fn) { /** * @private * A class responsible for... + * + * @class p5.Anchor + * @param {p5.Vertex} vertex the vertex to include in the anchor. */ p5.Anchor = Anchor; @@ -367,6 +387,9 @@ function customShapes(p5, fn) { /** * @private * A class responsible for... + * + * @class p5.LineSegment + * @param {p5.Vertex} vertex the vertex to include in the anchor. */ p5.LineSegment = LineSegment; @@ -397,6 +420,9 @@ function customShapes(p5, fn) { /** * @private * A class responsible for... + * + * @class p5.Line + * @param {...p5.Vertex} vertices the vertices to include in the line. */ p5.Line = Line; From 2326ff512a3fde1d5e46b441afb66fae0ae579f6 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 02:58:31 -0700 Subject: [PATCH 29/42] Add PATH to constants.js --- src/core/constants.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/constants.js b/src/core/constants.js index bb120fa712..b37ecaf393 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -696,6 +696,12 @@ export const BOTTOM = 'bottom'; * @final */ export const BASELINE = 'alphabetic'; +/** + * @typedef {unique symbol} PATH + * @property {PATH} PATH + * @final + */ +export const PATH = Symbol('path'); /** * @typedef {0x0000} POINTS * @property {POINTS} POINTS From 75030a5b4c4eb1cbf7d354d8d1e254556b349465 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 03:24:36 -0700 Subject: [PATCH 30/42] Implement Contour --- src/shape/custom_shapes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 3548d37a56..407d65a381 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -54,8 +54,9 @@ class Shape { } class Contour { - constructor() { - + constructor(kind = constants.PATH) { + this.kind = kind; + this.primitives = []; } } From 70a6fa6076b556c3bcf9969ad0b352299ff9d4e3 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 03:53:30 -0700 Subject: [PATCH 31/42] Add EMPTY_PATH to constants.js --- src/core/constants.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/constants.js b/src/core/constants.js index b37ecaf393..962375e63c 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -702,6 +702,12 @@ export const BASELINE = 'alphabetic'; * @final */ export const PATH = Symbol('path'); +/** + * @typedef {unique symbol} EMPTY_PATH + * @property {EMPTY_PATH} EMPTY_PATH + * @final + */ +export const EMPTY_PATH = Symbol('empty_path'); /** * @typedef {0x0000} POINTS * @property {POINTS} POINTS From d7aec76268fb207796bbc72c77edb9c37f4b189d Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 04:19:43 -0700 Subject: [PATCH 32/42] Add kind getter to Contour to handle EMPTY_PATH --- src/shape/custom_shapes.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 407d65a381..66d3e6ae95 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -54,10 +54,19 @@ class Shape { } class Contour { + #kind; + primitives; + constructor(kind = constants.PATH) { - this.kind = kind; + this.#kind = kind; this.primitives = []; } + + get kind() { + const isEmpty = this.primitives.length === 0; + const isPath = this.#kind === constants.PATH; + return isEmpty && isPath ? constants.EMPTY_PATH : this.#kind; + } } class ShapePrimitive { From 27f0b2642fe9e5dc71fb7b35c0e08b74c751d6f0 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 04:30:20 -0700 Subject: [PATCH 33/42] Add tag to Anchor docs --- src/shape/custom_shapes.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 66d3e6ae95..ff4ec4b9cd 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -382,6 +382,7 @@ function customShapes(p5, fn) { * A class responsible for... * * @class p5.Anchor + * @extends p5.ShapePrimitive * @param {p5.Vertex} vertex the vertex to include in the anchor. */ From 4eaa56fbc1d890bbc5c4fd1c62370c30267e2505 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sat, 23 Nov 2024 05:42:37 -0700 Subject: [PATCH 34/42] Implement Anchor, add vertices to ShapePrimitive --- src/shape/custom_shapes.js | 34 +++++++++++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index ff4ec4b9cd..5e12098eb9 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -70,10 +70,13 @@ class Contour { } class ShapePrimitive { - constructor() { + vertices; + + constructor(...vertices) { if (this.constructor === ShapePrimitive) { throw new Error("ShapePrimitive is an abstract class: it cannot be instantiated."); } + this.vertices = vertices; } get vertexCount() { @@ -115,9 +118,34 @@ class Vertex { // ---- PATH PRIMITIVES ---- -class Anchor { - constructor() { +class Anchor extends ShapePrimitive { + #vertexCapacity; + + constructor(...vertices) { + super(...vertices); + this.#vertexCapacity = 1; + } + get vertexCount() { + return this.vertices.length; + } + + get vertexCapacity() { + return this.#vertexCapacity; + } + + accept(visitor) { + visitor.visitAnchor(this); + } + + addToShape(shape) { + let lastContour = shape.contours.at(-1); + if (lastContour.kind === constants.EMPTY_PATH) { + lastContour.primitives.push(this); + } + else { + throw new Error("Anchor can only be added to an empty path contour."); + } } } From 13f21d9e1ff9059210c07223fcb579a6f34bb1b6 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sun, 24 Nov 2024 00:21:38 -0700 Subject: [PATCH 35/42] Implement Segment, add tags to docs --- src/shape/custom_shapes.js | 490 +++++++++++++++++++------------------ 1 file changed, 253 insertions(+), 237 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 5e12098eb9..2ce04ccd1c 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -6,254 +6,266 @@ * @requires constants */ -// uncomment the following once you need it (otherwise VS Code complains): -import * as constants from '../core/constants'; +// REMINDER: remove .js extension (currently using it to run file locally) +import * as constants from '../core/constants.js'; // ---- GENERAL CLASSES ---- class Shape { - vertexProperties; + vertexProperties; - constructor(vertexProperties) { - this.vertexProperties = vertexProperties; + constructor(vertexProperties) { + this.vertexProperties = vertexProperties; - for (const key in this.vertexProperties) { - if (key !== 'position' && key !== 'textureCoordinates') { - this[key] = function(value) { - this.vertexProperties[key] = value; - } - } + for (const key in this.vertexProperties) { + if (key !== 'position' && key !== 'textureCoordinates') { + this[key] = function(value) { + this.vertexProperties[key] = value; + }; } } - - reset() { - // TODO: remove existing vertices - } - - vertex(position, textureCoordinates) { - // Add the current position and texture coordiantes to the existing state - let vertex = this.createVertex({ ...this.vertexProperties, position, textureCoordinates }); - // TODO - // primitiveShapeCreator = primitiveShapeCreators.get(['vertex', this.kind]); - // primitiveShape = primitiveShapeCreator(vertex); - // primitiveShape.addToShape(this); - } - - createVertex(properties) { - // TODO - // return vertex; - } - - beginShape(shapeKind) { - // TODO - } - - endShape(closeMode = constants.OPEN) { - // TODO - } + } + + reset() { + // TODO: remove existing vertices + } + + vertex(position, textureCoordinates) { + // Add the current position and texture coordiantes to the existing state + let vertex = this.createVertex({ ...this.vertexProperties, position, textureCoordinates }); + // TODO + // primitiveShapeCreator = primitiveShapeCreators.get(['vertex', this.kind]); + // primitiveShape = primitiveShapeCreator(vertex); + // primitiveShape.addToShape(this); + } + + createVertex(properties) { + // TODO + // return vertex; + } + + beginShape(shapeKind) { + // TODO + } + + endShape(closeMode = constants.OPEN) { + // TODO + } } class Contour { - #kind; - primitives; - - constructor(kind = constants.PATH) { - this.#kind = kind; - this.primitives = []; - } - - get kind() { - const isEmpty = this.primitives.length === 0; - const isPath = this.#kind === constants.PATH; - return isEmpty && isPath ? constants.EMPTY_PATH : this.#kind; - } + #kind; + primitives; + + constructor(kind = constants.PATH) { + this.#kind = kind; + this.primitives = []; + } + + get kind() { + const isEmpty = this.primitives.length === 0; + const isPath = this.#kind === constants.PATH; + return isEmpty && isPath ? constants.EMPTY_PATH : this.#kind; + } } class ShapePrimitive { - vertices; + vertices; - constructor(...vertices) { - if (this.constructor === ShapePrimitive) { - throw new Error("ShapePrimitive is an abstract class: it cannot be instantiated."); - } - this.vertices = vertices; + constructor(...vertices) { + if (this.constructor === ShapePrimitive) { + throw new Error('ShapePrimitive is an abstract class: it cannot be instantiated.'); } + this.vertices = vertices; + } - get vertexCount() { - throw new Error("Getter vertexCount must be implemented."); - } + get vertexCount() { + throw new Error('Getter vertexCount must be implemented.'); + } - get vertexCapacity() { - throw new Error("Getter vertexCapacity must be implemented."); - } + get vertexCapacity() { + throw new Error('Getter vertexCapacity must be implemented.'); + } - accept(visitor) { - throw new Error("Method accept() must be implemented."); - } + accept(visitor) { + throw new Error('Method accept() must be implemented.'); + } - addToShape(shape) { - throw new Error("Method addToShape() must be implemented."); - } + addToShape(shape) { + throw new Error('Method addToShape() must be implemented.'); + } } class Vertex { - constructor(properties) { - for (const [key, value] of Object.entries(properties)) { - this[key] = value; - } - } - - get array() { - // convert to 1D array - // call `toArray()` if value is an object with a toArray() method - // handle primitive values separately - // maybe handle object literals too, with Object.values()? - // probably don’t need anything else for now? - } - - // TODO: make sure name of array conversion method is - // consistent with any modifications to the names of corresponding - // properties of p5.Vector and p5.Color + constructor(properties) { + for (const [key, value] of Object.entries(properties)) { + this[key] = value; + } + } + /* + get array() { + // convert to 1D array + // call `toArray()` if value is an object with a toArray() method + // handle primitive values separately + // maybe handle object literals too, with Object.values()? + // probably don’t need anything else for now? + } + */ + // TODO: make sure name of array conversion method is + // consistent with any modifications to the names of corresponding + // properties of p5.Vector and p5.Color } // ---- PATH PRIMITIVES ---- class Anchor extends ShapePrimitive { - #vertexCapacity; + #vertexCapacity; - constructor(...vertices) { - super(...vertices); - this.#vertexCapacity = 1; - } + constructor(...vertices) { + super(...vertices); + this.#vertexCapacity = 1; + } - get vertexCount() { - return this.vertices.length; - } + get vertexCount() { + return this.vertices.length; + } - get vertexCapacity() { - return this.#vertexCapacity; - } + get vertexCapacity() { + return this.#vertexCapacity; + } - accept(visitor) { - visitor.visitAnchor(this); - } + accept(visitor) { + visitor.visitAnchor(this); + } - addToShape(shape) { - let lastContour = shape.contours.at(-1); - if (lastContour.kind === constants.EMPTY_PATH) { - lastContour.primitives.push(this); - } - else { - throw new Error("Anchor can only be added to an empty path contour."); - } + addToShape(shape) { + let lastContour = shape.contours.at(-1); + if (lastContour.kind === constants.EMPTY_PATH) { + lastContour.primitives.push(this); + } + else { + throw new Error('Anchor can only be added to an empty path contour.'); } + } } // abstract class class Segment extends ShapePrimitive { - constructor() { + index = 0; + constructor(...vertices) { + super(...vertices); + if (this.constructor === Segment) { + throw new Error('Segment is an abstract class: it cannot be instantiated.'); } + } + + getStartVertex() { + throw new Error('Method getStartVertex() must be implemented.'); + } + + getEndVertex() { + throw new Error('Method getEndVertex() must be implemented.'); + } } class LineSegment extends Segment { - constructor() { - - } + constructor(...vertices) { + } } class BezierSegment extends Segment { - constructor() { + constructor() { - } + } } // consider type and end modes -- see #6766) // may want to use separate classes, but maybe not class SplineSegment extends Segment { - constructor() { + constructor() { - } + } } // ---- ISOLATED PRIMITIVES ---- class Point extends ShapePrimitive { - constructor() { + constructor() { - } + } } class Line extends ShapePrimitive { - constructor() { + constructor() { - } + } } class Triangle extends ShapePrimitive { - constructor() { + constructor() { - } + } } class Quad extends ShapePrimitive { - constructor() { + constructor() { - } + } } // ---- TESSELLATION PRIMITIVES ---- class TriangleFan extends ShapePrimitive { - constructor() { + constructor() { - } + } } class TriangleStrip extends ShapePrimitive { - constructor() { + constructor() { - } + } } class QuadStrip extends ShapePrimitive { - constructor() { + constructor() { - } + } } // ---- PRIMITIVE VISITORS ---- // abstract class class PrimitiveVisitor { - constructor() { + constructor() { - } + } } // using this instead of PrimitiveToContext2DConverter for now class PrimitiveToPath2DConverter extends PrimitiveVisitor { - constructor() { + constructor() { - } + } } class PrimitiveToVerticesConverter extends PrimitiveVisitor { - constructor() { + constructor() { - } + } } class PointAtLengthGetter extends PrimitiveVisitor { - constructor() { + constructor() { - } + } } function customShapes(p5, fn) { - // ---- GENERAL CLASSES ---- + // ---- GENERAL CLASSES ---- - /** + /** * @private * A class to describe a custom shape made with `beginShape()`/`endShape()`. * @@ -298,9 +310,9 @@ function customShapes(p5, fn) { * @param {Object} [vertexProperties={position: createVector(0, 0)}] vertex properties and their initial values. */ - p5.Shape = Shape; + p5.Shape = Shape; - /** + /** * @private * A class to describe a contour made with `beginContour()`/`endContour()`. * @@ -330,9 +342,9 @@ function customShapes(p5, fn) { * @class p5.Contour */ - p5.Contour = Contour; + p5.Contour = Contour; - /** + /** * @private * A base class to describe a shape primitive (a basic shape drawn with * `beginShape()`/`endShape()`). @@ -364,9 +376,9 @@ function customShapes(p5, fn) { * @abstract */ - p5.ShapePrimitive = ShapePrimitive; + p5.ShapePrimitive = ShapePrimitive; - /** + /** * @private * A class to describe a vertex (a point on a shape), in 2D or 3D. * @@ -401,258 +413,262 @@ function customShapes(p5, fn) { * @param {Object} [properties={position: createVector(0, 0)}] vertex properties. */ - p5.Vertex = Vertex; + p5.Vertex = Vertex; - // ---- PATH PRIMITIVES ---- + // ---- PATH PRIMITIVES ---- - /** + /** * @private * A class responsible for... - * + * * @class p5.Anchor * @extends p5.ShapePrimitive * @param {p5.Vertex} vertex the vertex to include in the anchor. */ - p5.Anchor = Anchor; + p5.Anchor = Anchor; - /** + /** * @private * A class responsible for... + * + * @class p5.Segment + * @extends p5.ShapePrimitive + * @param {...p5.Vertex} vertices the vertices to include in the segment. */ - p5.Segment = Segment; + p5.Segment = Segment; - /** + /** * @private * A class responsible for... - * + * * @class p5.LineSegment * @param {p5.Vertex} vertex the vertex to include in the anchor. */ - p5.LineSegment = LineSegment; + p5.LineSegment = LineSegment; - /** + /** * @private * A class responsible for... */ - p5.BezierSegment = BezierSegment; + p5.BezierSegment = BezierSegment; - /** + /** * @private * A class responsible for... */ - p5.SplineSegment = SplineSegment; + p5.SplineSegment = SplineSegment; - // ---- ISOLATED PRIMITIVES ---- + // ---- ISOLATED PRIMITIVES ---- - /** + /** * @private * A class responsible for... */ - p5.Point = Point; + p5.Point = Point; - /** + /** * @private * A class responsible for... - * + * * @class p5.Line * @param {...p5.Vertex} vertices the vertices to include in the line. */ - p5.Line = Line; + p5.Line = Line; - /** + /** * @private * A class responsible for... */ - p5.Triangle = Triangle; + p5.Triangle = Triangle; - /** + /** * @private * A class responsible for... */ - p5.Quad = Quad; + p5.Quad = Quad; - // ---- TESSELLATION PRIMITIVES ---- + // ---- TESSELLATION PRIMITIVES ---- - /** + /** * @private * A class responsible for... */ - p5.TriangleFan = TriangleFan; + p5.TriangleFan = TriangleFan; - /** + /** * @private * A class responsible for... */ - p5.TriangleStrip = TriangleStrip; + p5.TriangleStrip = TriangleStrip; - /** + /** * @private * A class responsible for... */ - p5.QuadStrip = QuadStrip; + p5.QuadStrip = QuadStrip; - // ---- PRIMITIVE VISITORS ---- + // ---- PRIMITIVE VISITORS ---- - /** + /** * @private * A class responsible for... */ - p5.PrimitiveVisitor = PrimitiveVisitor; + p5.PrimitiveVisitor = PrimitiveVisitor; - /** + /** * @private * A class responsible for... */ - p5.PrimitiveToPath2DConverter = PrimitiveToPath2DConverter; + p5.PrimitiveToPath2DConverter = PrimitiveToPath2DConverter; - /** + /** * @private * A class responsible for... */ - p5.PrimitiveToVerticesConverter = PrimitiveToVerticesConverter; + p5.PrimitiveToVerticesConverter = PrimitiveToVerticesConverter; - /** + /** * @private * A class responsible for... */ - p5.PointAtLengthGetter = PointAtLengthGetter; + p5.PointAtLengthGetter = PointAtLengthGetter; - // ---- FUNCTIONS ---- + // ---- FUNCTIONS ---- - // Note: Code is commented out for now, to avoid conflicts with the existing implementation. + // Note: Code is commented out for now, to avoid conflicts with the existing implementation. - /** + /** * Top-line description * * More details... */ - // fn.beginContour = function() { - // // example of how to call an existing p5 function: - // // this.background('yellow'); - // }; + // fn.beginContour = function() { + // // example of how to call an existing p5 function: + // // this.background('yellow'); + // }; - /** + /** * Top-line description * * More details... */ - // fn.beginShape = function() { + // fn.beginShape = function() { - // }; + // }; - /** + /** * Top-line description * * More details... */ - // fn.bezierVertex = function() { + // fn.bezierVertex = function() { - // }; + // }; - /** + /** * Top-line description * * More details... */ - // fn.curveVertex = function() { + // fn.curveVertex = function() { - // }; + // }; - /** + /** * Top-line description * * More details... */ - // fn.endContour = function() { + // fn.endContour = function() { - // }; + // }; - /** + /** * Top-line description * * More details... */ - // fn.endShape = function() { + // fn.endShape = function() { - // }; + // }; - /** + /** * Top-line description * * More details... */ - // fn.vertex = function() { + // fn.vertex = function() { - // }; + // }; - /** + /** * Top-line description * * More details... */ - // fn.normal = function() { + // fn.normal = function() { - // }; + // }; - /** + /** * Top-line description * * More details... */ - // fn.vertexProperty = function() { + // fn.vertexProperty = function() { - // }; + // }; } export default customShapes; export { - Shape, - Contour, - ShapePrimitive, - Vertex, - Anchor, - Segment, - LineSegment, - BezierSegment, - SplineSegment, - Point, - Line, - Triangle, - Quad, - TriangleFan, - TriangleStrip, - QuadStrip, - PrimitiveVisitor, - PrimitiveToPath2DConverter, - PrimitiveToVerticesConverter, - PointAtLengthGetter + Shape, + Contour, + ShapePrimitive, + Vertex, + Anchor, + Segment, + LineSegment, + BezierSegment, + SplineSegment, + Point, + Line, + Triangle, + Quad, + TriangleFan, + TriangleStrip, + QuadStrip, + PrimitiveVisitor, + PrimitiveToPath2DConverter, + PrimitiveToVerticesConverter, + PointAtLengthGetter }; if (typeof p5 !== 'undefined') { - customShapes(p5, p5.prototype); + customShapes(p5, p5.prototype); } From 787ca3c61269751a3373bd821e3a904837cb8df2 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sun, 24 Nov 2024 02:30:29 -0700 Subject: [PATCH 36/42] Implement addToShape on ShapePrimitive (base class) --- src/shape/custom_shapes.js | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 2ce04ccd1c..199b7cb5e2 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -92,7 +92,35 @@ class ShapePrimitive { } addToShape(shape) { - throw new Error('Method addToShape() must be implemented.'); + /* + TODO: Test this method once more primitives are implemented. + */ + let lastContour = shape.contours.at(-1); + + if (lastContour.length === 0) { + lastContour.push(this); + return; + } + + // last primitive in shape + let lastPrimitive = lastContour.primitives.at(-1); + let hasSameType = lastPrimitive instanceof this.constructor; + let spareCapacity = lastPrimitive.vertexCapacity - + lastPrimitive.vertexCount; + + // this primitive + let pushableVertices = this.vertices.splice(0, spareCapacity); + let remainingVertices = this.vertices; + + if (hasSameType && spareCapacity > 0) { + lastPrimitive.vertices.push(...pushableVertices); + if (remainingVertices.length > 0) { + lastContour.push(this); + } + } + else { + lastContour.push(this); + } } } From dbf64f6d256c475eb1b0dae4eead985ff3dec634 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sun, 24 Nov 2024 02:51:03 -0700 Subject: [PATCH 37/42] Changed initial value of index in Segment --- src/shape/custom_shapes.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 199b7cb5e2..38b14568b3 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -179,7 +179,7 @@ class Anchor extends ShapePrimitive { // abstract class class Segment extends ShapePrimitive { - index = 0; + index = null; constructor(...vertices) { super(...vertices); @@ -198,7 +198,8 @@ class Segment extends ShapePrimitive { } class LineSegment extends Segment { - constructor(...vertices) { + constructor() { + } } From 65c2d32d6db8ad6afdd783ee84feffc886c9c0a4 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sun, 24 Nov 2024 03:39:49 -0700 Subject: [PATCH 38/42] Add note in Segment docs --- src/shape/custom_shapes.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 38b14568b3..b4ce235868 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -461,6 +461,9 @@ function customShapes(p5, fn) { * @private * A class responsible for... * + * Note: When a segment is added to a shape, it's attached to an anchor or another segment. + * Adding it to another shape may result in unexpected behavior. + * * @class p5.Segment * @extends p5.ShapePrimitive * @param {...p5.Vertex} vertices the vertices to include in the segment. From 20922c7bf2ef6503c08f5ebfa653123ad1116ddc Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sun, 24 Nov 2024 03:51:06 -0700 Subject: [PATCH 39/42] Move index out of base Segment class --- src/shape/custom_shapes.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index b4ce235868..390569fa5a 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -179,8 +179,6 @@ class Anchor extends ShapePrimitive { // abstract class class Segment extends ShapePrimitive { - index = null; - constructor(...vertices) { super(...vertices); if (this.constructor === Segment) { @@ -198,8 +196,11 @@ class Segment extends ShapePrimitive { } class LineSegment extends Segment { - constructor() { + #index = null; + #shape = null; + // TODO: finish implementation + constructor() { } } From 9468d2e9a34e2ba687aaaeae3a9e12c5c1931d79 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Sun, 24 Nov 2024 08:32:16 -0700 Subject: [PATCH 40/42] Add at() method on Shape, revise Segment & Anchor, implement LineSegment, plus small changes --- src/shape/custom_shapes.js | 96 ++++++++++++++++++++++++++++---------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index 390569fa5a..c0461516b2 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -13,6 +13,7 @@ import * as constants from '../core/constants.js'; class Shape { vertexProperties; + contours = []; constructor(vertexProperties) { this.vertexProperties = vertexProperties; @@ -26,6 +27,24 @@ class Shape { } } + // TODO: TEST, MAYBE REFACTOR + at(contoursIndex, primitivesIndex, verticesIndex) { + let contour; + let primitive; + + contour = this.contours.at(contoursIndex); + + switch(arguments.length) { + case 1: + return contour; + case 2: + return contour.primitives.at(primitivesIndex); + case 3: + primitive = contour.primitives.at(primitivesIndex); + return primitive.vertices.at(verticesIndex); + } + } + reset() { // TODO: remove existing vertices } @@ -76,11 +95,16 @@ class ShapePrimitive { if (this.constructor === ShapePrimitive) { throw new Error('ShapePrimitive is an abstract class: it cannot be instantiated.'); } - this.vertices = vertices; + if (vertices.length > 0) { + this.vertices = vertices; + } + else { + throw new Error('At least one vertex must be passed to the constructor.'); + } } get vertexCount() { - throw new Error('Getter vertexCount must be implemented.'); + return this.vertices.length; } get vertexCapacity() { @@ -93,9 +117,11 @@ class ShapePrimitive { addToShape(shape) { /* - TODO: Test this method once more primitives are implemented. + TODO: + Test this method once more primitives are implemented. + Test segments separately (Segment adds an extra step to this method). */ - let lastContour = shape.contours.at(-1); + let lastContour = shape.at(-1); if (lastContour.length === 0) { lastContour.push(this); @@ -103,7 +129,7 @@ class ShapePrimitive { } // last primitive in shape - let lastPrimitive = lastContour.primitives.at(-1); + let lastPrimitive = shape.at(-1, -1); let hasSameType = lastPrimitive instanceof this.constructor; let spareCapacity = lastPrimitive.vertexCapacity - lastPrimitive.vertexCount; @@ -147,15 +173,10 @@ class Vertex { // ---- PATH PRIMITIVES ---- class Anchor extends ShapePrimitive { - #vertexCapacity; + #vertexCapacity = 1; constructor(...vertices) { super(...vertices); - this.#vertexCapacity = 1; - } - - get vertexCount() { - return this.vertices.length; } get vertexCapacity() { @@ -166,19 +187,17 @@ class Anchor extends ShapePrimitive { visitor.visitAnchor(this); } - addToShape(shape) { - let lastContour = shape.contours.at(-1); - if (lastContour.kind === constants.EMPTY_PATH) { - lastContour.primitives.push(this); - } - else { - throw new Error('Anchor can only be added to an empty path contour.'); - } + getEndVertex() { + return this.vertices[0]; } } // abstract class class Segment extends ShapePrimitive { + _primitivesIndex = null; + _contoursIndex = null; + _shape = null; + constructor(...vertices) { super(...vertices); if (this.constructor === Segment) { @@ -186,8 +205,26 @@ class Segment extends ShapePrimitive { } } + addToShape(shape) { + super.addToShape(shape); + + // if primitive itself was added + // (i.e. its individual vertices weren't all added to an existing primitive) + // give it a reference to the shape and store its location within the shape + if (this.vertices > 0) { + let lastContour = shape.at(-1); + this._primitivesIndex = lastContour.primitives.length - 1; + this._contoursIndex = shape.contours.length - 1; + this._shape = shape; + } + } + getStartVertex() { - throw new Error('Method getStartVertex() must be implemented.'); + let previousPrimitive = this._shape.at( + this._contoursIndex, + this._primitivesIndex - 1 + ); + return previousPrimitive.getEndVertex(); } getEndVertex() { @@ -196,11 +233,22 @@ class Segment extends ShapePrimitive { } class LineSegment extends Segment { - #index = null; - #shape = null; + #vertexCapacity = 1; - // TODO: finish implementation - constructor() { + constructor(...vertices) { + super(...vertices); + } + + get vertexCapacity() { + return this.#vertexCapacity; + } + + accept(visitor) { + visitor.visitLineSegment(this); + } + + getEndVertex() { + return this.vertices[0]; } } From 6adb65a68955e491ca1de4dea11e322a04d21f0f Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Tue, 26 Nov 2024 03:22:17 -0700 Subject: [PATCH 41/42] Implement all custom shape constants as Symbols --- src/core/constants.js | 60 +++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/core/constants.js b/src/core/constants.js index 962375e63c..7664b796dd 100644 --- a/src/core/constants.js +++ b/src/core/constants.js @@ -696,12 +696,6 @@ export const BOTTOM = 'bottom'; * @final */ export const BASELINE = 'alphabetic'; -/** - * @typedef {unique symbol} PATH - * @property {PATH} PATH - * @final - */ -export const PATH = Symbol('path'); /** * @typedef {unique symbol} EMPTY_PATH * @property {EMPTY_PATH} EMPTY_PATH @@ -709,77 +703,83 @@ export const PATH = Symbol('path'); */ export const EMPTY_PATH = Symbol('empty_path'); /** - * @typedef {0x0000} POINTS + * @typedef {unique symbol} PATH + * @property {PATH} PATH + * @final + */ +export const PATH = Symbol('path'); +/** + * @typedef {unique symbol} POINTS * @property {POINTS} POINTS * @final */ -export const POINTS = 0x0000; +export const POINTS = Symbol('points'); /** - * @typedef {0x0001} LINES + * @typedef {unique symbol} LINES * @property {LINES} LINES * @final */ -export const LINES = 0x0001; +export const LINES = Symbol('lines'); /** - * @property {0x0003} LINE_STRIP + * @typedef {unique symbol} LINE_STRIP * @property {LINE_STRIP} LINE_STRIP * @final */ -export const LINE_STRIP = 0x0003; +export const LINE_STRIP = Symbol('line_strip'); /** - * @typedef {0x0002} LINE_LOOP + * @typedef {unique symbol} LINE_LOOP * @property {LINE_LOOP} LINE_LOOP * @final */ -export const LINE_LOOP = 0x0002; +export const LINE_LOOP = Symbol('line_loop'); /** - * @typedef {0x0004} TRIANGLES + * @typedef {unique symbol} TRIANGLES * @property {TRIANGLES} TRIANGLES * @final */ -export const TRIANGLES = 0x0004; +export const TRIANGLES = Symbol('triangles'); /** - * @typedef {0x0006} TRIANGLE_FAN + * @typedef {unique symbol} TRIANGLE_FAN * @property {TRIANGLE_FAN} TRIANGLE_FAN * @final */ -export const TRIANGLE_FAN = 0x0006; +export const TRIANGLE_FAN = Symbol('triangle_fan'); /** - * @typedef {0x0005} TRIANGLE_STRIP + * @typedef {unique symbol} TRIANGLE_STRIP * @property {TRIANGLE_STRIP} TRIANGLE_STRIP * @final */ -export const TRIANGLE_STRIP = 0x0005; +export const TRIANGLE_STRIP = Symbol('triangle_strip'); /** - * @typedef {'quads'} QUADS + * @typedef {unique symbol} QUADS * @property {QUADS} QUADS * @final */ -export const QUADS = 'quads'; +export const QUADS = Symbol('quads'); /** - * @typedef {'quad_strip'} QUAD_STRIP + * @typedef {unique symbol} QUAD_STRIP * @property {QUAD_STRIP} QUAD_STRIP * @final */ -export const QUAD_STRIP = 'quad_strip'; +export const QUAD_STRIP = Symbol('quad_strip'); /** - * @typedef {'tess'} TESS + * @typedef {unique symbol} TESS * @property {TESS} TESS * @final */ -export const TESS = 'tess'; +export const TESS = Symbol('tess'); /** - * @typedef {'close'} CLOSE + * @typedef {unique symbol} CLOSE * @property {CLOSE} CLOSE * @final */ -export const CLOSE = 'close'; +export const CLOSE = Symbol('close'); /** - * @typedef {'open'} OPEN + * @typedef {unique symbol} OPEN * @property {OPEN} OPEN * @final */ -export const OPEN = 'open'; +export const OPEN = Symbol('open'); /** * @typedef {'chord'} CHORD * @property {CHORD} CHORD From ee77b1cc47e344cfc3ca8c9fe9c47e5677c559b5 Mon Sep 17 00:00:00 2001 From: Greg Stanton Date: Tue, 26 Nov 2024 03:56:12 -0700 Subject: [PATCH 42/42] Implement PrimitiveShapeCreators --- src/shape/custom_shapes.js | 101 ++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 17 deletions(-) diff --git a/src/shape/custom_shapes.js b/src/shape/custom_shapes.js index c0461516b2..9492180b21 100644 --- a/src/shape/custom_shapes.js +++ b/src/shape/custom_shapes.js @@ -27,7 +27,17 @@ class Shape { } } - // TODO: TEST, MAYBE REFACTOR + // TODO for at() method: + + // RENAME? + // -at() indicates it works like Array.prototype.at(), e.g. with negative indices + // -get() may work better if we want to add a corresponding set() method + // -a set() method could maybe check for problematic usage (e.g. inserting a Triangle into a PATH) + // -renaming or removing would necessitate changes at call sites (it's already in use) + + // REFACTOR? + + // TEST at(contoursIndex, primitivesIndex, verticesIndex) { let contour; let primitive; @@ -45,6 +55,7 @@ class Shape { } } + // maybe call this clear() for consistency with PrimitiveShapeCreators.clear()? reset() { // TODO: remove existing vertices } @@ -252,63 +263,119 @@ class LineSegment extends Segment { } } -class BezierSegment extends Segment { - constructor() { +// TOOO: Finish implementing remaining primitive classes +class BezierSegment extends Segment { + constructor(...vertices) { + super(...vertices); } } // consider type and end modes -- see #6766) // may want to use separate classes, but maybe not class SplineSegment extends Segment { - constructor() { - + constructor(...vertices) { + super(...vertices); } } // ---- ISOLATED PRIMITIVES ---- class Point extends ShapePrimitive { - constructor() { - + constructor(...vertices) { + super(...vertices); } } class Line extends ShapePrimitive { - constructor() { - + constructor(...vertices) { + super(...vertices); } } class Triangle extends ShapePrimitive { - constructor() { - + constructor(...vertices) { + super(...vertices); } } class Quad extends ShapePrimitive { - constructor() { - + constructor(...vertices) { + super(...vertices); } } // ---- TESSELLATION PRIMITIVES ---- class TriangleFan extends ShapePrimitive { - constructor() { - + constructor(...vertices) { + super(...vertices); } } class TriangleStrip extends ShapePrimitive { - constructor() { - + constructor(...vertices) { + super(...vertices); } } class QuadStrip extends ShapePrimitive { + constructor(...vertices) { + super(...vertices); + } +} + +// ---- PRIMITIVE SHAPE CREATORS ---- + +class PrimitiveShapeCreators { constructor() { + let creators = new Map(); + + // Store Symbols as strings for use in Map keys + const EMPTY_PATH = constants.EMPTY_PATH.description; + const PATH = constants.PATH.description; + const POINTS = constants.POINTS.description; + const LINES = constants.LINES.description; + const TRIANGLES = constants.TRIANGLES.description; + const QUADS = constants.QUADS.description; + const TRIANGLE_FAN = constants.TRIANGLE_FAN.description; + const TRIANGLE_STRIP = constants.TRIANGLE_STRIP.description; + const QUAD_STRIP = constants.QUAD_STRIP.description; + + // vertex + creators.set(`vertex-${EMPTY_PATH}`, (...vertices) => new Anchor(...vertices)); + creators.set(`vertex-${PATH}`, (...vertices) => new LineSegment(...vertices)); + creators.set(`vertex-${POINTS}`, (...vertices) => new Point(...vertices)); + creators.set(`vertex-${LINES}`, (...vertices) => new Line(...vertices)); + creators.set(`vertex-${TRIANGLES}`, (...vertices) => new Triangle(...vertices)); + creators.set(`vertex-${QUADS}`, (...vertices) => new Quad(...vertices)); + creators.set(`vertex-${TRIANGLE_FAN}`, (...vertices) => new TriangleFan(...vertices)); + creators.set(`vertex-${TRIANGLE_STRIP}`, (...vertices) => new TriangleStrip(...vertices)); + creators.set(`vertex-${QUAD_STRIP}`, (...vertices) => new QuadStrip(...vertices)); + + // bezierVertex + creators.set(`bezierVertex-${EMPTY_PATH}`, (...vertices) => new Anchor(...vertices)); + creators.set(`bezierVertex-${PATH}`, (...vertices) => new BezierSegment(...vertices)); + + // splineVertex + creators.set(`splineVertex-${EMPTY_PATH}`, (...vertices) => new Anchor(...vertices)); + creators.set(`splineVertex-${PATH}`, (...vertices) => new SplineSegment(...vertices)); + + this.creators = creators; + } + + get(vertexKind, shapeKind) { + const key = `${vertexKind}-${shapeKind.description}`; + return this.creators.get(key); + } + + set(vertexKind, shapeKind, creator) { + const key = `${vertexKind}-${shapeKind.description}`; + this.creators.set(key, creator); + } + clear() { + this.creators.clear(); } }