implements Historgrams
Last Edited by: Spreitzer, November 1, 1985 4:47:33 pm PST
DIRECTORY Rope, IO, ViewerClasses, BiScrollers, Imager, ImagerBox, ImagerColor, ImagerFont, ViewerOps, VFonts, Geom2D, Histograms, Icons, MessageWindow, Real, TIPUser;
HistogramsImpl: CEDAR PROGRAM
IMPORTS IO, Imager, ImagerBox, ImagerColor, ImagerFont, BS:BiScrollers, VF:VFonts, Geom2D, Icons, VO: ViewerOps, MessageWindow, Real, Rope, TIPUser
EXPORTS Histograms =
BEGIN
ROPE: TYPE = Rope.ROPE;
BiScroller: TYPE = BS.BiScroller;
DataRef: TYPE = REF Data;
Data: TYPE = RECORD [counts: SEQUENCE length:CARDINAL OF CARDINAL];
Histogram: TYPE = REF HistogramRep;
HistogramRep: PUBLIC TYPE = RECORD [
dimensionality: [1 .. 2],
data: DataRef,
iMin, iMax, jMin, jMax, nI, nJ: INT ← 0,
iFactor, iOffset, jFactor, jOffset: REAL,
maxCount: INTEGER ← 0, maxInvalid: BOOLEANFALSE
];
HistogramView: TYPE = REF HistogramViewRep;
HistogramViewRep: TYPE = RECORD [
h: Histogram,
iFormat: ROPE, iChars, iFreq: CARDINAL,
vFreq: INTEGER, dHeight: REAL,
dWidth, cWidth: REAL ← 0,
noR: BOOLEANTRUE,
rxmin, rxmax: REAL ← 0,
finding: BOOLEANFALSE,
findFirstI, findFirstJ: INT ← 0
];
bsStyle: BS.BiScrollerStyle ← BS.GetStyle[];
histogramViewer: BS.BiScrollerClass;
ts: REAL ← 1;
tl: REAL ← 5;
ls: REAL ← 2;
adx, ady, awx, awy: REAL ← -1;
ySpacing: INTEGER;
dHeightInitial: REAL ← 1;
vFreqInitial: INTEGER ← 15;
NewHistogram: PUBLIC PROC
[
factor: REAL ← 1, --x = I*factor + offset
offset: REAL ← 0]
RETURNS [h: Histogram] =
BEGIN
d: DataRef ← NEW[Data[0]];
h ← NEW [HistogramRep ← [
dimensionality: 1,
data: d,
iFactor: factor, iOffset: offset, jFactor: 1, jOffset: 0
]];
END;
Create2D: PUBLIC PROC
[ --sorry, no labelling yet
iMin, iMax, jMin, jMax: INT,
iFactor, jFactor: REAL ← 1, --x = I*iFactor + iOffset
iOffset, jOffset: REAL ← 0] --y = J*jFactor + jOffset
RETURNS [h: Histogram] =
BEGIN
IF iMax < iMin OR jMax < jMin THEN ERROR;
h ← NEW [HistogramRep ← [
dimensionality: 2,
data: NIL,
iMin: iMin, iMax: iMax, jMin: jMin, jMax: jMax,
nI: iMax + 1 - iMin, nJ: jMax + 1 - jMin,
iFactor: iFactor, iOffset: iOffset, jFactor: jFactor, jOffset: jOffset
]];
h.data ← NEW [Data[h.nI * h.nJ]];
FOR k: CARDINAL IN [0 .. h.data.length) DO h.data[k] ← 0 ENDLOOP;
END;
make sure that n can be accessed
Ensure: PROCEDURE [h: Histogram, n: CARDINAL] =
BEGIN
d: DataRef ← h.data;
IF h.dimensionality # 1 THEN ERROR;
IF d.length <= n THEN
BEGIN
i: CARDINAL;
new: DataRef ← NEW[Data[n+1]];
FOR i ← 0, i+1 WHILE i<d.length DO
new.counts[i] ← d.counts[i];
ENDLOOP;
FOR i ← d.length, i+1 WHILE i<new.length DO
new.counts[i] ← 0;
ENDLOOP;
h.data ← new;
END;
END;
Change: PUBLIC PROCEDURE [h: Histogram, who: CARDINAL, howMuch: INTEGER] =
BEGIN
Ensure[h, who];
h.data.counts[who] ← h.data.counts[who] + howMuch;
IF NOT h.maxInvalid THEN SELECT howMuch FROM
< 0 => h.maxInvalid ← TRUE;
>= 0 => h.maxCount ← MAX[h.maxCount, h.data.counts[who]];
ENDCASE => ERROR;
END;
Change2: PROC [h: Histogram, i, j: INT, delta: INTEGER] =
BEGIN
index: NAT ← (i - h.iMin)*h.nJ + (j - h.jMin);
h.data.counts[index] ← h.data.counts[index] + delta;
IF NOT h.maxInvalid THEN SELECT delta FROM
< 0 => h.maxInvalid ← TRUE;
>= 0 => h.maxCount ← MAX[h.maxCount, h.data.counts[index]];
ENDCASE => ERROR;
END;
IncrementTransformed: PUBLIC PROC [h: Histogram, xmin, xmax, x: REAL] =
BEGIN
n: CARDINAL ← Real.RoundC[(MIN[xmax, MAX[xmin, x]] - h.iOffset)/h.iFactor];
Change[h, n, 1];
END;
ChangeTransformed: PUBLIC PROC [h: Histogram, x: REAL, y: REAL ← 0, delta: INTEGER ← 1] =
BEGIN
i, j: INT;
i ← Real.RoundLI[(x - h.iOffset)/h.iFactor];
j ← Real.RoundLI[(y - h.jOffset)/h.jFactor];
IF NOT (i IN [h.iMin .. h.iMax] AND j IN [h.jMin .. h.jMax]) THEN ERROR;
SELECT h.dimensionality FROM
1 => Change[h, i, delta];
2 => Change2[h, MAX[h.iMin, MIN[h.iMax, i]], MAX[h.jMin, MIN[h.jMax, j]], delta];
ENDCASE => ERROR;
END;
ClearAll: PUBLIC PROC [h: Histogram] = {
FOR i: CARDINAL IN [0 .. h.data.length) DO h.data[i] ← 0 ENDLOOP;
h.maxCount ← 0; h.maxInvalid ← FALSE;
};
WriteTo: PUBLIC PROCEDURE [h: Histogram, to: IO.STREAM] =
BEGIN
d: DataRef ← h.data;
others: BOOLEANFALSE;
IF h.dimensionality # 1 THEN ERROR;
to.PutRope["["];
SELECT h.dimensionality FROM
1 => {
count: CARDINAL ← 0;
FOR i:CARDINAL ← 0, i+1 WHILE i<d.length DO
IF d.counts[i]>0 THEN
BEGIN
IF others THEN to.PutRope[IF (count MOD 5)=0 THEN "\n" ELSE ", "];
IF others THEN to.PutRope[", "] ELSE others ← TRUE;
to.PutF["%g of %g", IO.card[d.counts[i]], IO.real[i*h.iFactor + h.iOffset]];
count ← count + 1;
END;
ENDLOOP;
};
2 => {
index: NAT ← 0;
someI: BOOLFALSE;
FOR i: INT IN [h.iMin .. h.iMax] DO
someJ: BOOLFALSE;
Ensure: PROC = {IF someJ THEN {to.PutRope[", "]; RETURN};
someJ ← TRUE;
IF someI THEN to.PutRope[", "];
someI ← TRUE;
to.PutF["%g: [", IO.real[i*h.iFactor + h.iOffset]]};
FOR j: INT IN [h.jMin .. h.jMax] DO
IF h.data[index] # 0 THEN {Ensure[];
to.PutF["%g: %g", IO.real[j*h.jFactor + h.jOffset], IO.int[h.data[index]]]};
index ← index + 1;
ENDLOOP;
IF someJ THEN to.PutF["]"];
ENDLOOP;
};
ENDCASE => ERROR;
to.PutRope["]"];
END;
Show: PUBLIC PROCEDURE
[
h: Histogram,
viewerInit: ViewerClasses.ViewerRec ← [],
format: ROPENIL, --NIL means "%d"
width: CARDINAL ← 0, --max number of chars produced when formatting
iFreq: CARDINAL ← 0, --label every iFreq'th bin
paint: BOOLTRUE]
RETURNS [v: ViewerClasses.Viewer] =
BEGIN
hv: HistogramView ← NEW [HistogramViewRep ← [
h: h,
iFormat: IF format.Length[] = 0 THEN "%d" ELSE format,
iChars: IF width = 0 THEN 5 ELSE width,
iFreq: IF iFreq = 0 THEN 5 ELSE iFreq,
vFreq: vFreqInitial,
dHeight: dHeightInitial
]];
need that iChars * charwidth < dwidth * iFreq
hv.dWidth ← MAX[5.0, REAL [width * VF.CharWidth['X]] / REAL [hv.iFreq]];
hv.cWidth ← 0.8 * hv.dWidth;
viewerInit.data ← hv;
v ← bsStyle.CreateBiScroller[class: histogramViewer, info: viewerInit, paint: paint].QuaViewer[];
END;
Paint: PROC [self: ViewerClasses.Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOLFALSE] --ViewerClasses.PaintProc-- =
BEGIN
asBS: BiScroller ← BS.QuaBiScroller[self];
hv: HistogramView ← NARROW[asBS.ClientDataOf[]];
SELECT hv.h.dimensionality FROM
1 => Paint1D[self, asBS, hv, context, whatChanged, clear];
2 => Paint2D[self, asBS, hv, context, whatChanged, clear];
ENDCASE => ERROR;
END;
Paint1D: PROC [self: ViewerClasses.Viewer, asBS: BiScroller, hv: HistogramView, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] =
BEGIN
i, iMin, iMax, iMinS, iMaxS, jMin, jMax: INTEGER;
xMin, xMax, yMin, yMax: BS.Vec;
IF hv.h.maxInvalid THEN ComputeMax[hv.h];
context.MaskRectangle[[adx+awx, ady+awy, -awx, hv.dHeight*hv.h.maxCount - (ady+awy)]];
context.MaskRectangle[[adx+awx, ady+awy, hv.dWidth*hv.h.data.length - (adx+awx), -awy]];
[xMin, xMax] ← asBS.ViewportExtrema[[1, 0]];
iMin ← MAX[Real.RoundLI[(xMin.x - hv.cWidth/2)/hv.dWidth]-1, 0];
iMax ← MIN[Real.RoundLI[(xMax.x - hv.cWidth/2)/hv.dWidth]+1, INTEGER[hv.h.data.length]-1];
iMinS ← iMin / hv.iFreq;
iMaxS ← (iMax + hv.iFreq - 1) / hv.iFreq;
iMinS ← iMinS * hv.iFreq;
iMaxS ← iMaxS * hv.iFreq;
Imager.SetFont[context, VF.defaultFont];
FOR i ← iMinS, i+hv.iFreq WHILE i <= iMaxS DO
r: ROPEIO.PutFR[hv.iFormat, IO.real[i*hv.h.iFactor + hv.h.iOffset]];
xmin, xmax, ymin, ymax, cx: REAL;
cx ← hv.dWidth*i + hv.cWidth/2;
context.MaskRectangle[[cx-ts, ady-tl, ts*2, tl]];
[[xmin, ymin, xmax, ymax]] ← ImagerBox.BoxFromExtents[VF.defaultFont.RopeBoundingBox[r]];
context.SetXY[[cx - (xmin+xmax)/2, ady-tl-ls-ymax]];
context.ShowRope[r];
ENDLOOP;
[yMin, yMax] ← asBS.ViewportExtrema[[0, 1]];
jMin ← MAX[Real.RoundLI[yMin.y/(hv.dHeight*hv.vFreq)]-1, 0] * hv.vFreq;
jMax ← MIN[Real.RoundLI[yMax.y/(hv.dHeight*hv.vFreq)]+1,
(hv.h.maxCount + hv.vFreq-1)/hv.vFreq] * hv.vFreq;
FOR i ← jMin, i+hv.vFreq WHILE i <= jMax DO
r: ROPEIO.PutFR["%g", IO.card[i]];
xmin, ymin, xmax, ymax, cy: REAL;
cy ← hv.dHeight*i;
context.MaskRectangle[[adx-tl, cy-ts, tl, ts*2]];
[[xmin, ymin, xmax, ymax]] ← ImagerBox.BoxFromExtents[VF.defaultFont.RopeBoundingBox[r]];
context.SetXY[[adx-tl-ls-xmax, cy - (ymin+ymax)/2]];
context.ShowRope[r];
ENDLOOP;
FOR i ← iMin, i+1 WHILE i <= iMax DO
context.MaskRectangle[[
i*hv.dWidth, 0, hv.cWidth, hv.h.data.counts[i]*hv.dHeight]];
ENDLOOP;
END;
Paint2D: PROC [self: ViewerClasses.Viewer, asBS: BiScroller, hv: HistogramView, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] =
BEGIN
iMin, iMax, jMin, jMax: INTEGER;
xMin, xMax, yMin, yMax: BS.Vec;
maxCount: REAL;
IF hv.h.maxInvalid THEN ComputeMax[hv.h];
maxCount ← hv.h.maxCount;
[xMin, xMax] ← asBS.ViewportExtrema[[1, 0]];
[yMin, yMax] ← asBS.ViewportExtrema[[0, 1]];
iMin ← MAX[Real.RoundLI[(xMin.x - hv.h.iOffset)/hv.h.iFactor]-1, hv.h.iMin];
iMax ← MIN[Real.RoundLI[(xMax.x - hv.h.iOffset)/hv.h.iFactor]+1, hv.h.iMax];
jMin ← MAX[Real.RoundLI[(yMin.y - hv.h.jOffset)/hv.h.jFactor]-1, hv.h.jMin];
jMax ← MIN[Real.RoundLI[(yMax.y - hv.h.jOffset)/hv.h.jFactor]+1, hv.h.jMax];
IF maxCount > 0 THEN FOR i: INT IN [iMin .. iMax] DO
FOR j: INT IN [jMin .. jMax] DO
context.SetColor[ImagerColor.ColorFromGray[MAX[0.0, MIN[1.0,
hv.h.data[(i - hv.h.iMin)*hv.h.nJ + (j - hv.h.jMin)]/maxCount]]]];
context.MaskRectangle[[
x: i+0.05,
y: j+0.05,
w: 0.90,
h: 0.90]];
ENDLOOP;
ENDLOOP;
END;
Extrema: BS.ExtremaProc =
BEGIN
hv: HistogramView ← NARROW[clientData];
nb, ny: INTEGER;
SELECT hv.h.dimensionality FROM
1 => {
IF hv.h.maxInvalid THEN ComputeMax[hv.h];
IF hv.noR THEN ComputeR[hv];
nb ← (hv.h.data.length + hv.iFreq - 2) / hv.iFreq;
ny ← (hv.h.maxCount + hv.vFreq - 1) / hv.vFreq;
[min, max] ← Geom2D.ExtremaOfRect[
ImagerBox.RectFromBox[[
adx - tl - ls - hv.rxmax + hv.rxmin - 5,
-20,
hv.dWidth*(nb+0.5)*hv.iFreq,
hv.dHeight*(ny+0.5)*hv.vFreq]],
direction];
};
2 => [min, max] ← Geom2D.ExtremaOfRect[
[hv.h.iMin, hv.h.jMin, hv.h.iMax - hv.h.iMin, hv.h.jMax - hv.h.jMin],
direction];
ENDCASE => ERROR;
END;
ComputeR: PROC [hv: HistogramView] =
BEGIN
r: ROPEIO.PutFR["%g", IO.int[hv.h.maxCount]];
[[hv.rxmin, , hv.rxmax, ]] ← ImagerBox.BoxFromExtents[VF.defaultFont.RopeBoundingBox[r]];
hv.noR ← FALSE;
END;
Find1D: PROC [hv: HistogramView, wo: BS.ClientCoords] RETURNS [index: CARDINAL, valid: BOOLEAN] =
BEGIN
i: INT;
IF wo.x < -hv.dWidth OR wo.x > hv.dWidth * (hv.h.data.length + 1) THEN
RETURN [0, FALSE];
i ← Real.RoundLI[(wo.x - hv.cWidth/2.0) / hv.dWidth];
IF i < 0 OR i >= INT[hv.h.data.length] THEN RETURN [0, FALSE];
RETURN [i, TRUE];
END;
Find2D: PROC [hv: HistogramView, wo: BS.ClientCoords] RETURNS [i, j: INT, valid: BOOLEAN] =
BEGIN
i ← Real.RoundLI[wo.x];
j ← Real.RoundLI[wo.y];
valid ← i IN [hv.h.iMin .. hv.h.iMax] AND j IN [hv.h.jMin .. hv.h.jMax];
END;
Notify: PROC [self: ViewerClasses.Viewer, input: LIST OF REF ANY] --ViewerClasses.NotifyProc-- =
BEGIN
asBS: BiScroller ← BS.QuaBiScroller[self];
hv: HistogramView ← NARROW[asBS.ClientDataOf[]];
SELECT hv.h.dimensionality FROM
1 => Notify1D[self, asBS, hv, input];
2 => Notify2D[self, asBS, hv, input];
ENDCASE => ERROR;
END;
Notify1D: PROC [self: ViewerClasses.Viewer, asBS: BiScroller, hv: HistogramView, input: LIST OF REF ANY] =
BEGIN
WHILE input # NIL DO
SELECT input.first FROM
$Tally => BEGIN
sum1, sum2: REAL;
count: INT;
[count, sum1, sum2] ← Sums1D[hv.h];
MessageWindow.Append[
message: IO.PutFR["count = %g; sum = %g; sum of squares = %g", IO.int[count], IO.real[sum1], IO.real[sum2]],
clearFirst: TRUE];
input ← input.rest;
END;
$Average => BEGIN
sum0, sum1, sum2: REAL;
[sum0, sum1, sum2] ← Sums1D[hv.h];
MessageWindow.Append[
message: IF sum0 < 1 THEN "empty histogram!" ELSE IO.PutFR[
"average = %g; average square = %g",
IO.real[sum1/sum0], IO.real[sum2/sum0]],
clearFirst: TRUE];
input ← input.rest;
END;
$StartFind => BEGIN
index: CARDINAL;
valid: BOOLEAN;
coords: BS.ClientCoords ← NARROW[input.rest.first];
[index, valid] ← Find1D[hv, coords];
IF hv.finding ← valid THEN {hv.findFirstI ← index; FollowFind1D[hv, index]};
input ← input.rest.rest;
END;
$FollowFind => BEGIN
index: CARDINAL;
valid: BOOLEAN;
coords: BS.ClientCoords ← NARROW[input.rest.first];
[index, valid] ← Find1D[hv, coords];
IF valid THEN FollowFind1D[hv, index];
input ← input.rest.rest;
END;
$FinishFind => BEGIN
index: CARDINAL;
valid: BOOLEAN;
coords: BS.ClientCoords ← NARROW[input.rest.first];
[index, valid] ← Find1D[hv, coords];
IF valid THEN FollowFind1D[hv, index];
hv.finding ← FALSE;
input ← input.rest.rest;
END;
$DoubleResolution => BEGIN
IF hv.vFreq > 1 THEN
BEGIN
hv.dHeight ← hv.dHeight * 2;
hv.vFreq ← (hv.vFreq + 1) / 2;
VO.PaintViewer[viewer: self, hint: client, clearClient: TRUE];
END;
input ← input.rest;
END;
$HalveResolution => BEGIN
IF hv.vFreq < 16384 THEN
BEGIN
hv.dHeight ← hv.dHeight / 2;
hv.vFreq ← hv.vFreq * 2;
VO.PaintViewer[viewer: self, hint: client, clearClient: TRUE];
END;
input ← input.rest;
END;
$FitVertically => BEGIN
vMin, vMax: BS.Vec;
IF hv.h.maxCount >= 1 THEN
BEGIN
dHeightNew, yf: REAL;
[vMin, vMax] ← asBS.ViewportExtrema[[0, 1]];
dHeightNew ← 0.95 * (vMax.y - (vMin.y + ySpacing * 2)) / hv.h.maxCount;
yf ← ySpacing / dHeightNew;
IF dHeightNew > 0 AND yf < 32000 THEN
BEGIN
hv.dHeight ← dHeightNew;
hv.vFreq ← MAX[Real.RoundLI[yf + 0.5], 1];
VO.PaintViewer[viewer: self, hint: client, clearClient: TRUE];
END;
END;
input ← input.rest;
END;
$ResetVertically => BEGIN
hv.dHeight ← dHeightInitial;
hv.vFreq ← vFreqInitial;
VO.PaintViewer[viewer: self, hint: client, clearClient: TRUE];
input ← input.rest;
END;
ENDCASE => ERROR;
ENDLOOP;
END;
Notify2D: PROC [self: ViewerClasses.Viewer, asBS: BiScroller, hv: HistogramView, input: LIST OF REF ANY] =
BEGIN
WHILE input # NIL DO
SELECT input.first FROM
$Tally => BEGIN
count: INT;
sumx, sumy: REAL;
[count, sumx, sumy] ← Sums2D[hv.h];
MessageWindow.Append[
message: IO.PutFR["count = %g; sumX = %g; sumY = %g", IO.int[count], IO.real[sumx], IO.real[sumy]],
clearFirst: TRUE];
input ← input.rest;
END;
$Average => BEGIN
sum0, sumX, sumY: REAL;
[sum0, sumX, sumY] ← Sums2D[hv.h];
MessageWindow.Append[
message: IF sum0 = 0 THEN "empty histogram!" ELSE IO.PutFR[
"average X = %g; average Y = %g",
IO.real[sumX/sum0], IO.real[sumY/sum0]],
clearFirst: TRUE];
input ← input.rest;
END;
$StartFind => BEGIN
i, j: INT;
valid: BOOLEAN;
coords: BS.ClientCoords ← NARROW[input.rest.first];
[i, j, valid] ← Find2D[hv, coords];
IF hv.finding ← valid THEN {hv.findFirstI ← i; hv.findFirstJ ← j; FollowFind2D[hv, i, j]};
input ← input.rest.rest;
END;
$FollowFind => BEGIN
i, j: INT;
valid: BOOLEAN;
coords: BS.ClientCoords ← NARROW[input.rest.first];
[i, j, valid] ← Find2D[hv, coords];
IF valid THEN FollowFind2D[hv, i, j];
input ← input.rest.rest;
END;
$FinishFind => BEGIN
i, j: INT;
valid: BOOLEAN;
coords: BS.ClientCoords ← NARROW[input.rest.first];
[i, j, valid] ← Find2D[hv, coords];
IF valid THEN FollowFind2D[hv, i, j];
hv.finding ← FALSE;
input ← input.rest.rest;
END;
ENDCASE => ERROR;
ENDLOOP;
END;
FollowFind1D: PROC [hv: HistogramView, current: CARDINAL] =
BEGIN
lowest, highest: CARDINAL;
count: INT ← 0;
IF NOT hv.finding THEN {hv.finding ← TRUE; hv.findFirstI ← current};
lowest ← MIN[hv.findFirstI, current];
highest ← MAX[hv.findFirstI, current];
FOR i: CARDINAL IN [lowest .. highest] DO
count ← count + hv.h.data.counts[i];
ENDLOOP;
MessageWindow.Append[
message: IO.PutFR[
"%g in %g thru %g",
IO.int[count],
IO.real[hv.findFirstI * hv.h.iFactor + hv.h.iOffset],
IO.real[current * hv.h.iFactor + hv.h.iOffset]],
clearFirst: TRUE];
END;
FollowFind2D: PROC [hv: HistogramView, i, j: INT] =
BEGIN
iMin, iMax, jMin, jMax: INT;
count: INT ← 0;
IF NOT hv.finding THEN {hv.finding ← TRUE; hv.findFirstI ← i; hv.findFirstJ ← j};
iMin ← MIN[hv.findFirstI, i];
iMax ← MAX[hv.findFirstI, i];
jMin ← MIN[hv.findFirstJ, j];
jMax ← MAX[hv.findFirstJ, j];
FOR i: INT IN [iMin .. iMax] DO
FOR j: INT IN [jMin .. jMax] DO
index: NAT ← (i - hv.h.iMin) * hv.h.nJ + (j - hv.h.jMin);
count ← count + hv.h.data.counts[index];
ENDLOOP;
ENDLOOP;
MessageWindow.Append[
message: IO.PutFR[
"%g in <%g, %g> thru <%g, %g>",
IO.int[count],
IO.real[hv.findFirstI * hv.h.iFactor + hv.h.iOffset],
IO.real[hv.findFirstJ * hv.h.jFactor + hv.h.jOffset],
IO.real[i * hv.h.iFactor + hv.h.iOffset],
IO.real[j * hv.h.jFactor + hv.h.jOffset]
],
clearFirst: TRUE];
END;
Sums1D: PROC [h: Histogram] RETURNS [sum0: INT, sum1, sum2: REAL] =
BEGIN
x: REAL ← h.iOffset;
sum0 ← 0; sum1 ← sum2 ← 0;
FOR i: CARDINAL IN [0..h.data.length) DO
sum0 ← sum0 + h.data.counts[i];
sum1 ← sum1 + x * h.data.counts[i];
sum2 ← sum2 + x * x * h.data.counts[i];
x ← x + h.iFactor;
ENDLOOP;
END;
Sums2D: PROC [h: Histogram] RETURNS [sum0: INT, sumx, sumy: REAL] =
BEGIN
sum0 ← 0; sumx ← sumy ← 0;
FOR i: INT IN [h.iMin .. h.iMax] DO
x: REAL ← i*h.iFactor + h.iOffset;
FOR j: INT IN [h.jMin .. h.jMax] DO
y: REAL ← j*h.jFactor + h.jOffset;
index: NAT ← (i - h.iMin) * h.nJ + (j - h.jMin);
sum0 ← sum0 + h.data.counts[index];
sumx ← sumx + x * h.data.counts[index];
sumy ← sumy + y * h.data.counts[index];
ENDLOOP;
ENDLOOP;
END;
ComputeMax: PROC [h: Histogram] =
BEGIN
h.maxCount ← 0;
FOR i: CARDINAL ← 0, i+1 WHILE i < h.data.length DO
h.maxCount ← MAX[h.maxCount, h.data.counts[i]];
ENDLOOP;
h.maxInvalid ← FALSE;
END;
Setup: PROC =
BEGIN
ySpacing ← VF.FontHeight[] + 1;
histogramViewer ← bsStyle.NewBiScrollerClass[[flavor: $Histogram,
extrema: Extrema,
notify: Notify,
paint: Paint,
icon: Icons.NewIconFromFile["Histograms.icons", 0],
menu: BS.bsMenu,
tipTable: TIPUser.InstantiateNewTIPTable["Histograms.TIP"],
mayStretch: FALSE
]];
END;
Setup[];
END.