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

    PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma

    Cinema 4D SDK
    4
    6
    1.2k
    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
      mikegold10
      last edited by m_adam

      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?

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

        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

        MAXON SDK Specialist
        developers.maxon.net

        M 1 Reply Last reply Reply Quote 1
        • C4DSC
          C4DS
          last edited by

          As discussed here going from unique edge index to global edge index (polygon * 4 + polygon side) is not possible. You are better of working with global edge indeces to start with ... I have learned that the hard way.

          1 Reply Last reply Reply Quote 0
          • M
            mikegold10 @ferdinand
            last edited by mikegold10

            @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.

            1 Reply Last reply Reply Quote 0
            • M
              m_adam
              last edited by m_adam

              Hi @mikegold10, to adds some clarification.
              A PolygonObject doesn't have a strong edge representation. An edge is simply defined by 2 points.
              With that in mind, there is actually no direct way (like GetPolygonCount or GetPointsCount) to retrieves the count of an edge in a PolygonObject, but a BaseSelect to select all elements needs this count to be able to select all elements. But here the tricky part because there is also ngnon edge(aka hidden edges) that can't be selected directly.
              So the faster way to know the count of all selectable edges is to create a neighbor object and use GetEdgeCount.

              So now let's say I want via python to select edge from pt ID 80 to pt ID 81.
              Here is how to do it with both methods:

              import c4d
              
              def GetEdgeFromCpolygon(polygonObject, ptIdA, ptIdB):
                  for polyId, cPoly in enumerate(polygonObject.GetAllPolygons()):
                      edgeIdAB = cPoly.FindEdge(ptIdA, ptIdB)
                      if edgeIdAB != c4d.NOTOK:
                          return polyId, edgeIdAB
              
                  raise ValueError("Unable to found edge for ptId {0} to ptId {1}".format(ptIdA, ptIdB))
              
              def GetFromNeighboor(polygonObject, nbr, ptIdA, ptIdB):
                  polyIdListA = nbr.GetPointPolys(ptIdA)
                  polyIdListB = nbr.GetPointPolys(ptIdB)
              
                  commonPolygonIdList = list(set(polyIdListA) & set(polyIdListB))
                  for commonPolygonId in commonPolygonIdList:
                      nbrPolyInfo = nbr.GetPolyInfo(commonPolygonId)
              
                      neigboorPolyId = nbr.GetNeighbor(ptIdA, ptIdB, commonPolygonId)
                      if not neigboorPolyId in nbrPolyInfo["face"]:
                          continue
                      
                      nbrPolyId = nbrPolyInfo["face"].index(neigboorPolyId)
                      return nbrPolyInfo["edge"][nbrPolyId]
                  
                  raise ValueError("Unable to found edge for ptId {0} to ptId {1}".format(ptIdA, ptIdB))
              
              
              # Main function
              def main():
                  doc.StartUndo()
              
                  # Select from the Cpolygon by editing directly the Edge BaseSelect
                  polygonId, edgeId = GetEdgeFromCpolygon(op, 80, 81)
              
                  edges = op.GetEdgeS()
                  doc.AddUndo(c4d.UNDO_CHANGE, op)
                  #edges.Select(polygonId * 4 + edgeId)
              
                  # Select from SetSelectedEdges
                  nbr = c4d.utils.Neighbor()
                  nbr.Init(op)
                  
                  edges = c4d.BaseSelect()
                  edgeId = GetFromNeighboor(op, nbr, 80, 81)
                  edges.Select(edgeId)
              
                  doc.AddUndo(c4d.UNDO_CHANGE, op)
                  op.SetSelectedEdges(nbr, edges, c4d.EDGESELECTIONTYPE_SELECTION)
              
                  doc.EndUndo()
                  c4d.EventAdd()
              
              if __name__=='__main__':
                  main()
              

              Note that the SetSelectedEdges fail if one of the points targeted to be select have more than 1 open edge, since in GetFromNeighboor, neigboorPolyId will have an id of -1 and in the face list returned by GetPolyInfo 2 face will have an Id of -1, so there is no way to know which edge is the correct one.

              However, looking at the implementation of SetSelectedEdges I would really recommend not using it except for full selection since it iterates each polygon and do.

              for polyId in range(op.GetPolygonCount():
                  polyInfo = nbr.GetPolyInfo(polyId)
                      
                  # {0, 1, 2, 3} mean there is one condition for each so understand it as ["edge"][0] then ["edge"][1], etc..
                  if passedEdgeSelectToSetSelectedEdges.IsSelected(polyInfo["edge"][{0, 1, 2, 3}]:
                      baseSelect.Select(polyId  * 4 + {0, 1, 2, 3})
              

              So using SetSelectedEdges is way more inefficient, can give false result than using the BaseSelect directly so I will really recommend using GetEdgeS, GetEdgeH or GetPhongBreak.

              Hope it answers your questions,
              Cheers,
              Maxime

              MAXON SDK Specialist

              Development Blog, MAXON Registered Developer

              M 1 Reply Last reply Reply Quote 1
              • M
                mikegold10 @m_adam
                last edited by

                @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.

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