MoI discussion forum
MoI discussion forum

Full Version: how to terminate a script

From: pressure (PEER)
12 Sep 2022   [#1]
Hi,

Is there a way to terminate a script that's stuck in an infinite loop? If I run something dumb like
code:
while ( true ){}

hitting the Esc key fails to terminate the script. Instead, I have to terminate MoI itself.

What does the Esc key actually do? Say I set a script to repeat by ticking the repeat checkbox in the properties panel, but the script throws an alert. If I hit Esc, that seems to be the same as clicking ok on the alert modal, rather than terminating the script. And so it goes in a loop and I have to terminate MoI. IsoAtPoints on repeat is a good example.

A mirror image of the IsoAtPoints situation happens when setting a CPlane interactively from within a script. If I hit Esc while
code:
moi.View.setCPLaneInteractive();

is running, that seems to be the same as pressing the Cancel button in the Properties Panel. That sort of makes sense, but the problem is that the rest of the script runs. How can I terminate the script rather than just canceling the CPlane tool? Or, is there some way to suppress the Cancel button when calling the CPlane tool from a script?

Is there a standalone interactive Orientation Picker I can call that doesn't have the cancel button and that sets a coordinateFrame, but not a CPlane?

Additional weirdness happens if I wrap the CPlane tool in a loop:
code:
var box;
while ( true ){

	// user picks orientation of new CPlane
	moi.View.setCPLaneInteractive();
	
	// get bounding box of selected object in frame set by user
	box = moi.ui.PropertiesPanel.highAccuracyBoundingBox;
	
	var tol = 0.0001; //numerical tolerance
	
	// check the just-set CPlane to see if it has the correct orientation
	// relative to the selected object
	if ( compareNumbers( box.zLength, 0, tol ) != 0 ){
	
		// restore original CPlane in case user terminates script when error thrown
		moi.view.setCplane(activeCPlane);
		
		moi.ui.alert( "CPlane XY must coincide with face" );
	} else {
		break;
	}
}


My intent here was to re-prompt the user if the orientation of the cplane that they set with the orientation picker isn't right. But, if I hit Esc while the CPlane tool is active the result is an infinite loop of the alert getting thrown. In other words, it looks like the while loop keeps going after hitting Esc, but somehow misses the setCPlane line. What am I doing wrong?
From: Michael Gibson
12 Sep 2022   [#2] In reply to [#1]
Hi Peer, if you press escape that will cancel a script that has spent more than 3 seconds running since the last time it waited for an event.

But the canceling will work by causing all MoI API function calls to return an exception. So if you have a tight loop that does not call any MoI functions like your while ( true ){} example that won't get interrupted. So don't do something like that.


> What does the Esc key actually do?

It tries to do various exiting or clearing type functions.

If a script has been running for more than 3 seconds since the last event wait Esc will cancel the script by triggering an exception for any MoI API function call.

For more normal usage, if there is a dialog window that has keyboard focus or if there is a flyout menu open then Esc will close that window.

If a dialog or flyout was not closed then the next thing it tries is if there is any selection filter active it will clear that.

Then the next thing is if there wasn't any selection filter cleared it will cancel any currently running command, by making the .waitForEvent() function return false. When a script sees .waitForEvent() returning false it should exit.

If a command was not canceled then the next thing it tries is to clear all selection.

If there was no selection cleared then it will turn off points.


> How can I terminate the script rather than just canceling the CPlane tool?

There isn't currently any way for a script to know if moi.View.setCPLaneInteractive(); was cancelled or not but I will see about fixing that up and making it return false if canceled.


> Is there a standalone interactive Orientation Picker I can call that doesn't have the cancel
> button and that sets a coordinateFrame, but not a CPlane?

Yes, there is the general orientation picker you can call that is not directly wired into setting the cplane.

There's a wrapper for it in GetOrientation.js . It would go something like this (warning untested code):

var picker = moi.ui.createOrientationPicker();
if ( !GetOrientation( picker, 'BasePrompt1', 'BasePrompt2' ) )
return;

It's used in the Orient.js command. it has a .frame property to get the picked frame from it after it has finished.

- Michael
From: pressure (PEER)
13 Sep 2022   [#3] In reply to [#2]
Michael,

Thank you for the extensive description of what the Esc key does and for pointing me in the direction of createOrientationPicker(). I'll give that a try.

Having methods with a cancel button return false if canceled sounds good.

I'm still not clear on a couple of things.

First, how can I avoid the infinite loop that results if a script is set to repeat, but the script throws an alert and then exits as soon as the alert button is clicked? I ran into this with IsoAtPoints http://moi3d.com/forum/lmessages.php?webtag=MOI&msg=7978.6 and had to force quit MoI while I had unsaved work on a project that I cared about. Now I'm leary of ticking the Repeat checkbox for any script, even though it would be handy to use repeat sometimes. Here's what happens:



Second, how can I safely re-prompt a user for input? If I run the script below, set the CPlane at the wrong orientation, and then hit Esc on the next iteration through the While loop it goes into an infite loop. It's like the line moi.View.setCPLaneInteractive(); is getting skipped over, but the While loop is still running.



code:
var box;
while ( true ){

	// user picks orientation of new CPlane
	moi.View.setCPLaneInteractive();
	
	// get bounding box of selected object in frame set by user
	box = moi.ui.PropertiesPanel.highAccuracyBoundingBox;
	
	var tol = 0.0001; //numerical tolerance
	
	// check the just-set CPlane to see if it has the correct orientation
	// relative to the selected object
	if ( compareNumbers( box.zLength, 0, tol ) != 0 ){t
		
		moi.ui.alert( "CPlane XY must coincide with face" );
	} else {
		break;
	}
}

// Returns 1 if a > b, 0 if a = b, and -1 if a < b within some positive tolerance, t  
function compareNumbers(a, b, t) {
    var out = undefined;

    var dif = a - b;

    // a > b
    if (dif > 0 && Math.abs(dif) > t) {

        out = 1;

    }

    // a = b
    if (Math.abs(dif) < t) {

        out = 0;

    }

    // a < b
    if (dif < 0 && Math.abs(dif) > t) {

        out = -1;

    }

    return out;

}

Image Attachments:
reprompt_infinite_loop.gif  script_repeat_infinite_loop.gif 


From: Michael Gibson
13 Sep 2022   [#4] In reply to [#3]
Hi Peer,

re:
> First, how can I avoid the infinite loop that results if a script is set to repeat, but the script throws
> an alert and then exits as soon as the alert button is clicked?

That's a bug in that script, it should disable the repeat checkbox which is done by setting:
// config: norepeat
at the top of the .js script file. I've updated the script to have that now so you shouldn't run into this with the updated version.

The repeat checkbox is primarily meant for use with drawing commands and not ones that do selection.

You can still repeat a command that does selection by right clicking in the viewport or pushing the Enter key while in selection mode. If you only need to repeat things sometimes it's easier to use to use right click to do it, the repeat checkbox is for stuff like you're going to draw a whole bunch of points or lines.


> and had to force quit MoI while I had unsaved work on a project that I cared about. Now I'm leary
> of ticking the Repeat checkbox for any script, even though it would be handy to use repeat sometimes.

Sorry you ran into this data loss. If you run into this kind of loop again you can recover your file by triggering a crash.
To do that find the pid for the MoI process in Activity Monitor and from a Terminal prompt do:

kill -s SIGSEGV pid_number_here

That should cause MoI to trigger the crash dialog where you can save your file.


> Second, how can I safely re-prompt a user for input?

I'll take a look at this part in a bit.

- Michael
From: Michael Gibson
13 Sep 2022   [#5] In reply to [#3]
Hi Peer,

re:
> Second, how can I safely re-prompt a user for input? If I run the script below, set the
> CPlane at the wrong orientation, and then hit Esc on the next iteration through the
> While loop it goes into an infite loop. It's like the line moi.View.setCPLaneInteractive();
> is getting skipped over, but the While loop is still running.

What's happening is when you hit Esc or the Cancel button it's trying to cancel the current running command. This sets a "pending cancel command" state.

When moi.view.setCPlaneInteractive() goes into its internal event loop it sees that there is a pending cancel for the current command, and it immediately exits but there isn't a way currently for the script to know about that and so the script continues its while loop. With the next beta where moi.view.setCPlaneInteractive() will return false if it was canceled you can then update this script to have:

code:
	// user picks orientation of new CPlane
	if ( !moi.View.setCPLaneInteractive() )
		break;


If you use the orientation picker directly instead of moi.view.setCPlaneInteractive() you'll have your own event loop and you should be able to see a cancel when picker.waitForEvent() returns false. On the orientation picker you can also call picker.allowNestedCancel() which will then cause Esc or the Cancel button to cancel just that picker's wait instead of doing a full command cancel which is the default behavior.

I'll see about adding in an optional boolean to moi.view.setCPLaneInteractive() so you can set it to do a nested cancel as well.

- Michael
From: pressure (PEER)
14 Sep 2022   [#6] In reply to [#5]
Michael,

Thanks for explaining "// config: norepeat". I saw that at the top of a few scripts and wondered what it was about. What does this do?
// config: norepeat noautolaunch

I guess "// config: norepeat" should be at the top of every script that can create an alert triggered by a problem encountered by the script.

I'll keep your segmentation violation tip in mind in case I'm ever in that pickle again, but hopefully I'll be able to avoid it with everything else you've shared.

I haven't done anything with pickers or event loops yet, but I'll try out your suggestions.
From: Michael Gibson
14 Sep 2022   [#7] In reply to [#6]
Hi Peer,

> What does this do?
> // config: norepeat noautolaunch

The no autolaunch is for when a command set is launched. A "command set" is a group of commands like Draw curve > Lines is a command set that has 2 commands in it (Line, Polyline).

When you open a command set (like click on Draw curve > Lines), the last used command will automatically start unless it's marked with "noautolaunch" which should be set on a command that just does its action immediately like Boolean Union for example.


I think I'll set it up so that if an alert dialog is launched it will automatically clear out the Repeat checkbox to help avoid getting stuck like this.

Thanks,
- Michael
From: pressure (PEER)
14 Sep 2022   [#8] In reply to [#7]
Michael,

Thanks for explaining noautolaunch. I looked at BooleanUnion and Lines.xml and see what you mean. It didn't occur to me that a command set is a thing. Also, it's nice to know that I can repeat a command by hitting Enter. I only knew about right click.

I like your idea of clearing the Repeat checkbox.

Message 10847.9 was deleted