/****************************************************************
*																																*
*	 Filename : NivL2.c																						*
*																																*
*****************************************************************
*																																*
*		Comment : Diese Datei beinhaltet die verschiedenen					*
*							Funktionen für die Darstellung der Niveaukurven.	*
*																																*
*			                    Funktionen														*
*	                        ==========														*
*																																*
*			DrawNivCurves()			zeichnet Niveaukurven									*
*			CalcRect()					berechnet ein Rechteck								*
*			CheckCorner()				kontrolliert zugehörige Ecke					*
*			CheckNiv()					zählt verschiedene Niveaus						*
*			CalcStyle()					berechnet nächstes Muster							*
*																																*
*				Rev : V1.0																							*
*																																*
*		History : V1.0 erstellen dieses Files							01/01/90	*
*																																*
*				Doc : Plotlibrary User's Guide													*
*																																*
*			 Bugs : keine bekannten																		*
*																																*
*			Autor : Oesch Silvano																			*
*																																*
*			Datum : 01/01/90																					*
*																																*
****************************************************************/

/****************************************************************
*																																*
*	allgemeine Includedateien																			*
*																																*
****************************************************************/

#include <math.h>

/****************************************************************
*																																*
*	Plotlibrary Includedateien																		*
*																																*
****************************************************************/

#include "Plot.h"
#include "PlotL2.h"
#include "SuppL2.h"
#include "DispL2.h"
#include "NivL2.h"

/****************************************************************
*																																*
*	Globale statische Variablen																		*
*																																*
****************************************************************/

static int	xpicmin,								/* Grenzwerte für xmin,xmax	*/
						xpicmax,								/* ymin,ymax								*/
						ypicmin,
						ypicmax;

static int	bxs,										/* Rechteckdim. für die			*/
						bys;										/* Beschriftung							*/

/****************************************************************
*																																*
*	externe Variablen																							*
*																																*
****************************************************************/

extern struct Plot *plot;
extern int plerr;

/****************************************************************
*																																*
*	Function : drawnivcurves()																		*
*																																*
*****************************************************************
*																																*
*		 Input : void																								*
*																																*
*		Output : int returnset	allg Rückgabewert										*
*							!= FALSE    	alles klar													*
*							== FALSE			Fehler															*
*																																*
*****************************************************************
*																																*
*	 Comment : Diese Funktion zeichnet und beschriftet alle				*
*						 Niveaukurven. Das Vorgehen dieser komplexen				*
*						 Funktion ist vereinfacht in dem nachfolgenden			*
*						 Ablauf erklärt. Da diese Funktion ein Flächen-			*
*						 clipping durchführen muss, sollte eigentlich				*
*						 der Sutherland-Hodgman Algorithmus verwendet				*
*						 werden. Dieser Algorithmus ist jedoch sehr rechen-	*
*						 intensiv und nicht einfach zu programmieren. Aus		*
*						 Zeitgründen wurde auf den Cohen-Sutherland					*
*						 Algorithmus zurückgegriffen. Mithilfe des Rück-		*
*						 gabewertes könnte so ein einfaches und schnelles		*
*						 Flächenclipping durchgeführt werden. Für eine			*
*						 nähere Beschreibung des Clippings wird auf die			*
*						 Diplomarbeit verwiesen.														*
*																																*
****************************************************************/

int drawnivcurves()
{

	register GPT	*iarray1,							/* aktueller Punkt				*/
								*iarray2;							/* vorhergehender Punkt		*/

	GPT 	*iarray;											/* Speicher Arraystart		*/

	int		set,set1,											/* Startwertflags					*/
				count,												/* Schleifenzähler				*/
				ptc,													/* Anzahl Punkte Polygon	*/
				tris,													/* Translation Status			*/
				xs,xe,xe1,										/* X-Koordinaten					*/
				ys,ye,ye1,										/* Y-Koordinaten					*/
				returnset=TRUE,								/* allg. Rückgabewert			*/
				fill,													/* Polygon Rückgabewert		*/
				beschx,												/* horiz. Beschriftung		*/
				len,													/* länge des Niveautextes	*/
				oldcol=NULL;									/* Farbenspeicher					*/

	char 	cstat,												/* Clipping Status				*/
				cstat1,												/* Clipp Status Speicher	*/
				buffer[20];										/* für Niveautext					*/

	DATA 	*x,*y,												/* X und Y Wert						*/
				*array,												/* Zeiger auf Daten				*/
				value;												/* Niveauwert							*/

	struct Curve *curve;								/* Kurvenzeiger						*/

/****************************************************************
*																																*
*	Das zeichnen der Niveaukurven ist nicht mehr so einfach wie		*
*	dass der 2d-Kurven. Der Hauptunterschied liegt darin, das			*
*	für jedes Niveau eine Fläche und keine Linie gezeichnet wird.	*
*	Das nachfolgende Ablaufdiagramm zeigt vereinfacht die					*
*	verschiedenen Abschnitte auf, welche für eine Niveau-					*
*	Darstellung benötigt werden.																	*
*																																*
*	Bis alle Niveaus gezeichnet																		*
*		hole Integerarray																						*
*		Bis alle Werte dieses Niveau gezeichnet											*
*			Konvertieren des Punktes																	*
*			Wenn Startwert berechnet																	*
*				Wenn clipping																						*
*					clippen																								*
*					status Flächenclippen																	*
*						Ecke zuweisen																				*
*					status line																						*
*						Wenn Startwert nicht vorhanden diesen setzen				*
*					  Evt doch noch Flächenclippen												*
*						Punkt addieren																			*
*				else																										*
*					Wenn Startwert nicht vorhanden diesen setzen					*
*					Neuer Punkt addieren																	*
*			Startwert auf Endwert setzen															*
*		Berechne evt letzten Punkt des Polygones										*
*		Zeichne Polygon																							*
*		Beschrifte Niveau wenn noch nicht vorhanden									*
*	zurück mit dem Status dieser Funktion													*
*																																*
****************************************************************/

	trans2d(&plot->xmin,&plot->ymin,&xpicmin,&ypicmin);
	trans2d(&plot->xmax,&plot->ymax,&xpicmax,&ypicmax);
																			/* init. der Grenzwerte		*/
	bxs = (RECTSIZE)*plot->xtext;				/* Länge des Rechteckes		*/
	bys = 3*plot->ytext/2;							/* Höhe des Rechteckes		*/
	beschx = plot->xpic1;								/* Start der Beschriftung	*/
	curve = plot->first;								/* erste Kurve						*/
	value = curve->zmin+1;							/* Value sicher ungleich	*/
																			/* erstem Niveau					*/
	while (curve != NULL)								/* Für alle Kurven				*/
	{
		set = FALSE;											/* Für jede Kurve neu			*/
		set1 = FALSE;											/* setzten								*/
		cstat1 = 0x00;

		ptc = curve->count+2;							/* Grösse des Feldes			*/
		if (plot->clipp == CLIPP)					/* berechnen, auch für		*/
			ptc = 5*ptc/3;									/* clipping								*/
		iarray = malloc(ptc*valsize()*sizeof(GPT));
		if (iarray == NULL)								/* speicher bekommen			*/
		{																	/* Nein										*/
			seterror(GRAPHMEM);							/* Fehler setzten und			*/
			return(FALSE);									/* zurück									*/
		}

		iarray1 = iarray;									/* Arraypointer init.			*/
		iarray2 = iarray;
		if (value != curve->zmin)					/* Farbe und Muster für		*/
		{																	/* neues Niveau berechnen	*/
			calccolor(curve->color);
			calcstyle();
		}
		else
			SetFgColor(oldcol);							/* Farbe rücksetzten			*/


		array = curve->val;								/* Datenzeiger init.			*/
		for (count = 0;count<curve->count;count++)
		{																	/* für alle Datenpaare		*/
			x = array++;										/* x und y zuweisen				*/
			y = array++;										/* immer noch DATA				*/

			tris = trans2d(x,y,&xe,&ye);		/* und konvertieren				*/
			if (tris != TRUE)								/* Fehler									*/
			{																/* Ja,dann neue Grenz-		*/
				if ( tris & XTRANS)						/* koordinaten zuweisen		*/
					xe = plot->xpic1;						/* ACHTUNG! Darstellung		*/
				if ( tris & YTRANS)						/* stimmt nicht 100%			*/
					ye = plot->ypic2;
			}

			if (set == TRUE)								/* Startwert gesetzt			*/
			{																/* Ja											*/
				if (plot->clipp == CLIPP)			/* nur bei clippen				*/
				{															/* der komplexe Prozess		*/
					xe1 = xe;										/* speichern der orginal-	*/
					ye1 = ye;										/* endkoordinaten					*/
					cstat = doclipp(&xs,&ys,&xe1,&ye1);

/****************************************************************
*																																*
*	Die Clipping Routine liefert nur dann einen Status von NULL,	*
*	wenn die Linie gezeichnet werden muss. Ansonsten ist dieser		*
*	ungleich NULL. Dieser Status wird nun mit dem vorhergehenden	*
*	Status auf gleichheit kontrolliert. Nur wenn dieser Vergleich	*
*	negativ ausfällt, d.h. die Seite wurde gewechselt, muss die		*
*	dazwischenliegende Ecke gezeichnet werden.										*
*																																*
****************************************************************/

					if (cstat)									/* Status gegeben					*/
					{														/* also clippen						*/
						if ((cstat1) and					/* vorhergehender gesetzt	*/
								(cstat != cstat1))		/* und ungleich						*/
						{													/* dann Ecke zeichnen			*/
							switch (cstat | cstat1)	/* welcher Seitenwechsel	*/
							{
								case (C_L | C_B):			/* unten links						*/
									*iarray1++ = plot->xpic1;
									*iarray1++ = plot->ypic2;
									break;
								case (C_L | C_T):			/* oben links							*/
									*iarray1++ = plot->xpic1;
									*iarray1++ = plot->ypic1;
									break;
								case (C_R | C_B):			/* unten rechts						*/
									*iarray1++ = plot->xpic2;
									*iarray1++ = plot->ypic2;
									break;
								case (C_R | C_T):			/* oben rechts						*/
									*iarray1++ = plot->xpic2;
									*iarray1++ = plot->ypic1;
									break;
							}
							iarray2 +=2;
						}
						cstat1 = cstat;						/* Status speichern				*/
					}
					else												/* also kein spez. clipp.	*/
					{
						cstat1 = 0x00;						/* löschen alter Status		*/
						if (set1 == FALSE)				/* Start schon gesetzt ?	*/
						{													/* Nein, dann schreibe		*/
							*iarray1++ = xs;				/* diese Werte in das			*/
							*iarray1++ = ys;				/* Array									*/
							set1 = TRUE;						/* Startwert gesetzt			*/
						}

/****************************************************************
*																																*
*	Wenn die gespeicherten Startwerte ungleich den eff.						*
*	Startwerten sind, müssen diese die Startwerte in das Array		*
*	eingefügt werden. Dies ist dann der Fall, wenn xs1 bzw. ys1		*
*	geclippt wurden und xs und ys auch. Wenn nun der Unterschied	*
*	zwischen diesen Werten grösser gleich ein Punkt ist, dann			*
*	muss dieser Punkt zusätzlich eingefügt werden.								*
*																																*
****************************************************************/

						if ((iarray2[0] != xs) or (iarray2[1] != ys))
						{													/* Punkte ungleich				*/
							*iarray1++ = xs;				/* dann speichern dieser	*/
							*iarray1++ = ys;				/* Werte									*/
							iarray2 +=2;
						}

						*iarray1++ = xe1;					/* Endwerte in das Array	*/
						*iarray1++ = ye1;					/* eintragen							*/
						iarray2 +=2;
					}
				}
				else													/* kein Clipping					*/
				{
					if (set1==FALSE)						/* Startwert gesetzt			*/
					{														/* Nein										*/
						*iarray1++ = xs;					/* Also Startwerte				*/
						*iarray1++ = ys;					/* speichern und Flag			*/
						set1 = TRUE;							/* setzten								*/
					}
					*iarray1++ = xe;						/* Endwerte speichern			*/
					*iarray1++ = ye;
				}
			}
			xs = xe;												/* Startwerte auf orginal	*/
			ys = ye;												/* Endwerte setzen				*/
			set = TRUE;											/* und Flag setzen				*/
		}

/****************************************************************
*																																*
*	Ab dieser Stelle ist das Polygon mit den vorhandenen Daten		*
*	berechnet worden. Es geht jetzt darum, ein evt. nicht					*
*	geschlossenes Polygon über den dazugehörigen Ecken oder 			*
*	Kante zu schliessen. Der Zustand eines offenen Polygones ist	*
* normalerweise nicht legal. Es kann jedoch vorkommen, wenn			*
*	ein Niveau abgetastet wird, dass dies nur in einem Bereich		*
*	erfolgt. Danach ist ein offener Zustand möglich und dieser		*
*	wird hier beseitigt.																					*
*																																*
****************************************************************/

		xs = iarray[0];										/* hole 1. Koordinate			*/
		ys = iarray[1];

		if (plot->clipp == CLIPP)					/* Wenn geclippt, hole		*/
		{																	/* letzte Koordinate			*/
			xe = iarray2[0];
			ye = iarray2[1];
		}

		if ((xs != xe) or (ys != ye))			/* sind die Punkte gleich	*/
		{																	/* Nein, es ist offen			*/
			if ((xs != xe) and							/* sind x-werte und 			*/
					(ys != ye))									/* y-werte ungleich				*/
			{																/* dann muss es Ecke sein */
				switch(checkcorner(xs,ys,xe,ye))
				{															/* welche ist es denn			*/
					case TOP_LEFT:							/* die entsprechenden			*/
						*iarray1++ = xpicmin;			/* min- bzw. max-werte		*/
						*iarray1++ = ypicmax;			/* werden in das Array		*/
						break;										/* hinzugefügt.						*/
					case BOT_LEFT:							/* Am Ende kommt noch die	*/
						*iarray1++ = xpicmin;			/* Startkoordinate für		*/
						*iarray1++ = ypicmin;			/* alle dre Fälle hinzu.	*/
						break;
					case TOP_RIGHT:
						*iarray1++ = xpicmax;
						*iarray1++ = ypicmax;
						break;
					case BOT_RIGHT:
						*iarray1++ = xpicmax;
						*iarray1++ = ypicmin;
						break;
				}
				*iarray1++ = xs;							/* auch für die Kanten-		*/
				*iarray1++ = ys;							/* fälle									*/
			}
		}

/****************************************************************
*																																*
*	Nun ist alles berechnet und gespeichert in einem Array.				*
*	Bevor nun das Polygon gezeichnet werden kann, muss die				*
*	Anzahl der Koordinaten bestimmt werden. Danach wird es				*
*	gezeichnet und beschriftet. Für jedes Niveau wird am unteren	*
*	Bildrand ein kleines Rechteck mit dem entsprechenden Muster		*
*	gezeichnet. In der mitte diese Rechteckes steht dann der			*
*	Niveauwert.	Die Beschriftung erfolgt jedoch nur dann, wenn		*
*	der alte Niveauwert nicht mit den aktuellen übereinstimmt.		*
*																																*
****************************************************************/

		ptc = (iarray1 - iarray)/valsize();
																			/* Berechne Anzahl Punkte	*/
		fill = FillPolyLine(ptc,iarray);	/* und zeichne es					*/
		if (fill == FALSE)								/* hat es funktioniert		*/
		{																	/* Nein										*/
			seterror(GRAPHFILL);						/* Fehler									*/
			setreturn(FALSE);
			free(iarray);										/* speicher freigeben			*/
			break;													/* und WHILE verlassen		*/
		}

		if (value != curve->zmin)					/* beschriften ?					*/
		{																	/* Ja											*/
			value = curve->zmin;						/* akt. Wert speichern		*/
			len = sprintf(buffer,"%.4lg",value);
																			/* Niveauwert konv.				*/
			calcrect(beschx,plot->cpt+plot->yht,len);
																			/* und Rechteck zeichnen	*/
			oldcol = GetFgColor();					/* speichern von color		*/
			SetFgColor(TEXTCOL);						/* und für Text setzten		*/
			printtextabs(	buffer,						/* Wert schreiben					*/
										beschx+bxs/2,
										plot->cpt+plot->yht/2,
										T_CX|T_T);
			beschx += bxs;									/* Beschriftungswert			*/
			if (beschx > plot->xpic2-bxs)		/* am Ende einer Teile		*/
			{																/* Ja											*/
				beschx = plot->xpic1;					/* dann nächste Zeile			*/
				plot->cpt += bys+plot->yht;		/* nehmen									*/
			}
		}
		free(iarray);											/* Array freigeben				*/
		curve = curve->next;							/* und nächste Kurve			*/
	}																		/* Ende von WHILE					*/
	return(returnset);									/* endlich fertig					*/
}

/****************************************************************
*																																*
*	Function : calcrect()																					*
*																																*
*****************************************************************
*																																*
*		 Input : x,y																								*
*							int x								obere													*
*							int y									linke Ecke									*
*																																*
*		Output : void																								*
*																																*
*****************************************************************
*																																*
*	 Comment : CalcRect berechnet die Koordinaten der							*
*						 Beschriftungsrechtecke und schreibt diese in das		*
*						 Koordinatenarray Rect. Danach wird das Rechteck		*
*						 mit der gleichen Farbe und dem gleichen Muster			*
*						 gezeichnet.																				*
*																																*
****************************************************************/

static void calcrect(x,y,len)
int	x,
		y,
		len;
{

	int transa[] =										/* Koordinatenpunkte				*/
			{
				0,0,1,0,1,1,0,1,0,0
			},
			i,j,													/* Schleifenzähler					*/
			size,
			rsize;
	GPT	rect[10];											/* Koordinatenarray					*/

	size = (len+2)*plot->xtext;
	rsize = (bxs-size)/2;

	for (j=0;j<2;j++)
	{
		for (i=0;i<10;i+=2)								/* 4+Startpunkt							*/
		{
			rect[i] = x+transa[i]*rsize+j*(rsize+size);
																			/* X-Koordinate							*/
			rect[i+1] = y+transa[i+1]*bys;	/* Y-Koordinate							*/
		}
		FillPolyLine(5,rect);							/* und zeichnen							*/
	}
	DrawRectangle(x+rsize,y,x+rsize+size,y+bys);
}

/****************************************************************
*																																*
*	Function : CheckCorner()																			*
*																																*
*****************************************************************
*																																*
*		 Input : xs,ys,xe,ye																				*
*							int xs						X Startkoordinate								*
*							int ys						Y Startkoordinate								*
*							int xe						X Endkoordinate									*
*							int ye						Y Endkoordinate									*
*																																*
*		Output : int corner					zugehörige Ecke									*
*																																*
*****************************************************************
*																																*
*	 Comment : Diese Funktion berechnet die zugehörige Ecke einer	*
*						 Niveaukurve wenn diese nicht geschlossen vorhanden	*
*						 ist. Normalerweise muss eine geschlossen Kurve			*
*						 vorhanden sein. Wenn jedoch eine Kurve bei den			*
*						 min- oder maxwerten beginnt oder aufhört, kann			*
*						 diese Kurve nicht geschlossen sein. Es wird hier		*
*						 dann die entsprechende Flächenecke gesucht.				*
*						 Danach kann diese diese Ecke in die Fläche ein-		*
*						 gefügt werden. Es ist also zu beachten, dass nicht	*
*						 geschlossene Kurven immer über eine Ecke verbunden	*
*						 werden. Dadurch kann es zu einer nicht korrekten		*
*						 Darstellung kommen.																*
*						 Weiter muss das Integerarray für diese 						*
*						 zusätzlichen Punkte ( 2 ) vorbereitet sein.				*
*																																*
****************************************************************/

static int checkcorner(xs,ys,xe,ye)
int xs,ys,xe,ye;
{
	int temp,													/* Zwischenspeicher					*/
			returnset;										/* all. Rückgabewert				*/

	if (xs > xe)											/* Von einer Seite her			*/
	{																	/* betrachten								*/
		swap(xs,xe,temp);								/* tausche die Koordinaten	*/
		swap(ys,ye,temp);
	}

	if (xs <= xpicmin)								/* linke Seite ?						*/
		if (ye >= ypicmin)							/* untere Seite ?						*/
			setreturn(BOT_LEFT);					/* setzte Ecke							*/
		else														/* sonst										*/
			setreturn(TOP_LEFT);					/* obere Ecke								*/
	else															/* also rechte Seite				*/
		if (ys >= ypicmin)							/* untere Seite							*/
			setreturn(BOT_RIGHT);					/* setzte Ecke							*/
		else														/* sonst										*/
			setreturn(TOP_RIGHT);					/* obere Ecke								*/
	return(returnset);								/* und zurück								*/
}

/****************************************************************
*																																*
*	Function : checkniv()																					*
*																																*
*****************************************************************
*																																*
*		 Input : void																								*
*																																*
*		Output : int niveaus			Anzahl verschiedener Niveaus			*
*																																*
*****************************************************************
*																																*
*	 Comment : Diese Funktion zählt die verschiedenen Niveau			*
*						 welche im Speicher geladen sind. Der Niveauwert		*
*						 wird beim Laden der Daten in den zmin Wert					*
*						 geschrieben.																				*
*																																*
****************************************************************/

int checkniv()
{
	DATA value;												/* Niveauwert								*/
	int count = 1;										/* Niveaucount							*/
	struct Curve *curve;							/* Kurvenzeiger							*/

	curve = plot->first;							/* Erste Kurve							*/
	value = curve->zmin;							/* Wert der ersten Kurve		*/
	while (curve != NULL)							/* solange Kurven vorhanden	*/
	{
		if (curve->zmin != value)				/* neuer Wert ?							*/
		{																/* Ja												*/
			value = curve->zmin;					/* Neuer Wert zuweisen			*/
			count++;											/* und Zähler erhöhen				*/
		}
		curve = curve->next;						/* nächste Kurve						*/
	}
	return(count);										/* zurück mit Zähler				*/
}

/****************************************************************
*																																*
*	Function : calcstyle()																				*
*																																*
*****************************************************************
*																																*
*		 Input : void																								*
*																																*
*		Output : void																								*
*																																*
*****************************************************************
*																																*
*	 Comment : Diese Funktion berechnet das nächste verfügbare		*
*						 Füllmuster. In der Styletab sind die gewünschten		*
*						 Muster aufgeführt welche angewählt werden können.	*
*						 Wenn kein Muster mehr vorhanden ist, dann wird			*
*						 wieder bei dem ersten Muster begonnen.							*
*																																*
****************************************************************/

static void calcstyle()
{

	int styletab[5] =									/* Mustertabelle						*/
	{	4,5,7,8,10 };

	if (plot->style == NOSTYLE)				/* keine füllen erwünscht		*/
		SetFillStyle(0);								/* setzte NOFILL						*/
	else															/* sonst berechne neues			*/
	{																	/* Muster										*/
		if (plot->style > 4)						/* letztes Muster erreicht	*/
			plot->style = 0;							/* Ja, dann von neuem				*/
		SetFillStyle(styletab[plot->style++]);
	}																	/* setzte Muster						*/
}

