unit Graphics; {Graphics routines used by NIH Image} interface uses Types, Memory, QuickDraw, QuickDrawText, Packages, Menus, Events, Fonts, Controls, Scrap, ToolUtils, Dialogs, TextUtils, Windows, MixedMode, Palettes, Printing, TextEdit, globals, Utilities; procedure ShowLineWidth; function GetInterpolatedPixel (x, y: extended): extended; procedure GetObliqueLine (xstart, ystart, start, angle: extended; count: integer; var line: rLineType); procedure GetLengthOrPerimeter (var ulength, clength: extended); procedure PlotLineProfile; procedure PlotArbitraryLine; procedure DrawPlot; procedure UpdatePlotWindow; procedure ShowInfo; procedure ComputePlotMinAndMax; procedure SetupPlot (start: point; VerticalPlot: boolean); procedure MakePlotWindow (PlotLeft, PlotTop, PlotWidth, PlotHeight: integer); procedure DrawObject (obj: ObjectType; p1, p2: point); procedure DrawTools; function InvertingCalibrationFunction: boolean; procedure DrawHistogram; procedure DrawLabels (xL, yL, zL: str255); procedure ShowNextImage; procedure CascadeImages; procedure TileImages; function Duplicate (name: str255; SavingBlankField: boolean): boolean; procedure InvertPic; procedure ShowMessage (str: str255); procedure ShowTime (StartTicks: LongInt; r: rect; str: str255); procedure ShowFrameRate (str1: str255; StartTicks, nFrames: LongInt); procedure ConvertHistoToText; procedure ConvertPlotToText; procedure ConvertCalibrationCurveToText; procedure SetupUndoInfoRec; procedure ActivateWindow; procedure UpdateResultsWindow; procedure ScrollResultsText; procedure UpdateResultsScrollBars; procedure InitResultsTextEdit (font, size: integer); procedure DoMouseDownInResults (loc: point); procedure AppendResults; procedure DeleteLines (first, last: integer); procedure UpdateList; procedure ShowMeter; procedure UpdateMeter (percentdone: integer; str: str255); function RgnNotTooBig (Rgn1, Rgn2: RgnHandle): boolean; procedure MakeCoordinatesRelative; procedure MakeOutline (RoiKind: RoiTypeType); procedure ConvertCoordinates; function CoordinatesAvailable: boolean; function CoordinatesAvailableMsg: boolean; procedure DrawDropBox (r: rect); function PopUpMenu (theMenu: MenuHandle; left, top, PopUpItem: integer): integer; procedure GetDItemRect (d: DialogPtr; item: integer; var r: rect); procedure DrawPopUpText (str: str255; r: rect); procedure SetUProc (d: DialogPtr; item: integer; pptr: handle); procedure RemoveDensityCalibration; function isInvertingFunction:boolean; function CheckCalibration: boolean; procedure PlotTooLongMsg; implementation {$PUSH} {$D-} procedure DrawJustifiedReal (x, y: integer; r: extended); {Draws a right justified real number.} var str: str255; digits: integer; begin if abs(r) >= 1000.0 then digits := 0 else digits := 2; RealToString(r, 1, digits, str); MoveTo(x - StringWidth(str), y); DrawString(str); end; procedure DrawVerticalString (x, y: integer; str: str255); var i: integer; begin MoveTo(x, y); for i := 1 to length(str) do begin MoveTo(x, y); DrawChar(str[i]); y := y + 9; end; end; procedure LabelProfilePlot; var str: str255; min, max: extended; x, y: integer; begin min := PlotMin; max := PlotMax; DrawJustifiedReal(PlotLeftMargin - 2, PlotHeight - PlotBottomMargin, min); DrawJustifiedReal(PlotLeftMargin - 2, PlotTopMargin + 8, max); y := PlotTopMargin + (PlotHeight - (PlotTopMargin + PlotBottomMargin)) div 2 - length(PlotYUnits) * 9 div 2 + 6; DrawVerticalString(PlotLeftMargin - 15, y, PlotYUnits); MoveTo(PlotLeftMargin, PlotHeight - PlotBottomMargin + 11); DrawLong(0); if PlotScale <> 0.0 then RealToString((PlotCount - 1) * PlotScale, 1, Precision, str) else NumToString(PlotCount - 1, str); MoveTo(PlotWidth - PlotRightMargin - StringWidth(str) + 4, PlotHeight - PlotBottomMargin + 11); DrawString(str); x := PlotRightMargin + (PlotWidth - (PlotRightMargin + PlotLeftMargin)) div 2 - StringWidth(str) div 2; MoveTo(x, PlotHeight - PlotBottomMargin + 13); DrawString(PlotXUnits); end; procedure LabelCalibrationPlot; var pbottom, hloc, vloc, i: integer; letter: packed array[1..6] of char; c:char; begin pbottom := PlotHeight - PLotBottomMargin; DrawJReal(PlotLeftMargin, PlotTopMargin + 4, maxCValue, 2); DrawJReal(PlotLeftMargin, pbottom, minCValue, 2); MoveTo(PlotLeftMargin - 3, pbottom + 10); DrawString('0'); MoveTo(PlotWidth - PlotRightMargin - 14, pbottom + 10); DrawString('255'); MoveTo(PlotLeftMargin + 15, PlotTopMargin + 15); TextSize(12); case info^.fit of StraightLine: DrawString('y=a+bx'); Poly2: DrawString('y=a+bx+cx^2'); Poly3: DrawString('y=a+bx+cx^2+dx^3'); Poly4: DrawString('y=a+bx+cx^2+dx^3+ex^4'); Poly5: DrawString('y=a+bx+cx^2+dx^3+ex^4+fx^5'); ExpoFit: DrawString('y=aexp(bx)'); PowerFit: DrawString('y=ax^b'); LogFit: DrawString('y=aln(bx)'); RodbardFit: DrawString('y=c*((a-x)/(x-d))^(1/b)'); UncalibratedOD: DrawString('y=log10(255/(255-x))'); otherwise end; hloc := PlotWidth - PlotRightMargin + 5; vloc := PlotTopMargin + 25; letter := 'abcdef'; MoveTo(hloc, vloc); with info^ do for i := 1 to nCoefficients do begin MoveTo(hloc, vloc); TextSize(12); c:=letter[i]; DrawString(c); DrawString('='); TextSize(9); DrawReal(Coefficient[i], 1, 8); vloc := vloc + 15; end; if info^.fit <> UncalibratedOD then begin vloc := vloc + 25; MoveTo(hloc, vloc); DrawString('S.D.='); DrawReal(FitSD, 1, 4); vloc := vloc + 15; MoveTo(hloc, vloc); DrawString('R^2='); DrawReal(FitGoodness, 1, 4); end; end; procedure DrawPlot; var fRect: rect; begin SetRect(fRect, PlotLeftMargin, PlotTopMargin, PlotWidth - PlotRightMargin, PlotHeight - PlotBottomMargin); PenNormal; FrameRect(fRect); DrawPicture(PlotPICT, fRect); TextFont(Geneva); TextSize(9); if WindowPeek(PlotWindow)^.WindowKind = ProfilePlotKind then begin if DrawPlotLabels then LabelProfilePlot end else LabelCalibrationPlot; end; procedure UpdatePlotWindow; begin SetPort(PlotWindow); EraseRect(PlotWindow^.portRect); DrawPlot; DrawMyGrowIcon(PlotWindow); end; procedure MakePlotWindow; {(PlotLeft, PlotTop, PlotWidth, PlotHeight: integer)} var PLotRect, pwrect, dwrect, srect: rect; overlapping: boolean; begin if PlotWindow = nil then begin SetRect(PlotRect, PlotLeft, PlotTop, PlotLeft + PlotWidth, PlotTop + PlotHeight); PlotWindow := NewWindow(nil, PlotRect, 'Plot', true, DocumentProc, nil, true, 0); end else begin GetWindowRect(PlotWindow, pwrect); GetWindowRect(info^.wptr, dwrect); overlapping := SectRect(pwrect, dwrect, srect); if overlapping then MoveWindow(PlotWindow, PlotLeft, PlotTop, false); SizeWindow(PlotWindow, PlotWidth, PlotHeight, false); end; end; procedure GetDiagLine (start, finish: Point; var count: integer; var data: LineType; OptionKey: boolean); var sum: LongInt; p: ptr; deltax, deltay, xinc, yinc, accumulator, i: LongInt; xloc, yloc, j: LongInt; average: boolean; buf, fline: LineType; begin average := LineWidth > 1; if OptionKey and average then for i := 0 to MaxLine do fline[i] := ForegroundIndex; count := 0; xloc := start.h; yloc := start.v; deltax := finish.h - xloc; deltay := finish.v - yloc; if (deltax = 0) and (deltay = 0) then begin data[count] := MyGetPixel(xloc, yloc); if OptionKey then PutPixel(xloc, yloc, ForegroundIndex); count := 1; exit(GetDiagLine); end; if deltax < 0 then begin xinc := -1; deltax := -deltax end else xinc := 1; if deltay < 0 then begin yinc := -1; deltay := -deltay end else yinc := 1; if DeltaX > DeltaY then begin {More horizontal} if average and (CurrentTool <> LineTool) then deltax := deltax + LineWidth; accumulator := deltax div 2; i := deltax; repeat if count < MaxLine then count := count + 1; accumulator := accumulator + deltay; if accumulator >= deltax then begin accumulator := accumulator - deltax; yloc := yloc + yinc end; xloc := xloc + xinc; if average then begin GetColumn(xloc, yloc, LineWidth, buf); if OptionKey then PutColumn(xloc, yloc, LineWidth, fline); sum := 0; for j := 0 to LineWidth - 1 do sum := sum + buf[j]; data[count - 1] := round(sum / LineWidth); end else begin data[count - 1] := MyGetPixel(xloc, yloc); if OptionKey then PutPixel(xloc, yloc, ForegroundIndex); end; i := i - 1; until i = 0 end else begin {More vertical} if average and (CurrentTool <> LineTool) then deltay := deltay + LineWidth; accumulator := deltay div 2; i := deltay; repeat if count < MaxLine then count := count + 1; accumulator := accumulator + deltax; if accumulator >= deltay then begin accumulator := accumulator - deltay; xloc := xloc + xinc end; yloc := yloc + yinc; if average then begin GetLine(xloc, yloc, LineWidth, buf); if OptionKey then PutLine(xloc, yloc, LineWidth, fline); sum := 0; for j := 0 to LineWidth - 1 do sum := sum + buf[j]; data[count - 1] := round(sum / LineWidth); end else begin data[count - 1] := MyGetPixel(xloc, yloc); if OptionKey then PutPixel(xloc, yloc, ForegroundIndex); end; i := i - 1; until i = 0 end; end; function GetInterpolatedPixel (x, y: extended): extended; {Uses bilinear interpolation to computes the raw pixel value at real coordinates (x,y).} var i: integer; xbase, ybase, offset: LongInt; LowerLeft, LowerRight, UpperLeft, UpperRight: integer; xfraction, yfraction, UpperAverage, LowerAverage: extended; begin xbase := trunc(x); ybase := trunc(y); xFraction := x - xbase; yFraction := y - ybase; with info^ do if (xbase < 0) or (ybase < 0) or (xbase >= (PixelsPerLine - 1)) or (ybase >= (nlines - 1)) then begin LowerLeft := 0; LowerRight := 0; UpperLeft := 0; UpperRight := 0; end else begin offset := ybase * BytesPerRow + xbase; LowerLeft := ImageP(PicBaseAddr)^[offset]; LowerRight := ImageP(PicBaseAddr)^[offset + 1]; UpperLeft := ImageP(PicBaseAddr)^[offset + BytesPerRow]; UpperRight := ImageP(PicBaseAddr)^[offset + BytesPerRow + 1]; end; UpperAverage := UpperLeft + xfraction * (UpperRight - UpperLeft); LowerAverage := LowerLeft + xfraction * (LowerRight - LowerLeft); GetInterpolatedPixel := LowerAverage + yfraction * (UpperAverage - LowerAverage); end; function GetCInterpolatedPixel (x, y: extended): extended; {Uses bilinear interpolation to computes the calibrated pixel value at real coordinates (x,y).} var i, xbase, ybase: LongInt; LowerLeft, LowerRight, UpperLeft, UpperRight: extended; xfraction, yfraction, UpperAverage, LowerAverage: extended; begin xbase := trunc(x); ybase := trunc(y); xFraction := x - xbase; yFraction := y - ybase; LowerLeft := cvalue[MyGetPixel(xbase, ybase)]; LowerRight := cvalue[MyGetPixel(xbase + 1, ybase)]; UpperRight := cvalue[MyGetPixel(xbase + 1, ybase + 1)]; UpperLeft := cvalue[MyGetPixel(xbase, ybase + 1)]; UpperAverage := UpperLeft + xfraction * (UpperRight - UpperLeft); LowerAverage := LowerLeft + xfraction * (LowerRight - LowerLeft); GetCInterpolatedPixel := LowerAverage + yfraction * (UpperAverage - LowerAverage); end; procedure GetObliqueLine (xstart, ystart, start, angle: extended; count: integer; var line: rLineType); var i: integer; x, y, xinc, yinc: extended; IntegerStart: boolean; tLine:LineType; begin IntegerStart := (xstart = trunc(xstart)) and (ystart = trunc(ystart)); if IntegerStart and (angle = 0.0) then begin GetLine(trunc(xstart), trunc(ystart), count, tLine); for i := 0 to count - 1 do line[i] := cvalue[tLine[i]]; exit(GetObliqueLine); end; if IntegerStart and (angle = 270.0) then begin GetColumn(trunc(xstart), trunc(ystart), count, tLine); for i := 0 to count - 1 do line[i] := cvalue[tLine[i]]; exit(GetObliqueLine); end; angle := (angle / 180.0) * pi; xinc := cos(angle); yinc := -sin(angle); x := xstart + start * xinc; y := ystart + start * yinc; if info^.fit <> uncalibrated then for i := 0 to count - 1 do begin line[i] := GetCInterpolatedPixel(x, y); x := x + xinc; y := y + yinc; end else for i := 0 to count - 1 do begin line[i] := GetInterpolatedPixel(x, y); x := x + xinc; y := y + yinc; end; end; procedure DrawTools; var tPort: GrafPtr; tool: ToolType; tpRect, sRect, dRect: rect; hloc, vloc: integer; procedure CopyToolBits (src, dst: rect; CopyMode: integer); begin CopyBits(toolBits, BitMapHandle(CGrafPtr(ToolWindow)^.PortPixMap)^^, src, dst, CopyMode, nil); end; begin GetPort(tPort); SetPort(ToolWindow); tpRect := CGrafPtr(ToolWindow)^.portRect; SetFColor(BlackIndex); SetBColor(WhiteIndex); CopyToolBits(tpRect, tpRect, srcCopy); case LOIType of Straight: ; Freehand: begin SetRect(sRect, 46, 92, 62, 106); hloc := 27; vloc := 92; SetRect(dRect, hloc, vloc, hloc + 16, vloc + 14); CopyToolBits(sRect, dRect, SrcCopy); end; Segmented: begin SetRect(sRect, 46, 108, 62, 122); hloc := 27; vloc := 92; SetRect(dRect, hloc, vloc, hloc + 16, vloc + 14); CopyToolBits(sRect, dRect, SrcCopy); end; end; InvertRect(ToolRect[CurrentTool]); SetRect(sRect, 46, 226, 55, 233); hloc := 2; vloc := Lines[LineIndex].top - 4; SetRect(dRect, hloc, vloc, hloc + 9, vloc + 7); CopyToolBits(sRect, dRect, SrcCopy); {Check mark} SetFColor(ForegroundIndex); SetRect(sRect, 46, 81, 57, 87); hloc := 4; vloc := 101; SetRect(dRect, hloc, vloc, hloc + 11, vloc + 6); CopyToolBits(sRect, dRect, SrcOr); {Brush color} SetFColor(BackgroundIndex); SetRect(sRect, 46, 65, 61, 76); hloc := 3; vloc := 73; SetRect(dRect, hloc, vloc, hloc + 15, vloc + 11); CopyToolBits(sRect, dRect, SrcOr); {Eraser color} SetPort(tPort); end; procedure ShowLineWidth; begin LineIndex := LineWidth; if LineWidth = 6 then LineIndex := 5; if LineWidth > 6 then LineIndex := 6; DrawTools; end; procedure GetFatLine (xstart, ystart, angle: extended; count: integer; var line: rLineType); var i, j, xbase, ybase: integer; x, y, xinc, yinc, pAngle, xinc2, yinc2: extended; sum, value: extended; add: boolean; begin add := (angle > 90.0) and (angle <= 270.0); angle := (angle / 180.0) * pi; xinc := cos(angle); yinc := -sin(angle); if add then pAngle := angle + pi / 2.0 else pAngle := angle - pi / 2.0; xinc2 := cos(pAngle); yinc2 := -sin(pAngle); for i := 0 to count - 1 do begin x := xstart; y := ystart; sum := 0.0; for j := 1 to LineWidth do begin if info^.fit <> uncalibrated then value := GetCInterpolatedPixel(x, y) else value := GetInterpolatedPixel(x, y); sum := sum + value; x := x + xinc2; y := y + yinc2; end; line[i] := sum / LineWidth; xstart := xstart + xinc; ystart := ystart + yinc; end; end; procedure ComputePlotMinAndMax; var i: integer; temp: extended; begin if InvertPlots then for i := 0 to PlotCount - 1 do PlotData^[i] := maxCValue - (PlotData^[i] - minCValue); ActualPlotMin := 10e12; ActualPlotMax := -10e12; for i := 0 to PlotCount - 1 do begin temp := PlotData^[i]; if temp < ActualPlotMin then ActualPlotMin := temp; if temp > ActualPlotMax then ActualPlotMax := temp; end; end; procedure SetupPlot (start: point; VerticalPlot: boolean); const MinWidth = 150; var fRect, trect: rect; i, y, WindowWidth, fmax: integer; SaveClipRegion: RgnHandle; pt: point; scale, vscale: extended; AutoScale: boolean; index: Byte; begin with info^ do begin PlotLeftMargin := 38; PlotTopMargin := 10; PlotBottomMargin := 20; PlotRightMargin := 20; if FixedSizePlot then begin PlotWidth := ProfilePlotWidth; PlotHeight := ProfilePlotHeight end else begin PlotWidth := PlotCount * trunc(magnification + 0.5); if PlotWidth < MinWidth then PlotWidth := MinWidth; if PlotWidth + PlotRightMargin + PicLeftBase > ScreenWidth then PlotWidth := ScreenWidth - PlotRightMargin - PicLeftBase - 10; if PlotWidth > PicRect.right then PlotWidth := PicRect.right; PlotHeight := PlotWidth div 2; if PlotWidth > 300 then PlotHeight := PlotWidth div 3; if PlotWidth > 400 then PlotHeight := PlotWidth div 4; end; PlotWidth := PlotWidth + PlotLeftMargin + PlotRightMargin; PlotHeight := PlotHeight + PlotTopMargin + PlotBottomMargin; OffscreenToScreen(start); pt.h := start.h; pt.v := start.v + 40; SetPort(wptr); LocalToGlobal(pt); if VerticalPlot then PlotLeft := PicLeftBase else PlotLeft := pt.h - PlotLeftMargin; PlotTop := pt.v; if PlotLeft > (ScreenWidth - PlotWidth) then PlotLeft := ScreenWidth - PlotWidth - 10; if PlotTop < 60 then PlotTop := 60; if PlotTop > (ScreenHeight - PlotHeight) then PlotTop := ScreenHeight - PlotHeight - 10; if PlotTop < 60 then PlotTop := 60; MakePlotWindow(PlotLeft, PlotTop, PlotWidth, PlotHeight); if PlotWindow = nil then exit(SetupPlot); WindowPeek(PlotWindow)^.WindowKind := ProfilePlotKind; if SpatiallyCalibrated then begin PlotScale := 1 / xScale; if xUnit = 'inch' then PlotXUnits := 'Inches' else if xUnit = 'meter' then PlotXUnits := 'meters' else if xUnit = 'mile' then PlotXUnits := 'miles' else PlotXUnits := xUnit; end else begin PlotScale := 0.0; PlotXUnits := 'Pixels' end; if fit <> uncalibrated then PlotYUnits := UnitOfMeasure else PlotYUnits := ''; if AutoScalePlots then begin PlotMin := ActualPlotMin; PlotMax := ActualPlotMax; end else begin PlotMin := ProfilePlotMin; PlotMax := ProfilePlotMax; end; fmax := PlotCount - 1; if (PlotMax - PlotMin) <> 0 then vscale := fmax / (PlotMax - PlotMin) else vscale := 1.0; scale := 2048.0 / PlotCount; {This scaling needed to get around a 32-bit QD problem} if scale < 1.0 then scale := 1.0; fmax := round(fmax * scale); vscale := vscale * scale; SetRect(fRect, 0, 0, fmax, fmax); SetPort(PlotWindow); SaveClipRegion := PlotWindow^.ClipRgn; RectRgn(PlotWindow^.ClipRgn, fRect); PlotPICT := OpenPicture(fRect); PenNormal; if LinePlot then begin MoveTo(0, round(vscale * (PlotMax - PlotData^[0]))); for i := 1 to PlotCount - 1 do LineTo(round(i * scale), round(vscale * (PlotMax - PlotData^[i]))) end else for i := 1 to PlotCount - 1 do begin y := round(vscale * (PlotMax - PlotData^[i])); MoveTo(round(i * scale), y); LineTo(round(i * scale), y) end; ClosePicture; PlotWindow^.ClipRgn := SaveClipRegion; InvalRect(PlotWindow^.PortRect); SelectWindow(PlotWindow); end; {with} end; procedure PlotLineProfile; var x1, y1, x2, y2, ulength, clength: extended; start: point; i, count:integer; begin GetLengthOrPerimeter(ulength, clength); count := round(ulength); if count = 0 then begin PutError('Line length is zero.'); AbortMacro; exit(PlotLineProfile); end; if count > MaxLine then begin PlotTooLongMsg; exit(PlotLineProfile); end; PlotCount := count; GetLoi(x1, y1, x2, y2); PlotAngle := info^.LAngle; if LineWidth > 1 then GetFatLine(x1, y1, PlotAngle, PlotCount, PlotData^) else GetObliqueLine(x1, y1, 0.0, PlotAngle, PlotCount, PlotData^); PlotAvg := LineWidth; PlotStart.h := round(x1); PlotStart.v := round(y1); ComputePlotMinAndMax; if ShowPlot then SetupPlot(PlotStart, false); end; function CoordinatesAvailable: boolean; var available: boolean; begin with info^.RoiRect do available := (nCoordinates > 0) and ((right - left) = CoordinatesWidth) and ((bottom - top) = CoordinatesHeight) and (info^.RoiType = CoordinatesRoiType); if AnalyzingParticles and (nCoordinates > 0) then available := true; CoordinatesAvailable := available; end; function CoordinatesAvailableMsg: boolean; var available: boolean; begin available := CoordinatesAvailable; if not available then PutError('XY coordinates are not available.'); CoordinatesAvailableMsg := available; end; function GetArbitraryLine (var count: integer; var pdata: rLineType): boolean; var angle, length, leftover: extended; i, j, ilength, xbase, ybase: integer; x1, y1, x2, y2: LongInt; data: rLineType; begin if not CoordinatesAvailableMsg or (nCoordinates < 2) then begin GetArbitraryLine := false; exit(GetArbitraryLine); end; count := 0; length := 0.0; leftover := 0.0; with info^.RoiRect do begin xbase := left; ybase := top; end; for i := 2 to nCoordinates do begin x1 := xCoordinates^[i - 1] + xbase; y1 := yCoordinates^[i - 1] + ybase; x2 := xCoordinates^[i] + xbase; y2 := yCoordinates^[i] + ybase; length := sqrt(sqr(x2 - x1) + sqr(y2 - y1)); if length > 0.0 then begin length := length - LeftOver; ilength := round(length); if ilength > 0 then begin angle:=GetAngle(x2 - x1, y1 - y2); GetObliqueLine(x1, y1, leftover, angle, ilength, data); for j := 1 to ilength do begin pdata[count] := data[j - 1]; if count < MaxLine then count := count + 1; end; end; leftover := length - ilength; end; end; GetArbitraryLine := true; end; procedure PlotArbitraryLine; var angle, length, leftover: extended; x1, y1, x2, y2, i, j, count: integer; data: LineType; begin if not GetArbitraryLine(PlotCount, PlotData^) then exit(PlotArbitraryLine); PlotAvg := 1; with info^.RoiRect do begin PlotStart.h := left; PlotStart.v := top; end; ComputePlotMinAndMax; if ShowPlot then SetupPlot(PlotStart, false); end; procedure FindIntegratedDensity (var IntDen, Background: extended); var i, MinLevel, MaxLevel, iback: integer; MaxCount: LongInt; h, h2: HistogramType; sum, wsum: extended; procedure SmoothHistogram; var i: integer; begin h2 := h; h[0] := (3 * h2[0] + h2[1]) div 5; for i := 1 to 254 do h[i] := (h2[i - 1] + 2 * h2[i] + h2[i + 1]) div 4; end; begin with results do begin MinLevel := MinIndex; MaxLevel := round(UncalibratedMean); if MaxLevel > 254 then MaxLevel := 254; h := histogram; for i := 0 to 255 do h[i] := h[i] * 10; for i := 1 to 15 do SmoothHistogram; if OptionKeyDown then histogram := h; Background := 0.0; MaxCount := 0; for i := MinLevel to MaxLevel do if h[i] > MaxCount then begin MaxCount := h[i]; Background := cvalue[i] end; IntDen := mArea^[mCount] * (mean^[mCount] - Background); end; end; procedure ShowInfo; var vloc, hloc: integer; tPort: GrafPtr; trect: rect; clength, cx, cy, IntDen, BackgroundLevel: extended; tUnit: UnitType; TextStyle:style; procedure NewLine; begin vloc := vloc + 12; MoveTo(hloc, vloc); end; begin GetPort(tPort); vloc := 35; hloc := 4; SetPort(InfoWindow); TextFont(Geneva); TextSize(9); Setrect(trect, 0, vloc, rwidth, rheight); EraseRect(trect); if InfoMessage <> '' then begin Setrect(trect, hloc, vloc + 15, rwidth - 10, rheight); TETextBox(pointer(ord(@InfoMessage) + 1), length(InfoMessage), trect, teJustLeft) end else with results do begin NewLine; with info^ do begin if ShowCount then begin DrawBString('Count: '); DrawLong(mCount); NewLine; end; if SpatiallyCalibrated then begin DrawBString('Pixels: '); DrawLong(PixelCount^[mCount]); NewLine; DrawBString('Area: '); DrawReal(mArea^[mCount], 1, precision); DrawString(' square '); tUnit := xUnit; if tUnit = 'inch' then tUnit := 'Inches' else if tUnit = 'meter' then tUnit := 'meters' else if tUnit = 'mile' then tUnit := 'miles'; DrawString(tUnit); end else begin DrawBString('Area: '); DrawLong(PixelCount^[mCount]); DrawString(' square pixels'); end; NewLine; DrawBString('Mean: '); DrawReal(mean^[mCount], 1, precision); if fit <> uncalibrated then begin DrawString(' '); DrawBString(UnitOfMeasure); DrawString(' ('); DrawLong(round(results.UncalibratedMean)); DrawString(')'); end; if PixelCount^[mCount] > 1 then begin NewLine; DrawBString('Std Dev: '); DrawReal(sd^[mCount], 1, precision); NewLine; DrawBString('Min: '); DrawReal(mMin^[mCount], 1, precision); NewLine; DrawBString('Max: '); DrawReal(mMax^[mCount], 1, precision); end; if (xyLocM in measurements) or (nPoints > 0) then begin NewLine; DrawBString('X: '); DrawReal(xcenter^[mCount], 6, precision); NewLine; DrawBString('Y: '); DrawReal(ycenter^[mCount], 6, precision); end; if ModeM in Measurements then begin NewLine; DrawBString('Mode: '); DrawReal(mode^[mCount], 1, precision); end; if (LengthM in measurements) or (nLengths > 0) then begin NewLine; DrawBString('Length: '); DrawReal(plength^[mCount], 1, precision); end; if MajorAxisM in Measurements then begin NewLine; DrawBString(Concat(MajorLabel, ': ')); DrawReal(MajorAxis^[mCount], 1, precision); end; if MinorAxisM in Measurements then begin NewLine; DrawBString(Concat(MinorLabel, ': ')); DrawReal(MinorAxis^[mCount], 1, precision); end; if (AngleM in measurements) or (nAngles > 0) then begin NewLine; DrawBString('Angle: '); DrawReal(orientation^[mCount], 1, precision); end; if IntDenM in measurements then begin NewLine; FindIntegratedDensity(IntDen, BackgroundLevel); DrawBString('Integrated Density: '); DrawReal(IntDen, 1, precision); NewLine; DrawBString('Background Level: '); DrawReal(BackGroundLevel, 1, precision); end else begin IntDen := 0.0; BackGroundLevel := 0.0; end; IntegratedDensity^[mCount] := IntDen; idBackground^[mCount] := BackGroundLevel; if User1M in Measurements then begin NewLine; DrawBString(Concat(User1Label, ': ')); DrawReal(User1^[mCount], 1, precision); end; if User2M in Measurements then begin NewLine; DrawBString(Concat(User2Label, ': ')); DrawReal(User2^[mCount], 1, precision); end; end; end; {with} SetPort(tPort); mCount2 := mCount; end; procedure PaintCircle (hloc, vloc: integer); var r: rect; begin SetRect(r, hloc, vloc, hloc + LineWidth, vloc + LineWidth); PaintOval(r); end; procedure DrawBrush (start, finish: point); {Thanks to Robert Rimmer for suggesting the use of a line generator to implement the brush.} var deltax, deltay, xinc, yinc, accumulator, i: integer; xloc, yloc, offset, j: integer; begin xloc := start.h; yloc := start.v; deltax := finish.h - xloc; deltay := finish.v - yloc; if (deltax = 0) and (deltay = 0) then begin PaintCircle(xloc, yloc); exit(DrawBrush) end; if deltax < 0 then begin xinc := -1; deltax := -deltax end else xinc := 1; if deltay < 0 then begin yinc := -1; deltay := -deltay end else yinc := 1; if DeltaX > DeltaY then begin {More horizontal} accumulator := deltax div 2; i := deltax; repeat accumulator := accumulator + deltay; if accumulator >= deltax then begin accumulator := accumulator - deltax; yloc := yloc + yinc end; xloc := xloc + xinc; PaintCircle(xloc, yloc); i := i - 1; until i = 0 end else begin {More vertical} accumulator := deltay div 2; i := deltay; repeat accumulator := accumulator + deltax; if accumulator >= deltay then begin accumulator := accumulator - deltay; xloc := xloc + xinc end; yloc := yloc + yinc; PaintCircle(xloc, yloc); i := i - 1; until i = 0 end; end; procedure DrawObject;{ (obj: ObjectType; p1, p2: point)} var MaskRect, r, dstRect, osMaskRect: rect; tPort: GrafPtr; tmp: integer; SaveGDevice: GDHandle; begin SaveGDevice := GetGDevice; GetPort(tPort); Pt2Rect(p1, p2, MaskRect); with Info^ do begin changes := true; tmp := trunc(magnification + 0.5) * LineWidth; with MaskRect do begin if tmp < 32 then tmp := 32; right := right + tmp; bottom := bottom + tmp; if magnification > 1.0 then begin left := left - tmp; top := top - tmp; end; end; ScreenToOffscreen(p1); ScreenToOffscreen(p2); SetGDevice(osGDevice); SetPort(GrafPtr(osPort)); IndexToRgbForeColor(ForegroundIndex); PenNormal; PenSize(LineWidth, LineWidth); case obj of lineObj: begin MoveTo(p1.h, p1.v); LineTo(p2.h, p2.v); end; Rectangle: begin Pt2Rect(p1, p2, r); FrameRect(r); end; oval: begin Pt2Rect(p1, p2, r); FrameOval(r); end; BrushObj: DrawBrush(p1, p2); end; SetGDevice(SaveGDevice); SetPort(wptr); SetFColor(BlackIndex); SetBColor(WhiteIndex); RectRgn(MaskRgn, MaskRect); CopyBits(BitMapHandle(osPort^.portPixMap)^^, BitMapHandle(CGrafPtr(wptr)^.PortPixMap)^^, SrcRect, wrect, gCopyMode, MaskRgn); SetPort(tPort); end; {with} end; function InvertingCalibrationFunction: boolean; begin with info^ do begin InvertingCalibrationFunction := (fit = StraightLine) and (Coefficient[2] < 0.0) end; end; procedure DrawHistogram; var tPort: GrafPtr; i, h: integer; MaxCount, count, NextMaxCount: LongInt; str: str255; hscale: extended; ShowSlice: boolean; begin ShowSlice := (HistogramSliceStart > 0) or (HistogramSliceEnd < 255); if not printing then begin if HistoWindow = nil then exit(DrawHistogram); GetPort(tPort); SetPort(HistoWindow); EraseRect(HistoWindow^.portRect); end; with Results do begin MaxCount := histogram[imode]; if MaxCount > (hheight - 2) then begin if MaxCount / PixelCount^[mCount] > 0.08 then begin NextMaxCount := 0; for i := 0 to 255 do begin count := histogram[i]; if (i <> imode) and (count > NextMaxCount) then NextMaxCount := count; end; NextMaxCount := NextMaxCount + NextMaxCount div 2; if (NextMaxCount > MaxCount) or (NextMaxCount = 0) then NextMaxCount := MaxCount; hscale := NextMaxCount / (hheight - 2); end else hscale := MaxCount / (hheight - 2); end else hscale := 1.0; if ShowSlice then PenPat(qd.gray); if InvertingCalibrationFunction then for h := 0 to 255 do begin if h = HistogramSliceStart then PenPat(qd.black); MoveTo(255 - h, hheight); LineTo(255 - h, hheight - round(histogram[h] / hscale)); if h = HistogramSliceEnd then PenPat(qd.gray) end else for h := 0 to 255 do begin if h = HistogramSliceStart then PenPat(qd.black); MoveTo(h, hheight); LineTo(h, hheight - round(histogram[h] / hscale)); if h = HistogramSliceEnd then PenPat(qd.gray) end; end; if ShowSlice then PenNormal; if not Printing then SetPort(tPort); end; procedure DrawLabels (xL, yL, zL: str255); {Draws the labels(e.g., X:, Y:, Value:) used for the dynamically} {changing values displayed at the top of the Info window.} var tPort: GrafPtr; trect: rect; s:style; begin if xL = XLabel then if yL = yLabel then if zL = zLabel then exit(DrawLabels); GetPort(tPort); SetPort(InfoWindow); TextSize(9); TextFont(Monaco); TextFace([bold]); if length(xL) > 0 then begin xLabel := xL; xValueLoc := InfoHStart + StringWidth(xLabel); yLabel := yL; yValueLoc := InfoHStart + StringWidth(yLabel); zLabel := zL; zValueLoc := InfoHStart + StringWidth(zLabel); end; Setrect(trect, 0, 0, rwidth, 32); EraseRect(trect); MoveTo(InfoHStart, InfoVStart); DrawString(xLabel); MoveTo(InfoHStart, InfoVStart + 10); DrawString(yLabel); MoveTo(InfoHStart, InfoVStart + 19); DrawString(zLabel); s:=[]; {ppc-bug} TextFace(s); SetPort(tPort); end; procedure ShowNextImage; var n: integer; begin n := info^.PicNum + 1; if n > nPics then n := 1; SelectWindow(PicWindow[n]); end; procedure CascadeImages; var i, hloc, vloc, wwidth, wheight: integer; offset: boolean; begin DisableDensitySlice; hloc := PicLeftBase; vloc := PicTopBase; offset := not OptionKeyDown; for i := nPics downto 1 do begin Info := pointer(WindowPeek(PicWindow[i])^.RefCon); with Info^ do begin HideWindow(wptr); ScaleToFitWindow := false; WindowState := NormalWindow; if offset then wrect := initwrect else begin wwidth := PixelsPerLine; if (hloc + wwidth) > ScreenWidth then wwidth := ScreenWidth - hloc - 5; wheight := nlines; if (vloc + wheight) > ScreenHeight then wheight := ScreenHeight - vloc - 5; SetRect(wrect, 0, 0, wwidth, wheight); end; SrcRect := wrect; KillRoi; magnification := 1.0; if i = nPics then DrawMyGrowIcon(wptr); SizeWindow(wptr, wrect.right, wrect.bottom, true); MoveWindow(wptr, hloc, vloc, true); ShowWindow(wptr); UpdateTitleBar; end; {with} if offset then begin hloc := hloc + hPicOffset; vloc := vloc + vPicOffset; if (vloc + 40) > ScreenHeight then vloc := PicTopBase; end; end; {for} PicLeft := PicLeftBase; PicTop := PicTopBase; WhatToUndo := NothingToUndo; end; procedure TileImages; const gap = 2; TitleBarHeight = 20; var i, hloc, vloc, width, height, hspace, vspace, nRows, nColumns: integer; MinWidth, MinHeight: integer; tInfo: array[1..MaxPics] of InfoPtr; trect: rect; TheyFit: boolean; begin DisableDensitySlice; PicLeft := PicLeftBase; PicTop := PicTopBase; width := MaxInt; height := MaxInt; for i := 1 to nPics do begin tInfo[i] := pointer(WindowPeek(PicWindow[i])^.RefCon); with tinfo[i]^.PicRect do begin if right < width then width := right; if bottom < height then height := bottom; end; end; MinWidth := width; MinHeight := height; hspace := ScreenWidth - PicLeft - 2 * gap; if width > hspace then width := hspace; vspace := ScreenHeight - PicTop - TitleBarHeight; if height > vspace then height := vspace; repeat hloc := PicLeft; vloc := PicTop; TheyFit := true; i := 0; repeat i := i + 1; if (hloc + width) > ScreenWidth then begin hloc := PicLeft; vloc := vloc + TitleBarHeight + height; if (vloc + height) > ScreenHeight then begin TheyFit := false; end; end; hloc := hloc + width + gap; until (TheyFit = false) or (i = nPics); if TheyFit = false then begin width := round(width * 0.98); height := round(height * 0.98); end; until TheyFit; nColumns := (ScreenWidth - PicLeft) div (width + gap); nRows := nPics div nColumns; if (nPics mod nColumns) <> 0 then nRows := nRows + 1; {ShowMessage(concat('nRows= ', Long2str(nRows), crStr, 'nColumns= ', long2str(nColumns)));} if not OptionKeyWasDown then begin width := round((ScreenWidth - PicLeft) / nColumns); width := width - gap - 1; height := round((ScreenHeight - PicTop) / nRows); height := height - TitleBarHeight + 3; if width > MinWidth then width := MinWidth; if height > MinHeight then height := MinHeight; end; hloc := PicLeft; vloc := PicTop; for i := 1 to nPics do begin if (hloc + width) > ScreenWidth then begin hloc := PicLeft; vloc := vloc + TitleBarHeight + height; end; Info := tInfo[i]; with Info^ do begin SetRect(wrect, 0, 0, width, height); if ScaleToFitWindow then begin ScaleToFitWindow := false; SrcRect := wrect; magnification := 1; WindowState := NormalWindow; end; if OptionKeyWasDown then begin ScaleToFitWindow := true; SrcRect := PicRect; ScaleImageWindow(wrect); WindowState := TiledSmallScaled; end else begin SrcRect := wrect; magnification := 1.0; UpdateTitleBar; WindowState := TiledSmall; end; SizeWindow(wptr, wrect.right, wrect.bottom, true); KillRoi; UpdatePicWindow; end; {with} MoveWindow(PicWindow[i], hloc, vloc, true); hloc := hloc + width + gap; end; {for} WhatToUndo := NothingToUndo; end; function Duplicate (name: str255; SavingBlankField: boolean): boolean; var width, height, i, digit, len: integer; SaveInfo: InfoPtr; src, dst: ptr; hstart, vstart, offset: LongInt; AutoSelectAll: boolean; begin Duplicate := false; if nPics = MaxPics then exit(Duplicate); WhatToUndo := NothingToUndo; if (not SavingBlankField) and (NotRectangular or NotinBounds) then exit(Duplicate); AutoSelectAll := (not Info^.RoiShowing) or SavingBlankField; if AutoSelectAll then SelectAll(false); ShowWatch; with info^ do begin if name = '' then begin len := length(title); if len > 0 then digit := ord(title[len]) else digit := 0; if (len > 5) and (pos(' Copy', title) = (len - 4)) then name := concat(title, ' 2') else if (len > 7) and (pos(' Copy ', title) = (len - 6)) and (digit >= 49) and (digit <= 57) then begin digit := digit +1; if digit > 57 then digit := 49; name := title; name[length(name)] := chr(digit); end else name := concat(title, ' Copy'); TruncateString(name, maxTitle); end; with RoiRect do begin width := right - left; height := bottom - top; hstart := left; vstart := top; end; end; if AutoSelectAll then KillRoi; SaveInfo := Info; if NewPicWindow(name, width, height) then with SaveInfo^ do begin offset := vstart * BytesPerRow + hstart; src := ptr(ord4(PicBaseAddr) + offset); dst := Info^.PicBaseAddr; for i := 0 to height - 1 do begin BlockMove(src, dst, width); src := ptr(ord4(src) + BytesPerRow); dst := ptr(ord4(dst) + Info^.BytesPerRow); end; if SavingBlankField then begin Info^.PIctureType := BlankField; BlankFieldInfo := info; end; Duplicate := true; end; {with} end; procedure InvertPic; var tPort: GrafPtr; SaveGDevice: GDHandle; begin SaveGDevice := GetGDevice; SetGDevice(osGDevice); GetPort(tPort); with Info^ do begin SetPort(GrafPtr(osPort)); InvertRect(PicRect); end; SetPort(tPort); SetGDevice(SaveGDevice); end; procedure ShowMessage (str: str255); begin InfoMessage := str; ShowInfo; end; procedure ShowTime (StartTicks: LongInt; r: rect; str: str255); var width, height, nPixels: LongInt; seconds, rate: extended; begin with r do begin width := right - left; height := bottom - top; nPixels := width * height; end; seconds := (TickCount - StartTicks) / 60.0; if seconds <> 0.0 then rate := nPixels / seconds else rate := 0.0; ShowMessage(StringOf(nPixels:1, ' pixels ', crStr, seconds:1:2, ' seconds', crStr, rate:1:0, ' pixels/second', crStr, str)); end; procedure ShowFrameRate (str1: str255; StartTicks, nFrames: LongInt); var seconds: extended; str2: str255; begin seconds := (TickCount - StartTicks) / 60.0; if seconds = 0.0 then seconds := 0.167; RealToString(nFrames / seconds, 1, 2, str2); ShowMessage(concat(str1, str2, ' frames/second')); end; procedure ConvertHistoToText; var i: integer; ValuesInverted: boolean; begin ValuesInverted := InvertingCalibrationFunction; TextBufSize := 0; for i := 0 to 255 do begin if ValuesInverted then PutLong(Histogram[255 - i], 1) else PutLong(Histogram[i], 1); if i <> 255 then PutChar(cr); end; end; procedure ConvertPlotToText; var i: integer; begin TextBufSize := 0; for i := 0 to PlotCount - 1 do begin PutReal(PlotData^[i], 1, precision); if i <> PlotCount then PutChar(cr); end; end; procedure ConvertCalibrationCurveToText; var i: integer; begin TextBufSize := 0; for i := 0 to 255 do begin PutReal(cvalue[i], 1, 3); if i <> 255 then PutChar(cr); end; end; procedure SetupUndoInfoRec; {Initialize the Undo buffer's Info record so we can copy} {the current image to the Undo buffer and operate on it.} begin with UndoInfo^ do begin PixelsPerLine := info^.PixelsPerLine; BytesPerRow := info^.BytesPerRow; nLines := Info^.nLines; ImageSize := Info^.ImageSize; PixMapSize := info^.PixMapSize; RoiRect := info^.RoiRect; CopyRgn(Info^.roiRgn, roiRgn); roiType := Info^.roiType; PicRect := Info^.PicRect; with osPort^ do begin with portPixMap^^ do begin RowBytes := BitOr(BytesPerRow, $8000); bounds := PicRect; end; PortRect := PicRect; RectRgn(visRgn, PicRect); end; end; end; {$POP} procedure ActivateWindow; var tPort: GrafPtr; SaveGDevice: GDHandle; begin with info^ do begin IsInsertionPoint := false; WhatToUndo := NothingToUndo; UndoFromClip := false; DrawLabels('', '', ''); MouseState := NotInRoi; RoiUpdateTime := 0; if osPort <> nil then begin SaveGDevice := GetGDevice; SetGDevice(osGDevice); GetPort(tPort); SetPort(GrafPtr(osPort)); IndexToRgbForeColor(ForegroundIndex); IndexToRgbBackColor(BackgroundIndex); SetPort(tPort); SetGDevice(SaveGDevice); end; ShowRoi; end; end; procedure UpdateResultsWindow; begin SetPort(ResultsWindow); DrawControls(ResultsWindow); DrawGrowIcon(ResultsWindow); UpdateList; if ResultsWindow = FrontWindow then begin ShowControl(hScrollBar); ShowControl(vScrollBar); end else begin HideControl(hScrollBar); HideControl(vScrollBar); end; end; procedure ScrollResultsText; var value: INTEGER; begin with ListTE^^ do TEScroll((viewRect.left - destRect.left) - GetControlValue(hScrollBar), (viewRect.top - destRect.top) - (GetControlValue(vScrollBar) * LineHeight), ListTE); end; procedure UpdateResultsScrollBars; var vMax, vValue, hMax, hValue: integer; begin with ListTE^^, ListTE^^.viewRect do begin vListPageSize := (bottom - top) div LineHeight; hListPageSize := right - left; vMax := nLines - vListPageSize; hMax := (nListColumns + 1) * (FieldWidth + 1) * 6 - hListPageSize; vValue := (top - destRect.top) div LineHeight; hValue := left - destRect.left end; if vMax < 0 then vMax := 0; if vValue < 0 then vValue := 0; if hMax < 0 then hMax := 0; if vValue < 0 then vValue := 0; SetControlMaximum(vScrollBar, vMax); SetControlValue(vScrollBar, vValue); SetControlMaximum(hScrollBar, hMax); SetControlValue(hScrollBar, hValue); {ShowMessage(concat('nListColumns= ', Long2str(nListColumns), crStr, 'hListPageSize= ', long2str(hListPageSize)));} end; procedure ScrAction (theCtl: ControlHandle; partCode: integer); var bInc, pInc, delta: integer; begin if theCtl = vScrollBar then begin bInc := 1; pInc := vListPageSize end else begin bInc := 4; pInc := hListPageSize end; case partCode of kControlUpButtonPart: delta := -bInc; kControlDownButtonPart: delta := bInc; kControlPageUpPart: delta := -pInc; kControlPageDownPart: delta := pInc; otherwise exit(ScrAction); end; SetControlValue(theCtl, GetControlValue(theCtl) + delta); ScrollResultsText; end; procedure InitResultsTextEdit (font, size: integer); var dRect, vRect: rect; begin if ResultsScrollActionProc=nil then ResultsScrollActionProc:=NewRoutineDescriptor(@ScrAction, uppControlActionProcInfo, GetCurrentISA); SetPort(ResultsWindow); with ResultsWindow^.portRect do SetRect(dRect, left + 4, top, right - 18, bottom - 24); vRect := dRect; ListTE := TENew(dRect, vRect); with ListTE^^ do begin TxFont := font; TxSize := size; crOnly := -1; end; if TextBufSize > 0 then begin TESetText(ptr(TextBufP), TextBufSize, ListTe); TECalText(ListTE); end; UpdateResultsScrollBars; end; procedure DoMouseDownInResults (loc: point); var theCtl: ControlHandle; cValue: integer; begin SelectWindow(ResultsWindow); SetPort(ResultsWindow); GlobalToLocal(loc); case FindControl(loc, ResultsWindow, theCtl) of kControlUpButtonPart, kControlDownButtonPart, kControlPageUpPart, kControlPageDownPart: if TrackControl(theCtl, loc, ResultsScrollActionProc) <> 0 then ; kControlIndicatorPart: if TrackControl(theCtl, loc, nil) <> 0 then ScrollResultsText; otherwise end; end; procedure AppendResults; var vMax: integer; begin if ResultsWindow <> nil then with ListTE^^ do begin if teLength > 32000 then exit(AppendResults); CopyResultsToBuffer(mCount, mCount, true); TESetSelect(teLength, teLength, ListTE); TEInsert(ptr(TextBufP), TextBufSize, ListTE); with ListTE^^ do begin vListPageSize := (viewRect.bottom - viewRect.top) div LineHeight; vMax := nLines - vListPageSize; end; if vMax < 0 then vMax := 0; SetControlMaximum(vScrollBar, vMax); SetControlValue(vScrollBar, GetControlMaximum(vScrollBar)); ScrollResultsText; end; end; procedure DeleteLines (first, last: integer); begin if ResultsWindow <> nil then with ListTE^^ do begin first := first + 2; {Accounts for 2 line header} last := last + 2; if (first = 3) and (last = 3) then first := 1; {if deleting first line then delete header too} if (first < 1) or (first > nLines) or (last < 1) or (last > nLines) then exit(DeleteLines); TESetSelect(LineStarts[first - 1], LineStarts[last], ListTE); TEDelete(ListTE); end; end; procedure UpdateList; begin if (ResultsWindow <> nil) and (mCount > 0) then with ListTE^^ do begin CopyResultsToBuffer(1, mCount, true); TESetSelect(0, teLength, ListTE); TEDelete(ListTE); TEInsert(ptr(TextBufP), TextBufSize, ListTE); UpdateResultsScrollBars; end; end; procedure ShowMeter; const MeterWidth = 264; MeterHeight = 64; var trect: rect; hloc, vloc: integer; begin hloc := ScreenWidth div 2 - MeterWidth div 2; vloc := ScreenHeight div 4 - MeterHeight div 2; SetRect(trect, hloc, vloc, hloc + MeterWidth, vloc + MeterHeight); MeterWindow := NewWindow(nil, trect, '', true, dBoxProc, nil, false, 0); BringToFront(MeterWindow); end; procedure UpdateMeter; {(percentdone: integer; str: str255)} const left = 16; top = 28; right = 248; bottom = 44; var r: rect; begin if percentdone < 0 then begin if MeterWindow <> nil then DisposeWindow(MeterWindow); MeterWindow := nil; exit(UpdateMeter); end; if MeterWindow = nil then ShowMeter; SetPort(MeterWindow); TextFont(SystemFont); TextSize(12); TextMode(SrcCopy); MoveTo(left, top div 2); DrawString(str); SetRect(r, left + StringWidth(str), 0, right, top); EraseRect(r); SetRect(r, left, top, right, bottom); FrameRect(r); SetRect(r, left + 1, top + 1, left + (percentdone * (right - left)) div 100 - 1, bottom - 1); FillRect(r, qd.gray); end; function RgnNotTooBig; {(Rgn1, Rgn2: RgnHandle): boolean} begin RgnNotTooBig := GetHandleSize(handle(Rgn1)) + GetHandleSize(handle(Rgn2)) < 30000 end; procedure GetSmoothedLength (var ulength, clength: extended; FindPerimeter: boolean); {Finds the length of freehand line selections or perimeter of} {freehand area selections using a 3-point moving average.} var i, n: integer; x1, y1, x2, y2, dx, dy: extended; procedure AddDelta; begin with info^ do begin dx := x2 - x1; dy := y2 - y1; uLength := uLength + sqrt(dx * dx + dy * dy); if SpatiallyCalibrated then begin dx := dx / xScale; dy := dy / yScale; cLength := cLength + sqrt(dx * dx + dy * dy); end; end; end; begin with info^ do begin uLength := 0.0; cLength := 0.0; n := nCoordinates; if not CoordinatesAvailable then exit(GetSmoothedLength); if FindPerimeter then begin x1 := (xCoordinates^[n] + xCoordinates^[1] + xCoordinates^[2]) / 3.0; {1} y1 := (yCoordinates^[n] + yCoordinates^[1] + yCoordinates^[2]) / 3.0; end else begin x1 := (xCoordinates^[1] * 2.0 + xCoordinates^[2]) / 3.0; {1} y1 := (yCoordinates^[1] * 2.0 + yCoordinates^[2]) / 3.0; end; x2 := (xCoordinates^[1] + xCoordinates^[2] + xCoordinates^[3]) / 3.0; {2} y2 := (yCoordinates^[1] + yCoordinates^[2] + yCoordinates^[3]) / 3.0; AddDelta; for i := 2 to n - 2 do begin x1 := x2; {i} y1 := y2; x2 := (xCoordinates^[i] + xCoordinates^[i + 1] + xCoordinates^[i + 2]) / 3.0; {i+1} y2 := (yCoordinates^[i] + yCoordinates^[i + 1] + yCoordinates^[i + 2]) / 3.0; AddDelta; end; x1 := x2; {n-1} y1 := y2; if FindPerimeter then begin x2 := (xCoordinates^[n - 1] + xCoordinates^[n] + xCoordinates^[1]) / 3.0; {n} y2 := (yCoordinates^[n - 1] + yCoordinates^[n] + yCoordinates^[1]) / 3.0; AddDelta; x1 := x2; {n} y1 := y2; x1 := (xCoordinates^[n] + xCoordinates^[1] + xCoordinates^[2]) / 3.0; {1} y1 := (yCoordinates^[n] + yCoordinates^[1] + yCoordinates^[2]) / 3.0; AddDelta; end else begin x2 := (xCoordinates^[n - 1] + xCoordinates^[n] * 2.0) / 3.0; {n} y2 := (yCoordinates^[n - 1] + yCoordinates^[n] * 2.0) / 3.0; AddDelta; end; if not SpatiallyCalibrated then cLength := uLength; end; {with} end; procedure GetPerimeter (var uPerimeter, cPerimeter: extended); {Finds the perimeter of traced objects.} var SideLength1, SideLength2: integer; dx1, dx2, dy1, dy2, i: integer; sumdx, sumdy, nCorners, nexti: integer; corner: boolean; begin sumdx := 0; sumdy := 0; nCorners := 0; dx1 := xCoordinates^[1] - xCoordinates^[nCoordinates]; dy1 := yCoordinates^[1] - yCoordinates^[nCoordinates]; SideLength1 := abs(dx1) + abs(dy1); {one of these is 0} corner := false; for i := 1 to nCoordinates do begin nexti := i + 1; if nexti > nCoordinates then nexti := 1; dx2 := xCoordinates^[nexti] - xCoordinates^[i]; dy2 := yCoordinates^[nexti] - yCoordinates^[i]; sumdx := sumdx + abs(dx1); sumdy := sumdy + abs(dy1); SideLength2 := abs(dx2) + abs(dy2); if (SideLength1 > 1) or (not corner) then begin corner := true; nCorners := nCorners + 1; end else corner := false; dx1 := dx2; dy1 := dy2; SideLength1 := SideLength2; end; uPerimeter := sumdx + sumdy - nCorners * (2.0 - sqrt(2.0)); with info^ do if SpatiallyCalibrated then cPerimeter := sumdx/xscale + sumdy/yscale - (nCorners * ((1.0/xscale + 1.0/yscale) - sqrt(sqr(1.0/xscale) + sqr(1.0/yscale)))) else cPerimeter := uPerimeter; end; procedure GetLength (var ulength, clength: extended; FindPerimeter: boolean); {Finds the length of segmented line selections or the perimeter of polygon selections.} var i: integer; xtemp, ytemp: LongInt; xt, yt: extended; begin with info^ do begin uLength := 0.0; cLength := 0.0; if not CoordinatesAvailable then exit(GetLength); for i := 2 to nCoordinates do begin xtemp := xCoordinates^[i] - xCoordinates^[i - 1]; ytemp := yCoordinates^[i] - yCoordinates^[i - 1]; uLength := uLength + sqrt(xtemp * xtemp + ytemp * ytemp); if SpatiallyCalibrated then begin xt := xtemp / xScale; yt := ytemp / yScale; cLength := cLength + sqrt(xt * xt + yt * yt); end; end; if FindPerimeter then begin xtemp := xCoordinates^[1] - xCoordinates^[nCoordinates]; ytemp := yCoordinates^[1] - yCoordinates^[nCoordinates]; uLength := uLength + sqrt(xtemp * xtemp + ytemp * ytemp); if SpatiallyCalibrated then begin xt := xtemp / xScale; yt := ytemp / yScale; cLength := cLength + sqrt(xt * xt + yt * yt); end; end; if not SpatiallyCalibrated then cLength := uLength; end; {with} end; procedure GetStraightLineLength (var ulength, clength: extended); var dx, dy: extended; begin with info^ do begin dx := LX2 - LX1; dy := LY2 - LY1; uLength := sqrt(sqr(dx) + sqr(dy)); if SpatiallyCalibrated then cLength := sqrt(sqr(dx / xScale) + sqr(dy / yScale)) else cLength := uLength; end; end; procedure GetLengthOrPerimeter (var ulength, clength: extended); var t1,t2:extended; begin t1:=ulength; t2:=clength; case info^.RoiType of LineRoi: GetStraightLineLength(ulength, clength); PolygonRoi:begin GetLength(t1, t2{ulength, clength}, true); {ppc-bug} ulength:=t1; clength:=t2; end; FreehandRoi:begin GetSmoothedLength(t1,t2{ulength, clength}, true); ulength:=t1; clength:=t2; end; FreeLineRoi:begin GetSmoothedLength(t1,t2{ulength, clength}, false); ulength:=t1; clength:=t2; end; SegLineRoi:begin GetLength(t1, t2{ulength, clength}, false); ulength:=t1; clength:=t2; end; TracedRoi:begin GetPerimeter(t1,t2{ulength, clength}); ulength:=t1; clength:=t2; end; otherwise begin ulength := 0.0; clength := 0.0; end; end; end; procedure MakeCoordinatesRelative; var i: integer; begin with info^, info^.RoiRect do begin for i := 1 to nCoordinates do begin xCoordinates^[i] := xCoordinates^[i] - left; yCoordinates^[i] := yCoordinates^[i] - top; end; CoordinatesWidth := right - left; CoordinatesHeight := bottom - top; CoordinatesRoiType := RoiType; end; end; procedure MakeOutline (RoiKind: RoiTypeType); {Creates a "marching ants" outline from a list of absolute offscreen XY coordinates.} var i: integer; TempRgn: RgnHandle; spt, pt: point; begin with Info^ do begin if SelectionMode <> NewSelection then TempRgn := NewRgn; SetPort(wptr); PenNormal; OpenRgn; spt.h := xCoordinates^[1]; spt.v := yCoordinates^[1]; MoveTo(spt.h, spt.v); for i := 2 to nCoordinates do begin pt.h := xCoordinates^[i]; pt.v := yCoordinates^[i]; LineTo(pt.h, pt.v); end; LineTo(spt.h, spt.v); case SelectionMode of NewSelection: CloseRgn(roiRgn); AddSelection: begin CloseRgn(TempRgn); if RgnNotTooBig(roiRgn, TempRgn) then UnionRgn(roiRgn, TempRgn, roiRgn); nCoordinates := 0; end; SubSelection: begin CloseRgn(TempRgn); if RgnNotTooBig(roiRgn, TempRgn) then DiffRgn(roiRgn, TempRgn, roiRgn); nCoordinates := 0; end; end; RoiShowing := true; roiType := RoiKind; RoiRect := roiRgn^^.rgnBBox; UpdatePicWindow; end; if SelectionMode <> NewSelection then DisposeRgn(TempRgn); WhatToUndo := NothingToUndo; measuring := false; MakeCoordinatesRelative; end; procedure ConvertCoordinates; {Convert from screen to offscreen coordinates} var i: integer; begin with info^, info^.SrcRect do begin if (magnification <> 1.0) or (left <> 0) or (top <> 0) then begin if MakingLOI then for i := 1 to nCoordinates do begin xCoordinates^[i] := left + trunc(xCoordinates^[i] / magnification); yCoordinates^[i] := top + trunc(yCoordinates^[i] / magnification); end else for i := 1 to nCoordinates do begin xCoordinates^[i] := left + round(xCoordinates^[i] / magnification); yCoordinates^[i] := top + round(yCoordinates^[i] / magnification); end; end; end {with} end; procedure DrawTriangle (left, top: integer); var triangle: PolyHandle; begin triangle := OpenPoly; if triangle = nil then exit(DrawTriangle); MoveTo(left, top); LineTo(left + 12, top); LineTo(left + 6, top + 7); LineTo(left, top); ClosePoly; PaintPoly(triangle); KillPoly(triangle); end; procedure DrawDropBox (r: rect); {Draws the drop shadow box used for pop-up menus} begin with r do begin EraseRect(r); FrameRect(r); MoveTo(left + 2, bottom); LineTo(right, bottom); MoveTo(right, top + 2); LineTo(right, bottom); DrawTriangle(right - 15, top + 6); end; end; function PopUpMenu (theMenu: MenuHandle; left, top, PopUpItem: integer): integer; {Pops up the specified menu and returns item selected by user.} var PopupResult: LongInt; MenuLoc: point; begin with MenuLoc do begin h := left; v := top; LocalToGlobal(MenuLoc); PopUpResult := PopupMenuSelect(theMenu, v, h, PopUpItem); PopUpMenu := LoWrd(PopUpResult); end; end; procedure GetDItemRect (d: DialogPtr; item: integer; var r: rect); var iType: integer; ignore: handle; begin GetDialogItem(d, item, itype, ignore, r) end; procedure DrawPopUpText (str: str255; r: rect); var TextRect: rect; begin with r do begin TextFont(SystemFont); if (str = '+') or (str = 'Ð') or (str = 'Ö') then begin TextSize(24); MoveTo(left + 13, bottom - 2); end else begin TextSize(12); MoveTo(left + 13, bottom - 5); end; if length(str) = 1 then DrawString(str) else begin SetRect(TextRect, left + 13, top + 1, right - 15, bottom - 1); TETextBox(pointer(ord(@str) + 1), length(str), TextRect, TEJustLeft); end; end; TextSize(12); end; procedure SetUProc (d: DialogPtr; item: integer; pptr: handle); var itype: integer; r: rect; h: handle; begin GetDialogItem(d, item, itype, h, r); SetDialogItem(d, item, itype, pptr, r); end; procedure RemoveDensityCalibration; var i:integer; begin for i := 0 to 255 do cvalue[i] := i; info^.fit:=uncalibrated; NoInfo^.fit:=uncalibrated; InvertPixelValues:=false; DrawLabels('', '', ''); UpdateTitleBar; end; function isInvertingFunction:boolean; begin with info^ do isInvertingFunction:=(fit=StraightLine) and (nCoefficients=2) and (Coefficient[1]=255.0) and (Coefficient[2]=-1.0) end; function CheckCalibration: boolean; var result: integer; begin with info^ do begin CheckCalibration := true; if (fit <> uncalibrated) and (not isInvertingFunction) then begin result := PutMessageWithCancel('This operation will result in loss of density calibration.'); if result = cancel then begin CheckCalibration := false; AbortMacro end else RemoveDensityCalibration; end; end; {with} end; procedure PlotTooLongMsg; begin PutError(StringOf('Profile plots are limited to ', MaxLine:1, ' pixels.')); end; end.