Programming Tips

Program and Operating System Names

You may find it necessary to have a GSL script know the name of the executable that is running. For example, a script may contain different logic depending on whether the executable is Gsharp or GsharpWE (the Gsharp Web Edition).

The name of the executable can always be found in the first element of the argv array. Any command line options passed to the executable will be found in subsequent elements. For example, if Gsharp is started with the command:

>Gsharp ­edit ­logo ­data mydata.dat mytemplate.gsl 

then the file mytemplate.gsl will be executed by Gsharp, and the string array variable WORK.argv will contain the elements:

Gsharp 
­edit 
­logo 
­data 
mydata.dat 
mytemplate.gsl 

The command line parameter ­data is not predefined by Gsharp and will be stored in argv, together with the value mydata.dat, without any additional action occurring. The GSL program can act on this command line information as needed.

The version number of Gsharp is returned by the function version, which returns a string like "3.1", etc. The function appseat returns true (1) if a Gsharp application is running, otherwise false (0) is returned. The function os_type returns the type of  the operating system.

Example:

printf("%s %s running on %s\n",argv[1],version(),os_type()); 
>Gsharp 3.1 running on Windows 

Environment Variables

GSL programs can retrieve the value of environment variables using the function:

<value> = getenv(<variable>) 

variable is a string with the name of an environment variable, value is a returned string with its setting. If the environment variable does not exist, undef is returned. If you are writing a GSL program which will function as a graph template on a web server, you can use getenv to retrieve common gateway interface variables, as shown by this statement from the example program $UNIDIR/example/GsharpWE/contour/gs_contour.cgi:

XuNtext = getenv("REMOTE_HOST") + ", Date: " + today 

Environment variables can be set from GSL using the function putenv. For example:

putenv("mydir=/users/bob/"); 

Executing System Commands

You can execute an operating system command from within a GSL program using the function:

system(command) 

where command is a string set to an operative system command. There is no return value from this function, however you can still retrieve information from the command by directing output to a file and importing it. Here is an example of a function to perform the DOS dir command and return the directory listing:

/*************************************************************** 
* Function: XuUNIXFileList 
* Purpose: find files matching the spec 
***************************************************************/ 
function string XuUNIXFileList(string spec) 
  string files, tmpfile;
  tmpfile=tmpnam()+".dat"; 
  system("dir "+spec+" > "+tmpfile); 
  files = XuReadTextFile(tmpfile); 
  return mask(files,files<>"/dev/null"); 
endfunction 

The code for this example, plus the UNIX and VMS equivalents, are found in the GSL library $UNIDIR/lib/libxu.gsl. A system independent version, XuFileList, is also provided and is the recommended way to access these functions.

Programming Colors

The colors displayed in Gsharp graphs can be completely controlled by the user. Colors can be accessed on three levels:

color definition
A set of RGB (red­green­blue) values, ranging from 0 to 100, that define a single color.

color table
A collection of n color definitions. Colors are set by indexing from 0 to n­1 into the color table. Gsharp's color table can hold 255 color definitions. The first 88 hold default colors, the remainder are empty.

shading scale
A smooth color spectrum interpolated from fixed color definitions spaced at specified distances from each other.

Programming a Color Table Entry

Color definitions are set interactively using the Color Editor which can be popped up using the Edit->Colors command or the Colors icon. Colors are programmed from GSL by defining colors in the general color table. This is done by setting the resource:

colortables.GeneralColorTable.XuNrgbValues[index+1] = (<red>,
<green>, <blue>) 

Where:

index + 1 is the index into the color table. Object color resources are set using index which begins with 0, but colors are programmed beginning with index + 1 since Gsharp array indexing begins with 1. <red>,<green>,<blue> are each a number between 0 and 100 specifying the level of red, green, and blue respectively.

For example:

# add color "papaya whip" to color table 
myColsBegin = sizex(colortables.GeneralColorTable.XuNrgbValues); 
colortables.GeneralColorTable.XuNrgbValues[myColsBegin+1] = 
(100,93.73,83.53); 
#set a note color to the new color 
.note.XuNcolor = myColsBegin; 

Programming a New Shading Scale

Gsharp has 6 predefined shading scales, and four user­defined shading scales which are empty until programmed by the user. Shading scales are defined by setting fixed color points at specific distances on the scale. The remaining scale colors are interpolated from the fixed colors. Shading scale color points are defined as:

(<distance>, <red>, <green>, <blue>) 

For each color point, an additional value in the range of 0 to 100 specifying the relative distance to the next color point is needed. For thte last color point, the relative distance value is ignored. A shading scale is programmed by setting the fixed color points. For example:

# Temperature scale 
set shades 
( XuNuser3Scale = 
(50,0,90,100)// 
(25,100,0,0)// 
(20,100,100,0)// 
(5,100,100,100)// 
(0,100,100,100) 
); 

The shading scale definition contains only the fixed color points, not the interpolated colors between the fixed points. The number of colors used by a shading scale is dynamically determined from the Domain resource XuNnumValues (Number of Values).

Programming a Legend Class Color

When a shading scale is selected for the domain of a graph type with an XuNcolorData (Color Data) or XuNcolorDataGrid (Color Data Grid) resource, a domain color table is created from the shading scale and contains XuNnumValues + 1 color entries interpolated from the fixed color points. Data classes set in the domain determine how data values are mapped to a particular color index in the domain color table. Since each of the color definitions in the domain color table can be queried and programmed independently, you could highlight a particular class level by reprogramming its color to stand out from the interpolated colors. For example:

obj = "page_1.viewport_1.domain_1"; 
legClass = 6; 
# set a legend class color to hot pink 
$obj.XuNrgbValues[legClass] = (100, 41.18, 70.58) 

Since domain color tables are interpolated, the original shading scale fixed color points may not exist in the domain color table exactly as defined. Depending on XuNnumValues (Number of Values), the original color points may get color shifted slightly. The following function can find the closest matching color in a color table:

Example: libxu.gsl, function XuClosestColor

/*************************************************************** 
* Function to get closest matching color in colorTable 
***************************************************************/ 
function float XuClosestColor(float color, float colorTable) 
  float distance, minDistance, index, i; 
  for i = 1 to sizex(colorTable) 
    distance = sqrt(abs(color[1,1] ­ colorTable[i,1])**2 + 
      abs(color[1,2] ­ colorTable[i,2])**2 + 
      abs(color[1,3] ­ colorTable[i,3])**2); 
    if distance < minDistance or i = 1 then 
      minDistance = distance; 
      index = i; 
    endif 
  endfor 
  return index; 
endfunction 

This function is used in the example $UNIDIR/example/Gsharp/userguide/gsl_color.gsl to program selected scale colors, as used in the legend, in the general color table so that they can be used as note colors. Here a code segment showing the color handling in this example:

Example: gsl_color.gsl ­ color programming

... 
for i = 1 to sizex(shades.XuNuser4Scale) 
  # get shade rgb values, ignore distance value 
  rgb = slice(shades.XuNuser4Scale,i, 2:4,1); 
  # find the closest interpolated color in domain color table 
  legClass =
    XuClosestColor( rgb, 
                    slicex($domPtr.XuNrgbValues,
                           1:$domPtr.XuNnumValues+1)
                  ); 
    # program color in general color table 
    set colortables.GeneralColorTable 
      ( XuNrgbValues[myColsBegin+i] = 
          $domPtr.XuNrgbValues[legClass]  
      ); 
    ... 

Using Object Bounding Polygons

As you begin developing scripts which are more dynamic in the way they lay out the graph, you may find it useful to get the coordinates of graphics objects. These coordinates, called bounding polygons, can be used to position annotation, legends, or any graphics object relative to an existing object.

You can retrieve the bounding polygon of any object or object component with the function bounding_polygon. In its simplest form this function is called as follows:

npoly = bounding_polygon(string object, float nvert, float xpts,
                         float ypts) 

Where:
npoly is a float number of polygons returned.
object is a string pointer to an existing graphics object,.
nvert is a float array of size npoly containing the number of vertices for each polygon.
xpts is a float array of size npoly containing the X coordinates of each polygon.
ypts is a float array of size npoly containing the Y coordinates of each polygon.

The points returned are millimeter coordinates from the canvas (page) object origin. The coordinates that are returned are absolute, meaning that they are not scaled if the page resource XuNdisplayMode (Display Mode) is relative. If you are retrieving the bounding polygons from a relatively scaled page, you will need to call the $UNIDIR/lib/libxu.gsl function XuRelativeMM to scale the coordinates to relative millimeters. If you use bounding polygon coordinates to position a graphics object, remember to set the position size unit set to mm.

A simple use of bounding_polygon is appending text to an existing Note object. An additional note can be appended as shown in the following example.

Example: gsl_polygon1.gsl ­ use bounding_polygons to append text

... 
create Note .note_1 
  ( XuNtext = "appended", 
    XuNposition = (50,50), 
    XuNcolor = 2, 
    XuNbox = true 
  ); 
recalc; 
npoly = bounding_polygon(".note_1", nvert, xpts, ypts); 
XuRelativeMM(xpts,ypts); 
create Note .note_2 
  ( XuNtext = " text", 
    XuNposition = (max(xpts), min(ypts)) mm, 
    XuNhorizontalJustification = "left", 
    XuNverticalJustification = "fontBottom", 
    XuNbox = true 
  ); 
... 

After the first note is created, the recalc statement is used. This command causes the graphics attributes of the note to be calculated without the note actually being drawn. repaint will also do this, but at this point in the script it is not desirable to see the partial result. After the bounding polygons of the note are retrieved they are converted to relative millimeters, since the A4 size page object is scaled down relative to the display window.

Notice that the justification of the appended text, note_2, is set to "left" and "fontBottom". This is necessary to align the appended text with the first note. Also notice that the space between words was placed in front of the second note text.

Gsharp will remove trailing spaces from text, therefore the space could not be added at the end of the first note.

In the example gsl_polygon1.gsl, only one polygon was returned for the outline of the note. Depending on the object, multiple polygons each with a different number of vertices could be returned, as for example, for each sector of a pie graph. The following code fragment shows how the npoly and nvert information is used to index variable sized polygons.

Example: gsl_polygon2.gsl ­ variable size bounding_polygons

/* 
* get the exploded sector of a pie graph 
*/ 
npoly = bounding_polygon(".graph_1", nvert, xpts, ypts); 
if sector > 1 offset = sum(slicex(nvert,1:sector­1)); 
xpoly = slicex(xpts,offset+1:offset+nvert[sector]); 
ypoly = slicex(ypts,offset+1:offset+nv[sector]); 
/* 
* get exploded sector of pie graph 
*/ 
sector = sizex(mydata) ­ sector + 1; #polygons in reverse order 
npoly = bounding_polygon(".graph_1", nv, xpts, ypts); 
XuRelativeMM(xpts,ypts); 
if sector > 1 offset = sum(slicex(nv,1:sector­1)); 
xpoly = slicex(xpts,offset+1:offset+nv[sector]); 
ypoly = slicex(ypts,offset+1:offset+nv[sector]); 

Notice the statement:

if sector > 1 offset = sum(slicex(nvert,1:sector­1)); 

This statement calculates the array offset to polygon sector by summing the number of vertices of all previous polygons. The X and Y polygon coordinates for this sector are then copied with the statements:

xpoly = slicex(xpts,offset+1:offset+nvert[sector]); 
ypoly = slicex(ypts,offset+1:offset+nv[sector]); 

See the program $UNIDIR/example/Gsharp/userguide/gsl_polygon3.gsl for an additional example of using the bounding_polygon function.

Object Scaling Methods

Resources which define a width, height, angle, or location can be set to a value and a value unit. The unit is set by following the resource value with an enumerated unit constant: mm, cm, m, inch, ft, points, pixels, deg (degrees), or rad (radians). For example:

create Arrow .arrow_1 
  ( XuNfromPosition = (100,100) mm, 
    XuNtoPosition = (0,0) mm 
  ); 

Size units can only be specified this way when using the create and set keyword functions. When the size unit is omitted, relative sizing (%), or degrees where appropriate, is used.

When the size unit is relative (%), the size value is a percentage of the minimum edge of the parent Viewport object. For Viewport resources, the value is relative to the parent Page. For location resources, the location origin is the parent Viewport, except for the Viewport object where it is the parent Page.

For absolute size units, the size value is absolute in the selected unit. For location resources, the location origin is the absolute origin of the Page. Page margins settings are not considered. When an object is projected onto a 3D plane, any location coordinates using absolute units are transformed to the 3D plane.

Note: Be aware that an objects origin will change from the viewport origin to the page origin when absolute size units are used.

The unit setting of any size resource can be queried using the function getunit. Passed a resource as an argument, this function returns the unit setting as a string. For example:

unit = getunit(".arrow_1.XuNfromPosition"); 
echo(unit); 
> mm 

When two resources are used to define a position, such as with XuNfirstDiagonalPoint (1st Diagonal Point) and XuNsecondDiagonalPoint (2nd Diagonal Point), the unit setting specified for the second resource applies to both
if the two units are different.

Optimize Execution with Repaint and Recalc

The Gsharp repaint command is used to display a graphics object and its children. The command recalc behaves as though repaint were executed, but does not cause anything to be drawn. Through proper use of these commands you can optimize graphics performance by avoiding unnecessarily repainting an entire graph.

Executing repaint with no argument causes the entire page to be repainted. You can selectively repaint an object and its children by specifying an object name as an argument to repaint. When a selective repaint is performed, the selected object is first blanked out by filling its bounding polygon in the page background color, then only the specified object is repainted.

You may want to repaint an object immediately after it is created, or after it has been updated. The example $UNIDIR/example/Gsharp/bore_samples.gsl uses selective repaint to display each graph in a series as they are created without repainting previous graphs. The following code fragment is from this program, and shows an example of using selective repaint:

/* 
* Create sample graphs 
*/ 
for i = 2 to sizex(tests) 
  ... 
  vp = sampleGraph(i, vp1, vpWidth, vpHeight, 
    label[i], #label 
    if(i = sizex(tests), 1, i), #color 
    if(i = 2, true, false), #xaxis1 on 
    if(i = sizex(tests), true, false), #xaxis2 on 
    if(i = sizex(tests), true, false) #ticklines on 
    ); 
  repaint $vp; 
endfor

In this example, repainting the entire page each time a graph changes would cause unnecessary delay. By repainting just the changed object, the script behaves in a more user­friendly and optimized manner.

Some Gsharp object resources are referred to as calculated resources. Calculated resources are, as the name implies, calculated dynamically based on the values of other resources. The dynamic calculation takes place when the graph is repainted. The recalc command causes calculated resources to be calculated and set, but without actually repainting the graph.

The limits of the graph data are examples of calculated resources. You can use the recalc command to force these values to be calculated, and then refer to them later in a script. This is done in this code segment from the example $UNIDIR/example/Gsharp/trends.gsl, where the Domain resource XuNyMinimum (Y Minimum) must be calculated before the axis cross value is set:

... 
create Graph .graph_2 
  ( XuNgraphType = "line", 
    XuNgraphName = "% of total mkt (right scale)", 
    XuNxData = "Dates", 
    XuNyData = "TotMrkVal", 
    XuNcolor = 1 
  ); 
recalc; # needed so that data limits are recalculated 
set .xaxis2 
  ( XuNaxisLabelsLevel2Labels = false, 
    XuNaxisLabelsHeightActual = 2, 
    XuNaxle = true, 
    XuNaxleCrossValue = .XuNyMinimum ­ 
      XuWorkboxToWorld(domPtr, .xaxis1.XuNtickmarksMajorLength,
      "y"), 
    ... 

In this code example, a full repaint of the graph was not desirable before the axis was set up. By using recalc, the script avoids a repaint and behaves in the manner intended.

Debugging

GSL scripts can contain different types of errors: syntax errors, runtime errors, and program logic errors. This section discusses techniques that can be used to locate errors in GSL applications.

Checking for Syntax Errors

GSL scripts must be free of syntax errors before they will run. The Script Builder has a syntax checker that locates and identifies any syntax errors.

To check your script:

Select Tools->Syntax Check.

One of the most common errors is a missing semicolon at the end of a statement. If this occurs, a prompt informs you to refer to the message area. A message similar to the one shown below will be displayed:

line 81: syntax error at echo 

Errors associated with a missing semicolon are detected when the statement following the error is reached, so always check both the line and the line prior to the line number that is listed in the error message for problems. Another very common error is a missing comma between resource assignment statements in the set and create keyword functions. A message in the form of the one shown below will be displayed:

line #: <resource> is not a valid unit name 

This type of error is also detected when the line following the error is reached, so check the line prior to the specified line for a missing comma.

Tracing Program Execution with a Message Mask

Sometimes it is useful to trace the execution of a script to determine where a problem occurs. If you are developing a script with complex logic, such as looping, function calls, dynamic object creation, you may want to build in a message system to trace the flow of the program or just to control to amount of message output.

You can control diagnostic messages with a message mask. A mask can be set to enable tracing program flow while an application is being developed, and turned off when the application is deployed. This is done in the example program $UNIDIR/example/Gsharp/userguide/gsl_color.gsl, which uses to variable MASK to control function tracing and informational messages. Informational messages are only printed if MASK contains the letter "I'' and function calls are traced only if MASK contains the letter "F'', as shown in the following code segment:

/*************************************************************** 
* Main program to create graph 
****************************************************************/ 
... 
include "$UNIDIR/lib/libxu.gsl"; 
... 
MASK = "IF"; # I = information, F = functions 
XuMsg("I","­­­­­Begin shading_scales­­­­­") 
... 

The two functions used in by this example call a message function and identify
the message type using the mask character "F''. Only if the variable MASK
contains that character is the message actually printed:

function string create_legend( 
... 
XuMsg("F","create_legend for "+ scale"); 
... 

When the program is executed, the following messages are printed:

­­­­­Begin shading_scales­­­­­ 
create_legend for default 
create_legend for cyanYellowRed 
create_legend for grey 
create_legend for topographic 
create_legend for rainbow 
create_legend for blueWhiteRed 
create_legend for user1 
create_legend for user2 
create_legend for user3 
create_legend for user4 
­­­­­End shading_scales­­­­­ 

Messages can be turned off simply by deleting characters in MASK. The function XuMsg, which are used to print the messages, can be found in the library $UNIDIR/lib/libxu.gsl which is included by the main program. An additional function, XuTrace, is similar to XuMsg but directs the output to stdout instead of the Gsharp Message Area.

When tracing the execution of a script which includes GUI objects (see GUI Programming with GSL), the function GuiEchoCB found in $UNIDIR/lib/libgui.gsl can be used to display the parameters to a callback function. For example, inserting a call to this function just after the function declaration:

function myCallback(string object, string calldata, float reason) 
GuiEchoCB(object,calldata,reason); 

will produce output similar to the following each time the callback is invoked:

GuiEchoCB(gsharp_1.prompt, , XuCR_OK) 
GuiEchoCB(gsharp_1.prompt, , XuCR_CANCEL)