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: BOOLEAN ← FALSE
];
HistogramView: TYPE = REF HistogramViewRep;
HistogramViewRep:
TYPE =
RECORD [
h: Histogram,
iFormat: ROPE, iChars, iFreq: CARDINAL,
vFreq: INTEGER, dHeight: REAL,
dWidth, cWidth: REAL ← 0,
noR: BOOLEAN ← TRUE,
rxmin, rxmax: REAL ← 0,
finding: BOOLEAN ← FALSE,
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: BOOLEAN ← FALSE;
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: BOOL ← FALSE;
FOR i:
INT
IN [h.iMin .. h.iMax]
DO
someJ: BOOL ← FALSE;
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: ROPE ← NIL, --NIL means "%d"
width: CARDINAL ← 0, --max number of chars produced when formatting
iFreq: CARDINAL ← 0, --label every iFreq'th bin
paint: BOOL ← TRUE]
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:
BOOL ←
FALSE]
--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: ROPE ← IO.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: ROPE ← IO.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: ROPE ← IO.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.