int[] triangles = new int[vc*100];
int[] O = new int[vc*100];
pt[] medAxis = new pt[vc*100];        //geometry array for MA
float[] medAxisR = new float[vc*100]; //radius array for MA
int trav = vc;

/*Triangle Mesh Functions*/
//n,p,etc.
int n(int i){int ret = (i/3)*3 + (i+1)%3; return ret;}
int p(int i){return n(n(i));}
int o(int i){return O[i];}
int s(int i){return p(l(i));}
int r(int i){return o(n(i));}
int l(int i){return o(p(i));}

boolean clockWise(pt a, pt b, pt c){
  v3 A = new v3(b.x-a.x,b.y-a.y,0);
  v3 B = new v3(c.x-a.x,c.y-a.y,0);
  v3 C = cross(A,B);
  if (C.z >0){
    return true;
  }else{
     return false;
  }
}

//Set the O Tables
void setO(int[] V){
  O = new int[V.length];
  boolean found;
  for(int i=0; i<V.length; i++){
    found=false;
    for(int j=0; j<V.length; j++){
      if(i!=j){
        if((V[n(i)]==V[p(j)]) && (V[p(i)]==V[n(j)])){O[i]=j; O[j]=i; found=true; break;}
      }
    }
    if(!found){O[i]=i;}
  }
} 

/*Spray Functions*/
//Create the cloud from the spray and the loop.
pt[] cloud(Polyloop prime, pt[] spray){
  pt[] temp = new pt[spray.length + prime.vn];
  for(int i=0;i<spray.length;i++){
    temp[i]=spray[i];
  }for(int i=0;i<prime.vn;i++){
    temp[spray.length+i]=prime.P[i];
  }
  return temp;
}

pt[] cloud(Polyloop prime){
  pt[] temp = new pt[prime.vn];
  for(int i=0;i<prime.vn;i++){
    temp[i]=prime.P[i];
  }
  return temp;
}

//Checks to see if a point is in a polyloop.
boolean polyTest(pt a, Polyloop prime){
  int p = prime.vn;
  pt b = new pt(-10,-10);
  boolean clear = false;
  while(!clear){
    int hit=0;
    for(int i=0;i<p;i++){
      if(prime.P[i].disToLine(a,b)==0){b=new pt(random(-5,0),-10); hit++;}
    }
    if(hit==0){clear=!clear;}
  }
  int count=0;
  for(int i=0;i<(p-1);i++){
    if(crosses(a,b,prime.P[i],prime.P[i+1])){
      count++;
    }
  }if(crosses(a,b,prime.P[p-1],prime.P[0])){count++;}
  if(count%2==0){return false;}
  else{return true;}
}
  
//Computes AB-QR intersection 
boolean crosses(pt A, pt B, pt Q, pt R){if((A.isLeftOf(Q,R)!=B.isLeftOf(Q,R))&&(Q.isLeftOf(A,B)!=R.isLeftOf(A,B))){return true;} else{return false;}}
    
//Performs one set of Laplace Smoothing, returns true if points were moved
boolean laplace(pt[] cloud, float var, Polyloop con){
  boolean check=false;
  int c = cloud.length;
  for(int i=0;i<c;i++){ //for every point in the spray
    for(int j=i+1;j<c;j++){ //for every other point in the spray
      if(cloud[i].disTo(cloud[j])<var){ //if they're too close
        vec t = new vec(cloud[j],cloud[i]);
        vec s = new vec(cloud[i],cloud[j]);
        pt temp = new pt(cloud[i]); temp.translateBy(0.85,t);
        if(con.contains(temp)){cloud[i].translateBy(0.85,t); check=true;}
        temp = new pt(cloud[j]); temp.translateBy(0.85,s);
        if(con.contains(temp)){cloud[j].translateBy(0.85,s); check=true;}
      }
    }
    /*
    for(int j=0;j<(con.vn-1);j++){ 
      if(cloud[i].disToLineSeg(con.P[j],con.P[j+1])<var){
        pt lineTemp = cloud[i].makeProjectionOnLine(con.P[j],con.P[j+1]);
        vec t = new vec(lineTemp,cloud[i]);
        pt temp = new pt(cloud[i]); temp.translateBy(0.5,t);
        if(con.contains(temp)){cloud[i].translateBy(0.5,t); check=true;}
      }
    }
    if(cloud[i].disToLineSeg(con.P[con.vn],con.P[0])<var){
        pt lineTemp = cloud[i].makeProjectionOnLine(con.P[con.vn],con.P[0]);
        vec t = new vec(lineTemp,cloud[i]);
        pt temp = new pt(cloud[i]); temp.translateBy(0.5,t);
        if(con.contains(temp)){cloud[i].translateBy(0.5,t); check=true;}
    }
    */
    
    for(int j=0;j<con.vn;j++){ //for the cv of the polyloop
      if(cloud[i].disTo(con.P[j])<var){
        vec t = new vec(con.P[j],cloud[i]);
        vec s = new vec(cloud[i],con.P[j]);
        pt temp = new pt(cloud[i]); temp.translateBy(0.1,t);
        if(con.contains(temp)){cloud[i].translateBy(0.1,t); check=true;}
      }
    }
    for(int k=0;k<(con.vn-1);k++){
      pt temp = L(con.P[k],0.5,con.P[k+1]);
      if(cloud[i].disTo(temp)<var){
        vec t = new vec(temp,cloud[i]);
        cloud[i].translateBy(0.1,t); check=true;
      }
    }
    pt temp = L(con.P[con.vn-1],0.5,con.P[0]);
    if(cloud[i].disTo(temp)<var){
      vec t = new vec(temp,cloud[i]);
      cloud[i].translateBy(0.1,t); check=true;
    }
    
  }
  return check;
}



pt circumCenter (pt A, pt B, pt C) {    // computes the center of a circumscirbing circle to triangle (A,B,C)
  vec AB =  A.makeVecTo(B);  float ab2 = dot(AB,AB);
  vec AC =  A.makeVecTo(C); AC.turnLeft();  float ac2 = dot(AC,AC);
  float d = 2*dot(AB,AC);
  AB.turnLeft();
  AB.scaleBy(-ac2); 
  AC.scaleBy(ab2);
  AB.add(AC);
  AB.scaleBy(1./d);
  pt X =  A.makeClone();
  X.translateBy(AB);
  return(X);
};

int[] drawTriangles(pt[] cloud) { //hit shift w to activate
  int[] triangles = new int[vc*200];
  int triCount = 0;
  //int cirCount = 0;
  int vn = cloud.length;
  pt X = new pt(0,0);
  float r=1;
  for (int i=0; i<vn-2; i++) {for (int j=i+1; j<vn-1; j++) {for (int k=j+1; k<vn; k++) {  // needs to check the triangles and make sure their are out side of the polygon.
    if(i != j && j!=k && k!=i){
      if (cloud[i].disToLine(cloud[j],cloud[k])!=0){
    boolean found=false; 
    X=circumCenter (cloud[i],cloud[j],cloud[k]);  r = X.disTo(cloud[i]);
    
    for (int m=0; m<vn; m++) {
      if ((m!=i)&&(m!=j)&&(m!=k)&&(X.disTo(cloud[m])<=r)) {found=true; break;}
    };
    if (!found){
      float a = d(cloud[i],cloud[j]); float b = d(cloud[j],cloud[k]); float c = d(cloud[k],cloud[i]);
      float s = a+b+c;
      if(!polyTest(weightedSum((s-a)/2/s,cloud[i],(s-b)/2/s,cloud[j],(s-c)/2/s,cloud[k]),prime)) {
          found=true;
        }
      }
    if (!found) {
      //strokeWeight(1); stroke(green); ellipse(X.x,X.y,2*r,2*r); 
      triangles[triCount] = i; // stores the vertex index from teh cloud array.
      triangles[triCount+1] = j;
      triangles[triCount+2] = k;
      triCount +=3;
    };
    };
  };
  }; }; }; // end triple loop
  int[] temp = new int[triCount];
  for(int i=0; i<triCount; i++){
    temp[i]=triangles[i];
  }
  int holder;

  for(int i=0; i<triCount-3; i+=3){
    //println(i);
    if(clockWise(cloud[triangles[i]],cloud[triangles[i+1]],cloud[triangles[i+2]])){
      
    }else{
      holder = temp[i];
      temp[i] = temp[i+1];
      temp[i+1] = holder;
    }
  }
  return temp;
};  

pt weightedSum(float a, pt A, float b, pt B, float c, pt C) {pt P =  A.makeClone(); P.scaleBy(a); P.addScaledPt(b,B);  P.addScaledPt(c,C); return(P);}

pt[] medialAxis(pt[] cloud){
  pt[] axisLines = new pt[vc*100];
  int axisCount = 0;
  int vn = cloud.length;
  pt X = new pt(0,0);
  float r=1;
  for (int i=0; i<vn-2; i++) {for (int j=i+1; j<vn-1; j++) {for (int k=j+1; k<vn; k++) {  // needs to check the triangles and make sure their are out side of the polygon.
    if(i != j && j!=k && k!=i){
    boolean found=false; 
    X=circumCenter (cloud[i],cloud[j],cloud[k]);  r = X.disTo(cloud[i]);
    if(polyTest(X,prime)){
      for (int m=0; m<vn; m++) {
        if ((m!=i)&&(m!=j)&&(m!=k)&&(X.disTo(cloud[m])<=r) ) {found=true;}
      }
    }else{
      found =true;
    }
    if (!found){
       float a = d(cloud[i],cloud[j]); float b = d(cloud[j],cloud[k]); float c = d(cloud[k],cloud[i]);
      float s = a+b+c;
      if(!polyTest(weightedSum((s-a)/2/s,cloud[i],(s-b)/2/s,cloud[j],(s-c)/2/s,cloud[k]),prime)) {
          found=true;
        }
      }
    if (!found) {
      /*
      strokeWeight(1); stroke(green); ellipse(X.x,X.y,2*r,2*r); 
      stroke(blue);
      X.show(5);
      */
      axisLines[axisCount] = X.makeClone(); //store the points for the circles the radius can be computed when compared against the tirangle circles triangle == 3*circount
      medAxisR[axisCount] = r;
      axisCount ++;
    };
  };
  }; }; }; // end triple loop
  return axisLines;

}

void drawStructure(Polyloop prime) { 
  pt trace[] = new pt[5000];
  int traceCount = 0;
  int i= 0; int j = vc-1;
  pt N;
  float a,b,c,s;
  while (i+1 !=j && j-1 != i){
    strokeWeight(1); stroke(green); beginShape(POLYGON);  prime.P[i].v(); prime.P[j].v(); prime.P[i+1].v();prime.P[i].v(); endShape();      
      a=prime.P[i].disTo(prime.P[j]);
      b=prime.P[j].disTo(prime.P[i+1]);
      c=prime.P[i+1].disTo(prime.P[i]);
      s=a+b+c;
      N = weightedSum((s-a)/2/s,prime.P[i+1],(s-b)/2/s,prime.P[i],(s-c)/2/s,prime.P[j]);
     stroke(dmagenta); fill(magenta);   N.show();  N.showLabel("N");  noFill(); 
     
     trace[traceCount] = N.makeClone(); //finds the center points
     traceCount ++;
     
     i++;
      
     
     
     strokeWeight(1); stroke(green); beginShape(POLYGON);  prime.P[i].v(); prime.P[j].v(); prime.P[j-1].v();prime.P[i].v(); endShape(); 
     a=prime.P[i].disTo(prime.P[j]);
      b=prime.P[j].disTo(prime.P[i+1]);
      c=prime.P[i+1].disTo(prime.P[i]);
      s=a+b+c;
     stroke(dmagenta); fill(magenta);  N = weightedSum((s-a)/2/s,prime.P[i+1],(s-b)/2/s,prime.P[i],(s-c)/2/s,prime.P[j]); N.show();  N.showLabel("N");  noFill();
     
     trace[traceCount] = N.makeClone(); //finds the center points
     traceCount ++;
     
     
     j--;

  }
  

}
