The amount of background detail, history and documentation which accompanies this program is phenomenal: nine pages of a carefully-written and elaborate description of the harmonograph, plus the listing. This is followed by five pages of diagrams of two different types of harmonograph, and two pages and three photographs of harmonograph output. There just isn't room in Program File for all this documentation, and since the program is, essentially, concerned with program listings, I decided to concentrate on these.
Just to précis the documentation a little, a harmonograph is a mechanical device for producing abstract drawings. It consists of a large, flat board which can easily be swung in two dimensions. Over this is suspended a pen in such a way that it stays still, relative to the board, but remains in contact. If a sheet of paper is attached to the board and the board is set in motion, the harmonograph draws pleasing patterns made up of elliptical curves. The basic mechanism can be enhanced to produce more complex swinging and so more complex patterns.
The first three procedures in the program are support routines which interface to the Spectrum ROM for plotting points and drawing lines. They have been taken from the Hisoft Pascal Manual. The program produces points along the curve which the harmonograph produces, and joins these by straight lines. The program terminates when dimension or angle limits are reached, unlike the harmonograph itself which stops when friction finally gets the better of it.
It should be pointed out that friction is an important part of the harmonograph and helps to make the drawings more interesting.
The program is based upon the formulae for two swinging pendulums: one describing a rotating, diminishing ellips; the other a diminishing circle.
C4A2 20 {$L+} C4A2 30 C4A2 40 PROGRAM HARMONOGRAPH; C4A2 50 {Version 19} C4A2 60 C4A2 70 { C4A2 80 Aim - To draw a diminishing circle superimposed on a C4A2 90 rotating diminishing ellipse. C4A2 100 C4A2 110 Variable name terminology:- C4A2 120 C4A2 130 ellip, circ = ellipse, circle. C4A2 140 len, wid = ellipse length and width. C4A2 150 rad = circle radius. C4A2 160 ang = angle. C4A2 170 axisang = axis angle for rotation. C4A2 180 incr = increment (-ve = decrement). C4A2 190 start = starting value. C4A2 200 C4A2 210 e.g elliplenincr = increment for ellipse length C4A2 220 to be added at each calculation/plot. C4A2 230 C4A2 240 C4A2 250 C4A2 260 C4A2 270 C4A2 280 {Switch off run time checks for speed} C4A2 290 {$O-,S-,A-} C4A2 300 C4A2 310 C4A2 320 C4A2 330 {-----------------------------------------------------} C4A2 340 {Constants} C4A2 350 {-----------------------------------------------------} C4A2 360 CONST C4A2 370 {Offset from origin} C4A2 380 xoffset = 128; C4A2 390 yoffset = 87; C4A2 400 C4A2 410 C4A2 420 {Dimension start values} C4A2 430 elliplenstart = 120; C4A2 440 ellipwidstart = 20; C4A2 450 circradstart = 5; C4A2 460 C4A2 470 C4A2 480 {Angle start values} C4A2 490 ellipangstart = 1.5; C4A2 500 circangstart = 1.5; C4A2 510 axisangstart = 0; C4A2 520 C4A2 530 C4A2 540 {Dimension increments} C4A2 550 elliplenincr = -0.08; C4A2 560 ellipwidincr = -0.01; C4A2 570 circradincr = -0.001; C4A2 580 C4A2 590 C4A2 600 {Angle increments} C4A2 610 ellipangincr = -0.3; C4A2 620 circangincr = 0.32; C4A2 630 axisangincr = -0.003; C4A2 640 C4A2 650 C4A2 660 {Dimension limits} C4A2 670 elliplenlimit = 20; C4A2 680 ellipwidlimit = 5; C4A2 690 circradlimit = 1; C4A2 700 C4A2 710 {Angle limits (if reqd} C4A2 720 axisanglimit = -3; {radians} C4A2 730 C4A2 740 C4A2 750 {-----------------------------------------------------} C4A2 760 {Variables} C4A2 770 {-----------------------------------------------------} C4A2 780 VAR C4AB 790 {Dimensions} C4AB 800 elliplen, C4AB 810 ellipwid, C4AB 820 circrad : REAL; C4AB 830 C4AB 840 {Angles} C4AB 850 ellipang, C4AB 860 circang, C4AB 870 axisang : REAL; C4AB 880 C4AB 890 {Coordinates} C4AB 900 xold, C4AB 910 xnew, C4AB 920 yold, C4AB 930 ynew : INTEGER; C4AB 940 C4AB 950 C4AB 960 C4AB 970 {-----------------------------------------------------} C4AB 980 {Graphics Support Routines (from HISOFT manual} C4AB 990 {-----------------------------------------------------} C4AB 1000 C4AB 1010 PROCEDURE drawlinesupport C4AE 1020 (x, y, signx, signy : INTEGER); C4AE 1030 C4AE 1040 { C4AE 1050 Aim - used in conjunction with the LINEDRAW procedure. C4AE 1060 Machine code sets up Z80 regs & C4AE 1070 calls Spectrum ROM DRAW routine. C4AE 1080 } C4AE 1090 C4AE 1100 BEGIN C4BE 1110 C4BE 1120 INLINE C4BE 1130 ( C4BE 1140 #FD,#21,#3A,#5C, {LD IY,#5C3A} C4C2 1150 #DD,#56,#02, {LD D,(IX+2)} C4C5 1160 #DD,#5E,#04, {LD E,(IX+4)} C4C8 1170 #DD,#46,#06, {LD B,(IX+6)} C4CB 1180 #DD,#4E,#08, {LD C,(IX+8)} C4CE 1190 #CD,#BA,#24 {CALL #24BA ;ROM DRAW routine} C4D0 1200 ) C4D1 1210 C4D1 1220 END; C4DB 1230 {-----------------------------------------------------} C4DB 1240 C4DB 1250 PROCEDURE drawline (x, y : INTEGER); C4DE 1260 C4DE 1270 { C4DE 1280 Aim - To draw a line from the current plot position C4DE 1290 (CX, CY) to (CX+x, CY+y). C4DE 1300 (Equivalent to BASIC DRAW command). C4DE 1310 } C4DE 1320 C4DE 1330 VAR C4DE 1340 signx, C4DE 1350 signy : INTEGER; C4DE 1360 C4DE 1370 C4DE 1380 BEGIN C4EB 1390 C4EB 1400 {Calculate sign of x & y} C4EB 1410 IF x <0 THEN C502 1420 signx := -1 C506 1430 ELSE C511 1440 signx := 1; C51A 1450 C51A 1460 IF y <0 THEN C531 1470 signy := -1 C535 1480 ELSE C540 1490 signy := 1; C549 1500 C549 1510 drawlinesupport(ABS (x), ABS (y), signx, signy) C56B 1520 C56B 1530 END; C580 1540 {-----------------------------------------------------} C580 1550 C580 1560 PROCEDURE plot (x, y : INTEGER); C583 1570 C583 1580 { C583 1590 Aim - To plot the specified point (x,y). C583 1600 Machine code sets up Z80 regs & C583 1610 calls Spectrum ROM PLOT routine. C583 1620 (Equivalent to BASIC PLOT command). C583 1630 } C583 1640 C583 1650 C583 1660 BEGIN C593 1670 C593 1680 INLINE C593 1690 ( C593 1700 #FD,#21,#3A,#5C, {LD IY,#5C3A} C597 1710 #DD,#46,#02, {LD B,(IX+2)} C59A 1720 #DD,#4E,#04, {LD C,(IX+2)} C59D 1730 #CD,#E5,#22 {CALL #22E5 ;ROM PLOT routine} C59F 1740 ) C5A0 1750 C5A0 1760 END; C5AA 1770 {-----------------------------------------------------} C5AA 1780 C5AA 1790 C5AA 1800 C5AA 1810 C5AA 1820 C5AA 1830 {-----------------------------------------------------} C5AA 1840 {Harmonograph Routines} C5AA 1850 {-----------------------------------------------------} C5AA 1860 C5AA 1870 FUNCTION xcalc : INTEGER; C5AD 1880 C5AD 1890 { C5AD 1900 Aim - To calculate & return the new value of X. C5AD 1910 } C5AD 1920 C5AD 1930 BEGIN C5BD 1940 C5BD 1950 xcalc := ENTIER ( C5BD 1960 elliplen * COS (ellipang) * COS (axisang) + C5E4 1970 ellipwid * SIN (ellipang) * SIN (axisang) + C60E 1980 circrad * COS (circang) + C629 1990 xoffset) C634 2000 C634 2010 END; C641 2020 {-----------------------------------------------------} C641 2030 C641 2040 FUNCTION ycalc : INTEGER; C644 2050 C644 2060 { C644 2070 Aim - To calculate & return the new value of Y. C644 2080 } C644 2000 C644 2100 BEGIN C654 2110 C654 2120 ycalc := ENTIER ( C654 2130 ellipwid * SIN (ellipang) * COS (axisang) + C67B 2140 elliplen * COS (ellipang) * SIN (axisang) + C6A9 2150 circrad * SIN (circang) + C6C4 2160 yoffset) C6CF 2170 C6CF 2180 END; C6D8 2190 {-----------------------------------------------------} C6DC 2200 C6DC 2210 PROCEDURE initialisesizes; C6DF 2220 C6DF 2230 { C6DF 2240 Aim - To initialise all dimension & angle sizes C6DF 2250 to their start values as defined in the CONST C6DF 2260 section. C6DF 2270 } C6DF 2280 C6DF 2290 C6DF 2300 BEGIN C6EF 2310 C6EF 2320 elliplen := elliplenstart; C6FC 2330 ellipwid := ellipwidstart; C709 2340 circrad := circradstart; C716 2350 C716 2360 C716 2370 ellipang := ellipangstart; C723 2380 circang := circangstart; C730 2390 axisang := axisangstart; C73D 2400 C73D 2410 END; C743 2420 {-----------------------------------------------------} C743 2430 C743 2440 PROCEDURE incrementsizes; C746 2450 C746 2460 { C746 2470 Aim - To increment dimension & angle sizes by the C746 2480 values defined in the CONST section. C746 2490 Sizes can be decreased by using -ve increments. C746 2500 } C746 2510 C746 2520 BEGIN C756 2530 C756 2540 elliplen := elliplen + elliplenincr; C76F 2550 ellipwid := ellipwid + ellipwidincr; C788 2560 circrad := circrad + circradincr; C7A1 2570 C7A1 2580 ellipang := ellipang + ellipangincr; C7BA 2590 circang := circang + circangincr; C7D3 2600 axisang := axisang + axisangincr; C7EC 2610 C7EC 2620 END; C7F2 2630 {-----------------------------------------------------} C7F2 2640 C7F2 2650 C7F2 2660 {-----------------------------------------------------} C7F2 2670 {MAIN PROGRAM BODY} C7F2 2680 {-----------------------------------------------------} C7F2 2690 C7F2 2700 BEGIN C7FB 2710 C7FB 2720 WRITE (CHR (12)); {clear screen} C802 2730 C802 2740 initialisesizes; C807 2750 C807 2760 {Calculate & plot 1st point as a reference} C807 2770 xold := xcalc; C811 2780 yold := ycalc; C81B 2790 plot (xold, yold); C828 2800 C828 2810 WHILE C82B 2820 { C82B 2830 Loop until limits reached. C82B 2840 'Comment-out' checks as reqd. for speed. C82B 2850 } C82B 2860 (axisang > axisanglimit) C84E 2870 { C84E 2880 AND C84E 2890 (elliplen > elliplenlimit) C84E 2900 AND C84E 2910 (ellipwid > ellipwidlimit) C84E 2920 AND C84E 2930 (circrad > circradlimit) C84E 2940 } C84E 2950 DO C851 2960 BEGIN C851 2970 C851 2980 incrementsizes; C856 2990 C856 3000 {Calculate new point} C856 3010 xnew := xcalc; C860 3020 ynew := ycalc; C86A 3030 C86A 3040 {Draw line from old point to new} C86A 3050 drawline (xnew - xold, ynew - yold); C889 3060 C889 3070 {Transfer new point to old for next time} C889 3080 xold := xnew; C88F 3090 yold := ynew; C895 3100 C895 3110 END; C898 3120 C898 3130 C898 3140 { C898 3150 Loop-stop to prevent PASCAL prompt from racking up C898 3160 the picture & losing the top line! C898 3170 Can break in using SHIFT+SPACE as normal. C898 3180 } C898 3190 WHILE TRUE DO; C8A4 3200 C8A4 3210 END. End Address: C88D Run?