How to prevent a PShape object to close?

Hi everyone,

I’d like to display a TRIANGLE_STRIP mesh as a PShape object but when doing so the mesh “closes” on itself (lines connecting the last points to the first points of the mesh appear)

Here’s an example to be as clear as possible:

Mesh computed in draw() every frame (working correctly but slow)

add_library('peasycam')
scl = 10

def setup():
    global n_rows, n_cols, terrain
    size(1200, 800, P3D)
    
    cam = PeasyCam(this, 1800)
    n_cols, n_rows = width / scl, width / scl
    terrain = [[0 for e in range(n_rows)] for f in range(n_cols)]
 
    yoff = 0
    for y in range(n_rows):
        xoff = 0
        for x in range(n_cols):
            noiseDetail(8)
            ny = map(y, 0, n_rows, 0, 1)
            nx = map(x, 0, n_cols, 0, 2)
            n_value = map(noise(nx*1.6, ny), 0, 1, -200, 1000)
            terrain[x][y] = n_value
            xoff += .014
        yoff += .011

def draw():
    background(240)
    pushMatrix()
    translate(-width/2, height/2, -width/2)
    rotateX(PI/2)
        
    for y in range(n_rows-1):
        beginShape(TRIANGLE_STRIP) 
        fill(240)
        for x in range(n_cols):
            vertex(x * scl, y * scl, terrain[x][y]  )
            vertex(x * scl, (y + 1) * scl, terrain[x][y + 1]  )
        endShape()
        
    popMatrix()

Mesh computed in setup() and stored in a PShape object (not working correctly but fast)

add_library('peasycam')
scl = 10


def setup():
    global  mesh, n_rows, n_cols, terrain
    size(1200, 800, P3D)
    
    cam = PeasyCam(this, 1800)
    n_cols, n_rows = width / scl, width / scl
    terrain = [[0 for e in range(n_rows)] for f in range(n_cols)]
    
    yoff = 0
    for y in range(n_rows):
        xoff = 0
        for x in range(n_cols):
            noiseDetail(8)
            ny = map(y, 0, n_rows, 0, 1)
            nx = map(x, 0, n_cols, 0, 2)
            n_value = map(noise(nx*1.6, ny), 0, 1, -200, 1000)
            terrain[x][y] = n_value
            xoff += .014
        yoff += .011
     
    mesh = createShape()
    for y in range(n_rows-1):
        mesh.beginShape(TRIANGLE_STRIP) 
        mesh.fill(240)
        for x in range(n_cols):
            mesh.vertex(x * scl, y * scl, terrain[x][y]  )
            mesh.vertex(x * scl, (y + 1) * scl, terrain[x][y + 1]  )
        mesh.endShape()

def draw():
    background(240)
    pushMatrix()
    translate(-width/2, height/2, -width/2)
    rotateX(PI/2)
    
    shape(mesh, 0, 0)
    
    popMatrix()

I think the problem comes from the endShape() function being called within the double for loop and not ouside of it but can’t seem to find a proper solution to this.

Hi Solub!
I think this is a bug. I just reproduced it on my own as well. This is from the Daniel Shiffman’s noisy terrain. It appears to be an issue when you create a PShape variable and set it to TRIANGLE_STRIP rather than just using beginShape() on its own. (Check post below for clarification)

Here’s where the bug appears:

def setup():
    global cols,rws,scl,w,h,terrain,flying,mesh
    size(400,400,P3D)
    smooth(8)
    w = 600
    h = 600
    scl = 20
    flying = 0
    cols = w/scl
    rws = h/scl
    
    #Instantiating 2D matrix for noisy terrain values
    terrain = [[0 for j in range(rws)] for i in range(cols)]
    
    #Creating noisy values for terrain
    for y in range(cols):    
        for x in range(rws):
            terrain[x][y] = map(noise(x*0.1,y*0.1+flying),0,1,-100,100)
    
    #Creating noisy mesh
    mesh = createShape()
    for y in range(cols-1):
        mesh.beginShape(TRIANGLE_STRIP)
        for x in range(rws):
            mesh.vertex(x*scl,y*scl, terrain[x][y])
            mesh.vertex(x*scl,(y+1)*scl, terrain[x][y+1])
        mesh.endShape()
    
def draw():
    global flying
    
    #Display settings
    background(0)
    stroke(255)
    noFill()
    
    # Coordinate transformations
    translate(width/2,height/2)
    rotateX(PI/3)
    translate(-w/2.0,-h/2.0)
    
    #Calling the noisy mesh
    shape(mesh,0,0)
    
    #Flying parameter
    flying -= 0.05         

Here’s where it goes away if I stop using PShape as a variable.

def setup():
    global cols,rws,scl,w,h,terrain,flying,mesh
    size(400,400,P3D)
    background(0)
    smooth(8)
    w = 600
    h = 600
    scl = 20
    flying = 0
    cols = w/scl
    rws = h/scl
    
    #Instantiating 2D matrix for noisy terrain values
    terrain = [[0 for j in range(rws)] for i in range(cols)]
    
    #Creating noisy values for terrain
    for y in range(cols):    
        for x in range(rws):
            terrain[x][y] = map(noise(x*0.1,y*0.1+flying),0,1,-100,100)
    
    # Coordinate transformations
    translate(width/2,height/2)
    rotateX(PI/3)
    translate(-w/2.0,-h/2.0)
    
    #Creating noisy mesh
    for y in range(cols-1):
        with beginShape(TRIANGLE_STRIP):
            for x in range(rws):
                vertex(x*scl,y*scl, terrain[x][y])
                vertex(x*scl,(y+1)*scl, terrain[x][y+1])
        
     

Perhaps you can report it as an issue in the processing.py github issues.

How unfortunate ! I’ll report the issue, thank you @WakeMeAtThree.

Have you checked 1st whether a Java Mode version of the sketch would work or not before reporting it as a Python Mode exclusive bug?! :bug:

Anyways, just made a workaround version using createShape(GROUP) in order to separate each beginShape(TRIANGLE_STRIP) child’s scope: :snake:

"""
 PShape Noisy Terrain Gen (v1.2)
 mod GoToLoop (2018-Aug-04)

 Discourse.Processing.org/t/
 how-to-prevent-a-pshape-object-to-close/2340/4
"""

add_library('peasycam')

OPAQ, BLACK, WHITE = PImage.ALPHA_MASK, 0, -1
FG, BG, STROKE = WHITE, BLACK, 0x0000FF | OPAQ

SCL, MAG = 15, 200
HALF_MAG = MAG>>1
STEP, ROTATE = .2, HALF_PI

W, H = 1000, 800
CLS, RWS = W/SCL, H/SCL
COLS_RANGE, ROWS_RANGE = tuple(range(CLS)), tuple(range(RWS))

ZERO, SPC = [0.0], ' '
SCALED, FRAC = lambda n: n * SCL, lambda n: n * STEP

SCALED_XS = tuple(map(SCALED, COLS_RANGE))
SCALED_YS = tuple(map(SCALED, ROWS_RANGE))

NOISE_XS = tuple(map(FRAC, COLS_RANGE))
NOISE_YS = tuple(map(FRAC, ROWS_RANGE))

terrain = tuple(ZERO*CLS for row in ROWS_RANGE)

def setup():
    size(800, 600, P3D)
    smooth(8); stroke(STROKE); fill(FG)

    global cw, ch, cz
    cw, ch, cz = -width>>1, height>>1, -width>>1

    PeasyCam(this, width)
    renoiseTerrain(); createMesh()


def draw():
    background(BG)
    translate(cw, ch, cz); rotateX(ROTATE)
    shape(mesh, 0, 0)


def keyPressed():
    if key == SPC or key == ENTER:
        renoiseTerrain(); createMesh()


def renoiseTerrain():
    noiseSeed(int(random(-MAX_INT, MAX_INT)))

    for row, y in zip(terrain, NOISE_YS):
        for idx in COLS_RANGE:
            row[idx] = MAG*noise(NOISE_XS[idx], y) - HALF_MAG


def createMesh():
    global mesh
    mesh = createShape(GROUP)

    prevRow = terrain[0]; prevY = SCALED_YS[0]

    for row, y in zip(terrain, SCALED_YS):
        if row is prevRow: continue

        m = createShape(); mesh.addChild(m)
        m.beginShape(TRIANGLE_STRIP)

        for idx, x in enumerate(SCALED_XS):
            m.vertex(x, prevY, prevRow[idx])
            m.vertex(x, y, row[idx])

        m.endShape()
        prevRow = row; prevY = y
1 Like

Hi GoToLoop!

Good call! :clap: I just tested it and it is also happening in Java as well. Here’s the result from Daniel Shiffman code (tweaked it a bit to be in setup only):

// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/IKB1hWWedMk

int cols, rows;
int scl = 20;
int w = 2000;
int h = 1600;

float flying = 0;

float[][] terrain;

void setup() {
  size(600, 600, P3D);
  cols = w / scl;
  rows = h/ scl;
  terrain = new float[cols][rows];
  float yoff = flying;
  for (int y = 0; y < rows; y++) {
    float xoff = 0;
    for (int x = 0; x < cols; x++) {
      terrain[x][y] = map(noise(xoff, yoff), 0, 1, -100, 100);
      xoff += 0.2;
    }
    yoff += 0.2;
  }
  
  background(0);
  stroke(0);
  
  translate(width/2, height/2+50);
  rotateX(PI/3);
  translate(-w/2, -h/2);
  
  for (int y = 0; y < rows-1; y++) {
    beginShape(TRIANGLE_STRIP);
    for (int x = 0; x < cols; x++) {
      vertex(x*scl, y*scl, terrain[x][y]);
      vertex(x*scl, (y+1)*scl, terrain[x][y+1]);
    }
    endShape();
  }
}

Here’s the result from tweaked code to make it use createShape():

// Tweaking Code to check for a possible
// Bug in PShape from:
// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// Code for: https://youtu.be/IKB1hWWedMk

int cols, rows;
int scl = 20;
int w = 2000;
int h = 1600;

float flying = 0;

float[][] terrain;

void setup() {
  size(600, 600, P3D);
  cols = w / scl;
  rows = h/ scl;
  terrain = new float[cols][rows];
  float yoff = flying;
  for (int y = 0; y < rows; y++) {
    float xoff = 0;
    for (int x = 0; x < cols; x++) {
      terrain[x][y] = map(noise(xoff, yoff), 0, 1, -100, 100);
      xoff += 0.2;
    }
    yoff += 0.2;
  }
  
  background(0);
  stroke(0);
  
  translate(width/2, height/2+50);
  rotateX(PI/3);
  translate(-w/2, -h/2);
  
  PShape mesh = createShape();
  
  for (int y = 0; y < rows-1; y++) {
    mesh.beginShape(TRIANGLE_STRIP);
    for (int x = 0; x < cols; x++) {
      mesh.vertex(x*scl, y*scl, terrain[x][y]);
      mesh.vertex(x*scl, (y+1)*scl, terrain[x][y+1]);
    }
    mesh.endShape();
  }
  shape(mesh,0,0);
}

Both using Processing 3.3.7. If someone can kindly check with the later versions to confirm as I need to head to work. Seems like an issue worthy to report. (Check post below for clarification)

What’s making me hesitant now to flat out call this a bug for sure is that the createShape() example in processing doesn’t seem to have an issue, even after taking to 3D. Perhaps it needs more testing to see where it breaks, or maybe something is off in the original noisy terrain logic that’s calling the last and first strip of points again, making it close.

//Tweaking createShape() example
//from Processing reference

void setup() {
  size(400, 400, P3D);
  translate(width/2,height/2);
  rotateX(PI/3);
  
  PShape s = createShape();
  s.beginShape(TRIANGLE_STRIP);
  s.vertex(30, 75);
  s.vertex(40, 20);
  s.vertex(50, 75);
  s.vertex(60, 20);
  s.vertex(70, 75);
  s.vertex(80, 20);
  s.vertex(90, 75);
  s.endShape();
  shape(s,0,0);
}

That example got 1 pair of beginShape(TRIANGLE_STRIP) & endShape()'s vertex() block. :one:

Our “terrain” sketches got a loop of beginShape(TRIANGLE_STRIP) & endShape() pairs! :curly_loop:

BtW, we can use constant THIRD_PI in place of PI/3. :wink:

Hmm. Not sure it’s a bug now. I think @solub would just be fine by doing the following:

def setup():
    global cols,rws,scl,w,h,terrain,flying,mesh
    size(400,400,P3D)
    smooth(8)
    w = 600
    h = 600
    scl = 20
    flying = 0
    cols = w/scl
    rws = h/scl
    
    #Instantiating 2D matrix for noisy terrain values
    terrain = [[0 for j in range(rws)] for i in range(cols)]
    
    #Creating noisy values for terrain
    for y in range(cols):    
        for x in range(rws):
            terrain[x][y] = map(noise(x*0.1,y*0.1+flying),0,1,-100,100)
    
    #Creating noisy mesh
    mesh = createShape(GROUP);
    for y in range(cols-1):
        m = createShape()
        m.beginShape(TRIANGLE_STRIP)
        for x in range(rws):
            m.vertex(x*scl,y*scl, terrain[x][y])
            m.vertex(x*scl,(y+1)*scl, terrain[x][y+1])
        m.endShape()
        mesh.addChild(m)
    
def draw():
    global flying
    
    #Display settings
    background(0)
    stroke(255)
    noFill()
    
    # Coordinate transformations
    translate(width/2,height/2)
    rotateX(PI/3)
    translate(-w/2.0,-h/2.0)
    
    #Calling the noisy mesh
    shape(mesh,0,0)
    
    #Flying parameter
    flying -= 0.05         

Which creates multiple TRIANGLE_STRIP PShapes and adds them to a GROUP. I think my initial assessment assumed that beginShape(TRIANGLE_STRIP) and m.beginShape(TRIANGLE_STRIP) would behave similarly. Perhaps someone would clarify at a later point.

Maybe each call to the global PApplet::beginShape() method already adds itself to an internally pre-created createShape(GROUP)? :thinking:

@GoToLoop, @WakeMeAtThree

Anyways, just made a workaround version using createShape(GROUP) in order to separate each beginShape(TRIANGLE_STRIP) child’s scope

Hmm, interesting…That’s the first thing I tried before asking for help here but that solution didn’t work on my computer: Processing was crashing just after hitting the “run” button.

Anyway, thanks both of you. I’m sticking with the slower solution for the moment, computing the mesh in draw()
.

Edit: the problem occurs on both version 3.3.7 and 3.4

My sketch’s working on a 64-bit PDE 3.4 + Py Mode 3040 w/ a Win 8.1 AMD E2-3800 APU laptop. :computer:

Hi,

So I ran into the same issue.
I couldn’t find any open issue on github so I created this one: