
/* windTransform.c -
 *
 *	Routines to transform from screen coords to surface coords and the
 *	other way.
 *
 *     *********************************************************************
 *     * 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.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header$";
#endif  /* not lint */

#include <stdio.h>
#include "utils/magic.h"
#include "utils/geometry.h"
#include "windows/windows.h"
#include "graphics/glyphs.h"
#include "windows/windInt.h"


/*
 * ----------------------------------------------------------------------------
 * WindScreenToSurface --
 *
 *	Convert a rectangle in screen coordinates to surface coords.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Returns a rectangle that is big enough to fully contain everything
 *	that is displayed in the screen rectangle.
 * ----------------------------------------------------------------------------
 */

void
WindScreenToSurface(w, screen, surface)
    MagWindow *w;			/* Window in whose coordinates screen is
				 * is defined.
				 */
    Rect *screen;		/* A rectangle in screen coordinates */
    Rect *surface;		/* A pointer to a rectangle to be filled
				 * in with a rectangle in surface coords that
				 * is big enough to contain everything
				 * displayed within the screen rectangle.
				 */
{
    Rect r;
    WindPointToSurface(w, &(screen->r_ll), (Point *) NULL, surface);
    WindPointToSurface(w, &(screen->r_ur), (Point *) NULL, &r);
    surface->r_ur = r.r_ur;
}


/*
 * ----------------------------------------------------------------------------
 * WindPointToSurface --
 *
 *	Take a point in screen coordinates and find the nearest point
 *	in surface coordinates.  Also return a box in surface coords
 *	that completely surrounds the pixel.
 *
 * Results:
 *	None.
 *
 * Side effects:
 * 	The parameters surfaceBox and surfacePoint are modified, if
 *	they are non-NULL.  The box is always at least 1x1, but it
 *	will be larger if the scale is so coarse that a single screen
 *	coordinate is more than one surface coordinate.  In this case
 *	the box is large enough to include every surface coordinate
 *	that can map to the given pixel (this feature is essential
 *	if WindScreenToSurface is to use this routine).
 *
 * ----------------------------------------------------------------------------
 */

void
WindPointToSurface(w, screenPoint, surfacePoint, surfaceBox)
    MagWindow *w;		/* Window in whose coordinate system the
			 * transformation is to be done.
			 */
    Point *screenPoint;	/* The point in screen coordinates. */
    Point *surfacePoint;/* Filled in with the nearest surface coordinate.
			 * Nothing is filled in if the pointer is NULL.
			 */
    Rect *surfaceBox;	/* Filled in with a box in surface coordinates that
			 * surrounds the point.  It is not filled in if this
			 * is a NULL pointer.
			 */
{
    int tmp, adjust, unitsPerPixel;

    /* Do the inverse transformation twice, once with rounding for
     * the point and once with truncation for the box.
     */

    unitsPerPixel = SUBPIXEL/w->w_scale;

    if (surfaceBox != NULL)
    {
	tmp = (SUBPIXEL*screenPoint->p_x) - w->w_origin.p_x;
	if (tmp < 0) tmp -= w->w_scale-1;
	surfaceBox->r_xbot = w->w_surfaceArea.r_xbot + tmp/w->w_scale;
	surfaceBox->r_xtop = surfaceBox->r_xbot + unitsPerPixel + 1;

	tmp = (SUBPIXEL*screenPoint->p_y) - w->w_origin.p_y;
	if (tmp < 0) tmp -= w->w_scale-1;
	surfaceBox->r_ybot = w->w_surfaceArea.r_ybot + tmp/w->w_scale;
	surfaceBox->r_ytop = surfaceBox->r_ybot + unitsPerPixel + 1;
    }

    if (surfacePoint != NULL)
    {
        adjust = w->w_scale/2;
	tmp = (SUBPIXEL*screenPoint->p_x) - w->w_origin.p_x;
	if (tmp >= 0) tmp += adjust;
	else tmp -= adjust;
	surfacePoint->p_x = w->w_surfaceArea.r_xbot + tmp/w->w_scale;

	tmp = (SUBPIXEL*screenPoint->p_y) - w->w_origin.p_y;
	if (tmp >= 0) tmp += adjust;
	else tmp -= adjust;
	surfacePoint->p_y = w->w_surfaceArea.r_ybot + tmp/w->w_scale;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * WindSurfaceToScreen --
 *
 * 	This procedure transforms a rectangle in surface coordinates
 *	to a rectangle in the screen coordinates of a particular window.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The rectangle pointed to by screen is filled in with the
 *	coordinates (in pixels) of the screen area corresponding
 *	to surface.  A pixel is included in screen if it is
 *	touched in any way by surface.  If any of the edges of
 *	screen is outside w->w_surfaceArea, the corresponding
 *	edge of the result will also be outside w->w_screenArea.
 *
 * Design Note:
 *	This procedure is trickier than it looks, for a couple of
 *	reasons having to do with numerical accuracy.  The
 *	transformation is done in units of 1/SUBPIXEL pixel (fix-point
 *	arithmetic), in order to avoid accumulated round-off error
 *	when there are fractional pixels per surface unit.  Furthermore,
 *	it's important that the transformation to screen units NOT be
 *	done with a procedure like GeoTransRect.  The reason is that
 *	GeoTransRect does the multiplication before the addition or
 *	subtraction, which can result in arithmeic overflow.  We use
 *	a different form for the transform here, in terms of an origin
 *	and a scale, with the translation done FIRST and multiplication
 *	later.  This, combined with the clipping to w->w_surfaceArea,
 *	guarantees numerical accuracy as long as surface coordinates
 *	fit into an integer and SUBPIXEL*screenSize fits into an integer.
 *
 * ----------------------------------------------------------------------------
 */

void
WindSurfaceToScreen(w, surface, screen)
    MagWindow *w;			/* Window in whose coordinate system the
				 * transform is to be done.
				 */
    Rect *surface;		/* Rectangle in surface coordinates of w. */
    Rect *screen;		/* Rectangle filled in with screen coordinates
				 * (in w) of surface.
				 */
{
    int tmp;

    /* Do the four coordinates one at a time. */

    /* The apparently redundant clipping (also done later by the box	*/
    /* drawing routine) is necessary for two reasons:  1) it prevents	*/
    /* having to do a multiply and shift function for any box extending	*/
    /* outside the viewport, and more critically 2) it prevents integer	*/
    /* overflow when a tile extends very far out of the viewport.	*/

    tmp = surface->r_xbot;
    if (tmp > w->w_surfaceArea.r_xtop)
	screen->r_xbot = w->w_screenArea.r_xtop + 1;
    else
    {
	tmp -= w->w_surfaceArea.r_xbot;
	if (tmp < 0)
	    screen->r_xbot = w->w_screenArea.r_xbot - 1;
        else screen->r_xbot = (w->w_origin.p_x + (tmp * w->w_scale)) >> SUBPIXELBITS;
    }

    tmp = surface->r_ybot;
    if (tmp > w->w_surfaceArea.r_ytop)
	screen->r_ybot = w->w_screenArea.r_ytop + 1;
    else
    {
	tmp -= w->w_surfaceArea.r_ybot;
	if (tmp < 0)
	    screen->r_ybot = w->w_screenArea.r_ybot - 1;
        else screen->r_ybot = (w->w_origin.p_y + (tmp * w->w_scale)) >> SUBPIXELBITS;
    }

    tmp = surface->r_xtop;
    if (tmp > w->w_surfaceArea.r_xtop)
	screen->r_xtop = w->w_screenArea.r_xtop + 1;
    else
    {
	tmp -= w->w_surfaceArea.r_xbot;
	if (tmp < 0)
	    screen->r_xtop = w->w_screenArea.r_xbot - 1;
        else screen->r_xtop = (w->w_origin.p_x + (tmp * w->w_scale)) >> SUBPIXELBITS;
    }

    tmp = surface->r_ytop;
    if (tmp > w->w_surfaceArea.r_ytop)
	screen->r_ytop = w->w_screenArea.r_ytop + 1;
    else
    {
	tmp -= w->w_surfaceArea.r_ybot;
	if (tmp < 0)
	    screen->r_ytop = w->w_screenArea.r_ybot - 1;
        else screen->r_ytop = (w->w_origin.p_y + (tmp * w->w_scale)) >> SUBPIXELBITS;
    }
}

/*
 * ----------------------------------------------------------------------------
 *
 * WindSurfaceToScreenNoClip --
 *
 *	This procedure is like WindSurfaceToScreen except that it
 *	does not clip to the window boundary.  This is necessary for
 *	non-Manhattan tiles to make sure they are not clipped.
 *	However, this routine is also useful for Manhattan geometry
 *	when, for example, determining the transformation of a unit
 *	coordinate.
 *
 *	We convert to type double long because at close-in scales, large
 *	values of w_scale can cause integer overflow on 32-bit integers.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Pointer argument "screen" is filled with the transformed
 *	coordinates.
 *
 * ----------------------------------------------------------------------------
 */

void
WindSurfaceToScreenNoClip(w, surface, screen)
    MagWindow *w;
    Rect *surface;
    Rect *screen;
{
    dlong tmp, dval;

    tmp = (dlong)(surface->r_xbot - w->w_surfaceArea.r_xbot);
    dval = (dlong)w->w_origin.p_x + (tmp * (dlong)w->w_scale);
    screen->r_xbot = (int)(dval >> SUBPIXELBITS);
    tmp = surface->r_ybot - w->w_surfaceArea.r_ybot;
    dval = (dlong)w->w_origin.p_y + (tmp * (dlong)w->w_scale);
    screen->r_ybot = (int)(dval >> SUBPIXELBITS);
    tmp = surface->r_xtop - w->w_surfaceArea.r_xbot;
    dval = (dlong)w->w_origin.p_x + (tmp * (dlong)w->w_scale);
    screen->r_xtop = (int)(dval >> SUBPIXELBITS);
    tmp = surface->r_ytop - w->w_surfaceArea.r_ybot;
    dval = (dlong)w->w_origin.p_y + (tmp * (dlong)w->w_scale);
    screen->r_ytop = (int)(dval >> SUBPIXELBITS);
}


/*
 * ----------------------------------------------------------------------------
 *
 * WindPointToScreen --
 *
 * 	This is just like WindSurfaceToScreen, except it transforms a
 *	point instead of a rectangle.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Screen is modified to contain screen coordinates.  They may be
 *	clipped just as in WindSurfaceToScreen.
 *
 * ----------------------------------------------------------------------------
 */

void
WindPointToScreen(w, surface, screen)
    MagWindow *w;			/* Window in whose coordinate system the
				 * transform is to be done.
				 */
    Point *surface;		/* Point in surface coordinates of w. */
    Point *screen;		/* Point filled in with screen coordinates
				 * (in w) of surface.
				 */
{
    int tmp;

    /* Do the coordinates one at a time. */

    tmp = surface->p_x;
    if (tmp > w->w_surfaceArea.r_xtop)
	tmp = w->w_surfaceArea.r_xtop;
    tmp -= w->w_surfaceArea.r_xbot;
    if (tmp < 0) tmp = 0;
    screen->p_x = (w->w_origin.p_x + (tmp*w->w_scale)) >> SUBPIXELBITS;

    tmp = surface->p_y;
    if (tmp > w->w_surfaceArea.r_ytop)
	tmp = w->w_surfaceArea.r_ytop;
    tmp -= w->w_surfaceArea.r_ybot;
    if (tmp < 0) tmp = 0;
    screen->p_y = (w->w_origin.p_y + (tmp*w->w_scale)) >> SUBPIXELBITS;
}

/*
 * ----------------------------------------------------------------------------
 *
 * windScreenToFrame --
 *
 * 	Transform a point from screen coordinates to frame coordinates.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	Frame is filled in with the frame coordinates corresponding to
 *	the screen coordinates.
 *
 * ----------------------------------------------------------------------------
 */

void
windScreenToFrame(w, screen, frame)
    MagWindow *w;			/* Window in whose coordinate system the
				 * transform is to be done.
				 */
    Point *screen;		/* Point in screen coordinates of w. */
    Point *frame;		/* Point filled in with frame coordinates.
				 */
{
    switch ( WindPackageType )
    {
	case WIND_X_WINDOWS:
	    frame->p_x = screen->p_x + w->w_frameArea.r_xbot;
	    frame->p_y = screen->p_y + w->w_frameArea.r_ybot;
	    break;

	default:
	    /* WIND_MAGIC_WINDOWS */
	    *frame = *screen;
    }
}
