import java.awt.*;

class Elephant {

    static final double EP_DOT  = 0.015;
    static final double EP_LINE = 0.001;



    public static void main(String[] args) {
        StdDraw.setCanvasSize(2*512,2*512);
        StdDraw.enableDoubleBuffering();
        final double XMIN = -1400.0;
        final double XMAX = 600.0;
        final double YMIN = -400.0;
        final double YMAX = 1600.0;

        final int STEPS = 1000;
        String fileName;


        // elephant Wolfram 40 paires
		//https://www.wolframalpha.com/input/?i=elephant+curve
        final double[] tRayonsG = {214.5, 57.6, 8.83, 50.14, 71.20, 50.25, 55.67, 64.60,
                                    51.50, 22.43, 14.20, 19.89, 3.92, 15.04, 14.91, 5.50,
                                    10.33, 11.80, 5.57, 4.91, 8.60, 2.60, 3.40, 1.67, 2.43,
                                    5.50, 1.90, 1.67, 2.25, 7.50, 2.40, 1.33, 1.67, 3.88, 3.86,
                                    0.25, 3.20, 1.05, 2.67, 1.86};
        final double[] tRayonsH = {390.67, 60.09, 58.88, 60.71, 30.14, 9.75, 13.90, 37.25, 25.67,
                                    9.09, 16.88, 5.75, 23.75, 7.75, 6.09, 16.75, 6.05, 11.88, 16.50,
                                    6.25, 2.91, 7.75, 8.10, 4.33, 3.57, 2.67, 5.80, 1.75, 5.33, 5.40,
                                    2.20, 0.33, 3.25, 4.67, 1.80, 1.89, 0.33, 1.50, 1.33, 1.67};

        final double[] tVitG = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                                11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,
                                21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,
                                31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0};
        final double[] tVitH = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0,
                                11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0,20.0,
                                21.0,22.0,23.0,24.0,25.0,26.0,27.0,28.0,29.0,30.0,
                                31.0,32.0,33.0,34.0,35.0,36.0,37.0,38.0,39.0,40.0};

        final double[] tPhiG = {-1.053, 3.333, 2.5, 2.5, -1.0, 2.429, 3.8, 0.25, -0.1, 0.667,
                                1.2, -1.083, 2.2, 2.0, 0.143, 0.667, 0.143, -0.667, -1.0, 0.25,
                                4.333, 1.5, 2.2, -1.0, -1.5, -1.2, 4.0, -1.333, -1.333, 3.056,
                                -0.25, 0.6, 4.0, -0.091, 2.167, 3.909, 4.5, 3.833, 0.571, -0.5};
        final double[] tPhiH = {2.629, 1.429, -0.142, -2.571, 1.679, -0.142, -0.404, -1.571, 1.001,
                                -1.821, -1.071, 0.029, 2.429, -0.971, 0.763, -2.771, 2.679, -0.696,
                                -2.237, -2.904, 1.096, -2.142, 2.52, 1.679, -1.904, -1.821, -2.821,
                                -0.071, -2.696, -3.071, 0.929, 0.829, 2.829, 0.679, 0.029, 2.829,
                                0.572, 2.329, 1.929, 1.263};


        final int NG = tRayonsG.length;
        final int NH = tRayonsH.length;

        StdDraw.setXscale(XMIN, XMAX);
        StdDraw.setYscale(YMIN, YMAX);


        // positions des premiers cercles
        double xcg0 = -1000;
        double ycg0 = 0.0;

        double xch0 =  0.0;
        double ych0 =  1150;

        // coordonnées des points des cycloïdes
        double[] txg = new double[STEPS];
        double[] tyg = new double[STEPS];

        double[] txh = new double[STEPS];
        double[] tyh = new double[STEPS];

        // coordonnées des points de l'intersection
        double[] txp = new double[STEPS];
        double[] typ = new double[STEPS];



        for (int step=0; step<STEPS; step++){
            double t = 2*step*Math.PI/STEPS;
            StdDraw.clear();

            // cycloïde G
            double[] ptPlot = new double[2];
            ptPlot = cycloState(tRayonsG, tVitG, tPhiG, xcg0, ycg0, t);
            txg[step] = ptPlot[0];
            tyg[step] = ptPlot[1];
            plotTrace(txg, tyg, step, StdDraw.BLUE, EP_DOT);
            // ligne horizontale
            StdDraw.setPenRadius(EP_LINE);
            StdDraw.setPenColor(StdDraw.GREEN);
            StdDraw.line(XMIN, tyg[step], XMAX, tyg[step]);



            //cycloïde H
            ptPlot = cycloState(tRayonsH, tVitH, tPhiH, xch0, ych0, t);
            txh[step] = ptPlot[0];
            tyh[step] = ptPlot[1];
            plotTrace(txh, tyh, step, StdDraw.BLUE, EP_DOT);
            // ligne verticale
            StdDraw.setPenRadius(EP_LINE);
            StdDraw.setPenColor(StdDraw.GREEN);
            StdDraw.line(txh[step], YMIN, txh[step], YMAX);


            // position courante du point d'intersection
            txp[step] = txh[step];
            typ[step] = tyg[step];
            plotTrace(txp, typ, step, StdDraw.ORANGE, EP_DOT);

            StdDraw.show();
            StdDraw.pause(20);
            // pour la video
            fileName = "Probo" + "_" + step + ".jpg";
            StdDraw.save("vid/"+fileName);
        }

    }

    /**
     * Trace les step premiers points dont les coordonnées sont rangées dans les tableaux txp, typ.
     * @param txp tableau des abscisses
     * @param typ tableau des ordonnées
     * @param step nombre de points à tracer
     * @param c couleur du tracé
     * @param ep épaisseur des points
     */
    private static void plotTrace(double[] txp, double[] typ, int step, Color c, double ep) {
        StdDraw.setPenRadius(ep);
        StdDraw.setPenColor(c);
        for (int k = 0; k < step; k++) {
            StdDraw.point(txp[k], typ[k]);
        }
    }

    /**
     * Calcule et représente l'état de l'ensemble des cercles définissant la cycloïde.
     * La mémorisation de l'ensemble des points n'est pas indispensable, mais ça fait travailler les tableaux.
     * @param tR tableau des rayons des cercles
     * @param tV tableau des vitesses de rotation des cercles
     * @param tPhi tableau des phases à l'origine
     * @param x0 abscisse du centre du premier cercle (point fixe)
     * @param y0 ordonnée du centre du premier cercle (point fixe)
     * @param t instant du calcul
     * @return tableau à deux éléments contenat l'abscisse et l'ordonnée du point courant de la cycloïde (à tracer)
     */
    public static double[] cycloState(double[] tR, double[] tV, double[] tPhi, double x0, double y0, double t){
      int N = tR.length;
      double[] txcg = new double[N];
      double[] tycg = new double[N];
      double txg, tyg;

      txcg[0] = x0;
      tycg[0] = y0;

      for(int ig=1; ig<N; ig++){
          txcg[ig] = x(txcg[ig-1], tycg[ig-1], tR[ig-1], tV[ig-1]*t, (tPhi[ig-1]));
          tycg[ig] = y(txcg[ig-1], tycg[ig-1], tR[ig-1], tV[ig-1]*t, (tPhi[ig-1]));
          StdDraw.setPenRadius(EP_DOT);
          StdDraw.setPenColor(StdDraw.ORANGE);
          StdDraw.point(txcg[ig], tycg[ig]);
          StdDraw.setPenRadius(EP_LINE);
          StdDraw.setPenColor(StdDraw.DARK_GRAY);
          StdDraw.circle(txcg[ig], tycg[ig], tR[ig]);
      }
      // calcul de la position courante du point 'traçant' sur le dernier cercle
      txg = x(txcg[N-1], tycg[N-1], tR[N-1], tV[N-1]*t, (tPhi[N-1]));
      tyg = y(txcg[N-1], tycg[N-1], tR[N-1], tV[N-1]*t, (tPhi[N-1]));

      double[] result = new double[2];
      result[0] = txg;
      result[1] = tyg;
      return result;
    }

    /**
     * Calcule l'abscisse d'un point fixe sur un cercle tournant
     * @param xc abscisse du centre du cercle
     * @param yc ordonnée du centre du cercle
     * @param r rayon du cercle
     * @param t course angulaire du cercle depuis t=0
     * @param phi phase à l'origine
     * @return
     */
    static double x(double xc, double yc, double r, double t, double phi){
        return xc + r*Math.cos(t+phi);
    }

    /**
     * Calcule l'ordonnée d'un point fixé sur un cercle tournant
     * @param xc abscisse du centre du cercle
     * @param yc ordonnée du centre du cercle
     * @param r rayon du cercle
     * @param t course angulaire du cercle depuis t=0
     * @param phi phase à l'origine
     * @return
     */
    static double y(double xc, double yc, double r, double t, double phi){
        return yc + r*Math.sin(t+phi);
    }
}
