home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Libraries / Marching Ants Library / MarchingAnts.c next >
Encoding:
Text File  |  1994-07-12  |  31.5 KB  |  713 lines  |  [TEXT/MPCC]

  1. //=====================================================================================
  2. // MarchingAnts.c -- written by Aaron Giles
  3. // Last update: 7/7/94 (version 1.1)
  4. //=====================================================================================
  5. // A source code library for handling rectangular graphical selections involving the
  6. // famous "marching ants".  See the header file for a brief description of each
  7. // function; see the prelude to each function definition below for a full description
  8. // of its operation and expected use.
  9. //=====================================================================================
  10. // This code has been compiled successfully under MPW C, MPW PPCC, THINK C, and
  11. // Metrowerks C/C++, both 68k and PowerPC.  This code has been compiled under both the
  12. // Universal Headers and the old 7.1 headers; if it works earlier than that I cannot
  13. // say for sure.
  14. //=====================================================================================
  15. // If you find any bugs in this source code, please email me and I will attempt to fix
  16. // them.  If you have any additions/modifications that you think would be generally
  17. // useful, email those to me as well, and I will consider incorporating them into the
  18. // next release.  My email address is giles@med.cornell.edu.
  19. //=====================================================================================
  20. // This source code is copyright © 1994, Aaron Giles.  Permission to use this code in
  21. // your product is freely granted, provided that you credit me appropriately in your
  22. // application's About box/credits *and* documentation.  If you ship an application
  23. // which uses this code, I would also like to request that you provide me with one
  24. // complimentary copy of the application.
  25. //=====================================================================================
  26.  
  27. //=====================================================================================
  28. // Include standard MacOS definitions.
  29. //=====================================================================================
  30.  
  31. #include <Errors.h>
  32. #include <Events.h>
  33. #include <Memory.h>
  34. #include <QuickDraw.h>
  35. #include <Types.h>
  36. #include <Windows.h>
  37.  
  38. //=====================================================================================
  39. // Include our own external definitions.
  40. //=====================================================================================
  41.  
  42. #include "MarchingAnts.h"
  43.  
  44. //=====================================================================================
  45. // Prototypes for internal functions local to this module.
  46. //=====================================================================================
  47.  
  48. static OSErr DrawAnts(AntsReference theAnts);
  49. static OSErr EraseAnts(AntsReference theAnts);
  50. static OSErr MarchAnts(AntsReference theAnts);
  51. static void SaveToGWorlds(AntsReference theAnts);
  52. static void RestoreFromGWorlds(AntsReference theAnts);
  53. static void CopyToCurrentPort(GWorldPtr srcGWorld, Rect *srcRect, Rect *dstRect);
  54. static void CopyFromCurrentPort(GWorldPtr dstGWorld, Rect *srcRect, Rect *dstRect);
  55.  
  56. //=====================================================================================
  57. // Internal global variable declarations.
  58. //=====================================================================================
  59.  
  60. static Pattern gAnimatedAntsPatternTop =
  61.             { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 };
  62. static Pattern gAnimatedAntsPatternBottom =
  63.             { 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0 };
  64. static Pattern gAnimatedAntsPatternLeft =
  65.             { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
  66. static Pattern gAnimatedAntsPatternRight =
  67.             { 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00 };
  68. static Pattern gInactiveAntsPattern =
  69.             { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
  70.  
  71. //=====================================================================================
  72. // AntsReference NewAnts(void)
  73. //=====================================================================================
  74. // Allocates memory for a new marching ants record.  The newly-created ants are by
  75. // default active but not visible, and have no selection.  Following this call, you
  76. // will need to set the ants' port (SetAntsPort()) and the limit rectangle
  77. // (SetAntsLimitRect()) before doing anything really useful.  This function returns nil
  78. // if memory is incredibly tight.
  79. //=====================================================================================
  80.  
  81. extern AntsReference NewAnts(void)
  82. {
  83.     AntsReference theAnts = (AntsReference)NewHandleClear(sizeof(AntsRecord));
  84.     if (theAnts) (*theAnts)->active = true;            // active by default
  85.     return theAnts;
  86. }
  87.  
  88. //=====================================================================================
  89. // void DisposeAnts(AntsReference theAnts)
  90. //=====================================================================================
  91. // Disposes of all memory allocated for drawing the ants specified by theAnts.  After
  92. // this function is called, the AntsReference is no longer valid.
  93. //=====================================================================================
  94.  
  95. extern void DisposeAnts(AntsReference theAnts)
  96. {
  97.     if ((*theAnts)->verticalWorld) DisposeGWorld((*theAnts)->verticalWorld);
  98.     if ((*theAnts)->horizontalWorld) DisposeGWorld((*theAnts)->horizontalWorld);
  99.     DisposeHandle((Handle)theAnts);
  100. }
  101.  
  102. //=====================================================================================
  103. // Boolean AntsHaveSelection(AntsReference theAnts)
  104. //=====================================================================================
  105. // Returns true if the specified ants have a non-nil selection.  Use this function to
  106. // determine if there is, in fact, some selected area you should be operating on.
  107. //=====================================================================================
  108.  
  109. extern Boolean AntsHaveSelection(AntsReference theAnts)
  110. {
  111.     return (((*theAnts)->bounds.right - (*theAnts)->bounds.left) > antsMinimumX &&
  112.                 ((*theAnts)->bounds.bottom - (*theAnts)->bounds.top) > antsMinimumY);
  113. }
  114.  
  115. //=====================================================================================
  116. // void GetAntsSelection(AntsReference theAnts, Rect *theBounds)
  117. //=====================================================================================
  118. // Returns, in theBounds, the current selection specified by theAnts.  If there is no
  119. // selection, this function will return the rectangle { 0, 0, 0, 0 }, so be careful if
  120. // you're doing something like calling MapRect() on it.
  121. //=====================================================================================
  122.  
  123. extern void GetAntsSelection(AntsReference theAnts, Rect *theBounds)
  124. {
  125.     *theBounds = (*theAnts)->bounds;
  126. }
  127.  
  128. //=====================================================================================
  129. // void SetAntsSelection(AntsReference theAnts, Rect *newBounds)
  130. //=====================================================================================
  131. // Changes the current selection specified by theAnts to the rectangle described by
  132. // newBounds.  If the ants are currently visible, they are hidden and then reshown
  133. // after the operation is complete.  Attempting to set the selection to anything
  134. // smaller than 8 pixels in any direction will create a nil selection, causing any
  135. // visible selection to disappear.  An easier way of doing this latter operation is to
  136. // simply call ResetAntsSelection(), described below.
  137. //=====================================================================================
  138.  
  139. extern void SetAntsSelection(AntsReference theAnts, Rect *newBounds)
  140. {
  141.     Boolean wasDrawn = (*theAnts)->drawn;
  142.  
  143.     if (wasDrawn) HideAnts(theAnts);
  144.     if (newBounds->left != (*theAnts)->bounds.left ||
  145.                 newBounds->right != (*theAnts)->bounds.right ||
  146.                 newBounds->top != (*theAnts)->bounds.top ||
  147.                 newBounds->bottom != (*theAnts)->bounds.bottom) {
  148.         if ((*theAnts)->horizontalWorld)
  149.             DisposeGWorld((*theAnts)->horizontalWorld), (*theAnts)->horizontalWorld = nil;
  150.         if ((*theAnts)->verticalWorld)
  151.             DisposeGWorld((*theAnts)->verticalWorld), (*theAnts)->verticalWorld = nil;
  152.     }
  153.     if ((newBounds->right - newBounds->left) > antsMinimumX &&
  154.                 (newBounds->bottom - newBounds->top) > antsMinimumY) {
  155.         (*theAnts)->bounds = *newBounds;
  156.         SaveToGWorlds(theAnts);
  157.     } else (*theAnts)->bounds.top = (*theAnts)->bounds.bottom =
  158.                 (*theAnts)->bounds.left = (*theAnts)->bounds.right = 0;
  159.     if (wasDrawn) ShowAnts(theAnts);
  160. }
  161.  
  162. //=====================================================================================
  163. // void ResetAntsSelection(AntsReference theAnts)
  164. //=====================================================================================
  165. // Clears the current selection specified by theAnts to nil.  If the ants are currently
  166. // visible, the visible selection will disappear.
  167. //=====================================================================================
  168.  
  169. extern void ResetAntsSelection(AntsReference theAnts)
  170. {
  171.     static Rect gNullRect = { 0, 0, 0, 0 };
  172.     
  173.     HideAnts(theAnts);
  174.     SetAntsSelection(theAnts, &gNullRect);
  175. }
  176.  
  177. //=====================================================================================
  178. // void GetAntsLimitRect(AntsReference theAnts, Rect *limitRect)
  179. //=====================================================================================
  180. // Returns in limitRect the current limit rectangle specified by theAnts.  See also
  181. // the function SetAntsLimitRect() for a full description of what the limit rectangle
  182. // does.
  183. //=====================================================================================
  184.  
  185. extern void GetAntsLimitRect(AntsReference theAnts, Rect *limitRect)
  186. {
  187.     *limitRect = (*theAnts)->limitRect;
  188. }
  189.  
  190. //=====================================================================================
  191. // void SetAntsLimitRect(AntsReference theAnts, Rect *newLimit)
  192. //=====================================================================================
  193. // Sets the limit rectangle for theAnts to the rectangle newLimit.  The limit rectangle
  194. // is used in two places, and it is important that you update this value whenever your
  195. // window changes size.  The first and most common place the limit rectangle is used is
  196. // when drawing the ants; in this case, the limit rectangle is used as the clipping
  197. // rectangle for drawing.  The limit rectangle is also used when tracking the mouse
  198. // during a selection, to specify the outermost boundaries of the area the user may
  199. // select.
  200. //=====================================================================================
  201.  
  202. extern void SetAntsLimitRect(AntsReference theAnts, Rect *newLimit)
  203. {
  204.     (*theAnts)->limitRect = *newLimit;
  205. }
  206.  
  207. //=====================================================================================
  208. // GrafPtr GetAntsPort(AntsReference theAnts)
  209. //=====================================================================================
  210. // Returns the port the ants are currently drawn into.  Use the SetAntsPort() function,
  211. // below, to change the associated GrafPort.
  212. //=====================================================================================
  213.  
  214. extern GrafPtr GetAntsPort(AntsReference theAnts)
  215. {
  216.     return (*theAnts)->port;
  217. }
  218.  
  219. //=====================================================================================
  220. // void SetAntsPort(AntsReference theAnts, GrafPtr thePort)
  221. //=====================================================================================
  222. // Use this function to set the ants' destination port.  The destination port
  223. // determines where the ants will be drawn and tracked.
  224. //=====================================================================================
  225.  
  226. extern void SetAntsPort(AntsReference theAnts, GrafPtr thePort)
  227. {
  228.     (*theAnts)->port = thePort;
  229. }
  230.  
  231. //=====================================================================================
  232. // Boolean AreAntsVisible(AntsReference theAnts)
  233. //=====================================================================================
  234. // Returns true if the ants are currently visible.  Note that this merely reflects the
  235. // state of the internal visible flag, which can be set by the HideAnts() and
  236. // ShowAnts() functions.
  237. //=====================================================================================
  238.  
  239. extern Boolean AreAntsVisible(AntsReference theAnts)
  240. {
  241.     return (*theAnts)->visible;
  242. }
  243.  
  244. //=====================================================================================
  245. // void TrackAntsSelection(AntsReference theAnts, Point localStart,
  246. //            AntsScrollProc scroll, long refCon)
  247. //=====================================================================================
  248. // Call this function to track the mouse as a new selection is made.  The point
  249. // localStart is the location of mouse down event, in local coordinates.  scroll is a
  250. // pointer to a callback function which gets called whenever the mouse pointer moves
  251. // outside of the ants' limit rectangle.  It gets passed two pointers to shorts,
  252. // indicating how much we want to scroll by; your function should scroll as much as
  253. // you can and store the amount actually scrolled back into the two shorts.  You can
  254. // also pass nil for scroll if you don't want to do autoscrolling.  The refCon
  255. // parameter gets passed along to your callback function, for storing a pointer to
  256. // information which might be needed.  Once this function returns, you can use the
  257. // AntsHaveSelection() function to determine if a selection was, in fact, made.
  258. //=====================================================================================
  259.  
  260. extern void TrackAntsSelection(AntsReference theAnts, Point localStart,
  261.             AntsScrollProc scroll, long refCon)
  262. {
  263.     static Pattern gTrackSelectionPattern =
  264.                 { 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55 };
  265.     Rect newBounds, limitRect = (*theAnts)->limitRect;
  266.     Point lastPoint, newPoint;
  267.     RgnHandle saveClip;
  268.     PenState oldState;
  269.     GrafPtr oldPort;
  270.     short dx, dy;
  271.     long pinned;
  272.  
  273.     GetPort(&oldPort);
  274.     SetPort((*theAnts)->port);
  275.     if (saveClip = NewRgn()) {
  276.         GetClip(saveClip);
  277.         ClipRect(&limitRect);
  278.         GetPenState(&oldState);
  279.         PenNormal();
  280.         PenMode(srcXor);
  281.         PenPat(&gTrackSelectionPattern);
  282.         lastPoint = localStart;
  283.         newBounds.left = newBounds.right = localStart.h;
  284.         newBounds.top = newBounds.bottom = localStart.v;
  285.         FrameRect(&newBounds);
  286.         while (StillDown()) {
  287.             GetMouse(&newPoint);
  288.             if (newPoint.h == lastPoint.h && newPoint.v == lastPoint.v &&
  289.                         PtInRect(newPoint, &limitRect)) continue;
  290.             dx = dy = 0;
  291.             if (newPoint.h > limitRect.right) dx = (newPoint.h - limitRect.right > 8) ? -16 : -1;
  292.             else if (newPoint.h < limitRect.left) dx = (limitRect.left - newPoint.h > 8) ? 16 : 1;
  293.             if (newPoint.v > limitRect.bottom) dy = (newPoint.v - limitRect.bottom > 8) ? -16 : -1;
  294.             else if (newPoint.v < limitRect.top) dy = (limitRect.top - newPoint.v > 8) ? 16 : 1;
  295.             FrameRect(&newBounds);
  296.             if (scroll && (dx || dy)) {
  297.                 scroll(&dx, &dy, refCon);
  298.                 ClipRect(&limitRect);
  299.                 PenNormal();
  300.                 PenMode(srcXor);
  301.                 PenPat(&gTrackSelectionPattern);
  302.                 localStart.h += dx;
  303.                 localStart.v += dy;
  304.                 OffsetRect(&newBounds, dx, dy);
  305.             }
  306.             pinned = PinRect(&limitRect, newPoint);
  307.             newPoint = *(Point *)&pinned;
  308.             if (newPoint.h > localStart.h) {
  309.                 newBounds.left = localStart.h;
  310.                 newBounds.right = newPoint.h + 1;
  311.             } else {
  312.                 newBounds.left = newPoint.h;
  313.                 newBounds.right = localStart.h + 1;
  314.             }
  315.             if (newPoint.v > localStart.v) {
  316.                 newBounds.top = localStart.v;
  317.                 newBounds.bottom = newPoint.v + 1;
  318.             } else {
  319.                 newBounds.top = newPoint.v;
  320.                 newBounds.bottom = localStart.v + 1;
  321.             }
  322.             FrameRect(&newBounds);
  323.             lastPoint = newPoint;
  324.         }
  325.         FrameRect(&newBounds);
  326.         SetAntsSelection(theAnts, &newBounds);
  327.         SetPenState(&oldState);
  328.         SetClip(saveClip);
  329.         DisposeRgn(saveClip);
  330.     }
  331.     SetPort(oldPort);
  332. }
  333.  
  334. //=====================================================================================
  335. // void ShowAnts(AntsReference theAnts)
  336. //=====================================================================================
  337. // This function forces the ants to become visible if they weren't already.  At this
  338. // time we also save what is currently behind the selection rectangle in GWorlds so
  339. // that we can do a clean restore.  Note that this is the *only* time we save the area
  340. // behind the ants.  However, this is usually ok, since we need to call HideAnts()
  341. // before doing any drawing anyhow, and then ShowAnts() again after the drawing is
  342. // complete.
  343. //=====================================================================================
  344.  
  345. extern void ShowAnts(AntsReference theAnts)
  346. {
  347.     (*theAnts)->visible = true;
  348.     if (!(*theAnts)->drawn) {
  349.         SaveToGWorlds(theAnts);
  350.         DrawAnts(theAnts);
  351.     }
  352. }
  353.  
  354. //=====================================================================================
  355. // void HideAnts(AntsReference theAnts)
  356. //=====================================================================================
  357. // This function hides the ants and restores any background behind them.  This function
  358. // should get called before any drawing occurs, so as not to overwrite the current
  359. // ants.
  360. //=====================================================================================
  361.  
  362. extern void HideAnts(AntsReference theAnts)
  363. {
  364.     if ((*theAnts)->drawn) EraseAnts(theAnts);
  365.     (*theAnts)->visible = false;
  366. }
  367.  
  368. //=====================================================================================
  369. // void AnimateAnts(AntsReference theAnts)
  370. //=====================================================================================
  371. // This function rotates the ants forward one notch and redraws them.  Call this
  372. // function in your null event handler, whenever the time is appropriate.
  373. //=====================================================================================
  374.  
  375. extern void AnimateAnts(AntsReference theAnts)
  376. {
  377.     if (!(*theAnts)->verticalWorld || !(*theAnts)->horizontalWorld) EraseAnts(theAnts);
  378.     MarchAnts(theAnts);
  379.     DrawAnts(theAnts);
  380. }
  381.  
  382. //=====================================================================================
  383. // void ActivateAnts(AntsReference theAnts)
  384. //=====================================================================================
  385. // Call this function to activate the specified ants selection (i.e., when the window
  386. // the ants are in receives an activate event).  This means that the ants will be
  387. // displayed as an animated dashed rectangle that rotates whenever AnimateAnts() is
  388. // called.
  389. //=====================================================================================
  390.  
  391. extern void ActivateAnts(AntsReference theAnts)
  392. {
  393.     if (!(*theAnts)->verticalWorld || !(*theAnts)->horizontalWorld) EraseAnts(theAnts);
  394.     (*theAnts)->active = true;
  395.     DrawAnts(theAnts);
  396. }
  397.  
  398. //=====================================================================================
  399. // void DeactivateAnts(AntsReference theAnts)
  400. //=====================================================================================
  401. // Call this function to deactivate the specified ants selection (i.e., when the window
  402. // the ants are in receives an deactivate event).  This means that the ants will be
  403. // displayed as a steady dotted rectangle which does nothing when AnimateAnts() is
  404. // called.
  405. //=====================================================================================
  406.  
  407. extern void DeactivateAnts(AntsReference theAnts)
  408. {
  409.     if (!(*theAnts)->verticalWorld || !(*theAnts)->horizontalWorld) EraseAnts(theAnts);
  410.     (*theAnts)->active = false;
  411.     DrawAnts(theAnts);
  412. }
  413.  
  414. /**************************************************************************************
  415. ***************************************************************************************
  416. **********                                                                   **********
  417. **********                                                                   **********
  418. **********          E N D   O F   P U B L I C   I N T E R F A C E S          **********
  419. **********                                                                   **********
  420. **********                                                                   **********
  421. ***************************************************************************************
  422. **************************************************************************************/
  423.  
  424. //=====================================================================================
  425. // OSErr DrawAnts(AntsReference theAnts)
  426. //=====================================================================================
  427. // INTERNAL FUNCTION.  Draws the ants if they are supposed to be visible.  If there was
  428. // enough memory for the offscreen GWorlds, we draw them using srcCopy mode to make
  429. // them nice and visible.  If there was not enough memory for the GWorlds, we use
  430. // srcXor mode instead so that things still work, if not as prettily.
  431. //=====================================================================================
  432.  
  433. static OSErr DrawAnts(AntsReference theAnts)
  434. {
  435.     OSErr theErr = noErr;
  436.     RgnHandle saveClip;
  437.     PenState oldState;
  438.     GrafPtr oldPort;
  439.     Rect theRect;
  440.  
  441.     if (!(*theAnts)->visible) return noErr;
  442.     theRect = (*theAnts)->bounds;
  443.     if ((theRect.bottom - theRect.top) == 0 || (theRect.right - theRect.left) == 0)
  444.         return noErr;
  445.     GetPort(&oldPort);
  446.     SetPort((*theAnts)->port);
  447.     if (saveClip = NewRgn()) {
  448.         GetClip(saveClip);
  449.         theRect = (*theAnts)->limitRect;
  450.         ClipRect(&theRect);
  451.         GetPenState(&oldState);
  452.         PenNormal();
  453.         if (!(*theAnts)->horizontalWorld || !(*theAnts)->verticalWorld) PenMode(srcXor);
  454.         else PenMode(srcCopy);
  455.         MoveTo((*theAnts)->bounds.left, (*theAnts)->bounds.top);
  456.         if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternTop);
  457.         else PenPat(&gInactiveAntsPattern);
  458.         LineTo((*theAnts)->bounds.right - 1, (*theAnts)->bounds.top);
  459.         if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternRight);
  460.         LineTo((*theAnts)->bounds.right - 1, (*theAnts)->bounds.bottom - 1);
  461.         if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternBottom);
  462.         LineTo((*theAnts)->bounds.left, (*theAnts)->bounds.bottom - 1);
  463.         if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternLeft);
  464.         LineTo((*theAnts)->bounds.left, (*theAnts)->bounds.top);
  465.         SetPenState(&oldState);
  466.         (*theAnts)->drawn = true;
  467.         SetClip(saveClip);
  468.         DisposeRgn(saveClip);
  469.     } else theErr = memFullErr;
  470.     SetPort(oldPort);
  471.     return theErr;
  472. }
  473.  
  474. //=====================================================================================
  475. // OSErr EraseAnts(AntsReference theAnts)
  476. //=====================================================================================
  477. // INTERNAL FUNCTION.  Erases the ants if they are supposed to be visible.  If there
  478. // was enough memory for the offscreen GWorlds, we copy their contents back into the
  479. // window.  If there was not enough memory for the GWorlds, we just redraw the ants
  480. // using srcXor mode which erases them.
  481. //=====================================================================================
  482.  
  483. static OSErr EraseAnts(AntsReference theAnts)
  484. {
  485.     OSErr theErr = noErr;
  486.     RgnHandle saveClip;
  487.     PenState oldState;
  488.     GrafPtr oldPort;
  489.     Rect theRect;
  490.  
  491.     theRect = (*theAnts)->bounds;
  492.     if ((theRect.bottom - theRect.top) == 0 || (theRect.right - theRect.left) == 0)
  493.         return noErr;
  494.     GetPort(&oldPort);
  495.     SetPort((*theAnts)->port);
  496.     if (saveClip = NewRgn()) {
  497.         GetClip(saveClip);
  498.         theRect = (*theAnts)->limitRect;
  499.         ClipRect(&theRect);
  500.         GetPenState(&oldState);
  501.         PenNormal();
  502.         if (!(*theAnts)->horizontalWorld || !(*theAnts)->verticalWorld) {
  503.             PenMode(srcXor);
  504.             MoveTo((*theAnts)->bounds.left, (*theAnts)->bounds.top);
  505.             if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternTop);
  506.             else PenPat(&gInactiveAntsPattern);
  507.             LineTo((*theAnts)->bounds.right - 1, (*theAnts)->bounds.top);
  508.             if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternRight);
  509.             LineTo((*theAnts)->bounds.right - 1, (*theAnts)->bounds.bottom - 1);
  510.             if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternBottom);
  511.             LineTo((*theAnts)->bounds.left, (*theAnts)->bounds.bottom - 1);
  512.             if ((*theAnts)->active) PenPat(&gAnimatedAntsPatternLeft);
  513.             LineTo((*theAnts)->bounds.left, (*theAnts)->bounds.top);
  514.             SetPenState(&oldState);
  515.         } else RestoreFromGWorlds(theAnts);
  516.         (*theAnts)->drawn = false;
  517.         SetClip(saveClip);
  518.         DisposeRgn(saveClip);
  519.     } else theErr = memFullErr;
  520.     SetPort(oldPort);
  521.     return theErr;
  522. }
  523.  
  524. //=====================================================================================
  525. // OSErr MarchAnts(AntsReference theAnts)
  526. //=====================================================================================
  527. // INTERNAL FUNCTION.  Rotates the QuickDraw patterns used to draw the ants.
  528. //=====================================================================================
  529.  
  530. static OSErr MarchAnts(AntsReference theAnts)
  531. {
  532. #if applec
  533. #pragma unused(theAnts)
  534. #endif
  535.     char temp;
  536.     int i;
  537.     
  538. #ifdef dangerousPattern
  539.     temp = ((gAnimatedAntsPatternTop[0] & 1) ? 0x80 : 0) + (gAnimatedAntsPatternTop[0] >> 1);
  540.     for (i = 0; i < 8; i++) gAnimatedAntsPatternTop[i] = temp;
  541.     temp = ((gAnimatedAntsPatternBottom[0] & 0x80) ? 1 : 0) + (gAnimatedAntsPatternBottom[0] << 1);
  542.     for (i = 0; i < 8; i++) gAnimatedAntsPatternBottom[i] = temp;
  543.     temp = gAnimatedAntsPatternRight[7];
  544.     for (i = 7; i > 0; i--) gAnimatedAntsPatternRight[i] = gAnimatedAntsPatternRight[i - 1];
  545.     gAnimatedAntsPatternRight[0] = temp;
  546.     temp = gAnimatedAntsPatternLeft[0];
  547.     for (i = 0; i < 7; i++) gAnimatedAntsPatternLeft[i] = gAnimatedAntsPatternLeft[i + 1];
  548.     gAnimatedAntsPatternLeft[7] = temp;
  549. #else
  550.     temp = ((gAnimatedAntsPatternTop.pat[0] & 1) ? 0x80 : 0) + (gAnimatedAntsPatternTop.pat[0] >> 1);
  551.     for (i = 0; i < 8; i++) gAnimatedAntsPatternTop.pat[i] = temp;
  552.     temp = ((gAnimatedAntsPatternBottom.pat[0] & 0x80) ? 1 : 0) + (gAnimatedAntsPatternBottom.pat[0] << 1);
  553.     for (i = 0; i < 8; i++) gAnimatedAntsPatternBottom.pat[i] = temp;
  554.     temp = gAnimatedAntsPatternRight.pat[7];
  555.     for (i = 7; i > 0; i--) gAnimatedAntsPatternRight.pat[i] = gAnimatedAntsPatternRight.pat[i - 1];
  556.     gAnimatedAntsPatternRight.pat[0] = temp;
  557.     temp = gAnimatedAntsPatternLeft.pat[0];
  558.     for (i = 0; i < 7; i++) gAnimatedAntsPatternLeft.pat[i] = gAnimatedAntsPatternLeft.pat[i + 1];
  559.     gAnimatedAntsPatternLeft.pat[7] = temp;
  560. #endif
  561.     return noErr;
  562. }
  563.  
  564. //=====================================================================================
  565. // void SaveToGWorlds(AntsReference theAnts)
  566. //=====================================================================================
  567. // INTERNAL FUNCTION.  Attempts to create offscreen GWorlds for saving the area
  568. // behind the ants.  If successful, we go ahead and do CopyBits from the screen(s) to
  569. // the GWorlds; otherwise, we leave the GWorldPtrs set to nil to indicate that we
  570. // should draw using srcXor instead.
  571. //=====================================================================================
  572.  
  573. static void SaveToGWorlds(AntsReference theAnts)
  574. {
  575.     Rect srcRect, dstRect;
  576.     GWorldPtr theGWorld;
  577.     OSErr theErr;
  578.     Rect theRect;
  579.     
  580.     if (!(*theAnts)->horizontalWorld) {
  581.         theRect = (*theAnts)->bounds;
  582.         theRect.bottom = theRect.top + 2;
  583.         OffsetRect(&theRect, -theRect.left, -theRect.top);
  584.         theErr = NewGWorld(&theGWorld, 32, &theRect, nil, nil, 0);
  585.         if (theErr == noErr && theGWorld) (*theAnts)->horizontalWorld = theGWorld;
  586.         else return;
  587.     }
  588.     if (!(*theAnts)->verticalWorld) {
  589.         theRect = (*theAnts)->bounds;
  590.         theRect.right = theRect.left + 2;
  591.         OffsetRect(&theRect, -theRect.left, -theRect.top);
  592.         theErr = NewGWorld(&theGWorld, 32, &theRect, nil, nil, 0);
  593.         if (theErr == noErr && theGWorld) (*theAnts)->verticalWorld = theGWorld;
  594.         else return;
  595.     }
  596.     srcRect = (*theAnts)->bounds;
  597.     srcRect.bottom = srcRect.top + 1;
  598.     dstRect = srcRect;
  599.     OffsetRect(&dstRect, -dstRect.left, -dstRect.top);
  600.     CopyFromCurrentPort((*theAnts)->horizontalWorld, &srcRect, &dstRect);
  601.     srcRect.bottom = (*theAnts)->bounds.bottom;
  602.     srcRect.top = srcRect.bottom - 1;
  603.     dstRect.top++;
  604.     dstRect.bottom++;
  605.     CopyFromCurrentPort((*theAnts)->horizontalWorld, &srcRect, &dstRect);
  606.     srcRect = (*theAnts)->bounds;
  607.     srcRect.right = srcRect.left + 1;
  608.     dstRect = srcRect;
  609.     OffsetRect(&dstRect, -dstRect.left, -dstRect.top);
  610.     CopyFromCurrentPort((*theAnts)->verticalWorld, &srcRect, &dstRect);
  611.     srcRect.right = (*theAnts)->bounds.right;
  612.     srcRect.left = srcRect.right - 1;
  613.     dstRect.left++;
  614.     dstRect.right++;
  615.     CopyFromCurrentPort((*theAnts)->verticalWorld, &srcRect, &dstRect);
  616. }
  617.  
  618. //=====================================================================================
  619. // void RestoreFromGWorlds(AntsReference theAnts)
  620. //=====================================================================================
  621. // INTERNAL FUNCTION.  Restores the original pixels from the GWorlds saved in the
  622. // specified ants record.
  623. //=====================================================================================
  624.  
  625. static void RestoreFromGWorlds(AntsReference theAnts)
  626. {
  627.     static RGBColor gBlack = { 0x0000, 0x0000, 0x0000 }, gWhite = { 0xffff, 0xffff, 0xffff };
  628.     Rect srcRect, dstRect;
  629.     
  630.     RGBForeColor(&gBlack);
  631.     RGBBackColor(&gWhite);
  632.     dstRect = (*theAnts)->bounds;
  633.     dstRect.bottom = dstRect.top + 1;
  634.     srcRect = dstRect;
  635.     OffsetRect(&srcRect, -srcRect.left, -srcRect.top);
  636.     CopyToCurrentPort((*theAnts)->horizontalWorld, &srcRect, &dstRect);
  637.     dstRect.bottom = (*theAnts)->bounds.bottom;
  638.     dstRect.top = dstRect.bottom - 1;
  639.     srcRect.top++;
  640.     srcRect.bottom++;
  641.     CopyToCurrentPort((*theAnts)->horizontalWorld, &srcRect, &dstRect);
  642.     dstRect = (*theAnts)->bounds;
  643.     dstRect.right = dstRect.left + 1;
  644.     srcRect = dstRect;
  645.     OffsetRect(&srcRect, -srcRect.left, -srcRect.top);
  646.     CopyToCurrentPort((*theAnts)->verticalWorld, &srcRect, &dstRect);
  647.     dstRect.right = (*theAnts)->bounds.right;
  648.     dstRect.left = dstRect.right - 1;
  649.     srcRect.left++;
  650.     srcRect.right++;
  651.     CopyToCurrentPort((*theAnts)->verticalWorld, &srcRect, &dstRect);
  652. }
  653.  
  654. //=====================================================================================
  655. // void CopyToCurrentPort(GWorldPtr srcGWorld, Rect *srcRect, Rect *dstRect)
  656. //=====================================================================================
  657. // INTERNAL FUNCTION.  Performs a CopyBits operation from the specified offscreen
  658. // GWorld to the current onscreen port, mapping from srcRect to dstRect.  This makes
  659. // our life easier by taking care of foreground/background colors, destination ports,
  660. // and locked offscreen PixMaps.
  661. //=====================================================================================
  662.  
  663. static void CopyToCurrentPort(GWorldPtr srcGWorld, Rect *srcRect, Rect *dstRect)
  664. {
  665.     static RGBColor gBlack = { 0x0000, 0x0000, 0x0000 }, gWhite = { 0xffff, 0xffff, 0xffff };
  666.     PixMapHandle srcPixMap, dstPixMap;
  667.     char pixelsState;
  668.     GrafPtr thePort;
  669.     
  670.     RGBForeColor(&gBlack);
  671.     RGBBackColor(&gWhite);
  672.     GetPort(&thePort);
  673.     srcPixMap = GetGWorldPixMap(srcGWorld);
  674.     dstPixMap = GetGWorldPixMap((CGrafPtr)thePort);
  675.     pixelsState = GetPixelsState(srcPixMap);
  676.     LockPixels(srcPixMap);
  677.     CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, srcRect, dstRect, srcCopy, nil);
  678.     SetPixelsState(srcPixMap, pixelsState);
  679. }
  680.  
  681. //=====================================================================================
  682. // void CopyFromCurrentPort(GWorldPtr srcGWorld, Rect *srcRect, Rect *dstRect)
  683. //=====================================================================================
  684. // INTERNAL FUNCTION.  Performs a CopyBits operation from the current onscreen port
  685. // to the specified offscreen GWorld, mapping from srcRect to dstRect.  This makes our
  686. // life easier by taking care of foreground/background colors, destination ports, and
  687. // locked offscreen PixMaps.
  688. //=====================================================================================
  689.  
  690. static void CopyFromCurrentPort(GWorldPtr dstGWorld, Rect *srcRect, Rect *dstRect)
  691. {
  692.     PixMapHandle srcPixMap, dstPixMap;
  693.     RgnHandle maskRgn;
  694.     char pixelsState;
  695.     GrafPtr thePort;
  696.     
  697.     if (maskRgn = NewRgn()) {
  698.         GetPort(&thePort);
  699.         SetGWorld(dstGWorld, nil);
  700.         RectRgn(maskRgn, srcRect);
  701.         SectRgn(maskRgn, thePort->visRgn, maskRgn);
  702.         OffsetRgn(maskRgn, dstRect->left - srcRect->left, dstRect->top - srcRect->top);
  703.         srcPixMap = GetGWorldPixMap((CGrafPtr)thePort);
  704.         dstPixMap = GetGWorldPixMap(dstGWorld);
  705.         pixelsState = GetPixelsState(dstPixMap);
  706.         LockPixels(dstPixMap);
  707.         CopyBits((BitMap *)*srcPixMap, (BitMap *)*dstPixMap, srcRect, dstRect, srcCopy, maskRgn);
  708.         SetPixelsState(dstPixMap, pixelsState);
  709.         SetGWorld((CGrafPtr)thePort, nil);
  710.         DisposeRgn(maskRgn);
  711.     }
  712. }
  713.