aboutsummaryrefslogtreecommitdiff
path: root/engines/sci/engine/kpathing.c
diff options
context:
space:
mode:
authorEugene Sandulenko2009-02-15 11:39:07 +0000
committerEugene Sandulenko2009-02-15 11:39:07 +0000
commite241843bec22600ab4ef98e7a085e82aac73fc93 (patch)
tree61a793884d3462e1feb80e80f202d8816d0c8ec4 /engines/sci/engine/kpathing.c
parente9f742806362a84ffdb176a7414318dd2ab4df89 (diff)
downloadscummvm-rg350-e241843bec22600ab4ef98e7a085e82aac73fc93.tar.gz
scummvm-rg350-e241843bec22600ab4ef98e7a085e82aac73fc93.tar.bz2
scummvm-rg350-e241843bec22600ab4ef98e7a085e82aac73fc93.zip
- Remove some unneeded files
- Mass rename .c to .cpp svn-id: r38227
Diffstat (limited to 'engines/sci/engine/kpathing.c')
-rw-r--r--engines/sci/engine/kpathing.c1734
1 files changed, 0 insertions, 1734 deletions
diff --git a/engines/sci/engine/kpathing.c b/engines/sci/engine/kpathing.c
deleted file mode 100644
index 89273d1137..0000000000
--- a/engines/sci/engine/kpathing.c
+++ /dev/null
@@ -1,1734 +0,0 @@
-/***************************************************************************
- kpathing.c Copyright (C) 2002-2006 Lars Skovlund, Walter van Niftrik
-
-
- This program may be modified and copied freely according to the terms of
- the GNU general public license (GPL), as long as the above copyright
- notice and the licensing information contained herein are preserved.
-
- Please refer to www.gnu.org for licensing details.
-
- This work is provided AS IS, without warranty of any kind, expressed or
- implied, including but not limited to the warranties of merchantibility,
- noninfringement, and fitness for a specific purpose. The author will not
- be held liable for any damage caused by this work or derivatives of it.
-
- By using this source code, you agree to the licensing terms as stated
- above.
-
-
- Please contact the maintainer for bug reports or inquiries.
-
- Current Maintainer:
-
- Walter van Niftrik [w.f.b.w.v.niftrik@stud.tue.nl]
-
-***************************************************************************/
-
-/* Detailed information on the implementation can be found in the report
-** which can be downloaded from FIXME.
-*/
-
-#include <math.h>
-
-#include "sci/include/engine.h"
-#include "sci/include/aatree.h"
-#include "sci/include/list.h"
-
-#define POLY_LAST_POINT 0x7777
-#define POLY_POINT_SIZE 4
-
-#define POLY_GET_POINT(p, i, x, y) do { x = getInt16((p) + (i) * POLY_POINT_SIZE); \
- y = getInt16((p) + 2 + (i) * POLY_POINT_SIZE); \
-} while (0)
-#define POLY_SET_POINT(p, i, x, y) do { putInt16((p) + (i) * POLY_POINT_SIZE, x); \
- putInt16((p) + 2 + (i) * POLY_POINT_SIZE, y); \
-} while (0)
-#define POLY_GET_POINT_REG_T(p, i, x, y) do { x = KP_SINT((p)[(i) * 2]); \
- y = KP_SINT((p)[(i) * 2 + 1]); \
-} while (0)
-
-/* SCI-defined polygon types */
-#define POLY_TOTAL_ACCESS 0
-#define POLY_NEAREST_ACCESS 1
-#define POLY_BARRED_ACCESS 2
-#define POLY_CONTAINED_ACCESS 3
-
-/* Polygon containment types */
-#define CONT_OUTSIDE 0
-#define CONT_ON_EDGE 1
-#define CONT_INSIDE 2
-
-#define POINT_EQUAL(A, B) (((A).x == (B).x) && ((A).y == (B).y))
-
-#define HUGE_DISTANCE 1e10
-
-/* Visibility matrix */
-#define VIS_MATRIX_ROW_SIZE(N) (((N) / 8) + ((N) % 8 ? 1 : 0))
-#define SET_VISIBLE(S, P, Q) ((S)->vis_matrix)[(P) * VIS_MATRIX_ROW_SIZE((S)->vertices) \
- + (Q) / 8] |= (1 << ((Q) % 8))
-#define IS_VISIBLE(S, P, Q) (((S)->vis_matrix[(P) * VIS_MATRIX_ROW_SIZE((S)->vertices) \
- + (Q) / 8] & (1 << ((Q) % 8))) != 0)
-
-#define VERTEX_HAS_EDGES(V) ((V) != CLIST_NEXT(V, entries))
-
-/* Error codes */
-#define PF_OK 0
-#define PF_ERROR -1
-#define PF_FATAL -2
-
-/* Floating point struct */
-typedef struct pointf
-{
- float x, y;
-} pointf_t;
-
-pointf_t
-pointf(float x, float y)
-{
- pointf_t p;
-
- p.x = x;
- p.y = y;
-
- return p;
-}
-
-pointf_t
-to_pointf(point_t p)
-{
- return pointf(p.x, p.y);
-}
-
-typedef struct vertex
-{
- /* Location */
- point_t v;
-
- /* Index */
- int idx;
-
- /* Vertex list entry */
- CLIST_ENTRY(vertex) entries;
-
- /* Dijkstra list entry */
- LIST_ENTRY(vertex) dijkstra;
-
- /* Distance from starting vertex */
- float dist;
-
- /* Previous vertex in shortest path */
- struct vertex *path_prev;
-} vertex_t;
-
-typedef CLIST_HEAD(vertices_head, vertex) vertices_head_t;
-
-typedef struct polygon
-{
- /* Circular list of vertices */
- vertices_head_t vertices;
-
- /* Polygon list entry */
- LIST_ENTRY(polygon) entries;
-
- /* SCI polygon type */
- int type;
-} polygon_t;
-
-/* Pathfinding state */
-typedef struct pf_state
-{
- /* List of all polygons */
- LIST_HEAD(polygons_head, polygon) polygons;
-
- /* Original start and end points */
- point_t start, end;
-
- /* Flags for adding original points to final path */
- char keep_start, keep_end;
-
- /* Start and end points for pathfinding */
- vertex_t *vertex_start, *vertex_end;
-
- /* Array to quickly find vertices by idx */
- vertex_t **vertex_index;
-
- /* Visibility matrix */
- char *vis_matrix;
-
- /* Total number of vertices */
- int vertices;
-} pf_state_t;
-
-static vertex_t *vertex_cur;
-
-/* Temporary hack to deal with points in reg_ts */
-static int
-polygon_is_reg_t(unsigned char *list, int size)
-{
- int i;
-
- /* Check the first three reg_ts */
- for (i = 0; i < (size < 3 ? size : 3); i++)
- if ((((reg_t *) list) + i)->segment)
- /* Non-zero segment, cannot be reg_ts */
- return 0;
-
- /* First three segments were zero, assume reg_ts */
- return 1;
-}
-
-static point_t
-read_point(unsigned char *list, int is_reg_t, int offset)
-{
- point_t point;
-
- if (!is_reg_t) {
- POLY_GET_POINT(list, offset, point.x, point.y);
- } else {
- POLY_GET_POINT_REG_T((reg_t *) list, offset, point.x, point.y);
- }
-
- return point;
-}
-
-
- /*** Debug functions ***/
-
-static void
-draw_line(state_t *s, point_t p1, point_t p2, int type)
-{
- /* Colors for polygon debugging.
- ** Green: Total access
- ** Red : Barred access
- ** Blue: Near-point access
- ** Yellow: Contained access
- */
- int poly_colors[][3] = {{0, 255, 0}, {0, 0, 255}, {255, 0, 0}, {255, 255, 0}};
- gfx_color_t col;
- gfxw_list_t *decorations = s->picture_port->decorations;
- gfxw_primitive_t *line;
-
- col.visual.global_index = GFX_COLOR_INDEX_UNMAPPED;
- col.visual.r = poly_colors[type][0];
- col.visual.g = poly_colors[type][1];
- col.visual.b = poly_colors[type][2];
- col.alpha = 0;
- col.priority = -1;
- col.control = 0;
- col.mask = GFX_MASK_VISUAL | GFX_MASK_PRIORITY;
-
- p1.y += 10;
- p2.y += 10;
-
- line = gfxw_new_line(p1, p2, col, GFX_LINE_MODE_CORRECT, GFX_LINE_STYLE_NORMAL);
- decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) line);
-}
-
-static void
-draw_point(state_t *s, point_t p, int start)
-{
- /* Colors for starting and end point
- ** Green: End point
- ** Blue: Starting point
- */
- int point_colors[][3] = {{0, 255, 0}, {0, 0, 255}};
- gfx_color_t col;
- gfxw_list_t *decorations = s->picture_port->decorations;
- gfxw_box_t *box;
-
- col.visual.global_index = GFX_COLOR_INDEX_UNMAPPED;
- col.visual.r = point_colors[start][0];
- col.visual.g = point_colors[start][1];
- col.visual.b = point_colors[start][2];
- col.alpha = 0;
- col.priority = -1;
- col.control = 0;
- col.mask = GFX_MASK_VISUAL | GFX_MASK_PRIORITY;
-
- box = gfxw_new_box(s->gfx_state, gfx_rect(p.x - 1, p.y - 1 + 10, 3, 3), col, col, GFX_BOX_SHADE_FLAT);
- decorations->add((gfxw_container_t *) decorations, (gfxw_widget_t *) box);
-}
-
-static void
-draw_polygon(state_t *s, reg_t polygon)
-{
- reg_t points = GET_SEL32(polygon, points);
- int size = KP_UINT(GET_SEL32(polygon, size));
- int type = KP_UINT(GET_SEL32(polygon, type));
- point_t first, prev;
- unsigned char *list = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE);
- int is_reg_t = polygon_is_reg_t(list, size);
- int i;
-
- prev = first = read_point(list, is_reg_t, 0);
-
- for (i = 1; i < size; i++) {
- point_t point = read_point(list, is_reg_t, i);
- draw_line(s, prev, point, type);
- prev = point;
- }
-
- draw_line(s, prev, first, type % 3);
-}
-
-static void
-draw_input(state_t *s, reg_t poly_list, point_t start, point_t end, int opt)
-{
- list_t *list;
- node_t *node;
-
- draw_point(s, start, 1);
- draw_point(s, end, 0);
-
- if (!poly_list.segment)
- return;
-
- list = LOOKUP_LIST(poly_list);
-
- if (!list) {
- SCIkwarn(SCIkWARNING, "Could not obtain polygon list\n");
- return;
- }
-
- node = LOOKUP_NODE(list->first);
-
- while (node) {
- draw_polygon(s, node->value);
- node = LOOKUP_NODE(node->succ);
- }
-}
-
-static void
-print_polygon(state_t *s, reg_t polygon)
-{
- reg_t points = GET_SEL32(polygon, points);
- int size = KP_UINT(GET_SEL32(polygon, size));
- int type = KP_UINT(GET_SEL32(polygon, type));
- int i;
- unsigned char *point_array = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE);
- int is_reg_t = polygon_is_reg_t(point_array, size);
- point_t point;
-
- sciprintf("%i:", type);
-
- for (i = 0; i < size; i++) {
- point = read_point(point_array, is_reg_t, i);
- sciprintf(" (%i, %i)", point.x, point.y);
- }
-
- point = read_point(point_array, is_reg_t, 0);
- sciprintf(" (%i, %i);\n", point.x, point.y);
-}
-
-static void
-print_input(state_t *s, reg_t poly_list, point_t start, point_t end, int opt)
-{
- list_t *list;
- node_t *node;
-
- sciprintf("Start point: (%i, %i)\n", start.x, start.y);
- sciprintf("End point: (%i, %i)\n", end.x, end.y);
- sciprintf("Optimization level: %i\n", opt);
-
- if (!poly_list.segment)
- return;
-
- list = LOOKUP_LIST(poly_list);
-
- if (!list) {
- SCIkwarn(SCIkWARNING, "Could not obtain polygon list\n");
- return;
- }
-
- sciprintf("Polygons:\n");
- node = LOOKUP_NODE(list->first);
-
- while (node) {
- print_polygon(s, node->value);
- node = LOOKUP_NODE(node->succ);
- }
-}
-
-
- /*** Basic geometry functions ***/
-
-static int
-area(point_t a, point_t b, point_t c)
-/* Computes the area of a triangle
-** Parameters: (point_t) a, b, c: The points of the triangle
-** Returns : (int) The area multiplied by two
-*/
-{
- return (b.x - a.x) * (a.y - c.y) - (c.x - a.x) * (a.y - b.y);
-}
-
-static int
-left(point_t a, point_t b, point_t c)
-/* Determines whether or not a point is to the left of a directed line
-** Parameters: (point_t) a, b: The directed line (a, b)
-** (point_t) c: The query point
-** Returns : (int) 1 if c is to the left of (a, b), 0 otherwise
-*/
-{
- return area(a, b, c) > 0;
-}
-
-static int
-left_on(point_t a, point_t b, point_t c)
-/* Determines whether or not a point is to the left of or collinear with a
-** directed line
-** Parameters: (point_t) a, b: The directed line (a, b)
-** (point_t) c: The query point
-** Returns : (int) 1 if c is to the left of or collinear with (a, b), 0
-** otherwise
-*/
-{
- return area(a, b, c) >= 0;
-}
-
-static int
-collinear(point_t a, point_t b, point_t c)
-/* Determines whether or not three points are collinear
-** Parameters: (point_t) a, b, c: The three points
-** Returns : (int) 1 if a, b, and c are collinear, 0 otherwise
-*/
-{
- return area(a, b, c) == 0;
-}
-
-static int
-between(point_t a, point_t b, point_t c)
-/* Determines whether or not a point lies on a line segment
-** Parameters: (point_t) a, b: The line segment (a, b)
-** (point_t) c: The query point
-** Returns : (int) 1 if c lies on (a, b), 0 otherwise
-*/
-{
- if (!collinear(a, b, c))
- return 0;
-
- /* Assumes a != b. */
- if (a.x != b.x)
- return ((a.x <= c.x) && (c.x <= b.x)) || ((a.x >= c.x) && (c.x >= b.x));
- else
- return ((a.y <= c.y) && (c.y <= b.y)) || ((a.y >= c.y) && (c.y >= b.y));
-}
-
-static int
-intersect_proper(point_t a, point_t b, point_t c, point_t d)
-/* Determines whether or not two line segments properly intersect
-** Parameters: (point_t) a, b: The line segment (a, b)
-** (point_t) c, d: The line segment (c, d)
-** Returns : (int) 1 if (a, b) properly intersects (c, d), 0 otherwise
-*/
-{
- int ab = (left(a, b, c) && left(b, a, d))
- || (left(a, b, d) && left(b, a, c));
- int cd = (left(c, d, a) && left(d, c, b))
- || (left(c, d, b) && left(d, c, a));
-
- return ab && cd;
-}
-
-static int
-intersect(point_t a, point_t b, point_t c, point_t d)
-/* Determines whether or not two line segments intersect
-** Parameters: (point_t) a, b: The line segment (a, b)
-** (point_t) c, d: The line segment (c, d)
-** Returns : (int) 1 if (a, b) intersects (c, d), 0 otherwise
-*/
-{
- if (intersect_proper(a, b, c, d))
- return 1;
-
- return between(a, b, c) || between(a, b, d)
- || between (c, d, a) || between(c, d, b);
-}
-
-
- /*** Pathfinding ***/
-
-static vertex_t *
-vertex_new(point_t p)
-/* Allocates and initialises a new vertex
-** Parameters: (point_t) p: The position of the vertex
-** Returns : (vertex_t *) A newly allocated vertex
-*/
-{
- vertex_t *vertex = (vertex_t*)sci_malloc(sizeof(vertex_t));
-
- vertex->v = p;
- vertex->dist = HUGE_DISTANCE;
- vertex->path_prev = NULL;
-
- return vertex;
-}
-
-static polygon_t *
-polygon_new(int type)
-/* Allocates and initialises a new polygon
-** Parameters: (int) type: The SCI polygon type
-** Returns : (polygon_t *) A newly allocated polygon
-*/
-{
- polygon_t *polygon = (polygon_t*)sci_malloc(sizeof(polygon_t));
-
- CLIST_INIT(&polygon->vertices);
- polygon->type = type;
-
- return polygon;
-}
-
-static int
-contained(point_t p, polygon_t *polygon)
-/* Polygon containment test
-** Parameters: (point_t) p: The point
-** (polygon_t *) polygon: The polygon
-** Returns : (int) CONT_INSIDE if p is strictly contained in polygon,
-** CONT_ON_EDGE if p lies on an edge of polygon,
-** CONT_OUTSIDE otherwise
-*/
-{
- /* Number of ray crossing left and right */
- int lcross = 0, rcross = 0;
- vertex_t *vertex;
-
- /* Iterate over edges */
- CLIST_FOREACH(vertex, &polygon->vertices, entries) {
- point_t v1 = vertex->v;
- point_t v2 = CLIST_NEXT(vertex, entries)->v;
-
- /* Flags for ray straddling left and right */
- int rstrad, lstrad;
-
- /* Check if p is a vertex */
- if (POINT_EQUAL(p, v1))
- return CONT_ON_EDGE;
-
- /* Check if edge straddles the ray */
- rstrad = (v1.y < p.y) != (v2.y < p.y);
- lstrad = (v1.y > p.y) != (v2.y > p.y);
-
- if (lstrad || rstrad) {
- /* Compute intersection point x / xq */
- int x = v2.x * v1.y - v1.x * v2.y + (v1.x - v2.x) * p.y;
- int xq = v1.y - v2.y;
-
- /* Multiply by -1 if xq is negative (for comparison
- ** that follows)
- */
- if (xq < 0) {
- x = -x;
- xq = -xq;
- }
-
- /* Avoid floats by multiplying instead of dividing */
- if (rstrad && (x > xq * p.x))
- rcross++;
- else if (lstrad && (x < xq * p.x))
- lcross++;
- }
- }
-
- /* If we counted an odd number of total crossings the point is on an
- ** edge
- */
- if ((lcross + rcross) % 2 == 1)
- return CONT_ON_EDGE;
-
- /* If there are an odd number of crossings to one side the point is
- ** contained in the polygon
- */
- if (rcross % 2 == 1) {
- /* Invert result for contained access polygons. */
- if (polygon->type == POLY_CONTAINED_ACCESS)
- return CONT_OUTSIDE;
- return CONT_INSIDE;
- }
-
- /* Point is outside polygon. Invert result for contained access
- ** polygons
- */
- if (polygon->type == POLY_CONTAINED_ACCESS)
- return CONT_INSIDE;
-
- return CONT_OUTSIDE;
-}
-
-static int
-polygon_area(polygon_t *polygon)
-/* Computes polygon area
-** Parameters: (polygon_t *) polygon: The polygon
-** Returns : (int) The area multiplied by two
-*/
-{
- vertex_t *first = CLIST_FIRST(&polygon->vertices);
- vertex_t *v;
- int size = 0;
-
- v = CLIST_NEXT(first, entries);
-
- while (CLIST_NEXT(v, entries) != first) {
- size += area(first->v, v->v, CLIST_NEXT(v, entries)->v);
- v = CLIST_NEXT(v, entries);
- }
-
- return size;
-}
-
-static void
-fix_vertex_order(polygon_t *polygon)
-/* Fixes the vertex order of a polygon if incorrect. Contained access
-** polygons should have their vertices ordered clockwise, all other types
-** anti-clockwise
-** Parameters: (polygon_t *) polygon: The polygon
-** Returns : (void)
-*/
-{
- int area = polygon_area(polygon);
-
- /* When the polygon area is positive the vertices are ordered
- ** anti-clockwise. When the area is negative the vertices are ordered
- ** clockwise
- */
- if (((area > 0) && (polygon->type == POLY_CONTAINED_ACCESS))
- || ((area < 0) && (polygon->type != POLY_CONTAINED_ACCESS))) {
- vertices_head_t vertices;
-
- /* Create a new circular list */
- CLIST_INIT(&vertices);
-
- while (!CLIST_EMPTY(&polygon->vertices)) {
- /* Put first vertex in new list */
- vertex_t *vertex = CLIST_FIRST(&polygon->vertices);
- CLIST_REMOVE(&polygon->vertices, vertex, entries);
- CLIST_INSERT_HEAD(&vertices, vertex, entries);
- }
-
- polygon->vertices = vertices;
- }
-}
-
-static int
-vertex_compare(const void *a, const void *b)
-/* Compares two vertices by angle (first) and distance (second) in relation
-** to vertex_cur. The angle is relative to the horizontal line extending
-** right from vertex_cur, and increases clockwise
-** Parameters: (const void *) a, b: The vertices
-** Returns : (int) -1 if a is smaller than b, 1 if a is larger than b, and
-** 0 if a and b are equal
-*/
-{
- point_t p0 = vertex_cur->v;
- point_t p1 = (*(vertex_t **) a)->v;
- point_t p2 = (*(vertex_t **) b)->v;
-
- if (POINT_EQUAL(p1, p2))
- return 0;
-
- /* Points above p0 have larger angle than points below p0 */
- if ((p1.y < p0.y) && (p2.y >= p0.y))
- return 1;
-
- if ((p2.y < p0.y) && (p1.y >= p0.y))
- return -1;
-
- /* Handle case where all points have the same y coordinate */
- if ((p0.y == p1.y) && (p0.y == p2.y)) {
- /* Points left of p0 have larger angle than points right of
- ** p0
- */
- if ((p1.x < p0.x) && (p2.x >= p0.x))
- return 1;
- if ((p1.x >= p0.x) && (p2.x < p0.x))
- return -1;
- }
-
- if (collinear(p0, p1, p2)) {
- /* At this point collinear points must have the same angle,
- ** so compare distance to p0
- */
- if (abs(p1.x - p0.x) < abs(p2.x - p0.x))
- return -1;
- if (abs(p1.y - p0.y) < abs(p2.y - p0.y))
- return -1;
-
- return 1;
- }
-
- /* If p2 is left of the directed line (p0, p1) then p1 has greater
- ** angle
- */
- if (left(p0, p1, p2))
- return 1;
-
- return -1;
-}
-
-static void
-clockwise(vertex_t *v, point_t *p1, point_t *p2)
-/* Orders the points of an edge clockwise around vertex_cur. If all three
-** points are collinear the original order is used
-** Parameters: (vertex_t *) v: The first vertex of the edge
-** Returns : (void)
-** (point_t) *p1: The first point in clockwise order
-** (point_t) *p2: The second point in clockwise order
-*/
-{
- vertex_t *w = CLIST_NEXT(v, entries);
-
- if (left_on(vertex_cur->v, w->v, v->v)) {
- *p1 = v->v;
- *p2 = w->v;
- return;
- }
-
- *p1 = w->v;
- *p2 = v->v;
- return;
-}
-
-static int
-edge_compare(const void *a, const void *b)
-/* Compares two edges that are intersected by the sweeping line by distance
-** from vertex_cur
-** Parameters: (const void *) a, b: The first vertices of the edges
-** Returns : (int) -1 if a is closer than b, 1 if b is closer than a, and
-** 0 if a and b are equal
-*/
-{
- point_t v1, v2, w1, w2;
-
- /* We can assume that the sweeping line intersects both edges and
- ** that the edges do not properly intersect
- */
-
- if (a == b)
- return 0;
-
- /* Order vertices clockwise so we know vertex_cur is to the right of
- ** directed edges (v1, v2) and (w1, w2)
- */
- clockwise((vertex_t *) a, &v1, &v2);
- clockwise((vertex_t *) b, &w1, &w2);
-
- /* As the edges do not properly intersect one edge must lie entirely
- ** to one side of another. Note that the special case where edges are
- ** collinear does not need to be handled as those edges will never be
- ** in the tree simultaneously
- */
-
- /* b is left of a */
- if (left_on(v1, v2, w1) && left_on(v1, v2, w2))
- return -1;
-
- /* b is right of a */
- if (left_on(v2, v1, w1) && left_on(v2, v1, w2))
- return 1;
-
- /* a is left of b */
- if (left_on(w1, w2, v1) && left_on(w1, w2, v2))
- return 1;
-
- /* a is right of b */
- return -1;
-}
-
-static int
-inside(point_t p, vertex_t *vertex)
-/* Determines whether or not a line from a point to a vertex intersects the
-** interior of the polygon, locally at that vertex
-** Parameters: (point_t) p: The point
-** (vertex_t *) vertex: The vertex
-** Returns : (int) 1 if the line (p, vertex->v) intersects the interior of
-** the polygon, locally at the vertex. 0 otherwise
-*/
-{
- /* Check that it's not a single-vertex polygon */
- if (VERTEX_HAS_EDGES(vertex)) {
- point_t prev = CLIST_PREV(vertex, entries)->v;
- point_t next = CLIST_NEXT(vertex, entries)->v;
- point_t cur = vertex->v;
-
- if (left(prev, cur, next)) {
- /* Convex vertex, line (p, cur) intersects the inside
- ** if p is located left of both edges
- */
- if (left(cur, next, p) && left(prev, cur, p))
- return 1;
- } else {
- /* Non-convex vertex, line (p, cur) intersects the
- ** inside if p is located left of either edge
- */
- if (left(cur, next, p) || left(prev, cur, p))
- return 1;
- }
- }
-
- return 0;
-}
-
-static int
-visible(vertex_t *vertex, vertex_t *vertex_prev, int visible, aatree_t *tree)
-/* Determines whether or not a vertex is visible from vertex_cur
-** Parameters: (vertex_t *) vertex: The vertex
-** (vertex_t *) vertex_prev: The previous vertex in the sort
-** order, or NULL
-** (int) visible: 1 if vertex_prev is visible, 0 otherwise
-** (aatree_t *) tree: The tree of edges intersected by the
-** sweeping line
-** Returns : (int) 1 if vertex is visible from vertex_cur, 0 otherwise
-*/
-{
- vertex_t *edge;
- point_t p = vertex_cur->v;
- point_t w = vertex->v;
- aatree_t *tree_n = tree;
-
- /* Check if sweeping line intersects the interior of the polygon
- ** locally at vertex
- */
- if (inside(p, vertex))
- return 0;
-
- /* If vertex_prev is on the sweeping line, then vertex is invisible
- ** if vertex_prev is invisible
- */
- if (vertex_prev && !visible && between(p, w, vertex_prev->v))
- return 0;
-
- /* Find leftmost node of tree */
- while ((tree_n = aatree_walk(tree_n, AATREE_WALK_LEFT)))
- tree = tree_n;
- edge = (vertex_t*)aatree_get_data(tree);
-
- if (edge) {
- point_t p1, p2;
-
- /* Check for intersection with sweeping line before vertex */
- clockwise(edge, &p1, &p2);
- if (left(p2, p1, p) && left(p1, p2, w))
- return 0;
- }
-
- return 1;
-}
-
-static void
-visible_vertices(pf_state_t *s, vertex_t *vert)
-/* Determines all vertices that are visible from a particular vertex and
-** updates the visibility matrix
-** Parameters: (pf_state_t *) s: The pathfinding state
-** (vertex_t *) vert: The vertex
-** Returns : (void)
-*/
-{
- aatree_t *tree = aatree_new();
- point_t p = vert->v;
- polygon_t *polygon;
- int i;
- int is_visible;
- vertex_t **vert_sorted = (vertex_t**)sci_malloc(sizeof(vertex_t *) * s->vertices);
-
- /* Sort vertices by angle (first) and distance (second) */
- memcpy(vert_sorted, s->vertex_index, sizeof(vertex_t *) * s->vertices);
- vertex_cur = vert;
- qsort(vert_sorted, s->vertices, sizeof(vertex_t *), vertex_compare);
-
- LIST_FOREACH(polygon, &s->polygons, entries) {
- vertex_t *vertex;
-
- vertex = CLIST_FIRST(&polygon->vertices);
-
- /* Check that there is more than one vertex. */
- if (VERTEX_HAS_EDGES(vertex))
- CLIST_FOREACH(vertex, &polygon->vertices, entries) {
- point_t high, low;
-
- /* Add edges that intersect the initial position of the sweeping line */
- clockwise(vertex, &high, &low);
-
- if ((high.y < p.y) && (low.y >= p.y) && !POINT_EQUAL(low, p))
- aatree_insert(vertex, &tree, edge_compare);
- }
- }
-
- is_visible = 1;
-
- /* The first vertex will be vertex_cur, so we skip it */
- for (i = 1; i < s->vertices; i++) {
- vertex_t *v1;
-
- /* Compute visibility of vertex_index[i] */
- is_visible = visible(vert_sorted[i], vert_sorted[i - 1], is_visible, tree);
-
- /* Update visibility matrix */
- if (is_visible)
- SET_VISIBLE(s, vert->idx, vert_sorted[i]->idx);
-
- /* Delete anti-clockwise edges from tree */
- v1 = CLIST_PREV(vert_sorted[i], entries);
- if (left(p, vert_sorted[i]->v, v1->v)) {
- if (aatree_delete(v1, &tree, edge_compare))
- sciprintf("[avoidpath] Error: failed to remove edge from tree\n");
- }
-
- v1 = CLIST_NEXT(vert_sorted[i], entries);
- if (left(p, vert_sorted[i]->v, v1->v)) {
- if (aatree_delete(vert_sorted[i], &tree, edge_compare))
- sciprintf("[avoidpath] Error: failed to remove edge from tree\n");
- }
-
- /* Add clockwise edges of collinear vertices when sweeping line moves */
- if ((i < s->vertices - 1) && !collinear(p, vert_sorted[i]->v, vert_sorted[i + 1]->v)) {
- int j;
- for (j = i; (j >= 1) && collinear(p, vert_sorted[i]->v, vert_sorted[j]->v); j--) {
- v1 = CLIST_PREV(vert_sorted[j], entries);
- if (left(vert_sorted[j]->v, p, v1->v))
- aatree_insert(v1, &tree, edge_compare);
-
- v1 = CLIST_NEXT(vert_sorted[j], entries);
- if (left(vert_sorted[j]->v, p, v1->v))
- aatree_insert(vert_sorted[j], &tree, edge_compare);
- }
- }
- }
-
- sci_free(vert_sorted);
-
- /* Free tree */
- aatree_free(tree);
-}
-
-static float
-distance(pointf_t a, pointf_t b)
-/* Computes the distance between two pointfs
-** Parameters: (point_t) a, b: The two pointfs
-** Returns : (int) The distance between a and b, rounded to int
-*/
-{
- float w = a.x - b.x;
- float h = a.y - b.y;
-
- return sqrt(w * w + h * h);
-}
-
-static int
-point_on_screen_border(point_t p)
-/* Determines if a point lies on the screen border
-** Parameters: (point_t) p: The point
-** Returns : (int) 1 if p lies on the screen border, 0 otherwise
-*/
-{
- /* FIXME get dimensions from somewhere? */
- return (p.x == 0) || (p.x == 319) || (p.y == 0) || (p.y == 189);
-}
-
-static int
-edge_on_screen_border(point_t p, point_t q)
-/* Determines if an edge lies on the screen border
-** Parameters: (point_t) p, q: The edge (p, q)
-** Returns : (int) 1 if (p, q) lies on the screen border, 0 otherwise
-*/
-{
- /* FIXME get dimensions from somewhere? */
- return ((p.x == 0 && q.x == 0)
- || (p.x == 319 && q.x == 319)
- || (p.y == 0 && q.y == 0)
- || (p.y == 189 && q.y == 189));
-}
-
-static int
-find_free_point(pointf_t f, polygon_t *polygon, point_t *ret)
-/* Searches for a nearby point that is not contained in a polygon
-** Parameters: (pointf_t) f: The pointf to search nearby
-** (polygon_t *) polygon: The polygon
-** Returns : (int) PF_OK on success, PF_FATAL otherwise
-** (point_t) *ret: The non-contained point on success
-*/
-{
- point_t p;
-
- /* Try nearest point first */
- p = gfx_point((int) floor(f.x + 0.5),
- (int) floor(f.y + 0.5));
-
- if (contained(p, polygon) != CONT_INSIDE) {
- *ret = p;
- return PF_OK;
- }
-
- p = gfx_point((int) floor(f.x),
- (int) floor(f.y));
-
- /* Try (x, y), (x + 1, y), (x , y + 1) and (x + 1, y + 1) */
- if (contained(p, polygon) == CONT_INSIDE) {
- p.x++;
- if (contained(p, polygon) == CONT_INSIDE) {
- p.y++;
- if (contained(p, polygon) == CONT_INSIDE) {
- p.x--;
- if (contained(p, polygon) == CONT_INSIDE)
- return PF_FATAL;
- }
- }
- }
-
- *ret = p;
- return PF_OK;
-}
-
-static int
-near_point(point_t p, polygon_t *polygon, point_t *ret)
-/* Computes the near point of a point contained in a polygon
-** Parameters: (point_t) p: The point
-** (polygon_t *) polygon: The polygon
-** Returns : (int) PF_OK on success, PF_FATAL otherwise
-** (point_t) *ret: The near point of p in polygon on success
-*/
-{
- vertex_t *vertex;
- pointf_t near_p;
- float dist = HUGE_DISTANCE;
-
- CLIST_FOREACH(vertex, &polygon->vertices, entries) {
- point_t p1 = vertex->v;
- point_t p2 = CLIST_NEXT(vertex, entries)->v;
- float w, h, l, u;
- pointf_t new_point;
- float new_dist;
-
- /* Ignore edges on the screen border */
- if (edge_on_screen_border(p1, p2))
- continue;
-
- /* Compute near point */
- w = p2.x - p1.x;
- h = p2.y - p1.y;
- l = sqrt(w * w + h * h);
- u = ((p.x - p1.x) * (p2.x - p1.x) + (p.y - p1.y) * (p2.y - p1.y)) / (l * l);
-
- /* Clip to edge */
- if (u < 0.0f)
- u = 0.0f;
- if (u > 1.0f)
- u = 1.0f;
-
- new_point.x = p1.x + u * (p2.x - p1.x);
- new_point.y = p1.y + u * (p2.y - p1.y);
-
- new_dist = distance(to_pointf(p), new_point);
-
- if (new_dist < dist) {
- near_p = new_point;
- dist = new_dist;
- }
- }
-
- /* Find point not contained in polygon */
- return find_free_point(near_p, polygon, ret);
-}
-
-static int
-intersection(point_t a, point_t b, vertex_t *vertex, pointf_t *ret)
-/* Computes the intersection point of a line segment and an edge (not
-** including the vertices themselves)
-** Parameters: (point_t) a, b: The line segment (a, b)
-** (vertex_t *) vertex: The first vertex of the edge
-** Returns : (int) FP_OK on success, PF_ERROR otherwise
-** (pointf_t) *ret: The intersection point
-*/
-{
- /* Parameters of parametric equations */
- float s, t;
- /* Numerator and denominator of equations */
- float num, denom;
- point_t c = vertex->v;
- point_t d = CLIST_NEXT(vertex, entries)->v;
-
- denom = a.x * (float) (d.y - c.y) +
- b.x * (float) (c.y - d.y) +
- d.x * (float) (b.y - a.y) +
- c.x * (float) (a.y - b.y);
-
- if (denom == 0.0)
- /* Segments are parallel, no intersection */
- return PF_ERROR;
-
- num = a.x * (float) (d.y - c.y) +
- c.x * (float) (a.y - d.y) +
- d.x * (float) (c.y - a.y);
-
- s = num / denom;
-
- num = -(a.x * (float) (c.y - b.y) +
- b.x * (float) (a.y - c.y) +
- c.x * (float) (b.y - a.y));
-
- t = num / denom;
-
- if ((0.0 <= s) && (s <= 1.0) && (0.0 < t) && (t < 1.0)) {
- /* Intersection found */
- ret->x = a.x + s * (b.x - a.x);
- ret->y = a.y + s * (b.y - a.y);
- return PF_OK;
- }
-
- return PF_ERROR;
-}
-
-static int
-nearest_intersection(pf_state_t *s, point_t p, point_t q, point_t *ret)
-/* Computes the nearest intersection point of a line segment and the polygon
-** set. Intersection points that are reached from the inside of a polygon
-** are ignored as are improper intersections which do not obstruct
-** visibility
-** Parameters: (pf_state_t *) s: The pathfinding state
-** (point_t) p, q: The line segment (p, q)
-** Returns : (int) PF_OK on success, PF_ERROR when no intersections were
-** found, PF_FATAL otherwise
-** (point_t) *ret: On success, the closest intersection point
-*/
-{
- polygon_t *polygon;
- pointf_t isec;
- polygon_t *ipolygon;
- float dist = HUGE_DISTANCE;
-
- LIST_FOREACH(polygon, &s->polygons, entries) {
- vertex_t *vertex;
-
- CLIST_FOREACH(vertex, &polygon->vertices, entries) {
- float new_dist;
- pointf_t new_isec;
-
- /* Check for intersection with vertex */
- if (between(p, q, vertex->v)) {
- /* Skip this vertex if we hit it from the
- ** inside of the polygon
- */
- if (inside(q, vertex)) {
- new_isec.x = vertex->v.x;
- new_isec.y = vertex->v.y;
- } else
- continue;
- } else {
- /* Check for intersection with edges */
-
- /* Skip this edge if we hit it from the
- ** inside of the polygon
- */
- if (!left(vertex->v, CLIST_NEXT(vertex, entries)->v, q))
- continue;
-
- if (intersection(p, q, vertex, &new_isec) != PF_OK)
- continue;
- }
-
- new_dist = distance(to_pointf(p), new_isec);
- if (new_dist < dist) {
- ipolygon = polygon;
- isec = new_isec;
- dist = new_dist;
- }
- }
- }
-
- if (dist == HUGE_DISTANCE)
- return PF_ERROR;
-
- /* Find point not contained in polygon */
- return find_free_point(isec, ipolygon, ret);
-}
-
-static int
-fix_point(pf_state_t *s, point_t p, point_t *ret, polygon_t **ret_pol)
-/* Checks a point for containment in any of the polygons in the polygon set.
-** If the point is contained in a totally accessible polygon that polygon
-** is removed from the set. If the point is contained in a polygon of another
-** type the near point is returned. Otherwise the original point is returned
-** Parameters: (point_t) p: The point
-** Returns : (int) PF_OK on success, PF_FATAL otherwise
-** (point_t) *ret: A valid input point for pathfinding
-** (polygon_t *) *ret_pol: The polygon p was contained in if p
-** != *ret, NULL otherwise
-*/
-{
- polygon_t *polygon;
- *ret_pol = NULL;
-
- /* Check for polygon containment */
- LIST_FOREACH(polygon, &s->polygons, entries) {
- if (contained(p, polygon) != CONT_OUTSIDE)
- break;
- }
-
- if (polygon) {
- point_t near_p;
-
- if (polygon->type == POLY_TOTAL_ACCESS) {
- /* Remove totally accessible polygon if it contains
- ** p
- */
- LIST_REMOVE(polygon, entries);
- *ret = p;
- return PF_OK;
- }
-
- /* Otherwise, compute near point */
- if (near_point(p, polygon, &near_p) == PF_OK) {
- *ret = near_p;
-
- if (!POINT_EQUAL(p, *ret))
- *ret_pol = polygon;
-
- return PF_OK;
- }
-
- return PF_FATAL;
- }
-
- /* p is not contained in any polygon */
- *ret = p;
- return PF_OK;
-}
-
-static vertex_t *
-merge_point(pf_state_t *s, point_t v)
-/* Merges a point into the polygon set. A new vertex is allocated for this
-** point, unless a matching vertex already exists. If the point is on an
-** already existing edge that edge is split up into two edges connected by
-** the new vertex
-** Parameters: (pf_state_t *) s: The pathfinding state
-** (point_t) v: The point to merge
-** Returns : (vertex_t *) The vertex corresponding to v
-*/
-{
- vertex_t *vertex;
- vertex_t *v_new;
- polygon_t *polygon;
-
- /* Check for already existing vertex */
- LIST_FOREACH(polygon, &s->polygons, entries) {
- CLIST_FOREACH(vertex, &polygon->vertices, entries)
- if (POINT_EQUAL(vertex->v, v))
- return vertex;
- }
-
- v_new = vertex_new(v);
-
- /* Check for point being on an edge */
- LIST_FOREACH(polygon, &s->polygons, entries)
- /* Skip single-vertex polygons */
- if (VERTEX_HAS_EDGES(CLIST_FIRST(&polygon->vertices)))
- CLIST_FOREACH(vertex, &polygon->vertices, entries) {
- vertex_t *next = CLIST_NEXT(vertex, entries);
-
- if (between(vertex->v, next->v, v)) {
- /* Split edge by adding vertex */
- CLIST_INSERT_AFTER(vertex, v_new, entries);
- return v_new;
- }
- }
-
- /* Add point as single-vertex polygon */
- polygon = polygon_new(POLY_BARRED_ACCESS);
- CLIST_INSERT_HEAD(&polygon->vertices, v_new, entries);
- LIST_INSERT_HEAD(&s->polygons, polygon, entries);
-
- return v_new;
-}
-
-static polygon_t *
-convert_polygon(state_t *s, reg_t polygon)
-/* Converts an SCI polygon into a polygon_t
-** Parameters: (state_t *) s: The game state
-** (reg_t) polygon: The SCI polygon to convert
-** Returns : (polygon_t *) The converted polygon
-*/
-{
- int i;
- reg_t points = GET_SEL32(polygon, points);
- int size = KP_UINT(GET_SEL32(polygon, size));
- unsigned char *list = kernel_dereference_bulk_pointer(s, points, size * POLY_POINT_SIZE);
- polygon_t *poly = polygon_new(KP_UINT(GET_SEL32(polygon, type)));
- int is_reg_t = polygon_is_reg_t(list, size);
-
- for (i = 0; i < size; i++) {
- vertex_t *vertex = vertex_new(read_point(list, is_reg_t, i));
- CLIST_INSERT_HEAD(&poly->vertices, vertex, entries);
- }
-
- fix_vertex_order(poly);
-
- return poly;
-}
-
-static void
-free_polygon(polygon_t *polygon)
-/* Frees a polygon and its vertices
-** Parameters: (polygon_t *) polygons: The polygon
-** Returns : (void)
-*/
-{
- while (!CLIST_EMPTY(&polygon->vertices)) {
- vertex_t *vertex = CLIST_FIRST(&polygon->vertices);
- CLIST_REMOVE(&polygon->vertices, vertex, entries);
- sci_free(vertex);
- }
-
- sci_free(polygon);
-}
-
-static void
-free_pf_state(pf_state_t *p)
-/* Frees a pathfinding state
-** Parameters: (pf_state_t *) p: The pathfinding state
-** Returns : (void)
-*/
-{
- if (p->vertex_index)
- sci_free(p->vertex_index);
-
- if (p->vis_matrix)
- sci_free(p->vis_matrix);
-
- while (!LIST_EMPTY(&p->polygons)) {
- polygon_t *polygon = LIST_FIRST(&p->polygons);
- LIST_REMOVE(polygon, entries);
- free_polygon(polygon);
- }
-
- sci_free(p);
-}
-
-static void
-change_polygons_opt_0(pf_state_t *s)
-/* Changes the polygon list for optimization level 0 (used for keyboard
-** support). Totally accessible polygons are removed and near-point
-** accessible polygons are changed into totally accessible polygons.
-** Parameters: (pf_state_t *) s: The pathfinding state
-** Returns : (void)
-*/
-{
- polygon_t *polygon = LIST_FIRST(&s->polygons);
-
- while (polygon) {
- polygon_t *next = LIST_NEXT(polygon, entries);
-
- if (polygon->type == POLY_NEAREST_ACCESS)
- polygon->type = POLY_TOTAL_ACCESS;
- else if (polygon->type == POLY_TOTAL_ACCESS) {
- LIST_REMOVE(polygon, entries);
- free_polygon(polygon);
- }
-
- polygon = next;
- }
-}
-
-static pf_state_t *
-convert_polygon_set(state_t *s, reg_t poly_list, point_t start, point_t end, int opt)
-/* Converts the SCI input data for pathfinding
-** Parameters: (state_t *) s: The game state
-** (reg_t) poly_list: Polygon list
-** (point_t) start: The start point
-** (point_t) end: The end point
-** (int) opt: Optimization level (0, 1 or 2)
-** Returns : (pf_state_t *) On success a newly allocated pathfinding state,
-** NULL otherwise
-*/
-{
- polygon_t *polygon;
- int err;
- int count = 0;
- pf_state_t *pf_s = (pf_state_t*)sci_malloc(sizeof(pf_state_t));
-
- LIST_INIT(&pf_s->polygons);
- pf_s->start = start;
- pf_s->end = end;
- pf_s->keep_start = 0;
- pf_s->keep_end = 0;
- pf_s->vertex_index = NULL;
-
- /* Convert all polygons */
- if (poly_list.segment) {
- list_t *list = LOOKUP_LIST(poly_list);
- node_t *node = LOOKUP_NODE(list->first);
-
- while (node) {
- polygon = convert_polygon(s, node->value);
- LIST_INSERT_HEAD(&pf_s->polygons, polygon, entries);
- count += KP_UINT(GET_SEL32(node->value, size));
- node = LOOKUP_NODE(node->succ);
- }
- }
-
- if (opt == 0) {
- /* Keyboard support */
- change_polygons_opt_0(pf_s);
-
- /* Find nearest intersection */
- err = nearest_intersection(pf_s, start, end, &start);
-
- if (err == PF_FATAL) {
- sciprintf("[avoidpath] Error: fatal error finding nearest intersecton\n");
- free_pf_state(pf_s);
- return NULL;
- }
- else if (err == PF_OK)
- /* Keep original start position if intersection
- ** was found
- */
- pf_s->keep_start = 1;
- } else {
- if (fix_point(pf_s, start, &start, &polygon) != PF_OK) {
- sciprintf("[avoidpath] Error: couldn't fix start position for pathfinding\n");
- free_pf_state(pf_s);
- return NULL;
- }
- else if (polygon) {
- /* Start position has moved */
- pf_s->keep_start = 1;
- if ((polygon->type != POLY_NEAREST_ACCESS))
- sciprintf("[avoidpath] Warning: start position at unreachable location\n");
- }
- }
-
- if (fix_point(pf_s, end, &end, &polygon) != PF_OK) {
- sciprintf("[avoidpath] Error: couldn't fix end position for pathfinding\n");
- free_pf_state(pf_s);
- return NULL;
- }
- else {
- /* Keep original end position if it is contained in a
- ** near-point accessible polygon
- */
- if (polygon && (polygon->type == POLY_NEAREST_ACCESS))
- pf_s->keep_end = 1;
- }
-
- /* Merge start and end points into polygon set */
- pf_s->vertex_start = merge_point(pf_s, start);
- pf_s->vertex_end = merge_point(pf_s, end);
-
- /* Allocate and build vertex index */
- pf_s->vertex_index = (vertex_t**)sci_malloc(sizeof(vertex_t *) * (count + 2));
-
- count = 0;
-
- LIST_FOREACH(polygon, &pf_s->polygons, entries) {
- vertex_t *vertex;
-
- CLIST_FOREACH(vertex, &polygon->vertices, entries) {
- vertex->idx = count;
- pf_s->vertex_index[count++] = vertex;
- }
- }
-
- pf_s->vertices = count;
-
- /* Allocate and clear visibility matrix */
- pf_s->vis_matrix = (char*)sci_calloc(pf_s->vertices * VIS_MATRIX_ROW_SIZE(pf_s->vertices), 1);
-
- return pf_s;
-}
-
-static void
-visibility_graph(pf_state_t *s)
-/* Computes the visibility graph
-** Parameters: (pf_state_t *) s: The pathfinding state
-** Returns : (void)
-*/
-{
- polygon_t *polygon;
-
- LIST_FOREACH(polygon, &s->polygons, entries) {
- vertex_t *vertex;
-
- CLIST_FOREACH(vertex, &polygon->vertices, entries)
- visible_vertices(s, vertex);
- }
-}
-
-static int
-intersecting_polygons(pf_state_t *s)
-/* Detects (self-)intersecting polygons
-** Parameters: (pf_state_t *) s: The pathfinding state
-** Returns : (int) 1 if s contains (self-)intersecting polygons, 0 otherwise
-*/
-{
- int i, j;
-
- for (i = 0; i < s->vertices; i++) {
- vertex_t *v1 = s->vertex_index[i];
- if (!VERTEX_HAS_EDGES(v1))
- continue;
- for (j = i + 1; j < s->vertices; j++) {
- vertex_t *v2 = s->vertex_index[j];
- if (!VERTEX_HAS_EDGES(v2))
- continue;
-
- /* Skip neighbouring edges */
- if ((CLIST_NEXT(v1, entries) == v2)
- || CLIST_PREV(v1, entries) == v2)
- continue;
-
- if (intersect(v1->v, CLIST_NEXT(v1, entries)->v,
- v2->v, CLIST_NEXT(v2, entries)->v))
- return 1;
- }
- }
-
- return 0;
-}
-
-static void
-dijkstra(pf_state_t *s)
-/* Computes a shortest path from vertex_start to vertex_end. The caller can
-** construct the resulting path by following the path_prev links from
-** vertex_end back to vertex_start. If no path exists vertex_end->path_prev
-** will be NULL
-** Parameters: (pf_state_t *) s: The pathfinding state
-** Returns : (void)
-*/
-{
- polygon_t *polygon;
- /* Vertices of which the shortest path is known */
- LIST_HEAD(done_head, vertex) done;
- /* The remaining vertices */
- LIST_HEAD(remain_head, vertex) remain;
-
- LIST_INIT(&remain);
- LIST_INIT(&done);
-
- /* Start out with all vertices in set remain */
- LIST_FOREACH(polygon, &s->polygons, entries) {
- vertex_t *vertex;
-
- CLIST_FOREACH(vertex, &polygon->vertices, entries)
- LIST_INSERT_HEAD(&remain, vertex, dijkstra);
- }
-
- s->vertex_start->dist = 0.0f;
-
- /* Loop until we find vertex_end */
- while (1) {
- int i;
- vertex_t *vertex, *vertex_min = 0;
- float min = HUGE_DISTANCE;
-
- /* Find vertex at shortest distance from set done */
- LIST_FOREACH(vertex, &remain, dijkstra) {
- if (vertex->dist < min) {
- vertex_min = vertex;
- min = vertex->dist;
- }
- }
-
- if (min == HUGE_DISTANCE) {
- sciprintf("[avoidpath] Warning: end point (%i, %i) is unreachable\n", s->vertex_end->v.x, s->vertex_end->v.y);
- return;
- }
-
- /* If vertex_end is at shortest distance we can stop */
- if (vertex_min == s->vertex_end)
- return;
-
- /* Move vertex from set remain to set done */
- LIST_REMOVE(vertex_min, dijkstra);
- LIST_INSERT_HEAD(&done, vertex_min, dijkstra);
-
- for (i = 0; i < s->vertices; i++) {
- /* Adjust upper bound for all vertices that are visible from vertex_min */
- if (IS_VISIBLE(s, vertex_min->idx, i)) {
- float new_dist;
-
- /* Avoid plotting path along screen edge */
- if ((s->vertex_index[i] != s->vertex_end) && point_on_screen_border(s->vertex_index[i]->v))
- continue;
-
- new_dist = vertex_min->dist + distance(to_pointf(vertex_min->v),
- to_pointf(s->vertex_index[i]->v));
- if (new_dist < s->vertex_index[i]->dist) {
- s->vertex_index[i]->dist = new_dist;
- s->vertex_index[i]->path_prev = vertex_min;
- }
- }
- }
- }
-}
-
-static reg_t
-output_path(pf_state_t *p, state_t *s)
-/* Stores the final path in newly allocated dynmem
-** Parameters: (pf_state_t *) p: The pathfinding state
-** (state_t *) s: The game state
-** Returns : (reg_t) Pointer to dynmem containing path
-*/
-{
- int path_len = 0;
- byte *oref;
- reg_t output;
- vertex_t *vertex = p->vertex_end;
- int i;
- int unreachable = vertex->path_prev == NULL;
-
- if (unreachable) {
- /* If pathfinding failed we only return the path up to vertex_start */
- oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE * 3,
- AVOIDPATH_DYNMEM_STRING, &output);
-
- if (p->keep_start)
- POLY_SET_POINT(oref, 0, p->start.x, p->start.y);
- else
- POLY_SET_POINT(oref, 0, p->vertex_start->v.x, p->vertex_start->v.y);
- POLY_SET_POINT(oref, 1, p->vertex_start->v.x, p->vertex_start->v.y);
- POLY_SET_POINT(oref, 2, POLY_LAST_POINT, POLY_LAST_POINT);
-
- return output;
- }
-
- while (vertex) {
- /* Compute path length */
- path_len++;
- vertex = vertex->path_prev;
- }
-
- oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE * (path_len + 1 + p->keep_start + p->keep_end),
- AVOIDPATH_DYNMEM_STRING, &output);
-
- /* Sentinel */
- POLY_SET_POINT(oref, path_len + p->keep_start + p->keep_end, POLY_LAST_POINT, POLY_LAST_POINT);
-
- /* Add original start and end points if needed */
- if (p->keep_end)
- POLY_SET_POINT(oref, path_len + p->keep_start, p->end.x, p->end.y);
- if (p->keep_start)
- POLY_SET_POINT(oref, 0, p->start.x, p->start.y);
-
- i = path_len + p->keep_start - 1;
-
- if (unreachable) {
- /* Return straight trajectory from start to end */
- POLY_SET_POINT(oref, i - 1, p->vertex_start->v.x, p->vertex_start->v.y);
- POLY_SET_POINT(oref, i, p->vertex_end->v.x, p->vertex_end->v.y);
- return output;
- }
-
- vertex = p->vertex_end;
- while (vertex) {
- POLY_SET_POINT(oref, i, vertex->v.x, vertex->v.y);
- vertex = vertex->path_prev;
- i--;
- }
-
- if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) {
- sciprintf("[avoidpath] Returning path:");
- for (i = 0; i < path_len + p->keep_start + p->keep_end; i++) {
- point_t pt;
- POLY_GET_POINT(oref, i, pt.x, pt.y);
- sciprintf(" (%i, %i)", pt.x, pt.y);
- }
- sciprintf("\n");
- }
-
- return output;
-}
-
-reg_t
-kAvoidPath(state_t *s, int funct_nr, int argc, reg_t *argv)
-{
- point_t start = gfx_point(SKPV(0), SKPV(1));
-
- if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) {
- gfxw_port_t *port= s->picture_port;
-
- if (!port->decorations) {
- port->decorations = gfxw_new_list(gfx_rect(0, 0, 320, 200), 0);
- port->decorations->set_visual(GFXW(port->decorations), port->visual);
- } else {
- port->decorations->free_contents(port->decorations);
- }
- }
-
- switch (argc) {
-
- case 3 :
- {
- reg_t retval;
- polygon_t *polygon = convert_polygon(s, argv[2]);
-
- if (polygon->type == POLY_CONTAINED_ACCESS) {
- sciprintf("[avoidpath] Warning: containment test performed on contained access polygon\n");
-
- /* Semantics unknown, assume barred access semantics */
- polygon->type = POLY_BARRED_ACCESS;
- }
-
- retval = make_reg(0, contained(start, polygon) != CONT_OUTSIDE);
- free_polygon(polygon);
- return retval;
- }
- case 6 :
- case 7 :
- {
- point_t end = gfx_point(SKPV(2), SKPV(3));
- reg_t poly_list = argv[4];
- /* int poly_list_size = UKPV(5); */
- int opt = UKPV_OR_ALT(6, 1);
- reg_t output;
- pf_state_t *p;
-
- if (s->debug_mode & (1 << SCIkAVOIDPATH_NR)) {
- sciprintf("[avoidpath] Pathfinding input:\n");
- draw_point(s, start, 1);
- draw_point(s, end, 0);
-
- if (poly_list.segment) {
- print_input(s, poly_list, start, end, opt);
- draw_input(s, poly_list, start, end, opt);
- }
- }
-
- p = convert_polygon_set(s, poly_list, start, end, opt);
-
- if (intersecting_polygons(p)) {
- sciprintf("[avoidpath] Error: input set contains (self-)intersecting polygons\n");
- free_pf_state(p);
- p = NULL;
- }
-
- if (!p) {
- byte *oref;
- sciprintf("[avoidpath] Error: pathfinding failed for following input:\n");
- print_input(s, poly_list, start, end, opt);
- sciprintf("[avoidpath] Returning direct path from start point to end point\n");
- oref = sm_alloc_dynmem(&s->seg_manager, POLY_POINT_SIZE*3,
- AVOIDPATH_DYNMEM_STRING, &output);
-
- POLY_SET_POINT(oref, 0, start.x, start.y);
- POLY_SET_POINT(oref, 1, end.x, end.y);
- POLY_SET_POINT(oref, 2, POLY_LAST_POINT, POLY_LAST_POINT);
-
- return output;
- }
-
- visibility_graph(p);
- dijkstra(p);
-
- output = output_path(p, s);
- free_pf_state(p);
-
- /* Memory is freed by explicit calls to Memory */
- return output;
- }
-
- default:
- SCIkwarn(SCIkWARNING, "Unknown AvoidPath subfunction %d\n",
- argc);
- return NULL_REG;
- break;
- }
-}