scummvm/engines/sci/engine/kpathing.cpp
Willem Jan Palenstijn ced40b2266 Use new Palette class to manager pixmap palettes.
There are some remaining regressions with text colour in SCI1 games,
but overall it should fix more than it breaks.

svn-id: r39242
2009-03-08 20:17:01 +00:00

1581 lines
45 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* $URL$
* $Id$
*
*/
/* Detailed information on the implementation can be found in the report
** which can be downloaded from FIXME.
*/
#include "sci/engine/state.h"
#include "sci/engine/aatree.h"
#include "sci/engine/kernel.h"
#include "sci/gfx/gfx_widgets.h"
#include "sci/gfx/gfx_state_internal.h" // required for gfxw_port_t, gfxw_container_t
#include "common/list.h"
namespace Sci {
#define POLY_LAST_POINT 0x7777
#define POLY_POINT_SIZE 4
static void POLY_GET_POINT(byte *p, int i, Common::Point &pt) {
pt.x = (int16)READ_LE_UINT16((p) + (i) * POLY_POINT_SIZE);
pt.y = (int16)READ_LE_UINT16((p) + (i) * POLY_POINT_SIZE + 2);
}
static void POLY_SET_POINT(byte *p, int i, const Common::Point &pt) {
WRITE_LE_UINT16((p) + (i) * POLY_POINT_SIZE, pt.x);
WRITE_LE_UINT16((p) + (i) * POLY_POINT_SIZE + 2, pt.y);
}
static void POLY_GET_POINT_REG_T(reg_t *p, int i, Common::Point &pt) {
pt.x = KP_SINT((p)[(i) * 2]);
pt.y = KP_SINT((p)[(i) * 2 + 1]);
}
// SCI-defined polygon types
enum {
POLY_TOTAL_ACCESS = 0,
POLY_NEAREST_ACCESS = 1,
POLY_BARRED_ACCESS = 2,
POLY_CONTAINED_ACCESS = 3
};
// Polygon containment types
enum {
CONT_OUTSIDE = 0,
CONT_ON_EDGE = 1,
CONT_INSIDE = 2
};
#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))
// Error codes
enum {
PF_OK = 0,
PF_ERROR = -1,
PF_FATAL = -2
};
// Floating point struct
struct FloatPoint {
FloatPoint() : x(0), y(0) {}
FloatPoint(float x_, float y_) : x(x_), y(y_) {}
float x, y;
};
FloatPoint toFloatPoint(Common::Point p) {
return FloatPoint(p.x, p.y);
}
struct Vertex {
// Location
Common::Point v;
// Index
int idx;
// Vertex circular list entry
struct {
Vertex *cle_next; // next element
Vertex *cle_prev; // previous element
} entries;
// Distance from starting vertex
float dist;
// Previous vertex in shortest path
Vertex *path_prev;
public:
Vertex(const Common::Point &p) : v(p) {
dist = HUGE_DISTANCE;
path_prev = NULL;
}
};
typedef Common::List<Vertex *> VertexList;
class CircularVertexList {
public:
Vertex *_head;
public:
CircularVertexList() : _head(0) {}
Vertex *first() {
return _head;
}
void insertHead(Vertex *elm) {
if (_head == NULL) {
elm->entries.cle_next = elm->entries.cle_prev = elm;
} else {
elm->entries.cle_next = _head;
elm->entries.cle_prev = _head->entries.cle_prev;
_head->entries.cle_prev = elm;
elm->entries.cle_prev->entries.cle_next = elm;
}
_head = elm;
}
static void insertAfter(Vertex *listelm, Vertex *elm) {
elm->entries.cle_prev = listelm;
(elm)->entries.cle_next = listelm->entries.cle_next;
listelm->entries.cle_next->entries.cle_prev = elm;
listelm->entries.cle_next = elm;
}
void remove(Vertex *elm) {
if (elm->entries.cle_next == elm) {
_head = NULL;
} else {
if (_head == elm)
_head = elm->entries.cle_next;
elm->entries.cle_prev->entries.cle_next = elm->entries.cle_next;
elm->entries.cle_next->entries.cle_prev = elm->entries.cle_prev;
}
}
bool empty() const {
return _head == NULL;
}
};
/* Circular list definitions. */
#define CLIST_FOREACH(var, head) \
for ((var) = (head)->first(); \
(var); \
(var) = ((var)->entries.cle_next == (head)->first() ? \
NULL : (var)->entries.cle_next))
/* Circular list access methods. */
#define CLIST_NEXT(elm) ((elm)->entries.cle_next)
#define CLIST_PREV(elm) ((elm)->entries.cle_prev)
struct Polygon {
// SCI polygon type
int type;
// Circular list of vertices
CircularVertexList vertices;
public:
Polygon(int t) : type(t) {
}
~Polygon() {
while (!vertices.empty()) {
Vertex *vertex = vertices.first();
vertices.remove(vertex);
delete vertex;
}
}
};
typedef Common::List<Polygon *> PolygonList;
// Pathfinding state
struct PathfindingState {
// List of all polygons
PolygonList polygons;
// Original start and end points
Common::Point start, end;
// Flags for adding original points to final path
char keep_start, keep_end;
// Start and end points for pathfinding
Vertex *vertex_start, *vertex_end;
// Array to quickly find vertices by idx
Vertex **vertex_index;
// Visibility matrix
char *vis_matrix;
// Total number of vertices
int vertices;
PathfindingState(const Common::Point &s, const Common::Point &e) : start(s), end(e) {
keep_start = 0;
keep_end = 0;
vertex_start = NULL;
vertex_end = NULL;
vertex_index = NULL;
vis_matrix = NULL;
vertices = 0;
}
~PathfindingState() {
free(vertex_index);
free(vis_matrix);
for (PolygonList::iterator it = polygons.begin(); it != polygons.end(); ++it) {
delete *it;
}
}
};
static Vertex *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 Common::Point read_point(unsigned char *list, int is_reg_t, int offset) {
Common::Point point;
if (!is_reg_t) {
POLY_GET_POINT(list, offset, point);
} else {
POLY_GET_POINT_REG_T((reg_t *)list, offset, point);
}
return point;
}
static void draw_line(EngineState *s, Common::Point p1, Common::Point 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 = PaletteEntry(poly_colors[type][0], poly_colors[type][1], 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(EngineState *s, Common::Point 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 = PaletteEntry(point_colors[start][0], point_colors[start][1], 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(EngineState *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));
Common::Point 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++) {
Common::Point 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(EngineState *s, reg_t poly_list, Common::Point start, Common::Point end, int opt) {
List *list;
Node *node;
draw_point(s, start, 1);
draw_point(s, end, 0);
if (!poly_list.segment)
return;
list = LOOKUP_LIST(poly_list);
if (!list) {
warning("Could not obtain polygon list");
return;
}
node = LOOKUP_NODE(list->first);
while (node) {
draw_polygon(s, node->value);
node = LOOKUP_NODE(node->succ);
}
}
static void print_polygon(EngineState *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);
Common::Point 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(EngineState *s, reg_t poly_list, Common::Point start, Common::Point end, int opt) {
List *list;
Node *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) {
warning("Could not obtain polygon list");
return;
}
sciprintf("Polygons:\n");
node = LOOKUP_NODE(list->first);
while (node) {
print_polygon(s, node->value);
node = LOOKUP_NODE(node->succ);
}
}
static int area(Common::Point a, Common::Point b, Common::Point c) {
// Computes the area of a triangle
// Parameters: (Common::Point) 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(Common::Point a, Common::Point b, Common::Point c) {
// Determines whether or not a point is to the left of a directed line
// Parameters: (Common::Point) a, b: The directed line (a, b)
// (Common::Point) 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(Common::Point a, Common::Point b, Common::Point c) {
// Determines whether or not a point is to the left of or collinear with a
// directed line
// Parameters: (Common::Point) a, b: The directed line (a, b)
// (Common::Point) 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(Common::Point a, Common::Point b, Common::Point c) {
// Determines whether or not three points are collinear
// Parameters: (Common::Point) 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(Common::Point a, Common::Point b, Common::Point c) {
// Determines whether or not a point lies on a line segment
// Parameters: (Common::Point) a, b: The line segment (a, b)
// (Common::Point) 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(Common::Point a, Common::Point b, Common::Point c, Common::Point d) {
// Determines whether or not two line segments properly intersect
// Parameters: (Common::Point) a, b: The line segment (a, b)
// (Common::Point) 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(Common::Point a, Common::Point b, Common::Point c, Common::Point d) {
// Determines whether or not two line segments intersect
// Parameters: (Common::Point) a, b: The line segment (a, b)
// (Common::Point) 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);
}
static int contained(Common::Point p, Polygon *polygon) {
// Polygon containment test
// Parameters: (Common::Point) p: The point
// (Polygon *) 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 *vertex;
// Iterate over edges
CLIST_FOREACH(vertex, &polygon->vertices) {
Common::Point v1 = vertex->v;
Common::Point v2 = CLIST_NEXT(vertex)->v;
// Flags for ray straddling left and right
int rstrad, lstrad;
// Check if p is a vertex
if (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 *polygon) {
// Computes polygon area
// Parameters: (Polygon *) polygon: The polygon
// Returns : (int) The area multiplied by two
Vertex *first = polygon->vertices.first();
Vertex *v;
int size = 0;
v = CLIST_NEXT(first);
while (CLIST_NEXT(v) != first) {
size += area(first->v, v->v, CLIST_NEXT(v)->v);
v = CLIST_NEXT(v);
}
return size;
}
static void fix_vertex_order(Polygon *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 *) polygon: The polygon
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))) {
// Create a new circular list
CircularVertexList vertices;
while (!polygon->vertices.empty()) {
// Put first vertex in new list
Vertex *vertex = polygon->vertices.first();
polygon->vertices.remove(vertex);
vertices.insertHead(vertex);
}
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
Common::Point p0 = vertex_cur->v;
Common::Point p1 = (*(Vertex **) a)->v;
Common::Point p2 = (*(Vertex **) b)->v;
if (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 *v, Common::Point *p1, Common::Point *p2) {
// Orders the points of an edge clockwise around vertex_cur. If all three
// points are collinear the original order is used
// Parameters: (Vertex *) v: The first vertex of the edge
// Returns : (void)
// (Common::Point) *p1: The first point in clockwise order
// (Common::Point) *p2: The second point in clockwise order
Vertex *w = CLIST_NEXT(v);
if (left_on(vertex_cur->v, w->v, v->v)) {
*p1 = v->v;
*p2 = w->v;
} else {
*p1 = w->v;
*p2 = v->v;
}
}
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
Common::Point 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 *)a, &v1, &v2);
clockwise((Vertex *)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(Common::Point p, Vertex *vertex) {
// Determines whether or not a line from a point to a vertex intersects the
// interior of the polygon, locally at that vertex
// Parameters: (Common::Point) p: The point
// (Vertex *) 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)) {
Common::Point prev = CLIST_PREV(vertex)->v;
Common::Point next = CLIST_NEXT(vertex)->v;
Common::Point 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 *vertex, Vertex *vertex_prev, int visible, aatree_t *tree) {
// Determines whether or not a vertex is visible from vertex_cur
// Parameters: (Vertex *) vertex: The vertex
// (Vertex *) 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 *edge;
Common::Point p = vertex_cur->v;
Common::Point 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*)aatree_get_data(tree);
if (edge) {
Common::Point 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(PathfindingState *s, Vertex *vert) {
// Determines all vertices that are visible from a particular vertex and
// updates the visibility matrix
// Parameters: (PathfindingState *) s: The pathfinding state
// (Vertex *) vert: The vertex
aatree_t *tree = aatree_new();
Common::Point p = vert->v;
Polygon *polygon;
int i;
int is_visible;
Vertex **vert_sorted = (Vertex**)sci_malloc(sizeof(Vertex *) * s->vertices);
// Sort vertices by angle (first) and distance (second)
memcpy(vert_sorted, s->vertex_index, sizeof(Vertex *) * s->vertices);
vertex_cur = vert;
qsort(vert_sorted, s->vertices, sizeof(Vertex *), vertex_compare);
for (PolygonList::iterator it = s->polygons.begin(); it != s->polygons.end(); ++it) {
polygon = *it;
Vertex *vertex;
vertex = polygon->vertices.first();
// Check that there is more than one vertex.
if (VERTEX_HAS_EDGES(vertex))
CLIST_FOREACH(vertex, &polygon->vertices) {
Common::Point 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) && (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 *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]);
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]);
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]);
if (left(vert_sorted[j]->v, p, v1->v))
aatree_insert(v1, &tree, edge_compare);
v1 = CLIST_NEXT(vert_sorted[j]);
if (left(vert_sorted[j]->v, p, v1->v))
aatree_insert(vert_sorted[j], &tree, edge_compare);
}
}
}
free(vert_sorted);
// Free tree
aatree_free(tree);
}
static float distance(FloatPoint a, FloatPoint b) {
// Computes the distance between two pointfs
// Parameters: (Common::Point) 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(Common::Point p) {
// Determines if a point lies on the screen border
// Parameters: (Common::Point) 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(Common::Point p, Common::Point q) {
// Determines if an edge lies on the screen border
// Parameters: (Common::Point) 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(FloatPoint f, Polygon *polygon, Common::Point *ret) {
// Searches for a nearby point that is not contained in a polygon
// Parameters: (FloatPoint) f: The pointf to search nearby
// (Polygon *) polygon: The polygon
// Returns : (int) PF_OK on success, PF_FATAL otherwise
// (Common::Point) *ret: The non-contained point on success
Common::Point p;
// Try nearest point first
p = Common::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 = Common::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(Common::Point p, Polygon *polygon, Common::Point *ret) {
// Computes the near point of a point contained in a polygon
// Parameters: (Common::Point) p: The point
// (Polygon *) polygon: The polygon
// Returns : (int) PF_OK on success, PF_FATAL otherwise
// (Common::Point) *ret: The near point of p in polygon on success
Vertex *vertex;
FloatPoint near_p;
float dist = HUGE_DISTANCE;
CLIST_FOREACH(vertex, &polygon->vertices) {
Common::Point p1 = vertex->v;
Common::Point p2 = CLIST_NEXT(vertex)->v;
float w, h, l, u;
FloatPoint 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(toFloatPoint(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(Common::Point a, Common::Point b, Vertex *vertex, FloatPoint *ret) {
// Computes the intersection point of a line segment and an edge (not
// including the vertices themselves)
// Parameters: (Common::Point) a, b: The line segment (a, b)
// (Vertex *) vertex: The first vertex of the edge
// Returns : (int) FP_OK on success, PF_ERROR otherwise
// (FloatPoint) *ret: The intersection point
// Parameters of parametric equations
float s, t;
// Numerator and denominator of equations
float num, denom;
Common::Point c = vertex->v;
Common::Point d = CLIST_NEXT(vertex)->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(PathfindingState *s, Common::Point p, Common::Point q, Common::Point *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: (PathfindingState *) s: The pathfinding state
// (Common::Point) p, q: The line segment (p, q)
// Returns : (int) PF_OK on success, PF_ERROR when no intersections were
// found, PF_FATAL otherwise
// (Common::Point) *ret: On success, the closest intersection point
Polygon *polygon = 0;
FloatPoint isec;
Polygon *ipolygon = 0;
float dist = HUGE_DISTANCE;
for (PolygonList::iterator it = s->polygons.begin(); it != s->polygons.end(); ++it) {
polygon = *it;
Vertex *vertex;
CLIST_FOREACH(vertex, &polygon->vertices) {
float new_dist;
FloatPoint 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)->v, q))
continue;
if (intersection(p, q, vertex, &new_isec) != PF_OK)
continue;
}
new_dist = distance(toFloatPoint(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(PathfindingState *s, Common::Point p, Common::Point *ret, Polygon **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: (Common::Point) p: The point
// Returns : (int) PF_OK on success, PF_FATAL otherwise
// (Common::Point) *ret: A valid input point for pathfinding
// (Polygon *) *ret_pol: The polygon p was contained in if p
// != *ret, NULL otherwise
PolygonList::iterator it;
*ret_pol = NULL;
// Check for polygon containment
for (it = s->polygons.begin(); it != s->polygons.end(); ++it) {
if (contained(p, *it) != CONT_OUTSIDE)
break;
}
if (it != s->polygons.end()) {
Common::Point near_p;
if ((*it)->type == POLY_TOTAL_ACCESS) {
// Remove totally accessible polygon if it contains p
s->polygons.erase(it);
*ret = p;
return PF_OK;
}
// Otherwise, compute near point
if (near_point(p, (*it), &near_p) == PF_OK) {
*ret = near_p;
if (p != *ret)
*ret_pol = *it;
return PF_OK;
}
return PF_FATAL;
}
// p is not contained in any polygon
*ret = p;
return PF_OK;
}
static Vertex *merge_point(PathfindingState *s, Common::Point 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: (PathfindingState *) s: The pathfinding state
// (Common::Point) v: The point to merge
// Returns : (Vertex *) The vertex corresponding to v
Vertex *vertex;
Vertex *v_new;
Polygon *polygon;
// Check for already existing vertex
for (PolygonList::iterator it = s->polygons.begin(); it != s->polygons.end(); ++it) {
polygon = *it;
CLIST_FOREACH(vertex, &polygon->vertices) {
if (vertex->v == v)
return vertex;
}
}
v_new = new Vertex(v);
// Check for point being on an edge
for (PolygonList::iterator it = s->polygons.begin(); it != s->polygons.end(); ++it) {
polygon = *it;
// Skip single-vertex polygons
if (VERTEX_HAS_EDGES(polygon->vertices.first())) {
CLIST_FOREACH(vertex, &polygon->vertices) {
Vertex *next = CLIST_NEXT(vertex);
if (between(vertex->v, next->v, v)) {
// Split edge by adding vertex
polygon->vertices.insertAfter(vertex, v_new);
return v_new;
}
}
}
}
// Add point as single-vertex polygon
polygon = new Polygon(POLY_BARRED_ACCESS);
polygon->vertices.insertHead(v_new);
s->polygons.push_front(polygon);
return v_new;
}
static Polygon *convert_polygon(EngineState *s, reg_t polygon) {
// Converts an SCI polygon into a Polygon
// Parameters: (EngineState *) s: The game state
// (reg_t) polygon: The SCI polygon to convert
// Returns : (Polygon *) 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 *poly = new Polygon(KP_UINT(GET_SEL32(polygon, type)));
int is_reg_t = polygon_is_reg_t(list, size);
for (i = 0; i < size; i++) {
Vertex *vertex = new Vertex(read_point(list, is_reg_t, i));
poly->vertices.insertHead(vertex);
}
fix_vertex_order(poly);
return poly;
}
static void change_polygons_opt_0(PathfindingState *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: (PathfindingState *) s: The pathfinding state
PolygonList::iterator it = s->polygons.begin();
while (it != s->polygons.end()) {
Polygon *polygon = *it;
assert(polygon);
if (polygon->type == POLY_TOTAL_ACCESS) {
delete polygon;
it = s->polygons.erase(it);
} else {
if (polygon->type == POLY_NEAREST_ACCESS)
polygon->type = POLY_TOTAL_ACCESS;
++it;
}
}
}
static PathfindingState *convert_polygon_set(EngineState *s, reg_t poly_list, Common::Point start, Common::Point end, int opt) {
// Converts the SCI input data for pathfinding
// Parameters: (EngineState *) s: The game state
// (reg_t) poly_list: Polygon list
// (Common::Point) start: The start point
// (Common::Point) end: The end point
// (int) opt: Optimization level (0, 1 or 2)
// Returns : (PathfindingState *) On success a newly allocated pathfinding state,
// NULL otherwise
Polygon *polygon;
int err;
int count = 0;
PathfindingState *pf_s = new PathfindingState(start, end);
// Convert all polygons
if (poly_list.segment) {
List *list = LOOKUP_LIST(poly_list);
Node *node = LOOKUP_NODE(list->first);
while (node) {
polygon = convert_polygon(s, node->value);
pf_s->polygons.push_front(polygon);
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");
delete 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");
delete 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");
delete 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**)sci_malloc(sizeof(Vertex *) * (count + 2));
count = 0;
for (PolygonList::iterator it = pf_s->polygons.begin(); it != pf_s->polygons.end(); ++it) {
polygon = *it;
Vertex *vertex;
CLIST_FOREACH(vertex, &polygon->vertices) {
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(PathfindingState *s) {
// Computes the visibility graph
// Parameters: (PathfindingState *) s: The pathfinding state
Polygon *polygon;
for (PolygonList::iterator it = s->polygons.begin(); it != s->polygons.end(); ++it) {
polygon = *it;
Vertex *vertex;
CLIST_FOREACH(vertex, &polygon->vertices) {
visible_vertices(s, vertex);
}
}
}
static int intersecting_polygons(PathfindingState *s) {
// Detects (self-)intersecting polygons
// Parameters: (PathfindingState *) 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 *v1 = s->vertex_index[i];
if (!VERTEX_HAS_EDGES(v1))
continue;
for (j = i + 1; j < s->vertices; j++) {
Vertex *v2 = s->vertex_index[j];
if (!VERTEX_HAS_EDGES(v2))
continue;
// Skip neighbouring edges
if ((CLIST_NEXT(v1) == v2) || CLIST_PREV(v1) == v2)
continue;
if (intersect(v1->v, CLIST_NEXT(v1)->v,
v2->v, CLIST_NEXT(v2)->v))
return 1;
}
}
return 0;
}
static void dijkstra(PathfindingState *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: (PathfindingState *) s: The pathfinding state
Polygon *polygon;
// Vertices of which the shortest path is known
VertexList done;
// The remaining vertices
VertexList remain;
// Start out with all vertices in set remain
for (PolygonList::iterator it = s->polygons.begin(); it != s->polygons.end(); ++it) {
polygon = *it;
Vertex *vertex;
CLIST_FOREACH(vertex, &polygon->vertices) {
remain.push_front(vertex);
}
}
s->vertex_start->dist = 0.0f;
// Loop until we find vertex_end
while (1) {
// Find vertex at shortest distance from set done
VertexList::iterator it;
VertexList::iterator vertex_min_it = remain.end();
Vertex *vertex_min = 0;
float min = HUGE_DISTANCE;
for (it = remain.begin(); it != remain.end(); ++it) {
Vertex *vertex = *it;
if (vertex->dist < min) {
vertex_min_it = it;
vertex_min = *vertex_min_it;
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
done.push_front(vertex_min);
remain.erase(vertex_min_it);
for (int 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(toFloatPoint(vertex_min->v), toFloatPoint(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(PathfindingState *p, EngineState *s) {
// Stores the final path in newly allocated dynmem
// Parameters: (PathfindingState *) p: The pathfinding state
// (EngineState *) s: The game state
// Returns : (reg_t) Pointer to dynmem containing path
int path_len = 0;
byte *oref;
reg_t output;
Vertex *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 = s->seg_manager->allocDynmem(POLY_POINT_SIZE * 3, AVOIDPATH_DYNMEM_STRING, &output);
if (p->keep_start)
POLY_SET_POINT(oref, 0, p->start);
else
POLY_SET_POINT(oref, 0, p->vertex_start->v);
POLY_SET_POINT(oref, 1, p->vertex_start->v);
POLY_SET_POINT(oref, 2, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
return output;
}
while (vertex) {
// Compute path length
path_len++;
vertex = vertex->path_prev;
}
oref = s->seg_manager->allocDynmem(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, Common::Point(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);
if (p->keep_start)
POLY_SET_POINT(oref, 0, p->start);
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);
POLY_SET_POINT(oref, i, p->vertex_end->v);
return output;
}
vertex = p->vertex_end;
while (vertex) {
POLY_SET_POINT(oref, i, vertex->v);
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++) {
Common::Point pt;
POLY_GET_POINT(oref, i, pt);
sciprintf(" (%i, %i)", pt.x, pt.y);
}
sciprintf("\n");
}
return output;
}
reg_t kAvoidPath(EngineState *s, int funct_nr, int argc, reg_t *argv) {
Common::Point start = Common::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 *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);
delete polygon;
return retval;
}
case 6 :
case 7 : {
Common::Point end = Common::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;
PathfindingState *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");
delete 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 = s->seg_manager->allocDynmem(POLY_POINT_SIZE * 3,
AVOIDPATH_DYNMEM_STRING, &output);
POLY_SET_POINT(oref, 0, start);
POLY_SET_POINT(oref, 1, end);
POLY_SET_POINT(oref, 2, Common::Point(POLY_LAST_POINT, POLY_LAST_POINT));
return output;
}
visibility_graph(p);
dijkstra(p);
output = output_path(p, s);
delete p;
// Memory is freed by explicit calls to Memory
return output;
}
default:
warning("Unknown AvoidPath subfunction %d",
argc);
return NULL_REG;
break;
}
}
} // End of namespace Sci