Navigation

    • Register
    • Login
        No matches found
    • Search
    1. Home
    2. mikegold10
    3. Posts
    • Profile
    • More
      • Following
      • Followers
      • Topics
      • Posts
      • Best
      • Groups

    Posts made by mikegold10

    RE: A bug that should be fixed in your Python API code generation (Applies to all recent versions)

    Hi, Maxime, great!

    if I come across any additional issues of a similar nature, I'll update this post.

    posted in Cinema 4D SDK •
    A bug that should be fixed in your Python API code generation (Applies to all recent versions)

    The argument self should not appear in any of the following static method declarations that are part of the generated Python C4D API skeletal/placeholder code, since any good Python IDE will complain and/or flag correct user code that use these methods.

    In the future, you guys can do something like the following in ...resource\modules\python\libs:

    # You can also add the -n flag to the first grep in the pipe chain below, to add line number info after the file name
    grep -R -A2 -H "@staticmethod" * 2>&1 | grep -v "MAXON_" | grep -PB2 "def.*?self"
    

    I am not sure if the above grep comprehensively captures every one of these issues, but 99-100% of them should have been caught:

    python39/c4d/bitmaps/__init__.py:    @staticmethod
    python39/c4d/bitmaps/__init__.py-    def AllocWrapper(self, bmp):
    --
    python39/c4d/modules/mograph/__init__.py:    @staticmethod
    python39/c4d/modules/mograph/__init__.py-    def Create(self, flags, thread, doc, currentThreadIndex, threadCount, inputs, callers):
    --
    python39/c4d/utils/__init__.py:    @staticmethod
    python39/c4d/utils/__init__.py-    def PickObject(self, bd, doc, x, y, rad, flags):
    --
    python39/c4d/__init__.py:    @staticmethod
    python39/c4d/__init__.py-    def GetDistance(self, v1, v2):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def Auto(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def ReferenceConvert(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def Wrap(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def NativeDataOrBuiltin(self, *args):
    --
    python39/maxon/decorators.py:            @staticmethod
    python39/maxon/decorators.py-            @wraps(fn)
    python39/maxon/decorators.py-            def _MaxonConvertOrFail(self, *args):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def Free(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def GetLocal(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def AllocEmpty(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def AllocFromUuid(self, uuid):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def AllocFromString(self, uuidStr):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def DescribeIO(self):
    --
    python39/maxon/interface.py:    @staticmethod
    python39/maxon/interface.py-    def CreateUuidString(self):
    --
    python39.win64.framework/lib/pathlib.py:        @staticmethod
    python39.win64.framework/lib/pathlib.py-        def link_to(self, target):
    

    Also, for all of the following, the @classmethod attribute should be changed to @staticmethod and the self argument should be removed, just like for the above cases:

    python39.win64.framework/lib/importlib/_bootstrap.py-    @classmethod
    python39.win64.framework/lib/importlib/_bootstrap.py:    def create_module(self, spec):
    --
    python39.win64.framework/lib/importlib/_bootstrap.py-    @classmethod
    python39.win64.framework/lib/importlib/_bootstrap.py:    def exec_module(self, module):
    --
    python39.win64.framework/lib/_collections_abc.py-    @classmethod
    python39.win64.framework/lib/_collections_abc.py:    def _from_iterable(self, it):
    --
    python39.win64.framework/lib/_collections_abc.py-    @classmethod
    python39.win64.framework/lib/_collections_abc.py:    def _from_iterable(self, it):
    
    posted in Cinema 4D SDK •
    Keyboard button down issue in S24

    It seems like S24 is too sensitive when it comes to deciding whether a user intended to hold a key down for a period of time so as to temporarily toggle a state and revert back once the key is released or if the intent was to permanently change to that state.

    For example, the lowercase L key can be used to:

    • Toggle the Enable Axis state on/off when the user momentarily presses the L key
    • Temporarily enter (or exit) the Enable Axis state when the user holds down the L key for a "longer" period of time and then revert back to the original state when the user releases the key

    Unfortunately, the actual length of time used for the decision by C4D as to the user's intent seems to be too short. I don't know where exactly the threshold lies, but it seems to be in the 100 ms ballpark. There should be a way to change the number of milliseconds that a key must be held before C4D assumes that the user's intent was to toggle (and not permanently enter a state/mode). I have not been able to find this setting if it exists it in the preferences. Or, is this value "hard coded" into the app and cannot be modified by the user?

    This issue effects both mice and keyboards that allow for macros, since sometimes they can send the key down even followed by a key up event with some delay in between that cannot be modified via configuration and happens to be long enough to confuse C4D into thinking that the user's intent was to temporarily toggle. I should add that such interpretation can occur intermittently due to key down/key up delay jitter making things even more confusing.

    On the other hand, even using a decent wired keyboard, if I press a key reasonably briefly, but not really fast, I sometimes get a temporary toggle instead of a permanent mode switch. This is true for those commands/modes that are toggleable. One almost has to put in a mental effort when pressing a key on the keyboard so as not to wind up holding it down for what C4D considers too long a time (where "too long a time" could be as little as somewhere in the 100-150 ms ballpark...). Often when I press a key, without concentrating on pressing it really quickly, C4D thinks my intent was to hold the key down so as to temporarily toggle, and reverts the setting back once I release the key.

    There should be some way to control this delay and increase it in the software to make C4D wait longer before assuming that the user intended to temporarily toggle a command (say 250-500 ms would be a reasonable delay as compared to the unnecessarily brief delay it is presently set to by default (50-100 ms??).

    I should note that this issue was present in R23 as well, but the delay was longer and could be lived with (although some users of Bluetooth keyboards and other forms of wireless keyboards as well as macro capable mice experienced the issue). As of S24, the delay has gotten so short, that it even affects users with common run-of-the-mill wired keyboards like myself, unless they can press/release keyboard keys really briefly (as an analogy for piano players out there, think of playing staccato notes vs tenuto notes).

    Test setup:
    OS: Windows 10
    Hardware/PC: Very fast modern desktop
    Keyboards attempted: Velocifire TKL02/wired, an old Dell wired keyboard, an old Logitech wired keyboard

    posted in Maxon Announcements •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    Hi Maxime,

    Thanks for confirming that I was not missing something in the docs and this is just not possible.

    posted in Cineware SDK •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    @zipit I am hopeful that @r_gigante will take a look at my reply to his post and address the question in Step 2 of said post:

    How do you "Hand color information to Cinema 4D inside Draw() (or where else??), so that it can use it to correctly draw the object in a Viewport" ?

    posted in Cineware SDK •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    @zipit I tried this route and it doesn't work, at least not in the way I expected, because for this to work, it is critical that the ordering between Draw() and Output() functions across all object nodes and their respective tags in the node hierarchy, has to be in a certain well-defined and compatible (with this idea) way. Otherwise, the color you cache in Draw() based on the TextureTag received is not necessarily the correct color when Output() gets called (and if Output() gets called, because it almost never called unless you render, but Draw() gets called a lot).

    The reason things don't work as expected using this method of caching color info in Draw(), to be used later in Output (if object info is not available), is because either the object changed or the "current" texture tag is different by the time Output() is called, but often Draw() has not yet been called to update the cached color info. Also, aside from rendering, as I mentioned previously, calls to output are far and few in between. For example, if you use the mouse to move or rotate objects in a view, only Draw() gets called (for each texture tag/object), with no subsequent call to Output() as far as I can tell.

    posted in Cineware SDK •
    RE: S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    @r_gigante

    I understand what you are saying in your bullet point 1:

    • during a rendering process the public VolumeData member of the ChannelData (not of the BaseShader as shown in your code) is valid, you can retrieve the linked BaseTag and, from that, you can access you userdata attached to the TextureTag to find the color of interest

    I made a typo when I used pShaderData several times, both in the code which I wrote from memory and the original post. Obviously, there is no *VolumeData vd member variable in the BaseShader class, so where I originally typed pShaderData, I was referring to pChannelData - my apologies.

    I have edited the original post and corrected this. I just want to add that I have no issue during the rendering process, since I do exactly what you described in your first bullet point (using pChannelData of course). It's "outside a rendering process" where I run into problems and so let's go to your bullet point 2:

    • outside a rendering process, you can use the ShaderData::Draw() to find the BaseTag and, from that, you can access you userdata attached to the TextureTag to find the color of interest.

    The issue is not one of finding the color of interest in Draw(), this part is easy! The issue is giving per object tag based color information back to Cinema 4D at "the right time" for this information to be used to draw in Viewports that show the object (and by the way where does this happen??).

    During renders, within the Output() function, it is clear that you return the color vector from your shader and this is how you tell Cinema 4D what color your shader returns.

    But, during Viewport drawing, it is not clear how to give this color information from a custom shader (after getting color information based on the BaseTag parameter to Draw()) back to Cinema 4D, so that it uses it as part of the Material of an object.

    Unlike the Output() function, Draw() returns a boolean value, not a ColorVector, so how do I give a ColorVector back to Cinema 4D so that it knows how to draw an object, one that has a TextureTag using a material with my shader, correctly in a Viewport?

    That's my dilemma, having solved the render question.

    Instead of seeing this in the viewport (with red indicating that my shader could not get object info for purposes of viewport display and the default error (exception) color is being used, because I do not know how to supply the per object color back to Cinema 4D [presumably in the call to Draw()]):

    Incorrect Viewport Display
    Incorrect Viewport Display

    I want to see this - objects get correctly colored, after you explain to me how to perform Step #2, below:

    Inside the call to Draw():

    Step 1) Fetch the color info from the object's texture tag's user data (how to do this is obvious)
    Step 2) Hand this information to Cinema 4D inside Draw() (or where else??), so that it can use it to correctly draw the object in a Viewport

    Correct Viewport Display
    Correct Viewport Display

    Thanks for your help and advice,

    Michael

    posted in Cineware SDK •
    S22: Question about going from a custom shader's ShaderData::Output() function back to the texture tag of an object

    I have a question about the correct path from the ShaderData::Output() function which gets overriden by a custom shader to the texture tag that links to the material which hosts the shader in one of its slots. Before stating the problem, let me explain the reason why I would like to do this:

    I am trying to reuse the same material across multiple objects. This material hosts a custom shader called Object Controlled Shader in its Color channel's Texture slot, as indicated by the orange arrow in the image below:

    Orange Arrow points to Custom Shader assigned to a material's Color channel's Texture slot

    This custom shader is meant to enable me to drive the color from a custom user data Color property that is associated with a texture tag attached to an object. I can use this user data Color property to set a per object texture tag color for the material and be able to modify it based on various criteria, without having to physically change the material, since it is also shared by other objects. I can do this while reusing all other aspects of the material. So, in effect, this would enable me to parameterize various aspect of a material via custom shaders on a per object per texture tag basis. This is somewhat similar to what the Mograph Color Shader currently does, except that the Color Shader allows for only one point of customization control, since it is hard wired to an Object's Basic Color (ID_BASEOBJECT_COLOR) property.

    It should be noted that the built in Mograph Color Shader works correctly to set the color both in Cinema 4d's Viewports and at render time, which is important as I will explain, below.

    We start with the following overriden function in my custom shader:

    Vector MyShader::Output(BaseShader* pShader, ChannelData* pChanelData)
    {
      ...
    }
    

    Now let's look at what we have available in this function:

    • this - A pointer to an instance of my custom shader class derived from ShaderData
    • pShader - A pointer to the BaseShader that hosts the ShaderData derived object
    • pChannelData - A pointer to an object of type ChannelData

    After much trial and error, I have been able to figure out that the following code (potentially) gets me the texture tag user data based color property I am trying to extract and use that to return a per object texture tag specific color from the Output() function:

    Vector MyShader::Output(BaseShader* pShader, ChannelData* pChanelData)
    {
        int MY_COLOR_ID=1; // Id inside a texture BaseTag's user data
        Vector COLOR_VECTOR_NOT_PRESENT=Vector(1.0, 0.0, 0.0); // Bright red color - to indicate errors
    
        Vector colorVector=COLOR_VECTOR_NOT_PRESENT;
    
        // If the ChannelData has a valid pointer to a VolumeData object (vd), and this object points to a valid
        //   TexData object (tex), which itself points to a valid BaseTag object (link)
        if (pChannelData && pChannelData->vd && pChannelData->vd->tex && pChannelData->vd->tex->link)
        {
            // Then, I can get the texture tag linked to the material hosting my custom shader
            BaseTag *pTag=pChannelData->vd->tex->link;
    
            // ..., and that also means that I can get any UserData associated with this texture tag (if present)
            const BaseContainer *pUserData=pTag->GetDataInstance()->GetContainerInstance(ID_USERDATA);
    
            // If user data is present
            if (pUserData)
            {
                // ..., and it contains a Vector data item at MY_COLOR_ID with a color (i.e., Vector) property type
                colorVector=pUserData->GetVector(MY_COLOR_ID, COLOR_VECTOR_NOT_PRESENT);    
            }
        }
    
        // Return the color vector from the user data to customize the color of the shader on a per texture tag
        // basis (or COLOR_VECTOR_NOT_PRESENT, bright red, if the texture tag does not have this user data item)
        return colorVector;
    }
    

    This seems to work, but it only works during renders. I cannot, for the life of me, figure out how to get the texture tag of an object that is assigned a material using my custom shader for purposes of display in Cinema 4d's Viewports, regardless of the Viewport's Display Shading setting. I have tried, seemingly, every possible path through the pointers available in the objects passed by pointer to the Output() function.

    I have read many reports online that code like this: pChannelData->vd->op->link->GetCacheParent() , assuming of course that all pointers along the way are valid, gets the object that hosts the texture tag linked to the material hosting the custom shader, but this does not work in S22 - the latest C4D release. I only get some internal "virtual" or perhaps "cached" object named "Object" back and every object path through the hierarchy that I have tried using that link pointer seems to get me no closer - it's always BaseObject objects named "Object" which are clearly not my object but some sort of cached copies. I am trying to get my actual object (e.g., an object named "Cube" in the Object Manager's node hierarchy), so that I can access its texture tag and get the per object per texture tag specific user data I am looking for in order to properly set the color that my shader returns from its Output() function when called for that object to display it in a Viewport.

    Is there any possible way to get from a shader to a texture tag (or a BaseObject that hosts that tag) during calls to Output() while an object is being drawn in a Viewport, so that in Output() I can use some user data from this texture tag to set the color vector that my custom shader returns, similar to how I do it in the code above which works for renders. Perhaps my understanding is flawed and the Output() function is not what Viewports use to texture objects, but then where is the correct place to do this in my ShaderData derived custom shader and what code do I need to do this?

    As shown in the shader usage example in this post, my custom shader would enable me to get each object's color in the Viewport to match the color as specified in the user data associated with its assigned Texture tag and make each object get colored differently, assuming my custom shader is used in a material's Color layer's Texture slot.

    Of course, the ultimate goal of this exercise is to be able to use the custom shader in any place of a material where a shader can be placed (i.e., any Texture property in any channel of the material) and be able to customize the material on a per object texture tag user data basis.

    Update with additional info:

    Over the weekend, it has become clear to me that my custom shader's override of the ShaderData::Draw() function, gets called when an object that is assigned a material with my shader in a Texture Tag, gets refreshed and/or drawn in a Cinema 4D Viewport. What is not clear at all is how do I communicate any object texure tag specific customizations from my shader during the call to Draw(), assuming that this is the right place to do that for purposes of Viewport display of an object's material.

    To be clear, unlike the Output() member function which returns a Color Vector containing the color calculated by my custom shader, based on the input parameters supplied to Output(), namely: BaseShader* sh, ChannelData* cd, the Draw() function only returns a Bool success or failure value.

    It is not clear to me, assuming of course that the Draw() function is the right place to make per object per texture tag changes/updates on behalf of an object and based on my shader's custom rules, how I go about communicating this information back to Cinema 4D, so that the object can be properly drawn in a Viewport.

    Perhaps this can be done by making changes to some member variables of the objects, pointers to which are passed to Draw(). That is: BaseShader * sh, BaseObject *op, BaseTag *tag, BaseDraw *bd, BaseDrawHelp *bh .

    If this is the right way to do it. It is not clear to me what changes to make to what members of those objects, or what member functions to call using the pointers to the various objects passed to Draw(), in order to send a Color vector back to Cinema 4D so that a particular object in the scene can be drawn in a custom way using a material that holds my shader in the Texture property of its Color layer.

    posted in Cineware SDK •
    RE: From Line Object to Spline

    Also, wanted to share the result of following the ideas in this thread on a parametric Cissoid object being deformed by a Twist modifier.

    The green stars and blue circles indicate the deformed spline points/knot positions, respectively, and the magenta stars and orange circles indicate the pre-deformation spline points/knot positions, respectively. (Types of) Intermediate Points were set to Natural and Number was set to 4, resulting in about four times as many knots (circles) as spline points (stars), as can be seen from the image. You can see some of the effects of the Natural type on the rightmost vertically oriented curve at the bottom of the deformed Cissoid coming to its lowest point in the image. As a result of being relatively straight, it has a more sparse arrangement of spline points (and knots) than the more curved vertical portion immediately to its left.

    The varying sizes of the circles/star point markers in the image are not due to perspective distortion, at least not in total, but rather due to them being scaled based on the distance of surrounding spline points and knots, respectively, so as to try to reduce collisions among the markers with respect to other markers of the same type (the null shape radii were multiplied by 40% of the smaller of the distances between a point and its surrounding points resulting in a tiny gap between markers of the same type, at least when viewed along the normal of any particular point):

    deformed-cissoid.png

    Michael

    posted in Cinema 4D SDK •
    RE: Should the call to c4d.EventAdd() come before or after the call to doc.EndUndo() ?

    @m_adam Hi Maxime, thanks for your detailed reply. I had a feeling that EventAdd() performed an asynchronous operation (i.e., placed an event on the event queue) and therefore it didn't matter, since control of the main thread would not be relinquished until both EndUndo() were called (regardless of sequence). However, as C4DS and you yourself pointed out, it makes more sense to call EndUndo() prior to EventAdd().

    This is not only true from a safety perspective, but the calls to StartUndo() and EndUndo() can be wrapped in a Python Context Manager, along exception handling and cleanup to be done automatically and correctly when a scope is exited. If one is going to undo whatever operation in case of an error, anyways (e.g., as part of said exception handling), it makes sense to call c4d.EventAdd() after we know that the entire action was successfully performed.

    I am going on the assumption that if my code performs some action inside of a StartUndo() / EndUndo() sequence, possibly consisting of multiple sub-actions and changes resulting in multiple AddUndo() calls, then sees mid-action that there is an and it cannot continue, and assuming the Start/End undo sequence is wrapped in a Python context manager class. Python will call EndUndo() on my behalf, as part of exiting the block governed by the context manager and then in the exception handler, I can perform an undo of the last action, undoing whatever sub-action did perform successfully (and added their AddUndo() pieces). Since this will hopefully leave everything in the same state that it was before the action was started, I am going to assume that there is no point in calling c4d.EventAdd() after the undo of the action is performed in my exception handler.

    To summarize, here is an example scenario:

    try:
        with MyUndoContextManager(doc) as undo_manager: # Calls StartUndo() as part of its Context Manager __enter__() method
            # undo_manager will make the calls to AddUndo() based on our actions
            DoSomethingAndCallAddUndoOnTheDoc(undo_manager);
            DoSomethingElseAndCallAddUndoOnTheDoc(undo_manager);
    
            DoOneLastThingAndCallAddUndoOnTheDoc(undo_manager); # Oh, oh, fails and throws exception
            # Note: EndUndo() will get called as part of the implicit call to MyUndoContextManager's __exit__() method
            #       which will automatically get called when we exit this block due to the exception being thrown
            c4d.EventAdd(); # This code will not get reached due to exception being thrown above
    except:
            # An error occurred, get back to the initial state prior to the action
            # Undo whichever action sub-steps were performed successfully and AddUndo()s got called for, if any
    
            # Since the undo_manager object no longer exists, call DoUndo() on the doc object, directly
            doc.DoUndo()
    
            # Since, we reverted to the initial state before the action, there is no need to call c4d.EventAdd(), right?
    posted in Cinema 4D SDK •
    From Line Object to Spline

    (edited by Manuel : I splited this thread from this one)

    @m_magalhaes said in Post Deformer Spline:

    SplineToLineIndex

    Hi Manuel,

    I am not the one who asked the original question, but your find of the SplineToLineIndex() method in SplineHelp is a good one!

    I must have look at SplineHelp's API over a dozen times and never noticed it was there. I thought one would have to traverse all of the LineObject's points and correlate them with points on its parent SplineObject based on position, in order to come up with the mapping from SplineObject point index to LineObject point index. So, this feature of SplineHelp is really valuable, since it offers this very mapping as part of the core Python API.

    I guess this also implies that there is always a 1:1 correspondence between a point of a SplineObject (which represents one or more B-Splines via formulas) and a point of its underlying LineObject which represents a bunch of line segments connected together. I wasn't sure if this was always guaranteed to be the case.

    Now, for a somewhat related question to what has already been discussed, while we are on the topic of SplineHelp, is there some top secret way to do the opposite of what the GetPointIndex() method of SplineHelp does, that is to get the offset (i.e., 0<=offset<=1.0) along a spline's LineObject that a particular LineObject` point idx is located at? This would also be really helpful.

    Let's look at an example:

    Say you create a Rectangle parametric spline object that has a width and a height of 100 cm, so basically a square. We'll set the Intermediate Points setting to None, so that the spline object and its line object have identical point counts (i.e., four, for the four points that make up the corners of the square). Now, let's say you perform a Make Editable operation to convert the (parametric) Rectangle into an editable closed spline object, consisting of one segment with four points. If you go into the Structure Manager, you can see that the points are indexed 0-3 (since, there are four of them) and you see their coordinates, as well.

    These points are located, with regard to offset, at 0%, 25%, 50%, and 75% of the length of the spline (or offsets of 0.000, 0.250, 0.500, and 0.750), respectively, since our original parametric spline shape was a square. Since this is a closed spline, GetPointIndex() will return the (sentinel or logical) idx of 4 (i.e., the count of the number of points in that segment of the LineObject) if asked for the nearest point to 100% of the length of the spline.

    Now, given for example the point at Index #1, so the second point of the spline's LineObject, which is obviously 25% of the distance along the spline as previously mentioned, what method could you use on the SplineObject, it's underlying LineObject, or 'SplineHelp' to get a 0.25 offset value from that LineObject point index of 1.

    The GetPointIndex() member function of SplineHelp takes a (Spline space) offset value and a segment idx, and returns the closest LineObject point, by index, to that spline space offset value.

    As described in the above example, how do you go in the opposite direction, from a LineObject point, by index, to an offset value along the LineObject in spline space?

    posted in Cinema 4D SDK •
    Should the call to c4d.EventAdd() come before or after the call to doc.EndUndo() ?

    I'd like to hear what the canonical way of doing the following sequence, is:

    # Start a chain of undo actions
    doc.StartUndo()
    
    # ... Whatever actions that need undo and their associated calls to doc.AddUndo
    
    # Call EventAdd() before ending the undo chain
    c4d.EventAdd()
    
    # ..., then end the undo chain
    doc.EndUndo()
    

    or:

    # Start a chain of undo actions  
    doc.StartUndo()   
    
    # ... Whatever actions that need undo and their associated calls to doc.AddUndo()
    
    # End the undo chain first
    doc.EndUndo()
    
    # ..., and then call c4d.EventAdd()
    c4d.EventAdd()
    

    In other words, should the call to c4d.EventAdd() come before or after the call to EndUndo() on the document object. I've seen code doing it both ways and am wondering if makes any difference or if there is some advantage to one of the two orderings of calls.

    posted in Cinema 4D SDK •
    There is a bug and some issues in the inner extrude tool res/.h files (S22 and R21)

    Let me be specific:

    Extrude Inner Tool
    In toolextrudeinner.res (resource\modules\model\description subdirectory of the main install location), in the MDATA_MAINGROUP group, the following line:
    REAL MDATA_EXTRUDEINNER_SUBDIVISION { MIN 1; MINSLIDER 1; MAXSLIDER 10; CUSTOMGUI REALSLIDER; STEP 1;}
    should read:
    REAL MDATA_EXTRUDEINNER_SUBDIVISION { MIN 0; MINSLIDER 0; MAXSLIDER 10; CUSTOMGUI REALSLIDER; STEP 1;}

    The correct MIN value for the Subdivision property of the Extrude Inner tool is 0 .

    Also, toolextrudeinner.h documents the property as follows:

    MDATA_EXTRUDEINNER_SUBDIVISION						= 2170, // LONG
    

    Yet, in the actual tool dialog box, it's type is specified as a REAL . One (or both) of these is/are clearly wrong, it doesn't make much sense for the subdivision value to be either a FLOAT or a LONG, it should be an INT!

    I should also add to this that if one was to start using the Extrude Inner tool and to drag the Subdivision property into the Python Console in C4D, they would see:

    >>> ExtrudeInner[c4d.MDATA_EXTRUDEINNER_SUBDIVISION]
    0

    ..., and if one was then to do the following to see what the actual underlying type is:

    >>> type(ExtrudeInner[c4d.MDATA_EXTRUDEINNER_SUBDIVISION])
    <type 'int'>

    They would see that the actual type is the very type (int) that I suggested should appear in both the .res and .h files, above!

    Note: The help documentation is also in error, regarding the Subdivision property of the Extrude Inner tool, listing its range as follows: Subdivision [1..+∞]
    It specifies this and then goes on to present a diagram showing the perfectly valid examples of using values of zero (which is out of the range just documented, but perfectly valid as far as the actual tool is concerned) and two, as the text under the figure indicates:

    003685.jpg

    Subdivision set to 0 and 2.

    See the following section from which the example above was taken: Extrude Inner/Tool

    The actual range in the help should be changed to: [0-+∞]

    Fortunately, when the tool itself is employed, the Subdivision value does correctly default to zero, in contradiction to the Dialog's definition and constraints in the .res file, so I am not sure what is happening behind the scenes in the C4D source code which seems to correct things (at least as far as the tool goes).

    However, the issue becomes apparent when tools try to use Extrude Inner functionality including its dialog groups (e.g., Py-Extrude Inner Modifier that is part of Py-Parametric Tools at Maxon Labs) and cannot change the Subdivision property to a value of zero, even though this is possible using the actual Inner Extrude tool that is part of C4D core functionality.

    Please take a look at this and consider the fixes/changes described above.

    posted in Cinema 4D SDK •
    RE: PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma

    @m_adam said in PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma:
    ...

    Hope it answers your questions,
    Cheers,
    Maxime

    Thank you Maxime, this is a lot of useful info. Let me think through all of this and reply here if I have any further issues.

    posted in Cinema 4D SDK •
    RE: Copying All Compatible Properties from One Object to Another?

    @m_magalhaes said in Copying All Compatible Properties from One Object to Another?:

    Hi,

    as you may know, if the object's type were the same you could use CopyTo
    But in your case, there's no builtin function do to it.

    I've checked how it's done for external compositing programme and there's nothing helpfull.

    Cheers,
    Manuel

    What about copying data from the BaseContainer of the source object to the destination object only if the ids in the source BaseContainer are relevant and/or are already present in the destination object's BaseContainer (i.e., copy only those key/value pairs that are applicable to the destination)?

    posted in Cinema 4D SDK •
    RE: PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma

    @zipit said in PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma:

    Hi,

    edges are globally indexed by polygon_index * 4 + local edge_index (see PolygonObject.GetEdgeS), where the local edge index lies in the interval [0, 3]. So the edge between the points c and d of the polygon with the id 10 would have the index 43. You will have to pass edges in this form to SetSelectEdges, the function will then turn your partial edge selection into a full selection by selecting the corresponding global edge indices of polygons that share the selected edges.

    You can also do the same by using the Neighbor class and do it manually by retrieving the polygons attached to an edge defined by two points.

    Cheers,
    zipit

    The whole point of SetSelectedEdges is not to do the 4*PolygonIdx+PolygonEdgeIdx math and have to supply multiple physical edges where a single logical edge from Neighbor will suffice).

    Please reread the below quoted portion of my post and especially the part I marked in bold:

    SetSelectedEdges(self, e, pSel, ltype)
    

    ..., method takes a pSel argument of type BaseSelect.

    The help for this method further tells us that:

    The edges are indexed uniquely by a Neighbor object, so each
    edge has a single index.

    This tells us that we should not use the 4*PolygonIdx+PolygonEdgeIdx formula to specify the edge indices for the BaseSelect, but instead refer to the logical edge indices as determined and governed by the Neighbor object after it does its calculations. The problem and crux of the question is that we don't know what those edges are, since they don't seem to be accessible to code that is outside of the Neighbor object. This information seems to be encapsulated within the Neighbor's guts and hidden from the users of the object, making it inadequate for its intended role in this case: The selection of a subset of the logical edge indices from the Neighbor as required by the pSel argument of SetSelectedEdges().

    My point with regard to which edge indexes to use is further driven home by the following sample code that appears in the help for BaseSelect.SelectAll() which shows the correct usage of the BaseSelect with the SetSelectedEdges() method and coincides completely with what I am asserting:

    def main():
        nbr = c4d.utils.Neighbor()
        nbr.Init(op) # Initialize neighbor with a polygon object
    
        edges = c4d.BaseSelect()
        
        # Select all edges in the range [0, nbr.GetEdgeCount()-1]   
        edges.SelectAll(nbr.GetEdgeCount()-1) 
    
        # ### Dev Comments
        # ### ============
        # ### The preceding line clearly demonstrates that Neighbor based
        # ### logical indices should be used with the BaseSelect since
        # ### nbr.GetEdgeCount() returns the number of Neighbor deduced
        # ### logical edges and not the total number of (potential)
        # ### physical edges (i.e., which would be 4*num_polys), at least the
        # ### the way I understand it.
    
        # Select all edges from our edges selection
        op.SetSelectedEdges(nbr, edges, c4d.EDGESELECTIONTYPE_SELECTION) 
        c4d.EventAdd() # Update Cinema 4D
    
    if __name__ == '__main__':
        main()
    

    Note: The Dev Comments portion in the above code was added by me to point out what can be deduced from the line of significance in the sample code from the help documentation.

    posted in Cinema 4D SDK •
    PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma

    PolygonObject's

    SetSelectedEdges(self, e, pSel, ltype)
    

    ..., method takes a pSel argument of type BaseSelect.

    The help for this method further tells us that:

    The edges are indexed uniquely by a Neighbor object, so each 
    edge has a single index.
    

    OK, makes sense so far. We use the edge index from the Neighbor object to identify an edge once, rather than N times where N is the number of polygons that share said edge.

    But, in order to use a "logical" edge index from the Neighbor object, we need to be able to tell which edge and/or edges this logical edge maps to. Unfortunately, the Neighbor class offers no way to get the properties of the logical edge to physical edge mapping, that it stores internally, given a logical edge index.

    There is only one method in the Neighbor class that has anything to do with the logical indexes that it stores, and that is Neighbor.GetEdgeCount(). This puts a cap on the number of logical edges, which are clearly indexed from 0 to Neighbor.GetEdgeCount() - 1.

    But, how in the world does one get info about these logical edges (by logical edge index) in order to decide whether or not to use them with the PolygonObject.SetSelectedEdges() method, if the representation and properties of the logical edges are completely opaque to users of the c4d.utils.Neighbor class? There is not a single method in this class that gives the developer any information about which physical points, edges, or polygons a logical edge, as referred to by its edge index in the Neighbor class, represents.

    Without any information and mapping to something physical and concrete, how can one refer to a "distinct logical edge" by its index within Neighbor and use that as the basis for forming the selection criteria inside a BaseSelect object as required by the pSel argument of the SetSelectedEdges() method of PolygonObject?

    TL;DR: If I can't tell which actual edges the edge indices of a Neighbor object refer to, I can't make use of Neighbor edge indices for any operation that requires them.

    Am I overlooking something, or is this API completely broken?

    posted in Cinema 4D SDK •