From: Andre W. <wo...@us...> - 2004-07-28 17:02:14
|
Hi, On 28.07.04, André Wobst wrote: > Update of /cvsroot/pyx/pyx/test/experimental > In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13287 > > Modified Files: > solve.py > Log Message: > vector equations (can be mixed with scalar equations) [snip] I've just checked in another update to the experimental linear equation solver. You can now mix all kind of vector and scalar equations. But I'm not quite sure whether the "==" notation is a good idea, i.e. will work well in non-trivial code. My biggest concerns are in the area of usuability, i.e. is it too implicit? Comments? André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Joerg L. <jo...@us...> - 2004-07-28 17:52:49
|
Hi André, On 28.07.04, Andre Wobst wrote: [snip] > I've just checked in another update to the experimental linear > equation solver. You can now mix all kind of vector and scalar > equations. Nice! > But I'm not quite sure whether the "==" notation is a good > idea, i.e. will work well in non-trivial code. My biggest concerns are > in the area of usuability, i.e. is it too implicit? Comments? IMHO it's too implicit. In particular, not having to explicitly specify the solver is probably not a good idea. Jörg |
From: Magnus L. H. <ma...@he...> - 2004-07-28 19:38:48
|
Joerg Lehmann <jo...@us...>: > [snip] > IMHO it's too implicit. In particular, not having to explicitly specify > the solver is probably not a good idea.=20 I think using =3D=3D for anything with side-effects (or even anything tha= t isn't interpretable as a boolean equality check) is quite unfortunate. It might *look* good, but still... (if x =3D=3D y: alwaysexecuted()...) Using a method or function may be a bit more verbose but still much better, IMO. We don't really have much to complain about; the Java people have to use foo.equals(bar) where we can use foo =3D=3D bar :) I think solver.equate(x, y) or something similar (e.g. "equals", "equal", "eq") would be better. If one was feeling adventurous, one could even do stuff like solver(x =3D y) but only for single-variable left-hand-sides, of course. It would be possible to do some property magic too... I'm pretty sure I could write code such that a.x =3D b.y a.y =3D b.x could be turned into a set of equations (by using wrapper classes around the objects returned by the properties, somewhat like bound methods). Just musing. > J=F6rg --=20 Magnus Lie Hetland "Canned Bread: The greatest thing since sliced http://hetland.org bread!" [from a can in Spongebob Squarepants] |
From: Andre W. <wo...@us...> - 2004-07-29 06:11:39
|
Hi, On 28.07.04, Magnus Lie Hetland wrote: > Joerg Lehmann <jo...@us...>: > > > [snip] > > IMHO it's too implicit. In particular, not having to explicitly specify > > the solver is probably not a good idea. > > I think using == for anything with side-effects (or even anything that > isn't interpretable as a boolean equality check) is quite unfortunate. > It might *look* good, but still... (if x == y: alwaysexecuted()...) Thanks, Jörg, Magnus. I was not satisfied myself. The comparison makes me troubles as well ... see the id comparision and the XXX comment, where I check for zero using "is", which works, since all integer constants below 100 are predefined and used by reference. In the beginning I liked the idea, but its probably a bad idea. > If one was feeling adventurous, one could even do stuff like > > solver(x = y) > > but only for single-variable left-hand-sides, of course. I do not quite understand. What's the result of an assignment? Anyway, once we do not use x == y, we should allow for x = y as well. Its becoming even less symmetric: Currently you need to set x and y to be scalar instances (or variable instances). In your case this becomes assymetric. You would need to initialize just x. Beside that, I think where might be usecases, where you store a term. Say: x = scalar() y = scalar() z = scalar() A = x + 5*y B = A + y C = A + z solver.eq(B, C) I don't know whether this will make sense, but you can do so. > It would be possible to do some property magic too... I'm pretty sure > I could write code such that > > a.x = b.y > a.y = b.x > > could be turned into a set of equations (by using wrapper classes > around the objects returned by the properties, somewhat like bound > methods). > > Just musing. You can currently do so with the "==" logic. That's because the components of a vector ar scalars and you can formulate scalar eqations and vector equations. So you can currently write: a = vector(2) b = vector(2) a[0] == b[1] a[1] == b[0] Note I have not (yet) a getattr on the vectors for "x", "y", "z". But right, thats trivial to be added. Now you can write: a == point(0, 1) Guess, what b becomes? ... ;-) André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-07-29 11:09:02
|
Andre Wobst <wo...@us...>: > > Hi, >=20 > On 28.07.04, Magnus Lie Hetland wrote: > > Joerg Lehmann <jo...@us...>: > > > > > [snip] > > > IMHO it's too implicit. In particular, not having to explicitly spe= cify > > > the solver is probably not a good idea.=20 > >=20 > > I think using =3D=3D for anything with side-effects (or even anything= that > > isn't interpretable as a boolean equality check) is quite unfortunate. > > It might *look* good, but still... (if x =3D=3D y: alwaysexecuted()..= .) >=20 > Thanks, J=F6rg, Magnus. I was not satisfied myself. The comparison make= s > me troubles as well ... see the id comparision and the XXX comment, > where I check for zero using "is", which works, since all integer > constants below 100 are predefined and used by reference. In the > beginning I liked the idea, but its probably a bad idea. Comparing numbers usin 'is' is probably not a good idea, as this behavior isn't (AFAIK) defined as part of the language -- it's an implementation detail that might change. > > If one was feeling adventurous, one could even do stuff like > >=20 > > solver(x =3D y) > >=20 > > but only for single-variable left-hand-sides, of course. >=20 > I do not quite understand. What's the result of an assignment? No, no -- the above is just the use of a keyword argument. [snip] > Beside that, I think where might be usecases, where you store a term. > Say: >=20 > x =3D scalar() > y =3D scalar() > z =3D scalar() >=20 > A =3D x + 5*y > B =3D A + y > C =3D A + z >=20 > solver.eq(B, C) >=20 > I don't know whether this will make sense, but you can do so. This looks just fine to me. [snip] > You can currently do so with the "=3D=3D" logic. Yeah, I meant specifically fiddling with things so you could use '=3D' (which you normally couldn't do -- not in the same way you do with '=3D=3D'). [snip] > Now you can write: >=20 > a =3D=3D point(0, 1) >=20 > Guess, what b becomes? ... ;-) Hehe. BTW: I wrote pt to work like a sequence, and thus have a constructor of the type pt([1, 2 ,3]) and not pt(1, 2, 3). This was motivated by a similar discussion about the new set type. I guess it is sort of echoed by the discussion of the use of explicit lists in the decorator specifications in PyX. Just food for thought... > Andr=E9 --=20 Magnus Lie Hetland "Canned Bread: The greatest thing since sliced http://hetland.org bread!" [from a can in Spongebob Squarepants] |
From: Andre W. <wo...@us...> - 2004-07-29 11:37:23
|
Hi, On 29.07.04, Magnus Lie Hetland wrote: > Comparing numbers usin 'is' is probably not a good idea, as this > behavior isn't (AFAIK) defined as part of the language -- it's an > implementation detail that might change. I could already remove that, once I stopped misusing equality. (But I think, the solution of these two topics were not really releated. The solution was to introduce self.zero in variable and vector. But is doesn't matter.) > > > If one was feeling adventurous, one could even do stuff like > > > > > > solver(x = y) > > > > > > but only for single-variable left-hand-sides, of course. > > > > I do not quite understand. What's the result of an assignment? > > No, no -- the above is just the use of a keyword argument. I'm so stupid. Right, now I understand. Still, I don't like it and it doesn't seems necessary. > BTW: I wrote pt to work like a sequence, and thus have a > constructor of the type pt([1, 2 ,3]) and not pt(1, 2, 3). This was > motivated by a similar discussion about the new set type. I guess it > is sort of echoed by the discussion of the use of explicit lists in > the decorator specifications in PyX. Just food for thought... I'm not totally convinced, but I might need to think a bit. But I can't resist to answer right now (also I may change my mind after some thoughts): A point having two variables is a 2d-object, a point with 3 variables is a 3d-object. And each position as a special meaning (i.e. x, y, z). So here we have a tuple, not a list. (There *are* cases, where we have tuples, not lists.) In that sense I think its different from the attribute discussion ... André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-07-29 11:47:23
|
Andre Wobst <wo...@us...>: > [snip] >=20 > I'm not totally convinced, but I might need to think a bit. But I > can't resist to answer right now (also I may change my mind after some > thoughts): A point having two variables is a 2d-object, a point with 3 > variables is a 3d-object. And each position as a special meaning (i.e. > x, y, z). So here we have a tuple, not a list. Same thing, IMO. I think tuples in Python are really worthless in most cases. The only thing they'd buy us here would be immutability. (One could argue that that would be a good thing here, in accordance with the Value pattern.) But that doesn't really matter, IMO. Whether you do point([1, 2, 3]) or point((1, 2, 3)) is completely irrelevant (as long as you stay away from the naughtiness of type checking ;). The real difference is between this and point(1, 2, 3) Even the numarray arrays use the first form, as do, really, all sequence types, including tuples. > (There *are* cases, where we have tuples, not lists.) As I said, there's really no practical difference, IMO. > In that sense I think its different from the attribute discussion > ... Sure. I'm just saying that the "standard" constructor for sequence types admits a single iterable objects as its argument. And if one stays away from type checking, one could even use future enhancements, such as the upcoming generator comprehensions, such as: b =3D point(x**2 for x in a) :) You *could*, of course, still do b =3D point(*[x**2 for x in a]) of course... > Andr=E9 --=20 Magnus Lie Hetland "Canned Bread: The greatest thing since sliced http://hetland.org bread!" [from a can in Spongebob Squarepants] |
From: Andre W. <wo...@us...> - 2004-07-30 10:04:10
|
Hi, On 29.07.04, Magnus Lie Hetland wrote: > > I'm not totally convinced, but I might need to think a bit. But I > > can't resist to answer right now (also I may change my mind after some > > thoughts): A point having two variables is a 2d-object, a point with 3 > > variables is a 3d-object. And each position as a special meaning (i.e. > > x, y, z). So here we have a tuple, not a list. > > Same thing, IMO. I think tuples in Python are really worthless in most > cases. The only thing they'd buy us here would be immutability. (One > could argue that that would be a good thing here, in accordance with > the Value pattern.) > > But that doesn't really matter, IMO. Whether you do > > point([1, 2, 3]) > > or > > point((1, 2, 3)) > > is completely irrelevant (as long as you stay away from the > naughtiness of type checking ;). I'm not so sure. To me it makes a difference. A tuple is something, where the items do have a fixed, specific position and the meaning of the items might be different depending on their position. Hence a crucial feature of tuples is to be immutable in their length. Well, they are immutable in their values as well, but this keeps to be strange to me. It's kind of unpythonic ... well. I don't know. Vectors, where each component is a value for a certain dimension is not a very good example for that discussion, I think. At least when all dimensions are equal to each other like in an Euclid space. May be the theory of relativity with its four component vectors is a resonable example even for a vector being a tuple, not a list ... Beside that, you're right. We should forget about type checking and than it usually becomes unimportant. > The real difference is between this > and > > point(1, 2, 3) > > Even the numarray arrays use the first form, as do, really, all > sequence types, including tuples. In my redesign I'm only left with a single vector class. Here I'm using the list like version to create a constant and a plain number to specify a variable vectors dimension. I'm not sure whether you'll like this syntax in the end, but the user is left with just a scalar and a vector. Period. That's nice, I think, but we can discuss those details and the naming again once we see how it works out. > I'm just saying that the "standard" constructor for sequence > types admits a single iterable objects as its argument. BTW: How invented this stange *single* iterable object. I would really like to be allowed to use several of them. This limitation is annoying and unnecessary to me. (I haven't thought very hard about that, but I really don't see any reasoning why this limitation exist.) André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-07-30 20:22:06
|
Andre Wobst <wo...@us...>: > [snip] > > point([1, 2, 3]) > >=20 > > or > >=20 > > point((1, 2, 3)) > >=20 > > is completely irrelevant (as long as you stay away from the > > naughtiness of type checking ;). >=20 > I'm not so sure. To me it makes a difference. Not unless you're doing type checking ;) I'm talking from the perspective of the function, not the caller. The function should simply treat the argument as a sequence, and not bother with how it is implemented. > A tuple is something, where the items do have a fixed, specific > position and the meaning of the items might be different depending > on their position. That is the philosophical reasoning, yes. I still think tuples are pretty bogus. You could easily use lists to represent the same thing... *Or* you could have fixed-length lists. *Or* you could have mutable tuples. I don't know... Anyway, it really shouldn't have anything to do with how the function is implemented, as long as it doesn't realy on any functionality present in tuples but not elsewhere (and I can't think of any). > Hence a crucial feature of tuples is to be immutable in their > length. Sure. I can see how that can be useful -- sort of. Although not very much. > Well, they are immutable in their values as well, but this keeps to > be strange to me. It's kind of unpythonic ... well. I don't know. I think so too. When I wrote the chapter on sequences in my Python book I had real problems justifying the existence of tuples. I tried to ask Alex Martelly (who was one of my tech editors) and he didn't have any good explanations either. The only use of tuples, IMO (as a Python type, not as a concept -- you could easily use a list to model a mathematical tuple, where each position has a specific meaning and so forth) is that they are hashable, and thus can be used as keys in dicts, members of sets and so forth. You have the same dichotomy (list vs. tuple) in the set implementation (with set and frozenset). > Vectors, where each component is a value for a certain dimension is > not a very good example for that discussion, I think. At least when > all dimensions are equal to each other like in an Euclid space. May be > the theory of relativity with its four component vectors is a > resonable example even for a vector being a tuple, not a list ... Sure. But that's still just (IMO) the thing you're *modeling* using the specific object (a tuple or a list). I *don't* see why using a tuple is any better, unless you really need it to be static/frozen (which, in most cases, you really don't). And I really, really think this is not something a function such as this should care about. It should simply accept any iterable object. Now -- it may be that we're discussing two different things... I'm arguing that a list and a tuple as arguments should be seen as equivalent; you may, perhaps, be arguing that positional arguments (i.e. point(1, 2, 3)) is the way to go? I'm not really very opposed to that. I just brought the (somewhat "standard") point(seq) syntax as an alternative. > Beside that, you're right. We should forget about type checking and > than it usually becomes unimportant. >=20 > > The real difference is between this > > and > >=20 > > point(1, 2, 3) > >=20 > > Even the numarray arrays use the first form, as do, really, all > > sequence types, including tuples. >=20 > In my redesign I'm only left with a single vector class. Here I'm > using the list like version to create a constant and a plain number to > specify a variable vectors dimension. OK. > I'm not sure whether you'll like this syntax in the end, but the > user is left with just a scalar and a vector. Period. That's nice, I > think, but we can discuss those details and the naming again once we > see how it works out. Indeed. > > I'm just saying that the "standard" constructor for sequence > > types admits a single iterable objects as its argument. >=20 > BTW: How invented this stange *single* iterable object. I would really > like to be allowed to use several of them. This limitation is annoying > and unnecessary to me. (I haven't thought very hard about that, but I > really don't see any reasoning why this limitation exist.) What do you mean? Would you like to use several iterable objects as arguments to list(), for example? What would that mean? Something like list(chain(iter1,iter2,iter3))? (The chain function is available in the itertools module.) > Andr=E9 --=20 Magnus Lie Hetland "Canned Bread: The greatest thing since sliced http://hetland.org bread!" [from a can in Spongebob Squarepants] |
From: Andre W. <wo...@us...> - 2004-08-01 20:29:56
|
Hi, On 30.07.04, Magnus Lie Hetland wrote: > [snip] > > > point([1, 2, 3]) > > > > > > or > > > > > > point((1, 2, 3)) > > > > > > is completely irrelevant (as long as you stay away from the > > > naughtiness of type checking ;). > > > > I'm not so sure. To me it makes a difference. > > Not unless you're doing type checking ;) > > I'm talking from the perspective of the function, not the caller. The > function should simply treat the argument as a sequence, and not > bother with how it is implemented. Sure, that's right. My points were not related to the isinstance discussion we actually started at. > The only use of tuples, IMO (as a Python type, not as a concept -- you > could easily use a list to model a mathematical tuple, where each > position has a specific meaning and so forth) is that they are > hashable, and thus can be used as keys in dicts, members of sets and > so forth. You have the same dichotomy (list vs. tuple) in the set > implementation (with set and frozenset). I see, the hasability is an important point. I pretty much like your comparision to set and frozenset. It looks like a very reasonable point of view ... > What do you mean? Would you like to use several iterable objects as > arguments to list(), for example? What would that mean? Something like > list(chain(iter1,iter2,iter3))? (The chain function is available in > the itertools module.) I meant list(*chain(iter1, iter2, iter2)). I would really like to be able to write list(*iter1, *iter2, *iter3). Suppose we have the line function line(x1, y1, x2, y2) as it is defined in PyX. Having a function point(...) returning a list (x, y), it would be great if we could just write line(*point(...), *point(...)). Currently you have to write line(*(point() + point()), which I don't understand why ... Coming back to our MetaPost like equations I've just checked in yet another version, where there are scalars, vectors, and matrices now. You can define a matrix by equations like in MetaPost, i.e. which points should be transformed into which points (except for the missing translation; a matrix is not a full affine transformation, but building a trafo class on top of a matrix and a vector should be quite easy). You can also calculate the invers of a matrix by multiplying it to another matrix and set the result to the uniform matrix ... so in principle we should be able to do most of the things you can do in MetaPost now. One structually missing thing is the "redefinition" of an equation, but this should not be hard to add it to the solver. André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-08-01 20:40:53
|
Andre Wobst <wo...@us...>: > [snip] > Coming back to our MetaPost like equations I've just checked in yet > another version, where there are scalars, vectors, and matrices now. > You can define a matrix by equations like in MetaPost, i.e. which > points should be transformed into which points (except for the missing > translation; a matrix is not a full affine transformation, It can be... If you use homogenous coordinates :) > but building a trafo class on top of a matrix and a vector should be > quite easy). Yeah -- or just hiding the homogenous coordinates behind the scenes. > You can also calculate the invers of a matrix by multiplying it to > another matrix and set the result to the uniform matrix ... so in > principle we should be able to do most of the things you can do in > MetaPost now. Sounds veeery goood !-) > One structually missing thing is the "redefinition" of an equation, > but this should not be hard to add it to the solver. No -- and in a pinch, you could basically just build a new solver with one equation redefined. > Andr=E9 --=20 Magnus Lie Hetland "Canned Bread: The greatest thing since sliced http://hetland.org bread!" [from a can in Spongebob Squarepants] |
From: Andre W. <wo...@us...> - 2004-08-02 05:47:09
|
Hi, On 01.08.04, Magnus Lie Hetland wrote: > > One structually missing thing is the "redefinition" of an equation, > > but this should not be hard to add it to the solver. > > No -- and in a pinch, you could basically just build a new solver with > one equation redefined. I thought about that as well before, but there seems to be a problem: If you create several solver instances (which is trivial, of course), you can't store the solver results in the scalars itself. You would loose to know, whether a scalar is a constant or it got set by another solver (and should be overwritten?). We can find some way out of this problem, but it might be quite obscure to the user ... Beside that there are quite some other things to be discussed. Just a few which come into my mind immediately: In MetaPost you can define equations, which over-determine the problem. You can do so by none-scalar equations and I think it's a bad idea to do so, but this might be discussed with people, who are used to the MetaPost equation solver. I'm not sure whether this is a often used feature and whether we should spend time on that at all. Currently the integration in PyX is not thought about. We have at least to think about the interfaces to the path stuff, the transformations and the PyX lengths. And we might think about the basic functionality. Currently I've tried to implement some school-like vector algebra. For example we do have vectors and we can do scalar products between vectors. Multiplying a vector by another vector will lead to a scalar product. One could also think about introducing dual vectors, so that multiplying a dual vector by a vector will be a scalar product, but multiplying a vector by a dual vector will be a dyadic product and result in a matrix. While there would be *some* people how would like that (I think, I would join that portion), it might be inappropriate to our goals using the system for geometrical operations. So, yes, I think we should stick on what we already have (up to introducing a full transformation), but I'm not totally sure. Beside that we'll need to complete the tests and some fancy examples would be great as well ... André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Andre W. <wo...@us...> - 2004-08-02 08:59:17
Attachments:
quadrilateral.py
|
Hi, On 02.08.04, Andre Wobst wrote: > some fancy examples would be great as well ... Something like the enclosed one, which looks pretty well, but unfortunately it does use the solver to calculate the crossing point between two lines only ... André PS: Do not forget to fetch the latest version of solve.py before trying the example since I broke some multiplication logic due to the vector*matrix multiplication yesterday and the linear equation solver used an integer matrix by accident. -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Andre W. <wo...@us...> - 2004-07-29 07:01:35
|
Hi, On 28.07.04, Joerg Lehmann wrote: > > "==" > IMHO it's too implicit. In particular, not having to explicitly specify > the solver is probably not a good idea. I think, having only one solver (as in MetaPost) is not that problematic. OTOH, yes, you may make use of several solvers. But then I think we should do the bookkeeping, whether a variable is already defined (currently by the is_set() method), local within each solver. Could be usefull ... I'm not sure. AFAIRC in MetaPost there is a possibility to "redefine" the value of a variable and "resolve" the equations. Might be a task for several solvers. This would be much more elegant IMHO (compared to the MetaPost way; MetaPost doesn't have (several) solver instances.) André -- by _ _ _ Dr. André Wobst / \ \ / ) wo...@us..., http://www.wobsta.de/ / _ \ \/\/ / PyX - High quality PostScript figures with Python & TeX (_/ \_)_/\_/ visit http://pyx.sourceforge.net/ |
From: Magnus L. H. <ma...@he...> - 2004-07-28 19:42:48
|
Andre Wobst <wo...@us...>: > > Hi, >=20 > On 28.07.04, Andr=E9 Wobst wrote: > > Update of /cvsroot/pyx/pyx/test/experimental > > In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv13287 > >=20 > > Modified Files: > > solve.py=20 > > Log Message: > > vector equations (can be mixed with scalar equations) > [snip] >=20 > I've just checked in another update to the experimental linear > equation solver. You can now mix all kind of vector and scalar > equations. Sounds very cool. This basically means that most of the needed (basic) geometry functionality is in place, then... (It still would be nice to have access to the first three dimensions of vectors as v.x, v.y and v.z but that's just a minor convenience.) It would be interesting to see what could be done with the transform equations... But even without them, stuff like "the difference between a and b is equal to the difference between c and d" and the like is very useful. I couldn't get the vector stuff from CVS it seems -- maybe it's just the anonymous CVS caching that's a bit slow again. > But I'm not quite sure whether the "=3D=3D" notation is a good > idea, i.e. will work well in non-trivial code. My biggest concerns are > in the area of usuability, i.e. is it too implicit? Comments? (I replied to the "=3D=3D" part elsewhere.) > Andr=E9 --=20 Magnus Lie Hetland "Canned Bread: The greatest thing since sliced http://hetland.org bread!" [from a can in Spongebob Squarepants] |