Hi Georg,
Sorry for taking long to respond. Here is a little script that does what you want. I've tried to write a script as easy to follow and understand as possible.
#include <pjsr/NumericControl.jsh>
#include <pjsr/FrameStyle.jsh>
function MyDialog()
{
this.__base__ = Dialog;
this.__base__();
this.startingAngle = 0;
this.image = new Image;
this.abort = false;
this.busy = false;
this.terminate = false;
//
// Transformation of an image from Cartesian to polar coordinates.
// Code adapted from the PolarCoordinates example script.
//
this.cartesianToPolar = function( image )
{
// Gather working parameters
var n = image.numberOfChannels;
var w = image.width;
var h = image.height;
var w2 = 0.5*w;
var h2 = 0.5*h;
var r0 = Math.sqrt( w2*w2 + h2*h2 ); // semi-diagonal
// Total work
var N = n*h;
// Create a working image to store one channel of the target image
var tmp = new Image( w, h, 1 );
// Reset the rectangular selection to the whole image boundaries
image.resetRectSelection();
// For each channel
for ( var c = 0, status = 0; c < n; ++c )
{
tmp.fill( 0 ); // initialize working data with zeros
// For each row
for ( var i = 0; i < h; ++i, ++status )
{
// Polar angle for the current row
var theta = this.startingAngle + 360*i/h;
if ( theta >= 360 )
theta -= 360;
theta = Math.rad( theta );
// Sine and cosine of current polar angle
var stheta = Math.sin( theta );
var ctheta = Math.cos( theta );
// For each column
for ( var j = 0; j < w; ++j )
{
// Radial distance for the current column
var r = r0*j/w;
// Horizontal coordinate on the source image
var x = w2 + r*ctheta;
if ( x >= 0 && x < w )
{
// Vertical coordinate on the source image
var y = h2 - r*stheta;
// Copy the source pixel to the polar-transformed location on
// the working image.
if ( y >= 0 && y < h )
tmp.setSample( image.interpolate( x, y, c ), j, i );
}
}
// Process pending GUI events
processEvents();
// Check for abort condition
if ( this.abort )
break;
this.infoLabel.text = format( "Computing: %d%%", Math.round( 100.0*status/N ) );
}
// Check for abort condition
if ( this.abort )
break;
// Copy our working data to the channel c of image
image.selectedChannel = c;
image.apply( tmp );
}
image.resetChannelSelection();
// Destroy our working image
delete tmp;
};
//
// Asynchronous generation routine.
//
this.generate = function()
{
// If we are already generating data, request job abortion and return.
if ( this.busy )
{
this.abort = true;
return;
}
// Start of job
this.busy = true;
do
{
this.abort = false;
this.image.assign( ImageWindow.activeWindow.currentView.image );
this.cartesianToPolar( this.image );
}
while ( this.abort && !this.terminate );
// End of job
this.busy = false;
if ( !this.terminate )
{
this.infoLabel.text = "Ready.";
this.renderControl.update();
}
};
this.renderControl = new Control( this );
with ( this.renderControl )
{
setMinSize( 400, 400 );
onPaint = function()
{
var G = new Graphics( this );
if ( parent.busy )
G.eraseRect( this.boundsRect );
else
G.drawScaledBitmap( this.boundsRect, parent.image.render() );
G.end();
};
}
this.angleControl = new NumericControl( this );
with ( this.angleControl )
{
real = false;
setRange( 0, 360 );
label.text = "Starting angle:";
slider.setRange( 0, 360 );
slider.minWidth = 400;
setValue( this.startingAngle );
toolTip = "<p>Starting angle of the Cartesian to Polar transform.</p>";
onValueUpdated = function( value )
{
parent.startingAngle = value;
parent.generate();
};
}
this.infoLabel = new Label( this );
with ( this.infoLabel )
{
frameStyle = FrameStyle_Box;
margin = 4;
text = "";
}
this.sizer = new VerticalSizer;
with ( this.sizer )
{
margin = 6;
spacing = 6;
add( this.renderControl, 100 );
add( this.angleControl );
add( this.infoLabel );
}
// Ensure first-time generation
this.onGetFocus = function()
{
if ( this.image.isEmpty ) // but only for the first time
this.generate();
}
// Ensure unconditional job abortion upon dialog termination
this.onHide = function()
{
this.terminate = this.abort = true;
}
this.windowTitle = "Asynchronous PolarTransform";
this.adjustToContents();
}
MyDialog.prototype = new Dialog;
function main()
{
// Uncomment this to enable automatic garbage collection.
//jsAutoGC = true;
console.show();
var dialog = new MyDialog;
dialog.execute();
console.hide();
}
main();
The code above implements an asynchronous, fully interruptible general-purpose generation routine --just replace the call to cartesianToPolar with a compatible worker function that suits to your needs. Of course, the drawing and rendering code can be highly improved (I refer to the onPaint event handler). I've focused on the generation part, which is what you really need.
Be careful with these routines as it is easy to lead to infinite recursion if you break the internal logic. Always think in terms of the underlying time line as defined by the sequence of GUI events. In this case, it is relatively simple because it depends on a single component (the slider control). If you add more dialog controls that change generation parameters, don't forget the corresponding calls to parent.generate().
Let me know if this helps you.