//
//  GSGeometrieHelper.h
//
//  Created by Georg Seifert on 2009-12-01.
//  Copyright (c) 2009 schriftgestaltung.de. All rights reserved.
//

#import <Foundation/Foundation.h>
#import <Foundation/NSGeometry.h>
#ifndef GLYPHS_VIEWER
#import <GlyphsCore/GraphicsGems.h>
#endif

NS_ASSUME_NONNULL_BEGIN
#if defined(__cplusplus)
extern "C" {
#endif /* defined(__cplusplus) */

@class GSPathSegment;

typedef NS_ENUM(uint8_t, GSAlignment) {
	GSTopLeft = 6,
	GSTopCenter = 7,
	GSTopRight = 8,
	GSCenterLeft = 3,
	GSCenterCenter = 4,
	GSCenterRight = 5,
	GSBottomLeft = 0,
	GSBottomCenter = 1,
	GSBottomRight = 2,
};

typedef NS_ENUM(uint8_t, GSIntersectionState) {
	ONTOP = 1,
	PARALLEL = 2
};

struct GSPoint3 {
	CGFloat x;
	CGFloat y;
	CGFloat z;
};
typedef struct GSPoint3 GSPoint3;

NS_INLINE GSPoint3 GSMakePoint3(CGFloat x, CGFloat y, CGFloat z) {
	GSPoint3 p;
	p.x = x;
	p.y = y;
	p.z = z;
	return p;
}

extern const CGFloat twoThirds; // used in TrueType conversion

#define GSRound(Value) lround(Value)

#ifndef GSGeometrie_Helpers
#define GSGeometrie_Helpers

#define M_PI_180 0.017453292519943295

@interface GSGeometry : NSObject

+ (CGFloat)angleOfVector:(NSPoint)point;

+ (CGFloat)angleBetweenVector:(NSPoint)point1 andVector:(NSPoint)point2;

+ (NSPoint)subtractPoint:(NSPoint)point1 andPoint:(NSPoint)point2;

+ (CGFloat)distance:(NSPoint)point1 toPoint:(NSPoint)point2;

+ (CGFloat)distanceOfPoint:(NSPoint)point1
				  fromLine:(NSPoint)line1
						  :(NSPoint)line2;

+ (NSPoint)nearestPointUnlimited:(NSPoint)point1
						onLineP1:(NSPoint)line1
							  p2:(NSPoint)line2
							   t:(out CGFloat *)t;

+ (NSPoint)nearestPoint:(NSPoint)point1
			   onLineP1:(NSPoint)line1
					 p2:(NSPoint)line2
					  t:(out CGFloat *)t;

+ (bool)angle:(CGFloat)angle toRun:(out int *)run rise:(out int *)rise;

@end

static inline NSPoint GSUnitVector(NSPoint A);

static inline CGFloat GSCross(NSPoint A, NSPoint B) {
	return A.x * B.y - B.x * A.y;
}

static inline NSPoint GSRoundHandle(NSPoint point) {
	NSPoint Unit = GSUnitVector(point);
	NSPoint Temp1 = point;
	Temp1.x += Unit.x * 0.4;
	Temp1.y += Unit.y * 0.4;
	NSPoint Temp2 = point;
	Temp2.x -= Unit.x * 0.4;
	Temp2.y -= Unit.y * 0.4;

	NSPoint Temp0;
	Temp0.x = round(point.x);
	Temp0.y = round(point.y);
	CGFloat C0 = GSCross(Temp0, point);
	Temp1.x = round(Temp1.x);
	Temp1.y = round(Temp1.y);
	CGFloat C1 = GSCross(Temp1, point);
	Temp2.x = round(Temp2.x);
	Temp2.y = round(Temp2.y);
	CGFloat C2 = GSCross(Temp2, point);
	if (point.x * point.y > 0) {
		if (C0 < C1 && C0 < C2) {
			return Temp0;
		}
		if (C1 < C0 && C1 < C2) {
			return Temp1;
		}
		return Temp2;
	}
	else {
		if (C0 > C1 && C0 > C2) {
			return Temp0;
		}
		if (C1 > C0 && C1 > C2) {
			return Temp1;
		}
		return Temp2;
	}
}

static inline CGFloat GSRoundGrid(CGFloat value, CGFloat grid) {
	value = round(value / grid) * grid;
	if (grid > 1) {
		value = round(value);
	}
	return value;
}

static inline NSPoint GSRoundPoint(NSPoint point) {
	point.x = round(point.x);
	point.y = round(point.y);
	return point;
}

NSPoint GSRoundPointToDirection(NSPoint point, NSPoint unitVector, CGFloat grid);

NSPoint GSRoundPointToLine(NSPoint point, NSPoint A, NSPoint B, CGFloat grid);

static inline NSPoint GSRoundPointGrid(NSPoint point, CGFloat grid) {
	if (grid >= 0.001) {
		point.x = round((point.x + 0.0000001) / grid) * grid;
		point.y = round((point.y + 0.0000001) / grid) * grid;
		if (grid > 1) {
			point.x = round(point.x);
			point.y = round(point.y);
		}
	}
	return point;
}

static inline NSSize GSRoundSize(NSSize size) {
	size.width = round(size.width);
	size.height = round(size.height);
	return size;
}

static inline NSRect GSRoundRectGrid(NSRect rect, CGFloat grid) {
	NSPoint topRight = NSMakePoint(NSMaxX(rect), NSMaxY(rect));
	topRight = GSRoundPointGrid(topRight, grid);
	NSPoint bottomLeft = NSMakePoint(NSMinX(rect), NSMinY(rect));
	bottomLeft = GSRoundPointGrid(bottomLeft, grid);
	rect.origin.x = bottomLeft.x;
	rect.origin.y = bottomLeft.y;
	rect.size.width = topRight.x - bottomLeft.x;
	rect.size.height = topRight.y - bottomLeft.y;
	return rect;
}

static inline NSRect GSRoundRect(NSRect rect) {
	return GSRoundRectGrid(rect, 1);
}

static inline NSPoint GSMaxPoint(NSPoint p1, NSPoint p2) {
	return NSMakePoint(fmax(p1.x, p2.x), fmax(p1.y, p2.y));
}

static inline NSPoint GSMinPoint(NSPoint p1, NSPoint p2) {
	return NSMakePoint(fmin(p1.x, p2.x), fmin(p1.y, p2.y));
}

static inline NSRect GSScaleRect(NSRect rect, CGFloat scale) {
	rect.origin.x *= scale;
	rect.origin.y *= scale;
	rect.size.height *= scale;
	rect.size.width *= scale;
	return rect;
}

static __inline__ NSRect GSCenteredRectInRect(NSRect innerRect, NSRect outerRect) {
	innerRect.origin.x = outerRect.origin.x + floor((outerRect.size.width - innerRect.size.width) / 2.0);
	innerRect.origin.y = outerRect.origin.y + floor((outerRect.size.height - innerRect.size.height) / 2.0);
	return innerRect;
}

BOOL GSValidateRect(NSRect rect);

NSRange GSValidateRange(NSRange range, NSUInteger max);

static inline NSSize GSScaleSize(NSSize aSize, CGFloat scale) {
	aSize.width *= scale;
	aSize.height *= scale;
	return aSize;
}

static inline NSPoint GSScalePoint(NSPoint P, CGFloat scalar) {
	return NSMakePoint(P.x * scalar, P.y * scalar);
}

static inline NSPoint GSAddPoints(NSPoint A, NSPoint B) {
	A.x += B.x;
	A.y += B.y;
	return A;
}

static inline NSPoint GSSubtractPoints(NSPoint A, NSPoint B) {
	A.x -= B.x;
	A.y -= B.y;
	return A;
}

#ifndef GLYPHS_VIEWER
static inline NSPoint GSInterpolatePoints(NSPoint a, NSPoint b, CGFloat t) {
	return NSMakePoint(LERP(t, a.x, b.x), LERP(t, a.y, b.y));
}
#endif

static inline NSPoint GSAbsolutePointInRect(NSPoint relativePoint, NSRect rect) {
	return NSMakePoint(rect.origin.x + (NSWidth(rect) * relativePoint.x),
					   rect.origin.y + (NSHeight(rect) * relativePoint.y));
}

static inline NSPoint GSRelativePointInRect(NSPoint absolutePoint, NSRect rect) {
	return NSMakePoint((absolutePoint.x - rect.origin.x) / NSWidth(rect),
					   (absolutePoint.y - rect.origin.y) / NSHeight(rect));
}

static inline NSPoint GSUnitVector(NSPoint A) {
	CGFloat Length = sqrtf((float)((A.x * A.x) + (A.y * A.y)));
	A.x /= Length;
	A.y /= Length;
	return A;
}

static inline NSPoint GSUnitVectorFromTo(NSPoint B, NSPoint A) {
	A.x -= B.x;
	A.y -= B.y;
	CGFloat Length = sqrtf((float)((A.x * A.x) + (A.y * A.y)));
	A.x /= Length;
	A.y /= Length;
	return A;
}

static inline NSPoint GSUnitVectorFromAngle(CGFloat Angle) { // in degree, x axis is 0°
	Angle *= M_PI_180;
	return NSMakePoint(cos(Angle), sin(Angle));
}

static inline NSPoint GSNormalVector1(NSPoint A);

static inline NSPoint GSNormalVector1(NSPoint A) {
	return NSMakePoint(A.y, -A.x);
}

static inline NSPoint GSNormalVector2(NSPoint A);

static inline NSPoint GSNormalVector2(NSPoint A) {
	return NSMakePoint(-A.y, A.x);
}

#define GSSquareDistance(A, B) (((A.x - B.x) * (A.x - B.x)) + ((A.y - B.y) * (A.y - B.y)))
#define GSDistance(A, B) sqrtf((float)(((A.x - B.x) * (A.x - B.x)) + ((A.y - B.y) * (A.y - B.y))))

CGFloat GSDistanceN(CGFloat *A, CGFloat *B, unsigned int count);

// t may be just outside 0 or 1. -0.0001 <= t <= 1.0001
#define T_EPSILON 0.00001
// arbitrary but catching when intersection is found and points are close but just outside
//#define PT_EPSILON	0.0000001
#define PT_EPSILON 0.00000099

#define GSFloatsEqualThreshold(A, B, threshold) (fabs(A - B) < threshold)
#define GSFloatsEqual(A, B) GSFloatsEqualThreshold(A, B, T_EPSILON)

#define GSPointsEqual(A, B, threshold) (fabs(A.x - B.x) < threshold && fabs(A.y - B.y) < threshold)

#define GSRectsEqual(A, B, threshold) (fabs(A.origin.x - B.origin.x) < threshold && fabs(A.origin.y - B.origin.y) < threshold && fabs(A.size.width - B.size.width) < threshold && fabs(A.size.height - B.size.height) < threshold)

#define GSPointIsZero(A, threshold) (fabs(A.x) < threshold && fabs(A.y) < threshold)

#define RAD(A) ((A) * M_PI_180)

#define DEG(A) ((A) / M_PI_180)

static inline CGFloat GSNormalizeAngleRAD(CGFloat angle) {
	return angle - floor(angle / (2 * M_PI)) * 2 * M_PI;
}

static inline CGFloat GSNormalizeAngleDEG(CGFloat angle) {
	return angle - floor(angle / 360) * 360;
}

static inline CGFloat GSSlope(NSPoint P1, NSPoint P2) {
	if (fabs(P2.x - P1.x) < 0.001) {
		return NSNotFound;
	}
	return (P2.y - P1.y) / (P2.x - P1.x);
}

NSRect GSRectFromTwoPoints(NSPoint a, NSPoint b);
NSRect GSRectFromFourPoints(NSPoint a, NSPoint b, NSPoint c, NSPoint d);
#endif

CGFloat GSAngleOfVector(NSPoint P);

CGFloat GSAngleBetweenVectors(NSPoint firstPoint, NSPoint secondPoint);

BOOL GSOffsetLine(NSPoint *P0, NSPoint *P1, CGFloat OffsetX, CGFloat OffsetY);

/*****
 *
 *   dot
 *
 *****/
CGFloat GSDot(NSPoint P1, NSPoint P2);

BOOL GSPointIsLeftOfLine(NSPoint P0, NSPoint P1, NSPoint point);

int GSLeftRayHitsLine(NSPoint P0, NSPoint P1, NSPoint point);

/**
 @param segment a segment to hit
 @param point the start of the ray
 @param ignoreT t value to ignore. this is needed for the selftest branch
 */
int GSLeftRayHitsSegment(GSPathSegment *segment, NSPoint point, int ignoreT);

int GSLeftRayHitsCurve(NSPoint point, NSPoint p0, NSPoint p1, NSPoint p2, NSPoint p3, int ignoreT);

/*****
 *
 *   lerp
 *
 *****/
// NSPoint GSLerp(NSPoint P1, NSPoint P2, CGFloat t);

/*
GSPoint3 GSLerp3D(GSPoint3 A, GSPoint3 B, CGFloat t);
*/

NSPoint GSMiddlePointLine(NSPoint P1, NSPoint P2);

NSPoint GSMiddlePointCurve(NSPoint P1, NSPoint P2, NSPoint P3, NSPoint P4);
/*****
 *
 *   gte - greater than or equal
 *
 *****/
BOOL GSGte(NSPoint P1, NSPoint P2);

// check if line ab and bc have the same direction
static inline BOOL GSCheckColinearLines(NSPoint a, NSPoint b, NSPoint c) {
	CGFloat area = a.x * (b.y - c.y) + b.x * (c.y - a.y) + c.x * (a.y - b.y);
	// GSLog(@"__area %f", area);
	return fabs(area) < 0.001;
}

// better use GSIntersectBezier3Bezier3SegmentsOperations

NSArray *GSIntersectBezier3Bezier3(const NSPoint a1, const NSPoint a2, const NSPoint a3, const NSPoint a4, const NSPoint b1, const NSPoint b2, const NSPoint b3, const NSPoint b4);

NSArray *GSIntersectBezier3Bezier3Unlimited(const NSPoint P1, const NSPoint P2, const NSPoint P3, const NSPoint P4, const NSPoint Q1, const NSPoint Q2, const NSPoint Q3, const NSPoint Q4);
/** Finds all intersections between a curve and a line.

@param p1 start point of the curve.
@param p2 first offcurve point of the curve.
@param p3 second offcurve point of the curve.
@param p4 end point of the curve.
@param a1 first point of the line.
@param a2 second point of the line.
@return an NSArray of point values for all intersections.
*/
NSArray *_Nullable GSIntersectBezier3Line(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2);

int GSIntersectBezier3LineCount(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2, NSPoint results[_Nonnull 4]);

NSArray *_Nullable GSIntersectBezier3LineTimes(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2, BOOL checkBounds);

NSArray *_Nullable GSIntersectBezier3LineUnlimited(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2);

int GSIntersectBezier3LineUnlimitedCount(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2, NSPoint intersections[_Nonnull 3]);

NSArray *_Nullable GSIntersectBezier3LineUnlimitedTimes(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint a1, NSPoint a2);

NSArray *GSIntersectQuadraticSplineLine(NSPoint *points, NSUInteger count, NSPoint a1, NSPoint a2);

NSArray *GSIntersectQuadraticLine(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint a1, NSPoint a2);
/*
CGFloat GSDistanceOfPointFromLine3D(GSPoint3 inPoint, GSPoint3 a, GSPoint3 b);
*/

BOOL GSCollinearLines(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

NSPoint GSIntersectLineLine(const NSPoint a1, const NSPoint a2, const NSPoint b1, const NSPoint b2);

NSPoint GSIntersectLineLineUnlimited(const NSPoint aa, const NSPoint ab, const NSPoint ba, const NSPoint bb);

BOOL GSIntersectLineWithRect(NSPoint p1, NSPoint p2, NSRect rect, NSPoint *i1, NSPoint *i2);

BOOL GSClipLineWithRect(NSPoint *start, NSPoint *end, NSRect rect);

BOOL GSCircleContainsRect(NSPoint center, CGFloat radius, NSRect rect);

BOOL GSRectContainsRect(NSRect outer, NSRect inner);

void GSIntersectCircles(NSPoint M0, CGFloat R0, NSPoint M1, CGFloat R1, NSPoint *Q0, NSPoint *Q1);

NSPoint GSNearestPointOnCurveSegmentBounds(NSPoint P, NSPoint *V, CGFloat *__nullable tValue);

NSPoint GSNearestPointOnCurveSegment(NSPoint P, NSPoint *V, CGFloat *__nullable tValue);

NSPoint GSNearestPointOnQCurveSegment(NSPoint P, NSPoint *pt, CGFloat *__nullable tValue);

CGFloat GSDistanceOfPointFromCurve(NSPoint inPoint, NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

CGFloat GSSquareDistanceOfPointFromCurveSegment(NSPoint inPoint, NSPoint *V);

CGFloat GSDistanceOfPointFromLineSegment(NSPoint inPoint, NSPoint a, NSPoint b);

CGFloat GSDistanceOfPointFromLine(NSPoint inPoint, NSPoint a, NSPoint b);

NSPoint GSNearestPointOnCurve(NSPoint inPoint, NSPoint p1, const NSPoint p2, const NSPoint p3, const NSPoint p4, CGFloat *__nullable t);

NSPoint GSNearestPointOnQCurve(NSPoint inPoint, NSPoint *V, int count, CGFloat *__nullable t);

NSPoint GSNearestPointOnLineSegment(NSPoint inPoint, NSPoint a, NSPoint b, CGFloat *__nullable t);

NSPoint GSNearestPointOnLine(NSPoint inPoint, NSPoint a, NSPoint b, CGFloat *__nullable t);

void GSDividBezier3PointsAtDistance(NSPoint p0, NSPoint p1, NSPoint p2, NSPoint p3, NSPoint *q1, NSPoint *q2, NSPoint *q3, NSPoint *r1, NSPoint *r2, CGFloat offset, CGFloat *length);

NSPoint GSDividLineAtDistance(NSPoint p0, NSPoint p1, CGFloat offset);

void GSExtendCurvePoints(const NSPoint P0, NSPoint *P1, NSPoint *P2, NSPoint *P3, CGFloat offset);

BOOL GSFitCurveToOriginPoints(NSPoint fitPoints[_Nonnull 4], const NSPoint *originPoints, const unsigned short count, CGFloat *__nullable curveError);

BOOL GSFitCurveToOriginPointsDirection(NSPoint fitPoints[_Nonnull 4], const NSPoint *originPoints, const NSPoint *__nullable originDirections, const unsigned short count, CGFloat *__nullable curveError);

BOOL GSCurveIsFlat(NSPoint on1, NSPoint off1, NSPoint off2, NSPoint on2, CGFloat threshold);

BOOL GSCurveIsFlatError(NSPoint on1, NSPoint off1, NSPoint off2, NSPoint on2, CGFloat flatError);
/*
GSPoint3 GSNearestPointOnLine3D(GSPoint3 inPoint, GSPoint3 a, GSPoint3 b, CGFloat* t);
*/
/******
 *
 * Bezier dividing
 *
 ******/

void GSHalveCurve(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, NSPoint *q0, NSPoint *q1, NSPoint *q2, NSPoint *q3, NSPoint *r1, NSPoint *r2, NSPoint *r3);

void GSHalveSegment(NSPoint P[_Nonnull 4], NSPoint q[_Nonnull 4], NSPoint r[_Nonnull 4]);

void GSQuadratic2Qubic(NSPoint pt0, NSPoint pt1, NSPoint pt2, NSPoint *off1, NSPoint *off2);

void GSDividBezier2Points(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint *off1, NSPoint *on, NSPoint *off2, CGFloat t);

void GSDividBezier2Segment(NSPoint *V, int count, NSPoint *off1, NSPoint *on, NSPoint *off2, CGFloat segmentTime);

void GSDividBezier3Points(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, NSPoint *q0, NSPoint *q1, NSPoint *q2, NSPoint *q3, NSPoint *r1, NSPoint *r2, NSPoint *r3, CGFloat t);

void GSDividBezier3Segments(const NSPoint P[_Nonnull 4], NSPoint q[_Nonnull 4], NSPoint r[_Nonnull 4], CGFloat t);

void GSSegmentBetweenPoints(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, NSPoint *q0, NSPoint *q1, NSPoint *q2, NSPoint *q3, NSPoint StartRangePoint, NSPoint EndRangePoint);

// NSPoint GSDivideLine(NSPoint P0, NSPoint P1, CGFloat t); // Deprecated -> GSPointOnLine

NSPoint GSPointOnLine(NSPoint P0, NSPoint P1, CGFloat t);

__attribute__((deprecated("GSPointAtTime has been deprecated please use GSPointOnCurve instead")))
NSPoint
GSPointAtTime(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, CGFloat t);

NSPoint GSPointOnCurve(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, CGFloat t);

NSPoint GSPointOnQuadratic(NSPoint P0, NSPoint P1, NSPoint P2, CGFloat t);

CGFloat GSLengthOfSegment(NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3);

CGFloat GSTForDistance(CGFloat Distance, NSPoint P0, NSPoint P1, NSPoint P2, NSPoint P3, CGFloat maxT);

void GSExtremeTimesOfBezierUnlimited(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat *t1, CGFloat *t2, CGFloat *t3, CGFloat *t4);

void GSExtremeTimesOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat *t1, CGFloat *t2, CGFloat *t3, CGFloat *t4);

void GSExtremePointsOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, NSPoint *r1, NSPoint *r2, NSPoint *r3, NSPoint *r4);

NSArray *GSExtremesOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

void GSTangentPointsAtAngel(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat angle, CGFloat *t1, CGFloat *t2);

void GSTangetPointsAtAngel(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat angle, CGFloat *t1, CGFloat *t2) DEPRECATED_MSG_ATTRIBUTE("Use 'GSTangentPointsAtAngel' instead");

void GSBoundsOfBezier2(NSPoint p1, NSPoint p2, NSPoint p3, CGFloat *minX, CGFloat *minY, CGFloat *maxX, CGFloat *maxY);

void GSBoundsOfBezier2Points(NSPoint *elements, NSUInteger count, CGFloat *minX, CGFloat *minY, CGFloat *maxX, CGFloat *maxY);

void GSBoundsOfBezier3(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4, CGFloat *minX, CGFloat *minY, CGFloat *maxX, CGFloat *maxY, BOOL *isSimple);

NSRect GSBoundingRectOfBezier(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

NSRect GSBoundsOfTransformedRect(NSRect rect, NSAffineTransform *transform);

NSArray *GSComputeInflection(NSPoint p1, NSPoint p2, NSPoint p3, NSPoint p4);

void GSTransformComponents(NSAffineTransformStruct M, CGFloat *__nullable sX, CGFloat *__nullable sY, CGFloat *__nullable R, CGFloat *__nullable shearX, CGFloat *__nullable shearY);

NSArray *curvatureMaxima(NSPoint P1, NSPoint P2, NSPoint P3, NSPoint P4);

CGFloat curvatureSquaredForT(NSPoint P1, NSPoint P2, NSPoint P3, NSPoint P4, CGFloat t);

NSPoint GSNormalCubicForT(NSPoint P1, NSPoint P2, NSPoint P3, NSPoint P4, CGFloat t);

BOOL GSCombineSegments3(NSPoint *P, NSPoint *Q, NSPoint *R);

BOOL GSCombineSegments3Fit(NSPoint *P, NSPoint *Q, NSPoint *R, CGFloat *__nullable error);

BOOL GSFixDeadCurve(NSPoint P0, NSPoint *P1, NSPoint *P2, NSPoint P3, BOOL force);

void GSFixBounds(NSRect *bounds);

struct GSNVector {
	double coord[30];
};
typedef struct GSNVector GSNVector;
GSNVector GSMakeNVector(int n, ...);
GSNVector GSInitNVector(CGFloat d);
CGFloat GSDistanceOfPointFromLineN(GSNVector inPoint, GSNVector a, GSNVector b, int n);

GSNVector GSMultiplyNVectors(GSNVector a, GSNVector b, int n);

#pragma mark Compare
NSComparisonResult GSSortPointValue(id value1, id value2, void *reverse);

NSComparisonResult GSSortInteger(NSInteger idx1, NSInteger idx2);

NSComparisonResult GSSortFloat(CGFloat value1, CGFloat value2);

NSComparisonResult GSSortFloatLenient(CGFloat value1, CGFloat value2);

BOOL GSComparePoint(NSPoint P1, NSPoint P2);

static inline BOOL GSCompareSize(NSSize S1, NSSize S2) {
	return fabs(S1.width - S2.width) < 0.001 && fabs(S1.height - S2.height) < 0.001;
}

BOOL GSCompareTransformStruct(NSAffineTransformStruct T1, NSAffineTransformStruct T2);

static inline BOOL GSCompareRect(NSRect r1, NSRect r2) {
	return fabs(r1.origin.x - r2.origin.x) < 0.0001 && fabs(r1.origin.y - r2.origin.y) < 0.0001 && fabs(r1.size.width - r2.size.width) < 0.0001 && fabs(r1.size.height - r2.size.height) < 0.0001;
}

BOOL GSCompareRange(NSRange r1, NSRange r2);

BOOL GSFloatToFraction(CGFloat value, CGFloat accuracy, int *numr, int *dnom);

/// convert and angle (CCV from 12 o’clock) to run/rise as stored in the hhea table
BOOL GSAngleToSlope(CGFloat angle, int *run, int *rise);

/// The angle of run/rise. This can be used to write the italic angle so the angle is positive CCV from 12 o’clock
CGFloat GSAngleFromString(NSString *string);

NSPoint GSRotatePointAroundCenter(NSPoint point, NSPoint center, CGFloat angle);

NSPoint GSRotatePoint(NSPoint point, CGFloat angle);

NSPoint GSNearestPointOnEllipse(NSPoint center, NSSize size, CGFloat angle, NSPoint point);

CGFloat GSDistanceOfPointFromEllipse(NSPoint point, NSPoint center, NSSize size, CGFloat angle);

NSPoint GSNearestPointOnRect(NSRect rect, CGFloat angle, NSPoint point);

CGFloat GSDistanceOfPointFromRect(NSPoint point, NSRect rect, CGFloat angle);

NSString *GSStringFromPoint(NSPoint p);

#ifndef GLYPHS_VIEWER
NSString *GSStringFromTransformStruct(NSAffineTransformStruct ts);

void GSAffineSolver(NSAffineTransformStruct *ts, NSPoint *p, NSPoint *p_);

NSRect GSHandleRect(NSPoint pos, CGFloat handleSize);

NSPoint GSAlignedPointForBounds(NSRect bounds, GSAlignment corner);

NSRect GSHandleRectForBounds(NSRect bounds, GSAlignment corner, CGFloat handleSize);

CGFloat GSCalc(CGFloat operand1, char op, CGFloat operand2);

NSPoint GSPointFromArray(NSArray *value);

CGFloat GSFindDivisorClosestTo(CGFloat number, int target);
#endif

NSAffineTransform *GSAffineTransformMappingLine2Line(NSPoint p1, NSPoint p2, NSPoint q1, NSPoint q2, BOOL preserveLength, NSError **outError);

BOOL GSColinearLines(NSPoint p1, NSPoint mid, NSPoint p2);

BOOL GSColinearLinesTolerance(NSPoint p1, NSPoint mid, NSPoint p2, double eps);

NS_ASSUME_NONNULL_END
#if defined(__cplusplus)
} // extern "C"
#endif /* defined(__cplusplus) */
