public void endShape(int mode) {
    if (ctm.isIdentity()) {
      for (int i = 0; i < vertexCount; i++) {
        vertices[i][TX] = vertices[i][X];
        vertices[i][TY] = vertices[i][Y];
      }
    } else {
      for (int i = 0; i < vertexCount; i++) {
        vertices[i][TX] = ctm.multX(vertices[i][X], vertices[i][Y]);
        vertices[i][TY] = ctm.multY(vertices[i][X], vertices[i][Y]);
      }
    }

    // ------------------------------------------------------------------
    // TEXTURES

    fpolygon.texture(textureImage);

    // ------------------------------------------------------------------
    // COLORS
    // calculate RGB for each vertex

    spolygon.interpARGB = true; //strokeChanged; //false;
    fpolygon.interpARGB = true; //fillChanged; //false;

    // all the values for r, g, b have been set with calls to vertex()
    // (no need to re-calculate anything here)

    // ------------------------------------------------------------------
    // RENDER SHAPES

    int increment;

    switch (shape) {
    case POINTS:
      // stroke cannot change inside beginShape(POINTS);
      if (stroke) {
        if ((ctm.m00 == ctm.m11) && (strokeWeight == 1)) {
          for (int i = 0; i < vertexCount; i++) {
            thin_point(vertices[i][TX], vertices[i][TY], strokeColor);
          }
        } else {
          for (int i = 0; i < vertexCount; i++) {
            float[] v = vertices[i];
            thick_point(v[TX], v[TY], v[TZ],  v[SR], v[SG], v[SB], v[SA]);
          }
        }
      }
      break;

    case LINES:
      if (stroke) {
        // increment by two for individual lines
        increment = (shape == LINES) ? 2 : 1;
        draw_lines(vertices, vertexCount-1, 1, increment, 0);
      }
      break;

    case TRIANGLE_FAN:
      // do fill and stroke separately because otherwise
      // the lines will be stroked more than necessary
      if (fill) {
        fpolygon.vertexCount = 3;
        
        for (int i = 1; i < vertexCount-1; i++) {
//          System.out.println(i + " of " + vertexCount);

          fpolygon.vertices[2][R] = vertices[0][R];
          fpolygon.vertices[2][G] = vertices[0][G];
          fpolygon.vertices[2][B] = vertices[0][B];
          fpolygon.vertices[2][A] = vertices[0][A];

          fpolygon.vertices[2][TX] = vertices[0][TX];
          fpolygon.vertices[2][TY] = vertices[0][TY];

          if (textureImage != null) {
            fpolygon.vertices[2][U] = vertices[0][U];
            fpolygon.vertices[2][V] = vertices[0][V];
          }
//          System.out.println(fpolygon.vertices[2][TX] + " " + fpolygon.vertices[2][TY]);

          for (int j = 0; j < 2; j++) {
            fpolygon.vertices[j][R] = vertices[i+j][R];
            fpolygon.vertices[j][G] = vertices[i+j][G];
            fpolygon.vertices[j][B] = vertices[i+j][B];
            fpolygon.vertices[j][A] = vertices[i+j][A];

            fpolygon.vertices[j][TX] = vertices[i+j][TX];
            fpolygon.vertices[j][TY] = vertices[i+j][TY];
            
//            System.out.println(fpolygon.vertices[j][TX] + " " + fpolygon.vertices[j][TY]);

            if (textureImage != null) {
              fpolygon.vertices[j][U] = vertices[i+j][U];
              fpolygon.vertices[j][V] = vertices[i+j][V];
            }
          }
//          System.out.println();
          fpolygon.render();
        }
      }
      if (stroke) {
        // draw internal lines
        for (int i = 1; i < vertexCount; i++) {
          draw_line(vertices[0], vertices[i]);
        }
        // draw a ring around the outside
        for (int i = 1; i < vertexCount-1; i++) {
          draw_line(vertices[i], vertices[i+1]);
        }
        // close the shape
        draw_line(vertices[vertexCount-1], vertices[1]);
      }
      break;

    case TRIANGLES:
    case TRIANGLE_STRIP:
      increment = (shape == TRIANGLES) ? 3 : 1;
      // do fill and stroke separately because otherwise
      // the lines will be stroked more than necessary
      if (fill) {
        fpolygon.vertexCount = 3;
        for (int i = 0; i < vertexCount-2; i += increment) {
          for (int j = 0; j < 3; j++) {
            fpolygon.vertices[j][R] = vertices[i+j][R];
            fpolygon.vertices[j][G] = vertices[i+j][G];
            fpolygon.vertices[j][B] = vertices[i+j][B];
            fpolygon.vertices[j][A] = vertices[i+j][A];

            fpolygon.vertices[j][TX] = vertices[i+j][TX];
            fpolygon.vertices[j][TY] = vertices[i+j][TY];
            fpolygon.vertices[j][TZ] = vertices[i+j][TZ];

            if (textureImage != null) {
              fpolygon.vertices[j][U] = vertices[i+j][U];
              fpolygon.vertices[j][V] = vertices[i+j][V];
            }
          }
          fpolygon.render();
        }
      }
      if (stroke) {
        // first draw all vertices as a line strip
        if (shape == TRIANGLE_STRIP) {
          draw_lines(vertices, vertexCount-1, 1, 1, 0);
        } else {
          draw_lines(vertices, vertexCount-1, 1, 1, 3);
        }
        // then draw from vertex (n) to (n+2)
        // incrementing n using the same as above
        draw_lines(vertices, vertexCount-2, 2, increment, 0);
        // changed this to vertexCount-2, because it seemed
        // to be adding an extra (nonexistant) line
      }
      break;

    case QUADS:
      if (fill) {
        fpolygon.vertexCount = 4;
        for (int i = 0; i < vertexCount-3; i += 4) {
          for (int j = 0; j < 4; j++) {
            int jj = i+j;
            fpolygon.vertices[j][R] = vertices[jj][R];
            fpolygon.vertices[j][G] = vertices[jj][G];
            fpolygon.vertices[j][B] = vertices[jj][B];
            fpolygon.vertices[j][A] = vertices[jj][A];

            fpolygon.vertices[j][TX] = vertices[jj][TX];
            fpolygon.vertices[j][TY] = vertices[jj][TY];
            fpolygon.vertices[j][TZ] = vertices[jj][TZ];

            if (textureImage != null) {
              fpolygon.vertices[j][U] = vertices[jj][U];
              fpolygon.vertices[j][V] = vertices[jj][V];
            }
          }
          fpolygon.render();
        }
      }
      if (stroke) {
        for (int i = 0; i < vertexCount-3; i += 4) {
          draw_line(vertices[i+0], vertices[i+1]);
          draw_line(vertices[i+1], vertices[i+2]);
          draw_line(vertices[i+2], vertices[i+3]);
          draw_line(vertices[i+3], vertices[i+0]);
        }
      }
      break;

    case QUAD_STRIP:
      if (fill) {
        fpolygon.vertexCount = 4;
        for (int i = 0; i < vertexCount-3; i += 2) {
          for (int j = 0; j < 4; j++) {
            int jj = i+j;
            if (j == 2) jj = i+3;  // swap 2nd and 3rd vertex
            if (j == 3) jj = i+2;
            
            fpolygon.vertices[j][R] = vertices[jj][R];
            fpolygon.vertices[j][G] = vertices[jj][G];
            fpolygon.vertices[j][B] = vertices[jj][B];
            fpolygon.vertices[j][A] = vertices[jj][A];

            fpolygon.vertices[j][TX] = vertices[jj][TX];
            fpolygon.vertices[j][TY] = vertices[jj][TY];
            fpolygon.vertices[j][TZ] = vertices[jj][TZ];

            if (textureImage != null) {
              fpolygon.vertices[j][U] = vertices[jj][U];
              fpolygon.vertices[j][V] = vertices[jj][V];
            }
          }
          fpolygon.render();
        }
      }
      if (stroke) {
        draw_lines(vertices, vertexCount-1, 1, 2, 0);  // inner lines
        draw_lines(vertices, vertexCount-2, 2, 1, 0);  // outer lines
      }
      break;

    case POLYGON:
      if (isConvex()) {
        if (fill) {
          //System.out.println("convex");
          fpolygon.renderPolygon(vertices, vertexCount);
          //if (stroke) polygon.unexpand();
        }

        if (stroke) {
          draw_lines(vertices, vertexCount-1, 1, 1, 0);
          if (mode == CLOSE) {
            // draw the last line connecting back to the first point in poly
            //svertices[0] = vertices[vertexCount-1];
            //svertices[1] = vertices[0];
            //draw_lines(svertices, 1, 1, 1, 0);
            draw_line(vertices[vertexCount-1], vertices[0]);
          }
        }
      } else {  // not convex
        //System.out.println("concave");
        if (fill) {
          // the triangulator produces polygons that don't align
          // when smoothing is enabled. but if there is a stroke around
          // the polygon, then smoothing can be temporarily disabled.
          boolean smoov = smooth;
          //if (stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = false;
          if (stroke) smooth = false;
          concaveRender();
          //if (stroke && !hints[DISABLE_SMOOTH_HACK]) smooth = smoov;
          if (stroke) smooth = smoov;
        }

        if (stroke) {
          draw_lines(vertices, vertexCount-1, 1, 1, 0);
          if (mode == CLOSE) {
            // draw the last line connecting back
            // to the first point in poly
//            svertices[0] = vertices[vertexCount-1];
//            svertices[1] = vertices[0];
//            draw_lines(svertices, 1, 1, 1, 0);
            draw_line(vertices[vertexCount-1], vertices[0]);
          }
        }
      }
      break;
    }

    // to signify no shape being drawn
    shape = 0;
  }