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

  1. unit Analysis;
  2.  
  3. {Analysis routines used by the Image program}
  4.  
  5. interface
  6.  
  7.     uses
  8.         QuickDraw, Palettes, PrintTraps, globals, Utilities, LeastSquares, Graphics, file1, file2, Ellipse, camera, Lut;
  9.  
  10.  
  11.  
  12.     procedure DoHistogram;
  13.     procedure GetRectHistogram;
  14.     function SetupMask: boolean;
  15.     procedure GetNonRectHistogram;
  16.     procedure ShowContinuousHistogram;
  17.     procedure ComputeResults;
  18.     procedure FindThresholdingMode;
  19.     procedure Measure;
  20.     procedure ShowLineWidth;
  21.     procedure UpdateRoiLineWidth;
  22.     procedure DoProfilePlotOptions;
  23.     procedure ShowResults;
  24.     procedure PlotDensityProfile;
  25.     procedure SetScale;
  26.     procedure Calibrate;
  27.     procedure ResetCounter;
  28.     procedure DoMeasurementOptions;
  29.     procedure DoPoints (event: EventRecord);
  30.     procedure FindAngle (event: EventRecord);
  31.     procedure SaveBlankField;
  32.     procedure UndoLastMeasurement (DisplayResults: boolean);
  33.     procedure MarkSelection (count: integer);
  34.     procedure AutoOutline (start: point);
  35.     procedure RedoMeasurement;
  36.     procedure DeleteMeasurement;
  37.     procedure AnalyzeParticles;
  38.  
  39.  
  40. implementation
  41.  
  42.     var
  43.         WandMode: (LUTMode, GrayMapMode, BinaryMode);
  44.         GrayMapThreshold: integer;
  45.         InfoForRedirect: InfoPtr;
  46.  
  47.  
  48. {$PUSH}
  49. {$D-}
  50.  
  51.  
  52.     procedure DoHistogramOfLine (data: ptr; var histogram: HistogramType; width: LongInt);
  53. {}
  54. {VAR}
  55. {  line:LinePtr;}
  56. {  i,value:integer;}
  57. {BEGIN}
  58. {  line:=LinePtr(data);}
  59. {  FOR i:=0 TO width-1 DO BEGIN}
  60. {    value:=line^[i];}
  61. {    histogram[value]:=histogram[value]+1;}
  62. {  END;}
  63. {}
  64.     {a0=data}
  65.     {a1=histogram}
  66.     {d0=width}
  67.     {d1=pixel value}
  68.     inline
  69.         $4E56, $0000, {  link a6,#0}
  70.         $48E7, $C0C0, {  movem.l a0-a1/d0-d1,-(sp)}
  71.         $206E, $000C, {  move.l 12(a6),a0}
  72.         $226E, $0008, {  move.l 8(a6),a1}
  73.         $202E, $0004, {  move.l 4(a6),d0}
  74.         $5380,       {  subq.l #1,d0}
  75.         $4281,       {L clr.l d1}
  76.         $1218,       {  move.b (a0)+,d1}
  77.         $E541,       {  asl.w #2,d1}
  78.         $52B1, $1800, {  addq.l #1,0(a1,d1.l)}
  79.         $51C8, $FFF4, {  dbra d0,L}
  80.         $4CDF, $0303, {  movem.l (sp)+,a0-a1/d0-d1}
  81.         $4E5E,       {  unlk a6}
  82.         $DEFC, $000C; {  add.w #12,sp}
  83. {END;}
  84.  
  85.  
  86.     procedure GetRectHistogram;
  87.         var
  88.             width, i, NumberOfLines: integer;
  89.             offset: LongInt;
  90.             p: ptr;
  91.     begin
  92.         if TooWide then
  93.             exit(GetRectHistogram);
  94.         if RedirectSampling then
  95.             PutMessage('Redirected sampling requires a nonrectangular selection.');
  96.         ShowWatch;
  97.         for i := 0 to 255 do
  98.             Histogram[i] := 0;
  99.         with info^.RoiRect, info^ do begin
  100.                 offset := LongInt(top) * BytesPerRow + left;
  101.                 p := ptr(ord4(PicBaseAddr) + offset);
  102.                 width := right - left;
  103.                 NumberOfLines := bottom - top;
  104.             end;
  105.         if width > 0 then
  106.             for i := 1 to NumberOfLines do begin
  107.                     DoHistogramOfLine(p, histogram, width);
  108.                     p := ptr(ord4(p) + info^.BytesPerRow);
  109.                 end
  110.     end;
  111.  
  112.  
  113.     function SetupMask: boolean;
  114. {Creates a mask in the undo buffer for operating}
  115. {on non-rectangular selections .}
  116.         var
  117.             tPort: GrafPtr;
  118.             SaveInfo: InfoPtr;
  119.     begin
  120.         if NoUndo then begin
  121.                 SetupMask := false;
  122.                 exit(SetupMask)
  123.             end;
  124.         SetupUndoInfoRec;
  125.         SaveInfo := Info;
  126.         Info := UndoInfo;
  127.         GetPort(tPort);
  128.         with Info^ do begin
  129.                 SetPort(GrafPtr(osPort));
  130.                 pmForeColor(BlackIndex);
  131.                 pmBackColor(WhiteIndex);
  132.                 PenNormal;
  133.                 EraseRect(RoiRect);
  134.                 PaintRgn(roiRgn);
  135.             end;
  136.         SetPort(tPort);
  137.         Info := SaveInfo;
  138.         SetupMask := true;
  139.     end;
  140.  
  141.  
  142.     procedure SetupRedirectedSampling;
  143.         var
  144.             info1, info2: InfoPtr;
  145.     begin
  146.         InfoForRedirect := nil;
  147.         if nPics <> 2 then begin
  148.                 PutMessage('There must be exactly two image windows open to do redirected sampling.');
  149.                 AnalyzingParticles := false;
  150.                 exit(SetupRedirectedSampling);
  151.             end;
  152.         Info1 := pointer(WindowPeek(PicWindow[1])^.RefCon);
  153.         Info2 := pointer(WindowPeek(PicWindow[2])^.RefCon);
  154.         if not EqualRect(info1^.PicRect, info2^.PicRect) then begin
  155.                 PutMessage('The image windows must be exactly the same size to do redirected sampling.');
  156.                 AnalyzingParticles := false;
  157.                 exit(SetupRedirectedSampling);
  158.             end;
  159.         if info = info1 then
  160.             InfoForRedirect := info2
  161.         else
  162.             InfoForRedirect := info1;
  163.     end;
  164.  
  165.  
  166.     procedure GetNonRectHistogram;
  167.         var
  168.             MaskLine, DataLine: LineType;
  169.             width, i, vloc: integer;
  170.             sum, sum2, count, OverFlows: LongInt;
  171.             SaveInfo: InfoPtr;
  172.             value: LongInt;
  173.             trect: rect;
  174.     begin
  175.         if TooWide then
  176.             exit(GetNonRectHistogram);
  177.         ShowWatch;
  178.         if RedirectSampling then
  179.             SetupRedirectedSampling
  180.         else
  181.             InfoForRedirect := nil;
  182.         if not SetupMask then
  183.             beep;
  184.         SaveInfo := Info;
  185.         for i := 0 to 255 do
  186.             Histogram[i] := 0;
  187.         if FitEllipse then
  188.             ResetSums;
  189.         trect := info^.RoiRect;
  190.         with trect do begin
  191.                 width := right - left;
  192.                 for vloc := top to bottom - 1 do begin
  193.                         if InfoForRedirect <> nil then
  194.                             Info := InfoForRedirect
  195.                         else
  196.                             Info := SaveInfo;
  197.                         GetLine(left, vloc, width, DataLine);
  198.                         Info := UndoInfo;
  199.                         GetLine(left, vloc, width, MaskLine);
  200.                         if FitEllipse then
  201.                             ComputeSums(vloc - top, width, MaskLine);
  202.                         for i := 0 to width - 1 do
  203.                             if MaskLine[i] = BlackIndex then begin
  204.                                     value := DataLine[i];
  205.                                     histogram[value] := histogram[value] + 1;
  206.                                 end;
  207.                     end;
  208.             end;
  209.         Info := SaveInfo;
  210.         if not AnalyzingParticles then
  211.             SetupUndo; {Needed for drawing "marching ants".}
  212.     end;
  213.  
  214.  
  215.     procedure ComputeResults;
  216.         var
  217.             MaxCount, icount, isum, n: LongInt;
  218.             i: integer;
  219.             sum, sum2, ri, rcount, UncalibratedMean, tSD, rmode, xc, yc: extended;
  220.             Major, Minor, EllipseAngle, hcenter, vcenter, calValue: extended;
  221.             MinCalibratedValue, MaxCalibratedValue: extended;
  222.             IgnoreThresholding: boolean;
  223.     begin
  224.         with info^, results do begin
  225.                 case ThresholdingMode of
  226.                     DensitySlice: 
  227.                         MinIndex := SliceStart;
  228.                     GrayMapThresholding: 
  229.                         MinIndex := GrayMapThreshold;
  230.                     BinaryImage: 
  231.                         MinIndex := BlackIndex;
  232.                     NoThresholding: 
  233.                         MinIndex := 0;
  234.                 end;
  235.                 IgnoreThresholding := RedirectSampling or (IncludeHoles and (AnalyzingParticles or (CurrentTool = Wand)));
  236.                 if IgnoreThresholding then
  237.                     MinIndex := 0;
  238.                 while (histogram[MinIndex] = 0) and (MinIndex < 255) do
  239.                     MinIndex := MinIndex + 1;
  240.                 case ThresholdingMode of
  241.                     DensitySlice: 
  242.                         MaxIndex := SliceEnd;
  243.                     GrayMapThresholding: 
  244.                         MaxIndex := 255;
  245.                     BinaryImage: 
  246.                         MaxIndex := BlackIndex;
  247.                     NoThresholding: 
  248.                         MaxIndex := 255;
  249.                 end;
  250.                 if IgnoreThresholding then
  251.                     MaxIndex := 255;
  252.                 while (histogram[MaxIndex] = 0) and (MaxIndex > 0) do
  253.                     MaxIndex := MaxIndex - 1;
  254.                 MaxCount := 0;
  255.                 sum := 0.0;
  256.                 isum := 0;
  257.                 sum2 := 0.0;
  258.                 n := 0;
  259.                 minCalibratedValue := 10e100;
  260.                 maxCalibratedValue := -10e100;
  261.                 for i := MinIndex to MaxIndex do begin
  262.                         calValue := cvalue[i];
  263.                         icount := histogram[i];
  264.                         rcount := icount;
  265.                         sum := sum + rcount * calValue;
  266.                         isum := isum + icount * i;
  267.                         ri := i;
  268.                         sum2 := sum2 + sqr(calValue) * rcount;
  269.                         n := n + icount;
  270.                         if icount > MaxCount then begin
  271.                                 MaxCount := icount;
  272.                                 rmode := cvalue[i];
  273.                                 imode := i
  274.                             end;
  275.                         if calValue < minCalibratedValue then
  276.                             minCalibratedValue := calValue;
  277.                         if calValue > maxCalibratedValue then
  278.                             maxCalibratedValue := calValue;
  279.                     end;
  280.                 if ContinuousHistoGram then
  281.                     exit(ComputeResults);
  282.                 if n > 0 then begin
  283.                         tmean := sum / n;
  284.                         UncalibratedMean := isum / n
  285.                     end
  286.                 else begin
  287.                         tmean := 0.0;
  288.                         UncalibratedMean := 0.0
  289.                     end;
  290.                 imean := round(UncalibratedMean);
  291.                 IncrementCounter;
  292.                 mean^[mCount] := tmean;
  293.                 mMin^[mCount] := minCalibratedValue;
  294.                 mMax^[mCount] := maxCalibratedValue;
  295.                 if mCount <= MaxStandards then
  296.                     umean[mCount] := UncalibratedMean;
  297.                 if (n > 0) and (tmean > 0.0) then begin
  298.                         rcount := n;
  299.                         tSD := (rcount * Sum2 - sqr(sum)) / rcount;
  300.                         if tSD > 0.0 then
  301.                             tSD := sqrt(tSD / (rcount - 1.0))
  302.                         else
  303.                             tSD := 0.0
  304.                     end
  305.                 else
  306.                     tSD := 0.0;
  307.                 sd^[mCount] := tSD;
  308.                 with info^.RoiRect do begin
  309.                         xc := left + (right - left) / 2;
  310.                         yc := top + (bottom - top) / 2;
  311.                         if InvertYCoordinates then
  312.                             yc := PicRect.bottom - yc;
  313.                         if SpatiallyCalibrated then begin
  314.                                 xc := xc / xSpatialScale;
  315.                                 yc := yc / ySpatialScale;
  316.                             end;
  317.                         xcenter^[mCount] := xc;
  318.                         ycenter^[mCount] := yc;
  319.                     end;
  320.                 PixelCount^[mCount] := n;
  321.                 with RoiRect do
  322.                     case RoiType of
  323.                         RectRoi:  begin
  324.                                 uLength := ((right - left) + (bottom - top)) * 2.0;
  325.                                 cLength := uLength;
  326.                                 if SpatiallyCalibrated then
  327.                                     cLength := ((right - left) / xSpatialScale + (bottom - top) / ySpatialScale) * 2.0;
  328.                             end;
  329.                         OvalRoi:  begin
  330.                                 uLength := pi * ((right - left) + (bottom - top)) / 2.0;
  331.                                 cLength := uLength;
  332.                                 if SpatiallyCalibrated then
  333.                                     cLength := pi * ((right - left) / xSpatialScale + (bottom - top) / ySpatialScale) / 2.0;
  334.                             end;
  335.                         LineRoi, SegLineRoi, FreeLineRoi:  begin
  336.                                 if RoiType = LineRoi then
  337.                                     GetLoiLength;
  338.                                 nLengths := nLengths + 1;
  339.                             end;
  340.                         otherwise
  341.                     end;
  342.                 if SpatiallyCalibrated then
  343.                     plength^[mCount] := cLength
  344.                 else
  345.                     plength^[mcount] := uLength;
  346.                 if SpatiallyCalibrated then
  347.                     mArea^[mCount] := n / (xSpatialScale * ySpatialScale)
  348.                 else
  349.                     mArea^[mCount] := n;
  350.                 mode^[mCount] := rmode;
  351.                 if FitEllipse and ((RoiType = RgnRoi) or (RoiType = LineRoi) or (RoiType = FreeLineRoi) or (RoiType = SegLineRoi)) then begin
  352.                         GetEllipseParam(Major, Minor, EllipseAngle, xc, yc);
  353.                         if InvertYCoordinates then
  354.                             yc := PicRect.bottom - yc;
  355.                         if SpatiallyCalibrated then begin
  356.                                 Major := Major / xSpatialScale;
  357.                                 Minor := Minor / ySpatialScale;
  358.                                 xc := xc / xSpatialScale;
  359.                                 yc := yc / ySpatialScale;
  360.                             end;
  361.                         MajorAxis^[mCount] := Major * 2.0;
  362.                         MinorAxis^[mCount] := Minor * 2.0;
  363.                         orientation^[mCount] := EllipseAngle;
  364.                         xcenter^[mCount] := xc;
  365.                         ycenter^[mCount] := yc;
  366.                     end
  367.                 else if RoiType = OvalRoi then
  368.                     with RoiRect do begin
  369.                             Major := right - left;
  370.                             Minor := bottom - top;
  371.                             if SpatiallyCalibrated then begin
  372.                                     Major := Major / xSpatialScale;
  373.                                     Minor := Minor / ySpatialScale;
  374.                                 end;
  375.                             MajorAxis^[mCount] := Major;
  376.                             MinorAxis^[mCount] := Minor;
  377.                             orientation^[mCount] := 0.0;
  378.                         end
  379.                 else begin
  380.                         MajorAxis^[mCount] := 0.0;
  381.                         MinorAxis^[mCount] := 0.0;
  382.                         orientation^[mCount] := 0.0;
  383.                     end;
  384.             end; {with}
  385.         measuring := true;
  386.         ValuesMessage := '';
  387.     end;
  388.  
  389.  
  390.     procedure FindThresholdingMode;
  391.     begin
  392.         with info^ do begin
  393.                 if DensitySlicing then
  394.                     ThresholdingMode := DensitySlice
  395.                 else if thresholding then begin
  396.                         ThresholdingMode := GrayMapThresholding;
  397.                         GrayMapThreshold := ColorStart;
  398.                     end
  399.                 else if BinaryPic then
  400.                     ThresholdingMode := BinaryImage
  401.                 else
  402.                     ThresholdingMode := NoThresholding;
  403.             end;
  404.     end;
  405.  
  406.  
  407.     procedure Measure;
  408.         var
  409.             AutoSelectAll: boolean;
  410.             SaveN: integer;
  411.     begin
  412.         if NotInBounds then
  413.             exit(Measure);
  414.         with info^ do begin
  415.                 FindThresholdingMode;
  416.                 if ThresholdingMode = BinaryImage then
  417.                     ThresholdingMode := NoThresholding;
  418.                 StopDigitizing;
  419.                 AutoSelectAll := not RoiShowing;
  420.                 if AutoSelectAll then
  421.                     SelectAll(false);
  422.                 if RoiType = RectRoi then
  423.                     GetRectHistogram
  424.                 else
  425.                     GetNonRectHistogram;
  426.                 if MeasurementToRedo > 0 then begin
  427.                         SaveN := mCount;
  428.                         mCount := MeasurementToRedo - 1;
  429.                         ComputeResults;
  430.                         ShowValues;
  431.                         mCount := SaveN;
  432.                         MeasurementToRedo := 0;
  433.                         UpdateList;
  434.                     end
  435.                 else begin
  436.                         ComputeResults;
  437.                         ShowValues;
  438.                         AppendResults;
  439.                         if RoiType = LineRoi then
  440.                             if nLengths = 1 then
  441.                                 if not (LengthM in Measurements) then
  442.                                     UpdateList;
  443.                     end;
  444.                 RoiShowing := true;
  445.                 WhatToUndo := UndoMeasurement;
  446.                 if AutoSelectAll then
  447.                     KillRoi;
  448.                 UpdateScreen(OldRoiRect);
  449.             end;
  450.     end;
  451.  
  452.  
  453.     procedure ShowHistogram;
  454.         var
  455.             htop: integer;
  456.             tport: GrafPtr;
  457.             hrect, prect, srect: rect;
  458.     begin
  459.         GetPort(tPort);
  460.         if HistoWindow = nil then begin
  461.                 htop := ScreenHeight - hheight - 10;
  462.                 SetRect(HistoRect, hleft, htop, hleft + hwidth, htop + hheight);
  463.                 HistoWindow := NewWindow(nil, HistoRect, 'Histogram', true, NoGrowDocProc, nil, true, 0);
  464.                 WindowPeek(HistoWindow)^.WindowKind := HistoKind;
  465.             end;
  466.         SelectWindow(HistoWindow);
  467.         SetPort(HistoWindow);
  468.         InvalRect(HistoWindow^.PortRect);
  469.         SetPort(tPort);
  470.     end;
  471.  
  472.  
  473.     procedure ShowContinuousHistogram;
  474.         const
  475.             skip = 10;
  476.         var
  477.             i, NumberOfLines: integer;
  478.             offset: LongInt;
  479.             p: ptr;
  480.     begin
  481.         for i := 0 to 255 do
  482.             Histogram[i] := 0;
  483.         p := ptr(ptr(DTSlotBase));
  484.         NumberOfLines := ((qcHeight) div skip) - 1;
  485.         offset := qcRowBytes * skip;
  486.         for i := 1 to NumberOfLines do begin
  487.                 DoHistogramOfLine(p, histogram, qcWidth);
  488.                 p := ptr(ord4(p) + offset);
  489.             end;
  490.         ThresholdingMode := NoThresholding;
  491.         HistogramSliceStart := 0;
  492.         HistogramSliceEnd := 255;
  493.         ComputeResults;
  494.         ShowHistogram;
  495.     end;
  496.  
  497.  
  498.     procedure DoHistogram;
  499.         var
  500.             AutoSelectAll: boolean;
  501.     begin
  502.         if NotInBounds then
  503.             exit(DoHistogram);
  504.         if digitizing then begin
  505.                 if ContinuousHistogram then
  506.                     ContinuousHistogram := false
  507.                 else begin
  508.                         ContinuousHistogram := true;
  509.                         if info <> NoInfo then
  510.                             with info^ do begin
  511.                                     RoiType := NoRoi;
  512.                                     RoiRect := SrcRect;
  513.                                 end;
  514.                     end;
  515.                 exit(DoHistogram)
  516.             end;
  517.         AutoSelectAll := not info^.RoiShowing;
  518.         if AutoSelectAll then
  519.             SelectAll(false);
  520.         if info^.RoiType = RectRoi then
  521.             GetRectHistogram
  522.         else
  523.             GetNonRectHistogram;
  524.         ThresholdingMode := NoThresholding;
  525.         ComputeResults;
  526.         ShowCount := false;
  527.         ShowValues;
  528.         ShowCount := true;
  529.         FindThresholdingMode;
  530.         case ThresholdingMode of
  531.             DensitySlice:  begin
  532.                     HistogramSliceStart := SliceStart;
  533.                     HistogramSliceEnd := SliceEnd;
  534.                 end;
  535.             GrayMapThresholding:  begin
  536.                     HistogramSliceStart := GrayMapThreshold;
  537.                     HistogramSliceEnd := 255;
  538.                 end;
  539.             BinaryImage, NoThresholding:  begin
  540.                     HistogramSliceStart := 0;
  541.                     HistogramSliceEnd := 255;
  542.                 end;
  543.         end;
  544.         ShowHistogram;
  545.         UndoLastMeasurement(false);
  546.         WhatToUndo := NothingToUndo;
  547.         if AutoSelectAll then
  548.             KillRoi;
  549.     end;
  550.  
  551.  
  552. {$POP}
  553.  
  554.     procedure PlotDensityProfile;
  555.         var
  556.             hloc, vloc, value, width, height, i: integer;
  557.             sum: array[0..MaxLine] of LongInt;
  558.             start, p1, p2: point;
  559.     begin
  560.         with info^ do
  561.             if RoiShowing and (RoiType = LineRoi) then begin
  562.                     with RoiRect do begin
  563.                             p1.h := left + trunc(LX1);
  564.                             p1.v := top + trunc(LY1);
  565.                             p2.h := left + trunc(LX2);
  566.                             p2.v := top + trunc(LY2);
  567.                         end;
  568.                     DoProfilePlot(p1, p2);
  569.                     exit(PlotDensityProfile);
  570.                 end;
  571.         with info^ do
  572.             if RoiShowing and ((RoiType = FreeLineRoi) or (RoiType = SegLineRoi)) then begin
  573.                     PutMessage('Profiling currently only works with straight lines.');
  574.                     exit(PlotDensityProfile);
  575.                 end;
  576.         if NoSelection or NotRectangular or NotInBounds then
  577.             exit(PlotDensityProfile);
  578.         ShowWatch;
  579.         with info^.RoiRect do begin
  580.                 width := right - left;
  581.                 height := bottom - top;
  582.                 start.h := left;
  583.                 start.v := bottom;
  584.                 if (width >= height) or (OptionKeyWasDown) then begin
  585.             {Column Average Plot}
  586.                         if width > MaxLine then
  587.                             width := MaxLine;
  588.                         for i := 0 to width - 1 do
  589.                             sum[i] := 0;
  590.                         for vloc := top to bottom - 1 do begin
  591.                                 GetLine(left, vloc, width, PlotData^);
  592.                                 for i := 0 to width - 1 do
  593.                                     sum[i] := sum[i] + PlotData^[i];
  594.                             end;
  595.                         for i := 0 to width - 1 do
  596.                             PlotData^[i] := sum[i] div height;
  597.                         PlotCount := width;
  598.                         PlotAvg := height;
  599.                         SetupPlot(PlotData^, start, false);
  600.                     end
  601.                 else begin
  602.            {Row Aversage Plot}
  603.                         if height > MaxLine then
  604.                             height := MaxLine;
  605.                         for i := 0 to height - 1 do
  606.                             sum[i] := 0;
  607.                         for hloc := left to right - 1 do begin
  608.                                 GetColumn(hloc, top, height, PlotData^);
  609.                                 for i := 0 to height - 1 do
  610.                                     sum[i] := sum[i] + PlotData^[i];
  611.                             end;
  612.                         for i := 0 to height - 1 do
  613.                             PlotData^[i] := sum[i] div width;
  614.                         PlotCount := height;
  615.                         PlotAvg := width;
  616.                         SetupPlot(PlotData^, start, true);
  617.                     end;
  618.             end; {with}
  619.     end;
  620.  
  621.  
  622.     procedure SetScale;
  623.         const
  624.             FirstButtonID = 5;
  625.             LastButtonID = 14;
  626.             KnownDistanceID = 3;
  627.             ScaleID = 16;
  628.             UnitsTextID = 18;
  629.             MagnificationID = 20;
  630.             MeasuredDistanceID = 22;
  631.             AspectRatioID = 25;
  632.         var
  633.             mylog: DialogPtr;
  634.             item, i: integer;
  635.             SaveUnitsID: UnitsType;
  636.             KnownDistance, MeasuredDistance, SaveScale, TempScale, CalibratedDistance: extended;
  637.             OldUnitsPerCM, OldScale, SaveUnitsPErCM, SaveRawScale, SaveMagnification, SaveAspectRatio: extended;
  638.             ignore: integer;
  639.             str: str255;
  640.             SaveUnits: string[2];
  641.             isLineSelection: boolean;
  642.     begin
  643.         with info^ do begin
  644.                 if (not RoiShowing) and (CurrentTool = LineTool) and (NoInfo^.roiType = LineRoi) then
  645.                     RestoreRoi;
  646.                 isLineSelection := RoiShowing and (RoiType = LineRoi);
  647.                 InitCursor;
  648.                 if isLineSelection then begin
  649.                         GetLoiLength;
  650.                         MeasuredDistance := uLength;
  651.                     end
  652.                 else
  653.                     MeasuredDistance := 0.0;
  654.                 SaveUnits := units;
  655.                 SaveUnitsID := UnitsID;
  656.                 SaveRawScale := RawSpatialScale;
  657.                 SaveScale := xSpatialScale;
  658.                 SaveMagnification := ScaleMagnification;
  659.                 SaveAspectRatio := PixelAspectRatio;
  660.                 KnownDistance := 0.0;
  661.                 OldScale := 0.0;
  662.                 mylog := GetNewDialog(10, nil, pointer(-1));
  663.                 SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 1);
  664.                 SetDReal(MyLog, KnownDistanceID, KnownDistance, 1);
  665.                 SelIText(MyLog, KnownDistanceID, 0, 32767);
  666.                 SetDReal(MyLog, MagnificationID, ScaleMagnification, 1);
  667.                 SetDReal(MyLog, AspectRatioID, PixelAspectRatio, 4);
  668.                 if UnitsID = pixels then
  669.                     TempScale := 1.0
  670.                 else
  671.                     TempScale := xSpatialScale;
  672.                 SetDReal(MyLog, ScaleID, TempScale, 2);
  673.                 SetDString(MyLog, UnitsTextID, FullUnits);
  674.                 OutlineButton(MyLog, ok, 16);
  675.                 SetDialogItem(mylog, FirstButtonID + ord(UnitsID), 1);
  676.                 repeat
  677.                     ModalDialog(nil, item);
  678.                     if item = MeasuredDistanceID then
  679.                         MeasuredDistance := GetDReal(MyLog, MeasuredDistanceID);
  680.                     if item = KnownDistanceID then
  681.                         KnownDistance := GetDReal(MyLog, KnownDistanceID);
  682.                     if item = ScaleID then begin
  683.                             MeasuredDistance := GetDReal(MyLog, ScaleID);
  684.                             KnownDistance := 1;
  685.                             SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 1);
  686.                             SetDReal(MyLog, KnownDistanceID, KnownDistance, 1);
  687.                         end;
  688.                     if item = MagnificationID then begin
  689.                             ScaleMagnification := GetDReal(MyLog, MagnificationID);
  690.                             if ScaleMagnification < 0.0 then begin
  691.                                     beep;
  692.                                     ScaleMagnification := 1.0;
  693.                                 end
  694.                             else begin
  695.                                     xSpatialScale := RawSpatialScale * ScaleMagnification;
  696.                                     ySpatialScale := xSpatialScale / PixelAspectRatio;
  697.                                 end;
  698.                         end;
  699.                     if item = AspectRatioID then begin
  700.                             PixelAspectRatio := GetDReal(MyLog, AspectRatioID);
  701.                             if PixelAspectRatio <= 0.0 then begin
  702.                                     beep;
  703.                                     PixelAspectRatio := 1.0;
  704.                                 end
  705.                             else begin
  706.                                     xSpatialScale := RawSpatialScale * ScaleMagnification;
  707.                                     ySpatialScale := xSpatialScale / PixelAspectRatio;
  708.                                 end;
  709.                         end;
  710.                     if (item >= FirstButtonID) and (item <= LastButtonID) then begin
  711.                             for i := FirstButtonID to LastButtonID do
  712.                                 SetDialogItem(mylog, i, 0);
  713.                             SetDialogItem(mylog, item, 1);
  714.                             if (item = LastButtonID) and (UnitsID <> Pixels) then begin
  715.                                     OldScale := RawSpatialScale;
  716.                                     SaveUnitsPerCM := UnitsPerCM
  717.                                 end;
  718.                             OldUnitsPerCM := UnitsPerCM;
  719.                             GetUnits(item);
  720.                             if (UnitsID <> Pixels) and (RawSpatialScale = 0.0) and (OldScale <> 0.0) then begin
  721.                                     RawSpatialScale := OldScale;
  722.                                     xSpatialScale := RawSpatialScale * ScaleMagnification;
  723.                                     ySpatialScale := xSpatialScale / PixelAspectRatio;
  724.                                     OldUnitsPerCM := SaveUnitsPerCM;
  725.                                     OldScale := 0.0;
  726.                                 end;
  727.                             if (UnitsPerCM <> OldUnitsPerCM) and (UnitsPerCM <> 0.0) then begin
  728.                                     RawSpatialScale := RawSpatialScale * (OldUnitsPerCM / UnitsPerCM);
  729.                                     xSpatialScale := RawSpatialScale * ScaleMagnification;
  730.                                     ySpatialScale := xSpatialScale / PixelAspectRatio;
  731.                                 end;
  732.                             if UnitsID = Pixels then
  733.                                 KnownDistance := 0.0;
  734.                         end;
  735.                     if (item = KnownDistanceID) or (item = MeasuredDistanceID) or (item = ScaleID) then
  736.                         if (UnitsID = Pixels) and (item <> cancel) then
  737.                             PutMessage('You must select a measurent unit before setting or changing the scale.')
  738.                         else begin
  739.                                 if (MeasuredDistance > 0.0) and (KnownDistance > 0.0) then begin
  740.                                         RawSpatialScale := MeasuredDistance / KnownDistance;
  741.                                         xSpatialScale := RawSpatialScale * ScaleMagnification;
  742.                                         ySpatialScale := xSpatialScale / PixelAspectRatio;
  743.                                     end;
  744.                             end;
  745.                     if UnitsID = pixels then
  746.                         TempScale := 1.0
  747.                     else
  748.                         TempScale := xSpatialScale;
  749.                     SetDReal(MyLog, ScaleID, TempScale, 2);
  750.                     SetDString(MyLog, UnitsTextID, FullUnits);
  751.                 until (item = ok) or (item = cancel);
  752.                 DisposDialog(mylog);
  753.                 if item = cancel then begin
  754.                         units := SaveUnits;
  755.                         UnitsID := SaveUnitsID;
  756.                         RawSpatialScale := SaveRawScale;
  757.                         xSpatialScale := SaveScale;
  758.                         ScaleMagnification := SaveMagnification;
  759.                         PixelAspectRatio := SaveAspectRatio;
  760.                     end;
  761.                 SpatiallyCalibrated := xSpatialScale <> 0.0;
  762.                 UpdateTitleBar;
  763.             end; {with info^}
  764.     end;
  765.  
  766.  
  767.     procedure SetupCalibrationPlot;
  768.         const
  769.             hrange = 1024;
  770.             hmax = 1023;
  771.             vrange = 600;
  772.             vmax = 599;
  773.             SymbolSize = 11;
  774.         var
  775.             fRect, tRect: rect;
  776.             svalue, range, hscale, vscale, MinV, MaxV: extended;
  777.             tPort: GrafPtr;
  778.             i, hloc, vloc: integer;
  779.             SaveClipRegion: RgnHandle;
  780.             pt: point;
  781.     begin
  782.         PlotLeftMargin := 60;
  783.         PlotTopMargin := 15;
  784.         PlotBottomMargin := 30;
  785.         PlotRightMargin := 100;
  786.         MinV := MinValue;
  787.         MaxV := MaxValue;
  788.         for i := 1 to nStandards do begin
  789.                 svalue := StandardValues[i];
  790.                 if svalue < MinV then
  791.                     MinV := svalue;
  792.                 if svalue > MaxV then
  793.                     MaxV := svalue;
  794.             end;
  795.         range := MaxV - MinV;
  796.         PlotWidth := hrange div 3 + PlotLeftMargin + PlotRightMargin;
  797.         PlotHeight := vrange div 3 + PlotTopMargin + PlotBottomMargin;
  798.         PlotLeft := 64;
  799.         PlotTop := 64;
  800.         PlotCount := 256;
  801.         MakePlotWindow(PlotLeft, PlotTop, PlotWidth, PlotHeight);
  802.         WindowPeek(PlotWindow)^.WindowKind := CalibrationPlotKind;
  803.         SetRect(fRect, -SymbolSize, -SymbolSize, hmax + SymbolSize, vmax + SymbolSize);
  804.         GetPort(tPort);
  805.         SetPort(PlotWindow);
  806.         SaveClipRegion := PlotWindow^.ClipRgn;
  807.         RectRgn(PlotWindow^.ClipRgn, fRect);
  808.         hscale := 256 / hrange;
  809.         vscale := range / vrange;
  810.         PlotPICT := OpenPicture(fRect);
  811.         for i := 1 to nStandards do begin
  812.                 hloc := round(umean[i] / hscale);
  813.                 vloc := vmax - round((StandardValues[i] - MinValue) / vscale);
  814.                 SetRect(tRect, hloc - SymbolSize, vloc - SymbolSize, hloc + SymbolSize, vloc + SymbolSize);
  815.                 FrameOval(tRect);
  816.             end;
  817.         MoveTo(0, vmax - round((cvalue[0] - MinValue) / vscale));
  818.         for i := 1 to 255 do begin
  819.                 hloc := round(i / hscale);
  820.                 vloc := vmax - round((cvalue[i] - MinValue) / vscale);
  821.                 LineTo(hloc, vloc);
  822.             end;
  823.         ClosePicture;
  824.         PlotWindow^.ClipRgn := SaveClipRegion;
  825.         InvalRect(PlotWindow^.PortRect);
  826.         SetPort(tPort);
  827.         SelectWindow(PlotWindow);
  828.     end;
  829.  
  830.  
  831.     procedure DoCurveFitting;
  832.         var
  833.             i: integer;
  834.             XData, YData, YFit, Residuals, TempData: ColumnVector;
  835.             Variance: extended;
  836.             SumResidualsSqr, SumStandards, mean, SumMeanDiffSqr, DegreesOfFreedom: extended;
  837.             str1, str2: str255;
  838.     begin
  839.         with info^ do begin
  840.                 ShowWatch;
  841.                 if fit = RodbardFit then { need to reverse x and y to fit Rodbard equation }
  842.                     for i := 1 to nStandards do begin
  843.                             XData[i] := StandardValues[i];
  844.                             YData[i] := umean[i];
  845.                         end
  846.                 else
  847.                     for i := 1 to nStandards do begin
  848.                             XData[i] := umean[i];
  849.                             YData[i] := StandardValues[i];
  850.                         end;
  851.                 case fit of
  852.                     StraightLine: 
  853.                         nCoefficients := 2;
  854.                     Poly2: 
  855.                         nCoefficients := 3;
  856.                     Poly3: 
  857.                         nCoefficients := 4;
  858.                     Poly4: 
  859.                         nCoefficients := 5;
  860.                     Poly5: 
  861.                         nCoefficients := 6;
  862.                     ExpoFit: 
  863.                         nCoefficients := 2;
  864.                     PowerFit: 
  865.                         nCoefficients := 2;
  866.                     LogFit: 
  867.                         nCoefficients := 2;
  868.                     RodbardFit: 
  869.                         nCoefficients := 4;
  870.                 end;
  871.                 DegreesOfFreedom := nStandards - nCoefficients;
  872.                 if DegreesOfFreedom < 0 then begin
  873.                         FitGoodness := 0.0;
  874.                         DensityCalibrated := false;
  875.                         NumToString(nCoefficients, str1);
  876.                         case fit of
  877.                             StraightLine: 
  878.                                 str2 := 'straight line';
  879.                             Poly2: 
  880.                                 str2 := '2nd degree polynomial';
  881.                             Poly3: 
  882.                                 str2 := '3rd degree polynomial';
  883.                             Poly4: 
  884.                                 str2 := '4th degree polynomial';
  885.                             Poly5: 
  886.                                 str2 := '5th degree polynomial';
  887.                             ExpoFit: 
  888.                                 str2 := 'exponential';
  889.                             PowerFit: 
  890.                                 str2 := 'power';
  891.                             LogFit: 
  892.                                 str2 := 'log';
  893.                             RodbardFit: 
  894.                                 str2 := 'Rodbard';
  895.                         end;
  896.                         str2 := concat(' standards to do ', str2, ' fitting.');
  897.                         PutMessage(concat('You need at least ', str1, str2));
  898.                         exit(DoCurveFitting)
  899.                     end;
  900.                 DoSimplexFit(nStandards, nCoefficients, XData, YData, Coefficient, residuals);
  901.                 DensityCalibrated := true;
  902.                 ZeroClip := true;
  903.                 for i := 1 to nStandards do
  904.                     if ydata[i] < 0.0 then
  905.                         ZeroClip := false;
  906.                 GenerateValues;
  907.                 SumResidualsSqr := 0.0;
  908.                 SumStandards := 0.0;
  909.                 if fit = RodbardFit then
  910.                     for i := 1 to nStandards do begin
  911.                             tempdata[i] := StandardValues[i];
  912.                             StandardValues[i] := umean[i];
  913.                         end;
  914.                 for i := 1 to nStandards do begin
  915.                         SumResidualsSqr := SumResidualsSqr + sqr(residuals[i]);
  916.                         SumStandards := SumStandards + StandardValues[i];
  917.                     end;
  918.                 FitSD := Sqrt(SumResidualsSqr / nStandards);
  919.                 mean := SumStandards / nStandards;
  920.                 SumMeanDiffSqr := 0.0;
  921.                 for i := 1 to nStandards do
  922.                     SumMeanDiffSqr := SumMeanDiffSqr + sqr(StandardValues[i] - Mean);
  923.                 if (SumMeanDiffSqr > 0.0) and (DegreesOfFreedom <> 0) then
  924.                     FitGoodness := 1 - (SumResidualsSqr / DegreesOfFreedom) * ((nStandards - 1) / SumMeanDiffSqr)
  925.                 else
  926.                     FitGoodness := 1.0;
  927.                 if fit = RodbardFit then
  928.                     for i := 1 to nStandards do
  929.                         StandardValues[i] := tempdata[i];
  930.             end;
  931.         info^.changes := true;
  932.     end;
  933.  
  934.  
  935.     procedure GetStandardsFromFile (mylog: DialogPtr; FirstLevelID, FirstStandardID: integer);
  936.         var
  937.             fname, str: str255;
  938.             RefNum, i, nColumns, nValues: integer;
  939.             rLine: rLineType;
  940.     begin
  941.         if not OpenTextFile(fname, RefNum) then
  942.             exit(GetStandardsFromFile);
  943.         InitTextInput(fname, RefNum);
  944.         GetLineFromText(rLine, nValues);
  945.         if nValues = 1 then
  946.             nColumns := 1
  947.         else
  948.             nColumns := 2;
  949.         if (nStandards = 0) and (nColumns = 2) then begin
  950.                 i := 0;
  951.                 repeat
  952.                     i := i + 1;
  953.                     if i > MaxStandards then
  954.                         i := MaxStandards;
  955.                     umean[i] := rLine[1];
  956.                     SetDReal(MyLog, FirstLevelID + i - 1, umean[i], 2);
  957.                     StandardValues[i] := rLine[2];
  958.                     SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 3);
  959.                     GetLineFromText(rLine, nValues);
  960.                 until nValues = 0;
  961.                 nStandards := i;
  962.                 mCount := nStandards;
  963.                 for i := 1 to mCount do begin
  964.                         ClearResults(i);
  965.                         mean^[i] := umean[i];
  966.                     end;
  967.             end
  968.         else
  969.             for i := 1 to nStandards do begin
  970.                     if nValues = nColumns then begin
  971.                             StandardValues[i] := rLine[nColumns];
  972.                             SetDReal(MyLog, FirststandardID + i - 1, StandardValues[i], 3);
  973.                         end;
  974.                     GetLineFromText(rLine, nValues);
  975.                 end;
  976.         InitCursor;
  977.     end;
  978.  
  979.  
  980.     procedure SaveStandardsToFile (nStandards: integer);
  981.         var
  982.             where: Point;
  983.             reply: SFReply;
  984.             i: integer;
  985.             OptionKeyWasDown: boolean;
  986.     begin
  987.         OptionKeyWasDown := OptionKeyDown;
  988.         where.v := 50;
  989.         where.h := 50;
  990.         SFPutFile(Where, 'Save Calibration as?', 'Standards', nil, reply);
  991.         if reply.good then begin
  992.                 TextBufSize := 0;
  993.                 for i := 1 to nStandards do begin
  994.                         PutReal(umean[i], 1, 3);
  995.                         PutChar(tab);
  996.                         if StandardValues[i] >= 100.0 then
  997.                             PutReal(StandardValues[i], 1, 3)
  998.                         else
  999.                             PutReal(StandardValues[i], 1, 5);
  1000.                         if i <> nStandards then
  1001.                             PutChar(cr);
  1002.                     end;
  1003.                 with reply do
  1004.                     SaveAsText(fname, vRefNum);
  1005.             end;
  1006.         InitCursor;
  1007.     end;
  1008.  
  1009.  
  1010.     procedure CopyFunctionToLUT;
  1011.         var
  1012.             i: integer;
  1013.             value: LongInt;
  1014.             scale: extended;
  1015.     begin
  1016.         with info^ do begin
  1017.                 DisableDensitySlice;
  1018.                 scale := 65535.0 / (MaxValue - MinValue);
  1019.                 for i := 0 to 255 do begin
  1020.                         value := 65535 - round(scale * (cvalue[i] - MinValue));
  1021.                         with cTable[i].rgb do begin
  1022.                                 red := value;
  1023.                                 green := value;
  1024.                                 blue := value;
  1025.                             end;
  1026.                     end;
  1027.                 LoadLUT(cTable);
  1028.                 LutMode := CustomGrayScale;
  1029.                 SetupPseudocolor;
  1030.                 UpdateMap
  1031.             end;
  1032.     end;
  1033.  
  1034.  
  1035.     procedure SetupUncalibratedOD;
  1036.         var
  1037.             i: integer;
  1038.     begin
  1039.         with info^ do begin
  1040.                 DensityCalibrated := true;
  1041.                 ZeroClip := false;
  1042.                 nCoefficients := 0;
  1043.                 for i := 1 to 6 do
  1044.                     Coefficient[i] := 1.0;
  1045.                 fit := UncalibratedOD;
  1046.                 GenerateValues;
  1047.                 UnitOfMeasure := 'Uncalibrated OD';
  1048.                 nStandards := 0;
  1049.             end;
  1050.     end;
  1051.  
  1052.  
  1053.     function InvertOD (var temp: StandardsArray): boolean;
  1054.         var
  1055.             i: integer;
  1056.     begin
  1057.         for i := 1 to nStandards do
  1058.             if (StandardValues[i] < 0.000009) or (StandardValues[i] > 4.64) then begin
  1059.                     PutMessage('Known OD Values must be in the range 0.00001 to 4.62.');
  1060.                     InvertOD := false;
  1061.                     exit(InvertOD);
  1062.                 end;
  1063.         for i := 1 to nStandards do  {temp[i] := -log10(1.000 - exp10(-StandardValues[i]));}
  1064.             temp[i] := -0.434294481 * ln(1.000 - exp(-2.302585093 * StandardValues[i]));
  1065.         InvertOD := true;
  1066.     end;
  1067.  
  1068.  
  1069.     procedure Calibrate;
  1070.         const
  1071.             FirstLevelID = 3;
  1072.             FirstStandardID = 23;
  1073.             FirstFitID = 63;
  1074.             LastFitID = 74;
  1075.             UnitOfMeasureID = 75;
  1076.             OpenID = 77;
  1077.             SaveID = 78;
  1078.             CopyID = 81;
  1079.             RemoveID = 82;
  1080.             InvertID = 83;
  1081.         var
  1082.             mylog: DialogPtr;
  1083.             ignore, item, i, nBadReals: integer;
  1084.             str: str255;
  1085.             SaveStandards, temp, NewValues: StandardsArray;
  1086.             OptionKeyWasDown, CopyFunction, RemoveCalibration: boolean;
  1087.     begin
  1088.         OptionKeyWasDown := OptionKeyDown;
  1089.         SaveStandards := StandardValues;
  1090.         CopyFunction := false;
  1091.         RemoveCalibration := false;
  1092.         with info^ do begin
  1093.                 mylog := GetNewDialog(20, nil, pointer(-1));
  1094.                 OutlineButton(MyLog, ok, 16);
  1095.                 nStandards := mCount;
  1096.                 if nStandards > MaxStandards then
  1097.                     nStandards := MaxStandards;
  1098.                 for i := 1 to nStandards do begin
  1099.                         SetDReal(MyLog, FirstLevelID + i - 1, umean[i], 2);
  1100.                         if StandardValues[i] <> BadReal then
  1101.                             SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 3);
  1102.                     end;
  1103.                 SelIText(MyLog, FirstStandardID, 0, 32767);
  1104.                 if (fit = SpareFit1) or (fit = SpareFit2) then
  1105.                     fit := Poly3;
  1106.                 SetDialogItem(mylog, FirstFitID + ord(fit), 1);
  1107.                 if DensityCalibrated then
  1108.                     SetDString(MyLog, UnitOfMeasureID, UnitOfMeasure);
  1109.                 repeat
  1110.                     ModalDialog(nil, item);
  1111.                     if (item >= FirstStandardID) and (item < (FirstStandardID + MaxStandards)) then begin
  1112.                             i := item - FirstStandardID + 1;
  1113.                             if i <= nStandards then
  1114.                                 StandardValues[i] := GetDReal(MyLog, item)
  1115.                             else begin
  1116.                                     PutMessage('Before entering known values you must use the Measure command to read a set of standards.');
  1117.                                     SetDString(MyLog, item, '');
  1118.                                 end;
  1119.                         end;
  1120.                     if (item >= FirstLevelID) and (item < (FirstLevelID + MaxStandards)) then begin
  1121.                             i := item - FirstLevelID + 1;
  1122.                             if OptionKeyWasDown and (i <= nStandards) then
  1123.                                 umean[item - FirstLevelID + 1] := GetDReal(MyLog, item)
  1124.                             else begin
  1125.                                     PutMessage('Use the Measure command to record measured values.');
  1126.                                     if i <= nStandards then begin
  1127.                                             RealToString(umean[i], 1, 2, str);
  1128.                                             SetDString(MyLog, item, str)
  1129.                                         end
  1130.                                     else
  1131.                                         SetDString(MyLog, item, '');
  1132.                                 end;
  1133.                         end;
  1134.                     if (item >= FirstFitID) and (item <= LastFitID) then begin
  1135.                             for i := FirstFitID to LastFitID do
  1136.                                 SetDialogItem(mylog, i, 0);
  1137.                             SetDialogItem(mylog, item, 1);
  1138.                             fit := CurveFitType(item - FirstFitID);
  1139.                         end;
  1140.                     if item = UnitOfMeasureID then
  1141.                         UnitOfMeasure := GetDString(MyLog, item);
  1142.                     if item = OpenID then
  1143.                         GetStandardsFromFile(mylog, FirstLevelID, FirstStandardID);
  1144.                     if (item = SaveID) and (nStandards > 1) then
  1145.                         SaveStandardsToFile(nStandards);
  1146.                     if item = CopyID then begin
  1147.                             CopyFunction := not CopyFunction;
  1148.                             if CopyFunction then
  1149.                                 RemoveCalibration := false;
  1150.                             SetDialogItem(mylog, CopyID, ord(CopyFunction));
  1151.                             SetDialogItem(mylog, RemoveID, ord(RemoveCalibration));
  1152.                         end;
  1153.                     if item = RemoveID then begin
  1154.                             RemoveCalibration := not RemoveCalibration;
  1155.                             if RemoveCalibration then
  1156.                                 CopyFunction := false;
  1157.                             SetDialogItem(mylog, RemoveID, ord(RemoveCalibration));
  1158.                             SetDialogItem(mylog, CopyID, ord(CopyFunction));
  1159.                         end;
  1160.                     if (item = InvertID) and (nStandards > 1) then
  1161.                         if InvertOD(NewValues) then
  1162.                             for i := 1 to nStandards do begin
  1163.                                     StandardValues[i] := NewValues[i];
  1164.                                     SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 5);
  1165.                                 end;
  1166.                 until (item = ok) or (item = cancel);
  1167.                 DisposDialog(mylog);
  1168.                 if item = cancel then begin
  1169.                         StandardValues := SaveStandards;
  1170.                         exit(calibrate)
  1171.                     end;
  1172.                 if RemoveCalibration then begin
  1173.                         DensityCalibrated := false;
  1174.                         for i := 0 to 255 do
  1175.                             cvalue[i] := i;
  1176.                         UpdateTitleBar;
  1177.                         exit(calibrate)
  1178.                     end;
  1179.                 nBadReals := 0;
  1180.                 if fit = UncalibratedOD then
  1181.                     SetupUncalibratedOD
  1182.                 else begin
  1183.                         for i := 1 to nStandards do
  1184.                             if StandardValues[i] = BadReal then
  1185.                                 nBadReals := nBadReals + 1;
  1186.                         if (nStandards > 0) and (nBadReals = 0) then
  1187.                             DoCurveFitting
  1188.                         else if not DensityCalibrated then
  1189.                             beep;
  1190.                     end;
  1191.                 if DensityCalibrated then begin
  1192.                         SetupCalibrationPlot;
  1193.                         if CopyFunction then
  1194.                             CopyFunctionToLUT;
  1195.                     end;
  1196.                 UpdateTitleBar;
  1197.             end; {with info^}
  1198.     end;
  1199.  
  1200.  
  1201.     procedure ResetCounter;
  1202.         var
  1203.             AlertID: Integer;
  1204.     begin
  1205.         if UnsavedResults and (not macro) then begin
  1206.                 InitCursor;
  1207.                 AlertID := alert(500, nil);
  1208.             end
  1209.         else
  1210.             AlertID := ok;
  1211.         if AlertID <> CancelResetID then begin
  1212.                 nPoints := 0;
  1213.                 nLengths := 0;
  1214.                 nAngles := 0;
  1215.                 mCount := 0;
  1216.                 mCount2 := 0;
  1217.                 UnsavedResults := false;
  1218.                 ShowValues;
  1219.                 if ResultsWindow <> nil then begin
  1220.                         with ListTE^^ do
  1221.                             TESetSelect(0, teLength, ListTE);
  1222.                         TEDelete(ListTE);
  1223.                         UpdateScrollBars;
  1224.                     end;
  1225.             end;
  1226.         measuring := false;
  1227.     end;
  1228.  
  1229.  
  1230.     procedure ShowResults;
  1231.         const
  1232.             FontSize = 9;
  1233.         var
  1234.             wrect, crect, trect: rect;
  1235.             loc: point;
  1236.     begin
  1237.         mCount2 := mCount;
  1238.         if ResultsWindow <> nil then begin
  1239.                 SelectWindow(ResultsWindow);
  1240.                 exit(ShowResults);
  1241.             end;
  1242.         CopyResultsToBuffer(1, mCount, true);
  1243.         ShowMessage('');
  1244.         ResultsWidth := 110 + round(nListColumns * FieldWidth * 6.5);
  1245.         if ResultsWidth < 250 then
  1246.             ResultsWidth := 250;
  1247.         if (ResultsWidth + 20) > ScreenWidth then
  1248.             ResultsWidth := ScreenWidth - 20;
  1249.         ResultsHeight := ((LongInt(TextBufLineCount) * 2) + 2) * FontSize;
  1250.         if ResultsHeight < 200 then
  1251.             ResultsHeight := 200;
  1252.         if (ResultsHeight + ResultsTop + 50) > ScreenHeight then
  1253.             ResultsHeight := ScreenHeight - ResultsTop - 50;
  1254.         SetRect(wrect, ResultsLeft, ResultsTop, ResultsLeft + ResultsWidth, ResultsTop + ResultsHeight);
  1255.         ResultsWindow := NewWindow(nil, wrect, 'Results', true, 0, pointer(-1), true, 0);
  1256.         WindowPeek(ResultsWindow)^.WindowKind := ResultsKind;
  1257.         SetRect(crect, ResultsWidth - ScrollBarWidth, -1, ResultsWidth + 1, ResultsHeight - 14);
  1258.         vScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsHeight - 14, ScrollBarProc, 0);
  1259.         SetRect(crect, -1, ResultsHeight - ScrollBarWidth, ResultsWidth - 14, ResultsHeight + 1);
  1260.         hScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsWidth - 14, ScrollBarProc, 0);
  1261.         InitTextEdit(Monaco, FontSize);
  1262.         DrawControls(ResultsWindow);
  1263.         WhatToUndo := NothingToUndo;
  1264.     end;
  1265.  
  1266.  
  1267.     procedure DoMeasurementOptions;
  1268.         const
  1269.             FirstID = 3;
  1270.             LastID = 15;
  1271.             RedirectID = 26;
  1272.             LabelID = 27;
  1273.             OutlineID = 28;
  1274.             IgnoreID = 29;
  1275.             IncludeHolesID = 30;
  1276.             AutoID = 31;
  1277.             AdjustID = 32;
  1278.             HeadingsID = 33;
  1279.             MinID = 17;
  1280.             MaxID = 19;
  1281.             MaxRegionsID = 25;
  1282.             WidthID = 23;
  1283.             PrecisionID = 21;
  1284.         var
  1285.             mylog: DialogPtr;
  1286.             item, i, SavePrecision, SaveMaxRegions, SaveWidth: integer;
  1287.             mtype: MeasurementTypes;
  1288.             SaveMeasurements: set of MeasurementTypes;
  1289.             SaveRedirect, SaveIgnore, SaveLabel, SaveOutline: boolean;
  1290.             SaveAuto, SaveAdjust, SaveHeadings: boolean;
  1291.             SaveMin, SaveMax: LongInt;
  1292.     begin
  1293.         InitCursor;
  1294.         if nPoints > 0 then
  1295.             Measurements := Measurements + [XYLocM];
  1296.         if nLengths > 0 then
  1297.             Measurements := Measurements + [LengthM];
  1298.         if nAngles > 0 then
  1299.             Measurements := Measurements + [AngleM];
  1300.         SaveMeasurements := measurements;
  1301.         SaveMin := MinParticleSize;
  1302.         SaveMax := MaxParticleSize;
  1303.         SaveRedirect := RedirectSampling;
  1304.         SaveIgnore := IgnoreParticlesTouchingEdge;
  1305.         SaveLabel := LabelParticles;
  1306.         SaveOutline := OutlineParticles;
  1307.         SaveWidth := FieldWidth;
  1308.         SavePrecision := precision;
  1309.         SaveAuto := WandAdjustAreas;
  1310.         SaveAdjust := WandAdjustAreas;
  1311.         SaveMaxRegions := MaxRegions;
  1312.         SaveHeadings := ShowHeadings;
  1313.         mylog := GetNewDialog(4000, nil, pointer(-1));
  1314.         mtype := AreaM;
  1315.         for i := FirstID to LastID do begin
  1316.                 if mtype in measurements then
  1317.                     SetDialogItem(mylog, i, 1);
  1318.                 if i <> LastID then
  1319.                     mtype := succ(mtype);
  1320.             end;
  1321.         SetDNum(MyLog, MinID, MinParticleSize);
  1322.         SetDNum(MyLog, MaxID, MaxParticleSize);
  1323.         ParamText('Pixels', 'Pixels', '', '');
  1324.         SetDialogItem(mylog, RedirectID, ord(RedirectSampling));
  1325.         SetDialogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  1326.         SetDialogItem(mylog, LabelID, ord(LabelParticles));
  1327.         SetDialogItem(mylog, OutlineID, ord(OutlineParticles));
  1328.         SetDialogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1329.         SetDialogItem(mylog, AutoID, ord(WandAutoMeasure));
  1330.         SetDialogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1331.         SetDialogItem(mylog, HeadingsID, ord(ShowHeadings));
  1332.         SetDNum(MyLog, MaxRegionsID, MaxRegions);
  1333.         SetDNum(MyLog, WidthID, FieldWidth);
  1334.         SetDNum(MyLog, PrecisionID, precision);
  1335.         OutlineButton(MyLog, ok, 16);
  1336.         repeat
  1337.             ModalDialog(nil, item);
  1338.             if (item >= FirstID) and (item <= LastID) then begin
  1339.                     i := item - FirstID;
  1340.                     case i of
  1341.                         0: 
  1342.                             mtype := AreaM;
  1343.                         1: 
  1344.                             mtype := MeanM;
  1345.                         2: 
  1346.                             mtype := StdDevM;
  1347.                         3: 
  1348.                             mtype := xyLocM;
  1349.                         4: 
  1350.                             mtype := ModeM;
  1351.                         5: 
  1352.                             mtype := LengthM;
  1353.                         6: 
  1354.                             mtype := MajorAxisM;
  1355.                         7: 
  1356.                             mtype := MinorAxisM;
  1357.                         8: 
  1358.                             mtype := AngleM;
  1359.                         9: 
  1360.                             mtype := IntDenM;
  1361.                         10: 
  1362.                             mtype := MinMaxM;
  1363.                         11: 
  1364.                             mtype := User1M;
  1365.                         12: 
  1366.                             mtype := User2M;
  1367.                     end;
  1368.                     if mtype in measurements then begin
  1369.                             measurements := measurements - [mtype];
  1370.                             SetDialogItem(mylog, item, 0)
  1371.                         end
  1372.                     else begin
  1373.                             measurements := measurements + [mtype];
  1374.                             SetDialogItem(mylog, item, 1)
  1375.                         end;
  1376.                 end;
  1377.             if item = RedirectID then begin
  1378.                     RedirectSampling := not RedirectSampling;
  1379.                     SetDialogItem(mylog, RedirectID, ord(RedirectSampling));
  1380.                 end;
  1381.             if item = IgnoreID then begin
  1382.                     IgnoreParticlesTouchingEdge := not IgnoreParticlesTouchingEdge;
  1383.                     SetDialogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  1384.                 end;
  1385.             if item = LabelID then begin
  1386.                     LabelParticles := not LabelParticles;
  1387.                     SetDialogItem(mylog, LabelID, ord(LabelParticles));
  1388.                 end;
  1389.             if item = OutlineID then begin
  1390.                     OutlineParticles := not OutlineParticles;
  1391.                     SetDialogItem(mylog, OutlineID, ord(OutlineParticles));
  1392.                 end;
  1393.             if item = IncludeHolesID then begin
  1394.                     IncludeHoles := not IncludeHoles;
  1395.                     SetDialogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1396.                 end;
  1397.             if item = AutoID then begin
  1398.                     WandAutoMeasure := not WandAutoMeasure;
  1399.                     SetDialogItem(mylog, AutoID, ord(WandAutoMeasure));
  1400.                 end;
  1401.             if item = AdjustID then begin
  1402.                     WandAdjustAreas := not WandAdjustAreas;
  1403.                     SetDialogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1404.                 end;
  1405.             if item = HeadingsID then begin
  1406.                     ShowHeadings := not ShowHeadings;
  1407.                     SetDialogItem(mylog, HeadingsID, ord(ShowHeadings));
  1408.                 end;
  1409.             if item = MinID then
  1410.                 MinParticleSize := GetDNum(MyLog, MinID);
  1411.             if item = MaxID then
  1412.                 MaxParticleSize := GetDNum(MyLog, MaxID);
  1413.             if item = WidthID then
  1414.                 FieldWidth := GetDNum(MyLog, WidthID);
  1415.             if item = PrecisionID then
  1416.                 precision := GetDNum(MyLog, PrecisionID);
  1417.             if item = MaxRegionsID then
  1418.                 MaxRegions := GetDNum(MyLog, MaxRegionsID);
  1419.         until (item = ok) or (item = cancel);
  1420.         DisposDialog(mylog);
  1421.         if (MinParticleSize < 0) or (MinParticleSize >= MaxParticleSize) then begin
  1422.                 MinParticleSize := SaveMin;
  1423.                 beep;
  1424.             end;
  1425.         if MaxParticleSize <= MinParticleSize then begin
  1426.                 MaxParticleSize := SaveMax;
  1427.                 beep;
  1428.             end;
  1429.         if (FieldWidth < 1) or (FieldWidth > 18) then begin
  1430.                 FieldWidth := SaveWidth;
  1431.                 beep;
  1432.             end;
  1433.         if (precision < 0) or (precision > 8) then begin
  1434.                 precision := SavePrecision;
  1435.                 beep;
  1436.             end;
  1437.         if (MaxRegions < 1) or (MaxRegions > MaxMaxRegions) then begin
  1438.                 MaxRegions := SaveMaxRegions;
  1439.                 beep;
  1440.             end;
  1441.         if item = cancel then begin
  1442.                 measurements := SaveMeasurements;
  1443.                 MinParticleSize := SaveMin;
  1444.                 MaxParticleSize := SaveMax;
  1445.                 RedirectSampling := SaveRedirect;
  1446.                 IgnoreParticlesTouchingEdge := SaveIgnore;
  1447.                 LabelParticles := SaveLabel;
  1448.                 OutlineParticles := SaveOutline;
  1449.                 FieldWidth := SaveWidth;
  1450.                 precision := SavePrecision;
  1451.                 WandAutoMeasure := SaveAuto;
  1452.                 WandAdjustAreas := SaveAdjust;
  1453.                 MaxRegions := SaveMaxRegions;
  1454.                 ShowHeadings := SaveHeadings;
  1455.             end;
  1456.         if not (XYLocM in Measurements) then
  1457.             nPoints := 0;
  1458.         if not (LengthM in Measurements) then
  1459.             nLengths := 0;
  1460.         if not (AngleM in Measurements) then
  1461.             nAngles := 0;
  1462.         UpdateFitEllipse;
  1463.         if MaxRegions <> SaveMaxRegions then
  1464.             PutMessage('You must "Record Preferences" and restart before the change to Maximum Particles will take effect.');
  1465.         if (Measurements <> SaveMeasurements) or (SaveWidth <> FieldWidth) or (SavePrecision <> Precision) then
  1466.             UpdateList;
  1467.     end;
  1468.  
  1469.  
  1470.     procedure ShowLineWidth;
  1471.     begin
  1472.         LineIndex := LineWidth;
  1473.         if LineWidth = 6 then
  1474.             LineIndex := 5;
  1475.         if LineWidth > 6 then
  1476.             LineIndex := 6;
  1477.         DrawTools;
  1478.     end;
  1479.  
  1480.  
  1481.     procedure UpdateRoiLineWidth;
  1482.     begin
  1483.         with info^, info^.RoiRect do
  1484.             if RoiShowing and (RoiType = LineRoi) then begin
  1485.                     LX1 := left + LX1;
  1486.                     LY1 := top + LY1;
  1487.                     LX2 := left + LX2;
  1488.                     LY2 := top + LY2;
  1489.                     MakeRegion;
  1490.                 end;
  1491.     end;
  1492.  
  1493.  
  1494.     procedure DoProfilePlotOptions;
  1495.         const
  1496.             FixedScaleID = 17;
  1497.             MinID = 4;
  1498.             MaxID = 6;
  1499.             LinePlotID = 7;
  1500.             ScatterPlotID = 8;
  1501.             InvertID = 9;
  1502.             LabelsID = 10;
  1503.             FixedSizeID = 18;
  1504.             WidthID = 13;
  1505.             HeightID = 14;
  1506.             LineWidthID = 16;
  1507.         var
  1508.             mylog: DialogPtr;
  1509.             item, i: integer;
  1510.             SaveAutoscale, SaveLinePlot, SaveInvert, SaveDrawLabels, SaveFixedSize: boolean;
  1511.             SaveWidth, SaveHeight, SaveLineWidth, SaveLineIndex: integer;
  1512.             SaveMin, SaveMax: extended;
  1513.     begin
  1514.         InitCursor;
  1515.         SaveAutoscale := AutoscalePlots;
  1516.         SaveLinePlot := LinePlot;
  1517.         SaveInvert := InvertPlots;
  1518.         SaveMin := ProfilePlotMin;
  1519.         SaveMax := ProfilePlotMax;
  1520.         SaveLineWidth := LineWidth;
  1521.         SaveLineIndex := LineIndex;
  1522.         mylog := GetNewDialog(5000, nil, pointer(-1));
  1523.         SetDialogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1524.         SetDReal(MyLog, MinID, ProfilePlotMin, 2);
  1525.         SetDReal(MyLog, MaxID, ProfilePlotMax, 2);
  1526.         SetDialogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1527.         SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1528.         SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1529.         if LinePlot then
  1530.             SetDialogItem(mylog, LinePlotID, 1)
  1531.         else
  1532.             SetDialogItem(mylog, ScatterPlotID, 1);
  1533.         if InvertPlots then
  1534.             SetDialogItem(mylog, InvertID, 1);
  1535.         if DrawPlotLabels then
  1536.             SetDialogItem(mylog, LabelsID, 1);
  1537.         SetDNum(MyLog, LineWidthID, LineWidth);
  1538.         OutlineButton(MyLog, ok, 16);
  1539.         repeat
  1540.             ModalDialog(nil, item);
  1541.             if item = FixedScaleID then begin
  1542.                     AutoscalePlots := not AutoscalePlots;
  1543.                     SetDialogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1544.                 end;
  1545.             if item = MinID then begin
  1546.                     ProfilePlotMin := GetDReal(MyLog, MinID);
  1547.                     AutoscalePlots := false;
  1548.                     SetDialogItem(mylog, FixedScaleID, 1);
  1549.                 end;
  1550.             if item = MaxID then begin
  1551.                     ProfilePlotMax := GetDReal(MyLog, MaxID);
  1552.                     AutoscalePlots := false;
  1553.                     SetDialogItem(mylog, FixedScaleID, 1);
  1554.                 end;
  1555.             if item = FixedSizeID then begin
  1556.                     FixedSizePlot := not FixedSizePlot;
  1557.                     SetDialogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1558.                 end;
  1559.             if item = WidthID then begin
  1560.                     ProfilePlotWidth := GetDNum(MyLog, WidthID);
  1561.                     if (ProfilePlotWidth < 0) or (ProfilePlotWidth > 1023) then begin
  1562.                             ProfilePlotWidth := SaveWidth;
  1563.                             SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1564.                         end;
  1565.                     FixedSizePlot := true;
  1566.                     SetDialogItem(mylog, FixedSizeID, 1);
  1567.                 end;
  1568.             if item = HeightID then begin
  1569.                     ProfilePlotHeight := GetDNum(MyLog, HeightID);
  1570.                     if (ProfilePlotHeight < 0) or (ProfilePlotHeight > 1023) then begin
  1571.                             ProfilePlotHeight := SaveHeight;
  1572.                             SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1573.                         end;
  1574.                     FixedSizePlot := true;
  1575.                     SetDialogItem(mylog, FixedSizeID, 1);
  1576.                 end;
  1577.             if (item = LinePlotID) or (item = ScatterPlotID) then begin
  1578.                     SetDialogItem(mylog, LinePlotID, 0);
  1579.                     SetDialogItem(mylog, ScatterPlotID, 0);
  1580.                     SetDialogItem(mylog, item, 1);
  1581.                     LinePlot := item = LinePlotID;
  1582.                 end;
  1583.             if item = InvertID then begin
  1584.                     InvertPlots := not InvertPlots;
  1585.                     SetDialogItem(mylog, InvertID, ord(InvertPlots));
  1586.                 end;
  1587.             if item = LabelsID then begin
  1588.                     DrawPlotLabels := not DrawPlotLabels;
  1589.                     if DrawPlotLabels then {Attempt to fix a "sticky" check box bug.}
  1590.                         SetDialogItem(mylog, LabelsID, 1)
  1591.                     else
  1592.                         SetDialogItem(mylog, LabelsID, 0);
  1593.                 end;
  1594.             if item = LineWidthID then begin
  1595.                     LineWidth := GetDNum(MyLog, LineWidthID);
  1596.                     if (LineWidth < 1) or (LineWidth > 500) then begin
  1597.                             LineWidth := SaveLineWidth;
  1598.                             SetDNum(MyLog, LineWidthID, LineWidth);
  1599.                         end;
  1600.                     ShowLineWidth;
  1601.                 end;
  1602.         until (item = ok) or (item = cancel);
  1603.         DisposDialog(mylog);
  1604.         if item = cancel then begin
  1605.                 AutoscalePlots := SaveAutoscale;
  1606.                 LinePlot := SaveLinePlot;
  1607.                 InvertPlots := SaveInvert;
  1608.                 ProfilePlotMin := SaveMin;
  1609.                 ProfilePlotMax := SaveMax;
  1610.                 DrawPlotLabels := SaveDrawLabels;
  1611.                 LineWidth := SaveLineWidth;
  1612.                 if LineIndex <> SaveLineIndex then begin
  1613.                         LineIndex := SaveLineIndex;
  1614.                         DrawTools;
  1615.                     end;
  1616.             end;
  1617.         if LineWidth <> SaveLineWidth then
  1618.             UpdateRoiLineWidth;
  1619.         if ProfilePlotMax <= ProfilePlotMin then begin
  1620.                 beep;
  1621.                 ProfilePlotMin := SaveMin;
  1622.                 ProfilePlotMax := SaveMax;
  1623.             end;
  1624.     end;
  1625.  
  1626.  
  1627.     procedure DoPoints (event: EventRecord);
  1628.         var
  1629.             loc, tloc: point;
  1630.             hloc, vloc, y, offset: integer;
  1631.             r: rect;
  1632.             str, str1, str2: str255;
  1633.     begin
  1634.         SetPort(GrafPtr(info^.osPort));
  1635.         loc := event.where;
  1636.         ScreenToOffscreen(loc);
  1637.         with loc do begin
  1638.                 hloc := h;
  1639.                 vloc := v;
  1640.             end;
  1641.         with results, Info^ do begin
  1642.                 nPoints := nPoints + 1;
  1643.                 IncrementCounter;
  1644.                 if InvertYCoordinates then
  1645.                     y := info^.PicRect.bottom - vloc - 1
  1646.                 else
  1647.                     y := vloc;
  1648.                 ClearResults(mCount);
  1649.                 PixelCount^[mCount] := 1;
  1650.                 if SpatiallyCalibrated then
  1651.                     mArea^[mCount] := 1 / xSpatialScale * ySpatialScale
  1652.                 else
  1653.                     mArea^[mCount] := 1;
  1654.                 mean^[mCount] := cvalue[MyGetPixel(hloc, vloc)];
  1655.                 with info^ do
  1656.                     if SpatiallyCalibrated then begin
  1657.                             xcenter^[mCount] := hloc / xSpatialScale;
  1658.                             ycenter^[mCount] := y / ySpatialScale;
  1659.                         end
  1660.                     else begin
  1661.                             xcenter^[mCount] := hloc;
  1662.                             ycenter^[mCount] := y;
  1663.                         end;
  1664.             end;
  1665.         PenNormal;
  1666.         if OptionKeyDown then begin
  1667.                 NumToString(mCount, str);
  1668.                 tloc := loc;
  1669.                 tloc.v := tloc.v + CurrentSize div 3;
  1670.                 DrawTextString(str, tloc, TeJustCenter);
  1671.             end
  1672.         else begin
  1673.                 offset := LineWidth div 2;
  1674.                 SetRect(r, hloc - offset, vloc - offset, hloc + offset + 1, vloc + offset + 1);
  1675.                 PaintOval(r);
  1676.                 UpdateScreen(r);
  1677.                 if ControlKeyDown then
  1678.                     with info^ do begin
  1679.                             if SpatiallyCalibrated then begin
  1680.                                     RealToString(hloc / xSpatialScale, 1, Precision, str1);
  1681.                                     RealToString(y / ySpatialScale, 1, Precision, str2);
  1682.                                 end
  1683.                             else begin
  1684.                                     NumToString(hloc, str1);
  1685.                                     NumToString(y, str2);
  1686.                                 end;
  1687.                             tloc := loc;
  1688.                             with tloc do begin
  1689.                                     h := h + offset + 5;
  1690.                                     v := v + CurrentSize div 3;
  1691.                                 end;
  1692.                             str := concat('(', str1, ', ', str2, ')');
  1693.                             DrawTextString(str, tloc, TeJustLeft);
  1694.                         end; {Control Key Down}
  1695.             end;
  1696.         ValuesMessage := '';
  1697.         ShowValues;
  1698.         AppendResults;
  1699.         if (nPoints = 1) then
  1700.             if not (XYlocM in Measurements) then
  1701.                 UpdateList;
  1702.         measuring := true;
  1703.         WhatToUndo := UndoPoint;
  1704.     end;
  1705.  
  1706.  
  1707.     procedure FindAngle (event: EventRecord);
  1708.         var
  1709.             start, finish, OldFinish, MidPoint: point;
  1710.             ticks: LongInt;
  1711.             ff, x1, y1, x2, y2, imag: integer;
  1712.             angle, angle1, angle2: extended;
  1713.             StartRect: rect;
  1714.             FirstLineDone: boolean;
  1715.  
  1716.     begin
  1717.         DrawLabels('Angle:', '', '');
  1718.         FlushEvents(EveryEvent, 0);
  1719.         imag := trunc(info^.magnification + 0.5);
  1720.         ff := imag div 2;
  1721.         if ff < 1 then
  1722.             ff := 1;
  1723.         start := event.where;
  1724.         with start do begin
  1725.                 h := h - ff;
  1726.                 v := v - ff
  1727.             end;
  1728.         Pt2Rect(start, start, StartRect);
  1729.         InsetRect(StartRect, -2, -2);
  1730.         finish := start;
  1731.         SetPort(info^.wptr);
  1732.         PenNormal;
  1733.         PenMode(PatXor);
  1734.         PenSize(imag * LineWidth, imag * LineWidth);
  1735.         MoveTo(start.h, start.v);
  1736.         repeat
  1737.             repeat
  1738.                 OldFinish := finish;
  1739.                 GetMouse(finish);
  1740.                 with finish do begin
  1741.                         h := h - ff;
  1742.                         v := v - ff
  1743.                     end;
  1744.                 MoveTo(start.h, start.v);
  1745.                 LineTo(OldFinish.h, OldFinish.v);
  1746.                 MoveTo(start.h, start.v);
  1747.                 LineTo(finish.h, finish.v);
  1748.                 ticks := TickCount;
  1749.                 while ticks = TickCount do
  1750.                     ;
  1751.                 x1 := finish.h - start.h;
  1752.                 y1 := start.v - finish.v;
  1753.                 GetAngle(x1, y1, angle1);
  1754.                 Show1Value(angle1, NoValue);
  1755.             until GetNextEvent(mUpMask, event);
  1756.             FirstLineDone := not PtInRect(finish, StartRect);
  1757.             if not FirstLineDone then
  1758.                 start := finish;
  1759.         until FirstLineDone;
  1760.         DrawObject(LineObj, start, finish);
  1761.         MidPoint := finish;
  1762.         x1 := start.h - MidPoint.h;
  1763.         y1 := MidPoint.v - start.v;
  1764.         GetAngle(x1, y1, angle1);
  1765.         start := finish;
  1766.         finish := start;
  1767.         repeat
  1768.             OldFinish := finish;
  1769.             GetMouse(finish);
  1770.             with finish do begin
  1771.                     h := h - ff;
  1772.                     v := v - ff
  1773.                 end;
  1774.             MoveTo(start.h, start.v);
  1775.             LineTo(OldFinish.h, OldFinish.v);
  1776.             MoveTo(start.h, start.v);
  1777.             LineTo(finish.h, finish.v);
  1778.             ticks := TickCount;
  1779.             while ticks = TickCount do
  1780.                 ;
  1781.             x2 := finish.h - MidPoint.h;
  1782.             y2 := MidPoint.v - finish.v;
  1783.             GetAngle(x2, y2, angle2);
  1784.             with results do begin
  1785.                     if angle1 >= angle2 then
  1786.                         angle := angle1 - angle2
  1787.                     else
  1788.                         angle := angle2 - angle1;
  1789.                     if angle > 180.0 then
  1790.                         angle := 360.0 - angle;
  1791.                     Show1Value(angle, NoValue);
  1792.                 end;
  1793.         until GetNextEvent(mUpMask, event);
  1794.         DrawObject(LineObj, start, finish);
  1795.         nAngles := nAngles + 1;
  1796.         IncrementCounter;
  1797.         ClearResults(mCount);
  1798.         Orientation^[mCount] := angle;
  1799.         ValuesMessage := '';
  1800.         ShowValues;
  1801.         AppendResults;
  1802.         if nAngles = 1 then
  1803.             UpdateList;
  1804.         repeat
  1805.         until not GetNextEvent(EveryEvent, Event); {FlushEvent doesn't work under A/UX!}
  1806.         WhatToUndo := UndoEdit;
  1807.     end;
  1808.  
  1809.  
  1810.     procedure SaveBlankField;
  1811.         var
  1812.             SaveInfo, SaveBFInfo: InfoPtr;
  1813.             i, xLines, xPixelsPerLine: integer;
  1814.             src, dst: ptr;
  1815.             SaveFlag: boolean;
  1816.             name: str255;
  1817.     begin
  1818.         if (info^.PictureType = QuickCaptureType) or (info^.PictureType = ScionType) then begin
  1819.                 GetWTitle(info^.wptr, name);
  1820.                 if pos('(Corrected)', name) > 0 then begin
  1821.                         PutMessage('To save a blank field the captured image must be uncorrected.');
  1822.                         exit(SaveBlankField);
  1823.                     end;
  1824.                 SaveInfo := info;
  1825.                 SaveBFInfo := BlankFieldInfo;
  1826.                 BlankFieldInfo := nil; {Prevents StopDigitizing from doing shading correction.}
  1827.                 StopDigitizing;
  1828.                 BlankFieldInfo := SaveBFInfo;
  1829.                 if BlankFieldInfo = nil then begin
  1830.                         if not Duplicate('Blank Field', true) then
  1831.                             exit(SaveBlankField);
  1832.                     end;
  1833.                 src := info^.PicBaseAddr;
  1834.                 dst := BlankFieldInfo^.PicBaseAddr;
  1835.                 with Info^.PicRect do begin
  1836.                         xLines := bottom - top;
  1837.                         xPixelsPerLine := right - left;
  1838.                     end;
  1839.                 for i := 1 to xLines do begin
  1840.                         BlockMove(src, dst, xPixelsPerLine);
  1841.                         src := ptr(ord4(src) + info^.BytesPerRow);
  1842.                         dst := ptr(ord4(dst) + xPixelsPerLine);
  1843.                     end;
  1844.                 Info := BlankFieldInfo;
  1845.                 InvertPic;
  1846.                 SaveFlag := digitizing;
  1847.                 digitizing := false;
  1848.                 SelectAll(false);
  1849.                 ShowCount := false;
  1850.                 Measure;
  1851.                 ShowCount := true;
  1852.                 digitizing := SaveFlag;
  1853.                 BlankFieldMean := results.imean;
  1854.                 UndoLastMeasurement(false);
  1855.                 KillRoi;
  1856.                 UpdatePicWindow;
  1857.                 info := SaveInfo;
  1858.                 SelectWindow(Info^.wptr);
  1859.             end;
  1860.     end;
  1861.  
  1862.  
  1863.     procedure UndoLastMeasurement (DisplayResults: boolean);
  1864.     begin
  1865.         if mCount > 0 then begin
  1866.                 if DisplayResults then
  1867.                     DeleteLines(mCount, mCount);
  1868.                 mCount := mCount - 1;
  1869.                 if mCount = 0 then
  1870.                     UnsavedResults := false;
  1871.             end
  1872.         else
  1873.             WhatToUndo := NothingToUndo;
  1874.         if DisplayResults then
  1875.             ShowValues;
  1876.     end;
  1877.  
  1878.  
  1879.     function PixelInside (hloc, vloc: integer): boolean;
  1880.         var
  1881.             value: integer;
  1882.     begin
  1883.         value := MyGetPixel(hloc, vloc);
  1884.         case ThresholdingMode of
  1885.             DensitySlice: 
  1886.                 PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  1887.             GrayMapThresholding: 
  1888.                 PixelInside := value >= GrayMapThreshold;
  1889.             BinaryImage: 
  1890.                 PixelInside := value = BlackIndex;
  1891.         end;
  1892.     end;
  1893.  
  1894.  
  1895.     function TraceEdge (hstart, vstart: integer; StartingDirection: char; var TouchingEdge: boolean): boolean;
  1896.  
  1897.    {Traces the points(not pixels) that define the edge of an object using the following}
  1898.    {16 entry lookup table and converts the resulting outline to a QuickDraw region.}
  1899.  
  1900.       {Index  1234*  Code    Result}
  1901.  
  1902.       {0          0000     X      Should never happen}
  1903.       {1          000X     R      Go Right}
  1904.       {2          00X0     D     Go Down}
  1905.       {3          00XX     R     Go Right}
  1906.       {4          0X00     U     Go Up}
  1907.       {5          0X0X     U     Go Up}
  1908.       {6          0XX0     u      Go up or down depending on current direction}
  1909.       {7         0XXX     U      Go up}
  1910.       {8          X000     L      Go left}
  1911.       {9          X00X     l       Go left or right depending on current direction}
  1912.       {10       X0X0     D      Go down}
  1913.       {11        X0XX    R      Go right}
  1914.       {12        XX00     L      Go left}
  1915.       {13        XX0X     L      Go left}
  1916.       {14        XXX0     D     Go down}
  1917.       {15        XXXX     X     Should never happen}
  1918.  
  1919.        {*   1=Upper left pixel,  2=Upper right pixel, 3=Lower left pixel, 4=Lower right pixel}
  1920.  
  1921.         var
  1922.             count, hloc, vloc, hold, vold, index: integer;
  1923.             sqrt2, diagonal: extended;
  1924.             Saveport: GrafPtr;
  1925.             FindPerimeter, NonSquarePixels: boolean;
  1926.             direction, NewDirection: char;
  1927.             table: string[16];
  1928.             UL, UR, LL, LR, CuttingCorner: boolean;
  1929.             TempRgn: RgnHandle;
  1930.     begin
  1931.         TouchingEdge := false;
  1932.         table := 'XRDRUUuULlDRLLDX';
  1933.         GetPort(SavePort);
  1934.         SetPort(GrafPtr(info^.osPort));
  1935.         if SelectionMode <> NewSelection then
  1936.             TempRgn := NewRgn;
  1937.         with info^ do begin
  1938.                 uLength := 0.0;
  1939.                 cLength := 0.0;
  1940.                 FindPerimeter := not MakingLOI and ((LengthM in measurements) or (nLengths > 0) or WandAdjustAreas);
  1941.                 if FindPerimeter then begin
  1942.                         sqrt2 := sqrt(2.0);
  1943.                         CuttingCorner := false;
  1944.                     end;
  1945.                 NonSquarePixels := SpatiallyCalibrated and (PixelAspectRatio <> 1.0);
  1946.                 if NonSquarePixels then
  1947.                     diagonal := sqrt(sqr(1.0 / xSpatialScale) + sqr(1.0 / ySpatialScale));
  1948.                 count := 1;
  1949.                 PenNormal;
  1950.                 OpenRgn;
  1951.                 direction := StartingDirection;
  1952.                 hloc := hstart;
  1953.                 vloc := vstart;
  1954.                 UL := PixelInside(hloc - 1, vloc - 1);
  1955.                 UR := PixelInside(hloc, vloc - 1);
  1956.                 LL := PixelInside(hloc - 1, vloc);
  1957.                 LR := PixelInside(hloc, vloc);
  1958.                 hold := hstart;
  1959.                 vold := vstart;
  1960.                 MoveTo(hstart, vstart);
  1961.                 if CurrentTool = wand then begin
  1962.                         xCoordinates^[1] := hstart;
  1963.                         yCoordinates^[1] := vstart;
  1964.                         nCoordinates := 1;
  1965.                     end;
  1966.                 repeat
  1967.                     if IgnoreParticlesTouchingEdge then
  1968.                         with info^.PicRect do
  1969.                             TouchingEdge := TouchingEdge or (hloc = left) or (hloc = right) or (vloc = top) or (vloc = bottom);
  1970.                     count := count + 1;
  1971.                     index := 0;
  1972.                     if LR then
  1973.                         index := bor(index, 1);
  1974.                     if LL then
  1975.                         index := bor(index, 2);
  1976.                     if UR then
  1977.                         index := bor(index, 4);
  1978.                     if UL then
  1979.                         index := bor(index, 8);
  1980.                     NewDirection := table[index + 1];
  1981.                     if NewDirection = 'u' then begin
  1982.                             if direction = 'R' then
  1983.                                 NewDirection := 'U'
  1984.                             else
  1985.                                 NewDirection := 'D'
  1986.                         end;
  1987.                     if NewDirection = 'l' then begin
  1988.                             if direction = 'U' then
  1989.                                 NewDirection := 'L'
  1990.                             else
  1991.                                 NewDirection := 'R'
  1992.                         end;
  1993.                     case NewDirection of
  1994.                         'U':  begin
  1995.                                 vloc := vloc - 1;
  1996.                                 LL := UL;
  1997.                                 LR := UR;
  1998.                                 UL := PixelInside(hloc - 1, vloc - 1);
  1999.                                 UR := PixelInside(hloc, vloc - 1);
  2000.                             end;
  2001.                         'D':  begin
  2002.                                 vloc := vloc + 1;
  2003.                                 UL := LL;
  2004.                                 UR := LR;
  2005.                                 LL := PixelInside(hloc - 1, vloc);
  2006.                                 LR := PixelInside(hloc, vloc);
  2007.                             end;
  2008.                         'L':  begin
  2009.                                 hloc := hloc - 1;
  2010.                                 UR := UL;
  2011.                                 LR := LL;
  2012.                                 UL := PixelInside(hloc - 1, vloc - 1);
  2013.                                 LL := PixelInside(hloc - 1, vloc);
  2014.                             end;
  2015.                         'R':  begin
  2016.                                 hloc := hloc + 1;
  2017.                                 UL := UR;
  2018.                                 LL := LR;
  2019.                                 UR := PixelInside(hloc, vloc - 1);
  2020.                                 LR := PixelInside(hloc, vloc);
  2021.                             end;
  2022.                     end;
  2023.                     if FindPerimeter then begin
  2024.                             if CuttingCorner then
  2025.                                 CuttingCorner := false
  2026.                             else begin
  2027.                                     if NewDirection = direction then
  2028.                                         uLength := uLength + 1
  2029.                                     else begin
  2030.                                             uLength := uLength + sqrt2;
  2031.                                             CuttingCorner := true;
  2032.                                         end;
  2033.                                     if NonSquarePixels then begin
  2034.                                             if NewDirection = direction then
  2035.                                                 case NewDirection of
  2036.                                                     'L', 'R': 
  2037.                                                         cLength := cLength + 1.0 / xSpatialScale;
  2038.                                                     'U', 'D': 
  2039.                                                         cLength := cLength + 1.0 / ySpatialScale;
  2040.                                                 end
  2041.                                             else
  2042.                                                 cLength := cLength + diagonal;
  2043.                                         end; {NonSquarePixels}
  2044.                                 end;
  2045.                         end;
  2046.                     LineTo(hloc, vloc);
  2047.                     if CurrentTool = wand then begin
  2048.                             xCoordinates^[count] := hloc;
  2049.                             yCoordinates^[count] := vloc;
  2050.                             nCoordinates := count;
  2051.                         end;
  2052.                     hold := hloc;
  2053.                     vold := vloc;
  2054.                     direction := NewDirection;
  2055.                 until ((hloc = hstart) and (vloc = vstart) and (direction = StartingDirection)) or (count >= MaxCoordinates);
  2056.                 if SelectionMode <> NewSelection then
  2057.                     CloseRgn(TempRgn)
  2058.                 else
  2059.                     CloseRgn(roiRgn);
  2060.                 if count >= MaxCoordinates then begin
  2061.                         SetEmptyRgn(roiRgn);
  2062.                         SetPort(SavePort);
  2063.                         TraceEdge := false;
  2064.                         exit(TraceEdge);
  2065.                     end;
  2066.                 if (SelectionMode = AddSelection) then begin
  2067.                         if RgnNotTooBig(roiRgn, TempRgn) then
  2068.                             UnionRgn(roiRgn, TempRgn, roiRgn);
  2069.                     end
  2070.                 else if (SelectionMode = SubSelection) then begin
  2071.                         if RgnNotTooBig(roiRgn, TempRgn) then
  2072.                             DiffRgn(roiRgn, TempRgn, roiRgn);
  2073.                     end;
  2074.                 RoiShowing := true;
  2075.                 roiType := RgnRoi;
  2076.                 if SelectionMode = SubSelection then
  2077.                     UpdateScreen(RoiRect);
  2078.                 RoiRect := roiRgn^^.rgnBBox;
  2079.                 if FindPerimeter and (not NonSquarePixels) then begin
  2080.                         cLength := uLength;
  2081.                         if SpatiallyCalibrated then
  2082.                             cLength := cLength / xSpatialScale;
  2083.                     end;
  2084.             end; {with info}
  2085.         if SelectionMode <> NewSelection then
  2086.             DisposeRgn(TempRgn);
  2087.         SetPort(SavePort);
  2088.         TraceEdge := true;
  2089.     end;
  2090.  
  2091.  
  2092.     procedure MarkSelection (count: integer);
  2093.         var
  2094.             SavePort: GrafPtr;
  2095.             NumWidth, NumLeft, NumBottom, SaveForegroundIndex: integer;
  2096.             RoiWidth, inset, hcenter, vcenter: integer;
  2097.             str: str255;
  2098.             r: rect;
  2099.             OutlineWithEllipse: boolean;
  2100.             xc, yc: extended;
  2101.     begin
  2102.         OutlineWithEllipse := FitEllipse and OptionKeyWasDown;
  2103.         with info^ do begin
  2104.                 KillRoi;
  2105.                 SetupUndo;
  2106.                 WhatToUndo := UndoOutline;
  2107.                 GetPort(SavePort);
  2108.                 SetPort(GrafPtr(osPort));
  2109.                 SaveForegroundIndex := ForegroundIndex;
  2110.                 SetForegroundColor(WhiteIndex);
  2111.                 PenNormal;
  2112.                 TextFont(ApplFont);
  2113.                 TextSize(9);
  2114.                 NumToString(count, str);
  2115.                 with RoiRect do begin
  2116.                         NumWidth := StringWidth(str);
  2117.                         if AnalyzingParticles or OutlineWithEllipse then begin
  2118.                                 xc := xcenter^[count];
  2119.                                 yc := ycenter^[count];
  2120.                                 if SpatiallyCalibrated then begin
  2121.                                         xc := xc * xSpatialScale;
  2122.                                         yc := yc * ySpatialScale;
  2123.                                     end;
  2124.                                 hcenter := round(xc);
  2125.                                 vcenter := round(yc);
  2126.                                 if InvertYCoordinates then
  2127.                                     vcenter := PicRect.bottom - vcenter - 1
  2128.                             end
  2129.                         else begin
  2130.                                 hcenter := left + (right - left) div 2;
  2131.                                 vcenter := top + (bottom - top) div 2;
  2132.                             end;
  2133.                         NumLeft := hcenter - NumWidth div 2;
  2134.                         NumBottom := vcenter + 3;
  2135.                         if not BinaryPic and not AnalyzingParticles then begin
  2136.                                 FrameRgn(roiRgn);
  2137.                                 if OutlineWithEllipse then
  2138.                                     DrawEllipse;
  2139.                             end;
  2140.                     end;
  2141.                 PenNormal;
  2142.                 SetRect(r, NumLeft - 1, NumBottom - 9, NumLeft + NumWidth + 1, NumBottom + 1);
  2143.                 PaintRoundRect(r, 4, 4);
  2144.                 MoveTo(NumLeft, NumBottom);
  2145.                 TextMode(srcXor);
  2146.                 DrawString(str);
  2147.                 SetForegroundColor(SaveForegroundIndex);
  2148.                 if not analyzingParticles then
  2149.                     UpdateScreen(RoiRect);
  2150.                 SetPort(SavePort);
  2151.                 changes := true;
  2152.             end;
  2153.     end;
  2154.  
  2155.     function isBinaryImage: boolean;
  2156.         var
  2157.             SaveRoiRect: rect;
  2158.     begin
  2159.         with info^ do begin
  2160.                 SaveRoiRect := RoiRect;
  2161.                 RoiRect := PicRect;
  2162.                 GetRectHistogram;
  2163.                 BinaryPic := (histogram[0] + histogram[255]) = LongInt(PixelsPerLine) * nLines;
  2164.                 isBinaryImage := BinaryPic;
  2165.                 RoiRect := SaveRoiRect;
  2166.             end;
  2167.     end;
  2168.  
  2169.  
  2170.     function SetupAutoOutline (BinaryPixel: boolean): boolean;
  2171.     begin
  2172.         SetupAutoOutline := false;
  2173.         FindThresholdingMode;
  2174.         if ThresholdingMode = NoThresholding then
  2175.             if isBinaryImage or BinaryPixel then
  2176.                 ThresholdingMode := BinaryImage;
  2177.         if ThresholdingMode = NoThresholding then begin
  2178.                 PutMessage('Sorry, but you must be thresholding, or working with a binary image, to use the wand tool or to do particle analysis.');
  2179.                 exit(SetupAutoOutline);
  2180.             end;
  2181.         if (ThresholdingMode = GrayMapThresholding) and (GrayMapThreshold = 0) then begin
  2182.                 PutMessage(' Threshold must be non-zero.');
  2183.                 exit(SetupAutoOutline);
  2184.             end;
  2185.         if not MakingLOI then
  2186.             ShowWatch;
  2187.         SetupAutoOutline := true;
  2188.     end;
  2189.  
  2190.  
  2191.     procedure AutoOutline (start: point);
  2192.         var
  2193.             hloc, vloc: integer;
  2194.             TouchingEdge, BinaryPixel: boolean;
  2195.             direction: char;
  2196.             count: LongInt;
  2197.     begin
  2198.         ScreenToOffscreen(start);
  2199.         with start do
  2200.             BinaryPixel := (MyGetPixel(h, v) = WhiteIndex) or (MyGetPixel(h, v) = BlackIndex);
  2201.         if not SetupAutoOutline(BinaryPixel) then
  2202.             exit(AutoOutline);
  2203.         if SelectionMode = NewSelection then
  2204.             KillRoi;
  2205.         with info^ do begin
  2206.                 with start do
  2207.                     if PixelInside(h, v) then begin
  2208.                             repeat
  2209.                                 h := h + 1;
  2210.                             until not PixelInside(h, v);
  2211.                             if not PixelInside(h - 1, v - 1) then
  2212.                                 direction := 'R'
  2213.                             else if PixelInside(h, v - 1) then
  2214.                                 direction := 'L'
  2215.                             else
  2216.                                 direction := 'D';
  2217.                         end
  2218.                     else begin
  2219.                             repeat
  2220.                                 h := h + 1;
  2221.                             until PixelInside(h, v) or (h >= PicRect.right);
  2222.                             if h >= PicRect.right then begin
  2223.                                     beep;
  2224.                                     exit(AutoOutline);
  2225.                                 end;
  2226.                             direction := 'U';
  2227.                         end;
  2228.                 if TraceEdge(start.h, start.v, direction, TouchingEdge) then begin
  2229.                         WhatToUndo := NothingToUndo;
  2230.                         if WandAutoMeasure and not MakingLOI then begin
  2231.                                 GetNonRectHistogram;
  2232.                                 ComputeResults;
  2233.                                 if WandAdjustAreas then begin
  2234.                                         count := PixelCount^[mCount] + round(pLength^[mCount]);
  2235.                                         PixelCount^[mCount] := count;
  2236.                                         if SpatiallyCalibrated then
  2237.                                             mArea^[mCount] := count / (xSpatialScale * ySpatialScale)
  2238.                                         else
  2239.                                             mArea^[mCount] := count;
  2240.                                     end;
  2241.                                 ShowValues;
  2242.                                 AppendResults;
  2243.                                 WhatToUndo := UndoMeasurement;
  2244.                                 if LabelParticles then
  2245.                                     MarkSelection(mCount);
  2246.                             end;
  2247.                         if not (WandAutoMeasure and LabelParticles) then
  2248.                             RoiShowing := true;
  2249.                         if not MakingLOI then
  2250.                             UpdateScreen(RoiRect);
  2251.                     end; {if}
  2252.             end; {with info}
  2253.     end;
  2254.  
  2255.  
  2256.     procedure RedoMeasurement;
  2257.         var
  2258.             SaveN: integer;
  2259.             Canceled: boolean;
  2260.     begin
  2261.         if not isSelectionTool then begin
  2262.                 CurrentTool := SelectionTool;
  2263.                 isSelectionTool := true;
  2264.                 DrawTools;
  2265.             end;
  2266.         MeasurementToRedo := GetInt('Region measurent to redo:', mCount, Canceled);
  2267.         if canceled then
  2268.             exit(RedoMeasurement);
  2269.         if (MeasurementToRedo >= 1) and (MeasurementToRedo <= mCount) then begin
  2270.                 SaveN := mCount;
  2271.                 mCount := MeasurementToRedo;
  2272.                 ShowValues;
  2273.                 mCount := SaveN;
  2274.             end
  2275.         else begin
  2276.                 beep;
  2277.                 MeasurementToRedo := 0;
  2278.             end;
  2279.     end;
  2280.  
  2281.  
  2282.     procedure DeleteMeasurement;
  2283.         var
  2284.             nToDelete, i: integer;
  2285.             Canceled: boolean;
  2286.     begin
  2287.         nToDelete := GetInt('Measurent to delete:', mCount, Canceled);
  2288.         if (nToDelete >= 1) and (nToDelete <= mCount) and not Canceled then begin
  2289.                 for i := nToDelete to mCount - 1 do begin
  2290.                         mean^[i] := mean^[i + 1];
  2291.                         sd^[i] := sd^[i + 1];
  2292.                         PixelCount^[i] := PixelCount^[i + 1];
  2293.                         mArea^[i] := mArea^[i + 1];
  2294.                         mode^[i] := mode^[i + 1];
  2295.                         IntegratedDensity^[i] := IntegratedDensity^[i + 1];
  2296.                         idBackground^[i] := idBackground^[i + 1];
  2297.                         xcenter^[i] := xcenter^[i + 1];
  2298.                         ycenter^[i] := ycenter^[i + 1];
  2299.                         MajorAxis^[i] := MajorAxis^[i + 1];
  2300.                         MinorAxis^[i] := MinorAxis^[i + 1];
  2301.                         orientation^[i] := orientation^[i + 1];
  2302.                         mMin^[i] := mMin^[i + 1];
  2303.                         mMax^[i] := mMax^[i + 1];
  2304.                         plength^[i] := plength^[i + 1];
  2305.                     end; {for}
  2306.                 mCount := mCount - 1;
  2307.                 if mCount = 0 then begin
  2308.                         UnsavedResults := false;
  2309.                         beep;
  2310.                     end;
  2311.                 UpdateList;
  2312.             end
  2313.         else if not Canceled then
  2314.             beep;
  2315.     end;
  2316.  
  2317.  
  2318.     procedure AnalyzeParticles;
  2319.         var
  2320.             hloc, vloc, AlertID, index, MaxTriesPerLine, nParticles: integer;
  2321.             SaveSliceState, TouchingEdge, DrawOutlines, AutoSelectAll, finished, OutsideSelection: boolean;
  2322.             SaveForegroundIndex, SaveBackgroundIndex, EraseIndex, OutlineIndex: integer;
  2323.             tPort: GrafPtr;
  2324.             ScanRect: rect;
  2325.             side: (TopSide, RightSide, BottomSide, LeftSide);
  2326.             dstRgn: rgnHandle;
  2327.  
  2328.         function PixelInside: boolean;
  2329.             var
  2330.                 value: integer;
  2331.                 offset: LongInt;
  2332.                 p: ptr;
  2333.         begin
  2334.             with info^ do begin {MyGetPixel inlined to speed things up.}
  2335.                     offset := LongInt(vloc) * BytesPerRow + hloc;
  2336.                     p := ptr(ord4(PicBaseAddr) + offset);
  2337.                 end;
  2338.             value := BAND(p^, 255);
  2339.             case ThresholdingMode of
  2340.                 DensitySlice: 
  2341.                     PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  2342.                 GrayMapThresholding: 
  2343.                     PixelInside := value >= GrayMapThreshold;
  2344.                 BinaryImage: 
  2345.                     PixelInside := value = BlackIndex;
  2346.             end;
  2347.         end;
  2348.  
  2349.         procedure LabelBlobs;
  2350.             var
  2351.                 i: integer;
  2352.         begin
  2353.             if (nParticles <= MaxRegions) and (nParticles <= 200) then
  2354.                 for i := 1 to mCount do
  2355.                     MarkSelection(i);
  2356.         end;
  2357.  
  2358.     begin
  2359.         with info^ do begin
  2360.                 if NotInBounds or NoUndo then
  2361.                     exit(AnalyzeParticles);
  2362.                 if not SetupAutoOutline(false) then
  2363.                     exit(AnalyzeParticles);
  2364.                 StopDigitizing;
  2365.                 if RedirectSampling then begin
  2366.                         SetupRedirectedSampling;
  2367.                         if InfoForRedirect = nil then
  2368.                             exit(AnalyzeParticles)
  2369.                     end;
  2370.                 AutoSelectAll := not RoiShowing;
  2371.                 if AutoSelectAll then
  2372.                     SelectAll(false);
  2373.                 ScanRect := RoiRect;
  2374.                 if not AutoSelectAll then
  2375.                     with ScanRect do begin
  2376.                             left := picrect.left;
  2377.                             right := PicRect.right;
  2378.                         end;
  2379.                 KillRoi;
  2380.                 if UnsavedResults then begin
  2381.                         ResetCounter;
  2382.                         if UnsavedResults then
  2383.                             exit(AnalyzeParticles);
  2384.                         UpdatePicWindow;
  2385.                     end;
  2386.                 SetupUndoFromClip;
  2387.                 SaveSliceState := DensitySlicing;
  2388.                 SaveForegroundIndex := ForegroundIndex;
  2389.                 SaveBackgroundIndex := BackgroundIndex;
  2390.                 SetForegroundColor(WhiteIndex);
  2391.                 DensitySlicing := false;
  2392.                 DrawOutlines := false;
  2393.                 case ThresholdingMode of
  2394.                     DensitySlice:  begin
  2395.                             EraseIndex := SliceStart - 1;
  2396.                             if EraseIndex < 0 then
  2397.                                 EraseIndex := WhiteIndex;
  2398.                             DrawOutlines := OutlineParticles;
  2399.                             OutLineIndex := BlackIndex;
  2400.                         end;
  2401.                     GrayMapThresholding:  begin
  2402.                             EraseIndex := GrayMapThreshold - 1;
  2403.                             if EraseIndex < 0 then
  2404.                                 EraseIndex := WhiteIndex;
  2405.                         end;
  2406.                     BinaryImage:  begin
  2407.                             DrawOutlines := OutlineParticles;
  2408.                             OutLineIndex := 254;
  2409.                             EraseIndex := 128;
  2410.                         end;
  2411.                 end;
  2412.                 AnalyzingParticles := true;
  2413.                 nParticles := 0;
  2414.                 GetPort(tPort);
  2415.                 SetPort(GrafPtr(osPort));
  2416.                 dstRgn := NewRgn;
  2417.                 SelectionMode := NewSelection;
  2418.                 ShowWatch;
  2419.                 with ScanRect do
  2420.                     for vloc := top to bottom - 1 do
  2421.                         for hloc := left to right - 1 do begin
  2422.                                 if PixelInside then begin
  2423.                                         if TraceEdge(hloc, vloc, 'U', TouchingEdge) then begin
  2424.                                                 nParticles := nParticles + 1;
  2425.                                                 RoiShowing := false;
  2426.                                                 if mCount < MaxRegions then begin
  2427.                                                         GetNonRectHistogram;
  2428.                                                         ComputeResults;
  2429.                                                     end;
  2430.                                                 SetBackgroundColor(EraseIndex);
  2431.                                                 EraseRgn(roiRgn);
  2432.                                                 if AutoSelectAll then
  2433.                                                     OutSideSelection := false
  2434.                                                 else begin
  2435.                                                         SectRgn(roiRgn, NoInfo^.RoiRgn, dstRgn);
  2436.                                                         OutSideSelection := EmptyRgn(dstRgn);
  2437.                                                     end;
  2438.                                                 if (PixelCount^[mCount] < MinParticleSize) or (PixelCount^[mCount] > MaxParticleSize) or TouchingEdge or OutsideSelection then begin
  2439.                                                         mCount := mCount - 1;
  2440.                                                         nParticles := nParticles - 1;
  2441.                                                         UpdateScreen(RoiRect);
  2442.                                                     end
  2443.                                                 else begin
  2444.                                                         if DrawOutlines then begin
  2445.                                                                 SetForegroundColor(OutlineIndex);
  2446.                                                                 FrameRgn(roiRgn);
  2447.                                                             end;
  2448.                                                         UpdateScreen(RoiRect);
  2449.                                                         if nParticles <= MaxRegions then begin
  2450.                                                                 ShowValues;
  2451.                                                                 AppendResults;
  2452.                                                             end
  2453.                                                         else
  2454.                                                             ShowMessage(long2str(nParticles));
  2455.                                                         if nParticles = MaxRegions then
  2456.                                                             beep;
  2457.                                                         if CommandPeriod or (AnalyzingParticles = false) then begin {quit}
  2458.                                                                 beep;
  2459.                                                                 SetPort(tPort);
  2460.                                                                 if LabelParticles then
  2461.                                                                     LabelBlobs;
  2462.                                                                 DensitySlicing := SaveSliceState;
  2463.                                                                 SetForegroundColor(SaveForegroundIndex);
  2464.                                                                 SetBackgroundColor(SaveBackgroundIndex);
  2465.                                                                 KillRoi;
  2466.                                                                 UpdatePicWindow;
  2467.                                                                 WhatToUndo := UndoEdit;
  2468.                                                                 UndoFromClip := true;
  2469.                                                                 AnalyzingParticles := false;
  2470.                                                                 DisposeRgn(dstRgn);
  2471.                                                                 exit(AnalyzeParticles);
  2472.                                                             end; {quit}
  2473.                                                     end;
  2474.                                             end;  {if TraceEdge}
  2475.                                     end; {if PixelInside}
  2476.                             end; {for}
  2477.             end; {with}
  2478.         SetPort(tPort);
  2479.         if LabelParticles then
  2480.             LabelBlobs;
  2481.         DensitySlicing := SaveSliceState;
  2482.         SetForegroundColor(SaveForegroundIndex);
  2483.         SetBackgroundColor(SaveBackgroundIndex);
  2484.         KillRoi;
  2485.         UpdatePicWindow;
  2486.         if ThresholdingMode = GrayMapThresholding then
  2487.             ResetGrayMap;
  2488.         WhatToUndo := UndoEdit;
  2489.         UndoFromClip := true;
  2490.         AnalyzingParticles := false;
  2491.         DisposeRgn(dstRgn);
  2492.     end;
  2493.  
  2494. end.