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
    • Register
    • Login

    Snap settings issues in R21/R23?

    Cinema 4D SDK
    r21 r23 python
    2
    12
    1.6k
    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.
    • CairynC
      Cairyn @ferdinand
      last edited by

      @zipit Thanks for looking at this.

      (1) ok - I haven't consulted version documentations before R21, so I don't know how the snap settings used to work in prehistoric times. I'm going to ignore these constants.

      (2) hmm, this explanation is hard to understand without looking at the underlying code, and I don't quite see the connection to the explanation in the docs either. The BaseDocument is already another parameter in the function call for SetSnapSettings, so it's clear that this container is a valid target for the settings. We also have the "Tool Specific" flag that allows us to set snap settings per tool, so these would have their own snap settings container - however, the parameter snapmode defines a snapmode and not a tool.

      (I would be less surprised if snapmode was a tool ID, like a call SetSnapSettings(doc,bc,c4d.ID_MODELING_MOVE) which I could identify as "set these snap settings as local parameters when the Move tool is active, so this is used when "Tool Specific" is active. But ID_MODELING_MOVE is not a snapmode! -- I even tried that in spite of the documentation, but it doesn't do anything.)

      From a practical standpoint, if I execute the following script in R23.1:

      import c4d
      from c4d import gui, utils, modules
      from c4d.modules import snap
      
      def main():
          bc = c4d.BaseContainer()
          bc[c4d.SNAP_SETTINGS_ENABLED] = True
          bc[c4d.SNAP_SETTINGS_RADIUS] = 15
          snap.SetSnapSettings(doc, bc, c4d.SNAPMODE_POINT)
          c4d.EventAdd()
      
      if __name__=='__main__':
          main()
      

      then the Vertex checkbox will be checked, and all the other parameters will be ignored (enabled and radius). I see no way that the GUI (can't look at the underlying system, of course) would use these settings in any form.

      Maybe that parameter is used internally for very specific purposes, but from a user's perspective, the explanation given in the docs for the parameter snapmode is plain wrong and suggests a behavior that doesn't happen.

      (3) The simplest of all settings (R23.1):

          bc = c4d.BaseContainer()
          bc[c4d.SNAP_SETTINGS_ENABLED] = True
          bc[c4d.SNAP_SETTINGS_TOOL] = True
          snap.SetSnapSettings(doc, bc)
      

      The "Enable Snapping" checkbox gets checked; the "Tool Specific" checkbox does not. The same happens if I explicitly set the parameter NOTOK. All other snapmodes, like SNAPMODE_POINT, completely ignore the BaseContainer anyway, as mentioned in point (2).
      Meanwhile (after your reply) I tested whether calling c4d.SpecialEventAdd(440000118) would make a difference, but it doesn't.

      Using EnableSnap doesn't work either, I didn't expect it to but just for completeness' sake:

          snap.EnableSnap(True, doc, c4d.SNAP_SETTINGS_TOOL)
          c4d.SpecialEventAdd(440000118)
          c4d.EventAdd()
      

      I have not yet tried to modify the document's GetSettingsInstance() container directly, but all other tests simply don't acknowledge the SNAP_SETTINGS_TOOL.

      (4) The snap core, as documented in the c4d.modules.snap documentation, covers four functional areas: snap, quantize, mesh check, and workplane. Three of them are mentioned in the class docs: snap, quantize, and workplane. Mesh check is ignored.
      Granted, mesh check does not have any special unique functions in this class, but it uses SetSnapSettings to set its parameters:

      import c4d
      from c4d import gui, utils, modules
      from c4d.modules import snap
      
      def main():
          bc = c4d.BaseContainer()
          bc[c4d.MESH_CHECK_ENABLED] = True
          bc[c4d.MESH_CHECK_POINT] = True
          bc[c4d.MESH_CHECK_EDGEPOINT_THRESHOLD] = utils.DegToRad(90.0)
          bc[c4d.MESH_CHECK_EDGEPOINT] = True
          snap.SetSnapSettings(doc, bc)
          c4d.EventAdd()
      
      if __name__=='__main__':
          main()
      

      so I would expect the constants to be listed there. The quantize constants are!

      In fact, I remember the topic from some forum a few years ago: Before you (Maxon) started to list the constants from header files automatically, the mesh check constants and their relationship to the snap core weren't listed in the docs at all. Since the R23 docs, you can at least find the constants, and logically derive how they may be set practically. Nevertheless, I would expect them to be listed under SetSnapSettings like all others that belong to the snap core.

      Speaking of constants, since the R23 it is also possible to set the snapmode checkboxes through SetSnapSettings:
      bc[c4d.SNAPMODE_POINT] = True (which hard crashes C4D in R21, btw) instead of using EnableSnap. This has also not made its way into the docs (or maybe that wasn't intended to work?).

      (5) Okay, I will use that call in addition. But I don't see what that function does there... According to the Customize Command window, 440000118 belongs to "Snap Radius Slider" with the description "Icon Palette GUI to control the screen space radius of snapping". Does this constant have a different meaning when I use it in SpecialEventAdd()? What is C4D doing internally?

      Also, while checking this, I tried to add the Snap Radius Slider to a toolbar that I added to the Attribute Manager:

      8773fa44-ab0d-42a8-9f89-321b51eb6c9f-image.png

      As you can see, this Snap Radius does not follow the Snap Radius in the Attribute Manager, and vice versa. Is that not the same setting? Is that a bug?

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

        Hi @Cairyn,

        I do not have time to answer all your points today and I would probably also would have to look at the code again for untangling all the details there, but I feel there is at least a minor misconception of yours. When invoking SetSnapSettings with a snapmode other than the default, i.e. NOTOK, all the function is doing, is writing to the data container of the document passed as doc. In the document container there are multiple sub-containers in the snap container for the snap modes. See the little script at the end for clarification.

        As already stated in my previous posting, I do not think that there is much to gain for the end user with snapmode. It probably should be marked as private.

        Cheers,
        Ferdinand

        import c4d
        from c4d.modules import snap
        
        def peek_document_snap_settings(doc):
            """Peeks into the snap settings of the document.
            """
            bc = doc.GetSettingsInstance(c4d.DOCUMENTSETTINGS_MODELING)
            
            snap = bc[c4d.SNAP_SETTINGS]
            print ("Global - Enabled", snap[c4d.SNAP_SETTINGS_ENABLED])
            print ("Global - Radius", snap[c4d.SNAP_SETTINGS_RADIUS])
            snap = bc[c4d.SNAP_SETTINGS][c4d.SNAPMODE_POINT]
            print ("Point - Enabled", snap[c4d.SNAP_SETTINGS_ENABLED])
            print ("Point - Radius", snap[c4d.SNAP_SETTINGS_RADIUS])
            print ("-" * 100)
        
        def main():
            """Writes both to the point and global snapping container.
            """
            peek_document_snap_settings(doc)
            
            # Write to the point specific settings. This will only be reflected in
            # container of the document.
            data = c4d.BaseContainer()
            data[c4d.SNAP_SETTINGS_ENABLED] = True
            data[c4d.SNAP_SETTINGS_RADIUS] = 25
            snap.SetSnapSettings(doc, data, c4d.SNAPMODE_POINT)
            
            # Write to the global settings. This will be reflected both in the GUI
            # as well as the document container.
            data = c4d.BaseContainer()
            data[c4d.SNAP_SETTINGS_ENABLED] = True
            data[c4d.SNAP_SETTINGS_RADIUS] = 50
            snap.SetSnapSettings(doc, data)
        
            c4d.EventAdd()
            peek_document_snap_settings(doc)
        
        if __name__=='__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

        CairynC 1 Reply Last reply Reply Quote 0
        • CairynC
          Cairyn @ferdinand
          last edited by

          @zipit Hello, thanks for the code. I see now how the containers are arranged, but the question remains why these subcontainers are even there in the first place. After all, the GUI doesn't indicate any of this, so maybe it's just an internal help / cache / convenience thing?

          Let's assume that we have sub-containers with different radii for vertex and polygon snapping. I can imagine that internally each snapping works independently and uses its own radius from its specific container, and shows the snap only when applicable. But the user wouldn't know and has no way of setting it with the current GUI, which only has one radius setting that doesn't change with the selection of the snapmode. By script, it could be set in the subcontainer, but is it actually used that way?

          Even more interesting: SNAPMODE_MIDPOINT is nominally a snapmode by itself. But actually, it is a modifier for the snapmodes edge, polygon, spline, and guide. With the subcontainers, we could have an edge snap active where midpoint is set, and a polygon snap as well where midpoint is not set, so the interactive snapping would consider edge mid points but not polygon centers. That actually makes sense -- however, midpoint is a snapmode which therefore has its own subcontainer(???) and not a setting for the other snapmodes.

          I have the feeling that the subcontainers for snapping is actually a concept that just didn't make it into the GUI design, and midpoint (as well as some other snapmodes maybe) is wrongly classified...

          I need to try the subcontainers now to test whether the snapping is actually (notably) influenced by them. Which would raise the question why you can't see any of that in the GUI.

          1 Reply Last reply Reply Quote 0
          • CairynC
            Cairyn
            last edited by

            okay, I just cobbled together a test.
            The calls to SetSnapSettings actually do write into the subcontainers:

            import c4d
            from c4d import gui, utils, modules
            from c4d.modules import snap
            
            def peek_document_snap_settings(doc):
                """Peeks into the snap settings of the document.
                """
                bc = doc.GetSettingsInstance(c4d.DOCUMENTSETTINGS_MODELING)
                
                snap = bc[c4d.SNAP_SETTINGS]
                print ("Global - Enabled", snap[c4d.SNAP_SETTINGS_ENABLED])
                print ("Global - Radius", snap[c4d.SNAP_SETTINGS_RADIUS])
                print ("Global - Mid Point", snap[c4d.SNAPMODE_MIDPOINT]) # subcontainer
                snap = bc[c4d.SNAP_SETTINGS][c4d.SNAPMODE_POINT]
                print ("Point - Enabled", snap[c4d.SNAP_SETTINGS_ENABLED])
                print ("Point - Radius", snap[c4d.SNAP_SETTINGS_RADIUS])
                print ("Point - Mid Point", snap[c4d.SNAPMODE_MIDPOINT])
                snap = bc[c4d.SNAP_SETTINGS][c4d.SNAPMODE_EDGE]
                print ("Edge - Enabled", snap[c4d.SNAP_SETTINGS_ENABLED])
                print ("Edge - Radius", snap[c4d.SNAP_SETTINGS_RADIUS])
                print ("Edge - Mid Point", snap[c4d.SNAPMODE_MIDPOINT])
                snap = bc[c4d.SNAP_SETTINGS][c4d.SNAPMODE_POLYGON]
                print ("Polygon - Enabled", snap[c4d.SNAP_SETTINGS_ENABLED])
                print ("Polygon - Radius", snap[c4d.SNAP_SETTINGS_RADIUS])
                print ("Polygon - Mid Point", snap[c4d.SNAPMODE_MIDPOINT])
            
                print ("-" * 100)
            
            def main():
                bc = c4d.BaseContainer()
                bc[c4d.SNAP_SETTINGS_ENABLED] = True
                bc[c4d.SNAP_SETTINGS_RADIUS] = 40
                snap.SetSnapSettings(doc, bc, c4d.SNAPMODE_POINT)
            
                bc = c4d.BaseContainer()
                bc[c4d.SNAP_SETTINGS_ENABLED] = True
                bc[c4d.SNAP_SETTINGS_RADIUS] = 5
                bc[c4d.SNAPMODE_MIDPOINT] = True
                snap.SetSnapSettings(doc, bc, c4d.SNAPMODE_EDGE)
            
                bc = c4d.BaseContainer()
                bc[c4d.SNAP_SETTINGS_ENABLED] = True
                bc[c4d.SNAP_SETTINGS_RADIUS] = 5
                bc[c4d.SNAPMODE_MIDPOINT] = False
                snap.SetSnapSettings(doc, bc, c4d.SNAPMODE_POLYGON)
            
                c4d.EventAdd()
                
                peek_document_snap_settings(doc)
            
            if __name__=='__main__':
                main()
            

            However, these values don't have any effect on the behavior of the snapping in the viewport. Here, only the global radius and enablement is used, and midpoint is a subcontainer by itself anyway. Setting midpoint in the subcontainers is probably the wrong way to go anyway because it's a snapmode by itself, but it's worth a try...

            Which raises the question (again, or still): Are these subcontainers even used at all by the snapping system? Or is that a preparation for future things to come / sign of an abandoned concept? You already said it's probably of no use to the end user (and rightfully so, as the GUI doesn't offer any possibility to use it gainfully), but is there code in C4D itself where it comes to bear...?

            Perhaps in snapmodes defined by my own C++ plugins, which could have their own GUI and their own subcontainers. You mentioned BasePlugin before...

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

              Hi @Cairyn,

              yes, writing snap tolerances to these tool specific containers is not being respected. And after some further investigations I have come to the conclusion, that most things written to document snap settings (even the global settings) will be ignored. A workaround, which oddly is also done by Cinema internally, is to just invoke the respective plugin command for the snap implementations. I will have to ask the modelling team on Monday, why things are implemented this way and subsequently why they did choose to keep the snap settings around in this manner.

              In the mean time please take a look at the script below, which shows you how to read and write to SNAP_SETTINGS_TOOL with this rather odd approach.

              Cheers,
              Ferdinand

              import c4d
               
              def main():
                  """How to weasel your way around SNAP_SETTINGS_TOOL. 
              
                  The same approach can be applied to other snap tools (basically 
                  everything you can turn on or off in the snapping tool bar).
                  """
                  # Get the command state.
                  state = c4d.IsCommandChecked((c4d.SNAP_SETTINGS_TOOL))
                  print ("Tool Specific is enabled:", state)
              
                  # Toggle the command.
                  c4d.CallCommand(c4d.SNAP_SETTINGS_TOOL)
              
                  # Get the new command state.
                  state = c4d.IsCommandChecked((c4d.SNAP_SETTINGS_TOOL))
                  print ("Tool Specific is enabled:", state)
                  
              if __name__ == "__main__":
                  main()
              

              MAXON SDK Specialist
              developers.maxon.net

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

                Hi,

                without further feedback, we will consider this thread as solved by Monday and flag it accordingly.

                Cheers,
                Ferdinand

                MAXON SDK Specialist
                developers.maxon.net

                1 Reply Last reply Reply Quote 0
                • CairynC
                  Cairyn
                  last edited by

                  @zipit said in Snap settings issues in R21/R23?:

                  I will have to ask the modelling team on Monday, why things are implemented this way and subsequently why they did choose to keep the snap settings around in this manner.

                  I was just waiting whether the modeling team had any comments on that...

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

                    Hi @Cairyn,

                    I am so sorry, I totally forgot about this. I am on it.

                    Cheers,
                    Ferdinand

                    MAXON SDK Specialist
                    developers.maxon.net

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

                      Hi @Cairyn,

                      sorry again for the long wait, I have asked the devs and:

                      1. The functionality of snapmode has never been used and has no effect at all at the moment. It will probably be marked as deprecated in an upcoming release.
                      2. The approach shown by me is a valid approach. Alternatively you can also write to and read from the active tool settings (see example below).

                      Cheers,
                      Ferdinand

                      import c4d
                      
                      def main():
                          """
                          """
                          # Instance of the active tool's settings container.
                          bc = doc.GetActiveToolData()
                          if not isinstance(bc, c4d.BaseContainer):
                              raise MemoryError()
                      
                          # Get a copy of the tool's snap settings or create a container when
                          # there has no snap settings been created yet.
                          snap = bc.GetContainer(c4d.SNAP_SETTINGS) or c4d.BaseContainer()
                      
                          # Write some data for that tool, specifically set the tool specific snap radius.
                          snap[c4d.SNAP_SETTINGS_TOOL] = True
                          snap[c4d.SNAP_SETTINGS_RADIUS] = 50.
                      
                          # Write the snap settings back to the tool.
                          bc.SetContainer(c4d.SNAP_SETTINGS, snap)
                          c4d.EventAdd()
                      
                      if __name__=='__main__':
                          main()
                      

                      MAXON SDK Specialist
                      developers.maxon.net

                      CairynC 1 Reply Last reply Reply Quote 1
                      • CairynC
                        Cairyn @ferdinand
                        last edited by

                        @zipit Thanks, I thought so... but you never know whether some functionality has a hidden internal purpose. Deprecating it will probably be the best solution.

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