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; 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 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 => { FOR i:CARDINAL _ 0, i+1 WHILE i0 THEN BEGIN 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]]; 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 ]]; 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. implements Historgrams Last Edited by: Spreitzer, November 1, 1985 4:47:33 pm PST make sure that n can be accessed count: CARDINAL _ 0; IF others THEN to.PutRope[IF (count MOD 5)=0 THEN "\n" ELSE ", "]; count _ count + 1; need that iChars * charwidth < dwidth * iFreq Κ ˜Jšœ™Jšœ:™:Icode˜šΟk œœ•˜§K˜—šΠbxœœ˜Kš œœ.œœœ/˜“Kšœ ˜K˜—Kš˜K˜Kšœœœ˜Kšœ œœ ˜!K˜Kšœ œœ˜Kš œœœ œœœœ˜CK˜Kšœ œœ˜#šœœœœ˜$K˜Kšœ˜Kšœ œ˜(Kšœ$œ˜)Kšœ œœ˜2K˜—K˜Kšœœœ˜+šœœœ˜!Kšœ ˜ Kšœ œœ˜'Kšœœ œ˜Kšœœ˜Kšœœœ˜Kšœœ˜Kšœ œœ˜Kšœœ˜Kšœ˜—K˜Kšœ œœ ˜,Kšœœ˜$Kšœœ˜ Kšœœ˜ Kšœœ˜ Kšœœ˜Kšœ œ˜Kšœœ˜Kšœœ˜K˜K˜šΟn œœ˜šœ˜KšœœΟc˜)Kšœœ˜—Kšœ˜Kš˜Kšœ œ ˜šœœ˜K˜Kšœ˜Kšœ8˜8Kšœ˜—Kšœ˜K˜—šŸœœ˜šœ ˜Kšœœ˜Kšœœ ˜5Kšœœ ˜5—Kšœ˜Kš˜Kšœ œ œœ˜)šœœ˜K˜Kšœœ˜ K˜/Kšœ)˜)K˜FK˜—Kšœ œ˜!Kš œœœœœ˜AKšœ˜—K˜Jšœ ™ šŸœ œœ˜/Kš˜K˜Kšœœœ˜#šœ˜Kš˜Kšœœ˜ Kšœœ ˜šœ œ ˜"K˜Kšœ˜—šœœ˜+K˜Kšœ˜—K˜ Kšœ˜—Kšœ˜K˜—š Ÿœœ œœ œ˜JKš˜K˜K˜2š œœœœ ˜,Kšœœ˜Kšœœ!˜9Kšœœ˜—Kšœ˜—K˜šŸœœœ œ˜9Kš˜Kšœœ$˜.Kšœ4˜4š œœœœ˜*Kšœœ˜Kšœœ#˜;Kšœœ˜—Kšœ˜—K˜šŸœœœœ˜GKš˜Kšœœœœ#˜KK˜Kšœ˜—K˜š Ÿœœœœœ œ˜YKš˜Kšœœ˜ Kšœ,˜,Kšœ,˜,Kšœœœœœœœ˜Hšœ˜K˜Kš œœ œœ œ˜QKšœœ˜—Kšœ˜—K˜šŸœœœ˜(Kš œœœœœ˜AKšœœ˜%K˜—K˜š Ÿœœ œœœ˜9Kš˜K˜Kšœœœ˜Kšœœœ˜#K˜šœ˜˜Kšœœ™šœœ œ ˜+šœ˜Kš˜Kš œœ œœœœ™BKšœœœ œ˜3Kšœœœ ˜LKšœ™Kšœ˜—Kšœ˜—K˜—˜Kšœœ˜Kšœœœ˜šœœœ˜#Kšœœœ˜š Ÿœœœœœ˜9Kšœœ˜ Kšœœ˜Kšœœ˜ Kšœœ!˜4—šœœœ˜#šœœ ˜$Kšœœ œ˜L—K˜Kšœ˜—Kšœœ˜Kšœ˜—K˜—Kšœœ˜—K˜Kšœ˜K˜—šŸœœ ˜šœ˜Kšœ ˜ Kšœ)˜)Kšœœœ ˜$Kšœœ .˜CKšœœ ˜/Kšœœœ˜—Kšœ˜#Kš˜šœœ˜-K˜Kšœ œœœ˜6Kšœœ œœ˜'Kšœœ œœ˜&Kšœ˜Kšœ˜K˜—Jšœ-™-Kš œ œœ œœ ˜HK˜K˜Kšœa˜aKšœ˜K˜—šŸœœDœœ œœœœ œ˜ŸKš˜Kšœœ˜*Kšœœ˜0šœ˜K˜:K˜:Kšœœ˜—Kšœ˜—K˜š Ÿœœiœœ œ˜Kš˜Kšœ)œ˜1Kšœœ˜Kšœœ˜)K˜VK˜XKšœ,˜,Kšœœ6˜@Kšœœ3œ˜ZK˜K˜)K˜K˜Kšœœ˜(šœœ ˜-Kšœœœœ&˜GKšœœ˜!K˜K˜1Kšœ6œ!˜YK˜4Kšœ˜Kšœ˜—Kšœ,˜,Kšœœ=˜Gšœœ.˜8K˜2—šœœ ˜+Kšœœœ œ ˜%Kšœœ˜!K˜K˜1Kšœ6œ!˜YK˜4Kšœ˜Kšœ˜—šœœ ˜$˜K˜<—Kšœ˜—Kšœ˜—K˜š Ÿœœiœœ œ˜Kš˜Kšœœ˜ Kšœœ˜Kšœ œ˜Kšœœ˜)Kšœ˜Kšœ,˜,Kšœ,˜,KšœœB˜LKšœœB˜LKšœœB˜LKšœœB˜Lš œœœœœ˜4šœœœ˜šœ+œœ˜Kšœœ˜Kšœ˜—K˜š Ÿœœœœœ œ˜[Kš˜K˜K˜Kšœ œœœ˜HKšœ˜—K˜šŸœœ%œœœœ œ˜`Kš˜Kšœœ˜*Kšœœ˜0šœ˜K˜%K˜%Kšœœ˜—Kšœ˜—K˜š ŸœœJœœœœ˜jKš˜šœ œ˜šœ ˜šœ ˜Kšœ œ˜Kšœœ˜ K˜#˜Kš œ œ4œ œ œ ˜lKšœ œ˜—K˜Kšœ˜—šœ ˜Kšœœ˜K˜"˜š œ œ œœœ˜;K˜$Kšœœ˜(—Kšœ œ˜—K˜Kšœ˜—šœ˜Kšœœ˜Kšœœ˜Kšœœœ˜3K˜$Kšœœ2˜LK˜Kšœ˜—šœ˜Kšœœ˜Kšœœ˜Kšœœœ˜3K˜$Kšœœ˜&K˜Kšœ˜—šœ˜Kšœœ˜Kšœœ˜Kšœœœ˜3K˜$Kšœœ˜&Kšœ œ˜K˜Kšœ˜—šœ˜šœ˜Kš˜K˜K˜Kšœ6œ˜>Kšœ˜—K˜Kšœ˜—šœ˜šœ˜Kš˜K˜K˜Kšœ6œ˜>Kšœ˜—K˜Kšœ˜—šœ˜Kšœ œ˜šœ˜Kš˜Kšœœ˜Kšœ,˜,K˜GK˜šœœ ˜%Kš˜K˜Kšœ œ˜*Kšœ6œ˜>Kšœ˜—Kšœ˜—K˜Kšœ˜—šœ˜K˜K˜Kšœ6œ˜>K˜Kšœ˜—Kšœœ˜—Kšœ˜—Kšœ˜—K˜š ŸœœJœœœœ˜jKš˜šœ œ˜šœ ˜šœ ˜Kšœœ˜ Kšœ œ˜K˜#˜Kš œ œ+œ œ œ ˜cKšœ œ˜—K˜Kšœ˜—šœ ˜Kšœœ˜K˜"˜š œ œ œœœ˜;K˜!Kšœœ˜(—Kšœ œ˜—K˜Kšœ˜—šœ˜Kšœœ˜ Kšœœ˜Kšœœœ˜3Kšœ#˜#Kšœœ@˜ZK˜Kšœ˜—šœ˜Kšœœ˜ Kšœœ˜Kšœœœ˜3Kšœ#˜#Kšœœ˜%K˜Kšœ˜—šœ˜Kšœœ˜ Kšœœ˜Kšœœœ˜3Kšœ#˜#Kšœœ˜%Kšœ œ˜K˜Kšœ˜—Kšœœ˜—Kšœ˜—Kšœ˜—K˜šŸ œœœ˜;Kš˜Kšœœ˜Kšœœ˜Kšœœ œœ˜DKšœ œ˜%Kšœ œ˜&šœœœ˜)K˜$Kšœ˜—˜šœ œ˜K˜Kšœ ˜Kšœ3˜5Kšœ.˜0—Kšœ œ˜—Kšœ˜—K˜šŸ œœœ˜3Kš˜Kšœœ˜Kšœœ˜Kšœœ œœ(˜QKšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜šœœœ˜šœœœ˜Kšœœ/˜9K˜(Kšœ˜—Kšœ˜—˜šœ œ˜K˜Kšœ ˜Kšœ3˜5Kšœ3˜5Kšœ'˜)Kšœ&˜(Kšœ˜—Kšœ œ˜—Kšœ˜—K˜š Ÿœœœœœ˜CKš˜Kšœœ ˜Kšœ˜šœœœ˜(Kšœ˜K˜#K˜'Kšœ˜Kšœ˜—Kšœ˜—K˜š Ÿœœœœœ˜CKš˜Kšœ˜šœœœ˜#Kšœœ˜"šœœœ˜#Kšœœ˜"Kšœœ&˜0Kšœ#˜#Kšœ'˜'Kšœ'˜'Kšœ˜—Kšœ˜—Kšœ˜—K˜šŸ œœ˜!Kš˜K˜šœœ œ˜3Kšœ œ˜/Kšœ˜—Kšœœ˜Kšœ˜K˜—šŸœœ˜ Kš˜Kšœ œ˜šœA˜AK˜K˜K˜ Kšœ3˜3Kšœœ˜K˜;Kšœ ˜K˜—Kšœ˜K˜—K˜K˜Kšœ˜K˜—…—C['