> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.4FqHTh0SBk-/rev.4540
 * 
 * authors: 
 *   
 *   Jose Moya

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



/*
This sketch builds on a prior work, "Tablero Parchís", created by Jose Moya
http://studio.sketchpad.cc/sp/pad/view/ro.9yXnRNsOy2VPH/rev.4623

This fork will try to make a playable parchís (parcheesi/ludo).
*/


// Trying to draw a Parchís (ludo) board.
// Pressing Control-R will render this sketch.


int RMCORNERS=1;
int RMCENTER=2;
int RMRADIUS=3;

    
int i = 0; 
float tercio, anchocasilla, altocasilla;
float numberX[]=new float[70];
float numberY[]=new float[70];
Forma[][] shapePasillo=new Forma[4][10];
Forma[] shapeCasilla=new Forma[68];
PFont casillaFont;

void setup() {  // this is run once.   
int f,g;
    // canvas size (Variable aren't evaluated. Integers only, please.)
    size(600, 600); 
    //    For fullscreen, use:
    //size(screen.width,screen.height);   
     
    // El tablero está dividido visualmente en 9 cuadros, por tercios.
    tercio=min(width/3,height/3);
    
    // El ancho de casilla es un tercio de un tercio.
    anchocasilla=tercio/3;
    
    // El alto de casilla es un séptimo de un tercio
    // (8 casillas verticales por casa, las 7 primeras ocupan el mismo
    //  alto que la casa)
    altocasilla=tercio/7;
    
    // Tipografía para número de casilla
    casillaFont=createFont("Times",12);
    textFont(casillaFont);
    textAlign(LEFT);    
    // set the background color
    background(255);
    colorMode(RGB);    
      
    // smooth edges
    smooth();
    
    // limit the number of frames per second
    frameRate (5);
    // Inicializando las Formas, necesario 
    // para dibujar shapePasillo desde atrás hacia adelante, 
    // o para dibujar las shareCasillas sin empezar por el 1.
    for (f=0;f<4;f++) for (g=0;g<9;g++){
       shapePasillo[f][g]=new Forma();
    }
    for (f=0;f<68;f++) {
        shapeCasilla[f]=new Forma();
    }

} 


void draw(){
    if (1==1) Redraw();
    
          
    }
// this is run repeatedly.  
void Redraw() {
    float guardax,guarday;
    float anchotriangulo;   
    int f,g;
    float h;
    int numcasilla=1;
    color[] colores=new color[4];
    
    colores[0]=color(255,255,20);
    colores[1]=color(20,20,255);
    colores[2]=color(255,20,20);
    colores[3]=color(20,255,20);

    pushMatrix();   
      translate(width/2,height/2);
    
      for (f=0;f<4;f++) {
          casa (tercio,tercio, color(colores[f])); 
          fill(colores[f]);
          anchotriangulo=tercio/2-altocasilla;
          triangle(-anchotriangulo,anchotriangulo,anchotriangulo, anchotriangulo,0,0);
   
          if (shapePasillo[f][7]==null) shapePasillo[f][7]=new Forma();
          if (shapePasillo[f][7].initialised==0){
              shapePasillo[f][7].makeTriangle(
                  -anchotriangulo,anchotriangulo,
                  anchotriangulo, anchotriangulo,
                  0,0
              );              
          }
          ellipse(width/2+10,height/2+10,20,20);
          rectMode(CENTER);
          for (g=0;g<7;g++){
              rect(0,anchotriangulo+.5*altocasilla+g*altocasilla,anchocasilla,altocasilla);              

              //Guardando la forma de las casillas del pasillo...
              if (shapePasillo[f][6-g]==null) shapePasillo[f][8-g]=new Forma();
              if (shapePasillo[f][6-g].initialised==0){  
                  shapePasillo[f][6-g].makeRect(
                      0,
                      anchotriangulo+.5*altocasilla+g*altocasilla,
                      anchocasilla,
                      altocasilla,
                      CENTER
                  );
              }
              
          }
          rectMode(CENTER);
          numcasilla=f*17+1;
          for(g=0;g<17;g++){
              fill(255,255,255);              
              if (g<=7) { 
                  gg=g;
                  h=0;
              } else {
                  gg=7-(g-8);
                  h=-2*anchocasilla;
              }
              switch (g) {
              case 7:
                  // Casilla curva 1  (casillas 8, 42, 25, 59)
                  beginShape();
                      vertex(h+anchocasilla/2, 1.5*tercio-(gg+1)*altocasilla);
                      vertex(anchotriangulo, 1.5*tercio-(gg+1)*altocasilla);
                      vertex(h+anchocasilla*1.5, 1.5*tercio-gg*altocasilla);
                      vertex(h+anchocasilla/2,1.5*tercio-gg*altocasilla);                      
                  endShape(CLOSE);
                 if (shapeCasilla[numcasilla-1]==null) shapeCasilla[numcasilla-1]=new Forma();
                 if (shapeCasilla[numcasilla-1].initialised==0){
                      shapeCasilla[numcasilla-1].puntoR(h+anchocasilla/2, 1.5*tercio-(gg+1)*altocasilla);
                      shapeCasilla[numcasilla-1].puntoR(anchotriangulo, 1.5*tercio-(gg+1)*altocasilla);
                      shapeCasilla[numcasilla-1].puntoR(h+anchocasilla*1.5, 1.5*tercio-gg*altocasilla);
                      shapeCasilla[numcasilla-1].puntoR(h+anchocasilla/2,1.5*tercio-gg*altocasilla);                      
                  }
                  break;
              case 8:
                // Casilla curva 2 (casillas 9, 43, 26, 60)
                 rotate(-HALF_PI);
                 beginShape();                      
                      vertex(-anchotriangulo, 1.5*tercio-(gg+1)*altocasilla);
                      vertex(h+anchocasilla*1.5, 1.5*tercio-(gg+1)*altocasilla);
                      vertex(h+anchocasilla*1.5, 1.5*tercio-gg*altocasilla);
                      vertex(h+anchocasilla/2,1.5*tercio-gg*altocasilla);                     
                  endShape(CLOSE);
                 if (shapeCasilla[numcasilla-1]==null) shapeCasilla[numcasilla-1]=new Forma();
                 if (shapeCasilla[numcasilla-1].initialised==0){
                     shapeCasilla[numcasilla-1].puntoR(-anchotriangulo, 1.5*tercio-(gg+1)*altocasilla);
                     shapeCasilla[numcasilla-1].puntoR(h+anchocasilla*1.5, 1.5*tercio-(gg+1)*altocasilla);
                     shapeCasilla[numcasilla-1].puntoR(h+anchocasilla*1.5, 1.5*tercio-gg*altocasilla);
                     shapeCasilla[numcasilla-1].puntoR(h+anchocasilla/2,1.5*tercio-gg*altocasilla);                     
                 }

                 break;
              case 16:
                fill(255,255,255);
                rect(0,1.5*tercio-altocasilla/2,anchocasilla,altocasilla); 
                    ellipse(
                        0,
                        1.5*tercio-altocasilla/2,
                        altocasilla*.75,
                        altocasilla*.75
                    );
                 if (shapeCasilla[numcasilla-1]==null) shapeCasilla[numcasilla-1]=new Forma();
                 if (shapeCasilla[numcasilla-1].initialised==0){
                     shapeCasilla[numcasilla-1].makeRect(
                        0,1.5*tercio-altocasilla/2,
                        anchocasilla,altocasilla, CENTER
                     ); 
                 }
                break;
              // La última casilla de cada jugador
              case 4:
                  fill(colores[f]);
                  // do not break the switch, pass to following case sentence.                
              default:
                 rect(h+anchocasilla,1.5*tercio-altocasilla*.5-gg*altocasilla,anchocasilla,altocasilla); 
                 if (g==4 || g==11) {
                    ellipse(
                        h+anchocasilla,
                        1.5*tercio-altocasilla*.5-gg*altocasilla, 
                        altocasilla*.75,
                        altocasilla*.75
                    );                    
                 }
                 if (shapeCasilla[numcasilla-1]==null) shapeCasilla[numcasilla-1]=new Forma();
                 if (shapeCasilla[numcasilla-1].initialised==0){
                     shapeCasilla[numcasilla-1].makeRect(
                         h+anchocasilla,1.5*tercio-altocasilla*.5-gg*altocasilla,
                         anchocasilla,altocasilla, CENTER
                     ); 
                 }
                 break;             

              }

              fill(0,0,0);
              textAlign(LEFT);
              if (g>=8 && g<16){
                 textAlign(RIGHT)
                 text(
                    numcasilla, 
                    h+1.5*anchocasilla-textWidth("000"), 
                    1.5*tercio-altocasilla*.5-gg*altocasilla                        
                 );
                 numberX[numcasilla]=screenX(
                    h+1.5*anchocasilla-textWidth("000"), 
                    1.5*tercio-altocasilla*.5-gg*altocasilla                        
                 )
                 numberY[numcasilla]=screenY(
                    h+1.5*anchocasilla-textWidth("000"), 
                    1.5*tercio-altocasilla*.5-gg*altocasilla                        
                 )
              } else if (g==16) { 
                  textAlign(CENTER);
                  text(numcasilla,0,1.5*tercio-altocasilla/2);                  
                  numberX[numcasilla]=screenX(0,1.5*tercio-altocasilla/2)
                  numberY[numcasilla]=screenY(0,1.5*tercio-altocasilla/2)
              } else {
              textAlign(LEFT);
                  text(numcasilla, h+anchocasilla/2+textWidth("0"),1.5*tercio-altocasilla*.5-gg*altocasilla);
                numberX[numcasilla]=screenX(h+anchocasilla/2+textWidth("0"),1.5*tercio-altocasilla*.5-gg*altocasilla);
                numberY[numcasilla]=screenY(h+anchocasilla/2+textWidth("0"),1.5*tercio-altocasilla*.5-gg*altocasilla);
              }
              //Ahora se podrían meter las casillas en una matriz para crear posiciones de juego.
              numcasilla++;              
          }                              
      }
 popMatrix();

    int pos =int(frameCount/2) % 71;
    //fill(255*(int(frameCount/5)%68));

    int casilla=((pos+5));
fill(0);
text(pos,20,20);
    fill (int(random(255)),int(random(255)),int(random(255)));
    if (casilla<68){
       shapeCasilla[casilla-1].dibuja();
        fill(colores[0]);
       ellipse(numberX[casilla],numberY[casilla],30,30);
       fill(0);
   text(casilla,numberX[casilla],numberY[casilla]);
    } else {
         shapePasillo[0][casilla-68].dibuja();
        // Todavía no he calculado las posiciones de
        // las fichas en la zona pasillo.
    }
     fill(0);



/*
    int casilla=int(frameCount/5) % 8; 

    for (int ff=0;ff<4;ff++) {        
//       fill(colores[0]);        
    fill (int(random(255)),int(random(255)),int(random(255)))
        shapePasillo[ff][casilla].dibuja();
    }
 
*/
}

void casa(x,y, color colorcasa){
    strokeWeight(2);
    stroke(0);
    rectMode(CENTER);
    fill(255,255,255);
    rect(x,y,tercio,tercio);
    noStroke();
    fill(colorcasa);
    ellipse(x,y,.9*tercio,.9*tercio);
    ellipseMode(CENTER);
    stroke(255,255,255);
    strokeWeight(1);
    ellipse(x,y,.6*tercio,.6*tercio);
    ellipse(x,y,.6*tercio,.6*tercio);
    stroke(0,0,0);
    fill(255,255,255);
    ellipse(x,y,.3*tercio,.3*tercio);
    noFill();


}

class Forma {  
    public int initialised=0; 
    float[] pointx;
    float[] pointy;
    // Falsa inicialización
    // Hace que el objeto sea no-nulo, pero
    // no añade ningún punto.
    Forma (){
        /* Constructor. Do nothing */        
        /* Not really initialised */
        this.initialised=0;
    }
    // Inicializa de verdad la forma,
    // añadiendo un punto.    
    Forma (float x,float y){
        this.pointx=append(pointx,x);
        this.pointy=append(pointy,y);   
        this.pointx[0]=x;
        this.pointy[0]=y;
        this.initialised=-1;
    }
    // Equivalente al Vertex de Pshape,
    //añade un punto a la forma.
    void punto(float x,float y){
         if (this.pointx==null) {
             pointx=new float[1];
             pointx[0]=x;
             pointy=new float[1];
             pointy[0]=y;
         }
         this.pointx=append(pointx,x);
         this.pointy=append(pointy,y);     
         this.initialised=-1;
    }  
    // Lo mismo que 'punto' pero traduce a coordenadas de pantalla.
    void puntoR(float x,float y){
         if (this.pointx==null) {
             pointx=new float[1];
             pointx[0]=screenX(x,y);
             pointy=new float[1];
             pointy[0]=screenY(x,y);
         }
         this.pointx=append(pointx,screenX(x,y));
         this.pointy=append(pointy,screenY(x,y));     
         this.initialised=-1;
    }  

    void dibuja(){       
         if (this.initialised==0) return;
        beginShape();
        for (f=0;f<this.pointx.length;f++){
            vertex(pointx[f],pointy[f]);   
        }
        endShape(CLOSE);             
    }
    void dibuja(float x,float y,float szx,float szy){
        if (this.initialised==0) return;
        pushMatrix();               
        translate(x,y);
//        scale(szx,szy);        
        beginShape();        
        for (f=0;f<this.pointx.length;f++){          
            vertex(pointx[f]*szx,pointy[f]*szy);   
        }
        endShape();
        popMatrix();        
    }
    //Stores a triangle as Forma
    //It will get the REAL coordinates of the rect so we can reproduce it without changing coordinate system.            
    void makeTriangle(float x1,float y1, float x2, float y2, float x3,float y3){
    
        this.pointx=new float[3];
        this.pointy=new float[3];
        pointx[0]=screenX(x1,y1);
        pointx[1]=screenX(x2,y2);
        pointx[2]=screenX(x3,y3);
        pointy[0]=screenY(x1,y1);
        pointy[1]=screenY(x2,y2);
        pointy[2]=screenY(x3,y3);
        this.initialised=-1;
                
    }

    //Stores a rect as Forma
    //It will get the REAL coordinates of the rect so we can reproduce it without changing coordinate system.    
    void makeRect(float x1,float y1,float x2,float y2, int rectmode) {    
        this.pointx=new float[4];
        this.pointy=new float[4];    
        float xx1,yy1,xx2,yy2;
              
        switch(rectmode){
            case (RADIUS):            
                xx1=screenX(x1-x2,y1-y2);
                xx2=screenX(x1+x2,y1+y2);
                yy1=screenY(x1-x2,y1-y2);
                yy2=screenY(x1+x2,y1+y2);
            break;                        
            case (CENTER):
                xx1=screenX(x1-x2/2,y1-y2/2);
                xx2=screenX(x1+x2/2,y1+y2/2);
                yy1=screenY(x1-x2/2,y1-y2/2);
                yy2=screenY(x1+x2/2,y1+y2/2);
            break;
            case (CORNER):
                xx1=screenX(x1,y1);
                xx2=screenX(x1+x2,y1+y2);
                yy1=screenY(x1,y1);
                yy2=screenY(x1+x2,y1+y2);    
            case (CORNERS): 
            default:
                xx1=screenX(x1,y1);
                xx2=screenX(x2,y2);
                yy1=screenY(x1,y1);
                yy2=screenY(x2,y2);        
        
            break;
        }
        this.pointx[0]=xx1;
        this.pointy[0]=yy1;                
        this.pointx[1]=xx1;
        this.pointy[1]=yy2; 
        this.pointx[2]=xx2;
        this.pointy[2]=yy2; 
        this.pointx[3]=xx2;
        this.pointy[3]=yy1;
        this.initialised=-1;        
    }
    
}


// Comprobar si un punto está dentro de un polígono.
//
    // The following code is modified from:
    // Copyright 2000 softSurfer, 2012 Dan Sunday
    // This code may be freely used and modified for any purpose
    // providing that this copyright notice is included with it.
    // SoftSurfer makes no warranty for this code, and cannot be held
    // liable for any real or imagined damage resulting from its use.
    // Users of this code must verify correctness for their application.
    // a Point is defined by its coordinates {int x, y;}
    //===================================================================
    // isLeft(): tests if a point is Left|On|Right of an infinite line.
    //    Input:  three points P0, P1, and P2
    //    Return: >0 for P2 left of the line through P0 and P1
    //            =0 for P2  on the line
    //            <0 for P2  right of the line
    //    See: Algorithm 1 "Area of Triangles and Polygons"
     int  isLeft( float px1,float py1,float px1,float py1,float px2,float py2 )
{
    return ( (px1- px0) * (py2 - py0)
            - (px2 -  px0) * (py1 - py0) );
}
//===================================================================
// cn_PnPoly(): crossing number test for a point in a polygon
//      Input:   px,py = a point,
//               pointx, pointy[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//      Return:  0 = outside, 1 = inside
// This code is patterned after [Franklin, 2000]
int cn_PnPoly( float px, float py, float[] pointx, float[] pointy)
{
    int    cn = 0;    // the  crossing number counter
    int nexti;
    // loop through all edges of the polygon
    for (int i=0; i<pointx.length; i++) {
        nexti=(i % pointx.length);
       // edge from V[i]  to V[i+1]
       if (((pointy[i] <= py) && (pointy[nexti].y > py))     // an upward crossing
        || ((pointy[i] > py) && (pointy[nexti].y <=  py))) { // a downward crossing
            // compute  the actual edge-ray intersect x-coordinate
            float vt = (py  - pointy[i]) / (pointy[nexty] - pointy[i]);
            if (px <  pointx[i]+ vt * (pointx[nexti] - pointx[i])) // px < intersect
                 cn++;   // a valid crossing of y=P.y right of P.x
                 /* original: ++cn */
        }
    }
    return (cn & 1);    // 0 if even (out), and 1 if  odd (in)

}
//===================================================================

// wn_PnPoly(): winding number test for a point in a polygon
//      Input:   px,py = a point,
//               V[] = vertex points of a polygon V[n+1] with V[n]=V[0]
//      Return:  wn = the winding number (=0 only when P is outside)
int
wn_PnPoly( float px, float py, float[] pointx, float[] pointy )
{
    int    wn = 0;    // the  winding number counter

    // loop through all edges of the polygon
    for (int i=0; i<pointx.length; i++) {   // edge from V[i] to  V[i+1]
        nexti=pointx.length;
        if (pointy[i] <= py) {          // start y <= P.y
            if (pointy[nexti]  > py)      // an upward crossing
                 if (isLeft( pointx[i], pointy[i], pointx[nexti], pointy[nexti],px,py) > 0)  // P left of  edge
                     wn++;            // have  a valid up intersect
                    //originalmente ++wn
        }
        else {                        // start y > P.y (no test needed)
            if (V[i+1].y  <= P.y)     // a downward crossing
                 if (isLeft( V[i], V[i+1], P) < 0)  // P right of  edge
                     wn--;            // have  a valid down intersect
                     // originalmente --wn
        }
    }
    return wn;
}