source: trunk/anuga_core/source/anuga/geometry/quad.py @ 9516

Last change on this file since 9516 was 7872, checked in by hudson, 14 years ago

Fixed a few unit test errors.

File size: 6.4 KB
Line 
1"""quad.py - quad tree data structure for fast indexing of regions in the plane.
2
3This generic structure can be used to store any geometry in a quadtree.
4It is naive, and does not exploit any coherency - it merely tests a point
5against all bounding boxes in its heirarchy.
6
7It returns a list of bounding boxes which intersect with the test point, which
8may then be iterated over with a proper intersection test to detect actual
9geometry intersections.
10
11As of June 2010 this module has a pylint quality rating of 10/10.
12
13"""
14
15import anuga.utilities.log as log
16
17           
18class Cell():
19    """ One cell in the plane.
20        A cell is defined by an AABB, and can have smaller AABB children.
21        The children can be rapidly searched for intersections in log(n) time.
22    """
23 
24    def __init__(self, extents, parent,
25         name = 'cell'):
26        """ Construct a new cell.
27            extents is an AABB defining a region on the plane.
28            parent is the node above this one, or None if it is root.
29        """
30   
31        self.extents = extents
32        self.parent = parent
33       
34        # The points in this cell     
35        self.leaves = []
36        self.children = None
37       
38   
39    def __repr__(self):
40        """ String representation of the quadtree. """
41        ret_str = '%s: leaves: %d' \
42               % (self.name , len(self.leaves))   
43        if self.children:
44            ret_str += ', children: %d' % (len(self.children))
45        return ret_str
46
47
48    def insert(self, new_leaf):
49        """ Insert a leaf into the quadtree.
50            new_leaf is a tuple of (AABB extents, data), where data can
51                     be any user data (geometry, triangle index, etc.).
52        """
53        if type(new_leaf)==type(list()):
54            for leaf in new_leaf:
55                self.insert_item(leaf)
56        else:
57            self.insert_item(new_leaf)
58
59
60    def insert_item(self, new_leaf):   
61        """ Internal recursive insert a single item.
62            new_leaf is a tuple of (AABB extents, data), where data can
63                     be any user data (geometry, triangle index, etc.).       
64        """
65        new_region, _ = new_leaf
66       
67        # recurse down to any children until we get an intersection
68        if self.children:
69            for child in self.children:
70                if child.extents.is_trivial_in(new_region):
71                    child.insert_item(new_leaf)
72                    return
73        else:           
74            # try splitting this cell and see if we get a trivial in
75            subregion1, subregion2 = self.extents.split()
76           
77            # option 1 - try splitting 4 ways
78            #subregion11, subregion12 = subregion1.split()   
79            #subregion21, subregion22 = subregion2.split()
80            #regions = [subregion11, subregion12, subregion21, subregion22]
81            #for region in regions:
82                #if region.is_trivial_in(new_region):
83                    #self.children = [Cell(x, parent=self) for x in regions]
84                    #self.insert_item(new_leaf)
85                    #return               
86
87            # option 2 - try splitting 2 ways - no performance difference
88            # noticed in practise between this and the above option.
89            if subregion1.is_trivial_in(new_region):
90                self.children = [Cell(subregion1, self), \
91                                 Cell(subregion2, self)]   
92                self.children[0].insert_item(new_leaf)
93                return
94            elif subregion2.is_trivial_in(new_region):
95                self.children = [Cell(subregion1, self), \
96                                 Cell(subregion2, self)]   
97                self.children[1].insert_item(new_leaf)
98                return               
99   
100        # recursion ended without finding a fit, so attach it as a leaf
101        self.leaves.append(new_leaf)
102       
103     
104    def retrieve(self):
105        """Get all leaves from this tree.
106           return a traversal of the entire tree.
107        """
108       
109        leaves_found = list(self.leaves)
110       
111        if not self.children:
112            return leaves_found
113
114        for child in self.children:
115            leaves_found.extend(child.retrieve())
116           
117        return leaves_found
118
119    def count(self):
120        """Count all leaves from this tree.
121           return num of leaves in the tree.
122        """
123       
124        leaves_found = len(self.leaves)
125       
126        if not self.children:
127            return leaves_found
128
129        for child in self.children:
130            leaves_found += child.count()
131           
132        return leaves_found       
133
134    def show(self, depth=0):
135        """Traverse tree below self, dumping all information.
136        """
137        if depth == 0:
138            log.critical() 
139        print '%s%s'  % ('  '*depth, self.name), self.extents, ' [', \
140            self.leaves, ']'
141        if self.children:
142            log.critical()
143            for child in self.children:
144                child.show(depth+1)
145
146    def search(self, point):
147        """
148            Search the tree for intersection with leaves
149            point is a test point.
150            return a list of possible intersections with geometry.
151        """
152        intersecting_regions = self.test_leaves(point)
153       
154        # recurse down into nodes that the point passes through
155        if self.children:
156            for child in self.children:   
157                if child.extents.contains(point):
158                    intersecting_regions.extend(child.search(point))
159             
160        return intersecting_regions
161 
162 
163    def test_leaves(self, point):
164        """ Test all leaves on this node to see if they intersect x.
165            Does not recurse into children.
166            x is a point to test
167            return a list of leaves that intersect x
168        """
169        intersecting_regions = []
170       
171        # test all leaves to see if they intersect the point
172        for leaf in self.leaves:
173            aabb, data = leaf
174            if aabb.contains(point):
175                intersecting_regions.append([data, self])
176               
177        return intersecting_regions               
178 
179 
180    def get_siblings(self):
181        """ return siblings of this node. If there is no parent, it
182                   returns an empty list.
183        """
184        if not self.parent:
185            return []
186         
187        siblings = list(self.parent.children)
188        siblings.remove(self)
189        return siblings
190               
Note: See TracBrowser for help on using the repository browser.