<> <> DIRECTORY ImagerFont USING [Extents], Vector USING [VEC], MathTypes USING [FormatClass], MathBox; MathBoxImpl: CEDAR PROGRAM EXPORTS MathBox ~ BEGIN <> <<>> VEC: TYPE ~ Vector.VEC; FmtClass: TYPE ~ MathTypes.FormatClass; BoxType: TYPE ~ MathBox.BoxType; <> BoxRep: PUBLIC TYPE ~ RECORD [ tag: ATOM, -- named identifier aliases: LIST OF ATOM, -- list of aliases for name formatClass: FmtClass, -- formatting class, e.g. atom, binaryOp, radical type: BoxType, -- units type extents: ImagerFont.Extents _ [0.0, 0.0, 0.0, 0.0], -- bounding box extents offset: VEC _ [0.0, 0.0], -- offset vector to box origin subscript: BOX _ NIL, -- hint to place subscript superscript: BOX _ NIL -- hint to place superscript ]; BOX: TYPE ~ REF BoxRep; -- use concrete type inside implementation module <<>> <> Scale: PUBLIC PROC[b: BOX, v: VEC] RETURNS[BOX] ~ { <> << BOX extents and offset are scaled.>> RETURN[MakeBox[b.tag, b.aliases, b.formatClass, b.type, [leftExtent: v.x * b.extents.leftExtent, rightExtent: v.x * b.extents.rightExtent, descent: v.y * b.extents.descent, ascent: v.y * b.extents.ascent], [v.x * b.offset.x, v.y * b.offset.y], b.subscript, b.superscript]]; }; Inside: PUBLIC PROC[b: BOX, x, y: REAL] RETURNS[BOOL] ~ { <> << Otherwise returns FALSE.>> RETURN[(x >= b.offset.x - b.extents.leftExtent) AND (x <= b.offset.x + b.extents.rightExtent) AND (y >= b.offset.y - b.extents.descent) AND (y <= b.offset.y + b.extents.ascent)]; }; <<>> <<>> AlignHorizontal: PUBLIC PROC[box1: ImagerFont.Extents, offset1: REAL, box2: BOX, offset2: REAL] RETURNS[REAL] ~ { <> << points (offset1 * WIDTH[box1]) and (offset2 * WIDTH[box2]) are aligned.>> << Offsets are measured from the left edge of a box.>> << Returns the x offset for box1.>> <> RETURN[box2.offset.x + (offset2 * Width[box2]) - box2.extents.leftExtent + box1.leftExtent - (offset1 * (box1.leftExtent + box1.rightExtent))]; }; <<>> AlignVertical: PUBLIC PROC[box1: ImagerFont.Extents, offset1: REAL, box2: BOX, offset2: REAL] RETURNS[REAL] ~ { <> << (offset1 * HEIGHT[box1]) and (offset2 * HEIGHT[box2]) are aligned.>> << Offsets are measured from the bottom edge of a box.>> << Returns the y offset for box1.>> <<>> <> RETURN[box2.offset.y + (offset2 * Height[box2]) - box2.extents.descent + box1.descent - (offset1 * (box1.descent + box1.ascent))]; }; RelToAbsBox: PUBLIC PROC[relBox, parentBox: BOX] RETURNS[BOX] ~ { <> <> << by positioning it relative to parentBox>> IF (relBox.type # relative) OR (parentBox.type # absolute) THEN ERROR wrongBoxType; RETURN[MakeBox[tag: relBox.tag, aliases: relBox.aliases, formatClass: relBox.formatClass, type: absolute, extents: [leftExtent: Width[parentBox] * relBox.extents.leftExtent, rightExtent: Width[parentBox] * relBox.extents.rightExtent, ascent: Height[parentBox] * relBox.extents.ascent, descent: Height[parentBox] * relBox.extents.descent], offset: [parentBox.offset.x + (Width[parentBox] * relBox.offset.x), parentBox.offset.y + (Height[parentBox] * relBox.offset.y)], subscriptHint: relBox.subscript, superscriptHint: relBox.superscript]]; }; <> MakeBox: PUBLIC PROC[tag: ATOM, aliases: LIST OF ATOM, formatClass: FmtClass, type: BoxType, extents: ImagerFont.Extents _ [0.0, 0.0, 0.0, 0.0], offset: VEC _ [0.0, 0.0], subscriptHint, superscriptHint: BOX _ NIL] RETURNS[BOX] ~ { <> RETURN[NEW[BoxRep _ [tag: tag, aliases: aliases, formatClass: formatClass, type: type, extents: extents, offset: offset, subscript: subscriptHint, superscript: superscriptHint]]]; }; ChangeOffset: PUBLIC PROC[box: BOX, newOffset: VEC] RETURNS[BOX] ~ { <> RETURN[MakeBox[box.tag, box.aliases, box.formatClass, box.type, box.extents, newOffset, box.subscript, box.superscript]]; }; ChangeExtents: PUBLIC PROC[box: BOX, newExtents: ImagerFont.Extents] RETURNS[BOX] ~ { <> RETURN[MakeBox[box.tag, box.aliases, box.formatClass, box.type, newExtents, box.offset, box.subscript, box.superscript]]; }; ChangeType: PUBLIC PROC[box: BOX, newType: BoxType] RETURNS[BOX] ~ { <> RETURN[MakeBox[box.tag, box.aliases, box.formatClass, newType, box.extents, box.offset, box.subscript, box.superscript]]; }; <<>> ChangeFormatClass: PUBLIC PROC[box: BOX, newFormatClass: FmtClass] RETURNS[BOX] ~ { <> RETURN[MakeBox[box.tag, box.aliases, newFormatClass, box.type, box.extents, box.offset, box.subscript, box.superscript]]; }; ChangeSubHint: PUBLIC PROC[box: BOX, subscriptHint: BOX] RETURNS[BOX] ~ { <> RETURN[MakeBox[box.tag, box.aliases, box.formatClass, box.type, box.extents, box.offset, subscriptHint, box.superscript]]; }; ChangeSuperHint: PUBLIC PROC[box: BOX, superscriptHint: BOX] RETURNS[BOX] ~ { <> RETURN[MakeBox[box.tag, box.aliases, box.formatClass, box.type, box.extents, box.offset, box.subscript, superscriptHint]]; }; <<>> <<>> <> Width: PUBLIC PROC[b: BOX] RETURNS[REAL] ~ { <> RETURN[b.extents.leftExtent + b.extents.rightExtent]; }; Height: PUBLIC PROC[b: BOX] RETURNS[REAL] ~ { <> RETURN[b.extents.ascent + b.extents.descent]; }; Tag: PUBLIC PROC[b: BOX] RETURNS[ATOM] ~ { <> RETURN[b.tag]; }; Aliases: PUBLIC PROC[b: BOX] RETURNS[LIST OF ATOM] ~ { <> RETURN[b.aliases]; }; FormatClass: PUBLIC PROC[b: BOX] RETURNS[FmtClass] ~ { <> RETURN[b.formatClass]; }; Extents: PUBLIC PROC[b: BOX] RETURNS[ImagerFont.Extents] ~ { <> RETURN[b.extents]; }; <<>> Offset: PUBLIC PROC[b: BOX] RETURNS[VEC] ~ { <> RETURN[b.offset]; }; <<>> Origin: PUBLIC PROC[b: BOX] RETURNS[VEC] ~ { <> RETURN[[b.extents.leftExtent, b.extents.descent]]; }; Type: PUBLIC PROC[b: BOX] RETURNS[BoxType] ~ { <> << Else returns absolute.>> RETURN[b.type]; }; SubscriptHint: PUBLIC PROC[b: BOX] RETURNS[BOX] ~ { <> << Returns NIL if no hint exists.>> RETURN[b.subscript]; }; SuperscriptHint: PUBLIC PROC[b: BOX] RETURNS[BOX] ~ { <> << Returns NIL if no hint exists.>> RETURN[b.superscript]; }; <> GetBox: PUBLIC PROC[tag: ATOM, boxes: LIST OF BOX, useAliases: BOOL _ TRUE] RETURNS[BOX] ~ { <> << If useAliases = TRUE, then checks for BOX in boxes with alias tag.>> << SIGNALS boxNotFound if no association exists.>> FOR l: LIST OF BOX _ boxes, l.rest UNTIL l = NIL DO IF l.first.tag = tag THEN RETURN[l.first]; ENDLOOP; <> FOR l: LIST OF BOX _ boxes, l.rest UNTIL l = NIL DO FOR aliases: LIST OF ATOM _ l.first.aliases, aliases.rest UNTIL aliases = NIL DO IF aliases.first = tag THEN RETURN[l.first]; ENDLOOP; ENDLOOP; <> ERROR boxNotFound; }; MapBoxList: PUBLIC PROC[boxes: LIST OF BOX, proc: PROC[b: BOX] RETURNS[BOX]] RETURNS[LIST OF BOX] ~ { <> << BOX in boxes. Element positions in list are not preserved.>> newList: LIST OF BOX _ NIL; FOR l: LIST OF BOX _ boxes, l.rest UNTIL l = NIL DO newList _ CONS[proc[l.first], newList]; ENDLOOP; RETURN[newList]; }; <> boxNotFound: PUBLIC ERROR = CODE; wrongBoxType: PUBLIC ERROR = CODE; END.