Source/Grid.m
author Jens Alfke <jens@mooseyard.com>
Fri Jul 18 13:26:59 2008 -0700 (2008-07-18)
changeset 20 7c9ecb09a612
parent 12 4e567e11f45f
child 22 4cb50131788f
permissions -rw-r--r--
Checkers and Hexchequer now detect victory when the other player can't move.
     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 "Grid.h"
    24 #import "Bit.h"
    25 #import "Piece.h"
    26 #import "Game+Protected.h"
    27 #import "Player.h"
    28 #import "QuartzUtils.h"
    29 
    30 
    31 @implementation Grid
    32 
    33 
    34 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
    35             spacing: (CGSize)spacing
    36            position: (CGPoint)pos
    37 {
    38     NSParameterAssert(nRows>0 && nColumns>0);
    39     self = [super init];
    40     if( self ) {
    41         _nRows = nRows;
    42         _nColumns = nColumns;
    43         _spacing = spacing;
    44         _cellClass = [GridCell class];
    45         self.lineColor = kBlackColor;
    46         _allowsMoves = YES;
    47         _usesDiagonals = YES;
    48 
    49         self.bounds = CGRectMake(-1, -1, nColumns*spacing.width+2, nRows*spacing.height+2);
    50         self.position = pos;
    51         self.anchorPoint = CGPointMake(0,0);
    52         self.zPosition = kBoardZ;
    53         self.needsDisplayOnBoundsChange = YES;
    54         
    55         unsigned n = nRows*nColumns;
    56         _cells = [[NSMutableArray alloc] initWithCapacity: n];
    57         id null = [NSNull null];
    58         while( n-- > 0 )
    59             [_cells addObject: null];
    60 
    61         [self setNeedsDisplay];
    62     }
    63     return self;
    64 }
    65 
    66 
    67 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
    68               frame: (CGRect)frame
    69 {
    70     CGFloat spacing = floor(MIN( (frame.size.width -2)/(CGFloat)nColumns,
    71                                (frame.size.height-2)/(CGFloat)nRows) );
    72     CGSize size = CGSizeMake(nColumns*spacing+2, nRows*spacing+2);
    73     CGPoint position = frame.origin;
    74     position.x += round( (frame.size.width -size.width )/2.0 );
    75     position.y += round( (frame.size.height-size.height)/2.0 );
    76 
    77     return [self initWithRows: nRows columns: nColumns
    78                       spacing: CGSizeMake(spacing,spacing)
    79                      position: position];
    80 }
    81 
    82 
    83 - (void) dealloc
    84 {
    85     CGColorRelease(_cellColor);
    86     CGColorRelease(_lineColor);
    87     [_cells release];
    88     [super dealloc];
    89 }
    90 
    91 
    92 static void setcolor( CGColorRef *var, CGColorRef color )
    93 {
    94     if( color != *var ) {
    95         CGColorRelease(*var);
    96         *var = CGColorRetain(color);
    97     }
    98 }
    99 
   100 - (CGColorRef) cellColor                        {return _cellColor;}
   101 - (void) setCellColor: (CGColorRef)cellColor    {setcolor(&_cellColor,cellColor);}
   102 
   103 - (CGColorRef) lineColor                        {return _lineColor;}
   104 - (void) setLineColor: (CGColorRef)lineColor    {setcolor(&_lineColor,lineColor);}
   105 
   106 - (CGImageRef) backgroundImage                  {return _backgroundImage;}
   107 - (void) setBackgroundImage: (CGImageRef)image
   108 {
   109     if( image != _backgroundImage ) {
   110         CGImageRelease(_backgroundImage);
   111         _backgroundImage = CGImageRetain(image);
   112     }
   113 }
   114 
   115 @synthesize cellClass=_cellClass, rows=_nRows, columns=_nColumns, spacing=_spacing, reversed=_reversed,
   116             usesDiagonals=_usesDiagonals, allowsMoves=_allowsMoves, allowsCaptures=_allowsCaptures;
   117 
   118 
   119 #pragma mark -
   120 #pragma mark GEOMETRY:
   121 
   122 
   123 - (GridCell*) cellAtRow: (unsigned)row column: (unsigned)col
   124 {
   125     if( row < _nRows && col < _nColumns ) {
   126         id cell = [_cells objectAtIndex: row*_nColumns+col];
   127         if( cell != [NSNull null] )
   128             return cell;
   129     }
   130     return nil;
   131 }
   132 
   133 
   134 /** Subclasses can override this, to change the cell's class or frame. */
   135 - (GridCell*) createCellAtRow: (unsigned)row column: (unsigned)col 
   136                suggestedFrame: (CGRect)frame
   137 {
   138     GridCell *cell = [[_cellClass alloc] initWithGrid: self 
   139                                                   row: row column: col
   140                                                 frame: frame];
   141     cell.name = [NSString stringWithFormat: @"%c%u", ('A'+row),(1+col)];
   142     return [cell autorelease];
   143 }
   144 
   145 
   146 - (GridCell*) addCellAtRow: (unsigned)row column: (unsigned)col
   147 {
   148     NSParameterAssert(row<_nRows);
   149     NSParameterAssert(col<_nColumns);
   150     unsigned index = row*_nColumns+col;
   151     GridCell *cell = [_cells objectAtIndex: index];
   152     if( (id)cell == [NSNull null] ) {
   153         unsigned effectiveRow=row, effectiveCol=col;
   154         if( _reversed ) {
   155             effectiveRow = _nRows-1    - effectiveRow;
   156             effectiveCol = _nColumns-1 - effectiveCol;
   157         }
   158         CGRect frame = CGRectMake(effectiveCol*_spacing.width, effectiveRow*_spacing.height,
   159                                   _spacing.width,_spacing.height);
   160         cell = [self createCellAtRow: row column: col suggestedFrame: frame];
   161         if( cell ) {
   162             [_cells replaceObjectAtIndex: index withObject: cell];
   163             [self addSublayer: cell];
   164             [self setNeedsDisplay];
   165         }
   166     }
   167     return cell;
   168 }
   169 
   170 
   171 - (void) addAllCells
   172 {
   173     for( int row=_nRows-1; row>=0; row-- )                // makes 'upper' cells be in 'back'
   174         for( int col=0; col<_nColumns; col++ ) 
   175             [self addCellAtRow: row column: col];
   176 }
   177 
   178 
   179 - (void) removeCellAtRow: (unsigned)row column: (unsigned)col
   180 {
   181     NSParameterAssert(row<_nRows);
   182     NSParameterAssert(col<_nColumns);
   183     unsigned index = row*_nColumns+col;
   184     id cell = [_cells objectAtIndex: index];
   185     if( cell != [NSNull null] )
   186         [cell removeFromSuperlayer];
   187     [_cells replaceObjectAtIndex: index withObject: [NSNull null]];
   188     [self setNeedsDisplay];
   189 }
   190 
   191 
   192 - (NSArray*) cells
   193 {
   194     NSMutableArray *cells = [_cells mutableCopy];
   195     for( int i=cells.count-1; i>=0; i-- )
   196         if( [cells objectAtIndex: i] == [NSNull null] )
   197             [cells removeObjectAtIndex: i];
   198     return cells;
   199 }
   200 
   201 
   202 - (GridCell*) cellWithName: (NSString*)name
   203 {
   204     for( CALayer *layer in self.sublayers )
   205         if( [layer isKindOfClass: [GridCell class]] )
   206             if( [name isEqualToString: ((GridCell*)layer).name] )
   207                 return (GridCell*)layer;
   208     return nil;
   209 }
   210 
   211 
   212 - (NSCountedSet*) countPiecesByPlayer
   213 {
   214     NSCountedSet *players = [NSCountedSet set];
   215     for( GridCell *cell in self.cells ) {
   216         Player *owner = cell.bit.owner;
   217         if( owner )
   218             [players addObject: owner];
   219     }
   220     return players;
   221 }
   222 
   223 
   224 
   225 #pragma mark -
   226 #pragma mark GAME STATE:
   227 
   228 
   229 - (NSString*) stateString
   230 {
   231     NSMutableString *state = [NSMutableString stringWithCapacity: _cells.count];
   232     for( GridCell *cell in self.cells ) {
   233         Bit *bit = cell.bit;
   234         NSString *name = bit ?bit.name :@"-";
   235         NSAssert(name.length==1,@"Missing or multicharacter name");
   236         [state appendString: name];
   237     }
   238     return state;
   239 }
   240 
   241 - (void) setStateString: (NSString*)state
   242 {
   243     Game *game = self.game;
   244     int i = 0;
   245     for( GridCell *cell in self.cells )
   246         cell.bit = [game makePieceNamed: [state substringWithRange: NSMakeRange(i++,1)]];
   247 }
   248 
   249 
   250 - (BOOL) applyMoveString: (NSString*)move
   251 {
   252     GridCell *src = nil;
   253     for( NSString *ident in [move componentsSeparatedByString: @"-"] ) {
   254         while( [ident hasSuffix: @"!"] || [ident hasSuffix: @"*"] )
   255             ident = [ident substringToIndex: ident.length-1];
   256         GridCell *dst = [self cellWithName: ident];
   257         if( dst == nil )
   258             return NO;
   259         if( src && ! [self.game animateMoveFrom: src to: dst] )
   260             return NO;
   261         src = dst;
   262     }
   263     return YES;
   264 }
   265 
   266 
   267 #pragma mark -
   268 #pragma mark DRAWING:
   269 
   270 
   271 - (void) drawCellsInContext: (CGContextRef)ctx fill: (BOOL)fill
   272 {
   273     // Subroutine of -drawInContext:. Draws all the cells, with or without a fill.
   274     for( unsigned row=0; row<_nRows; row++ )
   275         for( unsigned col=0; col<_nColumns; col++ ) {
   276             GridCell *cell = [self cellAtRow: row column: col];
   277             if( cell )
   278                 [cell drawInParentContext: ctx fill: fill];
   279         }
   280 }
   281 
   282 - (void) drawBackgroundInContext: (CGContextRef)ctx
   283 {
   284     if( _backgroundImage ) {
   285         CGRect bounds = self.bounds;
   286         if( _reversed ) {
   287             CGContextSaveGState(ctx);
   288             CGContextRotateCTM(ctx, M_PI);
   289             CGContextTranslateCTM(ctx, -bounds.size.width, -bounds.size.height);
   290         }
   291         CGContextDrawImage(ctx, bounds, _backgroundImage);
   292         if( _reversed )
   293             CGContextRestoreGState(ctx);
   294     }
   295 }
   296 
   297 
   298 - (void)drawInContext:(CGContextRef)ctx
   299 {
   300     // Custom CALayer drawing implementation. Delegates to the cells to draw themselves
   301     // in me; this is more efficient than having each cell have its own drawing.
   302     [super drawInContext: ctx];
   303     
   304     [self drawBackgroundInContext: ctx];
   305     
   306     if( _cellColor ) {
   307         CGContextSetFillColorWithColor(ctx, _cellColor);
   308         [self drawCellsInContext: ctx fill: YES];
   309     }
   310     if( _lineColor ) {
   311         CGContextSetStrokeColorWithColor(ctx,_lineColor);
   312         [self drawCellsInContext:ctx fill: NO];
   313     }
   314 }
   315 
   316 
   317 @end
   318 
   319 
   320 
   321 #pragma mark -
   322 
   323 @implementation GridCell
   324 
   325 
   326 - (id) initWithGrid: (Grid*)grid 
   327                 row: (unsigned)row column: (unsigned)col
   328               frame: (CGRect)frame
   329 {
   330     self = [super init];
   331     if (self != nil) {
   332         _grid = grid;
   333         _row = row;
   334         _column = col;
   335         self.position = frame.origin;
   336         CGRect bounds = frame;
   337         bounds.origin.x -= floor(bounds.origin.x);  // make sure my coords fall on pixel boundaries
   338         bounds.origin.y -= floor(bounds.origin.y);
   339         self.bounds = bounds;
   340         self.anchorPoint = CGPointMake(0,0);
   341         self.borderColor = kHighlightColor;         // Used when highlighting (see -setHighlighted:)
   342     }
   343     return self;
   344 }
   345 
   346 - (NSString*) description
   347 {
   348     return [NSString stringWithFormat: @"%@(%u,%u)", [self class],_column,_row];
   349 }
   350 
   351 @synthesize grid=_grid, row=_row, column=_column;
   352 
   353 
   354 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
   355 {
   356     // Default implementation just fills or outlines the cell.
   357     CGRect frame = self.frame;
   358     if( fill )
   359         CGContextFillRect(ctx,frame);
   360     else
   361         CGContextStrokeRect(ctx, frame);
   362 }
   363 
   364 
   365 - (void) setBit: (Bit*)bit
   366 {
   367     if( bit != self.bit ) {
   368         [super setBit: bit];
   369         if( bit ) {
   370             // Center it:
   371             CGSize size = self.bounds.size;
   372             bit.position = CGPointMake(floor(size.width/2.0),
   373                                        floor(size.height/2.0));
   374         }
   375     }
   376 }
   377 
   378 - (Bit*) canDragBit: (Bit*)bit
   379 {
   380     if( _grid.allowsMoves && bit==self.bit )
   381         return [super canDragBit: bit];
   382     else
   383         return nil;
   384 }
   385 
   386 - (BOOL) canDropBit: (Bit*)bit atPoint: (CGPoint)point
   387 {
   388     return self.bit == nil || _grid.allowsCaptures;
   389 }
   390 
   391 
   392 - (BOOL) fwdIsN 
   393 {
   394     return self.game.currentPlayer.index == 0;
   395 }
   396 
   397 
   398 - (NSArray*) neighbors
   399 {
   400     BOOL orthogonal = ! _grid.usesDiagonals;
   401     NSMutableArray *neighbors = [NSMutableArray arrayWithCapacity: 8];
   402     for( int dy=-1; dy<=1; dy++ )
   403         for( int dx=-1; dx<=1; dx++ )
   404             if( (dx || dy) && !(orthogonal && dx && dy) ) {
   405                 GridCell *cell = [_grid cellAtRow: _row+dy column: _column+dx];
   406                 if( cell )
   407                     [neighbors addObject: cell];
   408             }
   409     return neighbors;
   410 }
   411 
   412 
   413 // Recursive subroutine used by getGroup:.
   414 - (void) x_addToGroup: (NSMutableSet*)group liberties: (NSMutableSet*)liberties owner: (Player*)owner
   415 {
   416     Bit *bit = self.bit;
   417     if( bit == nil ) {
   418         if( [liberties containsObject: self] )
   419             return; // already traversed
   420         [liberties addObject: self];
   421     } else if( bit.owner==owner ) {
   422         if( [group containsObject: self] )
   423             return; // already traversed
   424         [group addObject: self];
   425         for( GridCell *c in self.neighbors )
   426             [c x_addToGroup: group liberties: liberties owner: owner];
   427     }
   428 }
   429 
   430 
   431 - (NSSet*) getGroup: (int*)outLiberties
   432 {
   433     NSMutableSet *group=[NSMutableSet set], *liberties=nil;
   434     if( outLiberties )
   435         liberties = [NSMutableSet set];
   436     [self x_addToGroup: group liberties: liberties owner: self.bit.owner];
   437     if( outLiberties )
   438         *outLiberties = liberties.count;
   439     return group;
   440 }
   441 
   442 
   443 #pragma mark -
   444 #pragma mark DRAG-AND-DROP:
   445 
   446 
   447 #if ! TARGET_OS_IPHONE
   448 
   449 // An image from another app can be dragged onto a Grid to change its background pattern.
   450 
   451 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
   452 {
   453     if( CanGetCGImageFromPasteboard([sender draggingPasteboard]) )
   454         return NSDragOperationCopy;
   455     else
   456         return NSDragOperationNone;
   457 }
   458 
   459 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
   460 {
   461     CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
   462     if( image ) {
   463         CGColorRef pattern = CreatePatternColor(image);
   464         _grid.cellColor = pattern;
   465         CGColorRelease(pattern);
   466         [_grid setNeedsDisplay];
   467         return YES;
   468     } else
   469         return NO;
   470 }
   471 
   472 #endif
   473 
   474 @end
   475 
   476 
   477 
   478 
   479 #pragma mark -
   480 
   481 @implementation RectGrid
   482 
   483 
   484 - (id) initWithRows: (unsigned)nRows columns: (unsigned)nColumns
   485             spacing: (CGSize)spacing
   486            position: (CGPoint)pos
   487 {
   488     self = [super initWithRows: nRows columns: nColumns spacing: spacing position: pos];
   489     if( self ) {
   490         _cellClass = [Square class];
   491     }
   492     return self;
   493 }
   494 
   495 
   496 - (CGColorRef) altCellColor                         {return _altCellColor;}
   497 - (void) setAltCellColor: (CGColorRef)altCellColor  {setcolor(&_altCellColor,altCellColor);}
   498 
   499 
   500 @end
   501 
   502 
   503 
   504 #pragma mark -
   505 
   506 @implementation Square
   507 
   508 
   509 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
   510 {
   511     if( fill ) {
   512         CGColorRef c = ((RectGrid*)_grid).altCellColor;
   513         if( c ) {
   514             if( ! ((_row+_column) & 1) )
   515                 c = _grid.cellColor;
   516             CGContextSetFillColorWithColor(ctx, c);
   517         }
   518     }
   519     [super drawInParentContext: ctx fill: fill];
   520 }
   521 
   522 
   523 - (void) setHighlighted: (BOOL)highlighted
   524 {
   525     [super setHighlighted: highlighted];
   526     self.cornerRadius = self.bounds.size.width/2.0;
   527     self.borderWidth = (highlighted ?6 :0);
   528 }
   529 
   530 
   531 - (Square*) nw     {return (Square*)[_grid cellAtRow: _row+1 column: _column-1];}
   532 - (Square*) n      {return (Square*)[_grid cellAtRow: _row+1 column: _column  ];}
   533 - (Square*) ne     {return (Square*)[_grid cellAtRow: _row+1 column: _column+1];}
   534 - (Square*) e      {return (Square*)[_grid cellAtRow: _row   column: _column+1];}
   535 - (Square*) se     {return (Square*)[_grid cellAtRow: _row-1 column: _column+1];}
   536 - (Square*) s      {return (Square*)[_grid cellAtRow: _row-1 column: _column  ];}
   537 - (Square*) sw     {return (Square*)[_grid cellAtRow: _row-1 column: _column-1];}
   538 - (Square*) w      {return (Square*)[_grid cellAtRow: _row   column: _column-1];}
   539 
   540 // Directions relative to the current player:
   541 - (Square*) fl     {return self.fwdIsN ?self.nw :self.se;}
   542 - (Square*) f      {return self.fwdIsN ?self.n  :self.s;}
   543 - (Square*) fr     {return self.fwdIsN ?self.ne :self.sw;}
   544 - (Square*) r      {return self.fwdIsN ?self.e  :self.w;}
   545 - (Square*) br     {return self.fwdIsN ?self.se :self.nw;}
   546 - (Square*) b      {return self.fwdIsN ?self.s  :self.n;}
   547 - (Square*) bl     {return self.fwdIsN ?self.sw :self.ne;}
   548 - (Square*) l      {return self.fwdIsN ?self.w  :self.e;}
   549 
   550 
   551 static int sgn( int n ) {return n<0 ?-1 :(n>0 ?1 :0);}
   552 
   553 
   554 - (SEL) directionToCell: (GridCell*)dst
   555 {
   556     static NSString* const kDirections[9] = {@"sw", @"s", @"se",
   557                                              @"w",  nil,  @"e",
   558                                              @"nw", @"n", @"ne"};
   559     if( dst.grid != self.grid )
   560         return NULL;
   561     int dy=dst.row-_row, dx=dst.column-_column;
   562     if( dx && dy )
   563         if( !( _grid.usesDiagonals && abs(dx)==abs(dy) ) )
   564             return NULL;
   565     NSString *dir = kDirections[ 3*(sgn(dy)+1) + (sgn(dx)+1) ];
   566     return dir ?NSSelectorFromString(dir) :NULL;
   567 }
   568 
   569 - (NSArray*) lineToCell: (GridCell*)dst inclusive: (BOOL)inclusive;
   570 {
   571     SEL dir = [self directionToCell: dst];
   572     if( ! dir )
   573         return nil;
   574     NSMutableArray *line = [NSMutableArray array];
   575     GridCell *cell;
   576     for( cell=self; cell; cell = [cell performSelector: dir] ) {
   577         if( inclusive || (cell!=self && cell!=dst) )
   578             [line addObject: cell];
   579         if( cell==dst )
   580             return line;
   581     }
   582     return nil; // should be impossible, but just in case
   583 }
   584                 
   585 
   586 #if ! TARGET_OS_IPHONE
   587 
   588 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
   589 {
   590     CGImageRef image = GetCGImageFromPasteboard([sender draggingPasteboard],sender);
   591     if( image ) {
   592         CGColorRef color = CreatePatternColor(image);
   593         RectGrid *rectGrid = (RectGrid*)_grid;
   594         if( rectGrid.altCellColor && ((_row+_column) & 1) )
   595             rectGrid.altCellColor = color;
   596         else
   597             rectGrid.cellColor = color;
   598         CGColorRelease(color);
   599         [rectGrid setNeedsDisplay];
   600         return YES;
   601     } else
   602         return NO;
   603 }
   604 
   605 #endif
   606 
   607 @end
   608 
   609 
   610 
   611 #pragma mark -
   612 
   613 @implementation GoSquare
   614 
   615 @synthesize dotted=_dotted;
   616 
   617 - (void) drawInParentContext: (CGContextRef)ctx fill: (BOOL)fill
   618 {
   619     if( fill )
   620         [super drawInParentContext: ctx fill: fill];
   621     else {
   622         CGRect frame = self.frame;
   623         const CGFloat midx=floor(CGRectGetMidX(frame))+0.5, 
   624                     midy=floor(CGRectGetMidY(frame))+0.5;
   625         CGPoint p[4] = {{CGRectGetMinX(frame),midy},
   626                         {CGRectGetMaxX(frame),midy},
   627                         {midx,CGRectGetMinY(frame)},
   628                         {midx,CGRectGetMaxY(frame)}};
   629         if( ! self.s )  p[2].y = midy;
   630         if( ! self.n )  p[3].y = midy;
   631         if( ! self.w )  p[0].x = midx;
   632         if( ! self.e )  p[1].x = midx;
   633         CGContextStrokeLineSegments(ctx, p, 4);
   634         
   635         if( _dotted ) {
   636             CGContextSetFillColorWithColor(ctx,_grid.lineColor);
   637             CGRect dot = CGRectMake(midx-2.5, midy-2.5, 5, 5);
   638             CGContextFillEllipseInRect(ctx, dot);
   639         }
   640     }
   641 }
   642 
   643 
   644 @end