• 0 Votes
    3 Posts
    590 Views
    P
    @ferdinand Thank you for your reply
  • 0 Votes
    13 Posts
    2k Views
    G
    It works as expected in C4D 2024, but not in the previous versions.
  • 0 Votes
    2 Posts
    726 Views
    i_mazlovI
    Hi @Gaal-Dornik , Thanks for reaching out to us. Please excuse the delay. This issue is now tracked via internal bug tracking system. Thread is tagged to_fix, so that the thread can be tracked more closely. Cheers, Ilia
  • 0 Votes
    5 Posts
    801 Views
    gheyretG
    @ferdinand Indeed, the allure of good looks can be captivating at times. In fact, my primary objective was to explore the development of a pie toolbar, which may seem unconventional but has always held a deep fascination for me. I sincerely appreciate your valuable advice and will certainly give it a try. Cheers, Gheyret
  • Simulating mouse movement in C4D

    Cinema 4D SDK python r25 windows
    5
    0 Votes
    5 Posts
    3k Views
    D
    Hi @ferdinand, Thank you so much for the comprehensive reply! Given all this I don't think it would make sense to keep trying to unit test this particular part of our code with Python. The last approach you highlighted with testing directly on the C++ side seems like the best option, so I'll give that a try. I'll have to see how to integrate this test with the rest of the unit tests (we have a lot and they're all in Python) but hopefully that won't be too much of a problem. Thanks again for all the help, Daniel
  • 0 Votes
    3 Posts
    544 Views
    G
    I see, thanks. But honestly i don't get it why the plugins folder isn't in the search path...
  • Howto to pass arguments to a python script

    Cinema 4D SDK python windows
    4
    0 Votes
    4 Posts
    556 Views
    ferdinandF
    Hey @Gaal-Dornik, In Cinema 4D you can bind a command to multiple shortcuts, but adding arguments is not possible. You would have to poll the keyboard yourself to distinguish such events. Cheers, Ferdinand Result: [image: 1696252767040-49a4bee1-8135-412f-b238-43d21184e071-image-resized.png] Code: """Runs different code based on which keys are pressed. Must be run as a Script Manager script. Pressing Shift+ALT+A while the script is invoked will print "Foo", while pressing Ctrl+Alt+A will print "Bar". See: https://developers.maxon.net/docs/py/2024_0_0a/misc/inputevents.html """ import c4d def CheckKeyboardState(*args: tuple[str]) -> bool: """Returns #True when the given key sequence is pressed, #False otherwise. """ result = [] for char in (n.upper() for n in args if isinstance(n, str)): if char == "SHIFT": c = c4d.KEY_SHIFT elif char == "CTRL": c = c4d.KEY_CONTROL elif char == "ALT": c = c4d.KEY_ALT else: c = ord(char) bc = c4d.BaseContainer() if not c4d.gui.GetInputState(c4d.BFM_INPUT_KEYBOARD, c, bc): raise RuntimeError("Failed to poll the keyboard.") result += [True if bc[c4d.BFM_INPUT_VALUE] == 1 else False] return all(result) def main() -> None: """ """ if CheckKeyboardState("Shift", "Alt", "A"): print ("Foo") elif CheckKeyboardState("Ctrl", "Alt", "A"): print ("Bar") else: print("None") if __name__ == '__main__': main()
  • Python module load order in 2024

    Cinema 4D SDK windows 2024
    9
    0 Votes
    9 Posts
    1k Views
    I
    Thank you again, it worked. I just had to take into account that the python310.win64.framework directory is now named just win64 in Cinema 4D 2024. And I had to add the path to ai.dll too, because our Arnold translator had the same problem, the C4DtoA module no longer loads ai.dll on startup.
  • 0 Votes
    5 Posts
    1k Views
    ThomasBT
    @ferdinand This is a command, and you can c4d.CallCommand it, but that is all the control you have. thank you Ferdiand,
  • Capsules Drag & Drop

    Cinema 4D SDK 2023 c++ windows
    7
    1 Votes
    7 Posts
    1k Views
    J
    Thanks for the response, it's all working properly now. John Terenece
  • 0 Votes
    4 Posts
    1k Views
    S
    @ferdinand Just ran into the same problem. Thank you for the solution!
  • Running Automatic UV for all objects

    Cinema 4D SDK python windows
    3
    1
    1 Votes
    3 Posts
    1k Views
    M
    Hi @BineeMan sorry for the late reply I was pretty busy Friday and thanks @Dunhou for demonstrating how Packed Auto UV work. For Cubic and Angle you need to use c4d.modules.bodypaint.CallUVComman as demonstrated in the call_uv_command_ example. So for the Angle automatic UV you need to use the command ID c4d.UVCOMMAND_OPTIMALMAPPING with the next parameters: c4d.OPTIMALMAPPING_PRESERVEORIENTATION # Preserve Orientation c4d.OPTIMALMAPPING_STRETCHTOFIT # Stretch to Fit c4d.OPTIMALMAPPING_DISTORTION # Maximu, Distortion c4d.OPTIMALMAPPING_RELAXCOUNT # Relaxation Steps c4d.OPTIMALMAPPING_SPACING # Spacing And for the Cubic automatic UV you need to use the command ID c4d.UVCOMMAND_OPTIMALCUBICMAPPING with the next parameters: c4d.OPTIMALMAPPING_PRESERVEORIENTATION # Preserve Orientation c4d.OPTIMALMAPPING_STRETCHTOFIT # Stretch to Fit c4d.OPTIMALMAPPING_TWOD # 2D c4d.OPTIMALMAPPING_AREAFAK # Maximum Area Factor c4d.OPTIMALMAPPING_RELAXCOUNT # Relaxation Steps c4d.OPTIMALMAPPING_SPACING # Spacing Cheers, Maxime.
  • Can't snap to Generator output spline

    Cinema 4D SDK r25 python windows
    4
    0 Votes
    4 Posts
    761 Views
    ferdinandF
    Hey @Madara, in short, a Python generator object is not meant to generate splines. You can collapse the cache of your constructed spline and return its LineObject cache instead of the spline itself. For an example, see [1]. In your case that would be connect_obj. This will work in the sense that the generator cache will be then like a SplineObject cache and when you 'Current State to Object' your generator, it will behave like a spline and give you the interpolated editable linear spline and not a spline generator. However, spline snapping not only inspects caches, but also checks if OBJECT_ISSPLINE is true. So, you cannot bamboozle it by faking a SplineObject cache. You will have to implement a proper spline plugin for what you want to do, as you can overwrite there GetContour Cheers, Ferdinand PS: Your code contains multiple elements that can crash Cinema 4D and/or corrupt the loaded document. Specifically, these sections: if link_clone: #Parametric object pobj = u.SendModelingCommand( command = c4d.MCOMMAND_CURRENTSTATETOOBJECT, list = [connect_obj], mode = c4d.MODELINGCOMMANDMODE_ALL, doc = op.GetMain()) connect_obj = pobj[0] ... offspline = u.SendModelingCommand( c4d.MCOMMAND_SPLINE_CREATEOUTLINE, [connect_obj], c4d.MODELINGCOMMANDMODE_ALL, bc=settings, doc=doc) A Python generator's main() function is subject to the threading restrictions of Cinema 4D because it is just a wrapper for ObjectData.GetVirtualObjects. Executing SendModelingCommand in a threaded context is fine, but you cannot do it on the active document (both doc and op.GetDocument() are the active document). This is here even worsened by the fact that that the objects in list are not actually part of the document you pass as doc. To circumvent that you must create your own document just as I have in [1] with temp. Find a more detailed example of that technique in smc_extrude_s26.py. [1] import c4d op: c4d.BaseObject # The Python generator object. def main() -> c4d.BaseObject: """ """ # A user data Boolean to toggle collapsing the cache of this generator. collapse: bool = op[c4d.ID_USERDATA, 1] spline: c4d.BaseObject = c4d.BaseObject(c4d.Osplinecircle) if not spline: return c4d.BaseObject(c4d.Onull) # Just return the circle spline itself when #collapse is false. if not collapse: return spline # Otherwise grab the LineObject cache of the spline and return a copy of it. temp: c4d.documents.BaseDocument = c4d.documents.BaseDocument() temp.InsertObject(spline) temp.ExecutePasses(c4d.threading.GeGetCurrentThread(), False, False, True, c4d.BUILDFLAGS_NONE) cache: c4d.LineObject | None = spline.GetCache() if not isinstance(cache, c4d.LineObject): return c4d.BaseObject(c4d.Onull) return cache.GetClone(c4d.COPYFLAGS_NONE)
  • 1 Votes
    6 Posts
    1k Views
    ferdinandF
    Hello @HerrMay, Thank you for the updated information. I had a look at your problem, and I could reproduce it, but it is primarily a problem of your code as suspected. I document below how I analyzed this problem not as a passive aggressive expression of "look how easy that is!". But to give an insight on how to approach such debugging task. The core rule is here to question one's own assumptions and assure that things are indeed how we think they are. Debugging So, when one follows your steps, one indeed ends up with something like this: [image: 1694702115621-8fdef1aa-1a5e-43d8-8cdb-42b480fd4f0d-image.png] Your assumption here was that the icon handling is bugged on our side. For that to be true, the tag on Null.2 would have to be IDC_ACTIVE_ON but with the icon handling failing and Cinemas 4D rendering it as disabled. So, I checked the value with this code: # Set custom icon for node. if id == c4d.MSG_GETCUSTOMICON: host: c4d.BaseObject = tag.GetObject() isFirst: bool = host.GetUp() == None and host.GetPred() == None if isFirst: print(f"TOP: {host.GetName() = }, {tag[IDC_ACTIVE] = }") else: print(f"OTHER: {host.GetName() = }, {tag[IDC_ACTIVE] = }") icon = self.PLUGIN_ICON_INACTIVE if tag[IDC_ACTIVE] == IDC_ACTIVE_OFF else self.PLUGIN_ICON_ACTIVE data["bmp"] = icon data["w"] = icon.GetBw() data["h"] = icon.GetBh() data["filled"] = True Which will print this when you add the tags: [image: 1694702486106-baebed6a-fdc0-4d53-8214-1d6b2011cad8-image.png] So, the topmost tag is indeed inactive and the icon rendering is correct. The question is now: 'Why is that tag inactive because we initialize all tags as node[IDC_ACTIVE] = IDC_ACTIVE_ON ?' The likely culprit is just a few lines above, as we toggle there the active state of our tag. # Handle double click on tag. if id == c4d.MSG_EDIT: if tag[IDC_ACTIVE] == IDC_ACTIVE_OFF: tag[IDC_ACTIVE] = IDC_ACTIVE_ON else: tag[IDC_ACTIVE] = IDC_ACTIVE_OFF And indeed, when we comment out these lines, we end up with this when creating multiple new tags. [image: 1694702820259-ebddd8e2-ff78-47f3-a4ca-40cbd26ac047-image.png] Moreover, when we revert our change of commenting out that code and test our assumption 'only happens for multi-object selections', we can see that this does not hold true: [image: 1694703033146-e5f2a8dd-2ebd-4555-8188-d769b84895e3-image.png] We can now check our own comment: # Handle double click on tag. if id == c4d.MSG_EDIT: ... and check MSG_EDIT: [image: 1694703119443-de39ee11-81d9-4e68-a2a6-a4c4dd182b84-image.png] The documentation says, "for example". So, the event does not capture a double click, but double clicking a BaseObject to edit its name is one example of an edit event. Adding a tag to an object apparently also emits MSG_EDIT to the tag, and when you have a multi-selection, MSG_EDIT is only emitted to the first element in the selection. Conclusion That behavior of MSG_EDIT in regard to tags and multi-selections is of course quite odd, but I would not consider it to be a bug, as an edit event is not really defined for tags in the first place. And while I understand what you want to do here, I would also say that what you do in MSG_EDIT is a violation of the UX principles of Cinema 4D. Double clicking a tag should not edit a random parameter in it. We strive for Cinema 4D having a seamless UX-experience for users including plugins. So, we rather not support it when plugin authors are trying to break these UX rules. But I will nevertheless talk with my colleagues about the subject and if we want to consider this behavior of MSG_EDIT to be a bug. Cheers, Ferdinand
  • Converting ABC file to AEC using c4dpy?

    Cinema 4D SDK windows python
    3
    0 Votes
    3 Posts
    458 Views
    S
    Hi Maxime, thanks for the information - so just to clarify, I need to use RDATA_PROJECTFILESAVE in order to export as an AEC?
  • Nesting Pivot object causes parent to shoot up into space

    Cinema 4D SDK
    6
    0 Votes
    6 Posts
    884 Views
    ferdinandF
    Hey @HerrMay, Great to hear that you solved your problem. Cheers, Ferdinand
  • How can I use Plane Cut in Python

    Cinema 4D SDK python 2023 windows
    3
    0 Votes
    3 Posts
    609 Views
    C
    Thank you very much! Now it is working.
  • 0 Votes
    3 Posts
    592 Views
    DunhouD
    Hi @ferdinand , @ferdinand said in Can I use both MouseDown() and DoubleClick() in treeview?: In general, you should keep in mind that the concept of a double click is only represented very weakly in Cinema 4D, with TreeViewFunctions being one of the very few cases which uses that term. In the backbone of the API, the GUI message stream, the concept of a double click does not exist. Things are actions (BFM_ACTION_...) and inputs (BFM_INPUT_...) and each mouse click counts as an input and/or action. It is up to specific gadgets to bundle up multiple clicks into a double click. That helps foe better unstand how it worked, thanks! And this kind solution is a much more mature solution than what I used a simple one in my Octane Light Manager, I try to stop the set check event duplicate many times, I will keep more condition as your suggestion. [image: 1693920299018-e0be12a8-f277-400d-9d7a-14065e55ffee-image.png] Thanks again for your detailed explain for all the answer. Cheers~ DunHou
  • 0 Votes
    4 Posts
    812 Views
    ThomasBT
    @ferdinand Thank you Ferdinand for your leniency regarding the three questions.. Will take note of that in the future. =====BaseTime===== Which just surprises me about the BaseTime problem is, that it worked in the Python tag. I didn't get an error message and the prototype worked very well. So I also tried instead of BaseTime int type value in the plugin, but it crashed. It confused me a bit. So I try your suggestion. # In the python tag it worked with int # starf_frame was an int frame = doc.GetTime().GetFrame(doc.GetFps()) - op[c4d.ID_USERDATA,start_frame] ====PoseMorph Symbol==== Regarding the posmorph ID, I should have figured that out myself Thanks for your quick respond Tom
  • Automatic UV Rectangularize / MDATA_UVRECTANGULARIZE

    Cinema 4D SDK
    2
    0 Votes
    2 Posts
    591 Views
    M
    Hi sadly the only way is to use c4d.CallCommand(1055347) which does not open the window but directly execute the command. You can change the setting as you could in the windows, by editing the value in the BaseContainer of the document. Finally it act on the selected polygon, so the best way would be to select them. Find bellow a script that you can execute in the script manager that will select all polygon of an object and run the command. from typing import Optional import c4d doc: c4d.documents.BaseDocument # The active document op: Optional[c4d.BaseObject] # The active object, None if unselected def main() -> None: # Enables UV Polygon Mode if not already in any UV mode (needed for GetActiveUVSet to works) if doc.GetMode() not in [c4d.Muvpoints, c4d.Muvpolygons]: doc.SetMode(c4d.Muvpolygons) # Retrieves active UVSet, The UV windows need to be opened at least one time handle = c4d.modules.bodypaint.GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL) if handle is None: # If fail it may be because the Texture view is not open # Open A texture View c4d.CallCommand(170103) # In S22 you need to update the UV Mesh if c4d.API_VERSION >= 22000: c4d.modules.bodypaint.UpdateMeshUV(False) # Retrieves active UVSet, The UV windows need to be opened at least one time handle = c4d.modules.bodypaint.GetActiveUVSet(doc, c4d.GETACTIVEUVSET_ALL) if handle is None: raise RuntimeError("There is no Active UVSet") # Select the polygon you want to operate on currentSel = op.GetPolygonS() previousSel = currentSel.GetClone() # Will be used to restore the initial selection state at the end currentSel.SelectAll(op.GetPolygonCount() - 1 ) # Update the UV mesh to properly take into account the new selection c4d.modules.bodypaint.UpdateMeshUV(True) # Define the settings settings = doc.GetSettingsInstance(c4d.DOCUMENTSETTINGS_MODELING) settings[c4d.MDATA_UVRECTANGULARIZE_ALIGN] = True settings[c4d.MDATA_UVRECTANGULARIZE_EQUIDISTANT] = False # Execute the command c4d.CallCommand(1055347) # Restore the initial selection previousSel.CopyTo(currentSel) c4d.modules.bodypaint.UpdateMeshUV(True) c4d.EventAdd() if __name__ == '__main__': main() Cheers, Maxime.