Sysquake Pro – Table of Contents

Sysquake for LaTeX – Table of Contents

# SQ File Tutorial

This chapter shows you how to develop an SQ file for Sysquake. Like SQ scripts we saw in the previous chapter, SQ files are programs for Sysquake. But they are built in a stricter framework which provides automatically undo/redo, save/restore, and subplot layout; it also supports menus, choice of plots, periodic processing, a mechanism to compute the minimal amount of information required for the plots displayed at a given time, and an import/export system.

Follow each step of this tutorial, and starting from scratch, you will end up with an SQ file which lets you move the tangent of a quadratic function, and the knowledge to write your own SQ files. When you will need more information, you can refer to the Sysquake Reference chapter.

This tutorial assumes you have a basic knowledge of a procedural programming language, such as C, Pascal, Fortran, a modern Basic dialect, or MATLAB(R). The concepts of variable and function are supposed to be well known.

### Structure of an SQ file

This tutorial will describe each element when they are used. However, it is important to notice an important difference between SQ scripts and libraries, which contain LME code, and SQ files, which begin with static declarations of the application components, such as data, figures and menus. In an SQ file, LME code is located in function blocks, i.e. between the lines

functions {@

and

@}

Everything else consists of declarations and comments. Some of the declarations refer to functions defined in function blocks; for instance, a figure declaration includes the function called to draw it, together with its arguments. Sysquake reads all the declarations when the SQ file is loaded in memory; it uses them to reserve space for data, to set up user interface elements, and execute LME functions when required, for instance as a consequence of user actions.

## Displaying a Plot

In this section, we will write what is necessary to display in the
same graphics the quadratic function, a vertical line which defines
a value for `x0`, and the straight line which is tangent
to the quadratic function at `x0`.

An SQ file is written as a text file using any text editor. If you prefer
a word processor, make sure that you save the SQ file as raw text, or
ASCII, and *not* as styled text. On some versions of Sysquake, a
built-in editor is available; check if there is a New item in the File menu
(Load lets Sysquake load or reload the text of the front window, while
Open reads an SQ file from a files).
Sysquake handles end of lines in a sensible fashion; do not worry about the different
conventions between Mac OS, Unix, Windows and other operating systems.
For cross-platform compatibility, restrict yourself to the ASCII character set,
and avoid two-bytes characters like Unicode and Japanese kanji (depending on
the platform, bytes are interpreted as the native encoding, such as Latin-1
or Shift-JIS, or UTF-8). Once you have written and saved
a file you want to test, simply open it in Sysquake. Make sure that the Command
window or panel is visible, so that you can see error messages.

We can now begin to write the SQ file.

### Step 1: Choosing variables

The most important concept in Sysquake is the set of variables. Variables define
the state of the system (we use the word "system" in a broad meaning as what
the user perceives from the graphics). Everything that
can be changed, be it interactively, by specifying parameters in a dialog
box, or by loading an SQ data file, must be stored in variables. In addition,
auxiliary variables can be used as a convenience to avoid repetitive
computations or to transmit values between handler functions (more about
them later). Each variable can contain a real or complex array, a string,
a list, or a structure. Variables are identified by a name of up to 32 letters, digits, and
the underscore character "_" which begins with a letter (names beginning with
the underscore are reserved). As everything else in Sysquake, names are case-sensitive;
`x` and `X` are two different names and identify
two separate variables.

You can declare as many variables as you need. Do not use a big array to pack as many different things as you can; it is much more efficient to have a clean set of variables, so that you can use them and change them more easily.

Sysquake considers the values of the variables as a set. Each time the user changes a variable (interactively or otherwise), Sysquake creates a new set and changes the new values. The value of unmodified variables is retained. The command Undo reverts to the previous set. This is why you should usually not use global variables, which exist only in one copy.

For our example, we define variables `a`, `b`, and
`c` for the coefficients of the quadratic function; variables
`d`, `e`, and `f` for the tangent
`dx+ey=f``x0` for the horizontal position where the line is
tangent to the function.

To let Sysquake know about our choice, we write the following lines at the beginning of the SQ file:

variable a b c // coefficients of the quadratic function // y=ax^2+bx+c variable d e f // coefficients of the tangent dx+ey=f variable x0 // value of x where the line is tangent

The keyword `variable` is required; it is followed on the
same line by one or more variable names, separated by spaces or tabulators.
Everything following the two slashes `//` is a comment which
is ignored by Sysquake.

### Step 2: Giving initial values

At the beginning, each variable is set to the empty matrix `[]`.
Drawing functions could recognize them and not display anything, but it is
nicer for the user to start immediately with default values. In Sysquake,
variables are set and used by *handler* functions. Functions are
written in the LME language, and declared to
Sysquake by a handler declaration. Handler declarations and function definitions
are very similar. They both use variables, which do not necessarily have
matching names. Variables in the handler declaration correspond to the set of variables
declared at the level of the SQ file; variables in the function definition
are meaningful only in the function itself. The input arguments in the handler
declarations must be variables or integer numbers; they cannot be expressions. The handler
declaration begins with a keyword, for example `init` to define default
values. Here is an init handler for our SQ file:

init (a,b,c,x0,d,e,f) = init

We will use parenthesis for functions with several output arguments. You
may use square brackets if you prefer. The function declared above is defined
in a function block. We also write a function `calcTangent` to calculate
the tangent of the quadratic function.

function {@ function (a,b,c,x0,d,e,f) = init // initial values for the function coefficients // and the x0 value a = 1; b = 2; c = 4; x0 = 1; (d,e,f) = calcTangent(a,b,c,x0); function (d,e,f) = calcTangent(a,b,c,x0) // tangent to y=f(x) at x0 is y-f(x0)=f'(x0)(x-x0), // where f' is der f // derivative of ax^2+bx+c is 2ax+b d = 2*a*x0+b; e = -1; f = (2*a*x0+b)*x0-(a*x0^2+b*x0+c); @}

Notice the block in `{@ @}` (function block); now it contains only the
`init` and `calcTangent` functions, but we will add more
functions in the next
sections. The function block does not need to follow a particular handler
declaration; handlers are identified only by their name. Usually, we will put
the function block after all the declarations. In LME code it contains (but not
in declarations), the percent symbol can be used instead of the two slashes to begin
a comment.

Errors in an SQ file are detected when you open or load it in Sysquake.
To let Sysquake analyze your code and catch constructs which might be errors,
you can select SQ File Possible Error Warnings in the Preferences.
It would be the case if we do not provide initial values for all variables,
or if the order of variables in the init handler declaration does not match
the one in its implementation, here in function `init`.

### Step 3: Displaying a plot

Each figure is declared by a figure declaration line which contains a name between quotes, and one or more lines declaring handlers for drawing the plot and processing the manipulations with the mouse. For now, we just declare a draw handler (outside the function block), which needs to know the value of the seven variables.

figure "Quadratic Function" draw drawFunc(a,b,c,x0,d,e,f)

The figure displays the quadratic function
`line`; it
is an arbitrary positive identifier which will be used for interactive manipulation.

function drawFunc(a,b,c,x0,d,e,f) // values of x where the function is evaluated x = -10:0.1:10; // plot the function plot(x, a*x.^2+b*x+c); // plot in red ('r') a vertical line at x0 line([1,0],x0,'r',1); // plot in blue ('b') the tangent at x0 line([d,e],f,'b');

If you have typed all the code above, not forgetting to put the function
`drawFunc` in the function block, you can open it in Sysquake and
observe your first graphics

If you do not specify which figures you want to display when the SQ file
is opened, Sysquake displays the first one. With more than one, you may want to
specify explicitly which one(s) to show. Add a command `subplot`
to the init handler with the name of the figure:

subplots('Quadratic Function');

Make sure that the string matches exactly the name of the figure. To display several figures, you would separate their names with tabulators ('\t') for figures on the same row, and with line feeds ('\n') for separating each row. The complete SQ file is shown below.

variable a b c // coefficients of the quadratic function // y=ax^2+bx+c variable d e f // coefficients of the tangent dx+ey=f variable x0 // value of x where the line is tangent init (a,b,c,x0,d,e,f) = init figure "Quadratic Function" draw drawFunc(a,b,c,x0,d,e,f) function {@ function (a,b,c,x0,d,e,f) = init // initial values for the function coefficients // and the x0 value a = 1; b = 2; c = 4; x0 = 1; (d,e,f) = calcTangent(a,b,c,x0); subplots('Quadratic Function'); function (d,e,f) = calcTangent(a,b,c,x0) // tangent to y=f(x) at x0 is y-f(x0)=f'(x0)(x-x0), // where f' is der f // derivative of ax^2+bx+c is 2ax+b d = 2*a*x0+b; e = -1; f = (2*a*x0+b)*x0-(a*x0^2+b*x0+c); function drawFunc(a,b,c,x0,d,e,f) // values of x where the function is evaluated x = -10:0.1:10; // plot the function plot(x, a*x.^2+b*x+c); // plot in red ('r') a vertical line at x0 line([1,0],x0,'r',1); // plot in blue ('b') the tangent at x0 line([d,e],f,'b'); @}

## Adding Interactivity

The plot of the previous section is static; until now, we have not seen anything which makes Sysquake different, except for a slightly more complicated set-up. We will now dip into interactivity by allowing the user to move the tangent point and observe the tangent.

### Step 4: Writing a mouse drag handler

To enable the manipulation of a graphical element, a mouse drag
handler must be declared under the same `figure` heading as the
draw handler. The mouse drag handler is also
a function defined in the function block. There is an important
difference, however: it returns new values for one or several variables
(not necessarily the same as the input).

Values related to the user interaction are obtained as special
variables which begin with an underscore "_". We want to drag the vertical red line
at `x0`; hence we need the current x coordinate of the mouse, and
an indication about whether the user selected the line.
The horizontal position of the mouse during the
drag is given by `_x1`. A graphic ID is
given by `_id`; it corresponds to the last argument of graphical
commands like `line` or `plot`. If the user
clicks far away from any object drawn by a command with an ID, `_id`
is the empty matrix `[]`. Since we want the user to drag the vertical
line, we expect to have `_id` set to 1, the value passed to `line`
in the draw handler.

Special variables can be passed to the handler as input arguments, or used directly in the handler without any declaration. This is what we shall do here to reduce the number of arguments to the minimum.

mousedrag (x0,d,e,f) = dragX0(a,b,c)

The mousedrag handler should calculate not only the new
value of `x0`, but also all other variables which depend
on it, i.e. the coefficients of the tangent. The update of the graphics
is totally automatic, and you get a multilevel Undo/Redo for free!

function (x0,d,e,f) = dragX0(a,b,c) if isempty(_id) cancel; end x0 = _x1; (d,e,f) = calcTangent(a,b,c,x0);

In this definition (located in the function block), we note the check for
an empty `id`. If we do not click the red line, the handler
should not terminate normally; even if we kept the previous values,
a new Undo frame would be created, and the first execution of Undo
would have no visible effect.

If you type the code above, you have a figure where you can manipulate the vertical line with the mouse and see the tangent move.

### Step 5: The final touch, a mouseover handler

Interactive manipulation is much easier if subtle hints about what can
be manipulated are displayed. Such hints include the shape of the cursor,
which should be a finger only if a click permits the manipulation of an element,
and messages in the status bar at the bottom of the window. The mouseover handler,
which is called in Manipulate mode when the mouse is over a figure, gives
this kind of information to Sysquake. The input arguments are similar to
the mousedrag handler. The output arguments are special: they should
be `_msg`, `_cursor`, or both. `_msg`
should be set to a string which is displayed in the status bar.
`_cursor` should be set to true to have a finger cursor,
and to false to have the plain arrow cursor. Canceling the mouseover
handler is like setting `_msg` to the empty string `''` and
`_cursor` to false. Note also that if a figure has a
mousedown, mousedrag, and/or mouseup handler, but no mouseover handler,
the cursor will be set to the finger.

In our case, the user can manipulate the only object with a non-empty id. There is no need to define a function for such a simple task:

mouseover _cursor = ~isempty(id)

Adding messages is not much more complicated, but now we must define a function.
To display the value of `x0`, we can use
either the position of the vertical line or the value of `x0`.
The special variable `_x0` is the position of the line, not the position
of the mouse as in the declaration of the mousedrag handler. The early cancellation of
the execution of the handler is easier (and faster)
to handle the case where the mouse in not over an object. The handler definition is

function (_msg, _cursor) = overFunc if isempty(_id) cancel; end _msg = sprintf('x0: %g', _x0); _cursor = true;

and its declaration is

mouseover (_msg, _cursor) = overFunc

There is still a problem: the message is not displayed when the user
actually drags one of the frequencies, because the mouseover handler is
not called when the mouse button is held down. For this, `_msg` must
be added to the mousedrag handler. One way to do this is to declare
the handler as

mousedrag (x0,d,e,f,_msg) = dragX0(a,b,c)

and to define it as

function (x0,d,e,f,msg) = dragX0(a,b,c) if isempty(_id) cancel; end x0 = _x1; (d,e,f) = calcTangent(a,b,c,x0); msg = sprintf('x0: %g', x0);

## Menu Entries

It may be useful to set the value of some parameters with a menu entry.
In our case, it would be difficult to specify in a figure the coefficients
of the quadratic function. An SQ file can define
*menu handlers*; new entries are installed in the Settings menu
(which appears only if it is not empty), and the corresponding handler
is executed when the entry is selected in the menu.

Let us add a menu entry which displays a dialog box where we can change the coefficients. First, we declare it with

menu "Quadratic Function..." (a,b,c,d,e,f) = menuFunc(a,b,c,x0)

The input arguments allow to display the current values and to
calculate the new tangent for the current
value of `x0`. Note how lines can be split between its
components. Here is the handler definition:

function (a,b,c,d,e,f) = menuFunc(a,b,c,x0) (a,b,c) ... = dialog('Coefficients a,b,c of ax^2+bx+c:',a,b,c); (d,e,f) = calcTangent(a,b,c,x0);

The dialog function displays three kinds of alert or dialog boxes, depending on the number of input and output arguments. As we use it here, the first argument is a description, and the remaining input arguments are initial values which are displayed in an edit field. They can be modified by the user. When the OK button is clicked, the dialog box is dismissed and the output arguments receive the new values. If the Cancel button is clicked, the execution of the handler is aborted exactly as if the cancel command had been executed.

Each menu entry can be decorated in two ways: a checkmark can be
displayed on the left, and the entry can be disabled (it cannot be selected and
the text is written in gray). It does not make sense to use these
possibilities with our first menu. Let us add support to choose whether
the position of `x0` is displayed with a vertical line or
a small diamond. First, we add a variable
whose value is true for a line and false for a diamond.

variable x0Line

We initialize it to true in the init handler, whose declaration becomes

init (a,b,c,x0,d,e,f,x0Line) = init

and definition

function (a,b,c,x0,d,e,f,x0Line) = init // initial values for the function coefficients // and the x0 value a = 1; b = 2; c = 4; x0 = 1; (d,e,f) = calcTangent(a,b,c,x0); subplots('Quadratic Function'); // x0 is displayed as a line x0Line = true;

The draw handler should get the new variable and act accordingly.
Here is the new `drawFunc` handler declaration:

draw drawFunc(a,b,c,x0,d,e,f,x0Line)

and its definition:

function drawFunc(a,b,c,x0,d,e,f,x0Line) // values of x where the function is evaluated x = -10:0.1:10; // plot the function plot(x, a*x.^2+b*x+c); if x0Line // plot in red ('r') a vertical line at x0 line([1,0],x0,'r',1); else // plot in red ('r') a diamond ('<') at (x0,f(x0)) plot(x0,a*x0^2+b*x0+c,'r<',1); end // plot in blue ('b') the tangent at x0 line([d,e],f,'b');

The mousedrag handler needs no modification. Now the most interesting part. We add two menu entries, declared as

separator menu "Line" _checkmark(x0Line) x0Line = 1 menu "Diamond" _checkmark(~x0Line) x0Line = 0

The `separator` adds a horizontal line or a
space between the first menu entry and these two new elements. Between the entry
title and the handler declaration, the
`_checkmark` keyword is used to tell Sysquake to display a check mark
if the expression is parenthesis is true. This expression may be more complicated
than a variable; for the second entry, we use the *not* operator, so that depending
on the value of `x0Line`, either one or the other is checked. No handler
definition is needed here, because we set `x0Line` to a constant.
In handler declarations, only integers are permitted; fortunately, setting
`x0Line` to 1 or 0 works fine.

Here is the complete SQ file:

variable a b c // coefficients of the quadratic function // y=ax^2+bx+c variable d e f // coefficients of the tangent dx+ey=f variable x0 // value of x where the line is tangent variable x0Line init (a,b,c,x0,d,e,f,x0Line) = init menu "Quadratic Function..." (a,b,c,d,e,f) = menuFunc(a,b,c,x0) separator menu "Line" _checkmark(x0Line) x0Line = 1 menu "Diamond" _checkmark(~x0Line) x0Line = 0 figure "Quadratic Function" draw drawFunc(a,b,c,x0,d,e,f,x0Line) mousedrag (x0,d,e,f,_msg) = dragX0(a,b,c) mouseover (_msg,_cursor) = overFunc function {@ function (a,b,c,x0,d,e,f,x0Line) = init // initial values for the function coefficients // and the x0 value a = 1; b = 2; c = 4; x0 = 1; (d,e,f) = calcTangent(a,b,c,x0); subplots('Quadratic Function'); // x0 is displayed as a line x0Line = true; function (d,e,f) = calcTangent(a,b,c,x0) // tangent to y=f(x) at x0 is y-f(x0)=f'(x0)(x-x0), // where f' is der f // derivative of ax^2+bx+c is 2ax+b d = 2*a*x0+b; e = -1; f = (2*a*x0+b)*x0-(a*x0^2+b*x0+c); function (a,b,c,d,e,f) = menuFunc(a,b,c,x0) (a,b,c) ... = dialog('Coefficients a,b,c of ax^2+bx+c:',a,b,c); (d,e,f) = calcTangent(a,b,c,x0); function drawFunc(a,b,c,x0,d,e,f,x0Line) // values of x where the function is evaluated x = -10:0.1:10; // plot the function plot(x, a*x.^2+b*x+c); if x0Line // plot in red ('r') a vertical line at x0 line([1,0],x0,'r',1); else // plot in red ('r') a diamond ('<') at (x0,f(x0)) plot(x0,a*x0^2+b*x0+c,'r<',1); end // plot in blue ('b') the tangent at x0 line([d,e],f,'b'); function (x0,d,e,f,msg) = dragX0(a,b,c) if isempty(_id) cancel; end x0 = _x1; (d,e,f) = calcTangent(a,b,c,x0); msg = sprintf('x0: %g', x0); function (_msg,_cursor) = overFunc if isempty(_id) cancel; end _msg = sprintf('x0: %g', _x0); _cursor = true; @}

## More about graphic ID

Graphic ID have an important role: they permit to link drawing code in the draw handler with the code which handles user interactions in the mousedrag and mouseover handlers. Graphic are arbitrary positive integer numbers. Their value is not important, provided they are unique in each figure and they are used in a consistent way.

### Graphic ID in declarations

In the SQ file of the tutorial, the ID is used only to detect if
the mouse is located near the corresponding graphical object or not.
In more complicated cases where multiple graphical objects with
different ID are displayed in the same figure, mousedrag and mouseover
handlers would typically have a `switch` statement to
react in a different way for different objects. There is an alternative
way to let Sysquake choose which part of code to execute, which often
leads to simpler SQ files: specify the ID in the handler declaration,
right after the `mousedrag` or `mouseover`
declaration. In our SQ file, the figure declaration would become

figure "Quadratic Function" draw drawFunc(a,b,c,x0,d,e,f,x0Line) mousedrag 1 (x0,d,e,f,_msg) = dragX0(a,b,c,_x1) mouseover 1 _msg = overFunc(_x0)

and the definition of function `dragX0`

function (x0,d,e,f) = dragX0(a,b,c,x1) x0 = x1; (d,e,f) = calcTangent(a,b,c,x0);

If there were multiple graphical objects with different ID, the figure declaration would have multiple mousedrag handlers. It is also possible to keep a default mousedrag handler (without ID) for remaining objects and for mouse clicks elsewhere in the figure.

Mouseover handlers can also have a specific ID. But there is
an additional benefit: the cursor is set automatically to the finger
over objects with an ID for which a mousedrag is declared, and to
a plain arrow elsewhere. This is why the declaration of the mouseover
above does not produce a `_cursor` output argument
anymore.

### Constant naming

In programs, a good practice is to give names to all significant
constants, especially if they are reused at different locations. LME
provides the `define` programming construct to create named
constants. In SQ files, `define` can also be used outside
any function block, so that it has a scope in both declarations and
LME code. The special value `_auto` is set successively
to 1, 2, etc.; its main purpose is to produce unique values for constants
used as graphic ID. For instance

define kLowId = _auto define kHighId = _auto

defines `kLowId` as 1 and `kHighId` as 2.
Here is again the complete code of the tutorial SQ file.

variable a b c // coefficients of the quadratic function // y=ax^2+bx+c variable d e f // coefficients of the tangent dx+ey=f variable x0 // value of x where the line is tangent variable x0Line define kLineId = _auto init (a,b,c,x0,d,e,f,x0Line) = init menu "Quadratic Function..." (a,b,c,d,e,f) = menuFunc(a,b,c,x0) separator menu "Line" _checkmark(x0Line) x0Line = 1 menu "Diamond" _checkmark(~x0Line) x0Line = 0 figure "Quadratic Function" draw drawFunc(a,b,c,x0,d,e,f,x0Line) mousedrag kLineId (x0,d,e,f,_msg) = dragX0(a,b,c) mouseover kLineId _msg = overFunc(_x0) function {@ function (a,b,c,x0,d,e,f,x0Line) = init // initial values for the function coefficients // and the x0 value a = 1; b = 2; c = 4; x0 = 1; (d,e,f) = calcTangent(a,b,c,x0); // x0 is displayed as a line x0Line = true; function (d,e,f) = calcTangent(a,b,c,x0) // tangent to y=f(x) at x0 is y-f(x0)=f'(x0)(x-x0), // where f' is der f // derivative of ax^2+bx+c is 2ax+b d = 2*a*x0+b; e = -1; f = (2*a*x0+b)*x0-(a*x0^2+b*x0+c); function (a,b,c,d,e,f) = menuFunc(a,b,c,x0) (a,b,c) ... = dialog('Coefficients a,b,c of ax^2+bx+c:',a,b,c); (d,e,f) = calcTangent(a,b,c,x0); function drawFunc(a,b,c,x0,d,e,f,x0Line) // values of x where the function is evaluated x = -10:0.1:10; // plot the function plot(x, a*x.^2+b*x+c); if x0Line // plot in red ('r') a vertical line at x0 line([1,0],x0,'r',kLineId); else // plot in red ('r') a diamond ('<') at (x0,f(x0)) plot(x0,a*x0^2+b*x0+c,'r<',kLineId); end // plot in blue ('b') the tangent at x0 line([d,e],f,'b'); function (x0,d,e,f) = dragX0(a,b,c,x1) x0 = x1; (d,e,f) = calcTangent(a,b,c,x0); function msg = overFunc(x0) msg = sprintf('x0: %g', x0); @}

## Saving Data

Once the user has changed the tangent point, he might find convenient to save it to a file and read it back later. In the SQ file, nothing more is required; the contents of all the variables as well as the information necessary to restore the subplots are written to an SQ data file with the Save command. Opening such a file reloads everything provided that the original file is found. If more control is desired on what is stored in the SQ data file and how it is read back, input and output handlers can be added.