Notifications
Clear all

[Closed] Getting loop direction

Was wondering if anyone knew how to get the previous and next edges in an edge loop. I’m able to get both edges by getting the edge’s vertices, converting that to edges and subtracting that by edges converted by the original edge’s faces on both sides of the edge. Here’s an example –


  Edge1 = (polyop.getEdgeSelection $ as array)[1]
  Verts = polyop.getVertsUsingEdge $ #(Edge1)
  Edges = polyop.getEdgesUsingVert $ Verts
  Faces = polyop.getFacesUsingEdge $ #{Edge1}
  SideEdges = Edges - (polyop.getEdgesUsingFace $ Faces)
  

I figured you could get the previous and next edges in the loop by which edge index is higher, but I noticed that in various geometries, that you can’t use that because the edge indices of the entire loop isn’t always sequential. Should I be getting the edges by which vertex is higher or lower instead? Or should I try picking edges by an average of local face positions, maybe by using polyop.getFaceCenter? Unfortunately, I’m not that good with transformations. I also realized, I’ll have to check for cases when the current edge is at the beginning or end of a loop. Also, I noticed that special cases where the current edge is at a pole presents a problem, like at the bottom or top of a sphere.

The main reasons I’m trying to figure this out, instead of using setLoopShift, is so that I can then know how to make a similar function for meshes and patches. Also I try to avoid having to make selections whenever possible.

Thanks in advance!

6 Replies

Try this:

For open edge loop:

  • find the “starting edge” – this is edge that have only one neighbor edge from the loop edges
  • then, using your code find the next edges and so on.

For closed edge loop

  • get one edge, mark it as “starting edge”
  • get one vert of this edge and find the next edges. When the next edge is the “starting edge” – stop, because all edges from the loop are processed.
    Store the edge indexes in array, because the bitarray will rearenge them and you will loose the edge’s order.

Hey thanks for the quick reply. So, with this method I would have to do a whole loop to begin with to test if its open or not right?

How do you manually get all the edges in the loop, by using my method to get both side edges, and keep adding that to an array until the array count stops changing?

I think you can test for a closed loop, if the number of edges == the number of verts in the loop, right?

With a closed loop, how would you decide the first edge, maybe just the lowest index in the loop? I know t doesn’t matter, just curious what you would do.

It would be great to see the source for the original setloopshift function. My goal is make that for all 3 geometry types, along with setringshift.

Why you want to duplicate built in SetLoopShift?

This code works for open edge loop. To test it select some edges that have common verts. They can form a loop or…

Optimize it and addapt to works with closed edge loop.

(
  	curO = selection[1]
  	selEdges = polyop.getEdgeSelection curO
  	edgeVerts = polyop.getVertsUsingEdge curo selEdges
  	oneEdgeVerts = for v in edgeVerts where ( (selEdges * (polyop.getEdgesUsingVert curO v)).numberset == 1) collect v
  	vL1 = #{oneEdgeVerts[1]}
  	thisEdgeLoop = #()
  	e0 = #{}
  	for vv in edgeVerts do
  	(
  		e1 = (selEdges * (polyop.getEdgesUsingVert curO vL1)) - e0
  		if not e1.isEmpty then
  		(
  			for i in e1 do appendIfUnique thisEdgeLoop i
  			vL1 = (polyop.getVertsUsingEdge curO e1) - vL1
  			e0 = e1
  		)			
  	)	
  	format "Edge order: % 
" thisEdgeLoop
  )

Well I’m trying to duplicate it so I can figure out how it works, so I can do the same for meshes and patches. I also plan on making my own loop functions that aren’t in poly tools. I figured just getting the loop’s edges instead of needing to select them, getting that selection, and restoring the previous edge selection, would be easier and faster. For example, if I needed to test if a loop is open, or how many edges are in the current loop, or get a random selection within that loop, these would be cases where I need the loop’s edge’s. I would think that in most selection scripts, you should only need to set the selection once, at the end.

Your edge order method looks pretty good. I just finished a function to get the whole edge loop. So getting the edge order of that loop, should help. The function seems to work decent. Haven’t tested it extensively on different geometries though.


fn LoopPolyEdge Obj:$ Sel:(polyop.getEdgeSelection $) SetSelect:false =
(
	if (Obj != undefined and Sel != undefined and Sel.numberset >= 1) then
	(
		local LoopEdgeSel = #{} -- Initial selection
		for i in Sel where (LoopEdgeSel[i] != true) do
		(
			local EdgeLoop = #{i} -- Initial selection
			for j = 1 to (polyop.getNumEdges Obj) do
			(
				local OldSelCount = EdgeLoop.numberset -- Get previous bitarray count
				local PoleVerts = (for k in (polyop.getVertsUsingEdge Obj EdgeLoop) where ((polyop.getEdgesUsingVert Obj k).numberset > 4) collect k) as bitarray -- Get vertices in selection, whose edges are more than 4
				local PoleEdges = polyop.getEdgesUsingVert Obj PoleVerts -- Get edges from pole vertices
				local Verts = polyop.getVertsUsingEdge Obj EdgeLoop -- Get verts from current edge
				local Edges = polyop.getEdgesUsingVert Obj Verts -- Get verts from current edge
				local FaceEdges = polyop.getEdgesUsingFace Obj (polyop.getFacesUsingEdge Obj EdgeLoop) -- Get vertices from faces on both sides of edge
				local NewEdges = (Edges - FaceEdges - PoleEdges) + EdgeLoop -- Get edges within loop
				local BorderEdges = polyop.getOpenEdges Obj -- Get open edges
				if ((BorderEdges * NewEdges).numberset != 0) then
				(
					for k in NewEdges where (BorderEdges[k] != true) do 
					(
						local EdgeFaces = polyop.getFacesUsingEdge Obj k -- Get edge's faces
						local FaceEdges1 = polyop.getEdgesUsingFace Obj #{(EdgeFaces as array)[1]} -- Get edges from face 1
						local FaceEdges2 = polyop.getEdgesUsingFace Obj #{(EdgeFaces as array)[2]} -- Get edges from face 2
						NewEdges = (NewEdges - (FaceEdges1 + FaceEdges2)) + (FaceEdges1 * FaceEdges2) -- Get rid of edges not in loop
					) -- For edges that aren't on border, get only the edge that's between the two faces
				) -- If any edges in selection are open
				EdgeLoop = NewEdges -- Put edges to edge loop
				if (OldSelCount == EdgeLoop.numberset) then exit -- If selection doesn't change anymore
			) -- Keep growing loop until its full
			LoopEdgeSel += EdgeLoop -- Add edge loop to bitarray
		) -- Loop through edge selection, getting loop from edge, ignoring any edges that are already looped
		if (SetSelect == true) then polyop.setEdgeSelection Obj LoopEdgeSel -- Select edges
		return LoopEdgeSel -- Get edge loop selection
	) else return Sel -- Get original selection
)

Just updated function to try to deal with the ngons that the sphere in your picture has –


 fn LoopPolyEdge Obj:$ Sel:(polyop.getEdgeSelection $) SetSelect:false =
 (
 	if (Obj != undefined and Sel != undefined and Sel.numberset >= 1) then
 	(
 		local LoopEdgeSel = #{} -- Initial selection
 		for i in Sel where (LoopEdgeSel[i] != true) do
 		(
 			local EdgeLoop = #{i} -- Initial selection
 			for j = 1 to (polyop.getNumEdges Obj) do
 			(
 				local OldSelCount = EdgeLoop.numberset -- Get previous bitarray count
 				local PoleVerts = (for k in (polyop.getVertsUsingEdge Obj EdgeLoop) where ((polyop.getEdgesUsingVert Obj #{k}).numberset > 4) collect k) as bitarray -- Get vertices in selection, whose edges are more than 4
 				local PoleEdges = polyop.getEdgesUsingVert Obj PoleVerts -- Get edges from pole vertices
 				local Verts = polyop.getVertsUsingEdge Obj EdgeLoop -- Get verts from current edge
 				local Edges = polyop.getEdgesUsingVert Obj Verts -- Get verts from current edge
 				local FaceEdges = polyop.getEdgesUsingFace Obj (polyop.getFacesUsingEdge Obj EdgeLoop) -- Get vertices from faces on both sides of edge
 				local NewEdges = (Edges - FaceEdges - PoleEdges) + EdgeLoop -- Get edges within loop
 				local BorderEdges = polyop.getOpenEdges Obj -- Get open edges
 				if not (BorderEdges * NewEdges).isEmpty then
 				(
 					for k in NewEdges where (BorderEdges[k] != true) do 
 					(
 						local EdgeFaces = polyop.getFacesUsingEdge Obj #{k} -- Get edge's faces
 						local FaceEdges1 = polyop.getEdgesUsingFace Obj #{(EdgeFaces as array)[1]} -- Get edges from face 1
 						local FaceEdges2 = polyop.getEdgesUsingFace Obj #{(EdgeFaces as array)[2]} -- Get edges from face 2
 						NewEdges = (NewEdges - (FaceEdges1 + FaceEdges2)) + (FaceEdges1 * FaceEdges2) -- Get rid of edges not in loop
 					) -- For edges that aren't on border, get only the edge that's between the two faces
 				) -- If any edges in selection are open
 				EdgeLoop += NewEdges -- Put edges to edge loop
 				if (OldSelCount == EdgeLoop.numberset) then exit -- If selection doesn't change anymore
 			) -- Keep growing loop until its full
 			local VertLoop = polyop.getVertsUsingEdge Obj EdgeLoop -- Get vertices in loop
 			local NGonVerts = #{} -- Initial vert selection
 			for j in VertLoop where ((polyop.getEdgesUsingVert Obj #{j}).numberset == 3 and (polyop.getFacesUsingVert Obj #{j}).numberset == 3) do append NGonVerts j -- Get vertices with 3 edges and 3 faces
 			if not NGonVerts.isEmpty then
 			(
 				local Edges = #{} -- Initial edge selection
 				for j = 1 to VertLoop.numberset do Edges += (polyop.getEdgesUsingVert $ (VertLoop as array)[j]) * (polyop.getEdgesUsingVert $ (VertLoop - #{(VertLoop as array)[j]})) -- Get edges joined by vertices
 				EdgeLoop += Edges -- Add edges to edge loop
 			) -- If there are any vertices with 3 edges and 3 faces at all
 			LoopEdgeSel += EdgeLoop -- Add edge loop to bitarray
 		) -- Loop through edge selection, getting loop from edge, ignoring any edges that are already looped
 		if (SetSelect == true) then polyop.setEdgeSelection Obj LoopEdgeSel -- Select edges
 		return LoopEdgeSel -- Get edge loop selection
 	) else return Sel -- Get original selection
 )
 

Including your method for open loops, I just finished a function to get the edge order for closed and open loops. for closed loops, it subtracts the first vertex from the loop’s vertices, and gets the first edge from that vertex, adds that to array first, then collects the others, then adds the last edge from the first vertex.


  	fn GetLoopOrderPolyEdge Obj:$ Sel:(polyop.getEdgeSelection $) Reverse:false =
  	(
  		if (Obj != undefined and Sel != undefined and Sel.numberset >= 1) then
  		(
  			local EdgeLoopSel = #() -- Initial selection
  			local EdgeVerts = polyop.getVertsUsingEdge Obj Sel
  			if (Sel.numberset == EdgeVerts.numberset) then
  			(
  				local MiddeleVert = (EdgeVerts as array)[1] -- Get last vertex in vert selection
  				local OpenVertLoop = (EdgeVerts - #{MiddeleVert}) -- Get vert selection except the first one
  				local SideVerts = (OpenVertLoop * (polyop.getVertsUsingEdge Obj (polyop.getEdgesUsingVert Obj MiddeleVert))) as array -- Get vertices on either side of the first one
  				local StartVert = #{SideVerts[1]} -- Get first vertice next to the first one
  				local EndVert = SideVerts[SideVerts.count] -- Get last vertice next to the first one
  				local Edge0 = #{} -- First edge
  				local StartEdge = (((polyop.getEdgesUsingVert Obj MiddeleVert) * (polyop.getEdgesUsingVert Obj StartVert)) as array)[1] -- Get starting edge
  				local EndEdge = (((polyop.getEdgesUsingVert Obj MiddeleVert) * (polyop.getEdgesUsingVert Obj EndVert)) as array)[1] -- Get ending edge
  				local OpenEdgeLoop = (Sel - #{StartEdge, EndEdge}) -- Remove start and end edges
  				append EdgeLoopSel StartEdge -- Add starting edge to array
  				for i in OpenVertLoop do
  				(
  					local Edge1 = (OpenEdgeLoop * (polyop.getEdgesUsingVert Obj StartVert)) - Edge0 -- Get edge
  					if not Edge1.isEmpty then
  					(
  						for j in Edge1 do appendIfUnique EdgeLoopSel j -- Add edge to array
  						StartVert = (polyop.getVertsUsingEdge Obj Edge1) - StartVert -- Get next vertex
  						Edge0 = Edge1 -- Remove edge
  					)-- If there is an edge
  				) -- Loop through vertices
  				append EdgeLoopSel EndEdge -- Add ending edge to array
  			) else -- Closed loop
  			(
  				local SingleEdgeVerts = for i in edgeVerts where ((Sel * (polyop.getEdgesUsingVert Obj i)).numberset == 1) collect i -- Get vertices at both ends
  				local Vert1 = #{SingleEdgeVerts[1]} -- Get first vertex
  				local Edge0 = #{} -- First edge
  				for i in EdgeVerts do
  				(
  					local Edge1 = (Sel * (polyop.getEdgesUsingVert Obj Vert1)) - Edge0 -- Get edge
  					if not Edge1.isEmpty then
  					(
  						for j in Edge1 do appendIfUnique EdgeLoopSel j -- Add edge to array
  						Vert1 = (polyop.getVertsUsingEdge Obj Edge1) - Vert1 -- Get next vertex
  						Edge0 = Edge1 -- Remove edge
  					)-- If there is an edge
  				) -- Loop through vertices
  			) -- Open loop	
  			if (EdgeLoopSel.count > 0 and Reverse == true) then 
  			(
  				InvArray = #() -- Initial selection
  				for i = (EdgeLoopSel.count) to 1 by -1 do append InvArray EdgeLoopSel[i] -- Add elements to resulting array backwards
  				EdgeLoopSel = InvArray -- Get reversed array
  			) -- If reverse is true and array is not empty
  			return EdgeLoopSel -- Get edge loop selection
  		) else return Sel -- Get original selection
  	)
  

Now I can get the previous and next edge in the ordered array. For closed loops though, I’ll have to check when I’m trying to get the next edge, and the current edge is the last one, then make the next one be the first, and the previous edge will be checked with the first edge. Thanks for the help miauu.