Python & Splines

On 28/02/2018 at 20:54, xxxxxxxx wrote:

Hi, C4D artist here.

I'm trying to write a simple Python code that can:
— Draw / create a spline
— Manipulate the spline's point positions and tangents via User Data

Right now I was able to create the spline using the code below:

import c4d
def main() :
    curFrame = doc.GetTime().GetFrame(doc.GetFps())
    if curFrame == 0:
       spline = c4d.SplineObject(4, c4d.SPLINETYPE_BEZIER)
       points = []

I can't seem to find a way to use User Data to control this spline's point attributes. Any help here would be appreciated.

Also is there a way to activate the Python Script in a way that is frame independent? Right now it's activated on frame 0 so if you play and rewind it'll create another spline.

On 01/03/2018 at 01:54, xxxxxxxx wrote:

First of all welcome in the community Bittar :)

When reporting an issue is really important to tell us, in wich context you are.
According your message it's look's like you are running this code from a script tag or a python generator.

Python scripting tag get executed a bunch of time and may also be executed more than one time per frame.
Python scripting tag, are not allowed to add, change anything in the scene (current document).
So tag purpose is only to store data or to update the host object.

Python Generator are also executed a bunch of time and may also be executed more than one time per frame.
Python Generator are also not allowed to add stuff into the scene, but return an object (or multiple objects) which will represent this generator.

So there are two ways for doing what you want to achieve.
The first one create a generator, with user data and which will generate and modify a spline according theses parameters.

The second way could be to create a script in the script manager which will generate a spline, you can also create the spline manually, this do not matter.
Then create a python scripting tag with some user data on this spline. Then within this python tag you can modify the host spline according theses parameters.

Let me know what fit the best for you. 
And I will be happy to guide you :)

Regarding your question about how to access user data into your script, you can drag and drop any parameters (and user data, since it's nothing more than a parameter) into the console or the script manager.
It will output a "string" which let you access this parameter in the following structure nodeName[parameter].
So if you want to access some user data that are on your spline object you can do something like

spline = doc.SearchObject("SplineWithUserData")
if not spline: return
print spline[c4d.ID_USERDATA,1] #1 is ID of the user Data I want to print

cheers, Maxime

On 01/03/2018 at 11:30, xxxxxxxx wrote:

Hi, Maxime. 
Thanks for your thorough answer.

So I tried creating a Python Generator (was using a Python node inside Xpresso before)

And I got to get the Userdata from another object on my scene and apply that to a c4d.Vector like this:

    studioHeight = studio[c4d.ID_USERDATA,2]
    spline.SetPoint(0, c4d.Vector(0,studioHeight,0))

Not very clean but only for testing purposes. Now when I change the slider on the User Data I don't see the point's Pos Y changing live on the scene. I have to go back to frame 0 on the timeline. Is there a way for Python to 'listen' to the user data and execute if it changes?

Also it's only moving the point but doesn't seem like it's connected to point nº1 on the spline.

On 02/03/2018 at 03:00, xxxxxxxx wrote:

Hi Bittar,

Even if it's not 100% true, consider the xpresso tag like a python tag. (I mean they get the same limitations since they are both tag).
So It's really not recommended to add object into the scene from an expresso (and you can't do it with pure xpresso without python), since it can make crash or corrupt the scene.

Regarding your initial code, you get some issues I should have pointed you before.

First of all, you shouldn't use an user data from another objects. As you may be aware, the scene is executed in a given order.
So for example if you got a generator and an object, in this order in the hierarchy, Generator parent of object.
Your generator will be elevated before your object, so that mean if you get an animated user data/parameters into your object and you retrieve this parameter from your generator, you still get the old value.

Then you may be aware a c4d.SplineObject is composed of points and segments
Before to change the structure to an object (this is also true for PolygonObject or PointObject) you have to use ResizeObject.
Then for each segments you have to define how many points it contains by calling SetSegment.
After you can define the position of theses points, and when you finnish to edit your object/spline you need to send a Message to the object in order to generate it's cache.

Which will give us something like

def main() :
    ptnCnt = 3
    spline = c4d.SplineObject(ptnCnt, c4d.SPLINETYPE_BEZIER)
    if spline is None:
        return None
    spline.ResizeObject(ptnCnt, 1)
    # Offset & Set the Points
    spline.SetSegment(0, 3, False)
    spline.SetPoint(0, c4d.Vector(0, 0, 0))
    spline.SetPoint(1, c4d.Vector(50, op[c4d.ID_USERDATA,1], 0))
    spline.SetPoint(2, c4d.Vector(100, 0, 0))
    spline.SetTangent(1, c4d.Vector(0,-30,-40), c4d.Vector(0,30,40)) # Tangent are in local space of point
    return spline

Regarding your need to go back to frame 0 on the timeline I'm not able to reproduce it(even with optimize cache and reset on Frame0 checked). 
So please share your complete code, if it's not possible to do publicly then send us an email at [email protected]


On 02/03/2018 at 22:21, xxxxxxxx wrote:

Hey, Maxime

That worked perfectly. Thank you!

One thing I didn't understand is what does the spline.Message(c4d.MSG_UPDATE) do?
Is it to update the changes made to the spline?


On 05/03/2018 at 00:15, xxxxxxxx wrote:

Hi Bittar,

Yes, you are totally right. It's also generate the cache for this updated object.
So whenever you change the structure of an object (PolygonObject, PointObject or SplineObject), you have to send this message.