Solved CSO from virtual objects

i only need the tracer object as a return value but if i do so the results are empty. so how do i get the cso from the tracer?
a normal cso from the tracer itself is empty
if i put all objects into a list the console told me that the object document don't match the document from the modeling command.

import c4d
#Welcome to the world of Python


def createOutline(invert):
	
	bc = c4d.BaseContainer()
	bc.SetData(c4d.MDATA_SPLINE_OUTLINE, op[c4d.ID_USERDATA,2] * invert)
	bc.SetData(c4d.MDATA_SPLINE_OUTLINESEPARATE, True)
	spline = op[c4d.ID_USERDATA,1]    
	return c4d.utils.SendModelingCommand(command= c4d.MCOMMAND_SPLINE_CREATEOUTLINE,list= [spline],   mode= c4d.MODELINGCOMMANDMODE_ALL, bc = bc, doc        = doc)[0]




def main():

	ll = [] 
	null = c4d.BaseObject(c4d.Onull)      
	
	spline = createOutline(-1)        
	spline.InsertUnder(null) 
	
	
	matrix = c4d.BaseObject(1018545)   
	matrix.InsertUnder(spline)
	matrix[c4d.ID_MG_MOTIONGENERATOR_MODE] = 0
	matrix[c4d.MG_SPLINE_MODE] = 1
	matrix[c4d.MG_SPLINE_STEP] = op[c4d.ID_USERDATA,3]
	matrix [c4d.MG_OBJECT_LINK] = spline       
	
	
	tracer = c4d.BaseObject(1018655)
	tracer.InsertUnder(matrix)
	inexclude = c4d.InExcludeData()
	inexclude.InsertObject(matrix,1)    
	tracer[c4d.MGTRACEROBJECT_OBJECTLIST] = inexclude
	tracer[c4d.MGTRACEROBJECT_MODE] = 2
	tracer[c4d.SPLINEOBJECT_TYPE] = 3
	tracer[c4d.SPLINEOBJECT_SUB] = 6
	tracer[c4d.SPLINEOBJECT_INTERPOLATION] = 1  
	
	return null

Hi @pyr I was not able to reproduce your issue. I attached my scene.

On a side note, an this is important and maybe why I don't get any issue but I replaced spline = op[c4d.ID_USERDATA,1] by spline = op[c4d.ID_USERDATA,1].GetClone(c4d.COPYFLAGS_NONE). A Python Generator should never modify stuff outside of him. So When you Get the spline, you actually get the spline within the scene and you apply the modelingCommand to the object within the scene which is not correct and may crash Cinema 4D.

0_1547459168000_cso-from-object.c4d

If you have any question please, write explicitly in which context you are executing your python script and also in which Cinema 4D version.
Cheers,
Maxime

Hey,
unfortunally this won't work in R18, and it gives me not the desired result.

I updated my sourcecode a little bit and now i use a clone from my spline to create the outline

import c4d
#Welcome to the world of Python


def createOutline(spline,invert):

	bc = c4d.BaseContainer()
	bc.SetData(c4d.MDATA_SPLINE_OUTLINE, op[c4d.ID_USERDATA,2] * invert)
	bc.SetData(c4d.MDATA_SPLINE_OUTLINESEPARATE, True)
	return c4d.utils.SendModelingCommand(command= c4d.MCOMMAND_SPLINE_CREATEOUTLINE,list= [spline],   mode= c4d.MODELINGCOMMANDMODE_ALL, bc = bc, doc        = doc)[0]

def main():

	spline = op[c4d.ID_USERDATA,1].GetClone()

	null = c4d.BaseObject(c4d.Onull)

	newSpline = createOutline(spline,-1)
	newSpline.InsertUnder(null)

	matrix = c4d.BaseObject(1018545)
	matrix.InsertUnder(newSpline)
	matrix[c4d.ID_MG_MOTIONGENERATOR_MODE] = 0
	matrix[c4d.MG_SPLINE_MODE] = 1
	matrix[c4d.MG_SPLINE_STEP] = op[c4d.ID_USERDATA,3]
	matrix [c4d.MG_OBJECT_LINK] = newSpline

	tracer = c4d.BaseObject(1018655)
	tracer.InsertUnder(matrix)
	inexclude = c4d.InExcludeData()
	inexclude.InsertObject(matrix,1)
	tracer[c4d.MGTRACEROBJECT_OBJECTLIST] = inexclude
	tracer[c4d.MGTRACEROBJECT_MODE] = 2
	tracer[c4d.SPLINEOBJECT_TYPE] = 3
	tracer[c4d.SPLINEOBJECT_SUB] = 6
	tracer[c4d.SPLINEOBJECT_INTERPOLATION] = 1

	return tracer #not alive

i also uploaded a scenefile:
0_1547554705314_test.c4d

my plan is to return only the tracer as an spline object to the scene all other objects are only helpers to get the correct shape from the source spline.

Ok, another try:

i actually don't need the matrix and the tracer if i use a spline help and create a new spline object it works like a sharm.

sampleSpline = c4d.SplineObject(samples,c4d.SPLINETYPE_BSPLINE)
newPos = []
for i in range(0,samples):
	newPos.append(sh.GetPosition(sh.GetOffsetFromReal(i*step),0,realoffset = True,smooth =True))
sampleSpline.SetAllPoints(newPos)
sampleSpline.ResizeObject(samples,1)
sampleSpline.SetSegment(0,samples,True)

sh is a splinehelp from my spline offset, the only thing that bothers me is that the close segment seems to be ignored.

also a solution for the tracer based solution would be great

sampleSpline[c4d.SPLINEOBJECT_CLOSED] = True #close the spline

Hey @pyr, the tracer object is inserted, only under the null object, which is stored nowhere. That means at the end of the function, the Python Garbage collect actually free memory and all useless stuff it doesn't need anymore. And since the null which holds everything is stored nowhere else, the null is deleted and all its children even your tracer.
That's lead to return a no-valid BaseObject.

So the best way is to get a clone (aka object not inside any hierarchy) from a cache of the Tracer.

With that's said there is no way to make the Python Generator returning a spline since it's not registered with OBJECT_ISSPLINE flag. So the only way will be to make a plugin. Here the code it's already looking very nice! Nice idea indead! :smile:

import c4d, os

PYOUTLINE_ID  = 1000001  #Plugin ID

class Ooutline(c4d.plugins.ObjectData):

    def createOutline(self, spline, invert):

        bc = c4d.BaseContainer()
        bc.SetData(c4d.MDATA_SPLINE_OUTLINE, invert)
        bc.SetData(c4d.MDATA_SPLINE_OUTLINESEPARATE, True)
        res = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_SPLINE_CREATEOUTLINE,
                                            list=[spline],
                                            mode=c4d.MODELINGCOMMANDMODE_ALL,
                                            bc=bc)
        if not res:
            return False

        return res[0]

    def GetContour(self, op, doc, lod, bt):
        # Get a clone of the spline
        spline = op[c4d.ID_USERDATA,1].GetClone()

        # Create a null used to define our hierarchy
        null = c4d.BaseObject(c4d.Onull)

        # Execute the CreateOutline Function in the created document.
        newSpline = self.createOutline(spline, op[c4d.ID_USERDATA,2] * -1)
        if not newSpline: return

        # Insert it into the Tracer and set up a hierarchy to get things done
        newSpline.InsertUnder(null)

        matrix = c4d.BaseObject(1018545)
        matrix.InsertUnder(newSpline)
        matrix[c4d.ID_MG_MOTIONGENERATOR_MODE] = 0
        matrix[c4d.MG_SPLINE_MODE] = 1
        matrix[c4d.MG_SPLINE_STEP] = op[c4d.ID_USERDATA,3]
        matrix [c4d.MG_OBJECT_LINK] = newSpline

        tracer = c4d.BaseObject(1018655)
        tracer.InsertUnder(matrix)
        inexclude = c4d.InExcludeData()
        inexclude.InsertObject(matrix,1)
        tracer[c4d.MGTRACEROBJECT_OBJECTLIST] = inexclude
        tracer[c4d.MGTRACEROBJECT_MODE] = 2
        tracer[c4d.SPLINEOBJECT_TYPE] = 3
        tracer[c4d.SPLINEOBJECT_SUB] = 6
        tracer[c4d.SPLINEOBJECT_INTERPOLATION] = 1

        # Create a temporary document to compute the cache of the tracer
        workDoc = c4d.documents.BaseDocument()
        workDoc.InsertObject(null)
        workDoc.ExecutePasses(bt, False, False, True, c4d.BUILDFLAGS_EXPORT)

        # Get the SplineObject from the tracer cache
        tracerSpline = tracer.GetCache()
        if not tracerSpline: return
        
        outSpline = tracerSpline.GetClone()

        # Close the spline
        outSpline[c4d.SPLINEOBJECT_CLOSED] = True
        
        return outSpline


if __name__ == "__main__":
    bmp = c4d.bitmaps.BaseBitmap()
    dir, file = os.path.split(__file__)
    fn = os.path.join(dir, "res", "icon.tif")
    bmp.InitWith(fn)
    result          = c4d.plugins.RegisterObjectPlugin(
        id          = PYOUTLINE_ID, 
        str         = "Test", 
        g           = Ooutline, 
        description = "Ooutline", 
        info        = c4d.OBJECT_GENERATOR | c4d.OBJECT_ISSPLINE, 
        icon        = None)

Very Cool
Cheers,
Maxime.