Start a new topic

Delete Empty Nodes

Original Post by: Wed Dec 3 19:04:52 2014


How to delete all empty nodes in "C" ?


Original Post by: SteveThompson Wed Dec 3 20:43:09 2014


Can this be in a loop (i’m not that familiar with loops )

Probably not because you're calling mgWalk and this callback is being called inside a loop already inside of mgWalk. You typically DO NOT loop inside mgWalk callbacks because mgWalk is already looping for you for each child. That is not a rule, but generally the case. For example it is very common to call mgWalk telling it to not visit vertices. Then when you hit a face node, loop over the vertices in the face. But in your case, you don't want to loop over the children of the node being visited because you're callback is going to be called for each of those children already!


Can this be simplified as well?


Perhaps a bit but I don't want to inflict my style on you... you have to do what you're comfortable with. One thing you could do to make your code a bit more compact is to combine the outer if statements like this:

mgcode nodeType = mgGetCode(rec);

if (code==fltGroup ||

code == fltObject ||

code == fltLod)

{

mgrec* nodes = mgGetChild(rec);

if (nodes == MG_NULL)

{

mgDelete (nodes);

}

}


Now I have to hit the button kept for deleting each time to delete.

I suspect this is because you are doing this in the "preaction" callback. In general you should not change the hierarchy in your walk callbacks. Remember that mgWalk is traversing your hierarchy. If you change the hierarchy while mgWalk is doing its thing, you could really mess it up. If you delete the node that will definitely stop mgWalk from continuing because it can't get the "next" node of the node you just deleted (it's gone!!).


Furthermore, way back in 4.X, mgWalk was really NOT tolerant of the hierarchy changing during either of the walk callbacks (preaction or postaction). We made it much more tolerant in v5.0. That being the case, I would suggest that you CANNOT delete the nodes in either of your walk callbacks (so moving this code to the postaction won't work either until you upgrade to v5.0 or newer). That said you may need to devise another strategy to delete empty nodes using mgWalk. One strategy would be to add a "list of nodes" to the userData you pass to mgWalk. Then inside the walk callback, instead of deleting the nodes immediately, add the nodes you want to delete to the list. Then upon return from mgWalk, you simply loop over the nodes in the list you collected and delete them at that time.


One final note (and this is really a style thing, not something you are doing wrong)...


I noticed another part of your code that could be improved. It is not necessarily wrong but it is not as readable as it could be...


Here is the code:


mgrec* nodes = mgGetChild(rec);

if (nodes == 0)

{

mgDelete(rec);

}


Here you are assigning the value returned by mgGetChild to a variable of type mgrec* (which is a pointer type) called "nodes". mgGetChild returns a pointer to the first child of rec so you are calling this function correctly. But then (in the following "if" statement) you compare "nodes" to the literal value "0". It is much more readable if you compare the pointer type mgrec* variable to the predefined symbol "MG_NULL" as I have done in the code snippet I wrote above.


Again, using "0" is not technically wrong but can lead the casual observer to think that "nodes" is an integer type that might be "1" or "2" etc. This is just dangerous from a maintenance POV. Much more readable to write:


mgrec* nodes = mgGetChild(rec);

if (nodes == MG_NULL)

{

mgDelete(rec);

}

Original Post by: Wed Dec 3 20:25:03 2014


I just learned you are using Creator 4.1, so the following may not be of much use. I am leaving this here in case someone else with CR 13/14 is looking at this thread for some information.


With Creator 13 you can also use Creator script:


mgExecute ("Select Empty Nodes", None)

mgExecute ("Delete", None)


I apologize if I confused you.


I can see things are much simplified in CR 13/14, which is a great helping approach, and no you are not at all confusing me infact I am acquiring a bit of knowledge by this.

Original Post by: Wed Dec 3 20:22:15 2014


As Steve mentioned I rearranged mgWalk and added few more. Now this will delete once empty node level. Can this be in a loop (i'm not that familiar with loops )

Can this be simplified as well?

Now I have to hit the button kept for deleting each time to delete.


static mgbool emptyApply (mgrec* db, mgrec* parent, mgrec* rec, void* emptyB)

{

toolrec* toolRec = (toolrec*)emptyB;

if (mgGetCode(rec)==fltGroup)

{

mgrec* nodes = mgGetChild(rec);

if (nodes == 0)

{

mgDelete(rec);

}

}

else if (mgGetCode(rec)==fltObject)

{

mgrec* nodes = mgGetChild(rec);

if (nodes == 0)

{

mgDelete(rec);

}

}

else if (mgGetCode(rec)==fltLod)

{

mgrec* nodes = mgGetChild(rec);

if (nodes == 0)

{

mgDelete(rec);

}

}


return MG_TRUE;

Original Post by: Lars Wed Dec 3 20:06:32 2014


I just learned you are using Creator 4.1, so the following may not be of much use. I am leaving this here in case someone else with CR 13/14 is looking at this thread for some information.


With Creator 13 you can also use Creator script:


mgExecute ("Select Empty Nodes", None)

mgExecute ("Delete", None)


I apologize if I confused you.

Original Post by: SteveThompson Wed Dec 3 20:00:54 2014


This code is only deleting empty if the node is a fltHeader (db root node). I don't think you meant that.


There are other suspicious elements of this code:


1) you are passing "nodes" as a parameter to the function and then assigning it inside the function. I doubt you really mean to do that. You probably want to declare "nodes" as a local variable in the function and remove it from the parameter list. I could be wrong but this is suspect.


2) If this is a "walk" callback the function parameters are wrong. The order of the parameters MUST be

static mgbool WalkCB (mgrec* db, mgrec* parent, mgrec* rec, void* userData)

{

...

}


These are the parameters that mgWalk will call your function with. You absolutely cannot reorder them or your code will be very wrong!


If this is a walk callback you are treating the 2nd parameter as the record being visited but the 2nd parameter is the parent of the record being visited. The third parameter passed is the record being visited.


In conclusion... first if this is a walk callback, fix the parameters and rewrite the code. And then once you rewrite it, decide whether you only want to delete empty "header" records (which of course you don't).

Original Post by: Wed Dec 3 19:52:30 2014


Hi,


I want something like this. Below is the one i wrote i know this wont work but just to give an idea i wrote it. Can somebody please correct it to delete all nodes which are empty.


static mgbool emptyApply (mgrec* db, mgrec* rec,mgrec* nodes, void* emptyB)

{

toolrec* toolRec = (toolrec*)emptyB;

if (mgGetCode(rec)==fltHeader)

{

nodes = mgGetChild(rec);

if (nodes == 0)

{

mgDelete(rec);

}

}


return MG_TRUE;

}

Original Post by: SteveThompson Wed Dec 3 19:42:19 2014


To add to Chris' response...


As he suggests, it is true that an empty node does not have children. But it is not necessarily true that a node with no children is empty. A node could have "nested" children so you need to check for that in addition to regular children.


To count "nested" children, use mgCountNestedChild. To sum this up, a node is empty if both mgCountNestedChild and mgCountChild return 0.

Original Post by: ChrisRogers Wed Dec 3 19:30:37 2014


Select->Select Empty ->Nodes


If you are running Creator, then that is a great tool to do what you want. Also if you have 4.2 or greater, and you are writing a script or plugin to do so, then you can use mgExecute to run the Select Empty Nodes tool. However, if you are trying to do so in standalone code or with an api before 4.2, your only option is to find empty nodes yourself and delete them.


mgCountChild() will tell you how many children a node has

mgDelete() will delete a node.

mgGetCode () will tell you what the type of a node is (so you can delete only one type of empty node...)


Be very careful though, if you are deleting nodes in mgWalk. You shouldn't delete a container node in the pre walk function. It is however safe to delete nodes in the post walk function.

Original Post by: Wed Dec 3 19:26:05 2014


You have, via the Select menu, several selection options. One of them is what you are looking for.


Select->Select Empty ->Nodes selects all nodes (of any type except vertex) that do not have children. If one or more nodes are selected before you invoke this command, only nodes below the selected nodes will be searched. Otherwise all nodes in the database will be searched


What i meant is through Api how can i do that.

Original Post by: Lars Wed Dec 3 19:24:06 2014


You have, via the Select menu, several selection options. One of them is what you are looking for.


Select->Select Empty ->Nodes selects all nodes (of any type except vertex) that do not have children. If one or more nodes are selected before you invoke this command, only nodes below the selected nodes will be searched. Otherwise all nodes in the database will be searched

Login to post a comment