Solved Need help with Bend-Deformer in Object-Plugin

Hi,
I created a function in Object_Plugin which creates a leaf.
The leaf is created with a plane. In the SplineData in the Attribute-Manager you can shape the leaf shape, like in the hair-Material and depending on how much points the user adds, the plane subdivides. Also I move the points of the plane so that the axis is exact on the tip/bottom of the plane.

Then I added a bend deformer and wanted to fit it to the parent but I had to do it twice, so at the end i also had to move the deformer , so it behaves a little weird. It doesn`t really fit.
At the the end the deformer is in the right place but it just refresh if the user moves a point in the splinedata.
So the code is just a test and far from finished.
At the end I convert all into a polygon object.

  • why does I have to call fit to parent twice
  • why does it not refresh

So what is the problem here, can you help me.
So I added the code and for the res-file, I think the header and str file is in this case not necessary.
I also attached the plugin file and a small video do demonstate, what I mean.

Cheers
Thomas

2023-05-06 14-12-19.mp4

ivy_maker.zip

import c4d
from c4d import plugins, bitmaps
import os
import math

PLUGIN_ID = 999999999
doc = c4d.documents.GetActiveDocument()


def create_leaf(op):

    spline_data = op[c4d.PY_SPLINE]
    point_cnt = spline_data.GetKnotCount()
    leaf = c4d.BaseObject(c4d.Oplane)
    leaf[c4d.PRIM_PLANE_WIDTH] = 12
    leaf[c4d.PRIM_PLANE_HEIGHT] = 12
    leaf[c4d.PRIM_AXIS] = 2 #5
    leaf[c4d.PRIM_PLANE_SUBH] = 1
    leaf[c4d.PRIM_PLANE_SUBW] = 2

    if op[c4d.PY_CHECK_EXCLUDE]:
        if point_cnt >= 2:
            leaf[c4d.PRIM_PLANE_SUBH] = point_cnt - 1

            leaf = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[leaf], doc=op.GetDocument())[0]
            for tag in leaf.GetTags():
                if tag.IsInstanceOf(c4d.TAG_POLYSELECTION) or tag.IsInstanceOf(c4d.Tedgeselection):
                    tag.Remove()

            for index, pos in enumerate(leaf.GetAllPoints()):
                leaf.SetPoint(index, pos + c4d.Vector(0, 0, 6))

            knots = spline_data.GetKnots()
            counter = 0
            for index in range(0, (leaf.GetPointCount() - 1) - 1, 3):
                knot_pos = knots[counter]["vPos"]
                x = -knot_pos.y / 8
                y = knot_pos.z
                z = knot_pos.x / 2
                counter += 1

                leaf.SetPoint(index, c4d.Vector(x, y, z))
                if index + 1 == 1:
                    z_var = 0
                elif index + 1 == leaf.GetPointCount() - 2:
                    z_var = 0
                else:
                    z_var = 0 #x / 3
                leaf.SetPoint(index + 1, c4d.Vector(0, z_var, z))
                leaf.SetPoint(index + 2, c4d.Vector(-x, y, z))


        bend = c4d.BaseObject(c4d.Obend)
        bend.InsertUnder(leaf)

        bend[c4d.DEFORMOBJECT_ALIGNMENT] = 5
        if c4d.threading.GeIsMainThread():
            c4d.CallButton(bend, c4d.DEFORMOBJECT_FITTOPARENT)
        bend[c4d.DEFORMOBJECT_STRENGTH] = c4d.utils.DegToRad(30)
        bend[c4d.DEFORMOBJECT_ANGLE] = c4d.utils.DegToRad(90)
        bend[c4d.DEFORMOBJECT_ALIGNMENT] = 0
        if c4d.threading.GeIsMainThread():
            c4d.CallButton(bend, c4d.DEFORMOBJECT_FITTOPARENT)
        bend[c4d.DEFORMOBJECT_SIZE] = c4d.Vector(17, 50, 0)
        bend.SetRelPos(c4d.Vector(0, 0, 25))

        leaf = \
            c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[leaf], doc=op.GetDocument())[
                0]
        for tag in leaf.GetTags():
            if tag.IsInstanceOf(c4d.TAG_POLYSELECTION) or tag.IsInstanceOf(c4d.Tedgeselection):
                tag.Remove()

    else:
        leaf = \
        c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[leaf], doc=op.GetDocument())[0]
        for tag in leaf.GetTags():
            if tag.IsInstanceOf(c4d.TAG_POLYSELECTION) or tag.IsInstanceOf(c4d.Tedgeselection):
                tag.Remove()

        for index, pos in enumerate(leaf.GetAllPoints()):
            leaf.SetPoint(index, pos + c4d.Vector(0, 6, 0))

    return leaf


class Ivy_Maker(plugins.ObjectData):

    def __init__(self):
        self.SetOptimizeCache(True)

    def Init(self, op):
        self.InitAttr(op, bool, c4d.PY_CHECK_EXCLUDE)
        op[c4d.PY_CHECK_EXCLUDE] = True
        return True

    def GetVirtualObjects(self, op, hh):

        # dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_DATA)
        # if not dirty:
        #     return op.GetCache(hh)
        leaf = create_leaf(op)
        return leaf

    def GetDDescription(self, op, description, flags):
        if not description.LoadDescription(op.GetType()):
            return False

        spline_data = c4d.DescID(c4d.PY_SPLINE)
        in_exclude = c4d.DescID(c4d.PY_INEXCLUDE)
        db = description.GetParameterI(spline_data)
        db2 = description.GetParameterI(in_exclude)
        if op[c4d.PY_CHECK_EXCLUDE] == 0:

            db.SetBool(c4d.DESC_HIDE, True)
        else:
            if db2:
                db2.SetBool(c4d.DESC_HIDE, True)

        return (True, flags | c4d.DESCFLAGS_DESC_LOADED)

    def GetDEnabling(self, op, did, t_data, flags, itemdesc):

        # if did[0].id == c4d.PY_SPLINE and op[c4d.PY_CHECK_EXCLUDE] == 1:
        #     return False
        return True

    def Message(self, op, type, data):

        if type == c4d.MSG_MENUPREPARE:
            spline_data = c4d.SplineData()
            spline_data.MakeLinearSplineLinear(lPoints=6)
            spline_data.SetKnot(0, c4d.Vector(0, 10, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear)
            spline_data.SetKnot(1, c4d.Vector(10, 40, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear)
            spline_data.SetKnot(2, c4d.Vector(30, 75, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear)
            spline_data.SetKnot(3, c4d.Vector(52, 73, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear)
            spline_data.SetKnot(4, c4d.Vector(75, 50, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear)
            spline_data.SetKnot(5, c4d.Vector(105, 0, 0), c4d.FLAG_KNOT_T_BREAK, interpol=c4d.CustomSplineKnotInterpolationLinear)

            op[c4d.PY_SPLINE] = spline_data

        return True


if __name__ == "__main__":
    path, file = os.path.split(__file__)
    file = "icon.tif"
    new_path = os.path.join(path, "res", file)
    bitmap = bitmaps.BaseBitmap()
    bitmap.InitWith(new_path)
    plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="ivy_maker", g=Ivy_Maker, description="ivy_maker", icon=bitmap,
                                 info=c4d.OBJECT_GENERATOR)

res file:

CONTAINER Ivy_Maker //Grossbuchstaben
{
NAME IVY_MAKER; //dieser Name wird im Attribute Manager angezeigt
INCLUDE Obase;

GROUP ID_OBJECTPROPERTIES
{
    BOOL PY_CHECK_EXCLUDE {}

    SPLINE PY_SPLINE
    {
        SHOWGRID_H;
        SHOWGRID_V;

        MINSIZE_H 120;
        MINSIZE_V 120;

        EDIT_H;
        EDIT_V;

        X_MIN 0;
        X_MAX 120;

        Y_MIN 0;
        Y_MAX 120;

        X_STEPS 1;
        Y_STEPS 1;
    }
    IN_EXCLUDE PY_INEXCLUDE
    {

        ACCEPT { Obase; };

    }

}

}

Cheers
Tom

Hello @ThomasB,

Thank you for reaching out to us. As announced here, Maxon is currently conducting a company meeting. Please understand that our capability to answer questions is therefore limited at the moment.

Without debugging your plugin, I cannot give you here an exact answer, and I currently do not have the time to do that (extensively). Here are some pointers/ideas:

  1. The problem is located in GetVirtualObjects (GVO), i.e., create_leaf, with on certainty bordering probability.
  2. You use SendModelingCommand(SMC) in your create_leaf and with that GVO. Using SMC in GVO is possible, but you must adhere to threading restrictions.
  3. You violate threading restrictions when executing the SMC MCOMMAND_CURRENTSTATETOOBJECT in doc=op.GetDocument(), i.e., modify the document the node is part of while the caches of that document are being built (that is why GVO is being called).
  4. I personally would just write a function which generates the uvw mapped plane, and one which deforms it, but a middle ground could be to execute passes as shown below.
  5. There were also some other problems that I spotted, see code listing.

I could only partially reproduce your problem (in 2023.2) but the code might help you anyways. I have marked my changes with [FH].

Cheers,
Ferdinand

Result:
1d48419f-6332-41fb-8908-e44dd125d2e5-image.png

Code:

import c4d
from c4d import plugins, bitmaps
import os
import math

PLUGIN_ID = 999999999
doc = c4d.documents.GetActiveDocument() # No, just, no. Never do this in a plugin.

class Ivy_Maker(plugins.ObjectData):

    def __init__(self):
        self.SetOptimizeCache(True)

    def Init(self, op):
        """[FH] Added spline parameter and value init.
        """
        self.InitAttr(op, bool, c4d.PY_CHECK_EXCLUDE)
        self.InitAttr(op, c4d.SplineData, c4d.PY_SPLINE)

        op[c4d.PY_CHECK_EXCLUDE] = True
        spline: c4d.SplineData = c4d.SplineData()
        spline.MakeLinearSplineLinear(lPoints=6)
        spline.SetKnot(0, c4d.Vector(0, 10, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(1, c4d.Vector(10, 40, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(2, c4d.Vector(30, 75, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(3, c4d.Vector(52, 73, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(4, c4d.Vector(75, 50, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(5, c4d.Vector(105, 0, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        op[c4d.PY_SPLINE] = spline

        return True

    def GetVirtualObjects(self, op, hh):
        """[FH] 
        """
        def GetLeaf() -> c4d.PolygonObject:
            """[FH] This is your #create_leaf.
            """
            # Using SMC to create a plane is overkill, I personally would just write a function that
            # does create that PolygonObject manually. A middle ground could be executing the cache
            # pass on #plane so that we can hold of its cache.
            plane = c4d.BaseObject(c4d.Oplane)
            if not plane:
                raise MemoryError(f"{plane = }")

            spline: c4d.SplineData = op[c4d.PY_SPLINE]
            plane[c4d.PRIM_PLANE_WIDTH] = 12
            plane[c4d.PRIM_PLANE_HEIGHT] = 12
            plane[c4d.PRIM_AXIS] = 2
            plane[c4d.PRIM_PLANE_SUBH] = max(1, (spline.GetKnotCount() - 1))
            plane[c4d.PRIM_PLANE_SUBW] = 2

            temp: c4d.documents.BaseDocument = c4d.documents.BaseDocument()
            temp.InsertObject(plane)
            temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, 
                               c4d.BUILDFLAGS_NONE)

            cache: c4d.PolygonObject = plane.GetCache().GetClone() if plane.GetCache() else None
            if cache is None:
                raise RuntimeError("Could not build plane generator cache.")

            # Do your SplineData based profile deformation.
            knots = spline.GetKnots()
            counter = 0
            for index in range(0, (cache.GetPointCount() - 1) - 1, 3):
                knot_pos = knots[counter]["vPos"]
                x = -knot_pos.y / 8
                y = knot_pos.z
                z = knot_pos.x / 2
                counter += 1

                cache.SetPoint(index, c4d.Vector(x, y, z))
                if index + 1 == 1:
                    z_var = 0
                elif index + 1 == cache.GetPointCount() - 2:
                    z_var = 0
                else:
                    z_var = 0 #x / 3
                cache.SetPoint(index + 1, c4d.Vector(0, z_var, z))
                cache.SetPoint(index + 2, c4d.Vector(-x, y, z))

            cache.Message(c4d.MSG_UPDATE)

            # Get the bend deformation, removed never to be executed CallButton code since we are
            # not on the MT here. Instead, we use the bounding box of #cache to fit the bend object.
            boundingBox: c4d.Vector = cache.GetRad() * 2
            bend: c4d.BaseObject = c4d.BaseObject(c4d.Obend)
            if not bend:
                raise MemoryError(f"{bend = }")

            bend.InsertUnder(cache)
            bend[c4d.DEFORMOBJECT_STRENGTH] = c4d.utils.DegToRad(30)
            bend[c4d.DEFORMOBJECT_SIZE] = c4d.Vector(0, boundingBox.z, boundingBox.x)

            # Rotate and move the bend deformer into place.
            bend.SetMg(c4d.Matrix(off=cache.GetMp()) * 
                       c4d.utils.MatrixRotY(c4d.utils.DegToRad(90)) * 
                       c4d.utils.MatrixRotZ(c4d.utils.DegToRad(90)))

            # Get the deform cache for that bend deformation.
            temp.InsertObject(cache)
            temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, 
                               c4d.BUILDFLAGS_NONE)
            if not cache.GetDeformCache():
                raise RuntimeError("Could not build plane generator deform cache.")

            result: c4d.PolygonObject = cache.GetDeformCache().GetClone()
            temp.Flush()

            return result

        return GetLeaf()

    def GetDDescription(self, op, description, flags):
        if not description.LoadDescription(op.GetType()):
            return False

        spline_data = c4d.DescID(c4d.PY_SPLINE)
        in_exclude = c4d.DescID(c4d.PY_INEXCLUDE)
        db = description.GetParameterI(spline_data)
        db2 = description.GetParameterI(in_exclude)
        if op[c4d.PY_CHECK_EXCLUDE] == 0:

            db.SetBool(c4d.DESC_HIDE, True)
        else:
            if db2:
                db2.SetBool(c4d.DESC_HIDE, True)

        return (True, flags | c4d.DESCFLAGS_DESC_LOADED)

    def GetDEnabling(self, op, did, t_data, flags, itemdesc):

        # if did[0].id == c4d.PY_SPLINE and op[c4d.PY_CHECK_EXCLUDE] == 1:
        #     return False
        return True

    def Message(self, op, type, data):
        """[FH] 
        """
        # [FH] The message MSG_MENUPREPARE is not the place to init a parameter, unless you 
        # intentionally want to overwrite user settings.
        return True


if __name__ == "__main__":
    path, file = os.path.split(__file__)
    file = "icon.tif"
    new_path = os.path.join(path, "res", file)
    bitmap = bitmaps.BaseBitmap()
    bitmap.InitWith(new_path)
    plugins.RegisterObjectPlugin(
        id=PLUGIN_ID, str="ivy_maker", g=Ivy_Maker, description="ivy_maker", icon=bitmap,
        info=c4d.OBJECT_GENERATOR)

MAXON SDK Specialist
developers.maxon.net

Hello @ThomasB,

Thank you for reaching out to us. As announced here, Maxon is currently conducting a company meeting. Please understand that our capability to answer questions is therefore limited at the moment.

Without debugging your plugin, I cannot give you here an exact answer, and I currently do not have the time to do that (extensively). Here are some pointers/ideas:

  1. The problem is located in GetVirtualObjects (GVO), i.e., create_leaf, with on certainty bordering probability.
  2. You use SendModelingCommand(SMC) in your create_leaf and with that GVO. Using SMC in GVO is possible, but you must adhere to threading restrictions.
  3. You violate threading restrictions when executing the SMC MCOMMAND_CURRENTSTATETOOBJECT in doc=op.GetDocument(), i.e., modify the document the node is part of while the caches of that document are being built (that is why GVO is being called).
  4. I personally would just write a function which generates the uvw mapped plane, and one which deforms it, but a middle ground could be to execute passes as shown below.
  5. There were also some other problems that I spotted, see code listing.

I could only partially reproduce your problem (in 2023.2) but the code might help you anyways. I have marked my changes with [FH].

Cheers,
Ferdinand

Result:
1d48419f-6332-41fb-8908-e44dd125d2e5-image.png

Code:

import c4d
from c4d import plugins, bitmaps
import os
import math

PLUGIN_ID = 999999999
doc = c4d.documents.GetActiveDocument() # No, just, no. Never do this in a plugin.

class Ivy_Maker(plugins.ObjectData):

    def __init__(self):
        self.SetOptimizeCache(True)

    def Init(self, op):
        """[FH] Added spline parameter and value init.
        """
        self.InitAttr(op, bool, c4d.PY_CHECK_EXCLUDE)
        self.InitAttr(op, c4d.SplineData, c4d.PY_SPLINE)

        op[c4d.PY_CHECK_EXCLUDE] = True
        spline: c4d.SplineData = c4d.SplineData()
        spline.MakeLinearSplineLinear(lPoints=6)
        spline.SetKnot(0, c4d.Vector(0, 10, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(1, c4d.Vector(10, 40, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(2, c4d.Vector(30, 75, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(3, c4d.Vector(52, 73, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(4, c4d.Vector(75, 50, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        spline.SetKnot(5, c4d.Vector(105, 0, 0), c4d.FLAG_KNOT_T_BREAK, 
                       interpol=c4d.CustomSplineKnotInterpolationLinear)
        op[c4d.PY_SPLINE] = spline

        return True

    def GetVirtualObjects(self, op, hh):
        """[FH] 
        """
        def GetLeaf() -> c4d.PolygonObject:
            """[FH] This is your #create_leaf.
            """
            # Using SMC to create a plane is overkill, I personally would just write a function that
            # does create that PolygonObject manually. A middle ground could be executing the cache
            # pass on #plane so that we can hold of its cache.
            plane = c4d.BaseObject(c4d.Oplane)
            if not plane:
                raise MemoryError(f"{plane = }")

            spline: c4d.SplineData = op[c4d.PY_SPLINE]
            plane[c4d.PRIM_PLANE_WIDTH] = 12
            plane[c4d.PRIM_PLANE_HEIGHT] = 12
            plane[c4d.PRIM_AXIS] = 2
            plane[c4d.PRIM_PLANE_SUBH] = max(1, (spline.GetKnotCount() - 1))
            plane[c4d.PRIM_PLANE_SUBW] = 2

            temp: c4d.documents.BaseDocument = c4d.documents.BaseDocument()
            temp.InsertObject(plane)
            temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, 
                               c4d.BUILDFLAGS_NONE)

            cache: c4d.PolygonObject = plane.GetCache().GetClone() if plane.GetCache() else None
            if cache is None:
                raise RuntimeError("Could not build plane generator cache.")

            # Do your SplineData based profile deformation.
            knots = spline.GetKnots()
            counter = 0
            for index in range(0, (cache.GetPointCount() - 1) - 1, 3):
                knot_pos = knots[counter]["vPos"]
                x = -knot_pos.y / 8
                y = knot_pos.z
                z = knot_pos.x / 2
                counter += 1

                cache.SetPoint(index, c4d.Vector(x, y, z))
                if index + 1 == 1:
                    z_var = 0
                elif index + 1 == cache.GetPointCount() - 2:
                    z_var = 0
                else:
                    z_var = 0 #x / 3
                cache.SetPoint(index + 1, c4d.Vector(0, z_var, z))
                cache.SetPoint(index + 2, c4d.Vector(-x, y, z))

            cache.Message(c4d.MSG_UPDATE)

            # Get the bend deformation, removed never to be executed CallButton code since we are
            # not on the MT here. Instead, we use the bounding box of #cache to fit the bend object.
            boundingBox: c4d.Vector = cache.GetRad() * 2
            bend: c4d.BaseObject = c4d.BaseObject(c4d.Obend)
            if not bend:
                raise MemoryError(f"{bend = }")

            bend.InsertUnder(cache)
            bend[c4d.DEFORMOBJECT_STRENGTH] = c4d.utils.DegToRad(30)
            bend[c4d.DEFORMOBJECT_SIZE] = c4d.Vector(0, boundingBox.z, boundingBox.x)

            # Rotate and move the bend deformer into place.
            bend.SetMg(c4d.Matrix(off=cache.GetMp()) * 
                       c4d.utils.MatrixRotY(c4d.utils.DegToRad(90)) * 
                       c4d.utils.MatrixRotZ(c4d.utils.DegToRad(90)))

            # Get the deform cache for that bend deformation.
            temp.InsertObject(cache)
            temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, 
                               c4d.BUILDFLAGS_NONE)
            if not cache.GetDeformCache():
                raise RuntimeError("Could not build plane generator deform cache.")

            result: c4d.PolygonObject = cache.GetDeformCache().GetClone()
            temp.Flush()

            return result

        return GetLeaf()

    def GetDDescription(self, op, description, flags):
        if not description.LoadDescription(op.GetType()):
            return False

        spline_data = c4d.DescID(c4d.PY_SPLINE)
        in_exclude = c4d.DescID(c4d.PY_INEXCLUDE)
        db = description.GetParameterI(spline_data)
        db2 = description.GetParameterI(in_exclude)
        if op[c4d.PY_CHECK_EXCLUDE] == 0:

            db.SetBool(c4d.DESC_HIDE, True)
        else:
            if db2:
                db2.SetBool(c4d.DESC_HIDE, True)

        return (True, flags | c4d.DESCFLAGS_DESC_LOADED)

    def GetDEnabling(self, op, did, t_data, flags, itemdesc):

        # if did[0].id == c4d.PY_SPLINE and op[c4d.PY_CHECK_EXCLUDE] == 1:
        #     return False
        return True

    def Message(self, op, type, data):
        """[FH] 
        """
        # [FH] The message MSG_MENUPREPARE is not the place to init a parameter, unless you 
        # intentionally want to overwrite user settings.
        return True


if __name__ == "__main__":
    path, file = os.path.split(__file__)
    file = "icon.tif"
    new_path = os.path.join(path, "res", file)
    bitmap = bitmaps.BaseBitmap()
    bitmap.InitWith(new_path)
    plugins.RegisterObjectPlugin(
        id=PLUGIN_ID, str="ivy_maker", g=Ivy_Maker, description="ivy_maker", icon=bitmap,
        info=c4d.OBJECT_GENERATOR)

MAXON SDK Specialist
developers.maxon.net

@ferdinand

I could only partially reproduce your problem (in 2023.2) but the code might help you anyways. I have marked my changes with [FH].

And how that helped me. Thank you very much that was very clear for me to understand.
Thanks alot

Tom

Cheers
Tom

@ferdinand
Hi Ferdinand, I reproduced your code and changed the things with this cache and ExecutePasses etc. in my code.
But there is still a problem with it, when I call Reload Python Plugins or when I call the plugin from Menue. The leaf is not bended. Only when I click on the splinedata field to move a point does the leaf jump into the curved shape.

When I click make editable directly after I called it from the menue, the leaf is bended as polygon object, but not in the view after calling it from menue.

Cheers
Tom

Hello @ThomasB,

I cannot reproduce this here. On which version and OS are you? Judging by the code you have sent earlier, I would say you have simply reintroduced a bug.

ivy.gif

My code should not be able to return an unbent leaf, it either returns a leaf or throws and error. One of the problems of your code was that you had this call-button code in there which more or less led to a race condition between fit-command and the cache building of your leaf. I also removed quite a bit of questionable branching in your code (if elif else). I would suggest that you debug your plugin to see its branching when the bug occurs. We cannot debug your plugin for you.

Cheers,
Ferdinand

MAXON SDK Specialist
developers.maxon.net

Hello @ThomasB,

without further questions or postings, we will consider this topic as solved by Monday 05/06/2023 and flag it accordingly.

Thank you for your understanding,
Maxime.