Source/HexGrid.m
changeset 1 3eb7be1dd7b6
child 10 6c78cc6bd7a6
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/Source/HexGrid.m	Mon Mar 10 17:30:57 2008 -0700
     1.3 @@ -0,0 +1,200 @@
     1.4 +/*  This code is based on Apple's "GeekGameBoard" sample code, version 1.0.
     1.5 +    http://developer.apple.com/samplecode/GeekGameBoard/
     1.6 +    Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved.
     1.7 +
     1.8 +    Redistribution and use in source and binary forms, with or without modification, are permitted
     1.9 +    provided that the following conditions are met:
    1.10 +
    1.11 +    * Redistributions of source code must retain the above copyright notice, this list of conditions
    1.12 +      and the following disclaimer.
    1.13 +    * Redistributions in binary form must reproduce the above copyright notice, this list of
    1.14 +      conditions and the following disclaimer in the documentation and/or other materials provided
    1.15 +      with the distribution.
    1.16 +
    1.17 +    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
    1.18 +    IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
    1.19 +    FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
    1.20 +    BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    1.21 +    (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
    1.22 +    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
    1.23 +    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
    1.24 +    THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.25 +*/
    1.26 +#import "HexGrid.h"
    1.27 +#import "Game.h"
    1.28 +
    1.29 +
    1.30 +@implementation HexGrid
    1.31 +
    1.32 +
    1.33 +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
    1.34 +            spacing: (CGSize)spacing
    1.35 +           position: (CGPoint)pos
    1.36 +{
    1.37 +    // Ignore given spacing.height; set it to make the hexagons regular.
    1.38 +    CGFloat capHeight = spacing.height / 2 * tan(M_PI/6);
    1.39 +    CGFloat side = spacing.height / 2 / cos(M_PI/6);
    1.40 +    spacing.height = side + capHeight;
    1.41 +    
    1.42 +    self = [super initWithRows: nRows columns: nColumns
    1.43 +                       spacing: spacing
    1.44 +                      position: pos];
    1.45 +    if( self ) {
    1.46 +        _capHeight = capHeight;
    1.47 +        _side = side;
    1.48 +        self.bounds = CGRectMake(-1, -1, 
    1.49 +                                 (nColumns+0.5)*spacing.width + 2,
    1.50 +                                 nRows*spacing.height+capHeight + 2);
    1.51 +        _cellClass = [Hex class];
    1.52 +    }
    1.53 +    return self;
    1.54 +}
    1.55 +
    1.56 +
    1.57 +- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
    1.58 +              frame: (CGRect)frame;
    1.59 +{
    1.60 +    // Compute the horizontal spacing:
    1.61 +    CGFloat s = floor(MIN( (frame.size.width -2.0)/nColumns,
    1.62 +                         (frame.size.height-2.0)/(nRows+0.5*tan(M_PI/6)) / (0.5*(tan(M_PI/6)+1/cos(M_PI/6))) ));
    1.63 +    return [self initWithRows: nRows columns: nColumns
    1.64 +                      spacing: CGSizeMake(s,s)
    1.65 +                     position: frame.origin];
    1.66 +}
    1.67 +    
    1.68 +    
    1.69 +- (void) dealloc
    1.70 +{
    1.71 +    CGPathRelease(_cellPath);
    1.72 +    [super dealloc];
    1.73 +}
    1.74 +
    1.75 +
    1.76 +- (void) addCellsInHexagon
    1.77 +{
    1.78 +    int size = _nRows - !(_nRows & 1);      // make it odd
    1.79 +    for( int row=0; row<_nRows; row++ ) {
    1.80 +        int n;                              // # of hexes remaining in this row
    1.81 +        if( row < size )
    1.82 +            n = size - abs(row-size/2);
    1.83 +        else
    1.84 +            n = 0;
    1.85 +        int c0 = floor(((int)_nRows+1-n -(row&1))/2.0);       // col of 1st remaining hex
    1.86 +
    1.87 +        for( int col=0; col<_nColumns; col++ )
    1.88 +            if( col>=c0 && col<c0+n )
    1.89 +                [self addCellAtRow: row column: col];
    1.90 +    }
    1.91 +}
    1.92 +
    1.93 +
    1.94 +- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col 
    1.95 +               suggestedFrame: (CGRect)frame
    1.96 +{
    1.97 +    // Overridden to stagger the odd-numbered rows
    1.98 +    if( row & 1 )
    1.99 +        frame.origin.x += _spacing.width/2;
   1.100 +    frame.size.height += _capHeight;
   1.101 +    return [super createCellAtRow: row column: col suggestedFrame: frame];
   1.102 +}
   1.103 +
   1.104 +
   1.105 +// Returns a hexagonal CG path defining a cell's outline. Used by cells when drawing & hit-testing.
   1.106 +- (CGPathRef) cellPath
   1.107 +{
   1.108 +    if( ! _cellPath ) {
   1.109 +        CGFloat x1 = _spacing.width/2;
   1.110 +        CGFloat x2 = _spacing.width;
   1.111 +        CGFloat y1 = _capHeight;
   1.112 +        CGFloat y2 = y1 + _side;
   1.113 +        CGFloat y3 = y2 + _capHeight;
   1.114 +        CGPoint p[6] = { {0,y1}, {x1,0}, {x2,y1}, {x2,y2}, {x1,y3}, {0,y2} };
   1.115 +        
   1.116 +        CGMutablePathRef path = CGPathCreateMutable();
   1.117 +        CGPathAddLines(path, NULL, p, 6);
   1.118 +        CGPathCloseSubpath(path);
   1.119 +        _cellPath = path;
   1.120 +    }
   1.121 +    return _cellPath;
   1.122 +}
   1.123 +
   1.124 +
   1.125 +@end
   1.126 +
   1.127 +
   1.128 +
   1.129 +
   1.130 +
   1.131 +#pragma mark -
   1.132 +
   1.133 +@implementation Hex
   1.134 +
   1.135 +
   1.136 +- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
   1.137 +{
   1.138 +    CGContextSaveGState(ctx);
   1.139 +    CGPoint pos = self.position;
   1.140 +    CGContextTranslateCTM(ctx, pos.x, pos.y);
   1.141 +    CGContextBeginPath(ctx);
   1.142 +    CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
   1.143 +    CGContextDrawPath(ctx, (fill ?kCGPathFill :kCGPathStroke));
   1.144 +    
   1.145 +    if( !fill && self.highlighted ) {
   1.146 +        // Highlight by drawing my outline in the highlight color:
   1.147 +        CGContextSetStrokeColorWithColor(ctx, self.borderColor);
   1.148 +        CGContextSetLineWidth(ctx,6);
   1.149 +        CGContextBeginPath(ctx);
   1.150 +        CGContextAddPath(ctx, ((HexGrid*)_grid).cellPath);
   1.151 +        CGContextDrawPath(ctx, kCGPathStroke);
   1.152 +    }
   1.153 +    CGContextRestoreGState(ctx);
   1.154 +}
   1.155 +
   1.156 +
   1.157 +- (BOOL)containsPoint:(CGPoint)p
   1.158 +{
   1.159 +    return [super containsPoint: p]
   1.160 +        && CGPathContainsPoint( ((HexGrid*)_grid).cellPath, NULL, p, NO );
   1.161 +}
   1.162 +
   1.163 +
   1.164 +- (void) setHighlighted: (BOOL)highlighted
   1.165 +{
   1.166 +    if( highlighted != self.highlighted ) {
   1.167 +        [super setHighlighted: highlighted];
   1.168 +        [_grid setNeedsDisplay];        // So I'll be asked to redraw myself
   1.169 +    }
   1.170 +}
   1.171 +
   1.172 +
   1.173 +#pragma mark -
   1.174 +#pragma mark NEIGHBORS:
   1.175 +
   1.176 +
   1.177 +- (NSArray*) neighbors
   1.178 +{
   1.179 +    NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 6];
   1.180 +    Hex* n[6] = {self.nw, self.ne, self.w, self.e, self.sw, self.se};
   1.181 +    for( int i=0; i<6; i++ )
   1.182 +        if( n[i] )
   1.183 +            [neighbors addObject: n[i]];
   1.184 +    return neighbors;
   1.185 +}
   1.186 +
   1.187 +- (Hex*) nw     {return (Hex*)[_grid cellAtRow: _row+1 column: _column - ((_row+1)&1)];}
   1.188 +- (Hex*) ne     {return (Hex*)[_grid cellAtRow: _row+1 column: _column + (_row&1)];}
   1.189 +- (Hex*) e      {return (Hex*)[_grid cellAtRow: _row   column: _column + 1];}
   1.190 +- (Hex*) se     {return (Hex*)[_grid cellAtRow: _row-1 column: _column + (_row&1)];}
   1.191 +- (Hex*) sw     {return (Hex*)[_grid cellAtRow: _row-1 column: _column - ((_row-1)&1)];}
   1.192 +- (Hex*) w      {return (Hex*)[_grid cellAtRow: _row   column: _column - 1];}
   1.193 +
   1.194 +// Directions relative to the current player:
   1.195 +- (Hex*) fl     {return self.fwdIsN ?self.nw :self.se;}
   1.196 +- (Hex*) fr     {return self.fwdIsN ?self.ne :self.sw;}
   1.197 +- (Hex*) r      {return self.fwdIsN ?self.e  :self.w;}
   1.198 +- (Hex*) br     {return self.fwdIsN ?self.se :self.nw;}
   1.199 +- (Hex*) bl     {return self.fwdIsN ?self.sw :self.ne;}
   1.200 +- (Hex*) l      {return self.fwdIsN ?self.w  :self.e;}
   1.201 +
   1.202 +
   1.203 +@end