// ============================================================
    //                         FONCTIONS
    // ============================================================
    void ApplyColorFirstRow(CubeInfo[,] cubes, float hauteurBasses, float hauteurMoyennes, float hauteurAigues)
    {
        //On effecte la premiere rangée des cubes du plancher (depth = 0)
        int largeur = cubes.GetLength(0);
        int idxMidRight = 0;
        int idxGraves = 3*largeur/4;
        int idxMidLeft = largeur/2;
        int idxAigues = largeur/4;
        float ampli = 0.1f;
        float g = 0f;
        float r = 0f;
        float b = 0f;
        float timeSin=Mathf.Sin(Time.time/7f);
        float fastTime=Mathf.Sin(Time.time*2);
        Renderer renderer;
        //Color col;

        ///Valeurs connues:
        /// MidsR:
        cubes[idxMidRight,0].lastColor = cubes[idxMidRight,0].GetComponent<Renderer>().material.GetColor("_Color");
        ampli = cubes[idxMidRight,0].transform.localScale.y;
        g =  ((timeSin*0.7f) * (ampli/6f))-0.4f;
        r = (ampli/6f) - (g);
        b = 0.8f - (ampli/3);
        renderer = cubes[idxMidRight, 0].GetComponent<Renderer>();
        renderer.material.color = new Color(r,g,b);

        //MidsL
        cubes[idxMidLeft,0].lastColor = cubes[idxMidLeft,0].GetComponent<Renderer>().material.GetColor("_Color");
        ampli = cubes[idxMidLeft,0].transform.localScale.y;
        g = ((timeSin*0.7f) * (ampli/6f))-0.4f;
        r = (ampli/6f) - (0.8f*g);
        b = 0.8f - (ampli/3);
        cubes[idxMidLeft,0].GetComponent<Renderer>().material.SetColor("_Color", new Color(r,g,b));

        // Graves:
        cubes[idxGraves,0].lastColor = cubes[idxGraves,0].GetComponent<Renderer>().material.GetColor("_Color");
        ampli = cubes[idxGraves,0].transform.localScale.y;
        g = ((0.8f - ((timeSin*0.7f) * (ampli/6)))-0.2f);
        r = (ampli/4f) - (0.8f*g);
        b = 0.8f - (ampli/3);
        cubes[idxGraves,0].GetComponent<Renderer>().material.SetColor("_Color", new Color(r,g,b));

        // Aigues:
        cubes[idxAigues,0].lastColor = cubes[idxAigues,0].GetComponent<Renderer>().material.GetColor("_Color");
        ampli = cubes[idxAigues,0].transform.localScale.y;
        g = (ampli/4f);
        r = 0.4f*fastTime - (ampli/6);
        b = 0.5f - (ampli/6);
        cubes[idxAigues,0].GetComponent<Renderer>().material.SetColor("_Color", new Color(r,g,b));

        float prop1 = 0f;
        float prop2 = 0f;

        //interpoller:
        int dist = idxAigues - idxMidRight;
        for(int i = idxMidRight+1; i<idxAigues; i++){

            prop1 = (1f - (float)(i-idxMidRight)/dist);
            prop2 = 1f-prop1;

            r = prop1*cubes[idxMidRight,0].GetComponent<Renderer>().material.GetColor("_Color").r + prop2*cubes[idxAigues,0].GetComponent<Renderer>().material.GetColor("_Color").r;
            g = prop1*cubes[idxMidRight,0].GetComponent<Renderer>().material.GetColor("_Color").g + prop2*cubes[idxAigues,0].GetComponent<Renderer>().material.GetColor("_Color").g;
            b = prop1*cubes[idxMidRight,0].GetComponent<Renderer>().material.GetColor("_Color").b + prop2*cubes[idxAigues,0].GetComponent<Renderer>().material.GetColor("_Color").b;
            cubes[i,0].lastColor = cubes[i,0].GetComponent<Renderer>().material.GetColor("_Color");
            cubes[i,0].GetComponent<Renderer>().material.SetColor("_Color", new Color(r,g,b));
        }

        dist = idxMidLeft - idxAigues;
        for(int i = idxAigues+1; i<idxMidLeft; i++){

            prop1 = (1f - (float)(i-idxAigues)/dist);
            prop2 = 1f-prop1;

            r = prop1*cubes[idxAigues,0].GetComponent<Renderer>().material.GetColor("_Color").r + prop2*cubes[idxMidLeft,0].GetComponent<Renderer>().material.GetColor("_Color").r;
            g = prop1*cubes[idxAigues,0].GetComponent<Renderer>().material.GetColor("_Color").g + prop2*cubes[idxMidLeft,0].GetComponent<Renderer>().material.GetColor("_Color").g;
            b = prop1*cubes[idxAigues,0].GetComponent<Renderer>().material.GetColor("_Color").b + prop2*cubes[idxMidLeft,0].GetComponent<Renderer>().material.GetColor("_Color").b;
            cubes[i,0].lastColor = cubes[i,0].GetComponent<Renderer>().material.GetColor("_Color");
            cubes[i,0].GetComponent<Renderer>().material.SetColor("_Color", new Color(r,g,b));
        }

        dist = idxGraves - idxMidLeft;
        for(int i = idxMidLeft+1; i<idxGraves; i++){

            prop1 = (1f - (float)(i-idxMidLeft)/dist);
            prop2 = 1f-prop1;

            r = prop1*cubes[idxMidLeft,0].GetComponent<Renderer>().material.GetColor("_Color").r + prop2*cubes[idxGraves,0].GetComponent<Renderer>().material.GetColor("_Color").r;
            g = prop1*cubes[idxMidLeft,0].GetComponent<Renderer>().material.GetColor("_Color").g + prop2*cubes[idxGraves,0].GetComponent<Renderer>().material.GetColor("_Color").g;
            b = prop1*cubes[idxMidLeft,0].GetComponent<Renderer>().material.GetColor("_Color").b + prop2*cubes[idxGraves,0].GetComponent<Renderer>().material.GetColor("_Color").b;
            cubes[i,0].lastColor = cubes[i,0].GetComponent<Renderer>().material.GetColor("_Color");
            cubes[i,0].GetComponent<Renderer>().material.SetColor("_Color", new Color(r,g,b));
        }

        dist = largeur - idxGraves;
        for(int i = idxGraves+1; i<largeur; i++){

            prop1 = (1f - (float)(i-idxGraves)/dist);
            prop2 = 1f-prop1;

            r = prop1*cubes[idxGraves,0].GetComponent<Renderer>().material.GetColor("_Color").r + prop2*cubes[idxMidRight,0].GetComponent<Renderer>().material.GetColor("_Color").r;
            g = prop1*cubes[idxGraves,0].GetComponent<Renderer>().material.GetColor("_Color").g + prop2*cubes[idxMidRight,0].GetComponent<Renderer>().material.GetColor("_Color").g;
            b = prop1*cubes[idxGraves,0].GetComponent<Renderer>().material.GetColor("_Color").b + prop2*cubes[idxMidRight,0].GetComponent<Renderer>().material.GetColor("_Color").b;
            cubes[i,0].lastColor = cubes[i,0].GetComponent<Renderer>().material.GetColor("_Color");
            cubes[i,0].GetComponent<Renderer>().material.SetColor("_Color", new Color(r,g,b));
        }
    }
    // ============================================================
    void CopyFirstRow(CubeInfo[,] original, CubeInfo[,] copy, int z0, int nZ)
    {
        for (int i = 0; i<original.GetLength(0); i++) {
            for (int j = z0; j<(z0+nZ); j++) {
                copy[i,j].lastScaleY = original[i,0].lastScaleY;
                copy[i,j].lastScaleZ = original[i,0].lastScaleZ;
                copy[i,j].transform.localScale = original[i,0].transform.localScale;
                copy[i,j].lastColor = original[i,0].lastColor;
                copy[i,j].GetComponent<Renderer>().material.SetColor("_Color",original[i,0].GetComponent<Renderer>().material.GetColor("_Color"));

            }
        }
    }
 // ============================================================
 void ApplyScaleWave(CubeInfo[,] cubes)
 {
     for(int i = 0; i<cubes.GetLength(0); i++){
         for(int j = 1; j<cubes.GetLength(1); j++)	{
             //Le jratio sert a prendre en compte l'effet conique. Si on ne divisait pas par jratio,
             //on aurait l'impression que les scale augmente avec la profondeur
             cubes[i,j].lastScaleY = cubes[i,j].transform.localScale.y / cubes[i,j].jRatio;
             cubes[i,j].lastScaleZ = cubes[i,j].transform.localScale.z;
             cubes[i,j].transform.localScale = new Vector3( cubes[i,j].jWidth, cubes[i,j].jRatio * cubes[i,j-1].lastScaleY, cubes[i,j-1].lastScaleZ) ;
         }
     }
 }
    // ============================================================
    void ApplyScaleFirstRow(CubeInfo[,] cubes, float hauteurBasses, float hauteurMoyennes, float hauteurAigues)
    {
        //On effecte la premiere rangée des cubes du plancher (depth = 0)
        int largeur = cubes.GetLength(0);
        int idxMidRight = 0;
        int idxGraves = 3*largeur/4;
        int idxMidLeft = largeur/2;
        int idxAigues = largeur/4;
        //float timeSin=Mathf.Sin(Time.time/7f);
        //float fastTime=Mathf.Sin(Time.time*2);

        ///Valeurs connues:
        /// MidsR:
        cubes[idxMidRight,0].lastScaleY = cubes[idxMidRight,0].transform.localScale.y;
        cubes[idxMidRight,0].lastScaleZ = cubes[idxMidRight,0].transform.localScale.z;

        cubes[idxMidRight,0].transform.localScale = new Vector3( cubes[idxMidRight,0].jWidth, hauteurMoyennes, 1.0f + hauteurMoyennes/5.0f) ;

        //MidsL
        cubes[idxMidLeft,0].lastScaleY = cubes[idxMidLeft,0].transform.localScale.y;
        cubes[idxMidLeft,0].lastScaleZ = cubes[idxMidLeft,0].transform.localScale.z;
        cubes[idxMidLeft,0].transform.localScale = new Vector3( cubes[idxMidLeft,0].jWidth, hauteurMoyennes,1.0f + hauteurMoyennes/5.0f) ;

        // Graves:
        cubes[idxGraves,0].lastScaleY = cubes[idxGraves,0].transform.localScale.y;
        cubes[idxGraves,0].lastScaleZ = cubes[idxGraves,0].transform.localScale.z;
        cubes[idxGraves,0].transform.localScale = new Vector3( cubes[idxGraves,0].jWidth, hauteurBasses,1.0f + hauteurBasses/5.0f) ;

        // Aigues:
        cubes[idxAigues,0].lastScaleY = cubes[idxAigues,0].transform.localScale.y;
        cubes[idxAigues,0].lastScaleZ = cubes[idxAigues,0].transform.localScale.z;
        cubes[idxAigues,0].transform.localScale = new Vector3( cubes[idxAigues,0].jWidth, hauteurAigues, 1.0f + hauteurAigues/5.0f) ;

        float prop1 = 0f;
        float prop2 = 0f;

        //interpoller:
        float scale = 0f;
        float scalez = 0f;
        int dist = idxAigues - idxMidRight;
        for(int i = idxMidRight+1; i<idxAigues; i++){
            cubes[i,0].lastScaleY = cubes[i,0].transform.localScale.y;
            cubes[i,0].lastScaleZ = cubes[i,0].transform.localScale.z;

            prop1 = (1f - (float)(i-idxMidRight)/dist);
            prop2 = 1f-prop1;
            scale = (prop1*hauteurMoyennes) + (prop2*hauteurAigues);
            scalez = (prop1*(1.0f + hauteurMoyennes/5.0f)) + (prop2*(1.0f + hauteurAigues/5.0f));
            cubes[i,0].transform.localScale = new Vector3( cubes[i,0].jWidth, scale, scalez) ;
        }

        dist = idxMidLeft - idxAigues;
        for(int i = idxAigues+1; i<idxMidLeft; i++){
            cubes[i,0].lastScaleY = cubes[i,0].transform.localScale.y;
            cubes[i,0].lastScaleZ = cubes[i,0].transform.localScale.z;

            prop1 = (1f - (float)(i-idxAigues)/dist);
            prop2 = 1f-prop1;
            scale = (prop1*hauteurAigues) + (prop2*hauteurMoyennes);
            scalez = (prop1*(1.0f + hauteurAigues/5.0f)) + (prop2*(1.0f + hauteurMoyennes/5.0f));
            cubes[i,0].transform.localScale = new Vector3( cubes[i,0].jWidth, scale, scalez) ;
        }

        dist = idxGraves - idxMidLeft;
        for(int i = idxMidLeft+1; i<idxGraves; i++){
            cubes[i,0].lastScaleY = cubes[i,0].transform.localScale.y;
            cubes[i,0].lastScaleZ = cubes[i,0].transform.localScale.z;

            prop1 = (1f - (float)(i-idxMidLeft)/dist);
            prop2 = 1f-prop1;
            scale = (prop1*hauteurMoyennes) + (prop2*hauteurBasses);
            scalez = (prop1*(1.0f+hauteurMoyennes/5.0f)) + (prop2*(1.0f+hauteurBasses/5.0f));
            cubes[i,0].transform.localScale = new Vector3( cubes[i,0].jWidth, scale, scalez) ;
        }

        dist = largeur - idxGraves;
        for(int i = idxGraves+1; i<largeur; i++){
            cubes[i,0].lastScaleY = cubes[i,0].transform.localScale.y;
            cubes[i,0].lastScaleZ = cubes[i,0].transform.localScale.z;

            prop1 = (1f - (float)(i-idxGraves)/dist);
            prop2 = 1f-prop1;
            scale = (prop1*hauteurBasses) + (prop2*hauteurMoyennes);
            scalez = (prop1*(1.0f + hauteurBasses/5.0f)) + (prop2*(1.0f + hauteurMoyennes/5.0f));
            cubes[i,0].transform.localScale = new Vector3( cubes[i,0].jWidth, scale, scalez);
        }
    }
 // ============================================================
 void ApplyColorWave(CubeInfo[,] cubes)
 {
     for(int i = 0; i<cubes.GetLength(0); i++){
         for(int j = 1; j<cubes.GetLength(1); j++){
             cubes[i,j].lastColor = cubes[i,j].GetComponent<Renderer>().material.GetColor("_Color");
             cubes[i,j].GetComponent<Renderer>().material.SetColor("_Color", cubes[i,j-1].lastColor);
         }
     }
 }
    private bool flexTunnel(CubeInfo[,] cubes1,CubeInfo[,] cubes2, float sinOffsetX, float sinOffsetY, int flexionID)
    {
        bool flexionDone = false;
        float amp = 1000f;

        // Si c'est la premiere frame de cette flexion, avertir TunnelSpinner pour qu'il cesse d'enregistrer la position
        if ( firstFlexFrame[flexionID]==true ) {
            firstFlexFrame[flexionID] = false;
            startFlexion();
        }

        if ((song.time() - song.flexionTime[flexionID])<Mathf.Min (2,song.flexionLength[flexionID]/3)) {
            // La force d'amplication du sinus commence a 0 et augmente jusqu'a 4
            amp = (song.time()-song.flexionTime[flexionID])/2;

        }
        else if ((song.time() - song.flexionTime[flexionID])<(song.flexionLength[flexionID]-4))  {

            amp = 1f;
        }
        else {
            // La force d'amplication du sinus diminue jusqu'a 0 (parabole inversée)
            amp = Mathf.Max ((song.flexionLength[flexionID]-(song.time()-song.flexionTime[flexionID]))/4,0);
        }

        for(int i = 0; i<cubes1.GetLength(0); i++){
            for(int j = 1; j<cubes1.GetLength(1); j++){
                cubes1[i,j].transform.localPosition = new Vector3 ((cubes1[i,j].posSansFlexion.x+ amp*Mathf.Pow((float)j,2f)*sinOffsetX),cubes1[i,j].posSansFlexion.y + amp*Mathf.Pow((float)j,2f)*sinOffsetY,cubes1[i,j].transform.localPosition.z);
            }
        }
        for(int i = 0; i<cubes2.GetLength(0); i++){
            for(int j = 1; j<cubes2.GetLength(1); j++){
                cubes2[i,j].transform.localPosition = new Vector3 ((cubes2[i,j].posSansFlexion.x+ amp*Mathf.Pow((float)j,2f)*sinOffsetX),cubes2[i,j].posSansFlexion.y+ amp*Mathf.Pow((float)j,2f)*sinOffsetY,cubes2[i,j].transform.localPosition.z);
            }
        }
        // Stop the flexion script when amp and the offset are small
        if (amp < 0.0001f){
            flexionDone = true;
            endFlexion();
        }
        return flexionDone;
    }