home *** CD-ROM | disk | FTP | other *** search
/ Carousel Volume 2 #1 / carousel.iso / mactosh / lang / p_image1.sit / LSP Source / Analysis.p next >
Encoding:
Text File  |  1989-07-14  |  44.5 KB  |  1,784 lines

  1. unit Analysis;
  2.  
  3. {Analysis routines used by the Image program}
  4.  
  5. interface
  6.  
  7.     uses
  8.         QuickDraw, OSIntf, PickerIntf, PrintTraps, ToolIntf, globals, Utilities, LeastSquares, Graphics, FileUnit;
  9.  
  10.  
  11.  
  12.     procedure DoHistogram;
  13.     procedure GetRectHistogram;
  14.     procedure GetNonRectHistogram;
  15.     procedure ShowContinuousHistogram;
  16.     procedure ComputeResults;
  17.     procedure Measure;
  18.     procedure ComputeLength (nvertices: integer; var xa, ya: xyArray; FindingPerimeterLength: boolean);
  19.     procedure DoProfilePlotOptions;
  20.     procedure ListResults;
  21.     procedure ColumnAveragePlot;
  22.     procedure SetScale;
  23.     procedure Calibrate;
  24.     procedure ResetCounters;
  25.     procedure DoMeasurementOptions;
  26.     procedure DoPoints (event: EventRecord);
  27.     procedure FindAngle (event: EventRecord);
  28.     procedure SaveBlankField;
  29.     procedure UndoLastMeasurement;
  30.     procedure NumberSelection (count: integer);
  31.     procedure AutoOutline (start: point);
  32.  
  33.  
  34. implementation
  35.  
  36.     var
  37.         WandMode: (LUTMode, GrayMapMode, BinaryMode);
  38.  
  39.  
  40.     procedure DoHistogramOfLine (data: ptr; var histogram: HistogramType; width: LongInt);
  41. {}
  42. {TYPE}
  43. {  lptr=^LineType;}
  44. {VAR}
  45. {  line:lptr;}
  46. {  i,value:integer;}
  47. {BEGIN}
  48. {  line:=lptr(data);}
  49. {  FOR i:=0 TO width-1 DO BEGIN}
  50. {    value:=line^[i];}
  51. {    histogram[value]:=histogram[value]+1;}
  52. {  END;}
  53. {}
  54.     {a0=data}
  55.     {a1=histogram}
  56.     {d0=width}
  57.     {d1=pixel value}
  58.     inline
  59.         $4E56, $0000, {  link a6,#0}
  60.         $48E7, $C0C0, {  movem.l a0-a1/d0-d1,-(sp)}
  61.         $206E, $000C, {  move.l 12(a6),a0}
  62.         $226E, $0008, {  move.l 8(a6),a1}
  63.         $202E, $0004, {  move.l 4(a6),d0}
  64.         $5380,       {  subq.l #1,d0}
  65.         $4281,       {L clr.l d1}
  66.         $1218,       {  move.b (a0)+,d1}
  67.         $E541,       {  asl.w #2,d1}
  68.         $52B1, $1800, {  addq.l #1,0(a1,d1.l)}
  69.         $51C8, $FFF4, {  dbra d0,L}
  70.         $4CDF, $0303, {  movem.l (sp)+,a0-a1/d0-d1}
  71.         $4E5E,       {  unlk a6}
  72.         $DEFC, $000C; {  add.w #12,sp}
  73. {END;}
  74.  
  75.  
  76.     procedure GetRectHistogram;
  77.         var
  78.             width, i, NumberOfLines: integer;
  79.             offset: LongInt;
  80.             p: ptr;
  81.     begin
  82.         ShowWatch;
  83.         for i := 0 to 255 do
  84.             Histogram[i] := 0;
  85.         with info^.osroiRect, info^ do begin
  86.                 offset := LongInt(top) * BytesPerRow + left;
  87.                 p := ptr(ord4(PicBaseAddr) + offset);
  88.                 width := right - left;
  89.                 NumberOfLines := bottom - top;
  90.             end;
  91.         if width > 0 then
  92.             for i := 1 to NumberOfLines do begin
  93.                     DoHistogramOfLine(p, histogram, width);
  94.                     p := ptr(ord4(p) + info^.BytesPerRow);
  95.                 end
  96.     end;
  97.  
  98.  
  99.     procedure GetNonRectHistogram;
  100.         var
  101.             tPort: GrafPtr;
  102.             MaskLine, DataLine: LineType;
  103.             width, i, vloc: integer;
  104.             sum, sum2, count, OverFlows: LongInt;
  105.             SaveInfo: InfoPtr;
  106.             BoundingRect: rect;
  107.             value: LongInt;
  108.     begin
  109.         ShowWatch;
  110.         GetPort(tPort);
  111.         SetupUndo;
  112.         UndoInfoRec := info^;
  113.         UndoInfo := @UndoInfoRec;
  114.         with UndoInfo^ do begin
  115.                 PicBaseAddr := UndoBuf;
  116.                 BytesPerRow := PixelsPerLine;
  117.             end;
  118.         SaveInfo := Info;
  119.         for i := 0 to 255 do
  120.             Histogram[i] := 0;
  121.         with Info^ do begin
  122.                 SetPort(GrafPtr(osPort));
  123.                 with osPort^ do
  124.                     if fgcolor = bkcolor then
  125.                         bkColor := 255 - ForegroundColor;
  126.                 PenNormal;
  127.                 BoundingRect := osroiRect;
  128.                 EraseRect(BoundingRect);
  129.                 PaintRgn(osroiRgn);
  130.                 UpdateScreen(roiRect);
  131.             end;
  132.         with BoundingRect do begin
  133.                 width := right - left;
  134.                 for vloc := top to bottom - 1 do begin
  135.                         Info := UndoInfo;
  136.                         GetLine(left, vloc, width, DataLine);
  137.                         Info := SaveInfo;
  138.                         GetLine(left, vloc, width, MaskLine);
  139.                         for i := 0 to width - 1 do
  140.                             if MaskLine[i] = ForegroundColor then begin
  141.                                     value := DataLine[i];
  142.                                     histogram[value] := histogram[value] + 1;
  143.                                 end;
  144.                     end;
  145.             end;
  146.         undo;
  147.         with info^ do begin
  148.                 UpdateScreen(roiRect);
  149.                 osPort^.bkColor := BackgroundColor;
  150.             end;
  151.         SetPort(tPort);
  152.     end;
  153.  
  154.  
  155.     procedure ComputeResults;
  156.         var
  157.             MaxCount, icount, isum: LongInt;
  158.             i: integer;
  159.             sum, sum2, ri, rcount, UncalibratedMean, tSD, rmode, xc, yc: extended;
  160.     begin
  161.         with results do begin
  162.                 if Thresholding then
  163.                     i := ThresholdStart
  164.                 else
  165.                     i := 0;
  166.                 while (histogram[i] = 0) and (i < 255) do
  167.                     i := i + 1;
  168.                 min := value[i];
  169.                 imin := i;
  170.                 if Thresholding then
  171.                     i := ThresholdEnd
  172.                 else
  173.                     i := 255;
  174.                 while (histogram[i] = 0) and (i > 0) do
  175.                     i := i - 1;
  176.                 max := value[i];
  177.                 imax := i;
  178.                 MaxCount := 0;
  179.                 sum := 0.0;
  180.                 isum := 0;
  181.                 sum2 := 0.0;
  182.                 n := 0;
  183.                 for i := imin to imax do begin
  184.                         icount := histogram[i];
  185.                         rcount := icount;
  186.                         sum := sum + rcount * value[i];
  187.                         isum := isum + icount * i;
  188.                         ri := i;
  189.                         sum2 := sum2 + sqr(value[i]) * rcount;
  190.                         n := n + icount;
  191.                         if icount > MaxCount then begin
  192.                                 MaxCount := icount;
  193.                                 rmode := value[i];
  194.                                 imode := i
  195.                             end;
  196.                     end;
  197.                 if ContinuousHistoGram then
  198.                     exit(ComputeResults);
  199.                 if n > 0 then begin
  200.                         tmean := sum / n;
  201.                         UncalibratedMean := isum / n
  202.                     end
  203.                 else begin
  204.                         tmean := 0.0;
  205.                         UncalibratedMean := 0.0
  206.                     end;
  207.                 imean := round(UncalibratedMean);
  208.                 if nAreas < MaxAreas then begin
  209.                         nAreas := nAreas + 1;
  210.                         UnsavedAreas := UnsavedAreas + 1
  211.                     end
  212.                 else
  213.                     beep;
  214.                 mean[nAreas] := tmean;
  215.                 if nAreas <= MaxStandards then
  216.                     umean[nAreas] := UncalibratedMean;
  217.                 if (n > 0) and (tmean > 0.0) then begin
  218.                         rcount := n;
  219.                         tSD := (rcount * Sum2 - sqr(sum)) / rcount;
  220.                         if tSD > 0.0 then
  221.                             tSD := sqrt(tSD / (rcount - 1.0))
  222.                         else
  223.                             tSD := 0.0
  224.                     end
  225.                 else
  226.                     tSD := 0.0;
  227.                 SD[nAreas] := tSD;
  228.                 if xyLocM in measurements then
  229.                     with info^, info^.osRoiRect do begin
  230.                             xc := left + (right - left) / 2;
  231.                             yc := top + (bottom - top) / 2;
  232.                             yc := PicRect.bottom - yc - 1.0;
  233.                             if scale <> 0.0 then begin
  234.                                     xc := xc / scale;
  235.                                     yc := yc / scale;
  236.                                 end;
  237.                             xcenter[nAreas] := xc;
  238.                             ycenter[nAreas] := yc;
  239.                         end
  240.                 else begin
  241.                         xcenter[nAreas] := 0.0;
  242.                         ycenter[nAreas] := 0.0
  243.                     end;
  244.                 PixelCount[nAreas] := n;
  245.                 mode[nAreas] := rmode;
  246.                 if PerimeterM in measurements then
  247.                     if (CurrentTool = FreehandTool) or (CurrentTool = PolygonTool) then
  248.                         plength[nAreas] := Length
  249.                     else
  250.                         with info^, info^.osroirect do begin
  251.                                 case RoiType of
  252.                                     RectRoi, RoundRectRoi: 
  253.                                         length := ((right - left) + (bottom - top)) * 2.0;
  254.                                     OvalRoi: 
  255.                                         length := pi * ((right - left) + (bottom - top)) / 2.0;
  256.                                     otherwise
  257.                                 end;
  258.                                 if scale <> 0.0 then
  259.                                     length := length / scale;
  260.                                 plength[nAreas] := length;
  261.                             end;
  262.             end; {with}
  263.         measuring := true;
  264.     end;
  265.  
  266.  
  267.     procedure Measure;
  268.         var
  269.             AutoSelectAll: boolean;
  270.     begin
  271.         if NotInBounds then
  272.             exit(Measure);
  273.         AutoSelectAll := not info^.RoiShowing;
  274.         if AutoSelectAll then
  275.             SelectAll(false);
  276.         if info^.RoiType = RectRoi then
  277.             GetRectHistogram
  278.         else
  279.             GetNonRectHistogram;
  280.         ComputeResults;
  281.         ShowResults;
  282.         info^.RoiShowing := true;
  283.         WhatToUndo := UndoMeasurement;
  284.         if AutoSelectAll then
  285.             KillRoi;
  286.         UpdateScreen(OldRoiRect);
  287.     end;
  288.  
  289.  
  290.     procedure ShowHistogram;
  291.         var
  292.             htop: integer;
  293.             tport: GrafPtr;
  294.             hrect, prect, srect: rect;
  295.     begin
  296.         GetPort(tPort);
  297.         if HistoWindow = nil then begin
  298.                 htop := ScreenHeight - hheight - 10;
  299.                 SetRect(HistoRect, hleft, htop, hleft + hwidth, htop + hheight);
  300.                 HistoWindow := NewWindow(nil, HistoRect, 'Histogram', true, NoGrowDocProc, nil, true, 0);
  301.                 WindowPeek(HistoWindow)^.WindowKind := HistoKind;
  302.                 SetMenuItem(GetMHandle(WindowsMenu), 7, true);
  303.             end;
  304.         SelectWindow(HistoWindow);
  305.         SetPort(HistoWindow);
  306.         InvalRect(HistoWindow^.PortRect);
  307.         SetPort(tPort);
  308.     end;
  309.  
  310.  
  311.     procedure DoHistogram;
  312.         var
  313.             AutoSelectAll: boolean;
  314.     begin
  315.         if digitizing then begin
  316.                 if ContinuousHistogram then
  317.                     ContinuousHistogram := false
  318.                 else begin
  319.                         ContinuousHistogram := true;
  320.                         if info <> NoInfo then
  321.                             with info^ do begin
  322.                                     RoiType := NoRoi;
  323.                                     osroiRect := SrcRect;
  324.                                 end;
  325.                     end;
  326.                 exit(DoHistogram)
  327.             end;
  328.         if NotInBounds then
  329.             exit(DoHistogram);
  330.         AutoSelectAll := not info^.RoiShowing;
  331.         if AutoSelectAll then
  332.             SelectAll(false);
  333.         if info^.RoiType = RectRoi then
  334.             GetRectHistogram
  335.         else
  336.             GetNonRectHistogram;
  337.         ComputeResults;
  338.         ShowHistogram;
  339.         ShowResults;
  340.         WhatToUndo := UndoMeasurement;
  341.         if AutoSelectAll then
  342.             KillRoi;
  343.     end;
  344.  
  345.  
  346.     procedure ShowContinuousHistogram;
  347.         const
  348.             skip = 10;
  349.         var
  350.             width, i, NumberOfLines: integer;
  351.             offset: LongInt;
  352.             p: ptr;
  353.     begin
  354.         for i := 0 to 255 do
  355.             Histogram[i] := 0;
  356.         with info^.PicRect, info^ do begin
  357.                 p := PicBaseAddr;
  358.                 width := right - left;
  359.                 NumberOfLines := ((bottom - top) div skip) - 1;
  360.                 offset := BytesPerRow * skip;
  361.             end;
  362.         for i := 1 to NumberOfLines do begin
  363.                 DoHistogramOfLine(p, histogram, width);
  364.                 p := ptr(ord4(p) + offset);
  365.             end;
  366.         ComputeResults;
  367.         ShowHistogram;
  368.     end;
  369.  
  370.  
  371.     procedure ColumnAveragePlot;
  372.         var
  373.             vloc, value, width, height, i: integer;
  374.             sum: array[0..MaxPixelsPerLine] of LongInt;
  375.             start: point;
  376.             tPort: GrafPtr;
  377.     begin
  378.         if NoSelection or NotRectangular or NotInBounds then
  379.             exit(ColumnAveragePlot);
  380.         ShowWatch;
  381.         with info^.osroiRect do begin
  382.                 width := right - left;
  383.                 height := bottom - top;
  384.                 for i := 0 to width - 1 do
  385.                     sum[i] := 0;
  386.                 for vloc := top to bottom - 1 do begin
  387.                         GetLine(left, vloc, width, PlotData);
  388.                         for i := 0 to width - 1 do
  389.                             sum[i] := sum[i] + PlotData[i];
  390.                     end;
  391.                 start.h := left;
  392.                 start.v := bottom;
  393.                 OffscreenToScreen(start);
  394.             end;
  395.         for i := 0 to width - 1 do
  396.             PlotData[i] := sum[i] div height;
  397.         PlotCount := width;
  398.         PlotAvg := height;
  399.         SetupPlot(PlotData, start);
  400.     end;
  401.  
  402.  
  403.     procedure SetScale;
  404.         const
  405.             DistanceID = 3;
  406.             ScaleID = 15;
  407.             UnitsTextID = 17;
  408.         var
  409.             mylog: DialogPtr;
  410.             item, i, SaveUnitsID: integer;
  411.             distance, SaveScale: extended;
  412.             ignore: integer;
  413.             str: str255;
  414.             SaveUnits: string[2];
  415.     begin
  416.         if nLengths = 0 then begin
  417.                 PutMessage('Before setting the scale you must use the ruler tool to measure a known ', 'distance along a ruler or other scale.', '');
  418.                 exit(SetScale)
  419.             end;
  420.         InitCursor;
  421.         with info^ do begin
  422.                 SaveUnits := units;
  423.                 SaveUnitsID := UnitsID;
  424.                 SaveScale := scale;
  425.                 distance := 0.0;
  426.                 mylog := GetNewDialog(10, nil, pointer(-1));
  427.                 SetDReal(MyLog, DistanceID, distance, 1);
  428.                 SelIText(MyLog, DistanceID, 0, 32767);
  429.                 SetDReal(MyLog, ScaleID, scale, 2);
  430.                 SetDString(MyLog, UnitsTextID, units);
  431.                 OutlineButton(MyLog, ok, 16);
  432.                 SetDialogItem(mylog, UnitsID, 1);
  433.                 repeat
  434.                     ModalDialog(nil, item);
  435.                     if item = distanceID then
  436.                         distance := GetDReal(MyLog, DistanceID);
  437.                     if (item >= 5) and (item <= 13) then begin
  438.                             for i := 5 to 13 do
  439.                                 SetDialogItem(mylog, i, 0);
  440.                             SetDialogItem(mylog, item, 1);
  441.                             UnitsID := item;
  442.                             case UnitsID of
  443.                                 5: 
  444.                                     units := 'nm';
  445.                                 6: 
  446.                                     units := '╡m';
  447.                                 7: 
  448.                                     units := 'mm';
  449.                                 8: 
  450.                                     units := 'cm';
  451.                                 9: 
  452.                                     units := 'm ';
  453.                                 10: 
  454.                                     units := 'km';
  455.                                 11: 
  456.                                     units := 'in';
  457.                                 12: 
  458.                                     units := 'ft';
  459.                                 13: 
  460.                                     units := 'mi';
  461.                             end;
  462.                         end;
  463.                     if distance > 0.0 then
  464.                         scale := PixelLength / distance;
  465.                     SetDReal(MyLog, ScaleID, scale, 2);
  466.                     SetDString(MyLog, UnitsTextID, units);
  467.                 until (item = ok) or (item = cancel);
  468.                 DisposDialog(mylog);
  469.                 if item = cancel then begin
  470.                         units := SaveUnits;
  471.                         UnitsID := SaveUnitsID;
  472.                         scale := SaveScale;
  473.                     end;
  474.             end; {with info^}
  475.     end;
  476.  
  477.  
  478.     procedure SetupCalibrationPlot;
  479.         const
  480.             hrange = 1024;
  481.             hmax = 1023;
  482.             vrange = 600;
  483.             vmax = 599;
  484.             SymbolSize = 11;
  485.         var
  486.             fRect, tRect: rect;
  487.             svalue, range, hscale, vscale, MinV, MaxV: extended;
  488.             tPort: GrafPtr;
  489.             i, hloc, vloc: integer;
  490.             ClipRegion, SaveClipRegion: RgnHandle;
  491.             pt: point;
  492.     begin
  493.         PlotLeftMargin := 35;
  494.         PlotTopMargin := 15;
  495.         PlotBottomMargin := 30;
  496.         PlotRightMargin := 100;
  497.         MinV := MinValue;
  498.         MaxV := MaxValue;
  499.         for i := 1 to nStandards do begin
  500.                 svalue := StandardValues[i];
  501.                 if svalue < MinV then
  502.                     MinV := svalue;
  503.                 if svalue > MaxV then
  504.                     MaxV := svalue;
  505.             end;
  506.         range := MaxV - MinV;
  507.         PlotWidth := hrange div 3 + PlotLeftMargin + PlotRightMargin;
  508.         PlotHeight := vrange div 3 + PlotTopMargin + PlotBottomMargin;
  509.         PlotLeft := 64;
  510.         PlotTop := 64;
  511.         PlotCount := 256;
  512.         MakePlotWindow(PlotLeft, PlotTop, PlotWidth, PlotHeight);
  513.         WindowPeek(PlotWindow)^.WindowKind := CalibrationPlotKind;
  514.         SetRect(fRect, -SymbolSize, -SymbolSize, hmax + SymbolSize, vmax + SymbolSize);
  515.         GetPort(tPort);
  516.         SetPort(PlotWindow);
  517.         SaveClipRegion := PlotWindow^.ClipRgn;
  518.         ClipRegion := NewRgn;
  519.         OpenRgn;
  520.         FrameRect(fRect);
  521.         CloseRgn(ClipRegion);
  522.         PlotWindow^.ClipRgn := ClipRegion;
  523.         hscale := 256 / hrange;
  524.         vscale := range / vrange;
  525.         PlotPICT := OpenPicture(fRect);
  526.         for i := 1 to nStandards do begin
  527.                 hloc := round(umean[i] / hscale);
  528.                 vloc := vmax - round((StandardValues[i] - MinValue) / vscale);
  529.                 SetRect(tRect, hloc - SymbolSize, vloc - SymbolSize, hloc + SymbolSize, vloc + SymbolSize);
  530.                 FrameOval(tRect);
  531.             end;
  532.         MoveTo(0, vmax - round((value[0] - MinValue) / vscale));
  533.         for i := 1 to 255 do begin
  534.                 hloc := round(i / hscale);
  535.                 vloc := vmax - round((value[i] - MinValue) / vscale);
  536.                 LineTo(hloc, vloc);
  537.             end;
  538.         ClosePicture;
  539.         PlotWindow^.ClipRgn := SaveClipRegion;
  540.         DisposeRgn(ClipRegion);
  541.         InvalRect(PlotWindow^.PortRect);
  542.         SetPort(tPort);
  543.         SelectWindow(PlotWindow);
  544.     end;
  545.  
  546.  
  547.     procedure DoCurveFitting;
  548.         var
  549.             i: integer;
  550.             XData, YData, YFit, Residuals: TNColumnVector;
  551.             Solution: TNRowVector; {Coefficients}
  552.             TypeFit: FitType;
  553.             Variance: extended;
  554.             SumResidualsSqr, SumStandards, mean, SumMeanDiffSqr, DegreesOfFreedom: extended;
  555.             err: byte;
  556.             fil: text;
  557.             str1, str2: str255;
  558.     begin
  559.         with info^ do begin
  560.                 ShowWatch;
  561.                 for i := 1 to nStandards do begin
  562.                         XData[i] := umean[i];
  563.                         YData[i] := StandardValues[i];
  564.                     end;
  565.                 case fit of
  566.                     StrightLine: 
  567.                         begin
  568.                             nCoefficients := 2;
  569.                             TypeFit := poly
  570.                         end;
  571.                     Poly2: 
  572.                         begin
  573.                             nCoefficients := 3;
  574.                             TypeFit := poly
  575.                         end;
  576.                     Poly3: 
  577.                         begin
  578.                             nCoefficients := 4;
  579.                             TypeFit := poly
  580.                         end;
  581.                     Poly4: 
  582.                         begin
  583.                             nCoefficients := 5;
  584.                             TypeFit := poly
  585.                         end;
  586.                     Poly5: 
  587.                         begin
  588.                             nCoefficients := 6;
  589.                             TypeFit := poly
  590.                         end;
  591.                     ExpoFit: 
  592.                         begin
  593.                             nCoefficients := 2;
  594.                             TypeFit := expo
  595.                         end;
  596.                     PowerFit: 
  597.                         begin
  598.                             nCoefficients := 2;
  599.                             TypeFit := power
  600.                         end;
  601.                     LogFit: 
  602.                         begin
  603.                             nCoefficients := 2;
  604.                             TypeFit := log
  605.                         end;
  606.                 end;
  607.                 DegreesOfFreedom := nStandards - nCoefficients;
  608.                 if DegreesOfFreedom < 0 then begin
  609.                         FitGoodness := 0.0;
  610.                         calibrated := false;
  611.                         NumToString(nCoefficients, str1);
  612.                         case fit of
  613.                             StrightLine: 
  614.                                 str2 := 'straight line';
  615.                             Poly2: 
  616.                                 str2 := '2nd degree polynomial';
  617.                             Poly3: 
  618.                                 str2 := '3rd degree polynomial';
  619.                             Poly4: 
  620.                                 str2 := '4th degree polynomial';
  621.                             Poly5: 
  622.                                 str2 := '5th degree polynomial';
  623.                             ExpoFit: 
  624.                                 str2 := 'exponential';
  625.                             PowerFit: 
  626.                                 str2 := 'power';
  627.                             LogFit: 
  628.                                 str2 := 'log';
  629.                         end;
  630.                         str2 := concat(' standards to do ', str2, ' fitting.');
  631.                         PutMessage('You need at least ', str1, str2);
  632.                         exit(DoCurveFitting)
  633.                     end;
  634.                 LeastSquares(nStandards, XData, YData, nCoefficients, Solution, YFit, Residuals, FitSD, Variance, err, TypeFit);
  635.                 if err = 111 then begin {Borland's curve fitting routine is missing.}
  636.                         beep;
  637.                         exit(DoCurveFitting)
  638.                     end;
  639.                 for i := 1 to nCoefficients do
  640.                     Coefficient[i] := solution[i];
  641.                 calibrated := true;
  642.                 GenerateValues;
  643.                 SumResidualsSqr := 0.0;
  644.                 SumStandards := 0.0;
  645.                 for i := 1 to nStandards do begin
  646.                         SumResidualsSqr := SumResidualsSqr + sqr(residuals[i]);
  647.                         SumStandards := SumStandards + StandardValues[i];
  648.                     end;
  649.                 mean := SumStandards / nStandards;
  650.                 SumMeanDiffSqr := 0.0;
  651.                 for i := 1 to nStandards do
  652.                     SumMeanDiffSqr := SumMeanDiffSqr + sqr(StandardValues[i] - Mean);
  653.                 if SumMeanDiffSqr > 0.0 then
  654.                     FitGoodness := 1 - (SumResidualsSqr / DegreesOfFreedom) * ((nStandards - 1) / SumMeanDiffSqr)
  655.                 else
  656.                     FitGoodness := 1.0;
  657.             end;
  658.         info^.changes := true;
  659.     end;
  660.  
  661.  
  662.     procedure GetLineFromFile (f: integer; var str: string);
  663.         var
  664.             err: osErr;
  665.             a: packed array[1..2] of char;
  666.             c: char;
  667.             ByteCount: LongInt;
  668.             done: boolean;
  669.     begin
  670.         str := '';
  671.         repeat
  672.             ByteCount := 1;
  673.             err := fsRead(f, ByteCount, @a);
  674.             c := a[1];
  675.             done := (c = return) or (err <> NoErr);
  676.             if not done then
  677.                 str := concat(str, c);
  678.         until (c = return) or (err <> NoErr);
  679.     end;
  680.  
  681.  
  682.     procedure GetStandardsFromFile (mylog: DialogPtr; FirstID: integer);
  683.         var
  684.             where: Point;
  685.             typeList: SFTypeList;
  686.             reply: SFReply;
  687.             err: OSErr;
  688.             str: string;
  689.             f, i: integer;
  690.     begin
  691.         where.v := 120;
  692.         where.h := 120;
  693.         typeList[0] := 'TEXT';
  694.         SFGetFile(Where, '', nil, 1, typeList, nil, reply);
  695.         if reply.good then
  696.             with reply do begin
  697.                     err := FSOpen(fname, vRefNum, f);
  698.                     err := SetFPos(f, fsFromStart, 0);
  699.                     for i := 1 to nStandards do begin
  700.                             GetLineFromFile(f, str);
  701.                             if str <> '' then begin
  702.                                     StandardValues[i] := StringToReal(str);
  703.                                     SetDString(MyLog, FirstID + i - 1, str);
  704.                                 end;
  705.                         end;
  706.                     err := fsclose(f);
  707.                 end;
  708.     end;
  709.  
  710.  
  711.     procedure Calibrate;
  712.         const
  713.             FirstLevelID = 3;
  714.             FirstStandardID = 23;
  715.             FirstFitID = 63;
  716.             LastFitID = 70;
  717.             UnitOfMeasureID = 71;
  718.             UndoID = 73;
  719.             OpenID = 74;
  720.         var
  721.             mylog: DialogPtr;
  722.             ignore, item, i, nBadReals: integer;
  723.             str: str255;
  724.             SaveStandards, temp: StandardsArray;
  725.             OptionKeyWasDown: boolean;
  726.     begin
  727.         OptionKeyWasDown := OptionKeyDown;
  728.         if nAreas < 2 then begin
  729.                 PutMessage('Before calibrating you must use the Measure command to read a set of standards.', '', '');
  730.                 exit(Calibrate)
  731.             end;
  732.         SaveStandards := StandardValues;
  733.         with info^ do begin
  734.                 mylog := GetNewDialog(20, nil, pointer(-1));
  735.                 OutlineButton(MyLog, ok, 16);
  736.                 nStandards := nAreas;
  737.                 if nStandards > MaxStandards then
  738.                     nStandards := MaxStandards;
  739.                 for i := 1 to nStandards do begin
  740.                         RealToString(umean[i], 1, 2, str);
  741.                         SetDString(MyLog, FirstLevelID + i - 1, str);
  742.                         if StandardValues[i] <> BadReal then
  743.                             SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 2);
  744.                     end;
  745.                 SelIText(MyLog, FirstStandardID, 0, 32767);
  746.                 SetDialogItem(mylog, FirstFitID + ord(fit), 1);
  747.                 if calibrated then
  748.                     SetDString(MyLog, UnitOfMeasureID, UnitOfMeasure);
  749.                 repeat
  750.                     ModalDialog(nil, item);
  751.                     if (item >= FirstStandardID) and (item < (FirstStandardID + nStandards)) then
  752.                         StandardValues[item - FirstStandardID + 1] := GetDReal(MyLog, item);
  753.                     if (item >= FirstLevelID) and (item < (FirstLevelID + nStandards)) then
  754.                         if OptionKeyWasDown then
  755.                             umean[item - FirstLevelID + 1] := GetDReal(MyLog, item)
  756.                         else begin
  757.                                 beep;
  758.                                 i := item - FirstLevelID + 1;
  759.                                 RealToString(umean[i], 1, 2, str);
  760.                                 SetDString(MyLog, FirstLevelID + i - 1, str);
  761.                             end;
  762.                     if (item >= FirstFitID) and (item <= LastFitID) then begin
  763.                             for i := FirstFitID to LastFitID do
  764.                                 SetDialogItem(mylog, i, 0);
  765.                             SetDialogItem(mylog, item, 1);
  766.                             fit := CurveFitType(item - FirstFitID);
  767.                         end;
  768.                     if item = UnitOfMeasureID then
  769.                         UnitOfMeasure := GetDString(MyLog, item);
  770.                     if item = OpenID then
  771.                         GetStandardsFromFile(mylog, FirstStandardID);
  772.                 until (item = ok) or (item = cancel) or (item = UndoID);
  773.                 DisposDialog(mylog);
  774.                 if item = UndoID then begin
  775.                         calibrated := false;
  776.                         for i := 0 to 255 do
  777.                             value[i] := i;
  778.                         exit(calibrate)
  779.                     end;
  780.                 if item = cancel then begin
  781.                         StandardValues := SaveStandards;
  782.                     end
  783.                 else begin
  784.                         nBadReals := 0;
  785.                         for i := 1 to nStandards do
  786.                             if StandardValues[i] = BadReal then
  787.                                 nBadReals := nBadReals + 1;
  788.                         if nBadReals = 0 then begin
  789.                                 DoCurveFitting;
  790.                                 if calibrated then
  791.                                     SetupCalibrationPlot
  792.                             end
  793.                         else
  794.                             beep;
  795.                     end;
  796.             end; {with info^}
  797.     end;
  798.  
  799.  
  800.     procedure ResetCounters;
  801.         var
  802.             AlertID: Integer;
  803.     begin
  804.         if (UnsavedAreas > 0) or (UnsavedLengths > 0) or (UnsavedPoints > 0) then begin
  805.                 InitCursor;
  806.                 AlertID := alert(500, nil);
  807.             end;
  808.         if AlertID <> CancelResetID then begin
  809.                 nLengths := 0;
  810.                 TotalLength := 0.0;
  811.                 nAreas := 0;
  812.                 nAreas2 := 0;
  813.                 results.n := 0;
  814.                 nPoints := 0;
  815.                 UnsavedAreas := 0;
  816.                 UnsavedPoints := 0;
  817.                 UnsavedLengths := 0;
  818.                 ShowResults;
  819.             end;
  820.         measuring := false;
  821.     end;
  822.  
  823.  
  824.     procedure InitTextEdit (font, size: integer);
  825.         var
  826.             maxvalue: integer;
  827.             dRect, vRect: rect;
  828.     begin
  829.         SetPort(ListWindow);
  830.         with ListWindow^.portRect do
  831.             SetRect(dRect, left + 4, top, right - 18, bottom - 24);
  832.         vRect := dRect;
  833.         ListTE := TENew(dRect, vRect);
  834.         ListTE^^.TxFont := font;
  835.         ListTE^^.TxSize := size;
  836.         ListTE^^.TELength := TextBufSize;
  837.         TESetText(ptr(TextBufP), TextBufSize, ListTe);
  838.         TECalText(ListTE);
  839.         TEUpdate(ListWindow^.visRgn^^.rgnBBox, ListTE);
  840.         with ListTE^^ do
  841.             ListPageSize := (viewRect.bottom - viewRect.top) div LineHeight;
  842.         MaxValue := ListTE^^.nLines - ListPageSize;
  843.         if MaxValue < 0 then
  844.             maxvalue := 0;
  845.         SetCtlMax(ScrollBar, MaxValue);
  846.         InitCursor;
  847.     end;
  848.  
  849.  
  850.     procedure ScrAction (theCtl: ControlHandle; partCode: integer);
  851.         var
  852.             delta: integer;
  853.             S, dS: Point;
  854.     begin
  855.         case partCode of
  856.             inUpButton: 
  857.                 delta := -1;
  858.             inDownButton: 
  859.                 delta := +1;
  860.             inPageUp: 
  861.                 delta := -ListPageSize;
  862.             inPageDown: 
  863.                 delta := +ListPageSize;
  864.             otherwise
  865.                 exit(ScrAction);
  866.         end;
  867.         SetPt(S, 0, GetCtlValue(theCtl));
  868.         SetCtlValue(theCtl, GetCtlValue(theCtl) + delta);
  869.         SetPt(dS, 0, S.v - GetCtlValue(theCtl));
  870.         TEScroll(0, dS.v * ListTE^^.lineHeight, ListTE);
  871.     end;
  872.  
  873.  
  874.     procedure WindowControl (thePt: Point);
  875.         var
  876.             theCtl: ControlHandle;
  877.             S, dS: Point;
  878.     begin
  879.         case FindControl(thePt, ListWindow, theCtl) of
  880.             inUpButton, inDownButton, inPageUp, inPageDown: 
  881.                 if TrackControl(theCtl, thePt, @ScrAction) <> 0 then
  882.                     ;
  883.             inThumb: 
  884.                 begin
  885.                     SetPt(S, 0, GetCtlValue(theCtl));
  886.                     if TrackControl(theCtl, thePt, nil) <> 0 then begin
  887.                             SetPt(dS, 0, S.v - GetCtlValue(theCtl));
  888.                             TEScroll(0, dS.v * ListTE^^.lineHeight, ListTE);
  889.                         end;
  890.                 end;
  891.         end; {case}
  892.     end;
  893.  
  894.  
  895.     procedure TypeMismatch (fname: str255);
  896.         var
  897.             ignore: integer;
  898.     begin
  899.         ParamText('The file "', fname, '" is a different type, and therefore cannot be replaced', '');
  900.         InitCursor;
  901.         ignore := Alert(MessageID, nil);
  902.     end;
  903.  
  904.  
  905.     function IOCheck (err: OSerr): integer;
  906.     begin
  907.         if err <> 0 then
  908.             beep;
  909.         IOCheck := err;
  910.     end;
  911.  
  912.  
  913.     procedure SaveAsText;
  914.         var
  915.             err, f: integer;
  916.             where: Point;
  917.             reply: SFReply;
  918.             TheInfo: FInfo;
  919.             ByteCount: LongInt;
  920.     begin
  921.         where.v := 50;
  922.         where.h := 50;
  923.         SFPutFile(Where, 'Save Measurements as?', 'Measurements', nil, reply);
  924.         if not reply.good then
  925.             exit(SaveAsText);
  926.         err := GetFInfo(reply.fname, reply.vRefNum, TheInfo);
  927.         case err of
  928.             NoErr: 
  929.                 if TheInfo.fdType <> 'TEXT' then begin
  930.                         TypeMismatch(reply.fname);
  931.                         exit(SaveAsText)
  932.                     end;
  933.             FNFerr: 
  934.                 begin
  935.                     err := create(reply.fname, reply.vRefNum, 'MACA', 'TEXT');
  936.                     if IOCheck(err) <> 0 then
  937.                         exit(SaveAsText);
  938.                 end;
  939.             otherwise
  940.                 if IOCheck(err) <> 0 then
  941.                     exit(SaveAsTExt)
  942.         end;
  943.         CopyResultsToBuffer;
  944.         ShowWatch;
  945.         with reply do
  946.             err := fsopen(fname, vRefNum, f);
  947.         if IOCheck(err) <> 0 then
  948.             exit(SaveAsText);
  949.         ByteCount := TextBufSize;
  950.         err := fswrite(f, ByteCount, ptr(TextBufP));
  951.         if IOCheck(err) <> 0 then
  952.             exit(SaveAsText);
  953.         err := fsclose(f);
  954.         err := FlushVol(nil, reply.vRefNum);
  955.     end;
  956.  
  957.  
  958.     procedure DoButton (loc: point; var nbutton: integer);
  959.         var
  960.             i: integer;
  961.             TypeOfResults: ResultsType;
  962.             IgnoreResult: boolean;
  963.     begin
  964.         nbutton := 0;
  965.         for i := 1 to 4 do
  966.             if PtInRect(loc, ListButton[i]) then
  967.                 nbutton := i;
  968.         InvertRoundRect(ListButton[nbutton], 6, 6);
  969.         if nbutton > 0 then begin
  970.                 while Button do begin
  971.                         GetMouse(loc);
  972.                         if not PtInRect(loc, ListButton[nbutton]) then begin
  973.                                 InvertRoundRect(ListButton[nbutton], 6, 6);
  974.                                 nbutton := 0;
  975.                             end;
  976.                     end; {while}
  977.                 if nbutton > 0 then begin
  978.                         InvertRoundRect(ListButton[nbutton], 6, 6);
  979.                         TypeOfResults := GetResultsType;
  980.                         case nbutton of
  981.                             1: 
  982.                                 SaveAsText;
  983.                             2: 
  984.                                 begin
  985.                                     case TypeOfResults of
  986.                                         AreaT: 
  987.                                             WhatToPrint := PrintAreas;
  988.                                         LengthT: 
  989.                                             WhatToPrint := PrintLengths;
  990.                                         PointT: 
  991.                                             WhatToPrint := PrintPoints;
  992.                                     end;
  993.                                     print(true);
  994.                                 end;
  995.                             3: 
  996.                                 begin
  997.                                     CopyResultsToBuffer;
  998.                                     TextOnClip := true;
  999.                                     IgnoreResult := SystemEdit(3); {Fake Copy needed for MultiFinder}
  1000.                                 end;
  1001.                             4: 
  1002.                                 ResetCounters;
  1003.                         end;
  1004.                     end;
  1005.             end;
  1006.     end;
  1007.  
  1008.  
  1009.     procedure ShowList (title: Str255; font, size, MeasWidth: integer);
  1010.         const
  1011.             MeasLeft = 15;
  1012.             MeasTop = 50;
  1013.             ControlWidth = 15;
  1014.             ButtonWidth = 50;
  1015.         var
  1016.             wrect, crect, trect: rect;
  1017.             theEvt: EventRecord;
  1018.             tPort: GrafPtr;
  1019.             i, MeasHeight, ButtonLeft, nbutton: integer;
  1020.             loc: point;
  1021.             name: str255;
  1022.     begin
  1023.         GetPort(tPort);
  1024.         FlushEvents(everyEvent, 0);
  1025.         MeasHeight := ((TextBufLineCount * 2) + 2) * size;
  1026.         if (MeasHeight + MeasTop + 50) > ScreenHeight then
  1027.             MeasHeight := ScreenHeight - MeasTop - 50;
  1028.         SetRect(wrect, MeasLeft, MeasTop, MeasLeft + MeasWidth, MeasTop + MeasHeight);
  1029.         ListWindow := NewWindow(nil, wrect, title, true, 0, pointer(-1), true, 0);
  1030.         SetRect(crect, MeasWidth - ControlWidth, -1, MeasWidth + 1, MeasHeight - 15);
  1031.         ScrollBar := NewControl(ListWindow, crect, '', true, 0, 0, MeasHeight - 16, ScrollBarProc, 0);
  1032.         ListDone := false;
  1033.         InitTextEdit(font, size);
  1034.         DrawControls(ListWindow);
  1035.         SetRect(trect, -1, MeasHeight - 16, MeasWidth - 14, MeasHeight + 1);
  1036.         FrameRect(tRect);
  1037.         ButtonLeft := 4;
  1038.         TextFont(SystemFont);
  1039.         TextSize(12);
  1040.         for i := 1 to 4 do begin
  1041.                 SetRect(ListButton[i], ButtonLeft, MeasHeight - 14, ButtonLeft + ButtonWidth, MeasHeight - 1);
  1042.                 FrameRoundRect(ListButton[i], 6, 6);
  1043.                 case i of
  1044.                     1: 
  1045.                         name := 'Export';
  1046.                     2: 
  1047.                         name := 'Print';
  1048.                     3: 
  1049.                         name := 'Copy';
  1050.                     4: 
  1051.                         name := 'Reset';
  1052.                 end;
  1053.                 with ListButton[i] do
  1054.                     MoveTo(left + ((right - left) - StringWidth(name)) div 2, bottom - 2);
  1055.                 DrawString(name);
  1056.                 ButtonLeft := ButtonLeft + ButtonWidth + 4;
  1057.             end;
  1058.         repeat
  1059.             if GetNextEvent(EveryEvent, theEvt) then
  1060.                 if theEvt.what = MouseDown then begin
  1061.                         if PtInRect(theEvt.where, wrect) then begin
  1062.                                 loc := theEvt.where;
  1063.                                 GlobalToLocal(loc);
  1064.                                 if loc.v > (MeasHeight - 14) then begin
  1065.                                         DoButton(loc, nbutton);
  1066.                                         ListDone := nbutton > 0
  1067.                                     end
  1068.                                 else
  1069.                                     case FindWindow(theEvt.where, ListWindow) of
  1070.                                         inContent: 
  1071.                                             WindowControl(loc);
  1072.                                         InGoAway: 
  1073.                                             if TrackGoAway(ListWindow, TheEvt.where) then
  1074.                                                 ListDone := true;
  1075.                                     end
  1076.                             end
  1077.                         else
  1078.                             ListDone := true
  1079.                     end;
  1080.             if theEvt.what = KeyDown then
  1081.                 ListDone := true;
  1082.         until ListDone;
  1083.         TEDispose(ListTE);
  1084.         DisposeWindow(ListWindow);
  1085.         FlushEvents(everyEvent, 0);
  1086.         SetPort(tPort);
  1087.     end;
  1088.  
  1089.  
  1090.     procedure ListResults;
  1091.         var
  1092.             TypeOfResults: ResultsType;
  1093.             title: str255;
  1094.             width: integer;
  1095.     begin
  1096.         TypeOfResults := GetResultsType;
  1097.         if TypeOfResults = NoResults then
  1098.             PutMessage('Sorry, but no measurements are available to display.', '', '')
  1099.         else begin
  1100.                 Printing := true;
  1101.                 ShowingList := true;
  1102.                 CopyResultsToBuffer;
  1103.                 ShowingList := false;
  1104.                 Printing := false;
  1105.                 case TypeOfResults of
  1106.                     AreaT: 
  1107.                         begin
  1108.                             title := 'Area Measurements';
  1109.                             width := 120 + nMeasurements * 72;
  1110.                             if width < 250 then
  1111.                                 width := 250;
  1112.                         end;
  1113.                     LengthT: 
  1114.                         begin
  1115.                             title := 'Length Measurements';
  1116.                             width := 250
  1117.                         end;
  1118.                     PointT: 
  1119.                         begin
  1120.                             title := 'Point Measurements';
  1121.                             width := 275
  1122.                         end;
  1123.                     NoResults: 
  1124.                 end;
  1125.                 ShowList(title, Monaco, 9, width);
  1126.                 nAreas2 := nAreas;
  1127.             end;
  1128.     end;
  1129.  
  1130.  
  1131.     procedure DoMeasurementOptions;
  1132.         const
  1133.             FirstBoxID = 4;
  1134.             LastBoxID = 10;
  1135.         var
  1136.             mylog: DialogPtr;
  1137.             item, i: integer;
  1138.             mtype: MeasurementTypes;
  1139.     begin
  1140.         InitCursor;
  1141.         mylog := GetNewDialog(4000, nil, pointer(-1));
  1142.         mtype := AreaM;
  1143.         for i := FirstBoxID to LastBoxID do begin
  1144.                 if mtype in measurements then
  1145.                     SetDialogItem(mylog, i, 1);
  1146.                 if i <> LastBoxID then
  1147.                     mtype := succ(mtype);
  1148.             end;
  1149.         OutlineButton(MyLog, ok, 16);
  1150.         repeat
  1151.             ModalDialog(nil, item);
  1152.             if (item >= FirstBoxID) and (item <= LastBoxID) then begin
  1153.                     i := item - FirstBoxID;
  1154.                     case i of
  1155.                         0: 
  1156.                             mtype := AreaM;
  1157.                         1: 
  1158.                             mtype := MeanM;
  1159.                         2: 
  1160.                             mtype := StdDevM;
  1161.                         3: 
  1162.                             mtype := xyLocM;
  1163.                         4: 
  1164.                             mtype := ModeM;
  1165.                         5: 
  1166.                             mtype := PerimeterM;
  1167.                         6: 
  1168.                             mtype := IntDenM;
  1169.                     end;
  1170.                     if mtype in measurements then begin
  1171.                             measurements := measurements - [mtype];
  1172.                             SetDialogItem(mylog, item, 0)
  1173.                         end
  1174.                     else begin
  1175.                             measurements := measurements + [mtype];
  1176.                             SetDialogItem(mylog, item, 1)
  1177.                         end;
  1178.                 end;
  1179.         until (item = ok) or (item = cancel);
  1180.         DisposDialog(mylog);
  1181.         if item = cancel then begin
  1182.             end;
  1183.     end;
  1184.  
  1185.  
  1186.     procedure DoProfilePlotOptions;
  1187.         const
  1188.             AutoScaleID = 3;
  1189.             FixedScaleID = 4;
  1190.             MinID = 6;
  1191.             MaxID = 8;
  1192.             LinePlotID = 9;
  1193.             ScatterPlotID = 10;
  1194.             InvertID = 11;
  1195.             LabelsID = 12;
  1196.             VariableSizeID = 13;
  1197.             FixedSizeID = 14;
  1198.             WidthID = 17;
  1199.             HeightID = 18;
  1200.         var
  1201.             mylog: DialogPtr;
  1202.             item, i: integer;
  1203.             SaveAutoscale, SaveLinePlot, SaveInvert, SaveDrawLabels, SaveFixedSize: boolean;
  1204.             SaveWidth, SaveHeight: integer;
  1205.             SaveMin, SaveMax: extended;
  1206.     begin
  1207.         InitCursor;
  1208.         SaveAutoscale := AutoscalePlots;
  1209.         SaveLinePlot := LinePlot;
  1210.         SaveInvert := InvertPlots;
  1211.         SaveMin := ProfilePlotMin;
  1212.         SaveMax := ProfilePlotMax;
  1213.         mylog := GetNewDialog(5000, nil, pointer(-1));
  1214.         if AutoScalePlots then
  1215.             SetDialogItem(mylog, AutoScaleID, 1)
  1216.         else
  1217.             SetDialogItem(mylog, FixedScaleID, 1);
  1218.         SetDReal(MyLog, MinID, ProfilePlotMin, 2);
  1219.         SetDReal(MyLog, MaxID, ProfilePlotMax, 2);
  1220.         if FixedSizePlot then
  1221.             SetDialogItem(mylog, FixedSizeID, 1)
  1222.         else
  1223.             SetDialogItem(mylog, VariableSizeID, 1);
  1224.         SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1225.         SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1226.         if LinePlot then
  1227.             SetDialogItem(mylog, LinePlotID, 1)
  1228.         else
  1229.             SetDialogItem(mylog, ScatterPlotID, 1);
  1230.         if InvertPlots then
  1231.             SetDialogItem(mylog, InvertID, 1);
  1232.         if DrawPlotLabels then
  1233.             SetDialogItem(mylog, LabelsID, 1);
  1234.         OutlineButton(MyLog, ok, 16);
  1235.         repeat
  1236.             ModalDialog(nil, item);
  1237.             if (item = AutoScaleID) or (item = FixedScaleID) then begin
  1238.                     SetDialogItem(mylog, AutoScaleID, 0);
  1239.                     SetDialogItem(mylog, FixedScaleID, 0);
  1240.                     SetDialogItem(mylog, item, 1);
  1241.                     AutoscalePlots := item = AutoscaleID;
  1242.                 end;
  1243.             if item = MinID then begin
  1244.                     ProfilePlotMin := GetDReal(MyLog, MinID);
  1245.                     if (ProfilePlotMin < 0) or (ProfilePlotMin > 255) then begin
  1246.                             ProfilePlotMin := SaveMin;
  1247.                             SetDReal(MyLog, MinID, ProfilePlotMin, 2);
  1248.                         end;
  1249.                 end;
  1250.             if item = MaxID then begin
  1251.                     ProfilePlotMax := GetDReal(MyLog, MaxID);
  1252.                     if (ProfilePlotMax < 0) or (ProfilePlotMax > 255) then begin
  1253.                             ProfilePlotMax := SaveMax;
  1254.                             SetDReal(MyLog, MaxID, ProfilePlotMax, 2);
  1255.                         end;
  1256.                 end;
  1257.             if (item = FixedSizeID) or (item = VariableSizeID) then begin
  1258.                     SetDialogItem(mylog, FixedSizeID, 0);
  1259.                     SetDialogItem(mylog, VariableSizeID, 0);
  1260.                     SetDialogItem(mylog, item, 1);
  1261.                     FixedSizePlot := item = FixedSizeID;
  1262.                 end;
  1263.             if item = WidthID then begin
  1264.                     ProfilePlotWidth := GetDNum(MyLog, WidthID);
  1265.                     if (ProfilePlotWidth < 0) or (ProfilePlotWidth > 1023) then begin
  1266.                             ProfilePlotWidth := SaveWidth;
  1267.                             SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1268.                         end;
  1269.                 end;
  1270.             if item = HeightID then begin
  1271.                     ProfilePlotHEight := GetDNum(MyLog, HeightID);
  1272.                     if (ProfilePlotHeight < 0) or (ProfilePlotHeight > 1023) then begin
  1273.                             ProfilePlotHeight := SaveHeight;
  1274.                             SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1275.                         end;
  1276.                 end;
  1277.             if (item = LinePlotID) or (item = ScatterPlotID) then begin
  1278.                     SetDialogItem(mylog, LinePlotID, 0);
  1279.                     SetDialogItem(mylog, ScatterPlotID, 0);
  1280.                     SetDialogItem(mylog, item, 1);
  1281.                     LinePlot := item = LinePlotID;
  1282.                 end;
  1283.             if item = InvertID then begin
  1284.                     InvertPlots := not InvertPlots;
  1285.                     SetDialogItem(mylog, InvertID, ord(InvertPlots));
  1286.                 end;
  1287.             if item = LabelsID then begin
  1288.                     DrawPlotLabels := not DrawPlotLabels;
  1289.                     SetDialogItem(mylog, LabelsID, ord(DrawPlotLabels));
  1290.                 end;
  1291.         until (item = ok) or (item = cancel);
  1292.         DisposDialog(mylog);
  1293.         if item = cancel then begin
  1294.                 AutoscalePlots := SaveAutoscale;
  1295.                 LinePlot := SaveLinePlot;
  1296.                 InvertPlots := SaveInvert;
  1297.                 ProfilePlotMin := SaveMin;
  1298.                 ProfilePlotMax := SaveMax;
  1299.                 DrawPlotLabels := SaveDrawLabels;
  1300.             end;
  1301.     end;
  1302.  
  1303.  
  1304.     procedure DoPoints (event: EventRecord);
  1305.         var
  1306.             loc: point;
  1307.             yi: integer;
  1308.     begin
  1309.         loc := event.where;
  1310.         if LineWidth > 1 then
  1311.             with loc do begin
  1312.                     h := h - LineWidth div 2;
  1313.                     v := v - LineWidth div 2;
  1314.                 end;
  1315.         DrawObject(LineObj, loc, loc);
  1316.         with results, loc do begin
  1317.                 if nPoints < MaxLocs then begin
  1318.                         nPoints := nPoints + 1;
  1319.                         UnsavedPoints := UnsavedPoints + 1
  1320.                     end
  1321.                 else
  1322.                     beep;
  1323.                 ScreenToOffscreen(loc);
  1324.                 x := h;
  1325.                 yi := info^.PicRect.bottom - v - 1;
  1326.                 y := yi;
  1327.                 xLoc[nPoints] := h;
  1328.                 yLoc[nPoints] := yi;
  1329.                 with info^ do
  1330.                     if scale <> 0.0 then begin
  1331.                             x := x / scale;
  1332.                             y := y / scale;
  1333.                         end;
  1334.             end;
  1335.         ShowResults;
  1336.         measuring := true;
  1337.     end;
  1338.  
  1339.  
  1340.     procedure FindAngle (event: EventRecord);
  1341.         var
  1342.             start, finish, OldFinish, MidPoint: point;
  1343.             ticks: LongInt;
  1344.             ff, x1, y1, x2, y2, imag: integer;
  1345.             angle1, angle2: extended;
  1346.             StartRect: rect;
  1347.             FirstLineDone: boolean;
  1348.  
  1349.         procedure GetAngle (x, y: integer; var angle: extended);
  1350.             var
  1351.                 quadrant: (q1, q2orq3, q4);
  1352.         begin
  1353.             if x <> 0 then
  1354.                 angle := arctan(y / x)
  1355.             else begin
  1356.                     if y >= 0 then
  1357.                         angle := pi / 2.0
  1358.                     else
  1359.                         angle := -pi / 2.0
  1360.                 end;
  1361.             angle := (180.0 / pi) * angle;
  1362.             if (x >= 0) and (y >= 0) then
  1363.                 quadrant := q1
  1364.             else if x < 0 then
  1365.                 quadrant := q2orq3
  1366.             else
  1367.                 quadrant := q4;
  1368.             case quadrant of
  1369.                 q1: 
  1370.                     ;
  1371.                 q2orq3: 
  1372.                     angle := angle + 180.0;
  1373.                 q4: 
  1374.                     angle := angle + 360.0;
  1375.             end;
  1376.         end;
  1377.  
  1378.     begin
  1379.         ValuesMode := AngleValue;
  1380.         DrawLabels;
  1381.         FlushEvents(EveryEvent, 0);
  1382.         imag := trunc(info^.magnification + 0.5);
  1383.         ff := imag div 2;
  1384.         if ff < 1 then
  1385.             ff := 1;
  1386.         start := event.where;
  1387.         with start do begin
  1388.                 h := h - ff;
  1389.                 v := v - ff
  1390.             end;
  1391.         Pt2Rect(start, start, StartRect);
  1392.         InsetRect(StartRect, -2, -2);
  1393.         finish := start;
  1394.         PenNormal;
  1395.         PenMode(PatXor);
  1396.         PenSize(imag * LineWidth, imag * LineWidth);
  1397.         MoveTo(start.h, start.v);
  1398.         repeat
  1399.             repeat
  1400.                 OldFinish := finish;
  1401.                 GetMouse(finish);
  1402.                 with finish do begin
  1403.                         h := h - ff;
  1404.                         v := v - ff
  1405.                     end;
  1406.                 MoveTo(start.h, start.v);
  1407.                 LineTo(OldFinish.h, OldFinish.v);
  1408.                 MoveTo(start.h, start.v);
  1409.                 LineTo(finish.h, finish.v);
  1410.                 ticks := TickCount;
  1411.                 while ticks = TickCount do
  1412.                     ;
  1413.                 x1 := finish.h - start.h;
  1414.                 y1 := start.v - finish.v;
  1415.                 GetAngle(x1, y1, angle1);
  1416.                 Show1Value(angle1, NoValue);
  1417.             until GetNextEvent(mUpMask, event);
  1418.             FirstLineDone := not PtInRect(finish, StartRect);
  1419.             if not FirstLineDone then
  1420.                 start := finish;
  1421.         until FirstLineDone;
  1422.         DrawObject(LineObj, start, finish);
  1423.         MidPoint := finish;
  1424.         x1 := start.h - MidPoint.h;
  1425.         y1 := MidPoint.v - start.v;
  1426.         GetAngle(x1, y1, angle1);
  1427.         start := finish;
  1428.         finish := start;
  1429.         repeat
  1430.             OldFinish := finish;
  1431.             GetMouse(finish);
  1432.             with finish do begin
  1433.                     h := h - ff;
  1434.                     v := v - ff
  1435.                 end;
  1436.             MoveTo(start.h, start.v);
  1437.             LineTo(OldFinish.h, OldFinish.v);
  1438.             MoveTo(start.h, start.v);
  1439.             LineTo(finish.h, finish.v);
  1440.             ticks := TickCount;
  1441.             while ticks = TickCount do
  1442.                 ;
  1443.             x2 := finish.h - MidPoint.h;
  1444.             y2 := MidPoint.v - finish.v;
  1445.             GetAngle(x2, y2, angle2);
  1446.             with results do begin
  1447.                     if angle1 >= angle2 then
  1448.                         angle := angle1 - angle2
  1449.                     else
  1450.                         angle := angle2 - angle1;
  1451.                     if angle > 180.0 then
  1452.                         angle := 360.0 - angle;
  1453.                     Show1Value(angle, NoValue);
  1454.                 end;
  1455.         until GetNextEvent(mUpMask, event);
  1456.         DrawObject(LineObj, start, finish);
  1457.         ShowResults;
  1458.         repeat
  1459.         until not GetNextEvent(EveryEvent, Event); {FlushEvent doesn't work under A/UX!}
  1460.     end;
  1461.  
  1462.     procedure ComputeLength; {(nvertices: integer; var xa, ya: xyArray; FindingPerimeterLength: boolean)}
  1463.         var
  1464.             i: integer;
  1465.             xtemp, ytemp: LongInt;
  1466.     begin
  1467.         with results do begin
  1468.                 Length := 0.0;
  1469.                 for i := 2 to nvertices do begin
  1470.                         xtemp := xa[i] - xa[i - 1];
  1471.                         ytemp := ya[i] - ya[i - 1];
  1472.                         Length := Length + sqrt(xtemp * xtemp + ytemp * ytemp);
  1473.                     end;
  1474.                 if FindingPerimeterLength then begin
  1475.                         xtemp := xa[1] - xa[nvertices];
  1476.                         ytemp := ya[1] - ya[nvertices];
  1477.                         Length := Length + sqrt(xtemp * xtemp + ytemp * ytemp);
  1478.                     end;
  1479.                 with info^ do begin
  1480.                         Length := Length / magnification;
  1481.                         if scale <> 0.0 then
  1482.                             Length := Length / scale;
  1483.                     end;
  1484.             end;
  1485.     end;
  1486.  
  1487.  
  1488.     procedure SaveBlankField;
  1489.         var
  1490.             SaveInfo: InfoPtr;
  1491.             i, xLines, xPixelsPerLine: integer;
  1492.             src, dst: ptr;
  1493.             SaveFlag: boolean;
  1494.     begin
  1495.         if (info^.PictureType = camera) or (info^.PictureType = ScionType) then begin
  1496.                 SaveInfo := info;
  1497.                 if BlankFieldInfo = nil then
  1498.                     Duplicate(true)
  1499.                 else
  1500.                     with info^ do begin
  1501.                             if (PictureType = ScionType) or ((PictureType = camera) and not digitizing) then begin
  1502.                                     PutMessage('You must close the current blank field window before you can create a new one.', '', '');
  1503.                                     exit(SaveBlankField);
  1504.                                 end;
  1505.                             src := info^.PicBaseAddr;
  1506.                             dst := BlankFieldInfo^.PicBaseAddr;
  1507.                             with PicRect do begin
  1508.                                     xLines := bottom - top;
  1509.                                     xPixelsPerLine := right - left;
  1510.                                 end;
  1511.                             for i := 1 to xLines do begin
  1512.                                     BlockMove(src, dst, xPixelsPerLine);
  1513.                                     src := ptr(ord4(src) + info^.BytesPerRow);
  1514.                                     dst := ptr(ord4(dst) + xPixelsPerLine);
  1515.                                 end;
  1516.                         end;
  1517.                 Info := BlankFieldInfo;
  1518.                 InvertPic;
  1519.                 SaveFlag := digitizing;
  1520.                 digitizing := false;
  1521.                 SelectAll(false);
  1522.                 DoHistogram;
  1523.                 digitizing := SaveFlag;
  1524.                 BlankFieldMean := results.imean;
  1525.                 KillRoi;
  1526.                 UpdatePicWindow;
  1527.                 info := SaveInfo;
  1528.                 SelectWindow(Info^.wptr);
  1529.             end
  1530.         else
  1531.             PutMessage('You must be digitizing in order to save a blank field.', '', '');
  1532.     end;
  1533.  
  1534.  
  1535.     procedure UndoLastMeasurement;
  1536.     begin
  1537.         if nAreas > 0 then begin
  1538.                 nAreas := nAreas - 1;
  1539.                 if UnsavedAreas > 0 then
  1540.                     UnsavedAreas := UnsavedAreas - 1
  1541.             end
  1542.         else
  1543.             WhatToUndo := NothingToUndo;
  1544.         ShowResults;
  1545.     end;
  1546.  
  1547.     function PixelInside (p: point): boolean;
  1548.         var
  1549.             value: integer;
  1550.     begin
  1551.         with p do
  1552.             value := MyGetPixel(h, v);
  1553.         case ThresholdingMode of
  1554.             LutThresholding: 
  1555.                 PixelInside := (value >= ThresholdStart) and (value <= ThresholdEnd);
  1556.             GrayMapThresholding: 
  1557.                 PixelInside := value >= info^.p1x;
  1558.             BinaryImage: 
  1559.                 PixelInside := value = BlackC;
  1560.         end;
  1561.     end;
  1562.  
  1563.     function TraceEdge (FirstPoint: point; var circumference: extended): boolean;
  1564.         const
  1565.             MaxCount = 15000;
  1566.         type
  1567.             direction = (wasleft, wasright, wasup, wasdown);
  1568.         var
  1569.             CurrentDirection: direction;
  1570.             NewPoint, oldPoint, lastIn: point;
  1571.             count: integer;
  1572.             hside, vside: extended;
  1573.             Saveport: GrafPtr;
  1574.             TempRgn: RgnHandle;
  1575.             OutOfBounds, FindPerimeter: boolean;
  1576.     begin
  1577.         GetPort(SavePort);
  1578.         SetPort(GrafPtr(info^.osPort));
  1579.         oldPoint.h := firstPoint.h;
  1580.         oldPoint.v := firstPoint.v;
  1581.         newPoint.h := firstPoint.h;
  1582.         newPoint.v := firstPoint.v;
  1583.         lastIn.h := firstPoint.h;
  1584.         lastIn.v := firstPoint.v;
  1585.         circumference := 0.0;
  1586.         FindPerimeter := PerimeterM in measurements;
  1587.         CurrentDirection := wasleft;
  1588.         count := 0;
  1589.         PenNormal;
  1590.         OpenRgn;
  1591.         MoveTo(FirstPoint.h, FirstPoint.v);
  1592.         repeat
  1593.             count := count + 1;
  1594.             case CurrentDirection of
  1595.                 wasright: 
  1596.                     begin
  1597.                         if PixelInside(oldPoint) then begin
  1598.                                 newPoint.v := oldpoint.v - 1;
  1599.                                 newPoint.h := oldpoint.h;
  1600.                                 CurrentDirection := wasup;
  1601.                             end
  1602.                         else begin
  1603.                                 newPoint.v := oldpoint.v + 1;
  1604.                                 newPoint.h := oldpoint.h;
  1605.                                 CurrentDirection := wasdown;
  1606.                             end;
  1607.                     end;
  1608.                 wasleft: 
  1609.                     begin
  1610.                         if PixelInside(oldPoint) then begin
  1611.                                 newPoint.v := oldpoint.v + 1;
  1612.                                 newPoint.h := oldpoint.h;
  1613.                                 CurrentDirection := wasdown;
  1614.                             end
  1615.                         else begin
  1616.                                 newPoint.v := oldpoint.v - 1;
  1617.                                 newPoint.h := oldpoint.h;
  1618.                                 CurrentDirection := wasup;
  1619.                             end;
  1620.                     end;
  1621.                 wasup: 
  1622.                     begin
  1623.                         if PixelInside(oldPoint) then begin
  1624.                                 newPoint.v := oldpoint.v;
  1625.                                 newPoint.h := oldpoint.h - 1;
  1626.                                 CurrentDirection := wasleft;
  1627.                             end
  1628.                         else begin
  1629.                                 newPoint.v := oldpoint.v;
  1630.                                 newPoint.h := oldpoint.h + 1;
  1631.                                 CurrentDirection := wasright;
  1632.                             end;
  1633.                     end;
  1634.                 wasdown: 
  1635.                     begin
  1636.                         if PixelInside(oldPoint) then begin
  1637.                                 newPoint.v := oldpoint.v;
  1638.                                 newPoint.h := oldpoint.h + 1;
  1639.                                 CurrentDirection := wasright;
  1640.                             end
  1641.                         else begin
  1642.                                 newPoint.v := oldpoint.v;
  1643.                                 newPoint.h := oldpoint.h - 1;
  1644.                                 CurrentDirection := wasleft;
  1645.                             end;
  1646.                     end;
  1647.             end; {case of direction}
  1648.             if PixelInside(newPoint) then begin
  1649.                     if FindPerimeter then begin
  1650.                             hside := lastin.h - newpoint.h;
  1651.                             vside := lastin.v - newpoint.v;
  1652.                             circumference := circumference + sqrt(sqr(hside) + sqr(vside));
  1653.                         end;
  1654.                     LineTo(newPoint.h, newPoint.v);
  1655.                     lastIn.h := newPoint.h;
  1656.                     lastIn.v := newPoint.v;
  1657.                 end;
  1658.             oldPoint.h := newPoint.h;
  1659.             oldPoint.v := newPoint.v;
  1660.         until ((firstPoint.h = newPoint.h) and (firstPoint.v = newPoint.v) and (count <> 4)) or (count > MaxCount);
  1661.         with info^ do begin
  1662.                 CloseRgn(osroiRgn);
  1663.                 with osroiRgn^^.rgnBBox do
  1664.                     OutOfBounds := (left < 0) or (top < 0) or (right > PicRect.right) or (bottom > PicRect.bottom);
  1665.                 if (count > MaxCount) or OutOfBounds then begin
  1666.                         beep;
  1667.                         SetEmptyRgn(osroiRgn);
  1668.                         SetPort(SavePort);
  1669.                         TraceEdge := false;
  1670.                         exit(TraceEdge);
  1671.                     end;
  1672.                 TempRgn := NewRgn;
  1673.                 CopyRgn(osroiRgn, TempRgn);
  1674.                 OffsetRgn(TempRgn, 1, 1);
  1675.                 UnionRgn(TempRgn, osroiRgn, osroiRgn);
  1676.                 DisposeRgn(TempRgn);
  1677.                 RoiShowing := true;
  1678.                 roiType := RgnRoi;
  1679.                 osroiRect := osroiRgn^^.rgnBBox;
  1680.                 roiRect := osroiRect;
  1681.                 OffscreenToScreenRect(roiRect);
  1682.             end;
  1683.         SetPort(SavePort);
  1684.         TraceEdge := true;
  1685.     end;
  1686.  
  1687.  
  1688.     procedure NumberSelection (count: integer);
  1689.         var
  1690.             SavePort: GrafPtr;
  1691.             NumWidth, NumLeft, NumBottom, SaveForeground: integer;
  1692.             str: str255;
  1693.             r: rect;
  1694.     begin
  1695.         with info^ do begin
  1696.                 KillRoi;
  1697.                 SetupUndo;
  1698.                 WhatToUndo := UndoOutline;
  1699.                 GetPort(SavePort);
  1700.                 SetPort(GrafPtr(osPort));
  1701.                 PenNormal;
  1702.                 TextFont(ApplFont);
  1703.                 TextSize(9);
  1704.                 TextMode(SrcOr);
  1705.                 NumToString(count, str);
  1706.                 with osRoiRect do begin
  1707.                         NumWidth := StringWidth(str);
  1708.                         NumLeft := left + ((right - left) - NumWidth) div 2;
  1709.                         NumBottom := top + (bottom - top) div 2 + 3;
  1710.                         MoveTo(NumLeft, NumBottom);
  1711.                     end;
  1712.                 SaveForeground := ForegroundColor;
  1713.                 SetForegroundColor(WhiteC);
  1714.                 FrameRgn(osroiRgn);
  1715.                 SetRect(r, NumLeft - 1, NumBottom - 9, NumLeft + NumWidth + 1, NumBottom + 1);
  1716.                 PaintRoundRect(r, 4, 4);
  1717.                 TextMode(srcXor);
  1718.                 DrawString(str);
  1719.                 SetForegroundColor(SaveForeground);
  1720.                 UpdateScreen(RoiRect);
  1721.                 SetPort(SavePort);
  1722.                 changes := true;
  1723.             end;
  1724.     end;
  1725.  
  1726.  
  1727.     procedure AutoOutline (start: point);
  1728.         var
  1729.             hloc, vloc, counter: integer;
  1730.             SaveThresholdingState: boolean;
  1731.             circumference: extended;
  1732.     begin
  1733.         with info^ do begin
  1734.                 if not thresholding and (deltax > 1) and not BinaryPic then begin
  1735.                         PutMessage('Sorry, but you must be thresholding or working with a binary image to use the auto-outlining tool.', '', '');
  1736.                         exit(AutoOutline);
  1737.                     end;
  1738.                 ShowWatch;
  1739.                 KillRoi;
  1740.                 if thresholding then
  1741.                     ThresholdingMode := LutThresholding
  1742.                 else if BinaryPic then
  1743.                     ThresholdingMode := BinaryImage
  1744.                 else
  1745.                     ThresholdingMode := GrayMapThresholding;
  1746.                 ScreenToOffscreen(start);
  1747.                 if PixelInside(start) then begin
  1748.                         with start do begin
  1749.                                 repeat
  1750.                                     h := h + 1;
  1751.                                 until not PixelInside(start);
  1752.                                 h := h - 1;
  1753.                             end;
  1754.                         if not TraceEdge(start, circumference) then
  1755.                             exit(AutoOutline);
  1756.                         WhatToUndo := NothingToUndo;
  1757.                         if WandAutoMeasure then begin
  1758.                                 SaveThresholdingState := Thresholding;
  1759.                                 Thresholding := false;
  1760.                                 GetNonRectHistogram;
  1761.                                 ComputeResults;
  1762.                                 if scale <> 0.0 then
  1763.                                     circumference := circumference / scale;
  1764.                                 plength[nAreas] := circumference;
  1765.                                 ShowResults;
  1766.                                 WhatToUndo := UndoMeasurement;
  1767.                                 Thresholding := SaveThresholdingState;
  1768.                             end;
  1769.                         RoiShowing := true;
  1770.                         UpdateScreen(RoiRect);
  1771.                         if WandAutoNumber then begin
  1772.                                 if WandAutoMeasure then
  1773.                                     counter := nAreas
  1774.                                 else
  1775.                                     counter := nAreas + 1;
  1776.                                 NumberSelection(counter);
  1777.                             end;
  1778.                     end
  1779.                 else
  1780.                     beep;
  1781.             end; {with info}
  1782.     end;
  1783.  
  1784. end.