SML PROGRAMMING

Handling Coordinates

Supplying Key Menu Input

In SML, coordinate values may be passed to a command in ARCEDIT by setting COORDINATE KEYBOARD. This process is greatly aided in version 3.5 due to its enhancements of COO KEY, which allow an application to mimic the action of a coordinate device, as in the following example:

&routine addtic
&rem **** add tics from a text file
&rem **** with line format: ID X Y
&define coo -11 &var
&define temp -19 &var
&define winrtn 1 &var
WIN FILE 1 * 'Tic Input File'
&if &eq "x[winrtn]" "x" &do
   &return CANCEL
&end
&open [winrtn] error
EDITF TIC
SHOW COORDINATE [coo]
COO KEY
ADD
&while &do
   &read [temp] [break]
   &if &ne "x[temp]" "x" &do
      &extract 1 [temp] -1
      &extract 2 [temp] -2
      &extract 3 [temp] -3
      3 0 0
      %-1
      1 %-2 %-3
   &end
&end
9 0 0
&close
COO [coo]
&return OK

&label error
&type "ERROR: Cannot open [winrtn]"
&return ERROR
However, if a routine is to handle coordinates provided by a mouse or digitizer, &getxym values must be collected before setting COO KEY, a task which cannot be performed during an ADD session. Thus if one were to develop a GUI to add arcs, all coordinates would need to be collected prior to ADDing each arc, which could severely limit the practicality of such a routine[2].

Out of sheer determination I came up with the following example, in which vertices are plotted and/or removed using 3.5's MARKER command[3]. I'm going to try to make it more digestible by breaking it into sections. The first section creates and initializes the dialog box:

&routine addarc
&rem **** define variables
&define i    -8  &var
&define numv -9  &var
&define resp -10 &var
&define wksp -11 &var
&define arct -12 &var
&define sd1  -13 &var
&define sd2  -14 &var
&define coo  -15 &var
&define node -16 &var
&define weed -17 &var
&define temp -19 &var
&define winrtn 1 &var
&rem **** initialize dialog box
EDITF ARC
&value [wksp] WKSP
SHOW ARCTYPE [arct]
SHOW SETDRAWSYM [sd1] [sd2]
SHOW COORDINATE [coo]
SHOW WEED [weed]
SETD OFF
&openw [wksp]t$add.dlg
&write "BEGIN    0  5.00  5.00  6.75 17.00 v Add Arc(s)"
&write "PBUT  -101  0.00  0.00  1.50 8.50 v New"
&write "GBOX     0  1.50  0.00  5.00 8.50 v Type"
&write "RBUT  -102  3.00  1.00  1.00 7.00 v Line"
&write "RBUT  -103  4.00  1.00  1.00 7.00 v Box"
&write "RBUT  -104  5.00  1.00  1.00 7.00 v Circle"
&write "PBUT  -105  0.00  8.50  1.50 8.50 v Attach"
&write "PBUT  -106  2.00  9.00  1.50 8.00 v Back"
&write "PBUT  -107  3.50  9.00  1.50 8.00 v Clear"
&write "PBUT  -108  5.00  9.00  1.50 8.00 v Resume"
&write "END"
&write "G 105"
&write "G 106"
&write "G 107"
&write "G 108"
&if &eq [arct] LINE &do
   &write "C 102"
&elseif &eq [arct] BOX &do
   &write "C 103"
&else
   &write "C 104"
&end
&closew
WIN DB C [wksp]t$add.dlg
& DEL [wksp]t$add.dlg
Figure 1

Because simultaneous input from the coordinate device and dialog box is not possible, the routine uses the <1> key (or left mouse button) to enter coordinates and the <2> key (or right mouse button) to "escape" back to the dialog box. Coordinate entry begins when the NEW (return 101) or ATTACH (return 105) button is pressed. When the dialog box is closed (return value -1), the NEW or ATTACH button is pressed, or the ARCTYPE changed (return values 102-104), any stored coordinates (ARCTYPE LINE) are supplied to the ADD command:

&rem **** process responses
&sv [numv] 0
&sv [resp] 0
&while &ne [resp] -1 &do
   WIN DB R [wksp]t$add.cmd
   &sv [resp] [winrtn]
   &if &eq [resp] -1 &or &rn [resp] 101 105 &do
      &rem **** check for existing arc to process
      &if &eq %<[numv] max 2> [numv] &do
         &rem **** ADD IT!!!!
         SETD 7
         &value [temp] %<[numv] + 1000>
         MARKER [temp]
         SETD 1
         COO KEY
         GRAPHIC OFF
         ADD
         &sv [i] 1
         &while &rn [i] 1 [numv] &do
            &value [temp] %<[i] + 1000>
            &if &eq [i] 1 &or &eq [i] [numv] &do
               2 [temp]
            &else
               1 [temp]
            &end
            &inc [i]
         &end
         9 0 0
         GRAPHIC ON
         DRAWSELECT
         COO [coo]
         &value [node] %<[numv] + 1000>
         &sv [numv] 0
      &end
   &end
Coordinates are collected by storing them in SML variables 1001 and above:

   &if &eq [resp] 101 &or &eq [resp] 105 &do
      &sv [numv] 0
      &rem **** if attach, add starting node
      &if &eq [resp] 105 &do
         &sv [numv] 1
         &value 1001 [node]
      &end
      &rem **** collect coordinates for new arc
      &if &eq [arct] LINE &do
         &getxym -1 -2 -3
         &if &eq [resp] 101 &do
            SETD 7
         &else
            SETD 5
         &end
         &while &ne %-3 2 &do
            &sv -6 TRUE
            &if &ne [numv] 0 &do
               &rem **** check if < WEED
               &extract -4 %<[numv] + 1000> 1
               &extract -5 %<[numv] + 1000> 2
               &cv -4 %-4 - %-1
               &cv -5 %-5 - %-2
               &cv [temp] ( ( %-4 * %-4 ) + ( %-5 * %-5 ) ) ** 0.5
               &if &eq %<[temp] min [weed]> [temp] &do
                  &sv -6 FALSE
               &end
            &end
            &if &eq %-6 TRUE &do
               &inc [numv]
               &sv %<[numv] + 1000> "%-1 %-2"
               MARKER %-1 %-2
            &else
               &type "Last vertex within weed tolerance"
            &end
            &if &eq [numv] 1 &do
               SETD 5
            &end
            &getxym -1 -2 -3
         &end
      &else
         &rem **** add until cancel
         &getxym -1 -2 -3
         SETD 1
         &while &ne %-3 2 &do
            &if &eq [arct] BOX &do
               &rem **** box r.band for BOX
               &getxym -4 -5 -6 2
            &else
               &rem **** line r.band for RADIUS
               &getxym -4 -5 -6 1
            &end
            &if &ne %-6 2 &do
               &rem **** ADD IT!!!!
               GRAPHIC OFF
               COO KEY
               ADD
               2 %-1 %-2
               2 %-4 %-5
               9 0 0
               GRAPHIC ON
               COO [coo]
               DRAWSELECT
               &getxym -1 -2 -3
            &else
               &sv -3 2
            &end
         &end
      &end
The difference between NEW and ATTACH is that ATTACH uses the to_node of the previous arc as the from_node of the new one; the user simply adds vertices until the <2> key is pressed. As points are digitized, the MARKER command highlights the potential vertices.

Figure 2

A critical consideration in creating front ends to key menus is obeying the law of WYDIWYG (What You Digitize Is What You Get)! For that reason, each coordinate input after the first node is checked against the WEED tolerance: if it fails the test, it's not added to the coordinate list.

Note that boxes and circles are added immediately, allowing multiple ADDs before escaping to the dialog box. Coordinates are stored for lines so that the user may backup, clear, and/or redirect the potential arc. As will be shown shortly, if a point set for a line exists upon returning to the dialog box, the ATTACH and edit buttons are enabled:

Figure 3

Changing the ARCTYPE is simple enough:

   &elseif &eq [resp] 102 &do
      ARCTYPE LINE
   &elseif &eq [resp] 103 &do
      ARCTYPE BOX
   &elseif &eq [resp] 104 &do
      ARCTYPE CIRCLE
The BACK button decrements the point list and masks the corresponding markers with a dark color:

   &elseif &eq [resp] 106 &do
      &rem **** mask last point and decrement point list
      SETD 12
      &value [temp] %<[numv] + 1000>
      MARKER [temp]
      &dec [numv]
      SETD 7
Before:

Figure 4

After pressing twice:

Figure 5

The CLEAR button masks the entire point list and sets the counter to zero:

   &elseif &eq [resp] 107 &do
      &rem **** mask all points and clear point list
      SETD 12
      &sv [i] [numv]
      &while &rn [i] 1 [numv] &do
         &value [temp] %<[i] + 1000>
         MARKER [temp]
         &dec [i]
      &end
      &sv [numv] 0
      SETD 7
The RESUME button allows the user to continue adding points, e.g. after backing up:

   &elseif &eq [resp] 108 &do
      &rem **** resume adding points
      SETD 5
      &getxym -1 -2 -3
      &while &ne %-3 2 &do
         &inc [numv]
         &sv %<[numv] + 1000> "%-1 %-2"
         MARKER %-1 %-2
         &getxym -1 -2 -3
      &end
   &end
Before returning to WIN DB R, the line widgets are enabled or grayed as appropriate[4]:

   SHOW ARCTYPE [arct]
   &rem **** set LINE related widgets
   &openw [wksp]t$add.cmd
   &if &eq [arct] LINE &do
      &if &eq %<[numv] max 2> [numv] &do
         &write "E 105"
         &write "E 106"
         &write "E 107"
         &write "E 108"
      &elseif &eq %<[numv] max 1> [numv] &do
         &write "G 105"
         &write "E 106"
         &write "E 107"
         &write "E 108"
      &else
         &write "G 105"
         &write "G 106"
         &write "G 107"
         &write "G 108"
      &end
   &else
      &write "G 105"
      &write "G 106"
      &write "G 107"
      &write "G 108"
   &end
   &closew
&end
Finally, the routine cleans up and restores SETD settings before exiting:

&if &fn [wksp]t$add.cmd &do
   & DEL [wksp]t$add.cmd
&end
SETD [sd1]
SETD [sd2]
&if &eq [sd2] ON &do
   DRAWS
&end
&return
One nice feature of the routine is that it automatically adds the current point list when you quit (how many of us have forgotten to terminate an arc before hitting the <9> key?). Also, if you're supplying coordinates via a digitizer, you can watch the arc grow as you add vertices (one feature of 3.4D+ that I really miss).

Just as examining the WEED tolerance is important to maintain WYDIWYG when adding lines, examining the EDITDISTANCE is critical when manipulating vertices in a VERTEX key menu. In the following section of code, taken from an arc extend routine, the distance to the vertex next to the endpoint is compared to the edit distance—if necessary, the EDITD is altered before the endpoint is moved:

   SHOW ARC %[arc_f] VERTEX %[nod_t] [x1] [y1]
   SHOW ARC %[arc_f] VERTEX %<[nod_t] - 1> [x2] [y2]
   &cv [x2] [x2] - [x1]
   &cv [y2] [y2] - [y1]
   &cv [temp] ( ( [x2] * [x2] ) + ( [y2] * [y2] ) ) ** 0.5
   &cv [temp] [temp] / 2
   &if &eq %<[editd] min [temp]> [temp] &do
      EDITD [temp]
      &sv [temp] TRUE
   &else
      &sv [temp] FALSE
   &end
   V MOVE
   1 [x1] [y1]
   1 [x1] [y1]
   1 [int_t]
   9 0 0
   &if &eq [temp] TRUE &do
      EDITD [editd]
   &end

Capturing coordinates with &echo

The 3.5 &echo &watch directive, which is extremely useful for debugging, may also be used to record user actions. Because command input is bracketed with |> <| and coordinate input with |>* *<|, it is relatively simple to convert a watch file into a macro. The following routine captures ARCPLOT actions and converts them into an SML file:

&routine record
&define outfile -1  &var
&define inrec -2 &var
&define outrec -3 &var
&define wksp -4 &var
&define temp -9 &var
&rem **** create watch file
WIN FILE 2 SML 'Output SML File'
&if &eq "x%1" "x" &do
   &return
&end
&value -1 1
&value [wksp] WKSP
&if &fn [wksp]t$wat.wat &do
   & DEL [wksp]t$wat.wat
&end
&type "********************************"
&type "Begin entering ARCPLOT commands."
&type "Enter '&return' when finished."
&type "********************************"
&echo &watch [wksp]t$wat.wat
&tty
&echo &watch
&echo &off
&rem **** convert watch file to SML
&open [wksp]t$wat.wat error
&openw [outfile]
&while &do
   &read [inrec] [break]
   &extract [temp] [inrec] 2
   &if &eq "[temp]" "|>CIRCLE" &and &cn "[inrec]" "*" &do
      &value [temp] [inrec]
      &read [inrec] [break]
      &read [inrec] [break]
      &read [inrec] [break]
      &r ltrim "|>*" "[inrec]"
      &rv [inrec]
      &r rtrim "*<|" "[inrec]"
      &rv [outrec]
      &extract -11 [outrec] 1
      &extract -12 [outrec] 2
      &if &cn "[temp]" "*<|" &do
         &read [inrec] [break]
         &read [inrec] [break]
         &read [inrec] [break]
         &r ltrim "|>*" "[inrec]"
         &rv [inrec]
         &r rtrim "*<|" "[inrec]"
         &rv [outrec]
         &extract -13 [outrec] 1
         &extract -14 [outrec] 2
         &cv -13 %-13 - %-11
         &cv -14 %-14 - %-12
         &cv -13 ( ( %-13 * %-13 ) + ( %-14 * %-14 ) ) ** 0.5
      &else
         &r ltrim "*" "[temp]"
         &rv [inrec]
         &r rtrim "<|" "[inrec]"
         &rv -13
      &end
      &write " "
      &write "CIRCLE %-11 %-12 %-13"
   &elseif &cn "[inrec]" "|>*" &do
      &r ltrim "|>*" "[inrec]"
      &rv [inrec]
      &r rtrim "*<|" "[inrec]"
      &rv [outrec]
      &write "[outrec] |"
   &elseif &cn "[inrec]" "|>" &do
      &write " "
      &r ltrim "|>" "[inrec]"
      &rv [inrec]
      &if &cn "[inrec]" "*<|" &do
         &r rtrim "*<|" "[inrec]"
         &rv [outrec]
         &write "[outrec] |"
      &else
         &r rtrim "<|" "[inrec]"
         &rv [outrec]
         &write "[outrec]"
      &end
   &end
&end
&close
&closew
&rem & DEL [wksp]t$wat.wat
&return
&label error
&type "I/O Error"
&return

&routine ltrim
&value -3 -2 %<fpos "%-1" "%-2" + len "%-1">
&return "%-3"

&routine rtrim
&value -3 -2 1 %<lpos "%-1" "%-2" - 1>
&return "%-3"
Note the special handling of the CIRCLE command, which requires the input of a coordinate and radius value. The actions that created the following graphics:

Figure 6

are converted by the routine into the following macro:

CLEAR

LINE |
1.202362 2.128740 |
2.246063 3.569685 |
3.329134 2.181496 |
4.240945 3.649213 |

PATCH |
1.598425 2.115354 |
2.933071 2.512205 |

CIRCLE 3.289764 3.173229 0.407759

MOVE |
1.651575 3.807874 |

TEXT 'Naked Lunch'

&return
There are some obvious limitations to this approach:

1) Any line in the watch file longer than 80 characters will be truncated and most likely mishandled.

2) LINE or SHADE coordinate input is limited to a total of (I believe) around 120 characters. Thus more complex objects will be truncated.

3) You can't "freeze" statements involving SML variable substitutions. For example:

WIN EXEC 9
TEXTFONT %1
will be recorded verbatim in the watch file:

: |>WIN EXEC 9<|
: |>TEXTFONT %1<|

Next: More Fun with Widgets


[1]V5N6, "Customizing the ARCEDITW Toolbar in 3.5".

[2]I am, at this time, unable to test SML's ability to capture stream mode coordinates.

[3]Assuming that COLOR.MRK, or some suitable alternate, is the current markerset.

[4]The logic could be improved slightly by not building the dialog command file when [resp] equals -1.


Return to ArcTips page