Software Tools for Line Drawings

by Donald Daniel, 2018 revised mar 2024

Contents

If the lines of text are too long you can fix the problem with these instructions.

up one level

Introduction

This is about drawings that are made of lines, curved or straight, that go from a particular point to a particular point. Many points and lines will be used. That is the way drawings of useful objects usually are. By contrast, artistic drawings may use lines that go from no place in particular to no place in particular.

Line drawings today are usually done on a computer using a software package that already exists. This article is for people who would prefer to write a new program to draw a particular thing. If the program reads an input data file with dimensions and proportions of what they wish to draw, it will be easy to re-draw it with different dimensions and proportions.

The following set of line drawings was produced by a single program that used the software tools presented in this article.

file1.png

file2.png

file3.png

file4.png

file5.png

file6.png

Source code for a drawing program is provided at the end of this article. You will have to add source code to the program to draw whatever you want to draw. You will be able to see your drawing on the computer screen, to print your drawing at small scale on your printer, or to put your drawing in a web site. You can scale up your drawing to large scale, convert it to a pdf file, put it on a USB stick, and have a shop with a very large printer print it at large size. Such a shop might be called a blueprint shop, a reproduction shop or a graphics shop. You must draw a rectangular border around your figure to print it out on a large printer. The border tells the printer where to automatically cut the paper.

Before the age of computers line drawings of machine parts, clothing patterns, etc. were drawn by hand by a draftsman using a straight edge to draw straight lines and compasses to draw arcs. He used large and small compasses and dividers, a drop bow pen, a drafting board, a tee square, distance measuring scales, triangles, a protractor, pencils, ruling pens, lettering pens, india ink, a pen-filling ink stand, drafting paper and drafting tape. This free software replaces all of that expensive equipment and makes it easier to do precise drawings. The draftsman knew tricks to enable him to draw smooth curves that were far from simple arcs by carefully joining lines and arcs. The software tools presented here will enable you to draw such curves without learning the draftsman's tricks. The example below shows a smooth curve from point 1 to point 2 made of two arcs and a straight line.

sline.png

The drawings will start by positioning coordinates that establish the dimensions and proportions of what is to be drawn. Then the coordinates will be connected by drawing lines and smooth curves to draw the desired object.

Most computers now have available software called Postscript or a Postscript clone called Ghostscript. This software allows the drawing of lines and arcs and alphanumeric characters. It does not include the advanced tricks of the draftsman. The drawing program presented here draws lines, arcs and characters using Ghostscript but adds tools to make the job easier.

Computers can draw exotic mathematical curves. But practical drawing of objects long ago and in the present does not usually require such curves. Lines and arcs are sufficient. Practical drawing often relies on geometric construction. That is finding intersection points that will be used to draw lines, straight or curved. The intersection points may be of lines that have been drawn, or lines not drawn that are only temporarily used to find intersection points. Geometric construction is easy with straight lines and arcs, but difficult with cubic splines, bezier curves or other fancy mathematical ways of drawing lines.

The procedures already in this program provide extensive tools for drawing with lines and arcs. But if you have a special application that requires a mathematical curve, there is nothing that would prevent you from adding a procedure for that purpose to this software. For instance, there were laborious ways to use geometric construction to plot an ellipse with mechanical drafting tools. Those ways could be used with the procedures provided here. But it would make more sense for you to write a procedure to plot an ellipse using the equation for an ellipse that can be found in a textbook on analytic geometry or in a math handbook.

The source code of a basic drawing program including the software tools is presented. It is written in the Oberon-2 language and runs on the Ubuntu linux operating system. This operating system can be added to the Windows operating system already on your hard drive. If you wish to use a different programming language and operating system you will have to do the work of translating it from Oberon-2 to the language of your choice. Whether you wish to use this program as it is or translate it to another language you will need to refer to the how to program a computer article found elsewhere at this website. The article explains Oberon-2 and adding Ubuntu to Windows.

Tools for Coordinates

From now on the source code of the program will be discussed. You will have to look at the source code. Portions of the source code enclosed between (* and *) are comments for you to read, and do not affect the execution of the program. Also, parts of the program that are not being used in the present configuration of the program are commented out. This is so that when the program is compiled the compiler will not warn that these parts are not being used. If these parts of the program are later to be used, they will have to be uncommented. Commenting and uncommenting to get clean compilations will be a significant part of the effort required to use this program.

In the beginning of the program are global declarations. In the TYPE section of the declarations you can see the form of the coordinates, abbreviated as "coord". In the VAR section you can see that "cd" is an array of "coord". You will use array elements with the lowest index numbers first to assign locations of points needed to construct your drawing. Constants tempcoord1, tempcoord2, etc. are very high index numbers to be used for coordinates that are only of temporary use in constructing your drawing. There are many other temporary coordinates whose locations you will not assign. Their locations will be computed as a side effect by procedures that you may use. After you use the procedure, you can ignore these coordinates or you can if you wish copy the locations of these temporary coordinates to permanent coordinates whose locations have not yet been assigned.

Below the declarations find "PROCEDURE setcoord" which serves to assign a number to a new coordinate and place it where you want it on the drawing. Immediately below that is the procedure "moveto". The first parameter "crd" is the number of the coordinate you will move to. The second parameter "md" chooses whether you will draw a straight line from your present position to the coordinate or move without drawing a line. Its possible values are "move" or "draw".

The procedure "label" makes a coordinate visible as a spot in the drawing, and labels the spot with the number of the coordinate. As you construct your drawing you will need to label coordinates to keep track of where everything is. After you finish various parts of your drawing you will probably want to comment out the label commands. The second argument of the label command is the location around the spot where the coordinate number will be printed. Zero is to the right, and numbers 1 through 7 are in 45 degree increments in a counter clockwise direction.

The next procedure is "angle", which measures the angle of c2 from c1. Only positive angles are reported. The angles are measured counter clockwise from a horizontal direction pointing to the right. In most subsequent procedures that use angles only positive angles are acceptable. The angles must be in radians, not degrees. The angle 180 degrees is the angle pi in radians. A full circle is 2*pi. If you want to convert degrees to radians multiply the degrees by the constant "degtorad". If you want to convert radians to degrees multiply the radians by "radtodeg". If the procedure "angle" seems a bit complicated, it is because I wanted to avoid possible numerical difficulties by not using the function "arctan" beyond 45 degrees.

The procedure "cwccw" returns a real value of 1 if angle t2 is counter clockwise (ccw) from angle t1, or -1 if clockwise (cw).

The procedure "avgang" returns the average of two angles. Two points on a circle may be considered to have both an angle greater than 180 degrees and an angle less than 180 degrees between them. This average uses only the angle less than 180 degrees. The average of 0 and 1.5*pi is 1.75*pi, not 0.75*pi.

The next procedure "distance" uses the Pythagorean theorem to calculate the distance between two coordinates.

The procedure "newcoord" allows you to position a second coordinate at an angle and distance from the first coordinate. The second coordinate can have the same name as the first if you simply wish to move the location of a coordinate.

The procedure "midpoint" allows you to position a third coordinate a specified fraction of the way along a straight line between two coordinates.

We will ignore the procedure "mirror" because it is used by other procedures, and we will not normally use it ourselves.

The procedure "rotate" finds a new coordinate "nc" that is a result of rotating "oc" by an angle "dth" about center point "ctr". The angle dth is not an absolute angle, but a change from the original angle from ctr to oc. dth will normally be less than a full rotation and can be either positive or negative.

The procedure "intersect" finds the intersection of two straight lines defined by four coordinates. The intersection is found even if the lines are too short to reach the intersection.

The procedure "tangentarc" requires some explanation. We start with coordinates p1, p2 and "ofst". If an arc starts at ofst at angle ang, it will be tangent to a line p1-p2 only if the radius is chosen properly. Tangentarc finds the point on line p1-p2 where an arc with the right radius would be tangent to p1-p2. You do not have to specify the correct radius. The procedure does not draw the arc, it only finds the tangent point.

The procedure "circle" is primarily to draw small circles at coordinate locations to make the locations visible in the drawing. But circle could be used to draw a large circle if you need one.

The procedure "ntrsctarc" finds the first intersection of a line with an arc. The result is the coordinate "i1" in the parameter list.

The procedure "ntrsct2arc" finds the two intersection points of two overlapping circles. The intersection coordinates are not in the parameter list. They are the temporary coordinates "ntrsct1" and "ntrsct2". You can use the label command to plot your figure and see which is which. Then if you want one of the two assigned to a coordinate number of your choice, you would do so with a statement such as "cd[23]:=cd[ntrsct2]". The next time you use ntrsct2arc the coordinate ntrsct2 will refer to a different location than it did before.

Procedure "reflect" uses line o1,o2 like a door hinge to fold line c1,c2 over o1,o2 to create line r1,r2.

Procedure "shiftright" shifts the coordinates of a line to form coordinates of a new line. You can choose to name the temporary coordinates "shift11" and "shift12" or "shift21" and "shift22". That way you can use it twice and have two sets of temporary coordinates to locate an intersection of the shifted coordinates.

Procedure "tangentline" finds a point on a circle where a line from coordinate "ofst" outside of the circle would be tangent to the circle.

This is the end of the list of procedures that place or find coordinates.

Tools for Curve Drawing

Below will be different kinds of curves drawn in the procedure "test". The first part of test is the same for all examples. Only the code that computed each example will be reproduced here just before the curve is shown.

The procedure "arc" draws an arc through two or three of the points c1,c2,c3. Executing the procedure causes certain global variables to be assigned new values. These are the location of the coordinate cd[arccenter] and the angles "th1" and "th2". "th1" and "th2" are the angles from the arc center to the ends of the arc. These are also avialable after executing other procedures that produce curves which include an arc. The arc is always drawn in a counter clockwise (ccw) direction, even if the procedure that calls the arc can be called in the opposite direction. th1 is at the beginning of the arc and th2 is at the end of the arc. As an example of how useful these variables may be, the procedures ntrsctarc, ntrsct2arc and tangentline require the location of the center of an arc as an input parameter. Below is an example.

setcoord(1,11,1); setcoord(2,5,3); setcoord(3,1,1);
label(1,6);label(2,6);label(3,6);
arc(1,2,3,13,draw);

arc.png

The procedure "arcang" needs only two coordinates to draw an arc. The arc leaves c1 at angle ang1 and ends at c2. This may result in a cw or a ccw arc. But the procedure arcang internaly always calls the arc procedure for a ccw arc. Your program may need to know which way the arc bends. After executing the program the global boolean variable "forward" will be true if ccw, false if cw.

setcoord(1,11,1); setcoord(2,1,1); 
label(1,6);label(2,6);
arcang(140*degtorad,1,2,draw);

arcang.png

The procedure "arcline" draws an arc starting at c1 at an angle ang1. At a fraction "frac" of the distance from c1 to c2 the arc ends and a line goes the rest of the way to c2. Since both ang1 and frac can be changed, this procedure can produce a wide variety of curves. In the example below frac is 0.5. If frac were 0.01 the arc would disappear and the result would be a line from c1 to c2. If frac were 0.99 the result would be like the arcang example above. After executing arcline you will be left with the temporary coordinate "arcline2" where the arc meets the line.

setcoord(1,11,1); setcoord(2,1,1);
label(1,6);label(2,6);
arcline(140*degtorad,0.5,1,2,draw);

arcline.png

The procedure "midarc" draws a smooth curve through three points. The curve consists of two lines and an arc. The fraction "f" determines the size of the arc. If "f" is 0.01 the arc has almost zero radius and disappears. If "f" is 0.99 the arc is as large as it can be and almost reaches the outer point closest to the center point. One of the lines disappears. After executing the procedure you are left with temporary coordinates "mdacoord1" and "mdacoord2" which are the end points of the arc. In the example f is 0.7.

setcoord(1,11,1); setcoord(2,5,3);setcoord(3,1,1);
label(1,6);label(2,6);label(3,6);
midarc(1,2,3,draw,0.7);

midarc.png

The procedure "sline" makes "S" shaped curves. It connects two points with a smooth curve consisting of two arcs and a line. The angle that each arc leaves its point and the radius of each arc is adjustable, giving lots of freedom in shaping the curve. The curves must bend to make an "S", not a "C". The sum of the radii of the arcs must not be so large that the straight line portion goes to zero. If you make either mistake, the program will halt with a warning. After executing the procedure you will be left with the temporary coordinates "slt1" and "slt2" which are the ends of the straight line between the arcs.

setcoord(1,0,7);setcoord(2,10,10);
label(1,4);label(2,0);
sline(1,2,3,330*degtorad,5,180*degtorad);

sline.png

Since all of the above curve procedures leave you with knowledge of the angle of the ends of the curve, you can smoothly extend the curve.

Postscript provides another curve besides arcs, namely Bezier curves. In my experience you can waist a lot of time getting a Bezier curve to do what you want. I strongly recommend not using Bezier curves. With the curves provided here you should get a good enough curve with less waisted time. Bezier curves will change in a less predictable manner if you change the proportions of your drawing by changing the input data file.

Geometric Construction

While some of the procedures included here may eliminate some of the geometric construction that would otherwize be necessary, some geometric construction will probably be required in most drawings. Old fashioned mechanical drafting tools were typically used for geometric construction of drawings. This involved finding points where lines and or arcs intersected. The tools provided here are adequate for geometric construction. Repeatedly during the construction you will want to display your partially completed drawing on the screen. Below is a figure that was drawn with geometric construction.

durham.png

The procedure that produced this figure using the tools presented here follows.

PROCEDURE diag;
(*durham walk xcorg=1;ycorg=4;scale=72*) 
CONST uh=2;lh=0.5;
BEGIN
outfile.WriteString('/Bitstream-Charter-Bold ');
outfile.WriteLInt(fontsize,2);
outfile.WriteString(' selectfont '); outfile.WriteLn;
IF small THEN
xorg:=(xcorg+dxorg)*scale;yorg:=(ycorg+dyorg)*scale;ELSE
xorg:=(xcorgl+dxorg)*scale;yorg:=(ycorgl+dyorg)*scale;END;
setcoord(1,1,uh);label(1,5);
setcoord(2,1.5,uh);label(2,5);
setcoord(3,2,uh);label(3,5);
setcoord(4,2.5,uh);label(4,5);
setcoord(5,3,uh);label(5,5);
setcoord(6,3.5,uh);label(6,5);
ntrsct2arc(2,5,1.5,1.5);
cd[7]:=cd[ntrsct1];label(7,2);
arc(ntrsct2,5,7,23,draw);
arc(7,2,ntrsct2,12,draw);
ntrsct2arc(2,6,1.5,1.5);
arc(ntrsct1,3,ntrsct2,12,draw);
ntrsct2arc(2,6,1.5,1);
arc(ntrsct1,4,ntrsct2,12,draw);
ntrsct2arc(5,1,1.5,1);
arc(ntrsct1,3,ntrsct2,23,draw);
ntrsct2arc(5,1,1.5,1.5);
arc(ntrsct1,4,ntrsct2,23,draw);
setcoord(8,1.5,lh);
setcoord(9,2,lh);
setcoord(10,2.5,lh);
setcoord(11,3,lh);
moveto(8,move);moveto(2,draw);
moveto(9,move);moveto(3,draw);
moveto(10,move);moveto(4,draw);
moveto(11,move);moveto(5,draw);
moveto(8,move);moveto(11,draw);
arcang(50*degtorad,2,3,draw);
arcang(50*degtorad,3,4,draw);
arcang(50*degtorad,4,5,draw);
END diag;

To see some other drawings done in a similar way see this link.

Scale and Origin of the Plot

Any x,y coordinate graphic system must have an origin and scale. The origin of the postscript graphics is the lower left corner of the page. The scale is such that 72 postscript units represents one inch on the page. But that scale and origin is not likely to represent the requirements of your project. The scale and origin used in this program is what I needed for my last project. You can change the scale and origin to match the requirements of your project. In my project I was using an example in a book. In the example the origin was the upper left of the figure. The units were inches. The figure would be much larger than printed in the book. It would have to be printed on a large printer at a graphics shop. But I also needed to print the figure at small size so I could print it on my computer printer.

In the global declarations at the beginning of the program you can find xcorg and ycorg representing the origins in user units for small scale printing, and xcorgl and ycorgl representing the origins in user units for large scale printing. If you wanted the origin in the lower left corner you would set both to zero. At the beginning of the procedure "getdata" the user chooses between large scale and small scale. At that point the value of the variable "scale" is set according to the choice made.

You want user coordinates to be in user units. These might be inches, centimeters, meters, yards, miles or kilometers. In the sample program the large scale is 72. Since postscript has 72 units per inch, this means the a user unit of one represents one inch on the page. The small scale is 11.18. This means that one user unit is only 11.18/72=0.155 inches on the page. That is useful so a figure that would require a large printer could be printed on a small printer or could be viewed on the computer screen.

You want the user origin wherever you choose it to be in your drawing. It could be in the center, one of the four corners, or anywhere else you choose. If your program will draw several objects, like the illustrations at the beginning of this article, you may want the origin to be different for the different objects. If so you would declare different origins for different objects. You would declare origins in user units. Since the postscript origin is in the lower left corner, your user origins would be specified in user units relative to the lower left. In the program "xcorg" and "ycorg" are the coordinates of the user origin specified relative to the lower left in user sized units.

After you draw an object with the origin of your choice, you may want to print out multiple objects on the same page. To do that you will need to shift the origin of an object that has already been specified by a procedure. That is done by the variables "dxorg" and "dyorg". The first figure in this article has four items in the same drawing shifted to convenient locations by this method. The shifts are initialized to zero in the procedure "initvar". They remain zero until they are assigned new values in the procedure batchproc. The shifts were specified in "batchproc" which put out all the figures at one time. Batchproc is the place where the figures are converted to pdf format so you can put them on a USB stick and take them to a graphics shop.

The command in the border1 procedure "xorg:=xcorg*scale;" converts the origin in user units to postscript units. If you want your figure printed out on a large printer at a commercial shop you will have to have a rectangular border around it. This tells the printer how to automatically cut out the paper. In the procedures "lid" and "lidsize" the command "xorg:=(xcorg+dxorg)*scale; shifts the origin and converts it to postscript units. The border will not be shifted, put objects within the border can be shifted.

The array of coordinates "cd", is in user units. The drawing must actually be done in postscript units, not user units. The command "moveto" takes in user units and puts out postscript units as do all the procedures that actually draw on the page. The other procedures take in and put out only user units.

It is possible to use the original Postscript scale and origin. For this example we will use the full scale choice, but we could also have used the small scale choice. Set xcorgl=0; ycorgl=0;. Then in getdata set scale for the large choice to 1.0. Then in procedure test put this example:

setcoord(1,72,144);setcoord(2,306,648);
setcoord(3,540,144);
moveto(1,move);moveto(2,draw);moveto(3,draw);
moveto(1,draw);

Then use the program and choose the large scale. Draw the test example. You will get a large triangle that can be printed on 8.5 by 11 paper using the original Postscript origin and scale. You can use the vi editor to examine the file "temp1" and see the Postscript file that is plotted.

When through with this example be sure to set origin and scale values back to what they were because the examples depend on it.

Your printer will probably not allow you to print closer to the edge of the paper than a quarter inch, even though the Postscript coordinate system goes all the way to the edge of the paper.

Fonts

The drawings can be labeled with alphabetic characters. The font size is determined by the constant "fontsize" in the program. Most computer fonts are scaleable. The postscript command that selects the font you want also specifies the size you want the font to be. Font height is in postscript units of 1/72 inch. The size of fonts in your drawing is not scaled by the scale factor that scales the size of the drawing. You can used different font sizes in different parts of your drawing by initilizing the font in different places in your program. If you search for the word "fontsize" you can see the various places in the program where the font is initialized. If you want to see what other fonts are available for you to use then in a terminal window execute the command "gnome-font-viewer". The procedure "label" can be called to label any coordinate.

Compiling and Running the Program

It is assumed that you have read the article "how to program a computer" at this website at least up to the part about how to compile and run a program. Also you should have read the articles at this website "how to use the linux terminal" and "how to use the vi editor in linux".

You could use the vi editor to write the program in this html article as a text file. But text is different from html. Some of the lines in the text file will have the & character. These lines will have to be changed. When viewing these lines in the text file with the vi editor the lines should look the same as when viewing the html version in your browser. Once these lines have been fixed, if the program is moved to the "src" directory, the program can be compiled with the command "oocm curve". This command will be executed from the directory "prog" above the directory "src".

The input data file required to run the program is called "lidfile". It must be in the directory "prog". It is listed below:

"john smith"
16 wide 
14 high

In the trivial example we are using only two numbers are needed in the data file. If we were drawing something more complicated several numbers might be required.

The name "john smith" may not be needed for your application. Its purpose is if you wish to use the same program to send different drawings to different people where each person sends you a different data file to use in the program.

An example of running the program is given below:


--->bin/curve
enter 1 for small scale, 2 for full scale
--->1
enter 1 for plain, 2 for inlays and turnings
--->2
enter filename
--->lidfile
 16.00 wide
 14.00 high
draw, prtcrd, dist, ang, print, batch, q
enter command
--->draw
lid
 border1, test, q
enter command
--->lid
enter command
--->border1
enter command
--->q
press enter to view plot, then 
enter quit to finish viewing plot

quit
draw, prtcrd, dist, ang, print, batch, q
enter command
--->q

In the example the lines that start with ---> were entered by the user. The rest were responses by the program. The plot would be displayed on the screen during this process. The plot is a trivial example of a plot, the lid for a box. The dashed lines at the edge of the lid are the parts of the lid that are to be folded down over the edge of the box. The folded parts are referred to as inlays in the program. A large rectangular border is drawn around the lid. The purpose of this border is for use when the picture is drawn at large scale to be plotted on a large plotter at a graphics shop. The large plotter requires a rectangular border so it can automatically cut the paper about a half inch beyond the border. Large plotters are good, but not perfect. If your border is 50 inches long, the error may be a half inch.

The program only draws one item, a lid. Your program may need to draw several items. The drawings are normally written in ps format, or Postscript format. But if you select the command batch, they will be drawn in pdf format. The large plotters in graphics shops require pdf format.

The procedure batchproc in the program can be expanded to draw all the different items you wish to draw.

If you chose to draw "test", then the example in the procedure testproc would be displayed. When the program is finished, the drawing is in ps format in the file "temp1". If you use the command "gv temp1" you can see it. But if you want to put it in a website you will need to convert it to png format. In ps format the figure is drawn on a large sheet of paper even if you have not yet printed it. But you do not want to convert the large sheet of paper to png, only the figure that might be much smaller than the paper. To convert only the figure to png use the script "xform2p". The command "./xform2p temp1" will produce "temp1.png" which is what you want. You can see it with the command "display temp1.png" provided you have the imagemagick package installed. The script xform2p is listed below:

# convert ps to png 
namechange ()
{
oldname=$1
newname=${oldname%.ps}.png
echo "$newname"
pstopnm -stdout "$oldname" \
| pnmcrop | pnmscale 0.5 \
| pnmtopng > "$newname"
}
for filename in "$@" ; do
namechange "$filename"
done

You can change the name "temp1.png" to "newname.png" with the command "mv temp1.png newname.png".

Drawing your Application

In the example in this program the procedure lidsize computes the main coordinates that define the figure. In a more complicated example such a procedure might compute the coordinates of a complicated structure that would be broken into several figures.

The procedure lid actually draws the figure. In a more complicated example several such procedures might separately draw pieces of a complicated figure. In the procedure lid the first two lines after "IF inlays" change the mode of drawing to dashed lines. The last two lines of that section turn off the dashed line mode.

The Program

To use this program you must have ubuntu linux on your computer. You must install the oberon compiler as instructed in the article how to program a computer elsewhere at this site. Be sure to create the directories recommended and the script oocm. To use this program you must save this article to your scratch directory. The most reliable way to do this is in your scratch directory issue the command "wget waltzballs.org/other/engr/curve.html". Then in that directory issue the command "unhtml curve.html > curve.text". Open curve.text in the vi editor. With the cursor at the top of the file type "d /MODULE curve". That will delete everything down to the program. You can then save the program as curve.Mod in the directory ~/wkspc/prog/src. You can compile it from the directory above ~/wkspc/prog with "oocm curve". Then you can execute it from that directory with "bin/curve".

MODULE curve;
(*written 2018 donald daniel.  
This program is free software:
you can redistribute it and/or modify it under the terms of the
GNU General Public License as published by the Free Software
Foundation, version 3 of the License.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General
Public License along with this program.  If not, see
http://www.gnu.org/licenses/.  *)
IMPORT In,Out,Msg,Files,TextRider,
OS:ProcessManagement,rm:=RealMath,IntStr,Strings,Object;
CONST numcoords=320;er=0;q=1;drawv=3; print=4;lidv=5; 
prtcrd=9; test=11; dist=17;batch=18;ang=19;
bdr3=22; last=26; move=1;draw=2;fontsize=14;
pi=rm.pi; degtorad=pi/180; radtodeg=180/pi; 
(*origins*)
xcorg=14.49;ycorg=57.96; xcorgl=3;ycorgl=65;
(*coords named tempcoord are for user. temporary coords
for tools must be assigned positions only by each tool.*)
tempcoord1=270;tempcoord2=271; (*tempcoord3=272;
tempcoord4=273; tempcoord5=274;reflectcoord=275;*)
(*281 to 286 used by testproc*)
(*ntrsct2arc ntrsct1=288;ntrsct2=289; *)
(* arc*) arccenter=290; arccoord1=291;
arccoord2=292;arccoord3=293;arccoord4=294;
(*tangentarc pmcoord1=295;pmcoord2=296;*)
(* midarc mdacoord1=297;mdacoord2=298; mdacoord3=299;
mdacoord4=300;mdacoord5=301;*)
(*shiftright*) shift11=302; shift12=303;shift21=304;shift22=305;
(*arcang *)arcangcd=306;
(*label*) labcoord=307;
(* arcline arcline1=308;arcline2=309;*)
(*sline*) slc1=310;slc2=311;slcc=312;slt1=313;slt2=314;
TYPE cstr=ARRAY 10 OF CHAR; str=ARRAY 40 OF CHAR;
coord = RECORD x,y:REAL END;
VAR user:str;cmdara:ARRAY last+1 OF cstr; filename:str;
command,coordv,choice:LONGINT;
str1:str;invar,outvar:Files.File;resw:Msg.Msg;
infile:TextRider.Reader; outfile:TextRider.Writer;
cd:ARRAY numcoords OF coord; 
(*temp1,temp2,temp3,temp4,*)xorg,yorg,
scale,dashv,widthv,dxorg,dyorg,wide,high,
(*, arc*) th1,th2,arcradius :REAL;
small,inlays,forward:BOOLEAN;

PROCEDURE initvar;
BEGIN
cmdara[er]:='er';
cmdara[q]:='q';
cmdara[drawv]:='draw';
cmdara[print]:='print';
cmdara[prtcrd]:='prtcrd';
cmdara[dist]:='dist';
cmdara[ang]:='ang';
cmdara[test]:='test';
cmdara[batch]:='batch';
cmdara[bdr3]:='border1';
cmdara[lidv]:='lid';
cmdara[last]:='last';
dxorg:=0.0;dyorg:=0.0;
END initvar;

PROCEDURE determine(VAR cmdvar:LONGINT);
CONST bell=7;
VAR i:LONGINT;str1:cstr;
BEGIN
Out.String('enter command');Out.Ln; cmdvar:=er;
In.Identifier(str1);
FOR i:=q TO last DO
IF (cmdara[i]=str1)THEN cmdvar:=i;END;END;
IF cmdvar=er THEN
(*the line below must be modified to look the way it looks
when seen in your browser*)
Out.Char(CHR(bell));Out.String('<--<< error');
Out.Ln;END; END determine;

PROCEDURE initfile;
BEGIN
outvar:=Files.New('temp1',{Files.write},resw);
outfile:=TextRider.ConnectWriter(outvar);
outfile.WriteString('%!PS');outfile.WriteLn;
END initfile;

PROCEDURE width(w:REAL);
BEGIN
outfile.WriteRealFix(w,8,2);
outfile.WriteString (' setlinewidth'); outfile.WriteLn;
outfile.WriteString('stroke');outfile.WriteLn;
END width;

PROCEDURE dash(w:REAL);
(*it is assumed that width will be called
right after dash, because dash has no 'stroke' *)
BEGIN
outfile.WriteString('[ '); outfile.WriteRealFix(w,8,2);
outfile.WriteRealFix(w,8,2); 
outfile.WriteString(' ] 0 setdash'); outfile.WriteLn;
END dash;

PROCEDURE getdata;
BEGIN
Out.String('enter 1 for small scale, 2 for full scale');
Out.Ln;In.LongInt(choice);
IF(*scale*) choice=2 THEN small:=FALSE;
scale:=72.0; widthv:=2.8;dashv:=3;
ELSE small:=TRUE;scale:=11.18; 
widthv:=1;dashv:=2;END(*scale*);
Out.String('enter 1 for plain, 2 for inlays and turnings');
Out.Ln;In.LongInt(choice);
IF(*inlays*) choice=2 THEN inlays:=TRUE; ELSE inlays:=FALSE; 
END(*inlays*);
Out.String('enter filename');Out.Ln; In.Identifier(filename);
invar:=Files.Old(filename,{Files.read},resw);
infile:=TextRider.ConnectReader(invar);
infile.ReadString(user);infile.ReadLine(str1);
infile.ReadReal(wide); infile.ReadLine(str1);
Out.RealFix(wide,6,2);Out.String(' wide');Out.Ln;
infile.ReadReal(high); infile.ReadLine(str1);
Out.RealFix(high,6,2);Out.String(' high');Out.Ln;
invar.Close;
END getdata;

PROCEDURE printcoord(i:LONGINT);
BEGIN
Out.String('coord '); Out.LongInt(i,4); 
Out.RealFix(cd[i].x,12,2); 
Out.RealFix(cd[i].y,12,2);Out.Ln;
END printcoord;

 PROCEDURE setcoord(coord:LONGINT;x,y:REAL);
BEGIN cd[coord].x:=x;cd[coord].y:=y;END setcoord; 

 PROCEDURE moveto(crd,md:LONGINT);
VAR x,y:REAL;
BEGIN
x:=cd[crd].x;y:=cd[crd].y; x:=xorg+scale*x;y:=yorg+scale*y;
outfile.WriteRealFix(x,10,2); outfile.WriteRealFix(y,10,2);
IF md=move THEN 
outfile.WriteString(' moveto');
ELSE outfile.WriteString(' lineto');END;
outfile.WriteLn; END moveto;

PROCEDURE angle(c1,c2:LONGINT):REAL;
(*angle of vector from c1 to c2 in radians. The angle
is positive only, thus -10 degrees is reported as
plus 350 degrees, but in radians.*)
VAR x,y,ax,ay,at:REAL;
BEGIN
x:=cd[c2].x-cd[c1].x;y:=cd[c2].y-cd[c1].y;
ax:=ABS(x);ay:=ABS(y);
IF y >= 0 THEN 
  IF x >= 0 THEN 
   IF ay < ax THEN at:=rm.arctan(ay/ax);
   ELSE at:=0.5*pi-rm.arctan(ax/ay);END;
  ELSE 
   IF ay < ax THEN at:=pi-rm.arctan(ay/ax);
   ELSE at:=0.5*pi+rm.arctan(ax/ay);END;
  END;
ELSE
  IF x <= 0 THEN
   IF ay < ax THEN at:=pi+rm.arctan(ay/ax);
   ELSE at:=1.5*pi-rm.arctan(ax/ay);END;
  ELSE
   IF ay < ax THEN at:=2*pi-rm.arctan(ay/ax);
   ELSE at:=1.5*pi+rm.arctan(ax/ay);END;
  END;
END;
RETURN at END angle;

PROCEDURE distance(c1,c2:LONGINT):REAL;
(*straight line distance between two points*)
VAR dx,dy:REAL;
BEGIN
dx:=cd[c1].x-cd[c2].x;dy:=cd[c1].y-cd[c2].y;
RETURN ABS(rm.sqrt(dx*dx+dy*dy)); END distance;

PROCEDURE newcoord(oc,nc:LONGINT;th,ln:REAL);
(*new coord at angle and distance from old coord.
If you want the opposite direction of an angle, 
do not negate it, but subtract pi.*)
BEGIN
cd[nc].x:=cd[oc].x+ln*rm.cos(th); 
cd[nc].y:=cd[oc].y+ln*rm.sin(th); END newcoord;

PROCEDURE midpoint(c1,c2,cm:LONGINT;fract:REAL);
(*new coord along line from c1 to c2 fraction of way*)
VAR cx,cy:REAL;
BEGIN
cx:=(cd[c2].x-cd[c1].x)*fract+cd[c1].x;
cy:=(cd[c2].y-cd[c1].y)*fract+cd[c1].y;
cd[cm].x:=cx; cd[cm].y:=cy; END midpoint;

(*PROCEDURE rotate(ctr,oc,nc:LONGINT;dth:REAL);
(*coord rotated about ctr at angle dth*)
VAR cx,cy,r,th1,th2:REAL;
BEGIN
r:=distance(ctr,oc);th1:=angle(ctr,oc);
th2:=th1+dth;
cx:=cd[ctr].x+r*rm.cos(th2); cy:=cd[ctr].y+r*rm.sin(th2);
cd[nc].x:=cx;cd[nc].y:=cy; END rotate;*)

(*PROCEDURE mirror(other,center,offset,mirpt:LONGINT);
(*mirror offset about center for arc. other,center
on line, offset,mirpt ends of arc tangent to line*)
VAR dist,lineang,offsetang,perpang,mirang,diffang:REAL;
BEGIN
dist:=distance(center,offset);
lineang:=angle(other,center);
offsetang:=angle(center,offset);
perpang:=lineang-pi/2;
diffang:=offsetang-perpang;
IF ABS(diffang)>pi/2 THEN
perpang:=perpang-pi;
diffang:=offsetang-perpang;END;
mirang:=perpang-diffang;
newcoord(center,mirpt,mirang,dist);
END mirror;*)

PROCEDURE intersect(p11,p12,p21,p22,i:LONGINT);
(*i is the coordinate that is the intersection
of two straight lines, p11 to p12, and p21 to p22.
The interesection point will be calculated even
if the lines are too short to reach the intersection*)
VAR x1,y1,x2,y2,x3,y3,x4,y4,n1,d1,n2,d2:REAL;
BEGIN
x1:=cd[p11].x;y1:=cd[p11].y;x2:=cd[p12].x;y2:=cd[p12].y;
x3:=cd[p21].x;y3:=cd[p21].y;x4:=cd[p22].x;y4:=cd[p22].y;
n1:=((x1*y2-y1*x2)*(x3-x4))-((x1-x2)*(x3*y4-y3*x4));
d1:=((x1-x2)*(y3-y4))-((y1-y2)*(x3-x4));
n2:=((x1*y2-y1*x2)*(y3-y4))-((y1-y2)*(x3*y4-y3*x4));
d2:=((x1-x2)*(y3-y4))-((y1-y2)*(x3-x4));
cd[i].x:=n1/d1;cd[i].y:=n2/d2;
END intersect;

(*PROCEDURE tangentarc(ang:REAL;ofst,p1,p2,pt:LONGINT);
(*Find tangent point pt on line p1-p2 that will connect
with arc to offset point. The arc will leave ofst at angle
ang.  The direction from p1 to p2 is the direction of the
end of the arc at the tangent point.  Later mirror can be
called to mirror ofst about pt.  *)
VAR ang2,dist:REAL;
BEGIN
newcoord(ofst,pmcoord1,ang-pi,10);
(*pi is 180 degrees, pmcoord1 and ofst are on same line
with angle ang*)
intersect(pmcoord1,ofst,p2,p1,pmcoord2);
ang2:=angle(p1,p2);dist:=distance(ofst,pmcoord2);
newcoord(pmcoord2,pt,ang2,dist);
END tangentarc;*)

PROCEDURE circle(c1:LONGINT;r:REAL);
VAR cx,cy:REAL;
BEGIN
r:=r*scale;
outfile.WriteRealFix(widthv,8,2);
outfile.WriteString (' setlinewidth'); outfile.WriteLn;
outfile.WriteString('stroke');outfile.WriteLn;
cx:=xorg+scale*cd[c1].x;cy:=yorg+scale*cd[c1].y;
outfile.WriteRealFix(cx,14,3); 
outfile.WriteRealFix(cy,14,3);
outfile.WriteRealFix(r,14,3); 
outfile.WriteString('  0 360 arc'); outfile.WriteLn;
outfile.WriteRealFix(widthv,8,2);
outfile.WriteString (' setlinewidth'); outfile.WriteLn;
outfile.WriteString('stroke');outfile.WriteLn;
END circle;

PROCEDURE arc(c1,c2,c3,range,md:LONGINT);
(*c1,c2,c3 coords define arc. They must be in order of
ccw movement around arc. range=13 means arc from c1 to
c3, 12 from c1 to c2, 23 from c2 to c3.  After invoking
this procedure cd[arccenter] will be the center of the
arc. th1 and th2 will be the angles from the arc center
to the beginning and end of the arc. arcradius will be
the radius of the arc. *)
VAR ang12,ang23,dist,cx,cy,r:REAL;
BEGIN
ang12:=angle(c1,c2)-pi/2;
ang23:=angle(c2,c3)-pi/2;
dist:=distance(c1,c2);
IF distance(c2,c3)>dist THEN dist:=distance(c2,c3);END;
midpoint(c1,c2,arccoord1,0.5);
newcoord(arccoord1,arccoord2,ang12,dist);
midpoint(c2,c3,arccoord3,0.5);
newcoord(arccoord3,arccoord4,ang23,dist);
intersect(arccoord2,arccoord1,arccoord4,arccoord3,arccenter);
cx:=cd[arccenter].x;cy:=cd[arccenter].y;
r:=distance(arccenter,c1);
arcradius:=r;
r:=scale*r;cx:=xorg+scale*cx;cy:=yorg+scale*cy;
IF (range=12)OR(range=13)THEN moveto(c1,move)
ELSE moveto(c2,move);END;
IF md=draw THEN 
outfile.WriteRealFix(cx,12,2); outfile.WriteRealFix(cy,12,2);
outfile.WriteRealFix(r,12,2);END;
IF range=13 THEN
th1:=angle(arccenter,c1); th2:=angle(arccenter,c3);
ELSIF range=12 THEN th1:=angle(arccenter,c1);
th2:=angle(arccenter,c2);
ELSIF range=23 THEN
th1:=angle(arccenter,c2); th2:=angle(arccenter,c3);
ELSE Out.String('range out of range');Out.Ln;
HALT(1);END;
IF md=draw THEN outfile.WriteRealFix(th1*radtodeg,12,2); 
outfile.WriteRealFix(th2*radtodeg,12,2);
outfile.WriteString(' arc'); outfile.WriteLn;
END(*if md*);
END arc;

PROCEDURE cwccw(t1,t2:REAL):REAL;
(*returns -1 if angle t2 cw from t1, +1 if ccw*)
BEGIN
IF ABS(t2-t1)>pi THEN 
IF t2>t1 THEN t2:=t2-2.0*pi ELSE t1:=t1-2.0*pi END;END;
IF t2-t1>=0.0 THEN RETURN 1 ELSE RETURN -1 END;
END cwccw;

(*PROCEDURE avgang(t1,t2:REAL):REAL;
(*the average of two angles. we want the acute
angle between two points on a circle. thus the
average of 0 and 1.5*pi is 1.75*pi, not 0.75*pi.
this proceedure assumes both angles are positive,
and neither is greater than 2*pi*)
BEGIN
IF ABS(t2-t1)>pi THEN 
IF t2>t1 THEN t2:=t2-2.0*pi ELSE t1:=t1-2.0*pi END;END;
RETURN (t1+t2)*0.5;
END avgang;*)

(*PROCEDURE midarc(p1,p2,p3,md:LONGINT;f:REAL); (*line
from p1, arc with p2 in the middle, line to p3. Order of
points must trace arc ccw.  f is fraction of the largest
radius of arc that can be used.  This invokes arc, so
arccenter, th1, th2 will be useable after invocation.
After finish mdacoor1 and mdacoord2 store position of arc
ends, mdacoord3 stores arc center.*)
VAR r,r1,ang1,ang3,ang14,ang34,ang12,ang32,ang1x,ang3x,
angt,ang21,ang23,ang24,d21,d23,d41,d43,d1x,d3x:REAL;
p4,p1x,p3x:LONGINT;
BEGIN
ang21:=angle(p2,p1);ang23:=angle(p2,p3);
ang24:=avgang(ang21,ang23);
d21:=distance(p2,p1);d23:=distance(p2,p3);
IF d21 < d23 THEN 
angt:=ang21-pi/2;r1:=0.4*d21;
midpoint(p2,p1,mdacoord4,0.5);
newcoord(mdacoord4,mdacoord5,angt,r1);
ELSE angt:=ang23+pi/2; r1:=0.4*d23;
midpoint(p2,p3,mdacoord4,0.5);
newcoord(mdacoord4,mdacoord5,angt,r1);
END;
p4:=mdacoord3;
newcoord(p2,p4,ang24,r1);
intersect(mdacoord4,mdacoord5,p2,p4,p4);
midpoint(p2,p4,p4,f);
r:=distance(p2,p4);
d41:=distance(p4,p1);d43:=distance(p4,p3);
d1x:=rm.sqrt(d41*d41-r*r);
d3x:=rm.sqrt(d43*d43-r*r);
ang1:=rm.arcsin(r/d41);
ang3:=rm.arcsin(r/d43);
ang14:=angle(p1,p4);ang12:=angle(p1,p2);
ang34:=angle(p3,p4);ang32:=angle(p3,p2);
ang1x:=ang14+cwccw(ang14,ang12)*ang1;
ang3x:=ang34+cwccw(ang34,ang32)*ang3;
p1x:=mdacoord1;p3x:=mdacoord2;
newcoord(p1,p1x,ang1x,d1x);
newcoord(p3,p3x,ang3x,d3x);
moveto(p1,move);
IF md=draw THEN moveto(p1x,draw);
arc(p1x,p2,p3x,13,draw); moveto(p3,draw); 
ELSE moveto(p1x,move);
arc(p1x,p2,p3x,13,move); moveto(p3,move); END;
END midarc;*)

PROCEDURE ntrsctarc(ctr,o1,o2,i1:LONGINT;r:REAL);
(*first intersection point i1 found of line from o1 to
o2 with circle at ctr of radius r. Order of o1 and o2
matters. Triangle formula where one angle and two sides
known. Use quadratic key: a*x*x+b*x+c=0 . o1 cannot
be repeat of ctr*)
VAR sda,sdb,sdc,ang,angc,a,b,c:REAL;
BEGIN
IF o1=ctr THEN 
Out.String('o1 cannot be ctr in ntrsctarc');Out.Ln;
HALT(1);END;
ang:=angle(o1,o2); sdc:=r;angc:=angle(o1,ctr)-ang;
sda:=distance(o1,ctr); a:=1;b:=-2*sda*rm.cos(angc);
c:=sda*sda-sdc*sdc; sdb:=(-b-rm.sqrt(b*b-4*a*c))/(2*a);
newcoord(o1,i1,ang,sdb); END ntrsctarc;

(*PROCEDURE ntrsct2arc(ctr1,ctr2:LONGINT;r1,r2:REAL);
(*two intersection points of two circles found by triangle
formula c^2=a^2+b^2-2ab(cos(c)). r1 goes with ctr1, r2
goes with ctr2, Resulting points are ntrsct1 and ntrsct2.*)
VAR d,ad,ac,a,b,c:REAL;bad:BOOLEAN;
BEGIN
bad:=FALSE;
d:=distance(ctr1,ctr2); IF d>(r1+r2)THEN 
Out.String('ntrsct2arc abort centers too far apart');Out.Ln;
HALT(1);END;
IF r1>(d+r2)THEN
Out.String('ntrsct2arc r1 too great');Out.Ln;HALT(1);END;
IF r2>(d+r1)THEN
Out.String('ntrsct2arc r2 too great');Out.Ln;HALT(1);END;
ad:=angle(ctr1,ctr2);
c:=r2;a:=d;b:=r1;
ac:=rm.arccos((a*a+b*b-c*c)/(2*a*b));
newcoord(ctr1,ntrsct1,ad+ac,r1);
newcoord(ctr1,ntrsct2,ad-ac,r1);
END ntrsct2arc;*)

(*PROCEDURE reflect(c1,c2,o1,o2,r1,r2:LONGINT);
(*reflect line c about line o to produce line r *)
BEGIN
newcoord(o1,reflectcoord,angle(o2,o1)-pi/2,10);
mirror(reflectcoord,o1,c1,r1);
mirror(reflectcoord,o1,c2,r2);
END reflect;*)

PROCEDURE shiftright(c1,c2,i:LONGINT;s:REAL);
(*create temporary coordinates "shiftxx" shifted
to the right a distance s from the coordinates c1,c2.
i is 1 or 2 to designate shift1x or shift2x so two
sets of temporary shift coordinates can exist at the
same time, for calculating the intersection of shifted
lines. WARNING: if you get mixed up about whether the
first or second digit is determined by i, your program
may blow up!*)
BEGIN
IF (i=1) OR (i= 2)THEN ELSE Out.String('shiftright i wrong');
Out.Ln;HALT(1);END;
IF i=1 THEN
newcoord(c1,shift11,angle(c1,c2)-pi/2,s);
newcoord(c2,shift12,angle(c1,c2)-pi/2,s);
ELSE
newcoord(c1,shift21,angle(c1,c2)-pi/2,s);
newcoord(c2,shift22,angle(c1,c2)-pi/2,s);END;
END shiftright;

PROCEDURE tangentline(ctr,ofst,tp:LONGINT;r:REAL;left:BOOLEAN);
(*find tangent point tp on circle with center ctr and
radius r that will connect with straight line to offset
point ofst. As seen from ctr looking at ofst, if tp is on
left side of circle then left is true, otherwize tp is on
right side of circle.*)
VAR ang,dist:REAL;
BEGIN
dist:=distance(ctr,ofst);
ang:=rm.arccos(r/dist);
IF left THEN ang:=-ang;END;
newcoord(ctr,tp,angle(ctr,ofst)-ang,r);
END tangentline;

PROCEDURE arcang(ang1:REAL;c1,c2,md:LONGINT);
(*draw arc that starts at c1 at angle ang1, and ends at
c2. c1 and c2 may be specified in any order, but arc will
finish at ccw end.  *)
VAR ang2,ang3,dist,angdiff:REAL;
BEGIN
forward:=TRUE;
dist:=distance(c1,c2);
ang2:=angle(c1,c2);
angdiff:=ang1-ang2; 
ang3:=ang1-pi;
newcoord(c1,arcangcd,angdiff+ang3,dist);
IF cwccw(ang1,ang2) < 0 THEN forward:=FALSE;
moveto(c2,move);arc(c2,c1,arcangcd,12,md)
ELSE moveto(c1,move);arc(arcangcd,c1,c2,23,md) END;
END arcang;

(*PROCEDURE arcline(ang1,frac:REAL;c1,c2,md:LONGINT);
(*Draw arc that starts at c1 at angle ang1. At
approximately the fraction frac of the straight line
distance from c1 to c2 it smoothly transistions to a
line to c2. arcline2 is point where arc meets line. The
fractional approximation is only good for small angular
differences between the arc and the straight line from c1
to c2. arccenter and arcradius will be left over*)
VAR diff,dist:REAL;
BEGIN
frac:=0.5*frac;
diff:=ang1-angle(c1,c2);
dist:=frac*distance(c1,c2);
newcoord(c1,arcline1,ang1,dist/rm.cos(diff));
tangentarc(ang1,c1,arcline1,c2,arcline2);
arcang(ang1,c1,arcline2,md);
moveto(arcline2,move);
moveto(c2,md);
END arcline;*)

PROCEDURE sline(c1,c2:LONGINT;r1,a1,r2,a2:REAL);
(*draw s curve to connect points c1 and c2. Curve will be
two arcs with straight line segment between. Arcs leave
points c1 and c2 at specified angles a1, and a2. Radii
of arcs are specified to be r1 and r2. Centers of arcs
are computed to be slc1 and slc2. Point slcc is on line
between slc1 and slc2 at point where straight line segment
will cross. Ends of straight line segment will join arcs
at tangent points slt1 and slt2. As seen from c1 looking
at c2, if arc leaves c1 to left then left is true.*)
VAR ac1,ac2,ang,dist:REAL;left:BOOLEAN;
BEGIN
ang:=angle(c1,c2);
IF cwccw(ang,a1)=1 THEN left:=TRUE ELSE left:=FALSE;END;
IF (cwccw(ang,a1)*cwccw(ang+pi,a2))=-1 THEN 
Out.String('the proceedure sline is specified as C curve,');Out.Ln;
Out.String('it must be an S curve');Out.Ln;
Out.Ln;HALT(1);END;
IF left THEN ac1:=a1-pi/2;ac2:=a2-pi/2;
ELSE ac1:=a1+pi/2;ac2:=a2+pi/2;END;
newcoord(c1,slc1,ac1,r1);newcoord(c2,slc2,ac2,r2);
dist:=distance(slc1,slc2);
IF dist < (r1+r2) THEN 
Out.String('in procedure sline r1+r2 too great');
Out.Ln; HALT(1);END;
midpoint(slc1,slc2,slcc,r1/(r1+r2));
tangentline(slc1,slcc,slt1,r1,left);
tangentline(slc2,slcc,slt2,r2,left);
moveto(c1,move); arcang(a1,c1,slt1,draw);
moveto(slt1,move);moveto(slcc,draw);
moveto(slt2,draw);moveto(c2,move);
arcang(a2,c2,slt2,draw);
END sline;

PROCEDURE zerocoords;
VAR i:LONGINT;
BEGIN
FOR i:=0 TO numcoords-1 DO 
cd[i].x:=0.0;cd[i].y:=0.0;END;
END zerocoords;

PROCEDURE outint(i:LONGINT);
BEGIN
outfile.WriteString('(');
outfile.WriteLInt(i,2);
outfile.WriteString(')');
outfile.WriteString(' show');
outfile.WriteLn;
END outint;

PROCEDURE label(coord,location:LONGINT);
VAR ang,ln:REAL;
BEGIN
ang:=location*pi/4;
ln:=0.6*fontsize/scale;
(*the following line compensates for offset of
where character is printed relative to the location*)
newcoord(coord,labcoord,1.2*pi,0.6*fontsize/scale);
(*the following line positions it around the location*)
newcoord(labcoord,labcoord,ang,ln);
moveto(labcoord,move); outint(coord); circle(coord,2.16/scale);
END label;

PROCEDURE border1;
BEGIN
zerocoords;
IF small THEN
xorg:=xcorg*scale;yorg:=ycorg*scale;ELSE
xorg:=xcorgl*scale;yorg:=ycorgl*scale;END;
setcoord(0,-3,6);
setcoord(1,32,6);
setcoord(2,32,-36);
setcoord(3,-3,-36);
moveto(0,move);moveto(1,draw);moveto(2,draw);
moveto(3,draw);moveto(0,draw);
newcoord(3,tempcoord1,0,1);moveto(tempcoord1,move);
newcoord(tempcoord1,tempcoord2,pi/2,1);
moveto(tempcoord2,draw);
newcoord(2,tempcoord1,pi,1);moveto(tempcoord1,move);
newcoord(tempcoord1,tempcoord2,pi/2,1);
moveto(tempcoord2,draw);
newcoord(1,tempcoord1,1.5*pi,2);moveto(tempcoord1,move);
newcoord(tempcoord1,tempcoord2,pi,1);
moveto(tempcoord2,draw);
newcoord(2,tempcoord1,pi/2,2);moveto(tempcoord1,move);
newcoord(tempcoord1,tempcoord2,pi,1);
moveto(tempcoord2,draw);
END border1;

PROCEDURE lidsize;
CONST xctr=14.5;yctr=-15.0;
BEGIN
IF small THEN
xorg:=(xcorg+dxorg)*scale;yorg:=(ycorg+dyorg)*scale;ELSE
xorg:=(xcorgl+dxorg)*scale;yorg:=(ycorgl+dyorg)*scale;END;
setcoord(0,xctr-wide/2,yctr+high/2);
setcoord(1,xctr-wide/2,yctr-high/2);
setcoord(2,xctr+wide/2,yctr-high/2);
setcoord(3,xctr+wide/2,yctr+high/2);
END lidsize;

PROCEDURE lid;
BEGIN
zerocoords;lidsize;
IF small THEN
xorg:=(xcorg+dxorg)*scale;yorg:=(ycorg+dyorg)*scale;ELSE
xorg:=(xcorgl+dxorg)*scale;yorg:=(ycorgl+dyorg)*scale;END;
(*start drawing*)
moveto(0,move);moveto(1,draw);moveto(2,draw);
moveto(3,draw);moveto(0,draw);
IF inlays THEN
outfile.WriteString('gsave');outfile.WriteLn;
outfile.WriteString('newpath');outfile.WriteLn;
shiftright(0,1,1,2.0);
moveto(0,move);
moveto(shift11,draw);moveto(shift12,draw);
moveto(1,draw);
shiftright(1,2,1,2.0);
moveto(shift11,draw);moveto(shift12,draw);
moveto(2,draw);
shiftright(2,3,1,2.0);
moveto(shift11,draw);moveto(shift12,draw);
moveto(3,draw);
shiftright(3,0,1,2.0);
moveto(shift11,draw);moveto(shift12,draw);
moveto(0,draw);
dash(dashv);width(widthv);
outfile.WriteString('grestore');outfile.WriteLn;
END(*inlays*); END lid;

PROCEDURE testproc;
(*small scale is 11.18 units moves one inch.  origin is
two inches to right, two inches down from upper left corner
of screen; positive directions right, up*)
BEGIN
outfile.WriteString('/Bitstream-Charter-Bold ');
outfile.WriteLInt(fontsize,2);
outfile.WriteString(' selectfont '); outfile.WriteLn;
IF small THEN
xorg:=xcorg*scale;yorg:=ycorg*scale;ELSE
xorg:=xcorgl*scale;yorg:=ycorgl*scale;END;
setcoord(1,0,7);setcoord(2,10,10);
label(1,4);label(2,0);
sline(1,2,3,330*degtorad,5,180*degtorad);
END testproc;

PROCEDURE endfile;
BEGIN
outfile.WriteRealFix(widthv,8,2);
outfile.WriteString (' setlinewidth'); outfile.WriteLn;
outfile.WriteString('stroke');outfile.WriteLn;
outfile.WriteString('showpage'); outfile.WriteLn;
outvar.Close; END endfile;
 
PROCEDURE viewplot;
VAR i:LONGINT;
BEGIN
Out.String('press enter to view plot, then ');Out.Ln;
Out.String('enter quit to finish viewing plot');Out.Ln;
In.Line(str1);
i:=ProcessManagement.system("gs -sDEVICE=x11 temp1");
END viewplot;

PROCEDURE printplot;
VAR i:LONGINT;
BEGIN
i:=ProcessManagement.system("lpr temp1");
(*lpr is assumed to send file through 
Ghostscript to printer*)
Out.String('sent to printer');Out.Ln; END printplot;

PROCEDURE crdproc;
BEGIN
Out.String('enter number of coordinate');Out.Ln;
In.LongInt(coordv);printcoord(coordv);In.Line(str1);
END crdproc;

PROCEDURE distproc;
VAR c1,c2:LONGINT;d:REAL;
BEGIN
Out.String('enter numbers of two coordinates');Out.Ln;
In.LongInt(c1); In.LongInt(c2); In.Line(str1);
d:=distance(c1,c2);
Out.RealFix(d,8,2);Out.Ln;
END distproc;

PROCEDURE angproc;
VAR c1,c2:LONGINT;a:REAL;
BEGIN
Out.String('enter numbers of two coordinates');Out.Ln;
In.LongInt(c1); In.LongInt(c2); In.Line(str1);
a:=angle(c1,c2)*radtodeg;
Out.RealFix(a,8,2);Out.Ln;
END angproc;

(*PROCEDURE botlab(x,y:REAL);

PROCEDURE initfont;
BEGIN
outfile.WriteString('/Bitstream-Charter-Bold ');
outfile.WriteLInt(fontsize,2);
outfile.WriteString(' selectfont '); outfile.WriteLn;
END initfont; 

PROCEDURE outreal(r:REAL);
BEGIN
outfile.WriteString('(');
outfile.WriteRealFix(r,8,2);
outfile.WriteString(' ');
END outreal;

PROCEDURE outstring(s:str);
BEGIN
outfile.WriteString(s);
outfile.WriteString(') show');
outfile.WriteLn;
END outstring;

PROCEDURE outstring2(s:str);
BEGIN
outfile.WriteString('(');
outfile.WriteString(s);
outfile.WriteString(') show');
outfile.WriteLn;
END outstring2;

PROCEDURE list;
BEGIN
outstring2(user);
outreal(wide);outstring('wide');
outreal(high);outstring('high');
END list;

BEGIN
IF ~small THEN setcoord(tempcoord1,x,y);
moveto(tempcoord1,move);
initfont; list;END;
END botlab;*)

PROCEDURE batchproc;
VAR str1,str2:str;i,pat:LONGINT;str3:STRING;
BEGIN
FOR pat:=1 TO 1 DO
initfile;
CASE pat OF
1:lid; border1; dxorg:=0.0;dyorg:=0.0;(*botlab(5,-35);*) |
END(*case*);
endfile;
(*to make ps files comment out next 4 lines*)
i:=ProcessManagement.system
("pstoedit  -f fig temp1 temp1.fig");
i:=ProcessManagement.system
("fig2dev -L pdf -b 20 temp1.fig temp1.pdf");
(*to make ps files remove .pdf in next line*)
str1:="mv temp1.pdf file";
IntStr.IntToStr(pat,str2);
Strings.Append(str2,str1);
(*to make ps files change .pdf to .ps next line*) 
str2:=".pdf";Strings.Append(str2,str1);
str3:=Object.NewLatin1(str1);
i:=ProcessManagement.system(str3);
END(*for*);dxorg:=0.0;dyorg:=0.0;END batchproc;

PROCEDURE drawproc;
BEGIN
initfile;
Out.String('lid');
Out.Ln;
Out.String(' border1, test, q');
Out.Ln;
LOOP
determine(command); IF command=q THEN EXIT;END; 
CASE command OF er:|lidv:lid;
|bdr3:border1;
|test:testproc;END(*case*);
END(*loop*);endfile;viewplot;END drawproc;

BEGIN
initvar;getdata;
LOOP Out.String('draw, prtcrd, dist, ang, print, batch, q');
Out.Ln; 
determine(command); IF command=q THEN EXIT;END;
CASE command OF er:|drawv:drawproc;|prtcrd:crdproc;|
dist:distproc;|ang:angproc;|print:printplot;|batch:batchproc;
END(*case*); END(*loop*);END curve.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.

.