previous contents next
Previous: A Simple Example Next: Availability of SEnC

Some More Advanced Examples

The example in the preceding were chosen to be very simple so that a novice SEnC user can easily understand them. In this section, we will show a more advanced example, in which the Enif server commands and SEnC client commands are combined to perform more elaborate functions. This section is really meant for the more advanced SEnC and Enif users. So if you are reading the first time about SEnC and have not actually used SEnC yet, you can safely skip the following and move on to the next section.

For this, let us consider the function view used in the preceding section. It could be called to change the view to one of the named views which were coded right into the function code. Since each Enif application contains itself a set of predefined views, let us try to rewrite the function view in such a way that it actually looks up the the given view name in the Enif application and, it a predefined view with the given name exists, sets the current view accordingly.

For implementing this task, we first have to know where Enif stores the predefined views. This is done in an indexed Box type parameter called PredViews which is owned by the configurable object containing the preferences. E.g. in my Winnipeg application, the PredViews parameter have the following definition:

PredViews[0] = -10;-3;-7;3;Airport;
PredViews[1] = -1.2;1.5;4.2;4.8;Kildonan;
PredViews[2] = -3;-3;3;3;CBD;

The easiest way of accessing this information is using Enif's parameter substitution mechanism via the Enif server command echo, which was discussed earlier. Using e.g. a the command ``echo %<PredViews[1]%>'' will in our example data bank return the string ``-1.2;1.5;4.2;4.8;Kildonan;'' So what we need to do is loop through the indices of the PredViews parameter and find the one with the given name.

Before actually writing the new version of our function view, we will need to define first an auxiliary functions. This function is called get and allows retrieving one of the given arguments by its positional numbers and set the specified variable to the corresponding argument:

!function get VAR POS V1 V2 V3 V4 V5 V6 V7 V8 V9
!begin
  !set {VAR} = {V{POS}}
!end

Now we have all the ingredients to write the new version of our function view as follows:

!function view VIEWNAME
!begin
  !new OUTPUT = 1
  !new NOECHO = 1
  !new PVIEW =
  !new PNAME =
  !new I =
  !for I 0 999
  !begin
    echo %<PredViews[{I}]>%
    !set PVIEW = {OUTPUT}
    !if x == x{PVIEW}
    !begin
      !delete OUTPUT NOECHO PVIEW PNAME I
      !return
    !end
    !set PNAME = {PVIEW}
    !replace PNAME ;
    !call get PNAME 5 {PNAME}
    !if {VIEWNAME} == {PNAME}
    !begin
       View = {PVIEW}
       !delete OUTPUT NOECHO PVIEW PNAME I
       !return
    !end
  !end
  !delete OUTPUT NOECHO PVIEW PNAME I
!end

The function first defines new instances of the variables OUTPUT, NOECHO, PVIEW, PNAME and the loop variable I. This means that we have to make sure that these variable instances are deleted before returning from this function.

The main part of the function is a for-loop which iterates the variable I through the index values 0, 1, 2, ...

For each index, the echo server command is used to write the corresponding view box value into the SEnC special variable OUTPUT. If the value is empty, this indicates that the parameter PredViews does not contains a value at the corresponding index, i.e. that we have searched through all predefined views without having found the specified view. In this case, a message is issued and we return from the function without having changed the view.

Once we have obtained a valid view box value, we now break it into its components xmin ymin xmax ymax name [description] by replacing the separators ";" by blanks. We can now use the previously defined function get to extract the view's name from the fifth field and write it to the variable PNAME.

Finally, we have to compare the name of the current predefined view with the view which was asked for. If the names match, we set the View parameter (remember, it is grouped to Enif's current view!) to the corresponding value, at which time the view displayed by Enif will change to the requested view. The task is now completed and we can return from function view.

We can now e.g. try out the new function by calling it as to find and set the ``Kildonan'' view:
        !call view Kildonan

The last example show how the SEnC / Enif framework can be used to do computations. As an example, let's try to write a function zoom which implements a concentric zoom-in or zoom-out of Enif's current view. The zoom factor is provides function argument, where values $>1$ implying a zoom-in and values $<1$ a zoom-out operation.

Again, we first need to define some auxiliary functions. The first function is called shift and is used to discard the first of a set of arguments and set a specified variable to the remaining arguments:

!function shift VAR V0 V1 V2 V3 V4 V5 V6 V7 V8 V9
!begin
  !set {VAR} = {V1} {V2} {V3} {V4} {V5} {V6} {V7} {V8} {V9}
!end

The second function is in itself very interesting. It is called eval and sets the variable specified in the first argument to the result of the expression given in the second argument. E.g. the command ``!call eval X (1+0.5*(7-2))'' will set evaluate the expression $(1+0.5*(7-2))$ and set the variable X to the result value, i.e. after the command we have X$=3.5$.

!function eval VAR EXPRESSION
!begin
  !new NOECHO = 1
  !new OUTPUT = 1
  new Expression eval_expression Nodes
  new Selector eval_selector Nodes
  eval_expression = {EXPRESSION}
  eval_selector = index==1
  check eval_expression
  !iferror
  !begin
     !print Invalid expression {EXPRESSION}
     !set OUTPUT = 0 0
  !end
  eval eval_expression eval_selector
  !call shift {VAR} {OUTPUT}
  !delete NOECHO OUTPUT
  delete eval_expression
  delete eval_selector
!end

Since SEnC does not have any client commands which perform calculation, function eval actually uses Enif to perform this task. For this, an expression and a selector are set up accordingly and the eval server command is used to actually perform the computation. The result is then extracted and stored in the specified variable.

With the eval function, implementing the previously mentioned zoom function now becomes relatively straight forward:

!function zoom ZOOMFACTOR
!begin
  !new NOECHO = 1
  !new OUTPUT = 1
  echo %<$CurrentView>%
  !new CVIEW = {OUTPUT}
  !print CVIEW={CVIEW}
  !replace CVIEW ;
  !print {CVIEW}
  !new XMIN =
  !new YMIN =
  !new XMAX =
  !new YMAX =
  !new DX =
  !new DY =
  !call get XMIN 1 {CVIEW}
  !call get YMIN 2 {CVIEW}
  !call get XMAX 3 {CVIEW}
  !call get YMAX 4 {CVIEW}
  !call eval DX {XMAX}-{XMIN}
  !call eval DY {YMAX}-{YMIN}
  !call eval XMIN {XMIN}+{DX}*(1-1/({ZOOMFACTOR}))/2
  !call eval XMAX {XMAX}-{DX}*(1-1/({ZOOMFACTOR}))/2
  !call eval YMIN {YMIN}+{DY}*(1-1/({ZOOMFACTOR}))/2
  !call eval YMAX {YMAX}-{DY}*(1-1/({ZOOMFACTOR}))/2
  !print View = {XMIN};{YMIN};{XMAX};{YMAX};zoom
  View = {XMIN};{YMIN};{XMAX};{YMAX};zoom
  !delete NOECHO OUTPUT CVIEW XMIN XMAX YMIN YMAX DX DY
!end

The zoom function can now be tested with the command
        !call zoom 2
which will cause Enif to zoom-in by a linear factor of 2, i.e. showing only one quarter of the previously displayed part of the network.


previous contents next
Previous: A Simple Example Next: Availability of SEnC

SEnC - A Sequential Enif Client, Heinz Spiess, May 2003