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
28 # classes representing bounding boxes
33 """class for bounding boxes
35 This variant requires points in the constructor, and is used for internal
38 A bbox for which llx_pt is None represents an empty bbox, i.e., one containing
42 def __init__(self
, llx_pt
, lly_pt
, urx_pt
, ury_pt
):
48 def __nonzero__(self
):
49 return self
.llx_pt
is not None
51 def __add__(self
, other
):
53 if self
.llx_pt
is not None:
54 if other
.llx_pt
is not None:
55 return bbox_pt(min(self
.llx_pt
, other
.llx_pt
), min(self
.lly_pt
, other
.lly_pt
),
56 max(self
.urx_pt
, other
.urx_pt
), max(self
.ury_pt
, other
.ury_pt
))
58 return bbox_pt(self
.llx_pt
, self
.lly_pt
, self
.urx_pt
, self
.ury_pt
)
60 return bbox_pt(other
.llx_pt
, other
.lly_pt
, other
.urx_pt
, other
.ury_pt
)
62 def __iadd__(self
, other
):
63 """join two bboxes inplace"""
64 if self
.llx_pt
is not None:
65 if other
.llx_pt
is not None:
66 self
.llx_pt
= min(self
.llx_pt
, other
.llx_pt
)
67 self
.lly_pt
= min(self
.lly_pt
, other
.lly_pt
)
68 self
.urx_pt
= max(self
.urx_pt
, other
.urx_pt
)
69 self
.ury_pt
= max(self
.ury_pt
, other
.ury_pt
)
71 self
.llx_pt
= other
.llx_pt
72 self
.lly_pt
= other
.lly_pt
73 self
.urx_pt
= other
.urx_pt
74 self
.ury_pt
= other
.ury_pt
77 def __mul__(self
, other
):
78 """return intersection of two bboxes"""
79 if self
.llx_pt
is not None and other
.llx_pt
is not None:
80 return bbox_pt(max(self
.llx_pt
, other
.llx_pt
), max(self
.lly_pt
, other
.lly_pt
),
81 min(self
.urx_pt
, other
.urx_pt
), min(self
.ury_pt
, other
.ury_pt
))
85 def __imul__(self
, other
):
86 """intersect two bboxes in place"""
87 if self
.llx_pt
is not None and other
.llx_pt
is not None:
88 self
.llx_pt
= max(self
.llx_pt
, other
.llx_pt
)
89 self
.lly_pt
= max(self
.lly_pt
, other
.lly_pt
)
90 self
.urx_pt
= min(self
.urx_pt
, other
.urx_pt
)
91 self
.ury_pt
= min(self
.ury_pt
, other
.ury_pt
)
92 elif other
.llx_pt
is None:
97 return bbox_pt(self
.llx_pt
, self
.lly_pt
, self
.urx_pt
, self
.ury_pt
)
100 self
.llx_pt
= other
.llx_pt
101 self
.lly_pt
= other
.lly_pt
102 self
.urx_pt
= other
.urx_pt
103 self
.ury_pt
= other
.ury_pt
105 def lowrestuple_pt(self
):
106 if self
.llx_pt
is None:
107 raise ValueError("Cannot return low-res tuple for empty bbox")
108 return (math
.floor(self
.llx_pt
), math
.floor(self
.lly_pt
),
109 math
.ceil(self
.urx_pt
), math
.ceil(self
.ury_pt
))
111 def highrestuple_pt(self
):
112 if self
.llx_pt
is None:
113 raise ValueError("Cannot return high-res tuple for empty bbox")
114 return (self
.llx_pt
, self
.lly_pt
, self
.urx_pt
, self
.ury_pt
)
116 def intersects(self
, other
):
117 """check, if two bboxes intersect eachother"""
118 if self
.llx_pt
is None or other
.llx_pt
is None:
121 return not (self
.llx_pt
> other
.urx_pt
or
122 self
.lly_pt
> other
.ury_pt
or
123 self
.urx_pt
< other
.llx_pt
or
124 self
.ury_pt
< other
.lly_pt
)
126 def includepoint_pt(self
, x_pt
, y_pt
):
127 if self
.llx_pt
is None:
128 self
.llx_pt
= self
.urx_pt
= x_pt
129 self
.ury_pt
= self
.ury_pt
= y_pt
131 self
.llx_pt
= min(self
.llx_pt
, x_pt
)
132 self
.lly_pt
= min(self
.lly_pt
, y_pt
)
133 self
.urx_pt
= max(self
.urx_pt
, x_pt
)
134 self
.ury_pt
= max(self
.ury_pt
, y_pt
)
136 def transform(self
, trafo
):
137 """transform bbox in place by trafo"""
138 if self
.llx_pt
is None:
140 # we have to transform all four corner points of the bbox
141 llx_pt
, lly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.lly_pt
)
142 lrx_pt
, lry_pt
= trafo
.apply_pt(self
.urx_pt
, self
.lly_pt
)
143 urx_pt
, ury_pt
= trafo
.apply_pt(self
.urx_pt
, self
.ury_pt
)
144 ulx_pt
, uly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.ury_pt
)
146 # Now, by sorting, we obtain the lower left and upper right corner
147 # of the new bounding box.
148 self
.llx_pt
= min(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
)
149 self
.lly_pt
= min(lly_pt
, lry_pt
, ury_pt
, uly_pt
)
150 self
.urx_pt
= max(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
)
151 self
.ury_pt
= max(lly_pt
, lry_pt
, ury_pt
, uly_pt
)
153 def transformed(self
, trafo
):
154 """return bbox transformed by trafo"""
155 if self
.llx_pt
is None:
157 # we have to transform all four corner points of the bbox
158 llx_pt
, lly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.lly_pt
)
159 lrx_pt
, lry_pt
= trafo
.apply_pt(self
.urx_pt
, self
.lly_pt
)
160 urx_pt
, ury_pt
= trafo
.apply_pt(self
.urx_pt
, self
.ury_pt
)
161 ulx_pt
, uly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.ury_pt
)
163 # Now, by sorting, we obtain the lower left and upper right corner
164 # of the new bounding box.
165 return bbox_pt(min(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
), min(lly_pt
, lry_pt
, ury_pt
, uly_pt
),
166 max(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
), max(lly_pt
, lry_pt
, ury_pt
, uly_pt
))
168 def enlarge_pt(self
, all_pt
=0, bottom_pt
=None, left_pt
=None, top_pt
=None, right_pt
=None):
169 """enlarge bbox in place by the given amounts in pts
171 all is used, if bottom, left, top and/or right are not given.
174 if self
.llx_pt
is None:
176 if bottom_pt
is None:
184 self
.llx_pt
-= left_pt
185 self
.lly_pt
-= bottom_pt
186 self
.urx_pt
+= right_pt
187 self
.ury_pt
+= top_pt
189 def enlarged_pt(self
, all_pt
=0, bottom_pt
=None, left_pt
=None, top_pt
=None, right_pt
=None):
190 """return bbox enlarged by the given amounts in pts
192 all is used, if bottom, left, top and/or right are not given.
195 if self
.llx_pt
is None:
197 if bottom_pt
is None:
205 return bbox_pt(self
.llx_pt
-left_pt
, self
.lly_pt
-bottom_pt
, self
.urx_pt
+right_pt
, self
.ury_pt
+top_pt
)
207 def enlarge(self
, all
=0, bottom
=None, left
=None, top
=None, right
=None):
208 """enlarge bbox in place
210 all is used, if bottom, left, top and/or right are not given.
213 if self
.llx_pt
is None:
215 bottom_pt
= left_pt
= top_pt
= right_pt
= unit
.topt(all
)
216 if bottom
is not None:
217 bottom_pt
= unit
.topt(bottom
)
219 left_pt
= unit
.topt(left
)
221 top_pt
= unit
.topt(top
)
222 if right
is not None:
223 right_pt
= unit
.topt(right
)
224 self
.llx_pt
-= left_pt
225 self
.lly_pt
-= bottom_pt
226 self
.urx_pt
+= right_pt
227 self
.ury_pt
+= top_pt
229 def enlarged(self
, all
=0, bottom
=None, left
=None, top
=None, right
=None):
230 """return bbox enlarged
232 all is used, if bottom, left, top and/or right are not given.
235 if self
.llx_pt
is None:
237 bottom_pt
= left_pt
= top_pt
= right_pt
= unit
.topt(all
)
238 if bottom
is not None:
239 bottom_pt
= unit
.topt(bottom
)
241 left_pt
= unit
.topt(left
)
243 top_pt
= unit
.topt(top
)
244 if right
is not None:
245 right_pt
= unit
.topt(right
)
246 return bbox_pt(self
.llx_pt
-left_pt
, self
.lly_pt
-bottom_pt
, self
.urx_pt
+right_pt
, self
.ury_pt
+top_pt
)
249 """return rectangle corresponding to bbox"""
250 if self
.llx_pt
is None:
251 raise ValueError("Cannot return path for empty bbox")
253 return path
.rect_pt(self
.llx_pt
, self
.lly_pt
, self
.urx_pt
-self
.llx_pt
, self
.ury_pt
-self
.lly_pt
)
258 """return height of bbox in pts"""
259 if self
.llx_pt
is None:
260 raise ValueError("Cannot return heigth of empty bbox")
261 return self
.ury_pt
-self
.lly_pt
264 """return width of bbox in pts"""
265 if self
.llx_pt
is None:
266 raise ValueError("Cannot return width of empty bbox")
267 return self
.urx_pt
-self
.llx_pt
270 """return top coordinate of bbox in pts"""
271 if self
.llx_pt
is None:
272 raise ValueError("Cannot return top coordinate of empty bbox")
276 """return bottom coordinate of bbox in pts"""
277 if self
.llx_pt
is None:
278 raise ValueError("Cannot return bottom coordinate of empty bbox")
282 """return left coordinate of bbox in pts"""
283 if self
.llx_pt
is None:
284 raise ValueError("Cannot return left coordinate of empty bbox")
288 """return right coordinate of bbox in pts"""
289 if self
.llx_pt
is None:
290 raise ValueError("Cannot return right coordinate of empty bbox")
294 """return coordinates of the center of the bbox in pts"""
295 if self
.llx_pt
is None:
296 raise ValueError("Cannot return center coordinates of empty bbox")
297 return 0.5 * (self
.llx_pt
+self
.urx_pt
), 0.5 * (self
.lly_pt
+self
.ury_pt
)
300 """return height of bbox"""
301 return self
.height_pt() * unit
.t_pt
304 """return width of bbox"""
305 return self
.width_pt() * unit
.t_pt
308 """return top coordinate of bbox"""
309 return self
.ury_pt
* unit
.t_pt
312 """return bottom coordinate of bbox"""
313 return self
.lly_pt
* unit
.t_pt
316 """return left coordinate of bbox"""
317 return self
.llx_pt
* unit
.t_pt
320 """return right coordinate of bbox"""
321 return self
.urx_pt
* unit
.t_pt
324 """return coordinates of the center of the bbox"""
325 centerx_pt
, centery_pt
= self
.center_pt()
326 return centerx_pt
* unit
.t_pt
, centery_pt
* unit
.t_pt
331 """class for bounding boxes"""
333 def __init__(self
, llx_pt
, lly_pt
, urx_pt
, ury_pt
):
334 llx_pt
= unit
.topt(llx_pt
)
335 lly_pt
= unit
.topt(lly_pt
)
336 urx_pt
= unit
.topt(urx_pt
)
337 ury_pt
= unit
.topt(ury_pt
)
338 bbox_pt
.__init
__(self
, llx_pt
, lly_pt
, urx_pt
, ury_pt
)
341 class empty(bbox_pt
):
343 """empty bounding box, i.e., one containing no point"""
345 bbox_pt
.__init
__(self
, None, None, None, None)