//Idee:
//Bewegung radial auf Zielring mit größter passender Harmonie.

// ***************************************************************
//  Kepler104: Töne und Antitöne sollen sich gegenseitig anziehen.
// ***************************************************************

//Neuerungen:

//    Links: Schnitt, Rechts: Projektion Partikel
//    RuKu
//    Quelle NICHT zufällig
//    Partikel Z-sortiert
//    Rendern: >1/10 Vschall => 0

//    Alle Parameter fixiert
import java.io.File;
import java.io.PrintWriter;
import java.io.FileWriter;
import java.util.Random; //kein Zufallsprozeß, nur so deterministisches Chaos

private Random zufall = new Random(System.currentTimeMillis());

//*** KONSTANTEN ***
String BASISPFAD = "/home/fhbstud/sketchbook/Kepler114/data";
int    FRAMERATE = 25;
double DT = 1.0/(double)FRAMERATE; // 0.04
int    SAMPLERATE = 44100;
int BREITE = 1280;
int HOEHE  = 720;
float VSCHALL = 334.0;

int N_PULSE = 5; //Aufteilung einer Sekunde in N Pulse
int ANZAHL_TOENE = 64; //0..15: Positon, 16..31 Antiton, 32..47 Positon, 48..63 Antiton

// 1 Pixel == 1Meter, sichtbaerer Bereich: +/- 334 + Rand
float RAND = 16.0f;
float XOFF = VSCHALL + RAND + 20.0f; // == 350 + 20 //Mitte des linken Bereichs
float YOFF = VSCHALL + RAND + 10.0f; // == 350 + 10 (y-Richtung umdrehen!!!)
float XOFF_3D = 2.0f*(VSCHALL + RAND) + 20.0f + 30.0f; // 750
float YOFF_3D = HOEHE-10.0f; //unterer Bildrand (...y-Richtung umdrehen!!!)

//float BREITE_3D = BREITE - XOFF_3D - 80.0f; //  1280 - 700 - 80 == 500
//float HOEHE_3D  = HOEHE            - 20.0f; //  720 - 20        == 700
float BREITE_3D = 500.0f; //  1280 - 700 - 80 == 500
float HOEHE_3D  = 700.0f; //  720 - 20        == 700
float BREITE_SCHNITT = 700.0f;
float HOEHE_SCHNITT  = 700.0f;

float ZMAX3D = 980.0f;

//Aufteilung x:           20 + 700 + 30 + 500 + 30 == 1280
//Aufteilung y Schnitt:   10 + 700 + 10 == 720
//Aufteilung y 3D:        10 + 700 + 10 == 720

//*** PARAMETER ***

boolean RENDERN = true;

int[][] harmonisch = {{1,1},{1,2},{1,3},{1,4},{1,5},{1,6},
                                  {2,3},{3,4},{4,5},{5,6},
                                              {3,5},      {3,8},{5,8}};

//                   70Hz          105Hz          140Hz        175Hz
int[] samples = {(2*1*3*3*5*7),(2*2*3*1*5*7),(1*1*3*3*5*7),(2*2*3*3*1*7),
//                  205Hz          240Hz          315Hz        350Hz
                 (2*1*3*1*5*7),(2*2*3*3*5*1),(2*2*1*1*5*7),(2*1*3*3*1*7),
//                  420Hz          490Hz          525Hz        630Hz 
                 (1*1*3*1*5*7),(2*1*3*3*5*1),(2*2*3*1*1*7),(2*1*1*1*5*7),
//                  700Hz          735Hz          980Hz       1050Hz                    
                 (1*1*3*3*1*7),(2*2*3*1*5*1),(1*1*3*3*5*1),(2*1*3*1*1*7)};
                                              
int[] frequenzen = {70,105,140,175,  205,240,315,350,  420,490,525,630,  700,735,980,1050};



//Wo und wieviel wird rechts herausgwschnitten und links dargestellt:
double SCHNITTHOEHE = (double)(ZMAX3D*0.5f);
double SCHNITTDICKE = 100.0;


int MAXBILDER = 2000; //Simulationsdauer == MAXBILDER*0.04s 400=>16Sekunden
int MAXPARTIKEL = 320;  //Index%64 == TYP!!!! 
double DAEMPFUNG = 0.65;

//*** GLOBALE LAUFZEITVARIABLEN ***
int vSCHRITTE = 0; //bisher durchgeführte Simulationsschritte 
double[][] merker = new double[MAXBILDER][ANZAHL_TOENE*2]; 

//Simulation:
double[] yyneu = new double[MAXPARTIKEL*6]; 
double[] yyalt = new double[MAXPARTIKEL*6]; 
double[] yyalt_rot = new double[MAXPARTIKEL*6]; //als "Helix" rotieren lassen! 
double OMEGA = 1.0f;
double[] yywerte = new double[MAXPARTIKEL]; //zum Sortieren Vorder- / Hintetgrund

Partikel partikel = new Partikel(MAXPARTIKEL,yyalt,DAEMPFUNG); //Nicht benutzte liegen unten bei (0|0) ohne Kraft!
Integrator integrator = new Integrator(partikel);
Msort msort = new Msort(MAXPARTIKEL);
double tt = 0.0;
int GESTARTET = 0; //Partikel, die losgeschickt wurden

Kepler kepler = new Kepler();
KeplersortierungZ keplerZ = new KeplersortierungZ();

Analyse analyse = new Analyse(MAXPARTIKEL);

boolean[] anaus = new boolean[MAXPARTIKEL];

public void setup()
{
    for(int i=0;i<anaus.length;i++)
       if(zufall.nextInt(100)<20)
           anaus[i] = true;
       else    
           anaus[i] = false;
  
//    size(BREITE,HOEHE);
    orientation(LANDSCAPE);
    frameRate(FRAMERATE);
    strokeWeight(3.0f);
}

double[] xy_umstuelp = new double[2];

double[] dist_integral = new double[MAXPARTIKEL];
double tx=0.0;

double[] TTTT = {6.0,600.0,602.0,1000.0,1004.0};
//double[] TTTT = {6.0,1000.0,1002.0,1600.0,1604.0};

public void draw()
{
    background(200);
    noStroke();
    
    //Bereich für Schnitt:
    fill(0);
    rect(20.0f,10.0f,BREITE_SCHNITT,HOEHE_SCHNITT);
    
    //Bereich für 3D-Partikel
    fill(100);
    rect(XOFF_3D,10.0f,BREITE_3D,HOEHE_3D);

    //Schnittberech rechts einzeichnen:
    float ymin = (float)((5.0/7.0)*(SCHNITTHOEHE-SCHNITTDICKE));
    float ymax = (float)((5.0/7.0)*(SCHNITTHOEHE+SCHNITTDICKE));
    fill(0);
    rect(XOFF_3D,YOFF_3D-ymax,BREITE_3D,ymax-ymin);

    // *** Simulation KRÄFTE ***
    if(GESTARTET<MAXPARTIKEL)
    {
        //Immer nacheinander 5 Bahnen durchgehen:
        double r = (double)(GESTARTET%N_PULSE+1)*VSCHALL/(double)N_PULSE; //  1..5 * 1/5 * 334
        double phi = -(double)(GESTARTET%(N_PULSE*6))*2.0*Math.PI/(double)(N_PULSE*6);  //360 Grad / 30 (30==5*6)
//        double phi = (double)(GESTARTET%16)*2.0*Math.PI/16.0;  //
        double x = r*Math.cos(phi);
        double y = r*Math.sin(phi);
        //partikel.setzeZustand(int nr, double x, double y, double z, 
        //                             double vx, double vy, double vz, 
        //                             double Fx, double Fy, double Fz)
        
        partikel.setzeZustand(GESTARTET, x, y, 0.0, 
                                         0.0,0.0,0.0, 
                                         0.0,0.0,50.0);
        GESTARTET++;
        tx=tt;        
    }
    
    //Harmoniierung durch Driften zu verschiedenen Ringen
    if(GESTARTET==MAXPARTIKEL)
    {
        //Gravitation abstoßend / anziehend:
        for(int i=0;i<GESTARTET;i++)
        {
             double Fx = 0.0;
             double Fy = 0.0;
             double Fz = 50.0;
          
             double x1 = yyalt[i*6+0]; 
             double y1 = yyalt[i*6+2]; 
             double z1 = yyalt[i*6+4];
             
             double KANTE = VSCHALL/(double)N_PULSE;

             //Z-Sortierung
             
             
             // ENDE
             
             for(int k=0;k<GESTARTET;k++)
             {
                 if(i!=k)
                 {
                     double x2 = yyalt[k*6+0]; 
                     double y2 = yyalt[k*6+2]; 
                     double z2 = yyalt[k*6+4];
                     
                     double dist = Math.sqrt( (x1-x2)*(x1-x2) + (y1-y2)*(y1-y2) + (z1-z2)*(z1-z2) );
                     if(dist>0.0)
                     {
                          double ex = (x2-x1)/dist;
                          double ey = (y2-y1)/dist;
                          double ez = (z2-z1)/dist;
                          
                          if(dist<VSCHALL) //Anziehen
                          {
                                double faktor = (dist-KANTE);
                                /*
                                if(faktor>0.0)
                                    faktor = 30.0/(1.0+faktor);
                                if(faktor<0.0)
                                    faktor = 30.0/(-1.0+faktor);
                                */
                                
                                if(tt<tx+DT*TTTT[0])
                                {
                                    faktor*=0.1;
                                }    
                                else if(tt<tx+DT*TTTT[1]) //Keplersortierung in Z-Richtung
                                {
                                    faktor*=0.00001;
                                                                            
                                }    
                                else if(tt<tx+DT*TTTT[2])
                                {
                                    faktor*=0.1;
                                }    
                                else if(tt<tx+DT*TTTT[3]) //Keplersortierung in Z-Richtung
                                {
                                    faktor*=0.01;
                                }    
                                else if(tt<tx+DT*TTTT[4])
                                {
                                    faktor*=5.0;
                                }    
                                else  //Keplersortierung in Z-Richtung
                                {
                                    faktor*=0.000001;
                                }
                                
                                Fx+=faktor*ex;
                                Fy+=faktor*ey;
                                Fz+=0.25*faktor*ez;
                          }
                     }
                 }
             }
             
             partikel.setzeKraft(i,Fx,Fy,Fz);
        }
    }
    
    // *** ENDE Simulation KRÄFTE ***

    //Partikel, die den oberen Rand erreichen nach unten setzen:
    for(int i=0;i<yyalt.length;i+=6)
        if(yyalt[i+4]>ZMAX3D)
            yyalt[i+4]=0.0f;

    //Helix drehen lassen:
    double phi_rot = OMEGA*tt;
    for(int i=0;i<yyalt.length;i+=6)
    {            
         yyalt_rot[i+0] = Math.cos(phi_rot)*yyalt[i+0] - Math.sin(phi_rot)*yyalt[i+2];
         yyalt_rot[i+1] = yyalt[i+1];  //noch falsch, müßte bei bedarf auch analythisch bestimmt werden
         yyalt_rot[i+2] = Math.sin(phi_rot)*yyalt[i+0] + Math.cos(phi_rot)*yyalt[i+2];
         yyalt_rot[i+3] = yyalt[i+3];
         yyalt_rot[i+4] = yyalt[i+4];
         yyalt_rot[i+5] = yyalt[i+5];
    }

    //Der Y-Position nach sortieren:
    for(int i=0;i<yywerte.length;i++)
       yywerte[i] = yyalt_rot[i*6+2]; //links ist ja der Schnitt durch die xy-Ebene!
    int[] index = msort.nummern(yywerte);

    //Testausgabe:
    //for(int i=0;i<5;i++)
    //{
    //      println("x="+yyalt[i*6+0]+" y="+yyalt[i*6+2]+" z="+yyalt[i*6+4]);          
    //}
    //println();
    
    //rechts partikel gemäß Anordnung in y-Richtung einzeichnen, kleinste zuerst:
    for(int ii=0;ii<index.length;ii++)
    {
        int i=index[ii];
        //Koordinatentransformation:
        if(yyalt_rot[i*6+4]<980.0 && yyalt_rot[i*6+4]>0.0)
        {
            float xxx = (float)((yyalt_rot[i*6+0]+350.0)*5.0/7.0) + XOFF_3D;
            //Helligkeit und Größe gemäß y-Position:
            float yyy = (float)(-yyalt_rot[i*6+2])+350.0f;  //y-Achse falsche Richtung!!
            if(yyy<0.0f)
                yyy=0.0f;
            if(yyy>700.0f)
                yyy=700.0f;
            float DD = 10.0f + yyy/5.0f; //Durchmesser 10..150
            fill(DD+100.0f);
            float zzz = YOFF_3D - (float)(yyalt_rot[i*6+4]*5.0/7.0);  //700*7/5 == 980
            ellipse(xxx,zzz,0.1f*DD,0.1f*DD);
        }    
    }

    //Herausgeschnittene Scheibe links darstellen und später auch merken:
    fill(255);
    for(int i=0;i<yyalt_rot.length;i+=6)
    {
          if(yyalt_rot[i+4]>=SCHNITTHOEHE-SCHNITTDICKE*0.5 && yyalt_rot[i+4]<=SCHNITTHOEHE+SCHNITTDICKE*0.5)
          {
                
                int TYP = (i/6)%64;

//MERKER        
                if(vSCHRITTE<MAXBILDER && RENDERN==true)
                {
                    merker[vSCHRITTE][TYP*2+0] = yyalt_rot[i+0];
                    merker[vSCHRITTE][TYP*2+1] = yyalt_rot[i+2];
                }
                float xk = (float)yyalt_rot[i+0];
                float yk = (float)(-yyalt_rot[i+2]);
                
                //Testen: Umstülpen
                /*
                if((TYP/16)%2==1)
                {
                    float xk_auf = (float)yyalt_rot[i-16*6+0];
                    float yk_auf = (float)(-yyalt_rot[i-16*6+2]);
                    //double[] xy_umstuelp = new double[2];
                    umstuelpen(xy_umstuelp, (double)xk, (double)yk, (double)xk_auf, (double)yk_auf, HW[TYP%16]);
                    xk = (float)xy_umstuelp[0];
                    yk = (float)xy_umstuelp[1];                    
                }
                */
                float xxx = XOFF+xk;
                float yyy = YOFF+yk;

                float zusatz = 0.0f;
                if((TYP/16)%2==0)
                {
                   fill(TONFARBEN[TYP][0],TONFARBEN[TYP][1],TONFARBEN[TYP][2]);
                   noStroke();
                }
                else
                {
                   stroke(TONFARBEN[TYP][0],TONFARBEN[TYP][1],TONFARBEN[TYP][2]);
                   noFill();
                   zusatz = 6.0f; //Ring etwas größer, so, dass beides bei Deckung zu sehen unterscheiden ist.
                }
                ellipse(xxx,yyy,zusatz+10.0f+8.0f*(float)HW[TYP%16],zusatz+10.0f+8.0f*(float)HW[TYP%16]);
          }
    }
    noStroke();
    //Elemente außerhalb des Sichtbereiches verdecken:
    fill(200);
    rect(0,0,BREITE,10);
    rect(0,HOEHE-10,BREITE,10);
    
    integrator.zeitschritt(yyneu,yyalt,tt,DT);
    for(int i=0;i<yyneu.length;i++)
        yyalt[i] = yyneu[i];
    tt+=DT;

    if(vSCHRITTE<MAXBILDER && RENDERN==true)
    {
//         save("data/bild"+vSCHRITTE+".png");
    }
        
    vSCHRITTE++;

    if(vSCHRITTE==MAXBILDER && RENDERN==true)
    {
//        new Rendern(merker);
        new Rendern2(merker);
    }
}
