home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Toolkit for DOOM
/
DOOMTOOL.ISO
/
editors
/
dme301.zip
/
SOURCE.ZIP
/
LINEMATH.C
< prev
next >
Wrap
C/C++ Source or Header
|
1994-07-16
|
19KB
|
883 lines
/*
This is a DMapEdit source code module. Though it is copyrighted, you
may modify it and use it for your own personal use, meaning that new
modified code and anything derived from it (such as exe files) doesn't
get distributed to anyone, unless you get my permission first. Code
from this file, or code based on ideas from this file may be used with
other programs, provided that you give credit for it in the source code,
documentation, and 'about' windows or screens, if one exists, for the
programs using it. Giving credit means something like this:
Code from DMapEdit was used in this program
or
Some code for this program was based on ideas presented in DMapEdit
Whatever. Just be sure to mention "DMapEdit" in such a way that it's
self-evident how it was useful to the new program, and be sure to have
"DMapEdit is a trademark of Jason Hoffoss" in the docs. That's all..
*/
#include <stdio.h>
#include <math.h>
#include <graphics.h>
#include <limits.h>
#include "dme.h"
#include "dme2.h"
extern int simple_splits;
extern int complex_splits;
extern int *slivers;
/* calculate the (x,y) where 2 lines cross */
int calc_line_cross(int *vv, int x1, int y1, int v2, int v3, int v4, uint angle)
{
int x2, y2, x3, y3, x4, y4, angle2, old_angle=0, inc=1;
long dx1, dx2, dy1, dy2, x, y, ox, oy, oldx, oldy, p1, p2, p3;
float fy;
x2 = vertexes[v2].x;
x3 = vertexes[v3].x;
x4 = vertexes[v4].x;
y2 = vertexes[v2].y;
y3 = vertexes[v3].y;
y4 = vertexes[v4].y;
dx1 = x1 - x2;
dx2 = x3 - x4;
dy1 = y1 - y2;
dy2 = y3 - y4;
if (!dx1) /* line 1 is vertical */
{
x = x1;
if (!dy2) /* line 2 horizontal? */
{
y = y3;
goto check;
}
y = (float) dy2 * (x - x3) / dx2 + y3;
goto check;
}
if (!dx2) /* line 2 is vertical */
{
x = x3;
if (!dy1) /* line 1 horizontal? */
{
y = y1;
goto check;
}
y = (float) dy1 * (x - x1) / dx1 + y1;
goto check;
}
if (!dy1) /* line 1 is horizontal */
{
y = y1;
if (!dx2) /* line 2 vertical? */
{
x = x3;
goto check;
}
x = (float) dx2 * (y - y3) / dy2 + x3;
goto check;
}
if (!dy2) /* line 2 is horizontal */
{
y = y3;
if (!dx1) /* line 1 vertical? */
{
x = x1;
goto check;
}
x = (float) dx1 * (y - y1) / dy1 + x1;
goto check;
}
p1 = dx1 * dy2;
p2 = dx2 * dy1;
p3 = dy1 * dy2;
fy = (y1*p1 - y3*p2 + x3*p3 - x1*p3) / (p1 - p2);
y = fy + 0.5;
x = (float) dx2 * (fy - y3) / dy2 + x3;
check:
ox = x;
oy = y;
check2:
if (x1 == x && y1 == y)
angle2 = 0;
else
angle2 = adjusted_angle(angle, x1, y1, x, y);
if (!angle2 || angle2 == INT_MIN)
{
simple_splits++;
if (v_size == v_max)
{
void far *ptr;
ptr = resize_farmem(vertexes, (v_max+20) * sizeof(struct v_struct),
"Vertexes");
if (!ptr)
return -1;
vertexes = ptr;
v_max += 20;
}
vertexes[v_size].x = x;
vertexes[v_size].y = y;
*vv = v_size;
return v_size++;
}
if (angle2 > 0 && old_angle < 0) /* can't split with 0 angle */
{
slivers[complex_splits++] = v_size;
if (v_size+1 >= v_max)
{
void far *ptr;
ptr = resize_farmem(vertexes, (v_max+20) * sizeof(struct v_struct),
"Vertexes");
if (!ptr)
return -1;
vertexes = ptr;
v_max += 20;
}
vertexes[v_size].x = x;
vertexes[v_size++].y = y;
vertexes[v_size].x = oldx;
vertexes[v_size].y = oldy;
*vv = v_size++;
return (v_size - 2);
}
if (angle2 < 0 && old_angle > 0)
{
slivers[complex_splits++] = v_size;
if (v_size+1 >= v_max)
{
void far *ptr;
ptr = resize_farmem(vertexes, (v_max+20) * sizeof(struct v_struct),
"Vertexes");
if (!ptr)
return -1;
vertexes = ptr;
v_max += 20;
}
vertexes[v_size].x = x;
vertexes[v_size].y = y;
*vv = v_size++;
vertexes[v_size].x = oldx;
vertexes[v_size].y = oldy;
return v_size++;
}
oldx = ox;
oldy = oy;
ox = x;
oy = y;
old_angle = angle2;
if (abs(dx2) < abs(dy2)) /* more vertical than horizontal */
{
y += inc;
x = (float) dx2 * (y - y3) / dy2 + x3;
} else {
x += inc;
y = (float) dy2 * (x - x3) / dx2 + y3;
}
if (inc < 0)
inc = -inc + 1;
else
inc = -inc - 1;
goto check2;
}
/* determine if line can be seen on the screen (isn't totally out of
bounds), and if so, clip it to fit on the screen.
*/
int line_visible(int num)
{
int i, x1, y1, x2, y2, point1, point2;
point1 = linedefs[num].v1;
point2 = linedefs[num].v2;
x1 = adjvx[point1];
x2 = adjvx[point2];
y1 = adjvy[point1];
y2 = adjvy[point2];
return line_in_rect(x1, y1, x2, y2, 0, 0, maxx, maxy);
}
/* determine if any point along the line (x1,y1)-(x2,y2) lies within the
rectangle (xmin,ymin)-(xmax,ymax). If it does, the line is clipped
(values returned through global <win> struct)
*/
int line_in_rect(int x1, int y1, int x2, int y2, int xmin, int ymin, int xmax, int ymax)
{
int i, left1, right1, top1, bottom1, left2, right2, top2, bottom2;
while (1)
{
left1 = x1 < xmin; /* detect all out of bounds coords */
left2 = x2 < xmin;
right1 = x1 > xmax;
right2 = x2 > xmax;
top1 = y1 < ymin;
top2 = y2 < ymin;
bottom1 = y1 > ymax;
bottom2 = y2 > ymax;
if ((left1*left2 + right1*right2 + top1*top2 + bottom1*bottom2) != 0)
return 0; /* both points out of same range */
if (!(left1+left2+right1+right2+top1+top2+bottom1+bottom2))
{ /* line fully fits inside rectangle */
win.left = x1;
win.right = x2;
win.top = y1;
win.bottom = y2;
return 1;
}
if (left1) /* make a clip and re-check */
{
y1 = xclip(xmin, x1, y1, x2, y2);
x1 = xmin;
continue;
}
if (left2)
{
y2 = xclip(xmin, x1, y1, x2, y2);
x2 = xmin;
continue;
}
if (right1)
{
y1 = xclip(xmax, x1, y1, x2, y2);
x1 = xmax;
continue;
}
if (right2)
{
y2 = xclip(xmax, x1, y1, x2, y2);
x2 = xmax;
continue;
}
if (top1)
{
x1 = yclip(ymin, x1, y1, x2, y2);
y1 = ymin;
continue;
}
if (top2)
{
x2 = yclip(ymin, x1, y1, x2, y2);
y2 = ymin;
continue;
}
if (bottom1)
{
x1 = yclip(ymax, x1, y1, x2, y2);
y1 = ymax;
continue;
} /* must be bottom2 */
x2 = yclip(ymax, x1, y1, x2, y2);
y2 = ymax;
}
}
/* given line (x1,y1)-(x2,y2) and x, determine y. (x,y) lies on line */
int xclip(int x, int x1, int y1, int x2, int y2)
{
return (y1 + (float) (y2-y1) * (x-x1) / (x2-x1));
}
/* given line (x1,y1)-(x2,y2) and y, determine x. (x,y) lies on line */
int yclip(int y, int x1, int y1, int x2, int y2)
{
return (x1 + (float) (x2-x1) * (y-y1) / (y2-y1));
}
/* distance mouse is from line (x1,y1)-(x2,y2) */
int line_dist(int x1, int y1, int x2, int y2)
{
int x, y, dist1, dist2;
float m1, m2, fx, fy;
x1 -= mousex; /* put mouse at the origin for calculations */
y1 -= mousey;
x2 -= mousex;
y2 -= mousey;
if (x1 == x2) /* vertical line */
{
if (between(0, y1, y2))
return abs(x1);
dist1 = abs(x1) + abs(y1); /* not true distance, but faster */
dist2 = abs(x2) + abs(y2); /* actually diamond shaped */
if (dist1 < dist2)
return dist1;
return dist2;
}
if (y1 == y2) /* horizontal line */
{
if (between(0, x1, x2))
return abs(y1);
dist1 = abs(x1) + abs(y1);
dist2 = abs(x2) + abs(y2);
if (dist1 < dist2)
return dist1;
return dist2;
}
m1 = (float) (y1 - y2) / (x1 - x2); /* line's slope */
m2 = -(1/m1); /* perpendicular line's slope */
fx = (m1 * x1 - y1) / (m1 - m2); /* solve 2 equations: */
fy = m2 * fx; /* y = m1(x - x1) + y1 and y = m2 * x */
x = fx; /* now precision isn't important */
y = fy;
if (between(x, x1, x2))
return (abs(x) + abs(y)); /* lies on the line segment */
dist1 = abs(x1) + abs(y1);
dist2 = abs(x2) + abs(y2);
if (dist1 < dist2)
return dist1;
return dist2;
}
/* find vertical distance from (x,y) to a line (can be pos or neg) */
int line_dist2(int x, int y, int x1, int y1, int x2, int y2)
{
int yy;
x1 -= x; /* put (x,y) at the origin for calculations */
y1 -= y;
x2 -= x;
y2 -= y;
if (y1 == y2) /* horizontal line */
return y1;
yy = (float) x1 * (y2 - y1) / (x1 - x2) + y1;
return yy;
}
/* calculate the angle of line (x1,y1)-(x2,y2). (x1,y1) is origin */
uint calc_angle(int x1, int y1, int x2, int y2)
{
int x, y;
uint ang;
double angle;
x = abs(x2 - x1);
y = abs(y2 - y1);
angle = M_PI_2;
if (x != 0)
angle = atan( (float) y/x);
ang = angle * 32768 / M_PI;
x = ((x2-x1) < 0)*2 + ((y2-y1) < 0);
if (x == 1)
ang = 65536 - ang;
if (x == 2)
ang = 32768 - ang;
if (x == 3)
ang += 32768;
return ang;
}
/* find the next line around a polygon
vertex = consider lines connected to this vertex
angle = angle of line, vertex being the 'toward' point
line = current line
side = tracing around left(1) or right(0) side
return = direction of line (to(1) or from(0) vertex)
*/
int find_next_line(int *vertex, uint *angle, int *line, int side)
{
int i, num, sidedef, dir, line_num[50];
uint new_angle, angles[50];
long langle, angle_min, angle_max;
for (i=0, num=0; i<l_size; i++)
{
if (i == *line)
continue;
if (linedefs[i].v1 == *vertex)
{
line_num[num] = i + 1;
angles[num++] = calc_angle(vertexes[*vertex].x,
vertexes[*vertex].y, vertexes[linedefs[i].v2].x,
vertexes[linedefs[i].v2].y);
}
if (linedefs[i].v2 == *vertex)
{
line_num[num] = -i - 1;
angles[num++] = calc_angle(vertexes[*vertex].x,
vertexes[*vertex].y, vertexes[linedefs[i].v1].x,
vertexes[linedefs[i].v1].y);
}
}
if (!num)
return -1; /* dead end line */
*line = 0;
*angle ^= 0x8000; /* switch to opposite direction */
angle_min = 655360;
angle_max = 0;
for (i=0; i<num; i++)
{
langle = (long) angles[i] - *angle;
if (langle < 0)
langle += 65536;
if (side)
{
if (langle < angle_min)
{
angle_min = langle;
*line = line_num[i];
new_angle = angles[i];
}
} else {
if (langle > angle_max)
{
angle_max = langle;
*line = line_num[i];
new_angle = angles[i];
}
}
}
if (!(*line))
return -1;
if (*line > 0)
{
dir = 0;
*line -= 1;
*vertex = linedefs[*line].v2;
} else {
dir = 1;
*line = -(*line) - 1;
*vertex = linedefs[*line].v1;
}
*angle = new_angle;
return dir;
}
/* determine if a line & side faces the outside void */
int outside_detect(int line1, int side1)
{
int i, v1, v2, line, orig_line, side, dir;
uint angle;
if (!max_vertex || !l_size)
return -1;
if (line1 == -1)
return 1;
v1 = 0;
for (i=1; i<max_vertex; i++)
if (vertexes[i].y < vertexes[v1].y)
v1 = i; /* find the bottom most vertex on map */
if ((line = downward_line(v1, &side, testmode)) == -1)
return -1;
if (testmode > 1)
{
setcolor(0);
draw_side(tx3, ty3, tx4,ty4);
setcolor(96);
draw_line2(adjustx(tx3), adjusty(ty3),
adjustx(tx4), adjusty(ty4));
}
orig_line = line; /* this is our starting line */
v1 = linedefs[line].v1;
v2 = linedefs[line].v2;
side = dir = 0;
if (vertexes[v1].x < vertexes[v2].x)
side = 1;
angle = calc_angle(vertexes[v1].x, vertexes[v1].y,
vertexes[v2].x, vertexes[v2].y);
do /* go through the entire outside border, checking all lines */
{
if (line == line1)
{
if (dir)
{
if (side != side1)
return 1; /* is on outside */
} else {
if (side == side1)
return 1;
}
}
dir = find_next_line(&v2, &angle, &line, side);
if (dir == -1)
{
deadend_error();
return -1;
}
} while (line != orig_line);
return 0;
}
/* find an inside line of a polygon that (x,y) is within. Return this
line, as well as the side that is inside
*/
int inside_poly(int x, int y, int *side, int test)
{
char msg[60];
int line, new_line, dist, dist_min, v1, v2, x1, x2, y1, y2;
dist_min = INT_MAX; /* find sidedef straight down from (x,y) */
*side = new_line = -1;
for (line=0; line<l_size; line++)
{
v1 = linedefs[line].v1;
v2 = linedefs[line].v2;
x1 = vertexes[v1].x;
x2 = vertexes[v2].x;
y1 = vertexes[v1].y;
y2 = vertexes[v2].y;
if (x1 == x2)
continue; /* vertical line, so skip it */
if ((x1 < x) && (x2 < x))
continue; /* doesn't cross line */
if ((x1 > x) && (x2 > x))
continue; /* doesn't cross line either */
dist = -line_dist2(x, y, x1, y1, x2, y2);
if (dist < 1)
continue; /* wrong direction now */
if (dist > dist_min)
continue;
if (dist == dist_min) /* time to decide which one we want.. */
{
if (test > 1)
{
color2wall(line, 80, 80);
color2wall(new_line, 80, 80);
cursored_get(0, 9);
color2wall(line, 96, 96);
color2wall(new_line, 96, 96);
setcolor(60);
draw_side(tx1, ty1, tx2, ty2);
}
if (line_least_angle(line, new_line, 16384) == new_line)
continue;
}
dist_min = dist;
new_line = line;
if (x1 > x2)
*side = 1;
else
*side = 0;
if (test > 1)
{
if (new_line != -1)
{
setcolor(0);
draw_side(tx1, ty1, tx2, ty2);
setcolor(96);
draw_line2(adjustx(tx1), adjusty(ty1),
adjustx(tx2), adjusty(ty2));
}
setcolor(60);
if (x1 > x2)
{
tx1 = x1;
tx2 = x2;
ty1 = y1;
ty2 = y2;
} else {
tx1 = x2;
tx2 = x1;
ty1 = y2;
ty2 = y1;
}
draw_side(tx1, ty1, tx2, ty2);
sprintf(msg, "New line detected=%d Side=%d dist=%d",
new_line, *side, dist);
toptext(msg);
cursored_get(0, 9);
}
}
return new_line;
}
int downward_line(int vertex, int *side, int test)
{
char msg[60];
int x, y, line, new_line, angle, temp, dir, side1, sidedef;
uint angle_min;
x = vertexes[vertex].x;
y = vertexes[vertex].y;
new_line = -1;
angle_min = 65535;
for (line=0; line<l_size; line++) /* find sidedef of vertex */
{
dir = -1;
if (linedefs[line].v1 == vertex)
{
dir = 0;
temp = linedefs[line].v2;
}
if (linedefs[line].v2 == vertex)
{
dir = 1;
temp = linedefs[line].v1;
}
if (dir != -1)
{
angle = adjusted_angle(49152, x, y, vertexes[temp].x,
vertexes[temp].y);
if (labs(angle) < angle_min)
{
angle_min = labs(angle);
side1 = 0;
if (angle < 0)
side1 = 1;
if (dir != side1)
*side = 1;
else
*side = 0;
if (test > 1)
{
if (new_line != -1)
{
setcolor(0);
draw_side(tx3, ty3, tx4,ty4);
setcolor(96);
draw_line2(adjustx(tx3), adjusty(ty3),
adjustx(tx4), adjusty(ty4));
}
if (side1)
{
tx3 = x;
ty3 = y;
tx4 = vertexes[temp].x;
ty4 = vertexes[temp].y;
} else {
tx4 = x;
ty4 = y;
tx3 = vertexes[temp].x;
ty3 = vertexes[temp].y;
}
setcolor(120);
draw_side(tx3, ty3, tx4, ty4);
if (*side)
sidedef = linedefs[line].sd1;
else
sidedef = linedefs[line].sd2;
sprintf(msg, "detect point=(%d,%d) Sidedef=%d Angle=%d",
x, y, sidedef, angle);
toptext(msg);
cursored_get(0, 9);
}
new_line = line; /* new line found */
}
}
}
return new_line;
}
/* determine which of the 2 given lines (both with a common vertex) has
the closest angle to the given angle (common vertex is the origin
of all the angles)
*/
int line_least_angle(int line1, int line2, uint angle)
{
int v1, v2, v3, v4, x1, y1, x2, y2, x3, y3, x4, y4, temp;
v1 = linedefs[line1].v1;
v2 = linedefs[line1].v2;
v3 = linedefs[line2].v1;
v4 = linedefs[line2].v2;
if ((v1 == v4) || (v2 == v4))
{
temp = v3;
v3 = v4;
v4 = temp;
}
if (v2 == v3) {
temp = v1;
v1 = v2;
v2 = temp;
}
if (v1 != v3)
return -1;
x1 = vertexes[v1].x;
x2 = vertexes[v2].x;
x3 = vertexes[v3].x;
x4 = vertexes[v4].x;
y1 = vertexes[v1].y;
y2 = vertexes[v2].y;
y3 = vertexes[v3].y;
y4 = vertexes[v4].y;
if (abs(adjusted_angle(angle, x1, y1, x2, y2)) <
abs(adjusted_angle(angle, x3, y3, x4, y4)))
return line1;
return line2;
}
/* return angle of line (x1,y1)-(x2,y2) using <angle> as the reference */
int adjusted_angle(uint angle, int x1, int y1, int x2, int y2)
{
long angle2;
angle2 = (long) angle - calc_angle(x1, y1, x2, y2);
if (angle2 > INT_MAX)
angle2 -= 65536;
if (angle2 < INT_MIN)
angle2 += 65536;
return angle2;
}
/* determine where (x,y) lies relative to a line:
neg = left side
zero = on line
pos = right side
*/
int line_side(int line, int x, int y)
{
int x1, y1, x2, y2, v1, v2;
uint angle;
v1 = linedefs[line].v1;
v2 = linedefs[line].v2;
x1 = vertexes[v1].x;
x2 = vertexes[v2].x;
y1 = vertexes[v1].y;
y2 = vertexes[v2].y;
angle = calc_angle(x1, y1, x, y);
return adjusted_angle(angle, x1, y1, x2, y2);
}
/* take on characteristics of ajacent lines. */
int match_line(int vertex, int vertex2)
{
int i, num, v1, v2, sidedef, line_min, line_max, line_num[50];
uint angle, angles[50];
long langle, angle_min, angle_max;
num = 0;
for (i=0; i<l_size; i++)
{
v1 = linedefs[i].v1;
v2 = linedefs[i].v2;
if (v1 == vertex && num < 50)
{
line_num[num] = i + 1;
angles[num++] = calc_angle(vertexes[vertex].x,
vertexes[vertex].y, vertexes[v2].x, vertexes[v2].y);
}
if (v2 == vertex && num < 50)
{
line_num[num] = -i - 1;
angles[num++] = calc_angle(vertexes[vertex].x,
vertexes[vertex].y, vertexes[v1].x, vertexes[v1].y);
}
}
if (!num)
return 0; /* no other lines connected to this vertex */
angle = calc_angle(vertexes[vertex].x, vertexes[vertex].y,
vertexes[vertex2].x, vertexes[vertex2].y);
line_min = line_max = 0;
angle_min = 65536;
angle_max = -1;
for (i=0; i<num; i++)
{
langle = (long) angles[i] - angle;
if (langle < 0)
langle += 65536; /* 0-65535 range */
if (langle < angle_min)
{
angle_min = langle;
line_min = line_num[i];
}
if (langle > angle_max)
{
angle_max = langle;
line_max = line_num[i];
}
}
if ((!line_min) || (!line_max))
fatal_error("Impossibility #1");
if (line_min > 0)
{
line_min--;
sidedef = linedefs[line_min].sd1;
} else {
line_min = -line_min - 1;
sidedef = linedefs[line_min].sd2;
}
if (sidedef != -1)
linedefs[l_size].sd2 = add_sidedef(sidedef);
if (line_max > 0)
sidedef = linedefs[line_max - 1].sd2;
else
sidedef = linedefs[-line_max - 1].sd1;
if (sidedef != -1)
linedefs[l_size].sd1 = add_sidedef(sidedef);
if (line_min == l_size)
fatal_error("line_min == l_size");
linedefs[l_size].attrib = linedefs[line_min].attrib;
linedefs[l_size].type = linedefs[line_min].type;
linedefs[l_size].trig = linedefs[line_min].trig;
return 1;
}