//#import #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; PSgsave(); PSrotate( (float)10.0 ); 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]]; } PSgrestore(); return; } - (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]; /* [[self window] setAcceptsMouseMovedEvents: YES]; NS_DURING while( [event type] != NSLeftMouseUp ) { unsigned int mask = (NSLeftMouseUpMask | NSLeftMouseDraggedMask); event = [[self window] nextEventMatchingMask: mask]; } NS_HANDLER exception = localException; NS_ENDHANDLER */ } 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: