Syntax10.Scn.Fnt MODULE KeplerPorts; (* J. Templ, 30.10.90/07.06.94 *) (* Ports provide device independent drawing operations clipped on the port's borders. All drawing and mouse coordinates are relative to the origin x0, y0, which is relative to the top left corner of the port. Capital letter coordinates always denote screen coordinates. IMPORT Display, Display1, Fonts, Printer, TextPrinter; CONST Ceres = FALSE; (*conditional compilation*) TYPE Port* = POINTER TO PortDesc; PortDesc* = RECORD (Display.FrameDesc) x0*, y0*, scale*: INTEGER; ext*: Port; END ; DisplayPort* = POINTER TO DisplayPortDesc; DisplayPortDesc* = RECORD (PortDesc) END ; PrinterPort* = POINTER TO PrinterPortDesc; PrinterPortDesc* = RECORD (PortDesc) END ; BalloonPort* = POINTER TO BalloonPortDesc; BalloonPortDesc* = RECORD (PortDesc) END ; (* ----------------- abstract port methods ------------------ *) PROCEDURE (P: Port) FillRect* (x, y, w, h, col, pat, mode: INTEGER); END FillRect; PROCEDURE (P: Port) DrawString*(x, y: INTEGER; s: ARRAY OF CHAR; font: Fonts.Font; col, mode: INTEGER); END DrawString; (* ----------------- concrete port methods ------------------ *) PROCEDURE (P: Port) CX*(x: INTEGER): INTEGER; BEGIN RETURN P.X + (P.x0 + x) DIV P.scale END CX; PROCEDURE (P: Port) CY*(y: INTEGER): INTEGER; BEGIN RETURN P.Y + P.H + (P.y0 + y) DIV P.scale END CY; PROCEDURE (P: Port) Cx*(X: INTEGER): INTEGER; BEGIN RETURN (X - P.X) * P.scale - P.x0 END Cx; PROCEDURE (P: Port) Cy*(Y: INTEGER): INTEGER; BEGIN RETURN (Y - P.Y - P.H) * P.scale - P.y0 END Cy; PROCEDURE (P: Port) DrawLine*(x1, y1, x2, y2, col, mode: INTEGER); VAR x, y, dx, dy, d, inc, Xmin, Xmax, Ymin, Ymax: INTEGER; BEGIN x1 := P.CX(x1); y1 := P.CY(y1); x2 := P.CX(x2); y2 := P.CY(y2); IF x1 < x2 THEN Xmin := x1; Xmax := x2 ELSE Xmin := x2; Xmax := x1 END; IF y1 < y2 THEN Ymin := y1; Ymax := y2 ELSE Ymin := y2; Ymax := y1 END; IF (y2-y1) < (x1-x2) THEN x := x1; x1 := x2; x2 := x; y := y1; y1 := y2; y2 := y END; dx := 2*(x2-x1); dy := 2*(y2-y1); x := x1; y := y1; inc := 1; IF dy > dx THEN d := dy DIV 2; IF dx < 0 THEN inc := -1; dx := -dx END; WHILE y <= y2 DO P.FillRect(P.Cx(x), P.Cy(y), P.scale, P.scale, col, 5, mode); INC(y); DEC(d, dx); IF d < 0 THEN INC(d, dy); INC(x, inc) END END ELSE d := dx DIV 2; IF dy < 0 THEN inc := -1; dy := -dy END; WHILE x <= x2 DO P.FillRect(P.Cx(x), P.Cy(y), P.scale, P.scale, col, 5, mode); INC(x); DEC(d, dy); IF d < 0 THEN INC(d, dx); INC(y, inc) END END END END DrawLine; PROCEDURE (P: Port) DrawRect*(x, y, w, h, col, mode: INTEGER); BEGIN IF P.scale = 1 THEN DEC(x); DEC(y); P.FillRect(x, y, w+3, 3, col, 5, mode); P.FillRect(x+w, y, 3, h+3, col, 5, mode); P.FillRect(x, y+h, w+3, 3, col, 5, mode); P.FillRect(x, y, 3, h+3, col, 5, mode) ELSE P.FillRect(x, y, w, P.scale, col, 5, mode); P.FillRect(x+w-P.scale, y, P.scale, h, col, 5, mode); P.FillRect(x, y+h-P.scale, w, P.scale, col, 5, mode); P.FillRect(x, y, P.scale, h, col, 5, mode) END END DrawRect; PROCEDURE HairEllipse (P: Port; X, Y, A, B, col, mode: INTEGER); (* due to B. Stamm *) VAR x, y: INTEGER; d, dx, dy, x2, y2, a, a2, a8, b, b2, b8: LONGINT; PROCEDURE Dot4(x1, x2, y1, y2, col, mode: INTEGER); BEGIN P.FillRect(x1, y1, P.scale, P.scale, col, 5, mode); P.FillRect(x1, y2, P.scale, P.scale, col, 5, mode); P.FillRect(x2, y1, P.scale, P.scale, col, 5, mode); P.FillRect(x2, y2, P.scale, P.scale, col, 5, mode); END Dot4; BEGIN IF A = B THEN (* circle *) DEC(A); x := A; y := 0; dx := 8*(x-1); dy := 8*y+4; d := 1 - 4*A; WHILE x > y DO Dot4(P.Cx(X-x-1), P.Cx(X+x), P.Cy(Y-y-1), P.Cy(Y+y), col, mode); Dot4(P.Cx(X-y-1), P.Cx(X+y), P.Cy(Y-x-1), P.Cy(Y+x), col, mode); INC(d, dy); INC(dy, 8); INC(y); IF d >= 0 THEN DEC(d, dx); DEC(dx, 8); DEC(x) END END; IF x = y THEN Dot4(P.Cx(X-x-1), P.Cx(X+x), P.Cy(Y-y-1), P.Cy(Y+y), col, mode) END ELSIF (A > 0) & (B > 0) THEN (* ellipse *) DEC(A); DEC(B); a := A; a2 := a*a; a8 := 8*a2; b := B; b2 := b*b; b8 := 8*b2; x := A; y := 0; x2 := a*b2; y2 := 0; dx := b8*(a-1); dy := 4*a2; d := b2*(1- 4*a); WHILE y2 < x2 DO Dot4(P.Cx(X-x-1), P.Cx(X+x), P.Cy(Y-y-1), P.Cy(Y+y), col, mode); INC(d, dy); INC(dy, a8); INC(y); INC(y2, a2); IF d >= 0 THEN DEC(d, dx); DEC(dx, b8); DEC(x); DEC(x2, b2) END END; INC(d, 4*(x2+y2)-b2+a2); WHILE x >= 0 DO Dot4(P.Cx(X-x-1), P.Cx(X+x), P.Cy(Y-y-1), P.Cy(Y+y), col, mode); DEC(d, dx); DEC(dx, b8); DEC(x); IF d < 0 THEN INC(d, dy); INC(dy, a8); INC(y) END END END END HairEllipse; PROCEDURE (P: Port) DrawEllipse*(x, y, a, b, col, mode: INTEGER); BEGIN HairEllipse(P, P.CX(x), P.CY(y), a DIV P.scale, b DIV P.scale, col, mode) END DrawEllipse; PROCEDURE (P: Port) DrawCircle*(x, y, r, col, mode: INTEGER); BEGIN HairEllipse(P, P.CX(x), P.CY(y), r DIV P.scale, r DIV P.scale, col, mode) END DrawCircle; PROCEDURE Line2(P: Port; col, pat, mode, x1, x2, y1, y2: INTEGER); BEGIN x1 := P.Cx(x1); x2 := P.Cx(x2); y1 := P.Cy(y1); y2 := P.Cy(y2); P.FillRect(x1, y1, x2-x1, P.scale, col, pat, mode); P.FillRect(x1, y2, x2-x1, P.scale, col, pat, mode) END Line2; PROCEDURE (P: Port) FillCircle* (x, y, r, col, pat, mode: INTEGER); VAR x1, y1, d, dx, dy: INTEGER; BEGIN x := P.CX(x); y := P.CY(y); r := r DIV P.scale; IF (P.X < x + r) & (x - r < P.X + P.W) & (P.Y < y + r) & (y - r < P.Y + P.H) THEN x1 := r - 1; y1 := 0; dx := (x1-1)*8; dy := y1*8 + 4; d := 3 - r*4; WHILE x1 > y1 DO Line2(P, col, pat, mode, x-x1-1, x+x1, y-y1-1, y+y1); IF d+dy >= 0 THEN Line2(P, col, pat, mode, x-y1-1, x+y1, y-x1-1, y+x1) END ; INC(d, dy); INC(dy, 8); INC(y1); IF d >= 0 THEN DEC(d, dx); DEC(dx, 8); DEC(x1) END END; IF x1 = y1 THEN Line2(P, col, pat, mode, x-x1-1, x+x1, y-y1-1, y+y1) END END END FillCircle; PROCEDURE (P: Port) FillQuad* (x1, y1, x2, y2, x3, y3, x4, y4, col, pat, mode: INTEGER); (* by B. Stamm *) TYPE LineParms = RECORD x,y,d,dx,dy,inx,iny,drawX,drawY: INTEGER END; VAR x,y,RHS2,RHS3: INTEGER; left,right: LineParms; PROCEDURE InitLineParms(x1,y1,x2,y2: INTEGER; VAR p: LineParms); BEGIN p.x := x1; p.dx := x2-x1; IF p.dx > 0 THEN p.inx := 1 ELSIF p.dx < 0 THEN p.inx := -1; p.dx := -p.dx ELSE p.inx := 0 END; p.y := y1; p.dy := y2-y1; IF p.dy > 0 THEN p.iny := 1 ELSIF p.dy < 0 THEN p.iny := -1; p.dy := -p.dy ELSE p.iny := 0 END; p.d := p.dy - p.dx; p.dx := 2*p.dx; p.dy := 2*p.dy; END InitLineParms; PROCEDURE LineStep(VAR p: LineParms); (* H = (d(x,y) := (2*x - 2*x1 + 1)*dy - (2*y - 2*y1 + 1)*dx < 0) *) BEGIN WHILE p.d < 0 DO INC(p.x,p.inx); INC(p.d,p.dy) END; p.drawX := p.x; p.drawY := p.iny DIV 2 + p.y; DEC(p.d,p.dx); INC(p.y,p.iny); END LineStep; PROCEDURE Max4(a,b,c,d: LONGINT): LONGINT; VAR m: LONGINT; BEGIN m := a; IF b > m THEN m := b END ; IF c > m THEN m := c END ; IF d > m THEN m := d END ; RETURN m END Max4; PROCEDURE Min4(a,b,c,d: LONGINT): LONGINT; VAR m: LONGINT; BEGIN m := a; IF b < m THEN m := b END ; IF c < m THEN m := c END ; IF d < m THEN m := d END ; RETURN m END Min4; BEGIN (* Quadrangle *) x1 := P.CX(x1); x2 := P.CX(x2); x3 := P.CX(x3); x4 := P.CX(x4); y1 := P.CY(y1); y2 := P.CY(y2); y3 := P.CY(y3); y4 := P.CY(y4); IF (Max4(x1, x2, x3, x4) >= P.X) & (Min4(x1, x2, x3, x4) <= P.X + P.W) & (Max4(y1, y2, y3, y4) >= P.Y) & (Min4(y1, y2, y3, y4) <= P.Y + P.H) THEN IF (y1>y2) OR (y1=y2) & (x1>x2) THEN x := x1; x1 := x2; x2 := x; y := y1; y1 := y2; y2 := y END; IF (y2>y3) OR (y2=y3) & (x2>x3) THEN x := x2; x2 := x3; x3 := x; y := y2; y2 := y3; y3 := y END; IF (y3>y4) OR (y3=y4) & (x3>x4) THEN x := x3; x3 := x4; x4 := x; y := y3; y3 := y4; y4 := y END; IF (y1>y2) OR (y1=y2) & (x1>x2) THEN x := x1; x1 := x2; x2 := x; y := y1; y1 := y2; y2 := y END; IF (y2>y3) OR (y2=y3) & (x2>x3) THEN x := x2; x2 := x3; x3 := x; y := y2; y2 := y3; y3 := y END; IF (y1>y2) OR (y1=y2) & (x1>x2) THEN x := x1; x1 := x2; x2 := x; y := y1; y1 := y2; y2 := y END; IF LONG(x2-x1)*LONG(y4-y1) > LONG(y2-y1)*LONG(x4-x1) THEN RHS2 := 2 ELSE RHS2 := 0 END; IF LONG(x3-x1)*LONG(y4-y1) > LONG(y3-y1)*LONG(x4-x1) THEN RHS3 := 1 ELSE RHS3 := 0 END; CASE RHS2 + RHS3 OF | 0: InitLineParms(x1,y1,x2,y2,left); InitLineParms(x1,y1,x4,y4,right); | 1: InitLineParms(x1,y1,x2,y2,left); InitLineParms(x1,y1,x3,y3,right); | 2: InitLineParms(x1,y1,x3,y3,left); InitLineParms(x1,y1,x2,y2,right); | 3: InitLineParms(x1,y1,x4,y4,left); InitLineParms(x1,y1,x2,y2,right); END; WHILE left.y # y2 DO LineStep(left); LineStep(right); P.FillRect(P.Cx(left.drawX),P.Cy(left.drawY),P.Cx(right.drawX)-P.Cx(left.drawX),P.scale,col,pat,mode) END; CASE RHS2 + RHS3 OF | 0: InitLineParms(x2,y2,x3,y3,left); | 1: InitLineParms(x2,y2,x4,y4,left); | 2: InitLineParms(x2,y2,x4,y4,right); | 3: InitLineParms(x2,y2,x3,y3,right); END; WHILE left.y # y3 DO LineStep(left); LineStep(right); P.FillRect(P.Cx(left.drawX),P.Cy(left.drawY),P.Cx(right.drawX)-P.Cx(left.drawX),P.scale,col,pat,mode) END; CASE RHS2 + RHS3 OF | 0,2: InitLineParms(x3,y3,x4,y4,left); | 1,3: InitLineParms(x3,y3,x4,y4,right); END; WHILE left.y # y4 DO LineStep(left); LineStep(right); P.FillRect(P.Cx(left.drawX),P.Cy(left.drawY),P.Cx(right.drawX)-P.Cx(left.drawX),P.scale,col,pat,mode) END END END FillQuad; (* ----------------- display drawing methods ------------------ *) PROCEDURE (P: DisplayPort) DrawLine*(x1, y1, x2, y2, col, mode: INTEGER); BEGIN Display1.Line(P, col, P.CX(x1), P.CY(y1), P.CX(x2), P.CY(y2), mode) END DrawLine; PROCEDURE (P: DisplayPort) DrawCircle*(x, y, r, col, mode: INTEGER); BEGIN Display1.Circle(P, col, P.CX(x), P.CY(y), r DIV P.scale, mode) END DrawCircle; PROCEDURE (P: DisplayPort) DrawEllipse*(x, y, a, b, col, mode: INTEGER); BEGIN Display1.Ellipse(P, col, P.CX(x), P.CY(y), a DIV P.scale, b DIV P.scale, mode); END DrawEllipse; PROCEDURE Intersect(F: Port; VAR X, Y, W, H: INTEGER): BOOLEAN; VAR t: INTEGER; BEGIN t := X+W; IF F.X > X THEN X := F.X END; IF F.X+F.W < t THEN W := F.X+F.W-X ELSE W := t-X END; IF W <= 0 THEN RETURN FALSE END; t := Y+H; IF F.Y > Y THEN Y := F.Y END; IF F.Y+F.H < t THEN H := F.Y+F.H-Y ELSE H := t-Y END; RETURN H > 0 END Intersect; PROCEDURE (P: DisplayPort) DrawString*(x, y: INTEGER; s: ARRAY OF CHAR; font: Fonts.Font; col, mode: INTEGER); VAR ch: CHAR; pat: LONGINT; X, i, dx, chx, chy, chw, chh, Y, oldX, oldY: INTEGER; fno: SHORTINT; BEGIN fno := TextPrinter.FontNo(font); X := P.CX(x); y := P.CY(y); ch := s[0]; i := 0; WHILE ch # 0X DO Display.GetChar(font.raster, ch, dx, chx, chy, chw, chh, pat); IF Ceres THEN X := X + chx; Y := y + chy; IF (X >= P.X) & (X+chw <= P.X + P.W) & (Y >= P.Y) & (Y+chh <= P.Y + P.H) THEN Display.CopyPattern(col, pat, X, Y, mode) ELSE oldX := X; oldY := Y; IF Intersect(P, X, Y, chw, chh) THEN Display.CopyBlock(X, Y, chw, chh, X - oldX, Y - oldY - 200, Display.replace); Display.CopyPattern(col, pat, 0, -200, mode); Display.CopyBlock(X - oldX, Y - oldY - 200, chw, chh, X, Y, Display.replace) END END ELSE Display.CopyPatternC(P, col, pat, X+chx, y+chy, mode) END ; INC(x, SHORT(TextPrinter.DX(fno, ch) DIV 3048)); X := P.CX(x + P.scale DIV 2); INC(i); ch := s[i] END END DrawString; PROCEDURE (P: DisplayPort) FillRect* (x, y, w, h, col, pat, mode: INTEGER); VAR xp, yp: INTEGER; BEGIN x := P.CX(x); y := P.CY(y); w := w DIV P.scale; h := h DIV P.scale; xp := P.CX(0); yp := P.CY(0); IF Ceres THEN IF Intersect(P, x, y, w, h) THEN Display.ReplPattern(col, Display1.ThisPattern(pat), x, y, w, h, mode) END ELSIF pat = 5 THEN (* solid fg *) Display.ReplConstC(P, col, x, y, w, h, mode) ELSE Display.ReplPatternC(P, col, Display1.ThisPattern(pat), x, y, w, h, xp, yp, mode) END END FillRect; (* ----------------- printer drawing methods ------------------ *) PROCEDURE (P: PrinterPort) DrawLine* (x1, y1, x2, y2, col, mode: INTEGER); BEGIN x1 := P.CX(x1); y1 := P.CY(y1); x2 := P.CX(x2); y2 := P.CY(y2); Printer.Line(x1, y1, x2, y2) END DrawLine; PROCEDURE (P: PrinterPort) DrawCircle* (x, y, r, col, mode: INTEGER); BEGIN Printer.Circle(P.CX(x), P.CY(y), r) END DrawCircle; PROCEDURE (P: PrinterPort) DrawEllipse* (x, y, a, b, col, mode: INTEGER); BEGIN Printer.Ellipse(P.CX(x), P.CY(y), a, b) END DrawEllipse; PROCEDURE (P: PrinterPort) DrawString* (x, y: INTEGER; s: ARRAY OF CHAR; font: Fonts.Font; col, mode: INTEGER); BEGIN Printer.String(P.CX(x), P.CY(y), s, font.name) END DrawString; PROCEDURE (P: PrinterPort) FillRect* (x, y, w, h, col, pat, mode: INTEGER); BEGIN IF pat = 5 THEN Printer.ReplConst(P.CX(x), P.CY(y), w, h) ELSE Printer.ReplPattern(P.CX(x), P.CY(y), w, h, pat) END END FillRect; (* ----------------- methods for finding the bounding box------------------ *) PROCEDURE MinMax(x, y: INTEGER; VAR min, max: INTEGER); BEGIN IF x < y THEN min := x; max := y ELSE min := y; max := x END END MinMax; PROCEDURE BlowUp (P: Port; x, y, w, h: INTEGER); BEGIN x := x + P.x0; y := y + P.y0; IF x < P.X THEN P.W := P.W + P.X - x; P.X := x END ; IF x + w > P.X + P.W THEN P.W := x + w - P.X END ; IF y < P.Y THEN P.H := P.H + P.Y - y; P.Y := y END ; IF y + h > P.Y + P.H THEN P.H := y + h - P.Y END END BlowUp; PROCEDURE (P: BalloonPort) DrawRect* (x, y, w, h, col, mode: INTEGER); BEGIN P.DrawRect^(x, y, w, h, col, mode) (*BlowUp(P, x, y, w, h)*) END DrawRect; PROCEDURE (P: BalloonPort) DrawLine* (x1, y1, x2, y2, col, mode: INTEGER); VAR minx, miny, maxx, maxy: INTEGER; BEGIN MinMax(x1, x2, minx, maxx); MinMax(y1, y2, miny, maxy); BlowUp(P, minx, miny, maxx - minx, maxy - miny) END DrawLine; PROCEDURE (P: BalloonPort) DrawCircle* (x, y, r, col, mode: INTEGER); BEGIN BlowUp(P, x - r - 4 , y - r - 4, 2 * r + 4, 2 * r + 4) END DrawCircle; PROCEDURE (P: BalloonPort) DrawEllipse* (x, y, a, b, col, mode: INTEGER); BEGIN BlowUp(P, x - a - 4, y - b - 4, 2 * a + 4, 2 * b + 4) END DrawEllipse; PROCEDURE StringWidth*(VAR s: ARRAY OF CHAR; f: Fonts.Font): INTEGER; VAR fno: SHORTINT; ch: CHAR; dx, w, i, sdx, sx, sy, sw, sh: INTEGER; p: LONGINT; BEGIN fno := TextPrinter.FontNo(f); w := 0; i := 0; ch := s[0]; WHILE ch # 0X DO dx := SHORT(TextPrinter.DX(fno, ch) DIV 3048); INC(w, dx); INC(i); ch := s[i] END ; IF i > 0 THEN Display.GetChar(f.raster, s[i-1], sdx, sx, sy, sw, sh, p); sdx := sdx * 4; IF sdx > dx THEN INC(w, sdx - dx) END END ; RETURN w END StringWidth; PROCEDURE (P: BalloonPort) DrawString* (x, y: INTEGER; s: ARRAY OF CHAR; font: Fonts.Font; col, mode: INTEGER); BEGIN BlowUp(P, x, y+font.minY*4, StringWidth(s, font), font.height*4) END DrawString; PROCEDURE (P: BalloonPort) FillRect* (x, y, w, h, col, pat, mode: INTEGER); BEGIN BlowUp(P, x, y, w, h) END FillRect; PROCEDURE (P: BalloonPort) FillCircle* (x, y, r, col, pat, mode: INTEGER); BEGIN BlowUp(P, x - r - 4 , y - r - 4, 2 * r + 4, 2 * r + 4) END FillCircle; PROCEDURE (P: BalloonPort) FillQuad* (x1, y1, x2, y2, x3, y3, x4, y4, col, pat, mode: INTEGER); BEGIN MinMax(x1, x2, x1, x2); MinMax(x2, x3, x2, x3); MinMax(x3, x4, x3, x4); MinMax(x2, x3, x2, x3); MinMax(x1, x2, x1, x2); MinMax(y1, y2, y1, y2); MinMax(y2, y3, y2, y3); MinMax(y3, y4, y3, y4); MinMax(y2, y3, y2, y3); MinMax(y1, y2, y1, y2); BlowUp(P, x1, y1, x4 - x1, y4 - y1) END FillQuad; PROCEDURE InitBalloon*(P: BalloonPort); BEGIN P.scale := 1; P.X := 10000; P.Y := 10000; P.W := -20000; P.H := -20000 END InitBalloon; END KeplerPorts.