home *** CD-ROM | disk | FTP | other *** search
/ Otherware / Otherware_1_SB_Development.iso / mac / developm / source / image144.sit / Graphics.p < prev    next >
Encoding:
Text File  |  1992-03-30  |  52.8 KB  |  2,003 lines

  1. unit Graphics;
  2.  
  3. {Graphics routines used by Image program}
  4.  
  5. interface
  6.  
  7.     uses
  8.         QuickDraw, Palettes, Picker, PrintTraps, globals, Utilities;
  9.  
  10.     procedure DoProfilePlot (p1, p2: point);
  11.     procedure DrawPlot;
  12.     procedure UpdatePlotWindow;
  13.     procedure ShowValues;
  14.     procedure SetupPlot (var data: LineType; start: point; VerticalPlot: boolean);
  15.     procedure MakePlotWindow (PlotLeft, PlotTop, PlotWidth, PlotHeight: integer);
  16.     procedure DrawObject (obj: ObjectType; p1, p2: point);
  17.     procedure DrawTools;
  18.     function InvertingCalibrationFunction: boolean;
  19.     procedure DrawHistogram;
  20.     procedure DrawLabels (xL, yL, zL: str255);
  21.     procedure ShowNextWindow;
  22.     procedure StackWindows;
  23.     procedure TileWindows;
  24.     function Duplicate (name: str255; SavingBlankField: boolean): boolean;
  25.     procedure InvertPic;
  26.     procedure ShowMessage (str: str255);
  27.     procedure ShowTime (StartTicks: LongInt; r: rect; str: str255);
  28.     procedure ShowFrameRate (str1: str255; StartTicks, nFrames: LongInt);
  29.     function long2str (num: LongInt): str255;
  30.     procedure ConvertHistoToText;
  31.     procedure ConvertPlotToText;
  32.     procedure ConvertCalibrationCurveToText;
  33.     procedure SetupUndoInfoRec;
  34.     procedure ShowProgress (current, total: LongInt);
  35.     procedure ScaleAndRotate;
  36.     procedure ActivateWindow;
  37.     procedure UpdateResultsWindow;
  38.     procedure ScrollText;
  39.     procedure UpdateScrollBars;
  40.     procedure InitTextEdit (font, size: integer);
  41.     procedure DoMouseDownInResults (loc: point);
  42.     procedure AppendResults;
  43.     procedure DeleteLines (first, last: integer);
  44.     procedure UpdateList;
  45.     procedure SelectSlice (i: integer);
  46.     procedure ShowMeter;
  47.     procedure UpdateMeter (percentdone: integer; str: str255);
  48.     function RgnNotTooBig (Rgn1, Rgn2: RgnHandle): boolean;
  49.     procedure ComputeLength (FindingPerimeterLength: boolean);
  50.     procedure MakeOutline;
  51.  
  52.  
  53.  
  54. implementation
  55.  
  56.  
  57.     procedure DrawNum (x, y: integer; value: LongInt);
  58.         var
  59.             str: str255;
  60.     begin
  61.         MoveTo(x, y);
  62.         if value < 10 then
  63.             DrawString('0');
  64.         if value < 100 then
  65.             DrawString('0');
  66.         NumToString(value, str);
  67.         DrawString(str);
  68.     end;
  69.  
  70.  
  71.     procedure LabelProfilePlot;
  72.         var
  73.             str: str255;
  74.             min, max: extended;
  75.     begin
  76.         if InvertPlots then begin
  77.                 min := PlotMax;
  78.                 max := PlotMin
  79.             end
  80.         else begin
  81.                 min := PlotMin;
  82.                 max := PlotMax
  83.             end;
  84.         if info^.DensityCalibrated then begin
  85.                 MoveTo(1, PlotHeight - PlotBottomMargin);
  86.                 if abs(min) >= 1000.0 then
  87.                     DrawReal(min, 1, 0)
  88.                 else
  89.                     DrawReal(min, 1, 2);
  90.                 MoveTo(1, PlotTopMargin + 8);
  91.                 if abs(max) >= 1000.0 then
  92.                     DrawReal(max, 1, 0)
  93.                 else
  94.                     DrawReal(max, 1, 2);
  95.             end
  96.         else begin
  97.                 DrawNum(2, PlotHeight - PlotBottomMargin, trunc(Min));
  98.                 DrawNum(2, PlotTopMargin + 8, trunc(Max));
  99.             end;
  100.         MoveTo(PlotLeftMargin + 15, PlotHeight - PlotBottomMargin + 12);
  101.         DrawString('N=');
  102.         NumToString(PlotCount, str);
  103.         DrawString(str);
  104.         DrawString('     Mean=');
  105.         RealToString(PlotMean, 3, 2, str);
  106.         DrawString(str);
  107.         if PlotAvg > 1 then begin
  108.                 DrawString('    Width=');
  109.                 NumToString(PlotAvg, str);
  110.                 DrawString(str);
  111.             end;
  112.         DrawString('    ');
  113.         if info^.DensityCalibrated then begin
  114.                 DrawString('DensityCalibrated(');
  115.                 DrawString(info^.UnitOfMeasure);
  116.                 DrawString(')');
  117.             end
  118.         else
  119.             DrawString('Uncalibrated');
  120.     end;
  121.  
  122.  
  123.     procedure LabelCalibrationPlot;
  124.         var
  125.             pbottom, hloc, vloc, i: integer;
  126.             letter: packed array[1..6] of char;
  127.     begin
  128.         pbottom := PlotHeight - PLotBottomMargin;
  129.         DrawJReal(PlotLeftMargin, PlotTopMargin + 4, MaxValue, 2);
  130.         DrawJReal(PlotLeftMargin, pbottom, MinValue, 2);
  131.         MoveTo(PlotLeftMargin - 3, pbottom + 10);
  132.         DrawString('0');
  133.         MoveTo(PlotWidth - PlotRightMargin - 14, pbottom + 10);
  134.         DrawString('255');
  135.         MoveTo(PlotLeftMargin + 15, PlotTopMargin + 15);
  136.         TextSize(12);
  137.         case info^.fit of
  138.             StraightLine: 
  139.                 DrawString('y=a+bx');
  140.             Poly2: 
  141.                 DrawString('y=a+bx+cx^2');
  142.             Poly3: 
  143.                 DrawString('y=a+bx+cx^2+dx^3');
  144.             Poly4: 
  145.                 DrawString('y=a+bx+cx^2+dx^3+ex^4');
  146.             Poly5: 
  147.                 DrawString('y=a+bx+cx^2+dx^3+ex^4+fx^5');
  148.             ExpoFit: 
  149.                 DrawString('y=aexp(bx)');
  150.             PowerFit: 
  151.                 DrawString('y=ax^b');
  152.             LogFit: 
  153.                 DrawString('y=aln(bx)');
  154.             RodbardFit: 
  155.                 DrawString('y=c*((a-x)/(x-d))^(1/b)');
  156.             UncalibratedOD: 
  157.                 DrawString('y=log10(255/(255-x))');
  158.             otherwise
  159.         end;
  160.         hloc := PlotWidth - PlotRightMargin + 5;
  161.         vloc := PlotTopMargin + 25;
  162.         letter := 'abcdef';
  163.         MoveTo(hloc, vloc);
  164.         with info^ do
  165.             for i := 1 to nCoefficients do begin
  166.                     MoveTo(hloc, vloc);
  167.                     TextSize(12);
  168.                     DrawString(letter[i]);
  169.                     DrawString('=');
  170.                     TextSize(9);
  171.                     DrawReal(Coefficient[i], 1, 8);
  172.                     vloc := vloc + 15;
  173.                 end;
  174.         if info^.fit <> UncalibratedOD then begin
  175.                 vloc := vloc + 25;
  176.                 MoveTo(hloc, vloc);
  177.                 DrawString('S.D.=');
  178.                 DrawReal(FitSD, 1, 4);
  179.                 vloc := vloc + 15;
  180.                 MoveTo(hloc, vloc);
  181.                 DrawString('R^2=');
  182.                 DrawReal(FitGoodness, 1, 4);
  183.             end;
  184.     end;
  185.  
  186.  
  187.     procedure DoSpecialGelPlot;
  188.         var
  189.             i: integer;
  190.             vscale: extended;
  191.     begin
  192.         beep;
  193.         if (ProfilePlotMax - ProfilePlotMin) = 0 then
  194.             vscale := 1.0
  195.         else
  196.             vscale := ProfilePlotHeight / (ProfilePlotMax - ProfilePlotMin);
  197.         MoveTo(PlotLeftMargin, PlotTopMargin + round(vscale * (ProfilePlotMax - cvalue[PlotData^[i]])) - 2);
  198.         for i := 1 to PlotCount - 1 do
  199.             LineTo(PlotLeftMargin + i, PlotTopMargin + round(vscale * (ProfilePlotMax - cvalue[PlotData^[i]])) - 2)
  200.     end;
  201.  
  202.  
  203.     procedure DrawPlot;
  204.         var
  205.             fRect: rect;
  206.     begin
  207.         SetRect(fRect, PlotLeftMargin, PlotTopMargin, PlotWidth - PlotRightMargin, PlotHeight - PlotBottomMargin);
  208.         PenNormal;
  209.         FrameRect(fRect);
  210.         DrawPicture(PlotPICT, fRect);
  211.         TextFont(ApplFont);
  212.         TextSize(9);
  213.         if WindowPeek(PlotWindow)^.WindowKind = ProfilePlotKind then begin
  214.                 if DrawPlotLabels then
  215.                     LabelProfilePlot
  216.             end
  217.         else
  218.             LabelCalibrationPlot;
  219.     end;
  220.  
  221.  
  222.     procedure UpdatePlotWindow;
  223.     begin
  224.         SetPort(PlotWindow);
  225.         EraseRect(PlotWindow^.portRect);
  226.         DrawPlot;
  227.         DrawMyGrowIcon(PlotWindow);
  228.     end;
  229.  
  230.  
  231.     procedure MakePlotWindow; {(PlotLeft, PlotTop, PlotWidth, PlotHeight: integer)}
  232.         var
  233.             PLotRect, pwrect, dwrect, srect: rect;
  234.             overlapping: boolean;
  235.     begin
  236.         if PlotWindow = nil then begin
  237.                 SetRect(PlotRect, PlotLeft, PlotTop, PlotLeft + PlotWidth, PlotTop + PlotHeight);
  238.                 PlotWindow := NewWindow(nil, PlotRect, 'Plot', true, DocumentProc, nil, true, 0);
  239.             end
  240.         else begin
  241.                 GetWindowRect(PlotWindow, pwrect);
  242.                 GetWindowRect(info^.wptr, dwrect);
  243.                 overlapping := SectRect(pwrect, dwrect, srect);
  244.                 if overlapping then
  245.                     MoveWindow(PlotWindow, PlotLeft, PlotTop, false);
  246.                 SizeWindow(PlotWindow, PlotWidth, PlotHeight, false);
  247.             end;
  248.     end;
  249.  
  250.  
  251.     procedure SetupPlot (var data: LineType; start: point; VerticalPlot: boolean);
  252.         const
  253.             MinWidth = 100;
  254.         var
  255.             fRect, trect: rect;
  256.             i, y, WindowWidth, fmax: integer;
  257.             SaveClipRegion: RgnHandle;
  258.             pt: point;
  259.             temp, sum, scale, vscale: extended;
  260.             AutoScale: boolean;
  261.             RealData: array[0..MaxLine] of real;
  262.             index: UnsignedByte;
  263.     begin
  264.         with info^ do begin
  265.                 if DensityCalibrated then
  266.                     PlotLeftMargin := 35
  267.                 else
  268.                     PlotLeftMargin := 25;
  269.                 PlotTopMargin := 10;
  270.                 PlotBottomMargin := 20;
  271.                 PlotRightMargin := 10;
  272.                 for i := 0 to PlotCount - 1 do
  273.                     RealData[i] := cvalue[data[i]];
  274.                 if InvertPlots then
  275.                     for i := 0 to PlotCount - 1 do
  276.                         RealData[i] := MaxValue - RealData[i];
  277.                 if FixedSizePlot then begin
  278.                         PlotWidth := ProfilePlotWidth;
  279.                         PlotHeight := ProfilePlotHeight
  280.                     end
  281.                 else begin
  282.                         PlotWidth := PlotCount * trunc(magnification + 0.5);
  283.                         if PlotWidth < MinWidth then
  284.                             PlotWidth := MinWidth;
  285.                         if PlotWidth + PlotRightMargin + PicLeftBase > ScreenWidth then
  286.                             PlotWidth := ScreenWidth - PlotRightMargin - PicLeftBase - 10;
  287.                         PlotHeight := PlotWidth div 2;
  288.                         if PlotWidth > 300 then
  289.                             PlotHeight := PlotWidth div 3;
  290.                         if PlotWidth > 400 then
  291.                             PlotHeight := PlotWidth div 4;
  292.                     end;
  293.                 PlotWidth := PlotWidth + PlotLeftMargin + PlotRightMargin;
  294.                 PlotHeight := PlotHeight + PlotTopMargin + PlotBottomMargin;
  295.                 OffscreenToScreen(start);
  296.                 pt.h := start.h;
  297.                 pt.v := start.v + 40;
  298.                 SetPort(wptr);
  299.                 LocalToGlobal(pt);
  300.                 if VerticalPlot then
  301.                     PlotLeft := PicLeftBase
  302.                 else
  303.                     PlotLeft := pt.h - PlotLeftMargin;
  304.                 PlotTop := pt.v;
  305.                 if PlotLeft > (ScreenWidth - PlotWidth) then
  306.                     PlotLeft := ScreenWidth - PlotWidth - 10;
  307.                 if PlotTop < 60 then
  308.                     PlotTop := 60;
  309.                 if PlotTop > (ScreenHeight - PlotHeight) then
  310.                     PlotTop := ScreenHeight - PlotHeight - 10;
  311.                 if PlotTop < 60 then
  312.                     PlotTop := 60;
  313.                 MakePlotWindow(PlotLeft, PlotTop, PlotWidth, PlotHeight);
  314.                 WindowPeek(PlotWindow)^.WindowKind := ProfilePlotKind;
  315.                 PlotMin := MaxValue;
  316.                 PlotMax := MinValue;
  317.                 sum := 0.0;
  318.                 for i := 0 to PlotCount - 1 do begin
  319.                         temp := RealData[i];
  320.                         sum := sum + temp;
  321.                         if AutoscalePlots then begin
  322.                                 if temp < PlotMin then
  323.                                     PlotMin := temp;
  324.                                 if temp > PlotMax then
  325.                                     PlotMax := temp;
  326.                             end;
  327.                     end;
  328.                 if PlotCount > 0 then
  329.                     PlotMean := sum / PlotCount
  330.                 else
  331.                     PlotMean := 0.0;
  332.                 if not AutoscalePlots then begin
  333.                         PlotMin := ProfilePlotMin;
  334.                         PlotMax := ProfilePlotMax;
  335.                     end;
  336.                 fmax := PlotCount - 1;
  337.                 if (PlotMax - PlotMin) <> 0 then
  338.                     vscale := fmax / (PlotMax - PlotMin)
  339.                 else
  340.                     vscale := 1.0;
  341.                 scale := 2048.0 / PlotCount;  {This scaling needed to get around a 32-bit QD problem}
  342.                 if scale < 1.0 then
  343.                     scale := 1.0;
  344.                 fmax := round(fmax * scale);
  345.                 vscale := vscale * scale;
  346.                 SetRect(fRect, 0, 0, fmax, fmax);
  347.                 SetPort(PlotWindow);
  348.                 SaveClipRegion := PlotWindow^.ClipRgn;
  349.                 RectRgn(PlotWindow^.ClipRgn, fRect);
  350.                 PlotPICT := OpenPicture(fRect);
  351.                 PenNormal;
  352.                 if LinePlot then begin
  353.                         MoveTo(0, round(vscale * (PlotMax - RealData[0])));
  354.                         for i := 1 to PlotCount - 1 do
  355.                             LineTo(round(i * scale), round(vscale * (PlotMax - RealData[i])))
  356.                     end
  357.                 else
  358.                     for i := 1 to PlotCount - 1 do begin
  359.                             y := round(vscale * (PlotMax - RealData[i]));
  360.                             MoveTo(round(i * scale), y);
  361.                             LineTo(round(i * scale), y)
  362.                         end;
  363.                 ClosePicture;
  364.                 PlotWindow^.ClipRgn := SaveClipRegion;
  365.                 InvalRect(PlotWindow^.PortRect);
  366.                 SelectWindow(PlotWindow);
  367.             end;  {wifth}
  368.     end;
  369.  
  370.  
  371.     procedure GetDiagLine (start, finish: Point; var count: integer; var data: LineType; OptionKey: boolean);
  372.         var
  373.             sum: LongInt;
  374.             p: ptr;
  375.             deltax, deltay, xinc, yinc, accumulator, i: integer;
  376.             xloc, yloc, j: integer;
  377.             average: boolean;
  378.             buf, fline: LineType;
  379.     begin
  380.         average := LineWidth > 1;
  381.         if OptionKey and average then
  382.             for i := 0 to MaxLine do
  383.                 fline[i] := ForegroundIndex;
  384.         count := 0;
  385.         xloc := start.h;
  386.         yloc := start.v;
  387.         deltax := finish.h - xloc;
  388.         deltay := finish.v - yloc;
  389.         if (deltax = 0) and (deltay = 0) then begin
  390.                 data[count] := MyGetPixel(xloc, yloc);
  391.                 if OptionKey then
  392.                     PutPixel(xloc, yloc, ForegroundIndex);
  393.                 count := 1;
  394.                 exit(GetDiagLine);
  395.             end;
  396.         if deltax < 0 then begin
  397.                 xinc := -1;
  398.                 deltax := -deltax
  399.             end
  400.         else
  401.             xinc := 1;
  402.         if deltay < 0 then begin
  403.                 yinc := -1;
  404.                 deltay := -deltay
  405.             end
  406.         else
  407.             yinc := 1;
  408.         if DeltaX > DeltaY then begin {More horizontal}
  409.                 if average and (CurrentTool <> LineTool) then
  410.                     deltax := deltax + LineWidth;
  411.                 accumulator := deltax div 2;
  412.                 i := deltax;
  413.                 repeat
  414.                     if count < MaxLine then
  415.                         count := count + 1;
  416.                     accumulator := accumulator + deltay;
  417.                     if accumulator >= deltax then begin
  418.                             accumulator := accumulator - deltax;
  419.                             yloc := yloc + yinc
  420.                         end;
  421.                     xloc := xloc + xinc;
  422.                     if average then begin
  423.                             GetColumn(xloc, yloc, LineWidth, buf);
  424.                             if OptionKey then
  425.                                 PutColumn(xloc, yloc, LineWidth, fline);
  426.                             sum := 0;
  427.                             for j := 0 to LineWidth - 1 do
  428.                                 sum := sum + buf[j];
  429.                             data[count - 1] := round(sum / LineWidth);
  430.                         end
  431.                     else begin
  432.                             data[count - 1] := MyGetPixel(xloc, yloc);
  433.                             if OptionKey then
  434.                                 PutPixel(xloc, yloc, ForegroundIndex);
  435.                         end;
  436.                     i := i - 1;
  437.                 until i = 0
  438.             end
  439.         else begin          {More vertical}
  440.                 if average and (CurrentTool <> LineTool) then
  441.                     deltay := deltay + LineWidth;
  442.                 accumulator := deltay div 2;
  443.                 i := deltay;
  444.                 repeat
  445.                     if count < MaxLine then
  446.                         count := count + 1;
  447.                     accumulator := accumulator + deltax;
  448.                     if accumulator >= deltay then begin
  449.                             accumulator := accumulator - deltay;
  450.                             xloc := xloc + xinc
  451.                         end;
  452.                     yloc := yloc + yinc;
  453.                     if average then begin
  454.                             GetLine(xloc, yloc, LineWidth, buf);
  455.                             if OptionKey then
  456.                                 PutLine(xloc, yloc, LineWidth, fline);
  457.                             sum := 0;
  458.                             for j := 0 to LineWidth - 1 do
  459.                                 sum := sum + buf[j];
  460.                             data[count - 1] := round(sum / LineWidth);
  461.                         end
  462.                     else begin
  463.                             data[count - 1] := MyGetPixel(xloc, yloc);
  464.                             if OptionKey then
  465.                                 PutPixel(xloc, yloc, ForegroundIndex);
  466.                         end;
  467.                     i := i - 1;
  468.                 until i = 0
  469.             end;
  470.     end;
  471.  
  472.  
  473.     procedure DoProfilePlot (p1, p2: point);
  474.         var
  475.             i, range, width, value: integer;
  476.             pt: point;
  477.             OptionKey: boolean;
  478.     begin
  479.         if p2.h < p1.h then begin {Swap ends}
  480.                 pt := p1;
  481.                 p1 := p2;
  482.                 p2 := pt;
  483.             end;
  484.         OptionKey := OptionKeyDown;
  485.         GetDiagLine(p1, p2, PlotCount, PlotData^, OptionKey);
  486.         PlotAvg := LineWidth;
  487.         SetupPlot(PlotData^, p1, false);
  488.         if OptionKey then begin
  489.                 KillRoi;
  490.                 UpdatePicWindow;
  491.                 info^.changes := true;
  492.             end;
  493.     end;
  494.  
  495.  
  496.     procedure FindIntegratedDensity (var IntDen, Background: extended);
  497.         var
  498.             i, MinLevel, MaxLevel, iback: integer;
  499.             MaxCount: LongInt;
  500.             h, h2: HistogramType;
  501.             sum, wsum: extended;
  502.  
  503.         procedure SmoothHistogram;
  504.             var
  505.                 i: integer;
  506.         begin
  507.             h2 := h;
  508.             h[0] := (3 * h2[0] + h2[1]) div 5;
  509.             for i := 1 to 254 do
  510.                 h[i] := (h2[i - 1] + 2 * h2[i] + h2[i + 1]) div 4;
  511.         end;
  512.  
  513.     begin
  514.         with results do begin
  515.                 MinLevel := MinIndex;
  516.                 MaxLevel := imean;
  517.                 if MaxLevel > 254 then
  518.                     MaxLevel := 254;
  519.                 h := histogram;
  520.                 for i := 0 to 255 do
  521.                     h[i] := h[i] * 10;
  522.                 for i := 1 to 15 do
  523.                     SmoothHistogram;
  524.                 if OptionKeyDown then
  525.                     histogram := h;
  526.                 Background := 0.0;
  527.                 MaxCount := 0;
  528.                 for i := MinLevel to MaxLevel do
  529.                     if h[i] > MaxCount then begin
  530.                             MaxCount := h[i];
  531.                             Background := cvalue[i]
  532.                         end;
  533.                 IntDen := mArea^[mCount] * (mean^[mCount] - Background);
  534.             end;
  535.     end;
  536.  
  537.  
  538.     procedure ShowValues;
  539.         var
  540.             vloc, hloc: integer;
  541.             tPort: GrafPtr;
  542.             trect: rect;
  543.             clength, cx, cy, IntDen, BackgroundLevel: extended;
  544.  
  545.         procedure NewLine;
  546.         begin
  547.             vloc := vloc + 12;
  548.             MoveTo(hloc, vloc);
  549.         end;
  550.  
  551.     begin
  552.         GetPort(tPort);
  553.         vloc := 35;
  554.         hloc := 4;
  555.         SetPort(ValuesWindow);
  556.         TextFont(ApplFont);
  557.         TextSize(9);
  558.         Setrect(trect, 0, vloc, rwidth, rheight);
  559.         EraseRect(trect);
  560.         if ValuesMessage <> '' then begin
  561.                 Setrect(trect, hloc, vloc + 15, rwidth - 10, rheight);
  562.                 TextBox(pointer(ord(@ValuesMessage) + 1), length(ValuesMessage), trect, teJustLeft)
  563.             end
  564.         else
  565.             with results do begin
  566.                     NewLine;
  567.                     with info^ do begin
  568.                             if ShowCount then begin
  569.                                     DrawBString('Count: ');
  570.                                     DrawLong(mCount);
  571.                                     NewLine;
  572.                                 end;
  573.                             DrawBString('Pixels: ');
  574.                             DrawLong(PixelCount^[mCount]);
  575.                             if SpatiallyCalibrated then begin
  576.                                     NewLine;
  577.                                     DrawBString('Area: ');
  578.                                     DrawReal(mArea^[mCount], 1, precision);
  579.                                     DrawString(' square ');
  580.                                     DrawString(units);
  581.                                 end;
  582.                             NewLine;
  583.                             DrawBString('Mean: ');
  584.                             DrawReal(mean^[mCount], 1, precision);
  585.                             if DensityCalibrated then begin
  586.                                     DrawString(' ');
  587.                                     DrawBString(UnitOfMeasure);
  588.                                     DrawString('   (');
  589.                                     DrawLong(results.imean);
  590.                                     DrawString(')');
  591.                                 end;
  592.                             if PixelCount^[mCount] > 1 then begin
  593.                                     NewLine;
  594.                                     DrawBString('Std Dev: ');
  595.                                     DrawReal(sd^[mCount], 1, precision);
  596.                                     NewLine;
  597.                                     DrawBString('Min: ');
  598.                                     DrawReal(mMin^[mCount], 1, precision);
  599.                                     NewLine;
  600.                                     DrawBString('Max: ');
  601.                                     DrawReal(mMax^[mCount], 1, precision);
  602.                                 end;
  603.                             if (xyLocM in measurements) or (nPoints > 0) then begin
  604.                                     NewLine;
  605.                                     DrawBString('X: ');
  606.                                     DrawReal(xcenter^[mCount], 6, precision);
  607.                                     NewLine;
  608.                                     DrawBString('Y: ');
  609.                                     DrawReal(ycenter^[mCount], 6, precision);
  610.                                 end;
  611.                             if ModeM in Measurements then begin
  612.                                     NewLine;
  613.                                     DrawBString('Mode: ');
  614.                                     DrawReal(mode^[mCount], 1, precision);
  615.                                 end;
  616.                             if (LengthM in measurements) or (nLengths > 0) then begin
  617.                                     NewLine;
  618.                                     DrawBString('Length: ');
  619.                                     DrawReal(plength^[mCount], 1, precision);
  620.                                 end;
  621.                             if MajorAxisM in Measurements then begin
  622.                                     NewLine;
  623.                                     DrawBString(Concat(MajorLabel, ': '));
  624.                                     DrawReal(MajorAxis^[mCount], 1, precision);
  625.                                 end;
  626.                             if MinorAxisM in Measurements then begin
  627.                                     NewLine;
  628.                                     DrawBString(Concat(MinorLabel, ': '));
  629.                                     DrawReal(MinorAxis^[mCount], 1, precision);
  630.                                 end;
  631.                             if (AngleM in measurements) or (nAngles > 0) then begin
  632.                                     NewLine;
  633.                                     DrawBString('Angle: ');
  634.                                     DrawReal(orientation^[mCount], 1, precision);
  635.                                 end;
  636.                             if IntDenM in measurements then begin
  637.                                     NewLine;
  638.                                     FindIntegratedDensity(IntDen, BackgroundLevel);
  639.                                     DrawBString('Integrated Density: ');
  640.                                     DrawReal(IntDen, 1, precision);
  641.                                     NewLine;
  642.                                     DrawBString('Background Level: ');
  643.                                     DrawReal(BackGroundLevel, 1, precision);
  644.                                 end
  645.                             else begin
  646.                                     IntDen := 0.0;
  647.                                     BackGroundLevel := 0.0;
  648.                                 end;
  649.                             IntegratedDensity^[mCount] := IntDen;
  650.                             idBackground^[mCount] := BackGroundLevel;
  651.                             if User1M in Measurements then begin
  652.                                     NewLine;
  653.                                     DrawBString(Concat(User1Label, ': '));
  654.                                     DrawReal(User1^[mCount], 1, precision);
  655.                                 end;
  656.                             if User2M in Measurements then begin
  657.                                     NewLine;
  658.                                     DrawBString(Concat(User2Label, ': '));
  659.                                     DrawReal(User2^[mCount], 1, precision);
  660.                                 end;
  661.                         end;
  662.                 end; {with}
  663.         SetPort(tPort);
  664.         mCount2 := mCount;
  665.     end;
  666.  
  667.  
  668.     procedure PaintCircle (hloc, vloc: integer);
  669.         var
  670.             r: rect;
  671.     begin
  672.         SetRect(r, hloc, vloc, hloc + LineWidth, vloc + LineWidth);
  673.         PaintOval(r);
  674.     end;
  675.  
  676.  
  677.     procedure DrawBrush (start, finish: point);
  678.   {Thanks to Robert Rimmer for suggesting the use of a line generator to implement the brush.}
  679.         var
  680.             deltax, deltay, xinc, yinc, accumulator, i: integer;
  681.             xloc, yloc, offset, j: integer;
  682.     begin
  683.         xloc := start.h;
  684.         yloc := start.v;
  685.         deltax := finish.h - xloc;
  686.         deltay := finish.v - yloc;
  687.         if (deltax = 0) and (deltay = 0) then begin
  688.                 PaintCircle(xloc, yloc);
  689.                 exit(DrawBrush)
  690.             end;
  691.         if deltax < 0 then begin
  692.                 xinc := -1;
  693.                 deltax := -deltax
  694.             end
  695.         else
  696.             xinc := 1;
  697.         if deltay < 0 then begin
  698.                 yinc := -1;
  699.                 deltay := -deltay
  700.             end
  701.         else
  702.             yinc := 1;
  703.         if DeltaX > DeltaY then begin {More horizontal}
  704.                 accumulator := deltax div 2;
  705.                 i := deltax;
  706.                 repeat
  707.                     accumulator := accumulator + deltay;
  708.                     if accumulator >= deltax then begin
  709.                             accumulator := accumulator - deltax;
  710.                             yloc := yloc + yinc
  711.                         end;
  712.                     xloc := xloc + xinc;
  713.                     PaintCircle(xloc, yloc);
  714.                     i := i - 1;
  715.                 until i = 0
  716.             end
  717.         else begin          {More vertical}
  718.                 accumulator := deltay div 2;
  719.                 i := deltay;
  720.                 repeat
  721.                     accumulator := accumulator + deltax;
  722.                     if accumulator >= deltay then begin
  723.                             accumulator := accumulator - deltay;
  724.                             xloc := xloc + xinc
  725.                         end;
  726.                     yloc := yloc + yinc;
  727.                     PaintCircle(xloc, yloc);
  728.                     i := i - 1;
  729.                 until i = 0
  730.             end;
  731.     end;
  732.  
  733.  
  734.     procedure DrawObject;{ (obj: ObjectType; p1, p2: point)}
  735.         var
  736.             MaskRect, r, dstRect, osMaskRect: rect;
  737.             tPort: GrafPtr;
  738.             tmp: integer;
  739.     begin
  740.         GetPort(tPort);
  741.         Pt2Rect(p1, p2, MaskRect);
  742.         with Info^ do begin
  743.                 changes := true;
  744.                 tmp := trunc(magnification + 0.5) * LineWidth;
  745.                 with MaskRect do begin
  746.                         if tmp < 32 then
  747.                             tmp := 32;
  748.                         right := right + tmp;
  749.                         bottom := bottom + tmp;
  750.                         if magnification > 1.0 then begin
  751.                                 left := left - tmp;
  752.                                 top := top - tmp;
  753.                             end;
  754.                     end;
  755.                 ScreenToOffscreen(p1);
  756.                 ScreenToOffscreen(p2);
  757.                 SetPort(GrafPtr(osPort));
  758.                 PenNormal;
  759.                 PenSize(LineWidth, LineWidth);
  760.                 case obj of
  761.                     lineObj:  begin
  762.                             MoveTo(p1.h, p1.v);
  763.                             LineTo(p2.h, p2.v);
  764.                         end;
  765.                     Rectangle:  begin
  766.                             Pt2Rect(p1, p2, r);
  767.                             FrameRect(r);
  768.                         end;
  769.                     oval:  begin
  770.                             Pt2Rect(p1, p2, r);
  771.                             FrameOval(r);
  772.                         end;
  773.                     BrushObj: 
  774.                         DrawBrush(p1, p2);
  775.                 end;
  776.                 SetPort(wptr);
  777.                 pmForeColor(BlackIndex);
  778.                 pmBackColor(WhiteIndex);
  779.                 RectRgn(MaskRgn, MaskRect);
  780.                 hlock(handle(osPort^.portPixMap));
  781.                 hlock(handle(CGrafPort(wptr^).PortPixMap));
  782.                 CopyBits(BitMapHandle(osPort^.portPixMap)^^, BitMapHandle(CGrafPort(wptr^).PortPixMap)^^, SrcRect, wrect, SrcCopy, MaskRgn);
  783.                 hunlock(handle(osPort^.portPixMap));
  784.                 hunlock(handle(CGrafPort(wptr^).PortPixMap));
  785.                 SetPort(tPort);
  786.             end; {with}
  787.     end;
  788.  
  789.  
  790.     procedure DrawTools;
  791.         var
  792.             tPort: GrafPtr;
  793.             v, n, i: integer;
  794.             str: str255;
  795.             tool: ToolType;
  796.     begin
  797.         GetPort(tPort);
  798.         SetPort(ToolWindow);
  799.         TextFont(ToolFont);
  800.         TextSize(12);
  801.         EraseRect(CGrafPort(ToolWindow^).PortPixMap^^.bounds);
  802.         for tool := FirstTool to LastTool do
  803.             with ToolRect[tool] do begin
  804.                     MoveTo(left + ho, top + vo);
  805.                     DrawChar(ToolChar[tool]);
  806.                 end;
  807.         InvertRect(ToolRect[CurrentTool]);
  808.         pmForeColor(ForegroundIndex);
  809.         with ToolRect[brush] do
  810.             MoveTo(left + ho, top + vo);
  811.         DrawChar(chr(80));
  812.         pmForeColor(BackgroundIndex);
  813.         with ToolRect[Eraser] do
  814.             MoveTo(left + ho, top + vo);
  815.         DrawChar(chr(102));
  816.         pmForeColor(BlackIndex);
  817.         for i := 1 to nLineTypes do
  818.             PaintRect(lines[i]);
  819.         MoveTo(0, Lines[LineIndex].top - 9);
  820.         DrawChar(chr(CheckMarkChar));
  821.         SetPort(tPort);
  822.     end;
  823.  
  824.  
  825.     function InvertingCalibrationFunction: boolean;
  826.     begin
  827.         with info^ do begin
  828.                 InvertingCalibrationFunction := DensityCalibrated and (fit = StraightLine) and (Coefficient[2] < 0.0)
  829.             end;
  830.     end;
  831.  
  832.  
  833.     procedure DrawHistogram;
  834.         var
  835.             tPort: GrafPtr;
  836.             i, h: integer;
  837.             MaxCount, count, NextMaxCount: LongInt;
  838.             str: str255;
  839.             hscale: extended;
  840.             ShowSlice: boolean;
  841.     begin
  842.         ShowSlice := (HistogramSliceStart > 0) or (HistogramSliceEnd < 255);
  843.         if not printing then begin
  844.                 GetPort(tPort);
  845.                 SetPort(HistoWindow);
  846.                 EraseRect(HistoWindow^.portRect);
  847.             end;
  848.         with Results do begin
  849.                 MaxCount := histogram[imode];
  850.                 if MaxCount > (hheight - 2) then begin
  851.                         if MaxCount / PixelCount^[mCount] > 0.08 then begin
  852.                                 NextMaxCount := 0;
  853.                                 for i := 0 to 255 do begin
  854.                                         count := histogram[i];
  855.                                         if (i <> imode) and (count > NextMaxCount) then
  856.                                             NextMaxCount := count;
  857.                                     end;
  858.                                 NextMaxCount := NextMaxCount + NextMaxCount div 2;
  859.                                 if (NextMaxCount > MaxCount) or (NextMaxCount = 0) then
  860.                                     NextMaxCount := MaxCount;
  861.                                 hscale := NextMaxCount / (hheight - 2);
  862.                             end
  863.                         else
  864.                             hscale := MaxCount / (hheight - 2);
  865.                     end
  866.                 else
  867.                     hscale := 1.0;
  868.                 if ShowSlice then
  869.                     PenPat(gray);
  870.                 if InvertingCalibrationFunction then
  871.                     for h := 0 to 255 do begin
  872.                             if h = HistogramSliceStart then
  873.                                 PenPat(black);
  874.                             MoveTo(255 - h, hheight);
  875.                             LineTo(255 - h, hheight - round(histogram[h] / hscale));
  876.                             if h = HistogramSliceEnd then
  877.                                 PenPat(gray)
  878.                         end
  879.                 else
  880.                     for h := 0 to 255 do begin
  881.                             if h = HistogramSliceStart then
  882.                                 PenPat(black);
  883.                             MoveTo(h, hheight);
  884.                             LineTo(h, hheight - round(histogram[h] / hscale));
  885.                             if h = HistogramSliceEnd then
  886.                                 PenPat(gray)
  887.                         end;
  888.             end;
  889.         if ShowSlice then
  890.             PenNormal;
  891.         if not Printing then
  892.             SetPort(tPort);
  893.     end;
  894.  
  895.  
  896.     procedure DrawLabels (xL, yL, zL: str255);
  897.    {Draws the labels(e.g.,  X:, Y:, Value:) used for the dynamically}
  898.    {changing values displayed at the top of the Values window.}
  899.         var
  900.             tPort: GrafPtr;
  901.             trect: rect;
  902.     begin
  903.         if xL = XLabel then
  904.             if yL = yLabel then
  905.                 if zL = zLabel then
  906.                     exit(DrawLabels);
  907.         GetPort(tPort);
  908.         SetPort(ValuesWindow);
  909.         TextSize(9);
  910.         TextFont(Monaco);
  911.         TextFace([bold]);
  912.         if length(xL) > 0 then begin
  913.                 xLabel := xL;
  914.                 xValueLoc := ValuesHStart + StringWidth(xLabel);
  915.                 yLabel := yL;
  916.                 yValueLoc := ValuesHStart + StringWidth(yLabel);
  917.                 zLabel := zL;
  918.                 zValueLoc := ValuesHStart + StringWidth(zLabel);
  919.             end;
  920.         Setrect(trect, 0, 0, rwidth, 32);
  921.         EraseRect(trect);
  922.         MoveTo(ValuesHStart, ValuesVStart);
  923.         DrawString(xLabel);
  924.         MoveTo(ValuesHStart, ValuesVStart + 10);
  925.         DrawString(yLabel);
  926.         MoveTo(ValuesHStart, ValuesVStart + 19);
  927.         DrawString(zLabel);
  928.         TextFace([]);
  929.         SetPort(tPort);
  930.     end;
  931.  
  932.  
  933.     procedure ShowNextWindow;
  934.         var
  935.             n: integer;
  936.     begin
  937.         n := info^.PicNum + 1;
  938.         if n > nPics then
  939.             n := 1;
  940.         SelectWindow(PicWindow[n]);
  941.     end;
  942.  
  943.  
  944.     procedure StackWindows;
  945.         var
  946.             i, hloc, vloc, wwidth, wheight: integer;
  947.             offset: boolean;
  948.     begin
  949.         hloc := PicLeftBase;
  950.         vloc := PicTopBase;
  951.         offset := not OptionKeyDown;
  952.         for i := nPics downto 1 do begin
  953.                 Info := pointer(WindowPeek(PicWindow[i])^.RefCon);
  954.                 if Info^.PictureType <> ScionType then begin
  955.                         with Info^ do begin
  956.                                 HideWindow(wptr);
  957.                                 ScaleToFitWindow := false;
  958.                                 WindowState := NormalWindow;
  959.                                 if offset then
  960.                                     wrect := initwrect
  961.                                 else begin
  962.                                         wwidth := PixelsPerLine;
  963.                                         if (hloc + wwidth) > ScreenWidth then
  964.                                             wwidth := ScreenWidth - hloc - 5;
  965.                                         wheight := nlines;
  966.                                         if (vloc + wheight) > ScreenHeight then
  967.                                             wheight := ScreenHeight - vloc - 5;
  968.                                         SetRect(wrect, 0, 0, wwidth, wheight);
  969.                                     end;
  970.                                 SrcRect := wrect;
  971.                                 KillRoi;
  972.                                 magnification := 1.0;
  973.                                 if i = nPics then
  974.                                     DrawMyGrowIcon(wptr);
  975.                                 SizeWindow(wptr, wrect.right, wrect.bottom, true);
  976.                                 MoveWindow(wptr, hloc, vloc, true);
  977.                                 ShowWindow(wptr);
  978.                                 UpdateTitleBar;
  979.                             end;
  980.                         if offset then begin
  981.                                 hloc := hloc + hPicOffset;
  982.                                 vloc := vloc + vPicOffset;
  983.                                 if (vloc + 40) > ScreenHeight then
  984.                                     vloc := PicTopBase;
  985.                             end;
  986.                     end;
  987.             end;
  988.         PicLeft := PicLeftBase;
  989.         PicTop := PicTopBase;
  990.         WhatToUndo := NothingToUndo;
  991.     end;
  992.  
  993.  
  994.     procedure TileWindows;
  995.         const
  996.             gap = 2;
  997.             TitleBarHeight = 20;
  998.         var
  999.             i, hloc, vloc, width, height, hspace, vspace, nRows, nColumns: integer;
  1000.             MinWidth, MinHeight: integer;
  1001.             tInfo: array[1..MaxPics] of InfoPtr;
  1002.             trect: rect;
  1003.             TheyFit: boolean;
  1004.     begin
  1005.         PicLeft := PicLeftBase;
  1006.         PicTop := PicTopBase;
  1007.         width := MaxInt;
  1008.         height := MaxInt;
  1009.         for i := 1 to nPics do begin
  1010.                 tInfo[i] := pointer(WindowPeek(PicWindow[i])^.RefCon);
  1011.                 with tinfo[i]^.PicRect do begin
  1012.                         if right < width then
  1013.                             width := right;
  1014.                         if bottom < height then
  1015.                             height := bottom;
  1016.                     end;
  1017.             end;
  1018.         MinWidth := width;
  1019.         MinHeight := height;
  1020.         hspace := ScreenWidth - PicLeft - 2 * gap;
  1021.         if width > hspace then
  1022.             width := hspace;
  1023.         vspace := ScreenHeight - PicTop - TitleBarHeight;
  1024.         if height > vspace then
  1025.             height := vspace;
  1026.         repeat
  1027.             hloc := PicLeft;
  1028.             vloc := PicTop;
  1029.             TheyFit := true;
  1030.             i := 0;
  1031.             repeat
  1032.                 i := i + 1;
  1033.                 if (hloc + width) > ScreenWidth then begin
  1034.                         hloc := PicLeft;
  1035.                         vloc := vloc + TitleBarHeight + height;
  1036.                         if (vloc + height) > ScreenHeight then begin
  1037.                                 TheyFit := false;
  1038.                             end;
  1039.                     end;
  1040.                 hloc := hloc + width + gap;
  1041.             until (TheyFit = false) or (i = nPics);
  1042.             if TheyFit = false then begin
  1043.                     width := round(width * 0.98);
  1044.                     height := round(height * 0.98);
  1045.                 end;
  1046.         until TheyFit;
  1047.         nColumns := (ScreenWidth - PicLeft) div (width + gap);
  1048.         nRows := nPics div nColumns;
  1049.         if (nPics mod nColumns) <> 0 then
  1050.             nRows := nRows + 1;
  1051. {ShowMessage(concat('nRows= ', Long2str(nRows), cr, 'nColumns= ', long2str(nColumns)));}
  1052.         if not OptionKeyWasDown then begin
  1053.                 width := round((ScreenWidth - PicLeft) / nColumns);
  1054.                 width := width - gap - 1;
  1055.                 height := round((ScreenHeight - PicTop) / nRows);
  1056.                 height := height - TitleBarHeight + 3;
  1057.                 if width > MinWidth then
  1058.                     width := MinWidth;
  1059.                 if height > MinHeight then
  1060.                     height := MinHeight;
  1061.             end;
  1062.         hloc := PicLeft;
  1063.         vloc := PicTop;
  1064.         for i := 1 to nPics do begin
  1065.                 if (hloc + width) > ScreenWidth then begin
  1066.                         hloc := PicLeft;
  1067.                         vloc := vloc + TitleBarHeight + height;
  1068.                     end;
  1069.                 Info := tInfo[i];
  1070.                 if Info^.PictureType <> ScionType then begin
  1071.                         with Info^ do begin
  1072.                                 SetRect(wrect, 0, 0, width, height);
  1073.                                 if ScaleToFitWindow then begin
  1074.                                         ScaleToFitWindow := false;
  1075.                                         SrcRect := wrect;
  1076.                                         magnification := 1;
  1077.                                         WindowState := NormalWindow;
  1078.                                     end;
  1079.                                 if OptionKeyWasDown then begin
  1080.                                         ScaleToFitWindow := true;
  1081.                                         SrcRect := PicRect;
  1082.                                         ScaleImageWindow(wrect);
  1083.                                         WindowState := TiledSmallScaled;
  1084.                                     end
  1085.                                 else begin
  1086.                                         SrcRect := wrect;
  1087.                                         magnification := 1.0;
  1088.                                         UpdateTitleBar;
  1089.                                         WindowState := TiledSmall;
  1090.                                     end;
  1091.                                 SizeWindow(wptr, wrect.right, wrect.bottom, true);
  1092.                                 KillRoi;
  1093.                                 UpdatePicWindow;
  1094.                             end;
  1095.                         MoveWindow(PicWindow[i], hloc, vloc, true);
  1096.                         hloc := hloc + width + gap;
  1097.                     end;
  1098.             end;        {for}
  1099.         WhatToUndo := NothingToUndo;
  1100.     end;
  1101.  
  1102.  
  1103.     function Duplicate (name: str255; SavingBlankField: boolean): boolean;
  1104.         var
  1105.             width, height, hstart, vstart, i: integer;
  1106.             SaveInfo: InfoPtr;
  1107.             src, dst: ptr;
  1108.             offset: LongInt;
  1109.             AutoSelectAll: boolean;
  1110.     begin
  1111.         Duplicate := false;
  1112.         if nPics = MaxPics then
  1113.             exit(Duplicate);
  1114.         WhatToUndo := NothingToUndo;
  1115.         if (not SavingBlankField) and (NotRectangular or NotinBounds) then
  1116.             exit(Duplicate);
  1117.         AutoSelectAll := (not Info^.RoiShowing) or SavingBlankField;
  1118.         if AutoSelectAll then
  1119.             SelectAll(false);
  1120.         ShowWatch;
  1121.         with info^ do begin
  1122.                 if name = '' then begin
  1123.                         name := concat('Copy of ', title);
  1124.                         if length(name) > 32 then
  1125.                             delete(name, 33, length(name) - 32);
  1126.                     end;
  1127.                 with RoiRect do begin
  1128.                         width := right - left;
  1129.                         if odd(width) then begin
  1130.                                 if (left + width < PicRect.right) then
  1131.                                     width := Width + 1
  1132.                                 else
  1133.                                     Width := width - 1;
  1134.                             end;
  1135.                         height := bottom - top;
  1136.                         hstart := left;
  1137.                         vstart := top;
  1138.                     end;
  1139.             end;
  1140.         if AutoSelectAll then
  1141.             KillRoi;
  1142.         SaveInfo := Info;
  1143.         if NewPicWindow(name, width, height) then
  1144.             with SaveInfo^ do begin
  1145.                     offset := LongInt(vstart) * BytesPerRow + hstart;
  1146.                     src := ptr(ord4(PicBaseAddr) + offset);
  1147.                     dst := Info^.PicBaseAddr;
  1148.                     for i := 0 to height - 1 do begin
  1149.                             BlockMove(src, dst, width);
  1150.                             src := ptr(ord4(src) + BytesPerRow);
  1151.                             dst := ptr(ord4(dst) + width);
  1152.                         end;
  1153.                     if SavingBlankField then begin
  1154.                             Info^.PIctureType := BlankField;
  1155.                             BlankFieldInfo := info;
  1156.                         end;
  1157.                     Duplicate := true;
  1158.                 end; {with}
  1159.     end;
  1160.  
  1161.  
  1162.     procedure InvertPic;
  1163.         var
  1164.             tPort: GrafPtr;
  1165.     begin
  1166.         GetPort(tPort);
  1167.         with Info^ do begin
  1168.                 SetPort(GrafPtr(osPort));
  1169.                 InvertRect(PicRect);
  1170.             end;
  1171.         SetPort(tPort);
  1172.     end;
  1173.  
  1174.  
  1175.     procedure ShowMessage (str: str255);
  1176.     begin
  1177.         ValuesMessage := str;
  1178.         ShowValues;
  1179.     end;
  1180.  
  1181.  
  1182.     procedure ShowTime (StartTicks: LongInt; r: rect; str: str255);
  1183.         var
  1184.             nPixels: LongInt;
  1185.             str1, str2, str3: str255;
  1186.             seconds, rate: extended;
  1187.     begin
  1188.         with r do
  1189.             nPixels := LongInt(right - left) * (bottom - top);
  1190.         NumToString(nPixels, str1);
  1191.         seconds := (TickCount - StartTicks) / 60.0;
  1192.         RealToString(seconds, 1, 2, str2);
  1193.         if seconds <> 0.0 then
  1194.             rate := nPixels / seconds
  1195.         else
  1196.             rate := 0.0;
  1197.         NumToString(round(rate), str3);
  1198.         ShowMessage(concat(str1, ' pixels ', cr, str2, ' seconds', cr, str3, ' pixels/second', cr, str));
  1199.     end;
  1200.  
  1201.     procedure ShowFrameRate (str1: str255; StartTicks, nFrames: LongInt);
  1202.         var
  1203.             seconds: extended;
  1204.             str2: str255;
  1205.     begin
  1206.         seconds := (TickCount - StartTicks) / 60.0;
  1207.         if seconds = 0.0 then
  1208.             seconds := 0.167;
  1209.         RealToString(nFrames / seconds, 1, 2, str2);
  1210.         ShowMessage(concat(str1, str2, ' frames/second'));
  1211.     end;
  1212.  
  1213.  
  1214.     function long2str (num: LongInt): str255;
  1215.         var
  1216.             str: str255;
  1217.     begin
  1218.         NumToString(num, str);
  1219.         long2str := str;
  1220.     end;
  1221.  
  1222.  
  1223.     procedure ConvertHistoToText;
  1224.         var
  1225.             i: integer;
  1226.             ValuesInverted: boolean;
  1227.     begin
  1228.         ValuesInverted := InvertingCalibrationFunction;
  1229.         TextBufSize := 0;
  1230.         for i := 0 to 255 do begin
  1231.                 if ValuesInverted then
  1232.                     PutLong(Histogram[255 - i], 1)
  1233.                 else
  1234.                     PutLong(Histogram[i], 1);
  1235.                 if i <> 255 then
  1236.                     PutChar(cr);
  1237.             end;
  1238.     end;
  1239.  
  1240.  
  1241.     procedure ConvertPlotToText;
  1242.         var
  1243.             i: integer;
  1244.     begin
  1245.         TextBufSize := 0;
  1246.         for i := 0 to PlotCount - 1 do begin
  1247.                 if info^.DensityCalibrated then
  1248.                     PutReal(cvalue[PlotData^[i]], 1, 3)
  1249.                 else
  1250.                     PutLong(PlotData^[i], 1);
  1251.                 if i <> PlotCount then
  1252.                     PutChar(cr);
  1253.             end;
  1254.     end;
  1255.  
  1256.  
  1257.     procedure ConvertCalibrationCurveToText;
  1258.         var
  1259.             i: integer;
  1260.     begin
  1261.         TextBufSize := 0;
  1262.         for i := 0 to 255 do begin
  1263.                 PutReal(cvalue[i], 1, 3);
  1264.                 if i <> 255 then
  1265.                     PutChar(cr);
  1266.             end;
  1267.     end;
  1268.  
  1269.  
  1270.     procedure SetupUndoInfoRec;
  1271. {Initialize the Undo buffer's Info record so we can copy}
  1272. {the current image to the Undo buffer and operate on it.}
  1273.     begin
  1274.         with UndoInfo^ do begin
  1275.                 PixelsPerLine := info^.PixelsPerLine;
  1276.                 BytesPerRow := info^.BytesPerRow;
  1277.                 nLines := Info^.nLines;
  1278.                 ImageSize := Info^.ImageSize;
  1279.                 PixMapSize := info^.PixMapSize;
  1280.                 RoiRect := info^.RoiRect;
  1281.                 CopyRgn(Info^.roiRgn, roiRgn);
  1282.                 roiType := Info^.roiType;
  1283.                 PicRect := Info^.PicRect;
  1284.                 with osPort^ do begin
  1285.                         with portPixMap^^ do begin
  1286.                                 RowBytes := BitOr(BytesPerRow, $8000);
  1287.                                 bounds := PicRect;
  1288.                             end;
  1289.                         PortRect := PicRect;
  1290.                         RectRgn(visRgn, PicRect);
  1291.                     end;
  1292.             end;
  1293.     end;
  1294.  
  1295.  
  1296.     function GetScaleAndAngle: boolean;
  1297.         const
  1298.             AngleID = 3;
  1299.             hScaleID = 4;
  1300.             vScaleID = 5;
  1301.             NewWindowID = 9;
  1302.             BilinearID = 10;
  1303.             NearestNeighborID = 11;
  1304.         var
  1305.             mylog: DialogPtr;
  1306.             item, i: integer;
  1307.             vScaleUnchanged: boolean;
  1308.             str: str255;
  1309.     begin
  1310.         vScaleUnchanged := true;
  1311.         InitCursor;
  1312.         mylog := GetNewDialog(50, nil, pointer(-1));
  1313.         SetDReal(MyLog, AngleID, rsAngle, 2);
  1314.         SetDReal(MyLog, hScaleID, rsHScale, 2);
  1315.         SelIText(MyLog, hScaleID, 0, 32767);
  1316.         SetDReal(MyLog, vScaleID, rsVScale, 2);
  1317.         SetDialogItem(mylog, NewWindowID, ord(rsCreateNewWindow));
  1318.         SetDialogItem(mylog, BilinearID, ord(rsMethod = Bilinear));
  1319.         SetDialogItem(mylog, NearestNeighborID, ord(rsMethod = NearestNeighbor));
  1320.         OutlineButton(MyLog, ok, 16);
  1321.         repeat
  1322.             ModalDialog(nil, item);
  1323.             if item = AngleID then begin
  1324.                     rsAngle := GetDREal(MyLog, AngleID);
  1325.                     if rsAngle > 180.0 then
  1326.                         rsAngle := 180.0;
  1327.                     if rsAngle < -180.0 then
  1328.                         rsAngle := -180.0;
  1329.                 end;
  1330.             if item = hScaleID then begin
  1331.                     str := GetDString(MyLog, hScaleID);
  1332.                     rsHScale := StringToReal(str);
  1333.                     if rsHScale = BadReal then
  1334.                         rsHScale := 1.0;
  1335.                     if vScaleUnchanged then begin
  1336.                             rsVScale := rsHScale;
  1337.                             SetDString(MyLog, vScaleID, str);
  1338.                         end;
  1339.                     if rsHScale < 0.05 then
  1340.                         rsHScale := 0.05;
  1341.                 end;
  1342.             if item = vScaleID then begin
  1343.                     rsVScale := GetDReal(MyLog, vScaleID);
  1344.                     if rsVScale = BadReal then
  1345.                         rsVScale := 1.0;
  1346.                     if rsVScale < 0.05 then
  1347.                         rsVScale := 0.05;
  1348.                     vScaleUnchanged := false;
  1349.                 end;
  1350.             if item = NewWindowID then begin
  1351.                     rsCreateNewWindow := not rsCreateNewWindow;
  1352.                     SetDialogItem(mylog, NewWindowID, ord(rsCreateNewWindow));
  1353.                 end;
  1354.             if (item = BilinearID) or (item = NearestNeighborID) then begin
  1355.                     if item = BilinearID then
  1356.                         rsMethod := Bilinear;
  1357.                     if item = NearestNeighborID then
  1358.                         rsMethod := NearestNeighbor;
  1359.                     SetDialogItem(mylog, BilinearID, ord(rsMethod = Bilinear));
  1360.                     SetDialogItem(mylog, NearestNeighborID, ord(rsMethod = NearestNeighbor));
  1361.                 end;
  1362.         until (item = ok) or (item = cancel);
  1363.         DisposDialog(mylog);
  1364.         GetScaleAndAngle := item <> cancel;
  1365.     end;
  1366.  
  1367.  
  1368.     procedure ScaleAndRotate;
  1369.         const
  1370.             pi = 3.14159;
  1371.         type
  1372.             EraseType = (Erase, DontErase);
  1373.         var
  1374.             CosAngle, SinAngle, htemp, vtemp, h, v: extended;
  1375.             hloc, vloc, value, DstWidth, DstHeight, hstart, vstart, hend, vend: integer;
  1376.             hfraction, vfraction, UpperAverage, LowerAverage, AngleInRadians: extended;
  1377.             LowerLeft, LowerRight, UpperLeft, UpperRight, SaveWidth, SaveHeight: integer;
  1378.             hSrcCenter, vSrcCenter, hDstCenter, vDstCenter: integer;
  1379.             hRel, vRel, hbase, vbase, SrcWidth, SrcHeight, ExtraPixel: integer;
  1380.             SrcInfo, DstInfo, SaveInfo: InfoPtr;
  1381.             AutoSelectAll, UseNearestNeighbor, Rotate: boolean;
  1382.             MaskRect, SourceRect, DstRect: rect;
  1383.             StartTicks: LongInt;
  1384.             UseSameWindow: boolean;
  1385.  
  1386.         procedure DoInterpolatedScaling;
  1387.     {Does interpolated scaling, but no rotation, using scaled integer arithmetic.}
  1388.             const
  1389.                 CountsPerUpdate = 5;
  1390.             var
  1391.                 SrcLeft, hloc, vloc, vbase, hbase, hrel: integer;
  1392.                 LineCount, oldvloc, LastLine: integer;
  1393.                 DstLine, SrcLine1, SrcLine2: LineType;
  1394.                 MaskRect: rect;
  1395.                 v, SrcTop: extended;
  1396.                 h, hFraction, vFraction, UpperAverage, LowerAverage: LongInt;
  1397.                 scale, scale2, hscale: LongInt;
  1398.         begin
  1399.             scale := 1000;
  1400.             scale2 := scale * scale;
  1401.             hscale := round(rsHScale * scale);
  1402.             if SrcWidth >= MaxLine then
  1403.                 exit(DoInterpolatedScaling);
  1404.             LastLine := SrcInfo^.PicRect.bottom - 1;
  1405.             with SourceRect do begin
  1406.                     SrcLeft := left;
  1407.                     SrcTop := top;
  1408.                 end;
  1409.             with DstRect do begin
  1410.                     oldvloc := top;
  1411.                     LineCount := 0;
  1412.                     for vloc := top to bottom - 1 do begin
  1413.                             v := SrcTop + (vloc - top) / rsVScale;
  1414.                             vbase := trunc(v);
  1415.                             vFraction := round((v - vbase) * scale);
  1416.                             Info := SrcInfo;
  1417.                             GetLine(SrcLeft, vbase, SrcWidth, SrcLine1);
  1418.                             SrcLine1[SrcWidth] := SrcLine1[SrcWidth - 1];
  1419.                             if vbase <> LastLine then begin
  1420.                                     GetLine(SrcLeft, vbase + 1, SrcWidth, SrcLine2);
  1421.                                     SrcLine2[SrcWidth] := SrcLine2[SrcWidth - 1];
  1422.                                 end;
  1423.                             for hloc := left to right - 1 do begin
  1424.                                     hrel := hloc - left;
  1425.                                     h := hrel * scale2 div hscale;
  1426.                                     hbase := hrel * scale div hscale;
  1427.                                     hFraction := h mod scale;
  1428.                                     LowerAverage := SrcLine1[hbase] + hFraction * (SrcLine1[hbase + 1] - SrcLine1[hbase]) div scale;
  1429.                                     UpperAverage := SrcLine2[hbase] + hFraction * (SrcLine2[hbase + 1] - SrcLine2[hbase]) div scale;
  1430.                                     DstLine[hrel] := (LowerAverage + vfraction * (UpperAverage - LowerAverage) div scale);
  1431.                                 end;
  1432.                             Info := DstInfo;
  1433.                             PutLine(left, vloc, DstWidth, DstLine);
  1434.                             LineCount := LineCount + 1;
  1435.                             if LineCount >= CountsPerUpdate then begin
  1436.                                     LineCount := 0;
  1437.                                     SetRect(MaskRect, left, oldvloc, right, vloc + 1);
  1438.                                     UpdateScreen(MaskRect);
  1439.                                     oldvloc := vloc;
  1440.                                 end;
  1441.                             if CommandPeriod then begin
  1442.                                     beep;
  1443.                                     exit(DoInterpolatedScaling)
  1444.                                 end;
  1445.                         end; {for vloc:=}
  1446.                     SetRect(MaskRect, left, oldvloc, right, vloc + 1);
  1447.                     UpdateScreen(MaskRect);
  1448.                 end;
  1449.         end;
  1450.  
  1451.         procedure ScaleUsingCopyBits;
  1452.             var
  1453.                 srcPort: cGrafPtr;
  1454.                 SavePort: GrafPtr;
  1455.                 MaskRect: rect;
  1456.         begin
  1457.             with DstInfo^ do begin
  1458.                     GetPort(SavePort);
  1459.                     SetPort(GrafPtr(osPort));
  1460.                     pmForeColor(BlackIndex);
  1461.                     pmBackColor(WhiteIndex);
  1462.                     srcPort := SrcInfo^.osPort;
  1463.                     hlock(handle(srcPort^.portPixMap));
  1464.                     hlock(handle(osPort^.portPixMap));
  1465.                     CopyBits(BitMapHandle(srcPort^.portPixMap)^^, BitMapHandle(osPort^.PortPixMap)^^, SourceRect, DstRect, SrcCopy, nil);
  1466.                     hunlock(handle(srcPort^.portPixMap));
  1467.                     hunlock(handle(osPort^.PortPixMap));
  1468.                     pmForeColor(ForegroundIndex);
  1469.                     pmBackColor(BackgroundIndex);
  1470.                     SetPort(SavePort);
  1471.                 end;
  1472.             if UseSameWindow then begin
  1473.                     MaskRect := DstRect;
  1474.                     UpdateScreen(MaskRect);
  1475.                 end;
  1476.         end;
  1477.  
  1478.  
  1479.     begin
  1480.         if NotRectangular or NotInBounds then
  1481.             exit(ScaleAndRotate);
  1482.         if not (macro and not rsInteractive) then
  1483.             if not GetScaleAndAngle then
  1484.                 exit(ScaleAndRotate);
  1485.         UpdatePicWindow;
  1486.         UseSameWindow := not rsCreateNewWindow;
  1487.         if UseSameWindow then
  1488.             with info^ do
  1489.                 if NoUndo then begin
  1490.                         macro := false;
  1491.                         exit(ScaleAndRotate)
  1492.                     end;
  1493.         with info^ do
  1494.             UseNearestNeighbor := (rsMethod = NearestNeighbor) or (LutMode = ColorLut);
  1495.         DrawTools;
  1496.         AutoSelectAll := not Info^.RoiShowing;
  1497.         if AutoSelectAll then
  1498.             SelectAll(true);
  1499.         ShowWatch;
  1500.         if UseSameWindow then begin
  1501.                 SetupUndo;
  1502.                 WhatToUndo := UndoEdit;
  1503.                 SetupUndoInfoRec;
  1504.                 SrcInfo := UndoInfo;
  1505.                 DstInfo := Info;
  1506.                 if rsAngle = 0.0 then
  1507.                     DoOperation(EraseOp);
  1508.             end
  1509.         else
  1510.             SrcInfo := info;
  1511.         AngleInRadians := -((rsAngle + 270.0) / 360.0) * 2.0 * pi;
  1512.         CosAngle := cos(AngleInRadians);
  1513.         SinAngle := sin(AngleInRadians);
  1514.         with info^ do begin
  1515.                 SourceRect := RoiRect;
  1516.                 DstRect := RoiRect;
  1517.             end;
  1518.         with SourceRect do begin
  1519.                 SrcWidth := right - left;
  1520.                 SrcHeight := bottom - top;
  1521.                 hSrcCenter := left + (SrcWidth div 2);
  1522.                 vSrcCenter := top + (SrcHeight div 2);
  1523.                 DstWidth := SrcWidth;
  1524.                 DstHeight := SrcHeight;
  1525.             end;
  1526.         if UseSameWindow then
  1527.             with DstRect, info^ do begin
  1528.                     if rsHScale <> 1.0 then begin
  1529.                             DstWidth := round(SrcWidth * rsHScale);
  1530.                             SaveWidth := DstWidth;
  1531.                             left := left - (DstWidth - SrcWidth) div 2;
  1532.                             if DstWidth > PicRect.right then
  1533.                                 DstWidth := PicRect.right;
  1534.                             if left < 0 then
  1535.                                 left := 0;
  1536.                             right := left + DstWidth;
  1537.                             if DstWidth <> SaveWidth then begin
  1538.                                     SrcWidth := round(SrcWidth * (DstWidth / SaveWidth));
  1539.                                     SourceRect.left := hSrcCenter - SrcWidth div 2;
  1540.                                     SourceRect.right := SourceRect.left + SrcWidth;
  1541.                                 end;
  1542.                         end;
  1543.                     if rsVScale <> 1.0 then begin
  1544.                             DstHeight := round(SrcHeight * rsVScale);
  1545.                             SaveHeight := DstHeight;
  1546.                             top := top - (DstHeight - SrcHeight) div 2;
  1547.                             if DstHeight > PicRect.bottom then
  1548.                                 DstHeight := PicRect.bottom;
  1549.                             if top < 0 then
  1550.                                 top := 0;
  1551.                             bottom := top + DstHeight;
  1552.                             if DstHeight <> SaveHeight then begin
  1553.                                     SrcHeight := round(SrcHeight * (DstHeight / SaveHeight));
  1554.                                     SourceRect.top := vSrcCenter - SrcHeight div 2;
  1555.                                     SourceRect.bottom := SourceRect.top + SrcHeight;
  1556.                                 end;
  1557.                         end
  1558.                 end {with}
  1559.         else begin
  1560.                 DstWidth := round(SrcWidth * rsHScale);
  1561.                 DstHeight := round(SrcHeight * rsVScale);
  1562.                 if not NewPicWindow('Untitled', DstWidth, DstHeight) then begin
  1563.                         KillRoi;
  1564.                         exit(ScaleAndRotate)
  1565.                     end;
  1566.                 DstInfo := info;
  1567.                 DstRect := info^.PicRect;
  1568.             end;
  1569.         with DstRect do begin
  1570.                 hStart := left;
  1571.                 vStart := top;
  1572.                 hDstCenter := left + (DstWidth div 2);
  1573.                 vDstCenter := top + (DstHeight div 2);
  1574.             end;
  1575.         hend := hstart + DstWidth - 1;
  1576.         vend := vstart + DstHeight - 1;
  1577.         rotate := rsAngle <> 0.0;
  1578.         ShowMessage(CmdPeriodToStop);
  1579.         StartTicks := TickCount;
  1580.         if not rotate and (rsMethod = NearestNeighbor) then
  1581.             ScaleUsingCopyBits
  1582.         else if not rotate and not UseNearestNeighbor then
  1583.             DoInterpolatedScaling
  1584.         else
  1585.             for vloc := vStart to vEnd do begin
  1586.                     for hloc := hStart to hEnd do begin
  1587.                             hrel := hloc - hDstCenter;
  1588.                             vrel := vloc - vDstCenter;
  1589.                             htemp := hrel * SinAngle + vrel * CosAngle;
  1590.                             vtemp := vrel * SinAngle - hrel * CosAngle;
  1591.                             htemp := htemp / rsHScale;
  1592.                             vtemp := vtemp / rsVScale;
  1593.                             h := htemp + hSrcCenter;
  1594.                             v := vtemp + vSrcCenter;
  1595.                             info := SrcInfo;
  1596.                             if UseNearestNeighbor then
  1597.                                 value := MyGetPixel(round(h), round(v))
  1598.                             else begin {Use bilinear interpolation}
  1599.                                     hbase := trunc(h);
  1600.                                     vbase := trunc(v);
  1601.                                     hFraction := h - hbase;
  1602.                                     vFraction := v - vbase;
  1603.                                     LowerLeft := MyGetPixel(hbase, vbase);
  1604.                                     LowerRight := MyGetPixel(hbase + 1, vbase);
  1605.                                     UpperRight := MyGetPixel(hbase + 1, vbase + 1);
  1606.                                     UpperLeft := MyGetPixel(hbase, vbase + 1);
  1607.                                     UpperAverage := UpperLeft + hfraction * (UpperRight - UpperLeft);
  1608.                                     LowerAverage := LowerLeft + hfraction * (LowerRight - LowerLeft);
  1609.                                     value := round(LowerAverage + vfraction * (UpperAverage - LowerAverage));
  1610.                                 end;
  1611.                             Info := DstInfo;
  1612.                             PutPixel(hloc, vloc, value);
  1613.                         end; {for hloc:=}
  1614.                     SetRect(MaskRect, hstart, vloc, hend, vloc + 1);
  1615.                     UpdateScreen(MaskRect);
  1616.                     if CommandPeriod then begin
  1617.                             beep;
  1618.                             KillRoi;
  1619.                             exit(ScaleAndRotate)
  1620.                         end;
  1621.                 end; {for vloc:=}
  1622.         ShowTime(StartTicks, DstRect, '');
  1623.         KillRoi;
  1624.         with info^ do begin
  1625.                 changes := true;
  1626.                 if not UseSameWindow and (PixMapSize > UndoBufSize) then
  1627.                     PutWarning;
  1628.                 if SpatiallyCalibrated and (not UseSameWindow) then begin
  1629.                         RawSpatialScale := RawSpatialScale * (DstWidth / SrcWidth);
  1630.                         xSpatialScale := RawSpatialScale * ScaleMagnification;
  1631.                         PixelAspectRatio := PixelAspectRatio * rsHScale / rsVScale;
  1632.                         ySpatialScale := xSpatialScale / PixelAspectRatio;
  1633.                     end;
  1634.             end;
  1635.         if not UseSameWindow and AutoSelectAll then begin
  1636.                 SaveInfo := Info;
  1637.                 Info := SrcInfo;
  1638.                 KillRoi;
  1639.                 Info := SaveInfo;
  1640.             end;
  1641.         if UseSameWindow then
  1642.             with NoInfo^ do begin
  1643.                     roiType := RectRoi;
  1644.                     RoiRect := DstRect;
  1645.                     RectRgn(roiRgn, DstRect);
  1646.                 end;
  1647.     end;
  1648.  
  1649.  
  1650.     procedure ActivateWindow;
  1651.     begin
  1652.         with info^ do begin
  1653.                 IsInsertionPoint := false;
  1654.                 WhatToUndo := NothingToUndo;
  1655.                 UndoFromClip := false;
  1656.                 DrawLabels('', '', '');
  1657.                 MouseState := NotInRoi;
  1658.                 RoiUpdateTime := 0;
  1659.                 pmForeColor(ForegroundIndex);
  1660.                 pmBackColor(BackgroundIndex);
  1661.                 nCoordinates := 0;
  1662.                 ShowRoi;
  1663.             end;
  1664.     end;
  1665.  
  1666.  
  1667.     procedure UpdateResultsWindow;
  1668.     begin
  1669.         SetPort(ResultsWindow);
  1670.         DrawControls(ResultsWindow);
  1671.         DrawGrowIcon(ResultsWindow);
  1672.         if ResultsWindow = FrontWindow then begin
  1673.                 TEUpdate(ResultsWindow^.visRgn^^.rgnBBox, ListTE);
  1674.                 ShowControl(hScrollBar);
  1675.                 ShowControl(vScrollBar);
  1676.             end
  1677.         else begin
  1678.                 HideControl(hScrollBar);
  1679.                 HideControl(vScrollBar);
  1680.             end;
  1681.     end;
  1682.  
  1683.  
  1684.     procedure ScrollText;
  1685.         var
  1686.             value: INTEGER;
  1687.     begin
  1688.         with ListTE^^ do
  1689.             TEScroll((viewRect.left - destRect.left) - GetCtlValue(hScrollBar), (viewRect.top - destRect.top) - (GetCtlValue(vScrollBar) * LineHeight), ListTE);
  1690.     end;
  1691.  
  1692.  
  1693.     procedure UpdateScrollBars;
  1694.         var
  1695.             vMax, vValue, hMax, hValue: integer;
  1696.     begin
  1697.         with ListTE^^, ListTE^^.viewRect do begin
  1698.                 vListPageSize := (bottom - top) div LineHeight;
  1699.                 hListPageSize := right - left;
  1700.                 vMax := nLines - vListPageSize;
  1701.                 hMax := (nListColumns + 1) * (FieldWidth + 1) * 6 - hListPageSize;
  1702.                 vValue := (top - destRect.top) div LineHeight;
  1703.                 hValue := left - destRect.left
  1704.             end;
  1705.         if vMax < 0 then
  1706.             vMax := 0;
  1707.         if vValue < 0 then
  1708.             vValue := 0;
  1709.         if hMax < 0 then
  1710.             hMax := 0;
  1711.         if vValue < 0 then
  1712.             vValue := 0;
  1713.         SetCtlMax(vScrollBar, vMax);
  1714.         SetCtlValue(vScrollBar, vValue);
  1715.         SetCtlMax(hScrollBar, hMax);
  1716.         SetCtlValue(hScrollBar, hValue);
  1717. {ShowMessage(concat('nListColumns= ', Long2str(nListColumns), cr, 'hListPageSize= ', long2str(hListPageSize)));}
  1718.     end;
  1719.  
  1720.  
  1721.     procedure InitTextEdit (font, size: integer);
  1722.         var
  1723.             dRect, vRect: rect;
  1724.     begin
  1725.         SetPort(ResultsWindow);
  1726.         with ResultsWindow^.portRect do
  1727.             SetRect(dRect, left + 4, top, right - 18, bottom - 24);
  1728.         vRect := dRect;
  1729.         ListTE := TENew(dRect, vRect);
  1730.         with ListTE^^ do begin
  1731.                 TxFont := font;
  1732.                 TxSize := size;
  1733.                 crOnly := -1;
  1734.             end;
  1735.         if TextBufSize > 0 then begin
  1736.                 TESetText(ptr(TextBufP), TextBufSize, ListTe);
  1737.                 TECalText(ListTE);
  1738.             end;
  1739.         UpdateScrollBars;
  1740.     end;
  1741.  
  1742.  
  1743.     procedure ScrAction (theCtl: ControlHandle; partCode: integer);
  1744.         var
  1745.             bInc, pInc, delta: integer;
  1746.     begin
  1747.         if theCtl = vScrollBar then begin
  1748.                 bInc := 1;
  1749.                 pInc := vListPageSize
  1750.             end
  1751.         else begin
  1752.                 bInc := 4;
  1753.                 pInc := hListPageSize
  1754.             end;
  1755.         case partCode of
  1756.             inUpButton: 
  1757.                 delta := -bInc;
  1758.             inDownButton: 
  1759.                 delta := bInc;
  1760.             inPageUp: 
  1761.                 delta := -pInc;
  1762.             inPageDown: 
  1763.                 delta := pInc;
  1764.             otherwise
  1765.                 exit(ScrAction);
  1766.         end;
  1767.         SetCtlValue(theCtl, GetCtlValue(theCtl) + delta);
  1768.         ScrollText;
  1769.     end;
  1770.  
  1771.  
  1772.     procedure DoMouseDownInResults (loc: point);
  1773.         var
  1774.             theCtl: ControlHandle;
  1775.             cValue: integer;
  1776.     begin
  1777.         SelectWindow(ResultsWindow);
  1778.         SetPort(ResultsWindow);
  1779.         GlobalToLocal(loc);
  1780.         case FindControl(loc, ResultsWindow, theCtl) of
  1781.             inUpButton, inDownButton, inPageUp, inPageDown: 
  1782.                 if TrackControl(theCtl, loc, @ScrAction) <> 0 then
  1783.                     ;
  1784.             inThumb: 
  1785.                 if TrackControl(theCtl, loc, nil) <> 0 then
  1786.                     ScrollText;
  1787.             otherwise
  1788.         end;
  1789.     end;
  1790.  
  1791.  
  1792.     procedure AppendResults;
  1793.         var
  1794.             vMax: integer;
  1795.     begin
  1796.         if ResultsWindow <> nil then
  1797.             with ListTE^^ do begin
  1798.                     if teLength > 32000 then
  1799.                         exit(AppendResults);
  1800.                     CopyResultsToBuffer(mCount, mCount, true);
  1801.                     TESetSelect(teLength, teLength, ListTE);
  1802.                     TEInsert(ptr(TextBufP), TextBufSize, ListTE);
  1803.                     with ListTE^^ do begin
  1804.                             vListPageSize := (viewRect.bottom - viewRect.top) div LineHeight;
  1805.                             vMax := nLines - vListPageSize;
  1806.                         end;
  1807.                     if vMax < 0 then
  1808.                         vMax := 0;
  1809.                     SetCtlMax(vScrollBar, vMax);
  1810.                     SetCtlValue(vScrollBar, GetCtlMax(vScrollBar));
  1811.                     ScrollText;
  1812.                 end;
  1813.     end;
  1814.  
  1815.  
  1816.     procedure DeleteLines (first, last: integer);
  1817.     begin
  1818.         if ResultsWindow <> nil then
  1819.             with ListTE^^ do begin
  1820.                     first := first + 2; {Accounts for 2 line header}
  1821.                     last := last + 2;
  1822.                     if (first = 3) and (last = 3) then
  1823.                         first := 1; {if deleting first line then delete header too}
  1824.                     if (first < 1) or (first > nLines) or (last < 1) or (last > nLines) then
  1825.                         exit(DeleteLines);
  1826.                     TESetSelect(LineStarts[first - 1], LineStarts[last], ListTE);
  1827.                     TEDelete(ListTE);
  1828.                 end;
  1829.     end;
  1830.  
  1831.  
  1832.     procedure UpdateList;
  1833.     begin
  1834.         if (ResultsWindow <> nil) and (mCount > 0) then
  1835.             with ListTE^^ do begin
  1836.                     CopyResultsToBuffer(1, mCount, true);
  1837.                     TESetSelect(0, teLength, ListTE);
  1838.                     TEDelete(ListTE);
  1839.                     TEInsert(ptr(TextBufP), TextBufSize, ListTE);
  1840.                     UpdateScrollBars;
  1841.                 end;
  1842.     end;
  1843.  
  1844.  
  1845.     procedure SelectSlice (i: integer);
  1846.     begin
  1847.         with info^, info^.StackInfo^ do
  1848.             if i <= nSlices then begin
  1849.                     hunlock(PicBaseHandle);
  1850.                     PicBaseHandle := PicBaseH[i];
  1851.                     hlock(PicBaseHandle);
  1852.                     PicBaseAddr := StripAddress(PicBaseHandle^);
  1853.                     osPort^.PortPixMap^^.BaseAddr := PicBaseAddr;
  1854.                 end;
  1855.     end;
  1856.  
  1857.  
  1858.     procedure ShowMeter;
  1859.         const
  1860.             MeterWidth = 264;
  1861.             MeterHeight = 64;
  1862.         var
  1863.             trect: rect;
  1864.             hloc, vloc: integer;
  1865.     begin
  1866.         hloc := ScreenWidth div 2 - MeterWidth div 2;
  1867.         vloc := ScreenHeight div 4 - MeterHeight div 2;
  1868.         SetRect(trect, hloc, vloc, hloc + MeterWidth, vloc + MeterHeight);
  1869.         MeterWindow := NewWindow(nil, trect, '', true, dBoxProc, nil, false, 0);
  1870.         BringToFront(MeterWindow);
  1871.     end;
  1872.  
  1873.  
  1874.     procedure UpdateMeter; {(percentdone: integer; str: str255)}
  1875.         const
  1876.             left = 16;
  1877.             top = 28;
  1878.             right = 248;
  1879.             bottom = 44;
  1880.         var
  1881.             r: rect;
  1882.     begin
  1883.         if MeterWindow = nil then
  1884.             ShowMeter;
  1885.         if (percentdone >= 0) then begin
  1886.                 SetPort(MeterWindow);
  1887.                 TextFont(SystemFont);
  1888.                 TextSize(12);
  1889.                 TextMode(SrcCopy);
  1890.                 MoveTo(left, top div 2);
  1891.                 DrawString(str);
  1892.                 SetRect(r, left + StringWidth(str), 0, right, top);
  1893.                 EraseRect(r);
  1894.                 SetRect(r, left, top, right, bottom);
  1895.                 FrameRect(r);
  1896.                 SetRect(r, left + 1, top + 1, left + (percentdone * (right - left)) div 100 - 1, bottom - 1);
  1897.                 FillRect(r, gray);
  1898.             end     {then}
  1899.         else begin
  1900.                 DisposeWindow(MeterWindow);
  1901.                 MeterWindow := nil;
  1902.             end;     {else}
  1903.     end;
  1904.  
  1905.  
  1906.     function RgnNotTooBig; {(Rgn1, Rgn2: RgnHandle): boolean}
  1907.     begin
  1908.         RgnNotTooBig := GetHandleSize(handle(Rgn1)) + GetHandleSize(handle(Rgn2)) < 30000
  1909.     end;
  1910.  
  1911.  
  1912.     procedure ComputeLength (FindingPerimeterLength: boolean);
  1913.         var
  1914.             i: integer;
  1915.             xtemp, ytemp: LongInt;
  1916.             xt, yt: extended;
  1917.     begin
  1918.         with info^ do begin
  1919.                 uLength := 0.0;
  1920.                 cLength := 0.0;
  1921.                 for i := 2 to nCoordinates do begin
  1922.                         xtemp := xCoordinates^[i] - xCoordinates^[i - 1];
  1923.                         ytemp := yCoordinates^[i] - yCoordinates^[i - 1];
  1924.                         uLength := uLength + sqrt(xtemp * xtemp + ytemp * ytemp);
  1925.                         if SpatiallyCalibrated then begin
  1926.                                 xt := xtemp / xSpatialScale;
  1927.                                 yt := ytemp / ySpatialScale;
  1928.                                 cLength := cLength + sqrt(xt * xt + yt * yt);
  1929.                             end;
  1930.                     end;
  1931.                 if FindingPerimeterLength then begin
  1932.                         xtemp := xCoordinates^[1] - xCoordinates^[nCoordinates];
  1933.                         ytemp := yCoordinates^[1] - yCoordinates^[nCoordinates];
  1934.                         uLength := uLength + sqrt(xtemp * xtemp + ytemp * ytemp);
  1935.                         if SpatiallyCalibrated then begin
  1936.                                 xt := xtemp / xSpatialScale;
  1937.                                 yt := ytemp / ySpatialScale;
  1938.                                 cLength := cLength + sqrt(xt * xt + yt * yt);
  1939.                             end;
  1940.                     end;
  1941.                 if not SpatiallyCalibrated then
  1942.                     cLength := uLength;
  1943.                 uLength := uLength / magnification;
  1944.                 cLength := cLength / magnification;
  1945.             end; {with}
  1946.     end;
  1947.  
  1948.  
  1949.     procedure MakeOutline;
  1950. {Creates a "marching ants" outline from a list of offscreen XY coordinates.}
  1951.         var
  1952.             i: integer;
  1953.             TempRgn: RgnHandle;
  1954.             spt, pt: point;
  1955.     begin
  1956.         with Info^ do begin
  1957.                 if SelectionMode <> NewSelection then
  1958.                     TempRgn := NewRgn;
  1959.                 SetPort(wptr);
  1960.                 PenNormal;
  1961.                 OpenRgn;
  1962.                 spt.h := xCoordinates^[1];
  1963.                 spt.v := yCoordinates^[1];
  1964.                 MoveTo(spt.h, spt.v);
  1965.                 for i := 2 to nCoordinates do begin
  1966.                         pt.h := xCoordinates^[i];
  1967.                         pt.v := yCoordinates^[i];
  1968.                         LineTo(pt.h, pt.v);
  1969.                     end;
  1970.                 LineTo(spt.h, spt.v);
  1971.                 case SelectionMode of
  1972.                     NewSelection:  begin
  1973.                             CloseRgn(roiRgn);
  1974.                             ComputeLength(true);
  1975.                         end;
  1976.                     AddSelection:  begin
  1977.                             CloseRgn(TempRgn);
  1978.                             if RgnNotTooBig(roiRgn, TempRgn) then
  1979.                                 UnionRgn(roiRgn, TempRgn, roiRgn);
  1980.                             uLength := 0.0;
  1981.                             cLength := 0.0;
  1982.                         end;
  1983.                     SubSelection:  begin
  1984.                             CloseRgn(TempRgn);
  1985.                             if RgnNotTooBig(roiRgn, TempRgn) then
  1986.                                 DiffRgn(roiRgn, TempRgn, roiRgn);
  1987.                             uLength := 0.0;
  1988.                             cLength := 0.0;
  1989.                         end;
  1990.                 end;
  1991.                 RoiShowing := true;
  1992.                 roiType := RgnRoi;
  1993.                 RoiRect := roiRgn^^.rgnBBox;
  1994.                 UpdatePicWindow;
  1995.             end;
  1996.         if SelectionMode <> NewSelection then
  1997.             DisposeRgn(TempRgn);
  1998.         WhatToUndo := NothingToUndo;
  1999.         measuring := false;
  2000.     end;
  2001.  
  2002.  
  2003. end.