Changeset 1093
- Timestamp:
- Mar 16, 2005, 11:22:38 PM (20 years ago)
- Location:
- inundation/ga/storm_surge/pyvolution
- Files:
-
- 3 edited
Legend:
- Unmodified
- Added
- Removed
-
inundation/ga/storm_surge/pyvolution/test_util.py
r1091 r1093 936 936 res = outside_polygon( points, polygon ) 937 937 938 assert allclose( res, [3, 4] )938 assert allclose( res, [3, 4] ) 939 939 940 940 … … 955 955 #evaluate to True as the point 0.5, 1.0 is outside the unit square 956 956 957 def test_separate_points_by_polygon(self): 958 U = [[0,0], [1,0], [1,1], [0,1]] #Unit square 959 960 indices, count = separate_points_by_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U ) 961 assert allclose( indices, [0,2,1] ) 962 assert count == 2 963 964 #One more test of vector formulation returning indices 965 polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]] 966 points = [ [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]] 967 res, count = separate_points_by_polygon( points, polygon ) 968 969 assert allclose( res, [0,1,2,4,3] ) 970 assert count == 3 971 972 973 polygon = [[0,0], [1,0], [0.5,-1], [2, -1], [2,1], [0,1]] 974 points = [ [0.5, 1.4], [0.5, 0.5], [1, -0.5], [1.5, 0], [0.5, 1.5], [0.5, -0.5]] 975 res, count = separate_points_by_polygon( points, polygon ) 976 977 assert allclose( res, [1,2,3,5,4,0] ) 978 assert count == 3 979 980 957 981 def test_populate_polygon(self): 958 982 -
inundation/ga/storm_surge/pyvolution/util.py
r1091 r1093 1 """This modul contains various auxiliary function used by pyvolution.2 3 It is also a clearing house for function tat may later earn a module1 """This module contains various auxiliary function used by pyvolution. 2 3 It is also a clearing house for functions that may later earn a module 4 4 of their own. 5 5 """ … … 20 20 theta = acos(v1) 21 21 except ValueError, e: 22 print 'WARNING Angle acos(%s) failed: %s' %(str(v1), e)22 print 'WARNING (util.py): Angle acos(%s) failed: %s' %(str(v1), e) 23 23 24 24 #FIXME, hack to avoid acos(1.0) Value error … … 30 30 elif (v1+s > -1.0) and (v1-s < -1.0): 31 31 theta = 3.1415926535897931 32 print "WARNING, setting theta to ", theta 32 print 'WARNING (util.py): angle v1 is %f, setting acos to %f '\ 33 %(v1, theta) 34 33 35 if v2 < 0: 34 36 #Quadrant 3 or 4 … … 912 914 #POLYGON STUFF 913 915 # 914 #FIXME: ALl these should be put into new module polygon.py 915 916 def inside_polygon(point, polygon, closed = True, verbose = False): 916 #FIXME: All these should be put into new module polygon.py 917 918 919 920 #Redefine these to use separate_by_polygon which will put all inside indices 921 #in the first part of the indices array and outside indices in the last 922 923 def inside_polygon(points, polygon, closed = True, verbose = False): 924 """See separate_points_by_polygon for documentation 925 """ 926 927 from Numeric import array, Float, reshape 928 929 if verbose: print 'Checking input to inside_polygon' 930 try: 931 points = array(points).astype(Float) 932 except: 933 msg = 'Points could not be converted to Numeric array' 934 raise msg 935 936 try: 937 polygon = array(polygon).astype(Float) 938 except: 939 msg = 'Polygon could not be converted to Numeric array' 940 raise msg 941 942 943 944 if len(points.shape) == 1: 945 one_point = True 946 points = reshape(points, (1,2)) 947 else: 948 one_point = False 949 950 indices, count = separate_points_by_polygon(points, polygon, 951 closed, verbose) 952 953 if one_point: 954 return count == 1 955 else: 956 return indices[:count] 957 958 def outside_polygon(points, polygon, closed = True, verbose = False): 959 """See separate_points_by_polygon for documentation 960 """ 961 962 from Numeric import array, Float, reshape 963 964 if verbose: print 'Checking input to outside_polygon' 965 try: 966 points = array(points).astype(Float) 967 except: 968 msg = 'Points could not be converted to Numeric array' 969 raise msg 970 971 try: 972 polygon = array(polygon).astype(Float) 973 except: 974 msg = 'Polygon could not be converted to Numeric array' 975 raise msg 976 977 978 979 if len(points.shape) == 1: 980 one_point = True 981 points = reshape(points, (1,2)) 982 else: 983 one_point = False 984 985 indices, count = separate_points_by_polygon(points, polygon, 986 closed, verbose) 987 988 if one_point: 989 return count != 1 990 else: 991 return indices[count:][::-1] #return reversed 992 993 994 def separate_points_by_polygon(points, polygon, 995 closed = True, verbose = False): 996 """Determine whether points are inside or outside a polygon 997 998 Input: 999 points - Tuple of (x, y) coordinates, or list of tuples 1000 polygon - list of vertices of polygon 1001 closed - (optional) determine whether points on boundary should be 1002 regarded as belonging to the polygon (closed = True) 1003 or not (closed = False) 1004 1005 Outputs: 1006 indices: array of same length as points with indices of points falling 1007 inside the polygon listed from the beginning and indices of points 1008 falling outside listed from the end. 1009 1010 count: count of points falling inside the polygon 1011 1012 The indices of points inside are obtained as indices[:count] 1013 The indices of points outside are obtained as indices[count:] 1014 1015 1016 Examples: 1017 U = [[0,0], [1,0], [1,1], [0,1]] #Unit square 1018 1019 separate_points_by_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U) 1020 will return the indices [0, 2, 1] and count == 2 as only the first 1021 and the last point are inside the unit square 1022 1023 Remarks: 1024 The vertices may be listed clockwise or counterclockwise and 1025 the first point may optionally be repeated. 1026 Polygons do not need to be convex. 1027 Polygons can have holes in them and points inside a hole is 1028 regarded as being outside the polygon. 1029 1030 Algorithm is based on work by Darel Finley, 1031 http://www.alienryderflex.com/polygon/ 1032 1033 """ 1034 1035 from Numeric import array, Float, reshape, Int, zeros 1036 1037 1038 #Input checks 1039 try: 1040 points = array(points).astype(Float) 1041 except: 1042 msg = 'Points could not be converted to Numeric array' 1043 raise msg 1044 1045 try: 1046 polygon = array(polygon).astype(Float) 1047 except: 1048 msg = 'Polygon could not be converted to Numeric array' 1049 raise msg 1050 1051 assert len(polygon.shape) == 2,\ 1052 'Polygon array must be a 2d array of vertices' 1053 1054 assert polygon.shape[1] == 2,\ 1055 'Polygon array must have two columns' 1056 1057 assert len(points.shape) == 2,\ 1058 'Points array must be a 2d array' 1059 1060 assert points.shape[1] == 2,\ 1061 'Points array must have two columns' 1062 1063 N = polygon.shape[0] #Number of vertices in polygon 1064 M = points.shape[0] #Number of points 1065 1066 px = polygon[:,0] 1067 py = polygon[:,1] 1068 1069 #Used for an optimisation when points are far away from polygon 1070 minpx = min(px); maxpx = max(px) 1071 minpy = min(py); maxpy = max(py) 1072 1073 1074 #Begin main loop 1075 indices = zeros(M, Int) 1076 1077 inside_index = 0 #Keep track of points inside 1078 outside_index = M-1 #Keep track of points outside (starting from end) 1079 1080 for k in range(M): 1081 1082 if verbose: 1083 if k %((M+10)/10)==0: print 'Doing %d of %d' %(k, M) 1084 1085 #for each point 1086 x = points[k, 0] 1087 y = points[k, 1] 1088 1089 inside = False 1090 1091 if not x > maxpx or x < minpx or y > maxpy or y < minpy: 1092 #Check polygon 1093 for i in range(N): 1094 j = (i+1)%N 1095 1096 #Check for case where point is contained in line segment 1097 if point_on_line(x, y, px[i], py[i], px[j], py[j]): 1098 if closed: 1099 inside = True 1100 else: 1101 inside = False 1102 break 1103 else: 1104 #Check if truly inside polygon 1105 if py[i] < y and py[j] >= y or\ 1106 py[j] < y and py[i] >= y: 1107 if px[i] + (y-py[i])/(py[j]-py[i])*(px[j]-px[i]) < x: 1108 inside = not inside 1109 1110 if inside: 1111 indices[inside_index] = k 1112 inside_index += 1 1113 else: 1114 indices[outside_index] = k 1115 outside_index -= 1 1116 1117 return indices, inside_index 1118 1119 1120 def separate_points_by_polygon_c(points, polygon, 1121 closed = True, verbose = False): 1122 """Determine whether points are inside or outside a polygon 1123 1124 C-wrapper 1125 """ 1126 1127 from Numeric import array, Float, reshape, zeros, Int 1128 1129 1130 if verbose: print 'Checking input to separate_points_by_polygon' 1131 #Input checks 1132 try: 1133 points = array(points).astype(Float) 1134 except: 1135 msg = 'Points could not be converted to Numeric array' 1136 raise msg 1137 1138 try: 1139 polygon = array(polygon).astype(Float) 1140 except: 1141 msg = 'Polygon could not be converted to Numeric array' 1142 raise msg 1143 1144 assert len(polygon.shape) == 2,\ 1145 'Polygon array must be a 2d array of vertices' 1146 1147 assert polygon.shape[1] == 2,\ 1148 'Polygon array must have two columns' 1149 1150 assert len(points.shape) == 2,\ 1151 'Points array must be a 2d array' 1152 1153 assert points.shape[1] == 2,\ 1154 'Points array must have two columns' 1155 1156 N = polygon.shape[0] #Number of vertices in polygon 1157 M = points.shape[0] #Number of points 1158 1159 from util_ext import separate_points_by_polygon 1160 1161 if verbose: print 'Allocating array for indices' 1162 1163 indices = zeros( M, Int ) 1164 1165 if verbose: print 'Calling C-version of inside poly' 1166 count = separate_points_by_polygon(points, polygon, indices, 1167 int(closed), int(verbose)) 1168 1169 return indices, count 1170 1171 1172 1173 class Polygon_function: 1174 """Create callable object f: x,y -> z, where a,y,z are vectors and 1175 where f will return different values depending on whether x,y belongs 1176 to specified polygons. 1177 1178 To instantiate: 1179 1180 Polygon_function(polygons) 1181 1182 where polygons is a list of tuples of the form 1183 1184 [ (P0, v0), (P1, v1), ...] 1185 1186 with Pi being lists of vertices defining polygons and vi either 1187 constants or functions of x,y to be applied to points with the polygon. 1188 1189 The function takes an optional argument, default which is the value 1190 (or function) to used for points not belonging to any polygon. 1191 For example: 1192 1193 Polygon_function(polygons, default = 0.03) 1194 1195 If omitted the default value will be 0.0 1196 1197 Note: If two polygons overlap, the one last in the list takes precedence 1198 1199 """ 1200 1201 def __init__(self, regions, default = 0.0): 1202 1203 try: 1204 len(regions) 1205 except: 1206 msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons 1207 raise msg 1208 1209 1210 T = regions[0] 1211 try: 1212 a = len(T) 1213 except: 1214 msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons 1215 raise msg 1216 1217 assert a == 2, 'Must have two component each: %s' %T 1218 1219 self.regions = regions 1220 self.default = default 1221 1222 1223 def __call__(self, x, y): 1224 from util import inside_polygon 1225 from Numeric import ones, Float, concatenate, array, reshape, choose 1226 1227 x = array(x).astype(Float) 1228 y = array(y).astype(Float) 1229 1230 N = len(x) 1231 assert len(y) == N 1232 1233 points = concatenate( (reshape(x, (N, 1)), 1234 reshape(y, (N, 1))), axis=1 ) 1235 1236 if callable(self.default): 1237 z = self.default(x,y) 1238 else: 1239 z = ones(N, Float) * self.default 1240 1241 for polygon, value in self.regions: 1242 indices = inside_polygon(points, polygon) 1243 1244 #FIXME: This needs to be vectorised 1245 if callable(value): 1246 for i in indices: 1247 xx = array([x[i]]) 1248 yy = array([y[i]]) 1249 z[i] = value(xx, yy)[0] 1250 else: 1251 for i in indices: 1252 z[i] = value 1253 1254 return z 1255 1256 def read_polygon(filename): 1257 """Read points assumed to form a polygon 1258 There must be exactly two numbers in each line 1259 """ 1260 1261 #Get polygon 1262 fid = open(filename) 1263 lines = fid.readlines() 1264 fid.close() 1265 polygon = [] 1266 for line in lines: 1267 fields = line.split(',') 1268 polygon.append( [float(fields[0]), float(fields[1])] ) 1269 1270 return polygon 1271 1272 def populate_polygon(polygon, number_of_points, seed = None): 1273 """Populate given polygon with uniformly distributed points. 1274 1275 Input: 1276 polygon - list of vertices of polygon 1277 number_of_points - (optional) number of points 1278 seed - seed for random number generator (default=None) 1279 1280 Output: 1281 points - list of points inside polygon 1282 1283 Examples: 1284 populate_polygon( [[0,0], [1,0], [1,1], [0,1]], 5 ) 1285 will return five randomly selected points inside the unit square 1286 """ 1287 1288 from random import uniform, seed 1289 1290 seed(seed) 1291 1292 points = [] 1293 1294 #Find outer extent of polygon 1295 max_x = min_x = polygon[0][0] 1296 max_y = min_y = polygon[0][1] 1297 for point in polygon[1:]: 1298 x = point[0] 1299 if x > max_x: max_x = x 1300 if x < min_x: min_x = x 1301 y = point[1] 1302 if y > max_y: max_y = y 1303 if y < min_y: min_y = y 1304 1305 1306 while len(points) < number_of_points: 1307 x = uniform(min_x, max_x) 1308 y = uniform(min_y, max_y) 1309 1310 if inside_polygon( [x,y], polygon ): 1311 points.append([x,y]) 1312 1313 return points 1314 1315 #################################################################### 1316 #Python versions of function that are also implemented in util_gateway.c 1317 # 1318 1319 def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2): 1320 """ 1321 """ 1322 1323 det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0) 1324 a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0) 1325 a /= det 1326 1327 b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0) 1328 b /= det 1329 1330 return a, b 1331 1332 1333 1334 ############################################## 1335 #Initialise module 1336 1337 import compile 1338 if compile.can_use_C_extension('util_ext.c'): 1339 from util_ext import gradient, point_on_line 1340 separate_points_by_polygon = separate_points_by_polygon_c 1341 else: 1342 gradient = gradient_python 1343 1344 1345 if __name__ == "__main__": 1346 pass 1347 1348 1349 1350 1351 1352 1353 #OBSOLETED STUFF 1354 def inside_polygon_old(point, polygon, closed = True, verbose = False): 1355 #FIXME Obsoleted 917 1356 """Determine whether points are inside or outside a polygon 918 1357 … … 1036 1475 1037 1476 1477 #def remove_from(A, B): 1478 # """Assume that A 1479 # """ 1480 # from Numeric import search_sorted## 1481 # 1482 # ind = search_sorted(A, B) 1483 1484 1485 1486 def outside_polygon_old(point, polygon, closed = True, verbose = False): 1487 #OBSOLETED 1488 """Determine whether points are outside a polygon 1489 1490 Input: 1491 point - Tuple of (x, y) coordinates, or list of tuples 1492 polygon - list of vertices of polygon 1493 closed - (optional) determine whether points on boundary should be 1494 regarded as belonging to the polygon (closed = True) 1495 or not (closed = False) 1496 1497 Output: 1498 If one point is considered, True or False is returned. 1499 If multiple points are passed in, the function returns indices 1500 of those points that fall outside the polygon 1501 1502 Examples: 1503 U = [[0,0], [1,0], [1,1], [0,1]] #Unit square 1504 outside_polygon( [0.5, 0.5], U ) 1505 will evaluate to False as the point 0.5, 0.5 is inside the unit square 1506 1507 ouside_polygon( [1.5, 0.5], U ) 1508 will evaluate to True as the point 1.5, 0.5 is outside the unit square 1509 1510 outside_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U ) 1511 will return the indices [1] as only the first and the last point 1512 is inside the unit square 1513 """ 1514 1515 #FIXME: This is too slow 1516 1517 res = inside_polygon(point, polygon, closed, verbose) 1518 1519 if res is True or res is False: 1520 return not res 1521 1522 #Now invert indices 1523 from Numeric import arrayrange, compress 1524 outside_indices = arrayrange(len(point)) 1525 for i in res: 1526 outside_indices = compress(outside_indices != i, outside_indices) 1527 return outside_indices 1038 1528 1039 1529 def inside_polygon_c(point, polygon, closed = True, verbose = False): 1530 #FIXME: Obsolete 1040 1531 """Determine whether points are inside or outside a polygon 1041 1532 … … 1086 1577 1087 1578 if one_point: 1088 return count == 1 1579 return count == 1 #Return True if the point was inside 1089 1580 else: 1090 1581 if verbose: print 'Got %d points' %count 1091 return indices[:count] 1092 1093 1094 #def remove_from(A, B): 1095 # """Assume that A 1096 # """ 1097 # from Numeric import search_sorted## 1098 # 1099 # ind = search_sorted(A, B) 1100 1101 1102 1103 def outside_polygon(point, polygon, closed = True, verbose = False): 1104 """Determine whether points are outside a polygon 1105 1106 Input: 1107 point - Tuple of (x, y) coordinates, or list of tuples 1108 polygon - list of vertices of polygon 1109 closed - (optional) determine whether points on boundary should be 1110 regarded as belonging to the polygon (closed = True) 1111 or not (closed = False) 1112 1113 Output: 1114 If one point is considered, True or False is returned. 1115 If multiple points are passed in, the function returns indices 1116 of those points that fall outside the polygon 1117 1118 Examples: 1119 U = [[0,0], [1,0], [1,1], [0,1]] #Unit square 1120 outside_polygon( [0.5, 0.5], U ) 1121 will evaluate to False as the point 0.5, 0.5 is inside the unit square 1122 1123 ouside_polygon( [1.5, 0.5], U ) 1124 will evaluate to True as the point 1.5, 0.5 is outside the unit square 1125 1126 outside_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]], U ) 1127 will return the indices [1] as only the first and the last point 1128 is inside the unit square 1129 """ 1130 1131 res = inside_polygon(point, polygon, closed, verbose) 1132 1133 if res is True or res is False: 1134 return not res 1135 1136 #Now invert indices 1137 from Numeric import arrayrange, compress 1138 outside_indices = arrayrange(len(point)) 1139 for i in res: 1140 outside_indices = compress(outside_indices != i, outside_indices) 1141 return outside_indices 1142 1143 1144 1145 class Polygon_function: 1146 """Create callable object f: x,y -> z, where a,y,z are vectors and 1147 where f will return different values depending on whether x,y belongs 1148 to specified polygons. 1149 1150 To instantiate: 1151 1152 Polygon_function(polygons) 1153 1154 where polygons is a list of tuples of the form 1155 1156 [ (P0, v0), (P1, v1), ...] 1157 1158 with Pi being lists of vertices defining polygons and vi either 1159 constants or functions of x,y to be applied to points with the polygon. 1160 1161 The function takes an optional argument, default which is the value 1162 (or function) to used for points not belonging to any polygon. 1163 For example: 1164 1165 Polygon_function(polygons, default = 0.03) 1166 1167 If omitted the default value will be 0.0 1168 1169 Note: If two polygons overlap, the one last in the list takes precedence 1170 1171 """ 1172 1173 def __init__(self, regions, default = 0.0): 1174 1175 try: 1176 len(regions) 1177 except: 1178 msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons 1179 raise msg 1180 1181 1182 T = regions[0] 1183 try: 1184 a = len(T) 1185 except: 1186 msg = 'Polygon_function takes a list of pairs (polygon, value). Got %s' %polygons 1187 raise msg 1188 1189 assert a == 2, 'Must have two component each: %s' %T 1190 1191 self.regions = regions 1192 self.default = default 1193 1194 1195 def __call__(self, x, y): 1196 from util import inside_polygon 1197 from Numeric import ones, Float, concatenate, array, reshape, choose 1198 1199 x = array(x).astype(Float) 1200 y = array(y).astype(Float) 1201 1202 N = len(x) 1203 assert len(y) == N 1204 1205 points = concatenate( (reshape(x, (N, 1)), 1206 reshape(y, (N, 1))), axis=1 ) 1207 1208 if callable(self.default): 1209 z = self.default(x,y) 1210 else: 1211 z = ones(N, Float) * self.default 1212 1213 for polygon, value in self.regions: 1214 indices = inside_polygon(points, polygon) 1215 1216 #FIXME: This needs to be vectorised 1217 if callable(value): 1218 for i in indices: 1219 xx = array([x[i]]) 1220 yy = array([y[i]]) 1221 z[i] = value(xx, yy)[0] 1222 else: 1223 for i in indices: 1224 z[i] = value 1225 1226 return z 1227 1228 def read_polygon(filename): 1229 """Read points assumed to form a polygon 1230 There must be exactly two numbers in each line 1231 """ 1232 1233 #Get polygon 1234 fid = open(filename) 1235 lines = fid.readlines() 1236 fid.close() 1237 polygon = [] 1238 for line in lines: 1239 fields = line.split(',') 1240 polygon.append( [float(fields[0]), float(fields[1])] ) 1241 1242 return polygon 1243 1244 def populate_polygon(polygon, number_of_points, seed = None): 1245 """Populate given polygon with uniformly distributed points. 1246 1247 Input: 1248 polygon - list of vertices of polygon 1249 number_of_points - (optional) number of points 1250 seed - seed for random number generator (default=None) 1251 1252 Output: 1253 points - list of points inside polygon 1254 1255 Examples: 1256 populate_polygon( [[0,0], [1,0], [1,1], [0,1]], 5 ) 1257 will return five randomly selected points inside the unit square 1258 """ 1259 1260 from random import uniform, seed 1261 1262 seed(seed) 1263 1264 points = [] 1265 1266 #Find outer extent of polygon 1267 max_x = min_x = polygon[0][0] 1268 max_y = min_y = polygon[0][1] 1269 for point in polygon[1:]: 1270 x = point[0] 1271 if x > max_x: max_x = x 1272 if x < min_x: min_x = x 1273 y = point[1] 1274 if y > max_y: max_y = y 1275 if y < min_y: min_y = y 1276 1277 1278 while len(points) < number_of_points: 1279 x = uniform(min_x, max_x) 1280 y = uniform(min_y, max_y) 1281 1282 if inside_polygon( [x,y], polygon ): 1283 points.append([x,y]) 1284 1285 return points 1286 1287 #################################################################### 1288 #Python versions of function that are also implemented in util_gateway.c 1289 # 1290 1291 def gradient_python(x0, y0, x1, y1, x2, y2, q0, q1, q2): 1292 """ 1293 """ 1294 1295 det = (y2-y0)*(x1-x0) - (y1-y0)*(x2-x0) 1296 a = (y2-y0)*(q1-q0) - (y1-y0)*(q2-q0) 1297 a /= det 1298 1299 b = (x1-x0)*(q2-q0) - (x2-x0)*(q1-q0) 1300 b /= det 1301 1302 return a, b 1303 1304 1305 1306 ############################################## 1307 #Initialise module 1308 1309 import compile 1310 if compile.can_use_C_extension('util_ext.c'): 1311 from util_ext import gradient, point_on_line 1312 inside_polygon = inside_polygon_c 1313 else: 1314 gradient = gradient_python 1315 1316 1317 if __name__ == "__main__": 1318 pass 1319 1582 1583 return indices[:count] #Return those indices that were inside 1584 -
inundation/ga/storm_surge/pyvolution/util_ext.c
r1091 r1093 62 62 63 63 64 65 int _separate_points_by_polygon(int M, // Number of points 66 int N, // Number of polygon vertices 67 double* points, 68 double* polygon, 69 long* indices, // M-Array for storage indices 70 int closed, 71 int verbose) { 72 73 double minpx, maxpx, minpy, maxpy, x, y, px_i, py_i, px_j, py_j; 74 int i, j, k, outside_index, inside_index, inside; 75 76 //Find min and max of poly used for optimisation when points 77 //are far away from polygon 78 79 minpx = polygon[0]; maxpx = minpx; 80 minpy = polygon[1]; maxpy = minpy; 81 82 for (i=0; i<N; i++) { 83 px_i = polygon[2*i]; 84 py_i = polygon[2*i + 1]; 85 86 if (px_i < minpx) minpx = px_i; 87 if (px_i > maxpx) maxpx = px_i; 88 if (py_i < minpy) minpy = py_i; 89 if (py_i > maxpy) maxpy = py_i; 90 } 91 92 //Begin main loop (for each point) 93 inside_index = 0; //Keep track of points inside 94 outside_index = M-1; //Keep track of points outside (starting from end) 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 152 PyObject *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 return NULL; 164 165 166 // Call underlying routine 167 res = _point_on_line(x, y, x0, y0, x1, y1); 168 169 // Return values a and b 170 result = Py_BuildValue("i", res); 171 return result; 172 } 173 174 175 176 PyObject *separate_points_by_polygon(PyObject *self, PyObject *args) { 177 //def separate_points_by_polygon(points, polygon, closed, verbose, one_point): 178 // """Determine whether points are inside or outside a polygon 179 // 180 // Input: 181 // point - Tuple of (x, y) coordinates, or list of tuples 182 // polygon - list of vertices of polygon 183 // closed - (optional) determine whether points on boundary should be 184 // regarded as belonging to the polygon (closed = True) 185 // or not (closed = False) 186 187 // 188 // Output: 189 // indices: array of same length as points with indices of points falling 190 // inside the polygon listed from the beginning and indices of points 191 // falling outside listed from the end. 192 // 193 // count: count of points falling inside the polygon 194 // 195 // The indices of points inside are obtained as indices[:count] 196 // The indices of points outside are obtained as indices[count:] 197 // 198 // Examples: 199 // separate_polygon( [[0.5, 0.5], [1, -0.5], [0.3, 0.2]] ) 200 // will return the indices [0, 2, 1] as only the first and the last point 201 // is inside the unit square 202 // 203 // Remarks: 204 // The vertices may be listed clockwise or counterclockwise and 205 // the first point may optionally be repeated. 206 // Polygons do not need to be convex. 207 // Polygons can have holes in them and points inside a hole is 208 // regarded as being outside the polygon. 209 // 210 // 211 // Algorithm is based on work by Darel Finley, 212 // http://www.alienryderflex.com/polygon/ 213 // 214 // 215 216 PyArrayObject 217 *points, 218 *polygon, 219 *indices; 220 221 int closed, verbose; //Flags 222 int count, M, N; 223 224 // Convert Python arguments to C 225 if (!PyArg_ParseTuple(args, "OOOii", 226 &points, 227 &polygon, 228 &indices, 229 &closed, 230 &verbose)) 231 return NULL; 232 233 M = points -> dimensions[0]; //Number of points 234 N = polygon -> dimensions[0]; //Number of vertices in polygon 235 236 if (verbose) printf("Got %d points and %d polygon vertices\n", M, N); 237 238 //Call underlying routine 239 count = _separate_points_by_polygon(M, N, 240 (double*) points -> data, 241 (double*) polygon -> data, 242 (long*) indices -> data, 243 closed, verbose); 244 245 //NOTE: return number of points inside.. 246 return Py_BuildValue("i", count); 247 } 248 249 250 251 PyObject *gradient(PyObject *self, PyObject *args) { 252 // 253 // a,b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2) 254 // 255 256 double x0, y0, x1, y1, x2, y2, q0, q1, q2, a, b; 257 PyObject *result; 258 259 // Convert Python arguments to C 260 if (!PyArg_ParseTuple(args, "ddddddddd", &x0, &y0, &x1, &y1, &x2, &y2, 261 &q0, &q1, &q2)) 262 return NULL; 263 264 265 // Call underlying routine 266 _gradient(x0, y0, x1, y1, x2, y2, 267 q0, q1, q2, &a, &b); 268 269 // Return values a and b 270 result = Py_BuildValue("dd", a, b); 271 return result; 272 } 273 274 275 // Method table for python module 276 static struct PyMethodDef MethodTable[] = { 277 /* The cast of the function is necessary since PyCFunction values 278 * only take two PyObject* parameters, and rotate() takes 279 * three. 280 */ 281 282 //{"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"}, 283 {"gradient", gradient, METH_VARARGS, "Print out"}, 284 {"point_on_line", point_on_line, METH_VARARGS, "Print out"}, 285 //{"inside_polygon", inside_polygon, METH_VARARGS, "Print out"}, 286 {"separate_points_by_polygon", separate_points_by_polygon, 287 METH_VARARGS, "Print out"}, 288 {NULL, NULL, 0, NULL} /* sentinel */ 289 }; 290 291 292 293 // Module initialisation 294 void initutil_ext(void){ 295 Py_InitModule("util_ext", MethodTable); 296 297 import_array(); //Necessary for handling of NumPY structures 298 } 299 300 301 302 303 //OBSOLETE 64 304 65 305 int _inside_polygon(int M, // Number of points … … 144 384 145 385 146 // Gateways to Python147 PyObject *point_on_line(PyObject *self, PyObject *args) {148 //149 // point_on_line(x, y, x0, y0, x1, y1)150 //151 152 double x, y, x0, y0, x1, y1;153 int res;154 PyObject *result;155 156 // Convert Python arguments to C157 if (!PyArg_ParseTuple(args, "dddddd", &x, &y, &x0, &y0, &x1, &y1))158 return NULL;159 160 161 // Call underlying routine162 res = _point_on_line(x, y, x0, y0, x1, y1);163 164 // Return values a and b165 result = Py_BuildValue("i", res);166 return result;167 }168 169 170 171 386 PyObject *inside_polygon(PyObject *self, PyObject *args) { 172 387 //def inside_polygon(point, polygon, closed, verbose, one_point): … … 242 457 return Py_BuildValue("i", count); 243 458 } 244 245 246 247 PyObject *gradient(PyObject *self, PyObject *args) {248 //249 // a,b = gradient(x0, y0, x1, y1, x2, y2, q0, q1, q2)250 //251 252 double x0, y0, x1, y1, x2, y2, q0, q1, q2, a, b;253 PyObject *result;254 255 // Convert Python arguments to C256 if (!PyArg_ParseTuple(args, "ddddddddd", &x0, &y0, &x1, &y1, &x2, &y2,257 &q0, &q1, &q2))258 return NULL;259 260 261 // Call underlying routine262 _gradient(x0, y0, x1, y1, x2, y2,263 q0, q1, q2, &a, &b);264 265 // Return values a and b266 result = Py_BuildValue("dd", a, b);267 return result;268 }269 270 271 // Method table for python module272 static struct PyMethodDef MethodTable[] = {273 /* The cast of the function is necessary since PyCFunction values274 * only take two PyObject* parameters, and rotate() takes275 * three.276 */277 278 //{"rotate", (PyCFunction)rotate, METH_VARARGS | METH_KEYWORDS, "Print out"},279 {"gradient", gradient, METH_VARARGS, "Print out"},280 {"point_on_line", point_on_line, METH_VARARGS, "Print out"},281 {"inside_polygon", inside_polygon, METH_VARARGS, "Print out"},282 {NULL, NULL, 0, NULL} /* sentinel */283 };284 285 286 287 // Module initialisation288 void initutil_ext(void){289 Py_InitModule("util_ext", MethodTable);290 291 import_array(); //Necessary for handling of NumPY structures292 }
Note: See TracChangeset
for help on using the changeset viewer.