Changeset 6534


Ignore:
Timestamp:
Mar 17, 2009, 5:44:57 PM (16 years ago)
Author:
ole
Message:

Added new function is_inside_triangle. This helps the fitting functions.

Location:
anuga_core/source/anuga/utilities
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • anuga_core/source/anuga/utilities/numerical_tools.py

    r6438 r6534  
    8484    p = num.innerproduct(v1, v2)
    8585    c = num.innerproduct(v1, normal_vector(v2)) # Projection onto normal
    86                                             # (negative cross product)
     86                                                # (negative cross product)
    8787       
    8888    theta = safe_acos(p)
  • anuga_core/source/anuga/utilities/polygon.py

    r6223 r6534  
    186186    return status, value
    187187
    188 
    189 
    190 
     188def is_inside_triangle(point, triangle,
     189                       closed=True,
     190                       rtol=1.0e-6,
     191                       atol=1.0e-8,                     
     192                       check_inputs=True,
     193                       verbose=False):
     194    """Determine if one point is inside a triangle
     195   
     196    This uses the barycentric method:
     197   
     198    Triangle is A, B, C
     199    Point P can then be written as
     200   
     201    P = A + alpha * (C-A) + beta * (B-A)
     202    or if we let
     203    v=P-A, v0=C-A, v1=B-A   
     204   
     205    v = alpha*v0 + beta*v1
     206
     207    Dot this equation by v0 and v1 to get two:
     208   
     209    dot(v0, v) = alpha*dot(v0, v0) + beta*dot(v0, v1)
     210    dot(v1, v) = alpha*dot(v1, v0) + beta*dot(v1, v1)   
     211   
     212    or if a_ij = dot(v_i, v_j) and b_i = dot(v_i, v)
     213    the matrix equation:
     214   
     215    a_00 a_01   alpha     b_0
     216                       =
     217    a_10 a_11   beta      b_1
     218   
     219    Solving for alpha and beta yields:
     220   
     221    alpha = (b_0*a_11 - b_1*a_01)/denom
     222    beta =  (b_1*a_00 - b_0*a_10)/denom
     223   
     224    with denom = a_11*a_00 - a_10*a_01
     225   
     226    The point is in the triangle whenever
     227    alpha and beta and their sums are in the unit interval.
     228   
     229    rtol and atol will determine how close the point has to be to the edge
     230    before it is deemed on the edge.
     231   
     232    """
     233
     234    if check_inputs is True:
     235        triangle = ensure_numeric(triangle)   
     236
     237        point = ensure_numeric(point, num.Float) # Convert point to array of points
     238        msg = 'is_inside_triangle must be invoked with one point only'
     239        assert num.allclose(point.shape, [2]), msg
     240   
     241       
     242    # Quickly reject points that are clearly outside
     243    if point[0] < min(triangle[:,0]): return False
     244    if point[0] > max(triangle[:,0]): return False   
     245    if point[1] < min(triangle[:,1]): return False
     246    if point[1] > max(triangle[:,1]): return False       
     247
     248   
     249    # Start search   
     250    A = triangle[0, :]
     251    B = triangle[1, :]
     252    C = triangle[2, :]
     253   
     254    # Now check if point lies wholly inside triangle
     255    v0 = C-A
     256    v1 = B-A       
     257       
     258    a00 = num.innerproduct(v0, v0)
     259    a10 = a01 = num.innerproduct(v0, v1)
     260    a11 = num.innerproduct(v1, v1)
     261   
     262    denom = a11*a00 - a01*a10
     263   
     264    if abs(denom) > 0.0:
     265        v = point-A
     266        b0 = num.innerproduct(v0, v)       
     267        b1 = num.innerproduct(v1, v)           
     268   
     269        alpha = (b0*a11 - b1*a01)/denom
     270        beta = (b1*a00 - b0*a10)/denom       
     271
     272        if (alpha > 0.0) and (beta > 0.0) and (alpha+beta < 1.0):
     273            return True
     274
     275
     276    if closed is True:
     277        # Check if point lies on one of the edges
     278       
     279        for X, Y in [[A,B], [B,C], [C,A]]:
     280            res = _point_on_line(point[0], point[1],
     281                                 X[0], X[1],
     282                                 Y[0], Y[1],
     283                                 rtol, atol)
     284            if res:
     285                return True
     286               
     287               
     288
     289
     290   
     291
     292   
     293   
     294   
     295def is_inside_polygon_quick(point, polygon, closed=True, verbose=False):
     296    """Determine if one point is inside a polygon
     297    Both point and polygon are assumed to be numeric arrays or lists and
     298    no georeferencing etc or other checks will take place.
     299   
     300    As such it is faster than is_inside_polygon
     301    """
     302
     303    # FIXME(Ole): This function isn't being used
     304    polygon = ensure_numeric(polygon, num.Float)
     305    points = ensure_numeric(point, num.Float) # Convert point to array of points
     306    points = points[num.NewAxis, :]
     307    msg = 'is_inside_polygon must be invoked with one point only'
     308    msg += '\nI got %s and converted to %s' % (str(point), str(points.shape))
     309    assert points.shape[0] == 1 and points.shape[1] == 2, msg
     310
     311   
     312    indices = num.zeros(1, num.Int)
     313
     314    count = _separate_points_by_polygon(points, polygon, indices,
     315                                        int(closed), int(verbose))
     316
     317   
     318    #indices, count = separate_points_by_polygon(points, polygon,
     319    #                                            closed=closed,
     320    #                                            input_checks=False,
     321    #                                            verbose=verbose)
     322
     323    if count > 0:
     324        return True
     325                                               
     326       
     327       
     328   
    191329def is_inside_polygon(point, polygon, closed=True, verbose=False):
    192330    """Determine if one point is inside a polygon
     
    231369        raise msg
    232370
     371    polygon = ensure_absolute(polygon)       
    233372    try:
    234373        polygon = ensure_absolute(polygon)
     
    317456       
    318457
    319 def in_and_outside_polygon(points, polygon, closed = True, verbose = False):
     458def in_and_outside_polygon(points, polygon, closed=True, verbose=False):
    320459    """Determine points inside and outside a polygon
    321460
     
    362501
    363502def separate_points_by_polygon(points, polygon,
    364                                closed = True, verbose = False):
     503                               closed=True,
     504                               check_input=True,
     505                               verbose=False):
    365506    """Determine whether points are inside or outside a polygon
    366507
     
    371512       regarded as belonging to the polygon (closed = True)
    372513       or not (closed = False)
     514       check_input: Allows faster execution if set to False
    373515
    374516    Outputs:
     
    403545    """
    404546
    405 
    406     #if verbose: print 'Checking input to separate_points_by_polygon'
    407 
    408 
    409     #Input checks
    410 
    411     assert isinstance(closed, bool), 'Keyword argument "closed" must be boolean'
    412     assert isinstance(verbose, bool), 'Keyword argument "verbose" must be boolean'
    413 
    414 
    415     try:
    416         points = ensure_numeric(points, num.Float)
    417     except NameError, e:
    418         raise NameError, e
    419     except:
    420         msg = 'Points could not be converted to Numeric array'
    421         raise msg
    422 
    423     #if verbose: print 'Checking input to separate_points_by_polygon 2'
    424     try:
    425         polygon = ensure_numeric(polygon, num.Float)
    426     except NameError, e:
    427         raise NameError, e
    428     except:
    429         msg = 'Polygon could not be converted to Numeric array'
    430         raise msg
    431 
    432     msg = 'Polygon array must be a 2d array of vertices'
    433     assert len(polygon.shape) == 2, msg
    434 
    435     msg = 'Polygon array must have two columns'
    436     assert polygon.shape[1] == 2, msg
    437 
    438 
    439     msg = 'Points array must be 1 or 2 dimensional.'
    440     msg += ' I got %d dimensions' %len(points.shape)
    441     assert 0 < len(points.shape) < 3, msg
    442 
    443 
    444     if len(points.shape) == 1:
    445         # Only one point was passed in.
    446         # Convert to array of points
    447         points = num.reshape(points, (1,2))
    448 
    449    
    450     msg = 'Point array must have two columns (x,y), '
    451     msg += 'I got points.shape[1] == %d' %points.shape[0]
    452     assert points.shape[1] == 2, msg
     547    if check_input:
     548        #Input checks
     549
     550        assert isinstance(closed, bool), 'Keyword argument "closed" must be boolean'
     551        assert isinstance(verbose, bool), 'Keyword argument "verbose" must be boolean'
     552
     553
     554        try:
     555            points = ensure_numeric(points, num.Float)
     556        except NameError, e:
     557            raise NameError, e
     558        except:
     559            msg = 'Points could not be converted to Numeric array'
     560            raise msg
     561
     562        try:
     563            polygon = ensure_numeric(polygon, num.Float)
     564        except NameError, e:
     565            raise NameError, e
     566        except:
     567            msg = 'Polygon could not be converted to Numeric array'
     568            raise msg
     569
     570        msg = 'Polygon array must be a 2d array of vertices'
     571        assert len(polygon.shape) == 2, msg
     572
     573        msg = 'Polygon array must have two columns'
     574        assert polygon.shape[1] == 2, msg
     575
     576
     577        msg = 'Points array must be 1 or 2 dimensional.'
     578        msg += ' I got %d dimensions' %len(points.shape)
     579        assert 0 < len(points.shape) < 3, msg
     580
     581       
     582        if len(points.shape) == 1:
     583            # Only one point was passed in.
     584            # Convert to array of points
     585            points = num.reshape(points, (1,2))
     586
     587   
     588            msg = 'Point array must have two columns (x,y), '
     589            msg += 'I got points.shape[1] == %d' %points.shape[0]
     590            assert points.shape[1] == 2, msg
    453591
    454592       
    455     msg = 'Points array must be a 2d array. I got %s' %str(points[:30])
    456     assert len(points.shape) == 2, msg
    457 
    458     msg = 'Points array must have two columns'
    459     assert points.shape[1] == 2, msg
    460 
    461 
    462     N = polygon.shape[0] #Number of vertices in polygon
    463     M = points.shape[0]  #Number of points
    464 
    465 
    466     indices = num.zeros( M, num.Int )
     593            msg = 'Points array must be a 2d array. I got %s' %str(points[:30])
     594            assert len(points.shape) == 2, msg
     595
     596            msg = 'Points array must have two columns'
     597            assert points.shape[1] == 2, msg
     598
     599
     600    N = polygon.shape[0] # Number of vertices in polygon
     601    M = points.shape[0]  # Number of points
     602
     603    indices = num.zeros(M, num.Int)
    467604
    468605    count = _separate_points_by_polygon(points, polygon, indices,
     
    860997    class Found(exceptions.Exception): pass
    861998
     999    polygon = ensure_numeric(polygon)
     1000   
    8621001    point_in = False
    8631002    while not point_in:
     
    8791018
    8801019                        point = [x_delta, y_delta]
    881                         #print "point",point
    8821020                        if is_inside_polygon(point, polygon, closed=False):
    8831021                            raise Found
  • anuga_core/source/anuga/utilities/polygon_ext.c

    r6189 r6534  
    245245
    246246int __separate_points_by_polygon(int M,     // Number of points
    247                                 int N,     // Number of polygon vertices
    248                                 double* points,
    249                                 double* polygon,
    250                                 long* indices,  // M-Array for storage indices
    251                                 int closed,
    252                                 int verbose) {
     247                                 int N,     // Number of polygon vertices
     248                                 double* points,
     249                                 double* polygon,
     250                                 long* indices,  // M-Array for storage indices
     251                                 int closed,
     252                                 int verbose) {
    253253
    254254  double minpx, maxpx, minpy, maxpy, x, y, px_i, py_i, px_j, py_j, rtol=0.0, atol=0.0;
  • anuga_core/source/anuga/utilities/test_polygon.py

    r6215 r6534  
    195195
    196196
     197    def test_is_inside_polygon_quick(self):
     198        """test_is_inside_polygon_quick
     199       
     200        Test fast version of of is_inside_polygon
     201        """
     202
     203        # Simplest case: Polygon is the unit square
     204        polygon = [[0,0], [1,0], [1,1], [0,1]]
     205
     206        assert is_inside_polygon_quick( (0.5, 0.5), polygon )
     207        assert not is_inside_polygon_quick( (0.5, 1.5), polygon )
     208        assert not is_inside_polygon_quick( (0.5, -0.5), polygon )
     209        assert not is_inside_polygon_quick( (-0.5, 0.5), polygon )
     210        assert not is_inside_polygon_quick( (1.5, 0.5), polygon )
     211
     212        # Try point on borders
     213        assert is_inside_polygon_quick( (1., 0.5), polygon, closed=True)
     214        assert is_inside_polygon_quick( (0.5, 1), polygon, closed=True)
     215        assert is_inside_polygon_quick( (0., 0.5), polygon, closed=True)
     216        assert is_inside_polygon_quick( (0.5, 0.), polygon, closed=True)
     217
     218        assert not is_inside_polygon_quick( (0.5, 1), polygon, closed=False)
     219        assert not is_inside_polygon_quick( (0., 0.5), polygon, closed=False)
     220        assert not is_inside_polygon_quick( (0.5, 0.), polygon, closed=False)
     221        assert not is_inside_polygon_quick( (1., 0.5), polygon, closed=False)
     222
     223
     224       
     225       
    197226    def test_inside_polygon_main(self):
    198227
     
    223252        assert not is_inside_polygon( (0.5, -0.5), polygon )
    224253
     254       
     255        assert is_inside_polygon_quick( (0.5, 0.5), polygon )
     256        assert is_inside_polygon_quick( (1, -0.5), polygon )
     257        assert is_inside_polygon_quick( (1.5, 0), polygon )
     258       
     259        assert not is_inside_polygon_quick( (0.5, 1.5), polygon )
     260        assert not is_inside_polygon_quick( (0.5, -0.5), polygon )               
    225261
    226262        # Very convoluted polygon
     
    406442        assert num.allclose( res, [1,2,3,5,4,0] )       
    407443        assert count == 3
    408        
     444
     445       
     446       
     447    def test_is_inside_triangle(self):
     448
     449
     450        # Simplest case:
     451        triangle = [[0, 0], [1, 0], [0.5, 1]]       
     452
     453        assert is_inside_triangle((0.5, 0.5), triangle)
     454        assert is_inside_triangle((0.9, 0.1), triangle)       
     455        assert not is_inside_triangle((0.5, 1.5), triangle)
     456        assert not is_inside_triangle((0.5, -0.5), triangle)
     457        assert not is_inside_triangle((-0.5, 0.5), triangle)
     458        assert not is_inside_triangle((1.5, 0.5), triangle)
     459
     460        # Try point on borders
     461        assert is_inside_triangle((0.5, 0), triangle, closed=True)
     462        assert is_inside_triangle((1, 0), triangle, closed=True)
     463
     464        assert not is_inside_triangle((0.5, 0), triangle, closed=False)
     465        assert not is_inside_triangle((1, 0), triangle, closed=False)       
     466
     467        # Try vertices
     468        for P in triangle:
     469            assert is_inside_triangle(P, triangle, closed=True)           
     470            assert not is_inside_triangle(P, triangle, closed=False)                       
     471       
     472       
     473        # Slightly different
     474        triangle = [[0, 0.1], [1, -0.2], [0.5, 1]]       
     475        assert is_inside_triangle((0.5, 0.5), triangle)                       
     476        assert is_inside_triangle((0.4, 0.1), triangle)                               
     477        assert not is_inside_triangle((1, 1), triangle)                               
     478       
     479        # Try vertices
     480        for P in triangle:
     481            assert is_inside_triangle(P, triangle, closed=True)           
     482            assert not is_inside_triangle(P, triangle, closed=False)                               
     483           
     484           
     485           
    409486
    410487    def test_populate_polygon(self):
     
    416493        for point in points:
    417494            assert is_inside_polygon(point, polygon)
    418 
    419 
    420         #Very convoluted polygon
     495            assert is_inside_polygon_quick(point, polygon)           
     496
     497
     498        # Very convoluted polygon
    421499        polygon = [[0,0], [10,10], [15,5], [20, 10], [25,0], [30,10], [40,-10]]
    422500
     
    426504        for point in points:
    427505            assert is_inside_polygon(point, polygon)
     506            assert is_inside_polygon_quick(point, polygon)                       
    428507
    429508
     
    17821861#-------------------------------------------------------------
    17831862if __name__ == "__main__":
    1784     suite = unittest.makeSuite(Test_Polygon,'test')
     1863    suite = unittest.makeSuite(Test_Polygon, 'test')
    17851864    runner = unittest.TextTestRunner()
    17861865    runner.run(suite)
Note: See TracChangeset for help on using the changeset viewer.