home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Applications / NIH Image 1.55 / Source / Analysis.p next >
Encoding:
Text File  |  1994-04-20  |  71.9 KB  |  2,154 lines  |  [TEXT/PJMM]

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