/*
*   Parts Copyright (c) 1999  Martin Kroeker  All rights reserved.
*   (based on code written by Martin Skiba when working for Daveg GmbH,
*   used here with the permission of Daveg GmbH, Darmstadt,Germany) */
/*	Clip.C	Release : 2.2	Date : 12/09/93	by sk	*/
/*--------------------------------------------------------------------
*	Clip.C
*
*	Clipping von Geraden in Integerdarstellung nach einen
*	Mischmasch der Algorythmen von Cohen-Sutherland ( Bereichstests)
*	und Liang-Barsky ( DurchfÏhrung )
*/

#include <stdio.h>
#include <math.h>
#include "clip.h"


#define CLIP_INSIDE 0
#define CLIP_RIGHT 1
#define CLIP_BELOW 2
#define CLIP_ABOVE 4
#define CLIP_LEFT 8

#define ClipSurelyOutside(code1,code2) (code1 & code2)
#define ClipFullyInside(code1,code2) (! (code1 | code2 ))

static short ClipAreaCode(double x1, double y1,
			  double x2, double y2, double px, double py)
{
	short code = CLIP_INSIDE;

	if (px < x1 - 1.e-3)
		code |= CLIP_LEFT;
	else if (px > x2 + 1.e-3)
		code |= CLIP_RIGHT;

	if (py < y1 - 1.e-3)
		code |= CLIP_BELOW;
	else if (py > y2 + 1.e-3)
		code |= CLIP_ABOVE;

	return code;
}

static int ClipWithBorder(double delta,
			  double diff, double *tEnter, double *tLeave)
{
	double t;
	t = diff / delta;

	if (fabs(t) < 1.e-5)
		return 0;
	if (delta < 0.0) {
		if (t > *tLeave)
			return 0;
		else if (t > *tEnter)
			*tEnter = t;
	} else {
		if (t < *tEnter)
			return 0;
		else if (t < *tLeave)
			*tLeave = t;
	}
	return 1;
}


short DtClipLine(double xmin, double ymin,
		 double xmax, double ymax,
		 double *x1, double *y1, double *x2, double *y2)
{
	short area_code1, area_code2;
	double eps;

	eps = 1.e-10;
	area_code1 = ClipAreaCode(xmin, ymin, xmax, ymax, *x1, *y1);
	area_code2 = ClipAreaCode(xmin, ymin, xmax, ymax, *x2, *y2);

	if (ClipFullyInside(area_code1, area_code2))
		return CLIP_DRAW;

	if (ClipSurelyOutside(area_code1, area_code2)) {
/*	fprintf(stderr,"clipped line from %f %f to %f %f\n",*x1,*y1,*x2,*y2);*/
		return CLIP_NODRAW;
	} else {
		double dx, dy;
		int dx0, dy0;
		double tEnter, tLeave;
		double *tE = &tEnter;
		double *tL = &tLeave;

		dx = *x2 - *x1;
		dy = *y2 - *y1;
		tEnter = 0.0;
		tLeave = 1.;
		dx0 = (-eps < dx && dx < eps);
		dy0 = (-eps < dy && dy < eps);

		if (dx0 || ClipWithBorder(-dx, -(xmin - *x1), tE, tL))
			if (dy0
			    || ClipWithBorder(dy, (ymax - *y1), tE, tL))
				if (dx0
				    || ClipWithBorder(dx, (xmax - *x1), tE,
						      tL))
					if (dy0
					    || ClipWithBorder(-dy,
							      -(ymin -
								*y1), tE,
							      tL)) {
						*x2 = *x1 + (tLeave * dx);
						*y2 = *y1 + (tLeave * dy);
						*x1 = *x1 + (tEnter * dx);
						*y1 = *y1 + (tEnter * dy);

						return CLIP_DRAW;
					}
	}
	return CLIP_NODRAW;
}
