! Geometric Utilities for EDWIN, capitalising on device specific features. %record %format POINTF (%integer X, Y) %record %format LINEF (%real A, B, C) %const %integer TRUE = 0, FALSE = 1 %const %integer CHARLES = 2, HPPLOT=3, APPLOT=5, NEWHP=13, BIGHP=12 %const %integer MAX POINT = 100 %own %integer NUM C POINTS = 8 %own %integer CIRCLE MODE = FALSE ! Routines from EDWIN %external %integer %spec DEVICE %external %integer %spec CLIPPING %external %integer %fn %spec MUL DIV (%integer A, B, C) %external %routine %spec MOVE ABS (%integer X,Y) %external %routine %spec LINE ABS (%integer X,Y) %external %routine %spec MAP TO DEVICE COORDS (%integer %name X, Y) %external %routine %spec INQUIRE WINDOW (%integer %name XL, XR, YB, YT) %dynamic %routine %spec CHAS (%integer COM, X, Y) %dynamic %routine %spec CBOX (%integer XL,YL,XU,YU) %dynamic %routine %spec END MODE %dynamic %routine %spec HP CIRCLE (%integer D) %dynamic %routine %spec ABOX (%integer XL, YL, XU, YU) %dynamic %routine %spec APOLYGON (%integer NE, %record (POINTF) %array %name P) ! IMP Maths routines for VAX and MOUSES !%external %long %real %fn %spec COS (%long %real A) !%external %long %real %fn %spec SIN (%long %real A) !%external %long %real %fn %spec ARCTAN (%long %real Y, X) !%external %long %real %fn %spec SQRT (%long %real X) %external %routine SET FLASH POINTS (%integer I) NUM C POINTS = 8 NUM C POINTS = I %if I > 8 NUM C POINTS = MAX POINT-1 %if NUM C POINTS >= MAX POINT CIRCLE MODE = TRUE %end %routine %spec POLYGON (%integer NUM E, %record (POINTF) %array %name P) %external %routine FLASH (%record (POINTF) %name P, %integer D) ! This routine draws a flash symbol at P in absolute coordinates, ! with diameter D. ! The flash circle is INSIDE the polygon. ! The circle drawing routine is based on an original one by JRCC. %const %real D TO R = 57.2958; ! Magic number converts degrees to rads. %integer I, X, Y, W, XL, XR, YB, YT %real RV, R, ITA, CONT %record (POINTF) %array PTS (1:NUM C POINTS + 1) %if CLIPPING>=0 %start INQUIRE WINDOW (XL, XR, YB, YT) W = D//2 %return %unless XL-W <= P_X <= XR+W %and YB-W <= P_Y <= YT+W %finish %if DEVICE=HPPLOT %or DEVICE=NEWHP %or DEVICE=BIGHP %start MOVE ABS (P_X, P_Y) I = 0; X = D; Y = 0 MAP TO DEVICE COORDS (I, Y) MAP TO DEVICE COORDS (D, Y) HP CIRCLE (IMOD(I-D)//2) %return %finish ITA = 360/NUM C POINTS R = D/2*(2-COS(ITA/2/D TO R)) PTS(1)_X = P_X + INT(R) PTS(1)_Y = P_Y CONT = ITA I = 2 %cycle RV=CONT/D TO R PTS(I)_X = P_X + INT(R*COS(RV)) PTS(I)_Y = P_Y + INT(R*SIN(RV)) CONT = CONT + ITA %exit %if CONT>360 I = I + 1 %repeat POLYGON (I, PTS) %end %external %routine S BOX (%record (POINTF) %name L, U) ! This draws a simple box. %integer XL, XU, YL, YU %if DEVICE # CHARLES %and DEVICE # APPLOT %start MOVE ABS (L_X, L_Y) LINE ABS (U_X, L_Y) LINE ABS (U_X, U_Y) LINE ABS (L_X, U_Y) LINE ABS (L_X, L_Y) %finish %else %start XL = L_X YL = L_Y MAP TO DEVICE COORDS (XL,YL) XU = U_X YU = U_Y MAP TO DEVICE COORDS (XU,YU) %if DEVICE=CHARLES %then CBOX (XL,YL,XU,YU) %else ABOX (XL, YL, XU, YU) %finish %end %external %routine BOX (%integer L, W, %record (POINTF) %name C, D) ! This routines draws a box of length L, width W at centre C with direction D. %integer I %record (POINTF) PL, PU %record (POINTF) %array PTS (1:4) %real THETA, LC, WC, LS, WS %if D_X=0 %or D_Y=0 %start %if D_Y#0 %start I = L; L = W; W = I %finish PL_X = C_X - L//2; PL_Y = C_Y - W//2 PU_X = C_X + L//2; PU_Y = C_Y + W//2 SBOX (PL, PU) %finish %else %start THETA = ARCTAN (D_Y, D_X) LC = L * COS (THETA) WC = W * COS (THETA) LS = L * SIN (THETA) WS = W * SIN (THETA) PTS(1)_X = C_X - int((LC + WS)/2) PTS(1)_Y = C_Y + int((WC - LS)/2) PTS(2)_X = C_X + int((LC - WS)/2) PTS(2)_Y = C_Y + int((WC + LS)/2) PTS(3)_X = C_X + int((LC + WS)/2) PTS(3)_Y = C_Y - int((WC - LS)/2) PTS(4)_X = C_X - int((LC - WS)/2) PTS(4)_Y = C_Y - int((WC + LS)/2) POLYGON (4, PTS) %finish %end %external %routine WIRE (%integer W, N, %record (POINTF) %array %name P) ! This routine converts a wire to a POLYGON. ! wire width is given by W, and the wire has N points specifying it, ! whose coordinates are given in P. ! Algorithm is based on the SIMULA one in CIF20P. %integer I, NUM IN, NUM OUT %real HWIDTH %record (POINTF) %array IN (1:N) %record (POINTF) %array OUT (1:2*N+2) %record (LINEF) LNEW, LLAST, LBEGIN, LEND, MLLAST, MLNEW, LBEGINP, LENDP %routine BREAK UP WIRE (%integer W, N, %record (POINTF) %array %name P) ! Break up the wire into some boxes, with flashes over the points. %record (POINTF) P1, P2, D, C %integer I, L %real X, Y %routine SWAP (%record (POINTF) %name A, B) %record (POINTF) C C = A; A = B; B = C; %end %for I=1,1,N-1 %cycle P1 = P(I) P2 = P(I+1) ! Orthogonal boxes? %if P1_X = P2_X %start SWAP (P1, P2) %if P1_Y > P2_Y D_Y = P1_Y D_X = P1_X - W//2 C_Y = P2_Y C_X = D_X + W S BOX (D, C) %continue %finish %if P1_Y = P2_Y %start SWAP (P1, P2) %if P1_X > P2_X D_Y = P1_Y - W//2 D_X = P1_X C_Y = D_Y + W C_X = P2_X S BOX (D, C) %continue %finish ! Arbitary Box X = P2_X - P1_X Y = P2_Y - P1_Y L = INT ( SQRT ( X*X + Y*Y)) C_X = P1_X + INT(X/2) C_Y = P1_Y + INT(Y/2) D_X = - INT(X) D_Y = INT(Y) BOX (W, L, C, D) %repeat FLASH (P(I), W) %for I=N, -1, 1 %end %integer %fn EQ (%real A, B) %result = TRUE %if A - 0.05 < B < A + 0.05 %result = FALSE %end %routine NORMALISE (%record (LINEF) %name LINE) ! This normalises the line equation on the creation of a new line. %real D D = SQRT (LINE_A\2 + LINE_B\2) %return %if EQ(D,0)=TRUE LINE_A = LINE_A/D LINE_B = LINE_B/D LINE_C = LINE_C/D %end %routine MAKE LINE (%record (POINTF) %name P1, P2, %record (LINEF) %name LINE) ! given the points P1 & P2 compute the line equation in a b c form. LINE_A = P2_Y - P1_Y LINE_B = - ( P2_X - P1_X) LINE_C = - LINE_A*P1_X - LINE_B*P1_Y %if EQ(LINE_A,0)=TRUE %and EQ(LINE_B,0)=TRUE %and EQ(LINE_C,0)=TRUE %start LINE_B = -1 LINE_C = P1_Y %finish NORMALISE (LINE) %end %routine INFLATE (%record (LINEF) %name LINE, NLINE, %real W) ! Inflate LINE by width W NLINE = LINE NLINE_C = NLINE_C + W NORMALISE (NLINE) %end %integer %fn INTERSECT (%record(LINEF) %name L1, L2, %record (POINTF) %name P) ! TRUE if lines intersect, and P gets the intersection point. ! otherwise FALSE. %real D %real TX, TY D = L1_A*L2_B - L2_A*L1_B %result = FALSE %if EQ(D,0)=TRUE TX = (L1_B*L2_C - L2_B*L1_C)/D TY = (L2_A*L1_C - L1_A*L2_C)/D P_X = int(TX) P_Y = int(TY) %result = TRUE %end %routine PERP THROUGH (%record (LINEF) %name LINE, NLINE, %record (POINTF) P) ! Forms the perpendicular of LINE, passing through point P. %record (LINEF) TLINE TLINE = LINE TLINE_A = LINE_B TLINE_B = - LINE_A TLINE_C = -TLINE_A*P_X - TLINE_B*P_Y NORMALISE (TLINE) NLINE = TLINE %end FLASH (P(1), W) %and %return %if N = 1 BREAK UP WIRE (W, N, P) %and %return %if CIRCLE MODE=TRUE HWIDTH = W/2 NUM IN = 2 NUM OUT = 2 MAKE LINE (P(1), P(2), LBEGIN) LNEW = LBEGIN %for I=2,1,N-1 %cycle LLAST = LNEW MAKE LINE (P(I), P(I+1), LNEW) INFLATE (LLAST, MLLAST, H WIDTH) INFLATE (LNEW, MLNEW, HWIDTH) NUM OUT = NUM OUT + 1 %if INTERSECT (MLLAST, MLNEW, OUT(NUM OUT)) = TRUE INFLATE (LLAST, MLLAST, - HWIDTH) INFLATE (LNEW, MLNEW, - HWIDTH) NUM IN = NUM IN + 1 %if INTERSECT (MLLAST, MLNEW, IN(NUM IN)) = TRUE %repeat LEND = LNEW PERP THROUGH (LBEGIN, LBEGINP, P(1)) INFLATE (LBEGINP, LBEGINP, - HWIDTH) PERP THROUGH (LEND, LENDP, P(N)) INFLATE (LENDP, LENDP, HWIDTH) ! Compute end intersections. INFLATE (LBEGIN, MLNEW, HWIDTH) %signal 14,7 %unless INTERSECT (LBEGIN P, MLNEW, OUT (1)) = TRUE INFLATE (LBEGIN, MLNEW, - HWIDTH) %signal 14,7 %unless INTERSECT (LBEGIN P, MLNEW, IN (1)) = TRUE INFLATE (LEND, MLNEW, HWIDTH) %signal 14,7 %unless INTERSECT (LEND P, MLNEW, OUT (NUM OUT)) = TRUE INFLATE (LEND, MLNEW, - HWIDTH) %signal 14,7 %unless INTERSECT (LEND P, MLNEW, IN (NUM IN)) = TRUE ! make a set of ordered points from IN & OUT lists. N = NUM OUT N = N + 1 %and OUT(N) = IN(I) %for I=NUM IN, -1, 1 POLYGON (N, OUT) %end ! This is a routine for drawing clipped polygons, using the Sutherland- ! Hodgman algorithm, CACM Vol 17, Page 32, Jan 74. %external %routine POLYGON (%integer NUM E, %record (POINTF) %array %name AP) %const %integer LAST = 3 %integer PTR, ANY OUT, STAGE, XL, XR, YB, YT %record (POINTF) FIRST PT %record (POINTF) %array F, S (0:3) %byte %integer %array FIRST OF, OUT (0:3) %routine GET RID OF (%record (POINTF) %name P) %if ANY OUT=FALSE %start CHAS(10,3,0) %if DEVICE=CHARLES MOVE ABS (P_X,P_Y) FIRST PT = P ANY OUT = TRUE %finish LINE ABS (P_X, P_Y) %end %routine %spec DEAL WITH POINT (%record (POINTF) %name P) %routine OUTPUT (%record (POINTF) %name P) OUT (STAGE) = TRUE GET RID OF (P) %and %return %if STAGE = LAST STAGE = STAGE + 1 DEAL WITH POINT (P) STAGE = STAGE - 1 %end %integer %fn INTERSECT (%record (POINTF) %name S,P) ! Note if point is on the line it is assumed to intersect it. %switch SW(0:3) -> SW(STAGE) SW(0): ! XL %result = TRUE %if S_X<=XL SW(STAGE) SW(0): %result = TRUE %if S_X>=XL %result = FALSE SW(2): %result = TRUE %if S_X<=XR %result = FALSE SW(1): %result = TRUE %if S_Y>=YB %result = FALSE SW(3): %result = TRUE %if S_Y<=YT %result = FALSE %end %routine compute intersect (%record (POINTF) %name I, P, S) ! Computes intersect I from points P and S. %switch SW(0:3) -> SW(STAGE) SW(0): I_X = XL I_Y = MUL DIV (P_Y-S_Y, XL-S_X, P_X-S_X) + S_Y %return SW(2): I_X = XR I_Y = MUL DIV (P_Y-S_Y, XR-S_X, P_X-S_X) + S_Y %return SW(1): I_X = MUL DIV (P_X-S_X, YB-S_Y, P_Y-S_Y) + S_X I_Y = YB %return SW(3): I_X = MUL DIV (P_X-S_X, YT-S_Y, P_Y-S_Y) + S_X I_Y = YT %end %routine DEAL WITH INTERSECT (%record (POINTF) %name P) %record (POINTF) I %if FIRST OF (STAGE)=TRUE %start COMPUTE INTERSECT (I, P, S(STAGE)) %and OUTPUT (I) %if INTERSECT (P, S(STAGE)) = TRUE %finish %else %start F(STAGE) = P FIRST OF (STAGE) = TRUE %finish %end %routine DEAL WITH POINT (%record (POINTF) %name P) DEAL WITH INTERSECT (P) S(STAGE) = P OUTPUT (P) %if VISIBLE (P) = TRUE %end %if DEVICE=APPLOT %start MAP TO DEVICE COORDS (AP(PTR)_X, AP(PTR)_Y) %for PTR = 1, 1, NUM E APOLYGON (NUM E, AP) %return %finish ANY OUT = FALSE %if CLIPPING>=0 %start; ! Only clip if the user asks to INQUIRE WINDOW (XL,XR,YB,YT) STAGE = 0 FIRST OF (PTR) = FALSE %and OUT(PTR) = FALSE %for PTR=0,1,3 DEAL WITH POINT (AP(PTR)) %for PTR = 1,1,NUM E %return %unless ANYOUT=TRUE %for STAGE = 0,1,3 %cycle DEAL WITH INTERSECT (F(STAGE)) %if OUT(STAGE) = TRUE %repeat %finish %else %start GET RID OF (AP(PTR)) %for PTR = 1,1,NUM E %finish GET RID OF (FIRST PT); ! To close the polygon. END MODE %if DEVICE = CHARLES %end %end %of %file