diff --git a/src/core/p5.Renderer2D.js b/src/core/p5.Renderer2D.js
index 2a9690fcdf..7d091c9d1d 100644
--- a/src/core/p5.Renderer2D.js
+++ b/src/core/p5.Renderer2D.js
@@ -867,266 +867,33 @@ class Renderer2D extends p5.Renderer {
endShape(
mode,
- vertices,
isCurve,
isBezier,
isQuadratic,
isContour,
- shapeKind
+ shapeKind,
+ allVertsObj,
+ currentContour,
+ closeShape
) {
- if (vertices.length === 0) {
- return this;
- }
- if (!this._doStroke && !this._doFill) {
- return this;
- }
- const closeShape = mode === constants.CLOSE;
- let v;
- if (closeShape && !isContour) {
- vertices.push(vertices[0]);
- }
- let i, j;
- const numVerts = vertices.length;
- if (isCurve && shapeKind === null) {
- if (numVerts > 3) {
- const b = [],
- s = 1 - this._curveTightness;
- if (!this._clipping) this.drawingContext.beginPath();
- this.drawingContext.moveTo(vertices[1][0], vertices[1][1]);
- for (i = 1; i + 2 < numVerts; i++) {
- v = vertices[i];
- b[0] = [v[0], v[1]];
- b[1] = [
- v[0] + (s * vertices[i + 1][0] - s * vertices[i - 1][0]) / 6,
- v[1] + (s * vertices[i + 1][1] - s * vertices[i - 1][1]) / 6
- ];
- b[2] = [
- vertices[i + 1][0] +
- (s * vertices[i][0] - s * vertices[i + 2][0]) / 6,
- vertices[i + 1][1] +
- (s * vertices[i][1] - s * vertices[i + 2][1]) / 6
- ];
- b[3] = [vertices[i + 1][0], vertices[i + 1][1]];
- this.drawingContext.bezierCurveTo(
- b[1][0],
- b[1][1],
- b[2][0],
- b[2][1],
- b[3][0],
- b[3][1]
- );
- }
- if (closeShape) {
- this.drawingContext.lineTo(vertices[i + 1][0], vertices[i + 1][1]);
- }
- this._doFillStrokeClose(closeShape);
- }
- } else if (
- isBezier &&
- shapeKind === null
- ) {
- if (!this._clipping) this.drawingContext.beginPath();
- for (i = 0; i < numVerts; i++) {
- if (vertices[i].isVert) {
- if (vertices[i].moveTo) {
- this.drawingContext.moveTo(vertices[i][0], vertices[i][1]);
- } else {
- this.drawingContext.lineTo(vertices[i][0], vertices[i][1]);
- }
- } else {
- this.drawingContext.bezierCurveTo(
- vertices[i][0],
- vertices[i][1],
- vertices[i][2],
- vertices[i][3],
- vertices[i][4],
- vertices[i][5]
- );
- }
- }
- this._doFillStrokeClose(closeShape);
- } else if (
- isQuadratic &&
- shapeKind === null
- ) {
- if (!this._clipping) this.drawingContext.beginPath();
- for (i = 0; i < numVerts; i++) {
- if (vertices[i].isVert) {
- if (vertices[i].moveTo) {
- this.drawingContext.moveTo(vertices[i][0], vertices[i][1]);
- } else {
- this.drawingContext.lineTo(vertices[i][0], vertices[i][1]);
- }
- } else {
- this.drawingContext.quadraticCurveTo(
- vertices[i][0],
- vertices[i][1],
- vertices[i][2],
- vertices[i][3]
- );
- }
- }
- this._doFillStrokeClose(closeShape);
- } else {
- if (shapeKind === constants.POINTS) {
- for (i = 0; i < numVerts; i++) {
- v = vertices[i];
- if (this._doStroke) {
- this._pInst.stroke(v[6]);
- }
- this._pInst.point(v[0], v[1]);
- }
- } else if (shapeKind === constants.LINES) {
- for (i = 0; i + 1 < numVerts; i += 2) {
- v = vertices[i];
- if (this._doStroke) {
- this._pInst.stroke(vertices[i + 1][6]);
- }
- this._pInst.line(v[0], v[1], vertices[i + 1][0], vertices[i + 1][1]);
- }
- } else if (shapeKind === constants.TRIANGLES) {
- for (i = 0; i + 2 < numVerts; i += 3) {
- v = vertices[i];
- if (!this._clipping) this.drawingContext.beginPath();
- this.drawingContext.moveTo(v[0], v[1]);
- 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._doFill) {
- this._pInst.fill(vertices[i + 2][5]);
- this.drawingContext.fill();
- }
- if (!this._clipping && this._doStroke) {
- this._pInst.stroke(vertices[i + 2][6]);
- this.drawingContext.stroke();
- }
- }
- } else if (shapeKind === constants.TRIANGLE_STRIP) {
- for (i = 0; i + 1 < numVerts; i++) {
- v = vertices[i];
- 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._doStroke) {
- this._pInst.stroke(vertices[i + 1][6]);
- }
- if (!this._clipping && this._doFill) {
- 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._doStroke) {
- this._pInst.stroke(vertices[i + 2][6]);
- }
- if (!this._clipping && this._doFill) {
- this._pInst.fill(vertices[i + 2][5]);
- }
- }
- this._doFillStrokeClose(closeShape);
- }
- } else if (shapeKind === constants.TRIANGLE_FAN) {
- if (numVerts > 2) {
- // For performance reasons, try to batch as many of the
- // fill and stroke calls as possible.
- if (!this._clipping) this.drawingContext.beginPath();
- for (i = 2; i < numVerts; i++) {
- v = vertices[i];
- this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
- this.drawingContext.lineTo(vertices[i - 1][0], vertices[i - 1][1]);
- this.drawingContext.lineTo(v[0], v[1]);
- this.drawingContext.lineTo(vertices[0][0], vertices[0][1]);
- // If the next colour is going to be different, stroke / fill now
- if (i < numVerts - 1) {
- if (
- (this._doFill && v[5] !== vertices[i + 1][5]) ||
- (this._doStroke && v[6] !== vertices[i + 1][6])
- ) {
- if (!this._clipping && this._doFill) {
- this._pInst.fill(v[5]);
- this.drawingContext.fill();
- this._pInst.fill(vertices[i + 1][5]);
- }
- if (!this._clipping && this._doStroke) {
- this._pInst.stroke(v[6]);
- this.drawingContext.stroke();
- this._pInst.stroke(vertices[i + 1][6]);
- }
- this.drawingContext.closePath();
- if (!this._clipping) this.drawingContext.beginPath(); // Begin the next one
- }
- }
- }
- this._doFillStrokeClose(closeShape);
- }
- } else if (shapeKind === constants.QUADS) {
- for (i = 0; i + 3 < numVerts; i += 4) {
- v = vertices[i];
- if (!this._clipping) this.drawingContext.beginPath();
- this.drawingContext.moveTo(v[0], v[1]);
- for (j = 1; j < 4; j++) {
- this.drawingContext.lineTo(vertices[i + j][0], vertices[i + j][1]);
- }
- this.drawingContext.lineTo(v[0], v[1]);
- if (!this._clipping && this._doFill) {
- this._pInst.fill(vertices[i + 3][5]);
- }
- if (!this._clipping && this._doStroke) {
- this._pInst.stroke(vertices[i + 3][6]);
- }
- this._doFillStrokeClose(closeShape);
- }
- } else if (shapeKind === constants.QUAD_STRIP) {
- if (numVerts > 3) {
- for (i = 0; i + 1 < numVerts; i += 2) {
- v = vertices[i];
- if (!this._clipping) this.drawingContext.beginPath();
- if (i + 3 < numVerts) {
- this.drawingContext.moveTo(
- vertices[i + 2][0], vertices[i + 2][1]);
- this.drawingContext.lineTo(v[0], v[1]);
- this.drawingContext.lineTo(
- vertices[i + 1][0], vertices[i + 1][1]);
- this.drawingContext.lineTo(
- vertices[i + 3][0], vertices[i + 3][1]);
- if (!this._clipping && this._doFill) {
- this._pInst.fill(vertices[i + 3][5]);
- }
- if (!this._clipping && this._doStroke) {
- this._pInst.stroke(vertices[i + 3][6]);
- }
- } else {
- this.drawingContext.moveTo(v[0], v[1]);
- this.drawingContext.lineTo(
- vertices[i + 1][0], vertices[i + 1][1]);
- }
- this._doFillStrokeClose(closeShape);
- }
- }
- } else {
- if (!this._clipping) this.drawingContext.beginPath();
- this.drawingContext.moveTo(vertices[0][0], vertices[0][1]);
- for (i = 1; i < numVerts; i++) {
- v = vertices[i];
- if (v.isVert) {
- if (v.moveTo) {
- if (closeShape) this.drawingContext.closePath();
- this.drawingContext.moveTo(v[0], v[1]);
- } else {
- this.drawingContext.lineTo(v[0], v[1]);
- }
- }
- }
- this._doFillStrokeClose(closeShape);
- }
+ let initialVertex = allVertsObj[0];
+ allVertsObj.currentCoordinates = initialVertex.coordinates;
+ this.drawingContext.moveTo(...allVertsObj.currentCoordinates);
+
+ for (const vert of allVertsObj) {
+ vert.addToCanvasPath(this.drawingContext);
+ allVertsObj.currentCoordinates = vert.coordinates;
}
+ this._doFillStrokeClose(closeShape);
+
isCurve = false;
isBezier = false;
isQuadratic = false;
isContour = false;
+
if (closeShape) {
- vertices.pop();
+ currentContour.segmentss.pop();
}
-
return this;
}
//////////////////////////////////////////////
diff --git a/src/core/shape/shape.js b/src/core/shape/shape.js
new file mode 100644
index 0000000000..a0bb3ef66b
--- /dev/null
+++ b/src/core/shape/shape.js
@@ -0,0 +1,158 @@
+export class Shape {
+ constructor() {
+ this.contours = [];
+ }
+
+ addContour(c) {
+ this.contours.push(c);
+ }
+
+ get contourss() {
+ return this.contours;
+ }
+}
+
+export class Contour {
+ constructor(firstVertex = null) {
+ this.firstVertex = firstVertex;
+ //contourVertices/Segments
+ this.segments = [];
+ }
+
+ get segmentss() {
+ return this.segments;
+ }
+
+ get firstSegment() {
+ return this.segments[0];
+ }
+
+ addSegment(segment) {
+ this.segments.push(segment);
+ }
+}
+
+export class ContourSegment {
+ constructor() {
+ // this.index = index;
+ }
+
+ getStartVertex() {
+ // logic to retrieve start vertex
+ }
+
+ getEndVertex() {
+ // logic to retrieve end vertex
+ }
+
+ accept(visitor) {
+ // logic to accept a visitor
+ }
+}
+
+export class ContourSegment2D extends ContourSegment {
+ constructor() {
+ super();
+ this.vertices = [];
+ }
+ addVertex(vertex) {
+ this.vertices.push(vertex);
+ }
+ get verticess() {
+ return this.vertices;
+ }
+}
+
+export class Vertex {
+ constructor(vert) {
+ this.data = vert;
+ }
+
+ get coordinates() {
+ return [this.data[0], this.data[1]];
+ }
+
+ addToCanvasPath(drawingContext) {
+ drawingContext.lineTo(this.data[0], this.data[1]);
+ }
+}
+
+
+export class BezierVertex {
+ constructor(vert) {
+ this.data = vert;
+ }
+
+ get coordinates() {
+ return this.data.slice(-2);
+ }
+
+ addToCanvasPath(drawingContext) {
+ drawingContext.bezierCurveTo(...this.data);
+ }
+}
+
+export class QuadraticVertex {
+ constructor(vert) {
+ this.data = vert;
+ }
+
+ get coordinates() {
+ return this.data.slice(-2);
+ }
+
+ addToCanvasPath(drawingContext) {
+ drawingContext.quadraticCurveTo(...this.data);
+ }
+}
+
+
+export class CurveVertex {
+ constructor(x, y) {
+ this.data = [x, y];
+ this.type = 'curveVertex';
+ }
+
+ get coordinates() {
+ let isComplete = this.data.length >= 8;
+ let secondToLastPoint = this.data.slice(-4, -2);
+
+ if(isComplete) {
+ return secondToLastPoint;
+ }
+ else {
+ return null;
+ }
+ }
+
+ addToCanvasPath(drawingContext) {
+ let bezierArrays = catmullRomToBezier(this.data, 0);
+ for (const arr of bezierArrays) {
+ drawingContext.bezierCurveTo(...arr);
+ }
+ }
+}
+
+function catmullRomToBezier(vertices, tightness) {
+ let X0, Y0, X1, Y1, X2, Y2, X3, Y3;
+ let s = 1 - tightness;
+ let bezX1, bezY1, bezX2, bezY2, bezX3, bezY3;
+ let bezArrays = [];
+
+ for (let i = 2; i + 4 < vertices.length; i+= 2) {
+ [X0, Y0, X1, Y1, X2, Y2, X3, Y3] = vertices.slice(i - 2, i + 6);
+
+ bezX1 = X1 + (s * X2 - s * X0) / 6;
+ bezY1 = Y1 + (s * Y2 - s * Y0) / 6;
+
+ bezX2 = X2 + (s * X1 - s * X3) / 6;
+ bezY2 = Y2 + (s * Y1 - s * Y3) / 6;
+
+ bezX3 = X2;
+ bezY3 = Y2;
+
+ bezArrays.push([bezX1, bezY1, bezX2, bezY2, bezX3, bezY3]);
+ }
+ return bezArrays;
+}
+
diff --git a/src/core/shape/vertex.js b/src/core/shape/vertex.js
index b08d5830cf..f5df141920 100644
--- a/src/core/shape/vertex.js
+++ b/src/core/shape/vertex.js
@@ -8,15 +8,21 @@
import p5 from '../main';
import * as constants from '../constants';
+import {
+ ContourSegment2D,
+ Shape,
+ Vertex,
+ Contour,
+ BezierVertex, QuadraticVertex, CurveVertex
+} from './shape';
+
+let shape = null;
let shapeKind = null;
-let vertices = [];
-let contourVertices = [];
let isBezier = false;
let isCurve = false;
let isQuadratic = false;
let isContour = false;
let isFirstContour = true;
-
/**
* Use the beginContour() and
* endContour() functions to create negative shapes
@@ -58,11 +64,11 @@ let isFirstContour = true;
* @alt
* white rect and smaller grey rect with red outlines in center of canvas.
*/
-p5.prototype.beginContour = function() {
+p5.prototype.beginContour = function () {
if (this._renderer.isP3D) {
this._renderer.beginContour();
} else {
- contourVertices = [];
+ shape.addContour(new Contour());
isContour = true;
}
return this;
@@ -269,7 +275,7 @@ p5.prototype.beginContour = function() {
* 3 side-by-side white rectangles center rect is smaller in mid-right canvas.
* Thick white l-shape with black outline mid-top-left of canvas.
*/
-p5.prototype.beginShape = function(kind) {
+p5.prototype.beginShape = function (kind) {
p5._validateParameters('beginShape', arguments);
if (this._renderer.isP3D) {
this._renderer.beginShape(...arguments);
@@ -287,9 +293,9 @@ p5.prototype.beginShape = function(kind) {
} else {
shapeKind = null;
}
-
- vertices = [];
- contourVertices = [];
+ shape = new Shape();
+ // initialise first contour
+ shape.addContour(new Contour());
}
return this;
};
@@ -389,28 +395,26 @@ p5.prototype.beginShape = function(kind) {
* @param {Number} z4 z-coordinate for the anchor point (for WebGL mode)
* @chainable
*/
-p5.prototype.bezierVertex = function(...args) {
+p5.prototype.bezierVertex = function (...args) {
p5._validateParameters('bezierVertex', args);
if (this._renderer.isP3D) {
this._renderer.bezierVertex(...args);
} else {
- if (vertices.length === 0) {
+ let currentContour = shape.contourss[shape.contourss.length - 1];
+ if (currentContour.segmentss.length === 0) {
p5._friendlyError(
'vertex() must be used once before calling bezierVertex()',
'bezierVertex'
);
} else {
- isBezier = true;
const vert = [];
for (let i = 0; i < args.length; i++) {
vert[i] = args[i];
}
- vert.isVert = false;
- if (isContour) {
- contourVertices.push(vert);
- } else {
- vertices.push(vert);
- }
+
+ let cs = new ContourSegment2D();
+ cs.addVertex(new BezierVertex(vert));
+ currentContour.addSegment(cs);
}
}
return this;
@@ -514,13 +518,33 @@ p5.prototype.bezierVertex = function(...args) {
* @alt
* Upside-down u-shape line, mid canvas with the same shape in positive z-axis.
*/
-p5.prototype.curveVertex = function(...args) {
+p5.prototype.curveVertex = function (...args) {
p5._validateParameters('curveVertex', args);
if (this._renderer.isP3D) {
this._renderer.curveVertex(...args);
} else {
- isCurve = true;
- this.vertex(args[0], args[1]);
+ let allVertObj = [];
+
+ shape.contourss.forEach(c => {
+ c.segmentss.forEach(s => {
+ s.verticess.forEach(v => {
+ allVertObj.push(v);
+ });
+ });
+ });
+
+ let lastVertex = allVertObj.at(-1);
+ if (allVertObj.length === 0 || lastVertex.type === undefined) {
+ // first curveVertex
+ let cs = new ContourSegment2D();
+ cs.addVertex(new CurveVertex(args[0], args[1]));
+ let currentContour = shape.contourss[shape.contourss.length - 1];
+ currentContour.addSegment(cs);
+ } else {
+ lastVertex.data.push(args[0], args[1]);
+ }
+
+
}
return this;
};
@@ -566,25 +590,22 @@ p5.prototype.curveVertex = function(...args) {
* @alt
* white rect and smaller grey rect with red outlines in center of canvas.
*/
-p5.prototype.endContour = function() {
+p5.prototype.endContour = function () {
if (this._renderer.isP3D) {
return this;
}
- const vert = contourVertices[0].slice(); // copy all data
- vert.isVert = contourVertices[0].isVert;
- vert.moveTo = false;
- contourVertices.push(vert);
+ let currentContour = shape.contourss[shape.contourss.length - 1];
+ const vert = currentContour.firstSegment.verticess[0].coordinates.slice();
+ let cs = new ContourSegment2D();
+ cs.addVertex(new Vertex(vert));
+ currentContour.addSegment(cs);
- // prevent stray lines with multiple contours
if (isFirstContour) {
- vertices.push(vertices[0]);
+ // pushing first contour's first segment to the end
+ shape.contourss[0].addSegment(shape.contourss[0].segmentss[0]);
isFirstContour = false;
}
-
- for (let i = 0; i < contourVertices.length; i++) {
- vertices.push(contourVertices[i]);
- }
return this;
};
@@ -707,7 +728,7 @@ p5.prototype.endContour = function() {
* @alt
* Triangle line shape with smallest interior angle on bottom and upside-down L.
*/
-p5.prototype.endShape = function(mode, count = 1) {
+p5.prototype.endShape = function (mode, count = 1) {
p5._validateParameters('endShape', arguments);
if (count < 1) {
console.log('🌸 p5.js says: You can not have less than one instance');
@@ -728,7 +749,10 @@ p5.prototype.endShape = function(mode, count = 1) {
if (count !== 1) {
console.log('🌸 p5.js says: Instancing is only supported in WebGL2 mode');
}
- if (vertices.length === 0) {
+
+ let currentContour = shape.contourss[shape.contourss.length - 1];
+
+ if (currentContour.segmentss.length === 0) {
return this;
}
if (!this._renderer._doStroke && !this._renderer._doFill) {
@@ -739,19 +763,34 @@ p5.prototype.endShape = function(mode, count = 1) {
// if the shape is closed, the first element is also the last element
if (closeShape && !isContour) {
- vertices.push(vertices[0]);
+ currentContour.addSegment(currentContour.firstSegment);
}
+ // all verts from shape
+ let allVertObj = [];
+ shape.contourss.forEach(c => {
+ c.segmentss.forEach(s => {
+ s.verticess.forEach(v => {
+ allVertObj.push(v);
+ });
+ });
+ });
+
this._renderer.endShape(
mode,
- vertices,
isCurve,
isBezier,
isQuadratic,
isContour,
- shapeKind
+ shapeKind,
+ allVertObj,
+ currentContour,
+ closeShape
);
+ // Reset shape
+ shape.contourss.length = 0;
+
// Reset some settings
isCurve = false;
isBezier = false;
@@ -763,7 +802,7 @@ p5.prototype.endShape = function(mode, count = 1) {
// We must remove it again to prevent the list of vertices from growing
// over successive calls to endShape(CLOSE)
if (closeShape) {
- vertices.pop();
+ currentContour.segmentss.pop();
}
}
return this;
@@ -887,7 +926,7 @@ p5.prototype.endShape = function(mode, count = 1) {
* @alt
* backwards s-shaped black line with the same s-shaped line in positive z-axis.
*/
-p5.prototype.quadraticVertex = function(...args) {
+p5.prototype.quadraticVertex = function (...args) {
p5._validateParameters('quadraticVertex', args);
if (this._renderer.isP3D) {
this._renderer.quadraticVertex(...args);
@@ -905,18 +944,17 @@ p5.prototype.quadraticVertex = function(...args) {
return this;
}
- if (vertices.length > 0) {
- isQuadratic = true;
+ let currentContour = shape.contourss[shape.contourss.length - 1];
+
+ if (currentContour.segmentss.length > 0) {
const vert = [];
for (let i = 0; i < args.length; i++) {
vert[i] = args[i];
}
- vert.isVert = false;
- if (isContour) {
- contourVertices.push(vert);
- } else {
- vertices.push(vert);
- }
+
+ let cs = new ContourSegment2D();
+ cs.addVertex(new QuadraticVertex(vert));
+ currentContour.addSegment(cs);
} else {
p5._friendlyError(
'vertex() must be used once before calling quadraticVertex()',
@@ -1075,12 +1113,11 @@ p5.prototype.quadraticVertex = function(...args) {
* @param {Number} [v] the vertex's texture v-coordinate
* @chainable
*/
-p5.prototype.vertex = function(x, y, moveTo, u, v) {
+p5.prototype.vertex = function (x, y, moveTo, u, v) {
if (this._renderer.isP3D) {
this._renderer.vertex(...arguments);
} else {
const vert = [];
- vert.isVert = true;
vert[0] = x;
vert[1] = y;
vert[2] = 0;
@@ -1088,18 +1125,12 @@ p5.prototype.vertex = function(x, y, moveTo, u, v) {
vert[4] = 0;
vert[5] = this._renderer._getFill();
vert[6] = this._renderer._getStroke();
+ let currentContour = shape.contourss[shape.contourss.length - 1];
- if (moveTo) {
- vert.moveTo = moveTo;
- }
- if (isContour) {
- if (contourVertices.length === 0) {
- vert.moveTo = true;
- }
- contourVertices.push(vert);
- } else {
- vertices.push(vert);
- }
+ let cs = new ContourSegment2D();
+ cs.addVertex(new Vertex(vert));
+
+ currentContour.addSegment(cs);
}
return this;
};
@@ -1148,7 +1179,7 @@ p5.prototype.vertex = function(x, y, moveTo, u, v) {
* @param {Number} z The z component of the vertex normal.
* @chainable
*/
-p5.prototype.normal = function(x, y, z) {
+p5.prototype.normal = function (x, y, z) {
this._assert3d('normal');
p5._validateParameters('normal', arguments);
this._renderer.normal(...arguments);