fix typo
[PyX/mjg.git] / pyx / bbox.py
blob271cf7ba4040c593d50d18e87cf86fb8b63d6b72
1 #!/usr/bin/env python
2 # -*- coding: ISO-8859-1 -*-
5 # Copyright (C) 2002-2004 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 # TODO: - it would be nice to have a real bbox.transform
25 # - what about __iadd__, ...
27 import math
28 import unit
30 # helper routine for bbox manipulations
32 def _nmin(*args):
33 """minimum of a list of values, where None represents +infinity, not -infinity as
34 in standard min implementation of python"""
35 args = [x for x in args if x is not None]
36 if len(args):
37 return min(args)
38 else:
39 return None
41 def _nmax(*args):
42 """maximum of a list of values, where None represents +infinity, not -infinity as
43 in standard max implementation of python 2.0"""
44 args = [x for x in args if x is not None]
45 if len(args):
46 return max(args)
47 else:
48 return None
51 # classes representing bounding boxes
54 class _bbox:
56 """class for bounding boxes
58 This variant requires points in the constructor, and is used for internal
59 purposes."""
61 def __init__(self, llx=None, lly=None, urx=None, ury=None):
62 self.llx = llx
63 self.lly = lly
64 self.urx = urx
65 self.ury = ury
67 def __add__(self, other):
68 """join two bboxes"""
70 return _bbox(_nmin(self.llx, other.llx), _nmin(self.lly, other.lly),
71 _nmax(self.urx, other.urx), _nmax(self.ury, other.ury))
73 def __mul__(self, other):
74 """return intersection of two bboxes"""
76 return _bbox(_nmax(self.llx, other.llx), _nmax(self.lly, other.lly),
77 _nmin(self.urx, other.urx), _nmin(self.ury, other.ury))
79 def __str__(self):
80 return "%s %s %s %s" % (self.llx, self.lly, self.urx, self.ury)
82 def write(self, file):
83 file.write("%%%%BoundingBox: %d %d %d %d\n" %
84 (math.floor(self.llx), math.floor(self.lly),
85 math.ceil(self.urx), math.ceil(self.ury)))
86 file.write("%%%%HiResBoundingBox: %g %g %g %g\n" %
87 (self.llx, self.lly, self.urx, self.ury))
89 def intersects(self, other):
90 """check, if two bboxes intersect eachother"""
92 return not (self.llx > other.urx or
93 self.lly > other.ury or
94 self.urx < other.llx or
95 self.ury < other.lly)
97 def transformed(self, trafo):
98 """return bbox transformed by trafo"""
99 # we have to transform all four corner points of the bbox
100 # method correctly handles bboxes with None entries at the corners
101 if None not in (self.llx, self.lly):
102 llx, lly = trafo._apply(self.llx, self.lly)
103 else:
104 llx = lly = None
105 if None not in (self.urx, self.lly):
106 lrx, lry = trafo._apply(self.urx, self.lly)
107 else:
108 lrx = lry = None
109 if None not in (self.urx, self.ury):
110 urx, ury = trafo._apply(self.urx, self.ury)
111 else:
112 urx = ury = None
113 if None not in (self.llx, self.ury):
114 ulx, uly = trafo._apply(self.llx, self.ury)
115 else:
116 ulx = uly = None
118 # now, by sorting, we obtain the lower left and upper right corner
119 # of the new bounding box.
121 return _bbox(_nmin(llx, lrx, urx, ulx), _nmin(lly, lry, ury, uly),
122 _nmax(llx, lrx, urx, ulx), _nmax(lly, lry, ury, uly))
124 def enlarged(self, all=0, bottom=None, left=None, top=None, right=None):
125 """return bbox enlarged
127 all is used, if bottom, left, top and/or right are not given.
130 _bottom = _left = _top = _right = unit.topt(unit.length(all, default_type="v"))
131 if bottom is not None:
132 _bottom = unit.topt(unit.length(bottom, default_type="v"))
133 if left is not None:
134 _left = unit.topt(unit.length(left, default_type="v"))
135 if top is not None:
136 _top = unit.topt(unit.length(top, default_type="v"))
137 if right is not None:
138 _right = unit.topt(unit.length(right, default_type="v"))
139 return _bbox(self.llx-_left, self.lly-_bottom, self.urx+_right, self.ury+_top)
141 def rect(self):
142 """return rectangle corresponding to bbox"""
143 import path
144 return path.rect_pt(self.llx, self.lly, self.urx-self.llx, self.ury-self.lly)
146 path = rect
148 def height(self):
149 """return height of bbox"""
150 return unit.t_pt(self.ury-self.lly)
152 def width(self):
153 """return width of bbox"""
154 return unit.t_pt(self.urx-self.llx)
156 def top(self):
157 """return top coordinate of bbox"""
158 return unit.t_pt(self.ury)
160 def bottom(self):
161 """return bottom coordinate of bbox"""
162 return unit.t_pt(self.lly)
164 def left(self):
165 """return left coordinate of bbox"""
166 return unit.t_pt(self.llx)
168 def right(self):
169 """return right coordinate of bbox"""
170 return unit.t_pt(self.urx)
173 class bbox(_bbox):
175 """class for bounding boxes"""
177 def __init__(self, llx=None, lly=None, urx=None, ury=None):
178 if llx is not None:
179 llx = unit.topt(llx)
180 if lly is not None:
181 lly = unit.topt(lly)
182 if urx is not None:
183 urx = unit.topt(urx)
184 if ury is not None:
185 ury = unit.topt(ury)
186 _bbox.__init__(self, llx, lly, urx, ury)