# BezierKit

BezierKit is a comprehensive Bezier Path library written in Swift.

## Warning! Prerelease software!

Please note that BezierKit is currently pre-release software. Its releases follow semantic versioning which means that until it reaches 1.0 status the API may not be stable or backwards compatible.

## Features

- ☑ Constructs linear (line segment), quadratic, and cubic Bézier curves
- ☑ Draws curves via CoreGraphics
- ☑ Determines positions, derivatives, and normals along curves
- ☑ Lengths of curves via Legendre-Gauss quadrature
- ☑ Intersects curves and computes cubic curve self-intersection to any degree of accuracy
- ☑ Determines bounding boxes, extrema,
- ☑ Locates nearest on-curve location to point
- ☑ to any degree of accuracy
- ☑ Splits curves into subcurves
- ☑ Offsets and outlines curves
- ☐ Comprehensive Unit and Integration Test Coverage
- ☐ Complete Documentation

## Installation

### CocoaPods

CocoaPods is a dependency manager for Cocoa projects. You can install it with the following command:

```
$ gem install cocoapods
```

To integrate BezierKit into your Xcode project using CocoaPods, add it to your target in your `Podfile`

:

```
target '<Your Target Name>' do
pod 'BezierKit', '>= 0.8.0'
end
```

Then, run the following command:

```
$ pod install
```

### Swift Package Manager

The Swift Package Manager is a tool for automating the distribution of Swift code and is integrated into the `swift`

compiler.
Once you have your Swift package set up, adding BezierKit as a dependency is as easy as adding it to the `dependencies`

value of your `Package.swift`

.

```
// swift-tools-version:5.0
import PackageDescription
let package = Package(
name: "<Your Target Name>",
dependencies: [
.package(url: "https://github.com/hfutrell/BezierKit.git", from: "0.8.0"),
]
)
```

## Usage

### Constructing & Drawing Curves

BezierKit supports cubic Bezier curves (`CubicCurve`

) and quadratic Bezier curves (`QuadraticCurve`

) as well as line segments (`LineSegment`

) each of which adopts the `BezierCurve`

protocol that encompasses most API functionality.

```
import BezierKit
let curve = CubicCurve(
p0: CGPoint(x: 100, y: 25),
p1: CGPoint(x: 10, y: 90),
p2: CGPoint(x: 110, y: 100),
p3: CGPoint(x: 150, y: 195)
)
let context: CGContext = ... // your graphics context here
Draw.drawSkeleton(context, curve) // draws visual representation of curve control points
Draw.drawCurve(context, curve) // draws the curve itself
```

### Intersecting Curves

The `intersections(with curve: BezierCurve) -> [Intersection]`

method determines each intersection between `self`

and `curve`

as an array of `Intersection`

objects. Each intersection has two fields: `t1`

represents the t-value for `self`

at the intersection while `t2`

represents the t-value for `curve`

at the intersection. You can use the `ponit(at:)`

method on either of the curves to calculate the coordinates of the intersection by passing in the corresponding t-value for the curve.

Cubic curves may self-intersect which can be determined by calling the `selfIntersections()`

method.

```
let intersections: [Intersection] = curve1.intersections(with: curve2)
let points: [CGPoint] = intersections.map { curve1.point(at: $0.t1) }
Draw.drawCurve(context, curve: curve1)
Draw.drawCurve(context, curve: curve2)
for p in points {
Draw.drawPoint(context, origin: p)
}
```

### Splitting Curves

The `split(from:, to:)`

method produces a subcurve over a given range of t-values. The `split(at:)`

method can be used to produce a left subcurve and right subcurve created by splitting across a single t-value.

```
Draw.setColor(context, color: Draw.lightGrey)
Draw.drawSkeleton(context, curve: curve)
Draw.drawCurve(context, curve: curve)
let subcurve = curve.split(from: 0.25, to: 0.75) // or try (leftCurve, rightCurve) = curve.split(at:)
Draw.setColor(context, color: Draw.red)
Draw.drawCurve(context, curve: subcurve)
Draw.drawCircle(context, center: curve.point(at: 0.25), radius: 3)
Draw.drawCircle(context, center: curve.point(at: 0.75), radius: 3)
```

### Determining Bounding Boxes

```
let boundingBox = curve.boundingBox
Draw.drawSkeleton(context, curve: curve)
Draw.drawCurve(context, curve: curve)
Draw.setColor(context, color: Draw.pinkish)
Draw.drawBoundingBox(context, boundingBox: curve.boundingBox)
```

### More

BezierKit is a powerful library with *a lot* of functionality. For the time being the best way to see what it offers is to build the MacDemos target and check out each of the provided demos.

## License

BezierKit is released under the MIT license. See LICENSE for details.

## Github

link |

Stars: 93 |

## You may find interesting

## Dependencies

## Used By

Total: 0

## Releases

## -

- speeds up implementation of
`CubicCurve.project(_:)`

by a factor of 2 - provides implementation of
`Path.project(_:)`

and`PathComponent.project(_:)`

to help common task of finding closest point on path - additional performance tests

## -

Makes PathComponentRange properties mutable and exposes constructor publicly

## -

- adds support for installation via Swift Package Manager (SPM)
- adds support for
`NSSecureCoding`

in`Path`

- adds support for Linux (excluding interoperability with unavailable
`CoreGraphics`

APIs such as`CGPath`

)

## -

- reverts a change from v0.6.6 that was causing issues

## -

- improves performance of cubic curve self-intersection routine
`selfIntersects`

- minor bug fixes

## -

Fixes an issue where `curve.project(_:)`

could return incorrect results because the closest solution could be missed.

## -

- improved algorithm for
`curve.project(_:)`

so that accuracy parameter is no longer needed - renames
`curve.compute(_:)`

to`curve.point(at:)`

- renames
`curve.derivative(_:)`

to`curve.derivative(at:)`

- renames
`curve.normal(_:)`

to`curve.normal(at:)`

## -

- fixed an issue where importing BezierKit in an app extension would cause a build warning
- vector boolean operations such as
`Path.union(_ path: Path)`

no longer return an optional `Path.selfIntersections(accuracy:)`

once again includes intersections where a single curve element intersects itself

## -

`scale()`

now returns `nil`

when attempting to scale an unscalable curve, such as a point.

`PathComponent.offset()`

now ignores curves that cannot be scaled, and returns `nil`

if none of the component’s curves could be scaled. In previous releases offsetting a circle with zero radius would result in a crash.

Fixes issues with vector boolean operations on paths with coincident edges

Removes `cgPath`

property of `PathComponent`

. If you need this consider constructing a Path with a single component, ie using `Path(components: [component]).cgPath`

Improves the performance of computing `Path.cgPath`

in the case of multi-component paths. This is especially relevant to dashed paths which can have hundreds of components.

## -

This release patches the old 0.5.13 release so resolve compiler warnings. Most people should use version 0.6+.

## -

This release refactors a number of APIs and improves support for vector boolean operations in cases where edges are coincident.

It also removes some APIs from the core library which were questionably useful, including approximating curves as arcs (`ArcApproximateable`

) and support for variable width outlines.

## -

- improves the behavior of
`Path.contains(_:CGPoint)`

for complex paths - new implementation of lock that should appear as OK when thread sanitizer is used
- fixes behavior of
`disjointComponents()`

in certain cases

## -

- fixes behavior of
`Path.contains(:_)`

for some cases of paths where an path element is a cubic curve with multiple extrema

## -

- improves the reliability of
`Path.contains`

## -

- fixes bugs in vector boolean operations

## -

- improves thread safety of Path.cgPath and other lazily computed properties
- adds swiftlint rules for code quality (though these are not yet enforced)
- improvements to vector boolean methods
- improvements to line / curve intersection

## -

## -

- fixes a minor issue in an edge case of cubic curve normals at cusps regressed in version
`0.5.5`

- fixes an edge case issue where calling
`.reduce()`

on degenerate curves could result in a very large number of results - makes numerous minor improvements to code quality and unit tests

## -

- improves the behavior of
`BezierCurve.normal(_:)`

for`t=0`

and`t=1`

in edge cases where derivative may be zero because control points are exactly equal to starting or ending points of the curve. - improves behavior of
`BezierCurve.reduce()`

when extreme points are very close to each other - fixes crash in
`BezierCurve.scale(distance:)`

## -

refines the fix released in version `0.5.3`

to address more related crashes

## -

This releases fixes an uncommon crash that could occur vector boolean operations

## -

This release includes several minor bugfixes:

- ensures that calling
`BezierCurve.split(from:, to:)`

on a set of ranges creates a series of curves that are exactly continuous - fixes a crash when creating an instance of
`Path`

from a`cgPath`

which has a subpath that was started without a`move(to:)`

command - fixes behavior of
`Path.intersections(with:)`

for with open paths that intersect at end points.

## -

- added notion of
`PathComponentRange`

and`PathComponent.split(range:)`

using it. - made it easier for subclassers to support splitting by making it so method to override is guaranteed to be passed a standardized range.

## -

changes:

- upgraded swift version to 5.0
- renames
`BezierCurve.project(point: pointToProject, errorThreshold: epsilon)`

to`BezierCurve.project(_:, accuracy:)`

. The method now returns a tuple`(point: CGPoint, t: CGFloat)`

for applications that need the`t`

value of the projection. - the
`errorThreshold`

parameter of`Path.pointIsWithinDistanceOfBoundary`

has been renamed to`accuracy`

## -

New features:

- adds ability to split path components with
`PathComponent.split(from:, to:)`

- adds utility method for enumerating over points of
`PathComponent`

with option to include or exclude control points`PathComponent.enumeratePoints(includeControlPoints:, using:)`

- allows subclassing of
`Path`

and`PathComponent`

- adds
`Comparable`

support to`IndexedPathLocation`

and`IndexedPathComponentLocation`

- adds
`distanceSquared`

function between points

bugfixes

- fixes a memory leak in vector boolean operations
- improves support for converting to and from CoreGraphics paths that have single point subpaths

## -

This release refactors several intersection methods and adds new `intersects(with:) -> Bool`

methods for better API usage when the caller only cares if things intersect (without caring about the intersections themselves).

It makes public the `Flatness`

API for determining the maximum distance a Bezier curve strays from the line segment connecting its endpoints.

It renames "threshold" parameters to the more descriptive "accuracy"

It has several other miscellaneous renamings.

## -

greatly improves the accuracy of `Path.pointIsWithinDistanceOfBoundary(point:, distance:)`

and `BezierCurve.project(point:)`

. Adds an `errorThreshold`

parameter to these functions and related functions (with a default value) so that the caller can control speed vs accuracy of the result.

## -

This release adds `Path.data`

and `Path(data: Data)`

methods to convert between `Path`

instances and a compact binary representation for serialization / deserialization.

The release also dramatically reduces the memory usage of `Path`

instances.

`Path.subpaths`

has been renamed `Path.components`

`Path.curves`

may be deprecated in the future. To access elements it's recommended that you use `Path.element(at:)`

instead. It's recommended that you use `Path.elementCount`

instead of `Path.curves.count`

because it's much faster.

Exposed `CGPoint.cross(_:)`

vector cross product method