1/* 2 Project: Graphos 3 GRBezierPath.m 4 5 Copyright (C) 2000-2018 GNUstep Application Project 6 7 Author: Enrico Sersale (original GDraw implementation) 8 Author: Ing. Riccardo Mottola 9 10 This application is free software; you can redistribute it and/or 11 modify it under the terms of the GNU General Public 12 License as published by the Free Software Foundation; either 13 version 2 of the License, or (at your option) any later version. 14 15 This application is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 Library General Public License for more details. 19 20 You should have received a copy of the GNU General Public 21 License along with this library; if not, write to the Free 22 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 23 */ 24 25#import "GRBezierPath.h" 26#import "GRDocView.h" 27#import "GRFunctions.h" 28#import "GRBezierPathEditor.h" 29 30static double k = 0.025; 31 32@implementation GRBezierPath 33 34- (GRObjectEditor *)allocEditor 35{ 36 return [[GRBezierPathEditor alloc] initEditor:self]; 37} 38 39/** initializes by using the properties array as defaults */ 40- (id)initInView:(GRDocView *)aView 41 zoomFactor:(CGFloat)zf 42 withProperties:(NSDictionary *)properties 43{ 44 self = [super initInView:aView zoomFactor:zf withProperties:properties]; 45 if(self) 46 { 47 controlPoints = [[NSMutableArray alloc] initWithCapacity: 1]; 48 } 49 50 return self; 51} 52 53- (id)initFromData:(NSDictionary *)description 54 inView:(GRDocView *)aView 55 zoomFactor:(CGFloat)zf 56{ 57 self = [super initFromData:description inView:aView zoomFactor:zf]; 58 if(self != nil) 59 { 60 NSArray *psops, *linearr; 61 NSString *str; 62 NSPoint p, pp[3]; 63 GRBezierControlPoint *prevcp; 64 double distx, disty; 65 NSUInteger i, count; 66 NSArray *points; 67 BOOL symm; 68 69 psops = nil; 70 linearr = nil; 71 72 controlPoints = [[NSMutableArray alloc] initWithCapacity: 1]; 73 points = [description objectForKey: @"points"]; 74 for (i = 0; i < [points count]; i++) 75 { 76 GRBezierHandle h; 77 GRBezierControlPoint *cp; 78 79 linearr = [[points objectAtIndex: i] componentsSeparatedByString: @" "]; 80 h.firstHandle.x = [[linearr objectAtIndex: 0] floatValue]; 81 h.firstHandle.y = [[linearr objectAtIndex: 1] floatValue]; 82 h.firstHandleRect = NSMakeRect(h.firstHandle.x-2, h.firstHandle.y-2, 4, 4); 83 h.center.x = [[linearr objectAtIndex: 2] floatValue]; 84 h.center.y = [[linearr objectAtIndex: 3] floatValue]; 85 h.centerRect = NSMakeRect(h.center.x-3, h.center.y-3, 6, 6); 86 h.secondHandle.x = [[linearr objectAtIndex: 4] floatValue]; 87 h.secondHandle.y = [[linearr objectAtIndex: 5] floatValue]; 88 h.secondHandleRect = NSMakeRect(h.secondHandle.x-2, h.secondHandle.y-2, 4, 4); 89 symm = (BOOL)[[linearr objectAtIndex: 6] intValue]; 90 91 cp = [[GRBezierControlPoint alloc] initAtPoint:h.center forPath:self zoomFactor:zmFactor]; 92 [cp setBezierHandle:h]; 93 [cp setSymmetricalHandles:symm]; 94 [controlPoints addObject:cp]; 95 [cp release]; 96 } 97 [self confirmNewCurve]; 98 99 psops = [description objectForKey: @"psdata"]; 100 for(i = 0; i < [psops count]; i++) 101 { 102 linearr = [[psops objectAtIndex: i] componentsSeparatedByString: @" "]; 103 count = [linearr count]; 104 str = [linearr objectAtIndex: count -1]; 105 106 if([str isEqualToString: @"moveto"]) 107 { 108 pp[0].x = [[linearr objectAtIndex: 0] floatValue]; 109 pp[0].y = [[linearr objectAtIndex: 1] floatValue]; 110 [self addControlAtPoint: pp[0]]; 111 } 112 113 if([str isEqualToString: @"lineto"]) 114 { 115 pp[0].x = [[linearr objectAtIndex: 0] floatValue]; 116 pp[0].y = [[linearr objectAtIndex: 1] floatValue]; 117 [self addLineToPoint: pp[0]]; 118 } 119 120 if([str isEqualToString: @"curveto"]) 121 { 122 pp[0].x = [[linearr objectAtIndex: 0] floatValue]; 123 pp[0].y = [[linearr objectAtIndex: 1] floatValue]; 124 pp[1].x = [[linearr objectAtIndex: 2] floatValue]; 125 pp[1].y = [[linearr objectAtIndex: 3] floatValue]; 126 pp[2].x = [[linearr objectAtIndex: 4] floatValue]; 127 pp[2].y = [[linearr objectAtIndex: 5] floatValue]; 128 129 [self addControlAtPoint: pp[2]]; 130 prevcp = [controlPoints objectAtIndex: [controlPoints count] -2]; 131 [prevcp calculateBezierHandles: pp[0]]; 132 133 distx = grmax(pp[1].x, pp[2].x) - grmin(pp[1].x, pp[2].x); 134 if(pp[1].x > pp[2].x) 135 p.x = pp[2].x - distx; 136 else 137 p.x = pp[2].x + distx; 138 139 disty = grmax(pp[1].y, pp[2].y) - grmin(pp[1].y, pp[2].y); 140 if(pp[1].y > pp[2].y) 141 p.y = pp[2].y - disty; 142 else 143 p.y = pp[2].y + disty; 144 145 [self addCurveWithBezierHandlePosition: p]; 146 [self confirmNewCurve]; 147 } 148 } 149 150 151 } 152 153 return self; 154} 155 156 157- (NSDictionary *)objectDescription 158{ 159 NSMutableDictionary *dict; 160 NSMutableArray *points; 161 NSString *str; 162 NSUInteger i; 163 CGFloat strokeCol[3]; 164 CGFloat fillCol[3]; 165 CGFloat strokeAlpha; 166 CGFloat fillAlpha; 167 168 strokeCol[0] = [strokeColor redComponent]; 169 strokeCol[1] = [strokeColor greenComponent]; 170 strokeCol[2] = [strokeColor blueComponent]; 171 strokeAlpha = [strokeColor alphaComponent]; 172 173 fillCol[0] = [fillColor redComponent]; 174 fillCol[1] = [fillColor greenComponent]; 175 fillCol[2] = [fillColor blueComponent]; 176 fillAlpha = [fillColor alphaComponent]; 177 178 dict = [NSMutableDictionary dictionaryWithCapacity: 1]; 179 [dict setObject: @"path" forKey: @"type"]; 180 181 str = [NSString stringWithFormat: @"%.3f", (float)flatness]; 182 [dict setObject: str forKey: @"flatness"]; 183 str = [NSString stringWithFormat: @"%u", (unsigned)linejoin]; 184 [dict setObject: str forKey: @"linejoin"]; 185 str = [NSString stringWithFormat: @"%u", (unsigned)linecap]; 186 [dict setObject: str forKey: @"linecap"]; 187 str = [NSString stringWithFormat: @"%.3f", (float)miterlimit]; 188 [dict setObject: str forKey: @"miterlimit"]; 189 str = [NSString stringWithFormat: @"%.3f", (float)linewidth]; 190 [dict setObject: str forKey: @"linewidth"]; 191 [dict setObject: [NSNumber numberWithBool:stroked] forKey: @"stroked"]; 192 str = [NSString stringWithFormat: @"%.3f %.3f %.3f", 193 (float)strokeCol[0], (float)strokeCol[1], (float)strokeCol[2]]; 194 [dict setObject: str forKey: @"strokecolor"]; 195 str = [NSString stringWithFormat: @"%.3f", (float)strokeAlpha]; 196 [dict setObject: str forKey: @"strokealpha"]; 197 [dict setObject:[NSNumber numberWithBool:filled] forKey: @"filled"]; 198 str = [NSString stringWithFormat: @"%.3f %.3f %.3f", 199 (float)fillCol[0], (float)fillCol[1], (float)fillCol[2]]; 200 [dict setObject: str forKey: @"fillcolor"]; 201 str = [NSString stringWithFormat: @"%.3f", (float)fillAlpha]; 202 [dict setObject: str forKey: @"fillalpha"]; 203 [dict setObject:[NSNumber numberWithBool:visible] forKey: @"visible"]; 204 [dict setObject:[NSNumber numberWithBool:locked] forKey: @"locked"]; 205 206 points = [NSMutableArray arrayWithCapacity: 1]; 207 for (i = 0; i < [controlPoints count]; i++) 208 { 209 GRBezierControlPoint *cp; 210 GRBezierHandle handle; 211 212 cp = [controlPoints objectAtIndex:i]; 213 handle = [cp bzHandle]; 214 str = [NSString stringWithFormat: @"%f %f %f %f %f %f %d", 215 handle.firstHandle.x, handle.firstHandle.y, 216 handle.center.x, handle.center.y, 217 handle.secondHandle.x, handle.secondHandle.y, 218 [cp symmetricalHandles]]; 219 [points addObject:str]; 220 } 221 [dict setObject: points forKey:@ "points"]; 222 223 return dict; 224} 225 226- (id)copyWithZone:(NSZone *)zone 227{ 228 GRBezierPath *objCopy; 229 NSMutableArray *cpsCopy; 230 NSEnumerator *e; 231 GRBezierControlPoint *cp; 232 233 objCopy = [super copyWithZone:zone]; 234 235 cpsCopy = [[NSMutableArray alloc] initWithCapacity: [controlPoints count]]; 236 e = [controlPoints objectEnumerator]; 237 while ((cp = [e nextObject])) 238 { 239 GRBezierControlPoint *cpCopy; 240 241 cpCopy = [cp copy]; 242 [cpCopy setPath:objCopy]; 243 [cpsCopy addObject: cpCopy]; 244 [cpCopy release]; 245 } 246 247 objCopy->controlPoints = cpsCopy; 248 objCopy->calculatingHandles = calculatingHandles; 249 return objCopy; 250} 251 252- (void)dealloc 253{ 254 [controlPoints release]; 255 [super dealloc]; 256} 257 258- (NSMutableArray *)controlPoints 259{ 260 return controlPoints; 261} 262 263 264- (void)addControlAtPoint:(NSPoint)aPoint 265{ 266 GRBezierControlPoint *cp; 267 268 cp = [[GRBezierControlPoint alloc] initAtPoint: aPoint 269 forPath: self zoomFactor: zmFactor]; 270 [controlPoints addObject: cp]; 271 [cp select]; 272 currentPoint = cp; 273 [cp release]; 274 275 if([controlPoints count] == 1) 276 [displayPath moveToPoint: GRpointZoom(aPoint, zmFactor)]; 277} 278 279- (void)addLineToPoint:(NSPoint)aPoint 280{ 281 GRBezierControlPoint *mtopoint, *prevpoint; 282 GRBezierHandle handle; 283 284 [self addControlAtPoint: aPoint]; 285 mtopoint = [controlPoints objectAtIndex: 0]; 286 prevpoint = [controlPoints objectAtIndex: [controlPoints count] -2]; 287 288 if([prevpoint isActiveHandle]) 289 { 290 handle = [prevpoint bzHandle]; 291 [displayPath curveToPoint: [(GRBezierControlPoint *)currentPoint center] 292 controlPoint1: handle.firstHandle 293 controlPoint2: [(GRBezierControlPoint *)currentPoint center]]; 294 [self confirmNewCurve]; 295 return; 296 } 297 298 if([self isPoint: (GRBezierControlPoint *)currentPoint onPoint: mtopoint]) 299 { 300 [currentPoint moveToPoint: [mtopoint center]]; 301 [displayPath lineToPoint: GRpointZoom([mtopoint center], zmFactor)]; 302 [(GRBezierPathEditor *)editor setIsDone:YES]; 303 } 304 else 305 { 306 [displayPath lineToPoint: GRpointZoom(aPoint, zmFactor)]; 307 } 308} 309 310- (void)addCurveWithBezierHandlePosition:(NSPoint)handlePos 311{ 312 GRBezierControlPoint *mtopoint; 313 GRBezierHandle handle1, handle2; 314 NSBezierPathElement type; 315 NSPoint pts[3]; 316 317 mtopoint = [controlPoints objectAtIndex: 0]; 318 if([self isPoint: (GRBezierControlPoint *)currentPoint onPoint: mtopoint] && [controlPoints count] != 1) 319 { 320 if(!calculatingHandles) 321 { 322 [currentPoint moveToPoint:[mtopoint center]]; 323 } 324 else 325 { 326 [mtopoint calculateBezierHandles: handlePos]; 327 type = [displayPath elementAtIndex: 1]; 328 if(type == NSCurveToBezierPathElement) 329 { 330 [displayPath elementAtIndex: 1 associatedPoints: pts]; 331 pts[0] = GRpointZoom([mtopoint bzHandle].firstHandle, zmFactor); 332 333 [displayPath setAssociatedPoints: pts atIndex: 1]; 334 } 335 else 336 { 337 [self remakePath]; 338 } 339 } 340 } 341 342 [(GRBezierControlPoint *)currentPoint calculateBezierHandles: handlePos]; 343 if([controlPoints count] == 1) 344 return; 345 346 handle1 = [[controlPoints objectAtIndex: [controlPoints count] -2] bzHandle]; 347 handle2 = [(GRBezierControlPoint *)currentPoint bzHandle]; 348 349 if(calculatingHandles) 350 { 351 pts[0] = GRpointZoom(handle1.firstHandle, zmFactor); 352 pts[1] = GRpointZoom(handle2.secondHandle, zmFactor); 353 pts[2] = GRpointZoom([(GRBezierControlPoint *)currentPoint center], zmFactor); 354 [displayPath setAssociatedPoints: pts atIndex: [controlPoints count] -1]; 355 } 356 else 357 { 358 [displayPath curveToPoint: GRpointZoom([(GRBezierControlPoint *)currentPoint center], zmFactor) 359 controlPoint1: GRpointZoom(handle1.firstHandle, zmFactor) 360 controlPoint2: GRpointZoom(handle2.secondHandle, zmFactor)]; 361 calculatingHandles = YES; 362 } 363} 364 365- (void)subdividePathAtPoint:(NSPoint)p splitIt:(BOOL)split 366{ 367 GRBezierControlPoint *ncp, *prevcp, *nextcp, *cp = nil; 368 GRBezierHandle handle1, handle2; 369 hitData hitdata; 370 NSPoint pp[81], newpp[7]; 371 int i; 372 NSUInteger pcount, index; 373 double y, s, ax, ay; 374 375 pcount = 0; 376 y = (int)p.y -4; 377 while(pcount < 81) { 378 for(i = -4; i <= 4; i++) 379 { 380 pp[pcount].x = (int)p.x + i; 381 pp[pcount].y = y; 382 pcount++; 383 } 384 y++; 385 } 386 387 for(i = 0; i < 81; i++) 388 { 389 hitdata = [self hitDataOfPathSegmentOwningPoint: p]; 390 cp = hitdata.cp; 391 if(cp) 392 break; 393 } 394 if(cp == nil) 395 return; 396 397 index = [self indexOfPoint: cp]; 398 if (index == NSNotFound) 399 return; 400 if (index > 0) 401 index--; 402 403 ncp = [[GRBezierControlPoint alloc] initAtPoint: hitdata.p 404 forPath: self zoomFactor: zmFactor]; 405 [controlPoints insertObject: ncp atIndex: index]; 406 [ncp select]; 407 currentPoint = ncp; 408 [ncp release]; 409 410 if(index == 0) 411 prevcp = [controlPoints objectAtIndex: [controlPoints count] -1]; 412 else 413 prevcp = [controlPoints objectAtIndex: index -1]; 414 415 nextcp = [controlPoints objectAtIndex: index +1]; 416 417 s = 1 - hitdata.t; 418 419 newpp[0].x = [prevcp center].x; 420 newpp[0].y = [prevcp center].y; 421 newpp[6].x = [nextcp center].x; 422 newpp[6].y = [nextcp center].y; 423 424 handle1 = [prevcp bzHandle]; 425 handle2 = [nextcp bzHandle]; 426 427 ax = s * handle1.firstHandle.x + hitdata.t * handle2.secondHandle.x; 428 ay = s * handle1.firstHandle.y + hitdata.t * handle2.secondHandle.y; 429 430 newpp[1].x = s * newpp[0].x + hitdata.t * handle1.firstHandle.x; 431 newpp[1].y = s * newpp[0].y + hitdata.t * handle1.firstHandle.y; 432 newpp[2].x = s * newpp[1].x + hitdata.t * ax; 433 newpp[2].y = s * newpp[1].y + hitdata.t * ay; 434 435 newpp[5].x = s * newpp[2].x + hitdata.t * newpp[6].x; 436 newpp[5].y = s * newpp[2].y + hitdata.t * newpp[6].y; 437 newpp[4].x = s * ax + hitdata.t * newpp[5].x; 438 newpp[4].y = s * ay + hitdata.t * newpp[5].y; 439 440 newpp[3].x = s * newpp[2].x + hitdata.t * newpp[4].x; 441 newpp[3].y = s * newpp[2].y + hitdata.t * newpp[4].y; 442 443 444 NSLog(@"%i %i - %i %i", (int)[(GRBezierControlPoint *)currentPoint center].x, 445 (int)[(GRBezierControlPoint *)currentPoint center].y, (int)newpp[3].x, (int)newpp[3].y); 446 447 448 [prevcp calculateBezierHandles: newpp[1]]; 449 [(GRBezierControlPoint *)currentPoint calculateBezierHandles: newpp[4]]; 450 // [nextcp calculateBezierHandles: newpp[5]]; 451 452 [self remakePath]; 453} 454 455- (void)deletePoint:(GRBezierControlPoint *)p 456{ 457 NSUInteger i; 458 GRBezierControlPoint *cpToDelete; 459 460 if ([controlPoints count] < 2) 461 return; 462 463 i = 0; 464 cpToDelete = NULL; 465 while (i < [controlPoints count] && cpToDelete == NULL) 466 { 467 GRBezierControlPoint *cp; 468 469 cp = [controlPoints objectAtIndex:i]; 470 if (cp == p) 471 cpToDelete = cp; 472 else 473 i++; 474 } 475 476 [controlPoints removeObjectAtIndex:i]; 477 [self remakePath]; 478} 479 480- (BOOL)isPoint:(GRBezierControlPoint *)cp1 onPoint:(GRBezierControlPoint *)cp2 481{ 482 return pointInRect([cp2 centerRect], [cp1 center]); 483} 484 485- (GRBezierControlPoint *)pointOnPoint:(GRBezierControlPoint *)aPoint 486{ 487 GRBezierControlPoint *cp, *ponpoint = nil; 488 NSUInteger i; 489 490 for(i = 0; i < [controlPoints count]; i++) 491 { 492 cp = [controlPoints objectAtIndex: i]; 493 if([self isPoint: aPoint onPoint: cp] && (aPoint != cp)) 494 { 495 ponpoint = cp; 496 break; 497 } 498 } 499 500 return ponpoint; 501} 502 503- (void)confirmNewCurve 504{ 505 if (!controlPoints || [controlPoints count] == 0) 506 return; 507 calculatingHandles = NO; 508 if([controlPoints count] == 1) 509 return; 510 if (currentPoint == nil) 511 [(GRBezierPathEditor *)editor setIsDone:YES]; 512 else if([self isPoint: (GRBezierControlPoint *)currentPoint onPoint: [controlPoints objectAtIndex: 0]]) 513 [(GRBezierPathEditor *)editor setIsDone:YES]; 514 515 [self remakePath]; 516} 517 518- (void)remakePath 519{ 520 GRBezierControlPoint *cp, *prevcp, *mtopoint; 521 NSUInteger i; 522 523 [path removeAllPoints]; 524 [displayPath removeAllPoints]; 525 if (!controlPoints || [controlPoints count] == 0) 526 return; 527 528 mtopoint = [controlPoints objectAtIndex: 0]; 529 [path moveToPoint: [mtopoint center]]; 530 [displayPath moveToPoint: GRpointZoom([mtopoint center], zmFactor)]; 531 for(i = 1; i < [controlPoints count]; i++) 532 { 533 GRBezierHandle handle1, handle2; 534 BOOL isLine; 535 536 cp = [controlPoints objectAtIndex: i]; 537 prevcp = [controlPoints objectAtIndex: i -1]; 538 handle1 = [prevcp bzHandle]; 539 handle2 = [cp bzHandle]; 540 541 /* we have a line if the start and end control points have respectively 542 right and left center-coincident handles */ 543 isLine = NO; 544 if (NSEqualPoints(handle1.center, handle1.secondHandle) && NSEqualPoints(handle2.center, handle2.firstHandle)) 545 isLine = YES; 546 547 if (isLine) 548 { 549 [path lineToPoint: [cp center]]; 550 [displayPath lineToPoint: GRpointZoom([cp center], zmFactor)]; 551 } 552 else 553 { 554 [path curveToPoint: [cp center] 555 controlPoint1: handle1.firstHandle 556 controlPoint2: handle2.secondHandle]; 557 [displayPath curveToPoint: GRpointZoom([cp center], zmFactor) 558 controlPoint1: GRpointZoom(handle1.firstHandle, zmFactor) 559 controlPoint2: GRpointZoom(handle2.secondHandle, zmFactor)]; 560 [cp setPointPosition:GRPointMiddle]; 561 } 562 563 if([self isPoint: cp onPoint: mtopoint]) 564 [(GRBezierPathEditor *)editor setIsDone:YES]; 565 } 566 567 /* if the path is open, set the Start ad End points controls */ 568 if (!NSEqualPoints([(GRBezierControlPoint *)[controlPoints objectAtIndex:0] center], [(GRBezierControlPoint *)[controlPoints objectAtIndex:[controlPoints count]-1] center])) 569 { 570 [[controlPoints objectAtIndex:0] setPointPosition:GRPointStart]; 571 [[controlPoints objectAtIndex:[controlPoints count]-1] setPointPosition:GRPointEnd]; 572 } 573} 574 575 576- (hitData)hitDataOfPathSegmentOwningPoint:(NSPoint)pt 577{ 578 hitData hitdata; 579 GRBezierControlPoint *cp, *prevcp; 580 GRBezierHandle handle1, handle2; 581 NSPoint p, bp; 582 NSRect r; 583 double t; 584 NSUInteger i; 585 586 hitdata.cp = nil; 587 hitdata.t = 0; 588 hitdata.p = NSZeroPoint; 589 590 r = NSMakeRect((int)pt.x -4, (int)pt.y -4, 8, 8); 591 592 for(i = 0; i < [controlPoints count]; i++) 593 { 594 cp = [controlPoints objectAtIndex: i]; 595 596 if(pointInRect([cp centerRect], pt)) 597 return hitdata; 598 599 if(i == 0) 600 prevcp = [controlPoints objectAtIndex: [controlPoints count] -1]; 601 else 602 prevcp = [controlPoints objectAtIndex: i -1]; 603 604 handle1 = [prevcp bzHandle]; 605 handle2 = [cp bzHandle]; 606 607 bp.x = [prevcp center].x; 608 bp.y = [prevcp center].y; 609 for(t = k; t <= 1+k; t += k) { 610 p.x = (bp.x+t*(-bp.x*3+t*(3*bp.x-bp.x*t))) 611 +t*(3*handle1.firstHandle.x+t* 612 (-6*handle1.firstHandle.x+handle1.firstHandle.x*3*t)) 613 +t*t*(handle2.secondHandle.x*3-handle2.secondHandle.x*3*t) 614 +[cp center].x*t*t*t; 615 p.y = (bp.y+t*(-bp.y*3+t*(3*bp.y-bp.y*t))) 616 +t*(3*handle1.firstHandle.y+t* 617 (-6*handle1.firstHandle.y+handle1.firstHandle.y*3*t)) 618 +t*t*(handle2.secondHandle.y*3-handle2.secondHandle.y*3*t) 619 +[cp center].y*t*t*t; 620 621 if(pointInRect(r, p)) 622 { 623 hitdata.cp = cp; 624 hitdata.p.x = p.x; 625 hitdata.p.y = p.y; 626 hitdata.t = t - k; 627 return hitdata; 628 } 629 } 630 } 631 632 return hitdata; 633} 634 635- (void)moveAddingCoordsOfPoint:(NSPoint)p 636{ 637 NSUInteger i; 638 639 for(i = 0; i < [controlPoints count]; i++) 640 { 641 GRBezierControlPoint *cp = [controlPoints objectAtIndex: i]; 642 [cp moveToPoint: NSMakePoint([cp center].x + p.x, [cp center].y + p.y)]; 643 } 644 [self remakePath]; 645} 646 647- (void)setZoomFactor:(CGFloat)f 648{ 649 NSUInteger i; 650 651 zmFactor = f; 652 for(i = 0; i < [controlPoints count]; i++) 653 [[controlPoints objectAtIndex: i] setZoomFactor: zmFactor]; 654 655 [self remakePath]; 656} 657 658- (BOOL)objectHitForSelection:(NSPoint)p 659{ 660 return [self onPathBorder:p]; 661} 662 663/** Returns yes if the Point lies on a control point */ 664- (BOOL)onControlPoint:(NSPoint)p 665{ 666 NSInteger i; 667 GRBezierControlPoint *cp; 668 GRBezierHandle handle; 669 670 for(i = 0; i < [controlPoints count]; i++) 671 { 672 cp = [controlPoints objectAtIndex: i]; 673 handle = [cp bzHandle]; 674 if(pointInRect(handle.centerRect, p)) 675 return YES; 676 } 677 678 return NO; 679} 680 681/** checks if a given point is a control point or a point on the path border 682 683 ATTENTION: for closed path it retuns also YES if the point is inside the area 684*/ 685- (BOOL)onPathBorder:(NSPoint)p 686{ 687 if ([self onControlPoint:p]) 688 return YES; 689 690 /* mypath represents the Path in the current zoom, so it needs to be converted */ 691 if([displayPath containsPoint: GRpointZoom(p, zmFactor)]) 692 return YES; 693 694 return NO; 695} 696 697- (GRBezierControlPoint *)firstPoint 698{ 699 return (GRBezierControlPoint *)[controlPoints objectAtIndex: 0]; 700} 701 702 703 704- (GRBezierControlPoint *)lastPoint 705{ 706 return (GRBezierControlPoint *)[controlPoints objectAtIndex: [controlPoints count] -1]; 707} 708 709- (NSUInteger)indexOfPoint:(GRBezierControlPoint *)aPoint 710{ 711 NSUInteger i; 712 NSUInteger r; 713 BOOL found; 714 715 r = NSNotFound; 716 found = NO; 717 718 i = 0; 719 while (i < [controlPoints count] && !found) 720 { 721 if([controlPoints objectAtIndex: i] == aPoint) 722 found = YES; 723 i++; 724 } 725 726 if(found) 727 r = i; 728 729 return r; 730} 731 732 733 734 735/* override for editor handling */ 736- (void)setLocked:(BOOL)value 737{ 738 [super setLocked:value]; 739 740 if(!locked) 741 [editor unselect]; 742 else 743 [(GRBezierPathEditor *)editor selectAsGroup]; 744} 745 746 747- (void)draw 748{ 749 GRBezierControlPoint *cp; 750 NSUInteger i; 751 NSBezierPath *bzp; 752 CGFloat linew; 753 NSBezierPath *pathToDraw; 754 755 if(![controlPoints count] || !visible) 756 return; 757 758 linew = linewidth; 759 pathToDraw = path; 760 if ([[NSGraphicsContext currentContext] isDrawingToScreen]) 761 { 762 linew = linewidth * zmFactor; 763 pathToDraw = displayPath; 764 } 765 766 bzp = [NSBezierPath bezierPath]; 767 if(filled) 768 { 769 [NSGraphicsContext saveGraphicsState]; 770 [fillColor set]; 771 [pathToDraw fill]; 772 [NSGraphicsContext restoreGraphicsState]; 773 } 774 if(stroked) 775 { 776 [NSGraphicsContext saveGraphicsState]; 777 [pathToDraw setLineJoinStyle:linejoin]; 778 [pathToDraw setLineCapStyle:linecap]; 779 [pathToDraw setLineWidth:linew]; 780 [strokeColor set]; 781 [pathToDraw stroke]; 782 [NSGraphicsContext restoreGraphicsState]; 783 } 784 785 786 [bzp setLineWidth:1]; 787 if([(GRBezierPathEditor *)editor isGroupSelected]) 788 { 789 for(i = 0; i < [controlPoints count]; i++) 790 { 791 cp = [controlPoints objectAtIndex: i]; 792 [cp drawControlAsSelected:YES]; 793 } 794 } 795 796 if ([[NSGraphicsContext currentContext] isDrawingToScreen]) 797 [editor draw]; 798} 799 800@end 801