//#import <AppKit/AppKit.h>
#import <AppKit/NSImage.h>
#import <AppKit/NSPrintInfo.h>
#import <AppKit/NSBitmapImageRep.h>
#import <AppKit/NSWindow.h>
#import <AppKit/NSEvent.h>
#import <AppKit/NSGraphics.h> // NSBeep
#import <Foundation/NSBundle.h>
#import <Foundation/NSGeometry.h> // NSMakeSize, NSZeroPoint
#import <Foundation/NSUtilities.h> // MIN, MAX, ABS
#import <Foundation/NSException.h>
#include <math.h>
// 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 [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: