[Closed] Exit loop when bitarray stops changing
I’m trying to figure out how the best method for exiting a loop after a bitarray stops changing. For example if I’m growing a selection until it fills up the mesh’s elements, I could grow to the total number of verts, edges, or faces getting the selection before you grow, and exit when the numberset of the current selection equals the numberset of the previous selection. I think using a for loop is the only way to go for this, but I’m not 100 percent sure. I was curious if anyone knows a better / faster method for this.
Here’s a function using this method, which converts an edge selection to borders in editable mesh, by growing the edge selection once, and intersecting it with all open edges. –
fn GetBordersUsingEdgeMesh Obj:$ Sel:(getEdgeSelection $) =
(
local AllBorderSel = (meshop.getOpenEdges Obj)
local BorderSel = (Sel * AllBorderSel) -- Select within borders
for i = 1 to (Obj.numFaces * 3) do
(
local OldNum = BorderSel.numberset -- Get past selection
BorderSel += ((meshop.getEdgesUsingVert Obj (meshop.getVertsUsingEdge Obj BorderSel)) * AllBorderSel) -- Grow edge selection once within border
if (BorderSel.numberset == OldNum) then exit -- Exit if selection doesn't change
) -- Convert edges to border
return BorderSel -- Get edges
)
It seems to work ok, but I’m thinking there has to be a more efficient way…
It may be worth have a While loop and omitting the ‘if’ test that currently exits the loop. You already have the variable assignment necessary, so it’d basically be (pseduocode)
while BorderSel.numberset != OldNum do
(
grow border stuff
)
This way you avoid the (according to the maxscript documentation) slow ‘exit’ process, as well as save on generating the variable ‘i’ that it appears you actually don’t need (though you could do this by simply omitting the ‘i=’ entirely – you can, I believe, simply do ‘for 1 to 10 do’ etc).
Or, if you want to stay with for loop use this:
fn GetBordersUsingEdgeMesh Obj:$ Sel:(getEdgeSelection $) =
(
local AllBorderSel = (meshop.getOpenEdges Obj)
local BorderSel = (Sel * AllBorderSel) -- Select within borders
stopLoop = false
for i = 1 to (Obj.numFaces * 3) while stopLoop == false do
(
local OldNum = BorderSel.numberset -- Get past selection
BorderSel += ((meshop.getEdgesUsingVert Obj (meshop.getVertsUsingEdge Obj BorderSel)) * AllBorderSel) -- Grow edge selection once within border
if (BorderSel.numberset == OldNum) do stopLoop = true
) -- Convert edges to border
return BorderSel -- Get edges
)
the method itself is OK but Obj:$ Sel:(getEdgeSelection $) is very bad for many reasons…
Hi Denis, can you explain why this concept in not good
I supose that this is also bad
...Obj:$ Sel:($.selectededges as array)
the idea is stop loop when edge collection stopped grow. right? but we check only size (numberset) of this collection. but there’s possible topology when growing border edges doesn’t change the number of new border edges. for example when you grow edges from pole of a sphere. got it?
Thanks Denis for the explanation and example. I don’t think there is better solution.
Is it better(fester) to use meshOp and polyOp operations outside for-loop and while-loop as variable?
fn growEdgesToBorder node edges: show:on = if iskindof node editable_mesh or iskindof node trimesh do
(
if edges == unsupplied do edges = node.selectededges as bitarray
local
ee = #{},
border = edges,
opened = meshop.getopenedges node
while not (border - ee).isEmpty do
(
ee = border
border = edges - opened
edges = meshop.getedgesusingvert node (meshop.getvertsusingedge node edges)
border = edges - border
)
if show do node.selectededges = border
border
)
any smarter solution is welcome…
it’s better to local… not faster but saves a lot of memory
i didn’t do it the sample to not confuse rookies…
so the pro version might look as
fn growEdgesToBorder node edges: show:on = if iskindof node editable_mesh or iskindof node trimesh do
(
if edges == unsupplied do edges = node.selectededges as bitarray
local
ee = #{},
border = edges,
opened = meshop.getopenedges node,
getedgesusingvert = meshop.getedgesusingvert,
getvertsusingedge = meshop.getvertsusingedge
while not (border - ee).isEmpty do
(
ee = border
border = edges - opened
edges = getedgesusingvert node (getvertsusingedge node edges)
border = edges - border
)
if show do node.selectededges = border
border
)
meshop.getedgesusingvert
meshop.getvertsusingedge
these particular functions don't really leak much, but in general it's safer to put any interface function in a local variable and call it from there.
I posted several samples on this forum which show the difference.
Wow, thanks for all the replies guys! And thanks denis for the examples.
I’ve noticed the assignment of operations to variables in the reference before, and I can definitely see the performance gain with that. I also didn’t think about cases where your selection is outside of open borders, which would lead to an empty selection that wouldn’t grow. I still think though that cases where the edge selection is on a pole would still grow, I don’t see why it wouldn’t.
One thing I don’t understand is why using default values for node and the edge selection are bad, that’s what I’ve used for all of my functions and their inputs, is default values, so I don’t always need to specify them. Is specifying the default value in the function a lot safer?
Again, thanks for your help everyone.
look at what you have:
fn GetBordersUsingEdgeMesh Obj:$ Sel:(getEdgeSelection $)
i said there are reasons that make it bad… check yourself:
what if noting selected
what if multiple selection
what if not an editable mesh selected
of course you can use default values for additional arguments and do it, but be sure that these values don’t break the function before it goes to execution.
if you are absolutely sure that the function can never be called with wrong values where is a reason to give it defaults. does it make sense?
Well I usually check for if an object is selected and if there are subobjects selected, I forgot to put the checks in this function. Although I should put in a check for multiple node selections.I haven’t worried about checking the class of the the node in most of my functions, because I plan on checking that later, when I call the functions, depending on what the user has selected. That’s also why I put Mesh at the end of the function name, so I know what its used for.
Having defaults in the functions just seems more flexible to me. Especially for functions that have a lot of parameters, and can do many different things. Its nice to not have to specify all of the parameters all the time.