-- Graphics.mesa
-- Last changed by Doug Wyatt, August 30, 1982 5:38 pm

-- The principal client interface to the Cedar Graphics package

DIRECTORY
GraphicsBasic USING [baseMark, black, Box, Color, DeviceRef, FontRef, ImageRef,
Mark, nullMark, PaintMode, Path, StrokeEnds, Texture, white, YMode],
Rope USING [MaxLen, ROPE];

Graphics: CEDAR DEFINITIONS = {

------ Types ------

Context: TYPE = REF ContextRep; -- A display context
ContextRep: TYPE = RECORD[procs: REF GraphicsProcs, data: REF ANY];

Path: TYPE = GraphicsBasic.Path; -- A path container
DeviceRef: TYPE = GraphicsBasic.DeviceRef; -- A display device
FontRef: TYPE = GraphicsBasic.FontRef; -- A font
ImageRef: TYPE = GraphicsBasic.ImageRef; -- An image source

Box: TYPE = GraphicsBasic.Box; -- A rectangular box
--
RECORD[xmin,ymin,xmax,ymax: REAL]

------ Signals ------

WarningType: TYPE = {fontNotFound, notImplemented, temporarilyOutOfOrder, other};
ErrorType: TYPE = {singularTransformation, bug};
Warning: SIGNAL[type: WarningType]; -- may be resumed
Error: ERROR[type: ErrorType];

------ Procedures ------

-- Contexts --

NewContext: PROC[device: DeviceRef ← NIL] RETURNS[Context];
-- Return a new display context for the given device.
-- The initial clipping boundary is the entire screen.
-- The initial transformation puts the origin in the lower left corner
-- and establishes a coordinate system measured in points.
-- If device is NIL, NewContext returns a Context for the default display.

CopyContext: PROC[self: Context] RETURNS[Context];
-- Make a new context, copying the state of the given context.


-- Position --

-- A display context maintains a current position (CP). The CP is a location on the display
-- which is used to position characters and images. It is by altering the CP that an operator
-- displaying a character specifies where the next character on the text line should usually lie.

GetCP: PROC[self: Context, rounded: BOOLEANFALSE] RETURNS[x,y: REAL]
= INLINE { RETURN self.procs.GetCP[self, rounded] };
-- Return CP in user coordinates.
-- Note the effect of modifying the coordinate system: the position of CP
-- remains fixed on the display, but GetCP will return new coordinates.
-- May raise ERROR Error[singularTransformation]

SetCP: PROC[self: Context, x, y: REAL, rel: BOOLEANFALSE]
= INLINE { self.procs.SetCP[self, x, y, rel] };
-- Set CP to (x, y).
-- rel => use (CPx+x, CPy+y) instead of (x, y).


-- Paths --

-- Paths provide a mechanism for describing complex shapes. A path consists
-- of one or more trajectories; each trajectory is a connected sequence of
-- links; each link is a straight line segment or cubic curve segment.
-- A completed path can be interpreted as the centerline of a stroke or
-- as the outline(s) of a filled area.

-- A path always has a defined last point (LP). LP of an empty path is (0, 0).

NewPath: PROC[size: INT ← 0] RETURNS[Path];
-- Create a new path; its LP is (0, 0).
-- size is an optional hint for the number of points the path will hold

LastPoint: PROC[self: Path] RETURNS[x, y: REAL];
-- Return the path's current LP.

FlushPath: PROC[self: Path];
-- Empty the path; new LP is (0, 0).

MoveTo: PROC[self: Path, x, y: REAL, flush: BOOLEANTRUE];
-- Begin a new trajectory at (x, y); new LP is (x, y).
-- flush => empty the path first

LineTo: PROC[self: Path, x, y: REAL];
-- Extend a straight line from LP to (x, y); new LP is (x, y).

CurveTo: PROC[self: Path, x1, y1, x2, y2, x3, y3: REAL];
-- Extend a cubic curve from LP to (x3, y3); new LP is (x3, y3).
-- The curve is defined by four Bezier control points: LP, (x1, y1), (x2, y2), (x3, y3).

Rectangle: PROC[self: Path, x0, y0, x1, y1: REAL];
-- Add a rectangular trajectory to the path; new LP is (x0, y0).
-- The corners of the rectangle are (x0,y0) (x1,y0) (x1,y1) (x0,y1).
-- Equivalent to:
self.MoveTo[x0, y0, FALSE]; self.LineTo[x1, y0];
--
self.LineTo[x1, y1]; self.LineTo[x0, y1]; self.LineTo[x0, y0];


-- Drawing routines --

DrawTo: PROC[self: Context, x, y: REAL,
rel: BOOLEANFALSE]
= INLINE { self.procs.DrawTo[self, x, y, rel] };
-- Draw a thin line from CP to (x, y); new CP is (x, y).
-- rel => use (CPx+x, CPy+y) instead of (x, y)

StrokeEnds: TYPE = GraphicsBasic.StrokeEnds; -- {butt, square, round}

DrawStroke: PROC[self: Context, path: Path, width: REAL ← 0,
closed: BOOLEANFALSE, ends: StrokeEnds ← butt]
= INLINE { self.procs.DrawStroke[self, path, width, closed, ends] };
-- Draw a stroke of the given width centered along the given path.
-- If width=0, the "fat" flag (see below) is implicitly set, so the stroke will be visible.
-- closed => draw a closed stroke, connecting the last point to the first point
-- The ends parameter controls the treatment of the ends of the stroke:
-- butt => simply square off each end at its endpoint
-- square => extend the stroke by half its width at each end, then square off
-- round => form a semicircular cap at each end
-- Connections between straight segments of the stroke will be mitered.
-- If the stroke is closed, ends is ignored. (A closed stroke has no ends.)


DrawArea: PROC[self: Context, path: Path, parityFill: BOOLEANFALSE]
= INLINE { self.procs.DrawArea[self, path, parityFill] };
-- Draw the area outlined by the given path. All trajectories are
-- considered closed. If the path intersects itself or has overlapping
-- trajectories, a wrap number rule determines what points comprise
-- the interior: those with odd wrap number, if parityFill is TRUE,
-- nonzero wrap number otherwise.

DrawBox: PROC[self: Context, box: Box]
= INLINE { self.procs.DrawBox[self, box] };
-- Draw the area inside the specified box.

DrawImage: PROC[self: Context, image: ImageRef, raw: BOOLEANFALSE]
= INLINE { self.procs.DrawImage[self, image, raw] };
-- Draw the entire specified image, aligning its origin with CP.
-- raw => [Wizards only] store the uninterpreted image into the frame buffer


-- Mapping --

Translate: PROC[self: Context, tx,ty: REAL,
round: BOOLEANFALSE]
= INLINE { self.procs.Translate[self, tx,ty, round] };
-- Subsequent objects will be translated by tx,ty.
-- If round=TRUE, the translated origin will be rounded to integers
-- in the output device's coordinate system.

Scale: PROC[self: Context, sx,sy: REAL]
= INLINE { self.procs.Concat[self, sx, 0, 0, sy] };
-- Subsequent objects will be magnified in x and y by sx,sy respectively.

Rotate: PROC[self: Context, angle: REAL];
-- Subsequent objects will be rotated anticlockwise by angle degrees.

Concat: PROC[self: Context, m11,m12,m21,m22: REAL]
= INLINE { self.procs.Concat[self, m11,m12,m21,m22] };
-- The given transformation will be applied to subsequent objects.

Map: PROC[sc,dc: Context, sx,sy: REAL] RETURNS[dx,dy: REAL];
-- Map a point from one context coordinate system to another.
-- The source point (sx,sy) is interpreted in the source context (sc).
-- The resulting point (dx,dy) is the same point expressed in the
-- coordinate system of the destination context (dc).
-- May raise ERROR Error[singularTransformation]

WorldToUser: PROC[self: Context, wx,wy: REAL] RETURNS[x,y: REAL]
= INLINE { RETURN self.procs.WorldToUser[self, wx,wy] };
-- Transform a point from world to user coordinates.
-- May raise ERROR Error[singularTransformation]

UserToWorld: PROC[self: Context, x,y: REAL] RETURNS[wx,wy: REAL]
= INLINE { RETURN self.procs.UserToWorld[self, x,y] };
-- Transform a point from user to world coordinates.

-- Fill --

Color: TYPE = GraphicsBasic.Color; -- A color value
black: Color = GraphicsBasic.black;
white: Color = GraphicsBasic.white;
-- Some day, a Color will be a device-independent specification of a solid color.
-- For now, only
black and white are guaranteed to work for all devices.

SetColor: PROC[self: Context, color: Color]
= INLINE { self.procs.SetColor[self, color] };
-- Set a constant color for area filling.

GetColor: PROC[self: Context] RETURNS[Color]
= INLINE { RETURN self.procs.GetColor[self] };
-- Return the current color value.

SetStipple: PROC[self: Context, pattern: CARDINAL]
= INLINE { self.procs.SetColor[self, [tag: stipple, r: 0, g: pattern/256, b: pattern MOD 256]] };
-- Set a 4 by 4 bit stipple pattern for area filling.
-- This is an interim hack for producing various gray effects on bitmap displays; the
-- stipple pattern is simply aligned with the device pixels and tiled to fill the area.
-- Better conventions for sampled color are coming.


PaintMode: TYPE = GraphicsBasic.PaintMode;
-- {opaque, transparent, invert}

SetPaintMode: PROC[self: Context, mode: PaintMode] RETURNS[PaintMode]
= INLINE { RETURN self.procs.SetPaintMode[self, mode] };
-- Set the painting mode used to display an object:
-- opaque => all parts of the object replace the previous display
-- transparent => "white" parts of the object do not affect the display
-- invert => "black" parts of the object invert the previous display
-- Return the previous painting mode.

SetFat: PROC[self: Context, fat: BOOLEAN] RETURNS[BOOLEAN]
= INLINE { RETURN self.procs.SetFat[self, fat] };
-- If fat is TRUE, low resolution devices will overscan so that very
-- small areas and thin lines will not disappear; however, objects may
-- extend outside the clipping region by as much as a pixel.


-- Text --

MakeFont: PROC[name: Rope.ROPE] RETURNS[FontRef];
-- Return a FontRef for the specified font.
-- Example: font ← MakeFont["Helvetica10"];
-- Currently, fonts must be in "ks" (preferred) or "strike" format.
-- If the given name does not contain '., tries appending ".ks"
-- to obtain a file name for the font.
-- Raises SIGNAL Warning[fontNotFound] if no font file is found.
-- Returns a FontRef for a built in default font if the signal is RESUMEd.

GetDefaultFont: PROC[self: Context] RETURNS[FontRef]
= INLINE { RETURN self.procs.GetDefaultFont[self] };
-- Return the current default font for the given context.

SetDefaultFont: PROC[self: Context, font: FontRef]
= INLINE { self.procs.SetDefaultFont[self, font] };
-- Set the default font for the given context.

DrawRope: PROC[self: Context, rope: Rope.ROPE,
start: INT ← 0, len: INT ← Rope.MaxLen,
font: FontRef ← NIL];
-- Display a rope in the specified font (use the default font if font=NIL).
-- For each character in the given range:
-- place the reference point of the character at the current position,
-- then advance the current position by the character width.

DrawChar: PROC[self: Context, char: CHARACTER,
font: FontRef ← NIL];
-- Display a single character.

RopeBox: PROC[font: FontRef, rope: Rope.ROPE,
start: INT ← 0, len: INT ← Rope.MaxLen]
RETURNS[xmin,ymin,xmax,ymax: REAL];
-- Return the bounding box for the given (sub)string.
-- If the given text is placed at position [x,y],
-- it will lie within the box [x+xmin,y+ymin,x+xmax,y+ymax].

RopeWidth: PROC[font: FontRef, rope: Rope.ROPE,
start: INT ← 0, len: INT ← Rope.MaxLen]
RETURNS[xw,yw: REAL];
-- Return the width vector for the given (sub)string.
-- If the given text is placed at position [x,y],
-- following text will be placed at [x+xw,y+yw].

CharBox: PROC[font: FontRef, char: CHARACTER]
RETURNS[xmin,ymin,xmax,ymax: REAL];
-- Return the bounding box for a single character.

CharWidth: PROC[font: FontRef, char: CHARACTER]
RETURNS[xw,yw: REAL];
-- Return the width vector for a single character.

FontBox: PROC[font: FontRef]
RETURNS[xmin,ymin,xmax,ymax: REAL];
-- Return the bounding box for all the characters of the font superimposed.
-- The "font height" is ymax-ymin; "ascent" is ymax; "descent" is -ymin.


-- Clipping --

ClipArea: PROC[self: Context, path: Path, parityFill: BOOLEANFALSE,
exclude: BOOLEANFALSE]
= INLINE { self.procs.ClipArea[self, path, parityFill, exclude] };
-- Define a new clipping region.
-- If exclude is FALSE, the new region is that portion of the current
-- clipping region which lies inside the area specified by the given path.
-- If exclude is TRUE, the new region is that portion of the current
-- clipping region which lies OUTSIDE the area specified by the given path.

ClipBox: PROC[self: Context, box: Box,
exclude: BOOLEANFALSE]
= INLINE { self.procs.ClipBox[self, box, exclude] };
-- Define a new clipping region.
-- Similar to ClipArea; uses the specified box instead of a path.

IsPointVisible: PROC[self: Context, x,y: REAL] RETURNS[BOOLEAN]
= INLINE { RETURN self.procs.IsPointVisible[self, x, y] };
-- Return TRUE iff the given point is within the context's clipping region.

IsRectangular: PROC[self: Context] RETURNS[BOOLEAN]
= INLINE { RETURN self.procs.IsRectangular[self] };
-- Return TRUE iff the context's clipping region is a single rectangle.

GetBounds: PROC[self: Context] RETURNS[Box]
= INLINE { RETURN self.procs.GetBounds[self] };
-- Return a bounding box for the current clipping area.

Visible: PROC[self: Context] RETURNS[BOOLEAN]
= INLINE { RETURN self.procs.Visible[self] };
-- Return TRUE iff the current clipping region is nonempty.


-- Saving and Restoring

Mark: TYPE = GraphicsBasic.Mark;
-- RECORD[CARDINAL]
nullMark: Mark = GraphicsBasic.nullMark;
baseMark: Mark = GraphicsBasic.baseMark;

Save: PROC[self: Context] RETURNS[Mark]
= INLINE { RETURN self.procs.Save[self] };
-- Note the current state of the display context, and
-- return a mark value which can later be passed to Restore.
-- This call simply enables a copy-on-write mechanism; Save and Restore
-- are quite cheap if the context state is not actually changed.
-- The Save-Restore mechanism covers the following context state:
-- transformation ("Mapper") and YMode
-- clipping region ("Clipper")
-- color, paint mode, and "fat" flag
-- N.B.: the current position is NOT included.

Restore: PROC[self: Context, mark: Mark ← nullMark]
= INLINE { self.procs.Restore[self, mark] };
-- Restore the state of the display context to its condition
-- prior to the call on Save that returned the given mark.
-- Defaulting the mark means restore to the most recent Save.


GraphicsProcs: TYPE = RECORD[
GetCP: PROC[self: Context, rounded: BOOLEAN] RETURNS[x,y: REAL],
SetCP: PROC[self: Context, x,y: REAL, rel: BOOLEAN],
DrawTo: PROC[self: Context, x,y: REAL, rel: BOOLEAN],
DrawStroke: PROC[self: Context, path: Path, width: REAL, closed: BOOLEAN, ends: StrokeEnds],
DrawArea: PROC[self: Context, path: Path, parityFill: BOOLEAN],
DrawBox: PROC[self: Context, box: Box],
DrawImage: PROC[self: Context, image: ImageRef, raw: BOOLEAN],
Translate: PROC[self: Context, tx,ty: REAL, round: BOOLEAN],
Concat: PROC[self: Context, m11,m12,m21,m22: REAL],
WorldToUser: PROC[self: Context, wx,wy: REAL] RETURNS[x,y: REAL],
UserToWorld: PROC[self: Context, x,y: REAL] RETURNS[wx,wy: REAL],
SetColor: PROC[self: Context, color: Color],
GetColor: PROC[self: Context] RETURNS[Color],
SetPaintMode: PROC[self: Context, mode: PaintMode] RETURNS[PaintMode],
SetFat: PROC[self: Context, fat: BOOLEAN] RETURNS[BOOLEAN],
GetDefaultFont: PROC[self: Context] RETURNS[FontRef],
SetDefaultFont: PROC[self: Context, font: FontRef],
DrawChars: PROC[self: Context, font: FontRef, map: PROC[PROC[CHAR] RETURNS[BOOL]]],
ClipArea: PROC[self: Context, path: Path, parityFill: BOOLEAN, exclude: BOOLEAN],
ClipBox: PROC[self: Context, box: Box, exclude: BOOLEAN],
IsPointVisible: PROC[self: Context, x,y: REAL] RETURNS[BOOLEAN],
IsRectangular: PROC[self: Context] RETURNS[BOOLEAN],
GetBounds: PROC[self: Context] RETURNS[Box],
Visible: PROC[self: Context] RETURNS[BOOLEAN],
Save: PROC[self: Context] RETURNS[Mark],
Restore: PROC[self: Context, mark: Mark],
DrawBits: UNSAFE PROC[self: Context,
base: LONG POINTER, raster: CARDINAL, bitsPerPixel: [0..16),
x, y, w, h: CARDINAL, xorigin, yorigin: INTEGER],
UserToDevice: PROC[self: Context, x, y: REAL, rel: BOOLEAN] RETURNS[tx, ty: REAL],
DeviceToUser: PROC[self: Context, tx, ty: REAL, rel: BOOLEAN] RETURNS[x, y: REAL],
GetYMode: PROC[self: Context] RETURNS[GraphicsBasic.YMode],
SetYMode: PROC[self: Context, mode: GraphicsBasic.YMode],
DrawTexturedBox: PROC[self: Context, box: Box, texture: GraphicsBasic.Texture],
Disable: PROC[self: Context],
MoveDeviceRectangle: PROC[self: Context, width, height, fromX, fromY, toX, toY: NAT]
];

}.