libs-gui/Source/NSBezierPath.m

1833 lines
39 KiB
Mathematica
Raw Normal View History

/*
NSBezierPath.m
The NSBezierPath class
Copyright (C) 1999 Free Software Foundation, Inc.
Author: Enrico Sersale <enrico@imago.ro>
Date: Dec 1999
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111 - 1307, USA.
*/
#include <gnustep/gui/config.h>
#include <AppKit/AppKit.h>
#include <math.h>
#ifndef PI
#define PI 3.1415926535897932384626433
#endif
#define PMAX 10000
typedef struct {
NSPoint p1;
NSPoint p2;
} Line;
typedef struct {
NSPoint p;
double t;
} pointOnCurve;
typedef enum {
INTERIOR,
EXTERIOR,
VERTEX,
ONBORDER
} pointPosition;
pointPosition inPath(NSBezierPath *aPath, NSPoint p);
void subdiv(int degree, float coeff[], float t, float bleft[], float bright[]);
NSPoint rotatePoint(NSPoint p, NSPoint centre, float angle);
@interface PathElement : NSObject <NSCopying, NSCoding>
{
NSPoint p[3];
NSBezierPathElement type;
}
+ (PathElement *)pathElement;
- (void)setType:(NSBezierPathElement)t;
- (void)setPointAtIndex:(int)index toPoint:(NSPoint)aPoint;
- (NSBezierPathElement)type;
- (NSPoint *)points;
@end
@interface NSBezierPath (PrivateMethods)
- (void)setBounds:(NSRect)br;
- (void)setControlPointBounds:(NSRect)br;
@end
@interface GSBezierPath : NSBezierPath
{
NSMutableArray *pathElements;
NSMutableArray *subPaths;
NSPoint draftPolygon[PMAX];
int pcount;
NSPoint currentPoint;
BOOL cachesBezierPath;
NSImage *cacheimg;
BOOL isValid;
}
- (void)setCurrentPoint:(NSPoint)aPoint;
- (BOOL)isPathElement:(PathElement *)elm1 onPathElement:(PathElement *)elm2;
- (NSMutableArray *)pathElements;
- (PathElement *)moveToElement;
- (PathElement *)lastElement;
- (int)indexOfElement:(PathElement *)element;
- (PathElement *)elementPrecedingElement:(PathElement *)element;
- (void)calculateDraftPolygon;
- (NSPoint *)draftPolygon;
- (int)pcount;
- (void)movePathToPoint:(NSPoint)p;
- (void)rotateByAngle:(float)angle center:(NSPoint)center;
- (pointOnCurve)pointOnPathSegmentOfElement:(PathElement *)elm
nearestToPoint:(NSPoint)p;
- (PathElement *)pathElementSubdividingPathAtPoint:(NSPoint)p
pathSegmentOwner:(PathElement *)owner;
@end
@implementation PathElement
+ (PathElement *)pathElement
{
PathElement *element = [[self alloc] init];
[element setPointAtIndex: 0 toPoint: NSMakePoint(0, 0)];
return AUTORELEASE(element);
}
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[aCoder encodeValueOfObjCType: @encode(NSBezierPathElement) at: &type];
[aCoder encodeValueOfObjCType: @encode(NSPoint *) at: &p];
}
- (id)initWithCoder:(NSCoder *)aCoder
{
[aCoder decodeValueOfObjCType: @encode(NSBezierPathElement) at: &type];
[aCoder decodeValueOfObjCType: @encode(NSPoint *) at: &p];
return self;
}
- (id)copyWithZone:(NSZone *)zone
{
PathElement *element = [[PathElement allocWithZone: zone] init];
element->type = type;
if(type == NSMoveToBezierPathElement || type == NSLineToBezierPathElement) {
element->p[0].x = p[0].x;
element->p[0].y = p[0].y;
} else if(type == NSCurveToBezierPathElement) {
element->p[0].x = p[0].x;
element->p[0].y = p[0].y;
element->p[1].x = p[1].x;
element->p[1].y = p[1].y;
element->p[2].x = p[2].x;
element->p[2].y = p[2].y;
}
return AUTORELEASE(element);
}
- (void)setType:(NSBezierPathElement)t
{
type = t;
}
- (void)setPointAtIndex:(int)index toPoint:(NSPoint)aPoint
{
p[index].x = aPoint.x;
p[index].y = aPoint.y;
}
- (NSBezierPathElement)type
{
return type;
}
- (NSPoint *)points
{
return p;
}
@end
static Class NSBezierPath_concrete_class = nil;
@implementation NSBezierPath
+ (void)initialize
{
if(self == [NSBezierPath class])
NSBezierPath_concrete_class = [GSBezierPath class];
}
+ (void)_setConcreteClass:(Class)c
{
NSBezierPath_concrete_class = c;
}
+ (Class)_concreteClass
{
return NSBezierPath_concrete_class;
}
//
// Creating common paths
//
+ (id)bezierPath
{
return [[[self _concreteClass] alloc] init];
}
+ (NSBezierPath *)bezierPathWithRect:(NSRect)aRect
{
NSBezierPath *path;
NSPoint p;
path = [NSBezierPath bezierPath];
[path moveToPoint: aRect.origin];
p.x = aRect.origin.x + aRect.size.width;
p.y = aRect.origin.y;
[path lineToPoint: p];
p.x = aRect.origin.x + aRect.size.width;
p.y = aRect.origin.y + aRect.size.height;
[path lineToPoint: p];
p.x = aRect.origin.x;
p.y = aRect.origin.y + aRect.size.height;
[path lineToPoint: p];
[path closePath];
return path;
}
+ (NSBezierPath *)bezierPathWithOvalInRect:(NSRect)rect
{
NSBezierPath *path;
NSPoint p, p1, p2;
double originx = rect.origin.x;
double originy = rect.origin.y;
double width = rect.size.width;
double height = rect.size.height;
double hdiff = width / 2 * 0.5522847498;
double vdiff = height / 2 * 0.5522847498;
path = [NSBezierPath bezierPath];
p = NSMakePoint(originx + width / 2, originy + height);
[path moveToPoint: p];
p = NSMakePoint(originx, originy + height / 2);
p1 = NSMakePoint(originx + width / 2 - hdiff, originy + height);
p2 = NSMakePoint(originx, originy + height / 2 + vdiff);
[path curveToPoint: p controlPoint1: p1 controlPoint2: p2];
p = NSMakePoint(originx + width / 2, originy);
p1 = NSMakePoint(originx, originy + height / 2 - vdiff);
p2 = NSMakePoint(originx + width / 2 - hdiff, originy);
[path curveToPoint: p controlPoint1: p1 controlPoint2: p2];
p = NSMakePoint(originx + width, originy + height / 2);
p1 = NSMakePoint(originx + width / 2 + hdiff, originy);
p2 = NSMakePoint(originx + width, originy + height / 2 - vdiff);
[path curveToPoint: p controlPoint1: p1 controlPoint2: p2];
p = NSMakePoint(originx + width / 2, originy + height);
p1 = NSMakePoint(originx + width, originy + height / 2 + vdiff);
p2 = NSMakePoint(originx + width / 2 + hdiff, originy + height);
[path curveToPoint: p controlPoint1: p1 controlPoint2: p2];
return path;
}
//
// Immediate mode drawing of common paths
//
+ (void)fillRect:(NSRect)aRect
{
NSBezierPath *path = [NSBezierPath bezierPathWithRect: aRect];
[path fill];
}
+ (void)strokeRect:(NSRect)aRect
{
NSBezierPath *path = [NSBezierPath bezierPathWithRect: aRect];
[path stroke];
}
+ (void)clipRect:(NSRect)rect
{
}
+ (void)strokeLineFromPoint:(NSPoint)point1 toPoint:(NSPoint)point2
{
NSBezierPath *path = [NSBezierPath bezierPath];
[path moveToPoint: point1];
[path lineToPoint: point2];
[path stroke];
}
+ (void)drawPackedGlyphs:(const char *)packedGlyphs atPoint:(NSPoint)aPoint
{
}
//
// Default path rendering parameters
//
+ (void)setDefaultMiterLimit:(float)limit
{
PSsetmiterlimit(limit);
}
+ (float)defaultMiterLimit
{
float limit;
PScurrentmiterlimit(&limit);
return limit;
}
+ (void)setDefaultFlatness:(float)flatness
{
PSsetflat(flatness);
}
+ (float)defaultFlatness
{
float flatness;
PScurrentflat(&flatness);
return flatness;
}
+ (void)setDefaultWindingRule:(NSWindingRule)windingRule
{
}
+ (NSWindingRule)defaultWindingRule
{
return NSNonZeroWindingRule;
}
+ (void)setDefaultLineCapStyle:(NSLineCapStyle)lineCapStyle
{
PSsetlinecap(lineCapStyle);
}
+ (NSLineCapStyle)defaultLineCapStyle
{
int lineCapStyle;
PScurrentlinecap(&lineCapStyle);
return lineCapStyle;
}
+ (void)setDefaultLineJoinStyle:(NSLineJoinStyle)lineJoinStyle
{
PSsetlinejoin(lineJoinStyle);
}
+ (NSLineJoinStyle)defaultLineJoinStyle
{
int lineJoinStyle;
PScurrentlinejoin(&lineJoinStyle);
return lineJoinStyle;
}
+ (void)setDefaultLineWidth:(float)lineWidth
{
PSsetlinewidth(lineWidth);
}
+ (float)defaultLineWidth
{
float lineWidth;
PScurrentlinewidth(&lineWidth);
return lineWidth;
}
//
// Path construction
//
- (void)moveToPoint:(NSPoint)aPoint
{
[self subclassResponsibility:_cmd];
}
- (void)lineToPoint:(NSPoint)aPoint
{
[self subclassResponsibility:_cmd];
}
- (void)curveToPoint:(NSPoint)aPoint
controlPoint1:(NSPoint)controlPoint1
controlPoint2:(NSPoint)controlPoint2
{
[self subclassResponsibility:_cmd];
}
- (void)closePath
{
[self subclassResponsibility:_cmd];
}
- (void)removeAllPoints
{
[self subclassResponsibility:_cmd];
}
//
// Relative path construction
//
- (void)relativeMoveToPoint:(NSPoint)aPoint
{
[self subclassResponsibility:_cmd];
}
- (void)relativeLineToPoint:(NSPoint)aPoint
{
[self subclassResponsibility:_cmd];
}
- (void)relativeCurveToPoint:(NSPoint)aPoint
controlPoint1:(NSPoint)controlPoint1
controlPoint2:(NSPoint)controlPoint2
{
[self subclassResponsibility:_cmd];
}
//
// Path rendering parameters
//
- (float)lineWidth
{
return _lineWidth;
}
- (void)setLineWidth:(float)lineWidth
{
_lineWidth = lineWidth;
}
- (NSLineCapStyle)lineCapStyle
{
return _lineCapStyle;
}
- (void)setLineCapStyle:(NSLineCapStyle)lineCapStyle
{
_lineCapStyle = lineCapStyle;
}
- (NSLineJoinStyle)lineJoinStyle
{
return _lineJoinStyle;
}
- (void)setLineJoinStyle:(NSLineJoinStyle)lineJoinStyle
{
_lineJoinStyle = lineJoinStyle;
}
- (NSWindingRule)windingRule
{
return _windingRule;
}
- (void)setWindingRule:(NSWindingRule)windingRule
{
_windingRule = windingRule;
}
//
// Path operations
//
- (void)stroke
{
[self subclassResponsibility:_cmd];
}
- (void)fill
{
[self subclassResponsibility:_cmd];
}
- (void)addClip
{
[self subclassResponsibility:_cmd];
}
- (void)setClip
{
[self subclassResponsibility:_cmd];
}
//
// Path modifications.
//
- (NSBezierPath *)bezierPathByFlatteningPath
{
return self;
}
- (NSBezierPath *)bezierPathByReversingPath
{
return self;
}
//
// Applying transformations.
//
- (void)transformUsingAffineTransform:(NSAffineTransform *)transform
{
[self subclassResponsibility:_cmd];
}
//
// Path info
//
- (BOOL)isEmpty
{
[self subclassResponsibility:_cmd];
return NO;
}
- (NSPoint)currentPoint
{
[self subclassResponsibility:_cmd];
return NSZeroPoint;
}
- (NSRect)controlPointBounds
{
return _controlPointBounds;
}
- (NSRect)bounds
{
return _bounds;
}
//
// Elements
//
- (int)elementCount
{
[self subclassResponsibility:_cmd];
return 0;
}
- (NSBezierPathElement)elementAtIndex:(int)index
associatedPoints:(NSPoint *)points
{
[self subclassResponsibility:_cmd];
return 0;
}
- (NSBezierPathElement)elementAtIndex:(int)index
{
return [self elementAtIndex: index associatedPoints: NULL];
}
- (void)setAssociatedPoints:(NSPoint *)points atIndex:(int)index
{
[self subclassResponsibility:_cmd];
}
//
// Appending common paths
//
- (void)appendBezierPath:(NSBezierPath *)aPath
{
[self subclassResponsibility:_cmd];
}
- (void)appendBezierPathWithRect:(NSRect)rect
{
[self subclassResponsibility:_cmd];
}
- (void)appendBezierPathWithPoints:(NSPoint *)points count:(int)count
{
[self subclassResponsibility:_cmd];
}
- (void)appendBezierPathWithOvalInRect:(NSRect)aRect
{
[self subclassResponsibility:_cmd];
}
- (void)appendBezierPathWithArcWithCenter:(NSPoint)center
radius:(float)radius
startAngle:(float)startAngle
endAngle:(float)endAngle
clockwise:(BOOL)clockwise
{
[self subclassResponsibility:_cmd];
}
- (void)appendBezierPathWithArcWithCenter:(NSPoint)center
radius:(float)radius
startAngle:(float)startAngle
endAngle:(float)endAngle
{
[self appendBezierPathWithArcWithCenter: center radius: radius
startAngle: startAngle endAngle: endAngle clockwise: NO];
}
- (void)appendBezierPathWithArcFromPoint:(NSPoint)point1
toPoint:(NSPoint)point2
radius:(float)radius
{
[self subclassResponsibility:_cmd];
}
- (void)appendBezierPathWithGlyph:(NSGlyph)glyph inFont:(NSFont *)font
{
}
- (void)appendBezierPathWithGlyphs:(NSGlyph *)glyphs
count:(int)count
inFont:(NSFont *)font
{
}
- (void)appendBezierPathWithPackedGlyphs:(const char *)packedGlyphs
{
}
//
// Hit detection
//
- (BOOL)containsPoint:(NSPoint)point
{
[self subclassResponsibility:_cmd];
return NO;
}
//
// Caching paths
//
- (BOOL)cachesBezierPath
{
[self subclassResponsibility:_cmd];
return NO;
}
- (void)setCachesBezierPath:(BOOL)flag
{
[self subclassResponsibility:_cmd];
}
//
// NSCoding protocol
//
- (void)encodeWithCoder:(NSCoder *)aCoder
{
[self subclassResponsibility:_cmd];
}
- (id)initWithCoder:(NSCoder *)aCoder
{
[self subclassResponsibility:_cmd];
return self;
}
//
// NSCopying Protocol
//
- (id)copyWithZone:(NSZone *)zone
{
[self subclassResponsibility:_cmd];
return self;
}
@end
@implementation NSBezierPath (PrivateMethods)
- (void)setBounds:(NSRect)br
{
_bounds = NSMakeRect(br.origin.x, br.origin.y, br.size.width, br.size.height);
}
- (void)setControlPointBounds:(NSRect)br
{
_controlPointBounds = NSMakeRect(br.origin.x, br.origin.y, br.size.width, br.size.height);
}
@end
@implementation GSBezierPath
+ (id)bezierPath
{
return AUTORELEASE([[self alloc] init]);
}
- (id)init
{
self = [super init];
if(self) {
pathElements = [[NSMutableArray alloc] initWithCapacity: 1];
subPaths = [[NSMutableArray alloc] initWithCapacity: 1];
[self setLineWidth: 1];
[self setLineCapStyle: NSButtLineCapStyle];
[self setLineJoinStyle: NSMiterLineJoinStyle];
[self setWindingRule: NSNonZeroWindingRule];
[self setBounds: NSZeroRect];
[self setControlPointBounds: NSZeroRect];
cachesBezierPath = NO;
cacheimg = nil;
isValid = NO;
}
return self;
}
- (void)dealloc
{
[pathElements release];
[subPaths release];
if(cacheimg)
[cacheimg release];
[super dealloc];
}
//
// Path construction
//
- (void)moveToPoint:(NSPoint)aPoint
{
PathElement *elm = [PathElement pathElement];
[elm setType: NSMoveToBezierPathElement];
[elm setPointAtIndex: 0 toPoint: aPoint];
[pathElements addObject: elm];
[self setCurrentPoint: aPoint];
[self calculateDraftPolygon];
}
- (void)lineToPoint:(NSPoint)aPoint
{
PathElement *elm = [PathElement pathElement];
[elm setType: NSLineToBezierPathElement];
[elm setPointAtIndex: 0 toPoint: aPoint];
[pathElements addObject: elm];
[self setCurrentPoint: aPoint];
[self calculateDraftPolygon];
}
- (void)curveToPoint:(NSPoint)aPoint
controlPoint1:(NSPoint)controlPoint1
controlPoint2:(NSPoint)controlPoint2
{
PathElement *elm = [PathElement pathElement];
[elm setType: NSCurveToBezierPathElement];
[elm setPointAtIndex: 0 toPoint: controlPoint1];
[elm setPointAtIndex: 1 toPoint: controlPoint2];
[elm setPointAtIndex: 2 toPoint: aPoint];
[pathElements addObject: elm];
[self setCurrentPoint: aPoint];
[self calculateDraftPolygon];
}
- (void)closePath
{
PathElement *elm, *elm1, *elm2;
NSPoint p;
elm1 = [self moveToElement];
elm2 = [self lastElement];
if(![self isPathElement: elm2 onPathElement: elm1]) {
p = [elm1 points][0];
[self lineToPoint: p];
}
elm = [PathElement pathElement];
[elm setType: NSClosePathBezierPathElement];
[pathElements addObject: elm];
}
- (void)removeAllPoints
{
[pathElements removeAllObjects];
[subPaths removeAllObjects];
[self setBounds: NSZeroRect];
[self setControlPointBounds: NSZeroRect];
if(cacheimg != nil) {
[cacheimg release];
cacheimg = nil;
isValid = NO;
}
}
//
// Relative path construction
//
- (void)relativeMoveToPoint:(NSPoint)aPoint
{
NSBezierPath *path;
NSPoint p;
p.x = currentPoint.x + aPoint.x;
p.y = currentPoint.y + aPoint.y;
path = [NSBezierPath bezierPath];
[path moveToPoint: p];
[subPaths addObject: path];
[self setCurrentPoint: p];
}
- (void)relativeLineToPoint:(NSPoint)aPoint
{
NSPoint p;
if(![pathElements count]) {
[NSException raise: NSGenericException
format: @"attempt to append illegal path component"];
return;
}
p.x = currentPoint.x + aPoint.x;
p.y = currentPoint.y + aPoint.y;
[self lineToPoint: p];
}
- (void)relativeCurveToPoint:(NSPoint)aPoint
controlPoint1:(NSPoint)controlPoint1
controlPoint2:(NSPoint)controlPoint2
{
NSPoint p, cp1, cp2;
if(![pathElements count]) {
[NSException raise: NSGenericException
format: @"attempt to append illegal path component"];
return;
}
p.x = currentPoint.x + aPoint.x;
p.y = currentPoint.y + aPoint.y;
cp1.x = currentPoint.x + controlPoint1.x;
cp1.y = currentPoint.y + controlPoint1.y;
cp2.x = currentPoint.x + controlPoint2.x;
cp2.y = currentPoint.y + controlPoint2.y;
[self curveToPoint: p controlPoint1: cp1 controlPoint2: cp2];
}
//
// Path operations
//
- (void)stroke
{
PathElement *elm;
NSBezierPathElement t;
NSPoint *pts, origin;
int i;
if(cachesBezierPath) {
origin = [self bounds].origin;
if(!isValid) {
if(cacheimg)
[cacheimg release];
cacheimg = [[NSImage alloc] initWithSize: [self bounds].size];
[self movePathToPoint: NSMakePoint(0, 0)];
[cacheimg lockFocus];
PSsetlinewidth([self lineWidth]);
PSsetlinejoin([self lineJoinStyle]);
PSsetlinecap([self lineCapStyle]);
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
pts = [elm points];
t = [elm type];
switch(t) {
case NSMoveToBezierPathElement:
PSmoveto(pts[0].x, pts[0].y);
break;
case NSLineToBezierPathElement:
PSlineto(pts[0].x, pts[0].y);
break;
case NSCurveToBezierPathElement:
PScurveto(pts[0].x, pts[0].y, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
break;
default:
break;
}
}
PSstroke();
[cacheimg unlockFocus];
[self movePathToPoint: origin];
isValid = YES;
}
[cacheimg compositeToPoint: origin operation: NSCompositeCopy];
} else {
PSsetlinewidth([self lineWidth]);
PSsetlinejoin([self lineJoinStyle]);
PSsetlinecap([self lineCapStyle]);
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
pts = [elm points];
t = [elm type];
switch(t) {
case NSMoveToBezierPathElement:
PSmoveto(pts[0].x, pts[0].y);
break;
case NSLineToBezierPathElement:
PSlineto(pts[0].x, pts[0].y);
break;
case NSCurveToBezierPathElement:
PScurveto(pts[0].x, pts[0].y, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
break;
default:
break;
}
}
PSstroke();
}
for(i = 0; i < [subPaths count]; i++)
[[subPaths objectAtIndex: i] stroke];
}
- (void)fill
{
PathElement *elm;
NSBezierPathElement t;
NSPoint *pts, origin;
int i;
if(cachesBezierPath) {
origin = [self bounds].origin;
if(!isValid) {
if(cacheimg)
[cacheimg release];
cacheimg = [[NSImage alloc] initWithSize: [self bounds].size];
[self movePathToPoint: NSMakePoint(0, 0)];
[cacheimg lockFocus];
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
pts = [elm points];
t = [elm type];
switch(t) {
case NSMoveToBezierPathElement:
PSmoveto(pts[0].x, pts[0].y);
break;
case NSLineToBezierPathElement:
PSlineto(pts[0].x, pts[0].y);
break;
case NSCurveToBezierPathElement:
PScurveto(pts[0].x, pts[0].y, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
break;
default:
break;
}
}
if([self windingRule] == NSNonZeroWindingRule)
PSfill();
else
PSeofill();
[cacheimg unlockFocus];
[self movePathToPoint: origin];
isValid = YES;
}
[cacheimg compositeToPoint: origin operation: NSCompositeCopy];
} else {
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
pts = [elm points];
t = [elm type];
switch(t) {
case NSMoveToBezierPathElement:
PSmoveto(pts[0].x, pts[0].y);
break;
case NSLineToBezierPathElement:
PSlineto(pts[0].x, pts[0].y);
break;
case NSCurveToBezierPathElement:
PScurveto(pts[0].x, pts[0].y, pts[1].x, pts[1].y, pts[2].x, pts[2].y);
break;
default:
break;
}
}
if([self windingRule] == NSNonZeroWindingRule)
PSfill();
else
PSeofill();
}
for(i = 0; i < [subPaths count]; i++)
[[subPaths objectAtIndex: i] fill];
}
- (void)addClip
{
}
- (void)setClip
{
}
//
// Applying transformations.
//
- (void)transformUsingAffineTransform:(NSAffineTransform *)transform
{
PathElement *elm;
NSBezierPathElement t;
NSPoint p, *pts;
int i;
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
pts = [elm points];
t = [elm type];
if(t == NSCurveToBezierPathElement) {
p = [transform transformPoint: pts[0]];
[elm setPointAtIndex: 0 toPoint: p];
p = [transform transformPoint: pts[1]];
[elm setPointAtIndex: 1 toPoint: p];
p = [transform transformPoint: pts[2]];
[elm setPointAtIndex: 2 toPoint: p];
} else {
p = [transform transformPoint: pts[0]];
[elm setPointAtIndex: 0 toPoint: p];
}
}
[self calculateDraftPolygon];
}
//
// Path info
//
- (BOOL)isEmpty
{
if([pathElements count])
return NO;
return YES;
}
- (NSPoint)currentPoint
{
return currentPoint;
}
//
// Elements
//
- (int)elementCount
{
return [pathElements count];
}
- (NSBezierPathElement)elementAtIndex:(int)index
associatedPoints:(NSPoint *)points
{
PathElement *elm = [pathElements objectAtIndex: index];
NSBezierPathElement type = [elm type];
NSPoint *p = [elm points];
if(points != NULL) {
if(type == NSMoveToBezierPathElement || type == NSLineToBezierPathElement) {
points[0].x = p[0].x;
points[0].y = p[0].y;
} else if(type == NSCurveToBezierPathElement) {
points[0].x = p[0].x;
points[0].y = p[0].y;
points[1].x = p[1].x;
points[1].y = p[1].y;
points[2].x = p[2].x;
points[2].y = p[2].y;
}
}
return type;
}
- (void)setAssociatedPoints:(NSPoint *)points atIndex:(int)index
{
PathElement *elm = [pathElements objectAtIndex: index];
NSBezierPathElement type = [elm type];
if(type == NSMoveToBezierPathElement
|| type == NSLineToBezierPathElement
|| type == NSClosePathBezierPathElement) {
[elm setPointAtIndex: 0 toPoint: points[0]];
return;
}
[elm setPointAtIndex: 0 toPoint: points[0]];
[elm setPointAtIndex: 1 toPoint: points[1]];
[elm setPointAtIndex: 2 toPoint: points[2]];
[self calculateDraftPolygon];
}
//
// Appending common paths
//
- (void)appendBezierPath:(NSBezierPath *)aPath
{
NSArray *pathelems;
PathElement *last, *elm;
NSBezierPathElement t1, t2;
NSPoint p, *p1, *p2;
int i;
if(![pathElements count]) {
[subPaths addObject: aPath];
return;
}
last = [self lastElement];
t1 = [last type];
elm = [(GSBezierPath *)aPath lastElement];
t2 = [elm type];
if(t1 == NSClosePathBezierPathElement || t2 == NSClosePathBezierPathElement) {
[subPaths addObject: aPath];
return;
}
p1 = [last points];
if(t1 == NSCurveToBezierPathElement)
p = NSMakePoint(p1[2].x, p1[2].y);
else
p = NSMakePoint(p1[0].x, p1[0].y);
elm = [(GSBezierPath *)aPath moveToElement];
p2 = [elm points];
if((p.x != p2[0].x) || (p.y != p2[0].y)) {
[subPaths addObject: aPath];
return;
}
pathelems = [(GSBezierPath *)aPath pathElements];
for(i = 1; i < [pathelems count]; i++) {
elm = [pathelems objectAtIndex: i];
t2 = [elm type];
p2 = [elm points];
if(t2 == NSCurveToBezierPathElement)
[self curveToPoint: p2[2] controlPoint1: p2[0] controlPoint2: p2[1]];
else
[self lineToPoint: p2[0]];
}
}
- (void)appendBezierPathWithRect:(NSRect)rect
{
NSBezierPath *path = [NSBezierPath bezierPathWithRect: rect];
[subPaths addObject: path];
}
- (void)appendBezierPathWithPoints:(NSPoint *)points count:(int)count
{
int i, c;
if([[self lastElement] type] == NSClosePathBezierPathElement)
return;
if(![pathElements count]) {
[self moveToPoint: points[0]];
c = 1;
} else {
c = 0;
}
for(i = c; i < count; i++)
[self lineToPoint: points[i]];
}
- (void)appendBezierPathWithOvalInRect:(NSRect)aRect
{
NSBezierPath *path = [NSBezierPath bezierPathWithOvalInRect: aRect];
[subPaths addObject: path];
}
- (void)appendBezierPathWithArcWithCenter:(NSPoint)center
radius:(float)radius
startAngle:(float)startAngle
endAngle:(float)endAngle
clockwise:(BOOL)clockwise
{
NSRect r;
NSBezierPath *path, *npath;
NSMutableArray *pelements;
PathElement *elm;
float tmpangle, diffangle, strtangrd, endangrd;
NSPoint p, *pts;
int index;
if(startAngle < 0)
startAngle = 360 + startAngle;
if(endAngle < 0)
endAngle = 360 + endAngle;
if(endAngle < startAngle) {
tmpangle = endAngle;
endAngle = startAngle;
startAngle = tmpangle;
}
strtangrd = PI * startAngle / 180;
endangrd = PI * endAngle / 180;
r = NSMakeRect(center.x - radius, center.y - radius, radius * 2, radius * 2);
path = [NSBezierPath bezierPathWithOvalInRect: r];
[(GSBezierPath *)path rotateByAngle: -90 center: center];
[(GSBezierPath *)path rotateByAngle: startAngle center: center];
npath = [NSBezierPath bezierPath];
elm = [(GSBezierPath *)path moveToElement];
p = [elm points][0];
[npath moveToPoint: p];
pelements = [(GSBezierPath *)path pathElements];
diffangle = endAngle - startAngle;
index = 1;
while(diffangle >= 90) {
elm = [pelements objectAtIndex: index];
pts = [elm points];
[npath curveToPoint: pts[2] controlPoint1: pts[0] controlPoint2: pts[1]];
diffangle -= 90;
index++;
}
if(diffangle == 0) {
[self appendBezierPath: npath];
return;
}
p = NSMakePoint(center.x + radius * cos(endangrd),
center.y + radius * sin(endangrd));
elm = [(GSBezierPath *)path pathElementSubdividingPathAtPoint: p
pathSegmentOwner: [pelements objectAtIndex: index]];
pts = [elm points];
[npath curveToPoint: pts[2] controlPoint1: pts[0] controlPoint2: pts[1]];
[self appendBezierPath: npath];
}
- (void)appendBezierPathWithArcFromPoint:(NSPoint)point1
toPoint:(NSPoint)point2
radius:(float)radius
{
[self subclassResponsibility:_cmd];
}
//
// Hit detection
//
- (BOOL)containsPoint:(NSPoint)point
{
if(inPath(self, point) == INTERIOR)
return YES;
return NO;
}
//
// Caching
//
- (BOOL)cachesBezierPath
{
return cachesBezierPath;
}
- (void)setCachesBezierPath:(BOOL)flag
{
cachesBezierPath = flag;
if(flag)
[self calculateDraftPolygon];
}
//
// NSCoding protocol
//
- (void)encodeWithCoder:(NSCoder *)aCoder
{
float f;
int i;
NSRect r;
[aCoder encodeValueOfObjCType: @encode(NSMutableArray) at: &pathElements];
[aCoder encodeValueOfObjCType: @encode(NSMutableArray) at: &subPaths];
r = [self bounds];
[aCoder encodeValueOfObjCType: @encode(NSRect) at: &r];
r = [self controlPointBounds];
[aCoder encodeValueOfObjCType: @encode(NSRect) at: &r];
f = [self lineWidth];
[aCoder encodeValueOfObjCType: @encode(float) at: &f];
i = [self lineCapStyle];
[aCoder encodeValueOfObjCType: @encode(int) at: &i];
i = [self lineJoinStyle];
[aCoder encodeValueOfObjCType: @encode(int) at: &i];
i = [self windingRule];
[aCoder encodeValueOfObjCType: @encode(int) at: &i];
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &cachesBezierPath];
[aCoder encodeValueOfObjCType: @encode(NSImage) at: &cacheimg];
}
- (id)initWithCoder:(NSCoder *)aCoder
{
float f;
int i;
NSRect r;
self = [super init];
if(self) {
[aCoder decodeValueOfObjCType: @encode(NSMutableArray) at: &pathElements];
[aCoder decodeValueOfObjCType: @encode(NSMutableArray) at: &subPaths];
[aCoder decodeValueOfObjCType: @encode(NSRect) at: &r];
[self setBounds: r];
[aCoder decodeValueOfObjCType: @encode(NSRect) at: &r];
[self setControlPointBounds: r];
[aCoder decodeValueOfObjCType: @encode(float) at: &f];
[self setLineWidth: f];
[aCoder decodeValueOfObjCType: @encode(int) at: &i];
[self setLineCapStyle: i];
[aCoder decodeValueOfObjCType: @encode(int) at: &i];
[self setLineJoinStyle: i];
[aCoder decodeValueOfObjCType: @encode(int) at: &i];
[self setWindingRule: i];
[aCoder decodeValueOfObjCType: @encode(BOOL) at: &cachesBezierPath];
[aCoder decodeValueOfObjCType: @encode(NSImage) at: &cacheimg];
isValid = NO;
}
return self;
}
//
// NSCopying Protocol
//
- (id)copyWithZone:(NSZone *)zone
{
GSBezierPath *path = [[GSBezierPath allocWithZone: zone] init];
path->pathElements = [[pathElements copy] retain];
path->subPaths = [[subPaths copy] retain];
path->cachesBezierPath = cachesBezierPath;
if(cachesBezierPath && cacheimg)
path->cacheimg = [[cacheimg copy] retain];
[path setLineWidth: [self lineWidth]];
[path setLineCapStyle: [self lineCapStyle]];
[path setLineJoinStyle: [self lineJoinStyle]];
[path setWindingRule: [self windingRule]];
[path setBounds: [self bounds]];
[path setControlPointBounds: [self controlPointBounds]];
return path;
}
//
// Private Methods
//
- (void)calculateDraftPolygon
{
PathElement *elm;
NSBezierPathElement bpt;
NSPoint p, *pts;
double x, y, t, k = 0.25;
float maxx, minx, maxy, miny;
float cpmaxx, cpminx, cpmaxy, cpminy;
int i;
if(![pathElements count])
return;
pts = [[pathElements objectAtIndex: 0] points];
maxx = minx = cpmaxx = cpminx = pts[0].x;
maxy = miny = cpmaxy = cpminy = pts[0].y;
pcount = 0;
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
bpt = [elm type];
pts = [elm points];
if(bpt == NSMoveToBezierPathElement || bpt == NSLineToBezierPathElement) {
draftPolygon[pcount].x = pts[0].x;
draftPolygon[pcount].y = pts[0].y;
if(pts[0].x > maxx) maxx = pts[0].x;
if(pts[0].x < minx) minx = pts[0].x;
if(pts[0].y > maxy) maxy = pts[0].y;
if(pts[0].y < miny) miny = pts[0].y;
if(pts[0].x > cpmaxx) cpmaxx = pts[0].x;
if(pts[0].x < cpminx) cpminx = pts[0].x;
if(pts[0].y > cpmaxy) cpmaxy = pts[0].y;
if(pts[0].y < cpminy) cpminy = pts[0].y;
pcount++;
} else if(bpt == NSCurveToBezierPathElement) {
if(pcount) {
p.x = draftPolygon[pcount -1].x;
p.y = draftPolygon[pcount -1].y;
} else {
p.x = pts[0].x;
p.y = pts[0].y;
}
if(pts[2].x > maxx) maxx = pts[2].x;
if(pts[2].x < minx) minx = pts[2].x;
if(pts[2].y > maxy) maxy = pts[2].y;
if(pts[2].y < miny) miny = pts[2].y;
if(pts[0].x > cpmaxx) cpmaxx = pts[0].x;
if(pts[0].x < cpminx) cpminx = pts[0].x;
if(pts[0].y > cpmaxy) cpmaxy = pts[0].y;
if(pts[0].y < cpminy) cpminy = pts[0].y;
if(pts[1].x > cpmaxx) cpmaxx = pts[1].x;
if(pts[1].x < cpminx) cpminx = pts[1].x;
if(pts[1].y > cpmaxy) cpmaxy = pts[1].y;
if(pts[1].y < cpminy) cpminy = pts[1].y;
if(pts[2].x > cpmaxx) cpmaxx = pts[2].x;
if(pts[2].x < cpminx) cpminx = pts[2].x;
if(pts[2].y > cpmaxy) cpmaxy = pts[2].y;
if(pts[2].y < cpminy) cpminy = pts[2].y;
for(t = k; t <= 1+k; t += k) {
x = (p.x+t*(-p.x*3+t*(3*p.x-p.x*t)))
+t*(3*pts[0].x+t*(-6*pts[0].x+pts[0].x*3*t))
+t*t*(pts[1].x*3-pts[1].x*3*t)+pts[2].x*t*t*t;
y = (p.y+t*(-p.y*3+t*(3*p.y-p.y*t)))
+t*(3*pts[0].y+t*(-6*pts[0].y+pts[0].y*3*t))
+t*t*(pts[1].y*3-pts[1].y*3*t)+pts[2].y*t*t*t;
draftPolygon[pcount].x = x;
draftPolygon[pcount].y = y;
if(x > maxx) maxx = x;
if(x < minx) minx = x;
if(y > maxy) maxy = y;
if(y < miny) miny = y;
if(x > cpmaxx) cpmaxx = x;
if(x < cpminx) cpminx = x;
if(y > cpmaxy) cpmaxy = y;
if(y < cpminy) cpminy = y;
pcount++;
}
}
}
[self setBounds: NSMakeRect(minx, miny, maxx - minx, maxy - miny)];
[self setControlPointBounds: NSMakeRect(cpminx, cpminy, cpmaxx - cpminx, cpmaxy - cpminy)];
isValid = NO;
}
- (void)setCurrentPoint:(NSPoint)aPoint
{
currentPoint.x = aPoint.x;
currentPoint.y = aPoint.y;
}
- (BOOL)isPathElement:(PathElement *)elm1 onPathElement:(PathElement *)elm2
{
NSPoint p1, p2;
NSBezierPathElement t;
t = [elm1 type];
if(t == NSMoveToBezierPathElement || t == NSLineToBezierPathElement) {
p1.x = [elm1 points][0].x;
p1.y = [elm1 points][0].y;
} else if(t == NSCurveToBezierPathElement) {
p1.x = [elm1 points][2].x;
p1.y = [elm1 points][2].y;
} else {
return NO;
}
t = [elm2 type];
if(t == NSMoveToBezierPathElement || t == NSLineToBezierPathElement) {
p2.x = [elm2 points][0].x;
p2.y = [elm2 points][0].y;
} else if(t == NSCurveToBezierPathElement) {
p2.x = [elm2 points][2].x;
p2.y = [elm2 points][2].y;
} else {
return NO;
}
if(p1.x == p2.x && p1.y == p2.y)
return YES;
return NO;
}
- (NSMutableArray *)pathElements
{
return pathElements;
}
- (PathElement *)moveToElement
{
return [pathElements objectAtIndex: 0];
}
- (PathElement *)lastElement
{
return [pathElements objectAtIndex: [pathElements count] -1];
}
- (int)indexOfElement:(PathElement *)element
{
PathElement *elm;
int i;
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
if(elm == element)
break;
}
return i;
}
- (PathElement *)elementPrecedingElement:(PathElement *)element
{
PathElement *prevelm;
int index;
index = [self indexOfElement: element];
if(index == 0)
prevelm = [pathElements objectAtIndex: [pathElements count] -1];
else
prevelm = [pathElements objectAtIndex: index -1];
return prevelm;
}
- (NSPoint *)draftPolygon
{
return draftPolygon;
}
- (int)pcount
{
return pcount;
}
- (void)movePathToPoint:(NSPoint)p
{
PathElement *elm;
NSBezierPathElement t;
NSPoint pp, origin, *pts;
float diffx, diffy;
int i, j;
origin = [self bounds].origin;
diffx = origin.x - p.x;
diffy = origin.y - p.y;
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
pts = [elm points];
t = [elm type];
if(t == NSCurveToBezierPathElement) {
for(j = 0; j < 3; j++) {
pp.x = pts[j].x - diffx;
pp.y = pts[j].y - diffy;
[elm setPointAtIndex: j toPoint: pp];
}
} else if(t == NSMoveToBezierPathElement
|| t == NSLineToBezierPathElement
|| t == NSClosePathBezierPathElement) {
pp.x = pts[0].x - diffx;
pp.y = pts[0].y - diffy;
[elm setPointAtIndex: 0 toPoint: pp];
}
}
[self calculateDraftPolygon];
}
- (void)rotateByAngle:(float)angle center:(NSPoint)center
{
PathElement *elm;
NSBezierPathElement t;
NSPoint p, *pts;
int i;
for(i = 0; i < [pathElements count]; i++) {
elm = [pathElements objectAtIndex: i];
pts = [elm points];
t = [elm type];
if(t == NSCurveToBezierPathElement) {
p = rotatePoint(pts[0], center, angle);
[elm setPointAtIndex: 0 toPoint: p];
p = rotatePoint(pts[1], center, angle);
[elm setPointAtIndex: 1 toPoint: p];
p = rotatePoint(pts[2], center, angle);
[elm setPointAtIndex: 2 toPoint: p];
} else if(t == NSMoveToBezierPathElement
|| t == NSLineToBezierPathElement
|| t == NSClosePathBezierPathElement) {
p = rotatePoint(pts[0], center, angle);
[elm setPointAtIndex: 0 toPoint: p];
}
}
[self calculateDraftPolygon];
}
- (pointOnCurve)pointOnPathSegmentOfElement:(PathElement *)elm
nearestToPoint:(NSPoint)p
{
pointOnCurve pc;
PathElement *prevelm;
NSPoint *pp1, *pp2, cpp[4];
double x, y, d, dmin = 10000, t, k = 0.001;
prevelm = [self elementPrecedingElement: elm];
if([prevelm type] == NSClosePathBezierPathElement)
prevelm = [self elementPrecedingElement: prevelm];
pp1 = [prevelm points];
pp2 = [elm points];
if([prevelm type] == NSCurveToBezierPathElement) {
cpp[0].x = pp1[2].x;
cpp[0].y = pp1[2].y;
} else {
cpp[0].x = pp1[0].x;
cpp[0].y = pp1[0].y;
}
if([elm type] == NSCurveToBezierPathElement) {
cpp[1].x = pp2[0].x;
cpp[1].y = pp2[0].y;
cpp[2].x = pp2[1].x;
cpp[2].y = pp2[1].y;
cpp[3].x = pp2[2].x;
cpp[3].y = pp2[2].y;
} else {
cpp[1].x = cpp[0].x;
cpp[1].y = cpp[0].y;
cpp[2].x = pp2[0].x;
cpp[2].y = pp2[0].y;
cpp[3].x = pp2[0].x;
cpp[3].y = pp2[0].y;
}
for(t = k; t <= 1+k; t += k) {
x = (cpp[0].x+t*(-cpp[0].x*3+t*(3*cpp[0].x-cpp[0].x*t)))
+t*(3*cpp[1].x+t*(-6*cpp[1].x+cpp[1].x*3*t))
+t*t*(cpp[2].x*3-cpp[2].x*3*t)+cpp[3].x*t*t*t;
y = (cpp[0].y+t*(-cpp[0].y*3+t*(3*cpp[0].y-cpp[0].y*t)))
+t*(3*cpp[1].y+t*(-6*cpp[1].y+cpp[1].y*3*t))
+t*t*(cpp[2].y*3-cpp[2].y*3*t)+cpp[3].y*t*t*t;
d = pow(pow(x - p.x, 2) + pow(y - p.y, 2), 0.5);
if(d < dmin) {
dmin = d;
pc.p = NSMakePoint(x, y);
pc.t = t - k;
}
}
return pc;
}
- (PathElement *)pathElementSubdividingPathAtPoint:(NSPoint)p
pathSegmentOwner:(PathElement *)owner
{
PathElement *prevelm, *elm;
NSPoint *ownerpts, *prevpts;
pointOnCurve pc;
float coeffx[4], coeffy[4], bleftx[4], blefty[4], brightx[4], brighty[4];
prevelm = [self elementPrecedingElement: owner];
if([prevelm type] == NSClosePathBezierPathElement)
prevelm = [self elementPrecedingElement: prevelm];
prevpts = [prevelm points];
ownerpts = [owner points];
pc = [self pointOnPathSegmentOfElement: owner nearestToPoint: p];
if([prevelm type] == NSCurveToBezierPathElement) {
coeffx[0] = prevpts[2].x;
coeffy[0] = prevpts[2].y;
} else {
coeffx[0] = prevpts[0].x;
coeffy[0] = prevpts[0].y;
}
if([owner type] == NSCurveToBezierPathElement) {
coeffx[1] = ownerpts[0].x;
coeffy[1] = ownerpts[0].y;
coeffx[2] = ownerpts[1].x;
coeffy[2] = ownerpts[1].y;
coeffx[3] = ownerpts[2].x;
coeffy[3] = ownerpts[2].y;
} else {
coeffx[1] = ownerpts[0].x;
coeffy[1] = ownerpts[0].y;
coeffx[2] = ownerpts[0].x;
coeffy[2] = ownerpts[0].y;
coeffx[3] = ownerpts[0].x;
coeffy[3] = ownerpts[0].y;
}
subdiv(3, coeffx, pc.t, bleftx, brightx);
subdiv(3, coeffy, pc.t, blefty, brighty);
elm = [PathElement pathElement];
[elm setType: NSCurveToBezierPathElement];
[elm setPointAtIndex: 0 toPoint: NSMakePoint(bleftx[2], blefty[2])];
[elm setPointAtIndex: 1 toPoint: NSMakePoint(bleftx[1], blefty[1])];
[elm setPointAtIndex: 2 toPoint: p];
[owner setType: NSCurveToBezierPathElement];
[owner setPointAtIndex: 0 toPoint: NSMakePoint(brightx[1], brighty[1])];
[owner setPointAtIndex: 1 toPoint: NSMakePoint(brightx[2], brighty[2])];
return elm;
}
@end
//
// Functions
//
pointPosition inPath(NSBezierPath *aPath, NSPoint p)
{
NSPoint *pts;
int xs[PMAX], ys[PMAX];
double x;
int i, i1, pcount;
int Rcross = 0;
int Lcross = 0;
pts = [(GSBezierPath *)aPath draftPolygon];
pcount = [(GSBezierPath *)aPath pcount];
for(i = 0; i < pcount; i++) {
xs[i] = (int)pts[i].x - p.x;
ys[i] = (int)pts[i].y - p.y;
}
for(i = 0; i < pcount; i++) {
if(xs[i] == 0 && ys[i] == 0)
return VERTEX;
i1 = (i + pcount - 1) % pcount;
if((ys[i] > 0) != (ys[i1] > 0)) {
x = (xs[i] * (double)ys[i1] - xs[i1] * (double)ys[i])
/ (double)(ys[i1] - ys[i]);
if(x > 0)
Rcross++;
}
if((ys[i] < 0 ) != (ys[i1] < 0)) {
x = (xs[i] * ys[i1] - xs[i1] * ys[i])
/ (double)(ys[i1] - ys[i]);
if(x < 0)
Lcross++;
}
}
if((Rcross % 2) != (Lcross % 2))
return ONBORDER;
if((Rcross % 2) == 1)
return INTERIOR;
else
return EXTERIOR;
}
void subdiv(int degree, float coeff[], float t, float bleft[], float bright[])
{
int r, i;
float t1;
t1 = 1.0 - t;
// use Casteljeau to find the right Bezier polygon
for (i = 0; i <= degree; i++)
bright[i] = coeff[i];
for (r = 1; r <= degree; r++)
for (i = 0; i <= degree - r; i++)
bright[i] = t1 * bright[i] + t * bright[i + 1];
// to find the left Bezier polygon (inverse order)
t = 1.0 - t;
t1 = 1.0 - t;
for (i = 0; i <= degree; i++)
bleft[degree - i] = coeff[i];
for (r = 1; r <= degree; r++)
for (i = 0; i <= degree - r; i++)
bleft[i] = t1 * bleft[i] + t * bleft[i + 1];
}
NSPoint rotatePoint(NSPoint p, NSPoint centre, float angle)
{
NSPoint rp;
float rdangle = PI * angle / 180;
float dx, dy;
dx = p.x - centre.x;
dy = p.y - centre.y;
rp.x = (dx * cos(rdangle)) - (dy * sin(rdangle)) + centre.x;
rp.y = (dx * sin(rdangle)) + (dy * cos(rdangle)) + centre.y;
return rp;
}