
// Beispiel 3: Beziers zeichnen
// Die eleganteste Möglichkeit, Béziers zu zeichnen ist:
// Klicken und ziehen, um die Linie zu zeichnen, dann wieder Klicken und ziehen,
// um jeweils einen Stützpunkt zu zeichnen.

#include <stdio.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include <graphics/gfx.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 <time.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <stream.h>


double Bezier(struct RastPort *, double,double,double,double,double,double,double,double,double,int);
void SetPoints(int status, int x,int y, 
	double &k1x, double &k1y, double &k2x, double &k2y, double &k3x, double &k3y, double &k4x, double &k4y);


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,
			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_MouseQueue, 1,
			WA_BlockPen, 4,			WA_DetailPen, 3,
			WA_Title, "Bezier-Kurven",
			TAG_END);

struct RastPort *rp = win->RPort;

char buf[255]; 
int status = 0;

void main() {
	if(!win || !scn) { CloseWindow(win); CloseScreen(scn); return };
	try {
		int mbpressed=FALSE, mmoved;
		char buf[50];
		int zaehler = 0;
		int mcx, mcy;
		double k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y;
		while(1) {
			// Mausabfrage auf die Hardcore-Art; heute: Message-Cracking, 
			// Eigene Funktionen... doch hier nur ein Traum

			Wait(1 << win->UserPort->mp_SigBit);
			struct IntuiMessage *imsg, icpy;
			mmoved = FALSE;
			while(imsg = (struct IntuiMessage*)GetMsg(win->UserPort)) {
				icpy = *imsg;
				ReplyMsg((struct Message*)imsg);
				mmoved = FALSE;
				switch(icpy.Class) {
					case IDCMP_CLOSEWINDOW:
						throw(int(1));		// kein guter Stil, aber einfach...
						break;
					case IDCMP_MOUSEBUTTONS:
						if(icpy.Code == SELECTDOWN) {
							mbpressed=TRUE;
							SetDrMd(rp,JAM2|COMPLEMENT);
							SetAPen(rp,2);
							if(status)
								Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,25,1);
							SetPoints(status,icpy.MouseX,icpy.MouseY,
											k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y);
							Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,25,1);
							sprintf(buf, "%1d (%3d,%3d),       Pressed", status, icpy.MouseX,icpy.MouseY);
							SetWindowTitles(win,buf,(STRPTR)-1);
							if(status<2) status++;
						} else if(icpy.Code == SELECTUP){
							mbpressed = FALSE; 
							Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,25,1);
							SetPoints(status,icpy.MouseX,icpy.MouseY,
											k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y);
							Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,25,1);
							if(status == 3) {
								Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,25,1);
								SetDrMd(rp, JAM2);
								SetAPen(rp, 1);
								Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,100,0);
							}
							status=(status+1)%4;
								
							sprintf(buf, "%1d (%3d,%3d),              ", status, icpy.MouseX,icpy.MouseY);
							SetWindowTitles(win,buf,(STRPTR)-1);
						}
						break;
					case IDCMP_MOUSEMOVE:
						mmoved = TRUE;
						mcx = icpy.MouseX;
						mcy = icpy.MouseY;
				}
			}
			if(mmoved && mbpressed) {
				sprintf(buf, "%1d (%3d,%3d), Moved Pressed", status, mcx,mcy);
				SetWindowTitles(win,buf,(STRPTR)-1);
				Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,25,1);
				SetPoints(status,mcx,mcy,
								k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y);
				Bezier(rp,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y,25,1);
				
			}					
		}
	} catch(int n) {
		if(n>1) cout << "Fehler "<< n << "\n";
		CloseWindow(win); CloseScreen(scn); return; 
	}
}

void SetPoints(int status, int x, int y, 
		double &k1x, double &k1y, double &k2x, double &k2y, double &k3x, double &k3y, double &k4x, double &k4y)
{
	switch(status) {
		case 0:	// Erster Fixpunkt
			k1x=k2x=k3x=k4x = x;
			k1y=k2y=k3y=k4y = y;
			break;
		case 1:	// Zweiter Fixpunkt
			k3x=k4x = x;
			k3y=k4y = y;
			break;
		case 2:	// erster Kontrollpunkt
			k2x=x;
			k2y=y;
			break;
		case 3:	// zweiter
			k3x=x;
			k3y=y;
			break;
	}
					
	sprintf(buf, "s: %d; (%3.1f,%3.1f),(%3.1f,%3.1f),(%3.1f,%3.1f),(%3.1f,%3.1f)",status,k1x,k1y,k2x,k2y,k3x,k3y,k4x,k4y);
	SetWindowTitles(win, (STRPTR)-1, buf);
}

struct Point {
	int x;
	int y;
};

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) 
{
	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;
	double bx, by, emt, emt2, t2, tm3, emtm3;
	const double rst = range*step;
	
	Move(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);
		Draw(rp,(long)bx,(long)by);
	}
	
	return range;
}


