//111: Berücksichtigung der Neutralisierung zweier Elemente, die gleiche Frequenzen mit umgekehrtem Vorzeichen haben: 

//schon so lassen, dass sich harmonische gegenseitig anziehen, aber zusätzlich
//Pode und Antipode sich anziehen lassen, wenn sie in dissonanten Kontext

//********************************************************
//Neuproduktion von Partikel über die Gesamtanzahl regeln!
//********************************************************

import java.util.Random;

int GUETE = 0;
float KGUETE = 0.0f;
int[] KONTEXT = new int[16]; //dissonanter oder konsonanter Kontext jedes Tones

int MAXPARTIKEL = 32; //... in Beobachtungsebene

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

int SCHICHTEN = 7;
int DELTA = SCHICHTEN/7; 

int K = 100;
int KK = K*K;

int FARB = 256;
int FARBFARB = FARB*FARB;
int FARBMAX = FARB*FARB*FARB-1;

int NULLPUNKT = 127*FARBFARB;

PImage imgA = new PImage(K,K*(SCHICHTEN+1));
PImage imgB = new PImage(K,K*(SCHICHTEN+1));
PImage imgC = new PImage(K,K*(SCHICHTEN+1)); //Zeigen
PImage imgAA;
PImage imgBB;


int BREITE = 1400;
int HOEHE  = 700;

ArrayList<Partikel> par = new ArrayList<Partikel>();
Quelle quelle;

Kepler kepler = new Kepler();

public void setup()
{
    quelle = new Quelle();
  
    //size(BREITE,HOEHE);
    orientation(LANDSCAPE);
    frameRate(25);
}

float dt = 0.04f;
float D  = 40.5f;

int PANZ=0;
int ZZZ=0;
public void zeitschritt()
{
     //partikel in Beobachtungsebene zählen:
     int anz = 0;
     int[] px = imgC.pixels;
     for(int i=0;i<par.size();i++)
     {
          if(par.get(i).z>=(float)((SCHICHTEN+1)/2) && par.get(i).z<(float)((SCHICHTEN+1)/2+1))
              anz++;
     }
     PANZ=anz;
//     if(par.size()<MAXPARTIKEL)

     int TTT = (int)(0.08f/dt);

     if(anz<MAXPARTIKEL && ZZZ%TTT==0)
     {
         quelle.generieren();
     }    
     ZZZ++;    
     //Partikel entfernen, die oben angekommen sind:
  
     //neuer Wert:
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          p.x += p.vx*dt;
          p.y += p.vy*dt;
          p.z += p.vz*dt;

          if(p.x<-(float)(K/2))
             p.x=-(float)(K/2);
          if(p.x>(float)(K/2))
             p.x=(float)(K/2);
          if(p.y<-(float)(K/2))
             p.y=-(float)(K/2);
          if(p.y>(float)(K/2))
             p.y=(float)(K/2);

          if(p.z>(float)(SCHICHTEN+1))
             p.z=(float)(SCHICHTEN+1);
          if(p.z<0.0f)
             p.z=0.0f;

           /*  
          while(p.z>(float)(SCHICHTEN+1))
             p.z-=(float)(SCHICHTEN+1);
          if(p.z<0.0f)
             p.z=0.0f;
          */
          
          p.vx += p.ax*dt;
          p.vy += p.ay*dt;
          p.vz += p.az*dt;                   
     }

     //Partikel entfernen, die oben angekommen sind:
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          if(p.z>=(float)(SCHICHTEN+1))
              par.remove(i);
     }

     //Partikel entfernen, die unten am Rand sind:
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          if(p.z<=0.0f && p.x<=-(float)(K/2))
          {
              par.remove(i);
              i--;
          }    
          else if(p.z<=0.0f && p.y<=-(float)(K/2))
          {
              par.remove(i);
              i--;
          }    
          else if(p.z<=0.0f && p.x>=(float)(K/2))
          {
              par.remove(i);
              i--;
          }    
          else if(p.z<=0.0f && p.y>=(float)(K/2))
          {
              par.remove(i);
              i--;
          }    
     }

     //Partikel entfernen, die OBEN am Rand sind:
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          if(p.z>=(float)(SCHICHTEN) && p.x<=-(float)(K/2))
          {
              par.remove(i);
              i--;
          }    
          else if(p.z<=(float)(SCHICHTEN) && p.y<=-(float)(K/2))
          {
              par.remove(i);
              i--;
          }    
          else if(p.z<=(float)(SCHICHTEN) && p.x>=(float)(K/2))
          {
              par.remove(i);
              i--;
          }    
          if(p.z<=(float)(SCHICHTEN) && p.y>=(float)(K/2))
              par.remove(i);
     }


     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          
          p.ax = 0.0f;
          p.ay = 0.0f;
          p.az = 0.0f;
          
     }

     //gegenseitiges Abstoßen
/*     
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);

          for(int k=0;k<par.size();k++)
          {
                if(i!=k)
                {
                     Partikel q = par.get(k);
                     
                     float dist = sqrt((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)+(p.z-q.z)*(p.z-q.z));
                     if(dist<7.0f && dist>0.001f)
                     {
                          float ex = (p.x-q.x)/dist;
                          float ey = (p.y-q.y)/dist;
                          float ez = (p.z-q.z)/dist;
                          
                          float Fx = 0.003f*(7.0f-dist)*ex;
                          float Fy = 0.003f*(7.0f-dist)*ey;
                          float Fz = 0.003f*(7.0f-dist)*ez;
                          
                          p.ax += Fx;
                          p.ay += Fy;
                          p.az += Fz;
                          
                     }
                }
          }
     }
*/
     //gegenseitiges Anziehung Gravitation
     
     //Harmoniegrad:
     int guete = 0;

     for(int i=0;i<KONTEXT.length;i++)
         KONTEXT[i]=0; //weiter unten mit vermerken, wie gut jeder Ton in seinen zeitlichen Kontext passt
     
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);

          for(int k=0;k<par.size();k++)
          {
                if(i!=k)
                {
                     Partikel q = par.get(k);
                     
                     float dist = sqrt((p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)+(p.z-q.z)*(p.z-q.z));
                     if(dist<100.0f && dist>0.001f)
                     {
                          float ex = (p.x-q.x)/dist;
                          float ey = (p.y-q.y)/dist;
                          float ez = (p.z-q.z)/dist;
  
                          float faktor = kepler.faktorAnziehungskraftInline(p,q);
                          guete+=faktor;
//                          float faktor = 1.0f;

                          //Kontext jeder Tonhöhe (viermal vorhanden)
                          KONTEXT[p.TYP%16]+=faktor;
                          
                          float Fx = -faktor*0.008f*(100.0f-dist)*ex;
                          float Fy = -faktor*0.008f*(100.0f-dist)*ey;
                          float Fz = -faktor*0.008f*(100.0f-dist)*ez;
                          
                          p.ax += Fx;
                          p.ay += Fy;
                          p.az += Fz;
                          
                     }
                }
          }
     }
     GUETE=guete;

     //Zusammenziehen, bzw. Abstoßen der Antipoden, je nach harmonischem Kontext:
     float kguete = 0.0f;
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          int TYP1 = p.TYP%16;
          int SIGNUM1 = (p.TYP/16)%2;
          //Nächsten Antipoden suchen:
          Partikel qq = null;
          float rr = Float.MAX_VALUE;
          for(int k=0;k<par.size();k++)
          {
              Partikel q = par.get(k);
              int TYP2 = q.TYP%16;
              int SIGNUM2 = (q.TYP/16)%2;
              
              if(TYP1==TYP2 && SIGNUM1!=SIGNUM2)
              {
                   //Distanz bestimmen:
                   float dist = sqrt(  (p.x-q.x)*(p.x-q.x)+(p.y-q.y)*(p.y-q.y)  );
                   if(dist<rr)
                   {
                       qq=q;
                       rr=dist;
                   }
              }
              
              if(qq!=null)
              {
                  //Kraft etablieren:
                  if(rr<=20.0f)
                  {
                          float ex = (p.x-q.x)/rr;
                          float ey = (p.y-q.y)/rr;
                          float ez = (p.z-q.z)/rr;
  
                          float faktor = KONTEXT[TYP1];
                          if(faktor>0.0f)
                              kguete+=faktor/(0.1f+rr); //jenäher zusammen, desto besser bei Konsonanz
                          else    
                              kguete+=faktor*(0.0005f*rr); //je weiter auseinander bei Dissonanz desto schlechter
                          
                          float Fx = -faktor*0.000008f*(20.0f-rr)*ex;
                          float Fy = -faktor*0.000008f*(20.0f-rr)*ey;
                          float Fz = -faktor*0.000008f*(20.0f-rr)*ez;
                          
                          p.ax += Fx;
                          p.ay += Fy;
                          p.az += Fz;
                  }
                  //ENDE
              }
          }
     }
     KGUETE=kguete;
     
     //Abstoßung von Wänden
     
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          
          if(p.x<-(float)(K/2)+0.1f)
             p.ax += ((-(float)(K/2)+0.1f)-p.x);
          if(p.x>(float)(K/2)-0.1f)
             p.ax += (((float)(K/2)-0.1f) - p.x );
             
          if(p.y<-(float)(K/2)+0.1f)
             p.ay += ((-(float)(K/2)+0.1f)-p.y);
          if(p.y>(float)(K/2)-0.1f)
             p.ay += (((float)(K/2)-0.1f) - p.y );
             
          if(p.z<0.1f)
             p.az += (0.1f-p.z);
          if(p.z>(float)(SCHICHTEN+1)-0.1f)
             p.az += (((float)(SCHICHTEN+1)-0.1f) - p.z );
             
             
     }
     
     //Gravitation
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          
//          p.az -= 0.05;
          p.az = 10.4;
          
          //... und zum Zentrum hin:
          float rr = sqrt(  p.x*p.x+p.y*p.y  );
          if(rr>1.0f)
          {
             float ex = p.x/rr;
             float ey = p.y/rr;
             p.ax -= 6.4*ex;
             p.ay -= 6.4*ey;
          }
                    
     }     
     //Dämpfung und Bewegung nach oben:
     for(int i=0;i<par.size();i++)
     {
          Partikel p = par.get(i);
          
          p.ax += -D*p.vx;
          p.ay += -D*p.vy;
          p.az += -D*p.vz;// + 0.001f; 
          
     }
     
}



public void tragePartikelInBildEin(int[] pix, ArrayList<Partikel> par)
{
     for(int i=0;i<pix.length;i++)
         pix[i]=0;
  
     for(int i=0;i<par.size();i++)
     {
         Partikel p = par.get(i);
       
         int x = (int)p.x + K/2;  
         int y = (int)p.y + K/2;  
         int z = (int)p.z;
         
         if(x<0) x=0;
         if(y<0) y=0;
         if(z<0) z=0;
         
         if(x>=K-1) x=K-1;
         if(y>=K-1) y=K-1;
         if(z>SCHICHTEN) z=SCHICHTEN;
         
//         pix[x%K+(y%K)*K+(z%(SCHICHTEN+1))*KK]=FARBMAX;
         
         pix[x+y*K+z*KK]=FARBMAX;   
         
     }
}

public void draw()
{
    background(100);
    tragePartikelInBildEin(imgC.pixels, par);  
    imgC.updatePixels();    
    int x = 10;
    int y = 10;
    
    for(int i=0;i<=SCHICHTEN;i+=DELTA)
    {
        int yy = (i*K);
        image(imgC,0+x,0+y,K,K,0,yy,K-1,yy+K-1);
        x+=K+4;
        if(x+K>BREITE)
        {
             x=10;
             y+=K+4;
        }
    }

    //Beobachtete Schticht größer anzeigen:
    float OX = 120;
    float OY = 140;
    float FAK = 4.5;
    noStroke();
    fill(0);
    rect(OX-FAK*3.0f,OY-FAK*3.0f,K*FAK+FAK*6.0f,K*FAK+FAK*6.0f);
    fill(100,100,255);
    for(int i=0;i<par.size();i++)
    {
         Partikel p = par.get(i);
         
         int typ = p.TYP;
//         fill(50+(typ%4)*50,50+((typ/4)%4)*50,50+((typ/8)%4)*50);
         fill(165+(typ%4)*30,165+((typ/4)%4)*30,165+((typ/8)%4)*30);
         
         if(p.z>=(float)((SCHICHTEN+1)/2) && p.z<(float)((SCHICHTEN+1)/2+1))
         {
             float radius = 0.12f*(FAK+FAK*sqrt(p.x*p.x+p.y*p.y));
             ellipse(OX+(p.x+(float)(K/2))*FAK,OY+(p.y+(float)(K/2))*FAK,radius,radius);

         }
    }
    fill(255);
    text(""+PANZ,50,350);
    text("GUETE="+GUETE,50,400);
    text("KGUETE="+KGUETE,50,450);

    for(int i=0;i<10;i++)
      zeitschritt();
}
