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