Source/HexGrid.m
author Jens Alfke <jens@mooseyard.com>
Sun Feb 06 16:31:03 2011 -0800 (2011-02-06)
changeset 29 0b1c315ffc64
parent 16 28392c9a969f
permissions -rw-r--r--
Minor compiler-compatibility fixes.
     1 /*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
     2     http://developer.apple.com/samplecode/GeekGameBoard/
     3     Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
     4 
     5     Redistribution and use in source and binary forms, with or without modification, are permitted
     6     provided that the following conditions are met:
     7 
     8     * Redistributions of source code must retain the above copyright notice, this list of conditions
     9       and the following disclaimer.
    10     * Redistributions in binary form must reproduce the above copyright notice, this list of
    11       conditions and the following disclaimer in the documentation and/or other materials provided
    12       with the distribution.
    13 
    14     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    15     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
    16     FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
    17     BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    18     (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    19     PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
    20     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
    21     THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    22 */
    23 #import "HexGrid.h"
    24 #import "Game.h"
    25 
    26 
    27 @implementation HexGrid
    28 
    29 
    30 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
    31             spacing: (CGSize)spacing
    32            position: (CGPoint)pos
    33 {
    34     // Ignore given spacing.height; set it to make the hexagons regular.
    35     CGFloat capHeight = spacing.height / 2 * tan(M_PI/6);
    36     CGFloat side = spacing.height / 2 / cos(M_PI/6);
    37     spacing.height = side + capHeight;
    38     
    39     self = [super initWithRows: nRows columns: nColumns
    40                        spacing: spacing
    41                       position: pos];
    42     if( self ) {
    43         _capHeight = capHeight;
    44         _side = side;
    45         self.bounds = CGRectMake(-1, -1, 
    46                                  (nColumns+0.5)*spacing.width + 2,
    47                                  nRows*spacing.height+capHeight + 2);
    48         _cellClass = [Hex class];
    49     }
    50     return self;
    51 }
    52 
    53 
    54 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
    55               frame: (CGRect)frame;
    56 {
    57     // Compute the horizontal spacing:
    58     CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
    59                          (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
    60     self = [self initWithRows: nRows columns: nColumns
    61                       spacing: CGSizeMake(s,s)
    62                      position: frame.origin];
    63     if( self ) {
    64         // Center in frame:
    65         CGRect curFrame = self.frame;
    66         curFrame.origin.x = round( curFrame.origin.x + (frame.size.width - curFrame.size.width )/2.0f );
    67         curFrame.origin.y = round( curFrame.origin.y + (frame.size.height- curFrame.size.height)/2.0f );
    68         self.frame = curFrame;
    69     }
    70     return self;
    71 }
    72     
    73     
    74 - (void) dealloc
    75 {
    76     CGPathRelease(_cellPath);
    77     [super dealloc];
    78 }
    79 
    80 
    81 - (void) addCellsInHexagon
    82 {
    83     int size = _nRows - !(_nRows & 1);      // make it odd
    84     for( int row=0; row<_nRows; row++ ) {
    85         int n;                              // # of hexes remaining in this row
    86         if( row < size )
    87             n = size - abs(row-size/2);
    88         else
    89             n = 0;
    90         int c0 = floor(((int)_nRows+1-n -(row&1))/2.0);       // col of 1st remaining hex
    91 
    92         for( int col=0; col<_nColumns; col++ )
    93             if( col>=c0 && col<c0+n )
    94                 [self addCellAtRow: row column: col];
    95     }
    96 }
    97 
    98 
    99 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col 
   100                suggestedFrame: (CGRect)frame
   101 {
   102     // Overridden to stagger the odd-numbered rows
   103     BOOL stagger = (row & 1) != 0;
   104     if( _reversed && (_nRows & 1) )
   105         stagger = !stagger;
   106     if( stagger )
   107         frame.origin.x += _spacing.width/2;
   108     frame.size.height += _capHeight;
   109     return [super createCellAtRow: row column: col suggestedFrame: frame];
   110 }
   111 
   112 
   113 // Returns a hexagonal CG path defining a cell's outline. Used by cells when drawing & hit-testing.
   114 - (CGPathRef) cellPath
   115 {
   116     if( ! _cellPath ) {
   117         CGFloat x1 = _spacing.width/2;
   118         CGFloat x2 = _spacing.width;
   119         CGFloat y1 = _capHeight;
   120         CGFloat y2 = y1 + _side;
   121         CGFloat y3 = y2 + _capHeight;
   122         CGPoint p[6] = { {0,y1}, {x1,0}, {x2,y1}, {x2,y2}, {x1,y3}, {0,y2} };
   123         
   124         CGMutablePathRef path = CGPathCreateMutable();
   125         CGPathAddLines(path, NULL, p, 6);
   126         CGPathCloseSubpath(path);
   127         _cellPath = path;
   128     }
   129     return _cellPath;
   130 }
   131 
   132 
   133 @end
   134 
   135 
   136 
   137 
   138 
   139 #pragma mark -
   140 
   141 @implementation Hex
   142 
   143 
   144 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
   145 {
   146     CGContextSaveGState(ctx);
   147     CGPoint pos = self.position;
   148     CGContextTranslateCTM(ctx, pos.x, pos.y);
   149     CGContextBeginPath(ctx);
   150     CGContextAddPath(ctx, [(HexGrid*)_grid cellPath]);
   151     CGContextDrawPath(ctx, (fill ?kCGPathFill :kCGPathStroke));
   152     
   153     if( !fill && self.highlighted ) {
   154         // Highlight by drawing my outline in the highlight color:
   155         CGContextSetStrokeColorWithColor(ctx, self.borderColor);
   156         CGContextSetLineWidth(ctx,6);
   157         CGContextBeginPath(ctx);
   158         CGContextAddPath(ctx, [(HexGrid*)_grid cellPath]);
   159         CGContextDrawPath(ctx, kCGPathStroke);
   160     }
   161     CGContextRestoreGState(ctx);
   162 }
   163 
   164 
   165 - (BOOL)containsPoint:(CGPoint)p
   166 {
   167     return [super containsPoint: p]
   168         && CGPathContainsPoint( [(HexGrid*)_grid cellPath], NULL, p, NO );
   169 }
   170 
   171 
   172 - (void) setHighlighted: (BOOL)highlighted
   173 {
   174     if( highlighted != self.highlighted ) {
   175         [super setHighlighted: highlighted];
   176         [_grid setNeedsDisplay];        // So I'll be asked to redraw myself
   177     }
   178 }
   179 
   180 
   181 #pragma mark -
   182 #pragma mark NEIGHBORS:
   183 
   184 
   185 - (NSArray*) neighbors
   186 {
   187     NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 6];
   188     Hex* n[6] = {self.nw, self.ne, self.w, self.e, self.sw, self.se};
   189     for( int i=0; i<6; i++ )
   190         if( n[i] )
   191             [neighbors addObject: n[i]];
   192     return neighbors;
   193 }
   194 
   195 - (Hex*) nw     {return (Hex*)[_grid cellAtRow: _row+1 column: _column - ((_row+1)&1)];}
   196 - (Hex*) ne     {return (Hex*)[_grid cellAtRow: _row+1 column: _column + (_row&1)];}
   197 - (Hex*) e      {return (Hex*)[_grid cellAtRow: _row   column: _column + 1];}
   198 - (Hex*) se     {return (Hex*)[_grid cellAtRow: _row-1 column: _column + (_row&1)];}
   199 - (Hex*) sw     {return (Hex*)[_grid cellAtRow: _row-1 column: _column - ((_row-1)&1)];}
   200 - (Hex*) w      {return (Hex*)[_grid cellAtRow: _row   column: _column - 1];}
   201 
   202 // Directions relative to the current player:
   203 - (Hex*) fl     {return self.fwdIsN ?self.nw :self.se;}
   204 - (Hex*) fr     {return self.fwdIsN ?self.ne :self.sw;}
   205 - (Hex*) r      {return self.fwdIsN ?self.e  :self.w;}
   206 - (Hex*) br     {return self.fwdIsN ?self.se :self.nw;}
   207 - (Hex*) bl     {return self.fwdIsN ?self.sw :self.ne;}
   208 - (Hex*) l      {return self.fwdIsN ?self.w  :self.e;}
   209 
   210 
   211 @end