ezag / pyeuclid

2D and 3D maths module for Python
97 stars 41 forks source link

pyeuclid confuses points and vectors and ignores coordinate systems (frames) #1

Closed bootchk closed 7 years ago

bootchk commented 11 years ago

In pyeuclid: isinstance(foopoint, Vector2) returns True. Also you can add points. This is non-intuitive, and counter to many books on graphics programming.

Also, in many graphics programs you have different coordinate systems (such as world, view, local, user, device, scene,... there are many different names for them.) I find it helpful to explicitly identify the coordinate system (also known as the frame) of a point or vector, and support it in the classes. A sort of type safety: two points/vector instances in different frames are not the same type. I think you could implement frame safety without requiring modifications to the API, and I am not concerned about any performance hit.

Are you interested in such modifications?

Also, there are other outstanding issues on the original pyeuclid. Do you know whether the original is still being maintained? (Evidently not or you wouldn't have forked it?)

ezag commented 11 years ago

Unfortunatelly, original pyeuclid seems to be not maintained: a bug opened more than 2 years ago have not yet been addressed working patch being available.

In order to distinguish points from vectors, you may use type():

>>> foopoint = Point2()
>>> type(foopoint) == Point2
True
>>> type(foopoint) == Vector2
False

Internally points are derived from vectors, so you may treat points as vectors if you need (but not vice versa). Therefore isinstance(foopoint, Vector2) being True is valid and expected.

Regarding different coordinate systems, you may utilize Matrix* classes to define any linear transformation and then switch from one coordinate system to another one using matrix multiplication. If you have difficulties with doing that, feel free to provide your use cases, and I'll try to assist you.

I think implementing coordinate system based type safety would be overkill though; I can't imagine a problem for which it will be really usefull. (Possibly I think so just because use cases like your ones don't come to my mind; feel free to present those as well if you wish).

Thanks for your interest!

bootchk commented 11 years ago

My proposed solution is that Point2 not inherit from Vector2, but that both inherit from Coordinate2 (probably not the correct name), plus other changes. The traditional view is that vectors have length and direction, but not location, while points have a location but not direction nor length. Pyeuclid mashes it all together. If you know what you're doing, it is safe enough. But you must continually think about what you are doing. Pyeuclid doesn't even pass basic axioms of geometry e.g. magnitude e.g. length of a point i.e. abs(pointFoo) is defined and not zero (but it should raise an exception?)

Yes, you can easily switch (transform between) coordinate systems, but the use case I see is: a graphical application where it is a programming chore to keep track of which coordinate system your points are in. One solution is to use the names of objects to indicate their coordinate system, e.g. pointInScene or pointInUserCoords. But why not make pyeuclid keep track of the coordinate systems? See the book "Computer Graphics" by Hill, where it discusses how easy it is to introduce bugs by confusing points and vectors, and mixing coordinate systems.

My use case is actually an application in Qt framework. They have their own classes QPoint and Q2DVector, but they are primitive, and certainly don't come with geometry.

It is regrettable pyeuclid is not maintained and that there is at least one other fork on github. Until someone becomes the 'official' maintainer, then the bug fixes will be spread out among different forks. It seems like that there ought to be a very robust implementation of such basic math (dating to Euclid), even distributed with Python, but there isn't.

ezag commented 11 years ago

It is regrettable pyeuclid is not maintained and that there is at least one other fork on github.

I agree. I did merge forks created by Dov Grobgeld and Lorenzo Riano into my one. There is also fork by Evan Jones, but it just replaces acos with math.acos, so I ignored it.

After that is done, we have a repo with all enchancements to pyeuclid available on github, with full svn history. I hope that would help to gather all efforts for futher pyeuclid development from anyone interested.

Regarding your notes, I am not intrested in making Point2 class not a child of Vector2 class because I don't feel it's a problem from the practical point of view. It's a common practice to consider a point as a vector with origin in (0,0).

Once all known improvements to pyeuclid are in place, you may want to take them as the starting point and make your own contributions, possibly becoming the "official" maintainer.

bootchk commented 11 years ago

Maybe my approach is too theoretical, and not practical. But from an object oriented view, if a Point inherits from Vector, then it inherits ALL the behavior including length and angle, etc. which seems wrong.

What is the harm in changing the inheritance? In terms of changes to other users, it might simply require a method Vector.asPoint(). The intent of the code is clearer. (Also, it is not too difficult to change the inheritance.)

I thought Eiffel libraries might have a good geometry class, but I couldn't find it.

I might be interested in contributing test cases in doctest. The docs and tests for pyeuclid are incomplete. Maybe pyeuclid doesn't need to separate points and vectors, but the documentation and tests should make that clear.