class Pyramide implements Evenement
{
    /* Une pyramide est un carr'e de centre c et de diagonale ac qui lance une
     * pyramide de centre c et de diagonale a'c ou a' est obtenu par
     * rotation de t et homoth'etie de rapport r autour de c. 
     *
     * Un 'ev'enement de date > dateMort est consid'er'e comme mort et
     * ne fait plus rien. De plus il g'en'ere des 'ev'enements de 
     * date date+dt, ce qui nous assure une terminaison. 
     */

    int date, dateMort, dt ;
    Point c, a ;
    double t, r ;
    int coul, dc ;

    Pyramide (int date, int dateMort, int dt,
              Point c, Point a,
              double t, double r, int coul, int dc) {
        this.date = date ; this.dateMort = dateMort ; this.dt = dt ;
        this.c = c ; this.a = a ;
        this.t = t ; this.r = r ;
        this.coul = coul ; this.dc = dc ;
    }

    public int getDate () {
        return date ;
    }

    public void effectuer (Fenetre fen, FilePrio fp) {
        if (date < dateMort) {
            // Dessiner le carr'e de base.
            Point a = this.a ;
            for (int i=1 ; i<=4; i++) {
                Point b = c.rot(90.*i, this.a) ;
                fen.triangle (c.x, c.y, a.x, a.y, b.x, b.y, coul) ;
                fen.ligne (a.x, a.y, b.x, b.y, 1) ;
                a = b ;
            }
            // Suite de la pyramide :
            Point na = c.hom (r, c.rot (t, this.a)) ;
            fp.ajouter (new Pyramide (date+dt, dateMort, dt, c, na, 
                                      t, r, coul+dc, dc)) ;
        } // sinon rien.
    }
}

class Entrelas implements Evenement
{
    /* Entrelas de pyramides. Les variables correspondent `a celles
     * de la premi`ere pyramide. */

    int date, dateMort, dt ;
    Point c, a ;
    double t, r ;
    int coul, dc ;

    Entrelas (int date, int dateMort, int dt,
              Point c, Point a,
              double t, double r, int coul, int dc) {
        this.date = date ; this.dateMort = dateMort ; this.dt = dt ;
        this.c = c ; this.a = a ;
        this.t = t ; this.r = r ;
        this.coul = coul ; this.dc = dc ;
    }

    public int getDate () {
        return date ;
    }

    public void effectuer (Fenetre fen, FilePrio fp) {
        if (date < dateMort) {
            fp.ajouter (new Pyramide (date, date+10*dt, dt,
                                      c, a, t, r, coul, dc)) ;
            Point p = new Point (140, 240) ;
            fp.ajouter (new Entrelas (date+1, dateMort, dt,
                                      p.rot(15., c), p.rot (15., a),
                                      t, r, coul+25, dc)) ;
        }
    }
}

class Sierpinski implements Evenement
{
    /* Triangle de sierpinski entre les points a, b, c
     * avec d'egrad'e de cmin `a cmax `a la date date avec mort `a dateMort. */
    int date, dateMort, dt ;
    Point a, b, c ;
    int cmin, cmax ;
    
    Sierpinski (int date, int dateMort, int dt,
                Point a, Point b, Point c,
                int cmin, int cmax) {
        this.date = date ; this.dateMort = dateMort ; this.dt = dt ;
        this.a = a ; this.b = b ; this.c = c ;
        this.cmin = cmin ; this.cmax = cmax ;
    }

    public int getDate () {
        return date ;
    }

    public void effectuer (Fenetre fen, FilePrio fp) {
        if (date < dateMort) {
            // Dessiner le triangle avec la couleur moyenne de la plage.
            fen.triangle (a.x, a.y, b.x, b.y, c.x, c.y, 
                          (cmin + cmax) / 2) ;
            fen.ligne (a.x, a.y, b.x, b.y, 1) ;
            fen.ligne (b.x, b.y, c.x, c.y, 1) ;
            fen.ligne (c.x, c.y, a.x, a.y, 1) ;
            // Ajouter des 'ev'enements pour les dessins r'ecursifs :
            Point ab = a.hom (.5, b) ;
            Point bc = b.hom (.5, c) ;
            Point ca = c.hom (.5, a) ;
            int c1tiers = (cmin*2 + cmax*1)/3 ; 
            int c2tiers = (cmin*1 + cmax*2)/3 ;
            fp.ajouter (new Sierpinski (date+dt, dateMort, dt,
                                        a, ab, ca,
                                        cmin, c1tiers)) ;
            fp.ajouter (new Sierpinski (date+dt, dateMort, dt,
                                        b, bc, ab,
                                        c1tiers, c2tiers)) ;
            fp.ajouter (new Sierpinski (date+dt, dateMort, dt,
                                        c, ca, bc,
                                        c2tiers, cmax)) ;
        } // Sinon rien.
    }
        
}

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

    int x, y ;

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

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

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

