home *** CD-ROM | disk | FTP | other *** search
- /*
- 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;
- }
-