add a set method to the bbox
[PyX.git] / pyx / bbox.py
blob5baa6fa35be15dce4916c3fe5e9347df67f8e437
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2006 Jörg Lehmann <joergl@users.sourceforge.net>
6 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
8 # This file is part of PyX (http://pyx.sourceforge.net/).
10 # PyX is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2 of the License, or
13 # (at your option) any later version.
15 # PyX is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with PyX; if not, write to the Free Software
22 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 import math
25 import unit
28 # classes representing bounding boxes
31 class bbox_pt:
33 """class for bounding boxes
35 This variant requires points in the constructor, and is used for internal
36 purposes.
38 A bbox for which llx_pt is None represents an empty bbox, i.e., one containing
39 no points.
40 """
42 def __init__(self, llx_pt, lly_pt, urx_pt, ury_pt):
43 self.llx_pt = llx_pt
44 self.lly_pt = lly_pt
45 self.urx_pt = urx_pt
46 self.ury_pt = ury_pt
48 def __add__(self, other):
49 """join two bboxes"""
50 if self.llx_pt is not None:
51 if other.llx_pt is not None:
52 return bbox_pt(min(self.llx_pt, other.llx_pt), min(self.lly_pt, other.lly_pt),
53 max(self.urx_pt, other.urx_pt), max(self.ury_pt, other.ury_pt))
54 else:
55 return bbox_pt(self.llx_pt, self.lly_pt, self.urx_pt, self.ury_pt)
56 else:
57 return bbox_pt(other.llx_pt, other.lly_pt, other.urx_pt, other.ury_pt)
59 def __iadd__(self, other):
60 """join two bboxes inplace"""
61 if self.llx_pt is not None:
62 if other.llx_pt is not None:
63 self.llx_pt = min(self.llx_pt, other.llx_pt)
64 self.lly_pt = min(self.lly_pt, other.lly_pt)
65 self.urx_pt = max(self.urx_pt, other.urx_pt)
66 self.ury_pt = max(self.ury_pt, other.ury_pt)
67 else:
68 self.llx_pt = other.llx_pt
69 self.lly_pt = other.lly_pt
70 self.urx_pt = other.urx_pt
71 self.ury_pt = other.ury_pt
72 return self
74 def __mul__(self, other):
75 """return intersection of two bboxes"""
76 if self.llx_pt is not None and other.llx_pt is not None:
77 return bbox_pt(max(self.llx_pt, other.llx_pt), max(self.lly_pt, other.lly_pt),
78 min(self.urx_pt, other.urx_pt), min(self.ury_pt, other.ury_pt))
79 else:
80 return empty()
82 def __imul__(self, other):
83 """intersect two bboxes in place"""
84 if self.llx_pt is not None and other.llx_pt is not None:
85 self.llx_pt = max(self.llx_pt, other.llx_pt)
86 self.lly_pt = max(self.lly_pt, other.lly_pt)
87 self.urx_pt = min(self.urx_pt, other.urx_pt)
88 self.ury_pt = min(self.ury_pt, other.ury_pt)
89 elif other.llx_pt is None:
90 self.llx_pt = None
91 return self
93 def copy(self):
94 return bbox_pt(self.llx_pt, self.lly_pt, self.urx_pt, self.ury_pt)
96 def set(self, other):
97 self.llx_pt = other.llx_pt
98 self.lly_pt = other.lly_pt
99 self.urx_pt = other.urx_pt
100 self.ury_pt = other.ury_pt
102 def lowrestuple_pt(self):
103 if self.llx_pt is None:
104 raise ValueError("Cannot return low-res tuple for empty bbox")
105 return (math.floor(self.llx_pt), math.floor(self.lly_pt),
106 math.ceil(self.urx_pt), math.ceil(self.ury_pt))
108 def highrestuple_pt(self):
109 if self.llx_pt is None:
110 raise ValueError("Cannot return high-res tuple for empty bbox")
111 return (self.llx_pt, self.lly_pt, self.urx_pt, self.ury_pt)
113 def intersects(self, other):
114 """check, if two bboxes intersect eachother"""
115 if self.llx_pt is None or other.llx_pt is None:
116 return 0
117 else:
118 return not (self.llx_pt > other.urx_pt or
119 self.lly_pt > other.ury_pt or
120 self.urx_pt < other.llx_pt or
121 self.ury_pt < other.lly_pt)
123 def includepoint_pt(self, x_pt, y_pt):
124 if self.llx_pt is None:
125 self.llx_pt = self.urx_pt = x_pt
126 self.ury_pt = self.ury_pt = y_pt
127 else:
128 self.llx_pt = min(self.llx_pt, x_pt)
129 self.lly_pt = min(self.lly_pt, y_pt)
130 self.urx_pt = max(self.urx_pt, x_pt)
131 self.ury_pt = max(self.ury_pt, y_pt)
133 def transform(self, trafo):
134 """transform bbox in place by trafo"""
135 if self.llx_pt is None:
136 return
137 # we have to transform all four corner points of the bbox
138 llx_pt, lly_pt = trafo.apply_pt(self.llx_pt, self.lly_pt)
139 lrx_pt, lry_pt = trafo.apply_pt(self.urx_pt, self.lly_pt)
140 urx_pt, ury_pt = trafo.apply_pt(self.urx_pt, self.ury_pt)
141 ulx_pt, uly_pt = trafo.apply_pt(self.llx_pt, self.ury_pt)
143 # Now, by sorting, we obtain the lower left and upper right corner
144 # of the new bounding box.
145 self.llx_pt = min(llx_pt, lrx_pt, urx_pt, ulx_pt)
146 self.lly_pt = min(lly_pt, lry_pt, ury_pt, uly_pt)
147 self.urx_pt = max(llx_pt, lrx_pt, urx_pt, ulx_pt)
148 self.ury_pt = max(lly_pt, lry_pt, ury_pt, uly_pt)
150 def transformed(self, trafo):
151 """return bbox transformed by trafo"""
152 if self.llx_pt is None:
153 return empty()
154 # we have to transform all four corner points of the bbox
155 llx_pt, lly_pt = trafo.apply_pt(self.llx_pt, self.lly_pt)
156 lrx_pt, lry_pt = trafo.apply_pt(self.urx_pt, self.lly_pt)
157 urx_pt, ury_pt = trafo.apply_pt(self.urx_pt, self.ury_pt)
158 ulx_pt, uly_pt = trafo.apply_pt(self.llx_pt, self.ury_pt)
160 # Now, by sorting, we obtain the lower left and upper right corner
161 # of the new bounding box.
162 return bbox_pt(min(llx_pt, lrx_pt, urx_pt, ulx_pt), min(lly_pt, lry_pt, ury_pt, uly_pt),
163 max(llx_pt, lrx_pt, urx_pt, ulx_pt), max(lly_pt, lry_pt, ury_pt, uly_pt))
165 def enlarge_pt(self, all_pt=0, bottom_pt=None, left_pt=None, top_pt=None, right_pt=None):
166 """enlarge bbox in place by the given amounts in pts
168 all is used, if bottom, left, top and/or right are not given.
171 if self.llx_pt is None:
172 return
173 if bottom_pt is None:
174 bottom_pt = all_pt
175 if left_pt is None:
176 left_pt = all_pt
177 if top_pt is None:
178 top_pt = all_pt
179 if right_pt is None:
180 right_pt = all_pt
181 self.llx_pt -= left_pt
182 self.lly_pt -= bottom_pt
183 self.urx_pt += right_pt
184 self.ury_pt += top_pt
186 def enlarged_pt(self, all_pt=0, bottom_pt=None, left_pt=None, top_pt=None, right_pt=None):
187 """return bbox enlarged by the given amounts in pts
189 all is used, if bottom, left, top and/or right are not given.
192 if self.llx_pt is None:
193 return empty()
194 if bottom_pt is None:
195 bottom_pt = all_pt
196 if left_pt is None:
197 left_pt = all_pt
198 if top_pt is None:
199 top_pt = all_pt
200 if right_pt is None:
201 right_pt = all_pt
202 return bbox_pt(self.llx_pt-left_pt, self.lly_pt-bottom_pt, self.urx_pt+right_pt, self.ury_pt+top_pt)
204 def enlarge(self, all=0, bottom=None, left=None, top=None, right=None):
205 """enlarge bbox in place
207 all is used, if bottom, left, top and/or right are not given.
210 if self.llx_pt is None:
211 return
212 bottom_pt = left_pt = top_pt = right_pt = unit.topt(all)
213 if bottom is not None:
214 bottom_pt = unit.topt(bottom)
215 if left is not None:
216 left_pt = unit.topt(left)
217 if top is not None:
218 top_pt = unit.topt(top)
219 if right is not None:
220 right_pt = unit.topt(right)
221 self.llx_pt -= left_pt
222 self.lly_pt -= bottom_pt
223 self.urx_pt += right_pt
224 self.ury_pt += top_pt
226 def enlarged(self, all=0, bottom=None, left=None, top=None, right=None):
227 """return bbox enlarged
229 all is used, if bottom, left, top and/or right are not given.
232 if self.llx_pt is None:
233 return empty()
234 bottom_pt = left_pt = top_pt = right_pt = unit.topt(all)
235 if bottom is not None:
236 bottom_pt = unit.topt(bottom)
237 if left is not None:
238 left_pt = unit.topt(left)
239 if top is not None:
240 top_pt = unit.topt(top)
241 if right is not None:
242 right_pt = unit.topt(right)
243 return bbox_pt(self.llx_pt-left_pt, self.lly_pt-bottom_pt, self.urx_pt+right_pt, self.ury_pt+top_pt)
245 def rect(self):
246 """return rectangle corresponding to bbox"""
247 if self.llx_pt is None:
248 raise ValueError("Cannot return path for empty bbox")
249 import path
250 return path.rect_pt(self.llx_pt, self.lly_pt, self.urx_pt-self.llx_pt, self.ury_pt-self.lly_pt)
252 path = rect
254 def height_pt(self):
255 """return height of bbox in pts"""
256 if self.llx_pt is None:
257 raise ValueError("Cannot return heigth of empty bbox")
258 return self.ury_pt-self.lly_pt
260 def width_pt(self):
261 """return width of bbox in pts"""
262 if self.llx_pt is None:
263 raise ValueError("Cannot return width of empty bbox")
264 return self.urx_pt-self.llx_pt
266 def top_pt(self):
267 """return top coordinate of bbox in pts"""
268 if self.llx_pt is None:
269 raise ValueError("Cannot return top coordinate of empty bbox")
270 return self.ury_pt
272 def bottom_pt(self):
273 """return bottom coordinate of bbox in pts"""
274 if self.llx_pt is None:
275 raise ValueError("Cannot return bottom coordinate of empty bbox")
276 return self.lly_pt
278 def left_pt(self):
279 """return left coordinate of bbox in pts"""
280 if self.llx_pt is None:
281 raise ValueError("Cannot return left coordinate of empty bbox")
282 return self.llx_pt
284 def right_pt(self):
285 """return right coordinate of bbox in pts"""
286 if self.llx_pt is None:
287 raise ValueError("Cannot return right coordinate of empty bbox")
288 return self.urx_pt
290 def center_pt(self):
291 """return coordinates of the center of the bbox in pts"""
292 if self.llx_pt is None:
293 raise ValueError("Cannot return center coordinates of empty bbox")
294 return 0.5 * (self.llx_pt+self.urx_pt), 0.5 * (self.lly_pt+self.ury_pt)
296 def height(self):
297 """return height of bbox"""
298 return self.height_pt() * unit.t_pt
300 def width(self):
301 """return width of bbox"""
302 return self.width_pt() * unit.t_pt
304 def top(self):
305 """return top coordinate of bbox"""
306 return self.ury_pt * unit.t_pt
308 def bottom(self):
309 """return bottom coordinate of bbox"""
310 return self.lly_pt * unit.t_pt
312 def left(self):
313 """return left coordinate of bbox"""
314 return self.llx_pt * unit.t_pt
316 def right(self):
317 """return right coordinate of bbox"""
318 return self.urx_pt * unit.t_pt
320 def center(self):
321 """return coordinates of the center of the bbox"""
322 centerx_pt, centery_pt = self.center_pt()
323 return centerx_pt * unit.t_pt, centery_pt * unit.t_pt
326 class bbox(bbox_pt):
328 """class for bounding boxes"""
330 def __init__(self, llx_pt, lly_pt, urx_pt, ury_pt):
331 llx_pt = unit.topt(llx_pt)
332 lly_pt = unit.topt(lly_pt)
333 urx_pt = unit.topt(urx_pt)
334 ury_pt = unit.topt(ury_pt)
335 bbox_pt.__init__(self, llx_pt, lly_pt, urx_pt, ury_pt)
338 class empty(bbox_pt):
340 """empty bounding box, i.e., one containing no point"""
341 def __init__(self):
342 bbox_pt.__init__(self, None, None, None, None)