We are about to switch to a new forum software. Until then we have removed the registration on this forum.
Is there a way to remove a vertex from an existing Pshape object?
For example PShape has a getVertex()
, setVertex()
and (add) vertex()
command, but no removeVertex / deleteVertex / popVertex etc.
Here is a sketch that demonstrates vertex adding, with the missing remove command commented out:
PShape ps;
int lastv;
void setup(){
ps = createShape();
ps.beginShape();
//// add two "starter" vertices
ps.vertex(width/2,height/2);
ps.vertex(10+width/2,height/2);
ps.vertex(5+width/2,-10+height/2);
ps.endShape(CLOSE);
}
void draw(){
// background(0);
shape(ps,0,0);
}
//// click to add vertex
void mousePressed(){
ps.beginShape();
ps.vertex(mouseX,mouseY);
ps.endShape(CLOSE);
lastv = ps.getVertexCount()-1;
println("Adding vertex: ", lastv , ps.getVertex(lastv));
}
//// backspace to remove vertex
void keyPressed(){
if(keyCode == BACKSPACE){
println("'Deleting' last vertex: ", lastv , ps.getVertex(lastv));
//// HOW DO YOU REMOVE A VERTEX FROM A SHAPE?
// ps.beginShape();
// ps.removeVertex( ps.vertexCount()-1 );
// ps.endShape();
}
}
I have tried reading the PShape source, and it seems that float[][]vertices
is private. I played with Pshape.setPath()
, but it looks like this should perhaps be called addPath
? ... it appears to add additional vertices rather than setting the collection of all vertices to something new.
I'm specifically trying to avoid extended workarounds if possible. I know that a common solution is to save shape data -- e.g. in a PVector[] array or ArrayList -- and then re-render entries any time they change to a coordinated PShape output list, deleting and re-creating the shape. Because there is no shape copying or clearing, this means all the state of the shape (style, etc.) has to be tracked separately for re-rendering, and the point data, style data, and shape output lists all have to be kept in sync. Alternately, an "enhanced PShape" class would keep the points / style / etc. in class variables a rendered pShape, then redraw itself on change. This is better, although because I still need to intercept and cache all shape information I would still be remapping a lot of PShape functionality outside itself and doing a lot of rapid creating/destroying of PShape objects that I only want to modify.
Of course, any suggestions for how to make these approaches more workable would also be appreciated.
Answers
code is here:
https://github.com/processing/processing/blob/eb0429ae162c1dc82109feee69fd2a8e7b475f13/core/src/processing/core/PShape.java
vertices are a 2d array of floats (Line 199), and it's protected. no easy way of removing a value from this list the way it is at the moment,
Thank you for the code pointer, koogs. You are right, it looks like there is no easy way to remove a vertex from PShape.
If one really does want to remove vertices from PShapes then all the options I could see are ways to manage regenerating / replacing PShapes because they can't be edited.
I've looked at these approaches, most of which can be made to work with various amounts of pain:
lists: manage shape vertex data separately (e.g. in global lists like floatList or ArrayList) and then recreate a PShape any time its data changes. This assumes you are creating your PShapes, not loading them.
global functions: create a removeVertex function that takes a PShape and returns a new PShape (a copy, sans vertex). This assumes the PShapes are simple -- if they are complex, copying a PShape is really complex.
composition: encapsulate separate shape data by creating a custom class that contains a PShape, then add per-shape external data management through class methods (e.g. a removeVertex function as per approach #2).
DOESN'T WORK: inheritance: extend PShape. This seems like a dead-end when it comes to removing vertices: you cannot remove a vertex so you must replace the PShape object, yet you can never replace an object from within itself in Java. Unless:
child shapes: whether through PShape lists, composition, or inheritance, do all vertex rendering into child(0) of the shape, not into the shape itself. Then regenerate the shape after it changes with removeChild(0); addChild. This way shape vertices can be deleted and it stays the "same" Pshape object.
Whether using composition or inheritance, doing internal replacing of child objects requires PShape.createShape(), which requires the parent PApplet to be passed in on the constructor in order for createShape calls to work internally. Creating a constructor means changing the idiom for creating shapes in sketches -- so a shape with removable vertices isn't a drop-in replacement.
A few years ago on StackOverflow KevinWorkman gave an example of extending PShape (while advising against it) His example constructor solution also works for a composition approach.