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

Re: [pygame] API draft for vector type



On, Tue Apr 28, 2009, Lorenz Quack wrote:

[...]
> Marcus von Appen wrote:
> > On, Mon Apr 27, 2009, Lorenz Quack wrote:
> > 

[...] 
> > 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.
> 
> seems like you already have this worked out in your head. but module-level 
> batch functions sound like a good idea (even though I don't know how to 
> actually implement that, yet).

Here's a short snippet:

static PyMethodDef _audio_methods[] = {
    { "init", (PyCFunction) _sdl_audioinit, METH_NOARGS, DOC_AUDIO_INIT },
    ....
};

static PyObject*
_sdl_audioinit (PyObject *self)
{
   /* self resolves to the module, usually */
   ...
}
PyMODINIT_FUNC initaudio (void)
{
    PyObject *mod = Py_InitModule3 ("audio", _audio_methods, DOC_AUDIO);
    ...
}

It's fairly easy as you see. Instead of binding methods to the object,
you bind them to the module.


> > 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.
> 
> just for clarity: do you mean that the helper functions should operate on 
> arrays (numpy arrays?) of vectors of regular numbers. something like this:
>    v1 = Vector3d((1, 2, 3))
>    v2 = Vector3d((-4, 5.6, 7))
>    a = array(v1, v2)
>    axis = Vector3d((1,0,0))
>    angle = 90 #degrees
>    pygame.math.rotateBatch(axis, angle, a)
> ?

Arbitrary arrays, sequences or buffers, to be more exact. Let's say,
I've got a sequence of numbers, which denote 3d vectors to be rotated:

  a = [(1,2,3), (-1, 0, 0), ...]
  v = Vector3d (1, 0, 0)
  # rotateBatch performs a PySequence_Check() and acts accordingly.
  pygame.math.rotateBatch (v, 90, a)

Now the same for a buffer:

  # Read 30 elements from some file.
  buf = myfile.doread_binary (30)
  v = Vector3d (1, 0, 0)
  # 
  # rotateBatch checks for the buffer, reads the element size hint (2
  # bytes for each element) and acts accordingly in the following way:
  # 
  # for i 0 to = len (buf) 
  #   elem = get [size] bytes element from buf as [type] value
  #   result = rotate (elem v, 90)
  #   set result back into buf
  #
  pygame.math.rotateBatch (v, 90, buf, size=2, type=float)

and so forth. It might sound complex right now, but in fact it's quite
easy to realise it - we do it the whole time with surfaces :-).
 
[...]
> > 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 on floating point vectors (though using double precision).
> +0 on int/long. I would like to see some use cases of int vectors.
> -1 on the names. I think floating point is the thing users normally want so I 
> would call that Vector, Vector3d or whatever. The int vector is a special case 
> and therefore wait in second line with a name like IVector (in python3 we only 
> have int so I would go for the "I" instead of the "L")

Those were just examples. Personally I'd favour Vector and IntVector
(IVector sounds like an interface to me). IntVector however is something
to be considered later on.
 
> >> 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.
> 
> hm. ok. but the ip is after the name: v.normalize_ip()

Sure.

> >> 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.
> > 
> 
> true, but I think this can be very usefull. and I can live with the fact that
> v.length = 1.
> v.length == 1.  # -> false
> when dealing with floating point variables you have to expect weird behavior 
> like this.

As I said, it's risky. If I set v.length = 1, I expect v.length == 1 to
work. Any other behaviour is wrong for both, readability and behaviour
expectations, in my opinion.
 
> >> 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.
> 
> well. I thought you rarely want to twiddle with this so I marked it private. I 
>   thought the normal user doesn't want to be bothered with this and the pro can 
> access it.

If the normal user does not need it, he won't use it :-).

> >> 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?
> 
> with repr I wanted to make explicitly clear that this is not a list but I 
> thought that the normal str() would look nicer that way. what would you 
> suggest? "[x, y, z]" for both (or "(x, y, z)" if we choose to make vectors 
> immutable) or "Vector3d<x, y, z>"?

I can live with any of those. I just wondered about the difference.
 
> 
> >> 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.
> 
> I just checked: unfortunately pygame seems to be inconsistent: transform.rotate 
> use degrees and draw.arc uses radians.

Which is a problem to get solved. Thus we should stick to degrees.

> 
> >> 1.10.2  what about slicing?
> > 
> > Slicing in a fixed 1-column matrix sounds useless to me.
> 
> the thinking was that we might want to provide as much of the python sequence 
> interfaces as possible so that vectors can be used anywhere list/tuples are 
> used/expected and let the user figure out if it makes any sense. I don't see a 
> reason to prohibit it.

My failure - I mixed up subscripts and slices. Of course slices are
useful and necessary.
 
> >> 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.
> 
> I'm -1 on implicit element-wise multiplication. when I instantiate two vector 
> and multiply them I expect a scalar product to be carried out. But I do realize 
> that element-wise multiplication is useful in some cases. therefor I would 
> suggest to have a method v.elementwise() which returns a proxy performing the 
> following op elementwise. that could also include division, modulo and abs.

That's the thing I hate about math: 'define * to perform the dot product
on vectors' :-). v.elementwise() sounds like a good solution - we just
have to assure that the docs outline the multiplication behaviour.

Attachment: pgpptlBn1q21x.pgp
Description: PGP signature