/*
 * geofast.h --
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 *
 * This file contains macros for fast geometrical operations:
 * clipping and transforms.  For transforms, it works when they
 * all have unit scale factors, so we can avoid multiplication.
 * This wins a factor of 5 on machines like Suns.  In addition,
 * of course, each is a macro rather than a procedure call;
 * the net effect is about a 5x speedup on a VAX and a 7-8x
 * speedup on a Sun.
 */

/* rcsid "$Header: /usr/cvsroot/magic-8.0/utils/geofast.h,v 1.1.1.1 2008/02/03 20:43:50 tim Exp $" */

#ifndef _MAGIC__UTILS__GEOFAST_H
#define _MAGIC__UTILS__GEOFAST_H

/* ---------------------------- Clipping ------------------------------ */

/*
 * GEOCLIP(r, area) Rect *r, area;
 * clips the rectangle 'r' against the area 'area'.
 */

#define	GEOCLIP(r, area) \
    do { \
	if ((r)->r_xbot < (area)->r_xbot) (r)->r_xbot = (area)->r_xbot; \
	if ((r)->r_ybot < (area)->r_ybot) (r)->r_ybot = (area)->r_ybot; \
	if ((r)->r_xtop > (area)->r_xtop) (r)->r_xtop = (area)->r_xtop; \
	if ((r)->r_ytop > (area)->r_ytop) (r)->r_ytop = (area)->r_ytop; \
    } while(0)

/* --------------------- Transforming rectangles ---------------------- */

/*
 * The GEOTRANSRECT(t, r1, r2) macro has the same effect as the
 * following code.  It assumes that t_a, t_b, t_d, t_e are all
 * chosen from -1, 0, 1, so it can't handle scaling.
 *
 *	int x1, y1, x2, y2;
 *	x1 = r1->r_xbot*t->t_a + r1->r_ybot*t->t_b + t->t_c;
 *	y1 = r1->r_xbot*t->t_d + r1->r_ybot*t->t_e + t->t_f;
 *	x2 = r1->r_xtop*t->t_a + r1->r_ytop*t->t_b + t->t_c;
 *	y2 = r1->r_xtop*t->t_d + r1->r_ytop*t->t_e + t->t_f;
 *
 *	if (x1 < x2) r2->r_xbot = x1, r2->r_xtop = x2;
 *	else r2->r_xbot = x2, r2->r_xtop = x1;
 *
 *	if (y1 < y2) r2->r_ybot = y1, r2->r_ytop = y2;
 *	else r2->r_ybot = y2, r2->r_ytop = y1;
 *
 * We make use of the fact that if t_a != 0 for one of our transforms,
 * then t_e != 0 also, and t_b = t_d = 0.
 */

#define	transRectX(r1, r2, RBOT, RTOP, ta, tc) \
	if (ta > 0) \
	    r2->r_xbot = r1->RBOT + tc, r2->r_xtop = r1->RTOP + tc; \
	else \
	    r2->r_xtop = tc - r1->RBOT, r2->r_xbot = tc - r1->RTOP;

#define	transRectY(r1, r2, RBOT, RTOP, ta, tc) \
	if (ta > 0) \
	    r2->r_ybot = r1->RBOT + tc, r2->r_ytop = r1->RTOP + tc; \
	else \
	    r2->r_ytop = tc - r1->RBOT, r2->r_ybot = tc - r1->RTOP;

#define	GEOTRANSRECT(at, ar1, ar2) \
    if (1) { \
	Transform *xt = (at); \
	Rect *xr1 = (ar1), *xr2 = (ar2); \
	if (xt->t_a) \
	{ \
	    transRectX(xr1, xr2, r_xbot, r_xtop, xt->t_a, xt->t_c); \
	    transRectY(xr1, xr2, r_ybot, r_ytop, xt->t_e, xt->t_f); \
	} \
	else \
	{ \
	    transRectX(xr1, xr2, r_ybot, r_ytop, xt->t_b, xt->t_c); \
	    transRectY(xr1, xr2, r_xbot, r_xtop, xt->t_d, xt->t_f); \
	} \
    } else

/* --------------------- Transforming transforms ---------------------- */

/*
 * The GEOTRANSTRANS(first, second, net) macro has the same effect
 * as the following code.  It assumes that the t_a, t_b, t_d, and t_f
 * fields of 'second' are chosen from -1, 0, 1, so it can't handle scaling.
 *
 * net->t_a = first->t_a*second->t_a + first->t_d*second->t_b;
 * net->t_b = first->t_b*second->t_a + first->t_e*second->t_b;
 * net->t_c = first->t_c*second->t_a + first->t_f*second->t_b + second->t_c;
 * net->t_d = first->t_a*second->t_d + first->t_d*second->t_e;
 * net->t_e = first->t_b*second->t_d + first->t_e*second->t_e;
 * net->t_f = first->t_c*second->t_d + first->t_f*second->t_e + second->t_f;
 */

#define	transTransAC(t1, net, ta, tc, da, db, dc) \
    ((ta > 0) \
	? (net->t_a = t1->da, net->t_b = t1->db, net->t_c = t1->dc + tc) \
	: (net->t_a = -t1->da, net->t_b = -t1->db, net->t_c = tc - t1->dc))

#define	transTransDF(t1, net, ta, tc, da, db, dc) \
    ((ta > 0) \
	? (net->t_d = t1->da, net->t_e = t1->db, net->t_f = t1->dc + tc) \
	: (net->t_d = -t1->da, net->t_e = -t1->db, net->t_f = tc - t1->dc))

#define	GEOTRANSTRANS(xt1, xt2, xnet) \
	if (1) { \
	    Transform *_t1 = (xt1), *_t2 = (xt2), *_net = (xnet); \
	    if (_t2->t_a) \
		transTransAC(_t1, _net, _t2->t_a, _t2->t_c, t_a, t_b, t_c); \
	    else \
		transTransAC(_t1, _net, _t2->t_b, _t2->t_c, t_d, t_e, t_f); \
	    if (_t2->t_d) \
		transTransDF(_t1, _net, _t2->t_d, _t2->t_f, t_a, t_b, t_c); \
	    else \
		transTransDF(_t1, _net, _t2->t_e, _t2->t_f, t_d, t_e, t_f); \
	} else

/* ----------------------- Inverting transforms ----------------------- */

/*
 * The GEOINVERTTRANS(t, inv) macro has the same effect as the following
 * code.  The code assumes that t_a, t_b, t_c, and t_d are one of
 * -1, 1, or 0, so it can't invert scaled transforms (but neither can
 * the normal GeoInvertTrans, anyway).
 *
 *	Transform t3;
 *
 *	t3.t_a = t->t_a;
 *	t3.t_b = t->t_d;
 *	t3.t_d = t->t_b;
 *	t3.t_e = t->t_e;
 *	t3.t_c = t3.t_f = 0;
 *	GeoTransTranslate(-t->t_c, -t->t_f, &t3, inv);
 *
 * where GeoTranslateTrans(x, y, t, net) is
 *
 *	net->t_a = t->t_a;
 *	net->t_b = t->t_b;
 *	net->t_d = t->t_d;
 *	net->t_e = t->t_e;
 *	net->t_c = x*t->t_a + y*t->t_b + t->t_c;
 *	net->t_f = x*t->t_d + y*t->t_e + t->t_f;
 */

/*
 * GEOINVERTTRANS(t, tinv) inverts Transform t into tinv
 * without multiplication.
 *
 * tMul(c, a) implements (a*c) without multiplication,
 * assuming that each of a, c are chosen from 0, -1, 1.
 */
#define	tMul(c, a) \
	((a)	? ((a) > 0 ? (c) : -(c))	: 0)

#define	GEOINVERTTRANS(t, inv) \
    if (1) { \
	Transform *xt = (t), *xinv = (inv); \
	xinv->t_a = xt->t_a; \
	xinv->t_b = xt->t_d; \
	xinv->t_d = xt->t_b; \
	xinv->t_e = xt->t_e; \
	xinv->t_c = - tMul(xt->t_c, xinv->t_a) - tMul(xt->t_f, xinv->t_b); \
	xinv->t_f = - tMul(xt->t_c, xinv->t_d) - tMul(xt->t_f, xinv->t_e); \
    } else

/*
 * GEOTRANSTRANSLATE(x, y, t, tresult) transforms an (x, y) translation
 * by the transform t, resulting in the transform tresult.
 */
#define	GEOTRANSTRANSLATE(x, y, t, tresult) \
    { \
	Transform *xt = (t); \
	*(tresult) = (*xt); \
	(tresult)->t_c += tMul(x, xt->t_a) + tMul(y, xt->t_b); \
	(tresult)->t_f += tMul(x, xt->t_d) + tMul(y, xt->t_e); \
    }

#endif /* _MAGIC__UTILS__GEOFAST_H */
