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

    Help Needed: Filtering Selected Edges by Length

    Cinema 4D SDK
    windows python 2025
    2
    2
    305
    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.
    • M
      myosis
      last edited by

      (Using: Cinema 4D 2025.1.1)

      Hi there,

      My Issue: I'm developing a Python script in Cinema 4D 2025.1.1 to filter the current edge selection based on a minimum length threshold. The goal is to keep only edges that are equal to or longer than the specified length.

      Problem: While using the c4d.utils.Neighbor
      class, several essential methods required for accurate edge mapping are missing in my environment, resulting in AttributeError. Specifically, the following methods are unavailable:

      • GetEdgeID(polyIndex, localEdgeIndex)
      • GetEdgeInfo(edgeID, pointsArray, polysArray)
      • GetEdgePolygonPoints(polyIndex, localEdgeIndex)
      • GetEdgePoints(edgeID)

      Only GetPolyInfo(polyIndex) and GetEdgePolys(a, b) are accessible, leading to duplicate edge references and inaccurate filtering.

      Holy Grail: I need the following Neighbor class methods to achieve precise edge filtering:

      • GetEdgeID(polyIndex, localEdgeIndex)
      • Purpose: Maps a polygon's local edge to Cinema 4D's global edge ID.
      • GetEdgePolygonPoints(polyIndex, localEdgeIndex)
      • Purpose: Retrieves the two point indices defining the edge.

      These methods are crucial for eliminating indexing mismatches and ensuring each selected edge is measured accurately in global space.
      Diagnostic Script: Below is a script that tests all kinds of Neighbor methods to identify which ones are available in my Cinema 4D 2025.1.1 environment.

      import c4d
      from c4d import gui, utils
      
      def TestNeighborMethods():
          doc = c4d.documents.GetActiveDocument()
          obj = doc.GetActiveObject()
      
          if not obj or not obj.IsInstanceOf(c4d.Opolygon):
              gui.MessageDialog("Please select a Polygon Object first.")
              return
      
          neighbor = utils.Neighbor()
          if not neighbor.Init(obj):
              gui.MessageDialog("Failed to initialize Neighbor.")
              return
      
          polyCount = obj.GetPolygonCount()
      
          print("========================================================")
          print("Testing Neighbor methods on the selected object...")
          print(f"Object: {obj.GetName() or 'Unnamed'}, Polygons: {polyCount}")
          print("--------------------------------------------------------")
      
          # List of Neighbor methods to test
          methods = [
              ("GetPolyInfo", lambda: neighbor.GetPolyInfo(0)),
              ("GetEdgeID", lambda: neighbor.GetEdgeID(0, 0)),
              ("GetEdgeInfo", lambda: neighbor.GetEdgeInfo(0, [-1, -1], [-1, -1])),
              ("GetEdgePolygonPoints", lambda: neighbor.GetEdgePolygonPoints(0, 0)),
              ("GetEdgePoints", lambda: neighbor.GetEdgePoints(0)),
              ("GetEdgePolys", lambda: neighbor.GetEdgePolys(0, 1))
          ]
      
          for name, func in methods:
              try:
                  result = func()
                  print(f"{name}: Succeeded. Returned: {result}")
              except AttributeError as e:
                  print(f"{name}: [FAILED] AttributeError: {e}")
              except Exception as ex:
                  print(f"{name}: [FAILED] {ex}")
      
          print("========================================================\n")
      
      def main():
          TestNeighborMethods()
      
      if __name__ == "__main__":
          main()
      
      

      Request: Given that only GetPolyInfo() and GetEdgePolys(a, b) are available, how can I accurately map global edge IDs to their corresponding point indices to achieve precise edge filtering? Are there alternative methods or workarounds to obtain the necessary mappings without the missing Neighbor methods?

      Thank You! Any insights or solutions from the community on accessing the necessary Neighbor methods or alternative approaches to achieve accurate edge filtering would be greatly appreciated!

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

        Hello @myosis,

        Welcome to the Maxon developers forum and its community, it is great to have you with us!

        Getting Started

        Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.

        • Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
        • Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
        • Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.

        It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.

        About your First Question

        All the methods you list simply do not exist (neither in C++ nor in Python), see c4d.utils.Neighbor for the type overview.

        ⚠ I therefore must assume that you are using an AI, like, for example, ChatGPT, which hallucinated these methods. Please note that we reserve the right to refuse support when confronted with undisclosed AI gibberish, especially for beginner content. Always state when you used an AI to generate code.

        Something such as an edge does not exist concretely in our API and many other APIs, i.e., other than for points and polygons, there is no explicit data type for edges which would be stored. Edges are defined implicitly by CPolygon. To filter a selection for edges of a certain length, you would have to convert edge indices to polygon and point indices and then measure the distance between the relevant points.

        Cheers,
        Ferdinand

        Result

        074e2ada-a9ce-45b4-800a-acf7e060941a-image.png

        Code

        """Deselects all edges in the edge selection of the active object whose edge length exceeds
        MAX_EDGE_LENGTH.
        
        Must be run as a Script Manager script with an editable polygon object selected.
        """
        import c4d
        
        doc: c4d.documents.BaseDocument  # The currently active document.
        op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
        
        MAX_EDGE_LENGTH: float = 25.0  # The maximum length of an edge before to be considered too long.
        MAX_EDGE_LENGTH_SQUARED: float = MAX_EDGE_LENGTH ** 2  # The square of `MAX_EDGE_LENGTH`.
        
        def main() -> None:
            """Called by Cinema 4D when the script is being executed.
            """
            if not op or not op.IsInstanceOf(c4d.Opolygon):
                raise ValueError("The selected object is not a polygon object.")
            
            # Get the edge selection of the object and turn it into a list of selected edges indices. Also, 
            # get the points and polygons of the object.
            selection: c4d.BaseSelect = op.GetEdgeS()
            selectedEdges: list[int] = [i for i in range(op.GetEdgeCount()) if selection.IsSelected(i)]
            points: list[c4d.Vector] = op.GetAllPoints()
            polygons: list[c4d.CPolygon] = op.GetAllPolygons()
        
            def getPointByIndex(poly: c4d.CPolygon, index: int) -> c4d.Vector:
                """Returns the point of the polygon at the given index.
        
                CPolygon has no index access, so we fix that here.
                """
                if index == 0:
                    return points[poly.a]
                elif index == 1:
                    return points[poly.b]
                elif index == 2:
                    return points[poly.c]
                elif index == 3:
                    return points[poly.d]
                
            # Iterate over the edges and find the one's that are longer than MAX_EDGE_LENGTH. An edge index
            # is defined as: 
            #
            #   "The edges are indexed by 4 * polygon + edge where polygon is the polygon index and edge is 
            #    the edge index between 0 and 3."
            #
            # So, we must revert that here, then measure the edge length, and collect all too long edges.
            tooLongEdges: list[int] = []
            for edgeIndex in selectedEdges:
                polygonIndex: int = edgeIndex // 4
                edgeInPolygonIndex: int = edgeIndex % 4
                poly: c4d.CPolygon = polygons[polygonIndex]
                pointA: c4d.Vector = getPointByIndex(poly, edgeInPolygonIndex)
                pointB: c4d.Vector = getPointByIndex(poly, (edgeInPolygonIndex + 1) % 4)
                # Getting the length of a vector is quite expensive, so we compare the squared lengths.
                edgeLengthSq: float = (pointA - pointB).GetLengthSquared()
                
                if edgeLengthSq > MAX_EDGE_LENGTH_SQUARED:
                    tooLongEdges.append(edgeIndex)
        
            # Print the indices of the edges that are too long.
            print("The following edges are too long:", tooLongEdges)
            
            # Deselect all edges in the object's edge selection that are too long.
            for edgeIndex in tooLongEdges:
                selection.Deselect(edgeIndex)
        
            # Push an update event to Cinema 4D to redraw the object.
            c4d.EventAdd()
        
        if __name__ == '__main__':
            main()
        

        MAXON SDK Specialist
        developers.maxon.net

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