[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

Re: [pygame] API draft for vector type



On, Mon Apr 27, 2009, Lorenz Quack wrote:

[...]

Some general notes in short, which the other thread repliers already
pointed out:

Vector2d is needed
Vector3d is needed, too (at least for me), to have 2d/3d transformations.

Vector4d can be realised using the 'w' property easily in your Vector3d class.
Vector2d is basically Vector3d without considering a z component.

Daniel Jo <ostsol@xxxxxxxxx>:
> Vector classes work well for convenience and code readability, but
> from a performance standpoint they aren't very useful.

Right, that's what batch operations are suitable for. Lorenz' proposal
mentions e.g. creating vectors from sequence types. If we add a set of
helper functions to the module (pygame.math.do_stuff (many_vectors)), we
can easily get around that issue. This only requires us to keep the
internally optimised methods separated well enough.

Casey Duncan <casey@xxxxxxxxxxx>:
> array of C structs, but you can iterate them and access the individual
> vectors for position, velocity, etc. from python.

The proposed math helper functions thus should be suitable to operate on
array() objects or buffers, where the type and object size has to be
explicitily stated. We then can get equal results easily.

Brian Fisher:
> One aspect of vector class design that I've become more and more a fan of
> over time is vector immutability.

Seconded.

Casey Duncan <casey@xxxxxxxxxxx>:
> PEP-8 for method and property naming!

Seconded.

Rene Dudfield <renesd@xxxxxxxxx>
> What number types are used?  eg, can you have a float vector, a long vector,
> an int vector?  Any python number?  A uint8 ?

I'd vote for two major types here: long/int and float. Depending on what
the passed values are. If we argue to use 'any python number', we'll
have to refer to the number protocol anytime an arithmetic function
occurs. This makes anything painfully slow.

If the passed values are all int or long, the vector might be handled as
long/int vector with matching optimised functions. if the numbers are
all float, other matching optimised functions might be used.

However, those considerations should be handled later on. To implement
such hooks is a bit complex due to the behaviour specification. For now
I'd vote for using either float (double) as internal representation (for
being exact enough) and/or two vector types:

FVector (floating point)
LVector (long/int)

> 1.5.4.1  v.isNormalized() -> bool
> 1.5.4.2  v.normalize() -> None    # normalizes inplace
> 1.5.4.3  v1.normalized() -> v2    # returns normalized vector

In my opinion normalize() and normalized() are far too ambiguous. We
might should use something like ip_XXX() for inplace operations:

  v.ip_normalize () -> None
  v.normalize () -> v2

We do that naming already for Rect objects and should take care of doing
that anywhere where inplace operations happen. The same then applies to
the rest of your inplace methods.

> 1.6 properties
> ==============
> 1.6.1.1  v.length -> a # gets the magnitude/length of the vector
> 1.6.1.2  v.length = a
>           # sets the length of the vector while preserving its direction

The setter might be risky due to rounding issues.

> 1.6.2.1  a) v.lengthSquared -> a
>           b) v.length2 -> a
>           # gets the squared length of the vector. same as v.dot(v) or v * v
> 1.6.2.1  a) v.lengthSquared = a
>           b) v.length2 = a
>           # sets the squared length of the vector. preserving its direction

Same as above.

> 1.7 comparison operations
> =========================
> 1.7.0    the "==" and "!=" and "bool" operater compare the differences against
>           the attribute v._epsilon. this way the user can adjust the accuracy.
> 1.7.1.1  v == s -> bool
>           # true if all component differ at most by v._epsilon

How am I as user supposed to manipulate _epsilon? It should be made public.

> 1.8 misc
> ======================
> 1.8.1  support iter protocol
> 1.8.2  str(v) -> "[x, y, z]"
> 1.8.3  repr(v) -> "Vec<x, y, z>"

Most objects have the same output for str() and repr(). Why do you
differ here?
 
> 1.10 open questions (in no particular order)
> ============================================
> 1.10.1  a) use radians for all angles
>          b) use degrees for all angles

Degrees, I'd say. The api should be easy to use and letting users
calculate the rad values before is not that intuitive.

> 1.10.2  what about slicing?

Slicing in a fixed 1-column matrix sounds useless to me.

> 1.10.4  do we need int or complex vectors?

A specialised int vector class would be nice for even more optimised
code, but (in my opinion) that can be considered in a later stage. Most
of the time it's just about replacing C floating point functions with
the matching integer functions.

> 1.10.5  what about negative indices in the sequence protocol?

Required as supported by the Python standard. The sequence protocol is
pretty easy to implement and usually you receive a nicely converted
positive Py_ssize_t, if it is within the range. Take a look at the
PixelArray and Color classes, which implement sequence access.

> 1.10.6  is there need for explicit row- and column-vectors?

Only in conjunction with matrix types later on, I'd say.

> Contrast to existing implementations
> ####################################
> 
> There are of course already existing implementations of vector types.  In
> particular I want to take a look at pyeuclid [1], vectypes [2] and
> 3DVectorClass [3].  In this chapter I want to compare them and point out
> their similarities and differences.  This isn't a full review but I tried to
> find out and describe the most important differences.  If your favorite
> implementation is missing from this comparison feel free to contribute your
> own analysis.  Disclaimer: I never used any of these.
> 
> numerical behaviour:

[...]

>     vectypes and 3DVectorClass do elementwise multiplication
>       e.g. "vec2(1,2) * vec2(3,4) == vec(1*3, 2*4)"
>     this proposal preforms a dot product
>       e.g. "V(1,2) * V(3,4) == 1*3 + 2*4"

Which is intuitive as well. We have a dot() method proposed, so it might
make sense to perform an element-wise multiplication as they do.

>   * __div__
>     pyeuclid and this proposal only support division by (int, long, float)
>     vectypes and 3DVectorClass also do implicit elementwise division
>     same for __floordiv__ and __mod__
>   * __abs__
>     pyeuclid returns the magnitude.
>     3DVectorType returns vec3d(abs(x), abs(y), abs(z))
>     vectypes and this proposal don't implement __abs__ to avoid confusion

I'd expect abs() to get the magnitude/length, too.

> other differences:
>   * from the mentioned packages only pyeuclid optionally supports swizzleing
>   * pyeuclid has a method "magnitude" instead of "length"
>   * vectypes uses functions at a module level rather than instance methods.

For batch operations (to be speedy enough) we'd require both.

Regards
Marcus

Attachment: pgpPgb0z50uYc.pgp
Description: PGP signature