home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / ProgLangD.iso / C++-7 / DISK4 / SAMPLES / CPPTUTOR / OOD / LIFEWIN.CP$ / LIFEWIN
Encoding:
Text File  |  1991-12-26  |  20.4 KB  |  705 lines

  1. /********************************************************************
  2.  
  3.  FILE: LIFEWIN.CPP
  4.  
  5. ********************************************************************/
  6.  
  7. #include "lifewin.h"
  8.  
  9. #include <stdlib.h>
  10. #include <string.h>
  11.  
  12. /********************************************************************
  13.  
  14.  LifeWin::LifeWin
  15.  
  16.  This function creates a Life window. It moves the horizontal
  17.  scrollbar, if one exists, and allocates three PushButtons. It
  18.  also allocates two buffers used to represent the Life field.
  19.  
  20. ********************************************************************/
  21.  
  22. LifeWin::LifeWin( Point location, int wid, int len, Buffer *sheet )
  23. : TextWin ( location, wid, len, sheet )
  24. {
  25.     wid = min( wid, canvas.columns() + 2 );  // make sure window isn't
  26.     len = min( len, canvas.rows() + 2 );     //   larger than text buffer, or
  27.     wid = max( wid, LIFEWIN_MINWIDTH );      //   smaller than minimum
  28.     len = max( len, LIFEWIN_MINHEIGHT );
  29.  
  30.     textView = Rect(10, 1, wid - 2, len - 2);
  31.  
  32.     if( hscroller )
  33.     {
  34.         hscroller->move( Point(10, textView.bottom + 1) );
  35.         hscroller->resize( textView.width() + 1, 1 );
  36.     }
  37.  
  38.     buttons[0] = new PushButton( this, Point( 2, height() / 4 )," Clear ");
  39.     buttons[1] = new PushButton( this, Point( 2, height() / 2 )," Random");
  40.     buttons[2] = new PushButton( this, Point( 2, 3 * height() / 4 )," Next  ");
  41.  
  42.     int size = canvas.rows() * canvas.columns();
  43.  
  44.     currField = new char[size];
  45.     neighborField = new char[size];
  46.  
  47.     clearField();
  48.     randomize();
  49. }
  50.  
  51. /********************************************************************
  52.  
  53.  LifeWin::LifeWin - copy constructor
  54.  
  55. ********************************************************************/
  56.  
  57. LifeWin::LifeWin( const LifeWin &other )
  58. : TextWin( (TextWin &)other )
  59. {
  60.     int i, size;
  61.  
  62.     for( i = 0; i < 3; i++ )
  63.         buttons[i] = new PushButton( *other.buttons[i] );
  64.  
  65.     size = canvas.rows() * canvas.columns();
  66.     currField = new char[size];
  67.     strncpy( currField, other.currField, size );
  68.     neighborField = new char[size];
  69.     strncpy( neighborField, other.neighborField, size );
  70. }
  71.  
  72. /********************************************************************
  73.  
  74.  LifeWin::operator=
  75.  
  76. ********************************************************************/
  77.  
  78. LifeWin &LifeWin::operator=( const LifeWin &other )
  79. {
  80.     int i, size;
  81.  
  82.     if( &other == this )
  83.         return *this;
  84.  
  85.     for( i = 0; i < 3; i++ )
  86.     {
  87.         delete buttons[i];
  88.         buttons[i] = new PushButton( *other.buttons[i] );
  89.     }
  90.  
  91.     delete currField;
  92.     delete neighborField;
  93.  
  94.     size = canvas.rows() * canvas.columns();
  95.     currField = new char[size];
  96.     strncpy( currField, other.currField, size );
  97.     neighborField = new char[size];
  98.     strncpy( neighborField, other.neighborField, size );
  99.  
  100.     return *this;
  101. }
  102.  
  103. /********************************************************************
  104.  
  105.  LifeWin::resize
  106.  
  107.  This function resizes the window to the specified dimensions.
  108.  
  109. ********************************************************************/
  110.  
  111. void LifeWin::resize( int newWidth, int newHeight )
  112. {
  113.     if( (newWidth == width())  &&
  114.         (newHeight == height())  )
  115.         return;
  116.  
  117.     newWidth = min( newWidth, canvas.columns() + 11 );  // make sure window isn't
  118.     newHeight = min( newHeight, canvas.rows() + 2 );   //   larger than text buffer, or
  119.     newWidth = max( newWidth, LIFEWIN_MINWIDTH);       //   smaller than minimum
  120.     newHeight = max( newHeight, LIFEWIN_MINHEIGHT);
  121.  
  122.     TextWin::resize( newWidth - 10, newHeight );
  123.  
  124.     Interactor::resize( newWidth, newHeight );
  125.  
  126.     textView = Rect(10, 1, newWidth - 2, newHeight - 2);
  127.  
  128.     if( hscroller )
  129.     {
  130.         hscroller->move( Point(10, textView.bottom + 1) );
  131.         hscroller->resize( textView.width() + 1, 1 );
  132.     }
  133.  
  134.     if( vscroller )
  135.         vscroller->move( Point( textView.right + 1, textView.top ) );
  136.  
  137.     buttons[0]->move( Point( 2, height() / 4 ) );
  138.     buttons[1]->move( Point( 2, height() / 2 ) );
  139.     buttons[2]->move( Point( 2, 3 * height()/ 4 ) );
  140.  
  141. }
  142.  
  143. /********************************************************************
  144.  
  145.  LifeWin::activate
  146.  
  147.  This function activates the buttons and calls TextWin::activate.
  148.  
  149. ********************************************************************/
  150.  
  151. void LifeWin::activate()
  152. {
  153.     TextWin::activate();
  154.  
  155.     buttons[0]->activate();
  156.     buttons[1]->activate();
  157.     buttons[2]->activate();
  158. }
  159.  
  160. /********************************************************************
  161.  
  162.  LifeWin::deactivate
  163.  
  164.  This function deactivates the buttons and calls TextWin::deactivate.
  165.  
  166. ********************************************************************/
  167.  
  168. void LifeWin::deactivate()
  169. {
  170.     TextWin::deactivate();
  171.  
  172.     buttons[0]->deactivate();
  173.     buttons[1]->deactivate();
  174.     buttons[2]->deactivate();
  175. }
  176.  
  177. /********************************************************************
  178.  
  179.  LifeWin::paint
  180.  
  181.  This function repaints the window, including the life field,
  182.  scrollbars, and buttons. The painting region is set to the
  183.  invalid rectangle for the window's own painting. The invalid
  184.  rectangle is passed on the scrollbars' and buttons' paint
  185.  functions, displaced to fit their coordinate systems. Finally
  186.  the painting region is reset to the entire active window, for
  187.  the next time the window becomes active.
  188.  
  189. ********************************************************************/
  190.  
  191. void LifeWin::paint( Rect invalid )
  192. {
  193.     int i, j;
  194.  
  195.     paintingRegion = invalid;
  196.  
  197.     paintFrame();
  198.  
  199.     if( vscroller )
  200.         vscroller->paint( invalid - vscroller->origin() );
  201.     if( hscroller )
  202.         hscroller->paint( invalid - hscroller->origin() );
  203.  
  204.     for( i = 1; i <= 9; i++ )
  205.         for( j = 1; j <= height() - 2; j++ )
  206.             paintChar( Point(i,j), ' ', GREEN_FORE | GREEN_BACK );
  207.  
  208.     for( i = 0; i < 3; i++ )
  209.         buttons[i]->paint( invalid - buttons[i]->origin() );
  210.  
  211.     paintText();
  212.  
  213.     paintingRegion = Rect( 0, 0, width(), height() );
  214. }
  215.  
  216. /********************************************************************
  217.  
  218.  LifeWin::clearField
  219.  
  220.   This function initializes the Life matrixes. It clears currField
  221.   and neighborField, then initialize all the zones (1-9) of currField.
  222.  
  223.   The zones indicate edges and corners, and are used by the Life
  224.   algorithm to determine the method of calculating neighbors. This
  225.   causes the Life field to wrap around in all directions.
  226.  
  227.      +-+--------------+-+
  228.      |6|      2       |7|
  229.      +-+--------------+-+
  230.      | |              | |
  231.      |4|      1       |5|
  232.      | |              | |
  233.      +-+--------------+-+
  234.      |8|      3       |9|
  235.      +-+--------------+-+
  236.  
  237.   Zones are recorded in matrix 1 for ease of computation.
  238.  
  239. ********************************************************************/
  240.  
  241. void LifeWin::clearField()
  242. {
  243.     int x, y, size;
  244.  
  245.     size = canvas.rows() * canvas.columns();
  246.  
  247.     memset( currField, 0, size );
  248.     memset( neighborField, 0, size );
  249.  
  250.  
  251.     char *p = currField;
  252.  
  253.     *p++ = 6;
  254.     for( x = 0; x < (canvas.columns() - 2); x++ )
  255.         *p++ = 2;
  256.     *p++ = 7;
  257.  
  258.     /* Initialize center rows to zones 4, 1, and 5. */
  259.     for( y = 0; y < (canvas.rows() - 2); y++ )
  260.     {
  261.         *p++ = 4;
  262.         for( x = 0; x < (canvas.columns() - 2); x++ )
  263.             *p++ = 1;
  264.         *p++ = 5;
  265.     }
  266.  
  267.     /* Initialize bottom row to zones 8, 3, and 9. */
  268.     *p++ = 8;
  269.     for( x = 0; x < (canvas.columns() - 2); x++ )
  270.         *p++ = 3;
  271.     *p++ = 9;
  272.  
  273.     for( x = 0; x < canvas.rows(); x++ )
  274.         for( y = 0; y < canvas.columns(); y++ )
  275.             canvas.setChar( Point(x,y), ' ');
  276.  
  277. }
  278.  
  279. /********************************************************************
  280.  
  281.  LifeWin::randomize
  282.  
  283.  This function randomly places live cells in currField.
  284.  
  285. ********************************************************************/
  286.  
  287. void LifeWin::randomize()
  288. {
  289.     int i, x, y, size;
  290.  
  291.     clearField();
  292.  
  293.     size = canvas.columns() * canvas.rows();
  294.  
  295.     for( i = 0; i < (size / 10) ; i++ )
  296.     {
  297.         x = rand() % canvas.columns();
  298.         y = rand() % canvas.rows();
  299.         canvas.setChar( Point( x, y ), CELL_CHAR );
  300.         currField[(y * canvas.columns()) + x] += 100;
  301.     }
  302.  
  303. }
  304.  
  305. /********************************************************************
  306.  
  307.  LifeWin::handleEvent
  308.  
  309.  This function determines the type of event, and passes it on to
  310.  the appropriate event handler.
  311.  
  312. ********************************************************************/
  313.  
  314. void LifeWin::handleEvent( const Event &action )
  315. {
  316.     switch ( action.getType() )
  317.     {
  318.     case KBD_EVENT:
  319.     {
  320.         KbdEvent &key = (KbdEvent &)action;
  321.         handleKey( key );
  322.         break;
  323.     }
  324.     case MOUSE_EVENT:
  325.     {
  326.         MouseEvent &mouseAction = (MouseEvent &)action;
  327.         handleMouse( mouseAction );
  328.         break;
  329.     }
  330.     case PUSH_EVENT:
  331.     {
  332.         PushEvent &pushAction = (PushEvent &)action;
  333.         handlePush( pushAction );
  334.         break;
  335.     }
  336.     default:
  337.         TextWin::handleEvent( action );
  338.         break;
  339.     }
  340. }
  341.  
  342. /********************************************************************
  343.  
  344.  LifeWin::handleKey
  345.  
  346.  This function handles keyboard events. It handles the space bar
  347.  (for turning a cell on or off) and the letters C, R and N (for
  348.  "Clear," "Randomize," and "Next," respectively.)
  349.  
  350. ********************************************************************/
  351.  
  352. void LifeWin::handleKey( const KbdEvent &key )
  353. {
  354.     if( key.val() == ' ' )
  355.     {
  356.         if( (canvas.getChar( getCursorPos() )).character == CELL_CHAR )
  357.         {
  358.             currField[(getCursorPos().y * canvas.columns()) + getCursorPos().x] -= 100;
  359.             canvas.setChar( getCursorPos(), ' ');
  360.         }
  361.         else
  362.         {
  363.             currField[(getCursorPos().y * canvas.columns()) + getCursorPos().x] += 100;
  364.             canvas.setChar( getCursorPos(), CELL_CHAR );
  365.         }
  366.         paintText();
  367.     }
  368.     else if( ( key.val() == 'C' ) || ( key.val() == 'c') )
  369.     {
  370.         clearField();
  371.         paint( Rect( 0, 0, width(), height() ) );
  372.     }
  373.     else if( ( key.val() == 'R' ) || ( key.val() == 'r') )
  374.     {
  375.         clearField();
  376.         randomize();
  377.         paint( Rect( 0, 0, width(), height() ) );
  378.     }
  379.     else if( ( key.val() == 'N' ) || ( key.val() == 'n') )
  380.     {
  381.         nextGeneration();
  382.         paint( Rect( 0, 0, width(), height() ) );
  383.     }
  384.  
  385.     else
  386.         TextWin::handleEvent( key );
  387. }
  388.  
  389.  
  390. /********************************************************************
  391.  
  392.  LifeWin::handleMouse
  393.  
  394.  This function handles mouse events. Mouse events that fall on
  395.  one of the buttons are passed to that object. The location of
  396.  the mouse event is translated to the scrollbar's coordinate system
  397.  when it is passed. Mouse events that fall on the Life field cause
  398.  a cell to turn on or off.
  399.  
  400. ********************************************************************/
  401.  
  402. void LifeWin::handleMouse( const MouseEvent &action )
  403. {
  404.     int i;
  405.  
  406.     for( i = 0; i < 3; i++ )
  407.     {
  408.         if( buttons[i]->withinBounds( action.getPosition() ) )
  409.         {
  410.             MouseEvent localAction( action.getPosition() - buttons[i]->origin(),
  411.                                     action.getButton() );
  412.             buttons[i]->handleEvent( localAction );
  413.             updateCursor();
  414.         }
  415.     }
  416.  
  417.     if( textView.Contains( action.getPosition() ) )
  418.     {
  419.         setCursorPos( action.getPosition() -
  420.                       Point(textView.left, textView.top) +
  421.                       getCanvasOffset() );
  422.         updateCursor();
  423.  
  424.         if( ( canvas.getChar( getCursorPos() ) ).character == CELL_CHAR )
  425.         {
  426.             currField[(getCursorPos().y * canvas.columns()) + getCursorPos().x] -= 100;
  427.             canvas.setChar( getCursorPos(), ' ');
  428.         }
  429.         else
  430.         {
  431.             currField[(getCursorPos().y * canvas.columns()) + getCursorPos().x] += 100;
  432.             canvas.setChar( getCursorPos(), CELL_CHAR );
  433.         }
  434.         paintText();
  435.     }
  436.     else
  437.         TextWin::handleEvent( action );
  438. }
  439.  
  440. /********************************************************************
  441.  
  442.  LifeWin::handlePush
  443.  
  444.  This function handles button push events. The source of the push
  445.  event is checked against the window's buttons, and the Life field
  446.  is cleared, randomized, or recalculated accordingly.
  447.  
  448. ********************************************************************/
  449.  
  450. void LifeWin::handlePush( const PushEvent &action )
  451. {
  452.     PushButton *source = action.getSource();
  453.  
  454.     if( source == buttons[0] )  // clear
  455.     {
  456.         clearField();
  457.         paint( Rect( 0, 0, width(), height() ) );
  458.     }
  459.     else if( source == buttons[1] ) // new random
  460.     {
  461.         clearField();
  462.         randomize();
  463.         paint( Rect( 0, 0, width(), height() ) );
  464.     }
  465.     else  // source == buttons[2]
  466.     {
  467.         nextGeneration();
  468.         paint( Rect( 0, 0, width(), height() ) );
  469.     }
  470. }
  471.  
  472. /********************************************************************
  473.  
  474.  LifeWin::nextGeneration
  475.  
  476.  This function calculates the next generation of cells. First
  477.  neighborField is cleared, and then currField is scanned. Whenever
  478.  a living cell is found in currField, the corresponding neighbor
  479.  cells in neighborField are incremented by 1, and the corresponding
  480.  cell itself is incremented by 100. At the end of this stage,
  481.  neighborField contains a count of each cell's neighbors.
  482.  
  483.  The zone number stored in each cell of currField is used to
  484.  determine which cells are treated as neighbors. The zones at the
  485.  edges and corners have neighbors on the other side of the field,
  486.  causing the field to wrap around in all direction.
  487.  
  488.  The function updateField() is called to determine what actually
  489.  lives or dies, based on the neighbor-count of each cell.
  490.  
  491. ********************************************************************/
  492.  
  493. #define NW  (-1-canvas.columns())        // Directional constants, within
  494. #define N   (-canvas.columns())          //  matrix 2. For example, NW refers
  495. #define NE  (1-canvas.columns())         //  to the upper, left-hand neighbor
  496. #define E   (1)
  497. #define SE  (1+canvas.columns())
  498. #define S   (canvas.columns())
  499. #define SW  (-1+canvas.columns())
  500. #define W   (-1)
  501.  
  502. void LifeWin::nextGeneration()
  503. {
  504.     int x, y;
  505.  
  506.     int size,
  507.         zone;
  508.  
  509.     char *p1,
  510.          *p2;
  511.  
  512.     size = canvas.rows() * canvas.columns();
  513.  
  514.     memset(neighborField, 0, size);
  515.  
  516.     // For each cell . . .
  517.  
  518.     p1 = (char *) currField;
  519.  
  520.     for( y = 0; y < canvas.rows(); y++ )
  521.     {
  522.         for( x = 0; x < canvas.columns(); x++, p1++ )
  523.         {
  524.             // If matrix 1 cell is alive . . .
  525.  
  526.             if( *p1 > 100 )
  527.             {
  528.                 // Point to matrix 2 cell and update it.
  529.                 p2 = (char *)neighborField + (y * canvas.columns()) + x;
  530.                 *p2 += 100;
  531.     
  532.                 // Get the zone and update the neighbors accordingly.
  533.                 zone = (*p1 - 100);
  534.                 switch( zone )
  535.                 {
  536.                 case 1:
  537.                     ++*(p2 + NW);
  538.                     ++*(p2 + N);
  539.                     ++*(p2 + NE);
  540.                     ++*(p2 + E);
  541.                     ++*(p2 + SE);
  542.                     ++*(p2 + S);
  543.                     ++*(p2 + SW);
  544.                     ++*(p2 + W);
  545.                     break;
  546.                 case 2:
  547.                     ++*(p2 + NW + size);
  548.                     ++*(p2 + N + size);
  549.                     ++*(p2 + NE + size);
  550.                     ++*(p2 + E);
  551.                     ++*(p2 + SE);
  552.                     ++*(p2 + S);
  553.                     ++*(p2 + SW);
  554.                     ++*(p2 + W);
  555.                     break;
  556.                 case 3:
  557.                     ++*(p2 + NW);
  558.                     ++*(p2 + N);
  559.                     ++*(p2 + NE);
  560.                     ++*(p2 + E);
  561.                     ++*(p2 + SE - size);
  562.                     ++*(p2 + S - size);
  563.                     ++*(p2 + SW - size);
  564.                     ++*(p2 + W);
  565.                     break;
  566.                 case 4:
  567.                     ++*(p2 + NW + canvas.columns());
  568.                     ++*(p2 + N);
  569.                     ++*(p2 + NE);
  570.                     ++*(p2 + E);
  571.                     ++*(p2 + SE);
  572.                     ++*(p2 + S);
  573.                     ++*(p2 + SW + canvas.columns());
  574.                     ++*(p2 + W + canvas.columns() );
  575.                     break;
  576.                 case 5:
  577.                     ++*(p2 + NW);
  578.                     ++*(p2 + N);
  579.                     ++*(p2 + NE - canvas.columns());
  580.                     ++*(p2 + E - canvas.columns());
  581.                     ++*(p2 + SE - canvas.columns());
  582.                     ++*(p2 + S);
  583.                     ++*(p2 + SW);
  584.                     ++*(p2 + W);
  585.                     break;
  586.                 case 6:
  587.                     ++*(p2 + NW + size + canvas.columns());
  588.                     ++*(p2 + N + size);
  589.                     ++*(p2 + NE + size);
  590.                     ++*(p2 + E);
  591.                     ++*(p2 + SE);
  592.                     ++*(p2 + S);
  593.                     ++*(p2 + SW + canvas.columns());
  594.                     ++*(p2 + W + canvas.columns());
  595.                     break;
  596.                 case 7:
  597.                     ++*(p2 + NW + size);
  598.                     ++*(p2 + N + size);
  599.                     ++*(p2 + NE + size - canvas.columns());
  600.                     ++*(p2 + E - canvas.columns());
  601.                     ++*(p2 + SE - canvas.columns());
  602.                     ++*(p2 + S);
  603.                     ++*(p2 + SW);
  604.                     ++*(p2 + W);
  605.                     break;
  606.                 case 8:
  607.                     ++*(p2 + NW + canvas.columns());
  608.                     ++*(p2 + N);
  609.                     ++*(p2 + NE);
  610.                     ++*(p2 + E);
  611.                     ++*(p2 + SE - size);
  612.                     ++*(p2 + S - size);
  613.                     ++*(p2 + SW + canvas.columns() - size);
  614.                     ++*(p2 + W + canvas.columns());
  615.                     break;
  616.                 case 9:
  617.                     ++*(p2 + NW);
  618.                     ++*(p2 + N);
  619.                     ++*(p2 + NE - canvas.columns());
  620.                     ++*(p2 + E - canvas.columns());
  621.                     ++*(p2 + SE - size - canvas.columns());
  622.                     ++*(p2 + S - size);
  623.                     ++*(p2 + SW - size);
  624.                     ++*(p2 + W);
  625.                     break;
  626.                 default:
  627.                     break;
  628.                 }
  629.             } // End if
  630.         } // End for: rows
  631.     } // End for: columns
  632.  
  633.     updateField();
  634. }
  635.  
  636. /********************************************************************
  637.  
  638.  LifeWin::updateField
  639.  
  640.  This function scans neighborField and updates currField according
  641.  to the following rules:
  642.  
  643.   neighborField value      currField result
  644.   -------------------      ----------------
  645.           3                new cell is born (right conditions for birth)
  646.           102, 103         no change (right number of neighbors)
  647.           other > 100      cell dies (overcrowding or isolation)
  648.           other < 100      no change (wrong conditions for birth)
  649.  
  650. ********************************************************************/
  651.  
  652. void LifeWin::updateField()
  653. {
  654.    int x, y;
  655.    char *p2 = neighborField;
  656.  
  657.     // Outer loop counts rows.
  658.  
  659.     for( y = 0; y < canvas.rows(); y++ )
  660.     {
  661.         // Next loop counts columns.
  662.         for( x = 0; x < canvas.columns(); x++, p2++ )
  663.         {
  664.             if( *p2 < 100 )
  665.             {
  666.                 if( *p2 == 3 )   // Write live cell if 3.
  667.                 {
  668.                     currField[ (y * canvas.columns()) + x] += 100;
  669.                     canvas.setChar( Point(x,y), CELL_CHAR );
  670.                 }
  671.             }
  672.             else   // Dead cell if above 100, but not 102 or 103.
  673.             {
  674.                 if( (*p2 < 102) || (*p2 > 103) )
  675.                 {
  676.                     currField[ (y * canvas.columns()) + x] -= 100;
  677.                     canvas.setChar( Point(x,y), ' ');
  678.                 }
  679.             }
  680.         } // End for: columns
  681.     } // End for: rows
  682.  
  683. }
  684.  
  685.  
  686.  
  687. /********************************************************************
  688.  
  689.  LifeWin::~LifeWin
  690.  
  691.  This function deallocates the buttons and the buffers used for the
  692.  Life field.
  693.  
  694. ********************************************************************/
  695.  
  696. LifeWin::~LifeWin()
  697. {
  698.     int i;
  699.  
  700.     for( i = 0; i < 3; i++ )
  701.         delete buttons[i];
  702.     delete currField;
  703.     delete neighborField;
  704. }
  705.