
// Bézier-Kurven; (C) C. Marschner, 1996

// PsZeichnen:
// Zeichnet nach den Informatinen im Array (s.u.) den Buchstaben "P"
// in verschiedenen Größen 

#include <intuition/intuition.h>
#include <graphics/rastport.h>
#include <graphics/gfxmacros.h>
#include <pragma/intuition_lib.h>
#include <pragma/exec_lib.h>
#include <pragma/graphics_lib.h>
#include <pragma/dos_lib.h>
#include <math.h>
#include <stream.h>


double Bezier(struct RastPort *, double,double,double,double,
                                            double,double,double,double,double,int);


// Die Daten, um leichter skaliert werden zu können, etwa zwischen 0 u. 1
static double p[][4][2] = { // "P"
    {{ 0.000,0.000},{0.150,0.050},  {0.200,0.050},  {0.250,0.030}},
    {{ 0.250,0.030},{0.300,0.010},  {0.350,0.000},  {0.400,0.000}},
    {{ 0.400,0.000},{0.700,0.000},  {0.775,0.250},  {0.775,0.400}},
    {{ 0.775,0.400},{0.775,0.650},  {0.700,0.700},  {0.450,0.700}},
    {{ 0.450,0.700},{0.350,0.700},  {0.330,0.660},  {0.270,0.660}},
    {{ 0.270,0.660},{0.230,0.660},  {0.200,0.660},  {0.200,0.700}},
    {{ 0.200,0.700},{0.200,0.700},  {0.200,0.950},  {0.200,0.950}},
    {{ 0.200,0.950},{0.180,1.000},  {0.160,1.000},  {0.250,1.030}},
    {{ 0.250,1.030},{0.320,1.045},  {0.340,1.040},  {0.360,1.075}},
    {{ 0.360,1.075},{0.170,1.040},  {0.100,1.030},  {-0.08,1.100}},
    {{ -0.08,1.100},{-0.10,1.050},  {-0.06,1.025},  {0.030,1.025}},
    {{ 0.030,1.025},{0.100,1.010},  {0.100,1.010},  {0.100,0.950}},
    {{ 0.100,0.950},{0.100,0.950},  {0.100,0.200},  {0.100,0.200}},
    {{ 0.100,0.200},{0.100,0.110},  {0.090,0.100},  {0.000,0.100}},
    {{ 0.000,0.100},{0.000,0.010},  {0.000,0.000},  {0.000,0.000}},
        
    {{ 0.200,0.150},{0.210,0.080},  {0.300,0.050},  {0.450,0.050}},
    {{ 0.450,0.050},{0.590,0.050},  {0.650,0.150},  {0.650,0.400}},
    {{ 0.650,0.400},{0.650,0.600},  {0.630,0.650},  {0.450,0.650}},
    {{ 0.450,0.650},{0.400,0.650},  {0.400,0.650},  {0.350,0.630}},
    {{ 0.350,0.630},{0.260,0.605},  {0.300,0.610},  {0.200,0.610}},
    {{ 0.200,0.610},{0.200,0.610},  {0.200,0.150},  {0.200,0.150}},
};

/*
struct Screen *scn = OpenScreenTags
(NULL,
    SA_Depth, 2,                                SA_Width, 640,                              
    SA_Height, 523,                         SA_Quiet, TRUE,     
    SA_Type, CUSTOMSCREEN|AUTOSCROLL,   SA_DisplayID, PAL_MONITOR_ID|HIRESLACE_KEY,                 
    SA_Pens, pens,
    TAG_END
);
*/

struct Window *win =  OpenWindowTags
(NULL, 
            WA_Flags, WFLG_ACTIVATE|WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_DRAGBAR|
                         WFLG_NOCAREREFRESH|WFLG_SIMPLE_REFRESH|
                         WFLG_DEPTHGADGET|WFLG_REPORTMOUSE,
            WA_Top, 11,            WA_Width, 640,
            WA_Height, 512,        WA_Left, 0,
            WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_MOUSEBUTTONS|IDCMP_MOUSEMOVE,
       /*     WA_CustomScreen, scn, */
            WA_BlockPen, 4, WA_DetailPen, 3,
            WA_Title, (char*)"Bézier-Kurven...",
            TAG_END
);

struct RastPort *rp = win->RPort;
int status = 0;


void main() {
    double k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y;
    int zaehler = 0;
    TmpRas tmpr;
    AreaInfo ai;

    if(!win) { CloseWindow(win); return };
    try {
        SetAPen(rp,1);
        // Das Programm benutzt AreaMove/AreaDraw, um den Buchstaben zu füllen.
        // Dazu muß erstmal ein TmpRas besorgt, eine AreaInfo fertiggemacht und
        // das ganze in den RastPort eingetragen werden

        // 1. Raster für AreaDraw fertigmachen:

        PLANEPTR tr = AllocRaster(640,512);             // anfordern
        if(!tr)                                                 // -> Fehler Nummer drei auswerfen
            throw(3);
        InitTmpRas(&tmpr, tr, RASSIZE(640,512));        // initialisieren
        rp->TmpRas = &tmpr;                                 // und eintragen
        char *buffer = (char*)new WORD[25*5*21+50]; // Puffer für Koordinaten
        InitArea(&ai, buffer, 50*21);                       // AreaInfo-Liste
        AreaInfo *old = rp->AreaInfo;                   // und austauschen
        rp->AreaInfo = &ai;
        BNDRYOFF(rp);                                           // Ausschalten des Rahmen-Pens

        // 2. Jetzt kann AreaMove/Draw benutzt werden.

        // 6 mal zeichnen
        for(int j = 0; j < 6; j++) {                        
            double range = 0;
            SetAPen(rp,1);  
            // Das äußere vom "P": zwei Reihen a 3 Buchstaben; 6 Größen
            for(int i=0; i<15; i++) 
            {
                // Die Transformation ist weniger kompliziert, als sie aussieht
                // Die Figur wird auf 150 Punkt gestreckt, wobei bei jedem weiteren
                // "P" die Figur um 15 Pkt. größer wird. Schließlich wird noch der
                // Nullpunkt verschoben, wobei ab j=3 eine zweite Zeile aufgemacht wird
                k1x = p[i][0][0]    * (120.0+ 20*j) + 50.0 + 160.0 * (j<3?j:j-3);
                k1y = p[i][0][1]    * (120.0+ 20*j) + 50.0 + 190.0 * (j>2);
                k2x = p[i][1][0]    * (120.0+ 20*j) + 50.0 + 160.0 * (j<3?j:j-3); 
                k2y = p[i][1][1]    * (120.0+ 20*j) + 50.0 + 190.0 * (j>2); 
                k3x = p[i][2][0]    * (120.0+ 20*j) + 50.0 + 160.0 * (j<3?j:j-3); 
                k3y = p[i][2][1]    * (120.0+ 20*j) + 50.0 + 190.0 * (j>2); 
                k4x = p[i][3][0]    * (120.0+ 20*j) + 50.0 + 160.0 * (j<3?j:j-3);
                k4y = p[i][3][1]    * (120.0+ 20*j) + 50.0 + 190.0 * (j>2);
                if(!i) AreaMove(rp,(long)k1x,(long)k1y);
                Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,0,0);
            }
            AreaEnd(rp);
            SetAPen(rp,0);
            for(i=15; i<21; i++) {
                k1x = p[i][0][0]    * (120.0+20*j) + 50.0 + 160.0 * (j<3?j:j-3);
                k1y = p[i][0][1]    * (120.0+20*j) + 50.0 + 190.0 * (j>2);
                k2x = p[i][1][0]    * (120.0+20*j) + 50.0 + 160.0 * (j<3?j:j-3); 
                k2y = p[i][1][1]    * (120.0+20*j) + 50.0 + 190.0 * (j>2); 
                k3x = p[i][2][0]    * (120.0+20*j) + 50.0 + 160.0 * (j<3?j:j-3); 
                k3y = p[i][2][1]    * (120.0+20*j) + 50.0 + 190.0 * (j>2); 
                k4x = p[i][3][0]    * (120.0+20*j) + 50.0 + 160.0 * (j<3?j:j-3);
                k4y = p[i][3][1]    * (120.0+20*j) + 50.0 + 190.0 * (j>2);
                if(i==15) AreaMove(rp,(long)k1x,(long)k1y);
                Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,0,0);
            }
            AreaEnd(rp);
            
        }
        Delay(250);             // 5 Sekunden warten

        // 3. alte Werte wieder zurückschreiben:

        delete buffer;
        rp->AreaInfo = old;
        rp->TmpRas = 0;
        FreeRaster(tr,640,512);     
    } catch(int n) { 
        if(n>1) cout << "Fehler "<< n << "\n";
    }
    CloseWindow(win);
}


double Bezier(struct RastPort *rp, 
    double k0x,double k0y,double k1x,double k1y,
    double k2x,double k2y,double k3x,double k3y, double nr,int flag) 
{
    double bx, by, emt, emt2, t2, tm3, emtm3;

    // improvisierte Abschätzung der Schrittweite:
    const double range = 
        floor(10+(fabs(k0y-k1y)+fabs(k0x-k1x)+fabs(k1x-k2x)+
                        fabs(k1y-k2y)+fabs(k2x-k3x)+fabs(k2y-k3y))/30.0);
    const double step = 1.0/range;
    const double rst = range*step;  // Konstante z. schnelleren Bearbeiten
    
    AreaDraw(rp,(long)k0x,(long)k0y);
    
    for(double t = step; t <= rst; t+= step) 
    {
        emt = 1-t; emt2=emt*emt; t2=t*t; tm3=3*t; emtm3 = 3*emt;
        bx = emt2*(emt*k0x + tm3*k1x) + t2*(emtm3*k2x + t*k3x);
        by = emt2*(emt*k0y + tm3*k1y) + t2*(emtm3*k2y + t*k3y);
        AreaDraw(rp,(long)bx,(long)by);
    }
    
    return range;
}

