Skip to content

Add ShapePrimitive support for arcs and ellipses#8617

Open
VANSH3104 wants to merge 1 commit intoprocessing:dev-2.0from
VANSH3104:primitiveshapes
Open

Add ShapePrimitive support for arcs and ellipses#8617
VANSH3104 wants to merge 1 commit intoprocessing:dev-2.0from
VANSH3104:primitiveshapes

Conversation

@VANSH3104
Copy link
Contributor

@VANSH3104 VANSH3104 commented Mar 6, 2026

Resolves #8277

Changes:

  • Add ArcPrimitive and EllipsePrimitive classes extending ShapePrimitive
  • Implement visitor methods in PrimitiveToPath2DVisitor for 2D rendering
  • Add vertex tessellation for WebGL in PrimitiveToVerticesVisitor
  • Update arc() and ellipse() to use new ShapePrimitive system

PR Checklist

@VANSH3104
Copy link
Contributor Author

VANSH3104 commented Mar 6, 2026

@ksen0, @davepagurek could you please review this and suggest any changes?

Copy link
Contributor

@davepagurek davepagurek left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on! I left a few comments about how to make these primitives work more like the others. We should also add some unit visual in both 2D and WebGL mode of these methods if we don't already have some to make sure things keep working.


const primitive = new EllipsePrimitive(x, y, w, h);
const shape = { accept(visitor) { primitive.accept(visitor); } };
this.drawShape(shape);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like drawShape already handles clipping so we can remove the clipping code above:

drawShape(shape) {
const visitor = new PrimitiveToPath2DConverter({
strokeWeight: this.states.strokeWeight
});
shape.accept(visitor);
if (this._clipping) {
const currentTransform = this.drawingContext.getTransform();
const clipBaseTransform = this._clipBaseTransform.inverse();
const relativeTransform = clipBaseTransform.multiply(currentTransform);
this.clipPath.addPath(visitor.path, relativeTransform);
this.clipPath.closePath();

}

const primitive = new EllipsePrimitive(x, y, w, h);
const shape = { accept(visitor) { primitive.accept(visitor); } };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than making an object that isn't a full p5.Shape, could we instead make a new real p5.Shape and create a method on it that adds an ellipse primitive? similar to the relation between these methods and their corresponding primitives:

vertex(position, textureCoordinates, { isClosing = false } = {}) {
const added = this.#generalVertex('vertex', position, textureCoordinates);
added.isClosing = isClosing;
}
bezierVertex(position, textureCoordinates) {
this.#generalVertex('bezierVertex', position, textureCoordinates);
}
splineVertex(position, textureCoordinates) {
this.#generalVertex('splineVertex', position, textureCoordinates);
}
arcVertex(position, textureCoordinates) {
this.#generalVertex('arcVertex', position, textureCoordinates);
}

const centerY = arc.y + arc.h / 2;
const radiusX = arc.w / 2;
const radiusY = arc.h / 2;
const numPoints = Math.max(3, this.curveDetail);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curveDetail is a multiplier on the length, so numPoints should be something like ceil(this.curveDetail * arcLength)

#stop;
#mode;
// vertexCapacity 0 means this primitive should not accumulate normal path vertices
#vertexCapacity = 0;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than a capacity of zero, possibly we can contain a vertex representing the start and the end so that we can include all the other properties on a general vertex, which might include texture coordinates, fill, and stroke colors in webgl? Ideally it should go through #generalVertex in Shape to get all of that functionality.


for (let i = 0; i <= numPoints; i++) {
const angle = arc.start + (arc.stop - arc.start) * (i / numPoints);
verts.push(new Vertex({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to what I mentioned above in ArcPrimitive, ideally we should have some vertices in the primitive already so we can copy their data all around the ellipse but update the position part (the first 2 elements) for arcs and ellipses. For arcs, ideally we would also interpolate the properties of the vertex from the start to the end.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants