Dynamic game programming

If functions are rebinded, you’ll see a difference only if the function is called, which is not necessary the case with init. To check if it works, you should try with a function that is called in the main loop.

Andre_Mikulec > great work ! I haven’t tried yet because I’m not using eclipse but it sounds interesting. I’d certainly use it if I knew how to use it as an editor, ie without using “projects” (I often move, rename, duplicate python files quickly).

Good news, guys !

@ Cyan :

Of course, since you didn’t remove it before creating new instance. Simply removing every instance item using Python standard deletion won’t work, because the C++ object would remain. So it has to be removed using it’s own C++ destructor.

In my revision, I destroy some known type of C++ objects using it’s C++ destructor, and the rest will be removed using Python “None” override.
I guess, I’ve done it correctly :

  1. find the class and replace it
  2. find and remove all of the old class instances
  3. and create instances of the new class, exactly where the old ones belonged to

Here is my Finder.py revision :

ListOrTuple=[types.ListType,types.TupleType]
destructor = {
          'NodePath':'detachNode',
          'OnscreenText':'destroy',
          'DirectObject':'ignoreAll',
          'MetaInterval':'finish',
          }

def findClass(className):
    """
    Look in sys.modules dictionary for a module that defines a class
    with this className.
    """
    for moduleName, module in sys.modules.items():
        # Some modules are None for some reason
        if module:
            # print "Searching in ", moduleName
            classObj = module.__dict__.get(className)
            # If this modules defines some object called classname and the
            # object is a class or type definition and that class's module
            # is the same as the module we are looking in, then we found
            # the matching class and a good module namespace to redefine
            # our class in.
#            if (classObj and
#                ((type(classObj) == types.ClassType) or
#                 (type(classObj) == types.TypeType)) and
#                (classObj.__module__ == moduleName)):
#                return [classObj, module.__dict__]
            if (classObj and
               type(classObj) == types.ClassType and
               classObj.__module__ == moduleName):
               return [classObj, module.__dict__]
    return None

def rebindClass(builtinGlobals, filename):
    file = open(filename, 'r')
    lines = file.readlines()
    for i in xrange(len(lines)):
        line = lines[i]
        if (line[0:6] == 'class '):
            # Chop off the "class " syntax and strip extra whitespace
            classHeader = line[6:].strip()
            # Look for a open paren if it does inherit
            parenLoc = classHeader.find('(')
            if parenLoc > 0:
                className = classHeader[]
            else:
                # Look for a colon if it does not inherit
                colonLoc = classHeader.find(':')
                if colonLoc > 0:
                    className = classHeader[]
                else:
                    print 'error: className not found'
                    # Remove that temp file
                    file.close()
                    # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !!
                    #os.remove(filename)
                    return
            print '#################################################'
            print '####### REBIND STARTED ##########################'
            print '## Rebinding Class : %s'%className
            # YNJH_JO : let's rebind all classes in a file, don't break it !
            #break

            # YNJH_JO : to rebind the next classes, I indented the remaining of these lines
            # Try to find the original class with this class name
            res = findClass(className)

            if not res:
                print ('Warning: Finder could not find class')
                # Remove the temp file we made
                file.close()
                # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !!
                #os.remove(filename)
                return

            # Store the original real class
            realClass, realNameSpace = res

            # Now execute that class def in this namespace
            execfile(filename, realNameSpace)

            # That execfile should have created a new class obj in that namespace
            tmpClass = realNameSpace[className]

            # Copy the functions that we just redefined into the real class
            copyFuncs(tmpClass, realClass)

            # Now make sure the original class is in that namespace,
            # not our temp one from the execfile. This will help us preserve
            # class variables and other state on the original class.
            realNameSpace[className] = realClass

            ####################################################################
            # YNJH_JO : find, remove, and replace all instances of the old class
            print '//=====REMOVED INSTANCE ITEMS====='
            myWorld=sys.modules['__main__']
            for k in myWorld.__dict__.keys():
                findNreplaceInstance(realClass,myWorld,k)
            print '\\\================================'
            # end
            ####################################################################

    # Remove the temp file we made
    file.close()
    # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !!
    #os.remove(filename)
    print '####### FINISHED REBIND #########################'

def findNreplaceInstance(realClass,namespace,key,index=None):
    if index!=None:
       item=namespace.__dict__[key][index]
    else:
       item=namespace.__dict__[key]
    if type(item)==types.InstanceType:
       if isinstance(item,realClass):
          # class instance found !!
          # then remove it away for good
          #print 'instance of <%s>: '%realClass.__name__,classInst
          for i in item.__dict__.keys():
              removeItem(item.__dict__[i])
          destroyItem(item)
          # and re-create the instance
          if index!=None:
             namespace.__dict__[key][index]=realClass()
          else:
             namespace.__dict__[key]=realClass()
       else:
          for k in item.__dict__.keys():
              findNreplaceInstance(realClass,item,k)
    elif type(item) in ListOrTuple:
       for i in range(len(item)):
           findNreplaceInstance(realClass,namespace,key,index=i)
    elif type(item)==types.DictType:
       for k in item.keys():
           findNreplaceInstance(realClass,namespace,key,index=k)

def removeItem(item):
    if type(item) in ListOrTuple:
       for i in item:
           removeItem(i)
       item=None
    elif type(item)==types.DictType:
       for i in item.keys():
           removeItem(item[i])
       item=None
    elif type(item)==types.InstanceType:
       for i in item.__dict__.keys():
           removeItem(item.__dict__[i])
       destroyItem(item)
    else:
       destroyItem(item)

def destroyItem(item):
    print '||  - %s <ClassName>: %s' %(item, item.__class__.__name__)
    if item.__class__.__name__ in destructor:
       getattr(item,destructor[item.__class__.__name__])()
       print '||    (DESTROYED)'
    else:
       destroyed=0
       if hasattr(item.__class__,'__bases__'):
          baseClasses=item.__class__.__bases__
       if baseClasses:
          print '||   bases:',
          for b in baseClasses:
              print b.__name__+',',
              try:
                 if b.__name__ in destructor:
                    getattr(item,destructor[b.__name__])()
                    destroyed=1
              except:
                  pass
          print
       if destroyed:
          print '||    (DESTROYED from base class)'
       else:
          item=None
          print '||    (ERASED)'

EDIT :
I missed something. It could not find instances if they’re in a sequence (list, tuple, dictionary).
Now it can, but still doesn’t work for PandaSteer (I tried changing things at character level).

Major changes :

  1. the aforementioned weakness was a bug. It’s when each instance has a sequence of a pointer to the other instances, so there were outstanding everlasting circular references. Fixed by remembering if an instance was visited or not.
  2. no longer using non-reliable
    “if isinstance(item,realClass)”,
    now uses
    “if str(realClass)==str(item.class.module+’.’+item.class.name)”
  3. instance’s base classes destroyed first, and the next is it’s class’ turn.

updated code :

# YNJH_JO : additional stuff needed to go dynamic
import traceback
debugDynamic=0
visitedInstance = []
ListOrTuple = [types.ListType,types.TupleType]
destructor = {
          'Task':['remove'],
          'FSM':['cleanup'],
          'Actor':['stop','delete'],
          'DirectObject':['ignoreAll'],
          'DirectDialog':['cleanup'],
          'DirectFrame':['destroy'],
          'OnscreenText':['destroy'],
          'NodePath':['detachNode'],
          'MetaInterval':['finish'],
          'LerpFunctionInterval':['finish'],
          'LerpNodePathInterval':['finish'],
          }

def truncStr(item,length):
    s=str(item)[:length]
    if len(s)==length:
       s+='...'
    return s

def findClass(className):
    """
    Look in sys.modules dictionary for a module that defines a class
    with this className.
    """
    for moduleName, module in sys.modules.items():
        # Some modules are None for some reason
        if module:
            # print "Searching in ", moduleName
            classObj = module.__dict__.get(className)
            # If this modules defines some object called classname and the
            # object is a class or type definition and that class's module
            # is the same as the module we are looking in, then we found
            # the matching class and a good module namespace to redefine
            # our class in.
#            if (classObj and
#                ((type(classObj) == types.ClassType) or
#                 (type(classObj) == types.TypeType)) and
#                (classObj.__module__ == moduleName)):
#                return [classObj, module.__dict__]
            if (classObj and
               type(classObj) == types.ClassType and
               classObj.__module__ == moduleName):
               return [classObj, module.__dict__]
    return None

def rebindClass(builtinGlobals, filename):
    try:
       file = open(filename, 'r')
       lines = file.readlines()
       for i in xrange(len(lines)):
           line = lines[i]
           if (line[0:6] == 'class '):
               # Chop off the "class " syntax and strip extra whitespace
               classHeader = line[6:].strip()
               # Look for a open paren if it does inherit
               parenLoc = classHeader.find('(')
               if parenLoc > 0:
                   className = classHeader[]
               else:
                   # Look for a colon if it does not inherit
                   colonLoc = classHeader.find(':')
                   if colonLoc > 0:
                       className = classHeader[]
                   else:
                       print 'error: className not found'
                       # Remove that temp file
                       file.close()
                       # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !!
                       #os.remove(filename)
                       return
               print '#################################################'
               print '####### REBIND STARTED ##########################'
               print '### Rebinding Class : %s'%className
               # YNJH_JO : let's rebind all classes in a file, don't break it !
               #break

               # YNJH_JO : to rebind the next classes, I indented the remaining of these lines
               # Try to find the original class with this class name
               res = findClass(className)

               if not res:
                   print ('Warning: Finder could not find class')
                   # Remove the temp file we made
                   file.close()
                   # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !!
                   #os.remove(filename)
                   return

               # Store the original real class
               realClass, realNameSpace = res

               # Now execute that class def in this namespace
               execfile(filename, realNameSpace)

               # That execfile should have created a new class obj in that namespace
               tmpClass = realNameSpace[className]

               # Copy the functions that we just redefined into the real class
               copyFuncs(tmpClass, realClass)

               # Now make sure the original class is in that namespace,
               # not our temp one from the execfile. This will help us preserve
               # class variables and other state on the original class.
               realNameSpace[className] = realClass

               ####################################################################
               # YNJH_JO : find, remove, and replace all instances of the old class
               global visitedInstance
               visitedInstance=None
               visitedInstance=[]
               if debugDynamic:
                  print '//=====REMOVED INSTANCE ITEMS====='
               myWorld=sys.modules['__main__']
               for k in myWorld.__dict__.keys():
                   findNreplaceInstance(realClass,myWorld,k)
               if debugDynamic:
                  print '\\\================================'
#                print newInstanceQueue
#                print visitedInstance

               # end
               ####################################################################

       # Remove the temp file we made
       file.close()
       # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !!
       #os.remove(filename)
       print '####### FINISHED REBIND #########################'
       #print taskMgr
    except Exception:
        print '''#####################################
               \nEXCEPTION WHILE REBINDING THE CLASS :
               \n#####################################'''
        traceback.print_exc()

def findNreplaceInstance(realClass,namespace,key,index=None):
    if index!=None:
       item=namespace.__dict__[key][index]
    else:
       item=namespace.__dict__[key]
    if type(item)==types.InstanceType:
#       print item.__class__.__module__+'.'+item.__class__.__name__
       if str(realClass)==str(item.__class__.__module__+'.'+item.__class__.__name__):
          # CLASS INSTANCE FOUND !!
          # then remove it away for good
          if item not in visitedInstance:
             #print 'INSTANCE OF <%s>: '%realClass.__name__,item
             visitedInstance.append(item)
             for i in item.__dict__.keys():
                 removeItem(item.__dict__[i])
             destroyItem(item)
          else:
             if debugDynamic:
                print 'VISITED INSTANCE :',truncStr(item,50)
          # time to create an instance of the rebinded class
          if index!=None:
             namespace.__dict__[key][index]=realClass()
          else:
             namespace.__dict__[key]=realClass()
       else:
          # not the CLASS INSTANCE we want
          if item not in visitedInstance:
             visitedInstance.append(item)
             for k in item.__dict__.keys():
                 #print 'III:',k,item.__dict__[k]
                 findNreplaceInstance(realClass,item,k)
          else:
             if debugDynamic:
                print 'VISITED INSTANCE :',truncStr(item,50)
    elif type(item) in ListOrTuple:
       #print item
       for i in range(len(item)):
           #print i,item[i]
           findNreplaceInstance(realClass,namespace,key,index=i)
    elif type(item)==types.DictType:
       for k in item.keys():
           findNreplaceInstance(realClass,namespace,key,index=k)

def removeItem(item):
    if type(item) in ListOrTuple:
       for i in item:
           removeItem(i)
       item=None
    elif type(item)==types.DictType:
       for i in item.keys():
           removeItem(item[i])
       item=None
    elif type(item)==types.InstanceType and item not in visitedInstance:
       visitedInstance.append(item)
       for i in item.__dict__.keys():
           removeItem(item.__dict__[i])
       destroyItem(item)
    else:
       destroyItem(item)

def destroyItem(item):
    if debugDynamic:
       print '||= '+truncStr(item,70)
       print '||    <ClassName>: '+item.__class__.__name__
    destroyed=0
    if hasattr(item.__class__,'__bases__'):
       baseClasses=item.__class__.__bases__
    if baseClasses:
       if debugDynamic:
          print '||      bases:',
       for b in baseClasses:
           if debugDynamic:
              print b.__name__+',',
           if b.__name__ in destructor:
              if b.__name__=='Task':
                 taskMgr.remove(item)
              for d in destructor[b.__name__]:
                  getattr(item,d)()
              destroyed+=1
       if debugDynamic:
          print
    if destroyed:
       if debugDynamic:
          print '||      (DESTROYED from %i base class(es))'%destroyed
    if item.__class__.__name__ in destructor:
       if item.__class__.__name__=='Task':
          taskMgr.remove(item)
       for d in destructor[item.__class__.__name__]:
           getattr(item,d)()
       item=None
       if debugDynamic:
          print '||    (DESTROYED)'
    elif not destroyed:
       item=None
       if debugDynamic:
          print '||    (NULLIFIED)'

I used this scene :
main.py :

from pandac.PandaModules import *
import direct.directbase.DirectStart
from direct.interval.IntervalGlobal import *
from direct.gui.DirectGui import *
from direct.showbase.DirectObject import DirectObject
from direct.task import Task
from random import random, gauss
import sys

from smileyClass import Smiley
from pandaClass import Panda

class World(DirectObject):
  def __init__(self):
      camera.setPos(-20,-25,10)
      camera.lookAt(render)
      mat=Mat4(camera.getMat())
      mat.invertInPlace()
      base.mouseInterfaceNode.setMat(mat)
      self.accept('escape',sys.exit)

      self.title=OnscreenText(text = 'TITLE : try to change me', pos = (base.getAspectRatio()*.97, -.97), fg=(1,1,1,1),
                        shadow=(0,0,0,1),shadowOffset=(.01,.01),align = TextNode.ARight, scale = .07, mayChange=1)
      # create jacks
      num=5
      self.jacks=render.attachNewNode('')
      for j in range(num):
          jack=loader.loadModel('jack')
          jack.reparentTo(self.jacks)
          jack.setScale(.5)
          jack.setPos(-(num-1)*.5+j,-10,0)
          jack.setH(180)
      self.jacks.hprInterval(2,Vec3(0,0,360)).loop()
      taskMgr.add(self.changeColor,'cc')

      # create smileys
      self.smiley=Smiley()

      # create panda
      num=5
      self.pandas=[]
      for p in range(num):
          self.pandas.append(Panda())
      for p in range(num):
          for n in range(num):
              if p!=n:
                 self.pandas[p].other.append(self.pandas[n])

      # OK dialog
      self.OKdialog1 = OkDialog(text='OK DIALOG')
      self.OKdialog1.setColorScale(1,1,1,.9)

      # DirectEntry
      self.entrybox = DirectEntry(pos=Vec3(0,0,-.5),frameColor=(.50,.8,.9,.5),
           relief=DGG.GROOVE,text = "" ,scale=.05, width=18, numLines = 3,focus=1)

  def changeColor(self,t):
      self.jacks.setColorScale(random(),random(),random(),1)
      taskMgr.doMethodLater(.1,self.changeColor,'cc')

smileyClass.py :

from pandac.PandaModules import Vec3
from direct.interval.IntervalGlobal import *
from math import pi, sin

class Smiley:
  def __init__(self):
      scale=.9
      num=int(10/scale)
      self.smileys=[]
      self.moves = [0 for i in range(num)]
      self.roll = [0 for i in range(num)]
      for s in range(num):
          smi=loader.loadModel('smiley')
          smi.reparentTo(render)
          smi.setScale(scale)
          smi.setPos((-(num-1)*.5+s)*scale*2,-5,scale*1.25)
          self.smileys.append(smi)
          self.moves[s] = LerpFunc(
                       self.oscilateSmiley,
                       duration = .8,
                       fromData = 0,
                       toData = 2*pi,
                       extraArgs=[self.smileys[s], pi*(s%2)]
                       )
          self.moves[s].loop()
          self.roll[s]=self.smileys[s].hprInterval(.5,Vec3(0,0,360))
          self.roll[s].loop()

  def oscilateSmiley(self, rad, np, offset):
      np.setZ(sin(rad + offset) * .5)

pandaClass.py :

from direct.actor import Actor
from random import random,gauss

class Panda:
  def __init__(self):
      self.pandaModel=Actor.Actor('panda',{'walk':'panda-walk'})
      self.pandaModel.reparentTo(render)
      self.pandaModel.setScale(.4+random()*.2)
      self.pandaModel.setPos(gauss(0,6),gauss(0,3),0)
      self.pandaModel.setColorScale(random()*.7,random(),random(),1)
      self.pandaModel.loop('walk')
      self.other=[]

Each panda has a list of pointers to other pandas.

EDIT :
Another version of pandaClass.py, just for fun :

from direct.actor import Actor
from random import random,gauss

class Panda:
  def __init__(self):
      self.pandaModel=Actor.Actor('panda',{'walk':'panda-walk'})
      self.pandaModel.reparentTo(render)
      scale=.2+random()*.7
      self.pandaModel.setScale(scale)
      self.pandaModel.setPos(gauss(0,6),gauss(0,3),0)
      self.pandaModel.setColorScale(.4+random()*.3,.7+random()*.3,.7+random()*.3,1)
      self.pandaModel.setPlayRate(1/scale,'walk')
      self.pandaModel.setP(1/(.7*scale*scale))
      self.pandaModel.loop('walk')
      self.other=[]

I set the playrate to follow the scale, to let the smaller panda look trying to keep up with the bigger one, to maintain equal apparent forward speed. I also adjust the pitch to follow the scale too, so the smaller, the lower it leans forward.

oops, there was a ridiculous bug in removeItem. I must was very very sleepy. FIXED.

Sorry it took me so long to post back. These last 4 months I have been swamped with work. Now, I finally have some free time again.

Below is a follow up to ynjh_jo’s wonderful work.

Here is screenshot of ynjh_jo’s dynamically programmable panda program in action.

The python panda source code below is the compiled work of Disney: VR Studio & drwr, raytaller, myself, and especially ynjh_jo.

Simply create files, copy, and paste the contents of the files below into your C:\Panda3D-1.3.2\samples\Basic-Tutorials–Lesson-2-Carousel directory.

The files are

ppythonRUN.bat
ClassUpdater.py
FileChangeNotifier.py
Finder.py
go.py
main.py
pandaClass.py
pandaClassFUN.py
smileyClass.py
TutCarousel.py

Alternately, these files can taken from the zip file

http://andre_mikulec.tripod.com/pythonpanda/dynamicgameprogramming.zip

and extracted to the C:\Panda3D-1.3.2\samples\Basic-Tutorials–Lesson-2-Carousel directory.

Numeric parameters in the classes in the files of main.py, pandaClass.py, pandaClassFUN.py, smileyClass.py, and TutCarousel.py (no hyphen) can be changed on the fly. These changes are immediately seen in the running program. No program restarts are required.

Many thanks go out to all of the authors.

Just run the batch file

C:\Panda3D-1.3.2\samples\Basic-Tutorials--Lesson-2-Carousel
>ppythonRUN.BAT

ynjh_jo’s main/World program will run first.
Change the numeric parameters in the classes of of main.py, pandaClass.py, pandaClassFUN.py, smileyClass.py.
Press save. See that changes in the running program immediately.
Press escape to quit

Change the file go.py lines from

from main import World 
# from TutCarousel import World

to

# from main import World 
from TutCarousel import World

Save.

Run again the batch file ppythonRUN.BAT

C:\Panda3D-1.3.2\samples\Basic-Tutorials--Lesson-2-Carousel
>ppythonRUN.BAT

Now, see the Disney (slighly modified) TutCarousel.py program.
Change the numeric parameters in the class of World of TutCarousel.py
Save.

To quit, press the X in the upper right hand corner of the window (the Window’s X).

The file contents are below.

ppythonRUN.bat

C:
cd C:\Panda3D-1.3.2\samples\Basic-Tutorials--Lesson-2-Carousel
set PATH=C:\Panda3D-1.3.2\python;C:\WINDOWS\system32;C:\WINDOWS

ppython.exe go.py

ClassUpdater.py

import Finder
from FileChangeNotifier import FileChangeNotifier


class ClassUpdater(object):
    
    def __onFileChange(self, filename):
        try:
            print 'File :', filename
            Finder.rebindClass(None, filename)
        except Exception, ex:
            print 'Exception while rebinding the class : ', ex
            
    
    def __init__(self, directory, interval):
        self.fcn = FileChangeNotifier(directory, self.__onFileChange, interval)
    
    def start(self):
        self.fcn.startMonitor()
    
    def stop(self):
        self.fcn.stopMonitor()

FileChangeNotifier.py

import os
import thread
import time


class FileChangeNotifier(object):

    def __init__(self, path, callback, interval=0.5):
        self.path_to_scan = path
        self.files = {}
        self.callback = callback
        self.interval = interval
        self.is_monitoring = False


    def __stat(self, filename):
        stat=os.stat(filename)
        return str(stat[6]) + '_' + str(stat[8])
    
        
    def __walkCallback(self, args, directory, files):
        for file_name in files:
              if file_name.endswith('.py'): 
                # print file_name
                filename = directory + '/' + file_name
                if self.files.has_key(filename):
                    nrepr = self.__stat(filename)
                    if not nrepr == self.files[filename]:
                        self.files[filename]=self.__stat(filename)
                        self.callback(filename)
                        
                else :
                    self.files[filename]=self.__stat(filename)

    def __monitor(self):
        while self.is_monitoring :
            os.path.walk(self.path_to_scan, self.__walkCallback, '')
            time.sleep(self.interval)

    def startMonitor(self):
        self.is_monitoring = True
        thread.start_new_thread(self.__monitor, ())
        #self.__monitor()

    def stopMonitor(self):
        self.is_monitoring = False

        


def mytho(fich):
    print fich + ' has changed'

if __name__ ==  '__main__':
    fcn = FileChangeNotifier('.', mytho)
    #fcn.startMonitor()

Finder.py

__all__ = ['truncStr', 'findClass', 'rebindClass', 'findNreplaceInstance', 'removeItem', 'copyFuncs', 'replaceMessengerFunc', 'replaceTaskMgrFunc', 'replaceStateFunc', 'replaceCRFunc', 'replaceAIRFunc', 'replaceIvalFunc']

# YNJH_JO : additional stuff needed to go dynamic 
import traceback
import types
import sys
debugDynamic=0 
visitedInstance = [] 
ListOrTuple = [types.ListType,types.TupleType] 
destructor = { 
          'Task':['remove'], 
          'FSM':['cleanup'], 
          'Actor':['stop','delete'], 
          'DirectObject':['ignoreAll'], 
          'DirectDialog':['cleanup'], 
          'DirectFrame':['destroy'], 
          'OnscreenText':['destroy'], 
          'NodePath':['detachNode'], 
          'MetaInterval':['finish'], 
          'LerpFunctionInterval':['finish'], 
          'LerpNodePathInterval':['finish'], 
          } 

def truncStr(item,length): 
    s=str(item)[:length] 
    if len(s)==length: 
       s+='...' 
    return s 

def findClass(className): 
    """ 
    Look in sys.modules dictionary for a module that defines a class 
    with this className. 
    """ 
    for moduleName, module in sys.modules.items(): 
        # Some modules are None for some reason 
        if module: 
            # print "Searching in ", moduleName 
            classObj = module.__dict__.get(className) 
            # If this modules defines some object called classname and the 
            # object is a class or type definition and that class's module 
            # is the same as the module we are looking in, then we found 
            # the matching class and a good module namespace to redefine 
            # our class in. 
#            if (classObj and 
#                ((type(classObj) == types.ClassType) or 
#                 (type(classObj) == types.TypeType)) and 
#                (classObj.__module__ == moduleName)): 
#                return [classObj, module.__dict__] 
            if (classObj and 
               type(classObj) == types.ClassType and 
               classObj.__module__ == moduleName): 
               return [classObj, module.__dict__] 
    return None 

def rebindClass(builtinGlobals, filename): 
    try: 
       file = open(filename, 'r') 
       lines = file.readlines() 
       for i in xrange(len(lines)): 
           line = lines[i] 
           if (line[0:6] == 'class '): 
               # Chop off the "class " syntax and strip extra whitespace 
               classHeader = line[6:].strip() 
               # Look for a open paren if it does inherit 
               parenLoc = classHeader.find('(') 
               if parenLoc > 0: 
                   className = classHeader[] 
               else: 
                   # Look for a colon if it does not inherit 
                   colonLoc = classHeader.find(':') 
                   if colonLoc > 0: 
                       className = classHeader[] 
                   else: 
                       print 'error: className not found' 
                       # Remove that temp file 
                       file.close() 
                       # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !! 
                       #os.remove(filename) 
                       return 
               print '#################################################' 
               print '####### REBIND STARTED ##########################' 
               print '### Rebinding Class : %s'%className 
               # YNJH_JO : let's rebind all classes in a file, don't break it ! 
               #break 

               # YNJH_JO : to rebind the next classes, I indented the remaining of these lines 
               # Try to find the original class with this class name 
               res = findClass(className) 

               if not res: 
                   print ('Warning: Finder could not find class') 
                   # Remove the temp file we made 
                   file.close() 
                   # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !! 
                   #os.remove(filename) 
                   return 

               # Store the original real class 
               realClass, realNameSpace = res 

               # Now execute that class def in this namespace 
               execfile(filename, realNameSpace) 

               # That execfile should have created a new class obj in that namespace 
               tmpClass = realNameSpace[className] 

               # Copy the functions that we just redefined into the real class 
               copyFuncs(tmpClass, realClass) 

               # Now make sure the original class is in that namespace, 
               # not our temp one from the execfile. This will help us preserve 
               # class variables and other state on the original class. 
               realNameSpace[className] = realClass 

               #################################################################### 
               # YNJH_JO : find, remove, and replace all instances of the old class 
               global visitedInstance 
               visitedInstance=None 
               visitedInstance=[] 
               if debugDynamic: 
                  print '//=====REMOVED INSTANCE ITEMS=====' 
               myWorld=sys.modules['__main__'] 
               for k in myWorld.__dict__.keys(): 
                   findNreplaceInstance(realClass,myWorld,k) 
               if debugDynamic: 
                  print '\\\================================' 
#                print newInstanceQueue 
#                print visitedInstance 

               # end 
               #################################################################### 

       # Remove the temp file we made 
       file.close() 
       # YNJH_JO : DO NOT REMOVE IT !!  IT'S NOT TEMP FILE !! 
       #os.remove(filename) 
       print '####### FINISHED REBIND #########################' 
       #print taskMgr 
    except Exception: 
        print '''##################################### 
               \nEXCEPTION WHILE REBINDING THE CLASS : 
               \n#####################################''' 
        traceback.print_exc() 

def findNreplaceInstance(realClass,namespace,key,index=None): 
    if index!=None: 
       item=namespace.__dict__[key][index] 
    else: 
       item=namespace.__dict__[key] 
    if type(item)==types.InstanceType: 
#       print item.__class__.__module__+'.'+item.__class__.__name__ 
       if str(realClass)==str(item.__class__.__module__+'.'+item.__class__.__name__): 
          # CLASS INSTANCE FOUND !! 
          # then remove it away for good 
          if item not in visitedInstance: 
             #print 'INSTANCE OF <%s>: '%realClass.__name__,item 
             visitedInstance.append(item) 
             for i in item.__dict__.keys(): 
                 removeItem(item.__dict__[i]) 
             destroyItem(item) 
          else: 
             if debugDynamic: 
                print 'VISITED INSTANCE :',truncStr(item,50) 
          # time to create an instance of the rebinded class 
          if index!=None: 
             namespace.__dict__[key][index]=realClass() 
          else: 
             namespace.__dict__[key]=realClass() 
       else: 
          # not the CLASS INSTANCE we want 
          if item not in visitedInstance: 
             visitedInstance.append(item) 
             for k in item.__dict__.keys(): 
                 #print 'III:',k,item.__dict__[k] 
                 findNreplaceInstance(realClass,item,k) 
          else: 
             if debugDynamic: 
                print 'VISITED INSTANCE :',truncStr(item,50) 
    elif type(item) in ListOrTuple: 
       #print item 
       for i in range(len(item)): 
           #print i,item[i] 
           findNreplaceInstance(realClass,namespace,key,index=i) 
    elif type(item)==types.DictType: 
       for k in item.keys(): 
           findNreplaceInstance(realClass,namespace,key,index=k) 

def removeItem(item): 
    if type(item) in ListOrTuple: 
       for i in item: 
           removeItem(i) 
       item=None 
    elif type(item)==types.DictType: 
       for i in item.keys(): 
           removeItem(item[i]) 
       item=None 
    elif type(item)==types.InstanceType and item not in visitedInstance: 
       visitedInstance.append(item) 
       for i in item.__dict__.keys(): 
           removeItem(item.__dict__[i]) 
       destroyItem(item) 
    else: 
       destroyItem(item) 

def destroyItem(item): 
    if debugDynamic: 
       print '||= '+truncStr(item,70) 
       print '||    <ClassName>: '+item.__class__.__name__ 
    destroyed=0 
    if hasattr(item.__class__,'__bases__'): 
       baseClasses=item.__class__.__bases__ 
    if baseClasses: 
       if debugDynamic: 
          print '||      bases:', 
       for b in baseClasses: 
           if debugDynamic: 
              print b.__name__+',', 
           if b.__name__ in destructor: 
              if b.__name__=='Task': 
                 taskMgr.remove(item) 
              for d in destructor[b.__name__]: 
                  getattr(item,d)() 
              destroyed+=1 
       if debugDynamic: 
          print 
    if destroyed: 
       if debugDynamic: 
          print '||      (DESTROYED from %i base class(es))'%destroyed 
    if item.__class__.__name__ in destructor: 
       if item.__class__.__name__=='Task': 
          taskMgr.remove(item) 
       for d in destructor[item.__class__.__name__]: 
           getattr(item,d)() 
       item=None 
       if debugDynamic: 
          print '||    (DESTROYED)' 
    elif not destroyed: 
       item=None 
       if debugDynamic: 
          print '||    (NULLIFIED)'

def copyFuncs(fromClass, toClass):
    replaceFuncList = []
    newFuncList = []

    # Copy the functions from fromClass into toClass dictionary
    for funcName, newFunc in fromClass.__dict__.items():
        # Filter out for functions
        if (type(newFunc) == types.FunctionType):
            # See if we already have a function with this name
            oldFunc = toClass.__dict__.get(funcName)
            if oldFunc:
                replaceFuncList.append((oldFunc, funcName, newFunc))
            else:
                newFuncList.append((funcName, newFunc))

    # Look in the messenger, taskMgr, and other globals that store func
    # pointers to see if this old function pointer is stored there, and
    # update it to the new function pointer.
    replaceMessengerFunc(replaceFuncList)
    replaceTaskMgrFunc(replaceFuncList)
    replaceStateFunc(replaceFuncList)
    replaceCRFunc(replaceFuncList)
    replaceAIRFunc(replaceFuncList)
    replaceIvalFunc(replaceFuncList)

    # Now that we've the globals funcs, actually swap the pointers in
    # the new class to the new functions
    for oldFunc, funcName, newFunc in replaceFuncList:
        # print "replacing old func: ", oldFunc, funcName, newFunc
        setattr(toClass, funcName, newFunc)
    # Add the brand new functions too
    for funcName, newFunc in newFuncList:
        # print "adding new func: ", oldFunc, funcName, newFunc
        setattr(toClass, funcName, newFunc)

def replaceMessengerFunc(replaceFuncList):
    try:
        messenger
    except:
        return
    for oldFunc, funcName, newFunc in replaceFuncList:
        res = messenger.replaceMethod(oldFunc, newFunc)
        if res:
            print ('replaced %s messenger function(s): %s' % (res, funcName))

def replaceTaskMgrFunc(replaceFuncList):
    try:
        taskMgr
    except:
        return
    for oldFunc, funcName, newFunc in replaceFuncList:
        if taskMgr.replaceMethod(oldFunc, newFunc):
            print ('replaced taskMgr function: %s' % funcName)

def replaceStateFunc(replaceFuncList):
    if not sys.modules.get('direct.fsm.State'):
        return
    from direct.fsm.State import State
    for oldFunc, funcName, newFunc in replaceFuncList:
        res = State.replaceMethod(oldFunc, newFunc)
        if res:
            print ('replaced %s FSM transition function(s): %s' % (res, funcName))

def replaceCRFunc(replaceFuncList):
    try:
        base.cr
    except:
        return
    for oldFunc, funcName, newFunc in replaceFuncList:
        if base.cr.replaceMethod(oldFunc, newFunc):
            print ('replaced DistributedObject function: %s' % funcName)

def replaceAIRFunc(replaceFuncList):
    try:
        simbase.air
    except:
        return
    for oldFunc, funcName, newFunc in replaceFuncList:
        if simbase.air.replaceMethod(oldFunc, newFunc):
            print ('replaced DistributedObject function: %s' % funcName)

def replaceIvalFunc(replaceFuncList):
    # Make sure we have imported IntervalManager and thus created
    # a global ivalMgr.
    if not sys.modules.get('direct.interval.IntervalManager'):
        return
    from direct.interval.FunctionInterval import FunctionInterval
    for oldFunc, funcName, newFunc in replaceFuncList:
        res = FunctionInterval.replaceMethod(oldFunc, newFunc)
        if res:
            print ('replaced %s interval function(s): %s' % (res, funcName))

go.py

from ClassUpdater import ClassUpdater
from main import World 
# from TutCarousel import World
# import pydevd # uncomment if using PyDev (eclipse)

w = World()
c = ClassUpdater('.', 1)
# pydevd.settrace()  # uncomment if using PyDev (eclipse)
c.start()
w.start()

main.py

from pandac.PandaModules import *
import direct.directbase.DirectStart 
from direct.interval.IntervalGlobal import * 
from direct.gui.DirectGui import * 
from direct.showbase.DirectObject import DirectObject 
from direct.task import Task 
from random import random, gauss 
import sys 

from smileyClass import Smiley 
from pandaClass import Panda 

class World(DirectObject): 
  def __init__(self): 
      camera.setPos(-20,-25,10) 
      camera.lookAt(render) 
      mat=Mat4(camera.getMat()) 
      mat.invertInPlace() 
      base.mouseInterfaceNode.setMat(mat) 
      self.accept('escape',sys.exit) 

      self.title=OnscreenText(text = 'TITLE : try to change me', pos = (base.getAspectRatio()*.97, -.97), fg=(1,1,1,1), 
                        shadow=(0,0,0,1),shadowOffset=(.01,.01),align = TextNode.ARight, scale = .07, mayChange=1) 
      # create jacks 
      num=5 
      self.jacks=render.attachNewNode('') 
      for j in range(num): 
          jack=loader.loadModel('jack') 
          jack.reparentTo(self.jacks) 
          jack.setScale(.5) 
          jack.setPos(-(num-1)*.5+j,-10,0) 
          jack.setH(180) 
      self.jacks.hprInterval(2,Vec3(0,0,360)).loop() 
      taskMgr.add(self.changeColor,'cc') 

      # create smileys 
      self.smiley=Smiley() 

      # create panda 
      num=5 
      self.pandas=[] 
      for p in range(num): 
          self.pandas.append(Panda()) 
      for p in range(num): 
          for n in range(num): 
              if p!=n: 
                 self.pandas[p].other.append(self.pandas[n]) 

      # OK dialog 
      self.OKdialog1 = OkDialog(text='OK DIALOG') 
      self.OKdialog1.setColorScale(1,1,1,.9) 

      # DirectEntry 
      self.entrybox = DirectEntry(pos=Vec3(0,0,-.5),frameColor=(.50,.8,.9,.5), 
           relief=DGG.GROOVE,text = "" ,scale=.05, width=18, numLines = 3,focus=1) 

  def changeColor(self,t): 
      self.jacks.setColorScale(random(),random(),random(),1) 
      taskMgr.doMethodLater(.1,self.changeColor,'cc')

  def start(self):
      run()

pandaClass.py

from direct.actor import Actor 
from random import random,gauss 

class Panda: 
  def __init__(self): 
      self.pandaModel=Actor.Actor('panda',{'walk':'panda-walk'}) 
      self.pandaModel.reparentTo(render) 
      self.pandaModel.setScale(.4+random()*.2) 
      self.pandaModel.setPos(gauss(0,6),gauss(0,3),0) 
      self.pandaModel.setColorScale(random()*.7,random(),random(),1) 
      self.pandaModel.loop('walk') 
      self.other=[]

pandaClassFUN.py

from direct.actor import Actor 
from random import random,gauss 

class Panda: 
  def __init__(self): 
      self.pandaModel=Actor.Actor('panda',{'walk':'panda-walk'}) 
      self.pandaModel.reparentTo(render) 
      scale=.2+random()*.7 
      self.pandaModel.setScale(scale) 
      self.pandaModel.setPos(gauss(0,6),gauss(0,3),0) 
      self.pandaModel.setColorScale(.4+random()*.3,.7+random()*.3,.7+random()*.3,1) 
      self.pandaModel.setPlayRate(1/scale,'walk') 
      self.pandaModel.setP(1/(.7*scale*scale)) 
      self.pandaModel.loop('walk') 
      self.other=[]

smileyClass.py

from pandac.PandaModules import Vec3 
from direct.interval.IntervalGlobal import * 
from math import pi, sin 

class Smiley: 
  def __init__(self): 
      scale=0.9 
      num=int(10/scale) 
      self.smileys=[] 
      self.moves = [0 for i in range(num)] 
      self.roll = [0 for i in range(num)] 
      for s in range(num): 
          smi=loader.loadModel('smiley') 
          smi.reparentTo(render) 
          smi.setScale(scale) 
          smi.setPos((-(num-1)*.5+s)*scale*2,-5,scale*1.25) 
          self.smileys.append(smi) 
          self.moves[s] = LerpFunc( 
                       self.oscilateSmiley, 
                       duration = .8, 
                       fromData = 0, 
                       toData = 2*pi, 
                       extraArgs=[self.smileys[s], pi*(s%2)] 
                       ) 
          self.moves[s].loop() 
          self.roll[s]=self.smileys[s].hprInterval(.5,Vec3(0,0,360)) 
          self.roll[s].loop() 

  def oscilateSmiley(self, rad, np, offset): 
      np.setZ(sin(rad + offset) * .5)

TutCarousel.py

# Author: Shao Zhang, Phil Saltzman, and Eddie Canaan
# Last Updated: 4/19/2005
#
# This tutorial will demonstrate some uses for intervals in Panda
# to move objects in your panda world.
# Intervals are tools that change a value of something, like position, rotation
# or anything else, linearly, over a set period of time. They can be also be
# combined to work in sequence or in Parallel
# 
# In this lesson, we will simulate a carousel in motion using intervals.
# The carousel will spin using an hprInterval while 4 pandas will represent
# the horses on a traditional carousel. The 4 pandas will rotate with the
# carousel and also move up and down on their poles using a LerpFunc interval.
# Finally there will also be lights on the outer edge of the carousel that
# will turn on and off by switching their texture with intervals in Sequence
# and Parallel

import direct.directbase.DirectStart
from pandac.PandaModules import Lerp
from pandac.PandaModules import AmbientLight, DirectionalLight, LightAttrib
from pandac.PandaModules import NodePath
from pandac.PandaModules import Vec3, Vec4
from direct.interval.IntervalGlobal import *   #Needed to use Intervals
from direct.gui.DirectGui import *

#Importing math constants and functions
from math import pi, sin

class World:
  def __init__(self):
    #This creates the on screen title that is in every tutorial
    self.title = OnscreenText(text="Panda3D: Tutorial 2 - Carousel",
                              style=1, fg=(1,1,1,1),
                              pos=(0.87,-0.95), scale = .07)

    base.setBackgroundColor(.6, .6, 1) #Set the background color
    base.disableMouse()                #Allow manual positioning of the camera
    camera.setPosHpr ( 0, -8, 2.5, 0, -9, 0 )  #Set the cameras' position
                                               #and orientation

    self.loadModels()                  #Load and position our models
    self.setupLights()                 #Add some basic lighting
    self.startCarousel()               #Create the needed intervals and put the
                                       #carousel into motion

  def loadModels(self):
    #Load the carousel base
    self.carousel = loader.loadModel("models/carousel_base")
    self.carousel.reparentTo(render)   #Attach it to render

    #Load the modeled lights that are on the outer rim of the carousel
    #(not Panda lights)
    #There are 2 groups of lights. At any given time, one group will have the
    #"on" texture and the other will have the "off" texture.
    self.lights1 = loader.loadModelCopy("models/carousel_lights")
    self.lights1.reparentTo(self.carousel)
    
    #Load the 2nd set of lights
    self.lights2 = loader.loadModelCopy("models/carousel_lights")
    #We need to rotate the 2nd so it doesn't overlap with the 1st set.
    self.lights2.setH(36)
    self.lights2.reparentTo(self.carousel)

    #Load the textures for the lights. One texture is for the "on" state,
    #the other is for the "off" state.
    self.lightOffTex = loader.loadTexture("models/carousel_lights_off.jpg")
    self.lightOnTex = loader.loadTexture("models/carousel_lights_on.jpg")

    #Create an list (self.pandas) with filled with 4 dummy nodes attached to
    #the carousel.
    #This uses a python concept called "Array Comprehensions." Check the Python
    #manual for more information on how they work
    self.pandas = [self.carousel.attachNewNode("panda"+str(i))
                   for i in range(4)]
    self.models = [loader.loadModelCopy("models/carousel_panda")
                   for i in range(4)]
    self.moves = [0 for i in range(4)]

    for i in range(4):
      #set the position and orientation of the ith panda node we just created
      #The Z value of the position will be the base height of the pandas.
      #The headings are multiplied by i to put each panda in its own position
      #around the carousel
      self.pandas[i].setPosHpr(0, 0, 1.3, i*90, 0, 0)

      #Load the actual panda model, and parent it to its dummy node
      self.models[i].reparentTo(self.pandas[i])
      #Set the distance from the center. This distance is based on the way the
      #carousel was modeled in Maya
      self.models[i].setY(.85)       

    #Load the environment (Sky sphere and ground plane)
    self.env = loader.loadModel("models/env")
    self.env.reparentTo(render)
    self.env.setScale(7)

  #Panda Lighting
  def setupLights(self):
    #Create some lights and add them to the scene. By setting the lights on
    #render they affect the entire scene
    #Check out the lighting tutorial for more information on lights
    lAttrib = LightAttrib.makeAllOff()
    ambientLight = AmbientLight( "ambientLight" )
    ambientLight.setColor( Vec4(.4, .4, .35, 1) )
    lAttrib = lAttrib.addLight( ambientLight )
    directionalLight = DirectionalLight( "directionalLight" )
    directionalLight.setDirection( Vec3( 0, 8, -2.5 ) )
    directionalLight.setColor( Vec4( 0.9, 0.8, 0.9, 1 ) )
    lAttrib = lAttrib.addLight( directionalLight )
    render.attachNewNode( directionalLight.upcastToPandaNode() )
    render.attachNewNode( ambientLight.upcastToPandaNode() )
    render.node().setAttrib( lAttrib )

    #Explicitly set the environment to not be lit
    lAttrib = LightAttrib.makeAllOff()
    self.env.node().setAttrib( lAttrib )

  def startCarousel(self):
    #Here's where we actually create the intervals to move the carousel
    #The first type of interval we use is one created directly from a NodePath
    #This interval tells the NodePath to vary its orientation (hpr) from its
    #current value (0,0,0) to (360,0,0) over 20 seconds. Intervals created from
    #NodePaths also exist for position, scale, color, and shear
  
    self.carouselSpin = self.carousel.hprInterval(20, Vec3(360, 0, 0))
    #Once an interval is created, we need to tell it to actually move.
    #start() will cause an interval to play once. loop() will tell an interval
    #to repeat once it finished. To keep the carousel turning, we use loop()
    self.carouselSpin.loop()

    #The next type of interval we use is called a LerpFunc interval. It is
    #called that becuase it linearly interpolates (aka Lerp) values passed to
    #a function over a given amount of time.

    #In this specific case, horses on a carousel don't move contantly up,
    #suddenly stop, and then contantly move down again. Instead, they start
    #slowly, get fast in the middle, and slow down at the top. This motion is
    #close to a sine wave. This LerpFunc calls the function oscilatePanda
    #(which we will create below), which changes the hieght of the panda based
    #on the sin of the value passed in. In this way we achieve non-linear
    #motion by linearly changing the input to a function
    for i in range(4):
      self.moves[i] = LerpFunc(
                      self.oscilatePanda,  #function to call
                      duration = 3,  #3 second duration
                      fromData = 0,  #starting value (in radians)
                      toData = 2*pi, #ending value (2pi radians = 360 degrees)
                                     #Additional information to pass to
                                     #self.oscialtePanda
                      extraArgs=[self.models[i], pi*(i%2)]
                      )
      #again, we want these to play continuously so we start them with loop()
      self.moves[i].loop()

    #Finally, we combine Sequence, Parallel, Func, and Wait intervals,
    #to schedule texture swapping on the lights to simulate the lights turning
    #on and off.
    #Sequence intervals play other intervals in a sequence. In other words,
    #it waits for the current interval to finish before playing the next
    #one.
    #Parallel intervals play a group of intervals at the same time
    #Wait intervals simply do nothing for a given amount of time
    #Func intervals simply make a single function call. This is helpful because
    #it allows us to schedule functions to be called in a larger sequence. They
    #take virtually no time so they don't cause a Sequence to wait.

    self.lightBlink = Sequence(
      #For the first step in our sequence we will set the on texture on one
      #light and set the off texture on the other light at the same time
      Parallel(
        Func(self.lights1.setTexture, self.lightOnTex, 1),
        Func(self.lights2.setTexture, self.lightOffTex, 1)), 
      Wait(1), #Then we will wait 1 second
      #Then we will switch the textures at the same time
      Parallel(
        Func(self.lights1.setTexture, self.lightOffTex, 1),
        Func(self.lights2.setTexture, self.lightOnTex, 1)), 
      Wait(1)  #Then we will wait another second
    )

    self.lightBlink.loop() #Loop this sequence continuously


  def oscilatePanda(self, rad, panda, offset):
    #This is the oscillation function mentioned earlier. It takes in a degree
    #value, a NodePath to set the height on, and an offset. The offset is there
    #so that the different pandas can move opposite to each other.
    #The .2 is the amplitude, so the height of the panda will vary from -.2 to
    #.2
    panda.setZ(sin(rad + offset) * 0.2)


# w = World() # ORIGINAL CODE
# run()       # ORIGINAL CODE

  def start(self):
      run()

Post back.
What does EVERYONE Think!!!
Any comments.

AIM

I have no idea what the hell this is,
but i have a feeling its pretty indispensable!
Is this the new scene editor?
I’ve already downloaded everything on this webite,
and this will surely be one of the last few, unless i missed some snipplets!
ynjh_jo, smartest man on the planet!

No, it’s not a scene editor. It’s just a simple attempt to get the feeling of dynamic programming. I don’t know how exactly Emacs handles python process, and if the C++ objects and their references correctly adjusted upon rebind. I’m not sure if my attempt is correct.

:laughing: wow, great joke !
Obviously you haven’t met other people on this planet. Which planet do you come from ?

Ahh I thought it was the scene editor,
when I thought i needed it before I found the tk tools…
Smarter than I!
LOL ha LOL! I can’t even connect an animation to a collision!

HAHAHA :laughing: here in swedens with our lax marijuana laws,
smart people are hard to come by!
I’m going to be probably the 5th smartest person on the planet…
(If nothing happens to the internet, or yahoo starts deleting things)
I’m the guy that figured out that deep space travel is impossible
based on the example of Hallies comet. I think no matter how fast
or far an object is from the sun, the sun will create an orbit,
from all the space debris traveling in the direction of our sun.
If it were possible to get past this “Hallies comet thresh hold” boundary,
I think it would be a ‘free’ speed of light zone,
as the universe moves very quickly.
Steven hawkings deleted me as a friend on myspace over my theory.

slightly offtopic. but… first of… space vehicles are powered… in contrast to comets. and since this is the case… the escape velocity might intrest you too. even isaac newton knew about it :wink:

HAHAHA! Now I feel a little foolish… I thought halley’s comet
went significantly faster than 54 km an hour :slight_smile: I was under the
impression it travelled at least 1000 km an hour for 76 years. LOL !
Also I seem to have misspelled halley’s comet several times
in my previous post!
LOL! Nevertheless there are comets which orbit every
3 thousands years or so…I think. In retrospect though,
they’re probably traveling at extremely slow speeds…
What I like to call ‘strolling comets’, they probably travel at speeds
upwards of 4 or 5 miles an hour, maybe.