1/* VPath.m 2 * complex path 3 * 4 * Copyright (C) 1996-2012 by vhf interservice GmbH 5 * Author: Georg Fleischmann, Ilonka Fleischmann 6 * 7 * created: 1996-01-29 8 * modified: 2012-12-12 (-drawGraduatedWithPrincipal: alpha added, another rounding problem exposed) 9 * 2012-10-25 (-optimizePath: change position of inserted line for one -closeGapBetween::) 10 * 2012-08-14 (-subPathInsidePath:: on check alot better) 11 * 2012-07-17 (*pts = NULL, *iPts = NULL initialized) 12 * 2012-06-12 (-optimizePath:, -pointWithNumBecomeStartPoint: corrected for open paths) 13 * 2012-01-19 (-contour:inlay:splitCurves: simplified sc cases in two locations) 14 * 2011-08-25 (-contour:inlay:splitCurves: pathCopy, and Autorelease pool) 15 * (-subPathInsidePath::, -intersectionsForPtInside:with:, both, memory leaks closed) 16 * 2011-05-02 (-contour:inlay:splitCurves: calc parallel points with cut if possible) 17 * 2011-04-14 (-contour:inlay:splitCurves: replace curves with same curvePts in start/end) 18 * 2011-04-06 (-pointWithNumBecomeStartPoint: added) 19 * (-removePointWithNum:): [self deselectAll]; 20 * (-join:): getDirection 21 * 2010-07-08 (-transform: added) 22 * 2010-03-03 (-contour:inlay:splitCurves: and copy, setDirectionCCW:) 23 * 2008-12-18 (-contour:inlay:splitCurves:, [gThis length] < 15.0*TOLERANCE) 24 * 2008-12-01 (axial filling, draw unfilled path with stroke color) 25 * 2008-10-16 (-uniteWith:) 26 * 2008-10-11 (-changeDirection - open paths added) 27 * 2008-07-14 (-directionOfSubPath::, -subPathInsidePath::, 360 arc inside Path) 28 * 2008-07-25 (-contour:inlay:removeLoops: does not call -contourWithPixel any more !) 29 * 30 * This program is free software; you can redistribute it and/or 31 * modify it under the terms of the vhf Public License as 32 * published by vhf interservice GmbH. Among other things, the 33 * License requires that the copyright notices and this notice 34 * be preserved on all copies. 35 * 36 * This program is distributed in the hope that it will be useful, 37 * but WITHOUT ANY WARRANTY; without even the implied warranty of 38 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 39 * See the vhf Public License for more details. 40 * 41 * You should have received a copy of the vhf Public License along 42 * with this program; see the file LICENSE. If not, write to vhf. 43 * 44 * vhf interservice GmbH, Im Marxle 3, 72119 Altingen, Germany 45 * eMail: info@vhf.de 46 * http://www.vhf.de 47 */ 48 49#include <AppKit/AppKit.h> 50#include <math.h> 51#include <VHFShared/vhfMath.h> 52#include <VHFShared/vhf2DFunctions.h> 53#include <VHFShared/vhfCommonFunctions.h> 54#include <VHFShared/types.h> 55#include "../App.h" 56#include "../DocView.h" 57#include "../DocWindow.h" 58#include "VLine.h" 59#include "VCurve.h" 60#include "VPath.h" 61#include "PathContour.h" 62#include "VArc.h" 63#include "HiddenArea.h" 64#include "../Inspectors.h" 65 66static float angleBetweenGraphicsInStartOrEnd(id g1, id g2, BOOL end); 67 68/* Private methods 69 */ 70@interface VPath(PrivateMethods) 71- (void)addToClosestEnd:obj; 72- (BOOL)closeGapBetween:(VGraphic*)g1 and:(VGraphic*)g2; 73- (int)isPointInsideOrOn:(NSPoint)p dist:(float)dist; 74- (int)isPointInsideOrOn:(NSPoint)p dist:(float)dist subPath:(int)begIx :(int)endIx; 75//- (int)getLastObjectOfSubPath:(int)startIx; 76//- (int)directionOfSubPath:(int)startIx :(int)endIx; 77- (void)optimize; 78//- (BOOL)subPathInsidePath:(int)begIx :(int)endIx; 79- (void)optimizeSubPathsToClosedPath:(VPath*)path :(float)w :(int*)subPathSplitted; 80- (void)removeFaultGraphicsInSubpaths:(VPath*)path :(float)w; 81- (void)setDirectionCCW:(BOOL)ccw; 82- (id)contourOpen:(float)w; 83- (int)getFirstObjectOfSubPath:(int)ix; 84- (void)changeDirectionOfSubPath:(int)startIx :(int)endIx; 85 86//- (int)getLastObjectOfSubPath:(int)startIx tolerance:(float)tolerance; 87@end 88 89@implementation VPath 90 91/* 92 * This sets the class version so that we can compatibly read 93 * old VGraphic objects out of an archive. 94 */ 95+ (void)initialize 96{ 97 [VPath setVersion:3]; 98} 99 100+ (VPath*)path 101{ 102 return [[[VPath allocWithZone:[self zone]] init] autorelease]; 103} 104 105+ (VPath*)pathWithBezierPath:(NSBezierPath*)bezierPath 106{ int i, cnt = [bezierPath elementCount]; 107 VPath *path; 108 id g; 109 NSPoint p0, pts[3], startP = NSZeroPoint; 110 111 if (!cnt) 112 return nil; 113 path = [VPath path]; 114 for (i=0; i<cnt; i++) 115 { 116 switch ([bezierPath elementAtIndex:i associatedPoints:pts]) 117 { 118 case NSMoveToBezierPathElement: 119 p0 = startP = pts[0]; 120 break; 121 case NSLineToBezierPathElement: 122 g = [VLine lineWithPoints:p0 :pts[0]]; 123 [[path list] addObject:g]; 124 p0 = pts[0]; 125 break; 126 case NSCurveToBezierPathElement: 127 g = [VCurve curveWithPoints:p0 :pts[0] :pts[1] :pts[2]]; 128 [[path list] addObject:g]; 129 p0 = pts[2]; 130 break; 131 case NSClosePathBezierPathElement: 132 g = [VLine lineWithPoints:p0 :startP]; 133 [[path list] addObject:g]; 134 } 135 } 136 return path; 137} 138 139- init 140{ 141 [super init]; 142 selectedObject = -1; 143 list = [[NSMutableArray allocWithZone:[self zone]] init]; 144 fillColor = [[NSColor blackColor] retain]; 145 endColor = [[NSColor blackColor] retain]; 146 graduateAngle = 0.0; 147 stepWidth = 7.0; 148 radialCenter = NSMakePoint(0.5, 0.5); 149 graduateList = nil; 150 graduateDirty = YES; 151 coordBounds = bounds = NSZeroRect; 152 return self; 153} 154 155/* deep copy 156 * 157 * created: 2001-02-15 158 * modified: 2010-03-03 (setDirectionCCW:) 159 */ 160- copy 161{ VPath *path; 162 int i, cnt = [list count]; 163 164 path = [[VPath allocWithZone:[self zone]] init]; 165 [path setFilled:filled optimize:NO]; 166 [path setDirectionCCW:isDirectionCCW]; 167 [path setWidth:width]; 168 [path setSelected:isSelected]; 169 [path setLocked:NO]; 170 [path setColor:color]; 171 [path setFillColor:fillColor]; 172 [path setEndColor:endColor]; 173 [path setGraduateAngle:graduateAngle]; 174 [path setStepWidth:stepWidth]; 175 [path setRadialCenter:radialCenter]; 176 for (i=0; i<cnt; i++) 177 [[path list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 178 return path; 179} 180 181- (NSString*)description 182{ NSRect bnds = [self bounds]; 183 184 return [NSString stringWithFormat:@"VPath: %f %f %f %f", bnds.origin.x, bnds.origin.y, bnds.size.width, bnds.size.height]; 185} 186 187- (NSString*)title { return @"Path"; } 188 189/* whether we are a path object 190 * eg. line, polyline, arc, curve, rectangle, path 191 * group is not a path object because we don't know what is inside! 192 */ 193- (BOOL)isPathObject { return YES; } 194 195- (void)setRectangle:(NSPoint)ll :(NSPoint)ur 196{ VLine *line; 197 NSPoint p0, p1; 198 199 if (!list) 200 list = [[NSMutableArray allocWithZone:[self zone]] init]; 201 else 202 [list removeAllObjects]; 203 204 line = [VLine line]; 205 [line setColor:color]; 206 [line setWidth:width]; 207 p0 = ll; 208 p1.x = ur.x; p1.y = ll.y; 209 [line setVertices:p0 :p1]; 210 [list addObject:line]; 211 212 line = [VLine line]; 213 [line setColor:color]; 214 [line setWidth:width]; 215 p0 = p1; 216 p1 = ur; 217 [line setVertices:p0 :p1]; 218 [list addObject:line]; 219 220 line = [VLine line]; 221 [line setColor:color]; 222 [line setWidth:width]; 223 p0 = p1; 224 p1.x = ll.x; p1.y = ur.y; 225 [line setVertices:p0 :p1]; 226 [list addObject:line]; 227 228 line = [VLine line]; 229 [line setColor:color]; 230 [line setWidth:width]; 231 p0 = p1; 232 p1 = ll; 233 [line setVertices:p0 :p1]; 234 [list addObject:line]; 235 dirty = YES; 236} 237 238#define CREATEEVENTMASK NSLeftMouseDraggedMask|NSLeftMouseDownMask|NSLeftMouseUpMask|NSPeriodicMask 239- (BOOL)create:(NSEvent *)event in:(DocView*)view 240{ NSRect viewBounds, gridBounds, drawBounds; 241 NSPoint start, last, gridPoint, drawPoint, lastPoint = NSZeroPoint; 242 id window = [view window]; 243 BOOL ok = YES, dragging = NO, inTimerLoop = NO; 244 float grid = 1.0/*(float)[view rasterSpacing]*/; 245 int windowNum = [event windowNumber]; 246 NSColor *col = [self color]; 247 248 [[(App*)NSApp inspectorPanel] loadGraphic:self]; /* set the values of the inspector to self */ 249 250 start = [event locationInWindow]; 251 start = [view convertPoint:start fromView:nil]; /* convert window to view coordinates */ 252 [view hitEdge:&start spare:self]; /* snap to point */ 253 start = [view grid:start]; /* set on grid */ 254 viewBounds = [view visibleRect]; /* get the bounds of the view */ 255 [view lockFocus]; /* and lock the focus on view */ 256 257 [self setRectangle:start :start]; 258 gridBounds = [self extendedBoundsWithScale:[view scaleFactor]]; 259 260 last = start; 261 262 event = [NSApp nextEventMatchingMask:CREATEEVENTMASK untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:YES]; 263 StartTimer(inTimerLoop); 264 /* now entering the tracking loop 265 */ 266 while ( ((!dragging && [event type] != NSLeftMouseDown) || (dragging && [event type] != NSLeftMouseUp)) && [event type] != NSAppKitDefined && [event type] != NSSystemDefined ) 267 { 268 /* Since MouseMoved event is never send we use a periodic event instead */ 269 if ( [event type] == NSPeriodic ) 270 drawPoint = [[[self class] currentWindow] mouseLocationOutsideOfEventStream]; 271 else 272 drawPoint = [event locationInWindow]; 273 274 /* display only if mouse has moved */ 275 if ( drawPoint.x != lastPoint.x || drawPoint.y != lastPoint.y ) 276 { 277 lastPoint = drawPoint; 278 279 /* delete line from screen */ 280 [view drawRect:gridBounds]; 281 drawPoint = [view convertPoint:drawPoint fromView:nil]; 282 if ( ([event type] == NSLeftMouseDragged)&&(!dragging) ) 283 dragging = YES; 284 /* if user is dragging we scroll the view */ 285 if (dragging) 286 { [view scrollPointToVisible:drawPoint]; 287 viewBounds = [view bounds]; 288 } 289 gridPoint = drawPoint; 290 /* snap to point */ 291 [view hitEdge:&gridPoint spare:self]; 292 /* fix position to grid */ 293 gridPoint = [view grid:gridPoint]; 294 295 [window displayCoordinate:gridPoint ref:NO]; 296 297 [self setColor:[NSColor lightGrayColor]]; 298 [self setRectangle:start :drawPoint]; 299 drawBounds = [self extendedBoundsWithScale:[view scaleFactor]]; 300 if ( NSContainsRect(viewBounds , drawBounds) ) /* line inside view ? */ 301 [self drawWithPrincipal:view]; 302 303 [self setColor:col]; 304 [self setRectangle:start :gridPoint]; 305 gridBounds = [self extendedBoundsWithScale:[view scaleFactor]]; 306 /* if line is not inside view we set it invalid */ 307 if ( NSContainsRect(viewBounds , gridBounds) ) 308 [self drawWithPrincipal:view]; 309 else 310 drawPoint = gridPoint = start; 311 /* the united rect of the two rects we need to redraw the view */ 312 gridBounds = NSUnionRect(drawBounds, gridBounds); 313 314 [window flushWindow]; 315 } 316 event = [NSApp nextEventMatchingMask:CREATEEVENTMASK untilDate:[NSDate distantFuture] inMode:NSEventTrackingRunLoopMode dequeue:YES]; 317 } 318 StopTimer(inTimerLoop); 319 320 last = gridPoint; 321 if ( fabs(last.x-start.x) <= grid && fabs(last.y-start.y) <= grid ) /* no length -> not valid */ 322 ok = NO; 323 else if ( (!dragging && [event type]==NSLeftMouseDown)||(dragging && [event type]==NSLeftMouseUp) ) 324 { /* double click or out of window -> not valid */ 325 if ([event clickCount] > 1 || [event windowNumber] != windowNum) 326 ok = NO; 327 else 328 ok = NSMouseInRect(gridPoint , viewBounds , NO); 329 } 330 331 if ([event type] == NSAppKitDefined || [event type] == NSSystemDefined) 332 ok = NO; 333 334 [view unlockFocus]; 335 336 if (!ok) 337 { [view display]; 338 return NO; 339 } 340 341 dirty = YES; 342 [view cacheGraphic:self]; // add to graphic cache 343 344 return YES; 345} 346 347/* copy subpath-lists to main list 348 */ 349- (id)unnest 350{ NSMutableArray *pList = [NSMutableArray array]; 351 int i, cnt = [self count]; 352 353 for ( i=0; i<cnt; i++ ) 354 { int j, jCnt; 355 VGraphic *g = [[self list] objectAtIndex:i]; 356 357 if ([g isKindOfClass:[VPath class]]) 358 { 359 jCnt = [(VPath*)g count]; 360 for ( j=0; j<jCnt; j++ ) 361 [pList addObject:[[(VPath*)g list] objectAtIndex:j]]; 362 [[(VPath*)g list] removeAllObjects]; 363 } 364 else 365 [pList addObject:g]; 366 } 367 [self setList:pList]; 368 return self; 369} 370 371- (NSMutableArray*)list 372{ 373 return list; 374} 375 376/* set new list and return the old list 377 */ 378- (void)setList:aList 379{ 380 [self setList:aList optimize:YES]; 381} 382- (void)setList:aList optimize:(BOOL)optimize 383{ 384 if (list) 385 [list release]; 386 list = [aList retain]; 387 if ( optimize ) 388 [self optimize]; 389 coordBounds = bounds = NSZeroRect; 390 dirty = YES; 391 graduateDirty = YES; 392} 393 394/* returns the endpoints of an open path 395 */ 396- (void)getEndPoints:(NSPoint*)p1 :(NSPoint*)p2 397{ 398 *p1 = [[list objectAtIndex:0] pointWithNum:0]; 399 *p2 = [[list objectAtIndex:[list count]-1] pointWithNum:MAXINT]; 400} 401 402- (unsigned)count 403{ 404 return [list count]; 405} 406 407- (unsigned)countRecursive 408{ int i, cnt = [list count]; 409 410 for (i=[list count]-1; i>=0; i--) 411 { id g = [list objectAtIndex:i]; 412 413 if ([g isKindOfClass:[VPath class]]) 414 cnt += [g countRecursive]; 415 } 416 return cnt; 417} 418 419/* created: 24.09.95 420 * modified: 421 * purpose: deselect all planes 422 */ 423- (void)deselectAll 424{ int i; 425 426 for (i=[list count]-1; i>=0; i--) 427 [[list objectAtIndex:i] setSelected:NO]; 428} 429 430/* 431 * returns the selected knob (0 or 1) or -1 432 */ 433- (int)selectedKnobIndex 434{ 435 if ( ![list count] || selectedObject < 0) 436 return -1; 437 438 { int i, cnt, pCnt = 0, prevPCnt = 0; 439 440 for (i=0, cnt = [list count]; i<cnt; i++) 441 { pCnt += [[list objectAtIndex:i] numPoints]; 442 if ( i == selectedObject ) 443 break; // this object is our selected object 444 prevPCnt = pCnt; // count of pts befor this gr 445 } 446 return prevPCnt + [[list objectAtIndex:i] selectedKnobIndex]; 447 } 448 return -1; 449} 450 451/* 452 * set selection 453 */ 454- (void)setSelected:(BOOL)flag 455{ 456 if (!flag) 457 { 458 if (selectedObject >= 0) 459 [[list objectAtIndex:selectedObject] setSelected:NO]; 460 selectedObject = -1; 461 } 462 [super setSelected:flag]; 463} 464 465- (BOOL)filled 466{ 467 return filled; 468} 469 470- (void)setFilled:(BOOL)flag 471{ 472 [self setFilled:flag optimize:YES]; 473} 474- (void)setFilled:(BOOL)flag optimize:(BOOL)optimize 475{ 476 if (flag && optimize) 477 { [self closePath]; 478 [self setDirectionCCW:[self isDirectionCCW]]; 479 } 480 filled = flag; 481 if (optimize) 482 [self optimize]; 483 dirty = YES; 484 graduateDirty = YES; 485} 486 487- (void)setWidth:(float)w 488{ int i, cnt; 489 490 for (i=0, cnt = [list count]; i<cnt; i++) 491 [(VGraphic*)[list objectAtIndex:i] setWidth:w]; 492 493 width = w; 494 dirty = YES; 495} 496 497- (void)setFillColor:(NSColor*)col 498{ 499 if (fillColor) [fillColor release]; 500 fillColor = [((col) ? col : [NSColor blackColor]) retain]; 501 dirty = YES; 502 graduateDirty = YES; 503} 504- (NSColor*)fillColor { return fillColor; } 505 506- (void)setEndColor:(NSColor*)col 507{ 508 if (endColor) [endColor release]; 509 endColor = [((col) ? col : [NSColor blackColor]) retain]; 510 dirty = YES; 511 graduateDirty = YES; 512} 513- (NSColor*)endColor { return endColor; } 514 515- (void)setGraduateAngle:(float)a { graduateAngle = a; dirty = YES; graduateDirty = YES; } 516- (float)graduateAngle { return graduateAngle; } 517 518- (void)setStepWidth:(float)sw { stepWidth = sw; dirty = YES; graduateDirty = YES; } 519- (float)stepWidth { return stepWidth; } 520 521- (void)setRadialCenter:(NSPoint)rc { radialCenter = rc; dirty = YES; graduateDirty = YES; } 522- (NSPoint)radialCenter { return radialCenter; } 523 524- (NSMutableArray*)graduateList { return graduateList; } 525 526/* length of sub-path 527 * this returns the length of objects from index to index in list 528 * frIndex <= index <= toIndex 529 * 530 * created: 2008-01-13 531 * modified: 2008-01-17 532 */ 533- (float)lengthFrom:(int)frIx to:(int)toIx 534{ float len = 0.0; 535 int i; 536 537 if (toIx >= [list count]) 538 toIx = [list count] - 1; 539 for(i=frIx; i<=toIx; i++) 540 { VGraphic *g = [list objectAtIndex:i]; 541 len += [g length]; 542 } 543 return len; 544} 545 546/* 547 * add a list to our list 548 * after the operation we are selected, all of our objects are deselected 549 */ 550- (void)addList:(NSArray*)addList at:(int)index 551{ int i, cnt; 552 553 if (!list) 554 list = [[NSMutableArray allocWithZone:[self zone]] init]; 555 556 cnt = [addList count]; 557 for (i=0; i<cnt; i++) 558 { id g = [addList objectAtIndex:i]; 559 560 [g setSelected:NO]; 561 if ([g isKindOfClass:[VPath class]]) /* we dont want a path inside a path ! */ 562 [self addList:[g list] at:[list count]]; 563 else 564 [list insertObject:g atIndex:index++]; 565 } 566 [self optimize]; 567 coordBounds = bounds = NSZeroRect; 568 dirty = YES; 569 graduateDirty = YES; 570} 571 572/* optimize subpaths inside list of path 573 * inner subpaths before outer subpaths (smallest first) 574 */ 575- (NSRect)boundsOfSubpathFrom:(int)beg to:(int)end 576{ int i; 577 NSRect rect, bbox; 578 579 bbox = [[list objectAtIndex:0] bounds]; 580 for ( i=beg; i<end; i++ ) 581 { id g = [list objectAtIndex:i]; 582 583 rect = [g bounds]; 584 bbox = NSUnionRect(rect, bbox); 585 } 586 return bbox; 587} 588- (void)optimize 589{ int i, j, k, l, kcnt, cnt, beg1, end1, beg2, end2; 590 NSMutableArray *subpaths = [NSMutableArray array]; 591 NSMutableArray *array1 = [NSMutableArray array], *array2 = [NSMutableArray array]; 592 NSRect bounds1, bounds2; 593 594 if ( !filled ) 595 return; 596 597 /* array of indexes of subpaths (0 - 3 - 8) */ 598 [subpaths addObject:[NSNumber numberWithInt:0]]; 599 for ( i=0, cnt=[list count]; i<cnt; i=j+1 ) 600 { 601 j = [self getLastObjectOfSubPath:i]; 602 [subpaths addObject:[NSNumber numberWithInt:j+1]]; 603 } 604 605 /* compare bounds of subpath -> exchange if necessary */ 606 for ( i=0; i < (int)[subpaths count]-2; i++ ) 607 { 608 beg1 = [[subpaths objectAtIndex:i] intValue]; 609 end1 = [[subpaths objectAtIndex:i+1] intValue]-1; 610 bounds1 = [self boundsOfSubpathFrom:beg1 to:end1]; 611 612 for ( j=i+1; j<(int)[subpaths count]-1; j++ ) 613 { 614 beg2 = [[subpaths objectAtIndex:j] intValue]; 615 end2 = [[subpaths objectAtIndex:j+1] intValue]-1; 616 bounds2 = [self boundsOfSubpathFrom:beg2 to:end2]; 617 if ( bounds1.size.width*bounds1.size.height > bounds2.size.width*bounds2.size.height ) 618 { NSAutoreleasePool *pool; 619 620 /* swap subpaths in list */ 621 [array1 replaceObjectsInRange:NSMakeRange(0, [array1 count]) withObjectsFromArray:list range:NSMakeRange(beg1, end1-beg1+1)]; 622 [array2 replaceObjectsInRange:NSMakeRange(0, [array2 count]) withObjectsFromArray:list range:NSMakeRange(beg2, end2-beg2+1)]; 623 [list replaceObjectsInRange:NSMakeRange(beg2, end2-beg2+1) withObjectsFromArray:array1]; 624 [list replaceObjectsInRange:NSMakeRange(beg1, end1-beg1+1) withObjectsFromArray:array2]; 625 626 /* update array of indexes of subpaths (0 - 3 - 8) */ 627 [subpaths removeAllObjects]; 628 pool = [NSAutoreleasePool new]; 629 [subpaths addObject:[NSNumber numberWithInt:0]]; 630 for ( k=0, kcnt=[list count]; k<kcnt; k=l+1 ) 631 { 632 l = [self getLastObjectOfSubPath:k]; 633 [subpaths addObject:[NSNumber numberWithInt:l+1]]; 634 } 635 [pool release]; 636 dirty = YES; 637 i--; j=cnt; 638 } 639 } 640 } 641 642/* 643 for ( i=0, cnt=[list count]; i<cnt; i=j+1 ) 644 { 645 j = [self getLastObjectOfSubPath:i]; 646 NSLog(@"b:%d n:%d", i, j-i+1); 647 } 648*/ 649} 650 651/* end point of g1 must fit to start point of g2 652 */ 653- (BOOL)closeGapBetween:(VGraphic*)g1 and:(VGraphic*)g2 654{ NSPoint p, p0, p1; 655 656 if ( ![g1 isKindOfClass:[VArc class]] ) /* g1 != arc */ 657 { 658 if ( [g1 isKindOfClass:[VLine class]] && ![g2 isKindOfClass:[VArc class]] ) 659 { NSPoint p2, p3; 660 661 [(VLine*)g1 getVertices:&p0 :&p1]; 662 p2 = [g2 pointWithNum:0]; 663 p3 = [g2 pointWithNum:MAXINT]; 664 if ( Diff(p0.y, p1.y) < TOLERANCE ) // g1 horicontal 665 { 666 if ( Diff(p1.y, p2.y) > TOLERANCE ) 667 [g2 movePoint:0 to:p1]; // correct y difference of g2 668 else if ( Diff(p1.x, p2.x) > TOLERANCE ) 669 [g1 movePoint:MAXINT to:p2]; // correct x difference 670 } 671 else if ( Diff(p0.x, p1.x) < TOLERANCE ) // g1 vertical 672 { 673 if ( Diff(p1.x, p2.x) > TOLERANCE ) 674 [g2 movePoint:0 to:p1]; // correct x difference 675 else if ( Diff(p1.y, p2.y) > TOLERANCE ) 676 [g1 movePoint:MAXINT to:p2]; // correct y difference 677 } 678 else 679 { p = [g2 pointWithNum:0]; 680 [g1 movePoint:MAXINT to:p]; 681 } 682 } 683 else 684 { p = [g2 pointWithNum:0]; 685 [g1 movePoint:MAXINT to:p]; 686 } 687 } 688 else if ( ![g2 isKindOfClass:[VArc class]] ) /* g2 != arc - g1 is an arc */ 689 { 690 p = [g1 pointWithNum:MAXINT]; 691 [g2 movePoint:0 to:p]; 692 } 693 else /* g1, g2 == arc -> insert line */ 694 { p0 = [g1 pointWithNum:MAXINT]; 695 p1 = [g2 pointWithNum:0]; 696 if ( Diff(p0.x, p1.x) || Diff(p0.y, p1.y) ) 697 { VLine *line = [VLine line]; 698 [line setColor:[g1 color]]; 699 [line setWidth:[g1 width]]; 700 [line setVertices:p0 :p1]; 701 [list insertObject:line atIndex:[list indexOfObject:g1]+1]; 702 return YES; 703 } 704 } 705 dirty = YES; 706 return NO; 707} 708 709- (BOOL)closed 710{ int i, cnt = [list count]; 711 id g0, g1 = 0; 712 NSPoint beg, p0, p1; 713 714 if ( !cnt ) 715 return YES; 716 g0 = [list objectAtIndex:0]; 717 beg = [g0 pointWithNum:0]; 718 for (i=1; i<cnt; i++) 719 { g1 = [list objectAtIndex:i]; 720 721 p0 = [g0 pointWithNum:MAXINT]; 722 p1 = [g1 pointWithNum:0]; 723 /* connected with next object -> ok, continue */ 724 if ( Diff(p0.x, p1.x)<TOLERANCE && Diff(p0.y, p1.y)<TOLERANCE ) 725 { g0 = g1; 726 continue; 727 } 728 /* connected with beg -> ok, p1 is our new beg, continue */ 729 else if ( Diff(p0.x, beg.x)<TOLERANCE && Diff(p0.y, beg.y)<TOLERANCE ) 730 { g0 = g1; 731 beg = p1; 732 continue; 733 } 734 /* not connected -> open end */ 735 return NO; 736 } 737 /* last object connected with beg -> ok */ 738 p0 = [g0 pointWithNum:MAXINT]; 739 if ( Diff(p0.x, beg.x)<TOLERANCE && Diff(p0.y, beg.y)<TOLERANCE ) 740 return YES; 741 return NO; 742} 743 744- (void)closePath 745{ int i, iCnt; 746 VGraphic *g0, *g1 = nil; 747 NSPoint beg, p0, p1; 748 float dx=0, dy=0, dx0=0, dy0=0; 749 750 if ( ![list count] ) 751 return; 752 g0 = [list objectAtIndex:0]; 753 beg = [g0 pointWithNum:0]; 754 for ( i=1, iCnt = [list count]; i<iCnt; i++ ) 755 { g1 = [list objectAtIndex:i]; 756 757 p0 = [g0 pointWithNum:MAXINT]; 758 p1 = [g1 pointWithNum:0]; 759 /* gap */ 760 dx = Diff(p0.x, p1.x); 761 dy = Diff(p0.y, p1.y); 762 if ( dx > 10*TOLERANCE || dy > 10*TOLERANCE ) 763 { 764 /* if this is the end of a closed subpath we add no line */ 765 dx0 = Diff(p0.x, beg.x); 766 dy0 = Diff(p0.y, beg.y); 767 if ( dx0 > TOLERANCE || dy0 > TOLERANCE ) 768 { VLine *line = [VLine line]; 769 770 [line setColor:[g1 color]]; 771 [line setWidth:[g1 width]]; 772 773 // gap to next graphic smaller than to startgraphic -> close to nextG 774 if ( dx < dx0 && dy < dy0 && dx < dy0 && dy < dx0 ) 775 { 776 [line setVertices:p0 :p1]; 777 [list insertObject:line atIndex:i]; 778 dirty = YES; 779 graduateDirty = YES; 780 i++; iCnt++; 781 g0 = g1; 782 continue; 783 } 784 [line setVertices:p0 :beg]; 785 [list insertObject:line atIndex:i]; 786 dirty = YES; 787 graduateDirty = YES; 788 i++; iCnt++; 789 } 790 g0 = g1; 791 beg = p1; 792 } 793 else 794 g0 = g1; 795 } 796 797 p0 = [g0 pointWithNum:MAXINT]; 798 p1 = beg; 799 if ( /*iCnt==1 ||*/ Diff(p0.x, p1.x)>TOLERANCE || Diff(p0.y, p1.y)>TOLERANCE ) 800 { VLine *line = [VLine line]; 801 802 [line setColor:[g1 color]]; 803 [line setWidth:[g1 width]]; 804 [line setVertices:p0 :beg]; 805 [list addObject:line]; 806 dirty = YES; 807 graduateDirty = YES; 808 } 809} 810#if 0 811- (void)closePath 812{ int i, iCnt; 813 id g0, g1 = 0; 814 NSPoint beg, p0, p1; 815 816 if ( ![list count] ) 817 return; 818 g0 = [list objectAtIndex:0]; 819 beg = [g0 pointWithNum:0]; 820 for ( i=1, iCnt = [list count]; i<iCnt; i++ ) 821 { g1 = [list objectAtIndex:i]; 822 823 p0 = [g0 pointWithNum:MAXINT]; 824 p1 = [g1 pointWithNum:0]; 825 /* gap */ 826 if ( Diff(p0.x, p1.x)>10*TOLERANCE || Diff(p0.y, p1.y)>10*TOLERANCE ) 827 { 828 /* if this is the end of a closed subpath we add no line */ 829 if ( Diff(p0.x, beg.x)>TOLERANCE || Diff(p0.y, beg.y)>TOLERANCE ) 830 { VLine *line = [VLine line]; 831 832 [line setColor:[g1 color]]; 833 [line setWidth:[g1 width]]; 834 [line setVertices:p0 :beg]; 835 [list insertObject:line atIndex:i]; 836 dirty = YES; 837 i++; iCnt++; 838 } 839 g0 = g1; 840 beg = p1; 841 } 842 else 843 g0 = g1; 844 } 845 846 p0 = [g0 pointWithNum:MAXINT]; 847 p1 = beg; 848 if ( /*iCnt==1 ||*/ Diff(p0.x, p1.x)>TOLERANCE || Diff(p0.y, p1.y)>TOLERANCE ) 849 { VLine *line = [VLine line]; 850 851 [line setColor:[g1 color]]; 852 [line setWidth:[g1 width]]; 853 [line setVertices:p0 :beg]; 854 [list addObject:line]; 855 dirty = YES; 856 } 857} 858#endif 859 860/* optimize objects in list, so that they line up correctly 861 */ 862- (void)sortList 863{ int i, iCnt, begIx; 864 NSPoint beg, end, p; 865 id g; 866 NSMutableArray *jlist = list, *newList = [NSMutableArray array]; 867 float dist = TOLERANCE; 868 869 /* copy objects from jlist to nesList in correct order 870 */ 871 while ( (iCnt = [jlist count]) ) 872 { 873 g = [jlist objectAtIndex:0]; 874 [newList addObject:g]; 875 begIx = [newList count]-1; 876 beg = [g pointWithNum:0]; 877 end = [g pointWithNum:MAXINT]; 878 [jlist removeObject:g]; 879 880 iCnt = [jlist count]; 881 for ( i=0; i<iCnt; i++ ) 882 { g = [jlist objectAtIndex:i]; 883 p = [g pointWithNum:0]; 884 if ( SqrDistPoints(p, beg)<dist*dist ) /* start of new object fits to start of sequence */ 885 { [g changeDirection]; 886 [newList insertObject:g atIndex:begIx]; 887 beg = [g pointWithNum:0]; 888 } 889 else if ( SqrDistPoints(p, end)<dist*dist ) /* start of new object fits to end of sequence */ 890 { [newList addObject:g]; 891 end = [g pointWithNum:MAXINT]; 892 dirty = YES; 893 } 894 else 895 { p = [g pointWithNum:MAXINT]; 896 if ( SqrDistPoints(p, beg)<dist*dist ) /* end of new object fits to start of sequence */ 897 { [newList insertObject:g atIndex:begIx]; 898 beg = [g pointWithNum:0]; 899 } 900 else if ( SqrDistPoints(p, end)<dist*dist ) /* end of new object fits to end of sequence */ 901 { [g changeDirection]; 902 [newList addObject:g]; 903 end = [g pointWithNum:MAXINT]; 904 } 905 else 906 continue; 907 } 908 [jlist removeObject:g]; /* start from the beginning of jlist */ 909 iCnt = [jlist count]; 910 i = -1; 911 } 912 } 913 [self setList:newList]; 914} 915 916/* jlist list of objects 917 * dist maximum distance between objects 918 */ 919- (void)complexJoin:(NSMutableArray*)jlist distance:(float)dist 920{ int i; 921 VGraphic *g; 922 923 // jlist is selected list 924 [jlist removeObject:self]; 925 926 /* extract paths and add all objects inside jlist to list 927 */ 928 for ( i=0; i < [jlist count]; i++ ) 929 { 930 g = [jlist objectAtIndex:i]; 931 [g setOutputStream:nil]; 932 if ( [g isKindOfClass:[VRectangle class]] ) 933 { g = [(VRectangle*)g pathRepresentation]; 934 [jlist replaceObjectAtIndex:i withObject:g]; 935 } 936 if ( [g isKindOfClass:[VPath class]] ) 937 { int j, jCnt = [[(VPath*)g list] count]; 938 939 for ( j=0; j<jCnt; j++ ) /* copy object from g to jlist */ 940 [list addObject:[[(VPath*)g list] objectAtIndex:j]]; 941 } 942 else 943 [list addObject:g]; 944 [jlist removeObject:g]; 945 i--; 946 } 947 948 /* set new jlist 949 */ 950 [self optimizePath:dist]; 951 952 [jlist addObject:self]; 953 [self setSelected:YES]; 954 955 /* if an object has no contact we close to the start of the sequence 956 */ 957 if (filled) 958 [self closePath]; 959 coordBounds = bounds = NSZeroRect; 960 dirty = YES; 961 graduateDirty = YES; 962} 963#if 0 // old 964- (void)complexJoin:jlist distance:(float)dist 965{ int i, iCnt, begIx; 966 NSPoint beg, end, p; 967 id g; 968 //BOOL hasStart = NO; /* it may happen that we join subpaths at their starting and end points! */ 969 970 [jlist removeObject:self]; 971 972 /* extract paths inside jlist to jlist 973 */ 974 iCnt = [jlist count]; 975 for ( i=0; i<iCnt; i++ ) 976 { 977 g = [jlist objectAtIndex:i]; 978 [g setOutputStream:nil]; 979 if ( [g isKindOfClass:[VRectangle class]] ) 980 { g = [g pathRepresentation]; 981 [jlist replaceObjectAtIndex:i withObject:g]; 982 } 983 if ( [g isKindOfClass:[VPath class]] ) 984 { int j, jCnt = [[g list] count]; 985 986 for ( j=0; j<jCnt; j++ ) /* copy object from g to jlist */ 987 [jlist addObject:[[g list] objectAtIndex:j]]; 988 [jlist removeObject:g]; 989 i--; 990 } 991 } 992 993 /* copy objects from list to beginning of jlist 994 */ 995 for ( i=[list count]-1; i>=0; i-- ) 996 [jlist insertObject:[list objectAtIndex:i] atIndex:0]; 997 [list removeAllObjects]; 998 999 /* copy objects from jlist to list in correct order 1000 */ 1001 while ( (iCnt = [jlist count]) ) 1002 { 1003 g = [jlist objectAtIndex:0]; 1004 [list addObject:g]; 1005 begIx = [list count]-1; 1006 beg = [g pointWithNum:0]; 1007 end = [g pointWithNum:MAXINT]; 1008 [jlist removeObject:g]; 1009 1010 iCnt = [jlist count]; 1011 for ( i=0; i<iCnt; i++ ) 1012 { g = [jlist objectAtIndex:i]; 1013 p = [g pointWithNum:0]; 1014 if ( SqrDistPoints(p, beg)<dist*dist ) /* start of new object fits to start of sequence */ 1015 { [g changeDirection]; 1016 [list insertObject:g atIndex:begIx]; 1017 beg = [g pointWithNum:0]; 1018 [self closeGapBetween:g and:[list objectAtIndex:begIx+1]]; 1019 } 1020 else if ( SqrDistPoints(p, end)<dist*dist ) /* start of new object fits to end of sequence */ 1021 { [list addObject:g]; 1022 end = [g pointWithNum:MAXINT]; 1023 [self closeGapBetween:[list objectAtIndex:[list count]-2] and:g]; 1024 } 1025 else 1026 { p = [g pointWithNum:MAXINT]; 1027 if ( SqrDistPoints(p, beg)<dist*dist ) /* end of new object fits to start of sequence */ 1028 { [list insertObject:g atIndex:begIx]; 1029 beg = [g pointWithNum:0]; 1030 [self closeGapBetween:g and:[list objectAtIndex:begIx+1]]; 1031 } 1032 else if ( SqrDistPoints(p, end)<dist*dist ) /* end of new object fits to end of sequence */ 1033 { [g changeDirection]; 1034 [list addObject:g]; 1035 end = [g pointWithNum:MAXINT]; 1036 [self closeGapBetween:[list objectAtIndex:[list count]-2] and:g]; 1037 } 1038 else if ( SqrDistPoints(beg, end) < TOLERANCE*TOLERANCE ) 1039 break; // closed sequence - time 1040 else // open sequence // close to start or search next obj 1041 /* wenn current nicht closed zu start/beg / distance kleiner 1.0 */ 1042 /* hier muessen wir irgendwie das objekt mit dem kleinsten Abstand finden !? */ 1043 /* 4 distanzen merken ? - nur den kleinsten der 4 (zum Start oder continued !? - merken) */ 1044 /* objektIndex merken - flag setzen */ 1045 continue; 1046 } 1047 // if ( distanceObject ) beg / end setzen etc 1048 1049 [jlist removeObject:g]; /* start from the beginning of jlist */ 1050 iCnt = [jlist count]; 1051 i = -1; 1052 } 1053 } 1054 1055 /* if an object has no contact we close to the start of the sequence 1056 */ 1057 if (filled) 1058 [self closePath]; 1059 coordBounds = bounds = NSZeroRect; 1060 dirty = YES; 1061 graduateDirty = YES; 1062} 1063#endif 1064 1065- (void)join:obj 1066{ BOOL getDirection = NO; 1067 1068 if (!list) 1069 list = [[NSMutableArray allocWithZone:[self zone]] init]; 1070 1071 if ( ![list count] ) 1072 getDirection = YES; 1073 1074 if ( [obj isKindOfClass:[VRectangle class]] ) 1075 obj = [obj pathRepresentation]; 1076 if ( [obj isKindOfClass:[NSMutableArray class]] ) 1077 { 1078 [self complexJoin:obj distance:1.0]; // 0.1 30.0*TOLERANCE 1079 } 1080 /* two closed paths -> we simply place the objects of obj at the end of our list 1081 */ 1082 else if ( ([obj isKindOfClass:[VPath class]] && [obj closed] && [self closed]) ) 1083 //([obj isKindOfClass:[VPolyLine class]] && [obj filled] && [self closed]) 1084 { NSMutableArray *olist = [obj list]; 1085 int i, cnt = [olist count]; 1086 1087 for (i=0; i<cnt; i++) 1088 [list addObject:[olist objectAtIndex:i]]; 1089 } 1090 /* add other filled objects simply to the end of our list */ 1091 else if ( [obj respondsToSelector:@selector(filled)] && [obj filled] && [self closed] ) 1092 { 1093 [list addObject:obj]; 1094 } 1095 /* add object to the end of ourself 1096 */ 1097 else 1098 { 1099 if (![list count]) /* obj is the 1st object in list */ 1100 { 1101 [list addObject:obj]; 1102 [obj setOutputStream:nil]; 1103 } 1104 else if ([obj selectedKnobIndex] != -1) /* a knob of the new object is selected */ 1105 { 1106 if ([self selectedKnobIndex] != -1) /* a knob of us is selected */ 1107 ; /* add new object the way that both selected ends are connected */ 1108 else 1109 { [self addToClosestEnd:obj]; /* add new object with selected end to our closest end */ 1110 [obj setOutputStream:nil]; 1111 } 1112 } 1113 else /* connect the closest endpoints */ 1114 { [self addToClosestEnd:obj]; 1115 [obj setOutputStream:nil]; 1116 } 1117 } 1118 1119 //getDirection = YES; // for 1Line Font Direction editing ! ! ! 1120 if ( getDirection ) 1121 { int sIx=0, eIx; 1122 1123 eIx = [self getLastObjectOfSubPath2:sIx]; 1124 isDirectionCCW = [self directionOfSubPath:sIx :eIx]; 1125 } 1126 1127 if ( filled || [self closed] ) 1128 [self setDirectionCCW:[self isDirectionCCW]]; 1129 [self optimize]; 1130 [self deselectAll]; 1131 selectedObject = -1; 1132 coordBounds = bounds = NSZeroRect; 1133 dirty = YES; 1134 graduateDirty = YES; 1135} 1136 1137- (void)addToClosestEnd:obj 1138{ NSPoint pa, pb, p1, p2; 1139 float da1, da2, db1, db2, dist = 100.0*TOLERANCE; 1140 int ix; 1141 VLine *g = 0; 1142 1143 if ([obj selectedKnobIndex] >= 0) /* knob of obj is selected */ 1144 { pa = [obj pointWithNum:[obj selectedKnobIndex]]; 1145 if ( [obj selectedKnobIndex]==0 ) 1146 pb.x = pb.y = LARGE_COORD; 1147 else 1148 pb = pa, pa.x = pa.y = LARGE_COORD; 1149 } 1150 else /* no knob of object selected */ 1151 { if ([obj isKindOfClass:[VPath class]]) 1152 [obj getEndPoints:&pa :&pb]; 1153 else 1154 { pa = [obj pointWithNum:0]; 1155 pb = [obj pointWithNum:MAXINT]; 1156 } 1157 } 1158 [self getEndPoints:&p1 :&p2]; 1159 1160 /* closer 1st point (p1) -> add object at beginning of list */ 1161 da1 = SqrDistPoints(pa, p1); 1162 da2 = SqrDistPoints(pa, p2); 1163 db1 = SqrDistPoints(pb, p1); 1164 db2 = SqrDistPoints(pb, p2); 1165 /* circle */ 1166 if ( [obj isKindOfClass:[VArc class]] && Abs([obj angle]) >= 360.0 ) 1167 ix = [list count]; 1168 else if ( da1<da2 && da1<db1 && da1<db2 ) /* pa / p1 */ 1169 { 1170 [obj changeDirection]; 1171 if (da1 && da1 < dist) // close the gap with a line 1172 //if (da1) // close the gap with a line 2005-03-29 1173 { g = [VLine line]; 1174 [g setVertices:pa :p1]; 1175 [list insertObject:g atIndex:0]; 1176 } 1177 ix = 0; /* the new object becomes our 1st object in the list */ 1178 } 1179 else if ( da2<=da1 && da2<=db1 && da2<=db2 ) /* pa / p2 */ 1180 { 1181 if (da2 && da2 < dist) // close the gap with a line 1182 //if (da2) // close the gap with a line 2005-03-29 1183 { g = [VLine line]; 1184 [g setVertices:p2 :pa]; 1185 [list insertObject:g atIndex:[list count]]; 1186 } 1187 ix = [list count]; /* the new object becomes our last object */ 1188 } 1189 else if ( db1<=da1 && db1<=da2 && db1<=db2 ) /* pb / p1 */ 1190 { 1191 if (db1 && db1 < dist) // close the gap with a line 1192 //if (db1) // close the gap with a line 2005-03-29 1193 { g = [VLine line]; 1194 [g setVertices:pb :p1]; 1195 [list insertObject:g atIndex:0]; 1196 } 1197 ix = 0; /* the new object becomes our 1st object in the list */ 1198 } 1199 else /* pb / p2 */ 1200 { 1201 [obj changeDirection]; 1202 if (db2 && db2 < dist) // close the gap with a line 1203 //if (db2) // close the gap with a line 2005-03-29 1204 { g = [VLine line]; 1205 [g setVertices:p2 :pb]; 1206 [list insertObject:g atIndex:[list count]]; 1207 } 1208 ix = [list count]; /* the new object becomes our last object */ 1209 } 1210 1211 [g setColor:color]; 1212 [g setWidth:width]; 1213 if ([obj isKindOfClass:[VPath class]]) 1214 { [self addList:[obj list] at:ix]; 1215 [[obj list] removeAllObjects]; 1216 } 1217 else 1218 [list insertObject:obj atIndex:ix]; 1219} 1220 1221/* split 1222 * modified: 2012-01-20 ( use getLastObjectOfSubPath2: to split open paths faster) 1223 * copy all objects from the path to the ulist 1224 * the ungrouped objects are selected 1225 */ 1226- (void)splitTo:ulist 1227{ int i, begIx = 0, endIx = 0, cnt = [list count]; 1228 1229 endIx = [self getLastObjectOfSubPath2:begIx]; 1230 1231 /* path with subpaths splitted to paths ! */ 1232 if (endIx != cnt-1) 1233 { 1234 while (endIx <= cnt-1) 1235 { 1236 1237 if (begIx == endIx) 1238 { VGraphic *g = [list objectAtIndex:begIx]; 1239 1240 [g setSelected:YES]; 1241 [g setColor:color]; 1242 [g setWidth:width]; 1243 if (([g isKindOfClass:[VArc class]] && Diff(Abs([g angle]), 360.0) <= TOLERANCE) || 1244 [g isKindOfClass:[VRectangle class]] || [g isKindOfClass:[VPolyLine class]]) 1245 { 1246 [g setFilled:NO]; 1247 if (fillColor) 1248 { [(VArc*)g setFillColor:fillColor]; 1249 [(VArc*)g setStepWidth:stepWidth]; 1250 [(VArc*)g setRadialCenter:radialCenter]; 1251 } 1252 } 1253 [ulist addObject:g]; 1254 } 1255 else 1256 { VPath *pg = [VPath path]; 1257 1258 [pg setFilled:NO]; 1259 [pg setSelected:YES]; 1260 [pg setColor:color]; 1261 if (fillColor) 1262 { [pg setFillColor:fillColor]; 1263 [pg setStepWidth:stepWidth]; 1264 [pg setRadialCenter:radialCenter]; 1265 } 1266 [pg setWidth:width]; 1267 for (i=begIx; i<=endIx; i++) 1268 [[pg list] addObject:[list objectAtIndex:i]]; 1269 [ulist addObject:pg]; 1270 } 1271 begIx = endIx+1; 1272 if (begIx > cnt-1) 1273 break; 1274 endIx = [self getLastObjectOfSubPath2:begIx]; // tolerance:TOLERANCE 1275 } 1276 } 1277 else 1278 for ( i = 0, cnt = [list count]; i < cnt; i++ ) 1279 { VGraphic *g = [list objectAtIndex:i]; 1280 1281 [g setSelected:YES]; 1282 [g setColor:color]; 1283 [g setWidth:width]; 1284 [ulist addObject:g]; 1285 } 1286} 1287 1288 1289- (void)setSize:(NSSize)newSize 1290{ NSRect bRect = [self coordBounds]; 1291 1292 [self scale:((bRect.size.width) ? newSize.width /bRect.size.width : 1.0) 1293 :((bRect.size.height) ? newSize.height/bRect.size.height : 1.0) 1294 withCenter:bRect.origin]; 1295} 1296- (NSSize)size 1297{ NSRect bRect = [self coordBounds]; 1298 return bRect.size; 1299} 1300 1301- (void)setBoundsZero 1302{ 1303 coordBounds = bounds = NSZeroRect; 1304} 1305 1306/* created: 16.09.95 1307 * modified: 2000-11-03 1308 * 1309 * Returns the bounds. 1310 */ 1311- (NSRect)coordBounds 1312{ 1313 if ( ![list count] ) 1314 return NSZeroRect; 1315 1316 if (coordBounds.size.width == 0.0 && coordBounds.size.height == 0.0) 1317 { NSRect rect; 1318 int i, cnt = [list count]-1; 1319 1320 coordBounds = [[list objectAtIndex:cnt] coordBounds]; 1321 for (i=cnt-1; i>=0; i--) 1322 { rect = [[list objectAtIndex:i] coordBounds]; 1323 coordBounds = VHFUnionRect(rect, coordBounds); 1324 } 1325 } 1326 return coordBounds; 1327} 1328 1329- (NSRect)bounds 1330{ 1331 if ( ![list count] ) 1332 return NSZeroRect; 1333 1334 if (bounds.size.width == 0.0 && bounds.size.height == 0.0) 1335 { NSRect rect; 1336 int i, cnt = [list count]-1; 1337 1338 bounds = [[list objectAtIndex:cnt] bounds]; 1339 for (i=cnt-1; i>=0; i--) 1340 { rect = [[list objectAtIndex:i] bounds]; 1341 bounds = VHFUnionRect(rect, bounds); 1342 } 1343 } 1344 return bounds; 1345} 1346 1347/* 1348- (NSRect)extendedBoundsWithScale:(float)scale 1349{ NSRect rect, bRect; 1350 int i, cnt = [list count]-1; 1351 1352 if ( cnt<0 ) 1353 return NSZeroRect; 1354 bRect = [[list objectAtIndex:cnt] extendedBoundsWithScale:scale]; 1355 for (i=cnt-1; i>=0; i--) 1356 { rect = [[list objectAtIndex:i] extendedBoundsWithScale:scale]; 1357 bRect = NSUnionRect(rect , bRect); 1358 } 1359 return bRect; 1360} 1361*/ 1362/* created: 22.10.95 1363 * modified: 28.02.97 1364 * 1365 * Returns the bounds at the given rotation. 1366 */ 1367- (NSRect)boundsAtAngle:(float)angle withCenter:(NSPoint)cp 1368{ NSRect rect, bRect; 1369 int i, cnt = [list count]-1; 1370 1371 bRect = [(VGraphic*)[list objectAtIndex:cnt] boundsAtAngle:angle withCenter:cp]; 1372 for (i=cnt-1; i>=0; i--) 1373 { rect = [(VGraphic*)[list objectAtIndex:i] boundsAtAngle:angle withCenter:cp]; 1374 bRect = NSUnionRect(rect , bRect); 1375 } 1376 return bRect; 1377} 1378 1379- (void)drawKnobs:(NSRect)rect direct:(BOOL)direct scaleFactor:(float)scaleFactor 1380{ 1381 if ( (NSIsEmptyRect(rect) || !NSIsEmptyRect(NSIntersectionRect(rect, [self extendedBoundsWithScale:scaleFactor]))) ) 1382 { 1383 if ( VHFIsDrawingToScreen() && isSelected ) 1384 { int i, cnt = [list count], step = (cnt<2000) ? 1 : cnt/2000; 1385 1386 for ( i=cnt-1; i>=0; i-=step ) 1387 { id obj = [list objectAtIndex:i]; 1388 1389 if ( isSelected || [obj isSelected] ) 1390 { BOOL sel = [obj isSelected]; 1391 1392 [obj setSelected:YES]; 1393 [obj drawKnobs:rect direct:direct scaleFactor:scaleFactor]; 1394 [obj setSelected:sel]; 1395 } 1396 } 1397 } 1398 } 1399} 1400- (void)drawControls:(NSRect)rect direct:(BOOL)direct scaleFactor:(float)scaleFactor 1401{ 1402 if ( (NSIsEmptyRect(rect) || 1403 !NSIsEmptyRect(NSIntersectionRect(rect, [self extendedBoundsWithScale:scaleFactor]))) ) 1404 { 1405 if ( VHFIsDrawingToScreen() && isSelected ) 1406 { int i, cnt = [list count], step = (cnt<2000) ? 1 : cnt/2000; 1407 1408 for ( i=cnt-1; i>=0; i-=step ) 1409 { id obj = [list objectAtIndex:i]; 1410 BOOL sel = [obj isSelected]; 1411 1412 [obj setSelected:YES]; 1413 //[obj drawKnobs:rect direct:direct scaleFactor:scaleFactor]; 1414 [obj drawControls:rect direct:direct scaleFactor:scaleFactor]; 1415 [obj setSelected:sel]; 1416 if ([obj isMemberOfClass:[VCurve class]]) 1417 { [NSBezierPath setDefaultLineWidth:1.0/scaleFactor]; 1418 [NSBezierPath strokeLineFromPoint:[obj pointWithNum:0] toPoint:[obj pointWithNum:1]]; 1419 [NSBezierPath strokeLineFromPoint:[obj pointWithNum:3] toPoint:[obj pointWithNum:2]]; 1420 } 1421 } 1422 } 1423 } 1424} 1425 1426/* 1427 * Depending on the pt_num passed in, return the rectangle 1428 * that should be used for scrolling purposes. When the rectangle 1429 * passes out of the visible rectangle then the screen should 1430 * scroll. If the first and last points are selected, then the second 1431 * and third points are included in the rectangle. If the second and 1432 * third points are selected, then they are used by themselves. 1433 */ 1434- (NSRect)scrollRect:(int)pt_num inView:(id)aView 1435{ VGraphic *g = nil; 1436 1437 if (pt_num != -1) 1438 { 1439 if ( pt_num >= [self numPoints] ) 1440 { g = [list objectAtIndex:[list count]-1]; 1441 pt_num = MAXINT; 1442 } 1443 else if ( !pt_num ) 1444 g = [list objectAtIndex:0]; 1445 else 1446 { int i, cnt, pCnt = 0, prevPCnt = 0; 1447 1448 for (i=0, cnt = [list count]; i<cnt; i++) 1449 { pCnt += [[list objectAtIndex:i] numPoints]; 1450 if ( pCnt > pt_num ) 1451 break; // to this object refers our pt_num 1452 prevPCnt = pCnt; // count of pts befor this gr 1453 } 1454 g = [list objectAtIndex:i]; 1455 pt_num -= prevPCnt; 1456 } 1457 return [g scrollRect:pt_num inView:aView]; 1458 } 1459 return [self bounds]; 1460} 1461 1462/* 1463 * This method constains the point to the bounds of the view passed 1464 * in. Like the method above, the constaining is dependent on the 1465 * control point that has been selected. 1466 */ 1467- (void)constrainPoint:(NSPoint *)aPt andNumber:(int)pt_num toView:aView 1468{ VGraphic *g = nil; 1469 1470 if (pt_num < 0) 1471 return; 1472 1473 if ( pt_num >= [self numPoints] ) 1474 { g = [list objectAtIndex:[list count]-1]; 1475 pt_num = MAXINT; 1476 } 1477 else if ( !pt_num ) 1478 g = [list objectAtIndex:0]; 1479 else 1480 { int i, cnt, pCnt = 0, prevPCnt = 0; 1481 1482 for (i=0, cnt = [list count]; i<cnt; i++) 1483 { pCnt += [[list objectAtIndex:i] numPoints]; 1484 if ( pCnt > pt_num ) 1485 break; // to this object refers our pt_num 1486 prevPCnt = pCnt; // count of pts befor this gr 1487 } 1488 g = [list objectAtIndex:i]; 1489 pt_num -= prevPCnt; 1490 } 1491 [g constrainPoint:aPt andNumber:pt_num toView:aView]; 1492} 1493 1494- (NSPoint)nearestPointOnObject:(int*)objIndex distance:(float*)distance toPoint:(NSPoint)pt 1495{ int i, cnt = [list count]; 1496 NSPoint cpt = NSZeroPoint, tpt; 1497 1498 *distance = MAXCOORD; 1499 1500 /* search nearest object to pt */ 1501 for (i=0; i<cnt; i++) 1502 { float dist = MAXCOORD; 1503 VGraphic *g = [list objectAtIndex:i]; 1504 NSRect bRect = [g bounds]; 1505 1506 bRect = NSInsetRect(bRect, -2.0, -2.0); 1507 if ( !NSPointInRect(pt, bRect) ) 1508 continue; 1509 if ([g isKindOfClass:[VLine class]]) 1510 { NSPoint p0, p1; 1511 1512 [(VLine*)g getVertices:&p0 :&p1]; 1513 dist = pointOnLineClosestToPoint(p0, p1, pt, &tpt); 1514 } 1515 else if ([g isKindOfClass:[VArc class]]) 1516 dist = [(VArc*)g getPointOnArcClosestToPoint:pt intersection:&tpt]; 1517 //dist = pointOnArcClosestToPoint([g center], [g radius], [(VArc*)g begAngle], [g angle], pt, &tpt); 1518 else if ([g isKindOfClass:[VCurve class]]) 1519 { NSPoint pc[4]; 1520 1521 [(VCurve*)g getVertices:&pc[0] :&pc[1] :&pc[2] :&pc[3]]; 1522 dist = pointOnCurveNextToPoint(&tpt, pc, &pt); 1523 } 1524 else if ([g isKindOfClass:[VPolyLine class]]) 1525 { NSPoint ppt; 1526 float d; 1527 int j, count = [g numPoints]; 1528 1529 dist = MAXCOORD; 1530 /* check nearest polyline line to pt */ 1531 for (j=0; j<count-1; j++) 1532 { 1533 if ((d=pointOnLineClosestToPoint([g pointWithNum:j], [g pointWithNum:j+1], pt, &ppt)) <= dist) 1534 { tpt = ppt; 1535 dist = d; 1536 } 1537 } 1538 } 1539 else if ([g isKindOfClass:[VRectangle class]]) 1540 { VPath *rp = [(VRectangle*)g pathRepresentation]; 1541 int j, rcnt = [[rp list] count]; 1542 1543 dist = MAXCOORD; 1544 for (j=0; j<rcnt; j++) 1545 { float d = MAXCOORD; 1546 VGraphic *rg = [[rp list] objectAtIndex:j]; 1547 NSRect bRect = [g bounds]; 1548 NSPoint rpt; 1549 1550 bRect = NSInsetRect(bRect, -2.0, -2.0); 1551 if ( !NSPointInRect(pt, bRect) ) 1552 continue; 1553 if ([rg isKindOfClass:[VLine class]]) 1554 { NSPoint p0, p1; 1555 1556 [(VLine*)rg getVertices:&p0 :&p1]; 1557 d = pointOnLineClosestToPoint(p0, p1, pt, &rpt); 1558 } 1559 else if ([rg isKindOfClass:[VArc class]]) 1560 d = [(VArc*)rg getPointOnArcClosestToPoint:pt intersection:&rpt]; 1561 //d = pointOnArcClosestToPoint([rg center], [rg radius], [(VArc*)rg begAngle], [rg angle], pt, &rpt); 1562 if (d <= dist) 1563 { tpt = rpt; 1564 dist = d; 1565 } 1566 } 1567 } 1568 if (dist <= *distance) 1569 { cpt = tpt; 1570 *objIndex = i; 1571 *distance = dist; 1572 } 1573 } 1574 return cpt; 1575} 1576 1577- (VGraphic*)addPointAt:(NSPoint)pt 1578{ NSMutableArray *spList = nil; 1579 int i, splitI = -1; 1580 NSPoint cpt, start, end, sgStart, sgEnd; 1581 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 1582 float distance=MAXCOORD; 1583 VGraphic *splitg=nil; 1584 1585 cpt = [self nearestPointOnObject:&splitI distance:&distance toPoint:pt]; 1586 1587 splitg = [list objectAtIndex:splitI]; 1588 1589 /* VPolyLine */ 1590 if ([splitg isKindOfClass:[VPolyLine class]]) 1591 { 1592 [(VPolyLine*)splitg addPointAt:pt]; 1593 return self; 1594 } 1595 1596 start = [self pointWithNum:0]; 1597 end = [self pointWithNum:MAXINT]; 1598 if ( (Diff(start.x, cpt.x) < 100.0*TOLERANCE && Diff(start.y, cpt.y) < 100.0*TOLERANCE) || 1599 (Diff(end.x, cpt.x) < 100.0*TOLERANCE && Diff(end.y, cpt.y) < 100.0*TOLERANCE) ) 1600 { [pool release]; 1601 return nil; 1602 } 1603 sgStart = [splitg pointWithNum:0]; 1604 sgEnd = [splitg pointWithNum:MAXINT]; 1605 if ((Diff(sgStart.x, cpt.x) > 100.0*TOLERANCE || Diff(sgStart.y, cpt.y) > 100.0*TOLERANCE) && 1606 (Diff(sgEnd.x, cpt.x) > 100.0*TOLERANCE || Diff(sgEnd.y, cpt.y) > 100.0*TOLERANCE)) 1607 spList = [splitg getListOfObjectsSplittedFrom:&cpt :1]; 1608 else // nothing to add at s or e point 1609 { [pool release]; 1610 return nil; 1611 } 1612 /* remove splitg - add graphics from splist */ 1613 [list removeObjectAtIndex:splitI]; 1614 for (i=[spList count]-1; i>=0; i--) 1615 [list insertObject:[spList objectAtIndex:i] atIndex:splitI]; 1616 1617 [pool release]; 1618 coordBounds = bounds = NSZeroRect; 1619 dirty = YES; 1620 graduateDirty = YES; 1621 return self; 1622} 1623 1624/* the indices of the graphics around selected Point (which will be removed later) */ 1625/* in correct order */ 1626/* the index which will have the changed graphic will be returned */ 1627- (int)changedValuesForRemovePointUndo:(int*)changedIx :(int*)chPt_num :(NSPoint*)changedPt 1628{ int removedIx = -1, pt_num, begIx, endIx, nPts; 1629 VGraphic *sg = [list objectAtIndex:selectedObject], *g1; // selectedObject 1630 NSPoint sgPtStart, sgPtEnd; 1631 BOOL connected = NO; 1632 1633 if ( ![list count] || selectedObject < 0) 1634 return -4; 1635 1636 pt_num = [sg selectedKnobIndex]; 1637 nPts = [sg numPoints]; 1638 1639 /* point inside PolyLine */ 1640 if ([sg isKindOfClass:[VPolyLine class]] && pt_num && pt_num < nPts-1 && nPts > 2) 1641 { 1642 chPt_num[0] = pt_num; // we need pt_num inside PolyLine 1643 changedIx[0] = selectedObject; // and index of PolyLine inside Path 1644 removedIx = -1; 1645 return removedIx; 1646 } 1647 1648 /* beg/end to selectedObjects subPath, + or - gr */ 1649 begIx = [self getFirstObjectOfSubPath:selectedObject]; 1650 endIx = [self getLastObjectOfSubPath:begIx]; 1651 if (begIx == endIx) 1652 endIx = [list count]-1; 1653 1654 if (([sg isKindOfClass:[VCurve class]] && (pt_num == 1 || pt_num == 2)) || // curve points 1,2 1655 ([sg isKindOfClass:[VArc class]] && pt_num == 2)) // arc center 1656 return -4; 1657 1658 sgPtStart = [sg pointWithNum:0]; // notice old start point 1659 sgPtEnd = [sg pointWithNum:MAXINT]; // notice old end point 1660 1661 /* get the connected Graphic to sg (selectedObject) */ 1662 if (!pt_num) // corresponding gr is bevor sg 1663 { NSPoint g1PtEnd; 1664 1665 g1 = (selectedObject-1>=begIx) ? [list objectAtIndex:selectedObject-1] : [list objectAtIndex:endIx]; 1666 if ([sg isKindOfClass:[VArc class]] && [g1 isKindOfClass:[VArc class]]) 1667 return -4; // two arcs we cant remove one and close the path Fix Me: open path and arc at end 1668 1669 g1PtEnd = [g1 pointWithNum:MAXINT]; 1670 if ( Diff(g1PtEnd.x, sgPtStart.x) < TOLERANCE && Diff(g1PtEnd.y, sgPtStart.y) < TOLERANCE) 1671 connected = YES; 1672 1673 if (!connected && [sg isKindOfClass:[VPolyLine class]] && nPts > 2) 1674 { 1675 chPt_num[0] = pt_num; // we need pt_num inside PolyLine 1676 changedIx[0] = selectedObject; // and index of PolyLine inside Path 1677 changedPt[0] = [sg pointWithNum:pt_num]; // old start point 1678 removedIx = -3; 1679 //[(VPolyLine*)g removePointWithNum:pt_num]; 1680 } 1681 else if (!connected) 1682 { 1683 changedIx[0] = -1; 1684 removedIx = selectedObject; // notice the removed Graphic ! 1685 changedPt[0] = [g1 pointWithNum:MAXINT]; // no point 1686 chPt_num[0] = -1; 1687 //[list removeObjectAtIndex:selectedObject]; 1688 } 1689 1690 else if ([sg isKindOfClass:[VPolyLine class]] && nPts > 2 && ![g1 isKindOfClass:[VArc class]]) 1691 { 1692 chPt_num[0] = pt_num; // we need pt_num inside PolyLine // was pt_num+1 1693 changedIx[0] = selectedObject; // and index of PolyLine inside Path 1694 changedPt[0] = [sg pointWithNum:pt_num]; // old start point // was pt_num+1 1695 removedIx = -2; 1696 chPt_num[1] = MAXINT; 1697 changedIx[1] = (selectedObject-1>=begIx) ? (selectedObject-1) : (endIx); 1698 changedPt[1] = [g1 pointWithNum:MAXINT]; // g1 end will be moved to new sg start 1699 //[(VPolyLine*)g removePointWithNum:pt_num]; 1700 //[g1 movePoint:MAXINT to:[g pointWithNum:0]]; // move g1 end to new start of polyLine 1701 } 1702 /* else if ([g1 isKindOfClass:[VPolyLine class]] && [g1 numPoints] > 2) 1703 { 1704 chPt_num[0] = [g1 numPoints]-2; // we need pt_num inside PolyLine 1705 changedIx[0] = (selectedObject-1>=begIx) ? (selectedObject-1) : (endIx); // and index of PolyLine inside Path 1706 changedPt[0] = [g1 pointWithNum:[g1 numPoints]-2]; // old start point 1707 removedIx = -3; 1708 //[(VPolyLine*)g1 removePointWithNum:MAXINT]; 1709 //[g1 movePoint:MAXINT to:gPtStart]; // move previous (now last) point of polyline to close path 1710 } */ 1711 else if ([g1 isKindOfClass:[VArc class]]) // remove g1 1712 { 1713 changedIx[0] = selectedObject; 1714 removedIx = (selectedObject-1>=begIx) ? (selectedObject-1) : (endIx); 1715 changedPt[0] = [sg pointWithNum:0]; // old start point 1716 chPt_num[0] = 0; 1717 //[g movePoint:0 to:[g1 pointWithNum:0]]; // close Gap with sg start to g1 start 1718 //[list removeObjectAtIndex:(selectedObject-1>=begIx) ? (selectedObject-1) : (endIx)]; 1719 } 1720 else // remove g 1721 { 1722 changedIx[0] = (selectedObject-1>=begIx) ? (selectedObject-1) : (endIx); 1723 removedIx = selectedObject; 1724 changedPt[0] = [g1 pointWithNum:MAXINT]; // old end point 1725 chPt_num[0] = [g1 numPoints]-1; 1726 //[g1 movePoint:MAXINT to:gPtEnd]; // close Gap with g1 end to g end 1727 //[list removeObjectAtIndex:selectedObject]; 1728 } 1729 } 1730 else // corresponding gr is behind g 1731 { NSPoint g1PtStart ; 1732 1733 g1 = (selectedObject+1<=endIx) ? [list objectAtIndex:selectedObject+1] : [list objectAtIndex:begIx]; 1734 1735 if ([sg isKindOfClass:[VArc class]] && [g1 isKindOfClass:[VArc class]]) 1736 return -4; // two arcs we cant remove one and close the path Fix Me: open path and arc at end 1737 1738 g1PtStart = [g1 pointWithNum:0]; 1739 if ( Diff(g1PtStart.x, sgPtEnd.x) < TOLERANCE && Diff(g1PtStart.y, sgPtEnd.y) < TOLERANCE) 1740 connected = YES; 1741 1742 if (!connected && [sg isKindOfClass:[VPolyLine class]] && nPts > 2) 1743 { 1744 chPt_num[0] = nPts-1; // we need pt_num inside PolyLine 1745 changedIx[0] = selectedObject; // and index of PolyLine inside Path 1746 changedPt[0] = [sg pointWithNum:nPts-1]; // old start point 1747 removedIx = -3; 1748 //[(VPolyLine*)sg removePointWithNum:pt_num]; 1749 } 1750 else if (!connected) 1751 { 1752 changedIx[0] = -1; 1753 removedIx = selectedObject; // notice only the removed Gr 1754 changedPt[0] = [sg pointWithNum:0]; 1755 chPt_num[0] = -1; 1756 //[list removeObjectAtIndex:selectedObject]; 1757 } 1758 1759 else if ([g1 isKindOfClass:[VPolyLine class]] && [g1 numPoints] > 2 && ![sg isKindOfClass:[VArc class]]) 1760 { 1761 chPt_num[0] = 0; // we need pt_num inside PolyLine // was 1 1762 changedIx[0] = (selectedObject+1 <= endIx) ? (selectedObject+1) : (begIx); // index of PolyLine inside Path 1763 changedPt[0] = [g1 pointWithNum:0]; // old point // was 1 1764 removedIx = -2; 1765 chPt_num[1] = MAXINT; 1766 changedIx[1] = selectedObject; 1767 changedPt[1] = [sg pointWithNum:MAXINT]; // sg end will be moved to new g1 start 1768 //[(VPolyLine*)g1 removePointWithNum:0]; 1769 //[g movePoint:MAXINT to:[g1 pointWithNum:0]]; // move start of g to new start of polyline 1770 } 1771 /* else if ([sg isKindOfClass:[VPolyLine class]] && nPts > 2) 1772 { 1773 chPt_num[0] = nPts-2; // we need pt_num inside PolyLine 1774 changedIx[0] = selectedObject; // and index of PolyLine inside Path 1775 changedPt[0] = [sg pointWithNum:nPts-2]; // old start point 1776 removedIx = -3; 1777 //[(VPolyLine*)g removePointWithNum:MAXINT]; 1778 //[g movePoint:MAXINT to:gPtEnd]; // move previous (now last) point of polyline to close path 1779 } */ 1780 else if ([sg isKindOfClass:[VArc class]]) // remove g 1781 { 1782 changedIx[0] = (selectedObject+1 <= endIx) ? (selectedObject+1) : (begIx); 1783 removedIx = selectedObject; 1784 changedPt[0] = [g1 pointWithNum:0]; // old start point 1785 chPt_num[0] = 0; 1786 //[g1 movePoint:0 to:gPtStart]; // close Gap with g1 start to g start 1787 //[list removeObjectAtIndex:selectedObject]; 1788 } 1789 else // remove g1 1790 { changedIx[0] = selectedObject; 1791 removedIx = (selectedObject+1<=endIx) ? (selectedObject+1) : (begIx); 1792 changedPt[0] = [sg pointWithNum:MAXINT]; // old end point 1793 chPt_num[0] = [sg numPoints]-1; 1794 //[g movePoint:MAXINT to:[g1 pointWithNum:MAXINT]]; // close Gap with g end to g1 end 1795 //[list removeObjectAtIndex:(selectedObject+1<=endIx) ? (selectedObject+1) : (begIx)]; 1796 } 1797 } 1798 return removedIx; 1799} 1800 1801/* return YES if we remove something - undo (from -AddPoint) must add the oldGraphic */ 1802- (BOOL)removeGraphicsAroundPoint:(NSPoint)pt andIndex:(int)oldIndex 1803{ VGraphic *atGr, *oGr; 1804 int begIx, endIx; 1805 NSPoint gPtStart, gPtEnd, oPt={0,0}; 1806 1807 atGr = [list objectAtIndex:oldIndex]; 1808 if ([atGr isKindOfClass:[VPolyLine class]] && [atGr numPoints] > 2) 1809 { int pt_num; // num of pt in polyline 1810 1811 pt_num = [(VPolyLine*)atGr removePoint:pt]; 1812 /* close Gap to prev/next Object if start/end pt of polyline */ 1813 if (!pt_num || pt_num >= [atGr numPoints]-1) // start or end point 1814 [atGr movePoint:pt_num to:pt]; // set new start/end pt to old start/end point 1815 1816 return NO; 1817 } 1818 begIx = [self getFirstObjectOfSubPath:oldIndex]; 1819 endIx = [self getLastObjectOfSubPath:begIx]; 1820 if (begIx == endIx) 1821 endIx = [list count]-1; 1822 1823 gPtStart = [atGr pointWithNum:0]; // start point 1824 gPtEnd = [atGr pointWithNum:MAXINT]; // end point 1825 1826 /* check graphic atIndex and behind */ 1827 if ( Diff(gPtEnd.x, pt.x) <= TOLERANCE && Diff(gPtEnd.y, pt.y) <= TOLERANCE ) 1828 { 1829 oGr = (oldIndex+1<=endIx) ? [list objectAtIndex:oldIndex+1] : [list objectAtIndex:begIx]; 1830 oPt = [oGr pointWithNum:0]; 1831 if ( Diff(gPtEnd.x, oPt.x) <= TOLERANCE && Diff(gPtEnd.y, oPt.y) <= TOLERANCE ) 1832 { 1833 [list removeObjectAtIndex:(oldIndex+1<=endIx) ? (oldIndex+1) : (begIx)]; 1834 [list removeObjectAtIndex:oldIndex]; 1835 } 1836 else NSLog(@"VPath.m: removeGraphicsAroundPoint: normaly unpossible 1"); 1837 } 1838 /* check graphic atIndex and befor */ 1839 else if ( Diff(gPtEnd.x, pt.x) <= TOLERANCE && Diff(gPtEnd.y, pt.y) <= TOLERANCE ) 1840 { 1841 oGr = (oldIndex-1>=begIx) ? [list objectAtIndex:oldIndex-1] : [list objectAtIndex:endIx]; 1842 oPt = [oGr pointWithNum:MAXINT]; 1843 if ( Diff(gPtStart.x, oPt.x) <= TOLERANCE && Diff(gPtStart.y, oPt.y) <= TOLERANCE ) 1844 { 1845 [list removeObjectAtIndex:oldIndex]; 1846 [list removeObjectAtIndex:(oldIndex-1<=endIx) ? (oldIndex-1) : (endIx)]; 1847 } 1848 else NSLog(@"VPath.m: removeGraphicsAroundPoint: normaly unpossible 2"); 1849 } 1850 else NSLog(@"VPath.m: removeGraphicsAroundPoint: normaly unpossible 3"); 1851 1852 return YES; 1853} 1854 1855- (BOOL)removePointWithNum:(int)pt_num 1856{ VGraphic *g=nil, *g1; 1857 int begIx, endIx, curObject = -1, nPts; 1858 NSPoint gPtStart, gPtEnd, gPtWithNum; // notice old end point 1859 BOOL connected = NO; 1860 1861 if ( ![list count] || pt_num < 0 ) 1862 return YES; 1863 1864 [self deselectAll]; 1865 selectedObject = -1; 1866 1867 /* beyond list -> point number of end point */ 1868 if ( pt_num >= [self numPoints] ) 1869 { g = [list objectAtIndex:[list count]-1]; 1870 pt_num = MAXINT; 1871 curObject = [list count]-1; 1872 } 1873 else if ( !pt_num ) 1874 { g = [list objectAtIndex:0]; 1875 curObject = 0; 1876 } 1877 else 1878 { int i, cnt, pCnt = 0, prevPCnt = 0; 1879 1880 for (i=0, cnt = [list count]; i<cnt; i++) 1881 { pCnt += [[list objectAtIndex:i] numPoints]; 1882 if ( pCnt > pt_num ) 1883 break; // to this object refers our pt_num 1884 prevPCnt = pCnt; // count of pts befor this gr 1885 } 1886 g = [list objectAtIndex:i]; 1887 pt_num -= prevPCnt; 1888 curObject = i; 1889 } 1890 if (!g) 1891 return YES; 1892 begIx = [self getFirstObjectOfSubPath:curObject]; 1893 endIx = [self getLastObjectOfSubPath:begIx]; 1894 if (begIx == endIx) 1895 endIx = [list count]-1; 1896 1897 gPtStart = [g pointWithNum:0]; // notice old start point 1898 gPtEnd = [g pointWithNum:MAXINT]; // notice old end point 1899 gPtWithNum = [g pointWithNum:pt_num]; // notice old end point 1900 nPts = [g numPoints]; 1901 1902 if (([g isKindOfClass:[VCurve class]] && (pt_num == 1 || pt_num == 2)) || // curve points 1,2 1903 ([g isKindOfClass:[VArc class]] && pt_num == 2)) // arc center 1904 return YES; 1905 if ([g isKindOfClass:[VPolyLine class]] && nPts > 2 && pt_num && pt_num < nPts-1) 1906 { [(VPolyLine*)g removePointWithNum:pt_num]; 1907 return YES; 1908 } 1909 1910 /* get the connected Graphic to g (curObject) */ 1911 if (!pt_num) // corresponding gr is bevor g 1912 { NSPoint g1PtEnd; 1913 1914 g1 = (curObject-1>=begIx) ? [list objectAtIndex:curObject-1] : [list objectAtIndex:endIx]; 1915 if ([g isKindOfClass:[VArc class]] && [g1 isKindOfClass:[VArc class]]) 1916 return YES; // two arcs we cant remove one and close the path Fix Me: open path and arc at end 1917 1918 g1PtEnd = [g1 pointWithNum:MAXINT]; 1919 if ( Diff(g1PtEnd.x, gPtStart.x) < TOLERANCE && Diff(g1PtEnd.y, gPtStart.y) < TOLERANCE) 1920 connected = YES; 1921 1922 if (!connected && [g isKindOfClass:[VPolyLine class]] && nPts > 2) 1923 { 1924 [(VPolyLine*)g removePointWithNum:pt_num]; 1925 } 1926 else if (!connected) 1927 { 1928 [list removeObjectAtIndex:curObject]; 1929 } 1930 else if ([g isKindOfClass:[VPolyLine class]] && nPts > 2 && ![g1 isKindOfClass:[VArc class]]) 1931 { 1932 [(VPolyLine*)g removePointWithNum:pt_num]; 1933 [g1 movePoint:MAXINT to:[g pointWithNum:0]]; // move g1 end to new start of polyLine 1934 } 1935 /* else if ([g1 isKindOfClass:[VPolyLine class]] && [g1 numPoints] > 2) 1936 { 1937 [(VPolyLine*)g1 removePointWithNum:MAXINT]; 1938 [g1 movePoint:MAXINT to:gPtStart]; // move previous (now last) point of polyline to close path 1939 } */ 1940 else if ([g1 isKindOfClass:[VArc class]]) // remove g1 1941 { 1942 [g movePoint:0 to:[g1 pointWithNum:0]]; // close Gap with g start to g start 1943 [list removeObjectAtIndex:(curObject-1>=begIx) ? (curObject-1) : (endIx)]; 1944 } 1945 else // remove g 1946 { [g1 movePoint:MAXINT to:gPtEnd]; // close Gap with g1 end to g end 1947 [list removeObjectAtIndex:curObject]; 1948 } 1949 } 1950 else // corresponding gr is behind g 1951 { NSPoint g1PtStart; 1952 1953 g1 = (curObject+1<=endIx) ? [list objectAtIndex:curObject+1] : [list objectAtIndex:begIx]; 1954 1955 if ([g isKindOfClass:[VArc class]] && [g1 isKindOfClass:[VArc class]]) 1956 return YES; // two arcs we cant remove one and close the path Fix Me: open path and arc at end 1957 1958 g1PtStart = [g1 pointWithNum:0]; 1959 if ( Diff(g1PtStart.x, gPtEnd.x) < TOLERANCE && Diff(g1PtStart.y, gPtEnd.y) < TOLERANCE) 1960 connected = YES; 1961 1962 if (!connected && [g isKindOfClass:[VPolyLine class]] && nPts > 2) 1963 { 1964 [(VPolyLine*)g removePointWithNum:pt_num]; 1965 } 1966 else if (!connected) 1967 { 1968 [list removeObjectAtIndex:curObject]; 1969 } 1970 else if ([g1 isKindOfClass:[VPolyLine class]] && [g1 numPoints] > 2 && ![g isKindOfClass:[VArc class]]) 1971 { 1972 [(VPolyLine*)g1 removePointWithNum:0]; 1973 [g movePoint:MAXINT to:[g1 pointWithNum:0]]; // move start of g to new start of polyline 1974 } 1975 /* else if ([g isKindOfClass:[VPolyLine class]] && nPts > 2) 1976 { 1977 [(VPolyLine*)g removePointWithNum:MAXINT]; 1978 [g movePoint:MAXINT to:gPtEnd]; // move previous (now last) point of polyline to close path 1979 } */ 1980 else if ([g isKindOfClass:[VArc class]]) // remove g 1981 { 1982 [g1 movePoint:0 to:gPtStart]; // close Gap with g1 start to g start 1983 [list removeObjectAtIndex:curObject]; 1984 } 1985 else // remove g1 1986 { [g movePoint:MAXINT to:[g1 pointWithNum:MAXINT]]; // close Gap with g end to g1 end 1987 [list removeObjectAtIndex:(curObject+1<=endIx) ? (curObject+1) : (begIx)]; 1988 1989 } 1990 } 1991 1992 if ( ![list count] ) 1993 return NO; // hole graphic will removed in DocView.m -delete 1994 coordBounds = bounds = NSZeroRect; 1995 dirty = YES; 1996 graduateDirty = YES; 1997 return YES; 1998} 1999 2000/* 2001 * pt_num is the changing control point. pt holds the relative change in each coordinate. 2002 * The relative is needed and not the absolute because the closest inside control point 2003 * changes when one of the outside points change. 2004 */ 2005- (void)movePoint:(int)pt_num to:(NSPoint)p 2006{ VGraphic *g=nil, *g1; 2007 BOOL control = [(App*)NSApp control]; 2008 int begIx, endIx, curObject = -1; 2009 NSPoint gPtStart, gPtEnd, gPtWithNum; // notice old end point 2010 2011 if ( ![list count] || pt_num < 0 ) 2012 return; 2013 /* beyond list -> point number of end point */ 2014 if ( pt_num >= [self numPoints] ) 2015 { g = [list objectAtIndex:[list count]-1]; 2016 pt_num = MAXINT; 2017 curObject = [list count]-1; 2018 } 2019 else if ( !pt_num ) 2020 { g = [list objectAtIndex:0]; 2021 curObject = 0; 2022 } 2023 else 2024 { int i, cnt, pCnt = 0, prevPCnt = 0; 2025 2026 for (i=0, cnt = [list count]; i<cnt; i++) 2027 { pCnt += [[list objectAtIndex:i] numPoints]; 2028 if ( pCnt > pt_num ) 2029 break; // to this object refers our pt_num 2030 prevPCnt = pCnt; // count of pts befor this gr 2031 } 2032 g = [list objectAtIndex:i]; 2033 pt_num -= prevPCnt; 2034 curObject = i; 2035 } 2036 if (!g) 2037 return; 2038 2039 if ( [g isKindOfClass:[VCurve class]] && (pt_num == 1 || pt_num == 2) ) 2040 { 2041 [g movePoint:pt_num to:p]; // move only the control pt 2042 coordBounds = bounds = NSZeroRect; 2043 dirty = YES; 2044 graduateDirty = YES; 2045 return; 2046 } 2047 2048 begIx = [self getFirstObjectOfSubPath:curObject]; 2049 endIx = [self getLastObjectOfSubPath:begIx]; 2050 if (begIx == endIx) 2051 endIx = [list count]-1; 2052 2053 gPtStart = [g pointWithNum:0]; // notice old start point 2054 gPtEnd = [g pointWithNum:MAXINT]; // notice old end point 2055 gPtWithNum = [g pointWithNum:pt_num]; // notice old end point 2056 2057 /* move point connected to pt_num */ 2058 g1 = (curObject+1<=endIx) ? [list objectAtIndex:curObject+1] : [list objectAtIndex:begIx]; 2059 if ([g isKindOfClass:[VArc class]]) 2060 { int i = 2, stop = 0; 2061 NSPoint g1PtEnd = [g1 pointWithNum:MAXINT]; // notice old end point 2062 2063 [g movePoint:pt_num to:p]; 2064 /* move graphics at end of arc g */ 2065 if (DiffPoint([g1 pointWithNum:0], gPtEnd) <= TOLERANCE) 2066 { /* move only if control is set (else point never match) or no arc */ 2067 if (control || ![g1 isKindOfClass:[VArc class]]) 2068 [g1 movePoint:0 to:[g pointWithNum:MAXINT]]; 2069 if (control) // move also graphics at end of g1 2070 { 2071 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphic at g1 end 2072 { VGraphic *g2 = (curObject+i<=endIx) ? [list objectAtIndex:curObject+i] 2073 : [list objectAtIndex:begIx+(curObject+i-endIx)-1]; 2074 2075 if ((g2 == g) || 2076 (DiffPoint(g1PtEnd, [g2 pointWithNum:0]) > TOLERANCE)) 2077 { if (g1 == g) stop = 1; 2078 break; 2079 } 2080 else // if (DiffPoint(g1PtEnd, [g2 pointWithNum:0]) <= TOLERANCE) 2081 { g1PtEnd = [g2 pointWithNum:MAXINT]; // notice old end point g2 will become g1 2082 [g2 movePoint:0 to:[g1 pointWithNum:MAXINT]]; 2083 } 2084 g1 = g2; 2085 i++; 2086 } 2087 if (g1 == g) stop = 1; 2088 } 2089 } 2090 /* move graphics at start of arc g */ 2091 if (!stop) 2092 { i = 2; 2093 g1 = (curObject-1>=begIx) ? [list objectAtIndex:curObject-1] : [list objectAtIndex:endIx]; 2094 /* move graphics at start of g */ 2095 if (DiffPoint([g1 pointWithNum:MAXINT], gPtStart) <= TOLERANCE) 2096 { NSPoint g1PtStart = [g1 pointWithNum:0]; // notice old g1 start point 2097 2098 /* move only if control is set (else point never match) or no arc */ 2099 if (control || ![g1 isKindOfClass:[VArc class]]) 2100 [g1 movePoint:MAXINT to:[g pointWithNum:0]]; 2101 if (control) 2102 { 2103 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphics at g1 start 2104 { VGraphic *g2 = (curObject-i>=begIx) ? [list objectAtIndex:curObject-i] 2105 : [list objectAtIndex:endIx-(begIx-(curObject-i))+1]; 2106 2107 if ((g2 == g) || 2108 (DiffPoint(g1PtStart, [g2 pointWithNum:MAXINT]) > TOLERANCE)) 2109 break; 2110 else 2111 { g1PtStart = [g2 pointWithNum:0]; // note: old g2 start pt -> will become g1 2112 [g2 movePoint:MAXINT to:[g1 pointWithNum:0]]; 2113 } 2114 g1 = g2; 2115 i++; 2116 } 2117 } 2118 } 2119 } 2120 } 2121 else if (!pt_num)// if (![g isKindOfClass:[VArc class]]) // g is no arc 2122 { NSPoint g1PtStart = NSZeroPoint; // old end point 2123 2124 g1 = (curObject-1>=begIx) ? [list objectAtIndex:curObject-1] : [list objectAtIndex:endIx]; 2125 if (DiffPoint([g1 pointWithNum:MAXINT], gPtWithNum) <= 5.0*TOLERANCE) 2126 { 2127 g1PtStart = [g1 pointWithNum:0]; 2128 [g1 movePoint:MAXINT to:p]; 2129 p = [g1 pointWithNum:MAXINT]; 2130 } 2131 [g movePoint:pt_num to:p]; // no arc 2132 if (control) 2133 { int i = 2; 2134 2135 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphic at g1 start 2136 { VGraphic *g2 = (curObject-i>=begIx) ? [list objectAtIndex:curObject-i] 2137 : [list objectAtIndex:endIx-(begIx-(curObject-i))+1]; 2138 2139 if (DiffPoint(g1PtStart, [g2 pointWithNum:MAXINT]) <= TOLERANCE) 2140 { g1PtStart = [g2 pointWithNum:0]; // note: old g2 start pt -> will become g1 2141 [g2 movePoint:MAXINT to:[g1 pointWithNum:0]]; 2142 } 2143 else break; 2144 if (g2 == g) 2145 break; 2146 g1 = g2; 2147 i++; 2148 } 2149 } 2150 } 2151 else // if (![g isKindOfClass:[VArc class]]) // g is no arc 2152 { NSPoint g1PtEnd = [g1 pointWithNum:MAXINT]; // notice old end point 2153 2154 if (DiffPoint([g1 pointWithNum:0], gPtWithNum) <= 5.0*TOLERANCE) 2155 { [g1 movePoint:0 to:p]; // move g1 start to p 2156 p = [g1 pointWithNum:0]; 2157 } 2158 [g movePoint:pt_num to:p]; // no arc ! 2159 if (control) 2160 { int i = 2; 2161 2162 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphic at g1 end 2163 { VGraphic *g2 = (curObject+i<=endIx) ? [list objectAtIndex:curObject+i] 2164 : [list objectAtIndex:begIx+(curObject+i-endIx)-1]; 2165 2166 if (DiffPoint(g1PtEnd, [g2 pointWithNum:0]) <= TOLERANCE) 2167 { g1PtEnd = [g2 pointWithNum:MAXINT]; // notice old end point g2 will become g1 2168 [g2 movePoint:0 to:[g1 pointWithNum:MAXINT]]; 2169 } 2170 else break; 2171 if (g2 == g) 2172 break; 2173 g1 = g2; 2174 i++; 2175 } 2176 } 2177 } 2178 coordBounds = bounds = NSZeroRect; 2179 dirty = YES; 2180 graduateDirty = YES; 2181} 2182 2183/* needed for undo 2184 * if control button is set -> the radius of an arc will changed (else not!) 2185 * for the way back we need the possibility to say "the button is set" 2186 */ 2187- (void)movePoint:(int)pt_num to:(NSPoint)p control:(BOOL)control 2188{ VGraphic *g=nil, *g1; 2189 int begIx, endIx, curObject = -1; 2190 NSPoint gPtStart, gPtEnd, gPtWithNum; // notice old end point 2191 2192 if ( ![list count] || pt_num < 0 ) 2193 return; 2194 /* beyond list -> point number of end point */ 2195 if ( pt_num >= [self numPoints] ) 2196 { g = [list objectAtIndex:[list count]-1]; 2197 pt_num = MAXINT; 2198 curObject = [list count]-1; 2199 } 2200 else if ( !pt_num ) 2201 { g = [list objectAtIndex:0]; 2202 curObject = 0; 2203 } 2204 else 2205 { int i, cnt, pCnt = 0, prevPCnt = 0; 2206 2207 for (i=0, cnt = [list count]; i<cnt; i++) 2208 { pCnt += [[list objectAtIndex:i] numPoints]; 2209 if ( pCnt > pt_num ) 2210 break; // to this object refers our pt_num 2211 prevPCnt = pCnt; // count of pts befor this gr 2212 } 2213 g = [list objectAtIndex:i]; 2214 pt_num -= prevPCnt; 2215 curObject = i; 2216 } 2217 if (!g) 2218 return; 2219 2220 if ( [g isKindOfClass:[VCurve class]] && (pt_num == 1 || pt_num == 2) ) 2221 { 2222 [g movePoint:pt_num to:p]; // move only the control pt 2223 coordBounds = bounds = NSZeroRect; 2224 dirty = YES; 2225 graduateDirty = YES; 2226 return; 2227 } 2228 2229 begIx = [self getFirstObjectOfSubPath:curObject]; 2230 endIx = [self getLastObjectOfSubPath:begIx]; 2231 if (begIx == endIx) 2232 endIx = [list count]-1; 2233 2234 gPtStart = [g pointWithNum:0]; // notice old start point 2235 gPtEnd = [g pointWithNum:MAXINT]; // notice old end point 2236 gPtWithNum = [g pointWithNum:pt_num]; // notice old end point 2237 2238 /* move point connected to pt_num */ 2239 g1 = (curObject+1<=endIx) ? [list objectAtIndex:curObject+1] : [list objectAtIndex:begIx]; 2240 if ([g isKindOfClass:[VArc class]]) 2241 { int i = 2, stop = 0; 2242 NSPoint g1PtEnd = [g1 pointWithNum:MAXINT]; // notice old end point 2243 2244 [(VArc*)g movePoint:pt_num to:p control:control]; 2245 /* move graphics at end of arc g */ 2246 if (DiffPoint([g1 pointWithNum:0], gPtEnd) <= TOLERANCE) 2247 { /* move only if control is set (else point never match) or no arc */ 2248 if (control || ![g1 isKindOfClass:[VArc class]]) 2249 { 2250 if (![g1 isKindOfClass:[VArc class]]) 2251 [g1 movePoint:0 to:[g pointWithNum:MAXINT]]; 2252 else 2253 [(VArc*)g1 movePoint:0 to:[g pointWithNum:MAXINT] control:control]; 2254 } 2255 if (control) // move also graphics at end of g1 2256 { 2257 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphic at g1 end 2258 { VGraphic *g2 = (curObject+i<=endIx) ? [list objectAtIndex:curObject+i] 2259 : [list objectAtIndex:begIx+(curObject+i-endIx)-1]; 2260 2261 if ((g2 == g) || 2262 (DiffPoint(g1PtEnd, [g2 pointWithNum:0]) > TOLERANCE)) 2263 { if (g1 == g) stop = 1; 2264 break; 2265 } 2266 else // if (DiffPoint(g1PtEnd, [g2 pointWithNum:0]) <= TOLERANCE) 2267 { g1PtEnd = [g2 pointWithNum:MAXINT]; // notice old end point g2 will become g1 2268 if (![g2 isKindOfClass:[VArc class]]) 2269 [g2 movePoint:0 to:[g1 pointWithNum:MAXINT]]; 2270 else 2271 [(VArc*)g2 movePoint:0 to:[g1 pointWithNum:MAXINT] control:control]; 2272 } 2273 g1 = g2; 2274 i++; 2275 } 2276 if (g1 == g) stop = 1; 2277 } 2278 } 2279 /* move graphics at start of arc g */ 2280 if (!stop) 2281 { i = 2; 2282 g1 = (curObject-1>=begIx) ? [list objectAtIndex:curObject-1] : [list objectAtIndex:endIx]; 2283 /* move graphics at start of g */ 2284 if (DiffPoint([g1 pointWithNum:MAXINT], gPtStart) <= TOLERANCE) 2285 { NSPoint g1PtStart = [g1 pointWithNum:0]; // notice old g1 start point 2286 2287 /* move only if control is set (else point never match) or no arc */ 2288 if (control || ![g1 isKindOfClass:[VArc class]]) 2289 { 2290 if (![g1 isKindOfClass:[VArc class]]) 2291 [g1 movePoint:MAXINT to:[g pointWithNum:0]]; 2292 else 2293 [(VArc*)g1 movePoint:MAXINT to:[g pointWithNum:0] control:control]; 2294 } 2295 if (control) 2296 { 2297 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphics at g1 start 2298 { VGraphic *g2 = (curObject-i>=begIx) ? [list objectAtIndex:curObject-i] 2299 : [list objectAtIndex:endIx-(begIx-(curObject-i))+1]; 2300 2301 if ((g2 == g) || 2302 (DiffPoint(g1PtStart, [g2 pointWithNum:MAXINT]) > TOLERANCE)) 2303 break; 2304 else 2305 { g1PtStart = [g2 pointWithNum:0]; // note: old g2 start pt -> will become g1 2306 if (![g2 isKindOfClass:[VArc class]]) 2307 [g2 movePoint:MAXINT to:[g1 pointWithNum:0]]; 2308 else 2309 [(VArc*)g2 movePoint:MAXINT to:[g1 pointWithNum:0] control:control]; 2310 } 2311 g1 = g2; 2312 i++; 2313 } 2314 } 2315 } 2316 } 2317 } 2318 else if (!pt_num)// if (![g isKindOfClass:[VArc class]]) // g is no arc 2319 { NSPoint g1PtStart = NSZeroPoint; // note: old end point 2320 2321 g1 = (curObject-1>=begIx) ? [list objectAtIndex:curObject-1] : [list objectAtIndex:endIx]; 2322 if (DiffPoint([g1 pointWithNum:MAXINT], gPtWithNum) <= 5.0*TOLERANCE) 2323 { 2324 g1PtStart = [g1 pointWithNum:0]; 2325 if (![g1 isKindOfClass:[VArc class]]) 2326 [g1 movePoint:MAXINT to:p]; 2327 else 2328 [(VArc*)g1 movePoint:MAXINT to:p control:control]; 2329 p = [g1 pointWithNum:MAXINT]; 2330 } 2331 [g movePoint:pt_num to:p]; // no arc 2332 if (control) 2333 { int i = 2; 2334 2335 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphic at g1 start 2336 { VGraphic *g2 = (curObject-i>=begIx) ? [list objectAtIndex:curObject-i] 2337 : [list objectAtIndex:endIx-(begIx-(curObject-i))+1]; 2338 2339 if (DiffPoint(g1PtStart, [g2 pointWithNum:MAXINT]) <= TOLERANCE) 2340 { g1PtStart = [g2 pointWithNum:0]; // note: old g2 start pt -> will become g1 2341 if (![g2 isKindOfClass:[VArc class]]) 2342 [g2 movePoint:MAXINT to:[g1 pointWithNum:0]]; 2343 else 2344 [(VArc*)g2 movePoint:MAXINT to:[g1 pointWithNum:0] control:control]; 2345 } 2346 else break; 2347 if (g2 == g) 2348 break; 2349 g1 = g2; 2350 i++; 2351 } 2352 } 2353 } 2354 else // if (![g isKindOfClass:[VArc class]]) // g is no arc 2355 { NSPoint g1PtEnd = [g1 pointWithNum:MAXINT]; // notice old end point 2356 2357 if (DiffPoint([g1 pointWithNum:0], gPtWithNum) <= 5.0*TOLERANCE) 2358 { 2359 if (![g1 isKindOfClass:[VArc class]]) 2360 [g1 movePoint:0 to:p]; // move g1 start to p 2361 else 2362 [(VArc*)g1 movePoint:0 to:p control:control]; // move g1 start to p 2363 p = [g1 pointWithNum:0]; 2364 } 2365 [g movePoint:pt_num to:p]; // no arc ! 2366 if (control) 2367 { int i = 2; 2368 2369 while ([g1 isKindOfClass:[VArc class]] && g1 != g) // move graphic at g1 end 2370 { VGraphic *g2 = (curObject+i<=endIx) ? [list objectAtIndex:curObject+i] 2371 : [list objectAtIndex:begIx+(curObject+i-endIx)-1]; 2372 2373 if (DiffPoint(g1PtEnd, [g2 pointWithNum:0]) <= TOLERANCE) 2374 { g1PtEnd = [g2 pointWithNum:MAXINT]; // notice old end point g2 will become g1 2375 if (![g2 isKindOfClass:[VArc class]]) 2376 [g2 movePoint:0 to:[g1 pointWithNum:MAXINT]]; 2377 else 2378 [(VArc*)g2 movePoint:0 to:[g1 pointWithNum:MAXINT] control:control]; 2379 } 2380 else break; 2381 if (g2 == g) 2382 break; 2383 g1 = g2; 2384 i++; 2385 } 2386 } 2387 } 2388 coordBounds = bounds = NSZeroRect; 2389 dirty = YES; 2390 graduateDirty = YES; 2391} 2392 2393/* 2394 * pt_num is the changing control point. pt holds the relative change in each coordinate. 2395 * The relative is needed and not the absolute because the closest inside control point 2396 * changes when one of the outside points change. 2397 */ 2398- (void)movePoint:(int)pt_num by:(NSPoint)pt 2399{ NSPoint ptWithNum; 2400 2401 if ( ![list count] || pt_num < 0 ) 2402 return; 2403 2404 ptWithNum = [self pointWithNum:pt_num]; 2405 pt.x = ptWithNum.x + pt.x; 2406 pt.y = ptWithNum.y + pt.y; 2407 [self movePoint:pt_num to:pt]; 2408 dirty = YES; 2409 graduateDirty = YES; 2410} 2411 2412/* The pt argument holds the relative point change. */ 2413- (void)moveBy:(NSPoint)pt 2414{ int i; 2415 2416 for (i=[list count]-1; i>=0; i--) 2417 [[list objectAtIndex:i] moveBy:pt]; 2418 coordBounds = bounds = NSZeroRect; 2419 dirty = YES; 2420 if (!graduateDirty && graduateList) 2421 { 2422 for (i=[graduateList count]-1; i>=0; i--) 2423 [[graduateList objectAtIndex:i] moveBy:pt]; 2424 } 2425} 2426 2427/* Given the point number, return the point. 2428 * We either return the point of the path (nothing selected) or the selected object 2429 */ 2430- (NSPoint)pointWithNum:(int)pt_num 2431{ int i, cnt, pCnt = 0, prevPCnt = 0; 2432 2433 if ( ![list count] || pt_num < 0 ) 2434 return NSMakePoint( 0.0, 0.0); 2435 /* beyond list -> return point number of end point */ 2436 if ( pt_num >= [self numPoints] ) 2437 return [[list objectAtIndex:[list count]-1] pointWithNum:MAXINT]; 2438 /* nothing selected -> return point of path */ 2439 if ( !pt_num ) 2440 return [[list objectAtIndex:0] pointWithNum:0]; 2441 2442 for (i=0, cnt = [list count]; i<cnt; i++) 2443 { pCnt += [[list objectAtIndex:i] numPoints]; 2444 if ( pCnt > pt_num ) 2445 break; // to this object refers our pt_num 2446 prevPCnt = pCnt; // count of pts befor this gr 2447 } 2448 return [[list objectAtIndex:i] pointWithNum:pt_num - prevPCnt]; 2449} 2450 2451- (int)numPoints 2452{ int i, cnt, pCnt = 0; 2453 2454 for (i=0, cnt = [list count]; i<cnt; i++) 2455 pCnt += [[list objectAtIndex:i] numPoints]; 2456 return pCnt; 2457} 2458 2459/* modified: 2010-07-15 2460 * 2461 * we dont change the order (without split polylines) 2462 */ 2463- (void)pointWithNumBecomeStartPoint:(int)pt_num 2464{ int i, cnt, endIx, begIx = 0, curIx = -1, pCnt = 0, prevPCnt = 0; 2465 2466 if ( !pt_num ) 2467 return; 2468 2469 if ( ![self closed] ) // only start / end points ! 2470 { 2471 if ( pt_num == [self numPoints]-1 ) 2472 [self changeDirection]; 2473 return; 2474 } 2475 2476 for (i=0, cnt = [list count]; i<cnt; i++) 2477 { pCnt += [[list objectAtIndex:i] numPoints]; 2478 curIx = i; 2479 if ( pCnt > pt_num ) 2480 break; // to this object refers our pt_num 2481 prevPCnt = pCnt; // count of pts befor this gr 2482 } 2483 if ( pt_num-prevPCnt >= [[list objectAtIndex:curIx] numPoints]-1 ) // last point of Graphic 2484 curIx ++; // next Graphic is our start Graphic 2485 2486 begIx = [self getFirstObjectOfSubPath:curIx]; 2487 endIx = [self getLastObjectOfSubPath:begIx]; // tolerance:TOLERANCE 2488 2489 /* move objects bevor pt_num at the end of subpath */ 2490 for (i=curIx-1; i >= begIx; i--) 2491 { 2492 [list insertObject:[list objectAtIndex:i] atIndex:endIx+1]; 2493 [list removeObjectAtIndex:i]; 2494 endIx --; // need the same place to insert (remove destroy position 2495 } 2496 [self deselectAll]; 2497 selectedObject = -1; 2498} 2499 2500- (void)mirrorAround:(NSPoint)mp; 2501{ int i; 2502 2503 for (i=[list count]-1; i>=0; i--) 2504 [(VGraphic*)[list objectAtIndex:i] mirrorAround:mp]; 2505 coordBounds = bounds = NSZeroRect; 2506 dirty = YES; 2507 if (!graduateDirty && graduateList) 2508 { 2509 for (i=[graduateList count]-1; i>=0; i--) 2510 [(VGraphic*)[graduateList objectAtIndex:i] mirrorAround:mp]; 2511 } 2512} 2513 2514/* modified: 2008-10-11 2515 */ 2516- (void)changeDirection 2517{ 2518 if ( !filled && ![self closed] ) 2519 { [self changeDirectionOfSubPath:0 :[list count]-1]; 2520 return; 2521 } 2522 2523 [self setDirectionCCW:(isDirectionCCW) ? 0 : 1]; 2524 dirty = YES; 2525} 2526 2527/* created: 21.10.95 2528 * modified: 05.03.97 2529 * parameter: angle angle 2530 * cp rotation center 2531 * purpose: draws the plane with the given rotation angles 2532 */ 2533- (void)drawAtAngle:(float)angle withCenter:(NSPoint)cp in:view 2534{ int i; 2535 2536 for ( i=[list count]-1; i>=0; i-- ) 2537 [(VGraphic*)[list objectAtIndex:i] drawAtAngle:angle withCenter:cp in:view]; 2538} 2539 2540- (void)setAngle:(float)angle withCenter:(NSPoint)cp 2541{ int i; 2542 2543 if (filled) 2544 { graduateAngle -= angle; 2545 if (graduateAngle < 0.0) 2546 graduateAngle += 360.0; 2547 if (graduateAngle > 360.0) 2548 graduateAngle -= 360.0; 2549 vhfRotatePointAroundCenter(&radialCenter, NSMakePoint(0.5, 0.5), -angle); 2550 if (radialCenter.x > 1.0) radialCenter.x = 1.0; 2551 if (radialCenter.x < 0.0) radialCenter.x = 0.0; 2552 if (radialCenter.y > 1.0) radialCenter.y = 1.0; 2553 if (radialCenter.y < 0.0) radialCenter.y = 0.0; 2554 graduateDirty = YES; 2555 } 2556 for (i=[list count]-1; i>=0; i--) 2557 [(VGraphic*)[list objectAtIndex:i] setAngle:angle withCenter:cp]; 2558 coordBounds = bounds = NSZeroRect; 2559 dirty = YES; 2560 if (!graduateDirty && graduateList) 2561 { 2562 for (i=[graduateList count]-1; i>=0; i--) 2563 [(VGraphic*)[graduateList objectAtIndex:i] setAngle:angle withCenter:cp]; 2564 } 2565} 2566 2567- (void)transform:(NSAffineTransform*)matrix 2568{ int i; 2569 NSSize size = NSMakeSize(width, width); 2570 2571 size = [matrix transformSize:size]; 2572 width = (Abs(size.width) + Abs(size.height)) / 2; 2573 for ( i=[list count]-1; i >= 0; i-- ) 2574 [[list objectAtIndex:i] transform:matrix]; 2575 coordBounds = bounds = NSZeroRect; 2576 dirty = graduateDirty = YES; 2577} 2578 2579- (void)scale:(float)x :(float)y withCenter:(NSPoint)cp 2580{ int i; 2581 2582 width *= (Abs(x)+Abs(y))/2.0; 2583 for (i=[list count]-1; i>=0; i--) 2584 [(VGraphic*)[list objectAtIndex:i] scale:x :y withCenter:cp]; 2585 coordBounds = bounds = NSZeroRect; 2586 dirty = YES; 2587 graduateDirty = YES; 2588} 2589 2590/* created: 1995-09-19 2591 * modified: 2008-08-26 2592 * purpose: draw the path 2593 */ 2594#define DEBUG_TRACE 0 2595- (void)drawWithPrincipal:principal 2596{ int i, f; 2597 int cnt = [self count]; /* [self countRecursive] for path in path ! */ 2598 NSPoint currentPoint = NSMakePoint(LARGENEG_COORD, LARGENEG_COORD); 2599 NSBezierPath *bPath = [NSBezierPath bezierPath]; 2600 BOOL antialias = VHFAntialiasing(); 2601 2602 if (!cnt) 2603 return; 2604 2605#if DEBUG_TRACE 2606 [[NSDPSContext currentContext] setOutputTraced:YES]; 2607#endif 2608 2609 for (f=0; f <= 1; f++) // 0 = fill, 1 = stroke 2610 { NSColor *col; 2611 VFloat w; 2612 2613 if (!f && !filled) continue; // fill run: nothing to fill or allready filled 2614#if !defined(__APPLE__) // OpenStep 4.2, linux - FIXME: should be without antialiasing 2615 if (f && !(width || !filled)) continue; // stroke run: nothing to stroke 2616#else // TODO: this is a workaround to make 0-width fillings visible, we should better add a wireframe mode 2617 if (f && !width && (filled > 1 || ! antialias) ) 2618 continue; // stroke run: nothing to stroke and color shading -> skip 2619#endif 2620 if ( !f && filled == 2 && (graduateDirty || !graduateList)) 2621 { [self drawGraduatedWithPrincipal:principal]; 2622 continue; 2623 } 2624 else if (!f && filled == 3 && (graduateDirty || !graduateList)) 2625 { [self drawRadialWithPrincipal:principal]; 2626 continue; 2627 } 2628 else if (!f && filled == 4 && (graduateDirty || !graduateList)) 2629 { [self drawAxialWithPrincipal:principal]; 2630 continue; 2631 } 2632 else if (!f && (filled == 2 || filled == 3 || filled == 4) && graduateList && !graduateDirty) 2633 { int gCnt = [graduateList count]; 2634 2635 /* draw graduateList */ 2636 VHFSetAntialiasing(NO); 2637 for (i=0; i<gCnt; i++) 2638 [(VGraphic*)[graduateList objectAtIndex:i] drawWithPrincipal:principal]; 2639 if (antialias) VHFSetAntialiasing(antialias); 2640 continue; 2641 } 2642 col = (!f || (!width && filled)) ? fillColor : color; 2643 w = (!f) ? (0.0) : ((width > 0.0) ? width : [NSBezierPath defaultLineWidth]); // width 2644 if ( filled && width == 0.0 ) // if filled and no stroke width, we stroke very thin to make everything visible 2645 { w = 0.1/[principal scaleFactor]; 2646 if ( ! antialias && !f ) 2647 w = 0.0; 2648 } 2649 2650 /* colorSeparation */ 2651 if (!VHFIsDrawingToScreen() && [principal separationColor]) 2652 col = [self separationColor:col]; // get individual separation color 2653 2654 if ( [principal mustDrawPale] ) 2655 { VFloat h, s, b, a; 2656 2657 [[col colorUsingColorSpaceName:NSDeviceRGBColorSpace] getHue:&h saturation:&s brightness:&b alpha:&a]; 2658 [[NSColor colorWithCalibratedHue:h saturation:s brightness:(b<0.5) ? 0.5 : b alpha:a] set]; 2659 } 2660#if !defined(GNUSTEP_BASE_VERSION) && !defined(__APPLE__) // OpenStep 4.2 2661 else if (VHFIsDrawingToScreen() && [[col colorSpaceName] isEqualToString:NSDeviceCMYKColorSpace]) 2662 [[col colorUsingColorSpaceName:NSCalibratedRGBColorSpace] set]; 2663#endif 2664 else 2665 [col set]; 2666 2667 [bPath setLineWidth:w]; 2668 [bPath setLineCapStyle:NSRoundLineCapStyle]; 2669 [bPath setLineJoinStyle:NSRoundLineJoinStyle]; 2670 for (i=0; i<cnt; i++) 2671 currentPoint = [[list objectAtIndex:i] appendToBezierPath:bPath currentPoint:currentPoint]; 2672 2673 if (!f) // (filled) 2674 { [bPath setWindingRule:NSEvenOddWindingRule]; 2675 [bPath fill]; 2676 } 2677 else 2678 [bPath stroke]; 2679 } 2680 /* display directions */ 2681 if ( [principal showDirection] ) 2682 { NSPoint le=NSZeroPoint; 2683 2684 for (i=0; i<cnt; i++) 2685 { NSPoint s = [[list objectAtIndex:i] pointWithNum:0]; 2686 2687 if ( !i || (Diff(le.x, s.x) > TOLERANCE || Diff(le.y, s.y) > TOLERANCE) ) 2688 [[list objectAtIndex:i] drawDirectionAtScale:[principal scaleFactor]/2.0]; 2689 //[[list objectAtIndex:i] drawStartAtScale:[principal scaleFactor]]; 2690 else 2691 [[list objectAtIndex:i] drawDirectionAtScale:[principal scaleFactor]]; 2692 le = [[list objectAtIndex:i] pointWithNum:MAXINT]; 2693 } 2694 } 2695 2696#if DEBUG_TRACE 2697 PSWait(); 2698 [[NSDPSContext currentContext] setOutputTraced:NO]; 2699#endif 2700} 2701 2702/* modified: 2012-12-12 (alpha added, float -> double) 2703 * FIXME: there is a solid border between graduate steps, which is a rounding issue, 2704 * and additionally probably the w = 0.1 in -drawWithPrincipal: => better to use method without stroke 2705 */ 2706#define MAXSTEPS 1000 2707- (void)drawGraduatedWithPrincipal:principal 2708{ int steps = 15, poolCnt = 0, oldFilled = filled; 2709 double xMax, yMax, dx, dy, fsteps, angle = graduateAngle, length, rStepWidth = stepWidth; 2710 NSRect bRect = [self coordBounds]; 2711 NSPoint p0, p1, ls, le, p0e, p1e, p0End; 2712 VLine *line0, *line1, *line2, *line3; 2713 NSColor *endCol = fillColor, *startCol = endColor; 2714 double colDiff1 = 1.0, colDiff2 = 1.0, colDiff3 = 1.0, colDiff4 = 1.0; 2715 double colStep1 = 1.0, colStep2 = 1.0, colStep3 = 1.0, colStep4 = 1.0, alphaStep = 1.0; 2716 double curCol1 = 1.0, curCol2 = 1.0, curCol3 = 1.0, curCol4 = 1.0, curAlpha = 1.0; 2717 double endCol1 = 1.0, endCol2 = 1.0, endCol3 = 1.0, endCol4 = 1.0, endAlpha = 1.0; 2718 double startCol1 = 1.0, startCol2 = 1.0, startCol3 = 1.0, startCol4 = 1.0, startAlpha = 1.0, mul = 1.0; 2719 VPath *path, *rectP; 2720 NSAutoreleasePool *pool, *pool1; 2721 NSString *fcolSpaceName, *ecolSpaceName, *colSpaceName; 2722 BOOL antialias = VHFAntialiasing(); 2723 2724 if (!bRect.size.width || !bRect.size.height) 2725 return; 2726 2727 filled = 1; 2728 if ([startCol isEqual:endCol]) 2729 { 2730 [self drawWithPrincipal:principal]; 2731 filled = oldFilled; 2732 return; 2733 } 2734 2735 /* convert fill/endColor to one colorSpaceName */ 2736 fcolSpaceName = [fillColor colorSpaceName]; 2737 ecolSpaceName = [endColor colorSpaceName]; 2738 if ([fcolSpaceName isEqual:@"NSDeviceCMYKColorSpace"] || [ecolSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 2739 { 2740 startCol = [endColor colorUsingColorSpaceName:@"NSDeviceCMYKColorSpace"]; 2741 endCol = [fillColor colorUsingColorSpaceName:@"NSDeviceCMYKColorSpace"]; 2742 colSpaceName = @"NSDeviceCMYKColorSpace"; 2743 } 2744 else if ([fcolSpaceName isEqual:@"NSCalibratedWhiteColorSpace"] && 2745 [ecolSpaceName isEqual:@"NSCalibratedWhiteColorSpace"]) 2746 { startCol = [endColor colorUsingColorSpaceName:@"NSCalibratedWhiteColorSpace"]; 2747 endCol = [fillColor colorUsingColorSpaceName:@"NSCalibratedWhiteColorSpace"]; 2748 colSpaceName = @"NSCalibratedWhiteColorSpace"; 2749 } 2750 else 2751 { startCol = [endColor colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 2752 endCol = [fillColor colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 2753 colSpaceName = @"NSCalibratedRGBColorSpace"; 2754 } 2755 if (!startCol || !endCol) 2756 { NSLog(@"drawGraduatedWithPrincipal: ColorSpace not supported"); 2757 [self drawWithPrincipal:principal]; 2758 filled = oldFilled; 2759 return; 2760 } 2761 2762 if (graduateList) 2763 [graduateList release]; 2764 graduateList = [[NSMutableArray allocWithZone:[self zone]] init]; 2765 graduateDirty = NO; 2766 2767 pool = [NSAutoreleasePool new]; 2768 if (!rStepWidth) rStepWidth = 2.0; 2769 2770 angle = graduateAngle; 2771 if (angle >= 180.0) 2772 { NSColor *col; 2773 2774 col = startCol; 2775 startCol = endCol; 2776 endCol = col; 2777 angle -= 180.0; 2778 } 2779 2780 /* 135 > angle > 45 2781 * line y is fix and we calc start values for x start/end and dx 2782 */ 2783 if (angle < 135.0 && angle > 45.0) 2784 { NSColor *col; 2785 2786 col = startCol; 2787 startCol = endCol; 2788 endCol = col; 2789 p0.y = bRect.origin.y - rStepWidth; 2790 p1.y = bRect.origin.y + bRect.size.height + rStepWidth; 2791 p0.x = p1.x = bRect.origin.x; 2792 ls = le = bRect.origin; 2793 le.x = bRect.origin.x + bRect.size.width; 2794 if (angle > 90.0) 2795 { p0.x = bRect.origin.x; 2796 p1.x = p0.x - (bRect.size.height + 2.0*rStepWidth)/Tan(180.0-angle); 2797 ls.x = bRect.origin.x - bRect.size.height/Tan(180.0-angle); 2798 ls.y = le.y = bRect.origin.y + bRect.size.height; 2799 } 2800 else if (angle < 90.0) 2801 { p1.x = bRect.origin.x; 2802 p0.x = p1.x - (bRect.size.height + 2.0*rStepWidth)/Tan(angle); 2803 ls.x = bRect.origin.x - bRect.size.height/Tan(angle); 2804 ls.y = le.y = bRect.origin.y; 2805 } 2806 } 2807 /* 135 <= angle <= 45 2808 * line x is fix and we calc start values for y start/end and dy 2809 */ 2810 else 2811 { p0.x = bRect.origin.x - rStepWidth; 2812 p1.x = bRect.origin.x + bRect.size.width + rStepWidth; 2813 p0.y = p1.y = bRect.origin.y; 2814 ls = le = bRect.origin; 2815 le.y = bRect.origin.y + bRect.size.height; 2816 2817 if (angle && angle <= 45.0) 2818 { p1.y = bRect.origin.y; 2819 p0.y = p1.y - (bRect.size.width + 2.0*rStepWidth)*Tan(angle); 2820 ls.x = le.x = bRect.origin.x; 2821 ls.y = bRect.origin.y - bRect.size.width*Tan(angle); 2822 } 2823 else if (angle) 2824 { NSColor *col; 2825 2826 col = startCol; 2827 startCol = endCol; 2828 endCol = col; 2829 p0.y = bRect.origin.y; 2830 p1.y = p0.y - (bRect.size.width + 2.0*rStepWidth)*Tan(180.0-angle); 2831 ls.x = le.x = bRect.origin.x + bRect.size.width; 2832 ls.y = bRect.origin.y - bRect.size.width*Tan(180.0-angle); 2833 } 2834 } 2835 2836 length = sqrt(SqrDistPoints(ls, le)); 2837 fsteps = length / rStepWidth; 2838 steps = ((int)fsteps) + ((fsteps-((int)fsteps) > 0.0) ? 1 : 0); 2839 2840 dx = Diff(ls.x, le.x)/(double)steps; 2841 dy = Diff(ls.y, le.y)/(double)steps; 2842 2843 steps --; // for startCol 2844 2845 /* build path - else we must rotate the rectangle (sqrt, sin, ..) */ 2846 line0 = [VLine line]; 2847 line1 = [VLine line]; 2848 line2 = [VLine line]; 2849 line3 = [VLine line]; 2850 rectP = [VPath path]; 2851 [rectP setFilled:1]; // simple filling 2852 [line0 setVertices:p0 :p1]; 2853 [[rectP list] addObject:line0]; 2854 [line1 setVertices:p1 :NSMakePoint(p1.x+dx, p1.y+dy)]; 2855 [[rectP list] addObject:line1]; 2856 [line2 setVertices:NSMakePoint(p1.x+dx, p1.y+dy) :NSMakePoint(p0.x+dx, p0.y+dy)]; 2857 [[rectP list] addObject:line2]; 2858 [line3 setVertices:NSMakePoint(p0.x+dx, p0.y+dy) :p0]; 2859 [[rectP list] addObject:line3]; 2860 2861 pool1 = [NSAutoreleasePool new]; 2862 2863 yMax = bRect.origin.y+bRect.size.height; 2864 xMax = bRect.origin.x+bRect.size.width; 2865 while ( steps && ((!dx && (p0.y < yMax || p1.y < yMax)) || (!dy && (p0.x < xMax || p1.x < xMax))) ) 2866 { 2867 path = [rectP clippedFrom:self]; 2868 if (!path || ![[path list] count]) 2869 { //NSLog(@"VPolyLine -drawGraduatedWithPrincipal: troubles with extreme paths!"); 2870 p0.x += dx; p0.y += dy; 2871 p1.x += dx; p1.y += dy; 2872 [[[rectP list] objectAtIndex:0] setVertices:p0 :p1]; 2873 [[[rectP list] objectAtIndex:1] setVertices:p1 :NSMakePoint(p1.x+dx, p1.y+dy)]; 2874 [[[rectP list] objectAtIndex:2] setVertices:NSMakePoint(p1.x+dx, p1.y+dy) :NSMakePoint(p0.x+dx, p0.y+dy)]; 2875 [[[rectP list] objectAtIndex:3] setVertices:NSMakePoint(p0.x+dx, p0.y+dy) :p0]; 2876 [rectP setBoundsZero]; 2877 /* correct col steps */ 2878 steps--; 2879 } 2880 else 2881 break; // start p0 p1 ! 2882 poolCnt++; 2883 if (poolCnt > 50) 2884 { [pool1 release]; 2885 pool1 = [NSAutoreleasePool new]; 2886 poolCnt = 0; 2887 } 2888 } 2889 p0e.x = p0.x + steps*dx; 2890 p0e.y = p0.y + steps*dy; 2891 p1e.x = p1.x + steps*dx; 2892 p1e.y = p1.y + steps*dy; 2893 [[[rectP list] objectAtIndex:0] setVertices:p0e :p1e]; 2894 [[[rectP list] objectAtIndex:1] setVertices:p1e :NSMakePoint(p1e.x+dx, p1e.y+dy)]; 2895 [[[rectP list] objectAtIndex:2] setVertices:NSMakePoint(p1e.x+dx, p1e.y+dy) :NSMakePoint(p0e.x+dx, p0e.y+dy)]; 2896 [[[rectP list] objectAtIndex:3] setVertices:NSMakePoint(p0e.x+dx, p0e.y+dy) :p0e]; 2897 [rectP setBoundsZero]; 2898 while ( steps && ((!dx && (p0e.y < yMax || p1e.y < yMax)) || (!dy && (p0e.x < xMax || p1e.x < xMax))) ) 2899 { 2900 path = [rectP clippedFrom:self]; 2901 if (!path || ![[path list] count]) 2902 { //NSLog(@"VPolyLine -drawGraduatedWithPrincipal: troubles with extreme paths!"); 2903 p0e.x -= dx; p0e.y -= dy; 2904 p1e.x -= dx; p1e.y -= dy; 2905 [[[rectP list] objectAtIndex:0] setVertices:p0e :p1e]; 2906 [[[rectP list] objectAtIndex:1] setVertices:p1e :NSMakePoint(p1e.x+dx, p1e.y+dy)]; 2907 [[[rectP list] objectAtIndex:2] setVertices:NSMakePoint(p1e.x+dx, p1e.y+dy) :NSMakePoint(p0e.x+dx, p0e.y+dy)]; 2908 [[[rectP list] objectAtIndex:3] setVertices:NSMakePoint(p0e.x+dx, p0e.y+dy) :p0e]; 2909 [rectP setBoundsZero]; 2910 /* correct col steps */ 2911 steps--; 2912 if (steps <= 1) 2913 { [pool1 release]; [pool release]; 2914 filled = oldFilled; 2915 return; 2916 } 2917 } 2918 else 2919 break; // end 2920 poolCnt++; 2921 if (poolCnt > 50) 2922 { [pool1 release]; 2923 pool1 = [NSAutoreleasePool new]; 2924 poolCnt = 0; 2925 } 2926 } 2927 2928 2929 if ([colSpaceName isEqual:@"NSCalibratedRGBColorSpace"]) 2930 { 2931 startCol1 = curCol1 = [startCol redComponent]; 2932 startCol2 = curCol2 = [startCol greenComponent]; 2933 startCol3 = curCol3 = [startCol blueComponent]; 2934 endCol1 = [endCol redComponent]; 2935 endCol2 = [endCol greenComponent]; 2936 endCol3 = [endCol blueComponent]; 2937 } 2938 else if ([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 2939 { 2940 startCol1 = curCol1 = [startCol cyanComponent]; 2941 startCol2 = curCol2 = [startCol magentaComponent]; 2942 startCol3 = curCol3 = [startCol yellowComponent]; 2943 startCol4 = curCol4 = [startCol blackComponent]; 2944 endCol1 = [endCol cyanComponent]; 2945 endCol2 = [endCol magentaComponent]; 2946 endCol3 = [endCol yellowComponent]; 2947 endCol4 = [endCol blackComponent]; 2948 } 2949 else // NSCalibratedWhiteColorSpace 2950 { 2951 startCol1 = curCol1 = [startCol whiteComponent]; 2952 endCol1 = [endCol whiteComponent]; 2953 } 2954 startAlpha = curAlpha = [startCol alphaComponent]; 2955 endAlpha = [endCol alphaComponent]; 2956 2957 /* correct steps */ 2958 p0End.x = p0e.x + dx; 2959 p0End.y = p0e.y + dy; 2960 length = sqrt(SqrDistPoints(p0, p0End)); 2961 if (steps > MAXSTEPS) 2962 { 2963 steps = MAXSTEPS; 2964 rStepWidth = length / steps; 2965 fsteps = length / rStepWidth; 2966 steps = ((int)fsteps) + ((fsteps-((int)fsteps) > 0.0) ? 1 : 0); 2967 dx = Diff(p0.x, p0End.x)/(double)steps; 2968 dy = Diff(p0.y, p0End.y)/(double)steps; 2969 } 2970 colDiff1 = Diff(curCol1, endCol1); 2971 colDiff2 = Diff(curCol2, endCol2); 2972 colDiff3 = Diff(curCol3, endCol3); 2973 colDiff4 = Diff(curCol4, endCol4); 2974 colStep1 = colDiff1/steps; 2975 colStep2 = colDiff2/steps; 2976 colStep3 = colDiff3/steps; 2977 colStep4 = colDiff4/steps; 2978 alphaStep = (endAlpha-startAlpha)/2.0 / steps; 2979 2980 if (curCol1 > endCol1) colStep1 = -colStep1; 2981 if (curCol2 > endCol2) colStep2 = -colStep2; 2982 if (curCol3 > endCol3) colStep3 = -colStep3; 2983 if (curCol4 > endCol4) colStep4 = -colStep4; 2984 2985 colStep1 = floor(colStep1 *1000000.0)/1000000.0; 2986 colStep2 = floor(colStep2 *1000000.0)/1000000.0; 2987 colStep3 = floor(colStep3 *1000000.0)/1000000.0; 2988 colStep4 = floor(colStep4 *1000000.0)/1000000.0; 2989 alphaStep = floor(alphaStep*1000000.0)/1000000.0; 2990 2991 [[[rectP list] objectAtIndex:0] setVertices:p0 :p1]; 2992 [[[rectP list] objectAtIndex:1] setVertices:p1 :NSMakePoint(p1.x+dx, p1.y+dy)]; 2993 [[[rectP list] objectAtIndex:2] setVertices:NSMakePoint(p1.x+dx, p1.y+dy) :NSMakePoint(p0.x+dx, p0.y+dy)]; 2994 [[[rectP list] objectAtIndex:3] setVertices:NSMakePoint(p0.x+dx, p0.y+dy) :p0]; 2995 [rectP setBoundsZero]; 2996 VHFSetAntialiasing(NO); 2997 while ( (!dx && (p0.y < yMax || p1.y < yMax)) || (!dy && (p0.x < xMax || p1.x < xMax)) ) 2998 { 2999 path = [rectP clippedFrom:self]; 3000 if (p0.y >= p0e.y && p0.x >= p0e.x && p1.y >= p1e.y && p1.x >= p1e.x && (!path || ![[path list] count])) 3001 break; 3002 [path sortList]; 3003 if ([colSpaceName isEqual:@"NSCalibratedRGBColorSpace"]) 3004 [path setFillColor:[NSColor colorWithCalibratedRed:curCol1 green:curCol2 blue:curCol3 alpha:curAlpha]]; 3005 else if ([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3006 [path setFillColor:[NSColor colorWithDeviceCyan:curCol1 magenta:curCol2 yellow:curCol3 black:curCol4 alpha:curAlpha]]; 3007 else 3008 [path setFillColor:[NSColor colorWithCalibratedWhite:curCol1 alpha:curAlpha]]; 3009 [path drawWithPrincipal:principal]; // we want to fill online 3010 /* Fix me 3011 * clipfehler im openstep 3012 * path links und rechts vom clipbereich -> dann wird innerhalb des clipbereichs eine linie dargestellt 3013 */ 3014 /* moeglicher workaround 3015 * den pfad zerlegen in mehrere pfade 3016 * - check if path innerhalb einer der anderen nicht vergessen !!! 3017 { int j, pCnt = [[path list] count]; 3018 BOOL startIx = 0, endIx = 0; 3019 3020 while (startIx < pCnt) 3021 { 3022 endIx = [path getLastObjectOfSubPath:startIx]; 3023 if (startIx == endIx) 3024 { startIx++; 3025 continue; 3026 } 3027 else 3028 { VPath *p = [VPath path]; 3029 3030 [p setFilled:1]; 3031 if ([colSpaceName isEqual:@"NSCalibratedRGBColorSpace"]) 3032 [path setFillColor:[NSColor colorWithCalibratedRed:curCol1 green:curCol2 blue:curCol3 alpha:1.0]]; 3033 else if ([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3034 [path setFillColor:[NSColor colorWithDeviceCyan:curCol1 magenta:curCol2 yellow:curCol3 black:curCol4 alpha:1.0]]; 3035 else 3036 [path setFillColor:[NSColor colorWithCalibratedWhite:curCol1 alpha:1.0]]; 3037 for (j = startIx; j<=endIx; j++) 3038 [[p list] addObject:[[path list] objectAtIndex:j]]; 3039 3040 [graduateList addObject:[[p copy] autorelease]]; 3041 } 3042 startIx = endIx+1; 3043 } 3044 } 3045 */ 3046 [graduateList addObject:[[path copy] autorelease]]; 3047 3048 curCol1 = startCol1 + mul*colStep1; 3049 curCol2 = startCol2 + mul*colStep2; 3050 curCol3 = startCol3 + mul*colStep3; 3051 curCol4 = startCol4 + mul*colStep4; 3052 curAlpha = startAlpha + mul*alphaStep; 3053 mul += 1.0; 3054 p0.x += dx; p0.y += dy; 3055 p1.x += dx; p1.y += dy; 3056 /* build new path - else we must rotate the rectangle (sqrt, sin, ..) */ 3057 [[[rectP list] objectAtIndex:0] setVertices:p0 :p1]; 3058 [[[rectP list] objectAtIndex:1] setVertices:p1 :NSMakePoint(p1.x+dx, p1.y+dy)]; 3059 [[[rectP list] objectAtIndex:2] setVertices:NSMakePoint(p1.x+dx, p1.y+dy) :NSMakePoint(p0.x+dx, p0.y+dy)]; 3060 [[[rectP list] objectAtIndex:3] setVertices:NSMakePoint(p0.x+dx, p0.y+dy) :p0]; 3061 [rectP setBoundsZero]; 3062 3063 poolCnt++; 3064 if (poolCnt > 50) 3065 { [pool1 release]; 3066 pool1 = [NSAutoreleasePool new]; 3067 poolCnt = 0; 3068 } 3069 } 3070 if (antialias) VHFSetAntialiasing(antialias); 3071 3072 [pool1 release]; 3073 [pool release]; 3074 filled = oldFilled; 3075} 3076 3077- (void)drawRadialWithPrincipal:principal 3078{ int steps = 15, poolCnt = 0, oldFilled = filled; 3079 float fsteps, rRadius, endRadius, overlap = 1.0, rStepWidth = stepWidth; 3080 NSRect bRect = [self coordBounds]; 3081 NSPoint rCenter, ll, lr, ur, ul, maxDistP; 3082 VArc *theArc, *theArc2; 3083 NSColor *endCol = fillColor, *startCol = endColor; 3084 double colDiff1 = 1.0, colDiff2 = 1.0, colDiff3 = 1.0, colDiff4 = 1.0, distance, minRadius; 3085 double colStep1 = 1.0, colStep2 = 1.0, colStep3 = 1.0, colStep4 = 1.0; 3086 double curCol1 = 1.0, curCol2 = 1.0, curCol3 = 1.0, curCol4 = 1.0; 3087 double endCol1 = 1.0, endCol2 = 1.0, endCol3 = 1.0, endCol4 = 1.0; 3088 double startCol1 = 1.0, startCol2 = 1.0, startCol3 = 1.0, startCol4 = 1.0, mul = 1.0; 3089 VPath *path, *arcPath; 3090 NSAutoreleasePool *pool, *pool1; 3091 NSString *fcolSpaceName, *ecolSpaceName, *colSpaceName; 3092 BOOL antialias = VHFAntialiasing(); 3093 3094 if (!bRect.size.width || !bRect.size.height) 3095 return; 3096 3097 filled = 1; 3098 if ([startCol isEqual:endCol]) 3099 { 3100 [self drawWithPrincipal:principal]; 3101 filled = oldFilled; 3102 return; 3103 } 3104 3105 /* convert fill/endColor to one colorSpaceName */ 3106 fcolSpaceName = [fillColor colorSpaceName]; 3107 ecolSpaceName = [endColor colorSpaceName]; 3108 if ([fcolSpaceName isEqual:@"NSDeviceCMYKColorSpace"] || [ecolSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3109 { 3110 startCol = [endColor colorUsingColorSpaceName:@"NSDeviceCMYKColorSpace"]; 3111 endCol = [fillColor colorUsingColorSpaceName:@"NSDeviceCMYKColorSpace"]; 3112 colSpaceName = @"NSDeviceCMYKColorSpace"; 3113 } 3114 else if ([fcolSpaceName isEqual:@"NSCalibratedWhiteColorSpace"] && 3115 [ecolSpaceName isEqual:@"NSCalibratedWhiteColorSpace"]) 3116 { startCol = [endColor colorUsingColorSpaceName:@"NSCalibratedWhiteColorSpace"]; 3117 endCol = [fillColor colorUsingColorSpaceName:@"NSCalibratedWhiteColorSpace"]; 3118 colSpaceName = @"NSCalibratedWhiteColorSpace"; 3119 } 3120 else 3121 { startCol = [endColor colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 3122 endCol = [fillColor colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 3123 colSpaceName = @"NSCalibratedRGBColorSpace"; 3124 } 3125 if (!startCol || !endCol) 3126 { NSLog(@"drawGraduatedWithPrincipal: ColorSpace not supported"); 3127 [self drawWithPrincipal:principal]; 3128 filled = oldFilled; 3129 return; 3130 } 3131 3132 if (graduateList) 3133 [graduateList release]; 3134 graduateList = [[NSMutableArray allocWithZone:[self zone]] init]; 3135 graduateDirty = NO; 3136 3137 pool = [NSAutoreleasePool new]; 3138 if (!rStepWidth) rStepWidth = 2.0; 3139 3140 rCenter.x = bRect.origin.x + bRect.size.width*radialCenter.x; 3141 rCenter.y = bRect.origin.y + bRect.size.height*radialCenter.y; 3142 3143 ll = bRect.origin; 3144 ul = NSMakePoint(bRect.origin.x, bRect.origin.y+bRect.size.height); 3145 ur = NSMakePoint(bRect.origin.x+bRect.size.width, bRect.origin.y+bRect.size.height); 3146 lr = NSMakePoint(bRect.origin.x+bRect.size.width, bRect.origin.y); 3147 maxDistP = (SqrDistPoints(ll, rCenter) > SqrDistPoints(lr, rCenter)) ? ll : lr; 3148 if (SqrDistPoints(ul, rCenter) > SqrDistPoints(maxDistP, rCenter)) 3149 maxDistP = ul; 3150 if (SqrDistPoints(ur, rCenter) > SqrDistPoints(maxDistP, rCenter)) 3151 maxDistP = ur; 3152 rRadius = sqrt(SqrDistPoints(rCenter, maxDistP)); 3153 /* our ring */ 3154 arcPath = [VPath path]; 3155 theArc = [VArc arc]; 3156 theArc2 = [VArc arc]; 3157 [arcPath setFilled:1]; 3158 [theArc setFilled:1]; 3159 [theArc setCenter:rCenter start:NSMakePoint(rCenter.x+rRadius, rCenter.y) angle:360.0]; 3160 [[arcPath list] addObject:theArc]; 3161 [theArc2 setCenter:rCenter start:NSMakePoint(rCenter.x+rRadius-rStepWidth, rCenter.y) angle:360.0]; 3162 [[arcPath list] addObject:theArc2]; 3163 3164 fsteps = rRadius / rStepWidth; 3165 steps = ((int)fsteps) + ((fsteps-((int)fsteps) > 0.0) ? 1 : 0); 3166 steps --; // for startCol 3167 3168 pool1 = [NSAutoreleasePool new]; 3169 3170 while (rRadius > TOLERANCE*10.0) 3171 { 3172 path = [arcPath clippedFrom:self]; 3173 if (!path || [[path list] count] <= 1) 3174 { 3175 rRadius -= rStepWidth; 3176 [[[arcPath list] objectAtIndex:0] setCenter:rCenter start:NSMakePoint(rCenter.x+rRadius, rCenter.y) 3177 angle:360.0]; 3178 [[[arcPath list] objectAtIndex:1] setCenter:rCenter 3179 start:NSMakePoint(rCenter.x+rRadius-rStepWidth, rCenter.y) 3180 angle:360.0]; 3181 [arcPath setBoundsZero]; 3182 steps--; // correct col steps 3183 } 3184 else 3185 break; // start theArc 3186 poolCnt++; 3187 if (poolCnt > 50) 3188 { [pool1 release]; 3189 pool1 = [NSAutoreleasePool new]; 3190 poolCnt = 0; 3191 } 3192 } 3193 endRadius = rStepWidth; 3194 [[[arcPath list] objectAtIndex:0] setCenter:rCenter start:NSMakePoint(rCenter.x+endRadius, rCenter.y) 3195 angle:360.0]; 3196 [[[arcPath list] objectAtIndex:1] setCenter:rCenter start:NSMakePoint(rCenter.x+endRadius-rStepWidth, rCenter.y) 3197 angle:360.0]; 3198 [arcPath setBoundsZero]; 3199 while (endRadius < rRadius) 3200 { 3201 path = [arcPath clippedFrom:self]; 3202 if (!path || [[path list] count] <= 1) 3203 { 3204 endRadius += rStepWidth; 3205 [[[arcPath list] objectAtIndex:0] setCenter:rCenter start:NSMakePoint(rCenter.x+endRadius, rCenter.y) 3206 angle:360.0]; 3207 [[[arcPath list] objectAtIndex:1] setCenter:rCenter 3208 start:NSMakePoint(rCenter.x+endRadius-rStepWidth, rCenter.y) 3209 angle:360.0]; 3210 [arcPath setBoundsZero]; 3211 steps--; // correct col steps 3212 if (steps <= 1) 3213 { [pool1 release]; [pool release]; 3214 filled = oldFilled; 3215 return; 3216 } 3217 } 3218 else 3219 break; // end 3220 poolCnt++; 3221 if (poolCnt > 50) 3222 { [pool1 release]; 3223 pool1 = [NSAutoreleasePool new]; 3224 poolCnt = 0; 3225 } 3226 } 3227 3228 if ([colSpaceName isEqual:@"NSCalibratedRGBColorSpace"]) 3229 { 3230 startCol1 = curCol1 = [startCol redComponent]; 3231 startCol2 = curCol2 = [startCol greenComponent]; 3232 startCol3 = curCol3 = [startCol blueComponent]; 3233 endCol1 = [endCol redComponent]; 3234 endCol2 = [endCol greenComponent]; 3235 endCol3 = [endCol blueComponent]; 3236 } 3237 else if ([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3238 { 3239 startCol1 = curCol1 = [startCol cyanComponent]; 3240 startCol2 = curCol2 = [startCol magentaComponent]; 3241 startCol3 = curCol3 = [startCol yellowComponent]; 3242 startCol4 = curCol4 = [startCol blackComponent]; 3243 endCol1 = [endCol cyanComponent]; 3244 endCol2 = [endCol magentaComponent]; 3245 endCol3 = [endCol yellowComponent]; 3246 endCol4 = [endCol blackComponent]; 3247 } 3248 else // NSCalibratedWhiteColorSpace 3249 { 3250 startCol1 = curCol1 = [startCol whiteComponent]; 3251 endCol1 = [endCol whiteComponent]; 3252 } 3253 colDiff1 = Diff(curCol1, endCol1); 3254 colDiff2 = Diff(curCol2, endCol2); 3255 colDiff3 = Diff(curCol3, endCol3); 3256 colDiff4 = Diff(curCol4, endCol4); 3257 colStep1 = colDiff1/steps; 3258 colStep2 = colDiff2/steps; 3259 colStep3 = colDiff3/steps; 3260 colStep4 = colDiff4/steps; 3261 3262 distance = (rRadius-(endRadius-rStepWidth)); 3263 minRadius = endRadius-rStepWidth; 3264 3265 if (steps > MAXSTEPS) 3266 { 3267 steps = MAXSTEPS; 3268 colStep1 = colDiff1/steps; 3269 colStep2 = colDiff2/steps; 3270 colStep3 = colDiff3/steps; 3271 colStep4 = colDiff4/steps; 3272 rStepWidth = distance/steps; 3273 } 3274 3275#if 0 3276/* ultimative hack !!! */ 3277 for (i=0; i<4; i++) 3278 { double testCol, colStep, colDiff; 3279 3280 switch (i) 3281 { case 0: colStep = colStep1; colDiff = colDiff1; break; 3282 case 1: colStep = colStep2; colDiff = colDiff2; break; 3283 case 2: colStep = colStep3; colDiff = colDiff3; break; 3284 default: colStep = colStep4; colDiff = colDiff4; 3285 } 3286 testCol = (1.0/colStep)/(([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) ? 4.0 : 3.0); 3287 testCol = testCol-((int)testCol); 3288 while ( steps > 1 && colStep && /*(testCol < 0.15 || 1.0-testCol < 0.15)*/ 3289 (([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"] && 3290 (Diff(testCol, 0.2) < 0.07 || Diff(1.0-testCol, 0.2) < 0.07)) || // 0.6 - 0.15 3291 (/*![colSpaceName isEqual:@"NSDeviceCMYKColorSpace"] &&*/ (testCol < 0.15 || 1.0-testCol < 0.15)))) 3292 3293 { 3294 steps--; 3295 colStep1 = colDiff1/steps; 3296 colStep2 = colDiff2/steps; 3297 colStep3 = colDiff3/steps; 3298 colStep4 = colDiff4/steps; 3299 rStepWidth = distance/steps; 3300 switch (i) 3301 { case 0: colStep = colStep1; break; 3302 case 1: colStep = colStep2; break; 3303 case 2: colStep = colStep3; break; 3304 default: colStep = colStep4; 3305 } 3306 testCol = (1.0/colStep)/(([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) ? 4.0 : 3.0); 3307 testCol = testCol-((int)testCol); 3308 if (steps == 1) 3309 break; 3310 } 3311 } 3312#endif 3313 3314 if (curCol1 > endCol1) colStep1 = -colStep1; 3315 if (curCol2 > endCol2) colStep2 = -colStep2; 3316 if (curCol3 > endCol3) colStep3 = -colStep3; 3317 if (curCol4 > endCol4) colStep4 = -colStep4; 3318 3319 colStep1 = floor(colStep1*1000000.0)/1000000.0; 3320 colStep2 = floor(colStep2*1000000.0)/1000000.0; 3321 colStep3 = floor(colStep3*1000000.0)/1000000.0; 3322 colStep4 = floor(colStep4*1000000.0)/1000000.0; 3323 3324#if 0 3325NSLog(@"colStep1: %.15f testCol1: %.15f steps: %d\n", colStep1, ((1.0/colStep1)/(([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) ? 4.0 : 3.0))), steps; 3326NSLog(@"colStep2: %.15f testCol2: %.15f\n", colStep2, ((1.0/colStep2)/(([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) ? 4.0 : 3.0))); 3327NSLog(@"colStep3: %.15f testCol3: %.15f\n", colStep3, ((1.0/colStep3)/(([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) ? 4.0 : 3.0))); 3328#endif 3329 3330 /* so we get a continuous image if we move the center 3331 * because the radius of the inner circle is always rStepWidth 3332 */ 3333 fsteps = distance/rStepWidth; 3334// fsteps = rRadius/rStepWidth; 3335 fsteps = ((int)fsteps) + ((fsteps-((int)fsteps) > 0.0) ? 1 : 0); 3336// rRadius = fsteps*rStepWidth; 3337 distance = fsteps*rStepWidth; 3338 rRadius = distance + minRadius; 3339 endRadius = minRadius+rStepWidth; 3340 3341 if (rStepWidth < 3.0) 3342 overlap = rStepWidth * 0.2; 3343 3344 /* build our ring */ 3345 [[[arcPath list] objectAtIndex:0] setCenter:rCenter 3346 start:NSMakePoint(rCenter.x+rRadius, rCenter.y) 3347 angle:360.0]; 3348 if ((rRadius-rStepWidth-overlap) <= 0.0) 3349 [[[arcPath list] objectAtIndex:1] setCenter:rCenter 3350 start:NSMakePoint(rCenter.x, rCenter.y) 3351 angle:360.0]; 3352 else 3353 [[[arcPath list] objectAtIndex:1] setCenter:rCenter 3354 start:NSMakePoint(rCenter.x+rRadius-rStepWidth-overlap, rCenter.y) 3355 angle:360.0]; 3356 [arcPath setBoundsZero]; 3357 3358 VHFSetAntialiasing(NO); 3359 while (rRadius > TOLERANCE*10.0) 3360 { 3361 path = [arcPath clippedFrom:self]; 3362 if (rRadius <= endRadius && (!path || [[path list] count] <= 1)) 3363 break; 3364 if ([[path list] count] > 1) 3365 { [path sortList]; 3366 if ([colSpaceName isEqual:@"NSCalibratedRGBColorSpace"]) 3367 [path setFillColor:[NSColor colorWithCalibratedRed:curCol1 green:curCol2 blue:curCol3 alpha:1.0]]; 3368 else if ([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3369 [path setFillColor:[NSColor colorWithDeviceCyan:curCol1 magenta:curCol2 yellow:curCol3 black:curCol4 alpha:1.0]]; 3370 else 3371 [path setFillColor:[NSColor colorWithCalibratedWhite:curCol1 alpha:1.0]]; 3372 [path drawWithPrincipal:principal]; 3373 [graduateList addObject:[[path copy] autorelease]]; 3374 } 3375//else 3376// NSLog(@"fault in radialFilling\n"); 3377 curCol1 = startCol1 + mul*colStep1; 3378 curCol2 = startCol2 + mul*colStep2; 3379 curCol3 = startCol3 + mul*colStep3; 3380 curCol4 = startCol4 + mul*colStep4; 3381 mul += 1.0; 3382 rRadius -= rStepWidth; 3383 [[[arcPath list] objectAtIndex:0] setCenter:rCenter 3384 start:NSMakePoint(rCenter.x+rRadius, rCenter.y) 3385 angle:360.0]; 3386 if ((rRadius-rStepWidth-overlap) <= 0.0) 3387 [[[arcPath list] objectAtIndex:1] setCenter:rCenter 3388 start:NSMakePoint(rCenter.x, rCenter.y) 3389 angle:360.0]; 3390 else 3391 [[[arcPath list] objectAtIndex:1] setCenter:rCenter 3392 start:NSMakePoint(rCenter.x+rRadius-rStepWidth-overlap, rCenter.y) 3393 angle:360.0]; 3394 [arcPath setBoundsZero]; 3395 poolCnt++; 3396 if (poolCnt > 50) 3397 { [pool1 release]; 3398 pool1 = [NSAutoreleasePool new]; 3399 poolCnt = 0; 3400 } 3401 } 3402 if (antialias) VHFSetAntialiasing(antialias); 3403 3404 [pool1 release]; 3405 [pool release]; 3406 filled = oldFilled; 3407} 3408 3409- (void)drawAxialWithPrincipal:principal 3410{ int steps = 15, poolCnt = 0, oldFilled = filled; 3411 double fsteps, rRadius, stepAngle, startAngle, endAngle, rStepWidth = stepWidth; 3412 NSRect bRect = [self coordBounds]; 3413 NSPoint rCenter, ll, lr, ur, ul, maxDistP, startArc, endArc; 3414 VArc *theArc; 3415 VLine *ecline, *csline; 3416 NSColor *endCol = fillColor, *startCol = endColor; 3417 double colStep1 = 1.0, colStep2 = 1.0, colStep3 = 1.0, colStep4 = 1.0; 3418 double curCol1 = 1.0, curCol2 = 1.0, curCol3 = 1.0, curCol4 = 1.0; 3419 double endCol1 = 1.0, endCol2 = 1.0, endCol3 = 1.0, endCol4 = 1.0; 3420 double startCol1 = 1.0, startCol2 = 1.0, startCol3 = 1.0, startCol4 = 1.0, mul = 1.0; 3421 VPath *path, *cakePath; 3422 NSAutoreleasePool *pool, *pool1; 3423 NSString *fcolSpaceName, *ecolSpaceName, *colSpaceName; 3424 BOOL antialias = VHFAntialiasing(); 3425 3426 if (!bRect.size.width || !bRect.size.height) 3427 return; 3428 3429 filled = 1; 3430 if ([startCol isEqual:endCol]) 3431 { 3432 [self drawWithPrincipal:principal]; 3433 filled = oldFilled; 3434 return; 3435 } 3436 3437 /* convert fill/endColor to one colorSpaceName */ 3438 fcolSpaceName = [fillColor colorSpaceName]; 3439 ecolSpaceName = [endColor colorSpaceName]; 3440 if ([fcolSpaceName isEqual:@"NSDeviceCMYKColorSpace"] || [ecolSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3441 { 3442 startCol = [endColor colorUsingColorSpaceName:@"NSDeviceCMYKColorSpace"]; 3443 endCol = [fillColor colorUsingColorSpaceName:@"NSDeviceCMYKColorSpace"]; 3444 colSpaceName = @"NSDeviceCMYKColorSpace"; 3445 } 3446 else if ([fcolSpaceName isEqual:@"NSCalibratedWhiteColorSpace"] && 3447 [ecolSpaceName isEqual:@"NSCalibratedWhiteColorSpace"]) 3448 { startCol = [endColor colorUsingColorSpaceName:@"NSCalibratedWhiteColorSpace"]; 3449 endCol = [fillColor colorUsingColorSpaceName:@"NSCalibratedWhiteColorSpace"]; 3450 colSpaceName = @"NSCalibratedWhiteColorSpace"; 3451 } 3452 else 3453 { startCol = [endColor colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 3454 endCol = [fillColor colorUsingColorSpaceName:@"NSCalibratedRGBColorSpace"]; 3455 colSpaceName = @"NSCalibratedRGBColorSpace"; 3456 } 3457 if (!startCol || !endCol) 3458 { NSLog(@"drawRadialWithPrincipal: ColorSpace not supported"); 3459 [self drawWithPrincipal:principal]; 3460 filled = oldFilled; 3461 return; 3462 } 3463 3464 if (graduateList) 3465 [graduateList release]; 3466 graduateList = [[NSMutableArray allocWithZone:[self zone]] init]; 3467 graduateDirty = NO; 3468 3469 pool = [NSAutoreleasePool new]; 3470 if (!rStepWidth) rStepWidth = 2.0; 3471 3472 rCenter.x = bRect.origin.x + bRect.size.width*radialCenter.x; 3473 rCenter.y = bRect.origin.y + bRect.size.height*radialCenter.y; 3474 3475 ll = bRect.origin; 3476 ul = NSMakePoint(bRect.origin.x, bRect.origin.y+bRect.size.height); 3477 ur = NSMakePoint(bRect.origin.x+bRect.size.width, bRect.origin.y+bRect.size.height); 3478 lr = NSMakePoint(bRect.origin.x+bRect.size.width, bRect.origin.y); 3479 maxDistP = (SqrDistPoints(ll, rCenter) > SqrDistPoints(lr, rCenter)) ? ll : lr; 3480 if (SqrDistPoints(ul, rCenter) > SqrDistPoints(maxDistP, rCenter)) 3481 maxDistP = ul; 3482 if (SqrDistPoints(ur, rCenter) > SqrDistPoints(maxDistP, rCenter)) 3483 maxDistP = ur; 3484 rRadius = sqrt(SqrDistPoints(rCenter, maxDistP)); 3485 3486 fsteps = (2.0*Pi*rRadius)/rStepWidth; 3487 steps = ((int)fsteps) + ((fsteps-((int)fsteps) > 0.0) ? 1 : 0); 3488 steps --; // for startCol 3489 3490 if (steps > MAXSTEPS) 3491 { 3492 steps = MAXSTEPS; 3493 rStepWidth = (2.0*Pi*rRadius)/steps; 3494 fsteps = (2.0*Pi*rRadius)/rStepWidth; 3495 steps = ((int)fsteps) + ((fsteps-((int)fsteps) > 0.0) ? 1 : 0); 3496 } 3497 3498 stepAngle = 360.0/(double)steps; // umfang / rStepWidth = cnt, 360 / cnt = stepAngle 3499 3500 /* our cake */ 3501 startAngle = endAngle = graduateAngle; 3502 endAngle += 360.0; 3503 if (startAngle == 360.0) { startAngle = 0.0; endAngle = 360.0; } 3504 3505 /* korrekt start/endAngle if radCenter laying on bRect frame */ 3506 if ( Diff(rCenter.y, bRect.origin.y) <= 0.0001 ) // down 3507 { 3508 if ( Diff(rCenter.x, bRect.origin.x) <= 0.0001 && (!startAngle || startAngle >= 90.0) ) // down left 3509 { 3510 startAngle = 0.0; 3511 endAngle = 90.0; 3512 fsteps /= 4.0; 3513 } 3514 else if ( Diff(rCenter.x, bRect.origin.x+bRect.size.width) <= 0.0001 && 3515 (startAngle >= 180.0 || startAngle <= 90.0) ) // down right 3516 { 3517 startAngle = 90.0; 3518 endAngle = 180.0; 3519 fsteps /= 4.0; 3520 } 3521 else if ( !startAngle || startAngle >= 180.0 ) 3522 { 3523 startAngle = 0.0; 3524 endAngle = 180.0; 3525 fsteps /= 2.0; 3526 } 3527 } 3528 else if ( Diff(rCenter.y, bRect.origin.y+bRect.size.height) <= 0.0001 ) // up 3529 { 3530 if ( Diff(rCenter.x, bRect.origin.x) <= 0.0001 && startAngle >= 0.0 && startAngle <= 270.0 ) // up left 3531 { 3532 startAngle = 270.0; 3533 endAngle = 360.0; 3534 fsteps /= 4.0; 3535 } 3536 else if ( Diff(rCenter.x, bRect.origin.x+bRect.size.width) <= 0.0001 && 3537 (startAngle >= 270.0 || startAngle <= 180.0) ) // up right 3538 { 3539 startAngle = 180.0; 3540 endAngle = 270.0; 3541 fsteps /= 4.0; 3542 } 3543 else if ( startAngle <= 180.0 ) 3544 { 3545 startAngle = 180.0; 3546 endAngle = 360.0; 3547 fsteps /= 2.0; 3548 } 3549 } 3550 else if ( Diff(rCenter.x, bRect.origin.x) <= 0.0001 && startAngle >= 90.0 && startAngle <= 270.0 ) // left 3551 { 3552 startAngle = 270.0; 3553 endAngle = 450.0; 3554 fsteps /= 2.0; 3555 } 3556 else if ( Diff(rCenter.x, bRect.origin.x+bRect.size.width) <= 0.0001 && (startAngle <= 90.0 || startAngle >= 270.0) ) // right 3557 { 3558 startAngle = 90.0; 3559 endAngle = 270.0; 3560 fsteps /= 2.0; 3561 } 3562 steps = ((int)fsteps) + ((fsteps-((int)fsteps) > 0.0) ? 1 : 0); 3563 3564 startArc = vhfPointAngleFromRefPoint(rCenter, NSMakePoint(rCenter.x+rRadius, rCenter.y), startAngle); 3565 endArc = vhfPointAngleFromRefPoint(rCenter, startArc, stepAngle); 3566 cakePath = [VPath path]; 3567 [cakePath setFilled:1]; 3568 ecline = [VLine line]; 3569 [ecline setVertices:endArc :rCenter]; 3570 [[cakePath list] addObject:ecline]; 3571 csline = [VLine line]; 3572 [csline setVertices:rCenter :startArc]; 3573 [[cakePath list] addObject:csline]; 3574 theArc = [VArc arc]; 3575 [theArc setCenter:rCenter start:startArc angle:stepAngle]; 3576 [[cakePath list] addObject:theArc]; 3577 3578 pool1 = [NSAutoreleasePool new]; 3579 3580 while (startAngle < endAngle) 3581 { 3582 path = [cakePath clippedFrom:self]; 3583 if (!path || [[path list] count] <= 1) 3584 { 3585 startArc = endArc; 3586 endArc = vhfPointAngleFromRefPoint(rCenter, startArc, stepAngle); 3587 [[[cakePath list] objectAtIndex:0] setVertices:endArc :rCenter]; 3588 [[[cakePath list] objectAtIndex:1] setVertices:rCenter :startArc]; 3589 [[[cakePath list] objectAtIndex:2] setCenter:rCenter start:startArc angle:stepAngle]; 3590 [cakePath setBoundsZero]; 3591 /* set coordBounds to NSZeroRect */ 3592 [cakePath setList:[[cakePath list] retain] optimize:NO]; 3593 [[cakePath list] release]; 3594 steps--; // correct col steps 3595 startAngle += stepAngle; // correct startAngle ! 3596 } 3597 else 3598 break; // start theArc 3599 poolCnt++; 3600 if (poolCnt > 50) 3601 { [pool1 release]; 3602 pool1 = [NSAutoreleasePool new]; 3603 poolCnt = 0; 3604 } 3605 } 3606 startArc = vhfPointAngleFromRefPoint(rCenter, NSMakePoint(rCenter.x+rRadius, rCenter.y), endAngle); // -360.0 3607 endArc = vhfPointAngleFromRefPoint(rCenter, startArc, -stepAngle); 3608 [[[cakePath list] objectAtIndex:0] setVertices:endArc :rCenter]; 3609 [[[cakePath list] objectAtIndex:1] setVertices:rCenter :startArc]; 3610 [[[cakePath list] objectAtIndex:2] setCenter:rCenter start:startArc angle:-stepAngle]; 3611 [cakePath setBoundsZero]; 3612 /* set coordBounds to NSZeroRect */ 3613 [cakePath setList:[[cakePath list] retain] optimize:NO]; 3614 [[cakePath list] release]; 3615 3616 while (endAngle > graduateAngle-360.0) 3617 { 3618 path = [cakePath clippedFrom:self]; 3619 if (!path || [[path list] count] <= 1) 3620 { 3621 startArc = endArc; 3622 endArc = vhfPointAngleFromRefPoint(rCenter, startArc, -stepAngle); 3623 [[[cakePath list] objectAtIndex:0] setVertices:endArc :rCenter]; 3624 [[[cakePath list] objectAtIndex:1] setVertices:rCenter :startArc]; 3625 [[[cakePath list] objectAtIndex:2] setCenter:rCenter start:startArc angle:-stepAngle]; 3626 [cakePath setBoundsZero]; 3627 /* set coordBounds to NSZeroRect */ 3628 [cakePath setList:[[cakePath list] retain] optimize:NO]; 3629 [[cakePath list] release]; 3630 steps--; // correct col steps 3631 endAngle -= stepAngle; // correct endAngle ! 3632 if (steps <= 1) 3633 { [pool1 release]; [pool release]; 3634 filled = oldFilled; 3635 return; 3636 } 3637 } 3638 else 3639 break; // end 3640 poolCnt++; 3641 if (poolCnt > 50) 3642 { [pool1 release]; 3643 pool1 = [NSAutoreleasePool new]; 3644 poolCnt = 0; 3645 } 3646 } 3647 3648 if ([colSpaceName isEqual:@"NSCalibratedRGBColorSpace"]) 3649 { 3650 startCol1 = curCol1 = [startCol redComponent]; 3651 startCol2 = curCol2 = [startCol greenComponent]; 3652 startCol3 = curCol3 = [startCol blueComponent]; 3653 endCol1 = [endCol redComponent]; 3654 endCol2 = [endCol greenComponent]; 3655 endCol3 = [endCol blueComponent]; 3656 } 3657 else if ([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3658 { 3659 startCol1 = curCol1 = [startCol cyanComponent]; 3660 startCol2 = curCol2 = [startCol magentaComponent]; 3661 startCol3 = curCol3 = [startCol yellowComponent]; 3662 startCol4 = curCol4 = [startCol blackComponent]; 3663 endCol1 = [endCol cyanComponent]; 3664 endCol2 = [endCol magentaComponent]; 3665 endCol3 = [endCol yellowComponent]; 3666 endCol4 = [endCol blackComponent]; 3667 } 3668 else // NSCalibratedWhiteColorSpace 3669 { 3670 startCol1 = curCol1 = [startCol whiteComponent]; 3671 endCol1 = [endCol whiteComponent]; 3672 } 3673 3674 colStep1 = Diff(curCol1, endCol1); 3675 colStep2 = Diff(curCol2, endCol2); 3676 colStep3 = Diff(curCol3, endCol3); 3677 colStep4 = Diff(curCol4, endCol4); 3678 colStep1 /= steps; 3679 colStep2 /= steps; 3680 colStep3 /= steps; 3681 colStep4 /= steps; 3682 if (curCol1 > endCol1) colStep1 = -colStep1; 3683 if (curCol2 > endCol2) colStep2 = -colStep2; 3684 if (curCol3 > endCol3) colStep3 = -colStep3; 3685 if (curCol4 > endCol4) colStep4 = -colStep4; 3686 3687 colStep1 = floor(colStep1*1000000.0)/1000000.0; 3688 colStep2 = floor(colStep2*1000000.0)/1000000.0; 3689 colStep3 = floor(colStep3*1000000.0)/1000000.0; 3690 colStep4 = floor(colStep4*1000000.0)/1000000.0; 3691 3692 /* build our cake */ 3693 if (startAngle >= 360.0) startAngle -= 360.0; 3694 if (startAngle < 0.0) startAngle += 360.0; 3695 startArc = vhfPointAngleFromRefPoint(rCenter, NSMakePoint(rCenter.x+rRadius, rCenter.y), startAngle); 3696 endArc = vhfPointAngleFromRefPoint(rCenter, startArc, stepAngle); 3697 [[[cakePath list] objectAtIndex:0] setVertices:endArc :rCenter]; 3698 [[[cakePath list] objectAtIndex:1] setVertices:rCenter :startArc]; 3699 [[[cakePath list] objectAtIndex:2] setCenter:rCenter start:startArc angle:stepAngle]; 3700 [cakePath setBoundsZero]; 3701 /* set coordBounds to NSZeroRect */ 3702 [cakePath setList:[[cakePath list] retain] optimize:NO]; 3703 [[cakePath list] release]; 3704 3705 VHFSetAntialiasing(NO); 3706 while (startAngle <= endAngle-stepAngle+10.0*TOLERANCE) 3707 { 3708 path = [cakePath clippedFrom:self]; 3709 if (startAngle > endAngle-stepAngle && (!path || [[path list] count] <= 1)) 3710 break; 3711 if ([[path list] count] > 1) 3712 { [path sortList]; 3713 if ([colSpaceName isEqual:@"NSCalibratedRGBColorSpace"]) 3714 [path setFillColor:[NSColor colorWithCalibratedRed:curCol1 green:curCol2 blue:curCol3 alpha:1.0]]; 3715 else if ([colSpaceName isEqual:@"NSDeviceCMYKColorSpace"]) 3716 [path setFillColor:[NSColor colorWithDeviceCyan:curCol1 magenta:curCol2 yellow:curCol3 black:curCol4 alpha:1.0]]; 3717 else 3718 [path setFillColor:[NSColor colorWithCalibratedWhite:curCol1 alpha:1.0]]; 3719 [path drawWithPrincipal:principal]; 3720 [graduateList addObject:[[path copy] autorelease]]; 3721 } 3722 curCol1 = startCol1 + mul*colStep1; 3723 curCol2 = startCol2 + mul*colStep2; 3724 curCol3 = startCol3 + mul*colStep3; 3725 curCol4 = startCol4 + mul*colStep4; 3726 mul += 1.0; 3727 startArc = endArc; 3728 endArc = vhfPointAngleFromRefPoint(rCenter, startArc, stepAngle); 3729 [[[cakePath list] objectAtIndex:0] setVertices:endArc :rCenter]; 3730 [[[cakePath list] objectAtIndex:1] setVertices:rCenter :startArc]; 3731 [[[cakePath list] objectAtIndex:2] setCenter:rCenter start:startArc angle:stepAngle]; 3732 [cakePath setBoundsZero]; 3733 /* set coordBounds to NSZeroRect */ 3734 [cakePath setList:[[cakePath list] retain] optimize:NO]; 3735 [[cakePath list] release]; 3736 startAngle += stepAngle; 3737 poolCnt++; 3738 if (poolCnt > 50) 3739 { [pool1 release]; 3740 pool1 = [NSAutoreleasePool new]; 3741 poolCnt = 0; 3742 } 3743 } 3744 if (antialias) VHFSetAntialiasing(antialias); 3745 3746 [pool1 release]; 3747 [pool release]; 3748 filled = oldFilled; 3749} 3750 3751/* 3752 * Check for a control point hit. Return the point number hit in the pt argument. 3753 * Does not set the graphic selection!! 3754 */ 3755- (BOOL)hitEdge:(NSPoint)p fuzz:(float)fuzz :(NSPoint*)pt :(float)controlsize 3756{ int i; 3757 3758 for (i=[list count]-1; i>=0; i--) 3759 { id obj = [list objectAtIndex:i]; 3760 3761 if ([obj hitEdge:p fuzz:fuzz :pt :controlsize]) 3762 { NSPoint p0, plast, prevPt; 3763 int begIx = [self getFirstObjectOfSubPath:i]; 3764 int endIx = [self getLastObjectOfSubPath:begIx]; 3765 3766 p0 = [obj pointWithNum:0]; 3767 /* pt mit 0 oder 1 checken */ 3768 if ( Diff((*pt).x, p0.x) <= TOLERANCE && Diff((*pt).y, p0.y) <= TOLERANCE ) 3769 { int prevI = (i-1 < begIx) ? (endIx) : (i-1); 3770 id prevG = [list objectAtIndex:prevI]; 3771 3772 prevPt = [prevG pointWithNum:MAXINT]; 3773 /* previous graphic is connected and give us no hit */ 3774 if ( Diff(prevPt.x, p0.x) <= TOLERANCE && Diff(prevPt.y, p0.y) <= TOLERANCE && 3775 ![prevG hitEdge:p fuzz:fuzz :&plast :controlsize] ) 3776 continue; 3777 return YES; 3778 } 3779 plast = [obj pointWithNum:MAXINT]; 3780 if ( Diff((*pt).x, plast.x) <= TOLERANCE && Diff((*pt).y, plast.y) <= TOLERANCE ) 3781 { int nextI = (i+1 > endIx) ? (begIx) : (i+1); 3782 id nextG = [list objectAtIndex:nextI]; 3783 3784 prevPt = [nextG pointWithNum:0]; 3785 /* next graphic is connected and give us no hit */ 3786 if ( Diff(prevPt.x, plast.x) <= TOLERANCE && Diff(prevPt.y, plast.y) <= TOLERANCE && 3787 ![nextG hitEdge:p fuzz:fuzz :&p0 :controlsize] ) 3788 continue; 3789 return YES; 3790 } 3791 return YES; 3792 } 3793 } 3794 3795 return NO; 3796} 3797 3798/* 3799 * Check for a control point hit. 3800 * Return the point number hit in the pt_num argument. 3801 */ 3802- (BOOL)hitControl:(NSPoint)p :(int*)pt_num controlSize:(float)controlsize 3803{ int i, j, cnt = [list count], pCnt = 0; 3804 BOOL control = [(App*)NSApp control]; 3805 3806 for (i=0; i<cnt; i++) 3807 { id obj = [list objectAtIndex:i]; 3808 3809 if ([obj hitControl:p :pt_num controlSize:controlsize]) 3810 { 3811 /* check if we must grap the next/prev control pt (curves only) */ 3812 if ( control && (!(*pt_num) || (*pt_num) == [obj numPoints]-1) ) 3813 { int begIx = [self getFirstObjectOfSubPath:i]; 3814 int endIx = [self getLastObjectOfSubPath:begIx]; 3815 3816 /* check prev connected graphic */ 3817 if ( !(*pt_num) ) 3818 { NSPoint p0, prevPt; 3819 int pnum=0, prevI = (i-1 < begIx) ? (endIx) : (i-1); 3820 id prevG = [list objectAtIndex:prevI]; 3821 3822 p0 = [obj pointWithNum:0]; 3823 prevPt = [prevG pointWithNum:MAXINT]; 3824 /* previous graphic is connected and give us a hit != last */ 3825 if ( Diff(prevPt.x, p0.x) <= TOLERANCE && Diff(prevPt.y, p0.y) <= TOLERANCE ) 3826 { 3827 if ( [prevG hitControl:p :&pnum controlSize:controlsize] && 3828 pnum != [prevG numPoints]-1 ) 3829 { 3830 /* now we must take prev graphic */ 3831 if ( selectedObject >= 0 && selectedObject != prevI ) 3832 { [[list objectAtIndex:selectedObject] setSelected:NO]; 3833 selectedObject = -1; 3834 } 3835 *pt_num = pnum; 3836 if (*pt_num == [prevG selectedKnobIndex]) // arc center we do not select the object 3837 selectedObject = prevI; 3838 /* correct pCnt */ 3839 pCnt = 0; 3840 for (j=0; j<prevI; j++) 3841 pCnt += [[list objectAtIndex:j] numPoints]; 3842 *pt_num += pCnt; 3843 [self setSelected:YES]; 3844 return YES; 3845 } 3846 } 3847 } 3848 else /* check next connected graphic */ 3849 { NSPoint pl, nextPt; 3850 int pnum=0, nextI = (i+1 > endIx) ? (begIx) : (i+1); 3851 id nextG = [list objectAtIndex:nextI]; 3852 3853 nextPt = [nextG pointWithNum:0]; 3854 pl = [obj pointWithNum:MAXINT]; 3855 /* previous graphic is connected and give us a hit != last */ 3856 if ( Diff(nextPt.x, pl.x) <= TOLERANCE && Diff(nextPt.y, pl.y) <= TOLERANCE ) 3857 { 3858 if ( [nextG hitControl:p :&pnum controlSize:controlsize] && pnum ) 3859 { 3860 /* now we must take prev graphic */ 3861 if ( selectedObject >= 0 && selectedObject != nextI ) 3862 { [[list objectAtIndex:selectedObject] setSelected:NO]; 3863 selectedObject = -1; 3864 } 3865 *pt_num = pnum; 3866 if (*pt_num == [nextG selectedKnobIndex]) // arc center we do not select the object 3867 selectedObject = nextI; 3868 /* correct pCnt */ 3869 if ( nextI != i+1 ) 3870 { pCnt = 0; 3871 for (j=0; j<nextI; j++) 3872 pCnt += [[list objectAtIndex:j] numPoints]; 3873 } 3874 else 3875 pCnt += [obj numPoints]; 3876 *pt_num += pCnt; 3877 [self setSelected:YES]; 3878 return YES; 3879 } 3880 } 3881 } 3882 } 3883 if ( selectedObject >= 0 && selectedObject != i ) 3884 { [[list objectAtIndex:selectedObject] setSelected:NO]; 3885 selectedObject = -1; 3886 } 3887 if (*pt_num == [obj selectedKnobIndex]) // arc center we do not select the object 3888 selectedObject = i; 3889 *pt_num += pCnt; 3890 [self setSelected:YES]; 3891 return YES; 3892 } 3893 pCnt += [obj numPoints]; 3894 } 3895 return NO; 3896} 3897 3898/* created: 16.09.95 3899 * modified: 2001-08-18 3900 * parameter: p clicked point 3901 * purpose: check whether point hits object 3902 */ 3903- (BOOL)hit:(NSPoint)p fuzz:(float)fuzz 3904{ int i; 3905 int hit = NO; 3906 BOOL alternate = [(App*)NSApp alternate]; 3907 3908 if ( !Prefs_SelectByBorder && filled && [self isPointInside:p] ) 3909 return YES; 3910 3911 for (i=[list count]-1; i>=0; i--) 3912 { id obj = [list objectAtIndex:i]; 3913 3914 if ([obj hit:p fuzz:fuzz]) 3915 { hit = YES; 3916 [self deselectAll]; 3917 if (alternate) 3918 { [obj setSelected:YES]; 3919 if (selectedObject >= 0 && selectedObject != i) 3920 [[list objectAtIndex:selectedObject] setSelected:NO]; 3921 selectedObject = i; 3922 } 3923 break; 3924 } 3925 } 3926 3927 return (BOOL)hit; 3928} 3929 3930- (void)changeDirectionOfSubPath:(int)startIx :(int)endIx 3931{ int i, j; 3932 3933 for (i=startIx, j=endIx; i<=j; i++, j--) 3934 { id obj; 3935 3936 [[list objectAtIndex:i] changeDirection]; 3937 if ( i==j ) 3938 break; 3939 [[list objectAtIndex:j] changeDirection]; 3940 obj = [[list objectAtIndex:j] retain]; 3941 [list replaceObjectAtIndex:j withObject:[list objectAtIndex:i]]; 3942 [list replaceObjectAtIndex:i withObject:obj]; 3943 [obj release]; 3944 } 3945 dirty = YES; 3946} 3947 3948/* created: 1998-04-02 3949 * modified: 2012-01-20 (open path do not change direction here - allways wrong) 3950 * 2008-10-11 (use getLastObjectOfSubPath2 to allow open paths -- no) 3951 * optimize the way that the outer paths are optimized in ccw (cw) direction 3952 * and the inner paths are optimized in cw (ccw) direction 3953 * outer paths are the one with an even intersection count 3954 */ 3955- (void)setDirectionCCW:(BOOL)ccw 3956{ int begIx = 0, endIx = 0, cnt = [list count], i, lCount=0, iCntTot; 3957 NSPoint p, p0, p1, *pts = NULL; 3958 BOOL isCCW, needCCW; 3959 VLine *line = [VLine line]; 3960 NSRect bRect; 3961 float y, yMin, yMax, stepWi; 3962 3963 /* open path - we cant determine direction */ 3964 if ( ![self closed] ) 3965 { isDirectionCCW = ccw; 3966 return; 3967 } 3968 3969 while ( begIx < cnt ) 3970 { 3971 /* localize path */ 3972 endIx = [self getLastObjectOfSubPath:begIx]; // tolerance:TOLERANCE 3973 3974 /* single element (hack?) */ 3975 if ( begIx == endIx && ![[list objectAtIndex:begIx] isKindOfClass:[VArc class]] && 3976 ![[list objectAtIndex:begIx] isKindOfClass:[VPolyLine class]]) 3977 { begIx = endIx + 1; 3978 continue; 3979 } 3980 3981 /* count intersections of a horizontal line (with intersects our subpath) with the path */ 3982 [[list objectAtIndex:begIx] getPoint:&p at:0]; 3983 bRect = [self coordBounds]; 3984 p0.x = bRect.origin.x - 2000.0; p1.x = bRect.origin.x+bRect.size.width + 2000.0; 3985 bRect = [self coordBoundsOfSubPath:begIx :endIx]; 3986 yMin = bRect.origin.y; 3987 yMax = bRect.origin.y + bRect.size.height; 3988 stepWi = Max((yMax - yMin) / 10.0, TOLERANCE); 3989 for ( y=yMin+stepWi; y<yMax; y+=stepWi ) 3990 { 3991 p0.y = p1.y = y; 3992 [line setVertices:p0 :p1]; 3993 /* determine x for comparison left/right */ 3994 for (i=begIx; i<=endIx; i++) 3995 { 3996 if ( [[list objectAtIndex:i] getIntersections:&pts with:line] ) 3997 { p.x = pts[0].x; 3998 free(pts); pts = NULL; 3999 break; 4000 } 4001 } 4002 for (i=[list count]-1, lCount=0, iCntTot=0; i>=0; i--) 4003 { int j, iCnt; 4004 4005 if ( i >= begIx && i <= endIx ) 4006 continue; 4007 if ( (iCnt = [[list objectAtIndex:i] getIntersections:&pts with:line]) ) 4008 { iCntTot += iCnt; 4009 for ( j=0; j<iCnt; j++ ) 4010 if ( pts[j].x < p.x ) 4011 lCount++; 4012 free(pts); pts = NULL; 4013 } 4014 } 4015 //cnt = vhfFilterPoints(pts, cnt, 0.01) 4016 if ( Even(iCntTot) ) /* ok, we hit no edge */ 4017 break; 4018 } 4019 4020 /* if intersections to the left of our subpath are even this subpath becomes ccw (cw) */ 4021 isCCW = [self directionOfSubPath:begIx :endIx]; 4022 needCCW = (lCount%2) ? !ccw : ccw; 4023 if ( needCCW != isCCW ) 4024 [self changeDirectionOfSubPath:begIx :endIx]; 4025 begIx = endIx + 1; 4026 } 4027 isDirectionCCW = ccw; 4028} 4029 4030- (int)getFirstObjectOfSubPath:(int)ix 4031{ int i, cnt, begIx; 4032 NSPoint beg, end; 4033 4034 cnt = [list count]; 4035 beg = [[list objectAtIndex:ix] pointWithNum:0]; 4036 for ( i=ix-1; i>=0; i-- ) 4037 { 4038 end = [[list objectAtIndex:i] pointWithNum:MAXINT]; 4039 if ( !i || SqrDistPoints(beg, end) > (TOLERANCE*5)*(TOLERANCE*5) ) // small tolerance 4040 { begIx = i; 4041 if (i) 4042 begIx += 1; // dist to i -> i 4043 return begIx; 4044 } 4045 beg = [[list objectAtIndex:i] pointWithNum:0]; 4046 } 4047 return ix; 4048} 4049 4050/* find end of sub-path by testing between graphics for small distance 4051 * If we find a greater distance -> we find our end 4052 * 4053 * Note: This method works also for open paths ! 4054 * Attention for questionings like: If ( begIx == endIx ) for open paths ! 4055 * modified: 2008-07-06 4056 */ 4057- (int)getLastObjectOfSubPath2:(int)startIx 4058{ int i, cnt, endIx = startIx; 4059 float d; 4060 NSPoint start, beg, end; 4061 4062 cnt = [list count]; 4063 if (cnt <= startIx) 4064 return startIx; 4065 start = [[list objectAtIndex:startIx] pointWithNum:0]; 4066 end = [[list objectAtIndex:startIx] pointWithNum:MAXINT]; 4067 for ( i=startIx+1; i<cnt; i++ ) 4068 { 4069 beg = [[list objectAtIndex:i] pointWithNum:0]; 4070 if ( (d=SqrDistPoints(end, beg)) < (TOLERANCE*15)*(TOLERANCE*15) ) 4071 { end = [[list objectAtIndex:i] pointWithNum:MAXINT]; 4072 endIx = i; 4073 continue; // graphics connectet 4074 } 4075 else 4076 return endIx; 4077 } 4078 return endIx; 4079} 4080 4081/* find end of sub-path by testing path-points for a small distance 4082 * to start point. 4083 * If a point is closer to start than to the next point -> that's our end 4084 * 4085 * Note: This method only works for closed paths ! 4086 */ 4087- (int)getLastObjectOfSubPath:(int)startIx 4088{ int i, cnt, endIx; 4089 NSPoint beg, end; 4090 4091 cnt = [list count]; 4092 beg = [[list objectAtIndex:startIx] pointWithNum:0]; 4093 for ( i=startIx; i<cnt; i++ ) 4094 { 4095 /* for i == startIx we also check if the object is closed by itself (rectangle, circle, ...) */ 4096 end = [[list objectAtIndex:i] pointWithNum:MAXINT]; 4097 if ( SqrDistPoints(beg, end) < (TOLERANCE*15)*(TOLERANCE*15) ) // small tolerance for -removeSingleGraphicsAtEnd, setDirectionCCW, contour:inlay:removeloops 4098 { 4099 if (i+1 < cnt) 4100 { NSPoint begN = [[list objectAtIndex:i+1] pointWithNum:0]; // start of next element 4101 4102 if ( SqrDistPoints(end, begN) < (TOLERANCE*15)*(TOLERANCE*15) ) 4103 continue; // distance to next start point is better -> we stay in sub-path 4104 } 4105 endIx = i; 4106 return endIx; 4107 } 4108 } 4109 return startIx; 4110} 4111 4112#define GradientNear(g, t) ([g isKindOfClass:[VCurve class]]) ? [g gradientNear:t] : [g gradientAt:t] 4113 4114static NSPoint orthPointAt(id g, float r, int dirInd, float at) 4115{ float b; 4116 NSPoint p, grad, orthP; 4117 4118 p = [g pointAt:at]; /* point of object */ 4119 grad = GradientNear(g, at); /* gradient of point for outline object */ 4120 4121 if ( !(b = sqrt(grad.x*grad.x+grad.y*grad.y)) ) 4122 orthP = p; 4123 else 4124 { orthP.x = p.x + grad.y*r*dirInd/b; 4125 orthP.y = p.y - grad.x*r*dirInd/b; 4126 } 4127 return orthP; 4128} 4129 4130/* created: 2008-04-14 4131 * modified: 2008-04-14 4132 * purpose: get the direction of the elements from startIx until endIx 4133 * parameter: startIx 4134 * endIx 4135 * return: 1 = ccw / 0 = cw 4136 * 4137 * get a pt little left/right of one sp graphic which do not cut the subPath 4138 * look if pt is inside or outside of subPath -> so we have our direction 4139 * 4140 */ 4141- (int)directionOfSubPath:(int)startIx :(int)endIx 4142{ int i, j; 4143 float dirInd = 1.0, d = 0.3; // right = 1.0 left = -1.0 ????? 4144 VLine *tLine = [VLine line]; 4145 NSRect cBnds; 4146 4147 if ( startIx == endIx ) // this is a 360 degree arc (circle) 4148 { VGraphic *obj = [list objectAtIndex:startIx]; 4149 4150 if ( [obj isKindOfClass:[VArc class]] ) 4151 return ( [(VArc*)obj angle] > 0 ) ? 1 : 0; 4152 if ( [obj isKindOfClass:[VPolyLine class]] ) 4153 return [(VPolyLine*)obj isDirectionCCW]; 4154 return 1; 4155 } 4156 for ( i=startIx; i<=endIx; i++ ) 4157 { id objI = [list objectAtIndex:i]; 4158 NSPoint oPt, beg = [objI pointAt:0.5]; // gradient at start 4159 int cnt, intersection = NO, inside=-1; 4160 4161 /* get pt orthogonal pt right of objI */ 4162 oPt = orthPointAt(objI, d, dirInd, 0.5); // 0 is Beg 4163 [tLine setVertices:beg :oPt]; 4164 4165 /* check if the line between pt and p05 intersect our subPath */ 4166 for (j=startIx; j<=endIx; j++) 4167 { VGraphic *objJ = [list objectAtIndex:j]; 4168 NSPoint *pts; 4169 4170 if ( (cnt = [objJ getIntersections:&pts with:tLine]) ) 4171 { 4172 free(pts); pts = NULL; 4173 if ( !(cnt == 1 && objJ == objI) ) 4174 { 4175 intersection = YES; 4176 break; 4177 } 4178 } 4179 } 4180 /* else take the next graphic */ 4181 if ( intersection ) 4182 continue; 4183 4184 /* get check if pt is inside or outside our subPath */ 4185 /* 0 = outside * 1 = on * 2 = inside */ 4186 inside = [self isPointInsideOrOn:oPt dist:TOLERANCE subPath:startIx :endIx]; 4187 4188 if ( inside == 1 ) 4189 continue; // on ? 4190 /* outside - ccw - 1 */ 4191 if ( !inside ) 4192 return 1; 4193 /* inside - cw - 0 */ 4194 else 4195 return 0; 4196 } 4197 4198 dirInd = -1,0; // left 4199 /* check for points left of graphics */ 4200 for ( i=startIx; i<=endIx; i++ ) 4201 { id objI = [list objectAtIndex:i]; 4202 NSPoint oPt, beg = [objI pointAt:0.5]; // gradient at start 4203 int cnt, intersection = NO, inside=-1; 4204 4205 /* get pt orthogonal pt right of objI */ 4206 oPt = orthPointAt(objI, d, dirInd, 0.5); // 0 is Beg 4207 [tLine setVertices:beg :oPt]; 4208 4209 /* check if the line between pt and p05 intersect our subPath */ 4210 for (j=startIx; j<=endIx; j++) 4211 { VGraphic *objJ = [list objectAtIndex:j]; 4212 NSPoint *pts; 4213 4214 if ( (cnt = [objJ getIntersections:&pts with:tLine]) ) 4215 { 4216 free(pts); pts = NULL; 4217 if ( !(cnt == 1 && objJ == objI) ) 4218 { 4219 intersection = YES; 4220 break; 4221 } 4222 } 4223 } 4224 /* else take the next graphic */ 4225 if ( intersection ) 4226 continue; 4227 4228 /* get check if pt is inside or outside our subPath */ 4229 /* 0 = outside * 1 = on * 2 = inside */ 4230 inside = [self isPointInsideOrOn:oPt dist:TOLERANCE subPath:startIx :endIx]; 4231 4232 if ( inside == 1 ) 4233 continue; // on ? 4234 /* outside - cw - 0 */ 4235 if ( !inside ) 4236 return 0; 4237 /* inside - ccw - 1 */ 4238 else 4239 return 1; 4240 } 4241 cBnds = [self coordBoundsOfSubPath:startIx :endIx]; 4242 if ( !(Diff(startIx, endIx) <= 10 && (cBnds.size.width < 10.0*TOLERANCE || cBnds.size.height < 10.0*TOLERANCE)) ) 4243 NSLog(@"VPath.m: -directionOfSubPath: cant determine direction Bnds: o.x:%.2f o.y:%.2f s.w:%.2f s.h:%.2f", 4244 cBnds.origin.x, cBnds.origin.y, cBnds.size.width, cBnds.size.height); 4245 return 0; 4246} 4247#if 0 4248/* created: 1996-06-08 4249 * modified: 2001-02-16 4250 * purpose: get the direction of the elements from startIx until endIx 4251 * parameter: startIx 4252 * endIx 4253 * return: 1 = ccw / 0 = cw 4254 * 4255 * rangles = sum of ccw angles 4256 * langles = sum of 360 - ccw angles 4257 * 4258 * rAngles = ccw angle between next Object to current Object 4259 * 4260 * extreme curves building a loop in the path are still a problem! 4261 */ 4262- (int)directionOfSubPath:(int)startIx :(int)endIx 4263{ int i; 4264 float a = 0.0, rAngles = 0, lAngles = 0; 4265 4266 if ( startIx == endIx ) // this is a 360 degree arc (circle) 4267 { VGraphic *obj = [list objectAtIndex:startIx]; 4268 4269 if ( [obj isKindOfClass:[VArc class]] ) 4270 return ( [(VArc*)obj angle] > 0 ) ? 1 : 0; 4271 if ( [obj isKindOfClass:[VPolyLine class]] ) 4272 return [(VPolyLine*)obj isDirectionCCW]; 4273 return 1; 4274 } 4275 for ( i=startIx; i<=endIx; i++ ) 4276 { VGraphic *objI = [list objectAtIndex:i]; 4277 4278 /* angle of object */ 4279 if ( ([objI isKindOfClass:[VArc class]] || [objI isKindOfClass:[VCurve class]]) 4280 && (a = [objI angle]) ) 4281 { 4282 if ( [objI isKindOfClass:[VArc class]] ) 4283 { 4284 /* we process the arc in quadrants and 4285 * convert the arc angle into an angle between the tangents of 4286 * the start and end points of each quadrant (180 - angle) and the rest: 4287 * angle <= 90 -> 180 - angle 4288 * angle <= 180 -> 90 + (180-(angle-90)) 4289 * angle <= 270 -> 90 + 90 + (180-(angle-180)) 4290 * all angles have to be positive (ccw) in the end ! 4291 */ 4292 if (a >= 0.0) 4293 { float n = floor(a / 90.0); // number of quadrants. 4294 a = 180.0 - (a - n*90.0); // convert rest of angle to tangent angle. 4295 rAngles += n* 90.0; // add tangent angles for quadrants, but 4296 lAngles += n*270.0; // leave rest for usual addition 4297 } 4298 else 4299 { float n = floor(a / -90.0); // number of quadrants 4300 a = 180.0 - (a - n*-90.0); // convert rest to tangent angle 4301 rAngles += n*270.0; 4302 lAngles += n* 90.0; 4303 } 4304 4305 /*s = [objI pointWithNum:0]; // this gives not the correct angle 4306 e = [objI pointWithNum:MAXINT]; // we should take the intersection of the tangents 4307 m = [(VArc*)objI pointAt:0.5]; // But, why not convert the angle of the arc? 4308 a = vhfAngleBetweenPoints(e, m, s);*/ 4309 } 4310 rAngles += a; 4311 if ( [objI isKindOfClass:[VCurve class]] ) // curve builds two edges 4312 lAngles += 2*360.0 - a; 4313 else 4314 lAngles += 360 - a; 4315 } 4316 /* angle between end tangent and next start tangent */ 4317 a = 360.0 - angleBetweenGraphicsInStartOrEnd(objI, (i<endIx) ? [list objectAtIndex:i+1] 4318 : [list objectAtIndex:startIx], YES); 4319 if ( Diff(a, 0.0) > 1 && Diff(a, 360.0) > 1 ) // 0.0 was > TOLERANCE 4320 { rAngles += a; 4321 lAngles += 360.0 - a; 4322 } 4323 } 4324 return (rAngles < lAngles) ? 1 : 0; 4325} 4326#endif 4327 4328/* created: 05.06.98 4329 * modified: 4330 * purpose: get the bounds of the elements from startIx until endIx 4331 * parameter: startIx 4332 * endIx 4333 * return: bounds 4334 */ 4335- (NSRect)coordBoundsOfSubPath:(int)startIx :(int)endIx 4336{ int i; 4337 NSRect bbox, rect; 4338 4339 bbox = [[list objectAtIndex:startIx] coordBounds]; 4340 for ( i=startIx+1; i<=endIx; i++ ) 4341 { 4342 rect = [[list objectAtIndex:i] coordBounds]; 4343 bbox = VHFUnionRect(rect, bbox); 4344 } 4345 return bbox; 4346} 4347 4348//#define Even(x) (((x)%2) ? 0 : 1) 4349#define SIDESTEP 0.15 // 0.3 4350 4351- (BOOL)subPathInsidePath:(int)begIx :(int)endIx 4352{ int k, lenSP = Min(100, [list count]), len = Min(100, [list count]); 4353 int s, i, j, listCnt, iCnt=0, leftCnt, curSubPathEndIx = -1, spIcnt = 0, on = 0; 4354 float tol = 0.1; 4355 NSPoint end, lBeg, lEnd, *iPts, *spIpts; // iPts[[list count]], spIpts[[list count]]; 4356 NSRect bRect, spRect; 4357 VGraphic *lineG = [VLine line]; 4358 4359 iPts = malloc(len * sizeof(NSPoint)); 4360 spIpts = malloc(lenSP * sizeof(NSPoint)); 4361 4362 /* cut all graphics in path (but from begIx until endIx (our subPath)) 4363 * with horicontal line through end of begIx graphic 4364 */ 4365 spRect = [self coordBoundsOfSubPath:begIx :endIx]; 4366 bRect = [self bounds]; 4367 tol = (spRect.size.height/10.0); //*(spRect.size.height/10.0); 4368 4369 for (k=begIx; k<=endIx; k++) 4370 { 4371 on = 0; 4372 [[list objectAtIndex:k] getPoint:&end at:0.4]; 4373 4374 /*if ( k < endIx && (Diff(end.y, spRect.origin.y) <= tol || 4375 Diff(end.y, spRect.origin.y+spRect.size.height) <= tol) ) 4376 continue;*/ // we check this now inside in an exacter way 4377 lBeg.y = lEnd.y = end.y; 4378 lBeg.x = bRect.origin.x -10.0; 4379 lEnd.x = bRect.origin.x + bRect.size.width + 10.0; 4380 [(VLine*)lineG setVertices:lBeg :lEnd]; 4381 4382 for (s=1; s<9; s++) 4383 { int left = 0, spLeft = 0, spRight = 0; 4384 NSRect curRect=spRect; 4385 4386 on = 0; 4387 spIcnt = 0; 4388 iCnt = 0; 4389 curSubPathEndIx = ( 0 == begIx ) ? (endIx) : ([self getLastObjectOfSubPath2:0]); 4390 if ( 0 != begIx ) 4391 curRect = [self coordBoundsOfSubPath:0 :curSubPathEndIx]; 4392 4393 for ( i=0, listCnt = [list count]; i<listCnt; i++ ) 4394 { VGraphic *g = [list objectAtIndex:i]; 4395 NSRect gBnds = [g coordBounds]; 4396 NSPoint *pts = NULL; 4397 int cnt=0; 4398 4399 if ( i > curSubPathEndIx) 4400 { 4401 /* add spIpts to iPts*/ 4402 /* check if one spIpt is on end 4403 * remove on point if spIpts are right of end odd (else add) 4404 */ 4405 spLeft = 0; spRight = 0; 4406 on = 0; left = 0; 4407 for (j=0; j<spIcnt; j++) 4408 { 4409 if ( Diff(spIpts[j].x, end.x) < 100.0*TOLERANCE ) 4410 { on++; 4411 break; 4412 } 4413 else if ( spIpts[j].x < end.x ) 4414 left++; 4415 4416 if ( spIpts[j].x < spRect.origin.x ) 4417 spLeft++; 4418 else if ( spIpts[j].x > spRect.origin.x+spRect.size.width ) 4419 spRight++; 4420 } 4421 if ( on ) 4422 break; // need other k graphic 4423 /* we dont add the hole subPath if on/it is on right side (else we shot our Even iCnt) */ 4424 if ( !(on && Even(left)) && left && !(spLeft == spIcnt || spRight == spIcnt) ) 4425 { 4426 if (iCnt+spIcnt >= len) 4427 iPts = realloc(iPts, (len+=spIcnt*2) * sizeof(NSPoint)); 4428 for (j=0; j<spIcnt; j++) 4429 iPts[iCnt++] = spIpts[j]; 4430 } 4431// else if ( spIcnt ) 4432// NSLog(@"VPath.m: -subPathInsidePath: checken !!!"); 4433 4434 spIcnt = 0; 4435 4436 curSubPathEndIx = ( i == begIx ) ? (endIx) : ([self getLastObjectOfSubPath2:i]); 4437 if ( i != begIx ) 4438 curRect = [self coordBoundsOfSubPath:i :curSubPathEndIx]; 4439 } 4440 if ( i >= begIx && i <= endIx ) 4441 continue; 4442 4443 /* end.y is too near to other subPath up/down y */ 4444 //if ( k < endIx && (Diff(end.y, curRect.origin.y) <= SIDESTEP*SIDESTEP || 4445 // Diff(end.y, curRect.origin.y+curRect.size.height) <= SIDESTEP*SIDESTEP) ) 4446 if ( k < endIx && gBnds.size.height < gBnds.size.width 4447 /* gBnds width bereich sollte spRect treffen */ 4448 && ((gBnds.origin.x >= spRect.origin.x 4449 && gBnds.origin.x+gBnds.size.width <= spRect.origin.x+spRect.size.width) 4450 || (gBnds.origin.x <= spRect.origin.x 4451 && gBnds.origin.x+gBnds.size.width >= spRect.origin.x) 4452 || (gBnds.origin.x <= spRect.origin.x+spRect.size.width 4453 && gBnds.origin.x+gBnds.size.width >= spRect.origin.x+spRect.size.width)) 4454 && curRect.origin.x+curRect.size.width >= end.x-100.0*TOLERANCE 4455 && (Diff(lEnd.y, gBnds.origin.y) <= 100.0*TOLERANCE || 4456 Diff(lEnd.y, gBnds.origin.y+gBnds.size.height) <= 100.0*TOLERANCE) ) 4457 { 4458 k++; 4459 [[list objectAtIndex:k] getPoint:&end at:0.4]; 4460 4461 lBeg.y = lEnd.y = end.y; 4462 lBeg.x = bRect.origin.x -10.0; 4463 lEnd.x = bRect.origin.x + bRect.size.width + 10.0; 4464 [(VLine*)lineG setVertices:lBeg :lEnd]; 4465 s = 1; 4466 on++; // iCnt = -1; no sidestep ! 4467 break; 4468 } 4469 4470 /* check bounds of current subPath to spRect - completly left or right or inside spRect - continue behind */ 4471 if ( !vhfIntersectsRect(curRect, spRect) /*|| vhfContainsRect(spRect, curRect)*/ ) 4472 { i = curSubPathEndIx; 4473 continue; 4474 } 4475 4476 cnt = [g getIntersections:&pts with:lineG]; 4477 4478 if ( cnt == 2 && 4479 ( [g isKindOfClass:[VLine class]] || // horicontal line 4480 ([g isKindOfClass:[VArc class]] && pts[0].x == pts[1].x) ) ) // tangential arc 4481 { iCnt = -1; // not even 4482 free(pts); pts = NULL; 4483 break; 4484 } 4485 else if ( cnt && [g isKindOfClass:[VRectangle class]] ) 4486 { NSRect gBounds = [g coordBounds]; 4487 4488 /* if one intersectionpoint layes on the uppest OR lowest y value of the rectangle */ 4489 for (j=0; j<cnt; j++) 4490 { 4491 if ( (Diff(pts[j].y, gBounds.origin.y) <= TOLERANCE) || 4492 (Diff(pts[j].y, (gBounds.origin.y+gBounds.size.height)) <= TOLERANCE) ) 4493 { 4494 free(pts); pts = NULL; 4495 iCnt = -1; // not even 4496 break; 4497 } 4498 } 4499 } 4500 else if ( [g isKindOfClass:[VCurve class]] && cnt ) 4501 { NSPoint p0, p1, p2, p3, tpoints[3]; 4502 int cpt, realSol=0, numSol=0; 4503 double cy, by, ay, t[3]; 4504 4505 [(VCurve*)g getVertices:&p0 :&p1 :&p2 :&p3]; 4506 /* we must look if one of the intersection points lying on a extrem point of the curve 4507 * represent the curve with the equations 4508 * x(t) = ax*t^3 + bx*t^2 + cx*t + x(0) 4509 * y(t) = ay*t^3 + by*t^2 + cy*t + y(0) 4510 * -> 3ay*t^2 + 2by*t + cy = 0 4511 */ 4512 cy = 3*(p1.y - p0.y); 4513 by = 3*(p2.y - p1.y) - cy; 4514 ay = p3.y - p0.y - by - cy; 4515 4516 /* get the ts in which the tangente is horicontal 4517 */ 4518 numSol = svPolynomial2( 3.0*ay, 2.0*by, cy, t); 4519 4520 /* when t is on curve */ 4521 realSol=0; 4522 for ( j=0 ; j<numSol ; j++) 4523 if ( t[j] >= 0.0 && t[j] <= 1.0 ) 4524 tpoints[realSol++] = [(VCurve*)g pointAt:t[j]]; 4525 4526 /* if one intersection point is identical with one tpoint -> -1 */ 4527 for ( j=0 ; j<realSol ;j++ ) 4528 for ( cpt=0 ; cpt<cnt ; cpt++ ) 4529 if ( Diff(tpoints[j].x, pts[cpt].x) <= TOLERANCE && 4530 Diff(tpoints[j].y, pts[cpt].y) <= TOLERANCE ) 4531 { 4532 free(pts); pts = NULL; 4533 iCnt = -1; 4534 break; 4535 } 4536 } 4537 else if ( cnt > 1 && [g isKindOfClass:[VPolyLine class]] ) 4538 { int p, nPts = [g numPoints]; 4539 NSPoint pl0, pl1; 4540 4541 /* check each line in PolyLine if horicontal */ 4542 for (p=0; p < nPts-1; p++) 4543 { 4544 pl0 = [g pointWithNum:p]; 4545 pl1 = [g pointWithNum:p+1]; 4546 4547 if (pointWithToleranceInArray(pl0, TOLERANCE, pts, cnt) && // both point are in pts 4548 pointWithToleranceInArray(pl1, TOLERANCE, pts, cnt)) 4549 { 4550 free(pts); pts = NULL; 4551 iCnt = -1; 4552 break; 4553 } 4554 } 4555 } 4556 4557 // add points if not allways inside pparray 4558 // else check if pt is edge pt of graphic -> return -1 4559 for (j=0; j<cnt && iCnt != -1; j++) 4560 { 4561 if (spIcnt+cnt >= lenSP) 4562 spIpts = realloc(spIpts, (lenSP+=cnt*2) * sizeof(NSPoint)); 4563 4564 if ( !pointWithToleranceInArray(pts[j], 2.0*TOLERANCE, spIpts, spIcnt) ) 4565 spIpts[spIcnt++] = pts[j]; 4566 else 4567 { NSPoint startG, endG; 4568 4569 if ( [g isKindOfClass:[VLine class]] ) /* line */ 4570 [(VLine*)g getVertices:&startG :&endG]; 4571 else if ( [g isKindOfClass:[VArc class]] || [g isKindOfClass:[VCurve class]] ) 4572 { startG = [g pointWithNum:0]; 4573 endG = [g pointWithNum:MAXINT]; 4574 } 4575 else if ( [g isKindOfClass:[VRectangle class]] ) 4576 { NSPoint ur, ul, size; 4577 [(VRectangle*)g getVertices:&startG :&size]; // origin size 4578 endG = startG; endG.x += size.x; 4579 ul = startG; ul.y += size.y; 4580 ur = endG; ur.y += size.y; 4581 if ( (Diff(pts[j].x, ul.x) + Diff(pts[j].y, ul.y) < 10.0*TOLERANCE) || 4582 (Diff(pts[j].x, ur.x) + Diff(pts[j].y, ur.y) < 10.0*TOLERANCE) ) 4583 continue; // do not add 4584 } 4585 else if ( [g isKindOfClass:[VPolyLine class]] ) 4586 { int k, pCnt = [(VPolyLine*)g ptsCount], stop = 0; 4587 4588 for (k=1; k<pCnt-1; k++) 4589 { NSPoint pt = [(VPolyLine*)g pointWithNum:k]; 4590 if ( Diff(pts[j].x, pt.x) + Diff(pts[j].y, pt.y) < 10.0*TOLERANCE ) 4591 { stop = 1; break; } 4592 } 4593 if (stop) 4594 continue; // do not add 4595 [(VPolyLine*)g getEndPoints:&startG :&endG]; 4596 } 4597 else 4598 { startG.x = endG.x = pts[j].x; startG.y = endG.y = pts[j].y; 4599 } 4600 /* point is no edge point of g -> add */ 4601 if ( (Diff(pts[j].x, startG.x) + Diff(pts[j].y, startG.y) > 10.0*TOLERANCE) && 4602 (Diff(pts[j].x, endG.x) + Diff(pts[j].y, endG.y) > 10.0*TOLERANCE) ) 4603 spIpts[spIcnt++] = pts[j]; 4604 else 4605 { 4606 iCnt = -1; // /\ peek of this contraction twice force an error 4607 break; 4608 } 4609 } 4610 } 4611 if (pts) 4612 free(pts); 4613 if ( iCnt < 0 ) 4614 break; // sidestep ! 4615 } 4616 if ( on ) 4617 break; // need other k Graphic 4618 4619 if ( iCnt < 0 ) 4620 { spIcnt = 0; 4621 lBeg.y = lEnd.y = end.y + ((s%2) ? (-SIDESTEP*s) : (SIDESTEP*s)); 4622 while ( (lBeg.y < spRect.origin.y || lBeg.y > spRect.origin.y+spRect.size.width) && s < 7 ) 4623 { 4624 s++; 4625 lBeg.y = lEnd.y = end.y + ((s%2) ? (-SIDESTEP*s) : (SIDESTEP*s)); 4626 } 4627 [(VLine*)lineG setVertices:lBeg :lEnd]; 4628 continue; // sidestep ! 4629 } 4630 /* add spIpts */ 4631 /* check if one spIpt is on end 4632 * remove on point if spIpts are right of end odd (else add) 4633 */ 4634 spLeft = 0; spRight = 0; 4635 on = 0; left = 0; 4636 for (j=0; j<spIcnt; j++) 4637 { 4638 if ( Diff(spIpts[j].x, end.x) < 100.0*TOLERANCE ) 4639 { on++; 4640 break; 4641 } 4642 else if ( spIpts[j].x < end.x ) 4643 left++; 4644 4645 if ( spIpts[j].x < spRect.origin.x ) 4646 spLeft++; 4647 else if ( spIpts[j].x > spRect.origin.x+spRect.size.width ) 4648 spRight++; 4649 } 4650 if ( on ) 4651 break; // need other k Graphic 4652 /* we dont add the hole subPath if on/it is on right side (else we shot our Even iCnt) */ 4653 if ( !(on && Even(left)) && left && !(spLeft == spIcnt || spRight == spIcnt) ) 4654 { 4655 if (iCnt+spIcnt >= len) 4656 iPts = realloc(iPts, (len+=spIcnt*2) * sizeof(NSPoint)); 4657 for (j=0; j<spIcnt; j++) 4658 iPts[iCnt++] = spIpts[j]; 4659 } 4660 if ( (Even(iCnt) && iCnt) || (!iCnt && s == 1) ) // (cnt || s == 1) && 4661 break; 4662 4663 lBeg.y = lEnd.y = end.y + ((s%2) ? (-SIDESTEP*s) : (SIDESTEP*s)); 4664 while ( (lBeg.y < spRect.origin.y || lBeg.y > spRect.origin.y+spRect.size.width) && s < 7 ) 4665 { 4666 s++; 4667 lBeg.y = lEnd.y = end.y + ((s%2) ? (-SIDESTEP*s) : (SIDESTEP*s)); 4668 } 4669 [(VLine*)lineG setVertices:lBeg :lEnd]; 4670 } 4671 if ( !on && ((Even(iCnt) && iCnt) || (!iCnt && s == 1)) ) 4672 break; 4673// else 4674// NSLog(@"VPath.m: -subPathInsidePath: take next graphic"); 4675 } 4676 if ( iCnt < 0 ) 4677 { 4678 NSLog(@"VPath.m: -subPathInsidePath: possible fault x:%.2f y:%.2f", end.x, end.y); 4679 return NO; 4680 } 4681 /* count iPts left of end */ 4682 leftCnt = 0; 4683 for (i=0; i<iCnt; i++) 4684 if ( iPts[i].x < end.x || Diff(iPts[i].x, end.x) < 2.0*TOLERANCE ) 4685 leftCnt++; 4686 4687 /* release iPts */ 4688 if ( [list count] ) 4689 { free(iPts); 4690 free(spIpts); 4691 //NSZoneFree((NSZone*)[(NSObject*)NSApp zone], *ppArray); 4692 iPts = spIpts = 0; 4693 } 4694 4695 if ( !leftCnt || leftCnt == iCnt ) /* all points right or left of end */ 4696 return NO; 4697 return ( Even(leftCnt) ) ? NO : YES; 4698} 4699 4700/* 4701 * optimize path - return NO if we found a gap 4702 */ 4703- (BOOL)optimizePath:(float)w 4704{ int i1, i2, changeI, startIndex = 0; 4705 float startDistance = MAXCOORD, dist = w*w, dHalf = (w/2.0)*(w/2.0), d1, d2, d, ds1, ds2; 4706 NSPoint l1S, l1E, l2S, l2E, sGS = NSZeroPoint; 4707 BOOL closedPath = NO; // after YES no insertion befor startIndex 4708 4709 if ( ![list count] ) 4710 return NO; 4711 4712 for (i1 = 0; i1<(int)[list count]-1; i1++) 4713 { VGraphic *l1=[list objectAtIndex:i1]; 4714 BOOL insertAtStartIndex = NO; 4715 4716 if ( [l1 isKindOfClass:[VRectangle class]] || 4717 ([l1 isKindOfClass:[VArc class]] && Abs([(VArc*)l1 angle]) == 360.0) || 4718 ([l1 isKindOfClass:[VPolyLine class]] && 4719 SqrDistPoints([l1 pointWithNum:0], [l1 pointWithNum:MAXINT]) < TOLERANCE) ) 4720 { startIndex++; 4721 continue; 4722 } 4723 sGS = [[list objectAtIndex:startIndex] pointWithNum:0]; 4724 startDistance = MAXCOORD; 4725 changeI = i1+1; 4726 l1S = [l1 pointWithNum:0]; 4727 l1E = [l1 pointWithNum:MAXINT]; 4728 for (i2=i1+1; i2<(int)[list count]; i2++) 4729 { VGraphic *l2=[list objectAtIndex:i2]; 4730 4731 l2S = [l2 pointWithNum:0]; 4732 l2E = [l2 pointWithNum:MAXINT]; 4733 d1 = SqrDistPoints(l1E, l2S); d2 = SqrDistPoints(l1E, l2E); 4734 if ( d1 < startDistance || d2 < startDistance ) 4735 { 4736 if ( d2 < d1 ) 4737 { startDistance = d2; 4738 [l2 changeDirection]; 4739 l2S = [l2 pointWithNum:0]; 4740 l2E = [l2 pointWithNum:MAXINT]; 4741 } 4742 else 4743 startDistance = d1; 4744 changeI = i2; 4745 insertAtStartIndex = NO; 4746 if ( Diff(startDistance, 0.0) == 0.0 ) 4747 break; 4748 } 4749 /* distance to start graphic start point ! - open paths */ 4750 ds1 = SqrDistPoints(sGS, l2E); ds2 = SqrDistPoints(sGS, l2S); 4751 if ( closedPath == NO && (ds1 < startDistance || ds2 < startDistance) ) 4752 { 4753 if ( ds2 < ds1 ) 4754 { startDistance = ds2; 4755 [l2 changeDirection]; 4756 } 4757 else 4758 startDistance = ds1; 4759 changeI = i2; 4760 insertAtStartIndex = YES; 4761 if ( Diff(startDistance, 0.0) == 0.0 ) 4762 break; 4763 } 4764 } 4765 4766 if ( insertAtStartIndex && startDistance ) /* close hole - also for open paths */ 4767 { VGraphic *l2 = [list objectAtIndex:changeI], *sG = [list objectAtIndex:startIndex]; 4768 4769 d1 = SqrDistPoints(l1E, sGS); 4770 l2S = [l2 pointWithNum:0]; 4771 l2E = [l2 pointWithNum:MAXINT]; 4772 d = SqrDistPoints(l2E, sGS); 4773 /* if dist of l1E to startG is smaller than dist of nextG(l2) to l1S -> close to startG */ 4774 if ( d && d1 < d ) 4775 { d = d1; 4776 [l2 changeDirection]; // endpoint is nearest, not good vor startIndex, and l2 become next startGraphic 4777 l2 = sG; l2S = sGS; 4778 if ( d && d <= dist ) 4779 { VGraphic *lineG = [VLine line]; 4780 4781 if ( d <= dHalf ) 4782 { 4783 if ( [self closeGapBetween:l1 and:l2] ) // line added ! 4784 { i1 += 1; changeI += 1; } // behind l1 is correct here 4785 } 4786 else 4787 { [lineG setColor:[l2 color]]; 4788 [lineG setWidth:[l2 width]]; [lineG setSelected:NO]; 4789 [(VLine*)lineG setVertices:l1E :l2S]; 4790 [list insertObject:lineG atIndex:i1+1]; 4791 i1 += 1; changeI += 1; 4792 } 4793 } 4794 else if ( startDistance > dist && d1 > dist ) 4795 NSLog(@"VPath.m: -optimizePath:: Gap 0"); // return NO; 4796 insertAtStartIndex = NO; 4797 closedPath = YES; 4798 startIndex = i1+1; 4799 } 4800 else if ( d <= dist ) // close dist from l2 (changeG) to sG 4801 { VGraphic *lineG = [VLine line]; 4802 4803 if ( d <= dHalf ) 4804 { 4805 if ( [self closeGapBetween:l2 and:sG] ) 4806 { i1 += 1; changeI += 1; 4807 lineG = [list objectAtIndex:changeI]; 4808 [list insertObject:lineG atIndex:startIndex]; 4809 [list removeObjectAtIndex:changeI+1]; 4810 } // line added behind l2 ! falsch oft immer muss vor sG ! 4811 } 4812 else 4813 { [lineG setColor:[l2 color]]; 4814 [lineG setWidth:[l2 width]]; [lineG setSelected:NO]; 4815 [(VLine*)lineG setVertices:l2E :sGS]; 4816 [list insertObject:lineG atIndex:startIndex]; 4817 i1 += 1; changeI += 1; 4818 } 4819 } 4820 else if ( startDistance > dist && d > dist ) 4821 NSLog(@"VPath.m: -optimizePath:: Gap 0"); // return NO; 4822 if ( startDistance > dist ) 4823 startIndex = i1+1; 4824 } 4825 else if ( startDistance ) /* close hole */ 4826 { VGraphic *l2 = [list objectAtIndex:changeI], *sG = [list objectAtIndex:startIndex]; 4827 4828 d1 = SqrDistPoints(l1E, sGS); 4829 l2S = [l2 pointWithNum:0]; 4830 d = SqrDistPoints(l1E, l2S); 4831 /* if dist to startG is smaller than dist to nextG -> close to startG*/ 4832 if ( d && d1 < d ) 4833 { d = d1; 4834 l2 = sG; l2S = sGS; 4835 startIndex = i1+1; 4836 closedPath = YES; 4837 } 4838 if ( d && d <= dist ) 4839 { VGraphic *lineG = [VLine line]; 4840 4841 if ( d <= dHalf ) 4842 { 4843 if ( [self closeGapBetween:l1 and:l2] ) // line added ! 4844 { i1 += 1; changeI += 1; } // behind l1 ist correct here 4845 } 4846 else 4847 { [lineG setColor:[l2 color]]; 4848 [lineG setWidth:[l2 width]]; [lineG setSelected:NO]; 4849 [(VLine*)lineG setVertices:l1E :l2S]; 4850 [list insertObject:lineG atIndex:i1+1]; 4851 i1 += 1; changeI += 1; 4852 } 4853 } 4854 else if ( startDistance > dist && d1 > dist ) 4855 NSLog(@"VPath.m: -optimizePath:: Gap"); // return NO; 4856 if ( startDistance > dist ) 4857 startIndex = i1+1; 4858 } 4859 4860 if ( insertAtStartIndex ) 4861 { VGraphic *gCh=[list objectAtIndex:changeI]; 4862 4863 [list insertObject:gCh atIndex:startIndex]; 4864 [list removeObjectAtIndex:changeI+1]; 4865 } 4866 /* if the nearest element is not the next_in_list */ 4867 else if ( changeI != (i1+1) ) 4868 { VGraphic *gCh=[list objectAtIndex:changeI]; 4869 4870 [list insertObject:gCh atIndex:i1+1]; 4871 [list removeObjectAtIndex:changeI+1]; 4872 } 4873 } 4874 /* close hole from last to start element */ 4875 { VGraphic *l1=[list objectAtIndex:[list count]-1]; 4876 VGraphic *l2= [list objectAtIndex:startIndex]; 4877 4878 l1E = [l1 pointWithNum:MAXINT]; 4879 l2S = [l2 pointWithNum:0]; 4880 if ( (d=SqrDistPoints(l1E, l2S)) && d <= dist ) 4881 { VGraphic *lineG = [VLine line]; 4882 4883 if ( d <= dHalf ) 4884 [self closeGapBetween:l1 and:l2]; 4885 else 4886 { [lineG setColor:[l2 color]]; 4887 [lineG setWidth:[l2 width]]; [lineG setSelected:NO]; 4888 [(VLine*)lineG setVertices:l1E :l2S]; 4889 [list addObject:lineG]; 4890 } 4891 } 4892 else if ( d ) 4893 return NO; 4894 } 4895 4896 dirty = YES; 4897 return YES; 4898} 4899 4900/* 4901 * are the most meters of otherP inside path 4902 */ 4903- (BOOL)isMostOfOtherPathInsidePath:(VPath*)otherP :(VPath*)path 4904{ int i, cnt=[[otherP list] count], insideCnt=0, insideDist=0, outsideCnt=0, outsideDist=0; 4905 4906 /* otherPath is tiled from path ! 4907 * we count elements/distance of otherP wich are inside path and which not 4908 * if more than the half distance of hole otherP is inside path -> return YES 4909 */ 4910 for (i=0; i<cnt;i++) 4911 { VGraphic *g = [[otherP list] objectAtIndex:i]; 4912 NSPoint pIn, start, end; 4913 4914 [g getPoint:&pIn at:0.4]; 4915 start = [g pointWithNum:0]; 4916 end = [g pointWithNum:MAXINT]; 4917 4918 /* point inside path */ 4919 if ( [path isPointInside:pIn] ) 4920 { insideCnt++; 4921 insideDist += SqrDistPoints(start, end); 4922 } 4923 else 4924 { outsideCnt++; 4925 outsideDist += SqrDistPoints(start, end); 4926 } 4927 } 4928 if ( insideDist > (insideDist+outsideDist)/2.0 ) 4929 return YES; 4930 return NO; 4931} 4932 4933/* can only be used after optimize 4934 * remove graphics at list end which do not have two neighbours 4935 */ 4936- (BOOL)isShortGraphic:g 4937{ 4938 if ( [g isKindOfClass:[VLine class]] ) 4939 { NSPoint p0, p1; 4940 [(VLine*)g getVertices:&p0 :&p1]; 4941 if ( SqrDistPoints(p0, p1) < TOLERANCE*500.0 ) 4942 return YES; 4943 } 4944 else if ( [g isKindOfClass:[VRectangle class]] ) 4945 { NSPoint o, s; 4946 [(VRectangle*)g getVertices:&o :&s]; 4947 if ( s.x < TOLERANCE*100.0 && s.y < TOLERANCE*100.0 ) 4948 return YES; 4949 } 4950 else if ( [g isKindOfClass:[VArc class]] ) 4951 { float r = [(VArc*)g radius], a = [(VArc*)g angle]; 4952 if ( r < TOLERANCE*500.0 && Abs(a) < TOLERANCE*7000.0 ) 4953 return YES; 4954 } 4955 else if ( [g isKindOfClass:[VCurve class]] ) 4956 { NSPoint p0, p1, p2, p3; 4957 [(VCurve*)g getVertices:&p0 :&p1 :&p2 :&p3]; 4958 if ( SqrDistPoints(p0, p3) < TOLERANCE*1000.0 && SqrDistPoints(p0, p1) < TOLERANCE*500.0 && 4959 SqrDistPoints(p1, p2) < TOLERANCE*500.0 && SqrDistPoints(p2, p3) < TOLERANCE*500.0 ) 4960 return YES; 4961 } 4962 return NO; 4963} 4964 4965/*- (int)getLastObjectOfSubPath:(int)startIx tolerance:(float)tolerance 4966{ int i, cnt, endIx; 4967 NSPoint beg, end; 4968 4969 cnt = [list count]; 4970 beg = [[list objectAtIndex:startIx] pointWithNum:0]; 4971 for ( i=startIx; i<cnt; i++ ) 4972 { 4973 end = [[list objectAtIndex:i] pointWithNum:MAXINT]; 4974 if ( SqrDistPoints(beg, end) < tolerance ) 4975 { endIx = i; 4976 return endIx; 4977 } 4978 } 4979 return startIx; 4980}*/ 4981 4982#define CLOSETOLERANCE 0.03 4983- (void)removeSingleGraphicsAtEnd:(VPath*)aPath 4984{ long i, endIx; 4985 4986 // for (i=[alist count]-1 ;i >= 0 ; i--) 4987 for (i=0; i < (int)[[aPath list] count]; i++) 4988 { VGraphic *gs = [[aPath list] objectAtIndex:i]; 4989 4990 endIx = [aPath getLastObjectOfSubPath:i]; // tolerance:TOLERANCE*3.0 4991 4992 if ( i == endIx && [self isShortGraphic:gs] ) 4993 { [[aPath list] removeObjectAtIndex:i]; 4994 i--; 4995 } 4996 else if ( i != endIx && Diff(i, endIx) < 3 ) // max 3 gr 4997 { int j, remove = 1; 4998 4999 for (j=i ;j < endIx ; j++) 5000 { if ( ![self isShortGraphic:[[aPath list] objectAtIndex:j]] ) 5001 { remove = 0; 5002 break; 5003 } 5004 } 5005 if ( remove ) 5006 { for (j=i ;j < endIx ; j++) 5007 { [[aPath list] removeObjectAtIndex:j]; 5008 j--; 5009 endIx--; 5010 } 5011 i--; 5012 } 5013 else 5014 i = endIx; 5015 } 5016 else 5017 i = endIx; 5018 } 5019} 5020 5021/* always two points refer to each other in array 5022 * j is index of first point-pair we check 5023 * i pair we must change - cant't explain 5024 */ 5025BOOL begEndPairTwiceInArray(int ix, NSPoint *array, int cnt) 5026{ int i; 5027 5028 for (i=0; i<cnt-1; i+=2) 5029 if ( i != ix 5030 && Diff(array[ix].x, array[i+1].x) + Diff(array[ix].y, array[i+1].y) <= 30.0*TOLERANCE 5031 && Diff(array[ix+1].x, array[i].x) + Diff(array[ix+1].y, array[i].y) <= 30.0*TOLERANCE) 5032 return YES; 5033 return NO; 5034} 5035 5036/* here we remove the loops 5037 * first we remove hole selected subPaths 5038 * than we join subPaths wich overlap each other 5039 * and now we look for overlaps inside subPath itself 5040 * and for loops in subPath 5041 * this three things we do only if we find conforming points ! 5042 * and check if path is realy closed (closing little gaps) 5043 * modified: 2005-07-20 5044 */ 5045- (void)optimizeSubPathsToClosedPath:(VPath*)path :(float)w :(int*)subPathSplitted 5046{ int i, listCnt = [[path list] count], addedAtEnd[listCnt], addCnt = 0, noticeJ = -1, noticeK = -1; 5047 float tol = 10.0*TOLERANCE; // 5.0 5048 BOOL openPath = NO; 5049 5050 /* remove subPaths were all graphics are selected */ 5051 for (i=0; i<listCnt; i++) 5052 { VPath *sp = [[path list] objectAtIndex:i]; 5053 int j, spCnt = [[sp list] count], startJ = spCnt; 5054 5055 /* search for a not selected graphic */ 5056 for (j=0; j<spCnt; j++) 5057 { VGraphic *curG = [[sp list] objectAtIndex:j]; 5058 NSPoint s, e; 5059 5060 s = [curG pointWithNum:0]; 5061 e = [curG pointWithNum:MAXINT]; 5062 5063 if (![curG isSelected] /*&& SqrDistPoints(s, e) > minL*/ ) 5064 { startJ = j; 5065 break; 5066 } 5067 } 5068 /* no one found -> remove subPath */ 5069 if (startJ >= spCnt) 5070 { 5071 [[sp list] removeAllObjects]; 5072 subPathSplitted[i] = NO; // time 5073 continue; 5074 } 5075 } 5076 5077 /* joine subPaths with each other */ 5078 for (i=0, listCnt = [[path list] count]; i<listCnt; i++) 5079 { VPath *sp = [[path list] objectAtIndex:i]; 5080 int j, k, spCnt = [[sp list] count], startJ = spCnt, selected = 0; 5081 VGraphic *startG=nil; 5082 5083 if (!spCnt || subPathSplitted[i] == NO) 5084 continue; 5085 /* search for a not selected graphic */ 5086 for (j=0; j<spCnt; j++) 5087 { VGraphic *curG = [[sp list] objectAtIndex:j]; 5088 NSPoint s, e; 5089 5090 s = [curG pointWithNum:0]; 5091 e = [curG pointWithNum:MAXINT]; 5092 5093 if (![curG isSelected] /*&& SqrDistPoints(s, e) > minL*/ ) 5094 { startJ = j; 5095 break; 5096 } 5097 } 5098 /* no one found -> remove subPath - also possible after we remove something */ 5099 if (startJ >= spCnt) 5100 { 5101 [[sp list] removeAllObjects]; 5102 subPathSplitted[i] = NO; // time 5103 continue; 5104 } 5105 /* subPathSplitted -> look for a not selected graphic - and next for a selected graphic */ 5106 startG = [[sp list] objectAtIndex:startJ]; 5107 for (j=startJ; j<spCnt; j++) 5108 { VGraphic *curG = [[sp list] objectAtIndex:j]; 5109 NSPoint ce; 5110 5111 if ([curG isSelected]) 5112 continue; 5113 5114 ce = [curG pointWithNum:MAXINT]; 5115 5116 for (k=((j+1 < spCnt)?(j+1):0); k<((j+1 < spCnt)?spCnt:startJ); k++) 5117 { VGraphic *nextG = [[sp list] objectAtIndex:k]; 5118 NSPoint ns, ne; 5119 float distance = MAXCOORD; 5120 int l, m, n; 5121 5122 ns = [nextG pointWithNum:0]; 5123 ne = [nextG pointWithNum:MAXINT]; 5124 distance = SqrDistPoints(ce, ns); 5125 if (Diff(distance, 0.0) <= TOLERANCE && ![nextG isSelected]) 5126 { 5127 break; // nothing to do graphics laying correct 5128 } 5129 else if ([nextG isSelected]) 5130 { int selectedGs = 0; 5131 BOOL stop = NO, added = NO; 5132 VGraphic *cgp2 = [[sp list] objectAtIndex:(((k+1) < spCnt) ? (k+1):(0))]; 5133 5134 /* search the end of the selected graphics */ 5135 for (l=((k+1 < spCnt) ? (k+1):0); l < ((k+1 < spCnt) ? spCnt:k); l++) 5136 { VGraphic *lg = [[sp list] objectAtIndex:l]; 5137 NSPoint s, e; 5138 5139 s = [lg pointWithNum:0]; 5140 e = [lg pointWithNum:MAXINT]; 5141 /* lg is selected */ 5142 if ([lg isSelected]) 5143 { 5144 selectedGs++; 5145 if (l+1 >= spCnt) 5146 l = -1; // so we go on at 0 5147 if (l == k) 5148 break; // one time around !!! 5149 continue; 5150 } 5151 /* lg close to curG end !! - this is a loop inside subPath */ 5152 else if (Diff(SqrDistPoints(ce, s), 0.0) <= TOLERANCE) 5153 { stop = YES; 5154 break; 5155 } 5156 /* end of selected graphics in sp */ 5157 else 5158 { int o, backSelected = 0; 5159 VGraphic *lgm1, *lgm2; 5160 int from = -1, to = -1; 5161 int possCnt = 2, possibleSi[3], possibleEi[3]; 5162 NSPoint possibleS[3], possibleE[3], em1, em2; 5163 NSPoint nsp1 = [cgp2 pointWithNum:0]/*, nsm1 = [curG pointWithNum:0]*/; 5164 float sqrTolTen = (10.0*TOLERANCE)*(10.0*TOLERANCE); 5165 5166 /* check distance around - one/two graphic above end of selected graphic(s) */ 5167 lgm1 = [[sp list] objectAtIndex:(((l-1) < 0) ? (spCnt-1):(l-1))]; 5168 em1 = [lgm1 pointWithNum:MAXINT]; 5169 lgm2 = [[sp list] objectAtIndex:(((l-2) < 0) ? (spCnt+(l-2)):(l-2))]; 5170 em2 = [lgm2 pointWithNum:MAXINT]; 5171 5172 possibleS[0] = ns; possibleSi[0] = k; // nextG start 5173 possibleS[1] = nsp1; possibleSi[1] = ((k+1) < spCnt) ? (k+1) : (0); // nextG+1 start 5174 5175 possibleE[0] = em1; possibleEi[0] = ((l-1) < 0)?(spCnt-1):(l-1); // lgm1 end 5176 possibleE[1] = em2; // lgm2 end 5177 possibleEi[1] = ((l-2) < 0) ? (((l-2) == -1) ? (spCnt-1):(spCnt-2)) : (l-2); 5178 5179 if (!selectedGs) 5180 possCnt = 1; // only one point pair possible - only one graphic is selected 5181 else // if (selectedGs == 1) 5182 possCnt = 2; 5183 5184 /* check if backward are selected elements in sp path !!!!! */ 5185 from = ((l-1) < 0) ? (spCnt-1) : (l-1); 5186 to = k; 5187 for (m=to; m >= ((!to || to < from) ? 0:from); m--) 5188 { 5189 if ([[[sp list] objectAtIndex:m] isSelected]) 5190 backSelected++; 5191 if (!m && to < from) // we step over 0 - second part until from ! 5192 { m = spCnt; 5193 to = from+1; // little hack mh - second part until from ! 5194 } 5195 } 5196 5197 /* search through other subPaths which are splitted */ 5198 for (o=0; o < listCnt; o++) 5199 { VPath *oSp=nil; 5200 BOOL secondTime = NO; 5201 BOOL oStartIsSelected = YES; 5202 int notSelected = 0, selected = 0, oSpCnt, oStart = -1; 5203 NSPoint oStartPt = NSZeroPoint; 5204 5205 if (o == i || !subPathSplitted[o]) 5206 continue; 5207 5208 oSp = [[path list] objectAtIndex:o]; 5209 oSpCnt = [[oSp list] count]; 5210 5211 /* search in other subPath for graphic which hit one of possibleE/S */ 5212 /* with enoth space between ! */ 5213 /* selected or not selected is not interesting */ 5214 for (n=0; n < oSpCnt; n++) 5215 { VGraphic *nlG = [[oSp list] objectAtIndex:n]; 5216 VGraphic *nlGm1 = nil, *nlGm2 = nil; 5217 NSPoint nls, nle; 5218 5219 nlGm1 = [[oSp list] objectAtIndex:(((n-1) < 0) ? (oSpCnt-1) : (n-1))]; 5220 if (oSpCnt > 1) 5221 nlGm2 = [[oSp list] objectAtIndex:(((n-2) < 0)?(oSpCnt+(n-2)):(n-2))]; 5222 5223 if (n == oStart) 5224 break; 5225 nls = [nlG pointWithNum:0]; 5226 nle = [nlG pointWithNum:MAXINT]; 5227 /* nlG hit one of possibleE/possibleS */ 5228 if ((oStart == -1 || secondTime == NO) && 5229 (([nlG isSelected] && (![nlGm1 isSelected] || ![nlGm2 isSelected])) || 5230 (![nlG isSelected] && ([nlGm1 isSelected] || [nlGm2 isSelected]))) && 5231 (pointWithToleranceInArray(nls, tol, possibleE, possCnt) || 5232 pointWithToleranceInArray(nls, tol, possibleS, possCnt))) 5233 { 5234 if (oStart != -1) 5235 secondTime = YES; // else we got a loop 5236 oStart = n; oStartPt = nls; 5237 if (![nlG isSelected]) // nlG is NOT selected - we search not sels 5238 { oStartIsSelected = NO; 5239 selected = 0; 5240 } 5241 else 5242 { oStartIsSelected = YES; 5243 notSelected = 0; 5244 } 5245 } 5246 if (oStart != -1 && oStartIsSelected == YES && ![nlG isSelected]) 5247 notSelected++; // too much not selected elements to remove ! no join 5248 else if (oStart != -1 && oStartIsSelected == NO && [nlG isSelected]) 5249 selected++; // too much selected elements ! no join 5250 /* lg is selected and close to nextG(k) start OR to lgm1 end */ 5251 if (oStart != -1 && 5252 ((oStartIsSelected == YES && notSelected > 2 && backSelected < 3) || 5253 (oStartIsSelected == YES && notSelected < 3) || 5254 (oStartIsSelected == NO && selected < 3)) && 5255 SqrDistPoints(oStartPt, nle) > TOLERANCE && 5256 ((pointWithToleranceInArray(oStartPt, tol, possibleE, possCnt) && 5257 pointWithToleranceInArray(nle, tol, possibleS, possCnt)) || 5258 (pointWithToleranceInArray(oStartPt, tol, possibleS, possCnt) && 5259 pointWithToleranceInArray(nle, tol, possibleE, possCnt)))) 5260 { BOOL changeDirection = NO; 5261 int at = j+1, q; 5262 5263 from = to = -1; 5264 /* remove graphics - from current subPath - sp */ 5265 if (pointWithToleranceInArray(oStartPt, tol, possibleE, possCnt)) 5266 { 5267 for (q=0; q<possCnt; q++) 5268 if (SqrDistPoints(oStartPt, possibleE[q]) < sqrTolTen) 5269 { 5270 to = possibleEi[q]; 5271 break; 5272 } 5273 for (q=0; q<possCnt; q++) 5274 if (SqrDistPoints(nle, possibleS[q]) < sqrTolTen) 5275 { 5276 from = possibleSi[q]; 5277 at = from; 5278 break; 5279 } 5280 } 5281 else 5282 { for (q=0; q<possCnt; q++) 5283 if (SqrDistPoints(oStartPt, possibleS[q]) < sqrTolTen) 5284 { 5285 from = possibleSi[q]; 5286 at = from; 5287 break; 5288 } 5289 for (q=0; q<possCnt; q++) 5290 if (SqrDistPoints(nle, possibleE[q]) < sqrTolTen) 5291 { 5292 to = possibleEi[q]; 5293 break; 5294 } 5295 } 5296 if (from == -1 || to == -1) 5297 break; 5298 for (m=to; m >= ((!to || to < from) ? 0:from); m--) 5299 { 5300 [[sp list] removeObjectAtIndex:m]; 5301 spCnt--; 5302 /* we removed befor j -> j move one down in list !!! */ 5303 if (m < from) { from--; to--; } 5304 if (m < j) j--; 5305 if (m < startJ) startJ--; 5306 if (m < at) at--; 5307 5308 if (!m && to < from) // we step over 0 - second part until from ! 5309 { m = spCnt; 5310 to = from+1; // little hack mh - second part until from ! 5311 } 5312 } 5313 /* sort graphics from oSp into sp */ 5314 changeDirection = NO; 5315 if (pointWithToleranceInArray(nle, tol, possibleS, possCnt)) 5316 { 5317 if (oStartIsSelected == YES) 5318 { 5319 from = (n+1 < oSpCnt) ? (n+1) : 0; // n if remove 5320 to = ((oStart-1) < 0) ? (oSpCnt-1):(oStart-1); 5321 } 5322 else 5323 { from = oStart; 5324 to = n; 5325 changeDirection = YES; 5326 } 5327 } 5328 else 5329 { if (oStartIsSelected == YES) 5330 { from = (n+1 < oSpCnt) ? (n+1) : 0; 5331 to = ((oStart-1) < 0) ? (oSpCnt-1):(oStart-1); 5332 changeDirection = YES; 5333 } 5334 else 5335 { to = n; 5336 from = oStart; 5337 } 5338 } 5339 /* insert other subPath (from oStart to -end- (n) || to - from) */ 5340 /* at cur subPath j+1 */ 5341 /* remove graphics in other subPath */ 5342 if (!spCnt) 5343 at = 0; 5344 for (m=to; m >= ((!to || to < from) ? 0:from); m--) 5345 { VGraphic *g = [[oSp list] objectAtIndex:m]; 5346 5347 if (changeDirection) 5348 [g changeDirection]; 5349 [[sp list] insertObject:g atIndex:at]; 5350 spCnt++; 5351 //if (notSelected > 2) 5352 { [[oSp list] removeObjectAtIndex:m]; 5353 oSpCnt--; 5354 if (m < from) { from--; to--; } 5355 } 5356 if (!m && to < from) // we step over 0 - second part until from ! 5357 { m = oSpCnt; 5358 to = from+1; // little hack mh - second part until from ! 5359 } 5360 } 5361 /* check oSp if only selected inside !!! */ 5362 //if (notSelected > 2) 5363 { notSelected = 0; 5364 for (m=0; m<oSpCnt; m++) 5365 { 5366 if (![[[oSp list] objectAtIndex:m] isSelected]) 5367 { notSelected++; 5368 if (notSelected > 2) 5369 break; // we do not remove this subpath 5370 } 5371 } 5372 } 5373 /* remove other subPath graphics */ 5374 /* and subPathSplitted[o] = NO */ 5375 if (notSelected < 3) 5376 { [[oSp list] removeAllObjects]; 5377 subPathSplitted[o] = NO; // time 5378 } 5379 added = YES; 5380 // j = -1; // start new for removes at begin of sp with this oSp 5381 break; 5382 } 5383 if (n+1 >= oSpCnt && oStart != -1) 5384 n = -1; 5385 } 5386 if (added == YES) 5387 break; // o loop 5388 } 5389 } 5390 break; // l loop 5391 } 5392 if (stop == YES || added == YES) 5393 break; // k loop - look for next selectedG 5394 } 5395 } 5396 } 5397 /*move subPath at end of list perhaps two other paths must first join with each other 5398 * to get the right points 5399 */ 5400 if (subPathSplitted[i] == YES) 5401 { 5402 for (j=0; j<spCnt; j++) 5403 { 5404 if ([[[sp list] objectAtIndex:j] isSelected]) 5405 { selected++; 5406 if (selected > 1 && !valueInArray(i, addedAtEnd, addCnt)) 5407 { 5408 [[path list] insertObject:sp atIndex:listCnt]; 5409 [[path list] removeObjectAtIndex:i]; 5410 subPathSplitted[listCnt] = subPathSplitted[i]; 5411 for (k=i; k<listCnt; k++) 5412 subPathSplitted[k] = subPathSplitted[k+1]; 5413 5414 for (k=0; k<addCnt; k++) 5415 addedAtEnd[k]--; // index step one back ! 5416 addedAtEnd[addCnt++] = listCnt-1; 5417 i--; 5418 break; 5419 } 5420 else if (selected > 1) 5421 break; 5422 } 5423 } 5424 } 5425 } 5426 5427 /* search for overlaps inside subPath 5428 * and for loops 5429 */ 5430 for (i=0, listCnt = [[path list] count]; i<listCnt; i++) 5431 { VPath *sp = [[path list] objectAtIndex:i]; 5432 int j, k, spCnt = [[sp list] count], startJ = spCnt; 5433 VGraphic *startG=nil; 5434 5435 if (!spCnt) 5436 continue; 5437 /* search for a not selected graphic */ 5438 for (j=0; j<spCnt; j++) 5439 { VGraphic *curG = [[sp list] objectAtIndex:j]; 5440 NSPoint s, e; 5441 5442 s = [curG pointWithNum:0]; 5443 e = [curG pointWithNum:MAXINT]; 5444 5445 if (![curG isSelected] /*&& SqrDistPoints(s, e) > minL*/ ) 5446 { startJ = j; 5447 break; 5448 } 5449 } 5450 /* no one found -> remove subPath */ 5451 if (startJ >= spCnt) 5452 { 5453 [[sp list] removeAllObjects]; 5454 subPathSplitted[i] = NO; // time 5455 continue; 5456 } 5457 /* search for a selected graphic after a not selected graphic */ 5458 startG = [[sp list] objectAtIndex:startJ]; 5459 for (j=startJ; j<spCnt; j++) 5460 { VGraphic *curG = [[sp list] objectAtIndex:j]; 5461 NSPoint ce; 5462 5463 if ([curG isSelected]) 5464 continue; 5465 5466 ce = [curG pointWithNum:MAXINT]; 5467 5468 for (k=((j+1 < spCnt)?(j+1):0); k<((j+1 < spCnt)?spCnt:startJ); k++) 5469 { VGraphic *nextG = [[sp list] objectAtIndex:k]; 5470 NSPoint ns, ne; 5471 float distance = MAXCOORD; 5472 int l, m, n; 5473 5474 ns = [nextG pointWithNum:0]; 5475 ne = [nextG pointWithNum:MAXINT]; 5476 distance = SqrDistPoints(ce, ns); 5477 if (Diff(distance, 0.0) <= TOLERANCE && ![nextG isSelected]) 5478 { 5479 break; // nothing to do graphics laying correct 5480 } 5481 else if ([nextG isSelected]) 5482 { int selectedGs = 0; 5483 VGraphic *cgp2, *cgp3; 5484 NSPoint cep3; 5485 5486 /* check distance around - one/two graphics behind selected graphic(s) */ 5487 cgp2 = [[sp list] objectAtIndex:(((k+1) < spCnt) ? (k+1):(0))]; 5488 cgp3 = [[sp list] objectAtIndex:(((k+2) < spCnt) ? (k+2) : ((k+2) - spCnt))]; 5489 cep3 = [cgp3 pointWithNum:MAXINT]; 5490 5491 for (l=((k+1 < spCnt) ? (k+1):0); l < ((k+1 < spCnt) ? spCnt:k); l++) 5492 { VGraphic *lg = [[sp list] objectAtIndex:l]; 5493 NSPoint s, e; 5494 5495 s = [lg pointWithNum:0]; 5496 e = [lg pointWithNum:MAXINT]; 5497 /* lg is selected */ 5498 if ([lg isSelected]) 5499 { 5500 selectedGs++; 5501 if (l+1 >= spCnt) 5502 l = -1; // so we go on at 0 5503 if (l == k) 5504 break; // one time around !!! 5505 continue; 5506 } 5507 /* lg close to curG end !! - loop */ 5508 else if (Diff(SqrDistPoints(ce, s), 0.0) <= TOLERANCE) 5509 { int from = k; // nextG 5510 int to = ((l-1) < 0)?(spCnt-1):(l-1); // l-1 5511 5512 /* remove objects from k-(l-1) */ 5513 for (m=to; m >= ((!to || to < from) ? 0:from); m--) 5514 { 5515 [[sp list] removeObjectAtIndex:m]; 5516 spCnt--; 5517 if (m < from) { from--; to--; } 5518 if (m < j) j--; // we removed befor j -> j and k and l move one down in list !!! 5519 if (m < startJ) startJ--; 5520 5521 if (!m && to < from) // we step over 0 - second part until from ! 5522 { m = spCnt; 5523 to = from+1; // little hack mh - second part until from ! 5524 } 5525 } 5526 break; 5527 } 5528 /* search for overlaps 5529 * and loops which are need more tolerance to find 5530 */ 5531 else 5532 { VGraphic *lgm1, *lgm2; 5533 NSPoint em1; 5534 int from = -1, to = -1; 5535 int km2 = ((k-2) < 0) ? (spCnt+(k-2)) : (k-2); 5536 int kp3 = ((k+3) < spCnt) ? (k+3) : ((k+3) - spCnt); 5537 BOOL removeLoop = NO; 5538 5539 /* check distance around - one/two graphic above end of selected graphic(s) */ 5540 lgm1 = [[sp list] objectAtIndex:(((l-1) < 0) ? (spCnt-1):(l-1))]; 5541 em1 = [lgm1 pointWithNum:MAXINT]; 5542 lgm2 = [[sp list] objectAtIndex:(((l-2) < 0) ? (spCnt+(l-2)):(l-2))]; 5543 5544 { int sStart = -1, notSelected=0, from = -1, to = -1; 5545 int possCnt = 3, possibleSi[3], possibleEi[3]; 5546 BOOL removed = NO; 5547 NSPoint possibleS[3], possibleE[3], sStartPt = {0.0, 0.0}; 5548 NSPoint nsp1 = [cgp2 pointWithNum:0], em2 = [lgm2 pointWithNum:MAXINT]; 5549 NSPoint nsm1 = [curG pointWithNum:0]; 5550 float sqrTolTen = (10.0*TOLERANCE)*(10.0*TOLERANCE); 5551 5552 possibleS[0] = ns; possibleSi[0] = k; // nextG start 5553 possibleS[1] = nsp1; possibleSi[1] = ((k+1) < spCnt) ? (k+1) : (0); // nextG+1 start 5554 possibleS[2] = nsm1; possibleSi[2] = ((k-1) < 0)?(spCnt-1):(k-1); // nextG-1 start 5555 5556 possibleE[0] = em1; possibleEi[0] = ((l-1) < 0)?(spCnt-1):(l-1); // lgm1 end 5557 possibleE[1] = em2; // lgm2 end 5558 possibleEi[1] = ((l-2) < 0) ? (((l-2) == -1) ? (spCnt-1):(spCnt-2)) : (l-2); 5559 possibleE[2] = e; possibleEi[2] = l; // lg end 5560 5561 /* search for other graphics which hit possibleE/S */ 5562 for (n=((l+1 < spCnt) ? (l+1):0); n < ((l+1 < spCnt) ? spCnt:k); n++) 5563 { VGraphic *nlG = [[sp list] objectAtIndex:n]; 5564 NSPoint nls, nle; 5565 5566 if (n == k) 5567 break; // one round finished 5568 nls = [nlG pointWithNum:0]; 5569 nle = [nlG pointWithNum:MAXINT]; 5570 /* nlg is selected and hit a point from possibleE array */ 5571 if ((sStart == -1 || notSelected > 2) && [nlG isSelected] && 5572 (Diff(j, n) > 2 && Diff(Diff(j, n), spCnt) > 2) && 5573 (Diff(l, n) > 2 && Diff(Diff(l, n), spCnt) > 2) && 5574 pointWithToleranceInArray(nls, tol, possibleE, possCnt) ) 5575 { 5576 sStart = n; sStartPt = nls; 5577 notSelected = 0; // second try perhaps 5578 } 5579 if (sStart != -1 && ![nlG isSelected]) 5580 notSelected++; // too much not selected elements to remove ! 5581 /* nlg is hit a point from possibleS array */ 5582 if (sStart != -1 /*&& notSelected < 3*/ && 5583 (Diff(j, n) > 2 && Diff(Diff(j, n), spCnt) > 2) && 5584 (Diff(l, n) > 2 && Diff(Diff(l, n), spCnt) > 2) && 5585 Diff(SqrDistPoints(sStartPt, nle), 0.0) > TOLERANCE && 5586 pointWithToleranceInArray(nle, tol, possibleS, possCnt) ) 5587 { VPath *subP = nil; 5588 int q, sEnd1 = -1; 5589 5590 removed = YES; 5591 for (q=0; q<possCnt; q++) 5592 if (SqrDistPoints(sStartPt, possibleE[q]) < sqrTolTen) 5593 { to = possibleEi[q]; 5594 sEnd1 = ((possibleEi[q]+1) < spCnt) ? (possibleEi[q]+1) : (0); 5595 break; 5596 } 5597 for (q=0; q<possCnt; q++) 5598 if (SqrDistPoints(nle, possibleS[q]) < sqrTolTen) 5599 { from = possibleSi[q]; 5600 break; 5601 } 5602 if (from == -1 || to == -1) 5603 break; 5604 /* remove first part of selected graphics */ 5605 for (m=to; m >= ((!to || to < from) ? 0:from); m--) 5606 { 5607 [[sp list] removeObjectAtIndex:m]; 5608 spCnt--; 5609 /* we removed befor j -> j move one down in list !!! */ 5610 if (m < from) { from--; to--; } 5611 if (m < j) j--; 5612 if (m < startJ) startJ--; 5613 if (m < n) n--; 5614 if (m < sStart) sStart--; 5615 if (m < sEnd1) sEnd1--; 5616 5617 if (!m && to < from) // we step over 0 - second part until from ! 5618 { m = spCnt; 5619 to = from+1; // little hack mh - second part until from ! 5620 } 5621 } 5622 /* than we build a new subPath and add other graphics to this 5623 * so we can remove the rest of selected graphics later in i loop 5624 */ 5625 if (notSelected >= 3) 5626 { subP = [VPath path]; 5627 [subP setColor:[sp color]]; 5628 [subP setWidth:[sp width]]; 5629 [subP setFilled:[sp filled]]; 5630 } 5631 from = sStart; // first second selected Graphic 5632 to = n; 5633 for (m=to; m >= ((!to || to < from) ? 0:from) && spCnt > m; m--) 5634 { VGraphic *g = [[sp list] objectAtIndex:m]; 5635 5636 if (notSelected >= 3) 5637 [[subP list] insertObject:g atIndex:0]; // backwart ! 5638 [[sp list] removeObjectAtIndex:m]; 5639 spCnt--; 5640 if (m < from) { from--; to--; } 5641 if (m < j) j--; 5642 if (m < startJ) startJ--; 5643 if (m < sStart) sStart--; 5644 if (m < sEnd1) sEnd1--; 5645 5646 if (!m && to < from) // we step over 0 - second part until from ! 5647 { m = spCnt; 5648 to = from+1; // little hack mh - second part until from ! 5649 } 5650 } 5651 if (notSelected >= 3) 5652 { [[path list] addObject:subP]; 5653 subPathSplitted[listCnt] = YES; 5654 listCnt++; 5655 } 5656 /* sort graphics behind/after overlap (overlap allways create two supPaths) 5657 * in a new subPath */ 5658 subP = [VPath path]; 5659 [subP setColor:[sp color]]; 5660 [subP setWidth:[sp width]]; 5661 [subP setFilled:[sp filled]]; 5662 from = sEnd1; 5663 to = ((sStart-1) < 0) ? (spCnt-1):(sStart-1); // bevor second selection 5664 for (m=to; m >= ((!to || to < from) ? 0:from) && spCnt > m; m--) 5665 { VGraphic *g = [[sp list] objectAtIndex:m]; 5666 5667 [[subP list] insertObject:g atIndex:0]; // backwart ! 5668 [[sp list] removeObjectAtIndex:m]; // remove from cur sp 5669 spCnt--; 5670 if (m < from) { from--; to--; } 5671 if (m < j) j--; 5672 if (m < startJ) startJ--; 5673 if (!m && to < from) // we step over 0 - second part until from ! 5674 { m = spCnt; 5675 to = from+1; // little hack mh - second part until from ! 5676 } 5677 } 5678 [[path list] addObject:subP]; 5679 subPathSplitted[listCnt] = YES; 5680 listCnt++; 5681 j--; // check from curG new !! 5682 break; 5683 } 5684 if (n+1 >= spCnt) 5685 n = -1; // so we go on at 0 5686 } 5687 if (removed) 5688 break; 5689 } 5690 5691 /* now (no overlap) we search for a loop 5692 * which start/end points not exactly at notSelected/selected frontiers 5693 */ 5694 /*if (spCnt < selectedGs+8) // correct km2 5695 { 5696 if (spCnt < selectedGs+3) 5697 km2 = k; // none back 5698 else if (spCnt < selectedGs+5) 5699 km2 = j; // one back 5700 else // < ..+7 5701 km2 = ((k-2) < 0) ? (spCnt+(k-2)) : (k-2); // two back 5702 }*/ 5703 5704 if (selectedGs < 4) // correct kp3 5705 { 5706 if (selectedGs < 1) 5707 kp3 = k; // none forward 5708 else if (selectedGs < 2) 5709 kp3 = ((k+1) < spCnt) ? (k+1) : ((k+1) - spCnt); // one forward 5710 else // if (selectedGs <= 4) 5711 kp3 = ((k+2) < spCnt) ? (k+2) : ((k+2) - spCnt); // two forward 5712 } 5713 5714 for (n=kp3; n >= ((kp3 < km2) ? 0 : km2); n--) 5715 { VGraphic *gn = [[sp list] objectAtIndex:n]; // -- 5716 NSPoint gns = [gn pointWithNum:0]; 5717 int o, lp1 = ((l+1) < spCnt) ? (l+1) : ((l+1) - spCnt); 5718// int lm3 = (((l-3) < 0) ? (spCnt+(l-3)) : (l-3)); 5719 int lm3 = ((n+1) < spCnt) ? (n+1) : ((n+1) - spCnt); 5720 5721 if (selectedGs < 4) // correct lm3 5722 { 5723 if (selectedGs < 1) 5724 lm3 = l; // none backward 5725 else if (selectedGs < 2) 5726 lm3 = (((l-1) < 0) ? (spCnt+(l-1)) : (l-1)); // one backward 5727 else // if (selectedGs <= 4) 5728 lm3 = (((l-2) < 0) ? (spCnt+(l-2)) : (l-2)); // two backward 5729 } 5730 5731 if (n == (((j-1) < 0)?(spCnt-1):(j-1)) || 5732 n == (((j-2) < 0) ? (spCnt+(j-2)) : (j-2))) 5733 lm3 = k; // else perhaps we remove only bevor j !! 5734 5735 /*if (spCnt < selectedGs+8) // correct km2 / lp1 5736 { 5737 if (spCnt < selectedGs+3) 5738 lp1 = ((l-1) < 0) ? (spCnt+(l-1)) : (l-1); // none forward 5739 else if (spCnt < selectedGs+5) 5740 lp1 = l; // one forward 5741 else // < ..+7 5742 lp1 = ((l+1) < spCnt) ? (l+1) : ((l+1) - spCnt); // two forward 5743 }*/ 5744 5745 for (o=lm3; o <= ((lm3 <= lp1) ? lp1 : spCnt-1); o++) 5746 { VGraphic *go = [[sp list] objectAtIndex:(o = (o < spCnt) ? o : 0)]; // ++ 5747 NSPoint goe = [go pointWithNum:MAXINT]; 5748 5749 /* mal braucht man >= 1 und mal > 1 ? o > n sonst wird alles removed ! */ 5750 if (((Diff(n, o) > 1 || (Diff(n, o) == 1 && o > n)) && 5751 Diff(Diff(n, o), spCnt) > 1) 5752 && SqrDistPoints(gns, goe) < (15.0*TOLERANCE)*(15.0*TOLERANCE)) 5753 { 5754 if ( Diff(n, o) == 1 ) 5755 NSLog(@"VPath.m: Diff(n, o) == 1, look here if calculation fault\n"); 5756 from = n; 5757 to = o; 5758 /* remove objects from k-(l-1) */ 5759 for (m=to; m >= ((!to || to < from) ? 0:from); m--) 5760 { 5761 [[sp list] removeObjectAtIndex:m]; 5762 spCnt--; 5763 if (m < from) { from--; to--; } 5764 if (m < j) j--; 5765 if (m < startJ) startJ--; 5766 5767 if (!m && to < from) // we step over 0 - second part until from ! 5768 { m = spCnt; 5769 to = from+1; // little hack mh - second part until from ! 5770 } 5771 } 5772 removeLoop = YES; 5773 break; 5774 } 5775 if (o == spCnt-1 && lm3 > lp1) // ++ 5776 { o = -1; 5777 lm3 = lp1-1; // hack to stop at lp1 - after spCnt 5778 } 5779 } 5780 if (removeLoop) 5781 break; 5782 if (!n && kp3 < km2) // -- 5783 { n = spCnt; 5784 kp3 = km2+1; // hack to go on after 0 at kp3 5785 } 5786 } 5787 break; 5788 } 5789 } 5790 break; 5791 } 5792 else if (Diff(distance, 0.0) > TOLERANCE) 5793 { 5794 if ( !openPath ) 5795 { noticeK = k; 5796 noticeJ = j; 5797 openPath = YES; 5798 } 5799 continue; 5800 } 5801 } 5802 } 5803 } 5804 if (openPath) 5805 NSLog(@"VPath.m - optimizeSubPathsToClosedPath j:%d k:%d", noticeJ, noticeK); 5806 openPath = NO; 5807 5808 /* check if closed and close little gaps */ 5809 for (i=0; i<listCnt; i++) 5810 { VPath *sp = [[path list] objectAtIndex:i]; 5811 int j, spCnt = [[sp list] count]; 5812 5813 /* check end to next start */ 5814 for (j=0; j<spCnt; j++) 5815 { VGraphic *curG = nil, *nextG = nil; 5816 NSPoint e, ns; 5817 float dist; 5818 5819 if (j == spCnt-1) // last object !!!!!!! 5820 { curG = [[sp list] objectAtIndex:j]; 5821 nextG = [[sp list] objectAtIndex:0]; // startG 5822 } 5823 else 5824 { curG = [[sp list] objectAtIndex:j]; 5825 nextG = [[sp list] objectAtIndex:j+1]; 5826 } 5827 //s = [curG pointWithNum:0]; 5828 e = [curG pointWithNum:MAXINT]; 5829 ns = [nextG pointWithNum:0]; 5830 dist = SqrDistPoints(e, ns); 5831 5832 if (dist > TOLERANCE*TOLERANCE && dist < 5.0*TOLERANCE) 5833 { 5834 if (![nextG isKindOfClass:[VArc class]]) 5835 { 5836 [nextG movePoint:0 to:e]; 5837 } 5838 else if (![curG isKindOfClass:[VArc class]]) 5839 { 5840 [curG movePoint:MAXINT to:ns]; 5841 } 5842 else 5843 { id g = [VLine lineWithPoints:e :ns]; 5844 5845 [[sp list] insertObject:g atIndex:j+1]; 5846 spCnt++; 5847 } 5848 } 5849 else if (dist > TOLERANCE*TOLERANCE && dist < 50*TOLERANCE) 5850 { id g = [VLine lineWithPoints:e :ns]; 5851 5852 [[sp list] insertObject:g atIndex:j+1]; 5853 spCnt++; 5854 } 5855 /* 1 -> dublicate to close path ! */ 5856 else if (dist > TOLERANCE*TOLERANCE && j == spCnt-1 && spCnt == 1) // only one Object -> no 360 arc 5857 { VGraphic *gr = [curG copy]; 5858 5859 [gr changeDirection]; 5860 [[sp list] addObject:gr]; 5861 spCnt++; 5862 } 5863 else if (dist > TOLERANCE*TOLERANCE && !openPath) 5864 { 5865 openPath = YES; 5866 noticeJ = j; 5867 noticeK = i; 5868 } 5869 } 5870 } 5871 if ( openPath ) 5872 NSLog(@"VPath - optimizeSubPathsToClosedPath -- i: %d j: %d", noticeK, noticeJ); 5873} 5874 5875/* here we split graphics which intersect each other 5876 * inside subPaths itself 5877 * and subPath to subPaths 5878 * AND we set graphics which start/middle/end point is laying too near to hold the distance of tool width 5879 */ 5880- (void)removeFaultGraphicsInSubpaths:(VPath*)path :(float)w 5881{ int i, listCnt, subPathSplitted[[[path list] count]+Min(10, [[path list] count])]; 5882 float r; 5883 5884 /* we need more subPathSplitted[] for possible additions in - optimizeSubPathsToClosedPath */ 5885 5886 r = Abs((width + w) / 2.0); // the amount of growth 5887 // r -= Min(0.05, r/50.0); // 0.05 60.0 - 0.1 35.0 - 0.1 25.0 5888 r -= 0.0094; // 0.0075 0.0055 // Min(0.0055, r/100.0); // 0.01 r/100.0 - 0.005 is ArcArc tangentintersection !! 5889//r -= 0.005; 5890 5891 /* split each sub path itself 5892 */ 5893 for ( i=0, listCnt = [[path list] count]; i<listCnt; i++ ) 5894 { VPath *sp = [[path list] objectAtIndex:i]; 5895 int j, k, l, interCnt, spCnt = [[sp list] count]; 5896 5897 /* intersect and tile elements of subpath 5898 */ 5899 for ( j=0; j<spCnt; j++ ) 5900 { VGraphic *gJ = [[sp list] objectAtIndex:j]; 5901 NSRect jBounds = [gJ coordBounds]; 5902 BOOL splitted = NO; 5903 5904 for ( k=j+1; k<spCnt; k++ ) 5905 { VGraphic *gK = [[sp list] objectAtIndex:k]; 5906 NSPoint *interPts; 5907 NSRect kBounds = [gJ coordBounds]; 5908 5909 if (j == k || /*((j+1 < spCnt) ? (j+1) : (0)) == k || (((j-1) < 0) ? (spCnt-1):(j-1)) == k ||*/ 5910 !vhfIntersectsRect(jBounds, kBounds)) 5911 continue; 5912 5913 /* intersect graphics */ 5914 if ( (interCnt = [gJ getIntersections:&interPts with:gK]) ) 5915 { NSMutableArray *splitListJ=nil, *splitListK=nil; 5916 5917 /* tile graphics */ 5918 splitListJ = [gJ getListOfObjectsSplittedFrom:interPts :interCnt]; 5919 splitListK = [gK getListOfObjectsSplittedFrom:interPts :interCnt]; 5920 5921 /* insert tiled graphics */ 5922 if ( [splitListJ count] > 1 ) 5923 { 5924 splitted = YES; 5925 for (l=[splitListJ count]-1; l>=0; l--) 5926 { VGraphic *g = [splitListJ objectAtIndex:l]; 5927 5928 [[sp list] insertObject:g atIndex:j+1]; 5929 spCnt++; 5930 if (k > j) 5931 k++; 5932 } 5933 [[sp list] removeObjectAtIndex:j]; 5934 spCnt--; 5935 if (k > j) 5936 k--; 5937 j--; 5938 } 5939 if ( [splitListK count] > 1 ) 5940 { 5941// splitted = YES; // gJ perhaps not splitted - but later !!! 5942 for (l=[splitListK count]-1; l>=0; l--) 5943 { VGraphic *g = [splitListK objectAtIndex:l]; 5944 5945 [[sp list] insertObject:g atIndex:k+1]; 5946 spCnt++; 5947 //if (j > k) // not possible if k = j+1 5948 // j++; 5949 } 5950 [[sp list] removeObjectAtIndex:k]; 5951 spCnt--; 5952 //if (j > k) 5953 // j--; 5954 } 5955 free(interPts); 5956 if (splitted) 5957 break; 5958 } 5959 } 5960 } 5961 5962 } 5963 5964 /* split subpaths from each other !!! 5965 */ 5966 listCnt = [[path list] count]; 5967 for ( i=0; i<listCnt; i++ ) 5968 subPathSplitted[i] = NO; // initialize ! 5969 5970 for ( i=0; i<listCnt; i++ ) 5971 { VPath *sp = [[path list] objectAtIndex:i]; 5972 int j, c, o, l, spCnt = [[sp list] count], splittedPath = NO, oSpCnt=0; 5973 5974 for ( j=i+1; j<listCnt; j++ ) 5975 { VPath *oSp; 5976 BOOL oSplittedPath = NO; 5977 5978 if ( j==i ) 5979 continue; /* not the same path ! */ 5980 5981 oSp = [[path list] objectAtIndex:j]; /* other subpath */ 5982 oSpCnt = [[oSp list] count]; 5983 5984 /* intersect and tile elements of subpath -> with otherPaths */ 5985 for ( c=0; c<spCnt; c++ ) 5986 { VGraphic *gC = [[sp list] objectAtIndex:c]; 5987 NSRect cBounds = [gC coordBounds]; 5988 5989 for ( o=0; o<oSpCnt; o++ ) 5990 { VGraphic *gO = [[oSp list] objectAtIndex:o]; 5991 NSPoint *interPts; 5992 NSRect oBounds = [gO coordBounds]; 5993 int interCnt; 5994 BOOL splitted = NO; 5995 5996 if (!vhfIntersectsRect(cBounds, oBounds)) 5997 continue; 5998 5999 /* intersect graphics */ 6000 if ( (interCnt = [gC getIntersections:&interPts with:gO]) ) 6001 { NSMutableArray *splitListC=nil, *splitListO=nil; 6002 6003 /* tile graphics */ 6004 splitListC = [gC getListOfObjectsSplittedFrom:interPts :interCnt]; 6005 splitListO = [gO getListOfObjectsSplittedFrom:interPts :interCnt]; 6006 6007 /* insert tiled graphics */ 6008 if ( [splitListC count] > 1 ) 6009 { 6010 splitted = YES; 6011 splittedPath = YES; 6012 for (l=[splitListC count]-1; l>=0; l--) 6013 { VGraphic *g = [splitListC objectAtIndex:l]; 6014 6015 [[sp list] insertObject:g atIndex:c+1]; 6016 spCnt++; 6017 } 6018 [[sp list] removeObjectAtIndex:c]; 6019 spCnt--; 6020 gC = [[sp list] objectAtIndex:c]; 6021 cBounds = [gC coordBounds]; 6022 } 6023 if ( [splitListO count] > 1 ) 6024 { 6025 splitted = YES; 6026 oSplittedPath = YES; 6027 for (l=[splitListO count]-1; l>=0; l--) 6028 { VGraphic *g = [splitListO objectAtIndex:l]; 6029 6030 [[oSp list] insertObject:g atIndex:o+1]; 6031 oSpCnt++; 6032 } 6033 [[oSp list] removeObjectAtIndex:o]; 6034 oSpCnt--; 6035 } 6036 free(interPts); 6037 if (splitted) 6038 o = -1; // start at 0 ! for new current c graphics 6039 } 6040 } 6041 } 6042 if (oSplittedPath && subPathSplitted[j] == NO) 6043 subPathSplitted[j] = YES; // perhaps allready set and not splitted any more ! ? 6044 } 6045 if (splittedPath && subPathSplitted[i] == NO) // perhaps allready set and not splitted any more ! ? 6046 subPathSplitted[i] = YES; 6047 } 6048 6049 /* set objects selected - which distance is too small to original path (self) */ 6050 for ( i=0, listCnt = [[path list] count]; i<listCnt; i++ ) 6051 { VPath *sp = [[path list] objectAtIndex:i]; 6052 int j, spCnt = [[sp list] count]; 6053 6054 spCnt = [[sp list] count]; 6055 for ( j=spCnt-1; j>=0; j-- ) 6056 { VGraphic *g = [[sp list] objectAtIndex:j]; 6057 NSPoint s, e, m, sa; 6058 VArc *arc1 = [VArc arc], *arc2 = [VArc arc], *arc3 = [VArc arc]; 6059 6060/* if ([g isSelected]) 6061{ 6062// [[sp list] removeObjectAtIndex:j]; // debugging purpose only 6063 continue; 6064}*/ 6065 [g getPoint:&m at:0.4]; 6066 s = [g pointWithNum:0]; 6067 e = [g pointWithNum:MAXINT]; 6068 sa = s; sa.x += r; 6069 [arc1 setCenter:s start:sa angle:360.0]; 6070 sa = e; sa.x += r; 6071 [arc2 setCenter:e start:sa angle:360.0]; 6072 sa = m; sa.x += r; 6073 [arc3 setCenter:m start:sa angle:360.0]; 6074 6075 /* graphic nearer than r to original path (self) */ 6076 if ( /*SqrDistPoints(s, e) < 0.001 ||*/ 6077 ![arc1 tangentIntersectionWithPath:self] || ![arc2 tangentIntersectionWithPath:self] || 6078 ![arc3 tangentIntersectionWithPath:self] || (w < 0 && !width && ![self isPointInside:m]) || 6079 (w > 0 && [self isPointInside:m]) ) 6080 { 6081// [[sp list] removeObjectAtIndex:j]; // debugging purpose only 6082 [g setSelected:YES]; 6083 } 6084 else 6085 [g setSelected:NO]; 6086 } 6087 } 6088 6089 /* optimize to close path */ 6090 [self optimizeSubPathsToClosedPath:path :w :subPathSplitted]; 6091} 6092 6093/* return a path representing the outline of us 6094 * the path holds two lines and two arcs 6095 * if we need not build a contour a copy of self is returned 6096 */ 6097#define AngleNotSmallEnough(dir, w, angle) ((dir && w >= 0 && angle > 150.0) || (dir && w < 0 && angle < 210.0) || \ 6098 (!dir && w < 0 && angle > 150.0) || (!dir && w >= 0 && angle < 210.0)) 6099#define NeedArc(dir, w, angle) ((dir && w >= 0 && angle > 180.5) || (dir && w < 0 && angle < 179.5) || \ 6100 (!dir && w < 0 && angle > 180.5) || (!dir && w >= 0 && angle < 179.5)) 6101/*#define SmallAngle(dir, w, angle ) ((dir && w > 0 && angle < 89.5) || (dir && w < 0 && angle > 270.5) || \ 6102 (!dir && w < 0 && angle < 89.5) || (!dir && w > 0 && angle > 270.5))*/ 6103/*#define SmallAngle(dir, w, angle ) ((dir && w > 0 && angle < 85.5) || (dir && w < 0 && angle > 275.5) || \ 6104 (!dir && w < 0 && angle < 85.5) || (!dir && w > 0 && angle > 275.5))*/ 6105#define SmallAngle(dir, w, angle ) ((dir && w >= 0 && angle < 95.5) || (dir && w < 0 && angle > 265.5) || \ 6106 (!dir && w < 0 && angle < 95.5) || (!dir && w >= 0 && angle > 265.5)) 6107/*#define SmallAngle(dir, w, angle ) ((dir && w > 0 && angle < 120.0) || (dir && w < 0 && angle > 240.0) || \ 6108 (!dir && w < 0 && angle < 120.0) || (!dir && w > 0 && angle > 240.0))*/ 6109/* created: 1997-07-07 6110 * modified: 1997-07-07 6111 * get gradient 6112 * if a curve has several vertices in one point (gradient = 0, 0) we take the next vertice to get the gradient 6113 */ 6114//#define GradientNear(g, t) ([g isKindOfClass:[VCurve class]]) ? [g gradientNear:t] : [g gradientAt:t] 6115 6116static NSPoint orthPointAtBegOrEnd(id g, float r, int dirInd, BOOL end) 6117{ float b; 6118 NSPoint p, grad, orthP; 6119 6120 if ( !end ) /* calc to beg */ 6121 { 6122 p = [g pointWithNum:0]; /* start point of object */ 6123 grad = GradientNear(g, 0.0); /* gradient of start-point for outline object */ 6124 } 6125 else 6126 { p = [g pointWithNum:MAXINT]; /* end point of object */ 6127 grad = GradientNear(g, 1.0); /* gradient of start-point for outline object */ 6128 } 6129 if ( !(b = sqrt(grad.x*grad.x+grad.y*grad.y)) ) 6130 orthP = p; 6131 else 6132 { orthP.x = p.x + grad.y*r*dirInd/b; 6133 orthP.y = p.y - grad.x*r*dirInd/b; 6134 } 6135 return orthP; 6136} 6137 6138static float angleBetweenGraphicsInStartOrEnd(id g1, id g2, BOOL end) 6139{ NSPoint p, t1End, t2End, gradB, gradA; 6140 6141 if ( !end ) /* calc to beg */ 6142 { 6143 p = [g1 pointWithNum:0]; /* start point of object */ 6144 gradB = GradientNear(g1, 0.0); /* gradient of start-point object */ 6145 gradA = GradientNear(g2, 1.0); /* gradient of end-point for object */ 6146 } 6147 else 6148 { p = [g1 pointWithNum:MAXINT]; /* end point of object */ 6149 gradA = GradientNear(g1, 1.0); /* gradient of start-point for outline object */ 6150 gradB = GradientNear(g2, 0.0); /* gradient of end-point for prev object */ 6151 } 6152 t1End.x = p.x - gradA.x; 6153 t1End.y = p.y - gradA.y; 6154 t2End.x = p.x + gradB.x; 6155 t2End.y = p.y + gradB.y; 6156 return vhfAngleBetweenPoints(t1End, p, t2End); /* get angle (ccw) on right side */ 6157} 6158 6159static NSPoint parallelPointbetweenObjects(id g1, id g2, float angle, float r, int dirInd, BOOL end) 6160{ NSPoint p, gradA, gradB, pG, newP; 6161 double a, b, c, nr, na; 6162 6163 /* get gradients to calc start and end points of outline 6164 */ 6165 if ( !end ) 6166 { 6167 p = [g1 pointWithNum:0]; 6168 gradB = GradientNear(g1, 0.0); /* gradient of start-point for outline object */ 6169 gradA = GradientNear(g2, 1.0); /* gradient of end-point for prev object */ 6170 } 6171 else 6172 { p = [g1 pointWithNum:MAXINT]; 6173 gradA = GradientNear(g1, 1.0); /* gradient of start-point for outline object */ 6174 gradB = GradientNear(g2, 0.0); /* gradient of end-point for prev object */ 6175 } 6176 a = sqrt(gradA.x*gradA.x+gradA.y*gradA.y); 6177 b = sqrt(gradB.x*gradB.x+gradB.y*gradB.y); /* our gradient is orthogonal to the average of both gradients */ 6178 pG.x = gradA.y/a + gradB.y/b; 6179 pG.y = -(gradA.x/a + gradB.x/b); 6180 if ( !pG.x && !pG.y ) 6181 { pG.x = - gradA.y/a; 6182 pG.y = + gradA.x/a; 6183 } 6184 c = sqrt(pG.x*pG.x+pG.y*pG.y); // end point for outline object 6185 ( angle < 360.0-angle ) ? (na = angle/2.0) : (na = (360.0-angle)/2.0); 6186 nr = r / Sin(na); // need correct distance 6187 newP.x = p.x + pG.x*nr*dirInd/c; 6188 newP.y = p.y + pG.y*nr*dirInd/c; 6189 return newP; 6190} 6191 6192- (id)contour:(float)w useRaster:(BOOL)useRaster 6193{ 6194 if ( [self filled] ) 6195 return (useRaster) ? [self contourWithPixel:w] 6196 : [self contour:w inlay:NO splitCurves:YES]; 6197 else 6198 return [self contourOpen:w]; 6199} 6200- (id)contour:(float)w 6201{ 6202 if ( [self filled] ) 6203 return [self contour:w inlay:NO splitCurves:YES]; 6204 else 6205 return [self contourOpen:w]; 6206} 6207- (id)contour:(float)w inlay:(BOOL)inlay splitCurves:(BOOL)splitCurves useRaster:(BOOL)useRaster 6208{ 6209 return (useRaster) ? [self contourWithPixel:w] 6210 : [self contour:w inlay:inlay splitCurves:splitCurves]; 6211} 6212- (id)contour:(float)w inlay:(BOOL)inlay splitCurves:(BOOL)splitCurves 6213{ VPath *path, *subPath = nil, *pathCopy = nil; 6214 //VLine *line = [VLine new], *linePrev = [VLine new], *lineNext = [VLine new]; // to replace Curve if necessarie 6215 VLine *line = nil, *linePrev = nil, *lineNext = nil; // to replace Curve if necessarie 6216 int i, listCnt = [list count], begIx=0, endIx=0, dir=0; 6217 int direction, inside, directionArray[listCnt], insideArray[listCnt], dirInsideCnt=0; 6218 float r, dirInd=1.0, bAngle, eAngle; 6219 int cnt=0; 6220 NSAutoreleasePool *pool; 6221 6222 /* we just return a copy */ 6223 if ( (!filled && Abs(w)>width) || (Diff(w, 0.0) < 0.0001 && Diff(width, 0.0) < 0.0001) 6224 || (w<0.0 && Abs(w) == width) ) 6225 return [[self copy] autorelease]; 6226 6227 //if ( Prefs_UseRaster ) // FIXME: use version with useRaster flag in parameter! 6228 // return [self contourWithPixel:w]; 6229 6230 pathCopy = [[self copy] autorelease]; 6231 if (inlay) 6232 { VPath *oPath; 6233 6234 /* gegenrichtung contour mit w bilden */ 6235 oPath = [pathCopy contour:-w inlay:NO splitCurves:YES]; // self 6236 [oPath setFilled:YES optimize:NO]; 6237 path = [oPath contour:2.0*w inlay:NO splitCurves:NO]; 6238 return path; 6239 } 6240 6241 r = (w + width) / 2.0; // the amount of growth 6242 6243 path = [VPath path]; 6244 [path setColor:color]; 6245 [path setDirectionCCW:isDirectionCCW]; 6246 6247 pool = [NSAutoreleasePool new]; 6248 line = [VLine line]; linePrev = [VLine line]; lineNext = [VLine line]; 6249 6250 /* remove Elements with no length 6251 * the problem is that we destroy our closed path! 6252 */ 6253 for ( i=0, listCnt = [[pathCopy list] count]; i<listCnt; i++ ) 6254 { VGraphic *gThis = [[pathCopy list] objectAtIndex:i]; 6255 6256 if ( [gThis length] < 15.0*TOLERANCE ) 6257 { 6258 [[pathCopy list] removeObject:gThis]; 6259 i--; 6260 //[self closePath]; 6261 listCnt = [[pathCopy list] count]; 6262 continue; 6263 } 6264 } 6265 6266 /* what we do here: 6267 * step through elements of path 6268 * calculate start, end points for outline-elements in a distance to path (inside/outside) 6269 * calculate parallel elements through start and end points 6270 * we have to calculate each sub path separately, so we put them in real sub paths 6271 */ 6272 6273 /* walk through path list 6274 */ 6275 for ( i=0, listCnt = [[pathCopy list] count]; i<listCnt; i++ ) 6276 { VGraphic *g, 6277 *gThis, *gPrev, *gNext; /* this object, previous object, next object */ 6278 NSPoint begO = NSZeroPoint, endO = NSZeroPoint, /* start and endpoint of contour-object, if we dont add an arc (O = outline) */ 6279 begOrth, endOrth, /* start and endpoint of contour-object, if we add an arc (orthogonal points) */ 6280 center; /* center point of arc if needed */ 6281 int sc, needArc = 0; /* wether we need an arc to build correct contour around current edge */ 6282 NSMutableArray *splittedCurves = nil; 6283 6284 gThis = [[pathCopy list] objectAtIndex:i]; // this object 6285 6286 if ( [gThis isKindOfClass:[VCurve class]] ) 6287 { NSPoint p0, p1, p2, p3; 6288 6289 [(VCurve*)gThis getVertices:&p0 :&p1 :&p2 :&p3]; 6290 /* both Curve points are in its Start/End points - we build a line */ 6291 if ( SqrDistPoints(p0, p1) < 10*TOLERANCE && SqrDistPoints(p2, p3) < 10*TOLERANCE ) 6292 { 6293 [line setVertices:p0 :p3]; 6294 gThis = line; 6295 } 6296 } 6297 6298 /* new sub path 6299 */ 6300 if ( !i || i>endIx ) // new sub path 6301 { subPath = [VPath path]; 6302 [[path list] addObject:subPath]; 6303 begIx = i; 6304 //endIx = [self getLastObjectOfSubPath:begIx]; 6305 endIx = [pathCopy getLastObjectOfSubPath:begIx]; // tolerance:TOLERANCE 6306 6307 cnt = 0; // counter for cutIndex array 6308 6309 /* only one element in subpath, so this must be an arc */ 6310 if ( begIx == endIx ) 6311 { VGraphic *arc; 6312 int oldFillStyle=[gThis filled]; 6313 6314 if ( !([gThis isKindOfClass:[VArc class]] && Abs([(VArc*)gThis angle]) == 360.0) && 6315 ![gThis isKindOfClass:[VRectangle class]] && 6316 ![gThis isKindOfClass:[VPolyLine class]] ) // nothing 6317 { insideArray[dirInsideCnt] = 0; 6318 directionArray[dirInsideCnt++] = 0; /* doesnt matter */ 6319 continue; 6320 } 6321 [gThis setFilled:YES]; 6322 if ( [pathCopy subPathInsidePath:begIx :endIx] ) 6323 { 6324 if ( [gThis width] ) // special 6325 { VArc *gTh = [gThis copy]; 6326 6327 [gTh setWidth:0.0]; 6328 [gTh setRadius:[gThis radius]-([gThis width]/2.0)]; 6329 arc = [gTh contour:-w]; 6330 } 6331 else 6332 arc = [gThis contour:-w]; 6333 insideArray[dirInsideCnt] = 1; 6334 } 6335 else 6336 { arc = [gThis contour:w]; 6337 insideArray[dirInsideCnt] = 0; 6338 } 6339 directionArray[dirInsideCnt++] = 0; /* doesnt matter if only one object */ 6340 //arc = ( [self subPathInsidePath:begIx :endIx] ) ? ([gThis contour:-w]) : ([gThis contour:w]); 6341 if ([arc isKindOfClass:[VPath class]]) 6342 { int j, spCnt = [[(VPath*)arc list] count]; 6343 6344 for (j=0; j<spCnt; j++) 6345 [[subPath list] addObject:[[(VPath*)arc list] objectAtIndex:j]]; 6346 } 6347 else if (arc) 6348 [[subPath list] addObject:arc]; 6349 [gThis setFilled:oldFillStyle]; 6350 continue; 6351 } 6352 /* determine direction indicator inside(1), outside(-1) */ 6353 direction = [pathCopy directionOfSubPath:begIx :endIx]; // 1 = ccw, 0 = cw 6354 if ( (inside = [pathCopy subPathInsidePath:begIx :endIx]) ) 6355 dir = ( direction ) ? 0 : 1; 6356 else 6357 dir = direction; 6358 dirInd = ( dir ) ? 1.0 : -1.0; // 1 = ccw, 0 = cw 6359 6360 directionArray[dirInsideCnt] = direction; 6361 insideArray[dirInsideCnt++] = inside; 6362 } 6363 6364 /* split curves for better results */ 6365 if ( splitCurves && [(VGraphic*)gThis length] > 1000.0*TOLERANCE && [gThis isKindOfClass:[VCurve class]] ) 6366 { NSArray *splittedCurves1 = nil, *splittedCurves2 = nil; 6367 NSArray *splittedCurves3 = nil, *splittedCurves4 = nil; 6368 6369 splittedCurves = [NSMutableArray arrayWithCapacity:5]; 6370 splittedCurves1 = [(VCurve*)gThis splittedObjectsAt:0.333]; // 0.333 0.3 6371 6372 splittedCurves2 = [[splittedCurves1 objectAtIndex:0] splittedObjectsAt:0.4]; // 0.2 0.4 6373 [splittedCurves addObject:[splittedCurves2 objectAtIndex:0]]; 6374 [splittedCurves addObject:[splittedCurves2 objectAtIndex:1]]; 6375 6376 splittedCurves3 = [[splittedCurves1 objectAtIndex:1] splittedObjectsAt:0.5]; // 0.5 4/7 6377 [splittedCurves addObject:[splittedCurves3 objectAtIndex:0]]; 6378 6379 splittedCurves4 = [[splittedCurves3 objectAtIndex:1] splittedObjectsAt:0.6]; // 0.8 0.6 6380 [splittedCurves addObject:[splittedCurves4 objectAtIndex:0]]; 6381 [splittedCurves addObject:[splittedCurves4 objectAtIndex:1]]; 6382 } 6383 for ( sc=0; sc < ((splittedCurves) ? 5 : 1); sc++ ) 6384 { BOOL /*calcBegOWithCut = 0, */calcEndOWithCut = 0; 6385 6386 if ( splittedCurves ) 6387 gThis = [splittedCurves objectAtIndex:sc]; 6388 6389 if ( sc >= 1 && sc <= 4 ) // 2012-01-19 6390 gPrev = [splittedCurves objectAtIndex:sc-1]; 6391 else 6392 { 6393 gPrev = (i>begIx) ? [[pathCopy list] objectAtIndex:i-1] : [[pathCopy list] objectAtIndex:endIx]; 6394 6395 if ( [gPrev isKindOfClass:[VCurve class]] ) 6396 { NSPoint p0, p1, p2, p3; 6397 6398 [(VCurve*)gPrev getVertices:&p0 :&p1 :&p2 :&p3]; 6399 /* both Curve points are in its Start/End points - we build a line */ 6400 if ( SqrDistPoints(p0, p1) < 10*TOLERANCE && SqrDistPoints(p2, p3) < 10*TOLERANCE ) 6401 { 6402 [linePrev setVertices:p0 :p3]; 6403 gPrev = linePrev; 6404 } 6405 } 6406 if ( splitCurves && [(VGraphic*)gPrev length] > 1000.0*TOLERANCE && 6407 [gPrev isKindOfClass:[VCurve class]] ) 6408 { NSArray *splittedCurvePrev = nil, *splittedC = nil; 6409 6410 splittedC = [(VCurve*)gPrev splittedObjectsAt:0.666]; // 0.666 4/7 6411 if ( (splittedCurvePrev = [[splittedC objectAtIndex:1] splittedObjectsAt:0.6]) ) // 0.8 0.6 6412 gPrev = [splittedCurvePrev objectAtIndex:1]; // the last of third parts of prev curve 6413 } 6414 } 6415 bAngle = angleBetweenGraphicsInStartOrEnd(gThis, gPrev, 0); 6416 begOrth = orthPointAtBegOrEnd(gThis, r, dirInd, 0); /* beg orthogonal to beg of gThis */ 6417 6418 /* in this cases we need an arc between the graphics (added only at end points) 6419 * angle is greater than 180 at correction side 6420 */ 6421 if ( NeedArc(dir, w, bAngle) ) 6422 begO = begOrth; 6423/* so war es */ 6424#if 0 6425 else if ( ([gThis isKindOfClass:[VLine class]] && [gPrev isKindOfClass:[VLine class]]) 6426 || AngleNotSmallEnough(dir, w, bAngle) ) 6427 begO = parallelPointbetweenObjects(gThis, gPrev, bAngle, r, dirInd, 0); 6428 else 6429 calcBegOWithCut=1; // begO = parallelPointbetweenObjects(gThis, gPrev, bAngle, r, dirInd, 0); 6430 6431 /* cut of prevG(parallel) with gThis(parallel) is begO 6432 */ 6433 if ( calcBegOWithCut || SmallAngle(dir, w, bAngle) ) 6434 { VGraphic *pG, *thG; 6435 NSPoint bPrevOrth, ePrevOrth, *iPts = NULL; 6436 int iCnt = 0; 6437 6438 bPrevOrth = orthPointAtBegOrEnd(gPrev, r, dirInd, 0); 6439 ePrevOrth = orthPointAtBegOrEnd(gPrev, r, dirInd, 1); 6440 // get parallel object to gPrev and gThis 6441 pG = [gPrev parallelObject:bPrevOrth :ePrevOrth :bPrevOrth :ePrevOrth]; 6442 endOrth = orthPointAtBegOrEnd(gThis, r, dirInd, 1); 6443 thG = [gThis parallelObject:begOrth :endOrth :begOrth :endOrth]; 6444 6445 if ( pG && thG && (iCnt = [pG getIntersections:&iPts with:thG])==1 ) 6446 begO = iPts[0]; 6447 else 6448 begO = begOrth; 6449 if (iPts) 6450 free(iPts); 6451 } 6452#endif 6453/* neu */ 6454#if 1 6455 /* cut of prevG(parallel) with gThis(parallel) is begO 6456 */ 6457 else 6458 { VGraphic *pG, *thG; 6459 NSPoint bPrevOrth, ePrevOrth, *iPts = NULL; 6460 int iCnt = 0; 6461 6462 bPrevOrth = orthPointAtBegOrEnd(gPrev, r, dirInd, 0); 6463 ePrevOrth = orthPointAtBegOrEnd(gPrev, r, dirInd, 1); 6464 // get parallel object to gPrev and gThis 6465 pG = [gPrev parallelObject:bPrevOrth :ePrevOrth :bPrevOrth :ePrevOrth]; 6466 endOrth = orthPointAtBegOrEnd(gThis, r, dirInd, 1); 6467 thG = [gThis parallelObject:begOrth :endOrth :begOrth :endOrth]; 6468 6469 if ( pG && thG && (iCnt = [pG getIntersections:&iPts with:thG]) ) 6470 { 6471 if ( iCnt == 1 ) 6472 begO = iPts[0]; 6473 else 6474 { NSPoint bO = parallelPointbetweenObjects(gThis, gPrev, bAngle, r, dirInd, 0); 6475 float d=0, dist = MAXCOORD; 6476 int x; 6477 6478 for (x=0; x < iCnt; x++) 6479 { 6480 if ( (d = SqrDistPoints(iPts[x], bO)) < dist) 6481 { begO = iPts[x]; 6482 dist = d; 6483 } 6484 } 6485 } 6486 } 6487 else if ( /*([gThis isKindOfClass:[VLine class]] && [gPrev isKindOfClass:[VLine class]]) 6488 ||*/ AngleNotSmallEnough(dir, w, bAngle) ) 6489 begO = parallelPointbetweenObjects(gThis, gPrev, bAngle, r, dirInd, 0); 6490 else 6491 begO = begOrth; 6492 if (iPts) 6493 free(iPts); 6494 } 6495#endif 6496 6497 if ( splittedCurves && sc >= 0 && sc <= 3 ) // 2012-01-19 6498 gNext = [splittedCurves objectAtIndex:sc+1]; 6499 else 6500 { gNext = (i<endIx) ? [[pathCopy list] objectAtIndex:i+1] : [[pathCopy list] objectAtIndex:begIx]; 6501 6502 if ( [gNext isKindOfClass:[VCurve class]] ) 6503 { NSPoint p0, p1, p2, p3; 6504 6505 [(VCurve*)gNext getVertices:&p0 :&p1 :&p2 :&p3]; 6506 /* both Curve points are in its Start/End points - we build a line */ 6507 if ( SqrDistPoints(p0, p1) < 10*TOLERANCE && SqrDistPoints(p2, p3) < 10*TOLERANCE ) 6508 { 6509 [lineNext setVertices:p0 :p3]; 6510 gNext = lineNext; 6511 } 6512 } 6513 if ( splitCurves && [(VGraphic*)gNext length] > 1000.0*TOLERANCE && 6514 [gNext isKindOfClass:[VCurve class]] ) 6515 { NSArray *splittedCurveNext = nil, *splittedC = nil; 6516 6517 splittedC = [(VCurve*)gNext splittedObjectsAt:0.333]; // 0.333 0.3 6518 if ( (splittedCurveNext=[[splittedC objectAtIndex:0] splittedObjectsAt:0.4]) ) // 0.2 0.4 6519 gNext = [splittedCurveNext objectAtIndex:0]; // the first of third parts of next curve 6520 } 6521 } 6522 eAngle = angleBetweenGraphicsInStartOrEnd(gThis, gNext, 1); 6523 endOrth = orthPointAtBegOrEnd(gThis, r, dirInd, 1); // beg orthogonal to beg of gThis 6524 6525 if ( NeedArc(dir, w, eAngle) ) 6526 { endO = endOrth; 6527 needArc = 1; 6528 } 6529/* so war es */ 6530#if 0 6531 6532 else if ( ([gThis isKindOfClass:[VLine class]] && [gNext isKindOfClass:[VLine class]]) 6533 || AngleNotSmallEnough(dir, w, eAngle) ) 6534 endO = parallelPointbetweenObjects(gThis, gNext, eAngle, r, dirInd, 1); 6535 else 6536 calcEndOWithCut = 1; // endO = parallelPointbetweenObjects(gThis, gNext, eAngle, r, dirInd, 1); 6537 6538 if ( calcEndOWithCut || SmallAngle(dir, w, eAngle) ) 6539 { VGraphic *nG, *thG; 6540 NSPoint bNextOrth, eNextOrth, *iPts = NULL; 6541 int iCnt = 0; 6542 6543 bNextOrth = orthPointAtBegOrEnd(gNext, r, dirInd, 0); 6544 eNextOrth = orthPointAtBegOrEnd(gNext, r, dirInd, 1); 6545 // get parallel object to gNext and gThis 6546 nG = [gNext parallelObject:bNextOrth :eNextOrth :bNextOrth :eNextOrth]; 6547 thG = [gThis parallelObject:begOrth :endOrth :begOrth :endOrth]; 6548 6549 if ( nG && thG && (iCnt = [thG getIntersections:&iPts with:nG])==1 ) 6550 endO = iPts[0]; 6551 else 6552 { 6553 needArc = 2; // here we calc edge orhtogonal with an arc 6554 endO = endOrth; 6555 } 6556 if (iPts) 6557 free(iPts); 6558 } 6559 6560#endif 6561 6562/* neu */ 6563#if 1 6564 /* intersect parallel of gThis with parallel of gNext. Intersection point: begO 6565 */ 6566 else 6567 { VGraphic *nG, *thG; 6568 NSPoint bNextOrth, eNextOrth, *iPts = NULL; 6569 int iCnt = 0; 6570 6571 bNextOrth = orthPointAtBegOrEnd(gNext, r, dirInd, 0); 6572 eNextOrth = orthPointAtBegOrEnd(gNext, r, dirInd, 1); 6573 // get parallel object to gNext and gThis 6574 nG = [gNext parallelObject:bNextOrth :eNextOrth :bNextOrth :eNextOrth]; 6575 thG = [gThis parallelObject:begOrth :endOrth :begOrth :endOrth]; 6576 6577 if ( nG && thG && (iCnt = [thG getIntersections:&iPts with:nG]) ) 6578 { 6579 if ( iCnt == 1 ) 6580 endO = iPts[0]; 6581 else // new 6582 { NSPoint eO = parallelPointbetweenObjects(gThis, gNext, eAngle, r, dirInd, 1); 6583 float d=0, dist = MAXCOORD; 6584 int x; 6585 6586 for (x=0; x < iCnt; x++) 6587 { 6588 if ( (d = SqrDistPoints(iPts[x], eO)) < dist) 6589 { endO = iPts[x]; 6590 dist = d; 6591 } 6592 } 6593 } 6594 } 6595 else if ( /*([gThis isKindOfClass:[VLine class]] && [gNext isKindOfClass:[VLine class]]) 6596 ||*/ AngleNotSmallEnough(dir, w, eAngle) ) 6597 endO = parallelPointbetweenObjects(gThis, gNext, eAngle, r, dirInd, 1); 6598 else 6599 { calcEndOWithCut = 1; 6600 needArc = 2; // here we calc edge orhtogonal with an arc 6601 endO = endOrth; 6602 } 6603 if (iPts) 6604 free(iPts); 6605 } 6606#endif 6607 6608 /* now we can calc our parallel object of gThis */ 6609 if ( (g = [gThis parallelObject:begOrth :endOrth :begO :endO]) ) /* build parallel objects */ 6610 { 6611 if ( [g isKindOfClass:[VPath class]] ) // VPolyLine 6612 { int j, gCnt = [[(VPath*)g list] count]; 6613 6614 for (j=0; j<gCnt; j++) 6615 [[subPath list] addObject:[[(VPath*)g list] objectAtIndex:j]]; 6616 } 6617 else 6618 [[subPath list] addObject:g]; 6619 } 6620 6621 /* calulate arc to close ends 6622 * if we have to add an arc we use the end of gThis as center, 6623 * endO as start point, new angle is calculated 6624 */ 6625 if ( needArc ) 6626 { VArc *arc = [VArc arc]; 6627 float newA; 6628 6629 [arc setWidth:[gThis width]]; 6630 [arc setColor:[gThis color]]; 6631 if ( needArc == 2 ) // not cut 6632 { 6633 newA = ( eAngle > 360.0-eAngle ) ? ((360-eAngle)+180.0) : (eAngle+180.0); 6634 if ( (!dir && w >= 0 && eAngle > 180.0) || (dir && w <= 0 && eAngle > 180.0) ) 6635 { newA *= -1.0; 6636 if ( width && (calcEndOWithCut || SmallAngle(dir, w, eAngle)) ) 6637 { newA = 360.0 + newA; 6638 if (newA < -360.0) newA += 360.0; 6639 } 6640 } 6641 else if ( width && (calcEndOWithCut || SmallAngle(dir, w, eAngle)) ) 6642 { newA = - (360.0-newA); 6643 if (newA < -360.0) newA += 360.0; 6644 } 6645 } 6646 else 6647 { /* eAngle > 180 */ 6648 newA = ( eAngle > 360.0-eAngle ) ? (eAngle-180.0) : ((360.0-eAngle)-180.0); 6649 if ( (!dir && w >= 0 && eAngle < 180.0) || (dir && w <= 0 && eAngle < 180.0) ) 6650 newA *= -1.0; /* cw */ 6651 } 6652 center = [gThis pointWithNum:MAXINT]; // end pt of object is arc center - with out smoot edges 6653 if (Abs(newA) < 235.0) // we dont want arc greater than 180 degree (not possible in a contour) 6654 { [arc setCenter:center start:endO angle:newA]; 6655 [[subPath list] addObject:arc]; 6656 } 6657 } 6658 } 6659 } 6660 /* if (splitCurves) // debugging only */ 6661 [self removeFaultGraphicsInSubpaths:path :w]; 6662 6663 [path unnest]; /* copy elements of subpath to list of path */ 6664 [path setSelected:[self isSelected]]; 6665 6666 [pool release]; 6667 return path; 6668} 6669#if 0 /* 4 Curves a 0.3/0.7 */ 6670- (id)contour:(float)w inlay:(BOOL)inlay removeLoops:(BOOL)removeLoops 6671{ VPath *path, *subPath=nil; 6672 int i, listCnt = [list count], begIx=0, endIx=0, dir=0; 6673 int direction, inside, directionArray[listCnt], insideArray[listCnt], dirInsideCnt=0; 6674 float r, dirInd=1.0, bAngle, eAngle; 6675 int cnt=0; 6676 6677 /* we just return a copy */ 6678 if ( (!filled && Abs(w)>width) || Diff(w, 0.0) < 0.0001 || (w<0.0 && Abs(w) == width) ) 6679 return [[self copy] autorelease]; 6680 6681 //if ( Prefs_UseRaster ) // raster algorith must be called directly ! 6682 // return [self contourWithPixel:w]; 6683 6684 r = (w + width) / 2.0; // the amount of growth 6685 6686 path = [VPath path]; 6687 [path setColor:color]; 6688 6689 /* remove Elements with no length 6690 * the problem is that we destroy our closed path! 6691 */ 6692 for ( i=0, listCnt = [list count]; i<listCnt; i++ ) 6693 { VGraphic *gThis = [list objectAtIndex:i]; 6694 6695 if ( [gThis length] < 10.0*TOLERANCE ) 6696 { 6697 [list removeObject:gThis]; 6698 i--; 6699 //[self closePath]; 6700 listCnt = [list count]; 6701 continue; 6702 } 6703 } 6704 6705 /* what we do here: 6706 * step through elements of path 6707 * calculate start, end points for outline-elements in a distance to path (inside/outside) 6708 * calculate parallel elements through start and end points 6709 * we have to calculate each sub path separately, so we put them in real sub paths 6710 */ 6711 6712 /* walk through path list 6713 */ 6714 for ( i=0, listCnt = [list count]; i<listCnt; i++ ) 6715 { id g, 6716 gThis, gPrev, gNext; /* this object, previous object, next object */ 6717 NSPoint begO, endO, /* start and endpoint of contour-object, if we dont add an arc (O = outline) */ 6718 begOrth, endOrth, /* start and endpoint of contour-object, if we add an arc (orthogonal points) */ 6719 center; /* center point of arc if needed */ 6720 int sc, needArc = 0; /* wether we need an arc to build correct contour around current edge */ 6721 NSMutableArray *splittedCurves = nil; 6722 6723 gThis = [list objectAtIndex:i]; /* this object */ 6724 6725 /* new sub path 6726 */ 6727 if ( !i || i>endIx ) /* new sub path */ 6728 { subPath = [VPath path]; 6729 [[path list] addObject:subPath]; 6730 begIx = i; 6731 //endIx = [self getLastObjectOfSubPath:begIx]; 6732 endIx = [self getLastObjectOfSubPath:begIx]; // tolerance:TOLERANCE 6733 6734 cnt = 0; /* counter for cutIndex array */ 6735 6736 /* only one element in subpath, so this must be an arc */ 6737 if ( begIx == endIx ) 6738 { VGraphic *arc; 6739 int oldFillStyle=[gThis filled]; 6740 6741 if ( !([gThis isKindOfClass:[VArc class]] && Abs([(VArc*)gThis angle]) == 360.0) && 6742 ![gThis isKindOfClass:[VRectangle class]] ) /* nothing */ 6743 { insideArray[dirInsideCnt] = 0; 6744 directionArray[dirInsideCnt++] = 0; /* doesnt matter */ 6745 continue; 6746 } 6747 [gThis setFilled:YES]; 6748 if ( [self subPathInsidePath:begIx :endIx] ) 6749 { arc = [gThis contour:-w]; 6750 insideArray[dirInsideCnt] = 1; 6751 } 6752 else 6753 { arc = [gThis contour:w]; 6754 insideArray[dirInsideCnt] = 0; 6755 } 6756 directionArray[dirInsideCnt++] = 0; /* doesnt matter if only one object */ 6757// ( [self subPathInsidePath:begIx :endIx] ) ? (arc = [gThis contour:-w]) : (arc = [gThis contour:w]); 6758 if (arc) 6759 [[subPath list] addObject:arc]; 6760 [gThis setFilled:oldFillStyle]; 6761 continue; 6762 } 6763 /* determine direction indicator inside(1), outside(-1) 6764 */ 6765 direction = [self directionOfSubPath:begIx :endIx]; /* 1 = ccw, 0 = cw */ 6766 if ( (inside = [self subPathInsidePath:begIx :endIx]) ) 6767 dir = ( direction ) ? 0 : 1; 6768 else 6769 dir = direction; 6770 dirInd = ( dir ) ? 1.0 : -1.0; /* 1 = ccw, 0 = cw */ 6771 6772 directionArray[dirInsideCnt] = direction; 6773 insideArray[dirInsideCnt++] = inside; 6774 } 6775 6776 /* split curves for better results */ 6777 if ( [gThis isKindOfClass:[VCurve class]] ) 6778 { NSArray *splittedCurves1 = nil, *splittedCurves2 = nil, *splittedCurves3 = nil; 6779 6780 splittedCurves = [NSMutableArray arrayWithCapacity:4]; 6781 splittedCurves1 = [gThis splittedObjectsAt:0.5]; 6782 6783 splittedCurves2 = [[splittedCurves1 objectAtIndex:0] splittedObjectsAt:0.3]; 6784 [splittedCurves addObject:[splittedCurves2 objectAtIndex:0]]; 6785 [splittedCurves addObject:[splittedCurves2 objectAtIndex:1]]; 6786 splittedCurves3 = [[splittedCurves1 objectAtIndex:1] splittedObjectsAt:0.7]; 6787 [splittedCurves addObject:[splittedCurves3 objectAtIndex:0]]; 6788 [splittedCurves addObject:[splittedCurves3 objectAtIndex:1]]; 6789 } 6790 for ( sc=0; sc < ((splittedCurves) ? 4 : 1); sc++ ) 6791 { BOOL calcBegOWithCut = 0, calcEndOWithCut = 0; 6792 6793 if ( splittedCurves ) 6794 gThis = [splittedCurves objectAtIndex:sc]; 6795 6796 if ( sc == 1 ) 6797 gPrev = [splittedCurves objectAtIndex:0]; 6798 else if ( sc == 2 ) 6799 gPrev = [splittedCurves objectAtIndex:1]; 6800 else if ( sc == 3 ) 6801 gPrev = [splittedCurves objectAtIndex:2]; 6802 else 6803 { 6804 gPrev = (i>begIx) ? [list objectAtIndex:i-1] : [list objectAtIndex:endIx]; 6805 if ( [gPrev isKindOfClass:[VCurve class]] ) 6806 { NSArray *splittedCurvePrev = nil, *splittHalf = nil; 6807 6808 splittHalf = [gPrev splittedObjectsAt:0.5]; 6809 if ( (splittedCurvePrev = [[splittHalf objectAtIndex:1] splittedObjectsAt:0.7]) ) 6810 gPrev = [splittedCurvePrev objectAtIndex:1]; // the last of third parts of prev curve 6811 } 6812 } 6813 bAngle = angleBetweenGraphicsInStartOrEnd(gThis, gPrev, 0); 6814 begOrth = orthPointAtBegOrEnd(gThis, r, dirInd, 0); /* beg orthogonal to beg of gThis */ 6815 6816 /* in this cases we need an arc between the graphics (added only at end points) 6817 * angle is greater than 180 at correction side 6818 */ 6819 if ( NeedArc(dir, w, bAngle) ) 6820 begO = begOrth; 6821 else if ( ([gThis isKindOfClass:[VLine class]] && [gPrev isKindOfClass:[VLine class]]) 6822 || AngleNotSmallEnough(dir, w, bAngle) ) 6823 begO = parallelPointbetweenObjects(gThis, gPrev, bAngle, r, dirInd, 0); 6824 else 6825 calcBegOWithCut=1; // begO = parallelPointbetweenObjects(gThis, gPrev, bAngle, r, dirInd, 0); 6826 6827 /* cut of prevG(parallel) with gThis(parallel) is begO 6828 */ 6829 if ( calcBegOWithCut || SmallAngle(dir, w, bAngle) ) 6830 { VGraphic *pG, *thG; 6831 NSPoint bPrevOrth, ePrevOrth, *iPts = NULL; 6832 int iCnt = 0; 6833 6834 bPrevOrth = orthPointAtBegOrEnd(gPrev, r, dirInd, 0); 6835 ePrevOrth = orthPointAtBegOrEnd(gPrev, r, dirInd, 1); 6836 // get parallel object to gPrev and gThis 6837 pG = [gPrev parallelObject:bPrevOrth :ePrevOrth :bPrevOrth :ePrevOrth]; 6838 endOrth = orthPointAtBegOrEnd(gThis, r, dirInd, 1); 6839 thG = [gThis parallelObject:begOrth :endOrth :begOrth :endOrth]; 6840 6841 if ( pG && thG && (iCnt = [pG getIntersections:&iPts with:thG])==1 ) 6842 begO = iPts[0]; 6843 else 6844 begO = begOrth; 6845 if (iPts) 6846 free(iPts); 6847 } 6848 6849 if ( !sc && splittedCurves ) 6850 gNext = [splittedCurves objectAtIndex:1]; 6851 else if ( sc == 1 && splittedCurves ) 6852 gNext = [splittedCurves objectAtIndex:2]; 6853 else if ( sc == 2 && splittedCurves ) 6854 gNext = [splittedCurves objectAtIndex:3]; 6855 else 6856 { gNext = (i<endIx) ? [list objectAtIndex:i+1] : [list objectAtIndex:begIx]; 6857 if ( [gNext isKindOfClass:[VCurve class]] ) 6858 { NSArray *splittedCurveNext = nil, *splittHalf = nil; 6859 6860 splittHalf = [gNext splittedObjectsAt:0.5]; 6861 if ( (splittedCurveNext=[[splittHalf objectAtIndex:0] splittedObjectsAt:0.3]) ) 6862 gNext = [splittedCurveNext objectAtIndex:0]; // the first of third parts of next curve 6863 } 6864 } 6865 eAngle = angleBetweenGraphicsInStartOrEnd(gThis, gNext, 1); 6866 endOrth = orthPointAtBegOrEnd(gThis, r, dirInd, 1); /* beg orthogonal to beg of gThis */ 6867 6868 if ( NeedArc(dir, w, eAngle) ) 6869 { endO = endOrth; 6870 needArc = 1; 6871 } 6872 else if ( ([gThis isKindOfClass:[VLine class]] && [gNext isKindOfClass:[VLine class]]) 6873 || AngleNotSmallEnough(dir, w, eAngle) ) 6874 endO = parallelPointbetweenObjects(gThis, gNext, eAngle, r, dirInd, 1); 6875 else 6876 calcEndOWithCut = 1; // endO = parallelPointbetweenObjects(gThis, gNext, eAngle, r, dirInd, 1); 6877 6878 /* intersect parallel of gThis with parallel of gNext. Intersection point: begO 6879 */ 6880 if ( calcEndOWithCut || SmallAngle(dir, w, eAngle) ) 6881 { VGraphic *nG, *thG; 6882 NSPoint bNextOrth, eNextOrth, *iPts = NULL; 6883 int iCnt; 6884 6885 bNextOrth = orthPointAtBegOrEnd(gNext, r, dirInd, 0); 6886 eNextOrth = orthPointAtBegOrEnd(gNext, r, dirInd, 1); 6887 // get parallel object to gNext and gThis 6888 nG = [gNext parallelObject:bNextOrth :eNextOrth :bNextOrth :eNextOrth]; 6889 thG = [gThis parallelObject:begOrth :endOrth :begOrth :endOrth]; 6890 6891 if ( nG && thG && (iCnt = [thG getIntersections:&iPts with:nG])==1 ) 6892 endO = iPts[0]; 6893 else 6894 { 6895 needArc = 2; // here we calc edge orhtogonal with an arc 6896 endO = endOrth; 6897 } 6898 if (iPts) 6899 free(iPts); 6900 } 6901 6902 /* now we can calc our parallel object of gThis */ 6903 if ( (g = [gThis parallelObject:begOrth :endOrth :begO :endO]) ) /* build parallel objects */ 6904 { 6905 if ( [g isKindOfClass:[VPath class]] ) // VPolyLine 6906 { int j, gCnt = [[g list] count]; 6907 6908 for (j=0; j<gCnt; j++) 6909 [[subPath list] addObject:[[g list] objectAtIndex:j]]; 6910 } 6911 else 6912 [[subPath list] addObject:g]; 6913 } 6914 6915 /* calulate arc to close ends 6916 * if we have to add an arc we use the end of gThis as center, 6917 * endO as start point, new angle is calculated 6918 */ 6919 if ( needArc ) 6920 { VArc *arc = [VArc arc]; 6921 float newA; 6922 6923 [arc setWidth:[gThis width]]; 6924 [arc setColor:[gThis color]]; 6925 if ( needArc == 2 ) // not cut 6926 { ( eAngle > 360.0-eAngle ) ? (newA = (360-eAngle)+180.0) : (newA = eAngle+180.0); 6927 if ( (!dir && w >= 0 && eAngle > 180.0) || (dir && w <= 0 && eAngle > 180.0) ) 6928 { newA *= -1.0; 6929 if ( width && (calcEndOWithCut || SmallAngle(dir, w, eAngle)) ) 6930 { newA = 360.0 + newA; 6931 if (newA < -360.0) newA += 360.0; 6932 } 6933 } 6934 else if ( width && (calcEndOWithCut || SmallAngle(dir, w, eAngle)) ) 6935 { newA = - (360.0-newA); 6936 if (newA < -360.0) newA += 360.0; 6937 } 6938 } 6939 else 6940 { /* eAngle > 180 */ 6941 newA = ( eAngle > 360.0-eAngle ) ? (eAngle-180.0) : ((360.0-eAngle)-180.0); 6942 if ( (!dir && w >= 0 && eAngle < 180.0) || (dir && w <= 0 && eAngle < 180.0) ) 6943 newA *= -1.0; /* cw */ 6944 } 6945 center = [gThis pointWithNum:MAXINT]; // end pt of object is arc center - with out smoot edges 6946 if (Abs(newA) < 235.0) // we dont want arc greater than 180 degree (not possible in a contour) 6947 { [arc setCenter:center start:endO angle:newA]; 6948 [[subPath list] addObject:arc]; 6949 } 6950 } 6951 } 6952 } 6953 6954 [self removeFaultGraphicsInSubpaths:path :w]; 6955 6956 [path unnest]; /* copy elements of subpath to list of path */ 6957 [path setSelected:[self isSelected]]; 6958 6959 return path; 6960} 6961#endif 6962 6963/* Build Contour of unfilled path 6964 * modified: 2011-03-11 (build outline with stroke width + distance) 6965 */ 6966- (id)contourOpen:(float)w 6967{ VPath *path = [VPath path]; 6968 int i, cnt = [list count]; 6969 float cw = (w + width), oldWidth = 0.0; 6970 6971 /* we just return a copy */ 6972 //if ( (w < 0.0 && Abs(w) >= width) || Diff(w, 0.0) < 0.0001 ) 6973 if ( (Diff(w, 0.0) < 0.0001 && width == 0.0) || (w < 0.0 && Abs(w) >= width) ) 6974 return [[self copy] autorelease]; 6975 6976 /* remove Elements with no length 6977 * the problem is that we destroy our closed path! 6978 */ 6979 for ( i=0, cnt = [list count]; i<cnt; i++ ) 6980 { VGraphic *gThis = [list objectAtIndex:i]; 6981 6982 if ( [gThis length] < 10.0*TOLERANCE ) 6983 { 6984 [list removeObject:gThis]; 6985 i--; 6986 //[self closePath]; 6987 cnt = [list count]; 6988 continue; 6989 } 6990 } 6991 6992 [path setColor:[self color]]; 6993 // build contour of all elements in path 6994 for (i=0; i<cnt; i++) 6995 { VGraphic *gr = [list objectAtIndex:i], *ng; 6996 6997 //if ( [gr isKindOfClass:[VLine class]] ) // line did not have a width here ! arc/rect eigentlich auch nicht ? 6998 // falls doch -> [gr setWidth:0.0]; // nachher alten fillstyle wieder setzen ??? 6999 oldWidth = [gr width]; 7000 [gr setWidth:0.0]; 7001 ng = [gr contour:cw]; 7002 [gr setWidth:oldWidth]; 7003 if ( [ng isKindOfClass:[VPath class]] ) // line, open arc 7004 [(VPath*)ng setFilled:YES optimize:NO]; // allready optimized 7005 else // full arc, rectangle 7006 [ng setFilled:YES]; // need objects filled for uniteAreas 7007 [[path list] addObject:ng]; 7008 } 7009 7010 // unite these elements 7011 { HiddenArea *hiddenArea = [HiddenArea new]; 7012 [hiddenArea uniteAreas:[path list]]; 7013 [hiddenArea release]; 7014 } 7015 // unfill 7016 [path unnest]; 7017 [path setFilled:NO]; 7018 return path; 7019} 7020 7021/* get contour with pixels 7022 * return the calculated path and the linePath (with the up and down engraving lines) 7023 */ 7024- (VPath*)contourWithPixel:(float)w 7025{ PathContour *pathContour; 7026 VPath *path; 7027 7028 if ( !(w+width) ) 7029 { NSMutableData *data; 7030 NSArchiver *ts; 7031 NSUnarchiver *tsu; 7032 7033 /* writes the path to a stream and reads it back from this stream */ 7034 data = [[NSMutableData alloc] init]; 7035 ts = [[NSArchiver alloc] initForWritingWithMutableData:data]; 7036 [ts encodeRootObject:self]; 7037 [ts release]; 7038 tsu = [[NSUnarchiver alloc] initForReadingWithData:data]; 7039 path = [[tsu decodeObject] retain]; 7040 [tsu release]; 7041 [data release]; 7042 7043 [path setFilled:NO]; 7044 [path setSelected:[self isSelected]]; 7045 7046 return path; 7047 } 7048 7049 pathContour = [[PathContour new] autorelease]; 7050 7051 return [pathContour contourPath:self width:w]; 7052} 7053 7054/* returns a flattened copy of path 7055 */ 7056/*- flattenedObject 7057{ VPath *newPath = [[self copy] autorelease]; 7058 NSMutableArray *plist; 7059 int i, cnt; 7060 7061 cnt = [list count]; 7062 plist = [NSMutableArray array]; 7063 for ( i=0; i<cnt; i++) 7064 { id fg, g = [list objectAtIndex:i]; 7065 7066 fg = [g flattenedObject]; 7067 if ( [fg isKindOfClass:[VPath class]] ) // copy list of fg to path list 7068 { int j; 7069 7070 for (j=0; j<[fg count]; j++) 7071 [plist addObject:[[fg list] objectAtIndex:j]]; 7072 } 7073 else if (fg) 7074 [plist addObject:fg]; 7075 } 7076 [newPath setList:plist]; 7077 [newPath setSelected:[self isSelected]]; 7078 7079 return newPath; 7080}*/ 7081 7082- (NSMutableArray*)getListOfObjectsSplittedFromGraphic:g 7083{ NSMutableArray *splitList = [NSMutableArray array], *spList = nil; 7084 int i, j, cnt = [list count]; 7085 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 7086 7087 /* tile each graphic from path with pArray 7088 * add splitted objects to splitList (else object) 7089 */ 7090 for (i=0; i<cnt; i++) 7091 { VGraphic *gr = [list objectAtIndex:i]; 7092 7093 spList = [gr getListOfObjectsSplittedFromGraphic:g]; 7094 if ( spList ) 7095 { for ( j=0; j<(int)[spList count]; j++ ) 7096 [splitList addObject:[spList objectAtIndex:j]]; 7097 } 7098 else 7099 [splitList addObject:[[gr copy] autorelease]]; 7100 } 7101 [pool release]; 7102 if ( [splitList count] > [list count] ) 7103 return splitList; 7104 return nil; 7105} 7106 7107- getListOfObjectsSplittedFrom:(NSPoint*)pArray :(int)iCnt 7108{ NSMutableArray *splitList = [NSMutableArray array], *spList = nil; 7109 int i, j, cnt = [list count]; 7110 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 7111 7112 /* tile each graphic from path with pArray 7113 * add splitted objects to splitList (else object) 7114 */ 7115 for (i=0; i<cnt; i++) 7116 { VGraphic *g = [list objectAtIndex:i]; 7117 7118 spList = [g getListOfObjectsSplittedFrom:pArray :iCnt]; 7119 if ( spList ) 7120 { for ( j=0; j<(int)[spList count]; j++ ) 7121 [splitList addObject:[spList objectAtIndex:j]]; 7122 } 7123 else 7124 [splitList addObject:[[g copy] autorelease]]; 7125 } 7126 [pool release]; 7127 if ( [splitList count] > [list count] ) 7128 return splitList; 7129 return nil; 7130} 7131 7132- (NSMutableArray*)getListOfObjectsSplittedAtPoint:(NSPoint)pt 7133{ NSMutableArray *splitList = [NSMutableArray array], *spList = nil; 7134 int i, cnt = [list count], splitI = -1; 7135 NSPoint cpt, start, end, sgStart, sgEnd; 7136 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 7137 float distance=MAXCOORD; 7138 VGraphic *splitg=nil; 7139 7140 cpt = [self nearestPointOnObject:&splitI distance:&distance toPoint:pt]; 7141 7142 start = [self pointWithNum:0]; 7143 end = [self pointWithNum:MAXINT]; 7144 if ( (Diff(start.x, cpt.x) < 100.0*TOLERANCE && Diff(start.y, cpt.y) < 100.0*TOLERANCE) || 7145 (Diff(end.x, cpt.x) < 100.0*TOLERANCE && Diff(end.y, cpt.y) < 100.0*TOLERANCE) ) 7146 { [pool release]; 7147 return nil; 7148 } 7149 splitg = [list objectAtIndex:splitI]; 7150 sgStart = [splitg pointWithNum:0]; 7151 sgEnd = [splitg pointWithNum:MAXINT]; 7152 if ((Diff(sgStart.x, cpt.x) > 100.0*TOLERANCE || Diff(sgStart.y, cpt.y) > 100.0*TOLERANCE) && 7153 (Diff(sgEnd.x, cpt.x) > 100.0*TOLERANCE || Diff(sgEnd.y, cpt.y) > 100.0*TOLERANCE)) 7154 spList = [splitg getListOfObjectsSplittedFrom:&cpt :1]; 7155 if (splitI == 0 && Diff(sgEnd.x, cpt.x) <= 100.0*TOLERANCE && Diff(sgEnd.y, cpt.y) <= 100.0*TOLERANCE) 7156 splitI = 1; // (splitI+1 < cnt) ? (splitI+1) : (0); 7157 7158 if (splitI != -1) 7159 { VPath *sPath = [VPath path]; 7160 VGraphic *gr; 7161 7162 [sPath setWidth:width]; 7163 [sPath setColor:color]; 7164 for (i=0; i<splitI; i++) 7165 { 7166 if ([[sPath list] count] > 0) 7167 { NSPoint pEnd, cBeg; 7168 7169 pEnd = [[[sPath list] objectAtIndex:[[sPath list] count]-1] pointWithNum:MAXINT]; 7170 cBeg = [[list objectAtIndex:i] pointWithNum:0]; 7171 if (SqrDistPoints(pEnd, cBeg) < (TOLERANCE*15.0)*(TOLERANCE*15.0)) 7172 [[sPath list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 7173 else 7174 { 7175 if ([[sPath list] count] == 1) 7176 [splitList addObject:[[sPath list] objectAtIndex:0]]; 7177 else 7178 [splitList addObject:sPath]; 7179 sPath = [VPath path]; 7180 [sPath setWidth:width]; 7181 [sPath setColor:color]; 7182 [[sPath list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 7183 } 7184 } 7185 else 7186 [[sPath list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 7187 } 7188 if ([spList count] > 1) 7189 { 7190 gr = [spList objectAtIndex:0]; 7191 if ([gr isKindOfClass:[VPath class]]) 7192 { int j, pCnt = [[(VPath*)gr list] count]; 7193 7194 if ([[sPath list] count] > 0) 7195 { NSPoint pEnd, cBeg; 7196 7197 pEnd = [[[sPath list] objectAtIndex:[[sPath list] count]-1] pointWithNum:MAXINT]; 7198 cBeg = [gr pointWithNum:0]; 7199 if (SqrDistPoints(pEnd, cBeg) < (TOLERANCE*15.0)*(TOLERANCE*15.0)) 7200 { 7201 for (j=1; j<pCnt; j++) 7202 [[sPath list] addObject:[[(VPath*)gr list] objectAtIndex:j]]; 7203 } 7204 else 7205 { if ([[sPath list] count] == 1) 7206 [splitList addObject:[[sPath list] objectAtIndex:0]]; 7207 else 7208 [splitList addObject:sPath]; 7209 sPath = [VPath path]; 7210 [sPath setWidth:width]; 7211 [sPath setColor:color]; 7212 for (j=1; j<pCnt; j++) 7213 [[sPath list] addObject:[[(VPath*)gr list] objectAtIndex:j]]; 7214 } 7215 } 7216 else 7217 for (j=1; j<pCnt; j++) 7218 [[sPath list] addObject:[[(VPath*)gr list] objectAtIndex:j]]; 7219 } 7220 else 7221 { 7222 if ([[sPath list] count] > 0) 7223 { NSPoint pEnd, cBeg; 7224 7225 pEnd = [[[sPath list] objectAtIndex:[[sPath list] count]-1] pointWithNum:MAXINT]; 7226 cBeg = [gr pointWithNum:0]; 7227 if (SqrDistPoints(pEnd, cBeg) < (TOLERANCE*15.0)*(TOLERANCE*15.0)) 7228 [[sPath list] addObject:gr]; 7229 else 7230 { if ([[sPath list] count] == 1) 7231 [splitList addObject:[[sPath list] objectAtIndex:0]]; 7232 else 7233 [splitList addObject:sPath]; 7234 sPath = [VPath path]; 7235 [sPath setWidth:width]; 7236 [sPath setColor:color]; 7237 [[sPath list] addObject:gr]; 7238 } 7239 } 7240 else 7241 [[sPath list] addObject:gr]; 7242 } 7243 } 7244 if ([[sPath list] count] == 1) // 360 Arc or Rectangle 7245 [splitList addObject:[[sPath list] objectAtIndex:0]]; 7246 else 7247 [splitList addObject:sPath]; 7248 7249 7250 sPath = [VPath path]; 7251 [sPath setWidth:width]; 7252 [sPath setColor:color]; 7253 if ([spList count] > 1) 7254 { 7255 gr = [spList objectAtIndex:1]; 7256 if ([gr isKindOfClass:[VPath class]]) 7257 { int j, pCnt = [[(VPath*)gr list] count]; 7258 7259 for (j=1; j<pCnt; j++) 7260 [[sPath list] addObject:[[(VPath*)gr list] objectAtIndex:j]]; 7261 } 7262 else 7263 [[sPath list] addObject:gr]; 7264 } 7265 for (i=(([spList count] > 1) ? splitI+1 : splitI); i<cnt; i++) 7266 { 7267 if ([[sPath list] count] > 0) 7268 { NSPoint pEnd, cBeg; 7269 7270 pEnd = [[[sPath list] objectAtIndex:[[sPath list] count]-1] pointWithNum:MAXINT]; 7271 cBeg = [[list objectAtIndex:i] pointWithNum:0]; 7272 if (SqrDistPoints(pEnd, cBeg) < (TOLERANCE*15.0)*(TOLERANCE*15.0)) 7273 [[sPath list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 7274 else 7275 { 7276 if ([[sPath list] count] == 1) 7277 [splitList addObject:[[sPath list] objectAtIndex:0]]; 7278 else 7279 [splitList addObject:sPath]; 7280 sPath = [VPath path]; 7281 [sPath setWidth:width]; 7282 [sPath setColor:color]; 7283 [[sPath list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 7284 } 7285 } 7286 else 7287 [[sPath list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 7288 } 7289 if ([[sPath list] count] == 1) // 360 Arc or Rectangle 7290 [splitList addObject:[[sPath list] objectAtIndex:0]]; 7291 else 7292 [splitList addObject:sPath]; 7293 } 7294 [pool release]; 7295 if ([splitList count]) 7296 return splitList; 7297 return nil; 7298} 7299 7300- (BOOL)intersects:g 7301{ NSPoint *pts; 7302 7303 if ( [self getIntersections:&pts with:g] ) 7304 { free(pts); 7305 return YES; 7306 } 7307 return NO; 7308} 7309- (int)getIntersections:(NSPoint**)ppArray with:g 7310{ int i, j, iCnt = 0; 7311 int len = Min(100, [self numPoints]); 7312 NSPoint *pts = NULL; 7313 //NSMutableData *data = [NSMutableData dataWithLength:([list count]*9) * sizeof(NSPoint)]; 7314 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 7315 7316 //*ppArray = [data mutableBytes]; 7317 *ppArray = malloc(len * sizeof(NSPoint)); 7318 //*ppArray = NSZoneMalloc((NSZone*)[(NSObject*)NSApp zone], len * sizeof(NSPoint)); 7319 for (i=[list count]-1; i>=0; i--) 7320 { id gp = [list objectAtIndex:i]; 7321 int cnt, oldCnt = iCnt; 7322 7323 if ( gp == g ) 7324 continue; 7325 7326 cnt = [gp getIntersections:&pts with:g]; /* line, arc, curve, rectangle */ 7327 if (iCnt+cnt >= len) 7328 { //[data increaseLengthBy:cnt]; 7329 //*ppArray = [data mutableBytes]; 7330 *ppArray = realloc(*ppArray, (len+=cnt*2) * sizeof(NSPoint)); 7331 //*ppArray = NSZoneRealloc((NSZone*)[(NSObject*)NSApp zone], *ppArray, (len+=cnt) * sizeof(NSPoint)); 7332 } 7333 for (j=0; j<cnt; j++) 7334 { 7335 if ( !pointInArray(pts[j], *ppArray, oldCnt) ) 7336 (*ppArray)[iCnt++] = pts[j]; 7337 else 7338 { NSPoint start, end; 7339 7340 if ( [gp isKindOfClass:[VLine class]] ) /* line */ 7341 [(VLine*)gp getVertices:&start :&end]; 7342 else if ( [gp isKindOfClass:[VArc class]] || [gp isKindOfClass:[VCurve class]] ) 7343 { start = [gp pointWithNum:0]; 7344 end = [gp pointWithNum:MAXINT]; 7345 } 7346 else if ( [gp isKindOfClass:[VRectangle class]] ) 7347 { NSPoint ur, ul, size; 7348 [(VRectangle*)gp getVertices:&start :&size]; // origin size 7349 end = start; end.x += size.x; 7350 ul = start; ul.y += size.y; 7351 ur = end; ur.y += size.y; 7352 if ( (Diff(pts[j].x, ul.x) + Diff(pts[j].y, ul.y) < 10.0*TOLERANCE) || 7353 (Diff(pts[j].x, ur.x) + Diff(pts[j].y, ur.y) < 10.0*TOLERANCE) ) 7354 continue; // do not add 7355 } 7356 else if ( [gp isKindOfClass:[VPolyLine class]] ) 7357 { int k, pCnt = [(VPolyLine*)gp ptsCount], stop = 0; 7358 7359 for (k=1; k<pCnt-1; k++) 7360 { NSPoint pt = [(VPolyLine*)gp pointWithNum:k]; 7361 if ( Diff(pts[j].x, pt.x) + Diff(pts[j].y, pt.y) < 10.0*TOLERANCE ) 7362 { stop = 1; break; } 7363 } 7364 if (stop) 7365 continue; // do not add 7366 [(VPolyLine*)gp getEndPoints:&start :&end]; 7367 } 7368 else 7369 { 7370 start.x = end.x = pts[j].x; start.y = end.y = pts[j].y; 7371 } 7372 /* point is no edge point of gp -> add */ 7373 if ( (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) > 10.0*TOLERANCE) && 7374 (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) > 10.0*TOLERANCE) ) 7375 (*ppArray)[iCnt++] = pts[j]; 7376 } 7377 } 7378 if (pts) 7379 free(pts); 7380 } 7381 7382 if (!iCnt) 7383 { free(*ppArray); 7384 //NSZoneFree((NSZone*)[(NSObject*)NSApp zone], *ppArray); 7385 *ppArray = NULL; 7386 } 7387 [pool release]; 7388 return iCnt; 7389} 7390#if 0 7391 7392/* 7393 * FIXME: better intersection function - should be used for point in polygon test 7394 * 7395 * schiebt start/end punkte von horizontaler linie weg 7396 */ 7397- (int)getIntersectionsForFilling:(NSPoint**)ppArray with:g 7398{ int i, j, iCnt = 0; 7399 int len = Min(100, [self numPoints]); 7400 NSPoint *pts = NULL, ls, le; 7401 7402 *ppArray = malloc(len * sizeof(NSPoint)); 7403 //*ppArray = NSZoneMalloc((NSZone*)[(NSObject*)NSApp zone], len * sizeof(NSPoint)); 7404 7405 /* g is allways a line here */ 7406 [g getVertices:&ls :&le]; 7407 7408 for (i=[list count]-1; i>=0; i--) 7409 { id gp = [list objectAtIndex:i], tgp = nil; 7410 int cnt = 0, oldCnt = iCnt; 7411 float d=0.0; 7412 7413 if ( gp == g ) 7414 continue; 7415 7416 /* here we must move the s/e pts of gp far away from line */ 7417 if ( [gp isKindOfClass:[VLine class]] ) 7418 { NSPoint p0, p1; 7419 7420 [(VLine*)gp getVertices:&p0 :&p1]; 7421 d = p0.y - ls.y; 7422 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7423 p0.y += 3.0*TOLERANCE; 7424 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7425 p0.y -= 3.0*TOLERANCE; 7426 d = p1.y - ls.y; 7427 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7428 p1.y += 3.0*TOLERANCE; 7429 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7430 p1.y -= 3.0*TOLERANCE; 7431 7432 tgp = [[VLine allocWithZone:[self zone]] init]; 7433 [tgp setVertices:p0 :p1]; 7434 } 7435 else if ( [gp isKindOfClass:[VCurve class]] ) 7436 { NSPoint p0, p1, p2, p3; 7437 7438 [(VCurve*)gp getVertices:&p0 :&p1 :&p2 :&p3]; 7439 d = p0.y - ls.y; 7440 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7441 { p0.y += 3.0*TOLERANCE; 7442 p1.y += 3.0*TOLERANCE; 7443 } 7444 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7445 { p0.y -= 3.0*TOLERANCE; 7446 p1.y -= 3.0*TOLERANCE; 7447 } 7448 d = p3.y - ls.y; 7449 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7450 { p3.y += 3.0*TOLERANCE; 7451 p2.y += 3.0*TOLERANCE; 7452 } 7453 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7454 { p3.y -= 3.0*TOLERANCE; 7455 p2.y -= 3.0*TOLERANCE; 7456 } 7457 tgp = [[VCurve allocWithZone:[self zone]] init]; 7458 [(VCurve*)tgp setVertices:p0 :p1 :p2 :p3]; 7459 } 7460 else if ( [gp isKindOfClass:[VRectangle class]] ) 7461 { NSPoint o, s; 7462 7463 [(VRectangle*)gp getVertices:&o :&s]; 7464 d = o.y - ls.y; 7465 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7466 o.y += 3.0*TOLERANCE; 7467 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7468 o.y -= 3.0*TOLERANCE; 7469 d = (o.y+s.y) - ls.y; // upper line - Fix me: - - - - rotation not checked ! 7470 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7471 o.y += 3.0*TOLERANCE; // we move also the origin 7472 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7473 o.y -= 3.0*TOLERANCE; 7474 tgp = [[VRectangle allocWithZone:[self zone]] init]; 7475 [(VRectangle*)tgp setVertices:o :s]; 7476 } 7477 else if ( [gp isKindOfClass:[VArc class]] ) 7478 { NSPoint p0=[(VArc*)gp pointWithNum:0], p1=[(VArc*)gp pointWithNum:MAXINT]; 7479 7480 tgp = [(VArc*)gp copy]; 7481 7482 d = p0.y - ls.y; 7483 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7484 [tgp movePoint:0 by:NSMakePoint(0.0, 3.0*TOLERANCE)]; 7485 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7486 [tgp movePoint:0 by:NSMakePoint(0.0, -3.0*TOLERANCE)]; 7487 d = p1.y - ls.y; 7488 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7489 [tgp movePoint:MAXINT by:NSMakePoint(0.0, 3.0*TOLERANCE)]; 7490 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7491 [tgp movePoint:MAXINT by:NSMakePoint(0.0, -3.0*TOLERANCE)]; 7492 } 7493 else if ( [gp isKindOfClass:[VPolyLine class]] ) 7494 { int pCnt = [gp ptsCount]; 7495 7496 cnt = 0; 7497 pts = malloc(pCnt * sizeof(NSPoint)); 7498 7499 /* each line by itself */ 7500 for (j=0; j < pCnt-1; j++) 7501 { NSPoint pl0 = [gp pointWithNum:j], pl1 = [gp pointWithNum:j+1], pt; 7502 7503 /* we move the points away from our intersecting line, so we don't hit an edge */ 7504 d = pl0.y - ls.y; 7505 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7506 pl0.y += 3.0*TOLERANCE; 7507 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7508 pl0.y -= 3.0*TOLERANCE; 7509 d = pl1.y - ls.y; 7510 if (d >= 0.0 && d <= 3.0*TOLERANCE) 7511 pl1.y += 3.0*TOLERANCE; 7512 if (d < 0.0 && -d <= 3.0*TOLERANCE) 7513 pl1.y -= 3.0*TOLERANCE; 7514 7515 if (vhfIntersectLines(&pt, ls, le, pl0, pl1)) 7516 { 7517 pts[cnt++] = pt; 7518if (cnt >= pCnt) 7519 NSLog(@"'VPath.m Fuck"); 7520 } 7521 } 7522 } 7523 7524 if ( ![gp isKindOfClass:[VPolyLine class]] ) 7525 cnt = [tgp getIntersections:&pts with:g]; /* line, arc, curve, rectangle */ 7526 7527 if (iCnt+cnt >= len) 7528 *ppArray = realloc(*ppArray, (len+=cnt*2) * sizeof(NSPoint)); 7529 7530 for (j=0; j<cnt; j++) 7531 { 7532 if ( !pointInArray(pts[j], *ppArray, oldCnt) ) 7533 (*ppArray)[iCnt++] = pts[j]; 7534 7535 /* we need this allways - RubOut z.B. kann aufeinanderliegende linien erzeugen ! */ 7536 else 7537 { NSPoint start, end; 7538 7539 if ( [gp isKindOfClass:[VLine class]] ) /* line */ 7540 [(VLine*)gp getVertices:&start :&end]; 7541 else if ( [gp isKindOfClass:[VArc class]] || [gp isKindOfClass:[VCurve class]] ) 7542 { start = [gp pointWithNum:0]; 7543 end = [gp pointWithNum:MAXINT]; 7544 } 7545 else if ( [gp isKindOfClass:[VRectangle class]] ) 7546 { NSPoint ur, ul, size; 7547 [(VRectangle*)gp getVertices:&start :&size]; // origin size 7548 end = start; end.x += size.x; 7549 ul = start; ul.y += size.y; 7550 ur = end; ur.y += size.y; 7551 if ( (Diff(pts[j].x, ul.x) + Diff(pts[j].y, ul.y) < 10.0*TOLERANCE) || 7552 (Diff(pts[j].x, ur.x) + Diff(pts[j].y, ur.y) < 10.0*TOLERANCE) ) 7553 continue; // do not add 7554 } 7555 else if ( [gp isKindOfClass:[VPolyLine class]] ) 7556 { int k, pCnt = [(VPolyLine*)gp ptsCount], stop = 0; 7557 7558 for (k=1; k<pCnt-1; k++) 7559 { NSPoint pt = [(VPolyLine*)gp pointWithNum:k]; 7560 if ( Diff(pts[j].x, pt.x) + Diff(pts[j].y, pt.y) < 10.0*TOLERANCE ) 7561 { stop = 1; break; } 7562 } 7563 if (stop) 7564 continue; // do not add 7565 [(VPolyLine*)gp getEndPoints:&start :&end]; 7566 } 7567 else 7568 { 7569 start.x = end.x = pts[j].x; start.y = end.y = pts[j].y; 7570 } 7571 /* point is no edge point of gp -> add */ 7572 if ( (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) > 10.0*TOLERANCE) && 7573 (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) > 10.0*TOLERANCE) ) 7574 (*ppArray)[iCnt++] = pts[j]; 7575// else 7576// NSLog(@"VPath.m -getIntersectionsForFilling: point not added !!"); 7577 } 7578 } 7579 if ( tgp ) 7580 [tgp release]; 7581 if ( cnt ) 7582 free(pts); 7583 pts = 0; 7584 } 7585 7586 if (!iCnt) 7587 { free(*ppArray); 7588 //NSZoneFree((NSZone*)[(NSObject*)NSApp zone], *ppArray); 7589 *ppArray = NULL; 7590 } 7591 return iCnt; 7592} 7593#endif 7594 7595/* get intersections with line segment 7596 */ 7597- (int)intersectLine:(NSPoint*)pArray :(NSPoint)pl0 :(NSPoint)pl1 7598{ NSPoint *pts; 7599 VLine *line = [VLine lineWithPoints:pl0 :pl1]; 7600 int cnt; 7601 7602 if ( (cnt = [self getIntersections:&pts with:line]) ) 7603 { free(pts); 7604 return cnt; 7605 } 7606 return 0; 7607} 7608 7609#define INFO_OK 0 7610#define INFO_HORICONTAL_UP 1 7611#define INFO_HORICONTAL_DOWN 2 7612#define INFO_TANGENT 3 7613#define INFO_EDGE_UP 4 7614#define INFO_EDGE_DOWN 5 7615 7616#if 0 // new 7617- (int)intersectionsForPtInside:(NSPoint**)ppArray :(int**)ppInfo with:g 7618{ int i, j, listCnt = [list count], iCnt = 0, horicontals = 0; 7619 NSPoint p0 = [g pointWithNum:0]; // allway a line ! 7620 NSRect gBounds = [g bounds]; 7621 7622 *ppArray = malloc([self numPoints] * sizeof(NSPoint)); 7623 *ppInfo = malloc([self numPoints] * sizeof(int)); 7624 for (i=0; i<listCnt; i++) 7625 { id gp = [list objectAtIndex:i]; 7626 int cnt, oldCnt = iCnt; 7627 NSRect gpBounds = [gp bounds]; 7628 NSPoint *pts = NULL; 7629 7630 if ( gp == g ) 7631 continue; 7632 // check bounds 7633 if ( NSIsEmptyRect(NSIntersectionRect(gBounds , gpBounds)) ) 7634 continue; 7635 7636 cnt = [gp getIntersections:&pts with:g]; // line, arc, curve, rectangle 7637 if ( cnt == 2 && 7638 ( [gp isKindOfClass:[VLine class]] || 7639 ([gp isKindOfClass:[VArc class]] && pts[0].x == pts[1].x) ) ) 7640 { int info0 = INFO_HORICONTAL_UP, info1 = INFO_HORICONTAL_UP; 7641 7642 /* if there are two intersections with horicontal line the line of the polygon is allso horicontal */ 7643 /* or an arc tangent */ 7644 if ([gp isKindOfClass:[VLine class]]) 7645 { VGraphic *prevG = nil, *nextG = nil; 7646 NSPoint start, end, s, e; 7647 int k, l; 7648 7649 if (pointInArray(pts[0], *ppArray, oldCnt)) 7650 { /* remove all edge points */ 7651 for (k=0; k<oldCnt; k++) 7652 if ( (*ppInfo)[k] >= INFO_EDGE_UP && 7653 SqrDistPoints(pts[0], (*ppArray)[k]) < (10.0*TOLERANCE)*(10.0*TOLERANCE) ) 7654 { 7655 for (l=k; l<oldCnt-1; l++) 7656 { (*ppArray)[l] = (*ppArray)[l+1]; 7657 (*ppInfo)[l] = (*ppInfo)[l+1]; 7658 } 7659 oldCnt--; k--; iCnt--; 7660 } 7661 } 7662 if (pointInArray(pts[1], *ppArray, oldCnt)) 7663 { /* remove all edge points */ 7664 for (k=0; k<oldCnt; k++) 7665 if ( (*ppInfo)[k] >= INFO_EDGE_UP && 7666 SqrDistPoints(pts[1], (*ppArray)[k]) < (10.0*TOLERANCE)*(10.0*TOLERANCE) ) 7667 { 7668 for (l=k; l<oldCnt-1; l++) 7669 { (*ppArray)[l] = (*ppArray)[l+1]; 7670 (*ppInfo)[l] = (*ppInfo)[l+1]; 7671 } 7672 oldCnt--; k--; iCnt--; 7673 } 7674 } 7675 /* search prevG/nextG */ 7676 prevG = nextG = nil; 7677 [(VLine*)gp getVertices:&start :&end]; 7678 for (k=0; k<listCnt;k++) 7679 { VGraphic *gr = [list objectAtIndex:k]; 7680 7681 if (k == i) 7682 continue; 7683 7684 s = [gr pointWithNum:0]; 7685 e = [gr pointWithNum:MAXINT]; 7686 if (!prevG && SqrDistPoints(e, start) <= TOLERANCE) // prevG found 7687 prevG = gr; 7688 if (!nextG && SqrDistPoints(s, end) <= TOLERANCE) // nextG found 7689 nextG = gr; 7690 if (prevG && nextG) 7691 break; 7692 } 7693 /* check if prevG/nextG come from up and/or down */ 7694 if ( [prevG isKindOfClass:[VLine class]] ) /* prevG is a line */ 7695 { [(VLine*)prevG getVertices:&start :&end]; // horicontals are not down ! 7696 info0 = (start.y < pts[0].y - TOLERANCE) ? INFO_HORICONTAL_DOWN : INFO_HORICONTAL_UP; 7697 } 7698 else if ( [prevG isKindOfClass:[VArc class]] || [prevG isKindOfClass:[VCurve class]] ) 7699 { start = [prevG pointAt:0.85]; 7700 info0 = (start.y < pts[0].y) ? INFO_HORICONTAL_DOWN : INFO_HORICONTAL_UP; 7701 } 7702 else if ( [prevG isKindOfClass:[VPolyLine class]] ) 7703 { int pCnt = [(VPolyLine*)prevG ptsCount]; 7704 NSPoint pt = [(VPolyLine*)prevG pointWithNum:pCnt-2]; 7705 7706 info0 = (pt.y < pts[0].y - TOLERANCE) ? INFO_HORICONTAL_DOWN : INFO_HORICONTAL_UP; 7707 } 7708 if ( [nextG isKindOfClass:[VLine class]] ) /* nextG is a line */ 7709 { [(VLine*)nextG getVertices:&start :&end]; // horicontals are not down ! 7710 info1 = (end.y < pts[0].y - 2.0*TOLERANCE) ? INFO_HORICONTAL_DOWN : INFO_HORICONTAL_UP; 7711 } 7712 else if ( [nextG isKindOfClass:[VArc class]] || [nextG isKindOfClass:[VCurve class]] ) 7713 { end = [nextG pointAt:0.15]; 7714 info1 = (end.y < pts[0].y) ? INFO_HORICONTAL_DOWN : INFO_HORICONTAL_UP; 7715 } 7716 else if ( [nextG isKindOfClass:[VPolyLine class]] ) 7717 { NSPoint pt = [(VPolyLine*)nextG pointWithNum:1]; 7718 7719 info1 = (pt.y < pts[0].y - TOLERANCE) ? INFO_HORICONTAL_DOWN : INFO_HORICONTAL_UP; 7720 } 7721 if (info0 != info1) 7722 horicontals++; 7723 } 7724 (*ppArray)[iCnt] = pts[0]; 7725 (*ppInfo)[iCnt++] = ([gp isKindOfClass:[VArc class]]) ? INFO_TANGENT : info0; 7726 (*ppArray)[iCnt] = pts[1]; 7727 (*ppInfo)[iCnt++] = ([gp isKindOfClass:[VArc class]]) ? INFO_TANGENT : info1; 7728 if (pts) 7729 free(pts); 7730 continue; 7731 } 7732 else if ( [gp isKindOfClass:[VRectangle class]] && cnt == 2 ) 7733 { /* if one intersectionpoint layes on the uppest OR lowest y value of the rectangle 7734 * -> -1 !!! (horicontal...) 7735 */ 7736 for (j=0; j<cnt; j++) 7737 { 7738 if ( (Diff(pts[j].y, gpBounds.origin.y) <= TOLERANCE) || 7739 (Diff(pts[j].y, (gpBounds.origin.y+gpBounds.size.height)) <= TOLERANCE) ) 7740 { 7741 if ( cnt > 1 ) 7742 { 7743 (*ppArray)[iCnt].x = gpBounds.origin.x; 7744 (*ppArray)[iCnt].y = pts[j].y; 7745 (*ppInfo)[iCnt++] = INFO_HORICONTAL_DOWN; 7746 (*ppArray)[iCnt].x = gpBounds.origin.x + gpBounds.size.width; 7747 (*ppArray)[iCnt].y = pts[j].y; 7748 (*ppInfo)[iCnt++] = INFO_HORICONTAL_DOWN; 7749 } 7750 else 7751 { (*ppArray)[iCnt] = pts[j]; 7752 (*ppInfo)[iCnt++] = INFO_HORICONTAL_DOWN; 7753 (*ppArray)[iCnt] = pts[j]; 7754 (*ppInfo)[iCnt++] = INFO_HORICONTAL_DOWN; 7755 } 7756 free(pts); 7757 continue; 7758 } 7759 } 7760 } 7761 else if ( [gp isKindOfClass:[VCurve class]] && cnt ) 7762 { NSPoint p0, p1, p2, p3, tpoints[3]; 7763 int i, cpt, realSol=0, numSol=0, stop = 0; 7764 double cy, by, ay, t[3]; 7765 7766 [gp getVertices:&p0 :&p1 :&p2 :&p3]; 7767 /* we must look if one of the intersection points lying on a extrem point of the curve 7768 * represent the curve with the equations 7769 * x(t) = ax*t^3 + bx*t^2 + cx*t + x(0) 7770 * y(t) = ay*t^3 + by*t^2 + cy*t + y(0) 7771 * -> 3ay*t^2 + 2by*t + cy = 0 7772 */ 7773 cy = 3*(p1.y - p0.y); 7774 by = 3*(p2.y - p1.y) - cy; 7775 ay = p3.y - p0.y - by - cy; 7776 7777 /* get the ts in which the tangente is horicontal 7778 */ 7779 numSol = svPolynomial2( 3.0*ay, 2.0*by, cy, t); 7780 7781 /* when t is on curve */ 7782 realSol=0; 7783 for ( i=0 ; i<numSol ; i++) 7784 if ( t[i] >= 0.0 && t[i] <= 1.0 ) 7785 tpoints[realSol++] = [gp pointAt:t[i]]; 7786 7787 /* if intersection point is a tangent point */ 7788 for ( i=0 ; i<realSol ;i++ ) 7789 { 7790 for ( cpt=0 ; cpt<cnt ; cpt++ ) 7791 if ( Diff(tpoints[i].x, pts[cpt].x) <= 25.0*TOLERANCE && 7792 Diff(tpoints[i].y, pts[cpt].y) <= 25.0*TOLERANCE) 7793 { 7794 (*ppArray)[iCnt] = pts[cpt]; 7795 (*ppInfo)[iCnt++] = INFO_TANGENT; 7796 (*ppArray)[iCnt] = pts[cpt]; 7797 (*ppInfo)[iCnt++] = INFO_TANGENT; 7798 if (cnt == 1) 7799 { free(pts); 7800 stop = 1; 7801 break; 7802 } 7803 else 7804 { for (j = cpt; j<cnt-1; j++) 7805 pts[j] = pts[j+1]; 7806 cnt--; cpt--; 7807 } 7808 } 7809 if (stop) 7810 break; 7811 } 7812 if (stop) 7813 { free(pts); 7814 continue; 7815 } 7816 } 7817 // polyline ? 7818 else if ( [gp isKindOfClass:[VPolyLine class]] && cnt ) 7819 NSLog(@"VPath.m - intersectionsForPtInside::with:: VPolyLine not implemented"); 7820 7821 /* add points if not allways inside pparray 7822 * else check if pt is edge pt of graphic 7823 */ 7824 for (j=0; j<cnt; j++) 7825 { NSPoint start, end; 7826 BOOL edgePoint = NO, edgeInfo = NO; 7827 7828 /* check if edge point of gp */ 7829 /* and check if gp laying up or down the graphic */ 7830 if ( [gp isKindOfClass:[VLine class]] ) /* line */ 7831 { [(VLine*)gp getVertices:&start :&end]; 7832 if (((Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) < 10.0*TOLERANCE) && 7833 (start.y > pts[j].y /* + 2.0*TOLERANCE */)) || 7834 ((Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) < 10.0*TOLERANCE) && 7835 (end.y > pts[j].y /* + 2.0*TOLERANCE */))) 7836 { edgePoint = YES; edgeInfo = INFO_EDGE_UP; } 7837 else if (((Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) < 10.0*TOLERANCE) && 7838 (start.y < pts[j].y /* - 2.0*TOLERANCE */)) || 7839 ((Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) < 10.0*TOLERANCE) && 7840 (end.y < pts[j].y /* - 2.0*TOLERANCE */))) 7841 { edgePoint = YES; edgeInfo = INFO_EDGE_DOWN; } 7842 else if ((Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) < 10.0*TOLERANCE) || 7843 (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) < 10.0*TOLERANCE)) 7844 NSLog(@"VPath.m -intersectionsForPtInside::with:: line is not up or down ??"); 7845 } 7846 else if ( [gp isKindOfClass:[VArc class]] || [gp isKindOfClass:[VCurve class]] ) 7847 { start = [gp pointWithNum:0]; 7848 end = [gp pointWithNum:MAXINT]; 7849 7850 if ((Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) < 10.0*TOLERANCE) || 7851 (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) < 10.0*TOLERANCE)) 7852 { NSPoint p12 = {0,0}; 7853 7854 edgePoint = YES; 7855 if ((Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) < 10.0*TOLERANCE)) 7856 p12 = [gp pointAt:0.15]; 7857 else 7858 p12 = [gp pointAt:0.85]; 7859 edgeInfo = (p12.y > pts[j].y) ? INFO_EDGE_UP : INFO_EDGE_DOWN; 7860 } 7861 } 7862 else if ( [gp isKindOfClass:[VRectangle class]] ) 7863 { NSPoint ur, ul, size; 7864 [(VRectangle*)gp getVertices:&start :&size]; // origin size 7865 end = start; end.x += size.x; 7866 ul = start; ul.y += size.y; 7867 ur = end; ur.y += size.y; 7868 if ( (Diff(pts[j].x, ul.x) + Diff(pts[j].y, ul.y) < 10.0*TOLERANCE) || 7869 (Diff(pts[j].x, ur.x) + Diff(pts[j].y, ur.y) < 10.0*TOLERANCE) ) 7870 { edgePoint = YES; 7871 edgeInfo = INFO_EDGE_DOWN; 7872 } 7873 else if ( (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) < 10.0*TOLERANCE) && 7874 (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) < 10.0*TOLERANCE) ) 7875 { edgePoint = YES; 7876 edgeInfo = INFO_EDGE_UP; 7877 } 7878 } 7879 else if ( [gp isKindOfClass:[VPolyLine class]] ) 7880 { int k, pCnt = [(VPolyLine*)gp ptsCount]; 7881 NSPoint pt = [(VPolyLine*)gp pointWithNum:1]; 7882 7883 for (k=1; k<pCnt-1; k++) 7884 { NSPoint pt = [(VPolyLine*)gp pointWithNum:k]; 7885 if ( Diff(pts[j].x, pt.x) + Diff(pts[j].y, pt.y) < 10.0*TOLERANCE ) 7886 { NSPoint pm1 = [(VPolyLine*)gp pointWithNum:(((k-1) < 0)?(pCnt-1):(k-1))]; 7887 NSPoint pp1 = [(VPolyLine*)gp pointWithNum:(((k+1) < pCnt) ? (k+1):(0))]; 7888 7889 if ((pm1.y > pts[j].y /* + 2.0*TOLERANCE */ && pp1.y > pts[j].y + 2.0*TOLERANCE) || 7890 (pm1.y < pts[j].y /* - 2.0*TOLERANCE */ && pp1.y < pts[j].y - 2.0*TOLERANCE)) 7891 { edgePoint = YES; break; } 7892 } 7893 } 7894 if (edgePoint == YES) 7895 continue; // do not add !!!!!!!!!!!!!! 7896 7897 pt = [(VPolyLine*)gp pointWithNum:1]; 7898 [(VPolyLine*)gp getEndPoints:&start :&end]; 7899 if (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) < 10.0*TOLERANCE) 7900 { edgePoint = YES; 7901 edgeInfo = (pt.y > pts[j].y /* + 2.0*TOLERANCE */) ? INFO_EDGE_UP : INFO_EDGE_DOWN; 7902 } 7903 if (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) < 10.0*TOLERANCE) 7904 { edgePoint = YES; 7905 pt = [(VPolyLine*)gp pointWithNum:pCnt-2]; 7906 edgeInfo = (pt.y > pts[j].y /* + 2.0*TOLERANCE */) ? INFO_EDGE_UP : INFO_EDGE_DOWN; 7907 } 7908 } 7909 else 7910 NSLog(@"VPath.m -intersectionsForPtInside::with:: unknown graphic "); 7911 7912 /* point is not in array OR no edge point of gp -> add */ 7913 if ( !pointInArray(pts[j], *ppArray, oldCnt) || edgePoint == NO ) 7914 { (*ppArray)[iCnt] = pts[j]; 7915 (*ppInfo)[iCnt++] = (edgePoint == YES) ? edgeInfo : INFO_OK; 7916 } 7917 else if (edgePoint == YES) // check if prevG/curG(gp) up end down the line 7918 { int k, l; 7919 7920 for (k=0; k<oldCnt; k++) 7921 if ( SqrDistPoints(pts[j], (*ppArray)[k]) < (10.0*TOLERANCE)*(10.0*TOLERANCE) ) 7922 { 7923 /* up and down - do not add second point */ 7924 if (((*ppInfo)[k] == INFO_EDGE_UP && edgeInfo == INFO_EDGE_DOWN) || 7925 ((*ppInfo)[k] == INFO_EDGE_DOWN && edgeInfo == INFO_EDGE_UP)) 7926 continue; 7927 else if ((*ppInfo)[k] >= INFO_EDGE_UP) // remove only edge points from ppArray !! 7928 { /* only up or on/down remove allso other edge from point array - but horicontal points ! */ 7929 for (l=k; l<oldCnt-1; l++) 7930 { (*ppArray)[l] = (*ppArray)[l+1]; 7931 (*ppInfo)[l] = (*ppInfo)[l+1]; 7932 } 7933 oldCnt--; k--; iCnt--; 7934 } 7935 } 7936 } 7937 } 7938 if (pts) 7939 free(pts); 7940 } 7941 /* sort points from left to right */ 7942 for (i=0; i<iCnt-1; i++) 7943 { int j, jMin, info; 7944 float lastDist, newDist; 7945 NSPoint p; 7946 7947 jMin = iCnt; 7948 lastDist=SqrDistPoints((*ppArray)[i], p0); 7949 for (j=i+1; j<iCnt; j++) 7950 { 7951 if ((newDist=SqrDistPoints((*ppArray)[j], p0)) < lastDist) 7952 { lastDist = newDist; 7953 jMin = j; 7954 } 7955 } 7956 if (jMin<iCnt) 7957 { p = (*ppArray)[i]; 7958 info = (*ppInfo)[i]; 7959 (*ppArray)[i] = (*ppArray)[jMin]; 7960 (*ppInfo)[i] = (*ppInfo)[jMin]; 7961 (*ppArray)[jMin] = p; 7962 (*ppInfo)[jMin] = info; 7963 } 7964 } 7965 7966 if ((Even(horicontals) && !Even(iCnt)) || (!Even(horicontals) && Even(iCnt))) 7967 NSLog(@"VPath.m -intersectionsForPtInside:.with: one point less; y: %.3f, cnt: %d, hs: %d", p0.y, iCnt, horicontals); 7968 7969 if (!iCnt) 7970 { free(*ppArray); *ppArray = NULL; 7971 free(*ppInfo); *ppInfo = NULL; 7972 } 7973 return iCnt; 7974} 7975#endif 7976 7977/* return -1 if we hit a horicontal graphic or tangential point 7978 * return -2 if pt is ON a horicontal graphic or ON the tangential point 7979 */ 7980- (int)intersectionsForPtInside:(NSPoint**)ppArray with:g :(NSPoint)pt 7981{ int i, j, iCnt = 0, ptsCnt = Min(100, [self numPoints]); 7982 NSRect gBounds = [g bounds]; 7983 BOOL tangential = NO; 7984 7985 *ppArray = malloc(ptsCnt * sizeof(NSPoint)); 7986 for (i=[list count]-1; i>=0; i--) 7987 { id gp = [list objectAtIndex:i]; 7988 int cnt, oldCnt = iCnt; 7989 NSRect gpBounds = [gp bounds]; 7990 NSPoint *pts = NULL; 7991 7992 if ( gp == g ) 7993 continue; 7994 // check bounds 7995 if ( NSIsEmptyRect(NSIntersectionRect(gBounds, gpBounds)) ) 7996 continue; 7997 7998 cnt = [gp getIntersections:&pts with:g]; // line, arc, curve, rectangle 7999 if ( cnt == 2 && 8000 ( [gp isKindOfClass:[VLine class]] || 8001 ([gp isKindOfClass:[VArc class]] && 8002 (pts[0].x == pts[1].x || 8003 ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x)))) ) ) 8004 { 8005 /* if there are two intersections with horicontal line the line of the polygon is allso horicontal */ 8006 /* or an arc tangent */ 8007 if ([gp isKindOfClass:[VLine class]] || ([gp isKindOfClass:[VArc class]] && pts[0].x == pts[1].x)) 8008 { tangential = YES; 8009 (*ppArray)[0] = pts[0]; 8010 (*ppArray)[1] = pts[1]; 8011 } 8012 if ([gp isKindOfClass:[VLine class]] && 8013 ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x))) 8014 { free(pts); 8015 return -2; // on 8016 } 8017 else if ( pts[0].x != pts[1].x && 8018 ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x)) ) 8019 { NSPoint p0 = [g pointWithNum:0], p1 = [g pointWithNum:MAXINT], *aPts; 8020 int aCnt = 0; 8021 8022 /* we have made a sidestep */ 8023 if (Diff(pt.y, p0.y) > TOLERANCE/2.0) 8024 { VLine *line = [VLine line]; 8025 8026 p0.y = p1.y = pt.y; 8027 [line setVertices:p0 :p1]; 8028 if (!(aCnt = [line getIntersections:&aPts with:gp]) || aCnt == 2) 8029 { 8030 if (!aCnt || aPts[0].x == aPts[1].x) 8031 { 8032 /* not inside !! */ 8033 free(pts); 8034 free(aPts); 8035 return -1; // need a sidestep to other side ! 8036 } 8037 } 8038 } 8039 if (aCnt) 8040 free(aPts); 8041 } 8042 else if ([gp isKindOfClass:[VArc class]] && Diff(pt.x, pts[0].x) <= 5.0*TOLERANCE) 8043 { free(pts); 8044 return -2; // on 8045 } 8046 else if ([gp isKindOfClass:[VArc class]]) 8047 { NSPoint tpt = pts[0], p0 = [g pointWithNum:0], p1 = [g pointWithNum:MAXINT], *aPts; 8048 VLine *line = [VLine line]; 8049 int aCnt = 0; 8050 8051 p0.y += TOLERANCE; 8052 p1.y += TOLERANCE; 8053 [line setVertices:p0 :p1]; 8054 aCnt = [line getIntersections:&aPts with:gp]; 8055 if (!aCnt) 8056 { p0.y -= 2.0*TOLERANCE; 8057 p1.y -= 2.0*TOLERANCE; 8058 [line setVertices:p0 :p1]; 8059 aCnt = [line getIntersections:&aPts with:gp]; 8060 } 8061 if (((aCnt == 2 && 8062 ((pt.x >= aPts[0].x && pt.x <= aPts[1].x) || (pt.x <= aPts[0].x && pt.x >= aPts[1].x)))) || 8063 (aCnt == 1 && 8064 ((pt.x >= aPts[0].x && pt.x <= tpt.x) || (pt.x <= aPts[0].x && pt.x >= tpt.x)))) 8065 { free(pts); 8066 free(aPts); 8067 return -2; // on 8068 } 8069 if (aCnt) 8070 free(aPts); 8071 } 8072 } 8073 else if ( [gp isKindOfClass:[VRectangle class]] && cnt ) 8074 { /* if one intersectionpoint layes on the uppest OR lowest y value of the rectangle 8075 * -> -1 !!! (horicontal...) 8076 */ 8077 for (j=0; j<cnt; j++) 8078 { 8079 if ( (Diff(pts[j].y, gpBounds.origin.y) <= TOLERANCE) || 8080 (Diff(pts[j].y, (gpBounds.origin.y+gpBounds.size.height)) <= TOLERANCE) ) 8081 { //free(*ppArray); *ppArray = NULL; 8082 if ( cnt > 1 ) 8083 { if ( Diff(pts[j].y, gpBounds.origin.y) <= TOLERANCE ) 8084 { (*ppArray)[0].x = gpBounds.origin.x; 8085 (*ppArray)[1].x = gpBounds.origin.x + gpBounds.size.width; 8086 } 8087 else 8088 { (*ppArray)[0].x = gpBounds.origin.x; 8089 (*ppArray)[1].x = gpBounds.origin.x + gpBounds.size.width; 8090 } 8091 (*ppArray)[0].y = (*ppArray)[1].y = pts[j].y; 8092 } 8093 else 8094 { (*ppArray)[0] = (*ppArray)[1] = pts[j]; } 8095 8096 if ((pt.x >= (*ppArray)[0].x && pt.x <= (*ppArray)[1].x) || 8097 (pt.x <= (*ppArray)[0].x && pt.x >= (*ppArray)[1].x)) 8098 { free(pts); 8099 return -2; // on 8100 } 8101 tangential = YES; 8102 } 8103 } 8104 } 8105 else if ( [gp isKindOfClass:[VCurve class]] && cnt ) 8106 { NSPoint p0, p1, p2, p3, tpoints[3]; 8107 int i, cpt, realSol=0, numSol=0; 8108 double cy, by, ay, t[3]; 8109 8110 [gp getVertices:&p0 :&p1 :&p2 :&p3]; 8111 /* we must look if one of the intersection points lying on a extrem point of the curve 8112 * represent the curve with the equations 8113 * x(t) = ax*t^3 + bx*t^2 + cx*t + x(0) 8114 * y(t) = ay*t^3 + by*t^2 + cy*t + y(0) 8115 * -> 3ay*t^2 + 2by*t + cy = 0 8116 */ 8117 cy = 3*(p1.y - p0.y); 8118 by = 3*(p2.y - p1.y) - cy; 8119 ay = p3.y - p0.y - by - cy; 8120 8121 /* get the ts in which the tangente is horicontal 8122 */ 8123 numSol = svPolynomial2( 3.0*ay, 2.0*by, cy, t); 8124 8125 /* when t is on curve */ 8126 realSol=0; 8127 for ( i=0 ; i<numSol ; i++) 8128 if ( t[i] >= 0.0 && t[i] <= 1.0 ) 8129 tpoints[realSol++] = [gp pointAt:t[i]]; 8130 8131 /* if one intersection point is identical with one tpoint -> -1 */ 8132 for ( i=0 ; i<realSol ;i++ ) 8133 for ( cpt=0 ; cpt<cnt ; cpt++ ) 8134 if (Diff(tpoints[i].x, pts[cpt].x) <= TOLERANCE && Diff(tpoints[i].y, pts[cpt].y) <= TOLERANCE) 8135 { //free(*ppArray); 8136 //*ppArray = NULL; 8137 (*ppArray)[0] = (*ppArray)[1] = pts[cpt]; 8138 if (Diff(pt.x, pts[cpt].x) <= TOLERANCE) 8139 { free(pts); 8140 return -2; // on 8141 } 8142 tangential = YES; 8143 } 8144 } 8145 else if ( cnt > 1 && [gp isKindOfClass:[VPolyLine class]] ) 8146 { int p, nPts = [gp numPoints]; 8147 NSPoint pl0, pl1; 8148 8149 /* check each line in PolyLine if horicontal */ 8150 for (p=0; p < nPts-1; p++) 8151 { 8152 pl0 = [gp pointWithNum:p]; 8153 pl1 = [gp pointWithNum:p+1]; 8154 8155 if (pointWithToleranceInArray(pl0, TOLERANCE, pts, cnt) && // both point are in pts 8156 pointWithToleranceInArray(pl1, TOLERANCE, pts, cnt)) 8157 { 8158 tangential = YES; 8159 (*ppArray)[0] = pts[0]; 8160 (*ppArray)[1] = pts[1]; 8161 if ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x)) 8162 { 8163 free(pts); 8164 return -2; // on 8165 } 8166 } 8167 } 8168 } 8169 8170 if (iCnt+cnt >= ptsCnt) 8171 *ppArray = realloc(*ppArray, (ptsCnt+=cnt*2) * sizeof(NSPoint)); 8172 8173 // add points if not allways inside pparray 8174 // else check if pt is edge pt of graphic -> return -1 8175 for (j=0; j<cnt; j++) 8176 { 8177 if ( !pointInArray(pts[j], *ppArray, oldCnt) ) 8178 (*ppArray)[iCnt++] = pts[j]; 8179 else 8180 { NSPoint start, end; 8181 8182 if ( [gp isKindOfClass:[VLine class]] ) /* line */ 8183 [(VLine*)gp getVertices:&start :&end]; 8184 else if ( [gp isKindOfClass:[VArc class]] || [gp isKindOfClass:[VCurve class]] ) 8185 { start = [gp pointWithNum:0]; 8186 end = [gp pointWithNum:MAXINT]; 8187 } 8188 else if ( [gp isKindOfClass:[VRectangle class]] ) 8189 { NSPoint ur, ul, size; 8190 [(VRectangle*)gp getVertices:&start :&size]; // origin size 8191 end = start; end.x += size.x; 8192 ul = start; ul.y += size.y; 8193 ur = end; ur.y += size.y; 8194 if ( (Diff(pts[j].x, ul.x) + Diff(pts[j].y, ul.y) < 10.0*TOLERANCE) || 8195 (Diff(pts[j].x, ur.x) + Diff(pts[j].y, ur.y) < 10.0*TOLERANCE) ) 8196 continue; // do not add 8197 } 8198 else if ( [gp isKindOfClass:[VPolyLine class]] ) 8199 { int k, pCnt = [(VPolyLine*)gp ptsCount], stop = 0; 8200 8201 for (k=1; k<pCnt-1; k++) 8202 { NSPoint pt = [(VPolyLine*)gp pointWithNum:k]; 8203 if ( Diff(pts[j].x, pt.x) + Diff(pts[j].y, pt.y) < 10.0*TOLERANCE ) 8204 { stop = 1; break; } 8205 } 8206 if (stop) 8207 continue; // do not add 8208 [(VPolyLine*)gp getEndPoints:&start :&end]; 8209 } 8210 else 8211 { start.x = end.x = pts[j].x; start.y = end.y = pts[j].y; 8212 } 8213 /* point is no edge point of gp -> add */ 8214 if ( (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) > 10.0*TOLERANCE) && 8215 (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) > 10.0*TOLERANCE) ) 8216 (*ppArray)[iCnt++] = pts[j]; 8217 else 8218 { (*ppArray)[0] = (*ppArray)[1] = pts[j]; 8219 if (Diff(pt.x, pts[j].x) <= TOLERANCE) 8220 { free(pts); 8221 return -2; // on 8222 } 8223 tangential = YES; 8224 } 8225 } 8226 } 8227 if (pts) 8228 free(pts); 8229 } 8230 if (!iCnt) 8231 { free(*ppArray); 8232 *ppArray = NULL; 8233 } 8234 else if (tangential) 8235 return -1; 8236 return iCnt; 8237} 8238 8239- (int)intersectionsForPtInside:(NSPoint**)ppArray with:g :(NSPoint)pt subPath:(int)begIx :(int)endIx 8240{ int i, j, iCnt = 0, ptsCnt = Min(100, [self numPoints]); 8241 NSRect gBounds = [g bounds]; 8242 BOOL tangential = NO; 8243 8244 *ppArray = malloc(ptsCnt * sizeof(NSPoint)); 8245 for (i=endIx; i>=begIx; i--) 8246 { id gp = [list objectAtIndex:i]; 8247 int cnt, oldCnt = iCnt; 8248 NSRect gpBounds = [gp bounds]; 8249 NSPoint *pts = NULL; 8250 8251 if ( gp == g ) 8252 continue; 8253 // check bounds 8254 if ( NSIsEmptyRect(NSIntersectionRect(gBounds, gpBounds)) ) 8255 continue; 8256 8257 cnt = [gp getIntersections:&pts with:g]; // line, arc, curve, rectangle 8258 if ( cnt == 2 && 8259 ( [gp isKindOfClass:[VLine class]] || 8260 ([gp isKindOfClass:[VArc class]] && 8261 (pts[0].x == pts[1].x || 8262 ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x)))) ) ) 8263 { 8264 /* if there are two intersections with horicontal line the line of the polygon is allso horicontal */ 8265 /* or an arc tangent */ 8266 if ([gp isKindOfClass:[VLine class]] || ([gp isKindOfClass:[VArc class]] && pts[0].x == pts[1].x)) 8267 { tangential = YES; 8268 (*ppArray)[0] = pts[0]; 8269 (*ppArray)[1] = pts[1]; 8270 } 8271 if ([gp isKindOfClass:[VLine class]] && 8272 ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x))) 8273 { free(pts); 8274 return -2; // on 8275 } 8276 else if ( pts[0].x != pts[1].x && 8277 ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x)) ) 8278 { NSPoint p0 = [g pointWithNum:0], p1 = [g pointWithNum:MAXINT], *aPts; 8279 int aCnt = 0; 8280 8281 /* we have made a sidestep */ 8282 if (Diff(pt.y, p0.y) > TOLERANCE/2.0) 8283 { VLine *line = [VLine line]; 8284 8285 p0.y = p1.y = pt.y; 8286 [line setVertices:p0 :p1]; 8287 if (!(aCnt = [line getIntersections:&aPts with:gp]) || aCnt == 2) 8288 { 8289 if (!aCnt || aPts[0].x == aPts[1].x) 8290 { 8291 /* not inside !! */ 8292 free(pts); 8293 free(aPts); 8294 return -1; // need a sidestep to other side ! 8295 } 8296 } 8297 } 8298 if (aCnt) 8299 free(aPts); 8300 } 8301 else if ([gp isKindOfClass:[VArc class]] && Diff(pt.x, pts[0].x) <= 5.0*TOLERANCE) 8302 { free(pts); 8303 return -2; // on 8304 } 8305 else if ([gp isKindOfClass:[VArc class]]) 8306 { NSPoint tpt = pts[0], p0 = [g pointWithNum:0], p1 = [g pointWithNum:MAXINT], *aPts; 8307 VLine *line = [VLine line]; 8308 int aCnt = 0; 8309 8310 p0.y += TOLERANCE; 8311 p1.y += TOLERANCE; 8312 [line setVertices:p0 :p1]; 8313 aCnt = [line getIntersections:&aPts with:gp]; 8314 if (!aCnt) 8315 { p0.y -= 2.0*TOLERANCE; 8316 p1.y -= 2.0*TOLERANCE; 8317 [line setVertices:p0 :p1]; 8318 aCnt = [line getIntersections:&aPts with:gp]; 8319 } 8320 if (((aCnt == 2 && 8321 ((pt.x >= aPts[0].x && pt.x <= aPts[1].x) || (pt.x <= aPts[0].x && pt.x >= aPts[1].x)))) || 8322 (aCnt == 1 && 8323 ((pt.x >= aPts[0].x && pt.x <= tpt.x) || (pt.x <= aPts[0].x && pt.x >= tpt.x)))) 8324 { free(pts); 8325 free(aPts); 8326 return -2; // on 8327 } 8328 if (aCnt) 8329 free(aPts); 8330 } 8331 } 8332 else if ( [gp isKindOfClass:[VRectangle class]] && cnt ) 8333 { /* if one intersectionpoint layes on the uppest OR lowest y value of the rectangle 8334 * -> -1 !!! (horicontal...) 8335 */ 8336 for (j=0; j<cnt; j++) 8337 { 8338 if ( (Diff(pts[j].y, gpBounds.origin.y) <= TOLERANCE) || 8339 (Diff(pts[j].y, (gpBounds.origin.y+gpBounds.size.height)) <= TOLERANCE) ) 8340 { //free(*ppArray); *ppArray = NULL; 8341 if ( cnt > 1 ) 8342 { if ( Diff(pts[j].y, gpBounds.origin.y) <= TOLERANCE ) 8343 { (*ppArray)[0].x = gpBounds.origin.x; 8344 (*ppArray)[1].x = gpBounds.origin.x + gpBounds.size.width; 8345 } 8346 else 8347 { (*ppArray)[0].x = gpBounds.origin.x; 8348 (*ppArray)[1].x = gpBounds.origin.x + gpBounds.size.width; 8349 } 8350 (*ppArray)[0].y = (*ppArray)[1].y = pts[j].y; 8351 } 8352 else 8353 { (*ppArray)[0] = (*ppArray)[1] = pts[j]; } 8354 8355 if ((pt.x >= (*ppArray)[0].x && pt.x <= (*ppArray)[1].x) || 8356 (pt.x <= (*ppArray)[0].x && pt.x >= (*ppArray)[1].x)) 8357 { free(pts); 8358 return -2; // on 8359 } 8360 tangential = YES; 8361 } 8362 } 8363 } 8364 else if ( [gp isKindOfClass:[VCurve class]] && cnt ) 8365 { NSPoint p0, p1, p2, p3, tpoints[3]; 8366 int i, cpt, realSol=0, numSol=0; 8367 double cy, by, ay, t[3]; 8368 8369 [gp getVertices:&p0 :&p1 :&p2 :&p3]; 8370 /* we must look if one of the intersection points lying on a extrem point of the curve 8371 * represent the curve with the equations 8372 * x(t) = ax*t^3 + bx*t^2 + cx*t + x(0) 8373 * y(t) = ay*t^3 + by*t^2 + cy*t + y(0) 8374 * -> 3ay*t^2 + 2by*t + cy = 0 8375 */ 8376 cy = 3*(p1.y - p0.y); 8377 by = 3*(p2.y - p1.y) - cy; 8378 ay = p3.y - p0.y - by - cy; 8379 8380 /* get the ts in which the tangente is horicontal 8381 */ 8382 numSol = svPolynomial2( 3.0*ay, 2.0*by, cy, t); 8383 8384 /* when t is on curve */ 8385 realSol=0; 8386 for ( i=0 ; i<numSol ; i++) 8387 if ( t[i] >= 0.0 && t[i] <= 1.0 ) 8388 tpoints[realSol++] = [gp pointAt:t[i]]; 8389 8390 /* if one intersection point is identical with one tpoint -> -1 */ 8391 for ( i=0 ; i<realSol ;i++ ) 8392 for ( cpt=0 ; cpt<cnt ; cpt++ ) 8393 if (Diff(tpoints[i].x, pts[cpt].x) <= TOLERANCE && Diff(tpoints[i].y, pts[cpt].y) <= TOLERANCE) 8394 { //free(*ppArray); 8395 //*ppArray = NULL; 8396 (*ppArray)[0] = (*ppArray)[1] = pts[cpt]; 8397 if (Diff(pt.x, pts[cpt].x) <= TOLERANCE) 8398 { free(pts); 8399 return -2; // on 8400 } 8401 tangential = YES; 8402 } 8403 } 8404 else if ( cnt > 1 && [gp isKindOfClass:[VPolyLine class]] ) 8405 { int p, nPts = [gp numPoints]; 8406 NSPoint pl0, pl1; 8407 8408 /* check each line in PolyLine if horicontal */ 8409 for (p=0; p < nPts-1; p++) 8410 { 8411 pl0 = [gp pointWithNum:p]; 8412 pl1 = [gp pointWithNum:p+1]; 8413 8414 if (pointWithToleranceInArray(pl0, TOLERANCE, pts, cnt) && // both point are in pts 8415 pointWithToleranceInArray(pl1, TOLERANCE, pts, cnt)) 8416 { 8417 tangential = YES; 8418 (*ppArray)[0] = pts[0]; 8419 (*ppArray)[1] = pts[1]; 8420 if ((pt.x >= pts[0].x && pt.x <= pts[1].x) || (pt.x <= pts[0].x && pt.x >= pts[1].x)) 8421 { 8422 free(pts); 8423 return -2; // on 8424 } 8425 } 8426 } 8427 } 8428 8429 if (iCnt+cnt >= ptsCnt) 8430 *ppArray = realloc(*ppArray, (ptsCnt+=cnt*2) * sizeof(NSPoint)); 8431 8432 // add points if not allways inside pparray 8433 // else check if pt is edge pt of graphic -> return -1 8434 for (j=0; j<cnt; j++) 8435 { 8436 if ( !pointInArray(pts[j], *ppArray, oldCnt) ) 8437 (*ppArray)[iCnt++] = pts[j]; 8438 else 8439 { NSPoint start, end; 8440 8441 if ( [gp isKindOfClass:[VLine class]] ) /* line */ 8442 [(VLine*)gp getVertices:&start :&end]; 8443 else if ( [gp isKindOfClass:[VArc class]] || [gp isKindOfClass:[VCurve class]] ) 8444 { start = [gp pointWithNum:0]; 8445 end = [gp pointWithNum:MAXINT]; 8446 } 8447 else if ( [gp isKindOfClass:[VRectangle class]] ) 8448 { NSPoint ur, ul, size; 8449 [(VRectangle*)gp getVertices:&start :&size]; // origin size 8450 end = start; end.x += size.x; 8451 ul = start; ul.y += size.y; 8452 ur = end; ur.y += size.y; 8453 if ( (Diff(pts[j].x, ul.x) + Diff(pts[j].y, ul.y) < 10.0*TOLERANCE) || 8454 (Diff(pts[j].x, ur.x) + Diff(pts[j].y, ur.y) < 10.0*TOLERANCE) ) 8455 continue; // do not add 8456 } 8457 else if ( [gp isKindOfClass:[VPolyLine class]] ) 8458 { int k, pCnt = [(VPolyLine*)gp ptsCount], stop = 0; 8459 8460 for (k=1; k<pCnt-1; k++) 8461 { NSPoint pt = [(VPolyLine*)gp pointWithNum:k]; 8462 if ( Diff(pts[j].x, pt.x) + Diff(pts[j].y, pt.y) < 10.0*TOLERANCE ) 8463 { stop = 1; break; } 8464 } 8465 if (stop) 8466 continue; // do not add 8467 [(VPolyLine*)gp getEndPoints:&start :&end]; 8468 } 8469 else 8470 { start.x = end.x = pts[j].x; start.y = end.y = pts[j].y; 8471 } 8472 /* point is no edge point of gp -> add */ 8473 if ( (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) > 10.0*TOLERANCE) && 8474 (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) > 10.0*TOLERANCE) ) 8475 (*ppArray)[iCnt++] = pts[j]; 8476 else 8477 { (*ppArray)[0] = (*ppArray)[1] = pts[j]; 8478 if (Diff(pt.x, pts[j].x) <= TOLERANCE) 8479 { free(pts); 8480 return -2; // on 8481 } 8482 tangential = YES; 8483 } 8484 } 8485 } 8486 if (pts) 8487 free(pts); 8488 } 8489 if (!iCnt) 8490 { free(*ppArray); 8491 *ppArray = NULL; 8492 } 8493 else if (tangential) 8494 return -1; 8495 return iCnt; 8496} 8497 8498- (float)sqrDistanceGraphic:g 8499{ int i; 8500 float dist, distance = MAXCOORD; 8501 8502 for (i=[list count]-1; i>=0; i--) 8503 { id gp = [list objectAtIndex:i]; 8504 8505 if ( (dist=[gp sqrDistanceGraphic:g]) < distance) 8506 distance = dist; 8507 } 8508 return distance; 8509} 8510 8511- (float)distanceGraphic:g 8512{ float distance; 8513 8514 distance = [self sqrDistanceGraphic:g]; 8515 return sqrt(distance); 8516} 8517 8518- (BOOL)isPointInside:(NSPoint)p 8519{ int iVal=0; 8520 8521 return ( !(iVal=[self isPointInsideOrOn:p dist: TOLERANCE]) || iVal == 1 ) ? NO : YES; 8522} 8523 8524/*- (BOOL)isPointInside:(NSPoint)p 8525{ 8526 return ([self isPointInside:p dist:TOLERANCE]) ? YES : NO; 8527}*/ 8528 8529- (int)isPointInsideOrOn:(NSPoint)p 8530{ 8531 return [self isPointInsideOrOn:p dist:TOLERANCE]; 8532} 8533 8534/* return 8535 * 0 = outside 8536 * 1 = on 8537 * 2 = inside 8538 */ 8539#if 0 // new !!!!!! 8540- (int)isPointInsideOrOn:(NSPoint)p dist:(float)dist 8541{ int i, cnt, leftCnt=0, *info, horicontals = 0, hStart_upDown = -1; 8542 BOOL hStart = NO; 8543 NSPoint p0, p1, *pts = NULL; 8544 VLine *line; 8545 NSRect bRect; 8546 8547 bRect = [self coordBounds]; 8548 8549 line = [VLine line]; 8550 p0.x = bRect.origin.x - 2000.0; p1.x = bRect.origin.x + bRect.size.width+2000.0; 8551 p0.y = p1.y = p.y; 8552 8553 [line setVertices:p0 :p1]; 8554 if ( !(cnt = [self intersectionsForPtInside:&pts :&info with:line]) ) 8555 return 0; // outside 8556 8557 hStart = NO; 8558 for (i=0; i<cnt; i++) /* count points left of p */ 8559 { 8560 if (Diff(pts[i].x, p.x) <= dist) // *7.0 8561 { free(pts); 8562 free(info); 8563 return 1; // on 8564 } 8565 if (pts[i].x < p.x) 8566 { leftCnt++; 8567 if (hStart == NO && (info[i] == INFO_HORICONTAL_UP || info[i] == INFO_HORICONTAL_DOWN)) 8568 { hStart = YES; 8569 hStart_upDown = info[i]; 8570 } 8571 else if (hStart == YES && (info[i] == INFO_HORICONTAL_UP || info[i] == INFO_HORICONTAL_DOWN)) 8572 { hStart = NO; 8573 if (hStart_upDown != info[i]) 8574 horicontals++; // only changes from up to down change the even/odd creteria 8575 } 8576 } 8577 else 8578 break; 8579 } 8580 if (pts) 8581 free(pts); 8582 if (info) 8583 free(info); 8584 8585 if (hStart == YES) 8586 return 1; // on 8587 8588 /* odd number of points on the left and p is inside the polygon */ 8589 /* inside : outside */ 8590 return ((Even(horicontals) && Even(leftCnt)) || (!Even(horicontals) && !Even(leftCnt))) ? 0 : 2; 8591} 8592#endif 8593 8594//#if 0 // original 8595/* return 8596 * 0 = outside 8597 * 1 = on 8598 * 2 = inside 8599 */ 8600- (int)isPointInsideOrOn:(NSPoint)p dist:(float)dist 8601{ int i, cnt, leftCnt=0, iByBreak = 0; 8602 NSPoint p0, p1, *pts = NULL; 8603 VLine *line; 8604 NSRect bRect; 8605 8606 bRect = [self coordBounds]; 8607// if ( !NSPointInRect(p , bRect) ) 8608// return 0; 8609 8610 line = [VLine line]; 8611 p0.x = bRect.origin.x - 2000.0; p1.x = bRect.origin.x + bRect.size.width+2000.0; 8612 p0.y = p1.y = p.y; 8613 8614 for (i=2; i<16; i++) /* we need to find a position where we don't hit an edge */ 8615 { 8616 [line setVertices:p0 :p1]; 8617 p0.y = p1.y = p.y + ((i%2) ? (-i*(TOLERANCE/2.0)) : (i*(TOLERANCE/2.0))); // i*(TOLERANCE/2.0); 8618 if ( !(cnt = [self intersectionsForPtInside:&pts with:line :p]) && i==2 ) 8619 return 0; 8620 if ( i == 2 && cnt == -2 ) 8621 { 8622 /* we checked all horicontals in this range if point is on ! */ 8623 free(pts); 8624 return 1; // on 8625 } 8626 if ( cnt && Even(cnt) ) 8627 { if ( i != 2 ) 8628 iByBreak = i;// /2.0; 8629 break; 8630 } 8631 if (pts) 8632 { free(pts); pts = NULL; 8633 } 8634 } 8635 if ( cnt <= 1 || !Even(cnt) ) /* we hit an edge */ 8636 { if (cnt > 0) 8637 NSLog(@"VPath, -isPointInside: hit edge! p: %.3f %.3f cnt: %i", p.x, p.y, cnt); 8638 if (pts) 8639 free(pts); 8640 return 0; 8641 } 8642 sortPointArray(pts, cnt, p0); /* sort from left to right */ 8643 8644// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 8645 /* p is on the border of the polygon 8646 */ 8647 for (i=0; i<cnt; i++) // on polygon 8648 if ( Diff(pts[i].x, p.x) <= dist+(iByBreak*TOLERANCE) ) // *7.0 8649 { free(pts); 8650 return 1; 8651// return 0; // on meens not inside ! 8652 } 8653 8654 for (i=0; i<cnt && pts[i].x < p.x; i++) /* count points left of p */ 8655 leftCnt++; 8656 8657 if (pts) 8658 free(pts); 8659 8660 /* p is at the top or at the bottom of the polygon 8661 */ 8662 if ( !Even(leftCnt) && (Diff(bRect.origin.y, p.y) <= dist*1.5 8663 || Diff(bRect.origin.y+bRect.size.height, p.y) <= dist*1.5) ) 8664 return 1; 8665// return 0; // on meens not inside ! 8666 8667 return (Even(leftCnt)) ? 0 : 2; /* odd number of points on the left and p is inside the polygon */ 8668} 8669 8670/* return 8671 * 0 = outside 8672 * 1 = on 8673 * 2 = inside 8674 */ 8675- (int)isPointInsideOrOn:(NSPoint)p dist:(float)dist subPath:(int)begIx :(int)endIx 8676{ int i, cnt, leftCnt=0, iByBreak = 0; 8677 NSPoint p0, p1, *pts = NULL; 8678 VLine *line; 8679 NSRect bRect; 8680 8681 bRect = [self coordBoundsOfSubPath:begIx :endIx]; 8682// if ( !NSPointInRect(p , bRect) ) 8683// return 0; 8684 8685 line = [VLine line]; 8686 p0.x = bRect.origin.x - 2000.0; p1.x = bRect.origin.x + bRect.size.width+2000.0; 8687 p0.y = p1.y = p.y; 8688 8689 for (i=2; i<16; i++) /* we need to find a position where we don't hit an edge */ 8690 { 8691 [line setVertices:p0 :p1]; 8692 p0.y = p1.y = p.y + ((i%2) ? (-i*(TOLERANCE/2.0)) : (i*(TOLERANCE/2.0))); // i*(TOLERANCE/2.0); 8693 if ( !(cnt = [self intersectionsForPtInside:&pts with:line :p subPath:begIx :endIx]) && i==2 ) 8694 return 0; 8695 if ( i == 2 && cnt == -2 ) 8696 { 8697 /* we checked all horicontals in this range if point is on ! */ 8698 free(pts); 8699 return 1; // on 8700 } 8701 if ( cnt && Even(cnt) ) 8702 { if ( i != 2 ) 8703 iByBreak = i;// /2.0; 8704 break; 8705 } 8706 if (pts) 8707 { free(pts); pts = NULL; 8708 } 8709 } 8710 if ( cnt <= 1 || !Even(cnt) ) /* we hit an edge */ 8711 { if (cnt > 0) 8712 NSLog(@"VPath, -isPointInside: hit edge! p: %.3f %.3f cnt: %i", p.x, p.y, cnt); 8713 if (pts) 8714 free(pts); 8715 return 0; 8716 } 8717 sortPointArray(pts, cnt, p0); /* sort from left to right */ 8718 8719// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 8720 /* p is on the border of the polygon 8721 */ 8722 for (i=0; i<cnt; i++) // on polygon 8723 if ( Diff(pts[i].x, p.x) <= dist+(iByBreak*TOLERANCE) ) // *7.0 8724 { free(pts); 8725 return 1; 8726// return 0; // on meens not inside ! 8727 } 8728 8729 for (i=0; i<cnt && pts[i].x < p.x; i++) /* count points left of p */ 8730 leftCnt++; 8731 8732 if (pts) 8733 free(pts); 8734 8735 /* p is at the top or at the bottom of the polygon 8736 */ 8737 if ( !Even(leftCnt) && (Diff(bRect.origin.y, p.y) <= dist*1.5 8738 || Diff(bRect.origin.y+bRect.size.height, p.y) <= dist*1.5) ) 8739 return 1; 8740// return 0; // on meens not inside ! 8741 8742 return (Even(leftCnt)) ? 0 : 2; /* odd number of points on the left and p is inside the polygon */ 8743} 8744 8745//#endif 8746#if 0 8747- (int)isPointInside:(NSPoint)p dist:(float)dist 8748{ int i, cnt, leftCnt=0; 8749 NSPoint p0, p1, *pts = NULL; 8750 VLine *line; 8751 NSRect bRect; 8752 8753 bRect = [self coordBounds]; 8754 if ( !NSPointInRect(p , bRect) ) 8755 return 0; 8756 8757 line = [VLine line]; 8758 p0.x = bRect.origin.x - 2000.0; p1.x = bRect.origin.x + bRect.size.width+2000.0; 8759 p0.y = p1.y = p.y; 8760 8761 for (i=0; i<10; i++) /* we need to find a position where we don't hit an edge */ 8762 { 8763 [line setVertices:p0 :p1]; 8764 p0.y = p1.y = p0.y + 10.0*TOLERANCE; 8765 if ( !(cnt = [self getIntersections:&pts with:line]) ) 8766 return 0; 8767 sortPointArray(pts, cnt, p0); /* sort from left to right */ 8768 if ( Even(cnt) ) 8769 break; 8770 } 8771 if ( cnt <= 1 || !Even(cnt) ) /* we hit an edge */ 8772 { NSLog(@"VPath, -isPointInside: hit edge!"); 8773 if (pts) 8774 free(pts); 8775 return 0; 8776 } 8777 8778 8779 /* p is on the border of the polygon 8780 */ 8781 for (i=0; i<cnt; i++) /* on polygon */ 8782 if ( DiffPoint(pts[i], p) <= dist ) 8783 { free(pts); 8784 return 1; 8785 } 8786 8787 for (i=0; i<cnt && pts[i].x < p.x; i++) /* count points left of p */ 8788 leftCnt++; 8789 8790 if (pts) 8791 free(pts); 8792 8793 /* p is at the top or at the bottom of the polygon 8794 */ 8795 if ( !Even(leftCnt) && (Diff(bRect.origin.y, p.y)<=dist || Diff(bRect.origin.y+bRect.size.height, p.y)<=dist) ) 8796 return 1; 8797 8798 return (Even(leftCnt)) ? 0 : 2; /* odd number of points on the left and p is inside the polygon */ 8799} 8800#endif 8801 8802/* created: 1996-10-03 8803 * modified: 2001-04-10 8804 * check for all endpoints of the path, whether a point of our Array is on an endpoint 8805 */ 8806- (BOOL)pointArrayHitsCorner:(NSPoint*)pts :(int)ptsCnt 8807{ int i; 8808 8809 for (i=[list count]-1; i>=0; i--) 8810 { id obj = [list objectAtIndex:i]; 8811 NSPoint p; 8812 8813 [obj getPoint:&p at:0]; /* start point */ 8814 if (pointWithToleranceInArray(p, 0.003, pts, ptsCnt)) /* was 0.03 */ 8815 return YES; 8816 [obj getPoint:&p at:3]; /* end point */ 8817 if (pointWithToleranceInArray(p, 0.003, pts, ptsCnt)) /* was 0.03 */ 8818 return YES; 8819 } 8820 8821 return NO; 8822} 8823 8824- (id)clippedWithRect:(NSRect)rect 8825{ 8826 return [self clippedWithRect:rect close:NO]; 8827} 8828- (id)clippedWithRect:(NSRect)rect close:(BOOL)close 8829{ NSMutableArray *clipList = [NSMutableArray array]; 8830 id cObj; 8831 NSArray *cList; 8832 int i, cnt, c, cCnt; 8833 VPath *path; 8834 8835 /* clip objects */ 8836 for (i=0, cnt = [list count]; i<cnt; i++) 8837 { 8838 if (!(cObj = [[list objectAtIndex:i] clippedWithRect:rect])) 8839 continue; 8840 if ([cObj isMemberOfClass:[VGroup class]]) 8841 { 8842 cList = [cObj list]; 8843 for (c=0, cCnt = [cList count]; c<cCnt; c++) 8844 [clipList addObject:[cList objectAtIndex:c]]; 8845 } 8846 else 8847 [clipList addObject:cObj]; 8848 } 8849 8850 if (![clipList count]) 8851 return nil; 8852 8853 path = [VPath path]; 8854 if (close) 8855 { VRectangle *rectangle = [VRectangle rectangle]; 8856 NSArray *splitList; 8857 8858 [rectangle setVertices:rect.origin :NSMakePoint(rect.size.width, rect.size.height)]; 8859 8860 /* clip rectangle and add parts of rectangle which are inside path */ 8861 if ( [(splitList = [rectangle getListOfObjectsSplittedFromGraphic:self]) count] > 1 ) 8862 for (i=0; i<(int)[splitList count]; i++) 8863 if ( [self isPointInside:[[splitList objectAtIndex:i] pointAt:0.5]] ) 8864 [clipList addObject:[splitList objectAtIndex:i]]; 8865 8866 /* optimize */ 8867 [path setList:clipList]; 8868 [path sortList]; 8869 } 8870 else 8871 [path setList:clipList]; 8872 8873 return path; 8874} 8875 8876- getIntersectionsAndSplittedObjects:(NSPoint**)ppArray :(int*)iCnt with:g 8877{ int i, j, lCnt = [list count], ptsCnt = Min(100, [self numPoints]); 8878 NSRect gBounds = [g bounds]; 8879 NSMutableArray *splitList = [NSMutableArray array], *spList = nil; 8880 NSAutoreleasePool *pool = [NSAutoreleasePool new]; 8881 8882 *iCnt = 0; 8883 *ppArray = malloc(ptsCnt * sizeof(NSPoint)); 8884// for (i=[list count]-1; i>=0; i--) 8885 for (i=0; i<lCnt; i++) 8886 { id gp = [list objectAtIndex:i]; 8887 int cnt, oldCnt = *iCnt; 8888 NSRect gpBounds = [gp bounds]; 8889 NSPoint *pts = NULL; 8890 8891 if ( gp == g ) 8892 continue; 8893 // check bounds 8894 if ( NSIsEmptyRect(NSIntersectionRect(gBounds , gpBounds)) ) 8895 { 8896 // add to splitList 8897 [splitList addObject:[[gp copy] autorelease]]; 8898 continue; 8899 } 8900 /* line, arc, curve */ 8901 if ( !(cnt = [gp getIntersections:&pts with:g]) ) 8902 [splitList addObject:[[gp copy] autorelease]]; 8903 else 8904 { spList = [gp getListOfObjectsSplittedFrom:pts :cnt]; 8905 if ( spList ) 8906 { for ( j=0; j<(int)[spList count]; j++ ) 8907 [splitList addObject:[spList objectAtIndex:j]]; 8908 } 8909 else 8910 [splitList addObject:[[gp copy] autorelease]]; 8911 } 8912 if ((*iCnt)+cnt >= ptsCnt) 8913 *ppArray = realloc(*ppArray, (ptsCnt+=cnt*2) * sizeof(NSPoint)); 8914 8915 for (j=0; j<cnt; j++) 8916 { 8917 if ( !pointInArray(pts[j], *ppArray, oldCnt) ) 8918 (*ppArray)[(*iCnt)++] = pts[j]; 8919 else 8920 { NSPoint start, end; 8921 8922 if ( [gp isKindOfClass:[VLine class]] ) /* line */ 8923 [(VLine*)gp getVertices:&start :&end]; 8924 else if ( [gp isKindOfClass:[VArc class]] || [gp isKindOfClass:[VCurve class]] ) 8925 { start = [gp pointWithNum:0]; 8926 end = [gp pointWithNum:MAXINT]; 8927 } 8928 else if ( [gp isKindOfClass:[VRectangle class]] ) 8929 { NSPoint ur, ul, size; 8930 [(VRectangle*)gp getVertices:&start :&size]; // origin size 8931 end = start; end.x += size.x; 8932 ul = start; ul.y += size.y; 8933 ur = end; ur.y += size.y; 8934 if ( (Diff(pts[j].x, ul.x) + Diff(pts[j].y, ul.y) < 10.0*TOLERANCE) || 8935 (Diff(pts[j].x, ur.x) + Diff(pts[j].y, ur.y) < 10.0*TOLERANCE) ) 8936 continue; // do not add 8937 } 8938 else if ( [gp isKindOfClass:[VPolyLine class]] ) 8939 { int k, pCnt = [(VPolyLine*)gp ptsCount], stop = 0; 8940 8941 for (k=1; k<pCnt-1; k++) 8942 { NSPoint pt = [(VPolyLine*)gp pointWithNum:k]; 8943 if ( Diff(pts[j].x, pt.x) + Diff(pts[j].y, pt.y) < 10.0*TOLERANCE ) 8944 { stop = 1; break; } 8945 } 8946 if (stop) 8947 continue; // do not add 8948 [(VPolyLine*)gp getEndPoints:&start :&end]; 8949 } 8950 else 8951 { start.x = end.x = pts[j].x; start.y = end.y = pts[j].y; 8952 } 8953 /* point is no edge point of gp -> add */ 8954 if ( (Diff(pts[j].x, start.x) + Diff(pts[j].y, start.y) > 10.0*TOLERANCE) && 8955 (Diff(pts[j].x, end.x) + Diff(pts[j].y, end.y) > 10.0*TOLERANCE) ) 8956 (*ppArray)[(*iCnt)++] = pts[j]; 8957 } 8958 } 8959 if (pts) 8960 free(pts); 8961 } 8962 8963 if (!(*iCnt)) 8964 { free(*ppArray); 8965 *ppArray = NULL; 8966 } 8967 [pool release]; 8968 if ( [splitList count] > [list count] ) 8969 return splitList; 8970 return nil; 8971} 8972 8973/* optimize list from uniteWith with same tolerance - we did not close gaps here ??? 8974 */ 8975- (void)optimizeList:(NSMutableArray*)olist 8976{ int k, i1, i2, changeI, startIndex = 0; 8977 float startDistance=MAXCOORD, d1, d2; 8978 float tol = 10.0*TOLERANCE; 8979 NSPoint s1, e1, s2, e2; 8980 8981 if ( ![olist count] ) 8982 return; 8983 8984 for (i1 = 0; i1<(int)[olist count]-1; i1++) 8985 { VGraphic *g1 = [olist objectAtIndex:i1]; 8986 int closeK = -1; 8987 BOOL changeDirection = NO; 8988 8989 startDistance = MAXCOORD; 8990 changeI = i1+1; 8991 s1 = [g1 pointWithNum:0]; 8992 e1 = [g1 pointWithNum:MAXINT]; 8993 for (i2 = i1+1; i2 < (int)[olist count]; i2++) 8994 { VGraphic *g2 = [olist objectAtIndex:i2]; 8995 8996 s2 = [g2 pointWithNum:0]; 8997 e2 = [g2 pointWithNum:MAXINT]; 8998 d1 = SqrDistPoints(e1, s2); d2 = SqrDistPoints(e1, e2); 8999 if ( d1 < startDistance || d2 < startDistance ) 9000 { 9001 if ( d2 < d1 ) 9002 { startDistance = d2; 9003 changeDirection = YES; 9004 } 9005 else 9006 { startDistance = d1; 9007 changeDirection = NO; 9008 } 9009 changeI = i2; 9010 if ( startDistance < TOLERANCE*TOLERANCE ) 9011 break; 9012 } 9013 } 9014 closeK = changeI; 9015 /* if the nearest element is not the next_in_list */ 9016 /* search until connected part end - in both directions ! */ 9017 if (changeI != (i1+1) && changeDirection) // search backward 9018 { NSPoint prevS = e1; // first k == j == startPt 9019 9020 for ( k=changeI; k >= i1+1; k-- ) 9021 { VGraphic *gk; 9022 NSPoint sk, ek; 9023 9024 gk = [olist objectAtIndex:k]; 9025 sk = [gk pointWithNum:0]; 9026 ek = [gk pointWithNum:MAXINT]; 9027 9028 if (SqrDistPoints(prevS, ek) >= tol*tol || k == i1+1) 9029 { closeK = (k == i1+1 || k == changeI) ? k : (k+1); 9030 break; // end of connected part 9031 } 9032 prevS = sk; 9033 } 9034 9035 } 9036 else if (changeI != (i1+1)) // search forward 9037 { NSPoint prevE = e1; 9038 9039 for ( k=changeI; k < [olist count]; k++ ) 9040 { VGraphic *gk; 9041 NSPoint sk, ek; 9042 9043 gk = [olist objectAtIndex:k]; 9044 sk = [gk pointWithNum:0]; 9045 ek = [gk pointWithNum:MAXINT]; 9046 9047 if (SqrDistPoints(prevE, sk) >= tol*tol || k == [olist count]-1) 9048 { closeK = (k == [olist count]-1 || k == changeI) ? k : (k-1); 9049 break; // end of connected part 9050 } 9051 prevE = ek; 9052 } 9053 } 9054 9055 if ( startDistance > TOLERANCE*TOLERANCE ) /* close hole */ 9056 { VGraphic *g2; 9057 float dist = MAXCOORD; 9058 9059 g2 = [olist objectAtIndex:( startDistance <= tol*tol ) ? changeI : startIndex]; 9060 if ( startDistance <= tol*tol && changeDirection) 9061 { s2 = [g2 pointWithNum:MAXINT]; 9062 e2 = [g2 pointWithNum:0]; 9063 } 9064 else 9065 { s2 = [g2 pointWithNum:0]; 9066 e2 = [g2 pointWithNum:MAXINT]; 9067 } 9068 if ( (dist=SqrDistPoints(e1, s2)) > TOLERANCE*TOLERANCE && dist <= tol*tol ) 9069 { VGraphic *lineG = [VLine line]; 9070 9071 [(VLine*)lineG setVertices:e1 :s2]; 9072 [olist insertObject:lineG atIndex:i1+1]; 9073 i1 += 1; changeI += 1; closeK += 1; 9074 } 9075 if ( startDistance > tol*tol ) 9076 { 9077 /* g2 is start graphic ! if ( startDistance > tol*tol ) */ 9078 //if ( dist > tol*tol ) 9079 //NSLog(@"VPath.m: -optimizeList: distance: s: %.3f %.3f e: %.3f %.3f", s1.x, s1.y, e1.x, e1.y); 9080 startIndex = i1+1; 9081 } 9082 } 9083 9084 if ( changeI != (i1+1) && startDistance < tol*tol) 9085 { int m, insertCnt = 0, from = changeI, to = closeK; 9086 9087 if (changeDirection) 9088 { 9089 for (m=to; m <= from; m++) 9090 { VGraphic *g = [olist objectAtIndex:m]; 9091 9092 [g changeDirection]; 9093 if (m != i1+1) 9094 { [olist insertObject:g atIndex:i1+1]; 9095 [olist removeObjectAtIndex:m+1]; 9096 } 9097 } 9098 } 9099 else 9100 { for (m=to; m >= from; m--) 9101 { VGraphic *g = [olist objectAtIndex:m+insertCnt]; 9102 9103 [olist insertObject:g atIndex:i1+1]; 9104 insertCnt++; 9105 [olist removeObjectAtIndex:m+insertCnt]; 9106 } 9107 } 9108 if (insertCnt) 9109 i1 += insertCnt-1; 9110 } 9111 else if (changeDirection && startDistance < tol*tol) // else we destroy our sorted blocks ! 9112 { VGraphic *g2 = [olist objectAtIndex:changeI]; 9113 9114 [g2 changeDirection]; 9115 } 9116 } 9117 /* close hole from last to start element */ 9118 if ([olist count] > 1) 9119 { VGraphic *g1=[olist objectAtIndex:[olist count]-1]; 9120 VGraphic *g2= [olist objectAtIndex:startIndex]; 9121 float dist = MAXCOORD; 9122 9123 s1 = [g1 pointWithNum:0]; 9124 e1 = [g1 pointWithNum:MAXINT]; 9125 s2 = [g2 pointWithNum:0]; 9126 e2 = [g2 pointWithNum:MAXINT]; 9127 if ( (dist=SqrDistPoints(e1, s2)) > TOLERANCE*TOLERANCE && dist <= tol+tol ) 9128 { VGraphic *lineG = [VLine line]; 9129 9130 [(VLine*)lineG setVertices:e1 :s2]; 9131 [olist addObject:lineG]; 9132 } 9133 //else if ( dist > TOLERANCE*TOLERANCE ) 9134 // NSLog(@"VPath.m: -optimizeList: distance 2: s: %.3f %.3f e: %.3f %.3f", s1.x, s1.y, e1.x, e1.y); 9135 } 9136 return; 9137} 9138/* unite 9139 * returns a new graphic or nil 9140 * 9141 * modified: 2006-01-11 2008-10-16 9142 */ 9143- uniteWith:(id)ug 9144{ int i, j = 0, k, l, endIx=0, uStartIs[1000], startI, listCnt, uStartIsCnt = 0, uListCnt; 9145 int sPairsCnt = 0, ePairsCnt = 0, sPairsCnts[500], ePairsCnts[500]; 9146 int removedFromUg = 0, removedFromNg = 0, sCnt=0, eCnt=0, startIs[1000], endIs[1000]; 9147 float tol = (10.0*TOLERANCE); 9148 VPath *ng; 9149 NSMutableArray *splitListG, *splitListUg; 9150 NSPoint p, startPts[1000], endPts[1000]; // start/end point of removed graphic(s) 9151 BOOL first = YES, removing = NO; 9152 NSAutoreleasePool *pool; 9153 NSPoint gPrevE = NSZeroPoint; 9154 9155 if ( ![ug isKindOfClass:[VPath class]] && ![ug isKindOfClass:[VArc class]] && ![ug isKindOfClass:[VPolyLine class]] 9156 && ![ug isKindOfClass:[VRectangle class]] && ![ug isKindOfClass:[VGroup class]] ) 9157 return nil; 9158 9159 ng = [VPath path]; 9160 [ng setColor:[self color]]; 9161 [ng setFillColor:[self fillColor]]; 9162 [ng setEndColor:[self endColor]]; 9163 [ng setRadialCenter:[self radialCenter]]; 9164 [ng setStepWidth:[self stepWidth]]; 9165 [ng setGraduateAngle:[self graduateAngle]]; 9166 [ng setFilled:YES optimize:NO]; 9167 [ng setWidth:[self width]]; 9168 [ng setSelected:[self isSelected]]; 9169 9170 /* split self */ 9171 if ( (splitListG = [self getListOfObjectsSplittedFromGraphic:ug]) ) 9172 [ng setList:splitListG optimize:NO]; 9173 9174 if ( ![[ng list] count] ) 9175 for (i=0; i<(int)[list count]; i++) 9176 [[ng list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 9177 9178 pool = [NSAutoreleasePool new]; 9179 9180 /* split ug */ 9181 if ( !(splitListUg = [ug getListOfObjectsSplittedFromGraphic:self]) ) 9182 { 9183 splitListUg = [NSMutableArray array]; 9184 if ( [ug isKindOfClass:[VPath class]] ) 9185 for (i=0; i<(int)[[(VPath*)ug list] count]; i++) 9186 [splitListUg addObject:[[[[(VPath*)ug list] objectAtIndex:i] copy] autorelease]]; 9187 else 9188 [splitListUg addObject:[[ug copy] autorelease]]; 9189 } 9190 9191 /* get startIndexes from splitListUg */ 9192 uStartIsCnt = 1; 9193 uStartIs[0] = 0; 9194 uListCnt = [splitListUg count]; 9195 while (endIx != uListCnt-1) 9196 { NSPoint startPt, e; 9197 VGraphic *sg = [splitListUg objectAtIndex:uStartIs[uStartIsCnt-1]]; 9198 9199 endIx = -1; 9200 9201 startPt = [sg pointWithNum:0]; 9202 for ( i=uStartIs[uStartIsCnt-1]; i < uListCnt; i++ ) 9203 { 9204 e = [[splitListUg objectAtIndex:i] pointWithNum:MAXINT]; 9205 if ( SqrDistPoints(startPt, e) < tol*tol ) 9206 { 9207 if (i+1 < uListCnt) 9208 { NSPoint begN = [[splitListUg objectAtIndex:i+1] pointWithNum:0]; 9209 9210 if ( SqrDistPoints(e, begN) < (TOLERANCE*15)*(TOLERANCE*15) ) 9211 continue; // dist to next gr is smaller ! 9212 } 9213 endIx = i; 9214 break; 9215 } 9216 } 9217 if (endIx == -1) 9218 { uStartIs[uStartIsCnt++] = uListCnt-1; 9219 NSLog(@"VPath.m: -uniteWith: endIx not found !"); 9220 break; 9221 } 9222 else 9223 uStartIs[uStartIsCnt++] = endIx+1; 9224 } 9225 9226 /* first remove graphics from splitListUg inside self */ 9227 for (i=0; i<[splitListUg count]; i++) 9228 { VGraphic *gr = [splitListUg objectAtIndex:i]; 9229 9230 p = [gr pointAt:0.4]; 9231 if ( [self isPointInside:p] ) 9232 { [splitListUg removeObjectAtIndex:i]; 9233 /* correct all uStartIs behind i */ 9234 for (k=0; k < uStartIsCnt; k++) 9235 if (uStartIs[k] > i) uStartIs[k] -= 1; 9236 i--; 9237 removedFromUg++; 9238 } 9239 } 9240 /* we must check if we remove a hole subpath */ 9241 for (i=0; i< uStartIsCnt-1; i++) 9242 { 9243 if (uStartIs[i] == uStartIs[i+1]) 9244 { 9245 for (l=i; l < uStartIsCnt-1; l++) 9246 uStartIs[l] = uStartIs[l+1]; 9247 uStartIsCnt--; 9248 i--; // perhaps we remove two or three 9249 } 9250 } 9251 /* searching for our startI (not inside ug) */ 9252 startI = -1; 9253 for ( i=0, listCnt = [[ng list] count]; i<listCnt; i++ ) 9254 { id gThis; 9255 9256 gThis = [[ng list] objectAtIndex:i]; /* this object */ 9257 9258 /* first line normaly not possible !!! after split everything must be a path !! ! */ 9259 p = [gThis pointAt:0.4]; 9260 if ( ![ug isPointInside:p] ) 9261 { 9262 startI = i; 9263 break; 9264 } 9265 } 9266 9267 /* self is inside ug -> ug is it */ 9268 if (startI == -1 && !removedFromUg) 9269 { 9270 [pool release]; 9271 return [[ug copy] autorelease]; 9272 } 9273 9274 /* now we remove the parts of ng which are inside ug 9275 * and notice the start and end points .. 9276 */ 9277 first = YES; 9278 removing = NO; 9279 for ( i=startI, listCnt = [[ng list] count]; startI != -1 && (first || i != startI); i++ ) 9280 { id gThis; 9281 // NSPoint gPrevE = NSZeroPoint; 9282 BOOL currentlyRemoved = NO; 9283 9284 i = (i >= listCnt) ? 0 : i; 9285 if (!first && i == startI) 9286 break; 9287 9288 gThis = [[ng list] objectAtIndex:i]; /* this object */ 9289 first = NO; 9290 9291 if (removing && SqrDistPoints(gPrevE, [gThis pointWithNum:0]) > tol*tol) 9292 removing = NO; // last object removed but first object is not the same subpath and also removed 9293 gPrevE = [gThis pointWithNum:MAXINT]; 9294 9295 /* first line normaly not possible !!! after split everything must be a path !! ! */ 9296 p = ( [gThis isKindOfClass:[VPath class]] ) ? [[[(VPath*)gThis list] objectAtIndex:0] pointAt:0.4] 9297 : [gThis pointAt:0.4]; 9298 if ( [ug isPointInside:p] ) 9299 { 9300 removedFromNg++; 9301 currentlyRemoved = YES; 9302 if (!removing) 9303 { int l, prevI = -1; 9304 9305 removing = YES; 9306 eCnt++; 9307 sPairsCnt++; 9308 ePairsCnt++; 9309 ePairsCnts[ePairsCnt-1] = 0; // for check of second startPts[] / removing endpts 9310 sPairsCnts[sPairsCnt-1] = 1; 9311 startIs[sCnt] = ((i-1 < 0) ? (listCnt-1) : (i-1)); 9312 startPts[sCnt++] = [gThis pointWithNum:0]; 9313 /* search prevG for startPts[1] */ /* no prevG found - should be not possible ! */ 9314 prevI = ((i-1 < 0) ? (listCnt-1) : i-1); 9315 for (l=prevI; l != i; l--) 9316 { VGraphic *gr = [[ng list] objectAtIndex:l]; 9317 NSPoint e; 9318 9319 e = [gr pointWithNum:MAXINT]; 9320 if (SqrDistPoints(e, startPts[sCnt-1]) <= tol*tol) // prevG found 9321 { NSPoint s = [gr pointWithNum:0]; 9322 9323 startIs[sCnt-1] = l; // this is realy the right index ! 9324 9325 startIs[sCnt] = ((l-1 < 0) ? (listCnt-1) : (l-1)); 9326 startPts[sCnt++] = s; 9327 sPairsCnts[sPairsCnt-1] = 2; 9328 break; 9329 } 9330 if (!l) 9331 l = listCnt; // go around until i !! 9332 } 9333 } 9334 ePairsCnts[ePairsCnt-1] = 1; 9335 endIs[eCnt-1] = i; 9336 endPts[eCnt-1] = [gThis pointWithNum:MAXINT]; 9337 [[ng list] removeObjectAtIndex:i]; 9338 9339 /* check if we remove a second startPt !!!!!!!!! */ 9340 { int l, si0 = 0, spCnt = sPairsCnts[0]; 9341 9342 for (k=0; k < sPairsCnt; k++) 9343 { 9344 spCnt = sPairsCnts[k]; 9345 if (i == startIs[si0] /*|| (spCnt > 1 && i == startIs[si0+1])*/) 9346 { int removeI = si0; 9347 9348 if (spCnt > 1 && i == startIs[si0]) // remove both points ! 9349 { 9350 for (l=k; l < sPairsCnt-1; l++) 9351 sPairsCnts[l] = sPairsCnts[l+1]; 9352 sPairsCnt--; 9353 removeI = sCnt; // nothing more to remove 9354 for (l=si0; l < sCnt-2; l++) 9355 { 9356 startPts[l] = startPts[l+2]; 9357 startIs[l] = startIs[l+2]; 9358 } 9359 sCnt--; // we remove two points ! 9360// NSLog(@"VPath.m: -uniteWith: one startPt pair was currently removed"); 9361 } 9362 else if (spCnt > 1 && i == startIs[si0+1]) 9363 { 9364 break; 9365 sPairsCnts[k] = 1; 9366 removeI = si0+1; 9367 } 9368 else 9369 { 9370 for (l=k; l < sPairsCnt-1; l++) 9371 sPairsCnts[l] = sPairsCnts[l+1]; 9372 sPairsCnt--; 9373 removeI = si0; 9374 } 9375 /* remove startPts from startPts !!!!!!!!! */ 9376 for (l=removeI; l < sCnt-1; l++) 9377 { 9378 startPts[l] = startPts[l+1]; 9379 startIs[l] = startIs[l+1]; 9380 } 9381 sCnt--; 9382 break; 9383 } 9384 si0 += sPairsCnts[k]; 9385 } 9386 } 9387 /* correct all startIs/endIs behind i !! */ 9388 for (j=0; j < sCnt; j++) 9389 if (i <= startIs[j] && startIs[j]) startIs[j] -= 1; 9390 for (j=0; j < eCnt; j++) 9391 if (i < endIs[j] && endIs[j]) endIs[j] -= 1; 9392 9393 if (i < startI) 9394 startI--; 9395 i = (i-1 < -1) ? (listCnt-2) : (i-1); 9396 listCnt--; 9397 } 9398 /* close gap with graphics from splitListUg */ 9399 if ((!currentlyRemoved && removing == YES) // i+1 9400 || (removing == YES && !first && ((i+1 >= listCnt) ? (0) : (i+1)) == startI)) // last endpts to start gr 9401 { NSPoint s; 9402 9403 /* search nextG for endPts[1] no nextG found - startG == endG - only one Graphic ! */ 9404 if (!currentlyRemoved) 9405 { NSPoint e = [gThis pointWithNum:MAXINT]; 9406 9407 endIs[eCnt] = i; // ++++++++++++++++1 ((i+1 >= listCnt) ? 0 : i+1); 9408 endPts[eCnt++] = e; 9409 ePairsCnts[ePairsCnt-1] = 2; 9410 } 9411 else // currentlyRemoved -> i perhaps -1 9412 { int l, nextI = ((i+1 >= listCnt) ? 0 : i+1); 9413 9414 /* correct endIs[eCnt-1] !!! - we remove the last graphic in list to startI */ 9415 if (i+1 >= listCnt) // endIs[eCnt-1]++ ! - nextI wird i+2 ! 9416 { 9417 /* we have to correct only the index !!! */ 9418 for (l=nextI; l != ((i < 0) ? (listCnt-1) : i); l++) 9419 { VGraphic *gr = [[ng list] objectAtIndex:l]; 9420 9421 // if (k == i) break; // one time around 9422 s = [gr pointWithNum:0]; 9423 if (SqrDistPoints(s, endPts[eCnt-1]) <= tol*tol) // nextG found 9424 { //NSPoint e = [gr pointWithNum:MAXINT]; 9425 9426 endIs[eCnt-1] = l; 9427 //endPts[eCnt-1] = e; 9428 break; 9429 } 9430 if (l == listCnt-1) 9431 l = -1; // go around until i ! 9432 } 9433 //nextI = ((nextI+1 >= listCnt) ? 0 : nextI+1); 9434 } 9435 9436 for (l=nextI; l != ((i < 0) ? (listCnt-1) : i); l++) 9437 { VGraphic *gr = [[ng list] objectAtIndex:l]; 9438 9439 // if (k == i) break; // one time around 9440 s = [gr pointWithNum:0]; 9441 if (SqrDistPoints(s, endPts[eCnt-1]) <= tol*tol) // nextG found 9442 { NSPoint e = [gr pointWithNum:MAXINT]; 9443 9444 endIs[eCnt] = l; // ++++++++++++++++++++++1 9445 endPts[eCnt++] = e; 9446 ePairsCnts[ePairsCnt-1] = 2; 9447 break; 9448 } 9449 if (l == listCnt-1) 9450 l = -1; // go around until i ! 9451 } 9452 } 9453 removing = NO; 9454 } 9455 } 9456 9457 if (!removedFromNg || !removedFromUg) 9458 { 9459 /* look if graphics in splitListUg are identical with graphics in [ng list] */ 9460 for (i=0; i<[splitListUg count]; i++) 9461 { VGraphic *gi = [splitListUg objectAtIndex:i]; 9462 9463 for (j=0; j<listCnt; j++) 9464 { VGraphic *gj = [[ng list] objectAtIndex:j]; 9465 9466 if ([gi identicalWith:gj]) 9467 { [splitListUg removeObjectAtIndex:i]; 9468 /* korrect all uStartIs behind i */ 9469 for (k=0; k < uStartIsCnt; k++) 9470 if (uStartIs[k] > i) uStartIs[k] -= 1; 9471 i--; 9472 removedFromUg++; 9473 break; 9474 } 9475 } 9476 } 9477 /* we must check if we remove a hole subpath */ 9478 for (i=0; i< uStartIsCnt-1; i++) 9479 { 9480 if (uStartIs[i] == uStartIs[i+1]) 9481 { 9482 for (l=i; l < uStartIsCnt-1; l++) 9483 uStartIs[l] = uStartIs[l+1]; 9484 uStartIsCnt--; 9485 i--; // perhaps we remove two or three 9486 } 9487 } 9488 if (![splitListUg count]) 9489 { [pool release]; 9490 return ng; 9491 } 9492 } 9493 9494 if (!removedFromNg && !removedFromUg) 9495 { 9496 /* ug is'nt a path and not splitted now there are two possibilities 9497 * self is in ug or ug is in self else -> nothing to unite - NO 9498 */ 9499 if ( ![ug isKindOfClass:[VPath class]] && [splitListUg count] == 1 && ![ug isKindOfClass:[VGroup class]]) 9500 { NSPoint p; 9501 9502 [pool release]; 9503 /* ug is inside self -> self is ok can remove ug later */ 9504 [ug getPoint:&p at:0.4]; 9505 if ( [self isPointInside:p] ) 9506 return ng; 9507 9508 /* self is inside ug -> ug is it */ 9509 [[list objectAtIndex:0] getPoint:&p at:0.4]; 9510 if ( [(id)ug isPointInside:p] ) 9511 return [[ug copy] autorelease]; 9512 return nil; // nothing to unite 9513 } 9514 [pool release]; 9515 return nil; 9516 } 9517 9518 /* search graphics in splitListUg which close the gaps in ng */ 9519 /* our orientation we get through the startPts / sePairsCnt */ 9520 for (i=0; i< sPairsCnt; i++) 9521 { int sptCnt = sPairsCnts[i], eptCnt = ePairsCnts[i], sIx = 1; 9522 int sIs[2], eIs[2], sI0 = -1, eI0 = -1, endI = -1; 9523 NSPoint sPts[2], ePts[2] = {NSZeroPoint, NSZeroPoint}; 9524 9525 /* count with i and sePairsCnts to the current startIs/startPts index */ 9526 sI0 = 0; 9527 for (j=0; j < i; j++) 9528 sI0 += sPairsCnts[j]; 9529 9530 sIs[0] = startIs[sI0]; 9531 sPts[0] = startPts[sI0]; 9532 if (sptCnt == 2) 9533 { sIs[1] = startIs[sI0+1]; 9534 sPts[1] = startPts[sI0+1]; 9535 } 9536 9537 sIx = 1; // is the startIx of the next subPath ! 9538 for (j=0; j<[splitListUg count]; j++) 9539 { VGraphic *gj = [splitListUg objectAtIndex:j]; 9540 NSPoint sj, ej; 9541 9542 if (j >= uStartIs[sIx]) 9543 sIx++; 9544 9545 sj = [gj pointWithNum:0]; 9546 ej = [gj pointWithNum:MAXINT]; 9547 9548 if (pointWithToleranceInArray(sj, tol, sPts, sptCnt) || 9549 pointWithToleranceInArray(ej, tol, sPts, sptCnt)) 9550 { int closeK = -1, si = 0; 9551 BOOL ejIsNearer = NO, gjRemoved = NO, jumpOverOneEnd = NO; 9552 NSPoint closePt; 9553 9554 /* check if gj is a double graphic */ 9555 for (k=0; k < sPairsCnt; k++) 9556 { 9557 if ((startIs[si] < listCnt && 9558 [gj identicalWith:[[ng list] objectAtIndex:startIs[si]]])) 9559 //(endIs[si] < listCnt && 9560 // [gj identicalWith:[[ng list] objectAtIndex:endIs[si]]]) 9561 { 9562 [splitListUg removeObjectAtIndex:j]; 9563 /* korrect all uStartIs behind j */ 9564 for (l=0; l < uStartIsCnt; l++) 9565 if (uStartIs[l] > j) uStartIs[l] -= 1; 9566 j--; 9567 gjRemoved = YES; 9568 break; 9569 } 9570 si += sPairsCnts[k]; 9571 } 9572 if (gjRemoved) 9573 continue; 9574 9575 /* ej == sPts[1] && sj != sPts[0] and sPts[1] is also in endPts */ 9576 if (SqrDistPoints(ej, sPts[1]) < tol*tol && SqrDistPoints(sj, sPts[0]) > tol*tol) 9577 { VGraphic *gjn; 9578 NSPoint sjn; 9579 9580 /* ej == sPts[1] && sPts[1] is also in endPts */ 9581 if (pointWithToleranceInArray(ej, tol, endPts, eCnt)) 9582 continue; 9583 // ej to sPts[1] check if gjn s is to sPts[0] 9584 gjn = [splitListUg objectAtIndex:((j+1 >= uStartIs[sIx]) ? uStartIs[sIx-1] : (j+1))]; 9585 sjn = [gjn pointWithNum:0]; 9586 if (SqrDistPoints(sjn, sPts[0]) < tol*tol) 9587 continue; // took sj to sPts[0] 9588 } 9589 9590 if (pointWithToleranceInArray(sj, tol, sPts, sptCnt) && 9591 pointWithToleranceInArray(ej, tol, sPts, sptCnt)) 9592 { float ds, de; 9593 9594 /* check if ej is nearer to sPts -> search backward / take next start */ 9595 ds = SqrDistPoints(sj, sPts[0]); 9596 de = SqrDistPoints(ej, sPts[0]); 9597 if (sptCnt > 1) 9598 { ds = Min(ds, SqrDistPoints(sj, sPts[1])); 9599 de = Min(de, SqrDistPoints(ej, sPts[1])); 9600 } 9601 if (de < ds) 9602 ejIsNearer = YES; 9603 9604 if (SqrDistPoints(ej, sPts[0]) < tol*tol) 9605 { VGraphic *gjn; 9606 NSPoint sjn; 9607 9608 /* ej to sPts[0] check if gjn s is to sPts[0] */ 9609 gjn = [splitListUg objectAtIndex:((j+1 >= uStartIs[sIx]) ? uStartIs[sIx-1] : (j+1))]; 9610 sjn = [gjn pointWithNum:0]; 9611 if (SqrDistPoints(sjn, sPts[0]) < tol*tol) 9612 continue; // took gjn with sj to sPts[0] 9613 } 9614 } 9615 /* search forward in splitListUg */ 9616 if (!ejIsNearer && pointWithToleranceInArray(sj, tol, sPts, sptCnt)) 9617 { BOOL firstK = YES; 9618 NSPoint prevE = sj; // first k == j == startPt 9619 9620 closeK = -1; 9621 for ( k=j; firstK || k != j; k++ ) 9622 { VGraphic *gk, *gkn; 9623 NSPoint sk, ek, skn, ekn; 9624 9625 k = (k >= uStartIs[sIx]) ? uStartIs[sIx-1] : k; 9626 if (!firstK && k == j) 9627 break; 9628 firstK = NO; 9629 9630 gk = [splitListUg objectAtIndex:k]; 9631 sk = [gk pointWithNum:0]; 9632 ek = [gk pointWithNum:MAXINT]; 9633 9634 if (SqrDistPoints(prevE, sk) >= tol*tol) 9635 break; // nothing to close gap in ug splitlist 9636 9637 if (pointWithToleranceInArray(ek, tol, endPts, eCnt)) 9638 { 9639 gkn = [splitListUg objectAtIndex:((k+1 >= uStartIs[sIx]) ? uStartIs[sIx-1] : (k+1))]; 9640 skn = [gkn pointWithNum:0]; 9641 ekn = [gkn pointWithNum:MAXINT]; 9642 if (SqrDistPoints(ek, skn) < tol*tol && 9643 pointWithToleranceInArray(ekn, tol, endPts, eCnt)) 9644 { float dek = MAXCOORD, dekn = MAXCOORD, d; 9645 BOOL gknIsDouble = NO, eknIs0ePt = YES; 9646 int eki = 0, ekni = 0; 9647 9648 for (l=0; l < eCnt; l++) 9649 { 9650 if ((d=SqrDistPoints(ekn, endPts[l])) < dekn) 9651 { dekn = d; 9652 ekni = l; 9653 } 9654 if ((d=SqrDistPoints(ek, endPts[l])) < dek) 9655 { dek = d; 9656 eki = l; 9657 } 9658 } 9659 /* both endPts must be 0 ePts ! to use this !! */ 9660 if (dekn <= dek) 9661 { int eknI = 0, epCnt; // ekI = 0 9662 9663 for (l=0; l < ePairsCnt; l++) 9664 { 9665 epCnt = ePairsCnts[l]; 9666 if (eknI == ekni || (epCnt == 2 && eknI+1 == ekni)) 9667 { 9668 if (epCnt == 2 && eknI+1 == ekni) 9669 eknIs0ePt = NO; 9670 break; 9671 } 9672 eknI += ePairsCnts[l]; 9673 /*if (ekI == eki || (epCnt == 2 && ekI+1 == eki)) 9674 { 9675 if (epCnt == 2 && ekI+1 == eki) 9676 ekIs0ePt = NO; 9677 break; 9678 } 9679 ekI += ePairsCnts[l];*/ 9680 } 9681 } 9682 /* check if gkn is a double graphic */ 9683 si = 0; 9684 for (l=0; l < sPairsCnt; l++) 9685 { 9686 if ((startIs[si] < listCnt && 9687 [gkn identicalWith:[[ng list] objectAtIndex:startIs[si]]])) 9688 //(endIs[si] < listCnt && 9689 // [gkn identicalWith:[[ng list] objectAtIndex:endIs[si]]]) 9690 { 9691 gknIsDouble = YES; 9692 break; 9693 } 9694 si += sPairsCnts[l]; 9695 } 9696 if (!gknIsDouble && eknIs0ePt && (dekn < dek || (dekn <= dek && eki != ekni))) 9697 { prevE = ek; 9698 if (eki != ekni) // this is when we jump over an [ng list] graphic !! 9699 jumpOverOneEnd = YES; 9700 continue; // next gk is our close gk 9701 } 9702 } 9703 closePt = ek; 9704 closeK = k; 9705 break; // we can close the gap 9706 } 9707 prevE = ek; 9708 } 9709 } 9710 else // backward searching 9711 { BOOL firstK = YES; 9712 NSPoint prevS = ej; // first k == j == startPt 9713 9714 closeK = -1; 9715 for ( k=j; firstK || k != j; k-- ) 9716 { VGraphic *gk, *gkp; 9717 NSPoint sk, ek, skp, ekp; 9718 9719 k = (k < uStartIs[sIx-1]) ? (uStartIs[sIx]-1) : k; 9720 9721 if (!firstK && k == j) 9722 break; 9723 firstK = NO; 9724 9725 gk = [splitListUg objectAtIndex:k]; 9726 sk = [gk pointWithNum:0]; 9727 ek = [gk pointWithNum:MAXINT]; 9728 9729 if (SqrDistPoints(prevS, ek) >= tol*tol) 9730 break; // nothing to close gap in path 9731 if (pointWithToleranceInArray(sk, tol, endPts, eCnt)) 9732 { 9733 gkp = [splitListUg objectAtIndex:((k-1 < uStartIs[sIx-1]) ? (uStartIs[sIx]-1) : (k-1))]; 9734 skp = [gkp pointWithNum:0]; 9735 ekp = [gkp pointWithNum:MAXINT]; 9736 if (SqrDistPoints(sk, ekp) < tol*tol && 9737 pointWithToleranceInArray(skp, tol, endPts, eCnt)) 9738 { float dek = MAXCOORD, dekp = MAXCOORD, d; 9739 BOOL gkpIsDouble = NO, ekpIs0ePt = YES; 9740 int eki = 0, ekpi = 0; 9741 9742 for (l=0; l < eCnt; l++) 9743 { if ((d=SqrDistPoints(ekp, endPts[l])) < dekp) 9744 { dekp = d; 9745 ekpi = l; 9746 } 9747 if ((d=SqrDistPoints(ek, endPts[l])) < dek) 9748 { dek = d; 9749 eki = l; 9750 } 9751 } 9752 /* both endPts must be 0 ePts ! to use this !! */ 9753 if (dekp <= dek) 9754 { int ekpI = 0, epCnt; 9755 9756 for (l=0; l < ePairsCnt; l++) 9757 { 9758 epCnt = ePairsCnts[l]; 9759 if (ekpI == ekpi || (epCnt == 2 && ekpI+1 == ekpi)) 9760 { 9761 if (epCnt == 2 && ekpI+1 == ekpi) 9762 ekpIs0ePt = NO; 9763 break; 9764 } 9765 ekpI += ePairsCnts[l]; 9766 } 9767 } 9768 /* check if gkp is a double graphic */ 9769 si = 0; 9770 for (l=0; l < sPairsCnt; l++) 9771 { 9772 if ((startIs[si] < listCnt && 9773 [gkp identicalWith:[[ng list] objectAtIndex:startIs[si]]])) 9774 //(endIs[si] < listCnt && 9775 // [gkp identicalWith:[[ng list] objectAtIndex:endIs[si]]]) 9776 { 9777 gkpIsDouble = YES; 9778 break; 9779 } 9780 si += sPairsCnts[l]; 9781 } 9782 if (!gkpIsDouble && ekpIs0ePt && (dekp < dek || (dekp <= dek && eki != ekpi))) 9783 { prevS = sk; 9784 if (eki != ekpi) // this is when we jump over an [ng list] graphic !! 9785 jumpOverOneEnd = YES; 9786 continue; // next gk is our close gk 9787 } 9788 } 9789 closePt = sk; 9790 closeK = k; 9791 break; // we can close the gap 9792 } 9793 prevS = sk; 9794 } 9795 } 9796 if (closeK != -1) 9797 { int added = 0, from = closeK, to = j, m=0; 9798 float dist = MAXCOORD; 9799 9800 /* get endI, eptCnt and eI0 */ 9801 if (pointWithToleranceInArray(closePt, tol, endPts, eCnt)) 9802 { 9803 for (k=0; k < eCnt; k++) 9804 if (SqrDistPoints(closePt, endPts[k]) < tol*tol) 9805 { endI = k; 9806 break; 9807 } 9808 } 9809 else 9810 NSLog(@"VPath.m: -uniteWith: this should be not possible"); 9811 9812 eI0 = 0; 9813 for (k=0; k < ePairsCnt; k++) 9814 { 9815 eptCnt = ePairsCnts[k]; 9816 if (eI0 == endI || (eptCnt == 2 && eI0+1 == endI)) 9817 break; 9818 eI0 += ePairsCnts[k]; 9819 } 9820 eIs[0] = endIs[eI0]; 9821 ePts[0] = endPts[eI0]; 9822 if (eptCnt == 2) 9823 { eIs[1] = endIs[eI0+1]; 9824 ePts[1] = endPts[eI0+1]; 9825 } 9826 9827 /* here we remove this endPair from endPts */ 9828 /* k is our ePairs index - eI0 is our endPts/endIs index - eptCnt 1 or 2 epts */ 9829 /* remove endPts (1 or 2) / remove endIs (1 or 2) */ 9830 /* remove ePairsCnts (1) */ 9831 /* ePairsCnt -- */ 9832 if ( eptCnt == 2 ) 9833 { for (m=eI0+1; m < eCnt-1; m++) 9834 { endIs[m] = endIs[m+1]; 9835 endPts[m] = endPts[m+1]; 9836 } 9837 eCnt--; 9838 } 9839 for (m=eI0; m < eCnt-1; m++) 9840 { endIs[m] = endIs[m+1]; 9841 endPts[m] = endPts[m+1]; 9842 } 9843 eCnt--; 9844 for (m=k; m < ePairsCnt-1; m++) 9845 ePairsCnts[m] = ePairsCnts[m+1]; 9846 ePairsCnt--; 9847 9848 /* little gap will close from optimize list correctlier */ 9849 if (SqrDistPoints(sPts[0], ePts[0]) < tol*tol || 9850 (!pointWithToleranceInArray(sj, tol, sPts, sptCnt) && SqrDistPoints(closePt, ej) < tol*tol) || 9851 (pointWithToleranceInArray(sj, tol, sPts, sptCnt) && SqrDistPoints(closePt, sj) < tol*tol)) 9852 continue; // little gap will close from optimize list correctlier 9853 9854 /* if eCnt/sCnt > 1 9855 * && ej/sj... gleich zu ..Pts[1] -> ..Is[0] aus [ng list] removen !!!!!!!!!! */ 9856 if (sptCnt > 1 && 9857 (((dist=SqrDistPoints(ej, sPts[1])) < tol*tol && dist < SqrDistPoints(ej, sPts[0])) || 9858 ((dist=SqrDistPoints(sj, sPts[1])) < tol*tol && dist < SqrDistPoints(sj, sPts[0]))) && 9859 !pointWithToleranceInArray(sPts[1], tol, endPts, eCnt)) 9860 { /* remove object at sIs[0] from [ng list] */ 9861 [[ng list] removeObjectAtIndex:sIs[0]]; 9862 if (startI > sIs[0]) 9863 startI--; 9864 9865 /* correct all startIs/endIs behind sIs[0] !! */ 9866 for (k=0; k < sCnt; k++) 9867 if (startIs[k] >= sIs[0] && startIs[k]) startIs[k] -= 1; 9868 for (k=0; k < eCnt; k++) 9869 if (endIs[k] > sIs[0] && endIs[k]) endIs[k] -= 1; 9870 if (eIs[0] > sIs[0]) eIs[0] -= 1; 9871 sIs[0]--; 9872 listCnt--; 9873 } 9874 9875 if ((eptCnt > 1 && 9876 ((dist=SqrDistPoints(closePt, ePts[1])) < tol*tol && 9877 dist < SqrDistPoints(closePt, ePts[0]) && // tol*tol 9878 !pointWithToleranceInArray(ePts[1], tol, startPts, sCnt))) 9879 || (jumpOverOneEnd && eptCnt > 1 && SqrDistPoints(closePt, ePts[0]) < tol*tol)) 9880 { int ri = eIs[0]; 9881 9882 if (jumpOverOneEnd && eptCnt > 1 && SqrDistPoints(closePt, ePts[0]) < tol*tol) 9883 { 9884 if (sIs[0]+1 == eIs[0]-1) 9885 ri = sIs[0]+1; 9886 else if (Diff(sIs[0], eIs[0]) == 1 || Diff(sIs[0], eIs[0]) > 3) 9887 ri = -1; // nothing to remove 9888 else 9889 { ri = -1; 9890 NSLog(@"VPAth.m -uniteWith: not yet implemented"); 9891 } 9892 } 9893 if (ri != -1) 9894 { /* remove object at ri (eIs[0]) from [ng list] */ 9895 [[ng list] removeObjectAtIndex:ri]; 9896 9897 /* correct all startIs/endIs befor ri !! */ 9898 for (k=0; k < sCnt; k++) 9899 if (startIs[k] >= ri && startIs[k]) startIs[k] -= 1; 9900 for (k=0; k < eCnt; k++) 9901 if (endIs[k] > ri && endIs[k]) endIs[k] -= 1; 9902 if (sIs[0] >= ri) sIs[0] -= 1; 9903 listCnt--; 9904 } 9905 } 9906 9907 if (!pointWithToleranceInArray(sj, tol, sPts, sptCnt)) // war ej ohne ! 9908 { 9909 //if (pointWithToleranceInArray(ej, tol, sPts, sptCnt)) 9910 { to = closeK; 9911 from = j; 9912 } 9913 /* insert graphics forward */ /* dont stop with 0 (k) <= -1 (from) */ 9914 for (k=to; k <= ((to > from) ? (uStartIs[sIx]-1) : (from)) && from >= uStartIs[sIx-1]; k++) 9915 { VGraphic *gk = [splitListUg objectAtIndex:k]; 9916 9917 [gk changeDirection]; 9918 [[ng list] insertObject:gk atIndex:((sIs[0]==[[ng list] count]) ? (sIs[0]) : (sIs[0]+1))]; 9919 added++; 9920 if (k <= from) { from--; to--; } 9921 [splitListUg removeObjectAtIndex:k]; 9922 /* korrect all uStartIs behind k */ 9923 for (l=0; l < uStartIsCnt; l++) 9924 if (uStartIs[l] > k) uStartIs[l] -= 1; 9925 k--; 9926 if (k+1 >= uStartIs[sIx] && to > from) 9927 { k = uStartIs[sIx-1] - 1; // 0 - 1 9928 to = from-1; // little hack mh - second part until list end ! 9929 } 9930 } 9931 } 9932 else 9933 { //if (pointWithToleranceInArray(sj, tol, sPts, sptCnt)) 9934 { to = closeK; 9935 from = j; 9936 } 9937 /* insert graphics from backward (closeK-j) */ 9938 for (k=to; k >= ((!to || to < from) ? uStartIs[sIx-1] : from); k--) 9939 { VGraphic *gk = [splitListUg objectAtIndex:k]; 9940 9941 [[ng list] insertObject:gk atIndex:((sIs[0]==[[ng list] count]) ? (sIs[0]) : (sIs[0]+1))]; 9942 [splitListUg removeObjectAtIndex:k]; 9943 /* korrect all uStartIs behind k */ 9944 for (l=0; l < uStartIsCnt; l++) 9945 if (uStartIs[l] > k) uStartIs[l] -= 1; 9946 added++; 9947 if (k < from) { from--; to--; } 9948 9949 if (k <= uStartIs[sIx-1] && to < from) // we step over 0 - second part until from ! 9950 { k = uStartIs[sIx]; // [splitListUg count] 9951 to = from+1; // little hack mh - second part until from ! 9952 } 9953 } 9954 } 9955 /* we must check if we remove a hole subpath */ 9956 for (k=0; k< uStartIsCnt-1; k++) 9957 { 9958 if (uStartIs[k] == uStartIs[k+1]) 9959 { 9960 for (l=k; l < uStartIsCnt-1; l++) 9961 uStartIs[l] = uStartIs[l+1]; 9962 uStartIsCnt--; 9963 k--; // perhaps we remove two or three 9964 } 9965 } 9966 /* correct all startIs/endIs behind sIs[0] !! */ 9967 for (k=0; k < sCnt; k++) 9968 if (startIs[k] >= sIs[0]) startIs[k] += added; 9969 for (k=0; k < eCnt; k++) 9970 if (endIs[k] >= sIs[0]) endIs[k] += added; 9971 9972 listCnt += added; 9973 break; 9974 } 9975 } 9976 } 9977 } 9978 9979 /* add closed shapes from splitListUg to ng */ 9980 if (uStartIsCnt > 1 && [splitListUg count]) 9981 { 9982 for (i=0; i<uStartIsCnt-1; i++) 9983 { 9984 if (uStartIs[i+1]-1 == uStartIs[i] && [splitListUg count] == 1) // only one object 9985 { VGraphic *g = [splitListUg objectAtIndex:uStartIs[i]]; 9986 9987 if ([g isKindOfClass:[VArc class]] && Abs([(VArc*)g angle]) == 360.0) 9988 [[ng list] addObject:[splitListUg objectAtIndex:uStartIs[i]]]; 9989 continue; 9990 } 9991 else if ([splitListUg count] >= uStartIs[i+1]-1) 9992 { VGraphic *gs = [splitListUg objectAtIndex:uStartIs[i]]; 9993 VGraphic *ge = [splitListUg objectAtIndex:uStartIs[i+1]-1]; 9994 NSPoint s, e; 9995 9996 s = [gs pointWithNum:0]; 9997 e = [ge pointWithNum:MAXINT]; 9998 if (SqrDistPoints(s, e) <= tol*tol) /*&& (sPairsCnt || (!sPairsCnt && !removedFromUg))*/ 9999 { BOOL doNotAdd = NO; 10000 10001 for (j=uStartIs[i]; j < uStartIs[i+1]-1; j++) 10002 { VGraphic *gs = [splitListUg objectAtIndex:j]; 10003 VGraphic *ge = [splitListUg objectAtIndex:j+1]; 10004 NSPoint s, e; 10005 10006 e = [gs pointWithNum:MAXINT]; 10007 s = [ge pointWithNum:0]; 10008 if (SqrDistPoints(e, s) <= tol*tol) 10009 continue; 10010 else 10011 { doNotAdd = YES; 10012 break; 10013 } 10014 } 10015 if (doNotAdd) 10016 continue; 10017 10018 for (j=uStartIs[i]; j < uStartIs[i+1]; j++) 10019 [[ng list] addObject:[splitListUg objectAtIndex:j]]; 10020 } 10021 } 10022 } 10023 } 10024 10025 if (sPairsCnt > 1) // aukommentieren fuer debugging zwecke !!!!!!!!!!!!!!!! 10026 [self optimizeList:[ng list]]; 10027 10028 [pool release]; 10029 10030 return ng; 10031} 10032 10033/* return self - if self is completely inside cg 10034 * return ug - if cg is completely inside self 10035 * return the intersection path of both 10036 */ 10037- (id)clippedFrom:(VGraphic*)cg 10038{ int i, j, cnt, nothingRemoved = 0; 10039 VPath *ng; 10040 NSMutableArray *splitListG, *splitListUg; 10041 NSAutoreleasePool *pool; 10042 10043 if ( ![cg isKindOfClass:[VPath class]] && ![cg isKindOfClass:[VArc class]] && ![cg isKindOfClass:[VPolyLine class]] 10044 && ![cg isKindOfClass:[VRectangle class]] && ![cg isKindOfClass:[VGroup class]] ) 10045 return NO; 10046 10047 ng = [VPath path]; 10048 [ng setColor:[self color]]; 10049 [ng setFilled:filled optimize:NO]; 10050 [ng setWidth:0.0]; 10051 [ng setSelected:NO]; 10052 10053 /* split self */ 10054 if ( (splitListG = [self getListOfObjectsSplittedFromGraphic:cg]) ) 10055 [ng setList:splitListG optimize:NO]; 10056 10057 pool = [NSAutoreleasePool new]; 10058 10059 if ( ![[ng list] count] ) 10060 for (i=0; i<(int)[list count]; i++) 10061 [[ng list] addObject:[[[list objectAtIndex:i] copy] autorelease]]; 10062 10063 /* split cg */ 10064 if ( !(splitListUg = [cg getListOfObjectsSplittedFromGraphic:self]) ) 10065 { 10066 splitListUg = [NSMutableArray array]; 10067 if ( [cg isKindOfClass:[VPath class]] ) 10068 for (i=0; i<(int)[[(VPath*)cg list] count]; i++) 10069 [splitListUg addObject:[[[[(VPath*)cg list] objectAtIndex:i] copy] autorelease]]; 10070 else 10071 [splitListUg addObject:[[cg copy] autorelease]]; 10072 } 10073 10074 /* cg is'nt a path and not splitted now there are two possibilities 10075 * self is in cg or cg is in self else -> nothing to unite - NO 10076 */ 10077 if ( ![cg isKindOfClass:[VPath class]] && [splitListUg count] == 1 && ![cg isKindOfClass:[VGroup class]]) 10078 { NSPoint p; 10079 [pool release]; 10080 10081 /* return self - if self is completely inside cg 10082 * return cg - if cg is completely inside self 10083 */ 10084 /* cg is inside self */ 10085 [cg getPoint:&p at:0.4]; 10086 if ( [self isPointInside:p] ) 10087 return [[cg copy] autorelease]; 10088 10089 /* self is inside cg -> self is it */ 10090 [[list objectAtIndex:0] getPoint:&p at:0.4]; 10091 if ( [(id)cg isPointInside:p] ) 10092 return ng; 10093 return NO; /* nothing to clip */ 10094 } 10095 10096 /* now remove the graphictiles from cg wich are outside 10097 * if no tile is removed -> NO 10098 */ 10099 { HiddenArea *hiddenArea = [HiddenArea new]; 10100 10101 /* return self - if self is completely inside cg 10102 * return cg - if cg is completely inside self 10103 */ 10104 if ( ![hiddenArea removeGraphics:splitListUg outside:self] ) 10105 nothingRemoved++; 10106 10107 /* now remove the graphic tiles from ng(self splitted) wich are outside or on cg */ 10108 if ( ![hiddenArea removeGraphics:[ng list] outside:cg] && nothingRemoved ) 10109 { [hiddenArea release]; 10110 [pool release]; 10111 return ng; // self comletly inside cg 10112 } 10113 10114 /* add graphics from splitListUg to ng list */ 10115 for (i=0; i<(int)[splitListUg count]; i++) 10116 [[ng list] addObject:[[[splitListUg objectAtIndex:i] copy] autorelease]]; 10117 10118 /* we must remove identical graphics in list */ 10119 cnt = ([[ng list] count]-[splitListUg count]); 10120 10121 /* we check only added objects !!!!!!!!!! */ 10122 for (i=[[ng list] count]-1; i >= cnt; i--) 10123 { VGraphic *g = [[ng list] objectAtIndex:i]; 10124 10125 for (j=0; j<(int)[[ng list] count]; j++) 10126 { VGraphic *g2 = [[ng list] objectAtIndex:j]; 10127 10128 if ( g2 == g ) 10129 continue; 10130 if ( [g2 identicalWith:g] ) 10131 { 10132 [[ng list] removeObject:g]; 10133 break; 10134 } 10135 } 10136 } 10137 10138 [hiddenArea removeSingleGraphicsInList:[ng list] :[cg bounds]]; 10139 [hiddenArea release]; 10140 } 10141 10142 [pool release]; 10143 return ng; 10144} 10145 10146- (void)encodeWithCoder:(NSCoder *)aCoder 10147{ 10148 [super encodeWithCoder:aCoder]; 10149 [aCoder encodeObject:list]; // [aCoder encodeValuesOfObjCTypes:"@", &list]; 10150 [aCoder encodeValuesOfObjCTypes:"c", &isDirectionCCW]; 10151 [aCoder encodeValuesOfObjCTypes:"i", &filled]; // 2002-07-07 10152 [aCoder encodeObject:fillColor]; 10153 [aCoder encodeObject:endColor]; 10154 [aCoder encodeValuesOfObjCTypes:"ff", &graduateAngle, &stepWidth]; 10155 //[aCoder encodeValuesOfObjCTypes:"{NSPoint=ff}", &radialCenter]; 10156 [aCoder encodePoint:radialCenter]; // 2012-01-08 10157} 10158- (id)initWithCoder:(NSCoder *)aDecoder 10159{ int version; 10160 10161 [super initWithCoder:aDecoder]; 10162 version = [aDecoder versionForClassName:@"VPath"]; 10163 list = [[aDecoder decodeObject] retain]; // [aDecoder decodeValuesOfObjCTypes:"@", &list]; 10164 if ( version < 1 ) 10165 [aDecoder decodeValuesOfObjCTypes:"c", &filled]; 10166 else if (version < 2) // 07.04.98 10167 [aDecoder decodeValuesOfObjCTypes:"cc", &filled, &isDirectionCCW]; 10168 else // 2002-07-07 10169 { [aDecoder decodeValuesOfObjCTypes:"c", &isDirectionCCW]; 10170 [aDecoder decodeValuesOfObjCTypes:"i", &filled]; 10171 fillColor = [[aDecoder decodeObject] retain]; 10172 endColor = [[aDecoder decodeObject] retain]; 10173 [aDecoder decodeValuesOfObjCTypes:"ff", &graduateAngle , &stepWidth]; 10174 //[aDecoder decodeValuesOfObjCTypes:"{NSPoint=ff}", &radialCenter]; 10175 radialCenter = [aDecoder decodePoint]; // 2012-01-08 10176 } 10177 10178 if ( version < 2 ) 10179 { UPath fillUPath; 10180 10181 [aDecoder decodeValuesOfObjCTypes:"ii", &fillUPath.num_ops, &fillUPath.num_pts]; 10182 if ( fillUPath.num_ops ) // old 10183 { 10184 fillUPath.ops = malloc((fillUPath.num_ops+10) * sizeof(char)); 10185 fillUPath.pts = malloc((fillUPath.num_pts+10) * sizeof(float)); 10186 [aDecoder decodeArrayOfObjCType:"c" count:fillUPath.num_ops at:fillUPath.ops]; 10187 [aDecoder decodeArrayOfObjCType:"f" count:fillUPath.num_pts at:fillUPath.pts]; 10188 free(fillUPath.ops); 10189 free(fillUPath.pts); 10190 } 10191 } 10192 10193 selectedObject = -1; 10194 graduateDirty = YES; 10195 graduateList = nil; 10196 10197 return self; 10198} 10199 10200/* archiving with property list 10201 */ 10202- (id)propertyList 10203{ NSMutableDictionary *plist = [super propertyList]; 10204 10205 [plist setObject:propertyListFromArray(list) forKey:@"list"]; 10206 if (filled) 10207 [plist setObject:propertyListFromInt(filled) forKey:@"filled"]; 10208 // FIXME: it is not reliable to compare with "!=", it needs "isEqual:" 10209 //if (fillColor != [NSColor blackColor]) 10210 [plist setObject:propertyListFromNSColor(fillColor) forKey:@"fillColor"]; 10211 if (endColor != [NSColor blackColor]) 10212 [plist setObject:propertyListFromNSColor(endColor) forKey:@"endColor"]; 10213 if (graduateAngle) 10214 [plist setObject:propertyListFromFloat(graduateAngle) forKey:@"graduateAngle"]; 10215 if (stepWidth != 7) 10216 [plist setObject:propertyListFromFloat(stepWidth) forKey:@"stepWidth"]; 10217 if (!(radialCenter.x == 0.5 && radialCenter.y == 0.5)) 10218 [plist setObject:propertyListFromNSPoint(radialCenter) forKey:@"radialCenter"]; 10219 return plist; 10220} 10221- (id)initFromPropertyList:(id)plist inDirectory:(NSString *)directory 10222{ 10223 [super initFromPropertyList:plist inDirectory:directory]; 10224 list = arrayFromPropertyList([plist objectForKey:@"list"], directory, [self zone]); 10225 filled = [plist intForKey:@"filled"]; 10226 if (!filled && [plist objectForKey:@"filled"]) 10227 filled = 1; 10228 // Note: VPath, VArc, VRectangle 10229 // if fillColor == nil, we may have an old file where color == fillColor 10230 if (!(fillColor = colorFromPropertyList([plist objectForKey:@"fillColor"], [self zone]))) 10231 { [self setFillColor:[NSColor blackColor]/*[color copy]*/]; 10232 //[self setFillColor:[color copy]]; // loads old file format with color only 10233 } 10234 if (!(endColor = colorFromPropertyList([plist objectForKey:@"endColor"], [self zone]))) 10235 [self setEndColor:[NSColor blackColor]]; 10236 graduateAngle = [plist floatForKey:@"graduateAngle"]; 10237 if ( !(stepWidth = [plist floatForKey:@"stepWidth"])) 10238 stepWidth = 7.0; // default; 10239 if ([plist objectForKey:@"radialCenter"]) 10240 radialCenter = pointFromPropertyList([plist objectForKey:@"radialCenter"]); 10241 else 10242 radialCenter = NSMakePoint(0.5, 0.5); // default 10243 10244 selectedObject = -1; 10245 graduateDirty = YES; 10246 graduateList = nil; 10247 return self; 10248} 10249 10250 10251- (void)dealloc 10252{ 10253 [fillColor release]; 10254 [endColor release]; 10255 [list release]; 10256 list = nil; 10257 if (graduateList) 10258 { [graduateList release]; 10259 graduateList = nil; 10260 } 10261 [super dealloc]; 10262} 10263 10264@end 10265