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
    1. Maxon Developers Forum
    2. datamilch
    3. Posts
    D
    • Profile
    • Following 0
    • Followers 0
    • Topics 22
    • Posts 52
    • Best 3
    • Controversial 0
    • Groups 0

    Posts made by datamilch

    • RE: Knowing if a font exists

      for my use case i will probably have a fallback solution with web-save or system fonts like this:

      import platform
      system_name = platform.system()
      if system_name == "Windows": font_name = "Consolas"
      elif system_name == "Darwin": font_name = "Menlo"
      elif system_name == "Linux": font_name = "DejaVu Sans Mono"
      
      posted in Cinema 4D SDK
      D
      datamilch
    • RE: Knowing if a font exists

      hi @ferdinand exactly what i needed , thanks!
      the fontchek seems solid so far.

      did a bid of testing, the check with c4d.bitmaps.GeClipMap.GetFontDescription()
      seems only to work for the c4d.GE_FONT_NAME_POSTSCRIPT parameter. not with c4d.GE_FONT_NAME_FAMILY or c4d.GE_FONT_NAME_DISPLAY ... maybe because they could be ambiguous?

      then it was a bit unclear how the postscript names are constructed. i created a font-selector-user-data to print the ps-names of the selected font. these names could then be used to check if a font exists on another system. i just hope, these names are consistent across platfoms.

      the fontdata definiton still feels a bit unclear to me in respect to which values are needed or not. the ps-name alone will not work to define a fontdata. a single name in font[500] might work. ... in my test i used the bc returned by c4d.bitmaps.GeClipMap.GetFontDescription().

      here is some test code i used in a python generator:

      import c4d
      
      doc: c4d.documents.BaseDocument # The document the object `op` is contained in.
      op: c4d.BaseObject # The Python generator object holding this code.
      
      def main() -> c4d.BaseObject:
          
          fontdata = op[c4d.ID_USERDATA,1] # font selector data field
          font: c4d.BaseContainer = fontdata.GetFont()
          print( "==============================" )
          print( font[500] )
          print( font[501] )
          print( font[502] )
          print( font[503] )
          print( font[508],"  <- ps name" )
          print( font[509] )
          print( "------------" )
          
          # store the ps name for later
          font_ps_name = font[508]
          
          # we are testing the font, that we just selected in the user data - so it should always exist
          check_bc: c4d.BaseContainer | None = c4d.bitmaps.GeClipMap.GetFontDescription(font[508], c4d.GE_FONT_NAME_POSTSCRIPT)
          if not check_bc: print(f"508 nope")
          else:            print(f"508 exists" )
      
          print( "------------" )
          print( check_bc[500] )
          print( check_bc[502] )
          print( check_bc[503] )
          print( check_bc[504] )
          print( check_bc[509] )
          print( "------------" )
      
      
      
          # testing with spline text
          # --------------------------------------------------------
          
          # defing a font only with its ps-name will not work - yields default font   
          bc_font = c4d.BaseContainer()
          bc_font.SetString(508, font_ps_name)
          
          # using the data returned by GetFontDescription() works
          fontdata = c4d.FontData()
          fontdata.SetFont( check_bc )
          
          text_spline = c4d.BaseObject( c4d.Osplinetext )
          text_spline[c4d.PRIM_TEXT_TEXT] = "Text 0123"
          text_spline[c4d.PRIM_TEXT_FONT] = fontdata
          
          return text_spline
      
      posted in Cinema 4D SDK
      D
      datamilch
    • Knowing if a font exists

      hi there,

      i'd like to know if there is any way to know if the font i entered is valid / exists on the pc of the user.
      i can set the font 'Arial', but if i set a non existing font like 'Whatever', there will be a fallback to the default font. i tried to get some feedback, if this has happend, but i can not figure out how.

      font check 01.c4d

      this is my current code in a python generator:

      import c4d
      
      doc: c4d.documents.BaseDocument
      op: c4d.BaseObject
      
      def main() -> c4d.BaseObject:
      
          text = op[c4d.ID_USERDATA,2]
      
          # temp doc
          # ----------------------------------------------------------
          temp_doc: c4d.documents.BaseDocument = c4d.documents.BaseDocument()
          temp_doc.SetName("temp_doc")
          temp_null = c4d.BaseObject( c4d.Onull )
          temp_doc.InsertObject( temp_null )
      
          # create text spline
          # ----------------------------------------------------------
          text_spline = c4d.BaseObject( c4d.Osplinetext )
          text_spline[c4d.PRIM_TEXT_TEXT] = text
          text_spline[c4d.PRIM_TEXT_ALIGN] = 1
          text_spline[c4d.PRIM_TEXT_HEIGHT] = 1
          text_spline[c4d.PRIM_PLANE] = 2
      
          bc_font = c4d.BaseContainer()
          #bc_font.SetString(500, 'Arial')
          bc_font.SetString(500, 'Consolasssss')
          bc_font.SetInt32(502, 700) # weight
          fontdata = c4d.FontData()
          fontdata.SetFont( bc_font )
      
          text_spline[c4d.PRIM_TEXT_FONT] = fontdata
      
          # font check in temp doc
          # ----------------------------------------------------------
          text_spline.GetClone().InsertUnder( temp_null )
          temp_doc.ExecutePasses( c4d.threading.GeGetCurrentThread(), True, True, True, c4d.BUILDFLAGS_NONE )
      
          text_spline_2 = temp_null.GetDown()
          fontdata_check = text_spline_2[c4d.PRIM_TEXT_FONT]
          font_check = fontdata_check.GetFont()
          print( font_check[500] ) # will print the non existing font name, not the fallback default font
      
      
          return text_spline
      
      posted in Cinema 4D SDK windows python 2025
      D
      datamilch
    • save/keep cache of generator plugin

      hi there,
      i've built a python generator, where i use its cache unless some parameters are dirty. when close and reopen the scene, the cache is gone and has to be rebuild. in principal, is there (not too complicated) way to keep/save these caches inside the scene?
      would this be possible with a python generator or a proper plugin?
      i'm using python.

      to be a bit more specific: by clicking a checkbox on the python generator, i do a cloth simulation in a temp doc. the result of the sim is convertet to polygons and returned by the python generator. the simulation is not super heavy, but it woud be nice if i could avoid the recalculation. so i'd like to store the point data in the scene/generator. i'd like to avoid external files too, if possible.

      my code is a bit long at the moment, i would post a shorter version, if you say it should be possible ...
      best, sebastian

      posted in Cinema 4D SDK 2025 python windows
      D
      datamilch
    • RE: SAVEDOCUMENTFLAGS_AUTOSAVE still added to the recent file list?

      @ferdinand said in SAVEDOCUMENTFLAGS_AUTOSAVE still added to the recent file list?:

      But please provide instructions on how to use your code when required in the future.

      oh shit, i'm so sorry. you're right. completly missed this one. 😬
      ...
      and of cause thanks for the explaination / clarification.

      posted in Cinema 4D SDK
      D
      datamilch
    • SAVEDOCUMENTFLAGS_AUTOSAVE still added to the recent file list?

      hi there,
      i recreated the autosave functionality (and tweeked the post-fix a bit) to be able to trigger it on demand.
      then i noticed the saved files still show up in the recent file list, even though i added the SAVEDOCUMENTFLAGS_AUTOSAVE flag. the documentation says this should not happen. adding SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST makes it work.
      is this correct or a bug or something else wrong with my code?

      cheers sebastian

      import c4d, os, datetime
      
      doc: c4d.documents.BaseDocument  # The currently active document.
      op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
      
      def main() -> None:
      
          # autosave
          doc_path = doc.GetDocumentPath()
          doc_name = doc.GetDocumentName()
          now = datetime.datetime.now().strftime("%Y%d%m_%H%M%S")
          doc_name_post_fix = f"{doc_name}@export_{now}"
              
          prefs = c4d.GetWorldContainerInstance()
          ausosave_on   = prefs[c4d.WPREF_AUTOSAVE_ENABLE]
          ausosave_dest = prefs[c4d.WPREF_AUTOSAVE_DEST] # 0=project, 1=custom, 2=user
          if ausosave_on:
              if ausosave_dest==0:
                  if not doc_path:
                      #print( "no doc path, fallback to user" )
                      doc_path = c4d.storage.GeGetStartupWritePath()
                  save_path = os.path.join( doc_path, "backup", doc_name_post_fix )
              if ausosave_dest==1:
                  doc_path = prefs[c4d.WPREF_AUTOSAVE_DEST_PATH]
                  if not os.path.isdir(doc_path):
                      #print( "no custom path, fallback to user" )
                      doc_path = c4d.storage.GeGetStartupWritePath()
                      doc_path = os.path.join( doc_path, "backup")
                  save_path = os.path.join( doc_path, doc_name_post_fix )
              if ausosave_dest==2:
                  doc_path  = c4d.storage.GeGetStartupWritePath()
                  save_path = os.path.join( doc_path, "backup", doc_name_post_fix )
              #print( save_path )
              res = c4d.documents.SaveDocument(doc, save_path, saveflags=c4d.SAVEDOCUMENTFLAGS_AUTOSAVE | c4d.SAVEDOCUMENTFLAGS_DONTADDTORECENTLIST, format=c4d.FORMAT_C4DEXPORT)
              if not res:
                  print( "error writing autosave on export" )
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK windows python 2025
      D
      datamilch
    • RE: Docked Dialog Problem with width of CUSTOMGUI_FILENAME

      hi, thanks for the info.
      i just updated to 2025.2.0 ... but i suppose the fix is not in this one yet? not sure when the release date was ...

      posted in Cinema 4D SDK
      D
      datamilch
    • RE: Docked Dialog Problem with width of CUSTOMGUI_FILENAME

      Thanks for the preliminary feedback.
      I'll wait patiently 🙂

      posted in Cinema 4D SDK
      D
      datamilch
    • Docked Dialog Problem with width of CUSTOMGUI_FILENAME

      hi there,
      i'm working on a command plugin with gedialog, that i want to dock in the layout.
      one of the gui elements is AddCustomGui c4d.CUSTOMGUI_FILENAME.
      the paths that are stored can be quite long.
      the problem is, that the dialog wants to show the complete string, which makes the dialog extremely wide.
      this only happens when the dialog is docked. then i can not scale the dialog smaller then the length of the string/filename.
      if the dialog is not docked i can scale it smaller and the overflowing string is hidden.
      sadly AddCustomGui has no properties initw and inith. i tried some combinations of flags but had no success.
      am i missing something?

      i could place the element in a scrollgroup, but then the "open document" button might be hidden in the part that is scrolled away.
      any other options or workarouds?

      cheers, sebastian

      here is the code

      import c4d, typing
      
      ID_PLUGIN_CMD: int = 1025245
      
      class MainDialog(c4d.gui.GeDialog):
      
          ID_GRP_MAIN     = 1000
          ID_TEXT_1       = 1001
          ID_TEXT_2       = 1002
          extra_long_str  = "this is some realy looooooooooooooooooooooooooooooooong text"
      
      
          def __init__(self) -> None:
              super().__init__()
      
          def InitValues(self) -> bool:
              return super().InitValues()
          
          def CreateLayout(self) -> bool:
              self.SetTitle("My Dialog")
      
              self.GroupBegin(id=self.ID_GRP_MAIN, flags=c4d.BFH_SCALEFIT, cols=1)
              self.GroupSpace(spacex=5, spacey=5)
      
              self.GroupBegin(id=0, flags=c4d.BFH_SCALEFIT, cols=2)
              self.AddStaticText(0, c4d.BFH_LEFT, name="path", initw=0 )
              bc = c4d.BaseContainer()
              self.AddCustomGui(self.ID_TEXT_1, c4d.CUSTOMGUI_FILENAME, '' ,c4d.BFH_SCALEFIT, 0, 0, bc,)
              self.AddStaticText(0, c4d.BFH_LEFT, name="text", initw=0 )
              self.AddEditText(self.ID_TEXT_2, c4d.BFH_FIT, 20,10 )
              self.GroupEnd()
              
              self.GroupEnd()
      
              self.SetString( id=self.ID_TEXT_1, value=self.extra_long_str )
              self.SetString( id=self.ID_TEXT_2, value=self.extra_long_str )
      
              return  super().CreateLayout()
          
      
          def Command(self, cid: int, msg: c4d.BaseContainer) -> bool:
              return super().Command(cid, msg)
      
      class DialogManagerCommand (c4d.plugins.CommandData):
          dialog: typing.Optional[MainDialog] = None
      
          def Execute(self, doc: c4d.documents.BaseDocument) -> bool:
              if self.dialog is None:
                  self.dialog = MainDialog()
              if self.dialog.IsOpen() and not self.dialog.GetFolding():
                  self.dialog.SetFolding(True)
              else:
                  self.dialog.Open(c4d.DLG_TYPE_ASYNC, ID_PLUGIN_CMD, -2, -2, defaultw=300, defaulth=200)
              return True
      
          def RestoreLayout(self, secret: any) -> bool:
              if self.dialog is None:
                  self.dialog = MainDialog()
              return self.dialog.Restore(ID_PLUGIN_CMD, secret)
      
          def GetState(self, doc: c4d.documents.BaseDocument) -> int:
              result: int = c4d.CMD_ENABLED
              if self.dialog:
                  if self.dialog.IsOpen() and not self.dialog.GetFolding():
                      result |= c4d.CMD_VALUE
              return result
      
      def RegisterPlugin() -> bool:
          return c4d.plugins.RegisterCommandPlugin(
              id=ID_PLUGIN_CMD,
              str="filename_test_01",
              info=c4d.PLUGINFLAG_SMALLNODE,
              icon=None,
              help="",
              dat=DialogManagerCommand())
      
      def main():
          if not RegisterPlugin():
              raise RuntimeError( f"Failed to register {DialogManagerCommand} plugin." )
      
      if __name__ == '__main__':
          main()
      
      posted in Cinema 4D SDK windows python 2025
      D
      datamilch
    • RE: Message from Object or Tag to CommandData/GeDialog Plugin

      oh well ... tried option B) as planned.
      a python tag tracks the dirty status of some objects and starts a c4d.SpecialEventAdd(). this is received as a core message in my dialog. but when i go looking for the dirty objects, they are not dirty anymore.
      my workaround for now is, to have a str userdata on the pyhon tag. once the tag detects a dirty object, it writes its name into the str userdata. my dialog can then read the name of the object and find it. feels quite cumbersome but works for now.

      would there be a better/more hidden way to store the data?

      posted in Cinema 4D SDK
      D
      datamilch
    • Message from Object or Tag to CommandData/GeDialog Plugin

      hi there,

      i have a bunch of objects with userdata. then i have a GeDialog plugin with mostly the identical userdata. the dialog plugin is intended to control the objects from one common panel. but there might be cases, where the objects are adjusted direktly, so the dialog needs to be updated. i'm looking for a solution for this case.

      A)
      am i correct, the change of DIRTYFLAGS_DATA can not be tracked from GeDialog directly via the message system? i could only track things, that trigger a global event?
      i suppose it could be done with a timer, that checks the doc/objects every few millyseconds, but that feels very unprecise.

      B)
      with a python tag i can easily track the dirty status of objects, but from what i've read here, i can not send data (list of dirty objects) direkcly to the dialog/command plugin. i can just call my dialog and make it aware to look and find the dirty objects.

      C)
      there also seems to be c4d.GePluginMessage() and PluginMessage(). but i never found a clear statement, which plugin types are supported. i never even got c4d.GePluginMessage() to return True, which made me doubt myself a bit.

      so i would try to go with option B).
      any objections or completly different suggestions?
      cheers sebastian

      posted in Cinema 4D SDK windows 2023 python
      D
      datamilch
    • RE: use thicken generator inside a python generator

      hi @i_mazlov,
      ok, good to know.
      i also tried to insert the thicken generator into a temp_doc, activating the selections and executing passes. but that didn't work either.

      posted in Bugs
      D
      datamilch
    • RE: MCOMMAND_SELECTALL and MCOMMAND_SELECTINVERSE not working?

      @i_mazlov Thanks!
      makes sens. seems i only used the forgiving commands until now.

      the code is run in a python generator. so by returning the object, it will be inserted into the scene.

      posted in Cinema 4D SDK
      D
      datamilch
    • MCOMMAND_SELECTALL and MCOMMAND_SELECTINVERSE not working?

      hi there,
      i was shifting around some selections and noticed that some modeling commands seem to have no effect.
      MCOMMAND_SELECTALL
      MCOMMAND_SELECTINVERSE
      i realize i can use SelectAll on the BaseSelect. and i could surely build a loop, to invert the selection. but why are these commands there? they even return true, despite doing nothing.

      this is my code and a demo file:

      import c4d
      doc: c4d.documents.BaseDocument
      op: c4d.BaseObject
      
      def main() -> c4d.BaseObject:
          obj = op[c4d.ID_USERDATA,1].GetClone()
      
          bs_points = obj.GetPointS()
          #bs_points.SelectAll( obj.GetPointCount()-1 ) # will select all points
          c4d.utils.SendModelingCommand( c4d.MCOMMAND_SELECTALL,     [ obj ], c4d.MODELINGCOMMANDMODE_POINTSELECTION, doc=doc) # not working
          c4d.utils.SendModelingCommand( c4d.MCOMMAND_SELECTSHRINK,  [ obj ], c4d.MODELINGCOMMANDMODE_POINTSELECTION, doc=doc)
          c4d.utils.SendModelingCommand( c4d.MCOMMAND_SELECTINVERSE, [ obj ], c4d.MODELINGCOMMANDMODE_POINTSELECTION, doc=doc) # not working
              
          bc = c4d.BaseContainer()
          bc[c4d.MDATA_CONVERTSELECTION_LEFT]     = 0 # 0 = points
          bc[c4d.MDATA_CONVERTSELECTION_RIGHT]    = 1 # 1 = edges
          bc[c4d.MDATA_CONVERTSELECTION_TOLERANT] = False # tolerant conversion
          res = c4d.utils.SendModelingCommand( c4d.MCOMMAND_CONVERTSELECTION, [ obj ], bc=bc, doc=doc)
      
          res = c4d.utils.SendModelingCommand( c4d.MCOMMAND_EDGE_TO_SPLINE, [ obj ], doc=doc)
      
          return obj
      

      mcommand select test 01.c4d

      posted in Cinema 4D SDK python 2024 windows
      D
      datamilch
    • RE: use thicken generator inside a python generator

      circumvented the problem by generating 3 thicken objects - one for each surface part (shell, start cap, end cap). then created selection tags and stitched everything together with a connector object.

      posted in Bugs
      D
      datamilch
    • use thicken generator inside a python generator

      hi there,

      the thicken generator object is a super cool and powerful tool. i'd like to use it inside of a python generator object, to create some more complex geometry. for this i need some of the selection options of the thicken generator. but activating e.g. 'shell' selection from within the python generator will mess up the output geometry of the thicken object and not generate any polygon selection tag. see attached c4d file.

      after converting the python generator you have access to the built hierarchy and the generated thicken generator. when you select it and then try to activate some selection options ther will be no selection tags created. so it looks like something in the thicken generator is broken/uncomplete, when it is created within a python generator object.
      is there any way to make this work, or is this a special case/limitation?

      def main() -> c4d.BaseObject:
          source_obj = c4d.BaseObject( 5170 )
          source_obj[c4d.PRIM_CYLINDER_CAPS] = False
      
          thicken = c4d.BaseObject( 1060179 )
          source_obj.InsertUnder( thicken )
          thicken[c4d.SOLIDIFYGENERATOR_SELECTION_POLYGON_BOUNDARY] = 1 # does not create tag, will mess up (delete) geo of the selected polys
      
          return thicken
      
      

      thicken test 01.c4d

      thicken test 01.JPG

      posted in Bugs windows 2024 python
      D
      datamilch
    • RE: issue with inserting fieldlayers in r2024

      hi ilia @i_mazlov,
      thaks for the advice about threading and the dummy document and the fishy part.

      I continued testing the last days and noticed that it seems to work, when i insert a clone of the fieldlayer.

      posted in Bugs
      D
      datamilch
    • issue with inserting fieldlayers in r2024

      hi there,

      I'm having a weird issue with fieldlayers in r2024.
      I want to add a layer to a fieldlist. In r2023 it worked quite fine. Now I get a reference Error.
      But when I print anything, the error is not raised.
      In my example I use a python generator, but it seems to happen with a python tag, too.
      Or am I using/inserting the fieldlayers wrong?

      cheers sebastian

      python generator :

      import c4d
      
      def main() -> c4d.BaseObject:
          obj = c4d.BaseObject(c4d.Ocube)
          res = c4d.utils.SendModelingCommand( c4d.MCOMMAND_MAKEEDITABLE, [obj], c4d.MODELINGCOMMANDMODE_ALL, doc = doc )
          if not res: print("error")
          else: obj = res[0].GetClone()
      
          vertex_tag = obj.MakeVariableTag( c4d.Tvertexmap, len(obj.GetAllPoints()) )
          vertex_tag.SetName( "vertex_highlight_reinforce" )
          vertex_tag[c4d.ID_TAGFIELD_ENABLE] = 1
          fields = vertex_tag[c4d.ID_TAGFIELDS]
      
          f_layer_1 = c4d.modules.mograph.FieldLayer( c4d.FLquantize )
          fields.InsertLayer( f_layer_1 )
      
          vertex_tag[c4d.ID_TAGFIELDS] = fields
         
          #print ("")   # with print active, no error will be raised
      
          return obj
      

      results in the following error:
      ReferenceError: the object 'c4d.modules.mograph.FieldLayer' is not alive

      fieldlayer issue 01.c4d

      posted in Bugs windows python 2024
      D
      datamilch
    • RE: Create folder in Volume Builder

      hi @i_mazlov,
      thanks for the answer. good to know about this status / limitation.
      for my current case I found a solution without folders..

      posted in Cinema 4D SDK
      D
      datamilch
    • RE: fieldlayer with variable tag

      found the solution ... the corresponding type is called: FLweight

      posted in Cinema 4D SDK
      D
      datamilch