python generator, how to create spline points
-
On 19/02/2017 at 07:17, xxxxxxxx wrote:
Hello,
I've been thinking of trying to make my own version of UberTracer, using a Python Generator object, since that plugin no longer works with the new versions of C4D.
I recently saw this tutorial on generating spline connections http://www.entagma.com/creating-geometry-with-python-in-c4d/
However, I wanted to understand this further, in the tutorial, the point data is put into an array which is then set via SetAllPoints(array)This seems to work fine since P Pass AB is sorting out the distance and point order, I assume avoiding duplicate/overlapping connections.
My question is, what if you wanted to work with just one spline object, how do you create a path between two points, close it and start a new path? especially if you have multiple paths originating from the same point.
One way I think would be to use the approach used in the tutorial, but add an additional command to connect and delete after creation, but this seems like an inefficient approach, esp if you want to work with animated point positions. Can anyone help, give any tips on what commands could help with this?I made a bit of progress, I assume I will need to use "SplineObject.ResizeObject(pcnt[, scnt=-1])"
I assume this function would be able to define multiple separate connected segments in one spline object. Not considered yet how it would decide to which points the segments are allocated, also assuming segment does mean a sequence of connected points.
My immediate problem is that I have no idea why there is a comma next to an open square bracket.
I'm going to assume that's a typo, but why are there square brackets there at all? I've been googling hard.. at the moment I'm just trying to see if I can generate a spline at all, sometimes it works, then it breaks without me having changed anything, giving the following error:
ReferenceError: the object 'c4d.SplineObject' is not alivemy script: https://www.dropbox.com/s/gqwsl636ouv2i9c/tracem8_v001.c4d?dl=0
once I can get this working (generating the spline without the not alive error), then I have a plan of action for iterating through the points to do distance based connection, but that's all pointless (heh heh) if I can't figure out these basic issues..
-
On 20/02/2017 at 09:17, xxxxxxxx wrote:
Hi,
these are quite a few questions for a single thread.
Lets try to start with those I think are most important for you (and which have the potential to solve the rest). For any follow up questions please open new threads.
Also one more general request from our side, not just targeted at you, but actually everybody wanting to post to this forum. Please, don't just throw scenes or complete source code at us. At least explain, what you try to achieve, what to expect from the posted code and what the desired results are. In case of complete scenes, please direct our focus, where needed. In doing so you help increase our throughput and thus the amount of people we can help.
Now, for your questions, a spline with multiple segments. I think, it's best to take a look at the double circle example. There you find all the code, how to handle several segments: Py-DoubleCircle.pyp
For the square braces in our documentation: This is the commonly accepted notation for optional parameters, which by the way is also used in the official Python documentation. So, the stuff in square braces is optional.
An example from the official Python docs: string.find(s, sub[, start[, end]])
The parameter "start" is optional, if used, the optional comma needs to be set (content of outer square braces). And then there's a second optional parameter "end" (inner square braces), which is only available (somewhat obviously), if the first optional parameter got already specified (that's why those square braces are nested).Lastly the script inside of the provided scene:
The Python Generator somewhat acts like an ObjectData generator plugin. All rules for an ObjectData plugin also apply to the Python Generator and the code in the Python Generator's main function is roughly equivalent to the code in an ObjectData's GetVirtualObjects() function. So you shouldn't do things in the Python Generator's main, not allowed in a NodeData plugin (like using GetActiveDocumen(), EventAdd()). And you should be extremely careful about storing stuff in global variables. For example the MoData is not a good idea. And you definitely can not return a formerly prepared object from main. It needs to be freshly constructed on every run (well, it could be a clone of a prepared one). The later seems responsible for the "ReferenceError".So, I hope, this helps to get you started. For further questions, please consider opening separate threads. While saying this, I know, it can be difficult to separate the issues, if one has not yet wrapped the head around a topic completely.
-
On 20/02/2017 at 11:45, xxxxxxxx wrote:
Hi Andreas,
Thank you very much for your help, you have cleared a lot of confusion for me.
I will stick to one issue for the rest of this thread and post new, more concise threads if I have further issues.I didn't realise my code was set up so badly, I think its essential I understand how to structure it in a python generator object;
The main thing I didn't understand from your post was about about returning the object via main.
"You can not return a formerly prepared object from main"
Could you elaborate a little on this?The default python generator object has an empty main function which returns c4d.BaseObject(c4d.Ocube)
So when you say formerly prepared, does this mean its not possible to create, set properties, then return an object? or do you mean that you can but it needs to be created via a function, which is called by return?
Thanks again!
-
On 21/02/2017 at 01:00, xxxxxxxx wrote:
Hi,
sorry, if I wasn't clear and increased your confusion. In your code it looks as if you tried to generate a spline once, store the reference the created spline in a global variable and try to return this reference on successive calls of main. This is what I meant is not possible. You have to return a freshly allocated object from main. Of course you can modify this spline to your liking before returning it. Or if allocating and creating the spline takes too long, you could store a reference to a "prepared version" in a global variable, but then you need to return a clone of this prepared spline.
Hm, not sure I this is making it clear.
Another attempt:
When you return an object from main() of the Python Generator, then C4D will insert this object into the scene (technically it will be held in a list/tree consisting of GeListNode, note how any BaseObject and thus also SplineObject is derived from GeListNode) and thus will take over ownership of this object. This also means C4D can do with the returned object, whatever it wants, e.g. delete the object from memory for whatever reason. Now, if you have stored a reference to this object, you are referencing something you can't be sure about its state. Actually it's yet a bit more complicated, but this alone should make clear, why you should/can not store a reference to the object you are returning from main(). Instead you need to provide C4D with a new upon returning.This is what the default function does. It allocates a new cube and returns it. But of course you are free to do whatever you want with the cube in between allocation and returning it from main.
-
On 21/02/2017 at 01:44, xxxxxxxx wrote:
One thing I forgot: mengersponge.py, a more elaborate example for the Python Generator.
-
On 21/02/2017 at 07:37, xxxxxxxx wrote:
Thanks again for your help, although I'm still having trouble understanding.
When you say "You have to return a freshly allocated object from main"
and
"you can modify this spline to your liking before returning it."
I assumed that was what my code was doing, creating the object, modifying it and returning it..I have changed the structure of my code not to include any global values, and I changed the code to reflect the approach used in mengersponge.py;
In one def, I create the spline, set properties and return it
in main, I create an instance and make it reference the aforementioned def.
I am not sure why in that example return is null, and the generated object is inserted under it.
My code throws no errors, but it doesn't display the splines either, I'm not sure why..
this is my code:
import c4d
from c4d.modules import mograph as modef main() :
null = c4d.BaseObject(c4d.Onull)
s = makeSpline()inst = c4d.BaseObject(c4d.Oinstance)
inst[c4d.INSTANCEOBJECT_RENDERINSTANCE] = True
inst[c4d.INSTANCEOBJECT_LINK] = s
inst.InsertUnder(null)return null
def makeSpline() : #make a spline traveling through clones in index order (like a mograph tracer would)
md = mo.GeGetMoData(op[c4d.ID_USERDATA,1]) #get cloner data from user data link field
moArray = md.GetArray(c4d.MODATA_MATRIX) #get array of clones
count = len(moArray) #Number of clones
pointArray = [] #create empty array
outputSpline = c4d.BaseObject(c4d.Ospline) #create new spline object
outputSpline.ResizeObject(count,1) #add points based on clone count, 1 segment
for v in moArray: # loop through the array and store positions in pointArray
pointArray.append(v.off)
outputSpline.SetAllPoints(pointArray) #set the created spline points based on the pointArray values
print outputSpline #check
return outputSpline #return the output spline
#outputSpline.Message(c4d.MSG_UPDATE) #not sure if this is necessary?#c4d scene this is used in: https://www.dropbox.com/s/mnsygrvqm7yx4ir/tracem8_v003.c4d?dl=0
-
On 23/02/2017 at 04:28, xxxxxxxx wrote:
So, it looks like my issue was that I was defining the optional segment count mentioned earlier.
If I replace the line "outputSpline.ResizeObject(count,1) "
with "outputSpline.ResizeObject(count) "the script works properly and the spline is generated!
-
On 23/02/2017 at 10:16, xxxxxxxx wrote:
further discoveries:
It just occurred to me how spline segments work.
You define your point positions array first, then you set all points and define how many segments are in this point array.
next, you define the quantity of points of each segment and if the segment is closed.
so segment id = 0 , cnt = 2, closed = True, will mean the first two points of the spline will be connected, the third point would then be the start of the next segment and would not be connected to the 2nd point..
Its a bit annoying if you want to dynamically add new segments to the spline, since you have to re-iterate through all segments to update them. Not sure if there is an easier way to add, for example, a 2 point segment to a spline, without adding points.. maybe there is..