Changeset 674 for inundation/ga


Ignore:
Timestamp:
Dec 6, 2004, 10:01:54 AM (20 years ago)
Author:
duncan
Message:

alpha_shape from vanessa

Location:
inundation/ga/storm_surge/alpha_shape
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • inundation/ga/storm_surge/alpha_shape/alpha_shape.py

    r580 r674  
    33
    44import exceptions
    5 from Numeric import array, Float
     5from Numeric import array, Float, divide_safe, sqrt
    66from load_mesh.loadASCII import load_xya_file, export_boundary_file
    77
     
    99
    1010OUTPUT_FILE_TITLE = "# The alpha shape boundary defined by point index pairs of edges"
     11INF = pow(10,9)
    1112
    1213def alpha_shape_via_files(point_file, boundary_file, alpha= None):
     
    2526    def __init__(self, points, alpha = None):
    2627
    27        
    28         """ Build interpolation matrix mapping from
    29         function values at vertices to function values at data points
    30 
     28        """
    3129        Inputs:
    3230       
     
    3735        """
    3836        self._set_points(points)
    39         self._alpha_shape_algorithm()
     37        self._alpha_shape_algorithm(alpha)
    4038           
    4139
     
    4341        """
    4442        """
    45        
     43        # print "setting points"
    4644        if len (points) <= 2:
    4745            raise PointError, "Too few points to find an alpha shape"
     
    5048        self.points = array(points).astype(Float)
    5149
    52         if len (points) <= 2:
    53             raise PointError, "Too few points to find an alpha shape"
    54        
    5550   
    5651    def write_boundary(self,file_name):
     
    5853        Write the boundary to a file
    5954        """
    60         #print " this info will be in the file",boundary
     55        #print " this info will be in the file" 
    6156        export_boundary_file(file_name, self.get_boundary(),
    6257                             OUTPUT_FILE_TITLE, delimiter = ',')
     
    6560        """
    6661        """
     62        #print "getting boundary"
    6763        return self.boundary
    68    
    69     def _alpha_shape_algorithm(self):
     64
     65    def get_delaunay(self):
     66        """
     67        """
     68        return self.deltri
     69   
     70           
     71    def _alpha_shape_algorithm(self,alpha):
    7072       
    7173        # A python style guide suggests using _[function name] to
    72         # specify interal functions
    73         #- this is the first time I've used it though - DSG
    74        
     74        # specify internal functions
     75        # - this is the first time I've used it though - DSG
     76
     77        #print "starting alpha shape algorithm"
     78
     79        # Build Delaunay triangulation
     80        import triang
     81        points = []
     82        seglist = []
     83        holelist = []
     84        regionlist = []
     85        pointattlist = []
     86        segattlist = []
     87        trilist = []
     88
     89        points = [(pt[0], pt[1]) for pt in self.points]
     90        pointattlist = [ [] for i in range(len(points)) ]
     91        mode = "Qzcn"
     92        #print "compute delaunay triangulation"
     93        tridata = triang.genMesh(points,seglist,holelist,regionlist,pointattlist,segattlist,trilist,mode)
     94        #print tridata
     95        #print "point attribute list: ", tridata['generatedpointattributelist'],"\n"
     96        #print "hull segments: ", tridata['generatedsegmentlist'], "\n"
     97        self.deltri = tridata['generatedtrianglelist']
     98        self.deltrinbr = tridata['generatedtriangleneighborlist']
     99        self.hulledges = tridata['generatedsegmentlist']                             
     100
     101        #print "deltri: ", self.deltri, "\n"
     102        #print "deltrinbrs: ", self.deltrinbr, "\n"
     103
     104        # Build Alpha table
     105        self._tri_circumradius()
     106        self._edge_intervals()
     107        self._vertex_intervals()
     108
     109        if alpha==None:
     110            # Find optimum alpha
     111            # Ken Clarkson's hull program uses smallest alpha so that
     112            # every vertex is non-singular so...
     113            self.optimum_alpha = max([ interval[0] for interval in self.vertexinterval])
     114            #print "optimum alpha ", self.optimum_alpha
     115
     116            #alpha_tri = self.get_alpha_triangles(self.optimum_alpha)
     117            #print "alpha shape triangles ", alpha_tri
     118
     119            reg_edge = self.get_regular_edges(self.optimum_alpha)
     120            #print "alpha boundary edges", reg_edge
     121        else:
     122            reg_edge = self.get_regular_edges(alpha)
     123   
     124        self.boundary = [self.edge[k] for k in reg_edge]
     125        #print "alpha boundary edges ", self.boundary
     126           
    75127        # this produces a baaad boundary
    76         boundary = []
    77         for point_index in range(len(self.points)-1):
    78             boundary.append([point_index,point_index +1])
    79         boundary.append([len(self.points)-1,0])
    80         self.boundary = boundary
    81        
     128        #boundary = []
     129        #for point_index in range(len(self.points)-1):
     130        #    boundary.append([point_index,point_index +1])
     131        #boundary.append([len(self.points)-1,0])
     132        #self.boundary = boundary
     133        return
     134
     135    def _tri_circumradius(self):
     136
     137        # compute circumradii of the delaunay triangles
     138        x = self.points[:,0]
     139        y = self.points[:,1]
     140        ind1 = [self.deltri[j][0] for j in range(len(self.deltri))]
     141        ind2 = [self.deltri[j][1] for j in range(len(self.deltri))]
     142        ind3 = [self.deltri[j][2] for j in range(len(self.deltri))]
     143
     144        x1 = array([ x[j] for j in ind1 ])
     145        y1 = array([ y[j] for j in ind1 ])
     146        x2 = array([ x[j] for j in ind2 ])
     147        y2 = array([ y[j] for j in ind2 ])
     148        x3 = array([ x[j] for j in ind3 ])
     149        y3 = array([ y[j] for j in ind3 ])
     150
     151        x21 = x2-x1
     152        x31 = x3-x1
     153        y21 = y2-y1
     154        y31 = y3-y1
     155
     156        dist21 = x21*x21 + y21*y21
     157        dist31 = x31*x31 + y31*y31
     158
     159        denom = x21*y31 - x31*y21
     160        #print "denom = ", denom
     161               
     162        # dx/2, dy/2 give circumcenter relative to x1,y1.
     163        #dx = (y31*dist21 - y21*dist31)/denom
     164        #dy = (x21*dist31 - x31*dist21)/denom
     165        # from Numeric import divide_safe
     166        try:
     167            dx = divide_safe(y31*dist21 - y21*dist31,denom)
     168            dy = divide_safe(x21*dist31 - x31*dist21,denom)
     169        except ZeroDivisionError:
     170            print "Found some degenerate triangles."
     171            raise
     172            # really need to take care of cases when denom = 0.
     173            # eg: something like:
     174            # print "Handling degenerate triangles."
     175            # ... add some random displacments to trouble points?
     176           
     177        self.triradius = 0.5*sqrt(dx*dx + dy*dy)
     178        #print "triangle radii", self.triradius
     179        return
     180
     181    def _edge_intervals(self):
     182        # for each edge, find triples
     183        # (length/2, min_adj_triradius, max_adj_triradius) if unattached
     184        # (min_adj_triradius, min_adj_triradius, max_adj_triradius) if attached.
     185
     186        # It should be possible to rewrite this routine in an array-friendly form
     187        # like _tri_circumradius()  if we need to speed things up.
     188
     189        edges = []
     190        edgenbrs = []
     191        edgeinterval = []
     192        for t in range(len(self.deltri)):
     193            tri = self.deltri[t]
     194            trinbr = self.deltrinbr[t]
     195            dx = array([self.points[tri[(i+1)%3],0] - self.points[tri[(i+2)%3],0] for i in [0,1,2]])
     196            dy = array([self.points[tri[(i+1)%3],1] - self.points[tri[(i+2)%3],1] for i in [0,1,2]])
     197            elen = sqrt(dx*dx+dy*dy)
     198            #angle = array([(-dx[(i+1)%3]*dx[(i+2)%3]-dy[(i+1)%3]*dy[(i+2)%3])/(elen[(i+1)%3]*elen[(i+2)%3]) for i in [0,1,2]])
     199            #angle = arccos(angle)
     200            # really only need sign - not angle value:
     201            anglesign = array([(-dx[(i+1)%3]*dx[(i+2)%3]-dy[(i+1)%3]*dy[(i+2)%3]) for i in [0,1,2]])
     202           
     203            #print "dx ",dx,"\n"
     204            #print "dy ",dy,"\n"
     205            #print "edge lengths of triangle ",t,"\t",elen,"\n"
     206            #print "angles ",angle,"\n"
     207                         
     208            for i in [0,1,2]:
     209                j = (i+1)%3
     210                k = (i+2)%3
     211                if trinbr[i]==-1:
     212                    edges.append((tri[j], tri[k]))
     213                    edgenbrs.append((t, -1))
     214                    edgeinterval.append([0.5*elen[i], self.triradius[t], INF])
     215                elif (tri[j]<tri[k]):
     216                    edges.append((tri[j], tri[k]))
     217                    edgenbrs.append((t, trinbr[i]))
     218                    edgeinterval.append([0.5*elen[i],\
     219                                         min(self.triradius[t],self.triradius[trinbr[i]]),\
     220                                             max(self.triradius[t],self.triradius[trinbr[i]]) ])
     221                else:
     222                    continue
     223                #if angle[i]>pi/2:
     224                if anglesign[i] < 0:
     225                    edgeinterval[-1][0] = edgeinterval[-1][1]
     226                   
     227        self.edge = edges
     228        self.edgenbr = edgenbrs
     229        self.edgeinterval = edgeinterval
     230        #print "edges: ",edges, "\n"
     231        #print "edge nbrs:", edgenbrs ,"\n"
     232        #print "edge intervals: ",edgeinterval , "\n"
     233
     234    def _vertex_intervals(self):
     235        # for each vertex find pairs
     236        # (min_adj_triradius, max_adj_triradius)
     237        nv = len(self.points)
     238        vertexnbrs = [ [] for i in range(nv)]
     239        vertexinterval = [ [] for i in range(nv)]
     240        for t in range(len(self.deltri)):
     241            for j in self.deltri[t]:
     242                vertexnbrs[j].append(t)
     243        for h in range(len(self.hulledges)):
     244            for j in self.hulledges[h]:
     245                vertexnbrs[j].append(-1)
     246       
     247        for i in range(nv):
     248            radii = [ self.triradius[t] for t in vertexnbrs[i] if t>=0 ]
     249            vertexinterval[i] = [min(radii), max(radii)]
     250            if vertexnbrs[i][-1]==-1:
     251                vertexinterval[i][1]=INF
     252
     253        self.vertexnbr = vertexnbrs
     254        self.vertexinterval = vertexinterval
     255        #print "vertex nbrs ", vertexnbrs, "\n"
     256        #print "vertex intervals ",vertexinterval, "\n"
     257       
     258    def get_alpha_triangles(self,alpha):
     259        """
     260        Given an alpha value,
     261        return indices of triangles in the alpha-shape
     262        """
     263        def tri_rad_lta(k):
     264            return self.triradius[k]<=alpha
     265
     266        return filter(tri_rad_lta, range(len(self.triradius)))
     267
     268    def get_regular_edges(self,alpha):
     269        """
     270        Given and alpha value,
     271        return the edges on the boundary of the alpha-shape
     272        """
     273        def reg_edge(k):
     274            return self.edgeinterval[k][1]<=alpha and self.edgeinterval[k][2]>alpha
     275
     276        return filter(reg_edge, range(len(self.edgeinterval)))
     277
     278       
     279   
    82280#-------------------------------------------------------------
    83281if __name__ == "__main__":
     
    88286
    89287    usage: alpha_shape.py point_file.xya boundary_file.bnd [alpha]
    90 
     288   
    91289    The alpha value is optional.
    92290    """
    93291   
    94292    import os, sys
    95     usage = "usage: %s point_file.xya boundary_file.bnd [alpha]"\
    96             %os.path.basename(sys.argv[0])
     293    usage = "usage: %s point_file.xya boundary_file.bnd [alpha]"%os.path.basename(sys.argv[0])
    97294    # I made up the .bnd affix. Other ideas welcome. -DSG
    98295    if len(sys.argv) < 3:
     
    105302        else:
    106303            alpha = None
     304
    107305        alpha_shape_via_files(point_file, boundary_file, alpha)
    108306       
  • inundation/ga/storm_surge/alpha_shape/test_alpha_shape.py

    r579 r674  
    1717        pass
    1818
     19    def test_delaunay(self):
     20        #print "test_delaunay"
     21        a = [0.0, 0.0]
     22        b = [1.0, 0.0]
     23        c = [2.0, 0.0]
     24        d = [2.0, 2.0]
     25        e = [1.0, 2.0]
     26        f = [0.0, 2.0]
    1927
    20     def test_alpha(self):
     28        alpha = Alpha_Shape([a,b,c,d,e,f])
     29        result = alpha.get_delaunay()
     30        answer = [(0, 1, 5), (5, 1, 4), (4, 2, 3), (2, 4, 1)]
     31        # print "result",result
     32        assert allclose(answer, result)
     33
     34
     35    def test_alpha_1(self):
     36        #print "test_alpha"
    2137        a = [0.0, 0.0]
    2238        b = [1.0, 0.0]
     
    2844        alpha = Alpha_Shape([a,b,c,d,e,f])
    2945        result = alpha.get_boundary()
    30         answer = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 0]]
    3146        #print "result",result
     47        answer = [(5, 0), (0, 1), (4, 5), (2, 3), (3, 4), (1, 2)]
    3248        assert allclose(answer, result)
     49
     50
     51    def test_alpha_2(self):
     52        #print "test_alpha"
     53        a = [0.0, 0.0]
     54        b = [2.0, 0.0]
     55        c = [4.0, 0.0]
     56        cd = [3.0,2.0]
     57        d = [4.0, 4.0]
     58        e = [2.0, 4.0]
     59        f = [0.0, 4.0]
     60
     61        alpha = Alpha_Shape([a,b,c,cd,d,e,f])
     62        result = alpha.get_boundary()
     63        #print "result",result
     64        answer = [(0, 3), (3, 6), (0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]
     65        assert allclose(answer, result)
     66
     67
     68    def test_alpha_3(self):
     69        #print "test_alpha"
     70        a = [0.0, 0.0]
     71        b = [1.0, 0.0]
     72        c = [2.0, 0.0]
     73        d = [2.0, 2.0]
     74        e = [1.0, 2.0]
     75        f = [0.0, 2.0]
     76        alf = 1.5
     77
     78        alpha = Alpha_Shape([a,b,c,d,e,f],alf)
     79        result = alpha.get_boundary()
     80        #print "result",result
     81        answer = [(5, 0), (0, 1), (4, 5), (2, 3), (3, 4), (1, 2)]
     82        assert allclose(answer, result)
     83
    3384
    3485       
    3586    def test_not_enough_points(self):
     87        #print "test_not_enought_points"
    3688        a = [0.0, 0.0]
    3789        b = [1.0, 0.0]
     
    4799
    48100    def test_alpha_stand_alone(self):
     101        #print "test_alpha_stand_alone"
    49102        import os
    50103        import tempfile
    51        
     104
    52105        fileName = tempfile.mktemp(".xya")
     106        #(h,fileName) = tempfile.mkstemp(".xya")
    53107        file = open(fileName,"w")
    54108        file.write("title row \n\
     
    62116
    63117        output_file_name = tempfile.mktemp(".bnd")
    64         command = 'python .\\alpha_shape.py ' + fileName + ' ' + output_file_name
     118        #(ho, output_file_name) = tempfile.mkstemp(".bnd")
     119        command = 'python alpha_shape.py ' + fileName + ' ' + output_file_name
    65120        #print "command", command
    66121        os.system(command)
     
    71126        file.close()
    72127        os.remove(output_file_name)
    73         self.failUnless(lFile[1] == "0,1" and
    74                         lFile[2] == "1,2" and
    75                         lFile[3] == "2,3" and
    76                         lFile[4] == "3,4" and
    77                         lFile[5] == "4,5" and
    78                         lFile[6] == "5,0" and
     128        self.failUnless(lFile[1] == "5,0" and
     129                        lFile[2] == "0,1" and
     130                        lFile[3] == "4,5" and
     131                        lFile[4] == "2,3" and
     132                        lFile[5] == "3,4" and
     133                        lFile[6] == "1,2" and
    79134                        lFile[7] == ""
    80135                        ,
     
    84139#-------------------------------------------------------------
    85140if __name__ == "__main__":
     141    #print "starting tests \n"
    86142    suite = unittest.makeSuite(TestCase,'test')
    87143    runner = unittest.TextTestRunner(verbosity=1)
    88144    runner.run(suite)
     145    #print "finished tests \n"
     146   
    89147
    90148   
Note: See TracChangeset for help on using the changeset viewer.