> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://cd3d.sketchpad.cc/sp/pad/view/ro.VMM5CYF5AeM/rev.34
 * 
 * authors: 
 *   Pascal Romon
 *   Lorenzo Milesi

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



Vertex[] v;
Edge[] e;
Face[] f;
Edge S; // the surface, aka initial handle on an edge
float distance = 50;  // for camera

void setup() {
  size(300, 300, P3D);
  S = pyramid();
}


void draw() {
  background(50);
  // horizontal angle phi = PI*(2*mouseX/width -1)
  // vertical angle theta = (PI/2)*(2*mouseY/height/2 - 1)
  camera(width/2-distance*sin(TWO_PI*mouseX/width)*sin(PI*mouseY/height), 
    height/2+distance*cos(PI*mouseY/height), 
    -distance*cos(TWO_PI*mouseX/width)*sin(PI*mouseY/height), 
    width/2, height/2, 0, 
    0, 1, 0);
  
  /*
  for (int i=0; i < 5; i++) {
    f[i].draw(250,137);
  }
  */
  ArrayList<Face> list = faceTraversal(f[0]);
  for(Face currentFace : list) {
    currentFace.draw(250,137);
  }
  
  /*
  noStroke();
   fill(137);
   beginShape();
   vertex(p.xpos,p.ypos,p.zpos);
   vertex(q.xpos,q.ypos,q.zpos);
   vertex(r.xpos,r.ypos,r.zpos);
   endShape();
   */
}


void mouseWheel(MouseEvent event) {  // for zooming in and out
  float e = event.getCount();
  distance += e;
}

class Vertex {
  float xpos;
  float ypos;
  float zpos;
  Edge edgefrom;   // one of the oriented edges emanating from the vertex
  int status;    // for listing purposes
  
  Vertex(float x,float y,float z) {  
    xpos = x + width/2;
    ypos = y + height/2;
    zpos = z;
    status = 0;
  }
  
  void draw() {
    noStroke();
    lights();
    pushMatrix();
    translate(xpos,ypos,zpos);
    sphere(10);
    popMatrix();
  }
}


class Edge {  // oriented
  Vertex first;
  Vertex last;
  Edge opposite;
  Edge next;
  Edge previous;
  Face face;
  int status;    // for listing purposes
  
  Edge(Vertex theFirst, Vertex theLast) {
    first = theFirst;
    last = theLast;
    status = 0;
  }
  
  void draw() {
    stroke(255);
    line(first.xpos,first.ypos,first.zpos,last.xpos,last.ypos,last.zpos);
  }
}


class Face { //oriented
  Edge inedge;
  int degree;  // face degree, i.e. number of edges
  int status;    // for listing purposes
  String name;
  
  Face(Edge e, int d) {
    inedge = e;
    degree = d;
    status = 0;
  }
  
  void draw(int theStroke, int theFill) {
    if (theStroke == -1) {
      noStroke();
    } else {
      stroke(theStroke);
    }
    if (theFill == -1) {
      noFill();
    } else {
      fill(theFill);
    }
    Edge e = inedge;
    // vertex(e.xpos,e.first.ypos,e.first.zpos);
    beginShape();
    for (int i=0; i <= degree; i++) {
      vertex(e.last.xpos,e.last.ypos,e.last.zpos);
      // vertex(inedge.next.last.xpos,inedge.next.last.ypos,inedge.next.last.zpos);
      e = e.next;
    }
    endShape();
  }
  
  void drawEdges(int strokeColor) {
    draw(strokeColor,-1);
  }
  
  void drawFace(int fillColor) {
    draw(-1,fillColor);
  }
  
  ArrayList<Edge> boundingEdges() {  // returns the list of boundary edges
    Edge e = inedge;
    ArrayList<Edge> list = new ArrayList<Edge>();
    for (int i=0; i < degree; i++) {
      list.add(e);
      e = e.next;
    }
    return(list);
  }
  
  ArrayList<Face> incidentFaces() {  // returns the list of incident faces
    ArrayList<Face> faces = new ArrayList<Face>();
    Edge e = inedge;
    for (int i=0; i < degree; i++) {
      faces.add(e.opposite.face);
      e = e.next;
    }
    return(faces);
  }
}



class Surface {
  // not as half-edge convention
  Edge initialEdge;
  
  Surface(Edge e) {
    initialEdge = e;
  }
     
  void draw() {
    // draws all faces by enumeration
  }
}

Edge pyramid() {
  v = new Vertex[5];
  e = new Edge[16];  // two directions!
  f = new Face[5];
  
  v[0] = new Vertex(0,10,0);    // apex
  v[0].edgefrom = e[0];   // e01
  v[1] = new Vertex(0,0,10);
  v[1].edgefrom = e[8];   // e12
  v[2] = new Vertex(10,0,0);
  v[2].edgefrom = e[10];  // e23
  v[3] = new Vertex(0,0,-10);
  v[3].edgefrom = e[12];  // e34
  v[4] = new Vertex(-10,0,0);                                
  v[4].edgefrom = e[14];  // e41
  
  e[0] = new Edge(v[0],v[1]);   
  e[1] = new Edge(v[1],v[0]);
  e[0].opposite = e[1];
  e[1].opposite = e[0];
  e[2] = new Edge(v[0],v[2]);
  e[3] = new Edge(v[2],v[0]);
  e[2].opposite = e[3];
  e[3].opposite = e[2];
  e[4] = new Edge(v[0],v[3]);
  e[5] = new Edge(v[3],v[0]);
  e[4].opposite = e[5];
  e[5].opposite = e[4];
  e[6] = new Edge(v[0],v[4]);
  e[7] = new Edge(v[4],v[0]);
  e[6].opposite = e[7];
  e[7].opposite = e[6];
  e[8] = new Edge(v[1],v[2]);
  e[9] = new Edge(v[2],v[1]);
  e[8].opposite = e[9];
  e[9].opposite = e[8];
  e[10] = new Edge(v[2],v[3]);
  e[11] = new Edge(v[3],v[2]);
  e[10].opposite = e[11];
  e[11].opposite = e[10];
  e[12] = new Edge(v[3],v[4]);
  e[13] = new Edge(v[4],v[3]);
  e[12].opposite = e[13];
  e[13].opposite = e[12];
  e[14] = new Edge(v[4],v[1]);
  e[15] = new Edge(v[1],v[4]);
  
  f[0] = new Face(e[15],4);
  f[1] = new Face(e[0],3);
  f[2] = new Face(e[2],3);
  f[3] = new Face(e[4],3);
  f[4] = new Face(e[6],3);
  f[0].name = "f0";
  f[1].name = "f1";
  f[2].name = "f2";
  f[3].name = "f3";
  f[4].name = "f4";


  e[14].opposite = e[15];
  e[15].opposite = e[14];
  e[0].next = e[8];
  e[0].previous = e[3];
  e[0].face = f[1];
  e[1].next = e[6];
  e[1].previous = e[14];
  e[1].face = f[4];
  e[2].next = e[10];
  e[2].previous = e[5];
  e[2].face = f[2];
  e[3].next = e[0];
  e[3].previous = e[8];
  e[3].face = f[1];
  e[4].next = e[12];
  e[4].previous = e[7];
  e[4].face = f[3];
  e[5].next = e[2];
  e[5].previous = e[10];
  e[5].face = f[2];
  e[6].next = e[14];
  e[6].previous = e[1];
  e[6].face = f[4];
  e[7].next = e[4];
  e[7].previous = e[12];
  e[7].face = f[3];
  e[8].next = e[3];
  e[8].previous = e[0];
  e[8].face = f[1];
  e[9].next = e[15];
  e[9].previous = e[11];
  e[9].face = f[0];
  e[10].next = e[5];
  e[10].previous = e[2];
  e[10].face = f[2];
  e[11].next = e[9];
  e[11].previous = e[13];
  e[11].face = f[0];
  e[12].next = e[7];
  e[12].previous = e[4];
  e[12].face = f[3];
  e[13].next = e[11];
  e[13].previous = e[15];
  e[13].face = f[0];
  e[14].next = e[1];
  e[14].previous = e[6];
  e[14].face = f[4];
  e[15].next = e[13];
  e[15].previous = e[9];
  e[15].face = f[0];
    
  return e[0];
}

ArrayList<Face> faceTraversal(Face initialFace) {  // lists all faces starting from initialFace
  // assumes all face statuses are = 0
  // uses Dijkstra-like algorithm
  ArrayList<Face> finalList = new ArrayList<Face>();
  ArrayList<Face> discoveredList = new ArrayList<Face>();  // list of discovered faces
  ArrayList<Face> neighborsList = new ArrayList<Face>();
  Face currentFace;
  //Edge currentEdge = initialFace.inedge;
  discoveredList.add(initialFace);
  while (discoveredList.size() > 0) {
    currentFace = discoveredList.get(0);
    println(" currentFace = " + currentFace.name);
    discoveredList.remove(0);
    neighborsList = currentFace.incidentFaces();
    for (Face f : neighborsList) {
      if (f.status == 0) {   // new face
        f.status = 1;   // switch to discovered
        discoveredList.add(f);
      }
    }
    finalList.add(currentFace);
    currentFace.status = 2;    // switch to marked
  }
  // clean all faces once done
  for (Face f : finalList) {
    f.status = 0;
  }
  println("exit faceTraversal");
  return(finalList);
}