1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2002-2006 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2002-2004 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
27 # classes representing bounding boxes
32 """class for bounding boxes
34 This variant requires points in the constructor, and is used for internal
37 A bbox for which llx_pt is None represents an empty bbox, i.e., one containing
41 def __init__(self
, llx_pt
, lly_pt
, urx_pt
, ury_pt
):
47 def __nonzero__(self
):
48 return self
.llx_pt
is not None
50 def __add__(self
, other
):
52 if self
.llx_pt
is not None:
53 if other
.llx_pt
is not None:
54 return bbox_pt(min(self
.llx_pt
, other
.llx_pt
), min(self
.lly_pt
, other
.lly_pt
),
55 max(self
.urx_pt
, other
.urx_pt
), max(self
.ury_pt
, other
.ury_pt
))
57 return bbox_pt(self
.llx_pt
, self
.lly_pt
, self
.urx_pt
, self
.ury_pt
)
59 return bbox_pt(other
.llx_pt
, other
.lly_pt
, other
.urx_pt
, other
.ury_pt
)
61 def __iadd__(self
, other
):
62 """join two bboxes inplace"""
63 if self
.llx_pt
is not None:
64 if other
.llx_pt
is not None:
65 self
.llx_pt
= min(self
.llx_pt
, other
.llx_pt
)
66 self
.lly_pt
= min(self
.lly_pt
, other
.lly_pt
)
67 self
.urx_pt
= max(self
.urx_pt
, other
.urx_pt
)
68 self
.ury_pt
= max(self
.ury_pt
, other
.ury_pt
)
70 self
.llx_pt
= other
.llx_pt
71 self
.lly_pt
= other
.lly_pt
72 self
.urx_pt
= other
.urx_pt
73 self
.ury_pt
= other
.ury_pt
76 def __mul__(self
, other
):
77 """return intersection of two bboxes"""
78 if self
.llx_pt
is not None and other
.llx_pt
is not None:
79 return bbox_pt(max(self
.llx_pt
, other
.llx_pt
), max(self
.lly_pt
, other
.lly_pt
),
80 min(self
.urx_pt
, other
.urx_pt
), min(self
.ury_pt
, other
.ury_pt
))
84 def __imul__(self
, other
):
85 """intersect two bboxes in place"""
86 if self
.llx_pt
is not None and other
.llx_pt
is not None:
87 self
.llx_pt
= max(self
.llx_pt
, other
.llx_pt
)
88 self
.lly_pt
= max(self
.lly_pt
, other
.lly_pt
)
89 self
.urx_pt
= min(self
.urx_pt
, other
.urx_pt
)
90 self
.ury_pt
= min(self
.ury_pt
, other
.ury_pt
)
91 elif other
.llx_pt
is None:
96 return bbox_pt(self
.llx_pt
, self
.lly_pt
, self
.urx_pt
, self
.ury_pt
)
99 self
.llx_pt
= other
.llx_pt
100 self
.lly_pt
= other
.lly_pt
101 self
.urx_pt
= other
.urx_pt
102 self
.ury_pt
= other
.ury_pt
104 def lowrestuple_pt(self
):
105 if self
.llx_pt
is None:
106 raise ValueError("Cannot return low-res tuple for empty bbox")
107 return (math
.floor(self
.llx_pt
), math
.floor(self
.lly_pt
),
108 math
.ceil(self
.urx_pt
), math
.ceil(self
.ury_pt
))
110 def highrestuple_pt(self
):
111 if self
.llx_pt
is None:
112 raise ValueError("Cannot return high-res tuple for empty bbox")
113 return (self
.llx_pt
, self
.lly_pt
, self
.urx_pt
, self
.ury_pt
)
115 def intersects(self
, other
):
116 """check, if two bboxes intersect eachother"""
117 if self
.llx_pt
is None or other
.llx_pt
is None:
120 return not (self
.llx_pt
> other
.urx_pt
or
121 self
.lly_pt
> other
.ury_pt
or
122 self
.urx_pt
< other
.llx_pt
or
123 self
.ury_pt
< other
.lly_pt
)
125 def includepoint_pt(self
, x_pt
, y_pt
):
126 if self
.llx_pt
is None:
127 self
.llx_pt
= self
.urx_pt
= x_pt
128 self
.ury_pt
= self
.ury_pt
= y_pt
130 self
.llx_pt
= min(self
.llx_pt
, x_pt
)
131 self
.lly_pt
= min(self
.lly_pt
, y_pt
)
132 self
.urx_pt
= max(self
.urx_pt
, x_pt
)
133 self
.ury_pt
= max(self
.ury_pt
, y_pt
)
135 def transform(self
, trafo
):
136 """transform bbox in place by trafo"""
137 if self
.llx_pt
is None:
139 # we have to transform all four corner points of the bbox
140 llx_pt
, lly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.lly_pt
)
141 lrx_pt
, lry_pt
= trafo
.apply_pt(self
.urx_pt
, self
.lly_pt
)
142 urx_pt
, ury_pt
= trafo
.apply_pt(self
.urx_pt
, self
.ury_pt
)
143 ulx_pt
, uly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.ury_pt
)
145 # Now, by sorting, we obtain the lower left and upper right corner
146 # of the new bounding box.
147 self
.llx_pt
= min(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
)
148 self
.lly_pt
= min(lly_pt
, lry_pt
, ury_pt
, uly_pt
)
149 self
.urx_pt
= max(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
)
150 self
.ury_pt
= max(lly_pt
, lry_pt
, ury_pt
, uly_pt
)
152 def transformed(self
, trafo
):
153 """return bbox transformed by trafo"""
154 if self
.llx_pt
is None:
156 # we have to transform all four corner points of the bbox
157 llx_pt
, lly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.lly_pt
)
158 lrx_pt
, lry_pt
= trafo
.apply_pt(self
.urx_pt
, self
.lly_pt
)
159 urx_pt
, ury_pt
= trafo
.apply_pt(self
.urx_pt
, self
.ury_pt
)
160 ulx_pt
, uly_pt
= trafo
.apply_pt(self
.llx_pt
, self
.ury_pt
)
162 # Now, by sorting, we obtain the lower left and upper right corner
163 # of the new bounding box.
164 return bbox_pt(min(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
), min(lly_pt
, lry_pt
, ury_pt
, uly_pt
),
165 max(llx_pt
, lrx_pt
, urx_pt
, ulx_pt
), max(lly_pt
, lry_pt
, ury_pt
, uly_pt
))
167 def enlarge_pt(self
, all_pt
=0, bottom_pt
=None, left_pt
=None, top_pt
=None, right_pt
=None):
168 """enlarge bbox in place by the given amounts in pts
170 all is used, if bottom, left, top and/or right are not given.
173 if self
.llx_pt
is None:
175 if bottom_pt
is None:
183 self
.llx_pt
-= left_pt
184 self
.lly_pt
-= bottom_pt
185 self
.urx_pt
+= right_pt
186 self
.ury_pt
+= top_pt
188 def enlarged_pt(self
, all_pt
=0, bottom_pt
=None, left_pt
=None, top_pt
=None, right_pt
=None):
189 """return bbox enlarged by the given amounts in pts
191 all is used, if bottom, left, top and/or right are not given.
194 if self
.llx_pt
is None:
196 if bottom_pt
is None:
204 return bbox_pt(self
.llx_pt
-left_pt
, self
.lly_pt
-bottom_pt
, self
.urx_pt
+right_pt
, self
.ury_pt
+top_pt
)
206 def enlarge(self
, all
=0, bottom
=None, left
=None, top
=None, right
=None):
207 """enlarge bbox in place
209 all is used, if bottom, left, top and/or right are not given.
212 if self
.llx_pt
is None:
214 bottom_pt
= left_pt
= top_pt
= right_pt
= unit
.topt(all
)
215 if bottom
is not None:
216 bottom_pt
= unit
.topt(bottom
)
218 left_pt
= unit
.topt(left
)
220 top_pt
= unit
.topt(top
)
221 if right
is not None:
222 right_pt
= unit
.topt(right
)
223 self
.llx_pt
-= left_pt
224 self
.lly_pt
-= bottom_pt
225 self
.urx_pt
+= right_pt
226 self
.ury_pt
+= top_pt
228 def enlarged(self
, all
=0, bottom
=None, left
=None, top
=None, right
=None):
229 """return bbox enlarged
231 all is used, if bottom, left, top and/or right are not given.
234 if self
.llx_pt
is None:
236 bottom_pt
= left_pt
= top_pt
= right_pt
= unit
.topt(all
)
237 if bottom
is not None:
238 bottom_pt
= unit
.topt(bottom
)
240 left_pt
= unit
.topt(left
)
242 top_pt
= unit
.topt(top
)
243 if right
is not None:
244 right_pt
= unit
.topt(right
)
245 return bbox_pt(self
.llx_pt
-left_pt
, self
.lly_pt
-bottom_pt
, self
.urx_pt
+right_pt
, self
.ury_pt
+top_pt
)
248 """return rectangle corresponding to bbox"""
249 if self
.llx_pt
is None:
250 raise ValueError("Cannot return path for empty bbox")
251 return path
.rect_pt(self
.llx_pt
, self
.lly_pt
, self
.urx_pt
-self
.llx_pt
, self
.ury_pt
-self
.lly_pt
)
256 """return height of bbox in pts"""
257 if self
.llx_pt
is None:
258 raise ValueError("Cannot return heigth of empty bbox")
259 return self
.ury_pt
-self
.lly_pt
262 """return width of bbox in pts"""
263 if self
.llx_pt
is None:
264 raise ValueError("Cannot return width of empty bbox")
265 return self
.urx_pt
-self
.llx_pt
268 """return top coordinate of bbox in pts"""
269 if self
.llx_pt
is None:
270 raise ValueError("Cannot return top coordinate of empty bbox")
274 """return bottom coordinate of bbox in pts"""
275 if self
.llx_pt
is None:
276 raise ValueError("Cannot return bottom coordinate of empty bbox")
280 """return left coordinate of bbox in pts"""
281 if self
.llx_pt
is None:
282 raise ValueError("Cannot return left coordinate of empty bbox")
286 """return right coordinate of bbox in pts"""
287 if self
.llx_pt
is None:
288 raise ValueError("Cannot return right coordinate of empty bbox")
292 """return coordinates of the center of the bbox in pts"""
293 if self
.llx_pt
is None:
294 raise ValueError("Cannot return center coordinates of empty bbox")
295 return 0.5 * (self
.llx_pt
+self
.urx_pt
), 0.5 * (self
.lly_pt
+self
.ury_pt
)
298 """return height of bbox"""
299 return self
.height_pt() * unit
.t_pt
302 """return width of bbox"""
303 return self
.width_pt() * unit
.t_pt
306 """return top coordinate of bbox"""
307 return self
.ury_pt
* unit
.t_pt
310 """return bottom coordinate of bbox"""
311 return self
.lly_pt
* unit
.t_pt
314 """return left coordinate of bbox"""
315 return self
.llx_pt
* unit
.t_pt
318 """return right coordinate of bbox"""
319 return self
.urx_pt
* unit
.t_pt
322 """return coordinates of the center of the bbox"""
323 centerx_pt
, centery_pt
= self
.center_pt()
324 return centerx_pt
* unit
.t_pt
, centery_pt
* unit
.t_pt
329 """class for bounding boxes"""
331 def __init__(self
, llx_pt
, lly_pt
, urx_pt
, ury_pt
):
332 llx_pt
= unit
.topt(llx_pt
)
333 lly_pt
= unit
.topt(lly_pt
)
334 urx_pt
= unit
.topt(urx_pt
)
335 ury_pt
= unit
.topt(ury_pt
)
336 bbox_pt
.__init
__(self
, llx_pt
, lly_pt
, urx_pt
, ury_pt
)
339 class empty(bbox_pt
):
341 """empty bounding box, i.e., one containing no point"""
343 bbox_pt
.__init
__(self
, None, None, None, None)