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

Last change on this file since 7866 was 7866, checked in by hudson, 13 years ago

More swb tests passing. Cleaned up some pylint errors.

File size: 6.1 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    """class Cell
20
21    One cell in the plane.
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        ret_str = '%s: leaves: %d' \
41               % (self.name , len(self.leaves))   
42        if self.children:
43            ret_str += ', children: %d' % (len(self.children))
44        return ret_str
45
46
47    def insert(self, new_leaf):
48        """ Insert a leaf into the quadtree.
49            new_leaf is a tuple of (AABB extents, data), where data can
50                     be any user data (geometry, triangle index, etc.).
51        """
52        if type(new_leaf)==type(list()):
53            for leaf in new_leaf:
54                self.insert_item(leaf)
55        else:
56            self.insert_item(new_leaf)
57
58
59    def insert_item(self, new_leaf):   
60        """ Internal recursive insert a single item.
61            new_leaf is a tuple of (AABB extents, data), where data can
62                     be any user data (geometry, triangle index, etc.).       
63        """
64        new_region, _ = new_leaf
65       
66        # recurse down to any children until we get an intersection
67        if self.children:
68            for child in self.children:
69                if child.extents.is_trivial_in(new_region):
70                    child.insert_item(new_leaf)
71                    return
72        else:           
73            # try splitting this cell and see if we get a trivial in
74            subregion1, subregion2 = self.extents.split()
75           
76            # option 1 - try splitting 4 ways
77            #subregion11, subregion12 = subregion1.split()   
78            #subregion21, subregion22 = subregion2.split()
79            #regions = [subregion11, subregion12, subregion21, subregion22]
80            #for region in regions:
81                #if region.is_trivial_in(new_region):
82                    #self.children = [Cell(x, parent=self) for x in regions]
83                    #self.insert_item(new_leaf)
84                    #return               
85
86            # option 2 - try splitting 2 ways - no diff noticed in practise
87            if subregion1.is_trivial_in(new_region):
88                self.children = [Cell(subregion1, self), \
89                                 Cell(subregion2, self)]   
90                self.children[0].insert_item(new_leaf)
91                return
92            elif subregion2.is_trivial_in(new_region):
93                self.children = [Cell(subregion1, self), \
94                                 Cell(subregion2, self)]   
95                self.children[1].insert_item(new_leaf)
96                return               
97   
98        # recursion ended without finding a fit, so attach it as a leaf
99        self.leaves.append(new_leaf)
100       
101     
102    def retrieve(self):
103        """Get all leaves from this tree.
104           return a traversal of the entire tree.
105        """
106       
107        leaves_found = list(self.leaves)
108       
109        if not self.children:
110            return leaves_found
111
112        for child in self.children:
113            leaves_found.extend(child.retrieve())
114           
115        return leaves_found
116
117    def count(self):
118        """Count all leaves from this tree.
119           return num of leaves in the tree.
120        """
121       
122        leaves_found = len(self.leaves)
123       
124        if not self.children:
125            return leaves_found
126
127        for child in self.children:
128            leaves_found += child.count()
129           
130        return leaves_found       
131
132    def show(self, depth=0):
133        """Traverse tree below self, dumping all information.
134        """
135        if depth == 0:
136            log.critical() 
137        print '%s%s'  % ('  '*depth, self.name), self.extents, ' [', \
138            self.leaves, ']'
139        if self.children:
140            log.critical()
141            for child in self.children:
142                child.show(depth+1)
143
144    def search(self, point):
145        """
146            Search the tree for intersection with leaves
147            point is a test point.
148            return a list of possible intersections with geometry.
149        """
150        intersecting_regions = self.test_leaves(point)
151       
152        # recurse down into nodes that the point passes through
153        if self.children:
154            for child in self.children:   
155                if child.extents.contains(point):
156                    intersecting_regions.extend(child.search(point))
157             
158        return intersecting_regions
159 
160 
161    def test_leaves(self, point):
162        """ Test all leaves to see if they intersect x.
163            x is a point to test
164            return a list of leaves that intersect x
165        """
166        intersecting_regions = []
167       
168        # test all leaves to see if they intersect the point
169        for leaf in self.leaves:
170            aabb, data = leaf
171            if aabb.contains(point):
172                intersecting_regions.append([data, self])
173               
174        return intersecting_regions               
175 
176 
177    def get_siblings(self):
178        """ return siblings of this node. If there is no parent, it
179                   returns an empty list.
180        """
181        if not self.parent:
182            return []
183         
184        siblings = list(self.parent.children)
185        siblings.remove(self)
186        return siblings
187               
Note: See TracBrowser for help on using the repository browser.