/*
 * Orezavanie trojuholnikov a useciek - Richard Richter 97703
 *
 * Budem mat niekolko useciek a niekolko polygonov,
 * ktore budu na zaciatku trojuholniky. :-) Tieto budu umiestnene na
 * ploche (0,0)-(640,480), pricom vyrez bude v oblasti (160,120)-(480,360).
 * Budem mat dve tlacitka (o.i.) - Vykresli a Orez... to prve vykresli
 * scenu bez zmien. To druhe orezanu scenu - len ten vyrez. Orezavanie
 * robim na urovni objektov. Usecky orezavam Cohen-Sutherlandom a polygony
 * (u nas len trojuholniky) Sutherland-Hodgmanom...
 * Nova scena vygeneruje nove rozlozenie ciar a trojuholnikov v scene.
 */

#include <fox/fx.h>
struct FXmyPoint { FXuint x,y; };

typedef	FXPoint		my_line[2];
typedef FXPoint		my_poly[8];	// 8 kvoli orezavaniu
/*
 * Hoci po orezani trojuholnika moze byt maximalne sedemuholnik, pre
 * jednoduchost je "poposledny" bod (a teda v najhorsom pripade osmy)
 * rovnaky ako prvy... vid UrobOrezane()
 */

#define PCIAR	10	// pocet useciek
#define	PPOLY	10	// pocet polygonov
#define SCENE_W	640	// sirka sceny
#define SCENE_H	480	// vyska sceny


// Tu zacina nase hlavne okno
class Orezavatko : public FXMainWindow {

	FXDECLARE(Orezavatko)

private:
	FXHorizontalFrame	*obsah;		// hlavny ram
	FXVerticalFrame		*obraz;		// ram pre obraz
	FXVerticalFrame		*tlacitka;	// ram pre tlacitka
	FXCanvas		*plocha;	// plocha obrazu
	FXImage			*imidz;		// pamat obrazu
	// usecky (scene su originaly, orez su orezane veci)
	my_line			scene_ciara[PCIAR];
	my_line			orez_ciara[PCIAR];
	// body polygonu (z trojuholniku bude max 7-uholnik)
	my_poly			scene_poly[PPOLY];
	my_poly			orez_poly[PPOLY];
	// pocet bodov polygonu (v scene vzdy 3 (lebo trojuholnik))
	int			orez_bodov[PPOLY];
	int			orez_ciar,orez_polys;
	// farba ciar a polygonov (fo su orezane)
	FXColor			fciara[PCIAR];
	FXColor			fociara[PCIAR];
	FXColor			fpoly[PPOLY];
	FXColor			fopoly[PPOLY];
	// vyrez
	FXPoint			rohlu;
	FXPoint			rohrl;

	// funkcia vytvorenia novej sceny pre internu potrebu triedy
	void UrobScenu(void);
	// funkcia oreze scenu a ulozi do orez_...
	void UrobOrezane(void);
	// funkcia vykreslenia sceny do imidzu
	void VykresliScenu(void);
	// funkcia vykreslenia orezanej sceny do imidzu
	void VykresliOrezane(void);

protected:
	Orezavatko() {}

public:
	long onPaint(FXObject*,FXSelector,void *);
	long CmdCele(FXObject*,FXSelector,void *);
	long CmdVyrez(FXObject*,FXSelector,void *);
	long CmdNova(FXObject*,FXSelector,void *);

// Spravy naseho orezavatka
	enum {
		ID_PLOCHA=FXMainWindow::ID_LAST,
		ID_CELE,
		ID_VYREZ,
		ID_NOVA,
		ID_LAST
		};

// Konstruktor
	Orezavatko(FXApp *a);

// Inicializacia
	virtual void create();
	};	// Koniec deklaracie triedy


// Mapa udalosti a sprav
FXDEFMAP(Orezavatko) OrezavatkoMap[] = {
	FXMAPFUNC(SEL_PAINT,	Orezavatko::ID_PLOCHA,	Orezavatko::onPaint),
	FXMAPFUNC(SEL_COMMAND,	Orezavatko::ID_CELE,	Orezavatko::CmdCele),
	FXMAPFUNC(SEL_COMMAND,	Orezavatko::ID_VYREZ,	Orezavatko::CmdVyrez),
	FXMAPFUNC(SEL_COMMAND,	Orezavatko::ID_NOVA,	Orezavatko::CmdNova),
	};

FXIMPLEMENT(Orezavatko,FXMainWindow,OrezavatkoMap,ARRAYNUMBER(OrezavatkoMap))


// Vytvorenie okna
Orezavatko::Orezavatko(FXApp *a):FXMainWindow(a,"Orezavatko",NULL,NULL,DECOR_ALL,0,0,782,530) {

	obsah=new FXHorizontalFrame(this,LAYOUT_SIDE_TOP|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0, 0,0,0,0);
	  obraz=new FXVerticalFrame(obsah,FRAME_SUNKEN|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0, 10,10,10,10);
	    new FXLabel(obraz,"Tu bude / je obraz",NULL,JUSTIFY_CENTER_X|LAYOUT_FILL_X);
	    new FXHorizontalSeparator(obraz,SEPARATOR_GROOVE|LAYOUT_FILL_X);
	    plocha=new FXCanvas(obraz,this,ID_PLOCHA,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT);
	  tlacitka=new FXVerticalFrame(obsah,FRAME_SUNKEN|LAYOUT_FILL_Y|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0, 10,10,10,10);
	    new FXLabel(tlacitka,"Tlacitka",NULL,JUSTIFY_CENTER_X|LAYOUT_FILL_X);
	    new FXHorizontalSeparator(tlacitka,SEPARATOR_GROOVE|LAYOUT_FILL_X);
	    new FXButton(tlacitka,"&Cele",NULL,this,ID_CELE,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);
	    new FXButton(tlacitka,"&Vyrez",NULL,this,ID_VYREZ,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);
	    new FXButton(tlacitka,"&Nova scena",NULL,this,ID_NOVA,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);
	    new FXButton(tlacitka,"&Koniec",NULL,getApp(),FXApp::ID_QUIT,FRAME_THICK|FRAME_RAISED|LAYOUT_FILL_X|LAYOUT_TOP|LAYOUT_LEFT,0,0,0,0,10,10,5,5);

	imidz=new FXImage(a,NULL,BITMAP_OWNED,1024,1024);

	rohlu.x=160,rohlu.y=120;
	rohrl.x=480,rohrl.y=360;
	UrobScenu();			// Ideme robit scenu...
	UrobOrezane();			// Ideme robit scenu...
	}

// Vytvorenie naseho okna
void Orezavatko::create() {
	imidz->create();
	FXMainWindow::create();
	VykresliScenu();
	show();
	}

long Orezavatko::onPaint(FXObject*,FXSelector,void *) {
	FXDCWindow dc(plocha);

	dc.drawImage(imidz,0,0);
	return 1;
	}

long Orezavatko::CmdCele(FXObject*,FXSelector,void *) {
	FXDCWindow dc(plocha);

	VykresliScenu();
	dc.drawImage(imidz,0,0);
	}

long Orezavatko::CmdVyrez(FXObject*,FXSelector,void *) {
	FXDCWindow dc(plocha);

	VykresliOrezane();
	dc.drawImage(imidz,0,0);
	}

long Orezavatko::CmdNova(FXObject*,FXSelector,void *) {
	FXDCWindow dc(plocha);

	UrobScenu();
	UrobOrezane();
	VykresliScenu();
	dc.drawImage(imidz,0,0);
	}

void Orezavatko::UrobScenu(void) {
	int i,j;

	for (i=0; i<PCIAR; i++) for (j=0; j<2; j++) {
		scene_ciara[i][j].x=rand()%SCENE_W;	// budeme pracovat
		scene_ciara[i][j].y=rand()%SCENE_H;	// v super presnosti
		fciara[i]=FXRGB(rand()%256,rand()%256,rand()%256);
		}
	for (i=0; i<PPOLY; i++) for (j=0; j<3; j++) {
		scene_poly[i][j].x=rand()%SCENE_W;	// budeme pracovat
		scene_poly[i][j].y=rand()%SCENE_H;	// v super presnosti
		fpoly[i]=FXRGB(rand()%256,rand()%256,rand()%256);
		}
	}

void Orezavatko::VykresliScenu(void) {
	FXDCWindow dc(imidz);
	int i;
	
	dc.setForeground(FXRGB(255,255,255));
	dc.fillRectangle(0,0,SCENE_W,SCENE_H);
	dc.setForeground(FXRGB(0,0,0));
	dc.drawLine(0,119,639,119);
	dc.drawLine(0,361,639,361);
	dc.drawLine(159,0,159,479);
	dc.drawLine(481,0,481,479);
	for (i=0; i<PPOLY; i++) {
		dc.setForeground(fpoly[i]);
		dc.fillPolygon(scene_poly[i],3);
		}
	for (i=0; i<PCIAR; i++) {
		dc.setForeground(fciara[i]);
		dc.drawLines(scene_ciara[i],2);
		}
	}

// konstanty pre kody pre orezavanie useciek
#define	CD_LEFT		8
#define	CD_RIGHT	4
#define	CD_DOWN		2
#define	CD_UP		1

// Tu je samotne orezavanie
void Orezavatko::UrobOrezane(void) {
	my_poly	a,b;
	int	ap,bp,i,j,k,code1,code2;
	FXshort	x,y;
	
	// Najprv orezavanie ciar
	orez_ciar=0;
	for (i=0;i<PCIAR;i++) {
		int x1=scene_ciara[i][0].x,
			y1=scene_ciara[i][0].y,
			x2=scene_ciara[i][1].x,
			y2=scene_ciara[i][1].y;
		TEST_USECKY:
		// Test a ohodnotenie bodov kodmi (sorry for goto :-))
		code1=code2=0;
		if (x1<rohlu.x) code1|=CD_LEFT;
		if (x1>rohrl.x) code1|=CD_RIGHT;
		if (y1>rohrl.y) code1|=CD_DOWN;
		if (y1<rohlu.y) code1|=CD_UP;
		if (x2<rohlu.x) code2|=CD_LEFT;
		if (x2>rohrl.x) code2|=CD_RIGHT;
		if (y2>rohrl.y) code2|=CD_DOWN;
		if (y2<rohlu.y) code2|=CD_UP;
		if (code1 | code2) {		// usecku asi treba orezat
			if (code1 & code2) continue;	// je mimo, nekreslit
			else {			// treba skratit usecku
				if (code1 & CD_LEFT) {
					y=((rohlu.x-x1)<<16)/(x2-x1)*(y2-y1)>>16;
					x1=rohlu.x;
					y1+=y;
					goto TEST_USECKY;
					}
				if (code1 & CD_RIGHT) {
					y=((rohrl.x-x1)<<16)/(x2-x1)*(y2-y1)>>16;
					x1=rohrl.x;
					y1+=y;
					goto TEST_USECKY;
					}
				if (code1 & CD_UP) {
					x=((rohlu.y-y1)<<16)/(y2-y1)*(x2-x1)>>16;
					y1=rohlu.y;
					x1+=x;
					goto TEST_USECKY;
					}
				if (code1 & CD_DOWN) {
					x=((rohrl.y-y1)<<16)/(y2-y1)*(x2-x1)>>16;
					y1=rohrl.y;
					x1+=x;
					goto TEST_USECKY;
					}
				if (code2 & CD_LEFT) {
					y=((rohlu.x-x2)<<16)/(x1-x2)*(y1-y2)>>16;
					x2=rohlu.x;
					y2+=y;
					goto TEST_USECKY;
					}
				if (code2 & CD_RIGHT) {
					y=((rohrl.x-x2)<<16)/(x1-x2)*(y1-y2)>>16;
					x2=rohrl.x;
					y2+=y;
					goto TEST_USECKY;
					}
				if (code2 & CD_UP) {
					x=((rohlu.y-y2)<<16)/(y1-y2)*(x1-x2)>>16;
					y2=rohlu.y;
					x2+=x;
					goto TEST_USECKY;
					}
				if (code2 & CD_DOWN) {
					x=((rohrl.y-y2)<<16)/(y1-y2)*(x1-x2)>>16;
					y2=rohrl.y;
					x2+=x;
					goto TEST_USECKY;
					}
				}
			}
		// Sem sa dostane skratena, dobre orezana usecka
		orez_ciara[orez_ciar][0].x=x1;
		orez_ciara[orez_ciar][0].y=y1;
		orez_ciara[orez_ciar][1].x=x2;
		orez_ciara[orez_ciar][1].y=y2;
		fociara[orez_ciar]=fciara[i];
		// Pocitame orezane, lebo po vypadnuti ich nemusi byt tolko,
		// kolko bolo povodnych
		orez_ciar++;
		}

	// Orezanie trojuholnikov - vzniknu maximalne sedemuholniky
/*
 * Orezavanie je vzdy podla lavej hranice... porovnavam preto v najvnutornejsom
 * cykle a[k].x s a[k+1].x - resp, ci su na opacnych stranach hranic... ak ano,
 * treba najst priesecik... ten sa prida do bodov, takto sa ide dalej...
 * Po k-cykle sa zlikviduju body vlavo od hranice.
 */
	orez_polys=0;
	for (i=0; i<PPOLY; i++) {	// cyklus pre vsetky trojuholniky
		// do polygonu a hodime trojuholnik
		ap=3;
		for (j=0; j<3; j++) a[j]=scene_poly[i][j];
		for (j=0; j<4; j++) {	// orezavame zo styroch stran
			bp=0;
			a[ap]=a[0];	// kopia prveho do poposledneho
			x=rohlu.x<rohrl.x?rohlu.x:rohrl.x;
			for (k=0; k<ap; k++) {	// prejdeme po bodoch
				// do b dame a polygon plus prieseciky
				b[bp]=a[k]; bp++;
				if ((a[k].x<x && a[k+1].x>x) ||
				    (a[k].x>x && a[k+1].x<x)) {
					// Zarad priesecik s lavou hranicou
					y=((x-a[k+1].x)<<16)/(a[k].x-a[k+1].x)*(a[k].y-a[k+1].y)>>16;
					b[bp].x=x;
					b[bp].y=a[k+1].y+y;
					bp++;
					}
				}
			ap=0;
			// Vyradenie odrezanych bodov (plus presun do a)
			for (k=0; k<bp; k++) if (b[k].x>=x) {
				a[ap]=b[k];
				ap++;
				}
			// Pomenime suradnice (rotacia 90 stupnov)
			for (k=0; k<ap; k++) {
				x=a[k].x;
				a[k].x=a[k].y;
				a[k].y=-x;
				}
			x=rohlu.x;
			rohlu.x=rohlu.y;
			rohlu.y=-x;
			x=rohrl.x;
			rohrl.x=rohrl.y;
			rohrl.y=-x;
			}
		if (ap) {
			for (j=0; j<ap; j++) orez_poly[orez_polys][j]=a[j];
			orez_bodov[orez_polys]=ap;
			fopoly[orez_polys]=fpoly[i];
			orez_polys++;
			}
		}
	}

void Orezavatko::VykresliOrezane(void) {
	FXDCWindow dc(imidz);
	int i,j;
	
	dc.setForeground(FXRGB(255,255,255));
	dc.fillRectangle(0,0,SCENE_W,SCENE_H);
	dc.setForeground(FXRGB(0,0,0));
	dc.drawLine(0,119,639,119);
	dc.drawLine(0,361,639,361);
	dc.drawLine(159,0,159,479);
	dc.drawLine(481,0,481,479);
	for (i=0; i<orez_polys; i++) {
		dc.setForeground(fopoly[i]);
		dc.fillPolygon(orez_poly[i],orez_bodov[i]);
		}
	for (i=0; i<orez_ciar; i++) {
		dc.setForeground(fociara[i]);
		dc.drawLines(orez_ciara[i],2);
		}
	}


// Hlavny program
int main(int argc,char **argv) {
	FXApp app("Orezavatko","Seriozna aplikacia");
	app.init(argc,argv);
	new Orezavatko(&app);
	app.create();
	return app.run();
	}

