source: inundation/ga/storm_surge/pyvolution/util_ext.c @ 999

Last change on this file since 999 was 999, checked in by ole, 20 years ago

Optimised inside_polygon in C and tested

File size: 7.1 KB
Line 
1// Python - C extension for finite_volumes util module.
2//
3// To compile (Python2.3):
4//  gcc -c util_ext.c -I/usr/include/python2.3 -o util_ext.o -Wall -O
5//  gcc -shared util_ext.o  -o util_ext.so     
6//
7// See the module util.py
8//
9//
10// Ole Nielsen, GA 2004
11       
12#include "Python.h"
13#include "Numeric/arrayobject.h"
14#include "math.h"
15
16//Shared snippets
17#include "util_ext.h"
18
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 ) {   
44    //Point is somewhere on the infinite extension of the line
45
46    len_a = sqrt(a0*a0 + a1*a1);               
47    len_b = sqrt(b0*b0 + b1*b1);
48                                       
49    if (a0*b0 + a1*b1 >= 0 && len_a <= len_b) {
50      return 1;
51    } else {
52      return 0;
53    }
54  } else {
55    return 0;
56  }
57}
58
59
60
61int _inside_polygon(int M,           // Number of points
62                    int N,           // Number of polygon vertices
63                    double* points,
64                    double* polygon,
65                    int* indices,    // M-Array for storage indices
66                    int closed, 
67                    int verbose) {
68 
69  double minpx, maxpx, minpy, maxpy, x, y, px_i, py_i, px_j, py_j;
70  int i, j, k, count, inside;
71 
72  //Find min and max of poly used for optimisation when points
73  //are far away from polygon
74 
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  count = 0;
91  for (k=0; k<M; k++) {
92    //FIXME: Do this later
93    //if (verbose){
94    //  if (k %((M+10)/10)==0: print 'Doing %d of %d' %(k, M)
95       
96    x = points[2*k];
97    y = points[2*k + 1];       
98
99    inside = 0;
100
101    //Optimisation
102    if ((x > maxpx) || (x < minpx)) continue;
103    if ((y > maxpy) || (y < minpy)) continue;       
104
105    //Check polygon
106    for (i=0; i<N; i++) {
107      //printf("k,i=%d,%d\n", k, i);
108      j = (i+1)%N;
109     
110      px_i = polygon[2*i];
111      py_i = polygon[2*i+1];
112      px_j = polygon[2*j];
113      py_j = polygon[2*j+1];     
114     
115      //Check for case where point is contained in line segment
116      if (_point_on_line(x, y, px_i, py_i, px_j, py_j)) {
117        if (closed == 1) {
118          inside = 1;
119        } else {
120          inside = 0;
121        }
122        break; 
123      } else {   
124        //Check if truly inside polygon             
125        if ( ((py_i < y) && (py_j >= y)) ||
126             ((py_j < y) && (py_i >= y)) ) {
127          if (px_i + (y-py_i)/(py_j-py_i)*(px_j-px_i) < x)
128            inside = 1-inside;
129        }
130      }
131    }
132    if (inside == 1) {
133      indices[count] = k;
134      count++;
135    }       
136  } // End k
137 
138  return count;
139}
140
141
142
143// Gateways to Python
144PyObject *point_on_line(PyObject *self, PyObject *args) {
145  //
146  // point_on_line(x, y, x0, y0, x1, y1)
147  //
148 
149  double x, y, x0, y0, x1, y1;
150  int res;
151  PyObject *result;
152 
153  // Convert Python arguments to C 
154  if (!PyArg_ParseTuple(args, "dddddd", &x, &y, &x0, &y0, &x1, &y1)) 
155    return NULL;
156
157 
158  // Call underlying routine
159  res = _point_on_line(x, y, x0, y0, x1, y1);
160
161  // Return values a and b
162  result = Py_BuildValue("i", res);
163  return result;
164}
165
166
167
168PyObject *inside_polygon(PyObject *self, PyObject *args) {
169  //def inside_polygon(point, polygon, closed, verbose, one_point): 
170  //  """Determine whether points are inside or outside a polygon
171  // 
172  //  Input:
173  //     point - Tuple of (x, y) coordinates, or list of tuples
174  //     polygon - list of vertices of polygon
175  //     one_poin - If True Boolean value should be returned
176  //                If False, indices of points inside returned
177  //     closed - (optional) determine whether points on boundary should be
178  //     regarded as belonging to the polygon (closed = True)
179  //     or not (closed = False)
180 
181  // 
182  //  Output:
183  //     If one point is considered, True or False is returned.
184  //     If multiple points are passed in, the function returns indices
185  //     of those points that fall inside the polygon     
186  //     
187  //  Examples:
188  //     inside_polygon( [0.5, 0.5], [[0,0], [1,0], [1,1], [0,1]] )
189  //     will evaluate to True as the point 0.5, 0.5 is inside the unit square
190  //     
191  //     inside_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]] )
192  //     will return the indices [0, 2] as only the first and the last point
193  //     is inside the unit square
194  //     
195  //  Remarks:
196  //     The vertices may be listed clockwise or counterclockwise and
197  //     the first point may optionally be repeated.   
198  //     Polygons do not need to be convex.
199  //     Polygons can have holes in them and points inside a hole is
200  //     regarded as being outside the polygon.               
201  //           
202  //     
203  //  Algorithm is based on work by Darel Finley,
204  //  http://www.alienryderflex.com/polygon/
205  //
206  //
207 
208  PyArrayObject
209    *point, 
210    *polygon, 
211    *indices;
212   
213  int closed, verbose; //Flags     
214  int count, M, N;
215 
216  // Convert Python arguments to C 
217  if (!PyArg_ParseTuple(args, "OOOii", 
218                        &point, 
219                        &polygon, 
220                        &indices, 
221                        &closed, 
222                        &verbose)) 
223    return NULL;
224
225  M = point -> dimensions[0];    //Number of points         
226  N = polygon -> dimensions[0];  //Number of vertices in polygon       
227
228  //printf("M=%d, N=%d\n", M, N);
229  // Call underlying routine
230  count = _inside_polygon(M, N, 
231                          (double*) point -> data,
232                          (double*) polygon -> data,
233                          (int*) indices -> data,
234                          closed, verbose); 
235                           
236  //NOTE: return number of points inside..
237  //printf("COunt=%d\n", count);
238  return Py_BuildValue("i", count);
239}
240
241
242
243PyObject *gradient(PyObject *self, PyObject *args) {
244  //
245  // a,b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)
246  //
247 
248  double x0, y0, x1, y1, x2, y2, q0, q1, q2, a, b;
249  PyObject *result;
250 
251  // Convert Python arguments to C 
252  if (!PyArg_ParseTuple(args, "ddddddddd", &x0, &y0, &x1, &y1, &x2, &y2,
253                        &q0, &q1, &q2)) 
254    return NULL;
255
256 
257  // Call underlying routine
258  _gradient(x0, y0, x1, y1, x2, y2, 
259            q0, q1, q2, &a, &b);
260
261  // Return values a and b
262  result = Py_BuildValue("dd", a, b);
263  return result;
264}
265
266
267// Method table for python module
268static struct PyMethodDef MethodTable[] = {
269  /* The cast of the function is necessary since PyCFunction values
270   * only take two PyObject* parameters, and rotate() takes
271   * three.
272   */
273 
274  //{"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"}, 
275  {"gradient", gradient, METH_VARARGS, "Print out"},   
276  {"point_on_line", point_on_line, METH_VARARGS, "Print out"},     
277  {"inside_polygon", inside_polygon, METH_VARARGS, "Print out"},     
278  {NULL, NULL, 0, NULL}   /* sentinel */
279};
280
281
282
283// Module initialisation   
284void initutil_ext(void){
285  Py_InitModule("util_ext", MethodTable);
286 
287  import_array();     //Necessary for handling of NumPY structures 
288}
289
Note: See TracBrowser for help on using the repository browser.