home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Add-Ons / After Dark / Sort / Sort 1.0.p < prev    next >
Encoding:
Text File  |  1994-11-23  |  13.1 KB  |  509 lines  |  [TEXT/PJMM]

  1. {Sort - an After Dark module demonstrating an easy way to write and debug a module}
  2. {in the Think Pascal environment, several sorting algorithms, and while we’re at it,}
  3. {direct to screen drawing and object Pascal in a code resource (including working around}
  4. {a bug in the Think Pascal 4.0.2 libraries).}
  5.  
  6. {Written and © by Scott Lindhurst, lindhurs@math.wisc.edu, fall 1993 and Nov. 1994.}
  7. {Mail:    1107 Erin St.        Madison, WI 53715-1845}
  8. {or    123 Millwood Dr.        Tonawanda, NY 14150-5513}
  9. {If you use this source in your program, give me credit in the about box and documentation.}
  10.  
  11.  
  12.  
  13. {$SETC ADModule = true}
  14. {To make an After Dark module, set ADModule to True, use RSRCRuntime.Lib in the project,}
  15. {and change the project type to code resource.}
  16. {To develop under the Think Pascal environment, set ADmodule to False, use Runtime.Lib,}
  17. {and change the project type to Application.}
  18.  
  19.  
  20.  
  21. {Known problems and bugs:}
  22. {• Has not ever been tested with multiple monitors. Anyone want to buy me a machine with}
  23. {2 or 3 monitors for testing?}
  24. {• There is a bug in the Quicksort implementation. I don’t know where.}
  25. {• Not all the sort routines have been fully tested lately.}
  26.  
  27.  
  28. {$IFC ADModule}
  29. unit SortScreen;
  30. interface
  31. {$ELSEC}
  32.     program SortScreen;
  33. {$ENDC}
  34.  
  35.     uses
  36.         SortDetails, PixelUtils, GraphicsModuleTypes;
  37.  
  38.  
  39. {$IFC ADModule}
  40.     function main (var storage: Handle;
  41.                                     blankRgn: rgnHandle;
  42.                                     message: integer;
  43.                                     params: GMParamBlockPtr): OSErr;
  44. {$ENDC}
  45.  
  46.  
  47.     const
  48.         kSortTypeMenu = 0;
  49.         iQuicksort = 1;
  50.         iHeapsort = 2;
  51.         iShellsort = 3;
  52.         iBubblesort = 4;
  53.         iShakersort = 5;
  54.         iBatchersort = 6;
  55.         kBlankFirstControl = 1;
  56.         kDirectPixelsControl = 2;
  57.  
  58.     type
  59.         StoredStuff = record
  60.                 theSortObj: cSorter;
  61.             end;
  62.         StoredStuffP = ^StoredStuff;
  63.         StoredStuffH = ^StoredStuffP;
  64.  
  65. {$IFC not ADModule}
  66.  
  67.     var
  68.         gStorage: Handle;                {These are normally filled in by After Dark and passed}
  69.         gBlankRgn: RgnHandle;            {to your module.}
  70.         gParams: GMParamBlock;
  71.         gMonitors: MonitorsInfo;
  72.         gQDGlobals: QDGlobals;
  73.  
  74.         gModuleErrorMessage: str255;
  75.         gDrawWindow: WindowPtr;
  76. {$ENDC}
  77.  
  78. {$IFC ADModule}
  79. implementation
  80. {$ENDC}
  81.  
  82.     function DoInitialize (var storage: Handle;
  83.                                     blankRgn: rgnHandle;
  84.                                     params: GMParamBlockPtr): OSErr;
  85.         var
  86.             i: integer;
  87.             device: GDHandle;
  88.             drawRect: Rect;
  89.             quickObj: cQuicksort;
  90.             heapObj: cHeapsort;
  91.             shellObj: cShellsort;
  92.             bubbleObj: cBubbleSort;
  93.             shakerObj: cShakerSort;
  94.             batcherObj: cBatcherSort;
  95.             sorterObj: cSorter;
  96.     begin
  97.         DoInitialize := noErr;
  98.         storage := NewHandle(sizeof(StoredStuff));
  99.         if storage = nil then
  100.             begin
  101.                 DoInitialize := ModuleError;
  102.                 Exit(DoInitialize);
  103.             end;
  104.  
  105. {Find global coords of our drawing space, and shrink it down to a square because that's all I can deal with.}
  106.         i := -1;
  107.         repeat        {Pick a rectangle that's actually on some monitor and draw to that one.}
  108.             i := i + 1
  109.         until SectRect(blankRgn^^.rgnBBox, params^.monitors^.monitorList[i].bounds, DrawRect);
  110. {DrawRect should now be in global coordinates.}
  111.  
  112.         with params^ do
  113.             InitPixelUtils(DrawRect, monitors^.monitorList[i].curDepth, colorQDAvail, controlValues[kDirectPixelsControl]);
  114.  
  115.         case params^.controlValues[kSortTypeMenu] of
  116.             iQuicksort: 
  117.                 begin
  118.                     new(quickObj);
  119.                     SorterObj := quickObj;
  120.                 end;
  121.             iHeapSort: 
  122.                 begin
  123.                     new(heapObj);
  124.                     SorterObj := heapObj;
  125.                 end;
  126.             iShellSort: 
  127.                 begin
  128.                     new(shellObj);
  129.                     SorterObj := shellObj;
  130.                 end;
  131.             iBubbleSort: 
  132.                 begin
  133.                     new(bubbleObj);
  134.                     SorterObj := bubbleObj;
  135.                 end;
  136.             iShakerSort: 
  137.                 begin
  138.                     new(shakerObj);
  139.                     SorterObj := shakerObj;
  140.                 end;
  141.             iBatcherSort: 
  142.                 begin
  143.                     new(batcherObj);
  144.                     SorterObj := batcherObj;
  145.                 end;
  146.         end;    {case}
  147.  
  148.         SorterObj.Init(ScreenPixels);
  149.         StoredStuffH(storage)^^.theSortObj := SorterObj;
  150.     end;    {Function DoInitialize}
  151.  
  152.  
  153.     function DoBlank (storage: Handle;
  154.                                     blankRgn: rgnHandle;
  155.                                     params: GMParamBlockPtr): OSErr;
  156.     begin
  157. {First, erase outside the screen I’m going to draw on.}
  158.         EraseOutsideDrawArea(blankRgn, params);
  159. {Second, randomize the screen I’m going to draw on, if requested.}
  160.         if params^.controlValues[kBlankFirstControl] = 1 then
  161.             begin
  162.                 LockForDrawing;
  163.                 RandomFillScreen;
  164.                 UnlockForDrawing;
  165.             end;
  166.         DoBlank := noErr;
  167.     end;    {function DoBlank}
  168.  
  169.  
  170.     function DoDrawFrame (storage: Handle;
  171.                                     blankRgn: rgnHandle;
  172.                                     params: GMParamBlockPtr): OSErr;
  173.  
  174.     begin
  175.         LockForDrawing;
  176.         StoredStuffH(storage)^^.theSortObj.DoALittle;
  177.         if StoredStuffH(storage)^^.theSortObj.Done then
  178.             DoDrawFrame := ImDone
  179.         else
  180.             DoDrawFrame := noErr;
  181.         UnlockForDrawing;
  182.     end;
  183.  
  184.  
  185.     function DoClose (storage: Handle;
  186.                                     blankRgn: RgnHandle;
  187.                                     params: GMParamBlockPtr): OSErr;
  188.     begin
  189.         StoredStuffH(storage)^^.theSortObj.Free;
  190.         DisposHandle(storage);
  191.         DoClose := noErr;
  192.     end;
  193.  
  194.  
  195.     function DoSetup (blankRgn: rgnHandle;
  196.                                     message: integer;
  197.                                     params: GMParamBlockPtr): OSErr;
  198.     begin
  199.  
  200. {This is called when the used clicks on a button in the Control Panel.}
  201.  
  202.         DoSetup := noErr;
  203.  
  204.     end;
  205.  
  206.  
  207. {$S %_MethTables}
  208. {$Push}
  209. {$N-}
  210.     procedure LoadMethTables;
  211.     begin
  212.  
  213.     end;
  214. {$Pop}
  215. {$S}
  216.  
  217.  
  218.     function main (var storage: Handle;
  219.                                     blankRgn: rgnHandle;
  220.                                     message: integer;
  221.                                     params: GMParamBlockPtr): OSErr;
  222. {Taken from the After Dark programming info files.}
  223.         var
  224.             err: OSErr;
  225.     begin {main}
  226.  
  227.  
  228. {$IFC ADModule}
  229.         SetUpA4;                {Works around a bug in the ThP 4.0.2 libraries. Put RememberA4 before}
  230.         RememberA4;        {SetUpA4 if compiling with fixed libraries.}
  231.         LoadMethTables;
  232. {$ENDC}
  233.  
  234.         err := noErr;
  235.         case message of
  236.             Initialize: 
  237.                 err := DoInitialize(storage, blankRgn, params);
  238.             Close: 
  239.                 err := DoClose(storage, blankRgn, params);
  240.             Blank: 
  241.                 err := DoBlank(storage, blankRgn, params);
  242.             DrawFrame: 
  243.                 err := DoDrawFrame(storage, blankRgn, params);
  244.             otherwise
  245.                 if (message >= ButtonMessage) then
  246.                     err := DoSetup(blankRgn, message, params);
  247.         end;
  248.         main := err;
  249.  
  250. {$IFC ADModule}
  251.         RestoreA4;
  252. {$ENDC}
  253.     end; {main}
  254.  
  255.  
  256.  
  257.     procedure SelectionSort;
  258. {Selection sort the whole screen.}
  259.         var
  260.             i, j, min: longint;
  261.     begin
  262.         for i := 1 to ScreenPixels - 1 do
  263.             begin
  264.                 min := i;
  265.                 for j := i + 1 to ScreenPixels do
  266.                     if MyGetPixel(j).sortValue < MyGetPixel(min).sortValue then
  267.                         min := j;
  268.                 SwapPixels(i, min);
  269.             end;
  270.     end;    {procedure SelectionSort}
  271.  
  272.  
  273.  
  274.     procedure RadixExchange (left, right: longint;
  275.                                     bit: integer);
  276.         var
  277.             i, j, mask: longint;
  278.     begin
  279.         mask := BSL(1, bit);
  280.         if (right > left) and (bit >= 0) then
  281.             begin
  282.                 i := left;
  283.                 j := right;
  284.                 repeat
  285.                     while (BAND(mask, MyGetPixel(i).sortValue) = 0) & (i < j) do
  286.                         i := i + 1;
  287.                     while (BAND(mask, MyGetPixel(j).sortValue) <> 0) & (i < j) do
  288.                         j := j - 1;
  289.                     SwapPixels(i, j);
  290.                 until j = i;
  291.                 if BAND(mask, MyGetPixel(right).sortValue) = 0 then
  292.                     j := j + 1;
  293.                 RadixExchange(left, j - 1, bit - 1);
  294.                 RadixExchange(j, right, bit - 1);
  295.             end;
  296.     end;    {procedure RadixExchange}
  297.  
  298.  
  299.  
  300.     procedure SortOneBitScreen;
  301. {Sort the screen, assuming that it is in black and white mode.}
  302. {The screen won't get sorted right if the monitor is not monochrome.}
  303.         var
  304.             i, j: longint;
  305.             pix0, pix1: PixelRec;
  306.     begin
  307.         i := 0;
  308.         j := ScreenPixels + 1;
  309.         pix0.sortValue := 0;
  310.         pix1.sortValue := 1;
  311.         repeat
  312.             repeat
  313.                 i := i + 1
  314.             until (MyGetPixel(i).sortValue = 1) | (i >= j);
  315.             repeat
  316.                 j := j - 1
  317.             until (MyGetPixel(j).sortValue = 0) | (j <= i);
  318.             MySetPixel(i, pix0);
  319.             MySetPixel(j, pix1);
  320.         until i >= j;
  321.         SwapPixels(i, j);    {undo the extra swap with i=j}
  322.     end;    {procedure SortOneBitScreen}
  323.  
  324.  
  325. {$IFC not ADModule}
  326.  
  327.     procedure RunScreenSaver;
  328. {Call this is run your screen saver as if in the After Dark environment.}
  329. {It will print out some timing information as the program runs.}
  330. {To stop, click or press a key or otherwise create an event.}
  331.  
  332.         var
  333.             startFrameTicks, measuredTicks, startCallTicks, endCallTicks, ticksThisCall: longint;    {Timing variables}
  334.             numTimesCalled, maxCallTicks: longint;
  335.             err: OSErr;
  336.             theEvent: EventRecord;
  337.  
  338.  
  339.     begin
  340.         writeln('Timing data in ticks (1/60 second)');
  341.         ObscureCursor;
  342.  
  343.         measuredTicks := 0;
  344.         numTimesCalled := 0;
  345.         maxCallTicks := 0;
  346.  
  347.         startCallTicks := TickCount;
  348.  
  349.         SetPort(GrafPtr(gDrawWindow));
  350.         err := main(gStorage, gBlankRgn, Initialize, @gParams);
  351.         endCallTicks := TickCount;
  352.         writeln('Initialize time: ', endCallTicks - startCallTicks : 1);
  353.  
  354.         startCallTicks := endCallTicks;
  355.  
  356.         if err = noErr then
  357.             begin
  358.                 SetPort(GrafPtr(gDrawWindow));
  359.                 err := main(gStorage, gBlankRgn, Blank, @gParams);
  360.                 endCallTicks := TickCount;
  361.                 ticksThisCall := endCallTicks - startCallTicks;
  362.                 writeln('Blank time: ', ticksThisCall : 1);
  363.  
  364.                 startFrameTicks := TickCount;
  365.  
  366.                 while (err = noErr) and not OSEventAvail(everyEvent, theEvent) do
  367.                     begin
  368.                         startCallTicks := TickCount;
  369.  
  370.                         SetPort(GrafPtr(gDrawWindow));
  371.                         err := main(gStorage, gBlankRgn, DrawFrame, @gParams);
  372.  
  373.                         endCallTicks := TickCount;
  374.                         ticksThisCall := endCallTicks - startCallTicks;
  375.                         measuredTicks := measuredTicks + ticksThisCall;
  376.                         numTimesCalled := numTimesCalled + 1;
  377.                         if (numTimesCalled mod 100) = 0 then
  378.                             writeln('DrawFrame #', numTimesCalled : 1, ' time was ', ticksThisCall : 1, ' ticks.');
  379.                         if ticksThisCall > maxCallTicks then
  380.                             begin
  381.                                 maxCallTicks := ticksThisCall;
  382.                                 writeln('DrawFrame #', numTimesCalled : 1, ' took ', ticksThisCall : 1, ' ticks, a new maximum.');
  383.                             end;
  384.                     end;    {While}
  385.  
  386.                 if (err = noErr) then    {Kill the module because an event happened}
  387.                     begin
  388.                         startCallTicks := endCallTicks;
  389.                         SetPort(GrafPtr(gDrawWindow));
  390.                         err := main(gStorage, gBlankRgn, Close, @gParams);
  391.                         endCallTicks := TickCount;
  392.                         ticksThisCall := endCallTicks - startCallTicks;
  393.                         writeln('Close time: ', ticksThisCall : 1);
  394.                     end;
  395.             end;    {Main running of the module}
  396.  
  397. {Write out timing info.}
  398.         writeln;
  399.         write('Module ended ');
  400.         case err of
  401.             noErr: 
  402.                 writeln('normally.');
  403.             ModuleError: 
  404.                 writeln('with a module error (probably out of memory).');
  405.             RestartMe: 
  406.                 writeln('asking to be restarted.');
  407.             ImDone: 
  408.                 writeln('because it was done.');
  409.             otherwise
  410.                 writeln('with error number ', err : 1);
  411.         end;    {case}
  412.  
  413.         writeln('Timing summary.');
  414.         ticksThisCall := TickCount - startFrameTicks;    {Total time used}
  415.         writeln('Measured DrawFrame time: ', measuredTicks : 1, ' ticks, or ', measuredTicks div 60 : 1, ' seconds.');
  416.         writeln('Total DrawFrame time: ', ticksThisCall : 1, ' ticks, or ', ticksThisCall div 60 : 1, ' seconds.');
  417.         writeln('DrawFrame was called ', numTimesCalled : 1, ' times.');
  418.         if numTimesCalled > 0 then
  419.             writeln('Average time per frame: ', ticksThisCall / numTimesCalled : 1 : 1, ' ticks.');
  420.         writeln('Maximum time for a single frame: ', maxCallTicks : 1, ' ticks.');
  421.  
  422.     end;    {procedureRunScreenSaver}
  423.  
  424.  
  425.  
  426.     procedure GeneralImpersonationSetup;
  427. {Fill in the globals containing the After Dark-supplied info the module needs.}
  428. {Not everything is supplied because this isn't really After Dark.}
  429. {In particular, only one monitor, no sound, and systemConfig isn't set.}
  430.         var
  431.             GestaltResult: longint;
  432.     begin
  433.         gStorage := nil;
  434.         with gParams do
  435.             begin
  436.                 monitors := @gMonitors;
  437.                 if (Gestalt(gestaltQuickDrawVersion, GestaltResult) = noErr) & (GestaltResult >= gestalt8BitQD) then
  438.                     colorQDAvail := true
  439.                 else
  440.                     colorQDAvail := false;
  441.                 gMonitors.monitorCount := 0;
  442.                 with gMonitors.monitorList[0] do
  443.                     begin
  444.                         if colorQDAvail then        {Color machine}
  445.                             begin
  446.                                 gDrawWindow := WindowPtr(GetNewCWindow(128, nil, WindowPtr(-1)));
  447.                                 curDepth := GetMainDevice^^.gdPMap^^.pixelSize
  448.                             end
  449.                         else        {Monochrome machine}
  450.                             begin
  451.                                 gDrawWindow := GetNewWindow(128, nil, WindowPtr(-1));
  452.                                 curDepth := 1;
  453.                             end;
  454.                         bounds := gDrawWindow^.portRect;        {In local coords; (0,0) is the top left of the drawing area}
  455.                         synchFlag := true;
  456.                     end;    {Setting up monitors info}
  457.  
  458.                 systemConfig := 0;        {I'm not supporting this yet}
  459.                 qdGlobalsCopy := @gQdGlobals;
  460.                 SetRect(demoRect, 0, 0, 0, 0);
  461.                 errorMessage := @gModuleErrorMessage;
  462.                 sndChannel := nil;
  463.                 adVersion := $0200;
  464.             end;    {Initializing gParams}
  465.  
  466.         gBlankRgn := NewRgn;
  467.         RectRgn(gBlankRgn, gMonitors.monitorList[0].bounds);
  468.  
  469.         with gQdGlobals do
  470.             begin
  471.                 qdThePort := thePort;
  472.                 qdWhite := white;
  473.                 qdBlack := black;
  474.                 qdGray := gray;
  475.                 qdLtGray := ltGray;
  476.                 qdDkGray := dkGray;
  477.                 qdArrow := arrow;
  478.                 qdScreenBits := screenBits;
  479.                 qdRandSeed := RandSeed;
  480.             end;
  481.     end;        {Procedure GeneralImpersonationSetup}
  482.  
  483.  
  484.     procedure SpecificImpersonationSetup;
  485. {Settings for your module go here. The only thing I can think of to put here is the control values.}
  486. {Change the settings to simulate changing the settings in the control panel.}
  487.     begin
  488.         gParams.controlValues[kSortTypeMenu] := iHeapsort;
  489.         gParams.controlValues[kBlankFirstControl] := 1;        {1 to blank first, 0 to use screen as is}
  490.         gParams.controlValues[kDirectPixelsControl] := 1;    {0 for QD, 1 for direct to screen}
  491.     end;        {procedure SpecificImpersonationSetup}
  492.  
  493.  
  494. begin    {Impersonation of After Dark}
  495.     ShowText;
  496.  
  497.     GeneralImpersonationSetup;
  498.     SpecificImpersonationSetup;
  499.     ObscureCursor;
  500.  
  501.     SetPort(GrafPtr(gDrawWindow));
  502.     gQdGlobals.qdThePort := thePort;        {Just to be sure it's set up correctly.}
  503.  
  504.  
  505.     RunScreenSaver;
  506.  
  507.  
  508. {$ENDC}
  509. end.