.........1.........2.........3.........4.........5.........6.........7.........8

LIGHT SCREEN DESIGNER
ZX Computing, August/September 1985
Part 07 of 13


This issue's article is all about drawing circles. I shall combine the
circle drawing routine of last issue with a new routine given here.
The new routine is called CIRCLE_THRU. Like CIRCLE_CENTRE, CIRCLE_THRU
also draws a circle, but the difference between the two is that
different parameters must be specified for the two routines. Take a
look at Figure 1 [PART07.GIF] and you'll see what I mean.
CIRCLE_CENTRE is quite simple to program because it needs the same
information as the ROM routine - the coordinates of the centre, and
the radius (which you can work out if you know the coordinates of a
point on the edge) - but CIRCLE_THRU needs three points on the edge.

I'm dwelling on the subject of drawing circles deliberately, not only
because circles are interesting and magical shapes, but also because
this is the first time that we've come across any difficult maths in
the course of our programming. You see, it's actually quite difficult
to find the coordinates of the centre (which is what we need), if all
we're given is the coordinates of three points on the edge. Think
about it for a bit and see if you can come up with an easy solution.


Imaginary lines

The method I've used involves drawing an imaginary line halfway
between the Origin and the Marker, and another line halfway between
the Cursor and the Marker - where the two lines cross is the centre of
the circle.

The algorithm can be easily demonstrated with the BASIC program in
Figure two. ["demo07" in LSD.TAP - with the mistakes in the listing
corrected. JimG] We shall later see how to translate this algorithm
into machine code.

You see, the algorithm works out the point where the two imaginary
lines cross. It turns out that the equations of the two lines are:

	Ax+By=C
	Dx+Ey=F

where A, B, C, D, E and F are defined as above. From line 3170 to 3200
the program concentrates on finding values of x and y which solve both
equations.

Before I list the program, I'd like to point out - and, of course, to
cure - a bug which cropped up earlier in LSD. At address DDDD, the
instruction CALL SET_MIN minimises all of the workspace - the
calculator stack and so on. Unfortunately, it also clears the
edit-line, so that the current command is destroyed. This can
sometimes cause the error "C: Nonsense in BASIC". To cure the error, I
propose to CALL SET_WORK instead, which leaves the edit-line
untouched. So, to cure the bug, type the,following BASIC command:

	POKE 56798,191

Now for the program: note that I start from a new version of the
CIRCLE_CENT routine in order to make economic use of subroutines.

Only one address in the Command Addresses Table needs to be changed.
It is:

DB4E 30E1               DEFW E130,CIRC_THRU (CIRCLE_THRU)

-----------------------------------------------------------------------------
E08D 180A               JR   CC_MOVE
E08F C5       CIRC_CENT PUSH BC
E090 CDA0E0             CALL GETCURSOR     ;Store cursor coordinates
                                           ;in calculator memories
E093 CDC9E0   CC_FIN    CALL CIRCLEARG     ;Calculate parameters for
                                           ;CIRCLE routine
E096 CD2D23             CALL CIRCLE+13     ;Draw circle
E099 C1       CC_MOVE   POP  BC            ;BC= cursor coordinates
E09A CD41DD             CALL PIX_ADDR      ;HL= cursor position
E09D C3F3DE             JP   MOVE          ;Move origin
E0A0 21925C   GETCURSOR LD   HL,MEMBOT
E0A3 22655C             LD   (STKEND),HL   ;Point calculator stack into memory
E0A6 210CDB             LD   HL,#DB0C
E0A9 1E03               LD   E,#03         ;E= number of cursors
E0AB 23       CC_LOOP   INC  HL
E0AC 23                 INC  HL
E0AD 4E                 LD   C,(HL)
E0AE 23                 INC  HL
E0AF 46                 LD   B,(HL)
E0B0 23                 INC  HL
E0B1 CDEADE             CALL ADJUST_B      ;Adjust to ROM convention
E0B4 E5                 PUSH HL
E0B5 D5                 PUSH DE
E0B6 C5                 PUSH BC
E0B7 78                 LD   A,B
E0B8 CD282D             CALL STACK_A       ;Stack y coordinate
E0BB C1                 POP  BC
E0BC 79                 LD   A,C
E0BD CD282D             CALL STACK_A       ;Stack x coordinate
E0C0 D1                 POP  DE
E0C1 E1                 POP  HL
E0C2 1D                 DEC  E
E0C3 20E6               JR   NZ,CC_LOOP
E0C5 CDC516             CALL SET_STK       ;Restore calculator stack
E0C8 C9                 RET
E0C9 EF       CIRCLEARG RST  #28           ;Engage the calculator
E0CA E1                 recall M1          Ox
E0CB E0                 recall M0          Ox,Oy
E0CC 31                 duplicate          Ox,Oy,Oy
     E4                 recall M4          Ox,Oy,Oy,Cy
     03                 subtract           Ox,Oy,Oy-Cy
E0CF 31                 duplicate          Ox,Oy,Oy-Cy,Oy-Cy
     04                 multiply           Ox,Oy,(Oy-Cy)^2
     E1                 recall M1          Ox,Oy,(Oy-Cy)^2,Ox
E0D2 E5                 recall M5          Ox,Oy,(Oy-Cy)^2,Ox,Cx
E0D3 03                 subtract           Ox,Oy,(Oy-Cy)^2,Ox-Cx
E0D4 31                 duplicate          Ox,Oy,(Oy-Cy)^2,Ox-Cx,Ox-Cx
     04                 multiply           Ox,Oy,(Oy-Cy)^2,(Ox-Cx)^2
     0F                 add                Ox,Oy,(Oy-Cy)^2+(Ox-Cx)^2
E0D7 28                 sqr                Ox,Oy,radius
     38                 end calc
E0D9 C9                 RET
-----------------------------------------------------------------------------
E0DA EF       MATRIX    RST  #28
E0DB E2                 recall M2          My
     31                 duplicate          My,My
     04                 multiply           My^2
E0DE E0                 recall M0          My^2,Oy
E0DF 31                 duplicate          My^2,Oy,Oy
     04                 multiply           My^2,Oy^2
     03                 subtract           My^2-Oy^2
E0E2 E3                 recall M3          My^2-Oy^2,Mx
E0E3 31                 duplicate          My^2-Oy^2,Mx,Mx
     04                 multiply           My^2-Oy^2,Mx^2
     0F                 add                My^2-Oy^2+Mx^2
E0E6 E1                 recall M1          My^2-Oy^2+Mx^2,Ox
E0E7 31                 duplicate          My^2-Oy^2+Mx^2,Ox,Ox
     04                 multiply           My^2-Oy^2+Mx^2,Ox^2
     03                 subtract           My^2-Oy^2+Mx^2-Ox^2
E0EA A2                 const half         My^2-Oy^2+Mx^2-Ox^2,1/2
E0EB 04                 multiply           C
E0EC E2                 recall M2          C,My
     E0                 recall M0          C,My,Oy
     03                 subtract           C,A
E0EF C0                 store M0           C,A
E0F0 02                 delete             C
E0F1 E3                 recall M3          C,Mx
E0F2 E1                 recall M1          C,Mx,Ox
E0F3 03                 subtract           C,B
E0F4 C1                 store M1           C,B
E0F5 02                 delete             C
E0F6 38                 end calc
     C9                 RET
-----------------------------------------------------------------------------
E0F8 CDDAE0   CALC_CENT CALL MATRIX        ;Calculate A, B and C
E0FB FD362E9C           LD   (MEMBOT),#9C
E0FF CDDAE0             CALL MATRIX        ;Calculate D, E and F
E102 FD362E92           LD   (MEMBOT),#92
E106 EF                 RST  #28           C,F
E107 C4                 store M4           C,F
     02                 delete             C
     C5                 store M5           C
E10A 02                 delete
E10B E1                 recall M1          B
E10C E2                 recall M2          B,D
     04                 multiply           B*D
     E0                 recall M0          B*D,A
E10F E3                 recall M3          B*D,A,E
E110 04                 multiply           B*D,A*E
E111 03                 subtract           Det
E112 31                 duplicate          Det,Det
     30                 eq Z               Det,Det=0
     0017               jump_true,NOT_POS  Det
E116 31                 duplicate          Det,Det
     E5                 recall M5          Det,Det,C
     E2                 recall M2          Det,Det,C,D
E119 04                 multiply           Det,Det,C*D
E11A E0                 recall M0          Det,Det,C*D,A
E11B E4                 recall M4          Det,Det,C*D,A,F
     04                 multiply           Det,Det,C*D,A*F
     03                 subtract           Det,Det,C*D-A*F
E11E 01                 exchange           Det,C*D-A*F,Det
     05                 divide             Det,X
     01                 exchange           X,Det
E121 E1                 recall M1          X,Det,B
E122 E4                 recall M4          X,Det,B,F
     04                 multiply           X,Det,B*F
     E5                 recall M5          X,Det,B*F,C
E125 E3                 recall M3          X,Det,B*F,C,E
E126 04                 multiply           X,Det,B*F,C*E
E127 03                 subtract           X,Det,B*F-C*E
E128 01                 exchange           X,B*F-C*E,Det
     05                 divide             X,Y
     38                 end calc
E12B C9                 RET
E12C 38       NOT_POS   end calc
E12D E1                 POP  HL            ;HL= return address
E12E C1                 POP  BC            ;BC= current coordinates
E12F C9                 RET                ;Return from subroutine
-----------------------------------------------------------------------------
E130 CD22E0   CIRC_THRU CALL TEST_MRKR 
E133 C5                 PUSH BC
E134 CDA0E0             CALL GETCURSOR     ;Get current coordinates
                                           ;into calculator memories
E137 EF                 RST  #28
E138 E4                 recall M4          Cy
     E5                 recall M5          Cy,Cx
     38                 end calc
E13B CDF8E0             CALL CALC_CENT     ;Calculate the centre of the circle
E13E EF                 RST  #28
E13F C0                 store M0           M0=y
E140 02                 delete
E141 C1                 store M1           M1=x
E142 02                 delete
E143 C5                 store M5           M5=Cx
E144 02                 delete
E145 C4                 store M4           M4=Cy
     02                 delete
     38                 end calc
E148 CD38DF             CALL CANCEL_MK 
E14B C393E0             JP   CC_FIN
-----------------------------------------------------------------------------


Figure 2. Demonstration of algorithm.

3010 INPUT "Ox=";Ox
3020 INPUT "Oy=";Oy
3030 PLOT Ox,Oy
3040 INPUT "Mx=";Mx
3050 INPUT "My=";My
3060 PLOT Mx,My
3070 INPUT "Cx=";Cx
3080 INPUT "Cy=";Cy
3090 PLOT Cx,Cy
3100 PAUSE 0
3110 LET A=My-Oy
3120 LET B=Mx-Ox
3130 LET C=(Mx*Mx-Ox*Ox+My*My-Oy*Oy)/2
3140 LET D=Cy-My
3150 LET E=Cx-Mx
3160 LET F=(Cx*Cx-Mx*Mx+Cy*Cy-My*My)/2
3170 LET DET=B*D-A*E
3180 IF DET=0 THEN PRINT "Circle not possible": STOP 
3190 LET X=(C*D-A*F)/DET
3200 LET Y=(B*F-C*E)/DET
3210 LET Rx=Cx-X
3220 LET Ry=Cy-Y
3230 LET radius=SQR (Rx*Rx+Ry*Ry)
3240 CIRCLE X,Y,radius
