Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Login

    Object-Plugin - Message() method POSTSETPARAMETER c4d.CallCommand(12147) - Freezing

    Cinema 4D SDK
    windows python r23
    2
    13
    1.9k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ferdinandF
      ferdinand @ThomasB
      last edited by ferdinand

      Hello @thomasb,

      Thank you for reaching out to us. On the long run, it is best to share executable code here with us, as this will be otherwise more guess work than everything else for us. As always, you can share data confidentially with us as line out in the forum guidelines.

      Here are some points:

      1. NodeData hooks, especially things like ObjectData, should not enqueue scene redraw events. You can make it work when you are careful, but it simply should not be necessary as Cinema 4D does handle redraws for you.
      2. When the plugin of this thread is identical to the plugin in this thread of yours, the reason for you trying to do this might be that you are trying to modify the cache of your plugin, the data that is referenced by self.frame. Doing this is not supported, caches are static by definition. Modifying it anyways will then entail having to manually force a redraw upon the change event. Doing this is not supported.
      3. When you are really determined doing this anyways, you could try to only draw on the drawing main thread. if (GeIsMainThread() and not GeIsMainThreadAndNoDrawThread()). But I would not recommend doing it.
      4. I would recommend using c4d.EventAdd(c4d.EVENT_ENQUEUE_REDRAW) (slower since one is doing things potentially twice, but also safer since there can only be one additional redraw enqueued, i.e., one cannot completely saturate the system) or c4d.EventAdd(c4d.EVENT_FORCEREDRAW) (less safe, you can end up in constantly restarting an ongoing redraw when not careful). But that is all very hypothetical since a MSG_DESCRIPTION_POSTSETPARAMETER event should not entail a manual redraw.

      For the other issues you mention here, op being a null reference and the make editable behavior your plugin, I will have to look at your code to tell you anything concrete; but they are likely tied to (2.).

      Cheers,
      Ferdinand

      MAXON SDK Specialist
      developers.maxon.net

      ThomasBT 3 Replies Last reply Reply Quote 0
      • ThomasBT
        ThomasB @ferdinand
        last edited by ThomasB

        @ferdinand
        Details ansehen
        1.211 / 5.000
        Übersetzungsergebnisse
        Übersetzung
        I'm sorry Ferdinand for being such a stupid person. I haven't been programming plugins that long and I have to be honest, the road to get there was really rocky, so getting into the API wasn't particularly easy for me. I had to fight through it myself.
        But no matter here you have a test code.
        It's all still a bit muddled, especially as far as the description IDs are concerned, sometimes they don't initialize, well, it doesn't matter. I wasn't so aware of the self.parameter.
        Well, the class BowSpline calculates the spline, other splines are supposed to be calculated later in the same class with other methods. I could also do this with functions?

        Well the problem is it doesn't update the spline in the viewport when I change a parameter in the description even though it breaks. Then I thought I'd catch that with POSTSETPARAMETER and call a redraw.
        Oh, the problem here with the instances is that if I want to make them editable, Cinema freezes.
        I load the profile spline and the handle in the GVO using a method which loads the hyperfile..
        I could possibly do that in my init_(self) method.
        here is the code: as already said something messi yet.

        import c4d
        from c4d import plugins, bitmaps
        import os
        
        import math
        
        # import copy
        
        PLUGIN_ID = 223456789
        doc = c4d.documents.GetActiveDocument()
        HANDLE_X = 7
        HANDLE_Z = 8.6
        PY_WINDOW_LIST = 10002
        PY_WINDOW_WIDTH = 10101
        PY_WINDOW_HEIGHT = 10102
        PY_ARCH_HEIGHT = 10103
        PY_ARCH_SUBD = 10104
        PY_OFFSET = 10105
        PY_TILT_CHECK = 10106
        PY_WINDOW_OPEN = 10107
        PY_WINDOW_DIRECTION = 10108
        PY_BANK = 10109
        
        
        class BowSpline():
            def __init__(self, width, height, arch, subd):
                self.spline = c4d.BaseObject(c4d.Ospline)
                self.spline[c4d.SPLINEOBJECT_CLOSED] = True
        
                self.height = height
                self.width = width
                self.arch_height = arch
                if self.arch_height > self.width / 2:
                    self.arch_height = self.width / 2
                if self.height < self.arch_height:
                    self.height = self.arch_height
                self.subd_points = subd
                self.create()
        
            def create(self):
                if self.subd_points % 2 != 0:
                    pass
                elif self.subd_points % 2 == 0:
                    if self.subd_points >= 0:
                        self.subd_points = self.subd_points + 1
                    # else:
                    #     self.subd_points = 0
        
                if self.arch_height == 0:
                    self.subd_points = 0
                points = self.subd_points + 4
                self.spline.ResizeObject(points)
        
                y_value = self.height - self.arch_height
        
                self.spline.SetPoint(1, c4d.Vector(self.width, 0, 0))
                self.spline.SetPoint(2, c4d.Vector(self.width, y_value, 0))
                self.spline.SetPoint(points - 1, c4d.Vector(0, y_value, 0))
        
                if self.subd_points >= 0:
                    if self.arch_height != 0:
                        radius = (4 * (self.arch_height ** 2) + self.width ** 2) / (8 * self.arch_height)
                    else:
                        radius = 0
        
                midpoint_pos = c4d.Vector(self.width / 2, self.height - radius, 0)
                rv_corner = self.spline.GetPoint(2) - midpoint_pos
                rv_right = c4d.Vector(self.width, midpoint_pos.y, 0) - midpoint_pos
                first_point_angle = c4d.utils.GetAngle(rv_right, rv_corner)
                last_point_angle = c4d.utils.DegToRad(180 - c4d.utils.RadToDeg(first_point_angle))
                angle_adder = (c4d.utils.DegToRad(180) - (2 * first_point_angle)) / (self.subd_points + 1)
                angle = first_point_angle + angle_adder
                for index in range(3, points - 1):
                    sine, cose = c4d.utils.SinCos(angle)
                    y = sine * radius
                    x = cose * radius
                    new_point = c4d.Vector(midpoint_pos.x + x, midpoint_pos.y + y, 0)
                    self.spline.SetPoint(index, new_point)
                    angle += angle_adder
        
        
        class Windows2023(plugins.ObjectData):
        
            def __init__(self):
                self.pos = 5  # index where to position the window axis
                self.SetOptimizeCache(True)
                self.profile = None
                self.frame_spline = None
                self.window_spline = None
                self.new_doc = c4d.documents.BaseDocument()
                self.spline = c4d.BaseObject(c4d.Ospline)
                self.frame_sweep = c4d.BaseObject(c4d.Osweep)
                self.window_sweep = c4d.BaseObject(c4d.Osweep)
                self.extrude = None
                self.load_profile()
                self.start = True
        
            def Init(self, op):
                # self.SetOptimizeCache(True)
                self.InitAttr(op, int, c4d.PY_WINDOW_LIST)
                op[c4d.PY_WINDOW_LIST] = 1
                self.InitAttr(op, float, c4d.PY_WINDOW_WIDTH)
                op[c4d.PY_WINDOW_WIDTH] = 100
                self.InitAttr(op, float, c4d.PY_WINDOW_HEIGHT)
                op[c4d.PY_WINDOW_HEIGHT] = 120
                self.InitAttr(op, float, c4d.PY_ARCH_HEIGHT)
                op[c4d.PY_ARCH_HEIGHT] = 20
                self.InitAttr(op, int, c4d.PY_ARCH_SUBD)
                op[c4d.PY_ARCH_SUBD] = 12
                self.InitAttr(op, bool, 10106)
                op[10106] = False
                self.InitAttr(op, float, 10107)
                op[10107] = 0.0
                self.InitAttr(op, bool, 10108)
                op[10108] = False
                self.InitAttr(op, float, 10109)
                op[10109] = 0.0
        
                # self.InitAttr(op, float, c4d.PY_OFFSET)
                # op[c4d.PY_OFFSET] = 0.0
                return True
        
            def load_profile(self):
                # c4d.documents.MergeDocument(self.new_doc, f"{path}/res/profile.c4d", c4d.SCENEFILTER_OBJECTS)
                frame_path = os.path.join(path, "res", "w_f")
                window_path = os.path.join(path, "res", "w_w")
                frame_profile = c4d.BaseObject(c4d.Ospline)
                window_profile = c4d.BaseObject(c4d.Ospline)
                c4d.storage.ReadHyperFile(None, frame_profile, frame_path, 100)
                c4d.storage.ReadHyperFile(None, window_profile, window_path, 100)
                return frame_profile, window_profile
        
            def load_handle(self):
                handle_path = os.path.join(path, "res", "w_h")
                handle = c4d.BaseObject(c4d.Opolygon)
                c4d.storage.ReadHyperFile(None, handle, handle_path, 100)
                return handle
        
            def load_sill(self):
                sill_path = os.path.join(path, "res", "b_al")
                sill = c4d.BaseObject(c4d.Ospline)
                c4d.storage.ReadHyperFile(None, sill, sill_path, 100)
                return sill
        
            def make_sill(self, op):
                extrude: c4d.BaseObject = c4d.BaseObject(c4d.Oextrude)
                extrude[c4d.EXTRUDEOBJECT_EXTRUSIONOFFSET] = - op[c4d.PY_WINDOW_WIDTH]
                sill = self.load_sill()
                vector_z = op[PY_BANK]
                if vector_z < 0:
                    vector_z *= -1
                for point in range(sill.GetPointCount()):
                    if point == 6 or point == 7:
                        continue
                    if sill.GetPoint(point).z >= 0:
                        vector_z = 0
                    sill.SetPoint(point, sill.GetPoint(point) - c4d.Vector(0, 0, vector_z))
                    vector_z = op[PY_BANK]
        
                sill.InsertUnder(extrude)
                extrude = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT,
                                                        doc=op.GetDocument(),
        
        
                                                        list=[extrude])[0]
                for tag in extrude.GetTags():
                    if tag.IsInstanceOf(c4d.Tpolygonselection) or tag.IsInstanceOf(c4d.Tedgeselection):
                        tag.Remove()
        
        
        
        
                extrude.SetName("Sill")
                extrude.SetPhong(on=True, anglelimit=True, angle=c4d.utils.DegToRad(60))
                return extrude
        
            def bow_spline(self, width, height, arch, subd):
                spline = c4d.BaseObject(c4d.Ospline)
                spline[c4d.SPLINEOBJECT_CLOSED] = True
        
                if width != 0:
                    pass
                elif subd % 2 == 0:
                    if subd >= 0:
                        subd += 1
                    # else:
                    #     self.subd_points = 0
        
                if arch == 0:
                    subd = 1
                points = subd + 4
                spline.ResizeObject(points)
        
                # if self.arch_height > self.width/ 2:
                #
                #     self.arch_height = self.width/ 2
        
                # if self.width < self.arch_height:
                #     self.width = self.arch_height
        
                y_value = height - arch
                spline.SetPoint(1, c4d.Vector(width, 0, 0))
                spline.SetPoint(2, c4d.Vector(width, y_value, 0))
                spline.SetPoint(points - 1, c4d.Vector(0, y_value, 0))
        
                # b_height_pos = c4d.Vector(width / 2, height + b_height, 0)
        
                # B = 4  H = 5
        
                if subd > 0:
                    if arch != 0:
                        radius = (4 * (arch ** 2) + width ** 2) / (8 * arch)
                    else:
                        radius = 0
        
                bogen_height = arch
                midpoint_pos = c4d.Vector(width / 2, height - radius, 0)
                rv_corner = spline.GetPoint(2) - midpoint_pos
                rv_right = c4d.Vector(width, midpoint_pos.y, 0) - midpoint_pos
        
                first_point_angle = c4d.utils.GetAngle(rv_right, rv_corner)
                last_point_angle = c4d.utils.DegToRad(180 - c4d.utils.RadToDeg(first_point_angle))
                angle_adder = (c4d.utils.DegToRad(180) - (2 * first_point_angle)) / (subd + 1)
                angle = first_point_angle + angle_adder
                for index in range(3, points - 1):
                    sine, cose = c4d.utils.SinCos(angle)
                    y = sine * radius
                    x = cose * radius
                    new_point = c4d.Vector(midpoint_pos.x + x, midpoint_pos.y + y, 0)
                    spline.SetPoint(index, new_point)
                    angle += angle_adder
        
                return spline
        
            def make_glass(self, width, height, arch=0, subd=0):
        
        
                spline = BowSpline()
            def make_arch_frames(self, node):
                # self.new_doc.Flush()
        
                if node[10108]:
                    self.pos = 38 #the index of the frame where to position the axis
                else:
                    self.pos = 17
        
                frame_spline = BowSpline(node[c4d.PY_WINDOW_WIDTH], node[c4d.PY_WINDOW_HEIGHT], node[c4d.PY_ARCH_HEIGHT],
                                         node[c4d.PY_ARCH_SUBD]).spline
                frame_sweep = c4d.BaseObject(c4d.Osweep)
                frame_sweep.SetName("Frame")
                frame_spline.InsertUnder(frame_sweep)
        
                window_spline = BowSpline(node[c4d.PY_WINDOW_WIDTH], node[c4d.PY_WINDOW_HEIGHT], node[c4d.PY_ARCH_HEIGHT],
                                          node[c4d.PY_ARCH_SUBD]).spline
                window_sweep = c4d.BaseObject(c4d.Osweep)
                window_sweep.SetName("Window")
                window_spline.InsertUnder(window_sweep)
        
                profiles = self.load_profile()
                frame_profile = profiles[0]
                frame_profile.InsertUnder(frame_sweep)
        
                window_profile = profiles[1]
                window_profile.InsertUnder(window_sweep)
        
                frame_sweep: c4d.BaseObject = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[frame_sweep],
                                                      doc=node.GetDocument())[0]
                window_sweep: c4d.BaseObject = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT, list=[window_sweep],
                                                       doc=node.GetDocument())[0]
        
                frame_sweep.SetPhong(on=True, anglelimit=True, angle=c4d.utils.DegToRad(60))
                window_sweep.SetPhong(on=True, anglelimit=True, angle=c4d.utils.DegToRad(60))
        
                pos = window_sweep.GetPoint(self.pos)
                window_axis = c4d.BaseObject(c4d.Onull)
                window_axis.SetName("Axis")
                container = c4d.BaseObject(c4d.Onull)
                frame_sweep.InsertUnder(container)
                window_axis.InsertUnder(container.GetDown())
                window_axis.SetAbsPos(pos)
                window_sweep.InsertUnder(window_axis)
                window_sweep.SetRelPos(container.GetAbsPos() - pos)
        
                if node[10105]:
                    container.GetDown().SetRelPos(c4d.Vector(0, 0, node[10105]))
        
                if node[10106] is not None:
                    if node[10106]:
                        if self.pos == 17:
                            if node[10107] is not None:
                                window_axis.SetRelRot(c4d.Vector(0, c4d.utils.DegToRad(-5), 0))
                        else:
                            if node[10107] is not None:
                                window_axis.SetRelRot(c4d.Vector(0, c4d.utils.DegToRad(-5), 0))
        
                    else:
                        if self.pos == 38:
                            if node[10107] is not None:
                                window_axis.SetRelRot(c4d.Vector(-node[10107], 0, 0))
                        else:
                            if node[10107] is not None:
                                window_axis.SetRelRot(c4d.Vector(node[10107], 0, 0))
                #self.new_doc.Flush()
        
                return container
                # return frame, window
        
            def Message(self, node, type, data):
        
                # Arch Bow Settings
                # if node[c4d.PY_WINDOW_LIST] == 1:
        
                if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
                    if c4d.threading.GeIsMainThread():
                        c4d.CallCommand(12147)
        
        
                if type == c4d.MSG_DESCRIPTION_COMMAND:
        
                    if data['id'][0].id == c4d.PY_CREATE_BOOL:
                        self.extrude = c4d.BaseObject(c4d.Oextrude)
                        self.extrude.SetName("Boole")
                        self.extrude[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 1
                        tag = self.extrude.MakeTag(c4d.Tdisplay)
                        tag[c4d.DISPLAYTAG_AFFECT_DISPLAYMODE] = True
                        tag[c4d.DISPLAYTAG_SDISPLAYMODE] = 6
                        tag[c4d.DISPLAYTAG_WDISPLAYMODE] = 0
                        frame_spline = BowSpline(node[c4d.PY_WINDOW_WIDTH], node[c4d.PY_WINDOW_HEIGHT],
                                                 node[c4d.PY_ARCH_HEIGHT],
                                                 node[c4d.PY_ARCH_SUBD]).spline
                        frame_spline.InsertUnder(self.extrude)
        
                        self.extrude = c4d.utils.SendModelingCommand(command=c4d.MCOMMAND_CURRENTSTATETOOBJECT,
                                                                doc=node.GetDocument(),
                                                                list=[self.extrude])[0]
        
        
                        null = c4d.BaseObject(c4d.Onull)
                        null.SetName("Window_Bool Axis")
                        extrude: c4d.BaseObject = self.extrude.GetDown()
                        extrude.SetName("Window-Boole")
                        # del tags
                        for tag in extrude.GetTags():
                            if tag.IsInstanceOf(c4d.Tpolygonselection) or tag.IsInstanceOf(c4d.Tedgeselection):
                                tag.Remove()
        
                        extrude[c4d.ID_BASEOBJECT_VISIBILITY_RENDER] = 1
                        extrude.InsertUnder(null)
                        self.extrude = None
                        extrude.SetRelPos(extrude.GetRelPos() - c4d.Vector(0, 0, 10))
                        null.SetMg(node.GetMg())
                        node.GetDocument().InsertObject(null)
                        c4d.EventAdd()
        
                    elif data['id'][0].id == 10004:
                        c4d.CallCommand(5126)
        
        
        
                return True
        
            def GetVirtualObjects(self, op, hh):
                # dirty = True if a cache is dirty or if the data (any parameters) of the object changed.
                # If nothing changed and a cache is present, return the cache
        
                # cache deaktiveren
        
                dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_DESCRIPTION)
                if not dirty:
                    return op.GetCache(hh)
        
                """Settings for Arch Window"""
                if op[c4d.PY_WINDOW_LIST] == 1:
                    frames = self.make_arch_frames(op)
                    handle = self.load_handle()
                    handle.InsertUnder(frames.GetDown().GetDown().GetDown())
                    sill = self.make_sill(op)
                    sill.InsertUnder(frames.GetDown())
        
                    if op[PY_WINDOW_DIRECTION]:
                        handle.SetRelPos(c4d.Vector(HANDLE_X, op[c4d.PY_WINDOW_HEIGHT] / 2, HANDLE_Z))
        
                    else:
                        handle.SetRelPos(c4d.Vector(op[c4d.PY_WINDOW_WIDTH] - HANDLE_X, op[c4d.PY_WINDOW_HEIGHT] / 2, HANDLE_Z))
        
                    return frames
        
                elif op[c4d.PY_WINDOW_LIST] == 2:
                    return c4d.BaseObject(c4d.Ocube)
        
            def GetDDescription(self, op, description, flags):
        
                if not description.LoadDescription(op.GetType()):
                    return False
        
                single_id = description.GetSingleDescID()
                arch_height = c4d.DescID(c4d.PY_ARCH_HEIGHT)  # using ID from above post
                height = c4d.DescID(c4d.PY_WINDOW_HEIGHT)
                if single_id is None or arch_height.IsPartOf(single_id)[0]:
                    db = description.GetParameterI(c4d.PY_ARCH_HEIGHT)
                    db.SetFloat(c4d.DESC_MAX, op[c4d.PY_WINDOW_WIDTH] / 2)
        
                if op[c4d.PY_ARCH_HEIGHT] is not None:
                    if single_id is None or height.IsPartOf(single_id)[0]:
                        db2 = description.GetParameterI(c4d.PY_WINDOW_HEIGHT)
                        db2.SetFloat(c4d.DESC_MIN, op[c4d.PY_ARCH_HEIGHT])
        
                group_1 = c4d.DescID(c4d.PY_GROUP_1)
                if single_id is None or group_1.IsPartOf(single_id)[0]:
                    if op[c4d.PY_WINDOW_LIST] != 1:
                        db = description.GetParameterI(c4d.PY_GROUP_1)
                        db.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_ANIMATION_DISTANCE and op[c4d.PY_LED_MODE] == 0:
            #         return False
            #     elif did[0].id == c4d.PY_VERT_ANIMATION_DISTANCE and op[c4d.PY_LED_MODE] == 0:
            #         return False
            #     elif did[0].id == c4d.PY_PANEL_LENGTH and op[c4d.PY_LED_MODE] == 0:
            #         return False
            #     elif did[0].id == c4d.PY_VERT_ANIMATION_DISTANCE and op[c4d.PY_LED_MODE] == 2:
            #         return False
            #
            #     return True
        
        
        if __name__ == "__main__":
            path, file = os.path.split(__file__)
            files = "icon.tif"
            new_path = os.path.join(path, "res", files)
            bitmap = bitmaps.BaseBitmap()
            bitmap.InitWith(new_path)
            plugins.RegisterObjectPlugin(id=PLUGIN_ID, str="Windows2023", g=Windows2023, description="windows2023",
                                         icon=bitmap,
                                         info=c4d.OBJECT_GENERATOR)
        
        
        1 Reply Last reply Reply Quote 0
        • ThomasBT
          ThomasB
          last edited by

          the function bowspline in the object plugin is not neccessary , this is the part for the Bowspline class I just forget to delete it because first I did this in a class

          1 Reply Last reply Reply Quote 0
          • ThomasBT
            ThomasB @ferdinand
            last edited by ThomasB

            @ferdinand said in Object-Plugin - Message() method POSTSETPARAMETER c4d.CallCommand(12147) - Freezing:

            4d.EventAdd(c4d.EVENT_ENQUEUE_REDRAW)

            I have changed the code now a bit , I do not load the profiles, the handle inside the GVO via a function , I load it in my def ___init__(self) into variables.
            Here in the video you see the problem. (Videolink below) When I change a parameter in the gui it doesn´t update in the viewport . I have to press "A" for Redraw. That is the CallCommand I used in the Message() method
            So that was the reason why I chatched the MSG_DESCRIPTION_POSTSETPARAMETER

            if type == c4d.MSG_DESCRIPTION_POSTSETPARAMETER:
                        
                        if data['descid'][0].id == PY_MY_PARAMETER:
                            if c4d.threading.GeIsMainThread():
                                c4d.CallCommand(12147)
            

            But then if I make an Instance with the node as reference and try to make the Instance editable , Cinema freezes.

            So I tried your c4d.EventAdd() method ....where to call it? In the Message()?

            Or can I use node.Message(c4d.MSG_UPDATE) anywhere?. I do not now. the message system is still unclear to me.

            Anyways in the video you can see I can make the Instance Object editable. Cinema do not freeze because I commented this CallCommand thing out in the Message Method. Then it works. But as soon I use it , Cinema freezes.
            Also you can observe in the video when I inserted the Instance Object and then change a parameter the Instance Object shows the change the node itself not :-).
            That is not what I want.

            • First I need to force the object to update in the viewport

            • Second I need that to work with the instance, when I make it editabel that it does not freezes Cinema .

            Videolink

            1 Reply Last reply Reply Quote 0
            • ThomasBT
              ThomasB @ferdinand
              last edited by ThomasB

              @ferdinand
              I have rewritten a bit my code , now I load the profile spline in my
              __init__ method and keep hold of it in a porperty called
              self.profile. It returns a tuple with two profiles.
              when I call the method.

              frames  = self.make_arch_frames(op)
              

              to just create the frames, the profile are used there.
              And when I just return the frames....and simultanously in my message I call the redraw, no everything works fine the Instance Object is editable. Cinema no longer freezes.
              But when I create the handle which is also loaded in my

              __init__(self)
              

              method as property it doesnt work
              Why am I not able to place the handle in the hierachy of frames

              the virtual structure of frames is:
              Screenshot 2023-03-14 211521.png
              I did this handle placement directly now in the self.make_arch_frames, but anyways, Cinema freezes when I make the Instance editable.
              The frame profile is loaded in the same way as the handle.

              Ah I think this is a Bug in the Api of R23 and earlier, I opened a thread a few years ago...and cinema crashed also loading geometrie with hyperfile.....loading it into a virtual document would be the best maybe

              1 Reply Last reply Reply Quote 0
              • ThomasBT
                ThomasB
                last edited by

                This post is deleted!
                1 Reply Last reply Reply Quote 0
                • ThomasBT
                  ThomasB
                  last edited by

                  Yes that was the problem, everything works fine , but when I clone the object and try to make the clone object editable it doesn`t work with this redraw thing.
                  But I am not able to get my object show the change in realtime when I change the width-parameter for instance without this CallCommand(). I want it in the way like the cube object, when I change the width that it shows it directly in the viewport.

                  ThomasBT 1 Reply Last reply Reply Quote 0
                  • ThomasBT
                    ThomasB @ThomasB
                    last edited by

                    @ferdinand

                    it only works when I delete this :

                    dirty = op.CheckCache(hh) or op.IsDirty(c4d.DIRTYFLAGS_DESCRIPTION)
                            if not dirty:
                                return op.GetCache(hh)
                    

                    Then I also do not need this callcommand thing and also the Cloner and everything works fine.
                    I am to stupid for this

                    ferdinandF 1 Reply Last reply Reply Quote 0
                    • ferdinandF
                      ferdinand @ThomasB
                      last edited by

                      Hey @thomasb,

                      I did not had time to deal with your case yesterday, I will have a look today.

                      Cheers,
                      Ferdinand

                      MAXON SDK Specialist
                      developers.maxon.net

                      1 Reply Last reply Reply Quote 0
                      • ferdinandF
                        ferdinand
                        last edited by ferdinand

                        Hello @thomasb,

                        I would ask you to try to regulate a bit the amount of posting you create. It is not about volume of questions and ideas; you are welcome to ask many and detailed questions. But it can be a bit tricky to answer your questions when one must look back at a history of x postings where one does not know what is solved and what not. It is therefore best when you edit your last posting, removing things which have become irrelevant, and update things which have become/remain relevant - instead of creating multiple postings.

                        With that being said, there are some things which stand out to me in your code (the line numbers I use are related to the file 14432.py of your code.

                        • Since you did not provide executable code, the resources are missing, I could only read but not run your code.
                        • Something like BowSpline (L:25ff) should not be a class but a function. It causes not only memory overhead because a BowSpline is effectively just a SplineObject instance, but also can cause problems when parts of a Windows2023 cache are still referenced by BowSpline instances.
                        • My linter runs crazy with unused elements in your code , e.g., L:87-93. You might want to have a look at the concept of code linting for Python. Basically plugins for editors which tell you when you can/should remove or add something (or violate more complex rules).
                        • Just as with BowSpline you have the tendency to store nodes for long time usage (which then also makes your code hard to maintain and read), e.g., Windows2023.extrude (L:94), but also comes with access problems just as BowSpline. You should view the albeit confusingly named ObjectData more as an interface wich acts on scene data it gets passed in its various methods, rather than a CommandData plugin which is basically a blank slate. You should not attach BaseList2D instances to your plugin hook instance.
                        • I think the whole thing is related to the city/house builder thing you talked with @Manuel about. Your plugin could use a bit of abstraction/encapsulation, because at least I struggle a bit with keeping up what each method does in Windows2023.
                        • You should not invoke scene redraws on MSG_DESCRIPTION_POSTSETPARAMETER (L:313ff). This is simply not how an ObjectData plugin works.
                        • What you do in the next condition, (L:318ff), extruding self.extrude when the user presses a button, must be bound to the main thread since you modify the document there. It is also not what usually an ObjectData plugin usually does: Heavily modify scene data.
                        • You use both ObjectData.SetOptimizeCache (L:86) and manual cache handling (L:368) doing the same which does not make too much sense, but you seem to have already stumbled upon this yourself.
                        • Reading whole objects in your load functions still seems unnecessary. I see now that you want to store point data, I would then just store the points. You talk about HyperFile reading about the class crashing here on PC. T which thread are your refering?
                        • You read data from disk in Windows2023.GetVirtualObjects which should be avoided for performance and safety reasons. But you seem to also have fixed that already.

                        Long story short, your code seems to have grown a bit over your head. Which happens to the best of us, but it also limits the amount of support I can provide here, since even when I could run your code, I would probably struggle understanding what it does in all detail.

                        What I did instead, is I have implemented what I would consider a clean implementation of what you want to do - serialize and deserialize data used by an ObjectData hook to disk shared by all plugin instances (and documents), specifically at the example of splines. I simplified things a lot and I also used JSON instead of HyperFile, simply because I prefer using human-readble formats when possible. But using HyperFile would be fine too.

                        Cheers,
                        Ferdinand

                        Result:


                        File: py-ostoredprofiles_2023.zip
                        Code:

                        """Provides an example for manually serializing data of a plugin that should be shared between
                        plugin instances and documents.
                        
                        Entities:
                            GetRailSpline:          Returns a rail spline for the given parameters.
                            GetProfileSpline:       Returns a profile spline for the given parameters.
                            SplinePresetCollection: Abstracts reading and writing spline presets from and to disk.
                            StoredProfilesData:     Realizes an object plugin hook that relies for its output on data that 
                                                    has been serialized to disk.
                        """
                        import c4d
                        
                        import os
                        import json
                        import math
                        import typing
                        
                        # --- The generator functions for the core output of the plugin, a rail and profile spline. 
                        
                        def GetRailSpline(pointCount: int, length: float) -> c4d.SplineObject:
                            """Returns a rail spline for the given parameters.
                            """
                            spline: c4d.SplineObject = c4d.SplineObject(pointCount, c4d.SPLINEOBJECT_TYPE_LINEAR)
                            if not spline:
                                raise MemoryError("Could not allocate rail spline.")
                        
                            # Build the points between 0 and #length with #pointCount steps.
                            points: list[c4d.Vector] = [
                                c4d.Vector(c4d.utils.RangeMap(i, 0, pointCount - 1, 0, length, True), 0, 0) 
                                for i in range(pointCount)]
                            
                            spline.SetAllPoints(points)
                            spline.SetName("Rail")
                            spline.Message(c4d.MSG_UPDATE)
                        
                            return spline
                        
                        
                        def GetProfileSpline(pointCount: int, diameter: float) -> c4d.SplineObject:
                            """Returns a profile spline for the given parameters.
                            """
                            spline: c4d.SplineObject = c4d.SplineObject(pointCount, c4d.SPLINEOBJECT_TYPE_LINEAR)
                            if not spline:
                                raise MemoryError("Could not allocate profile spline.")
                        
                            # I saw some "manual" rotations in your code using the sine and cosine. It is a bit a question
                            # of taste, but I would recommend using transforms instead. We rotate here with each iteration
                            # in the list comprehension the vector #p with a length of #diameter around the z-axis,
                            # resulting in a circle (when the splined is closed)
                            p: c4d.Vector = c4d.Vector(0, diameter, 0)
                            points: list[c4d.Vector] = [
                                c4d.utils.MatrixRotZ(2 * math.pi * (float(i)/float(pointCount))) * p
                                for i in range(pointCount)]
                        
                            spline.SetAllPoints(points)
                            spline[c4d.SPLINEOBJECT_CLOSED] = True
                            spline.SetName("Profile")
                            spline.Message(c4d.MSG_UPDATE)
                        
                            return spline
                        
                        # --- The interface which wraps the whole preset logic. 
                        
                        class SplinePresetCollection:
                            """Abstracts reading and writing spline presets from and to disk.
                            
                            The interface returns and accepts SplineObject instances via __getitem__ and __setitem__, hiding
                            the internal data away for an internal user.
                        
                                handler = SplinePresetCollection(somePath, autoSave=True)
                                # This will cause #spline to be saved to disk.
                                handler["MyPreset"] = spline
                                # Load the data back, handler does not store SplineObject instances, but their point data.
                                otherSpline: c4d.SplineObject = handler["MyPreset"]
                        
                            There many ways how one can realize (de-)serialization of data. HyperFile has the advantage that
                            it can natively deal with many Cinema 4D data types. Human readable formats as JSON or XML have
                            the advantage that they are modifiable by users without specialized tools.
                        
                            I went here with JSON but one could also easily use HyperFile, XML, etc. It is mostly a question
                            of taste. I am also serializing point data instead of parameters. Simply because it is the 
                            (slightly) more complex thing to do and therefore covers a broader audience.
                            """
                            # Keys for an item in the internal data structure and the JSON IO.
                            KEY_LABEL: str = "label"
                            KEY_POINTS: str = "points"
                        
                            def __init__(self, path: str, autoSave: bool = True) -> None:
                                """Initializes a collection with a preset file at #path to read and write data to.
                        
                                When #autosave is #True, the interface will automatically serialize itself when its has 
                                state changed. Auto-loading is not required, because all plugin instances share the same
                                SplinePresetCollection handler below (but would also be possible if one wants to change
                                the class bound SplinePresetCollection instance below).
                                """
                                if not isinstance(path, str):
                                    raise TypeError(f"{path = }")
                                if not isinstance(autoSave, bool):
                                    raise TypeError(f"{autoSave = }")
                                if not os.path.exists(path):
                                    raise IOError(f"Profile preset path '{path}' does not exist.")
                                if not os.path.isfile(path) or not path.lower().endswith("json"):
                                    raise IOError(f"Profile preset path '{path}' is not a JSON file.")
                        
                                self._path: str = path
                                self._autoSave: bool = autoSave
                        
                                # The internal data of the collection. We store splines over their label and points in
                                # local spline space, e.g.,
                                #
                                #   [
                                #       {"label": "Rectangle", "points": [[-1, 1, 0], [1, 1, 0], [1, -1, 0], [-1, -1, 0]]},
                                #       {"label": "Triangle", "points": [[-100, 200, 0], [100, 100, 0], [100, -100, 0]]}
                                #   ]
                                #
                                # We only handle here linearly interpolated splines with no tangents. We could of course
                                # also store other parameters than the label of the spline, and then generate the spline
                                # over these parameters.
                                self._data: list[dict[str, typing.Union[str, list[list[float, int]]]]] = self.Read()
                            
                            def __len__(self) -> int:
                                """Returns the number of presets in the handler instance.
                                """
                                return len(self._data)
                        
                            def __getitem__(self, key: typing.Union[int, str]) -> c4d.SplineObject:
                                """Returns a spline object for the preset with the index or label #key.
                                """
                                if not isinstance(key, (int, str)):
                                    raise TypeError(f"Invalid key type: {key}.")
                        
                                # Get the data.
                                pointData: list[list[float, int]] = None
                                if isinstance(key, int):
                                    if key > (len(self._data) - 1):
                                        raise IndexError(f"The index {key} is out of bounds for {self}.")
                                    pointData = self._data[key][SplinePresetCollection.KEY_POINTS]
                                else:
                                    for item in self._data:
                                        if item[SplinePresetCollection.KEY_LABEL] == key:
                                            pointData = item[SplinePresetCollection.KEY_POINTS]
                                if pointData is None:
                                    raise KeyError(f"{key} is a key not contained in {self}.")
                        
                                points: list[c4d.Vector] = [c4d.Vector(*p) for p in pointData]
                                spline: c4d.SplineObject = c4d.SplineObject(len(points), c4d.SPLINEOBJECT_TYPE_LINEAR)
                                if not spline:
                                    raise MemoryError("Failed to allocate spline for preset data.")
                        
                                spline[c4d.SPLINEOBJECT_CLOSED] = True
                                spline.SetAllPoints(points)
                                spline.Message(c4d.MSG_UPDATE)
                        
                                return spline
                        
                            def __iter__(self) -> tuple[int, str]:
                                """Yields the indices and labels of the splines in the collection.
                        
                                Point/spline data is not being yielded, use __getitem__ with either the yielded index
                                or label for that.
                                """
                                for i, item in enumerate(self._data):
                                    yield i, item[SplinePresetCollection.KEY_LABEL]
                            
                            def AddItem(self, spline: c4d.SplineObject, label: str) -> None:
                                """Adds #spline to the data under #label.
                                """
                                if not isinstance(spline, c4d.SplineObject):
                                    raise TypeError(f"Invalid item type: {spline = }.")
                        
                                self._data.append({
                                    SplinePresetCollection.KEY_LABEL: label,
                                    SplinePresetCollection.KEY_POINTS: [[p.x, p.y, p.z] for p in spline.GetAllPoints()]
                                })
                        
                                if self._autoSave:
                                    self.Write()
                        
                            def Read(self) -> list[dict[str, typing.Union[str, list[list[float, int]]]]]:
                                """Reads the preset data from the path associated with this collection.
                                """
                                # Try to parse the data.
                                try:
                                    with open(self._path, "r") as f:
                                        data: dict = json.load(f)
                                except Exception as e:
                                    raise IOError(f"Loading profile data failed with the error: {e}")
                        
                                # Validate the schema of the data. One could also use JSON schema for that, but I kept it
                                # simple here. For more complex cases manual validation is usually not a good choice.
                                # HyperFile data would also have to be validated for being in a well-formed state, but
                                # there it would be only bugs in the plugin which could cause malformed data, and not also
                                # a user errors in manually editing the file.
                                if not isinstance(data, list):
                                    raise IOError(f"Invalid profile data type: {type(data) = }")
                        
                                for item in data:
                                    if not isinstance(item, dict) or len(item) != 2:
                                        raise TypeError(f"Invalid item type: {item = }")
                        
                                    if not "label" in item:
                                        raise KeyError(f"'label' key is missing in : {item = }")
                                    if not "points" in item:
                                        raise KeyError(f"'points' key is missing in : {item = }")
                        
                                    if not isinstance(item["label"], str):
                                        raise TypeError(f"Invalid label: {item['label'] = }")
                                    if not isinstance(item["points"], list):
                                        raise TypeError(f"Invalid points: {item['label'] = }")
                        
                                    for point in item["points"]:
                                        if not isinstance(point, list):
                                            raise TypeError(f"Invalid point data: {point = }")
                                        for component in point:
                                            if not isinstance(component, (float, int)):
                                                raise TypeError(f"Invalid point data: {point = }")
                                return data
                        
                            def Write(self) -> None:
                                """Writes the current state of the collection to disk.
                                """
                                if not c4d.threading.GeIsMainThreadAndNoDrawThread():
                                    raise RuntimeError(f"Could not save data from thread: {c4d.threading.GeGetCurrentThread()}")
                                try:
                                    with open(self._path, "w") as f:
                                        json.dump(self._data, f)
                                except Exception as e:
                                    raise IOError(f"Failed to serialize spline preset data: {e}")
                        
                        
                        class StoredProfilesData(c4d.plugins.ObjectData):
                            """Realizes an object plugin hook that relies for its output on data that has been serialized
                            to disk.
                        
                            The plugin hook returns an Osweep object as its cache. The rail spline is being built 
                            dynamically on each cache build-event. The profiles are wrapped by a "preset" logic which
                            writes the profile data as JSON to disk.
                            """
                            # The plugin ID of StoredProfilesData and the ID for the "Profiles" drop-down parameter,
                            # we will need it in #GetDDescription below. 
                            ID_PLUGIN: int = 1060702
                            DID_PROFILE_PRESETS: c4d.DescID = c4d.DescID(
                                c4d.DescLevel(c4d.ID_PROFILE_PRESETS, c4d.DTYPE_LONG, 0))
                        
                            # A class bound spline preset handler instance shared by all plugin hook instances. By binding
                            # it to the class instead of to each StoredProfilesData hook instance, we ensure that all plugin
                            # instances operate on the same data without having to read data back from disk. PRESET_HANDLER
                            # acts effectively as a singleton. 
                            PRESET_HANDLER: SplinePresetCollection = SplinePresetCollection(
                                os.path.join(os.path.dirname(__file__), "profiles.json"))
                        
                            def __init__(self):
                                """Initializes the plugin hook.
                                """
                                # We do not need any special cache handling, so we can SetOptimizeCache().
                                self.SetOptimizeCache(True)
                                super().__init__()
                        
                            def Init(self, node: c4d.GeListNode) -> bool:
                                """Called by Cinema 4D to initialize the plugin object #node.
                                """
                                self.InitAttr(node, int, c4d.ID_PROFILE_PRESETS)
                                self.InitAttr(node, float, c4d.ID_PROFILE_DIAMETER)
                                self.InitAttr(node, int, c4d.ID_PROFILE_SUBDIVISIONS)
                                self.InitAttr(node, float, c4d.ID_RAIL_LENGTH)
                                self.InitAttr(node, int, c4d.ID_RAIL_SUBDIVISIONS)
                        
                                node[c4d.ID_PROFILE_PRESETS] = 0
                                node[c4d.ID_PROFILE_DIAMETER] = 50.0
                                node[c4d.ID_PROFILE_SUBDIVISIONS] = 12
                                node[c4d.ID_RAIL_LENGTH] = 500.0
                                node[c4d.ID_RAIL_SUBDIVISIONS] = 24
                        
                                return True
                        
                            def GetVirtualObjects(self, op: c4d.BaseObject, hh: object) -> typing.Optional[c4d.BaseObject]:
                                """Called by Cinema 4D to build the cache for the plugin node #op.
                                """
                                # When you invoke SetOptimizeCache(True) in ObjectData.__Init__(), also applying the default
                                # manual cache handling as shown below and done in your code makes only little sense. 
                                # Because that is then already being done by Cinema 4D for you. When one is determining the
                                # dirty state manually, one usually also wants to deviate from the default behaviour by for 
                                # example updating the cache when an object linked in the parameters of #op is dirty, 
                                # when the current time of the document has changed, or something similarly custom.
                                #
                                # # Determine if the object is dirty, and if not, simply return its existing cache. Doing
                                # # this is equivalent to invoking self.SetOptimizeCache(True) in __init__().
                                # dirty = op.CheckCache(hierarchyhelp) or op.IsDirty(c4d.DIRTYFLAGS_DATA)
                                # if dirty is False: return op.GetCache(hierarchyhelp)
                        
                                # Get relevant parameters from the node in the scene.
                                railLength: float = op[c4d.ID_RAIL_LENGTH]
                                railSubdivisions: int = op[c4d.ID_RAIL_SUBDIVISIONS]
                                presetIndex: int = op[c4d.ID_PROFILE_PRESETS]
                        
                                # Allocate the cache root, in this case a sweep object we are going to attach our customly
                                # generated rail and profile splines to.
                                root: c4d.BaseObject = c4d.BaseObject(c4d.Osweep)
                                if not root:
                                    raise MemoryError("Could not allocate object in cache.")
                        
                                # Get profile spline from the preset handler and build the rail manually.
                                profileSpline: c4d.SplineObject = StoredProfilesData.PRESET_HANDLER[presetIndex]
                                railSpline: c4d.SplineObject = GetRailSpline(railSubdivisions, railLength)
                                profileSpline.InsertUnderLast(root)
                                railSpline.InsertUnderLast(root)
                        
                                # Return the new cache hierarchy.
                                return root
                        
                            def GetDDescription(self, node: c4d.GeListNode, description: c4d.Description,
                                                flags: int) -> typing.Union[bool, tuple[bool, int]]:
                                """Called by Cinema 4D to let a plugin modify its description GUI.
                        
                                We use this here to populate our "Profiles" parameter drop-down menu with the data wrapped
                                by #StoredProfilesData.PRESET_HANDLER.
                                """
                                # Make sure that the description of this plugin type has been loaded.
                                if not description.LoadDescription(node.GetType()):
                                    return False
                        
                                # Only modify the #ID_PROFILE_PRESETS cycle when Cinema 4D asks us to do so, or when 
                                # Cinema 4D does not specify for which parameter this call is for.
                                targetId: c4d.DescID = description.GetSingleDescID()
                                if not targetId or StoredProfilesData.DID_PROFILE_PRESETS.IsPartOf(targetId):
                                    # Get the whole description container instance for the parameter #ID_PROFILE_PRESETS.
                                    bc: c4d.BaseContainer = description.GetParameterI(StoredProfilesData.DID_PROFILE_PRESETS)
                                    if not bc:
                                        return (True, flags)
                                    
                                    # Get the cycle values container instance in it and flush all items.
                                    items: c4d.BaseContainer = bc.GetContainerInstance(c4d.DESC_CYCLE)
                                    items.FlushAll()
                        
                                    # Build the values from scratch with the items in #StoredProfilesData.PRESET_HANDLER.
                                    for index, label in StoredProfilesData.PRESET_HANDLER:
                                        items.SetString(index, label)
                        
                                return (True, flags | c4d.DESCFLAGS_DESC_LOADED)
                            
                            def Message(self, node: c4d.GeListNode, mid: int, data: object) -> bool:
                                """Called by Cinema 4D to convey events for the plugin object #node.
                        
                                Used here to catch the user pressing the "Save Preset" button and saving said preset.
                                """
                                # Our #ID_PROFILE_SAVE button has been pressed, we save a new preset.
                                if (mid == c4d.MSG_DESCRIPTION_COMMAND and isinstance(data, dict) and
                                    isinstance(data.get("id", None), c4d.DescID)):
                                    if data["id"][0].id != c4d.ID_PROFILE_SAVE:
                                        return True
                                    
                                    # Bail when we are not on the non drawing main thread as we are going to open a dialog
                                    # to ask the user for a preset label.
                                    if not c4d.threading.GeIsMainThreadAndNoDrawThread():
                                        return True
                                    label: str = c4d.gui.InputDialog(
                                        "Preset Name:", f"Preset {len(StoredProfilesData.PRESET_HANDLER) + 1}")
                        
                                    # Build the profile spline with the current settings.
                                    diameter: float = node[c4d.ID_PROFILE_DIAMETER]
                                    subdivisions: int = node[c4d.ID_PROFILE_SUBDIVISIONS]
                                    spline: c4d.SplineObject = GetProfileSpline(subdivisions, diameter)
                        
                                    # Store the spline in the handler, because we set #autoSave to #True, this will also 
                                    # cause the data to be saved to disk. After that, we flag ourselves as description dirty,
                                    # as the drop-down menu of the "Preset" parameter must be updated.
                                    StoredProfilesData.PRESET_HANDLER.AddItem(spline, label)
                                    node.SetDirty(c4d.DIRTYFLAGS_DESCRIPTION)
                        
                                return True
                        
                            @staticmethod
                            def Register() -> bool:
                                """Registers the plugin hook.
                        
                                This method is not part of the ObjectData interface, I just like to do it like this, attach
                                the plugin ID and registration method to the plugin hook they belong to. Could also be
                                done differently.
                                """
                                bmp: c4d.bitmaps.BaseBitmap = c4d.bitmaps.InitResourceBitmap(c4d.Osweep)
                                if not bmp:
                                    return False
                        
                                return c4d.plugins.RegisterObjectPlugin(id=StoredProfilesData.ID_PLUGIN,
                                                                        str="Stored Profiles Object",
                                                                        g=StoredProfilesData,
                                                                        description="ostoredprofiles",
                                                                        icon=bmp, info=c4d.OBJECT_GENERATOR)
                        
                        
                        if __name__ == "__main__":
                            if not StoredProfilesData.Register():
                                print(f"Warning: Failed to register 'StoredProfilesData' in '{__file__}'.")
                        

                        MAXON SDK Specialist
                        developers.maxon.net

                        ThomasBT 1 Reply Last reply Reply Quote 0
                        • ThomasBT
                          ThomasB @ferdinand
                          last edited by ThomasB

                          @ferdinand
                          sorry ferdinand for this amount of posts. I did not know that I may delete unnecessary posts.
                          I am writing code with PyCharm, when he complains , I improve the code.
                          As I said, the code is a bit messy.....Many unnecessary things that I would have improved in the end.
                          In the future, I will curb my euphoria a bit as far as the posts are concerned.
                          No, the plugin does not refer to the City Builder. This is different.
                          I'll try to put everything in methods and not in an extra class and try to implement some of your paradigms.

                          Thank you, I've read a lot from you now, takes a while.
                          Thank you for your effort. I'm really ashamed...
                          Cheers Thomas

                          ferdinandF 1 Reply Last reply Reply Quote 0
                          • ferdinandF
                            ferdinand @ThomasB
                            last edited by

                            Hey @thomasb,

                            no worries, don't be too concerned about it. And you should not be ashamed, that was certainly not what I wanted to convey. "Every person his or her book", the second so called law of library science, is something I truly believe in. Everyone has valid information needs and we are trying to help users on their individual journey. You should not feel discouraged, we are and were all once in your place.

                            But at the same time, I sometimes have to regulate a bit forum conduct as we, the Maxon SDK group, and other users are human too. And people tend to get confused, when they are being hit with a stream-of-conscious like threads were a user comments diary-style-like on his or her development state. It is not that I would not understand people are doing that. When one is in that situation, one is overwhelmed by the possible routes one can take, the amount of information to traverse. And verbalizing that does indeed help. But for someone who tries to help you or for people who later search for similar information, this "stream-of-conscious/diary" is then an obstacle rather than a help.

                            So, sometimes I try to regulate both interests a bit. But as I said, please do not feel discouraged.

                            Cheers,
                            Ferdinand

                            MAXON SDK Specialist
                            developers.maxon.net

                            1 Reply Last reply Reply Quote 1
                            • First post
                              Last post