TODO added (paint order should take into account linked axes)
[PyX/mjg.git] / pyx / unit.py
blob2b40e8988e032d2ad9734197ebc404384a646f1a
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 import types
25 import re
26 import helper
28 scale = { 't':1, 'u':1, 'v':1, 'w':1, 'x':1 }
30 _default_unit = "cm"
32 unit_pattern = re.compile(r"""^\s*([+-]?\d*((\d\.?)|(\.?\d))\d*(E[+-]?\d+)?)
33 (\s+([t-x]))?
34 (\s+(([a-z][a-z]+)|[^t-x]))?\s*$""",
35 re.IGNORECASE | re.VERBOSE)
38 _m = {
39 'm' : 1,
40 'cm': 0.01,
41 'mm': 0.001,
42 'inch': 0.01*2.54,
43 'pt': 0.01*2.54/72,
46 def set(uscale=None, vscale=None, wscale=None, xscale=None, defaultunit=None):
47 if uscale is not None:
48 scale['u'] = uscale
49 if vscale is not None:
50 scale['v'] = vscale
51 if wscale is not None:
52 scale['w'] = wscale
53 if xscale is not None:
54 scale['x'] = xscale
55 if defaultunit is not None:
56 global _default_unit
57 _default_unit = defaultunit
60 def _convert_to(l, dest_unit="m"):
61 if type(l) in (types.IntType, types.LongType, types.FloatType):
62 return l*_m[_default_unit]*scale['u']/_m[dest_unit]
63 elif not isinstance(l, length):
64 l=length(l) # convert to length instance if necessary
66 return ( l.length['t'] +
67 l.length['u']*scale['u'] +
68 l.length['v']*scale['v'] +
69 l.length['w']*scale['w'] +
70 l.length['x']*scale['x'] ) / _m[dest_unit]
72 def tom(l):
73 return _convert_to(l, "m")
75 def tocm(l):
76 return _convert_to(l, "cm")
78 def tomm(l):
79 return _convert_to(l, "mm")
81 def toinch(l):
82 return _convert_to(l, "inch")
84 def topt(l):
85 return _convert_to(l, "pt")
87 ################################################################################
88 # class for generic length
89 ################################################################################
91 class length:
92 """ general lengths
94 Lengths can either be a initialized with a number or a string:
96 - a length specified as a number corresponds to the default values of
97 unit_type and unit_name
98 - a string has to consist of a maximum of three parts:
99 -quantifier: integer/float value
100 -unit_type: "t", "u", "v", "w", or "x".
101 Optional, defaults to "u"
102 -unit_name: "m", "cm", "mm", "inch", "pt".
103 Optional, defaults to _default_unit
105 Internally all length are stored in units of m as a quadruple of the four
106 unit_types.
110 def __init__(self, l=1, default_type="u", dunit=None):
111 self.length = { 't': 0 , 'u': 0, 'v': 0, 'w': 0, 'x': 0 }
113 if isinstance(l, length):
114 self.length = l.length
115 elif helper.isnumber(l):
116 self.length[default_type] = l*_m[dunit or _default_unit]
117 elif helper.isstring(l):
118 unit_match = re.match(unit_pattern, l)
119 if unit_match is None:
120 raise ValueError("expecting number or string of the form 'number [u|v|w|x] unit'")
121 else:
122 self.prefactor = float(unit_match.group(1))
123 self.unit_type = unit_match.group(7) or default_type
124 self.unit_name = unit_match.group(9) or dunit or _default_unit
126 self.length[self.unit_type] = self.prefactor*_m[self.unit_name]
127 else:
128 raise ( NotImplementedError,
129 "cannot convert given argument to length type" )
131 def __cmp__(self, other):
132 return cmp(tom(self), tom(other))
134 def __mul__(self, factor):
135 newlength = self.__class__()
136 for unit_type in newlength.length.keys():
137 newlength.length[unit_type] = self.length[unit_type]*factor
138 return newlength
140 __rmul__=__mul__
142 def __div__(self, factor):
143 newlength = self.__class__()
144 for unit_type in newlength.length.keys():
145 newlength.length[unit_type] = self.length[unit_type]/factor
146 return newlength
148 def __add__(self, l):
149 # convert to length if necessary
150 ll = length(l)
151 newlength = self.__class__()
152 for unit_type in newlength.length.keys():
153 newlength.length[unit_type] = self.length[unit_type] + ll.length[unit_type]
154 return newlength
156 __radd__=__add__
158 def __sub__(self, l):
159 # convert to length if necessary
160 ll = length(l)
161 newlength = self.__class__()
162 for unit_type in newlength.length.keys():
163 newlength.length[unit_type] = self.length[unit_type] - ll.length[unit_type]
164 return newlength
166 def __rsub__(self, l):
167 # convert to length if necessary
168 ll = length(l)
169 newlength = self.__class__()
170 for unit_type in newlength.length.keys():
171 newlength.length[unit_type] = ll.length[unit_type] - self.length[unit_type]
172 return newlength
174 def __neg__(self):
175 newlength = self.__class__()
176 for unit_type in newlength.length.keys():
177 newlength.length[unit_type] = -self.length[unit_type]
178 return newlength
180 def __str__(self):
181 return "(%(t)f t + %(u)f u + %(v)f v + %(w)f w + %(x)f x) m" % self.length
184 ################################################################################
185 # class for more specialized lengths
186 ################################################################################
188 # lengths with user units as default
190 class u_pt(length):
191 def __init__(self, l=1, default_type="u"):
192 length.__init__(self, l, default_type=default_type, dunit="pt")
195 class u_m(length):
196 def __init__(self, l=1, default_type="u"):
197 length.__init__(self, l, default_type=default_type, dunit="m")
200 class u_mm(length):
201 def __init__(self, l=1, default_type="u"):
202 length.__init__(self, l, default_type=default_type, dunit="mm")
205 class u_cm(length):
206 def __init__(self, l=1, default_type="u"):
207 length.__init__(self, l, default_type=default_type, dunit="cm")
210 class u_inch(length):
211 def __init__(self, l=1, default_type="u"):
212 length.__init__(self, l, default_type=default_type, dunit="inch")
214 # without further specification, length are user length. Hence we
215 # define the following aliases
217 pt = u_pt
218 m = u_m
219 cm = u_cm
220 mm = u_mm
221 inch = u_inch
223 # true lengths
225 class t_pt(length):
226 def __init__(self, l=1):
227 length.__init__(self, l, default_type="t", dunit="pt")
230 class t_m(length):
231 def __init__(self, l=1):
232 length.__init__(self, l, default_type="t", dunit="m")
235 class t_cm(length):
236 def __init__(self, l=1):
237 length.__init__(self, l, default_type="t", dunit="cm")
240 class t_mm(length):
241 def __init__(self, l=1):
242 length.__init__(self, l, default_type="t", dunit="mm")
245 class t_inch(length):
246 def __init__(self, l=1):
247 length.__init__(self, l, default_type="t", dunit="inch")
249 # visual lengths
251 class v_pt(length):
252 def __init__(self, l=1):
253 length.__init__(self, l, default_type="v", dunit="pt")
256 class v_m(length):
257 def __init__(self, l=1):
258 length.__init__(self, l, default_type="v", dunit="m")
261 class v_cm(length):
262 def __init__(self, l=1):
263 length.__init__(self, l, default_type="v", dunit="cm")
266 class v_mm(length):
267 def __init__(self, l=1):
268 length.__init__(self, l, default_type="v", dunit="mm")
271 class v_inch(length):
272 def __init__(self, l=1):
273 length.__init__(self, l, default_type="v", dunit="inch")
275 # width lengths
277 class w_pt(length):
278 def __init__(self, l=1):
279 length.__init__(self, l, default_type="w", dunit="pt")
282 class w_m(length):
283 def __init__(self, l=1):
284 length.__init__(self, l, default_type="w", dunit="m")
287 class w_cm(length):
288 def __init__(self, l=1):
289 length.__init__(self, l, default_type="w", dunit="cm")
292 class w_mm(length):
293 def __init__(self, l=1):
294 length.__init__(self, l, default_type="w", dunit="mm")
297 class w_inch(length):
298 def __init__(self, l=1):
299 length.__init__(self, l, default_type="w", dunit="inch")
301 # tex lengths
303 class x_pt(length):
304 def __init__(self, l=1):
305 length.__init__(self, l, default_type="x", dunit="pt")
308 class x_m(length):
309 def __init__(self, l=1):
310 length.__init__(self, l, default_type="x", dunit="m")
313 class x_cm(length):
314 def __init__(self, l=1):
315 length.__init__(self, l, default_type="x", dunit="cm")
318 class x_mm(length):
319 def __init__(self, l=1):
320 length.__init__(self, l, default_type="x", dunit="mm")
323 class x_inch(length):
324 def __init__(self, l=1):
325 length.__init__(self, l, default_type="x", dunit="inch")