/* IMPORTANT: This Apple software is supplied to you by Apple Computer, 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 Computer, 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. $RCSfile: Board3D.m,v $ Chess Copyright (c) 2000-2001 Apple Computer. All rights reserved. */ #import #import #import #import #import #import // NSBeep #import #import // NSMakeSize, NSZeroPoint #import // MIN, MAX, ABS #import #include // own interface #import "Board3D.h" // messaging objects #import "Square3D.h" #import "Chess.h" // NSApp // portability layer #import "gnuglue.h" // floor_value, sleep_microsecs, ... // piece size #define PIECE_WIDTH_3D (float)55.0 #define PIECE_HEIGHT_3D (float)95.0 // back store size #define BACK_STORE_WIDTH (float)80.0 #define BACK_STORE_HEIGHT (float)110.0 /* Each set of points describes the vertical lines along the board. */ struct NXLine { NSPoint a, b; }; #define BASE_X 89 static struct NXLine vertical[] = { {{BASE_X,122}, {BASE_X+61, 467}}, {{BASE_X+71,122}, {BASE_X+111,467}}, {{BASE_X+135,122},{BASE_X+162,467}}, {{BASE_X+200,122},{BASE_X+212,467}}, {{BASE_X+265,122},{BASE_X+265,467}}, {{BASE_X+330,122},{BASE_X+316,467}}, {{BASE_X+393,122},{BASE_X+367,467}}, {{BASE_X+459,122},{BASE_X+419,467}}, {{BASE_X+524,122},{BASE_X+469,467}} }; /* Each coordinate describes the y value of each line of the board. */ #define BASE_Y 132 static float horizontal[] = { BASE_Y, BASE_Y+54, BASE_Y+106, BASE_Y+153, BASE_Y+196, BASE_Y+237, BASE_Y+277, BASE_Y+312, BASE_Y+345 }; // private functions static void squareOrigin( int r, int c, float *x, float *y ) { float dx, m, b; dx = (vertical[c].a.x - vertical[c].b.x); m = (vertical[c].a.y - vertical[c].b.y) / dx; b = vertical[c].b.y - (vertical[c].b.x * m); *x = (dx) ? ((horizontal[r] - b) / m) : vertical[c].a.x; *y = horizontal[r]; return; } static void squareBounds( int r, int c, NSPoint *p1, NSPoint *p2, NSPoint *p3, NSPoint *p4 ) /* (p2)----(p4) | | | | (p1)----(p3) */ { float dx, m, b; dx = (vertical[c].a.x - vertical[c].b.x); m = (vertical[c].a.y - vertical[c].b.y) / dx; b = vertical[c].b.y - (vertical[c].b.x * m); p1->x = (dx) ? ((horizontal[r] - b) / m) : vertical[c].a.x; p1->x = (float) floor_value( (double)p1->x ); p1->y = (float) floor_value( (double)horizontal[r] ); p2->x = (dx) ? ((horizontal[r+1] - b) / m) : vertical[c].a.x; p2->x = (float) floor_value( (double)p2->x ); p2->y = (float) floor_value( (double)horizontal[r+1] ); dx = (vertical[c+1].a.x - vertical[c+1].b.x); m = (vertical[c+1].a.y - vertical[c+1].b.y) / dx; b = vertical[c+1].b.y - (vertical[c+1].b.x * m); p3->x = (dx) ? ((horizontal[r] - b) / m) : vertical[c+1].a.x; p3->x = (float) floor_value( (double)p3->x ); p3->y = (float) floor_value( (double)horizontal[r] ); p4->x = (dx) ? ((horizontal[r+1] - b) / m) : vertical[c+1].a.x; p4->x = (float) floor_value( (double)p4->x ); p4->y = (float) floor_value( (double)horizontal[r+1] ); return; } static float check_point( struct NXLine *l, NSPoint *p ) { float dx = l->a.x - l->b.x; float dy = l->a.y - l->b.y; float dx1 = p->x - l->a.x; float dy1 = p->y - l->a.y; return( dx*dy1 - dy*dx1 ); } static void convert_point( NSPoint *p, int *r, int *c ) { int i; for( i = 0; i < 8; i++ ) { if( p->y >= horizontal[i] && p->y <= horizontal[i+1] ) { *r = i; break; } } for( i = 0; i < 8; i++ ) { float m1 = check_point( &vertical[i], p ); float m2 = check_point( &vertical[i+1], p ); if( m1 > 0 && m2 < 0 ) { *c = i; break; } } return; } // Board3D implementations @implementation Board3D - (id)initWithFrame: (NSRect)f { self = [super initWithFrame: f]; if( self ) { NSBundle *bundle; NSString *path1, *path2; NSSize size; int r, c; [self allocateGState]; bundle = [NSBundle mainBundle]; path1 = [bundle pathForImageResource: @"3d_board"]; _background = [[NSImage alloc] initWithContentsOfFile: path1]; path2 = [bundle pathForImageResource: @"3d_pieces"]; _pieces = [[NSImage alloc] initWithContentsOfFile: path2]; size = NSMakeSize( BACK_STORE_WIDTH, BACK_STORE_HEIGHT ); backBitmap = [[NSImage alloc] initWithSize: size]; for( r = 0; r < 8; r++ ) { for( c = 0; c < 8; c++ ) square[r][c] = [[Square3D alloc] init]; } [self setupPieces]; return self; } return nil; } - (void)setBackgroundBitmap: (NSImage *) bitmap { if( _background ) [_background release]; _background = [bitmap retain]; return; } - (id) backgroundBitmap { return _background; } - (void)setPiecesBitmap: (NSImage *) bitmap { if( _pieces ) [_pieces release]; _pieces = [bitmap retain]; return; } - (NSImage *)piecesBitmap { return _pieces; } - (void)setupPieces { short *pieces = default_pieces(); short *colors = default_colors(); [self layoutBoard: pieces color: colors]; return; } - (void)layoutBoard: (short *)p color: (short *)c { int sq; for( sq = 0; sq < SQUARE_COUNT; sq++ ) { int row = sq / 8; int col = sq % 8; [self placePiece: p[sq] at: row: col color: c[sq]]; } } - (void)placePiece: (short)p at: (int)row : (int)col color: (short)c { int col2; float m, b, dx, x; NSRect loc; Square3D *theSquare = square[row][col]; [theSquare setPieceType: p color: c]; [theSquare setRow: row]; dx = (vertical[col].a.x - vertical[col].b.x); m = (vertical[col].a.y - vertical[col].b.y) / dx; b = vertical[col].b.y - (vertical[col].b.x * m); x = (dx) ? ((horizontal[row] - b) / m) : vertical[col].a.x; loc.origin.x = x; loc.origin.y = horizontal[row]; col2 = col + 1; dx = (vertical[col2].a.x - vertical[col2].b.x); m = (vertical[col2].a.y - vertical[col2].b.y) / dx; b = vertical[col2].b.y - (vertical[col2].b.x * m); x = (dx) ? ((horizontal[row] - b) / m) : vertical[col2].a.x; loc.size.width = x - loc.origin.x; loc.size.height = 99999; [theSquare setLocation: loc]; return; } - (void)slidePieceFrom: (int)row1 : (int)col1 to: (int)row2 : (int)col2 { Square3D *theSquare; int pieceType, color; NSRect oldLocation; NSPoint backP, endP, roundedBackP; int controlGState; float incX, incY; int increments, i; theSquare = square[row1][col1]; pieceType = [theSquare pieceType]; if( ! pieceType ) return; color = [theSquare colorVal]; oldLocation = [theSquare location]; squareOrigin( row2, col2, &endP.x, &endP.y ); /* Remove piece and then save background */ [theSquare setPieceType: NO_PIECE color: NEUTRAL]; [self drawRect: [self frame]]; squareOrigin( row1, col1, &backP.x, &backP.y ); controlGState = [self gState]; [backBitmap lockFocus]; PSgsave(); PScomposite( roundedBackP.x = floor(backP.x), roundedBackP.y = floor(backP.y), BACK_STORE_WIDTH, BACK_STORE_HEIGHT, controlGState, (float)0.0, (float)0.0, NSCompositeCopy ); PSgrestore(); [backBitmap unlockFocus]; [self lockFocus]; [theSquare setPieceType: pieceType color: color]; [theSquare drawInteriorWithFrame: [self frame] inView: self]; [theSquare setMoving: YES]; [[self window] flushWindow]; incX = endP.x - backP.x; incY = endP.y - backP.y; increments = (int) MAX( ABS(incX), ABS(incY) ) / 7; // was 5 gcr incX = incX / increments; incY = incY / increments; for( i = 0; i < increments; i++ ) { int dr, dc; NSRect newLocation; /* Restore old background */ [self lockFocus]; [backBitmap compositeToPoint: roundedBackP operation: NSCompositeCopy]; [self unlockFocus]; backP.x += incX; backP.y += incY; convert_point( &backP, &dr, &dc ); /* Save new background */ [backBitmap lockFocus]; PSgsave(); PScomposite( roundedBackP.x = floor(backP.x), roundedBackP.y = floor(backP.y), BACK_STORE_WIDTH, BACK_STORE_HEIGHT, controlGState, (float)0.0, (float)0.0, NSCompositeCopy ); PSgrestore(); [backBitmap unlockFocus]; /* Draw piece at new location. */ [theSquare setRow: dr]; newLocation.origin = backP; newLocation.size = NSMakeSize( PIECE_WIDTH_3D, PIECE_HEIGHT_3D ); [theSquare setLocation: newLocation]; [theSquare drawInteriorWithFrame: [self frame] inView: self]; [[self window] flushWindow]; } [theSquare setMoving: NO]; [self unlockFocus]; return; } - (int) pieceAt: (int)row : (int)col { if( row >= 0 && col >= 0 ) { Square3D *theSquare = square[row][col]; return [theSquare pieceType]; } return (int)NO_PIECE; } - (int) colorAt: (int)row : (int)col { if( row >= 0 && col >= 0 ) { Square3D *theSquare = square[row][col]; return [theSquare colorVal]; } return (int)NEUTRAL; } - (void) highlightSquareAt: (int)row : (int)col { NSPoint p1, p2, p3, p4; int idx; squareBounds( row, col, &p1, &p2, &p3, &p4 ); [self lockFocus]; PSgsave(); PSsetlinewidth( (float)3.0 ); PSmoveto( p1.x, p1.y ); PSlineto( p2.x, p2.y ); PSlineto( p4.x, p4.y ); PSlineto( p3.x, p3.y ); PSlineto( p1.x, p1.y ); PSclosepath(); /* flash 2 times */ for( idx = 1; idx <= 3; idx++ ) { float color = NSWhite; // float color = NSWhite - [self colorAt: row : col]; // ?? PSsetgray( color ); PSgsave(); PSstroke(); PSgrestore(); if( [self pieceAt: row : col] || (row > 0 && [self pieceAt: row-1 : col]) ) [self drawRows: row from: col]; [[self window] flushWindow]; if( ! [square[row][col] isMoving] ) sleep_microsecs( (unsigned)15000 ); } PSgrestore(); [self unhighlightSquareAt: row : col]; [self unlockFocus]; return; } - (void) unhighlightSquareAt: (int)row : (int)col { NSPoint p1, p2, p3, p4, to; NSRect backR; squareBounds( row, col, &p1, &p2, &p3, &p4 ); p1.x = p1.x - 3; p1.y = p1.y - 3; p2.x = p2.x - 3; p2.y = p2.y + 3; p3.x = p3.x + 3; p3.y = p3.y - 3; p4.x = p4.x + 3; p4.y = p4.y + 3; to.x = MIN( p1.x, p2.x ); to.y = p1.y; backR.origin = to; backR.size.width = MAX( p3.x, p4.x ) - to.x; backR.size.height = p2.y - p1.y; [self lockFocus]; PSgsave(); PSsetlinewidth( (float)3.0 ); PSsetgray( NSWhite ); PSnewpath(); PSmoveto( p1.x, p1.y ); PSlineto( p2.x, p2.y ); PSlineto( p4.x, p4.y ); PSlineto( p3.x, p3.y ); PSlineto( p1.x, p1.y ); PSclosepath(); PSclip(); [_background compositeToPoint:to fromRect:backR operation:NSCompositeCopy]; PSgrestore(); if( [self pieceAt: row : col] || (row > 0 && [self pieceAt: row-1 : col]) ) [self drawRows: row from: col]; [[self window] flushWindow]; [self unlockFocus]; return; } - (void) flashSquareAt: (int)row : (int)col { NSPoint p1, p2, p3, p4; squareBounds( row, col, &p1, &p2, &p3, &p4 ); [self lockFocus]; PSgsave(); PSsetlinewidth( (float)3.0 ); PSsetgray( NSWhite ); PSnewpath(); PSmoveto( p1.x, p1.y ); PSlineto( p2.x, p2.y ); PSlineto( p4.x, p4.y ); PSlineto( p3.x, p3.y ); PSlineto( p1.x, p1.y ); PSclosepath(); PSstroke(); PSgrestore(); if( [self pieceAt: row : col] || (row > 0 && [self pieceAt: row-1 : col]) ) [self drawRows: row from: col]; [self unlockFocus]; return; } - (void) drawRows: (int)row from: (int)col { while( row >= 0 ) { Square3D *theSquare = square[row][col]; if( [self pieceAt: row : col] && ! [theSquare isMoving] ) [theSquare drawInteriorWithFrame: [self frame] inView: self]; row--; } return; } - (void) print: (id)sender { NSPrintInfo *pi = [NSPrintInfo sharedPrintInfo]; NSSize ps = [pi paperSize]; NSSize fs = [self frame].size; float hm = (ps.width - fs.width) / 2.0; float vm = (ps.height - fs.height) / 2.0; [pi setLeftMargin: hm]; [pi setRightMargin: hm]; [pi setTopMargin: vm]; [pi setBottomMargin: vm]; [self lockFocus]; printImage = [[NSBitmapImageRep alloc] initWithFocusedViewRect: [self bounds]]; [self unlockFocus]; [super print: sender]; [printImage release]; printImage = nil; return; } - (void) drawRect: (NSRect)f { if( ! printImage ) { int r, c; NSPoint p = NSZeroPoint; PSgsave(); [_background compositeToPoint: p operation: NSCompositeCopy]; for( r = 7; r >= 0; r-- ) { for( c = 7; c >= 0; c-- ) { Square3D *theSquare = square[r][c]; [theSquare drawWithFrame: f inView: self]; } } PSgrestore(); } else { [printImage draw]; } return; } - (void) mouseDown: (NSEvent *)event { NSException *exception = nil; if ( [NSApp bothsides] ) { NSBeep(); } else if( [NSApp finished] ) { [NSApp finishedAlert]; } else if ([self isEnabled]) { NSPoint pickedP, backP, roundedBackP; Square3D *theSquare; int t, clr; NSRect oldLocation; float x, y; int controlGState; int r2, c2; int r = -1, c = -1; int hi_r = -1, hi_c = -1; pickedP = [event locationInWindow]; pickedP = [self convertPoint: pickedP fromView: nil]; backP = pickedP; convert_point( &pickedP, &r, &c ); if( r == -1 || c == -1 ) return; theSquare = square[r][c]; t = [theSquare pieceType]; clr = [theSquare colorVal]; oldLocation = [theSquare location]; [self lockFocus]; PSgsave(); if( t ) { [theSquare setPieceType: 0 color: 0]; [self drawRect: [self frame]]; [self flashSquareAt: r : c]; hi_r = r; hi_c = c; /* Save background */ squareOrigin( r, c, &x, &y ); backP.x = x; backP.y = y; controlGState = [self gState]; [backBitmap lockFocus]; PSgsave(); PScomposite( roundedBackP.x = floor(backP.x), roundedBackP.y = floor(backP.y), BACK_STORE_WIDTH, BACK_STORE_HEIGHT, controlGState, (float)0.0, (float)0.0, NSCompositeCopy ); PSgrestore(); [backBitmap unlockFocus]; [theSquare setPieceType: t color: clr]; [theSquare drawInteriorWithFrame: [self frame] inView: self]; [theSquare setMoving: YES]; [[self window] flushWindow]; pickedP.x = (float) floor_value( (double)(pickedP.x - x) ); pickedP.y = (float) floor_value( (double)(pickedP.y - y) ); } r2 = 0; c2 = 0; [[self window] setAcceptsMouseMovedEvents: YES]; NS_DURING while( [event type] != NSLeftMouseUp ) { NSPoint p, centerP; NSRect newLocation; unsigned int mask = (NSLeftMouseUpMask | NSLeftMouseDraggedMask); event = [[self window] nextEventMatchingMask: mask]; if( ! t ) continue; p = [event locationInWindow]; p = [self convertPoint: p fromView: nil]; /* Restore old background */ [self lockFocus]; [backBitmap compositeToPoint: roundedBackP operation: NSCompositeCopy]; [self unlockFocus]; backP.x = p.x - pickedP.x; backP.y = p.y - pickedP.y; /* Unhighlight square */ centerP.y = backP.y + PIECE_HEIGHT_3D / 4.0; centerP.x = backP.x + PIECE_WIDTH_3D / 2.0; convert_point( ¢erP, &r2, &c2 ); if( r2 != hi_r || c2 != hi_c ) { if( hi_r != -1 && hi_c != -1 ) [self unhighlightSquareAt: hi_r : hi_c]; hi_r = r2; hi_c = c2; [self flashSquareAt: r2 : c2]; } /* Save new background */ [backBitmap lockFocus]; PSgsave(); PScomposite( roundedBackP.x = floor(backP.x), roundedBackP.y = floor(backP.y), BACK_STORE_WIDTH, BACK_STORE_HEIGHT, controlGState, (float)0.0, (float)0.0, NSCompositeCopy ); PSgrestore(); [backBitmap unlockFocus]; /* Draw piece at new location. */ [theSquare setRow: r2]; newLocation.origin.x = p.x - pickedP.x; newLocation.origin.y = p.y - pickedP.y; newLocation.size.width = PIECE_WIDTH_3D; newLocation.size.height = PIECE_HEIGHT_3D; [theSquare setLocation: newLocation]; [theSquare drawInteriorWithFrame: [self frame] inView: self]; [self setNeedsDisplay:YES]; // THIS WAS A PROBLEM! [[self window] flushWindow]; } NS_HANDLER exception = localException; NS_ENDHANDLER if( t ) { [theSquare setMoving: NO]; if( r2 != r || c2 != c ) { if( ! [NSApp makeMoveFrom: r : c to: r2 : c2] ) { [theSquare setLocation: oldLocation]; [theSquare setPieceType: t color: clr]; [theSquare setRow: r]; } } else { [theSquare setLocation: oldLocation]; [theSquare setPieceType: t color: clr]; [theSquare setRow: r]; } [self display]; [[self window] flushWindow]; } PSgrestore(); [self unlockFocus]; } if( exception ) [exception raise]; return; } @end // Local Variables: // tab-width: 8 // End: