Retired Document
Important: This sample code may not represent best practices for current development. The project may use deprecated symbols and illustrate technologies and techniques that are no longer recommended.
Source/Grid.m
/* |
File: Grid.m |
Abstract: Abstract superclass of regular geometric grids of GridCells that Bits can be placed on. |
Version: 1.0 |
Disclaimer: IMPORTANT: This Apple software is supplied to you by |
Apple Inc. ("Apple") in consideration of your agreement to the |
following terms, and your use, installation, modification or |
redistribution of this Apple software constitutes acceptance of these |
terms. If you do not agree with these terms, please do not use, |
install, modify or redistribute this Apple software. |
In consideration of your agreement to abide by the following terms, and |
subject to these terms, Apple grants you a personal, non-exclusive |
license, under Apple's copyrights in this original Apple software (the |
"Apple Software"), to use, reproduce, modify and redistribute the Apple |
Software, with or without modifications, in source and/or binary forms; |
provided that if you redistribute the Apple Software in its entirety and |
without modifications, you must retain this notice and the following |
text and disclaimers in all such redistributions of the Apple Software. |
Neither the name, trademarks, service marks or logos of Apple Inc. |
may be used to endorse or promote products derived from the Apple |
Software without specific prior written permission from Apple. Except |
as expressly stated in this notice, no other rights or licenses, express |
or implied, are granted by Apple herein, including but not limited to |
any patent rights that may be infringed by your derivative works or by |
other works in which the Apple Software may be incorporated. |
The Apple Software is provided by Apple on an "AS IS" basis. APPLE |
MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION |
THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS |
FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND |
OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. |
IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL |
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, |
MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED |
AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), |
STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE |
POSSIBILITY OF SUCH DAMAGE. |
Copyright © 2007 Apple Inc. All Rights Reserved. |
*/ |
#import "Grid.h" |
#import "Bit.h" |
#import "Game.h" |
#import "QuartzUtils.h" |
/** WARNING: THIS CODE REQUIRES GARBAGE COLLECTION! |
** This sample application uses Objective-C 2.0 garbage collection. |
** Therefore, the source code in this file does NOT perform manual object memory management. |
** If you reuse any of this code in a process that isn't garbage collected, you will need to |
** add all necessary retain/release/autorelease calls, and implement -dealloc methods, |
** otherwise unpleasant leakage will occur! |
**/ |
@implementation Grid |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns |
spacing: (CGSize)spacing |
position: (CGPoint)pos |
{ |
NSParameterAssert(nRows>0 && nColumns>0); |
self = [super init]; |
if( self ) { |
_nRows = nRows; |
_nColumns = nColumns; |
_spacing = spacing; |
_cellClass = [GridCell class]; |
_lineColor = kBlackColor; |
_allowsMoves = YES; |
_usesDiagonals = YES; |
self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2); |
self.position = pos; |
self.anchorPoint = CGPointMake(0,0); |
self.zPosition = kBoardZ; |
self.needsDisplayOnBoundsChange = YES; |
unsigned n = nRows*nColumns; |
_cells = [NSMutableArray arrayWithCapacity: n]; |
id null = [NSNull null]; |
while( n-- > 0 ) |
[_cells addObject: null]; |
[self setNeedsDisplay]; |
} |
return self; |
} |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns |
frame: (CGRect)frame |
{ |
CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns, |
(frame.size.height-2)/(CGFloat)nRows) ); |
return [self initWithRows: nRows columns: nColumns |
spacing: CGSizeMake(spacing,spacing) |
position: frame.origin]; |
} |
static void setcolor( CGColorRef *var, CGColorRef color ) |
{ |
if( color != *var ) { |
// Garbage collection does not apply to CF objects like CGColors! |
CGColorRelease(*var); |
*var = CGColorRetain(color); |
} |
} |
- (CGColorRef) cellColor {return _cellColor;} |
- (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);} |
- (CGColorRef) lineColor {return _lineColor;} |
- (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);} |
@synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing, |
usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures; |
#pragma mark - |
#pragma mark GEOMETRY: |
- (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col |
{ |
if( row < _nRows && col < _nColumns ) { |
id cell = [_cells objectAtIndex: row*_nColumns+col]; |
if( cell != [NSNull null] ) |
return cell; |
} |
return nil; |
} |
/** Subclasses can override this, to change the cell's class or frame. */ |
- (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col |
suggestedFrame: (CGRect)frame |
{ |
return [[_cellClass alloc] initWithGrid: self |
row: row column: col |
frame: frame]; |
} |
- (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col |
{ |
NSParameterAssert(row<_nRows); |
NSParameterAssert(col<_nColumns); |
unsigned index = row*_nColumns+col; |
GridCell *cell = [_cells objectAtIndex: index]; |
if( (id)cell == [NSNull null] ) { |
CGRect frame = CGRectMake(col*_spacing.width, row*_spacing.height, |
_spacing.width,_spacing.height); |
cell = [self createCellAtRow: row column: col suggestedFrame: frame]; |
if( cell ) { |
[_cells replaceObjectAtIndex: index withObject: cell]; |
[self addSublayer: cell]; |
[self setNeedsDisplay]; |
} |
} |
return cell; |
} |
- (void) addAllCells |
{ |
for( int row=_nRows-1; row>=0; row-- ) // makes 'upper' cells be in 'back' |
for( int col=0; col<_nColumns; col++ ) |
[self addCellAtRow: row column: col]; |
} |
- (void) removeCellAtRow: (unsigned)row column: (unsigned)col |
{ |
NSParameterAssert(row<_nRows); |
NSParameterAssert(col<_nColumns); |
unsigned index = row*_nColumns+col; |
id cell = [_cells objectAtIndex: index]; |
if( cell != [NSNull null] ) |
[cell removeFromSuperlayer]; |
[_cells replaceObjectAtIndex: index withObject: [NSNull null]]; |
[self setNeedsDisplay]; |
} |
#pragma mark - |
#pragma mark DRAWING: |
- (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill |
{ |
// Subroutine of -drawInContext:. Draws all the cells, with or without a fill. |
for( unsigned row=0; row<_nRows; row++ ) |
for( unsigned col=0; col<_nColumns; col++ ) { |
GridCell *cell = [self cellAtRow: row column: col]; |
if( cell ) |
[cell drawInParentContext: ctx fill: fill]; |
} |
} |
- (void)drawInContext:(CGContextRef)ctx |
{ |
// Custom CALayer drawing implementation. Delegates to the cells to draw themselves |
// in me; this is more efficient than having each cell have its own drawing. |
if( _cellColor ) { |
CGContextSetFillColorWithColor(ctx, _cellColor); |
[self drawCellsInContext: ctx fill: YES]; |
} |
if( _lineColor ) { |
CGContextSetStrokeColorWithColor(ctx,_lineColor); |
[self drawCellsInContext:ctx fill: NO]; |
} |
} |
@end |
#pragma mark - |
@implementation GridCell |
- (id) initWithGrid: (Grid*)grid |
row: (unsigned)row column: (unsigned)col |
frame: (CGRect)frame |
{ |
self = [super init]; |
if (self != nil) { |
_grid = grid; |
_row = row; |
_column = col; |
self.position = frame.origin; |
CGRect bounds = frame; |
bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries |
bounds.origin.y -= floor(bounds.origin.y); |
self.bounds = bounds; |
self.anchorPoint = CGPointMake(0,0); |
self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:) |
} |
return self; |
} |
- (NSString*) description |
{ |
return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row]; |
} |
@synthesize grid=_grid, row=_row, column=_column; |
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill |
{ |
// Default implementation just fills or outlines the cell. |
CGRect frame = self.frame; |
if( fill ) |
CGContextFillRect(ctx,frame); |
else |
CGContextStrokeRect(ctx, frame); |
} |
- (void) setBit: (Bit*)bit |
{ |
if( bit != self.bit ) { |
[super setBit: bit]; |
if( bit ) { |
// Center it: |
CGSize size = self.bounds.size; |
bit.position = CGPointMake(floor(size.width/2.0), |
floor(size.height/2.0)); |
} |
} |
} |
- (Bit*) canDragBit: (Bit*)bit |
{ |
if( _grid.allowsMoves && bit==self.bit ) |
return [super canDragBit: bit]; |
else |
return nil; |
} |
- (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point |
{ |
return self.bit == nil || _grid.allowsCaptures; |
} |
- (BOOL) fwdIsN |
{ |
return self.game.currentPlayer.index == 0; |
} |
- (NSArray*) neighbors |
{ |
BOOL orthogonal = ! _grid.usesDiagonals; |
NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8]; |
for( int dy=-1; dy<=1; dy++ ) |
for( int dx=-1; dx<=1; dx++ ) |
if( (dx || dy) && !(orthogonal && dx && dy) ) { |
GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx]; |
if( cell ) |
[neighbors addObject: cell]; |
} |
return neighbors; |
} |
// Recursive subroutine used by getGroup:. |
- (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner |
{ |
Bit *bit = self.bit; |
if( bit == nil ) { |
if( [liberties containsObject: self] ) |
return; // already traversed |
[liberties addObject: self]; |
} else if( bit.owner==owner ) { |
if( [group containsObject: self] ) |
return; // already traversed |
[group addObject: self]; |
for( GridCell *c in self.neighbors ) |
[c x_addToGroup: group liberties: liberties owner: owner]; |
} |
} |
- (NSSet*) getGroup: (int*)outLiberties |
{ |
NSMutableSet *group=[NSMutableSet set], *liberties=nil; |
if( outLiberties ) |
liberties = [NSMutableSet set]; |
[self x_addToGroup: group liberties: liberties owner: self.bit.owner]; |
if( outLiberties ) |
*outLiberties = liberties.count; |
return group; |
} |
#pragma mark - |
#pragma mark DRAG-AND-DROP: |
// An image from another app can be dragged onto a Dispenser to change the Piece's appearance. |
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender |
{ |
NSPasteboard *pb = [sender draggingPasteboard]; |
if( [NSImage canInitWithPasteboard: pb] ) |
return NSDragOperationCopy; |
else |
return NSDragOperationNone; |
} |
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender |
{ |
CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]); |
if( image ) { |
_grid.cellColor = CreatePatternColor(image); |
[_grid setNeedsDisplay]; |
return YES; |
} else |
return NO; |
} |
@end |
#pragma mark - |
@implementation RectGrid |
- (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns |
spacing: (CGSize)spacing |
position: (CGPoint)pos |
{ |
self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos]; |
if( self ) { |
_cellClass = [Square class]; |
} |
return self; |
} |
- (CGColorRef) altCellColor {return _altCellColor;} |
- (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);} |
@end |
#pragma mark - |
@implementation Square |
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill |
{ |
if( fill ) { |
CGColorRef c = ((RectGrid*)_grid).altCellColor; |
if( c ) { |
if( ! ((_row+_column) & 1) ) |
c = _grid.cellColor; |
CGContextSetFillColorWithColor(ctx, c); |
} |
} |
[super drawInParentContext: ctx fill: fill]; |
} |
- (void) setHighlighted: (BOOL)highlighted |
{ |
[super setHighlighted: highlighted]; |
self.borderWidth = (highlighted ?6 :0); |
} |
- (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];} |
- (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];} |
- (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];} |
- (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];} |
- (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];} |
- (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];} |
- (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];} |
- (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];} |
// Directions relative to the current player: |
- (Square*) fl {return self.fwdIsN ?self.nw :self.se;} |
- (Square*) f {return self.fwdIsN ?self.n :self.s;} |
- (Square*) fr {return self.fwdIsN ?self.ne :self.sw;} |
- (Square*) r {return self.fwdIsN ?self.e :self.w;} |
- (Square*) br {return self.fwdIsN ?self.se :self.nw;} |
- (Square*) b {return self.fwdIsN ?self.s :self.n;} |
- (Square*) bl {return self.fwdIsN ?self.sw :self.ne;} |
- (Square*) l {return self.fwdIsN ?self.w :self.e;} |
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender |
{ |
CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard]); |
if( image ) { |
CGColorRef color = CreatePatternColor(image); |
RectGrid *rectGrid = (RectGrid*)_grid; |
if( rectGrid.altCellColor && ((_row+_column) & 1) ) |
rectGrid.altCellColor = color; |
else |
rectGrid.cellColor = color; |
[rectGrid setNeedsDisplay]; |
return YES; |
} else |
return NO; |
} |
@end |
#pragma mark - |
@implementation GoSquare |
@synthesize dotted=_dotted; |
- (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill |
{ |
if( fill ) |
[super drawInParentContext: ctx fill: fill]; |
else { |
CGRect frame = self.frame; |
const CGFloat midx=floor(CGRectGetMidX(frame))+0.5, |
midy=floor(CGRectGetMidY(frame))+0.5; |
CGPoint p[4] = {{CGRectGetMinX(frame),midy}, |
{CGRectGetMaxX(frame),midy}, |
{midx,CGRectGetMinY(frame)}, |
{midx,CGRectGetMaxY(frame)}}; |
if( ! self.s ) p[2].y = midy; |
if( ! self.n ) p[3].y = midy; |
if( ! self.w ) p[0].x = midx; |
if( ! self.e ) p[1].x = midx; |
CGContextStrokeLineSegments(ctx, p, 4); |
if( _dotted ) { |
CGContextSetFillColorWithColor(ctx,_grid.lineColor); |
CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5); |
CGContextFillEllipseInRect(ctx, dot); |
} |
} |
} |
@end |
Copyright © 2007 Apple Inc. All Rights Reserved. Terms of Use | Privacy Policy | Updated: 2007-12-19