MODULE PS; /* A template for the PS interface to be read in Button or Public view only. */ (* The built-in Juno PostScript module. *) IMPORT Color; (* \section{Coordinate System} *) (* Juno uses the same coordinate system as the default PostScript coordinate system. The origin of this coordinate system is the lower-left corner of the page with "x" and "y" coordinates increasing to the left and up, respectively. The units of the coordinate system are PostScript {\it points}, equal to 1/72 of an inch. The "Unit" module defines constants and procedures for converting from points to other units. *) (* \section{Drawing Model} *) (* Juno uses the PostScript drawing model. In this model, ink is painted on the page by defining a {\it path} of straight and curved segments, and then {\it stroking} or {\it filling} the path with ink. There are separate procedures for painting text in a particular font. There are also procedures for reading the values of the PostScript state, such as the current point, and for measuring text strings. *) (* \section{Paths} *) (* A {\it segment} is a directed arc. A segment is defined mathematically by two parametric univariate cubic polynomials "x(t)" and "y(t)", where "t" ranges over the closed interval "[0, 1]". The segment starts at "(x(0), y(0))" and ends at "(x(1), y(1))". If "x" and "y" are linear functions of "t", then the segment is {\it linear}: it corresponds to a straight line segment. If "x" and "y" are constant functions of "t", then the segment is {\it degenerate}: it consists of a single point. A {\it sub-path} is a connected sequence of straight and curved line segments. Within a sub-path, each segment begins where the previous segment ends. Each sub-path is either {\it open} or {\it closed}. In a closed sub-path, the last segment ends where the first segment begins. (This may also happen for an open sub-path, but this coincidence does not make the sub-path closed.) A {\it path} is a (possibly empty) sequence of sub-paths. This module maintains a {\it current path}. The endpoint of the last segment of the last open sub-path of the current path is called the {\it current point}; if the current path is empty, or if the last sub-path of the current path is closed, the current point is undefined. *) (* Variables named "p", "q", "r", and "s" are points. *) PROC NewPath() IS SKIP END; (* Make the current path empty. *) PROC MoveTo(p) IS SKIP END; UI PointTool(MoveTo); (* Let "pp" be a new degenerate open sub-path starting and ending at "p". In the usual case, this procedure appends "pp" to the current path. However, in the special case that the current sub-path is non-empty, open, and degenerate (i.e., "MoveTo" was the most-recently called path construction procedure), this procedure instead replaces the current sub-path with "pp". In either case, "p" becomes the current point. *) PROC LineTo(p) IS SKIP END; UI PointTool(LineTo); (* Extend the current path with a straight line segment from the current point to "p"; "p" becomes the current point. It is a checked run-time error for the current point to be undefined. *) PROC CurveTo(q, r, s) IS SKIP END; UI PointTool(CurveTo); (* Extend the current path with a Bezier curve determined by the current point, "q", "r", and "s"; "s" becomes the current point. It is a checked run-time error for the current point to be undefined. *) (* "CurveTo" adds a curve that starts at the current point in the direction of "q", and ends at "s" coming from the direction of "r". More precisely, let "p" be the current point, and let "x(t)" and "y(t)" be the cubic polynomials such that: | (x(0), y(0)) = p | (x(1), y(1)) = s | (x'(0), y'(0)) = 3 * (q - p) | (x'(1), y'(1)) = 3 * (s - r) (where the primes denote differentiation with respect to "t"). Then "CurveTo" adds the segment "(x(t), y(t))" to the current path, for "t" in the closed interval "[0, 1]". This is called the {\it Bezier arc} determined by "p", "q", "r", and "s". *) PROC Close() IS SKIP END; UI PointTool(Close); (* Closes the last sub-path of the current path by adding a straight line segment from the current point to the starting point of the current sub-path. It is a checked run-time error for the current point to be undefined. *) (* \section{Painting} *) PROC Stroke() IS SKIP END; UI PointTool(Stroke); (* Stroke the current path using the current color, current width, current end style, and current joint style. *) PROC Fill() IS SKIP END; UI PointTool(Fill); (* Fill the region enclosed by the current path in the current color according to the current winding style. If the path consists of several disconnected sub-paths, the subpaths are filled individually. Any open subpaths are implicitly closed before being filled. *) PROC Type(p, txt) IS SKIP END; UI TextTool(Type); (* Paint the string "txt" at the point "p" in the current font and the current color. The current path is unchanged. The "Type" procedure uses the ISO-Latin-1 character encoding for the Courier, Helvetica, and Times font families, and the Symbol character encoding for the Symbol font family. See Appendix E of the ``PostScript Language Reference Manual, Second Edition''. Section E.7 shows the ISO-Latin-1 encoding, and Section E.12 shows the Symbol encoding. Warning: In the ISO-Latin-1 encoding, the easy-to-type "-" character with code value \055 is a minus sign, while the hyphen is hidden at code value \255. *) (* \section{Stroking} *) CONST DefaultWidth = 1; (* This module maintains a "current width", which is a number. *) PROC SetWidth(w) IS SKIP END; UI SetTool(SetWidth); (* Set the current width to "w". It is a checked run-time error for "w < 0". *) UI Param(SetWidth, DefaultWidth); UI Param(SetWidth, 0); UI Param(SetWidth, 1); UI Param(SetWidth, 2); UI Param(SetWidth, 3); UI Param(SetWidth, 4); UI Param(SetWidth, 5); UI Param(SetWidth, 10); UI Param(SetWidth, 20); CONST ButtEnds = 0, RoundEnds = 1, SquareEnds = 2, DefaultEnds = ButtEnds; (* This module maintains a "current end style", which is one of "ButtEnds", "RoundEnds", or "SquareEnds". *) PROC SetEndStyle(es) IS SKIP END; UI SetTool(SetEndStyle); (* Set the current end style to "es". It is a checked run-time error for "es" not to be one of the values "ButtEnds", "RoundEnds", or "SquareEnds". *) UI Param(SetEndStyle, DefaultEnds); UI Param(SetEndStyle, ButtEnds); UI Param(SetEndStyle, RoundEnds); UI Param(SetEndStyle, SquareEnds); CONST MiterJoints = 0, RoundJoints = 1, BevelJoints = 2, DefaultJoints = MiterJoints; (* This module maintains a "current joint style", which is one of "MiterJoints", "RoundJoints", and "BevelJoints". *) PROC SetJointStyle(js) IS SKIP END; UI SetTool(SetJointStyle); (* Set the current joint style to "js". It is a checked run-time error for "js" not to be one of the values "MiterJoints", "RoundJoints", or "BevelJoints". *) UI Param(SetJointStyle, DefaultJoints); UI Param(SetJointStyle, MiterJoints); UI Param(SetJointStyle, RoundJoints); UI Param(SetJointStyle, BevelJoints); PROC w := GetWidth() IS SKIP END; PROC es := GetEndStyle() IS SKIP END; PROC js := GetJointStyle() IS SKIP END; (* Return the current line width, end style, and joint style as set by the most recent calls to the corresponding "Set" procedures. *) (* \section{Filling} *) CONST DefaultColor = Color.Black; (* This module maintains a "current color", which is specified as a red, green, blue triple "[r, g, b]". A color is legal if "0 <= r, g, b <= 1". See the "Color" module. *) PROC SetColor(c) IS SKIP END; UI SetTool(SetColor); (* Set the current color to "c". It is a checked run-time error for "c" not to be a legal color. *) UI Param(SetColor, DefaultColor); UI Param(SetColor, Color.Black); UI Param(SetColor, Color.White); UI Param(SetColor, Color.Red); UI Param(SetColor, Color.Green); UI Param(SetColor, Color.Blue); UI Param(SetColor, Color.Cyan); UI Param(SetColor, Color.Magenta); UI Param(SetColor, Color.Yellow); UI Param(SetColor, Color.Grey25); UI Param(SetColor, Color.Grey50); UI Param(SetColor, Color.Grey75); CONST NonZeroWinding = 0, OddWinding = 1, DefaultWinding = NonZeroWinding; (* This module maintains a "current winding style", which is one of "OddWinding" or "NonZeroWinding". *) PROC SetWinding(ws) IS SKIP END; UI SetTool(SetWinding); (* Set the current winding style to "ws". It is a checked run-time error for "ws" not to be one of the constants "OddWinding" or "NonZeroWinding". *) UI Param(SetWinding, DefaultWinding); UI Param(SetWinding, OddWinding); UI Param(SetWinding, NonZeroWinding); PROC c := GetColor() IS SKIP END; PROC ws := GetWinding() IS SKIP END; (* Return the current color and winding style as set by the most recent calls to the corresponding "Set" procedures. *) (* \section{Text} *) CONST FontFaces = ["Times-Roman", "Times-Bold", "Times-Italic", "Times-BoldItalic", "Helvetica", "Helvetica-Bold", "Helvetica-Oblique", "Helvetica-BoldOblique", "Courier", "Courier-Bold", "Courier-Oblique", "Courier-BoldOblique", "Symbol"], DefaultFace = "Times-Roman"; (* This module maintains a current font face, which is one of the font faces named in the list "FontFaces". *) PROC SetFontFace(nm) IS SKIP END; UI SetTool(SetFontFace); (* Set the current font face to "nm"; the current font size is unchanged. It is a checked run-time error for "nm" not to be a member of the list "FontFaces". *) UI Param(SetFontFace, DefaultFace); UI Param(SetFontFace, "Times-Roman"); UI Param(SetFontFace, "Times-Bold"); UI Param(SetFontFace, "Times-Italic"); UI Param(SetFontFace, "Times-BoldItalic"); UI Param(SetFontFace, "Helvetica"); UI Param(SetFontFace, "Helvetica-Bold"); UI Param(SetFontFace, "Helvetica-Oblique"); UI Param(SetFontFace, "Helvetica-BoldOblique"); UI Param(SetFontFace, "Courier"); UI Param(SetFontFace, "Courier-Bold"); UI Param(SetFontFace, "Courier-Oblique"); UI Param(SetFontFace, "Courier-BoldOblique"); UI Param(SetFontFace, "Symbol"); CONST Tiny = 0, Small = 1, Medium = 2, Big = 3, Large = 4, Huge = 5, DefaultSize = Large, FontSizes = [Tiny, Small, Medium, Big, Large, Huge]; (* This module maintains a current font size, which is one of the symbolic font sizes in the list "FontSizes". *) PROC SetFontSize(sz) IS SKIP END; UI SetTool(SetFontSize); (* Set the current font size to "sz"; the current font face is unchanged. It is a checked run-time error for "sz" not to be a member of the list "FontSizes". *) UI Param(SetFontSize, DefaultSize); UI Param(SetFontSize, Tiny); UI Param(SetFontSize, Small); UI Param(SetFontSize, Medium); UI Param(SetFontSize, Big); UI Param(SetFontSize, Large); UI Param(SetFontSize, Huge); PROC SetFont(nm, sz) IS SKIP END; (* Equivalent to "SetFontFace(nm); SetFontSize(sz)". *) PROC nm := GetFontFace() IS SKIP END; PROC sz := GetFontSize() IS SKIP END; PROC nm, sz := GetFont() IS SKIP END; (* Return the current font face and size as set by the most recent calls to the corresponding "Set" procedures. *) PROC ptSz := GetFontPtSize() IS SKIP END; (* Return the height in points of the current font. This is the nominal distance between tightly-spaced lines of the font, and so is probably larger than the height of the font's capital letters. *) PROC asc, dec := FontHeight() IS SKIP END; (* Return the ascent and the decent of the current font. The ascent is the height of the character in the font that extends furthest above the baseline, and the decent is the depth of the character in the font that extends furthest below the baseline. Hence, the height of the font's bounding box is "asc + dec". *) PROC w := StringWidth(txt) IS SKIP END; (* Return the width of the string "txt" typed in the current font. *) PROC rect := StringBBox(txt) IS SKIP END; (* Return the bounding box of the string "txt" typed at the origin in the current font. The result "rect" is a pair of points "(sw, ne)", where "sw" and "ne" are the southwest and northeast corners of the bounding box, respectively. *) (* \section{Current Path State} *) PROC p := CurrentPoint() IS SKIP END; (* Returns the current point, or "NIL" if the current point is undefined. The current point is defined if and only if the current path is non-empty and its last sub-path is open. *) PROC path := CurrentPath() IS SKIP END; (* Returns a description of the current path, or "NIL" if the current path is empty. A path description is a list of sublists, where each sublist describes a segment of the path and takes one of the following forms: | ["MoveTo", p] | ["LineTo", p] | ["CurveTo", p, q, r] | ["Close"] In these descriptions, the variables "p", "q", and "r" range over points (i.e., pairs of numbers). *) (* \section{Bounding Box} *) (* This module maintains a "bounding box", which is a rectangle. The default bounding box is the bounding box of an 8-1/2" x 11" page. Juno displays the current bounding box as a grey rectangle on the screen, and writes the bounding box information into a "BoundingBox" comment when generating PostScript. *) PROC SetBBox(p, q) IS SKIP END; UI PointTool(SetBBox); (* Set the current bounding box to the rectangle whose opposite corners are "p" and "q". *) PROC p, q := GetBBox() IS SKIP END; (* Set "p" and "q" to points at opposite corners of the current bounding box. *) (* \section{PostScript State} *) PROC ShowPage() IS SKIP END; UI PointTool(ShowPage); (* Update the display to contain the current page. Since the Juno system executes an implicit ShowPage() at the end of the current command, you only need to use ShowPage() to achieve animation effects. *) PROC Reset() IS SKIP END; UI PointTool(Reset); (* Reset the PostScript state to its default value. Subsequent painting operations appear on a blank page. *) PROC SavePage() IS SKIP END; PROC RestorePage() IS SKIP END; (* Save/restore the current page to an offscreen buffer. This can be used in animations to quickly restore a background common to all frames. WARNING: When outputing PostScript, these procedures are no-ops. *) PROC Save() IS SKIP END; PROC Restore() IS SKIP END; UI PointTool(Save); UI PointTool(Restore); (* Save/Restore the entire state of this module. The state is saved to and restored from an internal stack. The state includes the current path, current width, current end style, current joint style, current color, current winding style, and current font. *)