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