jens@0: /* This code is based on Apple's "GeekGameBoard" sample code, version 1.0. jens@0: http://developer.apple.com/samplecode/GeekGameBoard/ jens@0: Copyright © 2007 Apple Inc. Copyright © 2008 Jens Alfke. All Rights Reserved. jens@0: jens@0: Redistribution and use in source and binary forms, with or without modification, are permitted jens@0: provided that the following conditions are met: jens@0: jens@0: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@0: and the following disclaimer. jens@0: * Redistributions in binary form must reproduce the above copyright notice, this list of jens@0: conditions and the following disclaimer in the documentation and/or other materials provided jens@0: with the distribution. jens@0: jens@0: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@0: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@0: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@0: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@0: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@0: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@0: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@0: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@0: */ jens@0: #import "Grid.h" jens@0: #import "Bit.h" jens@12: #import "Piece.h" jens@12: #import "Game+Protected.h" jens@10: #import "Player.h" jens@0: #import "QuartzUtils.h" jens@0: jens@0: jens@22: @interface GridCell () jens@22: - (void) setBitTransform: (CATransform3D)bitTransform; jens@22: @end jens@22: jens@22: jens@0: @implementation Grid jens@0: jens@0: jens@0: - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns jens@0: spacing: (CGSize)spacing jens@0: position: (CGPoint)pos jens@0: { jens@0: NSParameterAssert(nRows>0 && nColumns>0); jens@0: self = [super init]; jens@0: if( self ) { jens@0: _nRows = nRows; jens@0: _nColumns = nColumns; jens@0: _spacing = spacing; jens@0: _cellClass = [GridCell class]; jens@1: self.lineColor = kBlackColor; jens@0: _allowsMoves = YES; jens@0: _usesDiagonals = YES; jens@22: _bitTransform = CATransform3DIdentity; jens@0: jens@0: self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2); jens@0: self.position = pos; jens@0: self.anchorPoint = CGPointMake(0,0); jens@0: self.zPosition = kBoardZ; jens@0: self.needsDisplayOnBoundsChange = YES; jens@0: jens@0: unsigned n = nRows*nColumns; jens@0: _cells = [[NSMutableArray alloc] initWithCapacity: n]; jens@0: id null = [NSNull null]; jens@0: while( n-- > 0 ) jens@0: [_cells addObject: null]; jens@0: jens@0: [self setNeedsDisplay]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: jens@0: - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns jens@0: frame: (CGRect)frame jens@0: { jens@0: CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns, jens@0: (frame.size.height-2)/(CGFloat)nRows) ); jens@3: CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2); jens@3: CGPoint position = frame.origin; jens@3: position.x += round( (frame.size.width -size.width )/2.0 ); jens@3: position.y += round( (frame.size.height-size.height)/2.0 ); jens@3: jens@0: return [self initWithRows: nRows columns: nColumns jens@0: spacing: CGSizeMake(spacing,spacing) jens@3: position: position]; jens@0: } jens@0: jens@0: jens@0: - (void) dealloc jens@0: { jens@0: CGColorRelease(_cellColor); jens@0: CGColorRelease(_lineColor); jens@0: [_cells release]; jens@0: [super dealloc]; jens@0: } jens@0: jens@0: jens@0: static void setcolor( CGColorRef *var, CGColorRef color ) jens@0: { jens@0: if( color != *var ) { jens@0: CGColorRelease(*var); jens@0: *var = CGColorRetain(color); jens@0: } jens@0: } jens@0: jens@0: - (CGColorRef) cellColor {return _cellColor;} jens@0: - (void) setCellColor: (CGColorRef)cellColor {setcolor(&_cellColor,cellColor);} jens@0: jens@0: - (CGColorRef) lineColor {return _lineColor;} jens@0: - (void) setLineColor: (CGColorRef)lineColor {setcolor(&_lineColor,lineColor);} jens@0: jens@11: - (CGImageRef) backgroundImage {return _backgroundImage;} jens@11: - (void) setBackgroundImage: (CGImageRef)image jens@11: { jens@11: if( image != _backgroundImage ) { jens@11: CGImageRelease(_backgroundImage); jens@11: _backgroundImage = CGImageRetain(image); jens@11: } jens@11: } jens@11: jens@15: @synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing, reversed=_reversed, jens@22: usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures, jens@22: bitTransform=_bitTransform; jens@0: jens@0: jens@0: #pragma mark - jens@0: #pragma mark GEOMETRY: jens@0: jens@0: jens@0: - (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col jens@0: { jens@0: if( row < _nRows && col < _nColumns ) { jens@0: id cell = [_cells objectAtIndex: row*_nColumns+col]; jens@0: if( cell != [NSNull null] ) jens@0: return cell; jens@0: } jens@0: return nil; jens@0: } jens@0: jens@0: jens@0: /** Subclasses can override this, to change the cell's class or frame. */ jens@0: - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col jens@0: suggestedFrame: (CGRect)frame jens@0: { jens@7: GridCell *cell = [[_cellClass alloc] initWithGrid: self jens@15: row: row column: col jens@7: frame: frame]; jens@10: cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)]; jens@7: return [cell autorelease]; jens@0: } jens@0: jens@0: jens@0: - (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col jens@0: { jens@0: NSParameterAssert(row<_nRows); jens@0: NSParameterAssert(col<_nColumns); jens@0: unsigned index = row*_nColumns+col; jens@0: GridCell *cell = [_cells objectAtIndex: index]; jens@0: if( (id)cell == [NSNull null] ) { jens@15: unsigned effectiveRow=row, effectiveCol=col; jens@15: if( _reversed ) { jens@15: effectiveRow = _nRows-1 - effectiveRow; jens@15: effectiveCol = _nColumns-1 - effectiveCol; jens@15: } jens@15: CGRect frame = CGRectMake(effectiveCol*_spacing.width, effectiveRow*_spacing.height, jens@0: _spacing.width,_spacing.height); jens@0: cell = [self createCellAtRow: row column: col suggestedFrame: frame]; jens@0: if( cell ) { jens@0: [_cells replaceObjectAtIndex: index withObject: cell]; jens@24: if( _reversed ) jens@24: [self addSublayer: cell]; jens@24: else jens@24: [self insertSublayer: cell atIndex: 0]; jens@0: [self setNeedsDisplay]; jens@0: } jens@0: } jens@0: return cell; jens@0: } jens@0: jens@0: jens@0: - (void) addAllCells jens@0: { jens@24: for( int row=0; row<_nRows; row++ ) jens@0: for( int col=0; col<_nColumns; col++ ) jens@0: [self addCellAtRow: row column: col]; jens@0: } jens@0: jens@0: jens@0: - (void) removeCellAtRow: (unsigned)row column: (unsigned)col jens@0: { jens@0: NSParameterAssert(row<_nRows); jens@0: NSParameterAssert(col<_nColumns); jens@0: unsigned index = row*_nColumns+col; jens@0: id cell = [_cells objectAtIndex: index]; jens@0: if( cell != [NSNull null] ) jens@0: [cell removeFromSuperlayer]; jens@0: [_cells replaceObjectAtIndex: index withObject: [NSNull null]]; jens@0: [self setNeedsDisplay]; jens@0: } jens@0: jens@0: jens@12: - (NSArray*) cells jens@12: { jens@12: NSMutableArray *cells = [_cells mutableCopy]; jens@12: for( int i=cells.count-1; i>=0; i-- ) jens@12: if( [cells objectAtIndex: i] == [NSNull null] ) jens@12: [cells removeObjectAtIndex: i]; jens@12: return cells; jens@12: } jens@12: jens@12: jens@7: - (GridCell*) cellWithName: (NSString*)name jens@7: { jens@7: for( CALayer *layer in self.sublayers ) jens@7: if( [layer isKindOfClass: [GridCell class]] ) jens@7: if( [name isEqualToString: ((GridCell*)layer).name] ) jens@7: return (GridCell*)layer; jens@7: return nil; jens@7: } jens@7: jens@7: jens@12: - (NSCountedSet*) countPiecesByPlayer jens@12: { jens@12: NSCountedSet *players = [NSCountedSet set]; jens@12: for( GridCell *cell in self.cells ) { jens@12: Player *owner = cell.bit.owner; jens@12: if( owner ) jens@12: [players addObject: owner]; jens@12: } jens@12: return players; jens@12: } jens@12: jens@12: jens@22: - (CATransform3D) bitTransform jens@22: { jens@22: return _bitTransform; jens@22: } jens@22: jens@22: - (void) setBitTransform: (CATransform3D)t jens@22: { jens@22: _bitTransform = t; jens@22: for( GridCell *cell in self.cells ) jens@22: [cell setBitTransform: t]; jens@22: } jens@22: jens@22: - (void) updateCellTransform jens@22: { jens@22: CATransform3D t = self.aggregateTransform; jens@22: t.m41 = t.m42 = t.m43 = 0.0f; // remove translation component jens@22: t = CATransform3DInvert(t); jens@22: self.bitTransform = t; jens@22: } jens@22: jens@12: jens@12: #pragma mark - jens@12: #pragma mark GAME STATE: jens@12: jens@12: jens@12: - (NSString*) stateString jens@12: { jens@12: NSMutableString *state = [NSMutableString stringWithCapacity: _cells.count]; jens@12: for( GridCell *cell in self.cells ) { jens@12: Bit *bit = cell.bit; jens@12: NSString *name = bit ?bit.name :@"-"; jens@12: NSAssert(name.length==1,@"Missing or multicharacter name"); jens@12: [state appendString: name]; jens@12: } jens@12: return state; jens@12: } jens@12: jens@12: - (void) setStateString: (NSString*)state jens@12: { jens@12: Game *game = self.game; jens@12: int i = 0; jens@12: for( GridCell *cell in self.cells ) jens@12: cell.bit = [game makePieceNamed: [state substringWithRange: NSMakeRange(i++,1)]]; jens@12: } jens@12: jens@12: jens@12: - (BOOL) applyMoveString: (NSString*)move jens@12: { jens@12: GridCell *src = nil; jens@12: for( NSString *ident in [move componentsSeparatedByString: @"-"] ) { jens@12: while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] ) jens@12: ident = [ident substringToIndex: ident.length-1]; jens@12: GridCell *dst = [self cellWithName: ident]; jens@12: if( dst == nil ) jens@12: return NO; jens@12: if( src && ! [self.game animateMoveFrom: src to: dst] ) jens@12: return NO; jens@12: src = dst; jens@12: } jens@12: return YES; jens@12: } jens@12: jens@12: jens@0: #pragma mark - jens@0: #pragma mark DRAWING: jens@0: jens@0: jens@0: - (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill jens@0: { jens@0: // Subroutine of -drawInContext:. Draws all the cells, with or without a fill. jens@0: for( unsigned row=0; row<_nRows; row++ ) jens@0: for( unsigned col=0; col<_nColumns; col++ ) { jens@0: GridCell *cell = [self cellAtRow: row column: col]; jens@0: if( cell ) jens@0: [cell drawInParentContext: ctx fill: fill]; jens@0: } jens@0: } jens@0: jens@15: - (void) drawBackgroundInContext: (CGContextRef)ctx jens@15: { jens@15: if( _backgroundImage ) { jens@15: CGRect bounds = self.bounds; jens@15: if( _reversed ) { jens@15: CGContextSaveGState(ctx); jens@15: CGContextRotateCTM(ctx, M_PI); jens@15: CGContextTranslateCTM(ctx, -bounds.size.width, -bounds.size.height); jens@15: } jens@15: CGContextDrawImage(ctx, bounds, _backgroundImage); jens@15: if( _reversed ) jens@15: CGContextRestoreGState(ctx); jens@15: } jens@15: } jens@15: jens@0: jens@0: - (void)drawInContext:(CGContextRef)ctx jens@0: { jens@0: // Custom CALayer drawing implementation. Delegates to the cells to draw themselves jens@0: // in me; this is more efficient than having each cell have its own drawing. jens@3: [super drawInContext: ctx]; jens@11: jens@15: [self drawBackgroundInContext: ctx]; jens@11: jens@0: if( _cellColor ) { jens@0: CGContextSetFillColorWithColor(ctx, _cellColor); jens@0: [self drawCellsInContext: ctx fill: YES]; jens@0: } jens@0: if( _lineColor ) { jens@0: CGContextSetStrokeColorWithColor(ctx,_lineColor); jens@0: [self drawCellsInContext:ctx fill: NO]; jens@0: } jens@0: } jens@0: jens@0: jens@0: @end jens@0: jens@0: jens@0: jens@0: #pragma mark - jens@0: jens@0: @implementation GridCell jens@0: jens@0: jens@0: - (id) initWithGrid: (Grid*)grid jens@0: row: (unsigned)row column: (unsigned)col jens@0: frame: (CGRect)frame jens@0: { jens@0: self = [super init]; jens@0: if (self != nil) { jens@0: _grid = grid; jens@0: _row = row; jens@0: _column = col; jens@22: self.anchorPoint = CGPointMake(0,0); jens@0: self.position = frame.origin; jens@0: CGRect bounds = frame; jens@0: bounds.origin.x -= floor(bounds.origin.x); // make sure my coords fall on pixel boundaries jens@0: bounds.origin.y -= floor(bounds.origin.y); jens@0: self.bounds = bounds; jens@0: self.borderColor = kHighlightColor; // Used when highlighting (see -setHighlighted:) jens@22: [self setBitTransform: grid.bitTransform]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: - (NSString*) description jens@0: { jens@0: return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row]; jens@0: } jens@0: jens@0: @synthesize grid=_grid, row=_row, column=_column; jens@0: jens@0: jens@22: - (void) setBitTransform: (CATransform3D)bitTransform jens@22: { jens@22: // To make the bitTransform relative to my center, I need to offset the center to the origin jens@22: // first, and then back afterwards. jens@22: CGSize size = self.bounds.size; jens@22: CATransform3D x = CATransform3DMakeTranslation(-size.width/2, -size.height/2,0); jens@22: x = CATransform3DConcat(x, bitTransform); jens@22: x = CATransform3DConcat(x, CATransform3DMakeTranslation(size.width/2, size.height/2,0)); jens@22: self.sublayerTransform = x; jens@22: } jens@22: jens@22: jens@0: - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill jens@0: { jens@0: // Default implementation just fills or outlines the cell. jens@0: CGRect frame = self.frame; jens@0: if( fill ) jens@0: CGContextFillRect(ctx,frame); jens@0: else jens@0: CGContextStrokeRect(ctx, frame); jens@0: } jens@0: jens@0: jens@0: - (void) setBit: (Bit*)bit jens@0: { jens@0: if( bit != self.bit ) { jens@0: [super setBit: bit]; jens@22: if( bit ) jens@22: bit.position = GetCGRectCenter(self.bounds); jens@0: } jens@0: } jens@0: jens@0: - (Bit*) canDragBit: (Bit*)bit jens@0: { jens@0: if( _grid.allowsMoves && bit==self.bit ) jens@0: return [super canDragBit: bit]; jens@0: else jens@0: return nil; jens@0: } jens@0: jens@0: - (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point jens@0: { jens@0: return self.bit == nil || _grid.allowsCaptures; jens@0: } jens@0: jens@0: jens@0: - (BOOL) fwdIsN jens@0: { jens@0: return self.game.currentPlayer.index == 0; jens@0: } jens@0: jens@0: jens@0: - (NSArray*) neighbors jens@0: { jens@0: BOOL orthogonal = ! _grid.usesDiagonals; jens@0: NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8]; jens@0: for( int dy=-1; dy<=1; dy++ ) jens@0: for( int dx=-1; dx<=1; dx++ ) jens@0: if( (dx || dy) && !(orthogonal && dx && dy) ) { jens@0: GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx]; jens@0: if( cell ) jens@0: [neighbors addObject: cell]; jens@0: } jens@0: return neighbors; jens@0: } jens@0: jens@0: jens@0: // Recursive subroutine used by getGroup:. jens@0: - (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner jens@0: { jens@0: Bit *bit = self.bit; jens@0: if( bit == nil ) { jens@0: if( [liberties containsObject: self] ) jens@0: return; // already traversed jens@0: [liberties addObject: self]; jens@0: } else if( bit.owner==owner ) { jens@0: if( [group containsObject: self] ) jens@0: return; // already traversed jens@0: [group addObject: self]; jens@0: for( GridCell *c in self.neighbors ) jens@0: [c x_addToGroup: group liberties: liberties owner: owner]; jens@0: } jens@0: } jens@0: jens@0: jens@0: - (NSSet*) getGroup: (int*)outLiberties jens@0: { jens@0: NSMutableSet *group=[NSMutableSet set], *liberties=nil; jens@0: if( outLiberties ) jens@0: liberties = [NSMutableSet set]; jens@0: [self x_addToGroup: group liberties: liberties owner: self.bit.owner]; jens@0: if( outLiberties ) jens@0: *outLiberties = liberties.count; jens@0: return group; jens@0: } jens@0: jens@0: jens@0: #pragma mark - jens@0: #pragma mark DRAG-AND-DROP: jens@0: jens@0: jens@8: #if ! TARGET_OS_IPHONE jens@1: jens@11: // An image from another app can be dragged onto a Grid to change its background pattern. jens@0: jens@0: - (NSDragOperation)draggingEntered:(id )sender jens@0: { jens@11: if( CanGetCGImageFromPasteboard([sender draggingPasteboard]) ) jens@0: return NSDragOperationCopy; jens@0: else jens@0: return NSDragOperationNone; jens@0: } jens@0: jens@0: - (BOOL)performDragOperation:(id )sender jens@0: { jens@11: CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender); jens@0: if( image ) { jens@0: CGColorRef pattern = CreatePatternColor(image); jens@0: _grid.cellColor = pattern; jens@0: CGColorRelease(pattern); jens@0: [_grid setNeedsDisplay]; jens@0: return YES; jens@0: } else jens@0: return NO; jens@0: } jens@0: jens@1: #endif jens@0: jens@0: @end jens@0: jens@0: jens@0: jens@0: jens@0: #pragma mark - jens@0: jens@0: @implementation RectGrid jens@0: jens@0: jens@0: - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns jens@0: spacing: (CGSize)spacing jens@0: position: (CGPoint)pos jens@0: { jens@0: self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos]; jens@0: if( self ) { jens@0: _cellClass = [Square class]; jens@0: } jens@0: return self; jens@0: } jens@0: jens@0: jens@0: - (CGColorRef) altCellColor {return _altCellColor;} jens@0: - (void) setAltCellColor: (CGColorRef)altCellColor {setcolor(&_altCellColor,altCellColor);} jens@0: jens@0: jens@0: @end jens@0: jens@0: jens@0: jens@0: #pragma mark - jens@0: jens@0: @implementation Square jens@0: jens@0: jens@0: - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill jens@0: { jens@0: if( fill ) { jens@0: CGColorRef c = ((RectGrid*)_grid).altCellColor; jens@0: if( c ) { jens@0: if( ! ((_row+_column) & 1) ) jens@0: c = _grid.cellColor; jens@0: CGContextSetFillColorWithColor(ctx, c); jens@0: } jens@0: } jens@0: [super drawInParentContext: ctx fill: fill]; jens@0: } jens@0: jens@0: jens@0: - (void) setHighlighted: (BOOL)highlighted jens@0: { jens@0: [super setHighlighted: highlighted]; jens@3: self.cornerRadius = self.bounds.size.width/2.0; jens@0: self.borderWidth = (highlighted ?6 :0); jens@0: } jens@0: jens@0: jens@0: - (Square*) nw {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];} jens@0: - (Square*) n {return (Square*)[_grid cellAtRow: _row+1 column: _column ];} jens@0: - (Square*) ne {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];} jens@0: - (Square*) e {return (Square*)[_grid cellAtRow: _row column: _column+1];} jens@0: - (Square*) se {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];} jens@0: - (Square*) s {return (Square*)[_grid cellAtRow: _row-1 column: _column ];} jens@0: - (Square*) sw {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];} jens@0: - (Square*) w {return (Square*)[_grid cellAtRow: _row column: _column-1];} jens@0: jens@0: // Directions relative to the current player: jens@0: - (Square*) fl {return self.fwdIsN ?self.nw :self.se;} jens@0: - (Square*) f {return self.fwdIsN ?self.n :self.s;} jens@0: - (Square*) fr {return self.fwdIsN ?self.ne :self.sw;} jens@0: - (Square*) r {return self.fwdIsN ?self.e :self.w;} jens@0: - (Square*) br {return self.fwdIsN ?self.se :self.nw;} jens@0: - (Square*) b {return self.fwdIsN ?self.s :self.n;} jens@0: - (Square*) bl {return self.fwdIsN ?self.sw :self.ne;} jens@0: - (Square*) l {return self.fwdIsN ?self.w :self.e;} jens@0: jens@0: jens@12: static int sgn( int n ) {return n<0 ?-1 :(n>0 ?1 :0);} jens@12: jens@12: jens@12: - (SEL) directionToCell: (GridCell*)dst jens@12: { jens@12: static NSString* const kDirections[9] = {@"sw", @"s", @"se", jens@12: @"w", nil, @"e", jens@12: @"nw", @"n", @"ne"}; jens@12: if( dst.grid != self.grid ) jens@12: return NULL; jens@12: int dy=dst.row-_row, dx=dst.column-_column; jens@12: if( dx && dy ) jens@12: if( !( _grid.usesDiagonals && abs(dx)==abs(dy) ) ) jens@12: return NULL; jens@12: NSString *dir = kDirections[ 3*(sgn(dy)+1) + (sgn(dx)+1) ]; jens@12: return dir ?NSSelectorFromString(dir) :NULL; jens@12: } jens@12: jens@12: - (NSArray*) lineToCell: (GridCell*)dst inclusive: (BOOL)inclusive; jens@12: { jens@12: SEL dir = [self directionToCell: dst]; jens@12: if( ! dir ) jens@12: return nil; jens@12: NSMutableArray *line = [NSMutableArray array]; jens@12: GridCell *cell; jens@12: for( cell=self; cell; cell = [cell performSelector: dir] ) { jens@12: if( inclusive || (cell!=self && cell!=dst) ) jens@12: [line addObject: cell]; jens@12: if( cell==dst ) jens@12: return line; jens@12: } jens@12: return nil; // should be impossible, but just in case jens@12: } jens@12: jens@12: jens@8: #if ! TARGET_OS_IPHONE jens@1: jens@0: - (BOOL)performDragOperation:(id )sender jens@0: { jens@11: CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender); jens@0: if( image ) { jens@0: CGColorRef color = CreatePatternColor(image); jens@0: RectGrid *rectGrid = (RectGrid*)_grid; jens@0: if( rectGrid.altCellColor && ((_row+_column) & 1) ) jens@0: rectGrid.altCellColor = color; jens@0: else jens@0: rectGrid.cellColor = color; jens@0: CGColorRelease(color); jens@0: [rectGrid setNeedsDisplay]; jens@0: return YES; jens@0: } else jens@0: return NO; jens@0: } jens@0: jens@1: #endif jens@1: jens@0: @end jens@0: jens@0: jens@0: jens@0: #pragma mark - jens@0: jens@0: @implementation GoSquare jens@0: jens@0: @synthesize dotted=_dotted; jens@0: jens@0: - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill jens@0: { jens@0: if( fill ) jens@0: [super drawInParentContext: ctx fill: fill]; jens@0: else { jens@0: CGRect frame = self.frame; jens@0: const CGFloat midx=floor(CGRectGetMidX(frame))+0.5, jens@0: midy=floor(CGRectGetMidY(frame))+0.5; jens@0: CGPoint p[4] = {{CGRectGetMinX(frame),midy}, jens@0: {CGRectGetMaxX(frame),midy}, jens@0: {midx,CGRectGetMinY(frame)}, jens@0: {midx,CGRectGetMaxY(frame)}}; jens@0: if( ! self.s ) p[2].y = midy; jens@0: if( ! self.n ) p[3].y = midy; jens@0: if( ! self.w ) p[0].x = midx; jens@0: if( ! self.e ) p[1].x = midx; jens@0: CGContextStrokeLineSegments(ctx, p, 4); jens@0: jens@0: if( _dotted ) { jens@0: CGContextSetFillColorWithColor(ctx,_grid.lineColor); jens@0: CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5); jens@0: CGContextFillEllipseInRect(ctx, dot); jens@0: } jens@0: } jens@0: } jens@0: jens@1: jens@0: @end