1#import "NickSpaceView.h" 2#import <AppKit/AppKit.h> 3#if !defined(__FreeBSD__) && !defined(__DragonFly__) 4#import <values.h> 5#endif 6#import <time.h> 7#import <limits.h> 8 9/* 10 * the file originally used MAXINT and MAXLONG 11 * ANSI c89 defines LONG_MAX and INT_MAX 12 */ 13#ifndef INT_MAX 14#define INT_MAX MAXINT 15#endif 16#ifndef LONG_MAX 17#define LONG_MAX MAXLONG 18#endif 19 20 21#define COLORWIDTH 2 22#define ERASEWIDTH 5 23 24/** Draw a line segment */ 25void doSeg(float x1, float y1, float x2, float y2) 26{ 27 PSmoveto(x1,y1); 28 PSlineto(x2,y2); 29} 30 31@interface NSColor (GetColorsFromString) 32+ (NSColor *)colorFromStringRepresentation:(NSString *)colorString; 33- (NSString *)stringRepresentation; 34@end 35 36@implementation NSColor (GetColorsFromString) 37+ (NSColor *)colorFromStringRepresentation:(NSString *)colorString 38{ 39 float r, g, b, a; 40 NSArray *array = [colorString componentsSeparatedByString:@" "]; 41 if(!array) return nil; 42 if([array count] < 3) { 43 NSLog(@"%@: + colorFromStringRepresentation", [[self class] description]); 44 NSLog(@"%@: String must contain red, green, and blue components", [[self class] description]); 45 return nil; 46 } 47 r = [[array objectAtIndex:0] floatValue]; 48 g = [[array objectAtIndex:1] floatValue]; 49 b = [[array objectAtIndex:2] floatValue]; 50 a = [array count] > 3 ? [[array objectAtIndex:3] floatValue] : 1.0; 51 return [NSColor colorWithCalibratedRed:r green:g blue:b alpha:a]; 52} 53 54- (NSString *)stringRepresentation 55{ 56 float r, g, b, a; 57 [[self colorUsingColorSpaceName:NSCalibratedRGBColorSpace] getRed:&r green:&g blue:&b alpha:&a]; 58 return [NSString stringWithFormat:@"%f %f %f %f",r,g,b,a]; 59} 60@end 61 62@interface NSString (ColorValue) 63- (NSColor *) colorValue; 64@end 65 66@implementation NSString (ColorValue) 67- (NSColor *) colorValue 68{ 69 return [NSColor colorFromStringRepresentation: self]; 70} 71@end 72 73@implementation NickSpaceView 74 75- (void) calcNext 76{ 77 int i, j; 78 BOOL tryingLeft, tryingRight; // with respect to the CURRENT ORIENTATION!! 79 80 81 for (i=0;i<trailCount;i++) { 82 if ((!trails[i].dead && trails[i].maxLength > trails[i].currentLength) || 83 trails[i].currentLength<=1) 84 continue; 85 86 if (trails[i].tailOrient == UP || trails[i].tailOrient == DOWN) 87 VERTEDGE(trails[i].tailEdge.row,trails[i].tailEdge.col) = 0; 88 else 89 HOREDGE(trails[i].tailEdge.row,trails[i].tailEdge.col) = 0; 90 91 trails[i].currentLength--; 92 93 /* update tail edges */ 94 switch (trails[i].tailOrient) { 95 96 case UP: 97 if (VERTEDGE(trails[i].tailEdge.row+1,trails[i].tailEdge.col)) 98 trails[i].tailEdge.row++; 99 else if (HOREDGE(trails[i].tailEdge.row,trails[i].tailEdge.col)) 100 trails[i].tailOrient = LEFT; 101 else if (HOREDGE(trails[i].tailEdge.row,trails[i].tailEdge.col+1)) { 102 trails[i].tailEdge.col++; 103 trails[i].tailOrient = RIGHT; 104 } 105 break; 106 107 case DOWN: 108 if (VERTEDGE(trails[i].tailEdge.row-1,trails[i].tailEdge.col)) 109 trails[i].tailEdge.row--; 110 else if (HOREDGE(trails[i].tailEdge.row-1,trails[i].tailEdge.col)) { 111 trails[i].tailEdge.row--; 112 trails[i].tailOrient = LEFT; 113 } 114 else if (HOREDGE(trails[i].tailEdge.row-1,trails[i].tailEdge.col+1)) { 115 trails[i].tailEdge.row--; 116 trails[i].tailEdge.col++; 117 trails[i].tailOrient = RIGHT; 118 } 119 break; 120 121 case RIGHT: 122 if (HOREDGE(trails[i].tailEdge.row,trails[i].tailEdge.col+1)) 123 trails[i].tailEdge.col++; 124 else if (VERTEDGE(trails[i].tailEdge.row,trails[i].tailEdge.col)) 125 trails[i].tailOrient = DOWN; 126 else if (VERTEDGE(trails[i].tailEdge.row+1,trails[i].tailEdge.col)) { 127 trails[i].tailEdge.row++; 128 trails[i].tailOrient = UP; 129 } 130 break; 131 132 case LEFT: 133 if (HOREDGE(trails[i].tailEdge.row,trails[i].tailEdge.col-1)) 134 trails[i].tailEdge.col--; 135 else if (VERTEDGE(trails[i].tailEdge.row,trails[i].tailEdge.col-1)) { 136 trails[i].tailEdge.col--; 137 trails[i].tailOrient = DOWN; 138 } 139 else if (VERTEDGE(trails[i].tailEdge.row+1,trails[i].tailEdge.col-1)) { 140 trails[i].tailEdge.row++; 141 trails[i].tailEdge.col--; 142 trails[i].tailOrient = UP; 143 } 144 break; 145 } 146 } 147 148 /* update head edges */ 149 for (i=0;i<trailCount;i++) { 150 if (firstTime) { 151 trails[i].headEdge = trails[i].tailEdge; 152 trails[i].tailOrient = trails[i].headOrient; 153 } else { 154 trails[i].dead = NO; 155 switch (trails[i].headOrient) { 156 157 case UP: 158 if (trails[i].headEdge.row < horCount - 1 && 159 !VERTEDGE(trails[i].headEdge.row + 2,trails[i].headEdge.col) && 160 !HOREDGE(trails[i].headEdge.row + 1,trails[i].headEdge.col) && 161 !HOREDGE(trails[i].headEdge.row + 1,trails[i].headEdge.col + 1)) 162 trails[i].headEdge.row++; /* continue UP */ 163 else { 164 tryingLeft = (BOOL)random()%2; 165 for (j=0;j<2;j++) { 166 if (tryingLeft) { 167 if (trails[i].headEdge.col > 0 && 168 !VERTEDGE(trails[i].headEdge.row,trails[i].headEdge.col-1) && 169 !VERTEDGE(trails[i].headEdge.row+1,trails[i].headEdge.col-1) && 170 !HOREDGE(trails[i].headEdge.row,trails[i].headEdge.col-1)) { 171 trails[i].headOrient = LEFT; 172 break; 173 } 174 } else { 175 if (trails[i].headEdge.col < vertCount - 1 && 176 !VERTEDGE(trails[i].headEdge.row,trails[i].headEdge.col+1) && 177 !VERTEDGE(trails[i].headEdge.row+1,trails[i].headEdge.col+1) && 178 !HOREDGE(trails[i].headEdge.row,trails[i].headEdge.col+2)) { 179 trails[i].headEdge.col++; 180 trails[i].headOrient = RIGHT; 181 break; 182 } 183 } 184 if (j==1) 185 trails[i].dead = YES; 186 else 187 tryingLeft = 1 - tryingLeft; 188 } 189 } 190 break; 191 192 case DOWN: 193 if (trails[i].headEdge.row > 1 && 194 !VERTEDGE(trails[i].headEdge.row - 2,trails[i].headEdge.col) && 195 !HOREDGE(trails[i].headEdge.row - 2,trails[i].headEdge.col) && 196 !HOREDGE(trails[i].headEdge.row - 2,trails[i].headEdge.col + 1)) 197 trails[i].headEdge.row--; /* continue DOWN */ 198 else { 199 tryingRight = (BOOL)random()%2; 200 for (j=0;j<2;j++) { 201 if (tryingRight) { 202 if (trails[i].headEdge.col > 0 && 203 !VERTEDGE(trails[i].headEdge.row,trails[i].headEdge.col-1) && 204 !VERTEDGE(trails[i].headEdge.row-1,trails[i].headEdge.col-1) && 205 !HOREDGE(trails[i].headEdge.row-1,trails[i].headEdge.col-1)) { 206 trails[i].headEdge.row--; 207 trails[i].headOrient = LEFT; 208 break; 209 } 210 } else { 211 if (trails[i].headEdge.col < vertCount - 1 && 212 !VERTEDGE(trails[i].headEdge.row,trails[i].headEdge.col+1) && 213 !VERTEDGE(trails[i].headEdge.row-1,trails[i].headEdge.col+1) && 214 !HOREDGE(trails[i].headEdge.row-1,trails[i].headEdge.col+2)) { 215 trails[i].headEdge.col++; 216 trails[i].headEdge.row--; 217 trails[i].headOrient = RIGHT; 218 break; 219 } 220 } 221 if (j==1) 222 trails[i].dead = YES; 223 else 224 tryingRight = 1 - tryingRight; 225 } 226 } 227 break; 228 229 case RIGHT: 230 if (trails[i].headEdge.col < vertCount - 1 && 231 !HOREDGE(trails[i].headEdge.row,trails[i].headEdge.col+2) && 232 !VERTEDGE(trails[i].headEdge.row,trails[i].headEdge.col + 1) && 233 !VERTEDGE(trails[i].headEdge.row + 1,trails[i].headEdge.col + 1)) 234 trails[i].headEdge.col++; // continue RIGHT 235 else { 236 tryingRight = (BOOL)random()%2; 237 for (j=0;j<2;j++) { 238 if (tryingRight) { 239 if (trails[i].headEdge.row > 0 && 240 !HOREDGE(trails[i].headEdge.row-1,trails[i].headEdge.col) && 241 !HOREDGE(trails[i].headEdge.row-1,trails[i].headEdge.col+1) && 242 !VERTEDGE(trails[i].headEdge.row-1,trails[i].headEdge.col)) { 243 trails[i].headOrient = DOWN; 244 break; 245 } 246 } else { 247 if (trails[i].headEdge.row < horCount - 1 && 248 !HOREDGE(trails[i].headEdge.row+1,trails[i].headEdge.col) && 249 !HOREDGE(trails[i].headEdge.row+1,trails[i].headEdge.col+1) && 250 !VERTEDGE(trails[i].headEdge.row+2,trails[i].headEdge.col)) { 251 trails[i].headEdge.row++; 252 trails[i].headOrient = UP; 253 break; 254 } 255 } 256 if (j==1) 257 trails[i].dead = YES; 258 else 259 tryingRight = 1 - tryingRight; 260 } 261 } 262 break; 263 264 case LEFT: 265 if (trails[i].headEdge.col > 1 && 266 !HOREDGE(trails[i].headEdge.row,trails[i].headEdge.col-2) && 267 !VERTEDGE(trails[i].headEdge.row+1,trails[i].headEdge.col-2) && 268 !VERTEDGE(trails[i].headEdge.row,trails[i].headEdge.col-2)) 269 trails[i].headEdge.col--; // continue LEFT 270 else { 271 tryingLeft = (BOOL)random()%2; 272 for (j=0;j<2;j++) { 273 if (tryingLeft) { 274 if (trails[i].headEdge.row > 0 && 275 !HOREDGE(trails[i].headEdge.row-1,trails[i].headEdge.col) && 276 !HOREDGE(trails[i].headEdge.row-1,trails[i].headEdge.col-1) && 277 !VERTEDGE(trails[i].headEdge.row-1,trails[i].headEdge.col-1)) { 278 trails[i].headEdge.col--; 279 trails[i].headOrient = DOWN; 280 break; 281 } 282 } else { 283 if (trails[i].headEdge.row < horCount - 1 && 284 !HOREDGE(trails[i].headEdge.row+1,trails[i].headEdge.col) && 285 !HOREDGE(trails[i].headEdge.row+1,trails[i].headEdge.col-1) && 286 !VERTEDGE(trails[i].headEdge.row+2,trails[i].headEdge.col-1)) { 287 trails[i].headEdge.row++; 288 trails[i].headEdge.col--; 289 trails[i].headOrient = UP; 290 break; 291 } 292 } 293 if (j==1) 294 trails[i].dead = YES; 295 else 296 tryingLeft = 1 - tryingLeft; 297 } 298 } 299 } 300 if (!trails[i].dead) 301 trails[i].currentLength++; 302 } 303 if (!trails[i].dead) { 304 if (trails[i].headOrient == UP || trails[i].headOrient == DOWN) 305 VERTEDGE(trails[i].headEdge.row,trails[i].headEdge.col) = 1; 306 else 307 HOREDGE(trails[i].headEdge.row,trails[i].headEdge.col) = 1; 308 } 309 310 } 311 312 firstTime = 0; 313} 314 315-(void)oneStep 316{ 317 int i, level, currCol; 318 NSRect bounds = [self bounds]; 319 320 321 /* if window level changed, reinitialize and decide whether to buffer or not */ 322 level = [[self window] level]; 323 if (level != lastLevel) 324 { 325 lastLevel = level; 326 if (level < NSNormalWindowLevel) 327 { 328 [self newSize:YES]; 329 image = [[NSImage alloc] initWithSize: bounds.size]; 330 [image lockFocus]; 331 PSsetgray(0); 332 NSRectFill(bounds); 333 [image unlockFocus]; 334 } 335 else 336 { 337 [self newSize:YES]; 338 if (image) 339 { 340 [image free]; 341 image = nil; 342 } 343 } 344 } 345 346 /* erase tail edges, as needed (calc'ed last time through */ 347 PSsetgray(0); 348 PSsetlinewidth(ERASEWIDTH); 349 for (i=0;i<trailCount;i++) 350 { 351 if ((!trails[i].dead && trails[i].maxLength > trails[i].currentLength) || 352 trails[i].currentLength<=1) 353 continue; 354 355 if (trails[i].tailOrient == UP || trails[i].tailOrient == DOWN) 356 { 357 doSeg((float)((trails[i].tailEdge.col + 1) * spacing), 358 (float)(trails[i].tailEdge.row * spacing), 359 (float)((trails[i].tailEdge.col + 1) * spacing), 360 (float)((trails[i].tailEdge.row + 1) * spacing)); 361 } 362 else 363 { 364 doSeg((float)(trails[i].tailEdge.col * spacing), 365 (float)((trails[i].tailEdge.row + 1) * spacing), 366 (float)((trails[i].tailEdge.col + 1) * spacing), 367 (float)((trails[i].tailEdge.row + 1) * spacing)); 368 } 369 } 370 PSstroke(); 371 372 if (image) 373 { 374 [image lockFocus]; 375 /* erase tail edges, as needed (calc'ed last time through) */ 376 PSsetgray(0); 377 PSsetlinewidth(ERASEWIDTH); 378 for (i=0;i<trailCount;i++) 379 { 380 if ((!trails[i].dead && trails[i].maxLength > trails[i].currentLength) || 381 trails[i].currentLength<=1) 382 continue; 383 384 if (trails[i].tailOrient == UP || trails[i].tailOrient == DOWN) 385 { 386 doSeg((float)((trails[i].tailEdge.col + 1) * spacing), 387 (float)(trails[i].tailEdge.row * spacing), 388 (float)((trails[i].tailEdge.col + 1) * spacing), 389 (float)((trails[i].tailEdge.row + 1) * spacing)); 390 } 391 else 392 { 393 doSeg((float)(trails[i].tailEdge.col * spacing), 394 (float)((trails[i].tailEdge.row + 1) * spacing), 395 (float)((trails[i].tailEdge.col + 1) * spacing), 396 (float)((trails[i].tailEdge.row + 1) * spacing)); 397 } 398 } 399 PSstroke(); 400 [image unlockFocus]; 401 } 402 403 404 [self calcNext]; 405 406 /* draw head edges, as needed */ 407 currCol = 0; // historical accident--careful not to confuse with currColor 408 [[[colors objectAtIndex: currCol] colorValue] set]; 409 for(i = 0;i<trailCount; i++) 410 { 411 PSsetlinewidth(COLORWIDTH); 412 if (i==(trailCount*(currCol+1))/numColors) 413 { 414 currCol++; 415 PSstroke(); 416 [[[colors objectAtIndex: currCol] colorValue] set]; 417 } 418 if (trails[i].dead) 419 continue; 420 if (trails[i].headOrient == UP || trails[i].headOrient == DOWN) 421 { 422 PSmoveto((float)((trails[i].headEdge.col + 1) * spacing), 423 (float)(trails[i].headEdge.row * spacing)); 424 PSlineto((float)((trails[i].headEdge.col + 1) * spacing), 425 (float)((trails[i].headEdge.row + 1) * spacing)); 426 } 427 else 428 { 429 PSmoveto((float)(trails[i].headEdge.col * spacing), 430 (float)((trails[i].headEdge.row + 1) * spacing)); 431 PSlineto((float)((trails[i].headEdge.col + 1) * spacing), 432 (float)((trails[i].headEdge.row + 1) * spacing)); 433 } 434 } 435 PSstroke(); 436 437 if (image) 438 { 439 [image lockFocus]; 440 currCol = 0; 441 [[[colors objectAtIndex: currCol] colorValue] set]; 442 PSsetlinewidth(COLORWIDTH); 443 for(i = 0;i<trailCount; i++) 444 { 445 if (i==(trailCount*(currCol+1))/3) 446 { 447 currCol++; 448 PSstroke(); 449 [[[colors objectAtIndex: currCol] colorValue] set]; 450 } 451 452 if (trails[i].dead) 453 continue; 454 455 if (trails[i].headOrient == UP || trails[i].headOrient == DOWN) 456 { 457 PSmoveto((float)((trails[i].headEdge.col + 1) * spacing), 458 (float)(trails[i].headEdge.row * spacing)); 459 PSlineto((float)((trails[i].headEdge.col + 1) * spacing), 460 (float)((trails[i].headEdge.row + 1) * spacing)); 461 } 462 else 463 { 464 PSmoveto((float)(trails[i].headEdge.col * spacing), 465 (float)((trails[i].headEdge.row + 1) * spacing)); 466 PSlineto((float)((trails[i].headEdge.col + 1) * spacing), 467 (float)((trails[i].headEdge.row + 1) * spacing)); 468 } 469 } 470 PSstroke(); 471 [image unlockFocus]; 472 } 473} 474 475-(id)initWithFrame:(NSRect)frameRect 476{ 477 NSString *defaults = [NSString stringWithString: @"{\"spacing\" = \"\"; \"tcRatio\" = \"\"; \"tlRatio\" = \"\";}"]; 478 NSDictionary *defDict = [defaults propertyList]; 479 NSUserDefaults *userDef = [NSUserDefaults standardUserDefaults]; 480 int i; 481 482 srandom(time(0)); 483 484 if((self = [super initWithFrame: frameRect]) != nil) 485 { 486 487 /* these are preserved from bezierView--I don't know if they're doing any good */ 488 [self allocateGState]; // For faster lock/unlockFocus 489 // [self setClipping:NO]; // even faster... 490 491 /* Miscellaneous initializations */ 492 image = nil; 493 lastLevel = 0; 494 495 if(![NSBundle loadNibNamed: @"NickSpace" owner:self]) 496 { 497 NSLog(@"Failed to load inspector"); 498 } 499 500 /* Set target/action for buttons in the matrix */ 501 [[addRemoveButtons cellAtRow:0 column:0] setTarget:self]; 502 [[addRemoveButtons cellAtRow:0 column:0] setAction:@selector(addColor:)]; 503 [[addRemoveButtons cellAtRow:1 column:0] setTarget:self]; 504 [[addRemoveButtons cellAtRow:1 column:0] setAction:@selector(removeColor:)]; 505 506 /* Check the first default; if it hasn't been written before, get all 507 * parameters from the controls; else, read all defaults and set the controls 508 */ 509 [userDef registerDefaults: defDict]; 510 if ([[userDef stringForKey: @"spacing"] length] == 0) 511 { 512 // Some empirically determined initial settings: 513 spacing = 8; 514 tcRatio = .6; 515 tlRatio = 1.0; 516 517 numColors = 10; 518 currColor = 0; 519 colors = [[NSMutableArray alloc] init]; 520 521 [userDef setFloat: spacing forKey: @"spacing"]; 522 [userDef setFloat: tcRatio forKey: @"tcRatio"]; 523 [userDef setFloat: tlRatio forKey: @"tlRatio"]; 524 [userDef setInteger: numColors forKey: @"numColors"]; 525 526 // randomize an initial set of colors: 527 for (i=0;i<numColors;i++) 528 { 529 float red = (float)random()/(float)LONG_MAX; 530 float green = (float)random()/(float)LONG_MAX; 531 float blue = (float)random()/(float)LONG_MAX; 532 NSColor *color = [NSColor colorWithCalibratedRed: red 533 green: green 534 blue: blue 535 alpha: 1.0]; 536 [colors addObject: [color stringRepresentation]]; 537 } 538 539 [userDef setObject: colors forKey: @"colors"]; 540 } 541 else 542 { 543 spacing = [userDef floatForKey: @"spacing"]; 544 tcRatio = [userDef floatForKey: @"tcRatio"]; 545 tlRatio = [userDef floatForKey: @"tlRatio"]; 546 numColors = [userDef integerForKey: @"numColors"]; 547 colors = [[NSMutableArray alloc] initWithArray: AUTORELEASE([userDef arrayForKey: @"colors"])]; 548 currColor = 0; 549 } 550 551 [spaceControl setIntValue:spacing]; 552 [countControl setFloatValue:tcRatio]; 553 [lengthControl setFloatValue:tlRatio]; 554 [colorWell setColor: [[colors objectAtIndex: currColor] colorValue]]; 555 [numColorsField setStringValue: [NSString stringWithFormat: @"%d/%d", currColor+1, numColors]]; 556 557 [self newSize:NO]; 558 } 559 return self; 560} 561 562- (void) setFrame: (NSRect)frame 563{ 564 [super setFrame: frame]; 565 [self newSize:YES]; 566} 567 568/* 569- drawSelf:(const NXRect *)rects :(int)rectCount 570{ 571 int i; 572 if (!rects || !rectCount) return self; 573 574 for (i=0;i<rectCount;i++) 575 [image composite:NX_COPY fromRect:&(rects[i]) toPoint:&(rects[i].origin)]; 576 577 return self; 578} 579*/ 580 581- (void) drawRect:(NSRect)rects 582{ 583 PSsetlinewidth(0); 584 PSsetgray(0); 585 NSRectFill(rects); 586} 587 588/* next two methods do initializations */ 589- newSize:(BOOL)freeOld; 590{ 591 NSRect bounds = [self bounds]; 592 593 if (freeOld) { 594 free(horEdges); 595 free(vertEdges); 596 free(trails); 597 } 598 599 horCount = (int)((bounds.size.height - 5.0) / spacing); 600 vertCount = (int)((bounds.size.width - 5.0) / spacing); 601 602 horEdges = (char *)malloc(HORSIZE); 603 vertEdges = (char *)malloc(VERTSIZE); 604 bzero(horEdges,HORSIZE); 605 bzero(vertEdges,VERTSIZE); 606 607 trailCount = (int)((vertCount + horCount) * tcRatio); 608 trailCount = trailCount ? trailCount : 1; 609 trails = (trail *)malloc(sizeof(trail)*trailCount); 610 611 maxTrailLen = (vertCount + horCount) * 4 * tlRatio; 612 minTrailLen = maxTrailLen/40; 613 maxTrailLen = maxTrailLen < 2 ? 2 : maxTrailLen; 614 minTrailLen = minTrailLen < 2 ? 2 : minTrailLen; 615 616 firstTime = YES; 617 618 [self startTrails]; 619 620 if ([self window]) { 621 [self lockFocus]; 622 PSsetgray(0); 623 NSRectFill(bounds); 624 [self unlockFocus]; 625 } 626 627 if (image){ 628 [image lockFocus]; 629 PSsetgray(0); 630 NSRectFill(bounds); 631 [image unlockFocus]; 632 } 633 634 return self; 635} 636 637- startTrails 638{ 639 int i,j; 640 BOOL dup; 641 int initPos[trailCount]; 642 643 644 /* This could potentially take arbitrarily long--should tighten up; 645 */ 646 for (i=0;i<trailCount;) { 647 dup = NO; 648 if (trailCount < vertCount + horCount) { 649 initPos[i] = (random() % (vertCount + horCount))*2; 650 for (j=0;j<i;j++) { 651 if (initPos[j] == initPos[i]) { 652 dup = YES; 653 break; 654 } 655 } 656 } else 657 initPos[i] = i * 2; 658 if (!dup) 659 i++; 660 } 661 662 for (i=0;i<trailCount;i++) { 663 trails[i].currentLength = 1; 664 if (tlRatio == 1.0) 665 trails[i].maxLength = INT_MAX; 666 else 667 trails[i].maxLength = (random() % ((maxTrailLen - minTrailLen) + 1) + minTrailLen); 668 trails[i].dead = NO; 669 if (initPos[i] < vertCount) { 670 trails[i].tailEdge.row = 0; 671 trails[i].tailEdge.col = initPos[i]; 672 trails[i].headOrient = UP; 673 continue; 674 } 675 if (vertCount <= initPos[i] && initPos[i] < vertCount + horCount) { 676 trails[i].tailEdge.row = initPos[i] - vertCount; 677 trails[i].tailEdge.col = vertCount; 678 trails[i].headOrient = LEFT; 679 continue; 680 } 681 if (vertCount + horCount <= initPos[i] && initPos[i] < (2*vertCount + horCount)) { 682 trails[i].tailEdge.row = horCount; 683 trails[i].tailEdge.col = initPos[i] - (vertCount + horCount); 684 trails[i].headOrient = DOWN; 685 continue; 686 } 687 trails[i].tailEdge.row = initPos[i] - (2*vertCount + horCount); 688 trails[i].tailEdge.col = 0; 689 trails[i].headOrient = RIGHT; 690 } 691 692 return self; 693} 694 695- (id)inspector:(id)sender 696{ 697 return inspector; 698} 699 700- (id)updateCurrColor:(id)sender 701{ 702 NSColor *color = [sender color]; 703 [colors replaceObjectAtIndex: currColor withObject: [color stringRepresentation]]; 704 [[NSUserDefaults standardUserDefaults] setObject: colors forKey: @"colors"]; 705 return self; 706} 707 708- (id)scrollColor:(id)sender 709{ 710 if ([sender selectedRow]==1) 711 currColor = currColor==0 ? numColors-1 : currColor-1; 712 else 713 currColor = (currColor + 1)%numColors; 714 715 [colorWell setColor: [[colors objectAtIndex: currColor] colorValue]]; 716 [numColorsField setStringValue: [NSString stringWithFormat: @"%d/%d", currColor+1, numColors]]; 717 return self; 718} 719 720- (id)addColor:(id)sender 721{ 722 float 723 red = (float)random()/(float)LONG_MAX, 724 green = (float)random()/(float)LONG_MAX, 725 blue = (float)random()/(float)LONG_MAX; 726 NSUserDefaults *userDefs = [NSUserDefaults standardUserDefaults]; 727 NSColor *color = [NSColor colorWithCalibratedRed: red 728 green: green 729 blue: blue 730 alpha: 1.0]; 731 732 numColors++; 733 currColor = numColors-1; 734 735 [colors addObject: [color stringRepresentation]]; 736 737 [userDefs setObject: colors forKey: @"colors"]; 738 [userDefs setInteger: numColors forKey: @"numColors"]; 739 [colorWell setColor: color]; 740 741 [numColorsField setStringValue: [NSString stringWithFormat: @"%d/%d", currColor+1, numColors]]; 742 743 return self; 744} 745 746- (id)removeColor: (id)sender 747{ 748 NSUserDefaults *userDefs = [NSUserDefaults standardUserDefaults]; 749 NSColor *cColor = nil; 750 751 if (numColors==1) 752 return self; 753 754 [colors removeLastObject]; 755 [userDefs setObject: colors forKey: @"colors"]; 756 757 numColors--; 758 [userDefs setInteger: numColors forKey: @"numColors"]; 759 760 currColor %= numColors; 761 cColor = [[colors objectAtIndex: currColor] colorValue]; 762 [colorWell setColor: cColor]; 763 [numColorsField setStringValue: [NSString stringWithFormat: @"%d/%d", currColor+1, numColors]]; 764 765 return self; 766} 767 768 769- getSpacingFrom:sender; 770{ 771 NSUserDefaults *userDefs = [NSUserDefaults standardUserDefaults]; 772 spacing = [sender intValue]; 773 [userDefs setInteger: spacing forKey: @"spacing"]; 774 [self newSize:YES]; 775 return self; 776} 777 778- getNumberFrom:sender 779{ 780 NSUserDefaults *userDefs = [NSUserDefaults standardUserDefaults]; 781 tcRatio = [sender floatValue]; 782 [userDefs setFloat: tcRatio forKey: @"tcRatio"]; 783 [self newSize:YES]; 784 return self; 785} 786 787- getMaxLenFrom:sender 788{ 789 NSUserDefaults *userDefs = [NSUserDefaults standardUserDefaults]; 790 tlRatio = [sender floatValue]; 791 [userDefs setFloat: tlRatio forKey: @"tlRatio"]; 792 [self newSize:YES]; 793 return self; 794} 795 796@end 797