class Pluie implements Evenement
{
    /* Peint un carr'e al'atoire de largeur < largPluie,
     * et lance deux nouveaux 'ev'enements de Pluie.
     */

    int date, delaiSuiv ;
    int largFen, largPluie ;

    Pluie (int d, int dl, int lf, int lp) {
        date = d ; 
        delaiSuiv = dl ;
        largFen = lf ;
        largPluie = lp ;
    }

    public int getDate () {
	return date ;
    }

    final static int dateMort = 1000 ;

    static java.util.Random rand = new java.util.Random () ;

    public void effectuer (Fenetre fen, FilePrio fp) {
	if (date < dateMort) {
            int l = rand.nextInt (largPluie) ;
            int x = rand.nextInt (largFen - l) ;
            int y = rand.nextInt (largFen - l) ;
	    fen.rectangle (x, y, x+l, y+l, rand.nextInt (256)) ;
	    fp.ajouter (new Pluie (date + 2*delaiSuiv/3, (int)(.9 * delaiSuiv), largFen, largPluie)) ;
	    fp.ajouter (new Pluie (date + delaiSuiv, (int)(.9 * delaiSuiv), largFen, largPluie)) ;
	} // sinon rien
    }
}

class Pyramide implements Evenement
{
    /* Une pyramide est un rectangle plus une pyramide
     * un peu plus petite.
     */

    int date ;
    int xa, ya, xb, yb ;
    int coul ;
    int delaiSuiv ;

    Pyramide (int d, int c, 
              int xxa, int yya, int xxb, int yyb) {
        date = d ;
        coul = c ;
        xa = xxa ; ya = yya ;
        xb = xxb ; yb = yyb ;
        delaiSuiv = 300 ;
    }

    public int getDate () {
	return date ;
    }

    final static int largMin = 15 ;
    final static float facteur = .1f ;

    public void effectuer (Fenetre fen, FilePrio fp) {
	if (xa + largMin < xb && ya + largMin < yb) {
	    fen.rectangle (xa, ya, xb, yb, 1) ; // noir pour le cadre 
	    fen.rectangle (xa+1, ya+1, xb-1, yb-1, coul) ; // int'erieur
            int pasx = (int)(facteur * (xb - xa)) ; 
            int pasy = (int)(facteur * (yb - ya)) ; 
            // on r'eutilise l'objet :
            date = date + delaiSuiv ;
            delaiSuiv = (int)(.9 * delaiSuiv) ;
            coul = (coul + Math.min (pasx, pasy)) % 256 ;
            xa += pasx ; ya += pasy ;
            xb -= pasx ; yb -= pasy ;
	    fp.ajouter (this) ;
	}
    }

}

class Entrelas implements Evenement
{
    /* Entrelas de pyramides.
     */

    int date ;
    Point c, a, b ;
    int coul ;

    Entrelas (int d, int co, 
              Point cc, Point aa, Point bb) {
        date = d ;
        coul = co ;
        a = aa ; b = bb ; c = cc ;
    }

    public int getDate () {
	return date ;
    }

    final static int dateMort = 1000, delaiSuiv=100, incrCoul = 40 ;
    final static int largMin = 15 ;
    final static float facteur = .1f, angle = -16.f ;

    public void effectuer (Fenetre fen, FilePrio fp) {
	if (date < dateMort) {
	    fp.ajouter (new Pyramide (date+1, coul, a.x, a.y, b.x, b.y)) ;
            Point aa = c.rot (angle, a) ;
            Point bb = aa.plus (b.moins(a)) ;
	    fp.ajouter (new Entrelas (date+delaiSuiv, coul+incrCoul, c, aa, bb)) ;
	}
    }

}

class PyrInf implements Evenement
{
    /* Idem pyramide avec croisement des bors.
     */

    int date, delaiSuiv ;
    int xa, ya, xb, yb, dx, dy, xmax, ymax ;
    int coul ;

    PyrInf (int d, int ds, int c, int ddx, int ddy, int xm, int ym,
            int xxa, int yya, int xxb, int yyb) {
        date = d ; delaiSuiv = ds ;
        coul = c ;
        dx = ddx ; dy = ddy ; 
        xmax = xm ; ymax = ym ;
        xa = xxa ; ya = yya ;
        xb = xxb ; yb = yyb ;
    }

    public int getDate () {
	return date ;
    }

    final static int dateMort = 10000 ;

    public void effectuer (Fenetre fen, FilePrio fp) {
	if (date < dateMort) {
            int xm = Math.min (xa, xb) ;
            int ym = Math.min (ya, yb) ;
            int xM = Math.max (xa, xb) ;
            int yM = Math.max (ya, yb) ;
            // cadre :
	    fen.rectangle (xm, ym, xM, ym+1, 1) ;
	    fen.rectangle (xM, ym, xM, yM, 1) ;
	    fen.rectangle (xM, yM, xm, yM-1, 1) ;
	    fen.rectangle (xm, yM, xm, ym, 1) ;
            // int'erieur :
	    fen.rectangle (xm+1, ym+1, xM-1, yM-1, coul) ; 
            date += delaiSuiv ;
            xa += dx ; xb -= dx ;
            if (xa < 0 || xa > xmax || xb < 0 || xb > xmax) {
                dx = -dx ;
                xa = cadre (xa, xmax) ;
                xb = cadre (xb, xmax) ;
            }
            ya += dy ; yb -= dy ;
            if (ya < 0 || ya > ymax || yb < 0 || yb > ymax) {
                dy = -dy ;
                ya = cadre (ya, ymax) ;
                yb = cadre (yb, ymax) ;
            }
            coul += 1 ;
            if (coul > 255) coul = 2 ;
	    fp.ajouter (this) ;
	}
    }

    int cadre (int x, int xmax) {
        if (x < 0) return -x ;
        if (x > xmax) return xmax - (x - xmax) ;
        return x ;
    }

}

class Pates implements Evenement
{
    /* Peint un rectangle a,b et g'en`ere nbFils pates
     * `a l'int'erieur de ce carr'e.
     * La plage de couleur colMin, coulMax est partag'ee 
     * entre les fils, le rectangle a pour couleur le milieu 
     * de cette plage.
     */

    int date ;
    Point a, b ;
    int nbFils, coulMin, coulMax ;

    Pates (int d, int nb, Point aa, Point bb, int cmin, int cmax) {
        date = d ; 
        nbFils = nb ;
        a = aa ; b = bb ;
        coulMin = cmin ; coulMax = cmax ;
    }

    public int getDate () {
	return date ;
    }

    final static int delaiSuiv = 9, largMin = 15 ;

    static java.util.Random rand = new java.util.Random () ;
    static Point zero = new Point (0, 0) ;

    public void effectuer (Fenetre fen, FilePrio fp) {
	if (a.x + largMin < b.x && a.y + largMin < b.y) {
	    fen.rectangle (a.x, a.y, b.x, b.y, 1) ; // noir pour le cadre 
	    fen.rectangle (a.x+1, a.y+1, b.x-1, b.y-1, coulMin+rand.nextInt(-coulMin+coulMax+1)) ; // int'erieur

            int c = coulMin, dc = (coulMax - coulMin + 1)/nbFils ;
            if (dc <= 0) dc = 1 ;
            for (int i=0 ; i<nbFils ; i++, c+=dc) {
                Point d = b.moins (a) ;
                Point lh = zero.hom(.3, d).rand (d) ;
                Point aa = a.rand (b.moins (lh)) ;
                Point bb = aa.plus (lh) ;
                fp.ajouter (new Pates (date+rand.nextInt (delaiSuiv), nbFils, aa, bb, c, c+dc-1)) ;
            }
	}
    }
}

class PatesMK implements Evenement
{
    /* Peint un rectangle a,b et g'en`ere nbFils pates
     * `a l'int'erieur de ce carr'e.
     * La plage de couleur colMin, coulMax est partag'ee 
     * entre les fils, le rectangle a pour couleur le milieu 
     * de cette plage.
     */

    int date ;
    Point a, b ;
    int nbFils, coulMin, coulMax ;

    PatesMK (int d, int nb, Point aa, Point bb, int cmin, int cmax) {
        date = d ; 
        nbFils = nb ;
        a = aa ; b = bb ;
        coulMin = cmin ; coulMax = cmax ;
    }

    public int getDate () {
	return date ;
    }

    final static int delaiSuiv = 9, largMin = 2 ;

    static java.util.Random rand = new java.util.Random () ;

    public void effectuer (Fenetre fen, FilePrio fp) {
	if (a.x + largMin < b.x && a.y + largMin < b.y) {
	    fen.rectangle (a.x, a.y, b.x, b.y, 1) ; // noir pour le cadre 
	    fen.rectangle (a.x+1, a.y+1, b.x-1, b.y-1, coulMin+rand.nextInt(-coulMin+coulMax+1)) ; // int'erieur

            int c = coulMin, dc = (coulMax - coulMin + 1)/nbFils ;
            if (dc <= 0) dc = 1 ;
            for (int i=0 ; i<nbFils ; i++, c+=dc) {
                Point aa = a.rand (b) ;
                Point bb = aa.rand (b) ;
                fp.ajouter (new PatesMK (date+rand.nextInt (delaiSuiv), nbFils, aa, bb, c, c+dc-1)) ;
            }
	}
    }
}

class Point 
{
    /* Manipulations de points dans le plan. */

    int x, y ;

    Point (int xx, int yy) {
        x = xx ; y = yy ;
    }

    Point plus (Point p) {
        return new Point (x+p.x, y+p.y) ;
    }

    Point moins (Point p) {
        return new Point (x-p.x, y-p.y) ;
    }

    static java.util.Random rand = new java.util.Random () ;
    /* Point al'eatoire entre this et p. */
    Point rand (Point p) {
        Point d = p.moins (this) ;
        Point r = new Point (rand.nextInt(d.x), rand.nextInt(d.y)) ;
        return plus (r) ;
    }

    /* Image de p par l'homoth'etie de centre this et rapport r. */
    Point hom (double r, Point p) {
        int dx = p.x - x ;
        int dy = p.y - y ;
        return new Point ((int)(x + r * dx), (int)(y + r * dy)) ;
    }

    /* Milieu . */
    Point mil (Point p) {
        return new Point ((x+p.x)/2, (y+p.y)/2) ;
    }

    /* Image de p par la rotation de centre this et d'angle t en degr'es. */
    Point rot (double t, Point p) {
        double tr = t / 180. * Math.PI ;
        double c = Math.cos (tr), s = Math.sin (tr) ;
        int dx = p.x - x ;
        int dy = p.y - y ;
        double ddx = c * dx - s * dy ;
        double ddy = c * dy + s * dx ;
        return new Point ((int)(x+ddx), (int)(y+ddy)) ;
    }
}

class Gravite implements Evenement
{
    /* 
     */

    int date ; // millisecondes depuis le lancement du programme.
    float x, y, vx, vy ; // position, vitesse
    float xmax, ymax ;
    int coul ;

    Gravite (int d, int c, 
             float xx, float yy, float vvx, float vvy,
             float xm, float ym) {
        date = d ; coul = c ; 
        x = xx ; y = yy ; vx = vvx ; vy = vvy ;
        xmax = xm ; ymax = ym ;
    }

    public int getDate () {
	return date ;
    }

    final static int dateMort = 30000 ;
    final static float dy = 11.f, dtMax = .3f, g = 80.f, frein = 7.f ;

    public void effectuer (Fenetre fen, FilePrio fp) {
	if (date < dateMort) {
            int ax = (int) x, ay = (int) y ;
            fen.rectangle (ax, ay, ax+10, ay+10, 1) ;
            fen.rectangle (ax+1, ay+1, ax+9, ay+9, coul) ;
            float dt = dtMax ;
            float vit = (float) Math.sqrt (vy*vy + vx*vx) ;
            if (vit > .0001f) dt = dy / vit ;
            if (dt > dtMax) dt = dtMax ;
            //System.out.println (dt) ;
            x = x + vx * dt ;
            if (x > xmax) {
                x = xmax - (x - xmax) ;
                vx = -vx ;
            }
            if (x < 0.) {
                x = - x ;
                vx = -vx ;
            }
            y = y + vy * dt ;
            // signe de vy :
            float svy ;
            if (vy > 0.f) svy = 1.f ;
            else if (vy < 0.f) svy = -1.f ;
            else svy = 0.f ;
            // gravit'e + frein :
            vy = vy + g*dt - svy * frein * dt ;
            if (y > ymax) {
                y = ymax - (y - ymax) ;
                vy = -vy ;
            }
            if (y < 0.) {
                y = - y ;
                vy = -vy ;
            }
            date = date + (int)(1000.*dt) ;
            fp.ajouter (this) ;
        }
    }
}
