Skip to content

Commit

Permalink
a different way of fixing the issue, specifically targeting 32-bit ar…
Browse files Browse the repository at this point in the history
…chitectures.
  • Loading branch information
hfutrell committed Jun 27, 2022
1 parent e852785 commit c122462
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 29 deletions.
5 changes: 5 additions & 0 deletions BezierKit/Library/BezierCurve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ public struct Subcurve<CurveType> where CurveType: BezierCurve {
public let t2: CGFloat
public let curve: CurveType

internal var canSplit: Bool {
let mid = 0.5 * (self.t1 + self.t2)
return mid > self.t1 && mid < self.t2
}

internal init(curve: CurveType) {
self.t1 = 0.0
self.t2 = 1.0
Expand Down
58 changes: 29 additions & 29 deletions BezierKit/Library/Utils.swift
Original file line number Diff line number Diff line change
Expand Up @@ -311,29 +311,11 @@ internal class Utils {
// disable this SwiftLint warning about function having more than 5 parameters
// swiftlint:disable function_parameter_count

static func pairiteration<C1: PointClassifiable, C2: PointClassifiable>(_ c1: Subcurve<C1>, _ c2: Subcurve<C2>,
_ c1b: BoundingBox, _ c2b: BoundingBox,
_ results: inout [Intersection],
_ accuracy: CGFloat,
_ totalIterations: inout Int) -> Bool {

func splitCurveAndCheckIfShouldRecurse<C: PointClassifiable>(_ curve: Subcurve<C>, boundingBox: BoundingBox) -> (left: Subcurve<C>, right: Subcurve<C>, shouldRecurse: Bool) {
let (left, right) = curve.split(at: 0.5)
// first check if the curve is small enough that it falls within
// accuracy threshold and therefore does not require further recursion
guard boundingBox.size.x + boundingBox.size.y >= accuracy else {
return (left: left, right: right, shouldRecurse: false)
}
// next check if splitting the curve results in either half being a point
// if so, we've reached numerical precision limits of how far we can split things
guard left.curve.isPoint == false else {
return (left: left, right: right, shouldRecurse: false)
}
guard right.curve.isPoint == false else {
return (left: left, right: right, shouldRecurse: false)
}
return (left: left, right: right, shouldRecurse: true)
}
static func pairiteration<C1, C2>(_ c1: Subcurve<C1>, _ c2: Subcurve<C2>,
_ c1b: BoundingBox, _ c2b: BoundingBox,
_ results: inout [Intersection],
_ accuracy: CGFloat,
_ totalIterations: inout Int) -> Bool {

let maximumIterations = 900
let maximumIntersections = c1.curve.order * c2.curve.order
Expand All @@ -343,10 +325,24 @@ internal class Utils {
guard results.count <= maximumIntersections else { return false }
guard c1b.overlaps(c2b) else { return true }

let cc1 = splitCurveAndCheckIfShouldRecurse(c1, boundingBox: c1b)
let cc2 = splitCurveAndCheckIfShouldRecurse(c2, boundingBox: c2b)
func shouldRecurse<C>(for subcurve: Subcurve<C>, boundingBox: BoundingBox) -> Bool {
guard subcurve.canSplit else { return false }
guard boundingBox.size.x + boundingBox.size.y >= accuracy else { return false }
if MemoryLayout<CGFloat>.size == 4 {
// limit recursion when we exceed Float32 precision
let midPoint = subcurve.curve.point(at: 0.5)
if midPoint == subcurve.curve.startingPoint ||
midPoint == subcurve.curve.endingPoint {
guard c1.curve.selfIntersects else { return false }
}
}
return true
}

let shouldRecurse1 = shouldRecurse(for: c1, boundingBox: c1b)
let shouldRecurse2 = shouldRecurse(for: c2, boundingBox: c2b)

if cc1.shouldRecurse == false, cc2.shouldRecurse == false {
if shouldRecurse1 == false, shouldRecurse2 == false {
// subcurves are small enough or we simply cannot recurse any more
let l1 = LineSegment(p0: c1.curve.startingPoint, p1: c1.curve.endingPoint)
let l2 = LineSegment(p0: c2.curve.startingPoint, p1: c2.curve.endingPoint)
Expand All @@ -355,7 +351,9 @@ internal class Utils {
let t2 = intersection.t2
results.append(Intersection(t1: t1 * c1.t2 + (1.0 - t1) * c1.t1,
t2: t2 * c2.t2 + (1.0 - t2) * c2.t1))
} else if cc1.shouldRecurse, cc2.shouldRecurse {
} else if shouldRecurse1, shouldRecurse2 {
let cc1 = c1.split(at: 0.5)
let cc2 = c2.split(at: 0.5)
let cc1lb = cc1.left.curve.boundingBox
let cc1rb = cc1.right.curve.boundingBox
let cc2lb = cc2.left.curve.boundingBox
Expand All @@ -364,12 +362,14 @@ internal class Utils {
guard Utils.pairiteration(cc1.left, cc2.right, cc1lb, cc2rb, &results, accuracy, &totalIterations) else { return false }
guard Utils.pairiteration(cc1.right, cc2.left, cc1rb, cc2lb, &results, accuracy, &totalIterations) else { return false }
guard Utils.pairiteration(cc1.right, cc2.right, cc1rb, cc2rb, &results, accuracy, &totalIterations) else { return false }
} else if cc1.shouldRecurse {
} else if shouldRecurse1 {
let cc1 = c1.split(at: 0.5)
let cc1lb = cc1.left.curve.boundingBox
let cc1rb = cc1.right.curve.boundingBox
guard Utils.pairiteration(cc1.left, c2, cc1lb, c2b, &results, accuracy, &totalIterations) else { return false }
guard Utils.pairiteration(cc1.right, c2, cc1rb, c2b, &results, accuracy, &totalIterations) else { return false }
} else if cc2.shouldRecurse {
} else if shouldRecurse2 {
let cc2 = c2.split(at: 0.5)
let cc2lb = cc2.left.curve.boundingBox
let cc2rb = cc2.right.curve.boundingBox
guard Utils.pairiteration(c1, cc2.left, c1b, cc2lb, &results, accuracy, &totalIterations) else { return false }
Expand Down

0 comments on commit c122462

Please sign in to comment.