Vec3 zeroing out?

I encountered this strange behavior that I don’t remember happening in the past, and am wondering if it is suppossed to do this.

from pandac.PandaModules import Vec3
v = Vec3()
v += Vec3(3,2,5)
z = Vec3()
print v
print z

In this, v=<3,2,5> and z=<0,2,5> but shouldn’t z be <0,0,0> or am I supposed to use Vec3.zero() always? I thought that initiating a Vec3 with no parameters would return a zerod vector…

Another problem, upon expierementing with Vec3.zero(), is that you cannot change the vector then: it is stuck at (0,0,0) and you cannot add to it or subtract it… I changed the above first line to v=Vec3.zero() and it errors on the v+=… line. Is there a way to initialize a non-const vector to zeros or do I have to write Vec3(0,0,0) every time?

What platform/version are you using?

This is eigther a strange bug from your computer or a serious problem. Everyone would expect a Vec3() to be 0,0,0 by default.

Check if another version leads to the same problem. If it’s a version depending problem, re-download/install it and check again.

I have the same problem here. It appears to go away if you do something else (create a different vector) before creating the empty Vec3.

Not true! A Vec3() is unitialized memory. It might happen to be 0,0,0 on certain days of the week. If you rely on this behavior, though, you’re asking for trouble!

The reason this is unitialized memory is because the Vec3 class is implemented in C++, was originally designed to be used for C++, and in fact is used often in C++, and we don’t want to pay the cost for initializing this memory needlessly in C++.

If you want a Vec3() that is really zero and that you can modify, use Vec3(0).

David

Hmmm i think is !very! unpythonic… a=int() is allways 0, etc…

i’d highly recommend to change that behavior. At least if it’s called from python, i think this small delay doesnt matter in contrast to the possible problems. Dont think c++ in python, it’s the ease of use it makes it popular…

I have actually never thought that Vec3 might be something else then 0,0,0 when created. I’d even fork panda3d for to have this “feature” :wink:

Sure, I agree. In Python, that small time to initialize the data to zero is nothing, and it’s well worth the cost in order to avoid user confusion.

But there are other non-Pythonic aspects of Panda, things that can lead to similar confusions. The fact that Vec3.zero() can’t be modified is weird to Python users. The fact that if you let go of your Actor pointer, the actor stops playing is weird. The fact that if you find an Actor out of the scene graph you just get back a regular NodePath is weird. The fact that you can’t pass parameters by keyword to most Panda calls is weird. The fact that you can overload parameters with different kinds of arguments, and get different behaviors, is weird.

The list goes on. All of these non-Pythonic aspects, require a certain amount of user familiarity to learn the rules. They all exist because, frankly, Panda is not written in Python. It’s written in C++, and we’ve done a pretty good job of bolting it seamlessly to Python, but when you scratch the surface you can’t help but discover the C++ nature of it underneath, and some of these things have no good answer other than “you have to do it that way”.

In this particular case, we do need to have the default constructor perform no initialization–that’s very important for the low-level C++ performance.
But there’s no easy way to make the default constructor do one thing in Python, and something different in C++. (It could be done, but we’d have to shadow the constructor in Python, which would be likely to lead to greater user confusion down the line.)

However, it is possible to hide the default constructor from Python, so that Python programmers will no longer be able to call it at all–they’ll always have to use Vec3(0) or some other variant to create a new Vec3. Unfortunately, when I tried to do this, I immediately ran into a problem: there’s too much existing Python code that just creates a Vec3() on the fly (and then immediately initializes it, properly). If I made this change, all of this existing code would suddenly stop working.

At the end of the day, my conclusion was: we might be best off leaving it the way it is. New programmers learning Panda will come across this issue from time to time, and quickly learn that Vec3() is not necessarily zero. Add it to the list of things people must learn. It should be pointed out in the manual, of course, but we don’t really have a manual chapter that talks about the math libraries yet.

Note that this exact same issue has come up before: https://discourse.panda3d.org/viewtopic.php?t=2769

David

i second Hypnos if its a python Vec3() it should be same as Vec(0)

dont take me wrong, i know how hard it is to make something work so good as panda3d is. And now i understand the problems why it wasnt done the way i suggest.

Some things we should look at:

  • include this in the manuals
  • check the examples if they are using Vec3 correctly
  • post this on the front page of www.panda3d.org (just kidding)

I just was a bit shocked, that even after about 4 years of panda3d experience i have never experienced this error (well maybe i have but i didnt find the cause of it).

I might have misunderstood something from your explanation but woudnt changeing pandac/PandaModules.py the following fix this problem?

from libpandaexpressModules import *
from libpandaModules import *
from libpandaphysicsModules import *
from libdirectModules import *
from libpandafxModules import *
from libpandaeggModules import *

class Vec3(Vec3):
  def __init__( self, *args ):
    if len(args) == 0:
      super( Vec3, self ).__init__( 0 )
    else:
      super( Vec3, self ).__init__( *args )

edit: changed the possible fix

Yes, but:
(a) PandaModules.py is a generated file. So changes made here wouldn’t stick.
(b) There is, however, a place we can put changes like this. They’re buried in direct/src/extensions_native. However, this is a slippery slope. We’d have to create extensions for Vec2, Vec3, Vec4, Point2, Point3, Point4, VBase2, VBase3, VBase4, Mat3, Mat4, Quat, LRotation, LOrientation, as well as the “D” versions of all of these (Vec2D, Vec3D, etc.), in order to shadow all of these constructors. Then, what about Plane() and other similar classes, which are in a different part of the code? And how about the collision solids? And numerous other little classes that aren’t coming to mind right now? And then, what happens when someone adds a new class to this list, but doesn’t know about these shadows; or someone changes one of the default constructors in C++, and the change doesn’t become apparent in Python, because we’re no longer calling the default constructor?
(c) Finally, I suggest that the problem itself is actually quite small. As you point out, you’ve been using Panda for years, and you never ran into it. Many, many other Panda programmers have likewise never encountered it. I think this is because, even in Python, it’s rare to do something like a = int(); normally you would be explicit: a = 0. As Guido says, explicit is better than implicit, so if you really mean you want a Vec3 with all components zero, it’s better to spell that out: a = Vec3(0, 0, 0). I think this is the way most programmers tend to think anyway.

David

I do agree with most of the things you said (or i understand the implactions). But i disagree at one point, it’s a simple point but i has quite serve influences on the whole thing.

“It’s the way programmer’s think.”

I use python/panda3d because i want to forget about most of the things programmers think about (although i consider myself a programmer (or at least scripter)). Python is sooo nice to use, because we dont have to think about memory handling and memory initialisation.

And while it may make things a bit more inconsistent inside panda3d, if this fix was applied the most widely used things (Vec3, Point3), probably nobody would have ever found out about it, and it would make panda3d’s python interface appear more pythonic.

I know this discussion is a bit stuck at this point,
however it’s just my 2cents, and i wanted to show them to you :slight_smile:

I hear you. I feel a little dirty about attempting to sweep the problem under the rug.

But I also feel that we either have to solve it 100% or 0%; solving it for 60% of the classes is worse than not solving it at all. And I just don’t have the resources to reliably solve it for 100% right now.

David

Here are my two cent:

v = Vec( )

You asked for a vector, and you got a vector. You didn’t say which vector you want, so you get a random vector.

v = Vec( 0, 0, 0 )

You asked for the vector (0,0,0), and you got exactly this vector.

Perfectly sane, and exactly what I would expect. Imagine you go to a car dealer, and say “I want to have a car. Here is the cash”. You should not be pissed when you get a pile of rust with wheels on it. You have to be explicit: “I want to have this car over there”. So it is just like real life. You get what you ask for. Try this:

import this

enn0x

oh great, from now on i have to check len(list()) before i can use it… Oh didnt do that in the past? Well you asked for a list, you got a list, you even got free values in it…

let the flamewars begin…

There really should be a list of these “gotchas” in the manual or something. It would prevent a lot of confusion, and probably prevent new people from giving up on panda. Still, none of these problems you mention compare to the potential uninitialized memory bugs.

I’ll have to respectfully, but strongly disagree on this point. There is almost no way a Python programmer without C/C++ experience can be expected to find such a bug, because sometimes Vec3() really is (0,0,0)! Uninitialized memory isn’t supposed to exist in a language like Python.

We’ve already talked about this, but looking back at some of my own code it seems that Vec3() was the source of the errors. I had examined my code for days but found nothing. I had all but given up on Panda because I couldn’t find the bug. Since I couldn’t find the problem in my code after thorough inspection (the problem was inconsistent at best) the only logical conclusion I could come to was that this was a problem with the engine.

I came to the unfortunate conclusion that Panda3D was a bug-ridden half-finished difficult-to-use project instead of the easy to use rock-solid rapid prototyping tool advertised.

I’m disappointed to find out that you didn’t correct this. Couldn’t you simply find all instances of the string “vec3()” in the Python code and replace it with “vec3(0)”? Surely emacs can do a simple find and replace operation like that?

It may reduce the performance slightly (but you put all the performance intensive stuff in C++, right?), but it wouldn’t cause anything weird to happen, since as you said, it immediately gets initialized properly anyway.

I understand you’re busy. You and Josh Yelon hold this forum together in addition to all the work you do. Still, I really feel something should be done about this. If you can’t fix it now, at least put a warning in a “gotchas” page in the manual until you do. I’ll help if I can, but the manual is kind of messy. There’s a thread in General Discussion about overhauling the manual. I don’t want to make the problem worse, so I’ll wait for some direction.

I can–in all of the code that I have access to. There are dozens of Panda users within Disney alone, though, and thousands of Panda users worldwide, and each of them has their own Python code. Last time I tried to make a change with low-level implications like this, I was experiencing the fallout for months.

I admit to being a bit surprised at the vociferousness of the objections, though. I’ll grant you that it is a nasty “bug” to be burned by.

I’ll see if I can’t work out some more palatable solution for all.

David

Sorry to open this box…but I have to say that I “wasted” a day and a half looking for my error, but now that I know it is fixed, so thank you…

Hmm, that is a problem. When you said that there is too much existing Python code that uses the uninitialized constructor, I thought you just meant in the engine itself. Unititialized variables are just so unpythonic that it didn’t occur to be that there were so many other programs it would break.

What can be done? I’m just brainstorming here. The status quo is unacceptable.

At the very least this problem should be addressed in the manual. No matter what we decide to do, we should probably do that in the meantime anyway as a stopgap measure.

Here’s a list of ideas starting with the most extreme:

  • Make a fork of Panda. I don’t like the idea though. The community is one of the best things Panda3D has. I wouldn’t want to divide it.
  • Make a new major version of Panda (2.0.0) that doesn’t guarantee backwards compatibility. That would also provide an opportunity to correct lots of entrenched problems that would otherwise be too difficult to deal with. It would be a lot of work though. I’m not sure we’re ready to take this step.
  • Change the underlying C++ code to always zero the vectors and such. This would fix the problem without breaking existing Python code, but as was pointed out, Panda needs this optimization. The performance hit would probably be unacceptable.
  • Change the default constructors in Python to always zero vectors and such but not in C++. This would do what everyone wants, but may make maintaining Panda3D much more difficult. I think it’s a much better idea to simply –
  • Hide the default constructor from Python and deal with the fallout. As you said, this could break existing code. However, as evidenced by this thread, mindstormss, Hypnos, pro-rsoft, treeform, myself, and probably the majority of Python programmers expected the default constructors to do something else. In our cases this change would have actually helped to fix our code. It’s much easier to find a line of code that throws an exception saying “this function doesn’t exist” rather than doing something we didn’t expect only some of the time!

It’s also arguably bad practice to use uninitialized variables in Python even for those who know what they’re doing. Why take the risk of a major, but hidden bug for what in Python is a meager or even negligible performance gain? Besides, it’s a very simple fix in their case, if they know about it. As I pointed out, you could just do a find-and-replace. (e.g. replace all instances of Vec3() with Vec3(0) )

  • Change the default constructors in Python to emit a warning about uninitialized variables, then call their C++ counterpart as usual. This would make the bugs easy to find without breaking existing code, but would arguably be almost as difficult as changing the default Python constructors to just zero the variables.
  • Hide the default constructors from Python and make new additions to the engine more modular. The users who want to keep the old behavior, but would like to use new features would use an old version of Panda, but add the new modules they want. I’m really not sure how feasible this is.
  • Add a compiler switch or something that either hides the default constructor from Python or doesn’t. The released binaries (what the new users usually use) hides the default constructor from Python, but the old users could still use the old behavior if they want by recompiling the new versions from source with the switch.
    Just some thoughts :stuck_out_tongue: I’m sure I haven’t thought of everything.

Gosh, I think you’re over-thinking it. In fact, I’ve just implemented a solution, involving:

I did this by making changes to interrogate to support the concept of default constructors that behave differently in Python than in C++, and adding the appropriate definitions for the new Python-only default constructors for these linear algebra classes.

So the code is now a little more magical than it was, and this might confuse some Panda developer in the future. But it’s not so very bad, and everyone here has successfully persuaded me that it’s much better than keeping the code the way it was.

With luck, Josh may be able to pick up this fix for 1.5.1, and we can put this whole thing to bed.

David

wow David you are a star!

Thanks drwr you’re the best :smiley:

Does this go for all those math classes with uninitialized variables?

or just Vec3?