/* 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;
}