File: PlotViewer.mesa, Copyright (C) 1985 by Xerox Corporation. All rights reserved.
Last Edited by:
Sweetsun Chen, August 1, 1985 7:23:18 pm PDT
DIRECTORY
BasicTime USING [GMT, nullGMT],
Convert USING [Error, RealFromRope],
FS USING [Error],
Icons USING [IconFlavor, NewIconFromFile],
Imager USING [Box, ClipRectangle, ConstantColor, Context, DoSave, MaskBox, MaskStroke, MaskVector, PathProc, Rectangle, RotateT, Scale2T, ScaleT, SetColor, SetFont, SetStrokeEnd, SetStrokeJoint, SetStrokeWidth, SetXY, ShowRope, TranslateT, VEC],
ImagerBackdoor USING [GetBounds, GetCP],
ImagerColor USING [GrayFromColor, RGB],
ImagerColorMap USING [LoadEntries, MapEntry, SetStandardColorMap],
ImagerDitheredDevice USING [ColorFromSpecialRGB],
ImagerFont USING [Extents, Find, Font, FontBoundingBox, RopeBoundingBox, Scale],
ImagerInterpress USING [Close, Create, DoPage, Ref],
ImagerPress USING [Close, SimpleCreate],
IO USING [int, PutFR, real, rope, time],
Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc],
MessageWindow USING [Append, Blink],
Plot USING [Curves, PlotSpec, PlotSpecRec, ReadPlotFile, RopeSequence, SavePlot, Vector],
PlotOps USING [BackgroundType, ColorType, Handle, HandleData, MaxStep, LineState, LineStateRec, LineStep, Lock, StateSequence, Unlock],
PopUpMenu USING [RequestSelection],
Real USING [FixI, RoundC, RoundI, RoundLI],
RealFns USING [Log, Power, SqRt],
Rope USING [Cat, Equal, Find, IsEmpty, ROPE, Substr],
Terminal USING [ColorCursorPresentation, Current, GetColorCursorPresentation, SetColorCursorPresentation, Virtual],
UserProfile USING [CallWhenProfileChanges, ListOfTokens, ProfileChangedProc, Token],
VFonts USING [EstablishFont],
ViewerClasses USING [Column, PaintProc, PaintRectangle, Viewer, ViewerClass, ViewerClassRec],
ViewerOps USING [CreateViewer, EnumerateViewers, EnumProc, OpenIcon, PaintViewer, RegisterViewerClass, SwapIconAndViewer],
ViewerTools USING [GetSelectionContents],
WindowManager USING [colorDisplayOn, RestoreCursor];
PlotViewer:
CEDAR
PROGRAM
IMPORTS BasicTime, Convert, FS, Imager, ImagerBackdoor, ImagerColor, ImagerColorMap, ImagerDitheredDevice, ImagerFont, ImagerInterpress, ImagerPress, Icons, IO, Menus, MessageWindow, Plot, PlotOps, PopUpMenu, Real, RealFns, Rope, Terminal, UserProfile, VFonts, ViewerOps, ViewerTools, WindowManager
EXPORTS Plot = {
OPEN Plot, PlotOps;
types
OutputType: TYPE = {screen, press, interpress};
FontType: TYPE = {title, normal};
VectorPair: TYPE = RECORD[start, end: Vector];
NColors: TYPE = [0..MaxNumerOfColors);
constants
TextHeightFudge: REAL = 4.0;
TextWidthFudge: REAL = 4.0;
minLineWidth: REAL = 1.0;
MaxNumerOfColors: INTEGER = 16;
coordRatio: REAL = 0.25/600.0; -- 0.25 m ~ 600 pixels
(constant) variables
plotIcon: Icons.IconFlavor ← Icons.NewIconFromFile["Plot.icons", 0];
plotViewerClass: ViewerClasses.ViewerClass ←
NEW [ViewerClasses.ViewerClassRec ←
[paint: Display, icon: plotIcon, cursor: textPointer]];
t12b: ImagerFont.Font = VFonts.EstablishFont["TimesRoman", 12, TRUE]; -- bold
t12bp: ImagerFont.Font = ImagerFont.Scale[
ImagerFont.Find["Xerox/PressFonts/TimesRoman-mrr"], 13.0];
h8: ImagerFont.Font = VFonts.EstablishFont["Helvetica", 8];
h8p: ImagerFont.Font = ImagerFont.Scale[
ImagerFont.Find["Xerox/PressFonts/Helvetica-mrr"], 9.0];
textFont: ARRAY FontType OF ARRAY OutputType OF ImagerFont.Font = [[t12b, t12bp, t12bp], [h8, h8p, h8p]];
fontHeight: ARRAY FontType OF ARRAY OutputType OF REAL;
virtual: Terminal.Virtual = Terminal.Current[];
parameters initialized or updated by ProcessUserProfile
initialColumn: ViewerClasses.Column; -- {static, left, right, color}
createOption: {closeOtherViewers, swapLastViewer, iconic, badCreateOption};
backgroundType: BackgroundType; -- {white, gray, darkGray, black, unknown}
color: ARRAY NColors OF Imager.ConstantColor;
public procedures
CreateViewer:
PUBLIC
PROC [spec: PlotSpec ←
NIL, iconic, inhibitDestroy:
BOOL ←
FALSE]
RETURNS [viewer: ViewerClasses.Viewer ←
NIL] = {
handle
handle: Handle ←
NEW[HandleData ← [
plotSpec: spec,
background: backgroundType
] ];
viewer ← ViewerOps.CreateViewer[
flavor: $Plot,
info: [
name: spec.file,
menu: MakeMenu[],
icon: plotIcon,
column: initialColumn,
iconic: TRUE,
inhibitDestroy: inhibitDestroy,
data: handle],
paint: FALSE];
IF iconic OR (createOption = iconic) THEN ViewerOps.PaintViewer[viewer, all, TRUE, NIL]
ELSE {
SELECT createOption
FROM
closeOtherViewers => ViewerOps.OpenIcon[
icon: viewer,
closeOthers: TRUE,
bottom: TRUE,
paint: TRUE];
swapLastViewer => {
vLast: ViewerClasses.Viewer ← NIL;
LastViewer: ViewerOps.EnumProc = {
IF v.column = initialColumn AND NOT v.iconic THEN {vLast ← v; RETURN[FALSE]};
}; -- LastViewer
ViewerOps.EnumerateViewers[LastViewer];
IF vLast =
NIL
THEN ViewerOps.OpenIcon[
icon: viewer,
closeOthers: TRUE,
bottom: TRUE,
paint: TRUE]
ELSE {
ViewerOps.SwapIconAndViewer[
icon: viewer,
openViewer: vLast,
paint: TRUE]; -- this paints the new viewer only.
ViewerOps.PaintViewer[vLast, all, TRUE, NIL]; -- paint the last viewer.
};
};
ENDCASE;
};
}; -- CreateViewer
AddVector:
PUBLIC
PROC [viewer: ViewerClasses.Viewer ←
NIL, vector: Vector ←
NIL] = {
IF vector =
NIL
OR
NOT IsPlotViewer[viewer]
THEN
RETURN
ELSE {
shouldPaint: BOOL ← FALSE;
expectedSize: CARDINAL;
handle: Handle ← NARROW[viewer.data];
IF handle #
NIL
THEN {
Lock[handle];
expectedSize ← handle.plotSpec.nCurvesMax + 1;
IF vector.size # expectedSize
THEN MessageWindow.Append[
IO.PutFR["Only %g elements in vector when %g expected.",
IO.int[vector.size], IO.int[expectedSize]], TRUE]
ELSE {
IF handle.curves =
NIL
THEN {
handle.lineStates ←
NEW[StateSequence[handle.plotSpec.nCurvesMax]];
Note: lineStates[n] is the state of curve # n+1.
FOR i:
CARDINAL
IN[0..handle.plotSpec.nCurvesMax)
DO
handle.lineStates[i] ← NEW[LineStateRec ← []];
ENDLOOP;
}
ELSE IF handle.curves.first # NIL THEN shouldPaint ← TRUE;
handle.curves ← CONS[vector, handle.curves]
};
Unlock[handle];
};
IF shouldPaint THEN ViewerOps.PaintViewer[viewer, client, FALSE, handle.curves];
};
}; -- AddVector
CreateSpec:
PUBLIC
PROC[file, title: Rope.
ROPE ←
NIL,
time: BasicTime.GMT ← BasicTime.nullGMT,
bounds: Imager.Box ← [0, 0, 0, 0], -- [xmin, xmax, ymin, ymax]
nCurvesMax: CARDINAL ← 0,
legendEntries: REF RopeSequence ← NIL
] RETURNS[spec: PlotSpec ← NIL] = {
spec ← NEW[PlotSpecRec ← [file, title, time, bounds, nCurvesMax, legendEntries]];
}; -- CreateSpec
SetSpec:
PUBLIC
PROC [viewer: ViewerClasses.Viewer ←
NIL, newSpec: PlotSpec ←
NIL] = {
Note: if original nCurvesMax # new one, then old data will be cleared.
IF IsPlotViewer[viewer]
THEN {
handle: Handle ← NARROW[viewer.data];
IF handle = NIL THEN RETURN;
Lock[handle];
handle.curves ← NIL;
handle.plotSpec ← newSpec;
viewer.name ← handle.plotSpec.file;
Unlock[handle];
ViewerOps.PaintViewer[viewer, caption, FALSE, NIL];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL]; -- update the display.
};
}; -- SetSpec
Clear:
PUBLIC
PROC [viewer: ViewerClasses.Viewer] = {
IF IsPlotViewer[viewer]
THEN {
handle: Handle ← NARROW[viewer.data];
Lock[handle];
handle.plotSpec^ ← [];
handle.curves ← NIL;
Unlock[handle];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- Clear
Zoom:
PUBLIC
PROC [viewer: ViewerClasses.Viewer, newBounds: Imager.Box] = {
IF IsPlotViewer[viewer]
THEN {
handle: Handle ← NARROW[viewer.data];
Lock[handle];
handle.plotSpec.bounds ← newBounds;
Unlock[handle];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- Zoom
IsPlotViewer:
PUBLIC
PROC [viewer: ViewerClasses.Viewer]
RETURNS [
BOOL] = {
IF viewer = NIL THEN RETURN[FALSE];
RETURN[viewer.class.flavor = $Plot];
}; -- IsPlotViewer
menu commands
SetColor: Menus.MenuProc = {
[parent: REF, clientData: REF, mouseButton: MouseButton, shift, control: BOOL]
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
IF handle #
NIL
THEN {
newColorType: ColorType =
SELECT PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["my colors", "black and white", "Cedar default colors"],
default: 1] FROM
1 => mine, 2 => bw, 3 => cedar, ENDCASE => handle.colorType;
IF handle.colorType = newColorType THEN RETURN;
Lock[handle];
handle.colorType ← newColorType;
SELECT newColorType
FROM
mine => SetMyColors[];
cedar => SetCedarColors[];
ENDCASE;
Unlock[handle];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- SetColor
CursorColor: Menus.MenuProc = {
SELECT PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["black", "white"],
default: 1] FROM
1 => [] ← Terminal.SetColorCursorPresentation[virtual, onesAreBlack];
2 => [] ← Terminal.SetColorCursorPresentation[virtual, onesAreWhite];
ENDCASE;
}; -- CursorColor
BackgroundColor: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
IF handle #
NIL
THEN {
newBkGndType: BackgroundType =
SELECT PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["white", "gray", "dark gray", "black"],
default: 1] FROM
1 => white,
2 => gray,
3 => darkGray,
4 => black,
ENDCASE => handle.background;
IF handle.background = newBkGndType THEN RETURN;
Lock[handle];
handle.background ← newBkGndType;
SetCursorForBackgroundType[handle.background];
Unlock[handle];
ViewerOps.PaintViewer[viewer, client, TRUE, NIL];
};
}; -- BackgroundColor
Get: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
handle: Handle ← NARROW[viewer.data];
name: Rope.ROPE ← ViewerTools.GetSelectionContents[];
msg: Rope.
ROPE ←
IF name.IsEmpty[] THEN "Please select a plot file name."
ELSE IF handle = NIL THEN "Not a good plot viewer."
ELSE ReadPlotFile[name: name, viewer: viewer, iconic: FALSE];
IF msg #
NIL
THEN {
MessageWindow.Blink[];
MessageWindow.Append[msg, TRUE];
};
}; -- Get
Store: Menus.MenuProc = {
viewer: ViewerClasses.Viewer ← NARROW[parent];
name: Rope.ROPE ← ViewerTools.GetSelectionContents[];
msg: Rope.ROPE ← SavePlot[viewer, name]; -- viewer type checked there
IF msg #
NIL
THEN {
MessageWindow.Blink[];
MessageWindow.Append[message: msg, clearFirst: TRUE];
};
}; -- Store
AppendExtension:
PROC[old, ext: Rope.
ROPE ←
NIL]
RETURNS [Rope.
ROPE]= {
excl: INT ← old.Find["!"];
RETURN[Rope.Cat[
SELECT excl
FROM
< 0 => old,
> 0 => old.Substr[0, excl],
ENDCASE => "Plot", -- unlikely, though.
".",
ext]];
}; -- AppendExtension
GetPrintingColor:
PROC[]
RETURNS [type: ColorType, ok:
BOOL ←
TRUE] = {
colorChoice:
NAT = PopUpMenu.RequestSelection[
label: "Options",
choice: LIST["color", "black and white"],
default: 1];
SELECT colorChoice
FROM
1 => type ← mine;
2 => type ← bw;
ENDCASE => {
MessageWindow.Blink[];
MessageWindow.Append[
"You must choose either color or black-and-white.", TRUE];
ok ← FALSE;
};
}; -- GetPrintingColor
Print:
PROC[viewer: ViewerClasses.Viewer, output: OutputType ← interpress] = {
handle: Handle ← NARROW[viewer.data];
colorToPrint: ColorType;
okToPrint: BOOL;
[colorToPrint, okToPrint] ← GetPrintingColor[];
IF okToPrint
THEN {
context: Imager.Context ← NIL;
interpress: ImagerInterpress.Ref ← NIL;
fileName: Rope.ROPE;
colorSave: ColorType;
Lock[handle]; {
colorSave ← handle.colorType;
handle.colorType ← colorToPrint;
SELECT output
FROM
interpress
=> {
InterpressDrawMe:
PROC[context: Imager.Context] ~ {
context.RotateT[90];
context.ScaleT[coordRatio];
DrawMe[context, handle, NIL, interpress];
};
fileName ← AppendExtension[handle.plotSpec.file, "ip"];
interpress ← ImagerInterpress.Create[fileName
!
FS.Error => {
MessageWindow.Append[error.explanation, TRUE];
CONTINUE;
}
];
ImagerInterpress.DoPage[interpress, InterpressDrawMe];
MessageWindow.Append[Rope.Cat[fileName, " is written."]];
};
press => {
fileName ← AppendExtension[handle.plotSpec.file, "press"];
context ← ImagerPress.SimpleCreate[fileName
!
FS.Error => {
MessageWindow.Append[error.explanation, TRUE];
CONTINUE;
}
];
context.RotateT[90];
context.ScaleT[coordRatio];
DrawMe[context, handle, NIL, press];
MessageWindow.Append[Rope.Cat[fileName, " is written."]];
};
ENDCASE; -- (error)
IF output = interpress AND interpress # NIL THEN ImagerInterpress.Close[interpress]
ELSE IF output = press AND context # NIL THEN ImagerPress.Close[context];
handle.colorType ← colorSave;
}; Unlock[handle];
};
}; -- Print
Press: Menus.MenuProc = {Print[NARROW[parent], press]}; -- Press
Interpress: Menus.MenuProc = {Print[NARROW[parent], interpress]}; -- Interpress
SetCedarColors:
PROC [] = {
IF virtual.hasColorDisplay AND WindowManager.colorDisplayOn THEN {
ImagerColorMap.SetStandardColorMap[virtual];
WindowManager.RestoreCursor[];
IF Terminal.GetColorCursorPresentation[virtual] # onesAreBlack
THEN
[] ← Terminal.SetColorCursorPresentation[virtual, onesAreBlack];
};
}; -- SetCedarColors
PaintAllPlotViewers:
PROC [] = {
PaintEachPlotViewer: ViewerOps.EnumProc = {
IF v.column = color
AND
NOT v.iconic
AND v.class.flavor = $Plot
THEN
ViewerOps.PaintViewer[v, all, TRUE, NIL];
};
ViewerOps.EnumerateViewers[PaintEachPlotViewer];
}; -- PaintAllPlotViewers
SetMyColors:
PROC [] = {
defaultRGB:
ARRAY NColors
OF ImagerColor.
RGB ← [
white
[R: 1.0, G: 1.0, B: 1.0],
forgeround colors, all vivid
[R: 1.0, G: 0.0, B: 0.8], -- purple red
[R: 0.4, G: 0.0, B: 1.0], -- purple
[R: 0.0, G: 0.0, B: 1.0], -- blue
[R: 0.0, G: 0.56, B: 1.0], -- greenish blue
[R: 0.0, G: 1.0, B: 1.0], -- green blue
[R: 0.0, G: 1.0, B: 0.0], -- green
[R: 0.52, G: 1.0, B: 0.0], -- yellow green
[R: 1.0, G: 1.0, B: 0.0], -- yellow
[R: 1.0, G: 0.60, B: 0.0], -- orange yellow
[R: 1.0, G: 0.42, B: 0.0], -- yellowish orange
[R: 1.0, G: 0.25, B: 0.0], -- orange
[R: 1.0, G: 0.0, B: 0.0], -- red
gray
[R: 0.5, G: 0.5, B: 0.5], -- gray
[R: 0.17, G: 0.17, B: 0.17], -- dark gray
black
[R: 0.0, G: 0.0, B: 0.0] -- black
];
GetColor:
PROC [num: NColors]
RETURNS [ImagerColor.
RGB] = {
get color spec from user profile, or return default
ColorSpec: TYPE = LIST OF Rope.ROPE;
list: ColorSpec ← UserProfile.ListOfTokens[
key: IO.PutFR["Plot.Color%g", IO.int[num]],
default: NIL
];
c: ARRAY[1..3] OF REAL ← [defaultRGB[num].R, defaultRGB[num].G, defaultRGB[num].B];
i: CARDINAL ← 1;
FOR lr: ColorSpec ← list, list.rest
UNTIL list =
NIL
OR i > 3
DO
c[i] ← Convert.RealFromRope[list.first ! Convert.Error => EXIT];
c[i] ← MAX[0, MIN[1.0, c[i]]];
i ← i + 1;
ENDLOOP;
RETURN[[R: c[1], G: c[2], B: c[3]]];
}; -- GetColor
FOR i: NColors
IN NColors
DO
c: ImagerColor.RGB ← GetColor[i];
entry: ImagerColorMap.MapEntry ← [
mapIndex: i+100,
red: Real.RoundC[c.R*255],
green: Real.RoundC[c.G*255],
blue: Real.RoundC[c.B*255]
];
ImagerColorMap.LoadEntries[
vt: virtual,
mapEntries: LIST[entry],
shared: FALSE];
color[i] ← ImagerDitheredDevice.ColorFromSpecialRGB[
specialPixel: [i+100, null], rgb: c];
};
}; -- SetMyColors
other private procedures
Display: ViewerClasses.PaintProc = {
[self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL]
IF IsPlotViewer[self]
AND
NOT self.iconic
THEN {
there will be no checking of the viewer class in DrawMe.
handle: Handle ← NARROW[self.data];
IF handle #
NIL
THEN {
Lock[handle];
DrawMe[context, handle, whatChanged, screen];
Unlock[handle];
};
};
}; -- Display
DrawMe:
PROC [context: Imager.Context, handle: Handle, whatChanged:
REF
ANY, output: OutputType] = {
press context has been rotated
BoundsValid:
PROC [box: Imager.Box]
RETURNS [
BOOL] =
INLINE {
RETURN[box.xmax > box.xmin AND box.ymax > box.ymin];
}; -- BoundsValid
rect: Imager.Rectangle ←
IF output = interpress
THEN [0.0, -518.16, 670.56, 518.16]
ELSE ImagerBackdoor.GetBounds[context];
box: Imager.Box ← [rect.x, rect.y, rect.x + rect.w, rect.y + rect.h];
foreground: Imager.ConstantColor ←
IF output # screen
THEN color[15]
ELSE
IF handle.background = black OR handle.background = darkGray THEN color[0]
ELSE color[15];
IF whatChanged =
NIL
OR
ISTYPE[whatChanged, ViewerClasses.PaintRectangle]
THEN {
background: Imager.ConstantColor ←
IF output # screen
THEN color[0]
ELSE
SELECT handle.background
FROM
gray => color[13], darkGray => color[14], black => color[15], ENDCASE => color[0];
width: REAL ← rect.w;
height: REAL ← rect.h;
marginRatio: REAL = 0.05;
innerRatio: REAL = 0.9;
marginX: REAL ← width*marginRatio;
marginY: REAL ← height*marginRatio;
maxX: REAL ← box.xmax - marginX;
maxY: REAL ← box.ymax - marginY;
minX: REAL ← box.xmin + marginX;
minY: REAL ← box.ymin + marginY;
innerWidth: REAL ← width * innerRatio;
innerHeight: REAL ← height * innerRatio;
tenthHeight: REAL ← innerHeight/10.0;
textLineHeight: REAL ← fontHeight[normal][output] + TextHeightFudge;
windowTooLow: BOOL ← tenthHeight < textLineHeight;
legendTop: REAL ← IF windowTooLow THEN minY ELSE minY + tenthHeight*1.8;
axesTop: REAL ← IF windowTooLow THEN maxY ELSE maxY - tenthHeight;
titleBox: Imager.Box ← [minX, axesTop, maxX, maxY];
axesBox: Imager.Box ← [minX, legendTop, maxX, axesTop];
legendBox: Imager.Box ← [minX, minY, maxX, legendTop];
IF output = screen
THEN {
-- clear the screen
context.SetColor[background];
context.MaskBox[box];
};
title / legend / footnote
context.SetColor[foreground];
IF
NOT windowTooLow
THEN {
excl: INT ← handle.plotSpec.file.Find["!"];
pattern: Rope.ROPE = IF excl < 0 THEN handle.plotSpec.file ELSE handle.plotSpec.file.Substr[0, excl];
footNote: Rope.ROPE ← IO.PutFR["File: %g; time: %g.", IO.rope[pattern], IO.time[handle.plotSpec.time]];
DrawTitle[context, titleBox, handle.plotSpec.title, output];
DrawLegendFootnote[context, legendBox, handle.plotSpec.legendEntries,
footNote, output, handle.colorType];
};
axes and curves
IF BoundsValid[handle.plotSpec.bounds]
THEN {
c: Curves ← NIL;
[handle.curvesBox, handle.realBounds] ← DrawAxes[context, axesBox, handle.plotSpec.bounds, output];
context.ClipRectangle[
[handle.curvesBox.xmin,
handle.curvesBox.ymin,
handle.curvesBox.xmax - handle.curvesBox.xmin,
handle.curvesBox.ymax - handle.curvesBox.ymin]];
FOR l: Curves ← handle.curves, l.rest
UNTIL l =
NIL
DO
c ← CONS[l.first, c];
ENDLOOP;
DrawCurves[context, handle, c, output];
};
}
ELSE
IF
ISTYPE[whatChanged, Curves]
THEN {
OPEN handle;
cv: Curves ← NARROW[whatChanged];
IF cv #
NIL AND BoundsValid[handle.realBounds]
THEN {
m0, m1: Vector;
m1 ← cv.first;
IF m1 = NIL OR cv.rest = NIL THEN RETURN;
m0 ← cv.rest.first;
IF m0 = NIL THEN RETURN;
IF m0.size = m1.size
THEN {
tMin: REAL = realBounds.xmin;
tFactor: REAL = (curvesBox.xmax - curvesBox.xmin + 1)/(realBounds.xmax - tMin);
vMin: REAL = realBounds.ymin;
vFactor: REAL = (curvesBox.ymax - curvesBox.ymin + 1)/(realBounds.ymax - vMin);
t0: REAL = (m0[0] - tMin)*tFactor;
t1: REAL = (m1[0] - tMin)*tFactor;
context.ClipRectangle[
[handle.curvesBox.xmin,
handle.curvesBox.ymin,
handle.curvesBox.xmax - handle.curvesBox.xmin,
handle.curvesBox.ymax - handle.curvesBox.ymin]];
context.TranslateT[[curvesBox.xmin, curvesBox.ymin]];
context.SetStrokeEnd[round];
context.SetStrokeWidth[minLineWidth];
IF handle.colorType = bw
THEN {
context.SetColor[foreground];
FOR index:
CARDINAL
IN [1..m0.size)
DO
ipath: CARDINAL ← index - 1;
DrawLineSeg[context,
[t0, (m0[index]-vMin)*vFactor], [t1, (m1[index]-vMin)*vFactor],
ipath MOD 12, handle.lineStates[ipath]];
ENDLOOP;
}
ELSE FOR index:
CARDINAL
IN [1..m0.size)
DO
ipath: CARDINAL ← index - 1;
context.SetColor[color[(ipath MOD 12) + 1]];
context.MaskVector[
[t0, (m0[index]-vMin)*vFactor], [t1, (m1[index]-vMin)*vFactor]];
ENDLOOP;
};
};
};
}; -- DrawMe
DrawTitle:
PROC [context: Imager.Context, box: Imager.Box, title: Rope.
ROPE, output: OutputType] = {
xPos: REAL ← box.xmin + (box.xmax - box.xmin) / 2.0;
boxH: REAL ← box.ymax - box.ymin - fontHeight[normal][output];
IF title.IsEmpty[] THEN RETURN;
IF boxH >= fontHeight[title][output]
THEN
[] ← MyDrawText[context, title, xPos, box.ymax, center, top, title, output]
ELSE
IF boxH >= fontHeight[normal][output]
THEN
[] ← MyDrawText[context, title, xPos, box.ymax, center, top, normal, output];
}; -- DrawTitle
DrawAxes:
PROC [context: Imager.Context, box, bounds: Imager.Box, output: OutputType]
RETURNS [innerBox, realBounds: Imager.Box] = {
Border:
PROC = {
context.SetStrokeEnd[square];
context.SetStrokeJoint[round];
context.SetStrokeWidth[minLineWidth*2.0];
IF output = press
THEN {
-- Press doesn't support MaskStroke.
context.MaskVector[[innerBox.xmin, innerBox.ymin], [innerBox.xmin, innerBox.ymax]];
context.MaskVector[[innerBox.xmin, innerBox.ymax], [innerBox.xmax, innerBox.ymax]];
context.MaskVector[[innerBox.xmax, innerBox.ymax], [innerBox.xmax, innerBox.ymin]];
context.MaskVector[[innerBox.xmax, innerBox.ymin], [innerBox.xmin, innerBox.ymin]];
}
ELSE {
borderPath: Imager.PathProc = {
moveTo[[innerBox.xmin, innerBox.ymin]];
lineTo[[innerBox.xmin, innerBox.ymax]];
lineTo[[innerBox.xmax, innerBox.ymax]];
lineTo[[innerBox.xmax, innerBox.ymin]];
};
context.MaskStroke[path: borderPath, closed: TRUE];
};
}; -- Border
HorizontalAxisLabels:
PROC[context: Imager.Context, box: Imager.Box,
min, max, scale, step: REAL] = {
external vars referenced: output.
tickLen: REAL ← (box.ymax - box.ymin)/50.0;
tickCount: CARDINAL = Real.RoundI[(max - min)/step];
textTop: REAL ← box.ymin - TextHeightFudge;
tick: REAL;
FOR i:
CARDINAL
IN [0..tickCount]
DO
tick ← step*scale*i;
[] ← MyDrawText[context,
IO.PutFR["%g", IO.real[step*i + min]],
box.xmin + tick, textTop,
center, top, normal, output];
IF i # 0
AND i # tickCount
THEN {
xPos: REAL = box.xmin + tick;
yStart: REAL ← box.ymin;
yEnd: REAL ← box.ymin + tickLen;
Tick: Imager.PathProc = {moveTo[[xPos, yStart]]; lineTo[[xPos, yEnd]]};
proc: PROC = {context.MaskStroke[path: Tick, closed: FALSE]};
IF output = press THEN context.MaskVector[[xPos, yStart], [xPos, yEnd]]
ELSE context.DoSave[proc];
yStart ← box.ymax; yEnd ← box.ymax - tickLen;
IF output = press THEN context.MaskVector[[xPos, yStart], [xPos, yEnd]]
ELSE context.DoSave[proc];
};
ENDLOOP;
}; -- HorizontalAxisLabels
VerticalAxisLabels:
PROC[context: Imager.Context, box: Imager.Box,
min, max, scale, step: REAL] = {
external vars referenced: output.
tickLen: REAL ← (box.ymax - box.ymin)/50.0;
tickCount: CARDINAL = Real.RoundI[(max - min)/step];
textRight: REAL ← box.xmin - TextWidthFudge;
tick: REAL;
FOR i:
CARDINAL
IN [0..tickCount]
DO
tick ← step*scale*i;
[] ← MyDrawText[context,
IO.PutFR["%g", IO.real[step*i + min]],
textRight, box.ymin + tick,
right, center, normal, output];
IF i # 0
AND i # tickCount
THEN {
yPos: REAL = box.ymin + tick;
xStart: REAL ← box.xmin;
xEnd: REAL ← box.xmin + tickLen;
Tick: Imager.PathProc = {moveTo[[xStart, yPos]]; lineTo[[xEnd, yPos]]};
proc: PROC = {context.MaskStroke[path: Tick, closed: FALSE]};
IF output = press THEN context.MaskVector[[xStart, yPos], [xEnd, yPos]]
ELSE context.DoSave[proc];
xStart ← box.xmax; xEnd ← box.xmax - tickLen;
IF output = press THEN context.MaskVector[[xStart, yPos], [xEnd, yPos]]
ELSE context.DoSave[proc];
};
ENDLOOP;
}; -- VerticalAxisLabels
xSize: REAL = box.xmax-box.xmin;
ySize: REAL = box.ymax-box.ymin;
xStep, yStep: REAL; -- between ticks
xScale, yScale: REAL;
nLabelsX, nLabelsY: CARDINAL;
hLabelWidth, hLabelHeight, vLabelWidth, vLabelHeight, tLabelWidth, tLabelHeight: REAL;
inBoxW, inBoxH: REAL;
[tLabelWidth, tLabelHeight] ← GetRopeSize[
IO.PutFR["%g",
IO.real[bounds.xmin]],
normal, output];
[hLabelWidth, hLabelHeight] ← GetRopeSize[
IO.PutFR["%g",
IO.real[bounds.xmax]],
normal, output];
hLabelWidth ← MAX[tLabelWidth, hLabelWidth];
hLabelHeight ← MAX[tLabelHeight, hLabelHeight];
[tLabelWidth, tLabelHeight] ← GetRopeSize[
IO.PutFR["%g",
IO.real[bounds.ymin]],
normal, output];
[vLabelWidth, vLabelHeight] ← GetRopeSize[
IO.PutFR["%g",
IO.real[bounds.ymax]],
normal, output];
vLabelWidth ← MAX[tLabelWidth, vLabelWidth];
vLabelHeight ← MAX[tLabelHeight, vLabelHeight];
innerBox ← [
xmin: box.xmin + vLabelWidth + TextWidthFudge,
ymin: box.ymin + hLabelHeight + TextHeightFudge*2,
xmax: box.xmax,
ymax: box.ymax];
IF innerBox.xmin > innerBox.xmax THEN innerBox.xmin ← box.xmin;
IF innerBox.ymin > innerBox.ymax THEN innerBox.ymin ← box.ymin;
inBoxW ← innerBox.xmax - innerBox.xmin;
inBoxH ← innerBox.ymax - innerBox.ymin;
context.DoSave[Border];
nLabelsX ← MAX[1, MIN[10, Real.RoundI[inBoxW/hLabelWidth*5/8]]];
nLabelsY ← MAX[1, MIN[10, Real.RoundI[inBoxH/vLabelHeight*5/8]]];
[realBounds.xmin, realBounds.xmax, xStep, xScale] ← ScaleAxis[
bounds.xmin, bounds.xmax, inBoxW, nLabelsX];
[realBounds.ymin, realBounds.ymax, yStep, yScale] ← ScaleAxis[
bounds.ymin, bounds.ymax, inBoxH, nLabelsY];
context.SetStrokeEnd[butt];
context.SetStrokeWidth[minLineWidth];
HorizontalAxisLabels[context, innerBox, realBounds.xmin, realBounds.xmax, xScale, xStep];
VerticalAxisLabels[context, innerBox, realBounds.ymin, realBounds.ymax, yScale, yStep];
}; -- DrawAxes
GetRopeSize:
PROC [text: Rope.
ROPE, fontType: FontType ← normal, output: OutputType ← screen]
RETURNS [dx, dy:
REAL] = {
font: ImagerFont.Font← textFont[fontType][output];
extents: ImagerFont.Extents ← ImagerFont.RopeBoundingBox[font, text];
dx ← extents.rightExtent - extents.leftExtent;
dy ← extents.descent + extents.ascent;
}; -- GetRopeSize
ScaleAxis:
PROC [minDataValue, maxDataValue, innerBoxSize:
REAL, nLabels:
CARDINAL]
RETURNS [min, max, step, scale: REAL] = {
step ← FindStepSize[maxDataValue - minDataValue, nLabels];
min ← AlignEnd[minDataValue, step, FALSE];
max ← AlignEnd[maxDataValue, step, TRUE];
IF Almost[min, max] THEN {max ← max + 50.0; min ← min - 50.0};
scale ← innerBoxSize/(max - min);
FindStepSize:
PROC [range:
REAL, nSteps:
CARDINAL]
RETURNS[step:
REAL] = {
logRange: REAL;
mantissa, minStep: REAL;
characteristic: INTEGER;
steps: ARRAY [0..6) OF REAL = [0.2, 0.5, 1.0, 2.0, 5.0, 10.0];
IF Almost[range, 0.0] THEN range ← 100.0;
logRange ← RealFns.Log[10.0, range];
characteristic ← Real.FixI[logRange];
mantissa ← logRange - characteristic;
IF logRange < 0.0
THEN {
characteristic ← characteristic - 1;
mantissa ← mantissa + 1.0};
minStep ← RealFns.Power[10.0, mantissa]/nSteps;
FOR i:
CARDINAL
IN [0..5)
DO
step ← steps[i];
IF step > minStep OR Almost[step, minStep] THEN EXIT
ENDLOOP;
IF characteristic >= 0
THEN
THROUGH [1..characteristic]
DO step ← step*10.0
ENDLOOP
ELSE THROUGH [1..-characteristic] DO step ← step/10.0 ENDLOOP;
step ← MAX[1.0, step];
}; -- FindStepSize
AlignEnd:
PROC [e, step:
REAL, roundUp:
BOOL]
RETURNS [ae:
REAL] = {
absE: REAL = ABS[e];
nSteps: INTEGER;
xend: REAL;
IF e = 0.0 THEN RETURN[0.0];
IF e < 0.0 THEN roundUp ← ~roundUp;
nSteps ← Real.RoundLI[absE/step - 0.5];
xend ← step*nSteps;
IF Almost[nSteps, absE/step] THEN ae ← xend
ELSE
IF roundUp
THEN ae ← IF xend >= absE THEN xend ELSE step*(nSteps + 1)
ELSE ae ← IF xend <= absE THEN xend ELSE step*(nSteps - 1);
IF e < 0.0 THEN ae ← -ae;
Almost:
PROC [p, q:
REAL]
RETURNS [a:
BOOL ←
TRUE] = {
max: REAL ← MAX[ABS[p], ABS[q]];
IF max # 0.0 THEN a ← (ABS[p - q]/max) < 0.00001;
}; -- Almost
MyDrawText:
PROC[context: Imager.Context,
text: Rope.ROPE, x0, y0: REAL,
xJustification: {left, center, right} ← left,
yJustification: {top, center, bottom} ← bottom,
fontType: FontType ← normal,
output: OutputType ← screen,
xScale, yScale: REAL ← 1.0]
RETURNS [Imager.VEC] = {
proc:
PROC = {
context.TranslateT[[x0, y0]];
context.SetXY[[xmin, ymin]];
context.SetFont[font];
context.Scale2T[[xScale, yScale]];
context.ShowRope[text];
}; -- proc
extents: ImagerFont.Extents;
xmin, ymin, width, height: REAL;
font: ImagerFont.Font = textFont[fontType][output];
IF text.IsEmpty[] THEN RETURN[[x0, y0]];
extents ← ImagerFont.RopeBoundingBox[font: font, rope: text];
extents ← [extents.leftExtent*xScale, extents.rightExtent*xScale,
extents.descent*yScale, extents.ascent*yScale];
width ← extents.rightExtent - extents.leftExtent;
height ← extents.descent + extents.ascent;
xmin←
SELECT xJustification
FROM
right => - width - extents.leftExtent,
center => - width/2.0 - extents.leftExtent,
ENDCASE => - extents.leftExtent;
ymin←
SELECT yJustification
FROM
top => - height + extents.descent,
center => - height/2.0 + extents.descent,
ENDCASE => extents.descent;
context.DoSave[proc];
RETURN[
IF output = interpress
THEN [x0 + width, y0 + height]
ELSE ImagerBackdoor.GetCP[context]];
}; -- MyDrawText
DrawCurves:
PROC [context: Imager.Context, handle: Handle,
curves: Curves, output: OutputType] = {
box: Imager.Box ← handle.curvesBox;
bounds: Imager.Box ← handle.realBounds;
colorType: ColorType ← handle.colorType;
IF curves #
NIL
THEN {
nCurvesMax: CARDINAL = curves.first.size - 1;
tFactor: REAL ← (box.xmax - box.xmin)/(bounds.xmax - bounds.xmin);
vFactor: REAL ← (box.ymax - box.ymin)/(bounds.ymax - bounds.ymin);
drawThem:
PROC = {
context.TranslateT[[box.xmin, box.ymin]];
context.SetStrokeWidth[minLineWidth];
context.SetStrokeJoint[mitered];
IF colorType = bw
THEN {
context.SetStrokeEnd[butt];
FOR i:
CARDINAL
IN [1..nCurvesMax]
DO
firstPoint: BOOL ← TRUE;
lastVec, newVec: Imager.VEC;
index: CARDINAL ← i - 1;
handle.lineStates[index]^ ← []; -- cf. AddVector.
FOR graph: Curves ← curves, graph.rest
UNTIL graph =
NIL
DO
newVec ← [
(graph.first[0] - bounds.xmin)*tFactor,
(graph.first[i] - bounds.ymin)*vFactor];
IF firstPoint THEN firstPoint ← FALSE
ELSE DrawLineSeg[context, lastVec, newVec,
index MOD 12, handle.lineStates[index]];
lastVec ← newVec;
ENDLOOP;
ENDLOOP;
}
ELSE {
context.SetStrokeEnd[round];
FOR i:
CARDINAL
IN [1..nCurvesMax]
DO
context.SetColor[color[((i-1) MOD 12)+1]];
IF output = press
THEN {
Press doesn't support MaskStroke.
normal: BOOL ← FALSE;
lastVec: Imager.VEC;
FOR graph: Curves ← curves, graph.rest
UNTIL graph =
NIL
DO
t: REAL ← (graph.first[0] - bounds.xmin)*tFactor;
v: REAL ← (graph.first[i] - bounds.ymin)*vFactor;
IF normal THEN context.MaskVector[lastVec, [t, v]] ELSE normal ← TRUE;
lastVec ← [t, v];
ENDLOOP;
}
ELSE {
oneCurve: Imager.PathProc = {
start: BOOL ← TRUE;
FOR graph: Curves ← curves, graph.rest
UNTIL graph =
NIL
DO
t: REAL ← (graph.first[0] - bounds.xmin)*tFactor;
v: REAL ← (graph.first[i] - bounds.ymin)*vFactor;
IF start THEN moveTo[[t, v]] ELSE lineTo[[t, v]];
start ← FALSE;
ENDLOOP;
}; -- oneCurve
context.MaskStroke[path: oneCurve, closed: FALSE];
};
ENDLOOP;
};
}; -- drawThem
IF nCurvesMax > 0 THEN context.DoSave[drawThem];
};
}; -- DrawCurves
LineShape: TYPE = {solid, dot, dash, longDash};
StepSize: ARRAY LineShape[dot..longDash] OF REAL = [3.0, 6.0, 9.0];
LineLimit: ARRAY LineShape[dot..longDash] OF REAL = [1.0, 4.0, 7.0]; -- beyond which is space.
LineStyle:
TYPE = NColors[0..12);
shape:
ARRAY LineStyle
OF
ARRAY LineStep
OF LineShape = [
[solid, solid, solid, solid, solid, solid], -- solid, useless
[dot, dot, dot, dot, dot, dot], -- dot
[dash, dash, dash, dash, dash, dash], -- dash
[longDash, longDash, longDash, longDash, longDash, longDash], -- longDash
[dot, dash, dot, dash, dot, dash], -- dot-dash
[dot, longDash, dot, longDash, dot, longDash], -- dot-longDash
[dot, dot, dash, dot, dot, dash], -- dot-dot-dash
[dot, dot, longDash, dot, dot, longDash], -- dot-dot-longDash
[dot, dash, dash, dot, dash, dash], -- dot-dash-dash
[dot, longDash, longDash, dot, longDash, longDash], -- dot-longDash-longDash
[dash, longDash, dash, longDash, dash, longDash], -- dash-longDash
[dot, dash, longDash, dot, dash, longDash] -- dot-dash-longDash
];
DrawLineSeg:
PROC[context: Imager.Context,
v0, v1: Imager.VEC ← [0.0, 0.0],
style: LineStyle ← 0,
state: LineState ←
NIL] = {
IF Almost[v0.x, v1.x] AND Almost[v0.y, v1.y] THEN RETURN;
IF state = NIL THEN state ← NEW[LineStateRec ← []];
IF style = 0 THEN context.MaskVector[v0, v1]
ELSE {
-- Note that normally state.step and state.progress will be updated.
dx: REAL = v1.x - v0.x;
dy: REAL = v1.y - v0.y;
lengthToGo: REAL ← RealFns.SqRt[dx*dx + dy*dy];
cosine: REAL ← dx/lengthToGo;
sine: REAL ← dy/lengthToGo;
newVec, oldVec: Imager.VEC ← v0;
stepSize, lineLimit, increment: REAL;
drawIt: BOOL;
UNTIL Almost[lengthToGo, 0.0]
DO
stepSize← StepSize[shape[style][state.step]];
lineLimit← LineLimit[shape[style][state.step]];
IF state.progress < lineLimit
THEN {
drawIt ← TRUE;
increment ← MIN[lengthToGo, lineLimit - state.progress];
}
ELSE {
drawIt ← FALSE;
increment ← MIN[lengthToGo, stepSize - state.progress];
};
lengthToGo ← lengthToGo - increment;
state.progress ← state.progress + increment;
newVec ← [oldVec.x + cosine*increment, oldVec.y + sine*increment];
IF drawIt THEN context.MaskVector[oldVec, newVec]
ELSE
IF Almost[state.progress, stepSize]
THEN {
state.step ← (state.step + 1) MOD MaxStep;
state.progress ← 0.0;
};
oldVec ← newVec;
ENDLOOP;
};
DrawLegendFootnote:
PROC [context: Imager.Context, box: Imager.Box,
legendEntries: REF RopeSequence, footNote: Rope.ROPE,
output: OutputType, colorType: ColorType] = {
nEntries: CARDINAL ← legendEntries.size;
MaxEntriesPerColumn: CARDINAL = 5;
xmin, xmax: REAL ← 0;
yIncPerRow: REAL ← MAX[-fontHeight[normal][output], (box.ymin - box.ymax) / MaxEntriesPerColumn]; -- note: they are negative.
yScale: REAL = -yIncPerRow / fontHeight[normal][output];
colorSampleLineLength: REAL = 34.0;
sampleLineLength:
ARRAY[0..12)
OF
REAL = [
34.0, 34.0, 34.0, 34.0, 34.0, 34.0, 34.0, 34.0, 31.0, 31.0, 34.0, 34.0];
state: LineState ← NEW[LineStateRec];
proc:
PROC = {
context.TranslateT[[box.xmin, box.ymax]];
IF yScale >= 0.99
THEN
[] ← MyDrawText[context, footNote, 0, -(box.ymax-box.ymin), left, top, normal, output];
FOR i:
CARDINAL
IN [0..nEntries)
DO
row: CARDINAL ← i MOD MaxEntriesPerColumn;
col: CARDINAL ← i / MaxEntriesPerColumn;
index: CARDINAL ← i MOD 12;
textStartX, textStartY, lineY, newX: REAL;
IF row = 0
AND col > 0
THEN {
xmin ← xmax + TextWidthFudge;
IF xmin >= box.xmax THEN EXIT;
};
textStartX ← xmin + 36.0;
textStartY ← row*yIncPerRow;
lineY ← textStartY + (yIncPerRow/2.0);
context.SetStrokeWidth[minLineWidth];
context.SetStrokeEnd[butt];
IF colorType = bw
THEN {
context.SetColor[foreground]; -- already done. ... bad!
state^ ← [];
DrawLineSeg[context, [xmin, lineY], [xmin+sampleLineLength[index], lineY],
index, state];
}
ELSE {
context.SetColor[color[index+1]];
context.MaskVector[[xmin, lineY], [xmin+colorSampleLineLength, lineY]];
};
[[newX, ]] ← MyDrawText [
context,
legendEntries[i],
textStartX, textStartY,
left, top, normal, output, 1.0, yScale];
IF newX > xmax THEN xmax ← newX;
ENDLOOP;
}; -- proc
context.DoSave[proc];
}; -- DrawLegendFootnote
MakeMenu:
PROC []
RETURNS [menu: Menus.Menu] = {
menu ← Menus.CreateMenu[];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Color", SetColor]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Cursor", CursorColor]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Background", BackgroundColor]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry[name: "Get", proc: Get, guarded: TRUE]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Store", Store]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Press", Press]];
Menus.AppendMenuEntry
[menu, Menus.CreateEntry["Interpress", Interpress]];
}; -- MakeMenus
ProcessUserProfile: UserProfile.ProfileChangedProc = {
initialColumn
rope: Rope.ROPE ← UserProfile.Token[key: "Plot.Column", default: "color"];
initialColumn ←
SELECT
TRUE
FROM
rope.Equal["color", FALSE] => color,
rope.Equal["left", FALSE] => left,
rope.Equal["right", FALSE] => right,
ENDCASE => static;
IF initialColumn = static
THEN {
MessageWindow.Append[
Rope.Cat["Warning: Illegal entry in user profile, Plot.Column: ", rope, "."],
TRUE];
initialColumn ← color;
};
IF initialColumn = color AND NOT WindowManager.colorDisplayOn THEN initialColumn ← left;
createOption
rope ← UserProfile.Token[key: "Plot.CreateOption", default: "closeOtherViewers"];
createOption ←
SELECT
TRUE
FROM
rope.Equal["closeOtherViewers", FALSE] => closeOtherViewers,
rope.Equal["swapLastViewer", FALSE] => swapLastViewer,
rope.Equal["iconic", FALSE] => iconic,
ENDCASE => badCreateOption;
IF createOption = badCreateOption
THEN {
MessageWindow.Append[
Rope.Cat["Warning: Illegal entry in user profile, Plot.CreateOption: ", rope, "."],
TRUE];
createOption ← closeOtherViewers;
};
backgroundType
rope ← UserProfile.Token[key: "Plot.Background", default: "white"];
backgroundType ←
SELECT
TRUE
FROM
rope.Equal["white", FALSE] => white,
rope.Equal["gray", FALSE] => gray,
rope.Equal["darkGray", FALSE] => darkGray,
rope.Equal["black", FALSE] => black,
ENDCASE => unknown;
IF backgroundType = unknown
THEN {
MessageWindow.Append[
Rope.Cat["Warning: Illegal entry in user profile, Plot.Background: ", rope, "."],
TRUE];
backgroundType ← white;
};
colors
SetMyColors[];
SetCursorForBackgroundType[backgroundType];
apply
PaintAllPlotViewers[];
}; -- ProcessUserProfile
SetCursorForBackgroundType:
PROC [bkgnd: BackgroundType ← white] = {
bkGndColor: Imager.ConstantColor ←
SELECT bkgnd
FROM
gray => color[13], darkGray => color[14], black => color[15], ENDCASE => color[0];
cursorType: Terminal.ColorCursorPresentation ←
IF ImagerColor.GrayFromColor[bkGndColor] > 0.5 THEN onesAreWhite ELSE onesAreBlack;
IF cursorType # Terminal.GetColorCursorPresentation[virtual]
THEN
[] ← Terminal.SetColorCursorPresentation[virtual, cursorType];
}; -- SetCursorForBackgroundType
Init:
PROC [] = {
ViewerOps.RegisterViewerClass[$Plot, plotViewerClass];
FOR f: FontType
IN FontType
DO
FOR o: OutputType
IN OutputType
DO
extents: ImagerFont.Extents ← ImagerFont.FontBoundingBox[textFont[f][o]];
fontHeight[f][o] ← extents.descent+extents.ascent;
ENDLOOP;
ENDLOOP;
ProcessUserProfile[firstTime];
UserProfile.CallWhenProfileChanges[ProcessUserProfile];
}; -- Init
Init[];
}.
CHANGE
LOG.
Created by: SChen in Cedar5.
SChen, May 14, 1985 5:39:02 pm PDT, custom icon.
SChen, July 11, 1985 11:03:15 pm PDT, => Cedar6.0 (CG, press => Imager, Interpress).
SChen, cleaned up some stuff related to color display.