endShape()
Shape / VertexDescription
The companion to beginShape(). Pass CLOSE to close the shape.
Syntax
void endShape()
void endShape(int mode)
Parameters
| Name | Type | Description |
|---|---|---|
| mode | int | CLOSE to close the shape (optional) |
Returns
voidRelated
Under the Hood
From Processing.h:
void beginShape(); void endShape(int mode=0);
void endShape(bool close=false) { closed=close; }
void endShape(int mode=0);
inline void PGraphics::endShape(int mode) { if(PApplet::g_papplet) PApplet::g_papplet->endShape(mode); }
Under the Hood
From Processing.cpp:
void PApplet::endShape(int mode){
if(!inShape){return;}
bool cl=(mode==CLOSE);
int n3 = (int)shapeVerts3D.size();
// -- Explicit shape kinds (QUADS, QUAD_STRIP, TRIANGLES, etc.) ----------
// Draw fill and/or stroke from the collected vertex arrays.
if(shapeKind != -1 && n3 > 0){
GLenum gm;
switch(shapeKind){
case POINTS: gm=GL_POINTS; break;
case LINES: gm=GL_LINES; break;
case TRIANGLES: gm=GL_TRIANGLES; break;
case TRIANGLE_FAN: gm=GL_TRIANGLE_FAN; break;
case TRIANGLE_STRIP:gm=GL_TRIANGLE_STRIP;break;
case QUADS: gm=GL_QUADS; break;
case QUAD_STRIP: gm=GL_QUAD_STRIP; break;
default: gm=GL_POLYGON; break;
}
if(doFill && shapeKind != POINTS && shapeKind != LINES){
glEnable(GL_POLYGON_OFFSET_FILL);
glPolygonOffset(1.0f, 1.0f);
applyFill();
glBegin(gm);
for(auto& v : shapeVerts3D) glVertex3f(v[0], v[1], v[2]);
glEnd();
glDisable(GL_POLYGON_OFFSET_FILL);
}
// POINTS and LINES use stroke color drawn directly
if(shapeKind == POINTS){
applyStroke();
glPointSize(strokeW);
glBegin(GL_POINTS);
for(auto& v : shapeVerts3D) glVertex3f(v[0], v[1], v[2]);
glEnd();
restoreLighting();
inShape=false; shape3D=false; shapeVerts.clear(); shapeVerts3D.clear();
return;
}
if(shapeKind == LINES){
applyStroke(); glLineWidth(strokeW);
glBegin(GL_LINES);
for(auto& v : shapeVerts3D) glVertex3f(v[0], v[1], v[2]);
glEnd();
restoreLighting();
inShape=false; shape3D=false; shapeVerts.clear(); shapeVerts3D.clear();
return;
}
// Draw stroke outlines -- shapeVerts was collected in vertex()
if(doStroke && !shapeVerts.empty()){
applyStroke(); glLineWidth(strokeW);
int n=(int)shapeVerts.size();
// Use 3D verts for correct depth in transformed space
auto lv=[&](int i)->std::array<float,3>{ return shapeVerts3D[i]; };
switch(shapeKind){
case TRIANGLE_STRIP:
glBegin(GL_LINES);
for(int i=0;i+2<n;i++){
auto a=lv(i),b=lv(i+1),c=lv(i+2);
glVertex3f(a[0],a[1],a[2]);glVertex3f(b[0],b[1],b[2]);
glVertex3f(b[0],b[1],b[2]);glVertex3f(c[0],c[1],c[2]);
glVertex3f(c[0],c[1],c[2]);glVertex3f(a[0],a[1],a[2]);
}
glEnd();
break;
case TRIANGLE_FAN:
glBegin(GL_LINES);
for(int i=1;i+1<n;i++){
auto a=lv(0),b=lv(i),c=lv(i+1);
glVertex3f(a[0],a[1],a[2]);glVertex3f(b[0],b[1],b[2]);
glVertex3f(b[0],b[1],b[2]);glVertex3f(c[0],c[1],c[2]);
glVertex3f(c[0],c[1],c[2]);glVertex3f(a[0],a[1],a[2]);
}
glEnd();
break;
case TRIANGLES:
glBegin(GL_LINES);
for(int i=0;i+2<n;i+=3){
auto a=lv(i),b=lv(i+1),c=lv(i+2);
glVertex3f(a[0],a[1],a[2]);glVertex3f(b[0],b[1],b[2]);
glVertex3f(b[0],b[1],b[2]);glVertex3f(c[0],c[1],c[2]);
glVertex3f(c[0],c[1],c[2]);glVertex3f(a[0],a[1],a[2]);
}
glEnd();
break;
case QUADS:
glBegin(GL_LINES);
for(int i=0;i+3<n;i+=4){
auto a=lv(i),b=lv(i+1),c=lv(i+2),d=lv(i+3);
glVertex3f(a[0],a[1],a[2]);glVertex3f(b[0],b[1],b[2]);
glVertex3f(b[0],b[1],b[2]);glVertex3f(c[0],c[1],c[2]);
glVertex3f(c[0],c[1],c[2]);glVertex3f(d[0],d[1],d[2]);
glVertex3f(d[0],d[1],d[2]);glVertex3f(a[0],a[1],a[2]);
}
glEnd();
break;
case QUAD_STRIP:
glBegin(GL_LINES);
for(int i=0;i+3<n;i+=2){
auto a=lv(i),b=lv(i+1),c=lv(i+3),d=lv(i+2);
glVertex3f(a[0],a[1],a[2]);glVertex3f(b[0],b[1],b[2]);
glVertex3f(b[0],b[1],b[2]);glVertex3f(c[0],c[1],c[2]);
glVertex3f(c[0],c[1],c[2]);glVertex3f(d[0],d[1],d[2]);
glVertex3f(d[0],d[1],d[2]);glVertex3f(a[0],a[1],a[2]);
}
glEnd();
break;
default:
glBegin(cl?GL_LINE_LOOP:GL_LINE_STRIP);
for(auto& v:shapeVerts3D)glVertex3f(v[0],v[1],v[2]);
glEnd();
break;
}
}
restoreLighting();
inShape=false; shape3D=false; shapeVerts.clear(); shapeVerts3D.clear();
return;
}
if(shapeVerts.empty()){inShape=false;return;}
// Default beginShape() / endShape(CLOSE) path.
// Use stencil buffer to fill arbitrary (including concave) polygons correctly.
// This matches Java Processing's polygon fill behavior.
if(shapeKind==-1 || shapeKind==CLOSE){
if(doFill && shapeVerts.size()>=3){
glDisable(GL_CULL_FACE);
// Step 1: Write polygon to stencil buffer using odd-even fill rule
glEnable(GL_STENCIL_TEST);
glClear(GL_STENCIL_BUFFER_BIT);
glStencilFunc(GL_ALWAYS, 0, ~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_INVERT); // toggle stencil bit
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); // don't write color
glBegin(GL_TRIANGLE_FAN);
for(auto& v : shapeVerts3D) glVertex3f(v[0], v[1], v[2]);
glEnd();
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
// Step 2: Draw fill color only where stencil != 0
glStencilFunc(GL_NOTEQUAL, 0, ~0);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
applyFill();
// Draw a bounding quad covering the shape
float minx=shapeVerts3D[0][0], maxx=minx;
float miny=shapeVerts3D[0][1], maxy=miny;
float minz=shapeVerts3D[0][2], maxz=minz;
for(auto& v:shapeVerts3D){
minx=std::min(minx,v[0]); maxx=std::max(maxx,v[0]);
miny=std::min(miny,v[1]); maxy=std::max(maxy,v[1]);
minz=std::min(minz,v[2]); maxz=std::max(maxz,v[2]);
}
float mz=(minz+maxz)*0.5f;
glBegin(GL_QUADS);
glVertex3f(minx,miny,mz); glVertex3f(maxx,miny,mz);
glVertex3f(maxx,maxy,mz); glVertex3f(minx,maxy,mz);
glEnd();
glDisable(GL_STENCIL_TEST);
}
if(doStroke){
applyStroke(); glLineWidth(strokeW);
glBegin(cl ? GL_LINE_LOOP : GL_LINE_STRIP);
// Use 3D coords so transforms (rotateX/Y) are respected
for(auto& v : shapeVerts3D) glVertex3f(v[0], v[1], v[2]);
glEnd();
restoreLighting();
}
}
inShape=false; shapeVerts.clear(); shapeVerts3D.clear();
}