Skip to content

Commit

Permalink
Merge pull request #23 from hfutrell/0.1.15-release
Browse files Browse the repository at this point in the history
0.1.15 release
  • Loading branch information
hfutrell authored Dec 19, 2018
2 parents 56a9a86 + 74a5489 commit e7cd58d
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 26 deletions.
2 changes: 1 addition & 1 deletion BezierKit.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

Pod::Spec.new do |s|
s.name = "BezierKit"
s.version = "0.1.14"
s.version = "0.1.15"
s.summary = "comprehensive Bezier curve library written in Swift"
s.homepage = "https://github.com/hfutrell/BezierKit"
s.license = "MIT"
Expand Down
6 changes: 6 additions & 0 deletions BezierKit/BezierKit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
FD4A6404200ACBD200930E10 /* ShapeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4A6400200ACB8900930E10 /* ShapeTests.swift */; };
FD4A6408200B11DF00930E10 /* PathComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4A6405200B11DA00930E10 /* PathComponentTests.swift */; };
FD4A6409200B11DF00930E10 /* PathComponentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD4A6405200B11DA00930E10 /* PathComponentTests.swift */; };
FD6D9C9F21BEE75C008652C5 /* TransformableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6D9C9E21BEE75C008652C5 /* TransformableTests.swift */; };
FD6D9CA021BEE75C008652C5 /* TransformableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FD6D9C9E21BEE75C008652C5 /* TransformableTests.swift */; };
FDA727591ED5035300011871 /* CubicBezierCurveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA727581ED5035300011871 /* CubicBezierCurveTests.swift */; };
FDA7275A1ED5035300011871 /* CubicBezierCurveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDA727581ED5035300011871 /* CubicBezierCurveTests.swift */; };
FDB512C821373BBD007CBA20 /* AugmentedGraphTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB512C521373A3D007CBA20 /* AugmentedGraphTests.swift */; };
Expand Down Expand Up @@ -129,6 +131,7 @@
FD4A63FD200AA50B00930E10 /* Shape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Shape.swift; path = Library/Shape.swift; sourceTree = SOURCE_ROOT; };
FD4A6400200ACB8900930E10 /* ShapeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShapeTests.swift; sourceTree = "<group>"; };
FD4A6405200B11DA00930E10 /* PathComponentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PathComponentTests.swift; sourceTree = "<group>"; };
FD6D9C9E21BEE75C008652C5 /* TransformableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransformableTests.swift; sourceTree = "<group>"; };
FDA727581ED5035300011871 /* CubicBezierCurveTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CubicBezierCurveTests.swift; sourceTree = "<group>"; };
FDB512C521373A3D007CBA20 /* AugmentedGraphTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AugmentedGraphTests.swift; sourceTree = "<group>"; };
FDB6B3F71EAFD6DF00001C61 /* BezierCurve.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BezierCurve.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -247,6 +250,7 @@
FDC7D705211288BD00A9EEF0 /* PathTests.swift */,
FDC859592118EC5600AF7642 /* DrawTests.swift */,
FDB512C521373A3D007CBA20 /* AugmentedGraphTests.swift */,
FD6D9C9E21BEE75C008652C5 /* TransformableTests.swift */,
);
path = BezierKitTests;
sourceTree = "<group>";
Expand Down Expand Up @@ -604,6 +608,7 @@
FD05111820153EAE000D035E /* BoundingBoxTests.swift in Sources */,
FDB512C821373BBD007CBA20 /* AugmentedGraphTests.swift in Sources */,
FDC7D706211288BD00A9EEF0 /* PathTests.swift in Sources */,
FD6D9C9F21BEE75C008652C5 /* TransformableTests.swift in Sources */,
FDC8595A2118EC5600AF7642 /* DrawTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -624,6 +629,7 @@
FD05111920153EAE000D035E /* BoundingBoxTests.swift in Sources */,
FDB512C921373BBD007CBA20 /* AugmentedGraphTests.swift in Sources */,
FDC7D707211288BD00A9EEF0 /* PathTests.swift in Sources */,
FD6D9CA021BEE75C008652C5 /* TransformableTests.swift in Sources */,
FDC8595B2118EC5600AF7642 /* DrawTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
12 changes: 12 additions & 0 deletions BezierKit/BezierKitTests/PathTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,18 @@ class PathTests: XCTestCase {
XCTAssertFalse(circleWithHole.contains(CGPoint(x: 4.0, y: 0.0), using: .winding))
}

func testContainsPath() {
let rect1 = Path(cgPath: CGPath(rect: CGRect(x: 1, y: 1, width: 5, height: 5), transform: nil))
let rect2 = Path(cgPath: CGPath(rect: CGRect(x: 2, y: 2, width: 3, height: 3), transform: nil)) // fully contained inside rect1
let rect3 = Path(cgPath: CGPath(rect: CGRect(x: 2, y: 2, width: 5, height: 3), transform: nil)) // starts inside, but not contained in rect1
let rect4 = Path(cgPath: CGPath(rect: CGRect(x: 7, y: 1, width: 5, height: 5), transform: nil)) // fully outside rect1
XCTAssertTrue(rect1.contains(rect2))
XCTAssertFalse(rect1.contains(rect3))
XCTAssertFalse(rect1.contains(rect4))
}

// TODO: more tests of contains path using .winding rule and where intersections are not crossings

// MARK: - vector boolean operations

private func componentsEqualAsideFromElementOrdering(_ component1: PathComponent, _ component2: PathComponent) -> Bool {
Expand Down
71 changes: 71 additions & 0 deletions BezierKit/BezierKitTests/TransformableTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// TransformableTests.swift
// BezierKit
//
// Created by Holmes Futrell on 12/10/18.
// Copyright © 2018 Holmes Futrell. All rights reserved.
//

import XCTest
import BezierKit

class TransformableTests: XCTestCase {

// rotates by 90 degrees ccw and then shifts (-1, 1)
let transform = CGAffineTransform(a: 0, b: 1, c: -1, d: 0, tx: -1, ty: 1)

override func setUp() {
// assert(CGPoint(x: 1, y: 0).applying(transform) == CGPoint(x: -1, y: 2))
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}

func testTransformLineSegment() {
let l = LineSegment(p0: CGPoint(x: -1, y: -1), p1: CGPoint(x: 3, y: 1))
XCTAssertEqual(l.copy(using: transform), LineSegment(p0: CGPoint(x: 0, y: 0), p1: CGPoint(x: -2, y: 4)))
}

func testTransformQuadraticCurve() {
let q = QuadraticBezierCurve(p0: CGPoint(x: -1, y: -1),
p1: CGPoint(x: 3, y: 1),
p2: CGPoint(x: 7, y: -1))
XCTAssertEqual(q.copy(using: transform), QuadraticBezierCurve(p0: CGPoint(x: 0, y: 0),
p1: CGPoint(x: -2, y: 4),
p2: CGPoint(x: 0, y: 8)))
}

func testTransformCubicCurve() {
let c = CubicBezierCurve(p0: CGPoint(x: -1, y: -1),
p1: CGPoint(x: 3, y: 1),
p2: CGPoint(x: 7, y: -1),
p3: CGPoint(x: 8, y: 0))
XCTAssertEqual(c.copy(using: transform), CubicBezierCurve(p0: CGPoint(x: 0, y: 0),
p1: CGPoint(x: -2, y: 4),
p2: CGPoint(x: 0, y: 8),
p3: CGPoint(x: -1, y: 9)))
}

func testTransformPath() {

// just a simple path with two path components made up of line segments

let l1 = LineSegment(p0: CGPoint(x: -1, y: -1), p1: CGPoint(x: 3, y: 1))
let l2 = LineSegment(p0: CGPoint(x: 1, y: 1), p1: CGPoint(x: 2, y: 3))

let path = Path(subpaths: [PathComponent(curves: [l1]), PathComponent(curves: [l2])])

let transformedPath = path.copy(using: transform)

let expectedl1 = LineSegment(p0: CGPoint(x: 0, y: 0), p1: CGPoint(x: -2, y: 4))
let expectedl2 = LineSegment(p0: CGPoint(x: -2, y: 2), p1: CGPoint(x: -4, y: 3))

XCTAssertEqual(transformedPath.subpaths.count, 2)
XCTAssertEqual(transformedPath.subpaths[0].curves.count, 1)
XCTAssertEqual(transformedPath.subpaths[0].curves.count, 1)
XCTAssertEqual(transformedPath.subpaths[0].curves[0] as! LineSegment, expectedl1)
XCTAssertEqual(transformedPath.subpaths[1].curves[0] as! LineSegment, expectedl2)
}

}
2 changes: 1 addition & 1 deletion BezierKit/Library/AugmentedGraph.swift
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ internal class PathLinkedListRepresentation {
assert(end !== list[location.elementIndex+1])
end = end.next
}
insertIntersectionVertex(v, between: start, and: end, at: location.t, for: path.element(at: location), inList: &list)
insertIntersectionVertex(v, between: start, and: end, at: location.t, for: path.elementAtComponentIndex(location.componentIndex, elementIndex: location.elementIndex), inList: &list)
}
self.lists[location.componentIndex] = list
}
Expand Down
38 changes: 20 additions & 18 deletions BezierKit/Library/Path.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ internal func windingCountImpliesContainment(_ count: Int, using rule: PathFillR

public let subpaths: [PathComponent]

public func pointIsWithinDistanceOfBoundary(point p: CGPoint, distance d: CGFloat) -> Bool {
@objc(point:isWithinDistanceOfBoundary:) public func pointIsWithinDistanceOfBoundary(point p: CGPoint, distance d: CGFloat) -> Bool {
return self.subpaths.contains {
$0.pointIsWithinDistanceOfBoundary(point: p, distance: d)
}
Expand Down Expand Up @@ -159,11 +159,11 @@ internal func windingCountImpliesContainment(_ count: Int, using rule: PathFillR
// MARK: - vector boolean operations

public func point(at location: IndexedPathLocation) -> CGPoint {
return self.element(at: location).compute(location.t)
return self.elementAtComponentIndex(location.componentIndex, elementIndex: location.elementIndex).compute(location.t)
}

internal func element(at location: IndexedPathLocation) -> BezierCurve {
return self.subpaths[location.componentIndex].curves[location.elementIndex]
internal func elementAtComponentIndex(_ componentIndex: Int, elementIndex: Int) -> BezierCurve {
return self.subpaths[componentIndex].curves[elementIndex]
}

internal func windingCount(_ point: CGPoint, ignoring: PathComponent? = nil) -> Int {
Expand All @@ -178,16 +178,23 @@ internal func windingCountImpliesContainment(_ count: Int, using rule: PathFillR
return windingCount
}

private func contains(_ other: Path) -> Bool {
guard other.subpaths.isEmpty == false else {
return true
}
guard self.intersects(path: other).isEmpty else {
return false
}
return other.subpaths.reduce(true) {
$0 && self.contains($1.curves[0].startingPoint)
@objc(containsPoint:usingRule:) public func contains(_ point: CGPoint, using rule: PathFillRule = .winding) -> Bool {
let count = self.windingCount(point)
return windingCountImpliesContainment(count, using: rule)
}

@objc(containsPath:) public func contains(_ other: Path) -> Bool {
// first, check that each component of `other` starts inside self
for component in other.subpaths {
let p = component.curves[0].startingPoint
guard self.contains(p) else {
return false
}
}
// next, for each intersection (if there are any) check that we stay inside the path
// TODO: use enumeration over intersections so we don't have to necessarily have to find each one
// TODO: make this work with winding fill rule and intersections that don't cross (suggestion, use AugmentedGraph)
return self.intersects(path: other).isEmpty
}

@objc(offsetWithDistance:) public func offset(distance d: CGFloat) -> Path {
Expand All @@ -196,11 +203,6 @@ internal func windingCountImpliesContainment(_ count: Int, using rule: PathFillR
})
}

@objc public func contains(_ point: CGPoint, using rule: PathFillRule = .winding) -> Bool {
let count = self.windingCount(point)
return windingCountImpliesContainment(count, using: rule)
}

private func performBooleanOperation(_ operation: BooleanPathOperation, withPath other: Path, threshold: CGFloat) -> Path? {
let intersections = self.intersects(path: other, threshold: threshold)
let augmentedGraph = AugmentedGraph(path1: self, path2: other, intersections: intersections)
Expand Down
6 changes: 1 addition & 5 deletions BezierKit/Library/PathComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ public final class PathComponent: NSObject, NSCoding {
let intersections = self.intersects(line: line)
var windingCount = 0
intersections.forEach {
let element = self.element(at: $0)
let element = self.curves[$0.elementIndex]
let t = $0.t
assert(element.derivative($0.t).length > 1.0e-3, "possible NaN normal vector. Possible data for unit test?")
let dotProduct = delta.dot(element.normal(t))
Expand All @@ -225,10 +225,6 @@ public final class PathComponent: NSObject, NSCoding {
return windingCount
}

private func element(at location: IndexedPathComponentLocation) -> BezierCurve {
return self.curves[location.elementIndex]
}

public func contains(_ point: CGPoint, using rule: PathFillRule = .winding) -> Bool {
let windingCount = self.windingCount(at: point)
return windingCountImpliesContainment(windingCount, using: rule)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ To integrate BezierKit into your Xcode project using CocoaPods, add it to your t

```ruby
target '<Your Target Name>' do
pod 'BezierKit', '>= 0.1.14'
pod 'BezierKit', '>= 0.1.15'
end
```

Expand Down

0 comments on commit e7cd58d

Please sign in to comment.