How to draw an icoSphere made of triangle_fans ?

edited February 2018 in Python Mode

Hi !

I'm trying to make an icoSphere using the beginShape() function.

I've managed to draw a sphere made of particles but don't know how to connect those particles using the TRIANGLE_FAN parameter.

More specifically, there are 2 key parts that I can't figure out (from line 57):

  • How many vertex() functions do I need to write within the double for loop ?
  • What should be the coordinates of those vertices ?

Any help would be much appreciated.

add_library('peasycam')
add_library('toxiclibscore')
add_library('verletphysics')


from toxi.physics import VerletPhysics    ### using VerletPhysics engine
from particle import Particle   ### simple class containing a particle object


def setup():
    global particules, physics, total
    size(1080, 800, P3D)
    smooth(8)


    cam = PeasyCam(this, 110)
    physics = VerletPhysics()

    r = 180   ### radius of the sphere
    total = 10  ### number of particles/points


    particules = [[[] for e in range(total +1)] for f in range(total+1)]    ### 2D array list storing the particles locations

    for e in range(0, total+1):
            lon = map(e, 0, total, 0, PI)
            for f in range(0, total+1):
                lat = map(f, 0, total , 0, TWO_PI)

                x = r * sin(lat) * cos(lon) 
                y = r * sin(lat) * sin (lon) 
                z = r * cos(lat)

                particules[e][f] = Particle(x, y, z) 

    for e in range(total+1):
        for f in range(total+1):
            p = particules[e][f]
            physics.addParticle(p)   ### adding the particles to the physics engine


def draw():
    global particules, physics
    background(0)

    for e in range(total+1):    ### displaying the particles
        for f in range(total+1):
            p = particules[e][f]
            p.display()

    for e in range(total):
        beginShape(TRIANGLE_FAN)  ### Trying to connect the particles with triangle fans
        for f in range(total+1):
            noFill()
            strokeWeight(1)

            p = particules[e][f-1]            ### PART TO CORRECT #### vertices and their coordinates ####
            vertex(p.x(), p.y(), p.z())
            p2 = particules[e+1][f]        
            vertex(p2.x(), p2.y(), p2.z())      

        endShape()

Answers

  • edited February 2018

    The code above gives me this shape:

    https://imgur.com/IaV2d7A

    As you can see the triangle fans and not completed and most of the points are connected to one pole.

    Any idea of what I should change in the following snippet ?

    for e in range(total):
            beginShape(TRIANGLE_FAN)  
            for f in range(total+1):
                noFill()
                strokeWeight(1)
    
                p = particules[e][f-1]         ### PART TO CORRECT #### 
                vertex(p.x(), p.y(), p.z())
                p2 = particules[e+1][f]        
                vertex(p2.x(), p2.y(), p2.z())      
    
            endShape()
    
  • Please share your ideas, suggestions, code (even in Java)... anything !

  • I tried and I failed... Shouldn't you be using TRIANGLE_STRIPS btw?

    Check this post: https://www.openprocessing.org/sketch/92464

    My other lame suggestion: https://www.google.ca/search?source=hp&ei=wxyWWsmdMMScjwO16Y2ABA&q=icosphere+using+triangles&oq=icosphere+using+triangles&gs_l=psy-ab.3...1416.7710.0.7920.26.25.0.0.0.0.142.2201.21j4.25.0....0...1c.1.64.psy-ab..1.17.1546.0..0j35i39k1j0i131k1j0i10k1j0i22i30k1j33i160k1j33i21k1.0.JLiDaHcOEWE

    My attempt below but it has a bug.

    Kf

    //===========================================================================
    // IMPORTS:
    import peasy.PeasyCam;
    
    
    
    //===========================================================================
    // GLOBAL VARIABLES:
    
    PeasyCam cam;
    
    int r=180;
    int sr=3;
    int total=10;
    
    PVector[][] vv=new PVector[total][total];
    
    //===========================================================================
    // PROCESSING DEFAULT FUNCTIONS:
    
    void settings() {
      size(1080, 800, P3D);
    }
    
    void setup() {
    
      textAlign(CENTER, CENTER);
      rectMode(CENTER);
    
      noStroke();
    
      smooth(8);
      cam = new PeasyCam(this, 400);
    
    
    
    
    
    
      for (int e=0; e<total; e++) {
        float lon = map(e, 0, total, 0, TWO_PI);
    
        for (int f=0; f<total; f++) {
          float lat = map(f, 0, total, 0, PI);
    
          PVector p = new PVector( r * sin(lat) * cos(lon), 
            r * sin(lat) * sin (lon), 
            r * cos(lat));
          vv[e][f]=p;
        }
      }
    }
    
    void draw() {
      background(0);
      //noStroke();
      stroke(0);
    
      for (float e=0; e<total/1.0; e++) {
        beginShape(TRIANGLE_STRIP);
        for (float f=0; f<total; f++) {
          fill(lerp(0, 255, e*1.0/total), lerp(0, 255, f*1.0/total), 25);
    
          if (f-1>0) {
            PVector p= vv[int(e)][int(f-1)%total];
            vertex(p.x, p.y, p.z);
          }
    
          //if (e+1<total) {
          PVector q= vv[int(e+1)%total][int(f-0)%total];
          vertex(q.x, q.y, q.z);
          //}
        }
        vertex(0, 0, -180);
        endShape();
      }
    }
    
  • You didn't need to start a new Question for this.

    You don't use all triangle fans, just the top and bottom rows where you have a central point at the pole. The rest is triangle strips.

    The sphere code in github does exactly this.

  • edited February 2018

    @kfrajer: Hey that's super nice of you to share your attempt ! I did use TRIANGLE_STRIPS at first but koogs suggested me to use TRIANGLE_FANS instead. I've now managed to draw a sphere made of TRIANGLE_STRIPS and where the top and bottom only are made of TRIANGLE_FANS.

    And thanks for the links, the icosahedron sketch is really interesting. I'm now wondering what are the difference between an icosphere and an icosahedron. Anyway I'll probably use a part of the code for my next attempt.

    @koogs: As I just mentionned, I could draw a sphere made of triangle strips, where tops and bottoms only are triangle fans. HOWEVER (and this has more to do with my other question about springs) when I connect the vertices by springs the whole sphere falls apart. This is partly because I've now different meshes:

    • 4 made of triangle fans for the poles (2 for each pole)
    • 2 made of triangle strips for the "body" of the sphere (front and back)

    I guess it'd be a better idea to draw a sphere based on a single mesh. Is that possible ? (with an icosahedron for instance ?)

    Also what "sphere code" are you refering to ? Would you mind sharing a link ?

    PS: This is what the same sphere looks like when I add springs to each vertices and tie the different meshes together:

  • processing sphere code. it's complicated but you can see from the comments it's three bits, the top, the middle and the bottom.

    https://github.com/processing/processing/blob/master/core/src/processing/opengl/PGraphicsOpenGL.java#L8961

  • edited February 2018

    Do you think it's possible to draw a sphere with a single mesh ? (one single part, no top, middle or bottom)

  • Just brainstorming -- you might try basing your mesh algorithm on the points of a Fibonacci spiral. That walks a sphere from top to bottom as a spiral.

    https://forum.processing.org/two/discussion/18779/move-a-single-point

  • edited March 2018

    @jeremydouglass: That's a very interesting suggestion. I'll definitely look into it after I'm finished with my current polyhedron experiment. Thanks a lot !

  • I just wanted to share a much less cumbersome approach based on @jeremydouglass last suggestion. It's a 2 step process:

    • displaying points around a Fibonacci sphere
    • triangulating those points with the Hemesh library

      add_library('hemesh')
      
      phi = (sqrt(5) + 1) / 2 - 1 #Golden Ratio
      angle = phi * 2 * PI #Golden Angle
      n_points, radius = 300, 200
      liste = []
      
      def setup():
          global triangles, render
          size(600, 600, P3D)
          smooth(8)
          render = WB_Render(this)
      
          for p in range(n_points):
              lon = angle * p
              lon /= 2 * PI; lon -= floor(lon); lon *= 2 * PI
              if lon > PI: lon -= 2 * PI 
              lat = asin(-1 + 2 * p / float(n_points))
              new_points = WB_Point(radius * cos(lat) * cos(lon), radius * cos(lat) * sin(lon), radius * sin(lat))
              liste.append(new_points)
      
          triangulation = WB_Triangulate.alphaTriangulate3D(liste)
          triangles = triangulation.getAlphaTriangles(radius+1)
      
          noFill()
          beginShape(TRIANGLES)
      
      def draw():
          background(225,225,230)
      
          translate(width/2, height/2)
          for i in range(0, len(triangles), 3):
              render.drawTriangle(liste[triangles[i]], liste[triangles[i+1]], liste[triangles[i+2]])
      

Sign In or Register to comment.