StarField.Mesa
Copyright 1984 by Xerox Corporation. All rights reserved.
Last Edited by: Spreitzer, May 13, 1986 2:43:46 pm PDT
DIRECTORY Basics, BasicTime, FS, Histograms, IdleBackdoor, Imager, ImagerBackdoor, ImagerBox, ImagerFont, ImagerPath, ImagerPixelArray, ImagerPixelMap, ImagerPrivate, ImagerTransformation, IO, Menus, PrincOps, Process, Random, Real, RealFns, Rope, Terminal, ThisMachine, TIPUser, Vector2, ViewerClasses, ViewerOps;
StarField: CEDAR PROGRAM
IMPORTS BasicTime, FS, Histograms, IdleBackdoor, Imager, ImagerBackdoor, ImagerBox, ImagerFont, ImagerPixelMap, ImagerTransformation, IO, Process, Random, Real, RealFns, Rope, Terminal, ThisMachine, TIPUser, ViewerOps =
{
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;
PixelMap: TYPE = ImagerPixelMap.PixelMap;
PixelArray: TYPE = ImagerPixelArray.PixelArray;
Font: TYPE = ImagerFont.Font;
Transformation: TYPE = ImagerTransformation.Transformation;
Index: TYPE = NAT;
Data: TYPE = REF DataSeq;
DataSeq: TYPE = RECORD [data: SEQUENCE size: NAT OF Datum];
Datum: TYPE = RECORD [xw, yw, zw, xp, yp, rp: REAL, ci: NAT ← 0];
data: Data ← NIL;
ImageCache: TYPE = REF ImageCacheRep;
ImageCacheRep: TYPE = RECORD [
length: NAT ← 0,
images: SEQUENCE size: NAT OF CachedImage];
CachedImage: TYPE = RECORD [
rpMax, d: REAL,
di: INTEGER,
image: PixelMap];
TextData: TYPE = RECORD [
texts: TextList,
numTexts: NAT,
totalProbability: REAL];
TextList: TYPE = LIST OF Text;
Text: TYPE = RECORD [
text: ROPE,
bounds: Imager.Box,
cumProb: REAL
];
Request: TYPE = REF RequestRep;
RequestRep: TYPE = RECORD [
proc: PROC [data: REF ANY, context: Imager.Context],
data: REF ANY];
td: TextData;
imageCache: ImageCache ← NIL;
font: Font ← NIL;
toUnit: Transformation;
rs: Random.RandomStream ← Random.Create[seed: -1];
Milliseconds: TYPE = INT;
OneSecond: Milliseconds = 1000;
maxData: NAT ← 200;
minData: NAT ← 5;
histDtMin: Milliseconds ← 10;
histDtMax: Milliseconds ← 70;
goalDtOffsetLow: Milliseconds;
goalDtOffsetHigh: Milliseconds;
goalNDtProduct: INT;
FOVX: REAL ← 45;
edgeDegrees: REAL ← 65;
hither: REAL ← 0.01;
border: REAL ← 1.0;
viewerStartRp: REAL ← 0.35;
vtStartRp: REAL ← 2.5;
viewerRpMin: REAL ← 0.5;
vtRpMin: REAL ← 1.0;
rw: REAL ← 1;
initialDzw: REAL ← 10;
slovershoot: REAL ← 1.5;
upTextMin: Milliseconds ← 10*OneSecond;
upTextMax: Milliseconds ← 60*OneSecond;
downTextMin: Milliseconds ← 1*OneSecond;
downTextMax: Milliseconds ← 6*OneSecond;
usePeriod: BOOLFALSE;
squareThresh: REAL ← 1.25;
useXOR: BOOLTRUE;
histPerf: BOOLFALSE;
pickRandomNs: BOOLFALSE;
pickPeriod: NAT ← 10;
nData: NAT ← minData;
Dzw: REAL;
slowFactor: REAL ← 0.95;
avgDt: REAL--Milliseconds-- ← OneSecond;
decay: REAL ← 0.1;
hold: REAL ← 1.0 - decay;
pausePeriod: Process.Ticks ← 0;
retraces: NAT ← 1;
machineName: ROPE ← ThisMachine.Name[];
sfvcFlavor: ATOM ← $StarFieldViewerClass;
sfvc: ViewerClasses.ViewerClass ← NEW [ViewerClasses.ViewerClassRec ← [
flavor: sfvcFlavor,
notify: NotifySFV,
paint: PaintSFV,
tipTable: TIPUser.InstantiateNewTIPTable["StarFielder.TIP"]
]];
sfv: Viewer;
hists: ARRAY BOOL--useXOR-- OF Histograms.Histogram ← ALL[NIL];
okToGo: BOOLFALSE;
going: BOOLFALSE;
SetGoal: PROC [n1, dt1, n2, dt2, dd: REAL] = {
dt1 = o + p/n1
dt2 = o + p/n2
o: REAL;
goalNDtProduct ← Real.RoundLI[(dt1 - dt2)/(1.0/n1 - 1.0/n2)];
dt1 * n1 = o * n1 + p
dt2 * n2 = o * n2 + p
o ← (dt1*n1 - dt2*n2)/(n1 - n2);
goalDtOffsetLow ← Real.RoundLI[o - dd/2];
goalDtOffsetHigh ← Real.RoundLI[o + dd/2];
};
NotifySFV: PROC [self: Viewer, input: LIST OF REF ANY] = {
a: ATOMNARROW[input.first];
SELECT a FROM
$Start => {okToGo ← TRUE; IF NOT going THEN TRUSTED {Process.Detach[FORK Viewit[]]}};
$Stop => okToGo ← FALSE;
ENDCASE => ERROR;
};
PaintSFV: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOLFALSE] --ViewerClasses.PaintProc-- = {
IF whatChanged = NIL
THEN sfvPM ← PixelMapFromViewer[self]
ELSE WITH whatChanged SELECT FROM
r: Request => r.proc[r.data, context];
ENDCASE => ERROR;
};
sfvPM: PixelMap;
icConsumer: PROC [context: Imager.Context, pm: PixelMap];
Satisfy: PROC [data: REF ANY, context: Imager.Context] = {
icConsumer[context, sfvPM];
};
Viewit: PROC = {
r: Request;
GiveContext: PROC [to: PROC [context: Imager.Context, pm: PixelMap]] = {
TRUSTED {icConsumer ← to};
ViewerOps.PaintViewer[viewer: sfv, hint: client, clearClient: FALSE, whatChanged: r];
};
TRUSTED {
Process.SetPriority[Process.priorityNormal];
r ← NEW [RequestRep ← [Satisfy, NIL]]};
going ← TRUE;
sfvPM ← PixelMapFromViewer[sfv];
Dewit[
giveContext: GiveContext, xp0: 0, yp0: 0, xp1: sfv.cw, yp1: sfv.ch,
FOVX: FOVX, edgeDegrees: edgeDegrees, hither: hither, border: border,
startRp: viewerStartRp, rpMin: viewerRpMin, Stop: StopViewing,
background: Imager.black, vt: Terminal.Current[]];
going ← FALSE;
};
StopViewing: PROC RETURNS [BOOL] =
{RETURN [NOT okToGo]};
Staridle: PROC [parent: REF ANY, clientData: REF ANYNIL,
mouseButton: Menus.MouseButton ← red, shift, control: BOOLFALSE] --Buttons.ButtonProc-- = {Sleepit[NOT control]};
Sleepit: PROC [logout: BOOL] = {
[] ← IdleBackdoor.UseAlternateVT[vtProc: DoForVT, logout: logout];
};
vtContext: Imager.Context;
vtPM: PixelMap;
GiveVTContext: PROC [to: PROC [context: Imager.Context, pm: PixelMap]] =
{to[vtContext, vtPM]};
KeyTyped: PROC RETURNS [stop: BOOL] = {
stop ← IdleBackdoor.KeyTyped[IdleBackdoor.defaultKeyFilter]};
DoForVT: PROC [vt: Terminal.Virtual] = {
vt.Select[];
[] ← vt.SetBWBitmapState[allocated];
[vtContext, vtPM] ← ContextAndPMFromVT[vt];
[] ← vt.SetBWBitmapState[displayed];
Dewit[
giveContext: GiveVTContext, xp0: 0, yp0: 0,
xp1: vt.bwWidth, yp1: vt.bwHeight, FOVX: FOVX,
edgeDegrees: edgeDegrees, hither: hither, border: border,
startRp: vtStartRp, rpMin: vtRpMin, Stop: KeyTyped,
background: Imager.white, vt: vt];
[] ← vt.SetBWBitmapState[none];
};
Draw: PROC [context: Imager.Context, pm: PixelMap, index: Index] = {
xc: REAL ← data[index].xp;
yc: REAL ← data[index].yp;
r: REAL ← data[index].rp;
IF usePeriod THEN {
res: REALIF r < 1.5 THEN 0.25 ELSE IF r < 3 THEN 0.5 ELSE 1.0;
n: INT ← Real.RoundLI[r/res];
rr: REAL ← n*res;
nHalves: INT ← Real.RoundLI[r*2];
d: REALIF (nHalves MOD 2) = 0 THEN 0.0 ELSE 0.5;
size: Transformation ← ImagerTransformation.Concat[toUnit, ImagerTransformation.Scale[rr]];
Doit: PROC = {
context.SetXY[[xc, yc]];
context.ConcatT[size];
context.SetFont[font];
context.ShowChar['.];
};
xc ← Real.RoundLI[xc-d]+d;
yc ← Real.RoundLI[yc-d]+d;
context.DoSave[Doit];
}
ELSE IF imageCache # NIL AND imageCache.length # 0 AND r <= imageCache[imageCache.length-1].rpMax THEN {
ci: NAT ← data[index].ci;
xlated: PixelMap;
x: INTEGER ← Real.RoundI[xc];
y: INTEGER ← Real.RoundI[yc];
IF ci > 0 AND imageCache[ci-1].rpMax >= r THEN ERROR;
WHILE imageCache[ci].rpMax < r DO ci ← ci + 1 ENDLOOP;
data[index].ci ← ci;
xlated ← imageCache[ci].image.ShiftMap[pm.sSize - y + imageCache[ci].di - imageCache[ci].image.sSize, x - imageCache[ci].di];
pm.Transfer[source: xlated, function: [xor, null]];
}
ELSE {
d: REAL;
n: INT;
path: ImagerPath.PathProc = {
moveTo[[xc-r, yc]];
arcTo[[xc+r, yc], [xc-r, yc]];
};
n ← Real.RoundLI[r*2];
d ← IF (n MOD 2) = 0 THEN 0.0 ELSE 0.5;
xc ← Real.RoundLI[xc-d]+d;
yc ← Real.RoundLI[yc-d]+d;
IF r < squareThresh
THEN Imager.MaskRectangle[context, [xc-r, yc-r, r*2, r*2]]
ELSE Imager.MaskFill[context, path];
};
};
upText, downText, T: Milliseconds ← 0;
curText: Text;
cto: Vector2.VEC;
cts: REAL;
PMContext: PROC [pm: PixelMap] RETURNS [context: Imager.Context] = {
bm: ImagerBackdoor.Bitmap ← NEW [ImagerBackdoor.BitmapRep ← [
ref: pm.refRep.ref,
base: pm.refRep.pointer,
wordsPerLine: pm.refRep.rast,
width: pm.refRep.rast*Basics.bitsPerWord,
height: pm.refRep.lines]];
IF pm.refRep.lgBitsPerPixel # 0 OR pm.sMin # 0 OR pm.fMin # 0 THEN ERROR;
context ← ImagerBackdoor.BitmapContext[bm];
Imager.ConcatT[context, ImagerTransformation.Invert[ImagerBackdoor.GetT[context]]];
};
Dewit: PROC
[
giveContext: PROC [to: PROC [context: Imager.Context, pm: PixelMap]],
xp0, yp0, xp1, yp1, FOVX, edgeDegrees, hither, border, startRp, rpMin: REAL,
Stop: PROC RETURNS [BOOL], background: Imager.Color, vt: Terminal.Virtual] =
{
halfDxp, halfDyp, TanEdgeDegrees, TanHalfFOVX, TanHalfFOVY, scale, xo, yo: REAL;
PickText: PROC [T: Milliseconds] = {
p: REAL ← Choose[0, td.totalProbability*0.999];
tl: TextList;
FOR tl ← td.texts, tl.rest WHILE p > tl.first.cumProb DO NULL ENDLOOP;
curText ← tl.first;
upText ← T + rs.ChooseInt[upTextMin, upTextMax];
downText ← upText + rs.ChooseInt[downTextMin, downTextMax];
cts ← (xp1 - xp0)/(curText.bounds.xmax - curText.bounds.xmin)/2;
cto ← [
x: Choose[
xp0 - cts*curText.bounds.xmin,
xp1 - cts*curText.bounds.xmax],
y: Choose[
yp0 - cts*curText.bounds.ymin,
yp1 - cts*curText.bounds.ymax]];
};
DrawText: PROC [context: Imager.Context, pm: PixelMap] = {
InnerDoit: PROC = {
context.SetXY[cto];
context.TranslateT[cto];
context.ScaleT[cts];
context.SetFont[font];
context.ShowRope[curText.text];
};
Imager.DoSave[context, InnerDoit];
};
PickNew: PROC [index: Index, pickLast: BOOLFALSE] = {
data[index].ci ← 0;
data[index].zw ← scale*rw/startRp;
IF pickLast THEN {
prepareToDie ← FALSE;
dying ← TRUE;
data[index].yw ← data[index].xw ← 0;
}
ELSE {
halfDxw, halfDyw: REAL;
halfDxw ← data[index].zw * TanHalfFOVX * border;
halfDyw ← data[index].zw * TanHalfFOVY * border;
data[index].yw ← RealFns.TanDeg[Choose[-edgeDegrees, edgeDegrees]] * halfDxw/TanEdgeDegrees;
data[index].xw ← RealFns.TanDeg[Choose[-edgeDegrees, edgeDegrees]] * halfDyw/TanEdgeDegrees;
};
ToPort[index];
};
ToPort: PROC [index: Index] = {
data[index].xp ← (data[index].xw/data[index].zw)*scale + xo;
data[index].yp ← (data[index].yw/data[index].zw)*scale + yo;
data[index].rpMAX[rpMin, (rw/data[index].zw)*scale];
};
DrawInit: PROC [context: Imager.Context, pm: PixelMap] = {
Imager.SetColor[context, background];
Imager.MaskRectangle[context, [xp0, yp0, xp1 - xp0, yp1 - yp0]];
Imager.SetColor[context, ImagerBackdoor.invert];
FOR index: Index IN [0 .. nData) DO
PickNew[index];
Draw[context, pm, index];
ENDLOOP;
};
Update: PROC [index: Index] RETURNS [clip: BOOL] = {
clip ← FALSE;
data[index].zw ← data[index].zw - Dzw;
IF data[index].zw < hither
THEN clip ← TRUE
ELSE {ToPort[index];
IF
data[index].xp - data[index].rp > xp1 OR
data[index].xp + data[index].rp < xp0 OR
data[index].yp - data[index].rp > yp1 OR
data[index].yp + data[index].rp < yp0
THEN clip ← TRUE;
};
};
DrawDelta: PROC [context: Imager.Context, pm: PixelMap] ← IF useXOR THEN DrawDeltaByXOR ELSE DrawDeltaBuffered;
prevUp: BOOLFALSE;
DrawDeltaByXOR: PROC [context: Imager.Context, pm: PixelMap] = {
shouldUp: BOOL ← (T >= upText) AND (T < downText);
max: NATMAX[nData, newN];
index: Index ← 0;
Imager.SetColor[context, ImagerBackdoor.invert];
FOR index ← 0, index + 1 WHILE index < nData DO
need: BOOLTRUE;
WHILE need DO
Draw[context, pm, index];
need ← FALSE;
IF Update[index].clip THEN {
IF nData > newN THEN {
IF index < nData - 1 THEN {
data[index] ← data[nData-1];
need ← TRUE;
};
nData ← nData - 1;
}
ELSE PickNew[index];
};
ENDLOOP;
IF index < nData THEN Draw[context, pm, index];
ENDLOOP;
FOR index ← nData, index+1 WHILE index < newN DO
nData ← index + 1;
PickNew[index, prepareToDie];
IF index < nData THEN Draw[context, pm, index];
ENDLOOP;
IF shouldUp # prevUp THEN DrawText[context, pm];
prevUp ← shouldUp;
IF T >= downText THEN PickText[T];
};
DrawDeltaBuffered: PROC [context: Imager.Context, pm: PixelMap] = {
index: Index ← 0;
Imager.SetColor[bufferContext, background];
Imager.MaskRectangle[bufferContext, [0, 0, pm.fSize, pm.sSize]];
Imager.SetColor[bufferContext, ImagerBackdoor.invert];
FOR index ← 0, index + 1 WHILE index < nData DO
need: BOOLTRUE;
WHILE need DO
need ← FALSE;
IF Update[index].clip THEN {
IF nData > newN THEN {
IF index < nData - 1 THEN {
data[index] ← data[nData-1];
need ← TRUE;
};
nData ← nData - 1;
}
ELSE PickNew[index];
};
ENDLOOP;
IF index < nData THEN Draw[bufferContext, bufferPM, index];
ENDLOOP;
FOR index ← nData, index+1 WHILE index < newN DO
nData ← index + 1;
PickNew[index, prepareToDie];
IF index < nData THEN Draw[bufferContext, bufferPM, index];
ENDLOOP;
IF (T >= upText) AND (T < downText) THEN DrawText[bufferContext, bufferPM];
pm.Transfer[bufferPM];
IF T >= downText THEN PickText[T];
};
newN: NAT ← nData ← 1;
prepareToDie, dying: BOOLFALSE;
bufferPM: PixelMap;
bufferContext: Imager.Context;
oldP: BasicTime.Pulses;
choice: NAT ← 0;
data ← NEW [DataSeq[maxData]];
Dzw ← initialDzw;
halfDxp ← (xp1 - xp0)/2;
halfDyp ← (yp1 - yp0)/2;
TanEdgeDegrees ← RealFns.TanDeg[edgeDegrees];
TanHalfFOVX ← RealFns.TanDeg[FOVX/2];
TanHalfFOVY ← (halfDyp / halfDxp) * TanHalfFOVX;
scale ← halfDxp / TanHalfFOVX;
xo ← xp0 + halfDxp;
yo ← yp0 + halfDyp;
slowFactor ← 1 - (Dzw)/(slovershoot * scale*rw/startRp);
IF NOT useXOR THEN [bufferContext, bufferPM] ← MakeBuffer[xp0, yp0, xp1, yp1];
PickText[T ← 0];
giveContext[DrawInit];
oldP ← BasicTime.GetClockPulses[];
FOR i: INT ← 0, i+1 DO
newP: BasicTime.Pulses;
IF dying THEN DzwDzw * slowFactor
ELSE IF NOT prepareToDie THEN prepareToDie ← Stop[];
giveContext[DrawDelta];
IF nData = 0 THEN EXIT;
IF pausePeriod # 0 THEN Process.Pause[pausePeriod];
FOR i: NAT IN [0 .. retraces) DO
Terminal.WaitForBWVerticalRetrace[vt];
ENDLOOP;
newP ← BasicTime.GetClockPulses[];
IF newP > oldP THEN {
Dt: Milliseconds ← BasicTime.PulsesToMicroseconds[newP - oldP]/1000;
q: Milliseconds ← goalNDtProduct/nData;
goalDtLow: Milliseconds ← q + goalDtOffsetLow;
goalDtHigh: Milliseconds ← q + goalDtOffsetHigh;
IF histPerf THEN hists[useXOR].ChangeTransformed[x: nData, y: MIN[histDtMax, MAX[histDtMin, Dt]]];
avgDt ← hold*avgDt + decay*Dt;
IF pickRandomNs
THEN {IF (choice ← choice + 1) MOD pickPeriod = 1 THEN newN ← rs.ChooseInt[minData, maxData] ELSE newN ← nData}
ELSE newN ←
IF prepareToDie THEN nData + 1 ELSE
IF dying THEN 0 ELSE
IF avgDt < goalDtLow THEN MIN[maxData, nData + 1] ELSE
IF avgDt > goalDtHigh THEN MAX[minData, nData - 1] ELSE nData;
T ← T + Dt};
oldP ← newP;
ENDLOOP;
};
MakeBuffer: PROC [xmin, ymin, xmax, ymax: REAL] RETURNS [context: Imager.Context, pm: PixelMap] = {
ixmin, iymin, ixmax, iymax: INT;
ixmin ← Floor[xmin];
iymin ← Floor[ymin];
ixmax ← Ceiling[xmax];
iymax ← Ceiling[ymax];
pm ← ImagerPixelMap.Create[lgBitsPerPixel: 0, bounds: [sMin: 0, fMin: 0, sSize: iymax - iymin, fSize: ixmax - ixmin]];
context ← PMContext[pm];
};
ContextAndPMFromVT: PROC [vt: Terminal.Virtual] RETURNS [context: Imager.Context, pm: PixelMap] = {
pm ← PixelMapFromVT[vt];
context ← PMContext[pm];
};
PixelMapFromVT: PROC [vt: Terminal.Virtual] RETURNS [pm: PixelMap] = {
fb: Terminal.FrameBuffer ← vt.GetBWFrameBuffer[];
IF fb.bitsPerPixel # 1 THEN ERROR;
pm ← [
sOrigin: 0, fOrigin: 0,
sMin: 0, fMin: 0,
sSize: fb.height, fSize: fb.width,
refRep: NEW [ImagerPixelMap.PixelMapRep ← [
ref: fb.vm,
pointer: fb.base,
words: fb.vm.words,
lgBitsPerPixel: 0,
rast: fb.wordsPerLine,
lines: fb.height]]
];
};
PixelMapFromViewer: PROC [v: Viewer] RETURNS [pm: PixelMap] = {
vx1, vx2, vy1, vy2: INTEGER;
height: NAT;
pm ← PixelMapFromVT[Terminal.Current[]];
height ← pm.sSize;
[vx1, vy1] ← ViewerOps.UserToScreenCoords[v, 0, 0];
[vx2, vy2] ← ViewerOps.UserToScreenCoords[v, v.cw, v.ch];
vy1 ← height - vy1;
vy2 ← height - vy2;
IF vy1 > vy2 THEN {y: INTEGER ← vy1; vy1 ← vy2; vy2 ← y};
pm ← pm.Clip[[sMin: vy1, fMin: vx1, sSize: vy2-vy1, fSize: vx2 - vx1]];
pm ← pm.ShiftMap[s: -vy1, f: -vx1];
};
CacheImages: PROC = {
Do: PROC [rpMax: REAL] = {
r: REAL ← rpMax*0.99;
n: INT ← Real.RoundLI[r*2];
o: REALIF (n MOD 2) = 0 THEN 0.0 ELSE 0.5;
l: INTEGER ← Floor[o-r];
size: NAT ← Ceiling[o+r] - l;
pm: PixelMap ← ImagerPixelMap.Create[0, [0, 0, size, size]];
context: Imager.Context ← PMContext[pm];
path: ImagerPath.PathProc = {
moveTo[[o-r, o]];
arcTo[[o+r, o], [o-r, o]];
};
context.TranslateT[[-l, -l]];
context.SetColor[Imager.white];
context.MaskRectangle[[-l, -l, size, size]];
context.SetColor[ImagerBackdoor.invert];
context.MaskFill[path];
imageCache[imageCache.length] ← [rpMax: rpMax, d: o-l, di: -l, image: pm];
imageCache.length ← imageCache.length + 1;
};
imageCache ← NEW [ImageCacheRep[6+8+9+9]];
imageCache.length ← 0;
Do[0.75];
Do[1.25];
Do[RealFns.SqRt[2]];
Do[1.75];
Do[2.25];
Do[2.75];
FOR i: NAT IN [3 .. 10] DO Do[i+0.5] ENDLOOP;
FOR i: NAT IN [1 .. 9] DO Do[11+2*i] ENDLOOP;
FOR i: NAT IN [1 .. 9] DO Do[32.5+5*i] ENDLOOP;
};
Floor: PROC [r: REAL] RETURNS [i: INT] = {
d: INT ← 1 - Real.Fix[r];
i ← Real.Fix[r+d]-d};
Ceiling: PROC [r: REAL] RETURNS [i: INT] = {
d: INT ← 1 + Real.Fix[r];
i ← Real.Fix[r-d]+d};
ShowCache: PROC = {
space: INT ← Real.RoundLI[2.5*imageCache[imageCache.length-1].rpMax];
s, f: INT ← space;
FOR i: NAT IN [0 .. imageCache.length) DO
xlated: PixelMap ← imageCache[i].image.ShiftMap[s, f];
sfvPM.Transfer[xlated];
f ← f + space;
IF f + space > sfvPM.fSize THEN {f ← space; s ← s + space};
ENDLOOP;
};
SizePeriod: PROC = {
bounds: ImagerFont.Extents;
aspectRatio: REAL;
bounds ← ImagerFont.BoundingBox[font, [0, ORD['.]]];
toUnit ← ImagerTransformation.Concat[
ImagerTransformation.Translate[
[-(bounds.leftExtent+bounds.rightExtent)/2, -(bounds.descent+bounds.ascent)/2]],
ImagerTransformation.Scale2[
[2/(bounds.rightExtent-bounds.leftExtent), 2/(bounds.ascent-bounds.descent)]]];
aspectRatio ← (bounds.ascent-bounds.descent) / (bounds.rightExtent-bounds.leftExtent);
IF aspectRatio < 0.999 OR aspectRatio > 1.001 THEN ERROR;
};
ReadTextData: PROC [fileName: ROPE] RETURNS [td: TextData] = {
from: IO.STREAMFS.StreamOpen[fileName];
last: TextList ← NIL;
td ← [
texts: NIL,
numTexts: 0,
totalProbability: 0];
DO
prob: REAL;
text: ROPE;
this: TextList;
[] ← from.SkipWhitespace[];
IF from.EndOf[] THEN EXIT;
prob ← from.GetReal[];
text ← from.GetRopeLiteral[];
text ← Replace[text, "<machine name>", machineName];
td.numTexts ← td.numTexts + 1;
this ← LIST[ [
text: text,
bounds: ImagerBox.BoxFromExtents[ImagerFont.RopeBoundingBox[font, text]],
cumProb: td.totalProbability ← td.totalProbability + prob] ];
IF last = NIL THEN td.texts ← this ELSE last.rest ← this;
last ← this;
ENDLOOP;
from.Close[];
};
Choose: PROC [min, max: REAL] RETURNS [r: REAL] =
{r ← min + (rs.ChooseInt[0, 10000]/1.0E4) * (max-min)};
Replace: PROC [in, what, with: ROPE] RETURNS [new: ROPE] = {
start, len: INT;
ousLen: INT ← what.Length[];
new ← in;
WHILE (start ← new.Index[s2: what]) < (len ← new.Length[]) DO
new ← new.Substr[len: start].Cat[with, new.Substr[start: start+ousLen, len: len - (start+ousLen)]];
ENDLOOP;
};
CreateViewer: PROC = {
sfv ← ViewerOps.CreateViewer[flavor: sfvcFlavor, info: [name: "StarField"]]};
NewHistograms: PROC = {
hists[FALSE] ← Histograms.Create2D[iMin: minData, iMax: maxData, jMin: histDtMin, jMax: histDtMax];
[] ← hists[FALSE].Show[[name: "Dt vs n (buffered)"]];
hists[TRUE] ← Histograms.Create2D[iMin: minData, iMax: maxData, jMin: histDtMin, jMax: histDtMax];
[] ← hists[TRUE].Show[[name: "Dt vs n (XOR)"]];
};
Start: PROC = {
SetGoal[n1: 15, dt1: OneSecond/20, n2: 50, dt2: OneSecond/40, dd: OneSecond/200];
IF histPerf THEN NewHistograms[];
ViewerOps.RegisterViewerClass[flavor: sfvcFlavor, class: sfvc];
CacheImages[];
font ← ImagerFont.Find["Xerox/PressFonts/TimesRoman-MRR"];
td ← ReadTextData["Starfield.texts"];
SizePeriod[];
};
Start[];
}.
At edge of viewing cone: xw = zw tan a/2
xp = (Dxp/2)(1 + xw/(zw tan a/2)) + xp0
rp = (Dxp/2)(rw/(zw tan a/2))
zw = (Dxp/2)(rw/(rp tan a/2))
{ &Test: PROC [xc, yc, r: REAL] = {Imager.MaskFill[StarField.bridgeContext, Imager.ArcTo[Imager.MoveTo[[xc-r, yc]], [xc+r, yc], [xc-r, yc]]]}; &Test[0, 0, 0]}
abcdefghijklmnopqrstuvwxyz1234567890-=\[]←',./
ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%~&*()—+|{}^:"<>?
蝩C%gfh). "`z{bP +
![ ]H*({ '`˧de