Object Position w/o Updating Viewport



  • On 10/08/2015 at 06:31, xxxxxxxx wrote:

    Thanks for the reply Sebastian.

    I use C4D to animate concept designs of machinery, and it would be extremely helpful to have a real-time feedback of an object's velocity and acceleration in order to assess the real-world physical feasibility of creating these motions.  (I actually was asking a few months back about understanding the equations representing the C4D f-curves in order to perform vel/accel derivations, but am trying a new approach)

    Ultimately I want to use this script to read position values of an object (at 3 different times: previous frame, current frame, and next frame), and use these to calculate the velocity/acceleration.  I can get it to work when not previewing the animation, but was wondering if it was possible to do it as the animation is playing--but that is where I run into issues since it didn't seem I could play the animation AND update it at different times in order to read the position.  I won't have a chance to try this for a bit, but can I use SetTime() with ExecutePasses() while the animation is playing to do this?

    By "store in a Tag," I just meant that I was planning to put this code in a Python Tag on a Null to serve as a motion calculator tool of sorts that I could keep with my .c4d projects.  Currently the Tag has user data fields that are populated by the calculations performed in the Python code.  My thought was to either have the code read the Tag's object as the object of interest, or just have a link field.  But having it as a Tag made sense to me when trying to develop for the real-time update approach

    Hope that makes sense!
    --Marcus



  • On 11/08/2015 at 00:33, xxxxxxxx wrote:

    Hello,

    please remember that Cinema is no real time application framework or simulation tool. So there may be limits to what you can do.

    When the animation is playing the most simple thing to do would be to store the position of the object in the last frame and compare it to the position in the current frame. This ways you would get a velocity (but only during animation, like the "Position velocity" of the Xpresso Object node).  The Python Tag is evaluated not just on every frame but every time the document is calculated, so also when you edit something in the scene. This means you would have to check if the frame has changed.

    Another approach could be to cache all the data you need by calculating the desired values for each frame of the scene and storing them. Then you could display the desired result for the current frame.

    Best wishes,
    Sebastian



  • On 11/08/2015 at 04:50, xxxxxxxx wrote:

    Thanks again for looking into it, Sebastian.  I realize it's a different application than that for which C4D is intended--we use it for sales and marketing as well, so it's a broad spectrum!  As an engineer by training, my role is to bridge the gap between conceptual animation and hard engineering numbers.

    But, it's good to know the limitations!  I'll try to post an update once I get a chance to be back working on this--the cache idea occurred to me as well...  just need to sit down and think about how I'd want to do it. (Other projects have come up in the meantime)

    --MV



  • On 20/08/2015 at 06:51, xxxxxxxx wrote:

    Okay, so I've been able to return to this for a bit, and have my code such that it will approximate the instantaneous velocity and acceleration of an object based on its change in position over the course of 3 frames.  It's set up to accomodate my looping/cyclical animations running from 0-359 frames (thinking of frames as if they were degrees) which makes frame 360 the same as frame 0.

    At this point, I think I've found the ideal real-time motion feedback is not possible within C4D's framework.  The next best thing would be to have a "Motion Calculator" object with user data fields on it, such that when I click a button "Calculate Motion" my script runs once.  When I've tried to implement it in a Python tag or a Python generator, I get: RuntimeError: maximum recursion depth exceeded.

    Is it possible to restrict the Python Tag or Python Generator's refresh rate so it only runs the script once?
    Here's what I've been working with, in part:

    FPS = doc.GetFps()                              #Get the FPS of the document
    obj = doc.GetActiveObject()                 #Grab the selected object
        
    if not obj:                                 #If no object is selected
        print "No object selected"                  #Print message in the console                    
      
    fFrame = doc.GetMinTime().GetFrame(FPS)  #Get the first frame   "FFRAME"
    lFrame = doc.GetMaxTime().GetFrame(FPS)  #Get the last frame    "LFRAME"
        
    def RetrievePosData() :
        cTime = doc.GetTime()
        cFrame = cTime.GetFrame(FPS)    #Get the current frame "CFRAME"
        
        if cFrame == 0:            #If current frame = 0, prevFrame loops to end
            pTime = c4d.BaseTime(lFrame,FPS)
            nTime = c4d.BaseTime(cFrame+1,FPS)
            
        elif cFrame == lFrame:     #If current frame = end, nextFrame loops to begining
            pTime = c4d.BaseTime(cFrame-1,FPS)
            nTime = c4d.BaseTime(fFrame,FPS)
        
        else:                      #Otherwise, prev/next Frame are just 1-index from current
            pTime = c4d.BaseTime(cFrame-1,FPS)
            nTime = c4d.BaseTime(cFrame+1,FPS)
        
        doc.SetTime(pTime)
        doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_0)
        pPos = obj.GetRelPos()
      
        doc.SetTime(nTime)
        doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_0)
        nPos = obj.GetRelPos()
            
        doc.SetTime(cTime)
        doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_0)
        cPos = obj.GetRelPos()
        
        pos = [pPos, cPos, nPos]  #Store PREVIOUS-Position, CURRENT-Position, and NEXT-Position
            
        return pos
        
      
    def CalcPVA(pos) :
        
        """ APPROXIMATION OF FIRST AND SECOND DERIVATIVE
        Position     --> f(t)
        Velocity     --> f'(t) ~= [f(t+h)-f(t-h)]/2h
        Acceleration --> f"(t) ~= [f(t+h)-2f(t)+f(t-h)]/h^2
        """
        pPos = pos[0]  #Set the previous position value = f(t-h)
        cPos = pos[1]  #Set the current position value  = f(t)
        nPos = pos[2]  #Set the next position value     = f(t+h)
        
        vel = (nPos - pPos)/2         	#Approx velocity --> in/frame (VECTOR)    
        accel = (nPos-2*cPos+pPos) #Approx acceleration --> in/frame^2 (Vector)     
        return vel, accel
      
    def main() :
        if obj:
            (pos) = RetrievePosData()
            (vel, accel) = CalcPVA(pos)
            
            print "Velocity is %3.3f in/frame" %vel.GetLength()
            print "Acceleration is %3.3f in/frame^2" %(accel, gs)
    if __name__=='__main__':
        main()
    


  • On 21/08/2015 at 00:06, xxxxxxxx wrote:

    Hello,

    a generator or a tag are executed every time the scene is calculated. This is when a new frame is started or when anything in the scene changed, for example the position of the current camera. So it is intentional that generators and tags are executed very often when you edit the scene interactively.

    You could store the current frame in your script and compare that stored frame every time when the script is executed with the new given frame. Then you could executed you code only when the frame changed. If you do not limit the execution of your code you run into infinite recursions, as you describe.

    Best wishes,
    Sebastian



  • On 21/08/2015 at 04:51, xxxxxxxx wrote:

    Sebastian,

    I'm not sure I understand how one could "store" values in the script, since I assumed all the variables are cleared each time the script is run.  I worked with Matlab prior to any experience with Python, and we always set it up to "clear all" variables between runs--I just assumed Python would default to be the same.

    I guess limiting the execution of the code is exactly what I was hoping to do, while still retaining the convenience of using a generator/tag as the container for the code and user data.

    What might the structure of the code look like as you are describing it?  As stated above, I'm not seeing how I would approach "storing" values between executions of the code, and would that idea still work with the python generator/tag?

    I'll just note that the one method I had been trying (but still was running into infinite recursions) was to have a Boolean check box

    op[c4d.ID_USERDATA,33]
    

    that would turn itself off immediately after the main body of code is run...  but it still wants to evaluate the bit within the if statement despite my efforts to restrict it.  Do you know why I am still running into the infinite recursion even with that Boolean in place?

        if op[c4d.ID_USERDATA,33] == True:
            print pTime.GetFrame(FPS), cTime.GetFrame(FPS), nTime.GetFrame(FPS)
            
            if obj:
                doc.SetTime(pTime)
                doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_INTERNALRENDERER)
                pPos = obj.GetRelPos()
                
                doc.SetTime(nTime)
                doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_INTERNALRENDERER)
                nPos = obj.GetRelPos()
                
                doc.SetTime(cTime)
                doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_INTERNALRENDERER)
                cPos = obj.GetRelPos()
     
            op[c4d.ID_USERDATA,33] = False
    

    As a side-note, what exactly is the purpose of the BuildFlags?  I just picked one that seemed to make sense, but otherwise it was a shot in the dark.

    Thanks!
    Marcus



  • On 21/08/2015 at 08:38, xxxxxxxx wrote:

    Hello,

    using a user data parameter would have been my suggestion to store data. You should be able to set and get proper values of a user data parameter. Have you tried to store the current frame this way?

    The build flags define the context of the operation. Some functions may work differently when the current rendering process is done for internal rendering (in the viewport) or external rendering (in the picture viewer etc.).

    Best wishes,
    Sebastian



  • On 21/08/2015 at 09:28, xxxxxxxx wrote:

    I had not tried storing the current time in a user data field, but wouldn't that amount to the same thing as assigning the value to a variable in the code?  I may just be misunderstanding what you are describing, but it sounds to me like storing it in user data would look like this:

    cTime  = doc.GetTime()
    cFrame = cTime.Get()*FPS
    op[c4d.ID_USERDATA,34] = cTime.GetFrame(FPS)    
    

    But then to get the stored value, am I not just doing the reverse operation of:

    cTime = op[c4d.ID_USERDATA,34]
    

    Which ( I think...) would leave me in the same boat in which I started.  Again, sorry if I've missed the mark on your intended approach.  But I'm also still confused why I'm falling into an infinite recursion with this bit of code:

    cTime  = doc.GetTime()					#Current time
    cFrame = cTime.Get()*FPS				#Current frame
    pTime  = c4d.BaseTime(cFrame-1,FPS) 		#Previous Frame's time
    nTime = c4d.BaseTime(cFrame+1,FPS)		#Next Frame's time
      
    doc.SetTime(pTime)
    doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_INTERNALRENDERER)
    pPos = obj.GetRelPos()
                
    doc.SetTime(nTime)
    doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_INTERNALRENDERER)
    nPos = obj.GetRelPos()
                
    doc.SetTime(cTime)
    doc.ExecutePasses(None,True,True,True,c4d.BUILDFLAGS_INTERNALRENDERER)
    cPos = obj.GetRelPos()
    

    I had thought that if the playhead is not moving, this code should run with no problem.  From the script manager it does (and works like a charm!).  But once it's in a generator/tag, Cinema locks up (When it's not giving me the infinite recursion error).



  • On 23/08/2015 at 23:48, xxxxxxxx wrote:

    Hello,

    ExecutePasses() executs the whole document with everything in it, including all tags. This means you Python tag calls ExecutePasses() which in return calls your Python tag wich again calls ExcecutePasses() etc.

    A script in the script manager is not part of the document. So calling ExecutePasses() will not execute that script again.

    Best wishes,
    Sebastian



  • On 24/08/2015 at 05:48, xxxxxxxx wrote:

    Ah... okay; so if I'm understanding correctly (in my case) the tag runs, hits the first ExectuePasses() command, at which point the process starts over, such that it never actually hits the Boolean command in order to quit?  Thus you fall into the infinite recursion I guess.  Funny thing is that sometimes I get the infinite recursion error, but sometimes C4D just locks up.

    I'm going to go back to my original method of updating the view-port to see if I can't get that to work with my Boolean switch in the UserData... will let you know if I make any progress.

    Thanks again, Sebastian!

    --MV


Log in to reply