PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma
-
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 isNeighbor.GetEdgeCount()
. This puts a cap on the number of logical edges, which are clearly indexed from0
toNeighbor.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 thec4d.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 theNeighbor
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 aBaseSelect
object as required by thepSel
argument of theSetSelectedEdges()
method ofPolygonObject
?TL;DR: If I can't tell which actual edges the edge indices of a
Neighbor
object refer to, I can't make use ofNeighbor
edge indices for any operation that requires them.Am I overlooking something, or is this API completely broken?
-
Hi,
edges are globally indexed by
polygon_index * 4 + local edge_index
(seePolygonObject.GetEdgeS
), where the local edge index lies in the interval[0, 3]
. So the edge between the pointsc
andd
of the polygon with the id10
would have the index43
. You will have to pass edges in this form toSetSelectEdges
, 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 -
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.
-
@zipit said in PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma:
Hi,
edges are globally indexed by
polygon_index * 4 + local edge_index
(seePolygonObject.GetEdgeS
), where the local edge index lies in the interval[0, 3]
. So the edge between the pointsc
andd
of the polygon with the id10
would have the index43
. You will have to pass edges in this form toSetSelectEdges
, 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,
zipitThe 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 fromNeighbor
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 theBaseSelect
, but instead refer to the logical edge indices as determined and governed by theNeighbor
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 theNeighbor
'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 theNeighbor
as required by thepSel
argument ofSetSelectedEdges()
.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 theBaseSelect
with theSetSelectedEdges()
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.
-
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 -
@m_adam said in PolygonObject.SetSelectEdges() and the Neighbor() class, a giant dilemma:
...Hope it answers your questions,
Cheers,
MaximeThank 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.