343 lines
12 KiB
C
343 lines
12 KiB
C
|
#ifndef lint
|
|||
|
static char sccs_id[] = "@(#)try_sweep.c 5.3 9/1/88";
|
|||
|
#endif
|
|||
|
|
|||
|
/*
|
|||
|
* Copyright 1988 by Siemens Research and Technology Laboratories, Princeton, NJ
|
|||
|
*
|
|||
|
* All Rights Reserved
|
|||
|
*
|
|||
|
* 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 and that
|
|||
|
* both that copyright notice and this permission notice appear in
|
|||
|
* supporting documentation, and that the name of Siemens Research and Technology
|
|||
|
* Laboratories not be used in advertising or publicity pertaining to
|
|||
|
* distribution of the software without specific, written prior permission.
|
|||
|
*
|
|||
|
*
|
|||
|
* SIEMENS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
|
|||
|
* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
|
|||
|
* SIEMENS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
|
|||
|
* ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
|
|||
|
* WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
|
|||
|
* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
|||
|
* SOFTWARE.
|
|||
|
*/
|
|||
|
#include "copyright.h"
|
|||
|
#include <stdio.h>
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
try_edge_sweep.c: by Michael Berman
|
|||
|
RTL Windows System
|
|||
|
last modified: March 27, 1987
|
|||
|
version: 0.4
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
#include "tileD.h"
|
|||
|
#include "tile.h"
|
|||
|
#include "twin.h"
|
|||
|
|
|||
|
#define SPLIT_RECT(r) r.left_x,r.top_y,r.right_x,r.bottom_y
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
EDGE SWEEPING
|
|||
|
|
|||
|
Problem: Given a line segment which is parallel with one of the
|
|||
|
sides of the desktop, and which is contained within one or
|
|||
|
more tiles, 'sweep' it perpendicularly until it touches a new
|
|||
|
tile or the desktop edge. The value returned represents the
|
|||
|
pixel position, on the desktop, of the furthest extent of the
|
|||
|
sweep. (Note that this is NOT the same as the distance the
|
|||
|
edge moved; you can derive that by subtracting/adding the
|
|||
|
extent from/to the original edge position.)
|
|||
|
|
|||
|
Due to the asymmetry of corner stitching, there are two quite
|
|||
|
different versions of this, namely horizontal (which sweeps a
|
|||
|
vertical segment either to the Left or R) and vertical (sweeps
|
|||
|
a horizontal segment up or Down).
|
|||
|
|
|||
|
This will be implemented as two boolean funtions, returning
|
|||
|
TRUE if the sweep was successful, and FALSE if there is a
|
|||
|
logical inconsistency in the arguments. The "extent" argument
|
|||
|
will be a pointer to a location to give the answer, which is
|
|||
|
the x (in the case of horizontal) or y (in the case of
|
|||
|
vertical) marking the extent of the sweep.
|
|||
|
|
|||
|
The external entry point for edge sweeping is: Trial_Edge_Sweep
|
|||
|
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
/* variables with scope over all functions in this file */
|
|||
|
|
|||
|
Basetype current_extent; /* best current guess at extent */
|
|||
|
Side global_dir; /* direction of sweep, used by enumeration fcn */
|
|||
|
bool touched_solid_tile; /* flag set if the enumeration fcn finds solid */
|
|||
|
|
|||
|
#define ASSERT(a,m) \
|
|||
|
{ \
|
|||
|
if (!(a)) \
|
|||
|
printf("YOW! %s \n",m);\
|
|||
|
}
|
|||
|
|
|||
|
void Enum_Horiz_Extent();
|
|||
|
bool Vert_Sweep(), Horiz_Sweep();
|
|||
|
|
|||
|
bool
|
|||
|
Trial_Edge_Sweep(p1,p2,dir,extent)
|
|||
|
TWPoint p1, p2;
|
|||
|
Side dir;
|
|||
|
Basetype *extent;
|
|||
|
{
|
|||
|
if ((dir == Top) || (dir == Bottom))
|
|||
|
return(Vert_Sweep(p1,p2,dir,extent));
|
|||
|
else if ((dir == Left) || (dir == Right))
|
|||
|
return(Horiz_Sweep(p1,p2,dir,extent));
|
|||
|
else
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
bool
|
|||
|
Horiz_Sweep(p1, p2, dir, extent)
|
|||
|
TWPoint p1, p2;
|
|||
|
Side dir;
|
|||
|
Basetype *extent;
|
|||
|
/****************************************************************************
|
|||
|
Arguments:
|
|||
|
p1, p2: The points defining the line segment. Since this is a
|
|||
|
horizontal sweep, p1.x must equal p2.x. p1 must be above p2;
|
|||
|
hence p1.y <= p2.y.
|
|||
|
dir: The direction to sweep-- must be Left or Right.
|
|||
|
extent: Pointer to Basetype in which result is returned.
|
|||
|
|
|||
|
Returns: Boolean flag indicating success/failure of sweep.
|
|||
|
|
|||
|
Modifies: *extent (parameter)
|
|||
|
|
|||
|
Description: Horiz_Sweep performs a horizontal line sweep, that is,
|
|||
|
it simulates moving a vertical line segment in the horizontal
|
|||
|
direction indicated by "dir" (= Left or Right) as far as it will go
|
|||
|
until it first touches a solid tile. The assumption made is
|
|||
|
that the line segment you start from is at the edge of a solid
|
|||
|
tile; therefore, it is moved one pixel in the appropriate
|
|||
|
direction before the sweep begins. If after moving it is
|
|||
|
still on a solid tile, the original position is returned. The
|
|||
|
value of the Boolean function indicates whether the sweep was
|
|||
|
performed; a return of "false" indicates that the arguments
|
|||
|
were incorrect or contradictory. The pixel location of the
|
|||
|
furthest position of the sweep is returned in the Basetype
|
|||
|
pointed to by "extent". Because all left tile edges are even
|
|||
|
and right edges odd, a left-hand sweep always returns an even
|
|||
|
number, while a right-hand sweep returns an odd number.
|
|||
|
|
|||
|
Logic: First, the arguments are checked for consistency; if an error
|
|||
|
is found, return(FALSE). Before the call, the value of
|
|||
|
current_extent is initialized to point to the appropriate
|
|||
|
desktop edge (left for a left sweep, right for a right sweep.)
|
|||
|
current_extent is used to store the position of the "closest"
|
|||
|
edge found so far. Because the horizontal space tiles are
|
|||
|
required to be maximal, we simply examine each tile that the
|
|||
|
line segment crosses and find the tile that ends "closest" to
|
|||
|
the line segment, which is equivalent to the maximum extent of
|
|||
|
a sweep before touching a new tile. Tiles_In_Area is used to
|
|||
|
enumerate the set of tiles crossed by the line segment, which
|
|||
|
actually is passed as a rectangle of width 2. The
|
|||
|
call to Tiles_In_Area uses the enumeration function
|
|||
|
Enum_Horiz_Tiles which is defined above; the result is stored
|
|||
|
in current_extent. The enumeration also checks for solid tiles;
|
|||
|
if found, then a flag "touched_solid_tile" is set, so that the
|
|||
|
unchanged edge can be returned.
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
{
|
|||
|
TWRectangle Desktop_View, r;
|
|||
|
|
|||
|
if (p1.x != p2.x)
|
|||
|
return(FALSE);
|
|||
|
r.top_y = p1.y;
|
|||
|
r.bottom_y = p2.y;
|
|||
|
if (r.top_y > r.bottom_y)
|
|||
|
return(FALSE);
|
|||
|
Desktop_View = Tilwin_Get_Desktop_Rectangle();
|
|||
|
switch(dir)
|
|||
|
{
|
|||
|
case Left:
|
|||
|
if (!Is_Even_Number(p1.x))
|
|||
|
return(FALSE);
|
|||
|
else
|
|||
|
{
|
|||
|
current_extent = Desktop_View.left_x;
|
|||
|
r.left_x = p1.x - 2;
|
|||
|
r.right_x = r.left_x + 1;
|
|||
|
}
|
|||
|
break;
|
|||
|
case Right:
|
|||
|
if (Is_Even_Number(p1.x))
|
|||
|
return(FALSE);
|
|||
|
else
|
|||
|
{
|
|||
|
current_extent = Desktop_View.right_x;
|
|||
|
r.right_x = p1.x + 2;
|
|||
|
r.left_x = r.right_x - 1;
|
|||
|
}
|
|||
|
break;
|
|||
|
default:
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
global_dir = dir;
|
|||
|
touched_solid_tile = FALSE;
|
|||
|
Tiles_In_Area(SPLIT_RECT(r), Enum_Horiz_Extent);
|
|||
|
*extent = (touched_solid_tile)? p1.x : current_extent;
|
|||
|
ASSERT((Is_Even_Number(current_extent) && dir == Left) ||
|
|||
|
(!(Is_Even_Number(current_extent)) && dir == Right),
|
|||
|
"Parity error in current_extent -- Horiz_Sweep");
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
bool
|
|||
|
Vert_Sweep(p1, p2, dir, extent)
|
|||
|
TWPoint p1, p2;
|
|||
|
Side dir;
|
|||
|
Basetype *extent;
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
Arguments:
|
|||
|
p1, p2: The points defining the line segment. Since this is a
|
|||
|
vertical sweep, p1.y must equal p2.y. p1 must be to the left
|
|||
|
of p2; hence, p1.x <= p2.x;
|
|||
|
dir: The direction to sweep-- must be Top or Bottom.
|
|||
|
extent: pointer to Basetype in which result is returned.
|
|||
|
|
|||
|
Returns: Boolean flag indicating success/failure of sweep.
|
|||
|
|
|||
|
Modifies: *extent (parameter)
|
|||
|
|
|||
|
Description: Vert_Sweep performs a vertical line sweep, that is, it
|
|||
|
simulates moving a horizontal line segment in the vertical
|
|||
|
direction indicated by "dir" (= Top or Bottom) as far as it will
|
|||
|
go until it first touches a solid tile. The assumption made is
|
|||
|
that the line segment you start from is at the edge of a solid
|
|||
|
tile; therefore it is moved one pixel in the appropriate direction
|
|||
|
before the sweep begins. If, after moving it is still on a solid
|
|||
|
tile, the original position is returned. The values for the
|
|||
|
function and the return value *extent are the same as for
|
|||
|
Horiz_Sweep. Because all top tile edges are even and bottom
|
|||
|
edges odd, a sweep Up will always return an even number, while
|
|||
|
a sweep down returns an odd number.
|
|||
|
|
|||
|
Logic: A little more complicated than the horizontal case due to the
|
|||
|
asymetry of corner stitching. After checking for consistency,
|
|||
|
we find the initial line segment, which is one pixel above or
|
|||
|
below the line segment passed (depending on the direction of
|
|||
|
the sweep. We find the tile(s) which
|
|||
|
contain the initial line segment. If the initial line is not
|
|||
|
entirely within a single space tile, then the original edge is
|
|||
|
returned without change. If the segment is contained
|
|||
|
entirely within a single space tile, then we move to the edge
|
|||
|
of the tile, and advance the line segment just one pixel
|
|||
|
further, beyond the original space tile. This process is
|
|||
|
iterated until at least part of the line segment is not in a
|
|||
|
space tile. (Note that if the two points of the segment are
|
|||
|
in different space tiles, there MUST be a solid tile somewhere
|
|||
|
in between.) This process may lead to the segment crossing
|
|||
|
solid tiles, or to the edge of the desktop. Once this is
|
|||
|
done, the y value is "backed up" by 1.
|
|||
|
****************************************************************************/
|
|||
|
|
|||
|
{
|
|||
|
Tile *tpl, *tpr;
|
|||
|
TWRectangle r;
|
|||
|
Basetype y_extent;
|
|||
|
|
|||
|
if (p1.y != p2.y)
|
|||
|
return(FALSE);
|
|||
|
r.left_x = p1.x;
|
|||
|
r.right_x = p2.x;
|
|||
|
if (r.left_x > r.right_x)
|
|||
|
return(FALSE);
|
|||
|
if (dir == Top)
|
|||
|
{
|
|||
|
if (!Is_Even_Number(p1.y))
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
else if (dir == Bottom)
|
|||
|
{
|
|||
|
if (Is_Even_Number(p1.y))
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
else
|
|||
|
return(FALSE);
|
|||
|
y_extent = (dir == Top)? p1.y - 1: p1.y + 1;
|
|||
|
tpl = (Tile *)Tile_at_Point(p1.x,y_extent);
|
|||
|
tpr = (Tile *)Tile_at_Point(p2.x,y_extent);
|
|||
|
|
|||
|
while ((tpl == tpr) &&
|
|||
|
(tpl != NULL_TILE) &&
|
|||
|
(Tile_Get_Type(tpl) == Space))
|
|||
|
{
|
|||
|
y_extent = (dir == Top)? Tile_Get_Corners(tpl).top_y - 1:
|
|||
|
Tile_Get_Corners(tpl).bottom_y + 1;
|
|||
|
tpl = (Tile *)Tile_at_Point(p1.x,y_extent);
|
|||
|
tpr = (Tile *)Tile_at_Point(p2.x,y_extent);
|
|||
|
}
|
|||
|
y_extent = (dir == Top)? y_extent + 1 : y_extent - 1;
|
|||
|
ASSERT((Is_Even_Number(y_extent) && dir == Top) ||
|
|||
|
(!(Is_Even_Number(y_extent)) && dir == Bottom),
|
|||
|
"Parity error in y_extent -- Vert_Sweep");
|
|||
|
*extent = y_extent;
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
Enum_Horiz_Extent(tp)
|
|||
|
Tile *tp;
|
|||
|
|
|||
|
/****************************************************************************
|
|||
|
|
|||
|
Arguments:
|
|||
|
tp: Pointer to tile to be examined.
|
|||
|
|
|||
|
Modifies:
|
|||
|
current_extent (as a side effect)
|
|||
|
touched_solid_tile (as a side effect)
|
|||
|
|
|||
|
Description: Enum_Horiz_Extent is used by Horiz_Sweep only. It
|
|||
|
looks at a tile and checks whether the left (or right,
|
|||
|
depending on sweep direction) edge of the tile is more
|
|||
|
constraining than the previous best, which is current_extent.
|
|||
|
We also set a flag if we hit a solid tile -- once this is
|
|||
|
set TRUE, any additional enums do nothing.
|
|||
|
This function is passed as a parameter to Tiles_In_Area, and
|
|||
|
executed once for each tile (tp) in the specified range.
|
|||
|
|
|||
|
Logic: The operation of the function is controlled by the variable
|
|||
|
current_extent, which is global to this file. Each time
|
|||
|
Enum_Horiz_Extent is called (for a particular tile) it checks
|
|||
|
whether that tile represents a greater "extent" than the
|
|||
|
previous best. That is, is this tile further to the left (or
|
|||
|
right) than the previous best. The choice of left or
|
|||
|
right is determined by the value of global_dir, which is
|
|||
|
of scope external to Enum_Horiz_Extent.
|
|||
|
****************************************************************************/
|
|||
|
{
|
|||
|
if (!touched_solid_tile) /* otherwise, don't bother */
|
|||
|
if (Tile_Get_Type(tp) == Space)
|
|||
|
{
|
|||
|
if (global_dir == Left)
|
|||
|
{
|
|||
|
if (Left_X(tp) > current_extent)
|
|||
|
{
|
|||
|
current_extent = Left_X(tp);
|
|||
|
}
|
|||
|
}
|
|||
|
else /* global_dir == Right */
|
|||
|
{
|
|||
|
if (Right_X(tp) < current_extent)
|
|||
|
{
|
|||
|
current_extent = Right_X(tp);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
touched_solid_tile = TRUE;
|
|||
|
}
|