/****************************************************************************
*                 KOCH-KURVEN    written 1988 by Rolf Beck
*                 zeichnet Koch-, Peano- und andere Fraktale Kurven
*
*     Compilieren: cc +p Koch             (Aztec C V3.6a)
*                  ln Koch -lml32 -lcl32
*
*     Aufruf:      Koch <Generator> <Initiator> <Iterationen> <Modus>
*                   Modus: Art des Generators :  
*                            0=normal,    1=gespiegelt,
*                            2=wechselnd, 3=zufällig
*
*     Beenden mit ESC
*/

#include <math.h>
#include <stdio.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <functions.h>
#include <intuition/intuition.h>

#define SCREEN_WIDTH 640
#define SCREEN_HEIGHT 530
#define PSIZE (ULONG)sizeof(struct Point)
#define PI 3.1415927
#define L1 2.64575

#define UP 0         /* Generator-Modi */
#define DOWN 1
#define CHANGE 2
#define RANDOM 3

#define Abstand(x1,y1,x2,y2) sqrt((double)((x1)-(x2))*((x1)-(x2))+((y1)-(y2))*((y1)-(y2)))

struct GfxBase *GfxBase;
struct IntuitionBase *IntuitionBase;

struct Window *Wind;
struct Screen *Scre;
struct RastPort *Rp;
struct ViewPort *Vp;

struct NewWindow NW = {
   0,0,SCREEN_WIDTH,SCREEN_HEIGHT,-1,-1,
   VANILLAKEY,
   BACKDROP|BORDERLESS|SMART_REFRESH,
   NULL,NULL,NULL,0,NULL,0,0,SCREEN_WIDTH,SCREEN_HEIGHT,CUSTOMSCREEN };

struct NewScreen NS = {
   0,0,SCREEN_WIDTH,SCREEN_HEIGHT,2,0,1,HIRES|LACE,CUSTOMSCREEN,NULL,
   (UBYTE *)"               ··· Koch-Kurven   1988 by Rolf Beck ··· "
   ,NULL,NULL };
   
struct Point   /* Punktkoordinaten */
{
   SHORT x;
   SHORT y;
};

UWORD Mult;		/* Anzahl Elemente des Generators */
UWORD Gen;              /* Nummer des Generators */
UWORD In;               /* Nummer des Initiators */
UBYTE Modus=UP;         /* Art der Generatoranwendung */
BYTE MM=1;              /* Wert, der die Generatoranwendung bestimmt */
struct Point *A1,*A2;   /* dynamische Arrays */

struct Point Initiator [][9] =
/* Format: {{X1,Y1},{X2,Y2}},{nächster Generator},... */
{
   {{320,20},{600,410},{40,410},{320,20}},             /* Dreieck groß */
   {{160,160},{480,160},{480,370},{160,370},{160,160}},/* Rechteck */
   {{0,265},{639,265}},                                /* Linie waagrecht */
   {{0,265},{639,265},{0,265}},                        /* doppelte Linie */
   {{220,100},{420,100},{540,265},
      {420,430},{220,430},{100,265},{220,100}},        /* Sechseck */
   {{320,100},{470,360},{170,360},{320,100}},          /* Dreieck klein */
   {{140,80},{320,80},{500,80},{500,260},{500,440},
      {320,440},{140,440},{140,260},{140,80}},         /* Quadrat unterteilt */
   {{0,265},{320,265},{639,265}},                      /* Linie unterteilt */
};
UBYTE InitAnz [] =       /* Anzahl der Punkte jedes Initiators */
{
   4,5,2,3,7,4,9,3
};

float Generator [][36] =
/* Format: { Länge (Nenner eines Bruches),Winkel in Bogenmaß, ... } , ... */
{
   { 3,0 ,3,-PI/3 ,3,PI/3 ,3,0 },
   { 4,0, 4,-PI/2, 4,0, 4,PI/2, 4,PI/2, 4,0, 4,-PI/2, 4,0 },
   { 4,0, 3,-17*PI/36, 3,17*PI/36 },
   { 6,0, 6,-PI/2, 6,-PI/2, 6,0, 6,0, 6,PI/2, 6,PI, 6,PI/2, 6,0, 6,0,
      6,PI/2, 6,PI, 6,PI/2, 6,0, 6,0, 6,-PI/2, 6,-PI/2, 6,0 },
   { 1.73205,-PI/6, 1.73205,PI/6 },
   { 12/5,0, 5/2,-17*PI/36, 5/2,17*PI/36 }, 
   { 3.4641,-PI/6, 3.4641,PI/6, 3.4641,PI/6, 3.4641,-PI/6 },
   { 5,0, 5,PI/2, 5,0, 5,-PI/3, 5,PI/3, 5,0, 5,-PI/2, 5,0 },
   { 2,PI/2, 2,0, 2,-PI/2, 2,-PI/3, 2,PI/3 },
   { 5,0, 5,PI/2, 5,PI*5/6, 5,PI/6, 5,PI/2, 5,0, 5,PI/3, 5,-PI/3, 5,0,
      5,-PI/2, 5,-PI/6, 5,-PI*5/6, 5,-PI/2, 5,0 },
   { 4,PI/3, 4,0, 4,-PI/3, 4,-PI/3, 4,0, 4,PI/3 },
   { 3,0, 3,PI/3, 3,-PI/3, 3,-PI, 3,-PI/3, 3,PI/3, 3,0 },
   { L1,-PI/9, L1,PI/4, L1,8*PI/9, L1,5*PI/9, L1,-PI/9, L1,-PI/9, L1,-4*PI/9 },
   { 3,PI/3, 3,PI/3, 3,0, 3,-PI/3, 1.5,-5*PI/6, 3,0, 3,0 },
   { 3,0, 6,PI/2, 6,PI, 6,PI/2, 3,0, 6,-PI/2, 6,PI, 6,-PI/2, 3,0,
      6,-PI/2, 6,PI, 6,-PI/2, 3,0, 6,PI/2, 6,PI, 6,PI/2, 3,0 },
   { 2.12132,PI/4, 3,-PI/2, 2.598076,-PI/6, 2.598076,PI/6 }
};
UBYTE GenAnz [] =        /* Mult-Wert für jeden Generator */
{
   4,8,4,18,2,4,4,8,5,14,6,7,7,7,17,4
};

void Cleanup()
{
   if(Wind) CloseWindow(Wind);
   if(Scre) CloseScreen(Scre);
   
   if(GfxBase) CloseLibrary(GfxBase);
   if(IntuitionBase) CloseLibrary(IntuitionBase);
}

void OpenAll ()
{
   if(!(IntuitionBase=(struct IntuitionBase *)
      OpenLibrary("intuition.library",0L)))  Cleanup();
   if(!(GfxBase=(struct GfxBase *)
      OpenLibrary("graphics.library",0L)))   Cleanup();

   if(!(Scre=OpenScreen(&NS)))   Cleanup();
   NW.Screen=Scre;
   if(!(Wind=OpenWindow(&NW)))   Cleanup();

   Rp=Wind->RPort;
   Vp=&(Scre->ViewPort);

   SetRGB4(Vp,0L,0L,0L,0L); SetRGB4(Vp,1L,2L,11L,4L);
   SetRGB4(Vp,2L,8L,8L,8L); SetRGB4(Vp,3L,12L,6L,5L);
}

float Winkel(x1,y1,x2,y2)
UWORD x1,y1,x2,y2;
   /* berechnet Winkel einer Geraden mit der Horizontalen */
{
   SHORT x,y;
   float Betrag,winkel;
   
   x=x2-x1; y=y2-y1;
   if(!(Betrag=sqrt((double)(x*x+y*y))))
      return(0);
   winkel=acos((double)(x/Betrag));
   if (y1>y2) 
      return(2*PI-winkel);
   else
      return(winkel);
}

void Generate(ind)
UCOUNT ind;
   /* holt 2 Punkte aus A1, verwendet Generator, legt Punkte in A2 ab */
{
   struct Point P;         /*aktueller Punkt */
   register float L,W,l,w; /*Ausgangswinkel,-länge ,aktueller Winkel, Länge */
   register UCOUNT i1,i2;  /*Schleifenvariable */
   
   P=A1[ind];
   L=Abstand (P.x,P.y,A1[ind+1].x,A1[ind+1].y);
   W=Winkel (P.x,P.y,A1[ind+1].x,A1[ind+1].y);

   for(i2=0,i1=1+ind*Mult;i2<2*Mult-2;i2+=2,i1++)
   {
      l=L/Generator [Gen][i2];
      w=W+Generator [Gen][i2+1]*MM;
      P.x= P.x+l*cos((double)w);
      P.y= P.y+l*sin((double)w);
      A2[i1]=P;
   }
   A2[i1]= A1[ind+1];
}

void main (argc,argv)
int argc;
char *argv[];
{
   UCOUNT n;            /* Anzahl der Iterationen */
   ULONG Anzahl;        /* Anzahl Punkte im Initiator */
   register UCOUNT i;   /* Schleifenzähler */
   struct IntuiMessage* msg;
   ULONG class;         /* Bestandteile der */
   USHORT code;         /* IDCMP-Message */
   BOOL Continue=TRUE;  /* kennzeichnet Programmende */

   if( argc!=5 || (In=atoi(argv[1]))>7 || 
   (Gen=atoi(argv[2]))>15 || (Modus=atoi(argv[4]))>RANDOM )
   {
      puts("Usage: Koch <Initiator> <Generator> <Level> <Modus>\n");
      exit(10);
   }
   n=atoi(argv[3]); 

   OpenAll();
   srand((unsigned int)time((long *)0L));

   A1=A2=0L;

   
   Mult=GenAnz[Gen]; Anzahl=InitAnz[In];
   if(!(A1=AllocMem((long)Anzahl*PSIZE,(ULONG)MEMF_FAST|MEMF_PUBLIC)))
      Cleanup();
   movmem((char *)&Initiator[In][0],(char *)A1,(int)(Anzahl*PSIZE));
   
   switch(Modus)
   {
    case UP:
      MM=1;
      break;
    case DOWN:
      MM=-1;
   }
   while (n--)
   {
      if(!(A2=AllocMem( (long)(Mult*(Anzahl-1)+1)*PSIZE ,(long)MEMF_FAST|MEMF_PUBLIC)))
         Cleanup();
      A2[0]= A1[0];
      
      if (Modus==CHANGE)
         MM=-MM;
      
      for(i=0;i<Anzahl-1;i++)
      {
         if (Modus==RANDOM)
            MM=(rand()&1)? 1:-1;
         Generate(i);
      }
      
      FreeMem(A1,(long)Anzahl*PSIZE);
      A1= A2;
      Anzahl= Mult*(Anzahl-1)+1;

      SetRast(Rp,0L);
      SetAPen(Rp,1L);
      Move(Rp,(long)A1[0].x,(long)A1[0].y);
      PolyDraw(Rp,(long)Anzahl,A1);
   }
   FreeMem(A1,(long)Anzahl*PSIZE);

   while(Continue)
   {
      Wait(1L<<Wind->UserPort->mp_SigBit);
      while(msg=(struct IntuiMessage*)GetMsg(Wind->UserPort))
      {
         class=msg->Class;
         code=msg->Code;
         ReplyMsg(msg);

         switch(class)
         {
          case VANILLAKEY:
            if (code==27)  /* ESC */
               Continue=FALSE;
            break;
          default:
            fputs("Unbekannte Message\n",stderr);
         }
      }
   }
   Cleanup();
}
