In a
recent post on the Release Information forum board, I have described the new approximating surface splines implemented in the StarAlignment tool. To generate the 3-D graphs included in that post, I wrote a small script that automates execution of the
gnuplot utility. I think this script can be interesting as an example for PI/PJSR developers, since it covers several important topics:
- Running external processes with the ExternalProcess core JavaScript object.
- Generation of a plain text file with tabular data and a script to control an external process.
- Using the integrated
gnuplot utility, which is part of the standard PixInsight distribution on all platforms.
- Using the new SurfaceSpline core JavaScript object.
To test the script with the same data I used in my post, you can load this image:
http://forum-images.pixinsight.com/20131207/SA/spline-test.tifand just run the script from the Script Editor. As you see, thanks to the integrated gnuplot utility PixInsight has really impressive graph generation capabilities.
/*
* A script for testing the SurfaceSpline PJSR object.
*
* Copyright (C) 2013 Pleiades Astrophoto. All Rights Reserved.
* Written by Juan Conejero (PTeam)
*/
#include <pjsr/ProcessExitStatus.jsh>
/*
* Routine to execute an external process, including command line arguments.
*
* program Executable file name. Can be followed by a series of
* command line arguments separated by spaces.
*
* maxRunningTimeSec Maximum execution time in seconds (default=10)
*
* This routine shows a spin status indicator if the process takes more than
* 0.25 seconds to complete.
*/
function run( program, maxRunningTimeSec )
{
if ( maxRunningTimeSec === undefined )
maxRunningTimeSec = 10;
var P = new ExternalProcess( program );
if ( P.waitForStarted() )
{
processEvents();
var n = 0;
var nmax = Math.round( maxRunningTimeSec*1000/250 );
for ( ; n < nmax && !P.waitForFinished( 250 ); ++n )
{
console.write( "<end>\b" + "-/|\\".charAt( n%4 ) );
processEvents();
}
if ( n > 0 )
console.writeln( "<end>\b" );
}
if ( P.exitStatus == ProcessExitStatus_Crash || P.exitCode != 0 )
{
var e = P.stderr;
throw new ParseError( "Process failed:\n" + program +
((e.length > 0) ? "\n" + e : ""), tokens, index );
}
}
/*
* A SurfaceSpline test routine with SVG 3-D graphics generation using the
* integrated gnuplot utility.
*/
function Spline3DTest( image, gridStep )
{
if ( gridStep === undefined )
gridStep = 8;
var xGridSize = Math.trunc( image.width/gridStep );
var yGridSize = Math.trunc( image.height/gridStep );
var N = xGridSize*yGridSize;
if ( N < 9 )
throw new Error( "I won't work with less than 9 data points." );
if ( N > 2000 )
throw new Error( "I won't work with more than 2000 data points." );
/*
* Interpolate data points
*/
var X = new Vector( N );
var Y = new Vector( N );
var Z = new Vector( N );
for ( var x = 0, n = 0; x < xGridSize; ++x )
{
for ( var y = 0; y < yGridSize; ++y, ++n )
{
var xn = x*gridStep;
var yn = y*gridStep;
X.at( n, xn );
Y.at( n, yn );
Z.at( n, image.interpolate( xn, yn ) );
}
}
/*
* Build an interpolating 2-D surface spline.
*/
var S0 = new SurfaceSpline;
S0.initialize( X, Y, Z );
/*
* Build some approximating 2-D surface splines.
*/
var S1 = new SurfaceSpline;
S1.smoothing = 0.05;
S1.initialize( X, Y, Z );
var S2 = new SurfaceSpline;
S2.smoothing = 0.15;
S2.initialize( X, Y, Z );
var S3 = new SurfaceSpline;
S3.smoothing = 0.35;
S3.initialize( X, Y, Z );
var tmpDir = File.systemTempDirectory;
var datFilepath = tmpDir + "/spline.dat";
var gnuFilePath = tmpDir + "/spline.gnu";
var svgFilePath = [
tmpDir + "/spline.svg",
tmpDir + "/spline-smoothing-0.05.svg",
tmpDir + "/spline-smoothing-0.15.svg",
tmpDir + "/spline-smoothing-0.35.svg"
];
/*
* Generate the data table
*/
var f = new File;
f.createForWriting( datFilepath );
for ( var i = 0; i < X.length; ++i )
f.outTextLn( format( "%5d %5d %.4f %.4f %.4f %.4f %.4f",
X.at( i ), Y.at( i ), Z.at( i ),
S0.evaluate( X.at( i ), Y.at( i ) ),
S1.evaluate( X.at( i ), Y.at( i ) ),
S2.evaluate( X.at( i ), Y.at( i ) ),
S3.evaluate( X.at( i ), Y.at( i ) ) ) );
f.close();
/*
* Generate the gnuplot script
*/
f.createForWriting( gnuFilePath );
f.outTextLn( "set terminal svg size 800,600 enhanced font 'helvetica,12'" );
f.outTextLn( "set samples " + xGridSize.toString() + ", " + yGridSize.toString() );
f.outTextLn( "set isosamples " + xGridSize.toString() + ", " + yGridSize.toString() );
f.outTextLn( "set ticslevel 0" );
f.outTextLn( "set dgrid3d " + xGridSize.toString() + ", " + yGridSize.toString() );
f.outTextLn( "set hidden3d" );
f.outTextLn( "set xrange [ 0 : " + image.width.toString() + " ]" );
f.outTextLn( "set yrange [ 0 : " + image.height.toString() + " ]" );
f.outTextLn( "set zrange [ 0.0 : 1.0 ]" );
f.outTextLn( "set ytics offset 1" );
f.outTextLn( "unset key" );
f.outTextLn( "set title 'Interpolating 2-D Surface Spline' font 'helvetica,16'" );
f.outTextLn( "set output '" + svgFilePath[0] + "'" );
f.outTextLn( "splot '" + datFilepath + "' using 1:2:4 with lines lc rgbcolor '#E00000'" );
f.outTextLn( "set title 'Approximating 2-D Surface Spline, s = 0.05' font 'helvetica,16'" );
f.outTextLn( "set output '" + svgFilePath[1] + "'" );
f.outTextLn( "splot '" + datFilepath + "' using 1:2:5 with lines lc rgbcolor '#E00000'" );
f.outTextLn( "set title 'Approximating 2-D Surface Spline, s = 0.15' font 'helvetica,16'" );
f.outTextLn( "set output '" + svgFilePath[2] + "'" );
f.outTextLn( "splot '" + datFilepath + "' using 1:2:6 with lines lc rgbcolor '#E00000'" );
f.outTextLn( "set title 'Approximating 2-D Surface Spline, s = 0.35' font 'helvetica,16'" );
f.outTextLn( "set output '" + svgFilePath[3] + "'" );
f.outTextLn( "splot '" + datFilepath + "' using 1:2:7 with lines lc rgbcolor '#E00000'" );
f.close();
/*
* Run gnuplot
*/
run( "\"" + getEnvironmentVariable( "PXI_BINDIR" ) + "/gnuplot\" \"" + gnuFilePath + "\"" );
/*
* Load the images
*/
for ( var i = 0; i < svgFilePath.length; ++i )
ImageWindow.open( svgFilePath[i] )[0].show();
}
var view = ImageWindow.activeWindow.currentView;
if ( view.isNull )
throw new Error( "No active image." );
Spline3DTest( view.image, 6 );