source: anuga_core/source/anuga/utilities/polygon_ext.c @ 5225

Last change on this file since 5225 was 5225, checked in by ole, 15 years ago

Work done during Water Down Under 2008.
Line Mesh intersections towards ticket:175 (flow through a cross section).

File size: 7.0 KB
Line 
1// Python - C extension for polygon module.
2//
3// To compile (Python2.3):
4//  gcc -c polygon_ext.c -I/usr/include/python2.3 -o polygon_ext.o -Wall -O
5//  gcc -shared polygon_ext.o  -o polygon_ext.so
6//
7// See the module polygon.py
8//
9//
10// Ole Nielsen, GA 2004
11//
12// NOTE: We use long* instead of int* for Numeric arrays as this will work both
13//       for 64 as well as 32 bit systems
14
15
16#include "Python.h"
17#include "Numeric/arrayobject.h"
18#include "math.h"
19
20
21int __point_on_line(double x, double y,
22                    double x0, double y0,
23                    double x1, double y1) {
24  /*Determine whether a point is on a line segment
25
26    Input: x, y, x0, x0, x1, y1: where
27        point is given by x, y
28        line is given by (x0, y0) and (x1, y1)
29
30  */
31
32  double a0, a1, a_normal0, a_normal1, b0, b1, len_a, len_b;
33
34  a0 = x - x0;
35  a1 = y - y0;
36
37  a_normal0 = a1;
38  a_normal1 = -a0;
39
40  b0 = x1 - x0;
41  b1 = y1 - y0;
42
43  if ( a_normal0*b0 + a_normal1*b1 == 0.0 ) {
44    //Point is somewhere on the infinite extension of the line
45    // FIXME (Ole): Perhaps add a tolerance here instead of 0.0
46
47    len_a = sqrt(a0*a0 + a1*a1);
48    len_b = sqrt(b0*b0 + b1*b1);
49
50    if (a0*b0 + a1*b1 >= 0 && len_a <= len_b) {
51      return 1;
52    } else {
53      return 0;
54    }
55  } else {
56    return 0;
57  }
58}
59
60
61
62int __separate_points_by_polygon(int M,     // Number of points
63                                int N,     // Number of polygon vertices
64                                double* points,
65                                double* polygon,
66                                long* indices,  // M-Array for storage indices
67                                int closed,
68                                int verbose) {
69
70  double minpx, maxpx, minpy, maxpy, x, y, px_i, py_i, px_j, py_j;
71  int i, j, k, outside_index, inside_index, inside;
72
73  //Find min and max of poly used for optimisation when points
74  //are far away from polygon
75
76  minpx = polygon[0]; maxpx = minpx;
77  minpy = polygon[1]; maxpy = minpy;
78
79  for (i=0; i<N; i++) {
80    px_i = polygon[2*i];
81    py_i = polygon[2*i + 1];
82
83    if (px_i < minpx) minpx = px_i;
84    if (px_i > maxpx) maxpx = px_i;
85    if (py_i < minpy) minpy = py_i;
86    if (py_i > maxpy) maxpy = py_i;
87  }
88
89  //Begin main loop (for each point)
90  inside_index = 0;    //Keep track of points inside
91  outside_index = M-1; //Keep track of points outside (starting from end)   
92  if (verbose){
93     printf("Separating %d points\n", M);
94  } 
95  for (k=0; k<M; k++) {
96    if (verbose){
97      if (k %((M+10)/10)==0) printf("Doing %d of %d\n", k, M);
98    }
99   
100    x = points[2*k];
101    y = points[2*k + 1];
102
103    inside = 0;
104
105    //Optimisation
106    if ((x > maxpx) || (x < minpx) || (y > maxpy) || (y < minpy)) {
107      //Nothing
108    } else {   
109      //Check polygon
110      for (i=0; i<N; i++) {
111        //printf("k,i=%d,%d\n", k, i);
112        j = (i+1)%N;
113
114        px_i = polygon[2*i];
115        py_i = polygon[2*i+1];
116        px_j = polygon[2*j];
117        py_j = polygon[2*j+1];
118
119        //Check for case where point is contained in line segment
120        if (__point_on_line(x, y, px_i, py_i, px_j, py_j)) {
121          if (closed == 1) {
122            inside = 1;
123          } else {
124            inside = 0;
125          }
126          break;
127        } else {
128          //Check if truly inside polygon
129          if ( ((py_i < y) && (py_j >= y)) ||
130               ((py_j < y) && (py_i >= y)) ) {
131            if (px_i + (y-py_i)/(py_j-py_i)*(px_j-px_i) < x)
132              inside = 1-inside;
133          }
134        }
135      }
136    } 
137    if (inside == 1) {
138      indices[inside_index] = k;
139      inside_index += 1;
140    } else {
141      indices[outside_index] = k;
142      outside_index -= 1;   
143    }
144  } // End k
145
146  return inside_index;
147}
148
149
150
151// Gateways to Python
152PyObject *_point_on_line(PyObject *self, PyObject *args) {
153  //
154  // point_on_line(x, y, x0, y0, x1, y1)
155  //
156
157  double x, y, x0, y0, x1, y1;
158  int res;
159  PyObject *result;
160
161  // Convert Python arguments to C
162  if (!PyArg_ParseTuple(args, "dddddd", &x, &y, &x0, &y0, &x1, &y1)) {
163    PyErr_SetString(PyExc_RuntimeError, 
164                    "point_on_line could not parse input");   
165    return NULL;
166  }
167
168
169  // Call underlying routine
170  res = __point_on_line(x, y, x0, y0, x1, y1);
171
172  // Return values a and b
173  result = Py_BuildValue("i", res);
174  return result;
175}
176
177
178
179PyObject *_separate_points_by_polygon(PyObject *self, PyObject *args) {
180  //def separate_points_by_polygon(points, polygon, closed, verbose, one_point):
181  //  """Determine whether points are inside or outside a polygon
182  //
183  //  Input:
184  //     point - Tuple of (x, y) coordinates, or list of tuples
185  //     polygon - list of vertices of polygon
186  //     closed - (optional) determine whether points on boundary should be
187  //     regarded as belonging to the polygon (closed = True)
188  //     or not (closed = False)
189
190  //
191  //  Output:
192  //     indices: array of same length as points with indices of points falling
193  //     inside the polygon listed from the beginning and indices of points
194  //     falling outside listed from the end.
195  //     
196  //     count: count of points falling inside the polygon
197  //     
198  //     The indices of points inside are obtained as indices[:count]
199  //     The indices of points outside are obtained as indices[count:]       
200  //
201  //  Examples:
202  //     separate_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]] )
203  //     will return the indices [0, 2, 1] as only the first and the last point
204  //     is inside the unit square
205  //
206  //  Remarks:
207  //     The vertices may be listed clockwise or counterclockwise and
208  //     the first point may optionally be repeated.
209  //     Polygons do not need to be convex.
210  //     Polygons can have holes in them and points inside a hole is
211  //     regarded as being outside the polygon.
212  //
213  //
214  //  Algorithm is based on work by Darel Finley,
215  //  http://www.alienryderflex.com/polygon/
216  //
217  //
218
219  PyArrayObject
220    *points,
221    *polygon,
222    *indices;
223
224  int closed, verbose; //Flags
225  int count, M, N;
226
227  // Convert Python arguments to C
228  if (!PyArg_ParseTuple(args, "OOOii",
229                        &points,
230                        &polygon,
231                        &indices,
232                        &closed,
233                        &verbose)) {
234   
235
236    PyErr_SetString(PyExc_RuntimeError, 
237                    "separate_points_by_polygon could not parse input");
238    return NULL;
239  }
240
241  M = points -> dimensions[0];   //Number of points
242  N = polygon -> dimensions[0];  //Number of vertices in polygon
243
244  //FIXME (Ole): This isn't send to Python's sys.stdout
245  if (verbose) printf("Got %d points and %d polygon vertices\n", M, N);
246 
247  //Call underlying routine
248  count = __separate_points_by_polygon(M, N,
249                                       (double*) points -> data,
250                                       (double*) polygon -> data,
251                                       (long*) indices -> data,
252                                       closed, verbose);
253 
254  //NOTE: return number of points inside..
255  return Py_BuildValue("i", count);
256}
257
258
259
260// Method table for python module
261static struct PyMethodDef MethodTable[] = {
262  /* The cast of the function is necessary since PyCFunction values
263   * only take two PyObject* parameters, and rotate() takes
264   * three.
265   */
266
267  {"_point_on_line", _point_on_line, METH_VARARGS, "Print out"},
268  {"_separate_points_by_polygon", _separate_points_by_polygon, 
269                                 METH_VARARGS, "Print out"},
270  {NULL, NULL, 0, NULL}   /* sentinel */
271};
272
273
274
275// Module initialisation
276void initpolygon_ext(void){
277  Py_InitModule("polygon_ext", MethodTable);
278
279  import_array();     //Necessary for handling of NumPY structures
280}
281
282
283
Note: See TracBrowser for help on using the repository browser.