Plugin Cafe Homepage
Forum Home Forum Home > Plugin Cafe > PYTHON Development
  New Posts New Posts
  FAQ FAQ  Forum Search

python effector shift clones array

Author
Message
  Topic Search Topic Search
NNenov View Drop Down
Member
Member
Avatar

Joined: 2016 Apr 26
Location: UK
Online Status: Offline
Posts: 69
Direct Link To This Post Topic: python effector shift clones array
    Posted: 2017 Mar 10 at 2:07pm
Hello,

Put briefly: I am trying to understand how to offset clones index in a cloner object using python effector.
Essentially an array index shift by a set integer.

As a starting point I've been looking at douwe's python effector example on vimeo:
https://vimeo.com/92374484

I have tried for a few hours to duplicate this (in c4d r18) but have failed,
I ended up downloading his example file ( https://www.dropbox.com/s/wyvjxpdi86pe0c6/d_ShuffleArrayEffector.c4d )

It works when you press play, however, if you try and use a different cloner, or even just change the properties of the existing cloner like size properties, it throws index error.

I've spent a few hours trying different approaches, looking at other examples, and I haven't been able to successfully create a python effector which correctly updates the clones..

Many thanks for your time!



Edited by NNenov - 2017 Mar 10 at 2:09pm
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2017 Mar 13 at 3:17am
Hi,

Douwe's effector example (d_Shuffle Array Effector) shows how to offset/shuffle clones.
It simply calls random.shuffle() on the clones matrix array to randomize their indices.

The index error is given by the call to MoData.SetArray() (line 51 in the effector).
This will need further investigation to check if it's an issue in the Python API, or if this can be solved in the effector's script.




MAXON
Developer Support
Back to Top
NNenov View Drop Down
Member
Member
Avatar

Joined: 2016 Apr 26
Location: UK
Online Status: Offline
Posts: 69
Direct Link To This Post Posted: 2017 Mar 13 at 5:45am
Yeah, I can understand almost everything happening in the script, but it breaks the instant you change pretty much anything.
I noticed Douwe said if you copy the contents of the scene into a new empty scene, it starts working with the new changes. I confirm this works, but what a dirty fix... 

Could there be a more elegant way to offset clones indices?

I tried a different approach, based on an example python effector from the Python SDK (py_effector)
In that example there is no "return" present, and when I try my code (seen below) I have to include "return 1.0" otherwise I get an error:
"TypeError: main expected float or c4d.Vector, not None"

import c4d
from c4d.modules import  mograph as mo
import  random

def my_shuffle(array):
        random.shuffle(array)
        print "I shuffled"
        return array
    
def main():
    md = mo.GeGetMoData(op)
    if md==None: return
    
    cnt = md.GetCount()
    marr = md.GetArray(c4d.MODATA_MATRIX)
    fall = md.GetFalloffs()
    frame = doc.GetTime().GetFrame(doc.GetFps())
    
    try:
        marr = newmarr
        
    except NameError:
        pass
    
    if frame%5==0 and frame != 0:
        newmarr = my_shuffle(marr)

    md.SetArray(c4d.MODATA_MATRIX, marr, True)
    return 1.0


So, this code doesn't work, it throws no errors either, it does dump about a million "I shuffled" messages to the log every 5th frame. Why that is happening, I have no clue.. I assumed python effector executes once per frame..?



Edited by NNenov - 2017 Mar 13 at 5:48am
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2017 Mar 14 at 3:59am
Originally posted by NNenov

I tried a different approach, based on an example python effector from the Python SDK (py_effector)
In that example there is no "return" present, and when I try my code (seen below) I have to include "return 1.0" otherwise I get an error:
"TypeError: main expected float or c4d.Vector, not None"

To not get this error, set the Effector mode to Full Control and return a bool instead (True if successful - usually at the end - and False in error cases). More information on this mode below.


Originally posted by NNenov

So, this code doesn't work, it throws no errors either, it does dump about a million "I shuffled" messages to the log every 5th frame. Why that is happening, I have no clue.. I assumed python effector executes once per frame..?

A Python Effector is executed 21 times for each frame in Parameter Control mode. This mode means the Effector is value driven.
main() is then called for each blend value to return its strength (see EffectorDataStruct::strengths). Retrieve the current blend value ID with MoData.GetBlendID()
In Parameter Control mode, a Python Effector main() is called from C++ EffectorData::CalcPointValue().

In Full Control mode, a Python Effector main() function is executed once per frame.
In this mode, main() is called from C++ EffectorData::ModifyPoints() to change the MoData returned by GeGetMoData().

EDIT: Provided accurate information for EffectorData::ModifyPoints() and EffectorData::CalcPointValue().





Edited by Yannick Puech - 2017 Mar 14 at 4:33am
MAXON
Developer Support
Back to Top
NNenov View Drop Down
Member
Member
Avatar

Joined: 2016 Apr 26
Location: UK
Online Status: Offline
Posts: 69
Direct Link To This Post Posted: 2017 Mar 14 at 1:16pm
Yannick thank you so much for explaining in such depth.

I had not noticed there was a different control mode available! Now everything works perfectly.

Here is my refined code for offsetting a cloner array:

import c4d
from c4d.modules import mograph as mo

def main():
    md = mo.GeGetMoData(op) #get cloner data from user data link field
    
    if md==None: return 
    
    frame = doc.GetTime().GetFrame(doc.GetFps())
    
    offset = op[c4d.ID_USERDATA,3]

    moArray = md.GetArray(c4d.MODATA_MATRIX) #get array of clones
     
    newArray = shiftArray(op[c4d.ID_USERDATA,3], moArray)
        
    md.SetArray(c4d.MODATA_MATRIX, newArray, True)
    
    return True
    
    
def shiftArray(offset, array):
    offset = offset % len(array)
    return array[offset:] + array[:offset]
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2017 Mar 15 at 1:01am
Glad you wrote an offsetting effector.

About Douwe's effector example, it's useless and wrong to store the MODATA_MATRIX in a global array.
This causes the range error when changing the cloner size. Because the previously stored array (with an invalid size for the MoData) was set again.

In your effector you don't use a global variable and that's right as the latest MoData array can always be retrieved with MoData.GetArray().



MAXON
Developer Support
Back to Top
douwe View Drop Down
Member
Member


Joined: 2011 May 31
Online Status: Offline
Posts: 33
Direct Link To This Post Posted: 2017 Mar 15 at 8:11am
:)

At last.

This issue dates back to november 2012 for me ( http://www.plugincafe.com/%5Cforum/forum_posts.asp?TID=7394 )

So, I want to shuffle clones every 30 frames.
This is where I got stuck ( as in,  I keep on getting the same order ),
which made me "invent" the global overwrite.

import c4d
import random
from c4d.modules import mograph as mo

def my_shuffle(array):
    random.shuffle(array)
    return array

def main():
    md = mo.GeGetMoData(op)
    if md==None: return
    frame = doc.GetTime().GetFrame(doc.GetFps())
    marr = md.GetArray(c4d.MODATA_MATRIX)
        
    if frame%30==0:
        newmarr = my_shuffle(marr)
        md.SetArray(c4d.MODATA_MATRIX, newmarr, True)
        return True


Anybody ?
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2017 Mar 17 at 10:48am
Hi Douwe,

Thanks for joining the discussion and pointing the issue that made you use a global array.
You're right, for each frame the same matrix array is retrieved. So the shuffled array is set each 30 frame and reset the next frame.

This would need further investigation to check if this is a limitation of effectors/python effectors.



MAXON
Developer Support
Back to Top
NNenov View Drop Down
Member
Member
Avatar

Joined: 2016 Apr 26
Location: UK
Online Status: Offline
Posts: 69
Direct Link To This Post Posted: 2017 Mar 19 at 2:21pm
Hey guys, I'm really new to python, I know my approach doesn't utilise history/memory, just a simple offset translation on the source. 

I don't know how you could do it without using a global value?
Back to Top
douwe View Drop Down
Member
Member


Joined: 2011 May 31
Online Status: Offline
Posts: 33
Direct Link To This Post Posted: 2017 Mar 20 at 5:28pm
Originally posted by Yannick Puech

Hi Douwe,

Thanks for joining the discussion and pointing the issue that made you use a global array.
You're right, for each frame the same matrix array is retrieved. So the shuffled array is set each 30 frame and reset the next frame.

This would need further investigation to check if this is a limitation of effectors/python effectors.


Thanks for listening, Yannick.
Would love to come across a clean way to do this / get this fixed !

d
Back to Top
supergeordie View Drop Down
Member
Member
Avatar

Joined: 2010 Oct 28
Online Status: Offline
Posts: 73
Direct Link To This Post Posted: 2017 Mar 21 at 7:42am
Hey, just having a look at another way of doing this without global variables. I smashed this together really quickly so needs error handling, tightening up, optimising, minimising etc:

import c4d, random
from c4d.modules import mograph as mo
from c4d import Vector as v, utils as u, Matrix as m


def my_shuffle(array):
        random.shuffle(array)
        return array

def main():
    md = mo.GeGetMoData(op)
    if md is None: return False
    
    cnt     = md.GetCount()
    indarr = md.GetArray(c4d.MODATA_ALT_INDEX) 
    marr  = md.GetArray(c4d.MODATA_MATRIX)
    fall     = md.GetFalloffs()
    
    
    frame = doc.GetTime().GetFrame(doc.GetFps()) 

    if frame == 0 or -1 in indarr : indarr = range(cnt)
    
    if frame % 30 == 0:
        my_shuffle(indarr)
        md.SetArray(c4d.MODATA_ALT_INDEX, indarr, True)
    
    positions = []

    for i in xrange(cnt):
        index = indarr[i]
        positions.append(marr[index].off)
    
    for i in xrange(cnt):
        marr[i].off = positions[i]

    md.SetArray(c4d.MODATA_MATRIX, marr, True)

    return True


So it looks like the MODATA_ALT_INDEX array does not get reset each frame so you can use it for all kinds of stuff :)

Hope this helps,

Adam


Edited by supergeordie - 2017 Mar 21 at 7:51am
http://pixelsinprogress.com
Back to Top
NNenov View Drop Down
Member
Member
Avatar

Joined: 2016 Apr 26
Location: UK
Online Status: Offline
Posts: 69
Direct Link To This Post Posted: 2017 Mar 23 at 12:24pm
Hi Supergeordie,

I just tried your code and it doesn't seem to be working for me, it resets to original array on the next frame like the original problem we had. 
Does it work for you?
Back to Top
supergeordie View Drop Down
Member
Member
Avatar

Joined: 2010 Oct 28
Online Status: Offline
Posts: 73
Direct Link To This Post Posted: 2017 Mar 23 at 12:50pm
It works for me. :-/

Saying that, I never actually expected it to work considering the behavior of the other mograph arrays When I threw it together the other day, I happily had clones switching position randomly every Nth frame and I was printing to the console the ALT_INDEX array and it looked like it was doing what it should. Maybe there's something funny going on? (Or maybe I'm being stupid and missed something) Either way I'm out of town until Sunday so I'll have a look then. :)

Cheers,

Adam
http://pixelsinprogress.com
Back to Top
NNenov View Drop Down
Member
Member
Avatar

Joined: 2016 Apr 26
Location: UK
Online Status: Offline
Posts: 69
Direct Link To This Post Posted: 2017 Mar 23 at 1:11pm
Perhaps there's something different about my scene rather than the code, I made a fresh new scene, one cloner, one python effector set to "full control" with your code. would be interesting to see your scenefile, could it be a c4d version difference? I'm using r18.041
Back to Top
supergeordie View Drop Down
Member
Member
Avatar

Joined: 2010 Oct 28
Online Status: Offline
Posts: 73
Direct Link To This Post Posted: 2017 Mar 27 at 1:43am
OK, I think I know why. For me it seems to work on the matrix object but not the cloner. I imagine this is because the matrix object does not use this ALT_INDEX array or something. 

In my setup I was already using the matrix object with a cloner mapped onto it.
Here's how it works for me:

https://vimeo.com/210222042
password: altind3x

Thanks,

Adam

http://pixelsinprogress.com
Back to Top
NNenov View Drop Down
Member
Member
Avatar

Joined: 2016 Apr 26
Location: UK
Online Status: Offline
Posts: 69
Direct Link To This Post Posted: 2017 Mar 28 at 3:01am
Interesting.. I don't know what to say..
Is this just to avoid using global variables?
Back to Top
supergeordie View Drop Down
Member
Member
Avatar

Joined: 2010 Oct 28
Online Status: Offline
Posts: 73
Direct Link To This Post Posted: 2017 Mar 28 at 3:14am
Just saw it as a bit of a challenge really, I use global values mainly for this type of thing :)
http://pixelsinprogress.com
Back to Top
Thom0ne View Drop Down
Member
Member
Avatar

Joined: 2017 Apr 22
Online Status: Offline
Posts: 10
Direct Link To This Post Posted: 2018 Apr 03 at 12:25pm
Interesting thread.  I bumbled onto this, and another related thread, while trying to manipulate the color of individual clones.  I thought I had lost all my marbles (clones) not being able to do something that seemed simple to do.

I'm glad to see this is just a bug and that I am not without a brain.  I wonder now if I should just wait to see if this is fixed in R20 eh?

Thom
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2018 Apr 06 at 8:35am
Hi Thom,

I'm afraid this is not a bug but a limitation of effectors.



MAXON
Developer Support
Back to Top
Thom0ne View Drop Down
Member
Member
Avatar

Joined: 2017 Apr 22
Online Status: Offline
Posts: 10
Direct Link To This Post Posted: 2018 Apr 06 at 3:22pm
Thanks for the clarification, Yannick.

I hope the limitation can be removed in the future.  I suppose one's work is never done in the API scope of work.  I would have liked to stay within Mograph, getting the control I was looking for (individual clone color control via python), but will shift my approach over to particles given the limitation in Mograph.

Best,  Thom. 
Back to Top

Forum Jump Forum Permissions View Drop Down

Bulletin Board Software by Web Wiz Forums® version 9.61 [Free Express Edition]
Copyright ©2001-2009 Web Wiz

This page was generated in 0.108 seconds.