Skip to content

Commit

Permalink
use projected intersection rather than linear average to make offset …
Browse files Browse the repository at this point in the history
…paths contiguous (fixes hfutrell#78)
  • Loading branch information
mbutterick committed Oct 20, 2021
1 parent 6c0c257 commit 57ad4b3
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 11 deletions.
16 changes: 16 additions & 0 deletions BezierKit/Library/BezierCurve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -322,3 +322,19 @@ public extension Flatness {
return sqrt(flatnessSquared)
}
}

extension BezierCurve {
public func projectedIntersection(with other : BezierCurve) -> CGPoint? {
// using determinant formula described at
// https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
let x1 = self.startingPoint.x; let y1 = self.startingPoint.y
let x2 = self.endingPoint.x; let y2 = self.endingPoint.y
let x3 = other.startingPoint.x; let y3 = other.startingPoint.y
let x4 = other.endingPoint.x; let y4 = other.endingPoint.y
let d = ((x1 - x2) * (y3 - y4)) - ((y1 - y2) * (x3 - x4))
guard d != 0 else { return nil }
let nx = (((x1 * y2) - (y1 * x2)) * (x3 - x4)) - ((x1 - x2) * ((x3 * y4) - (y3 * x4)))
let ny = (((x1 * y2) - (y1 * x2)) * (y3 - y4)) - ((y1 - y2) * ((x3 * y4) - (y3 * x4)))
return CGPoint(x: nx/d, y: ny/d)
}
}
22 changes: 11 additions & 11 deletions BezierKit/Library/PathComponent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,22 +194,22 @@ import Foundation
$0 + $1.offset(distance: d)
}
guard offsetCurves.isEmpty == false else { return nil }
let referenceCurves = offsetCurves.map { $0.copy(using: .identity) }
func makeContiguous(_ thisCurveIdx : Int, _ nextCurveIdx : Int) {
guard offsetCurves[thisCurveIdx].endingPoint != offsetCurves[nextCurveIdx].startingPoint else { return }
guard let intersection = referenceCurves[thisCurveIdx].projectedIntersection(with: referenceCurves[nextCurveIdx]) else { fatalError("what the heck") }
offsetCurves[thisCurveIdx].endingPoint = intersection
offsetCurves[nextCurveIdx].startingPoint = intersection
}

// force the set of curves to be contiguous
for i in 0..<offsetCurves.count-1 {
let start = offsetCurves[i+1].startingPoint
let end = offsetCurves[i].endingPoint
let average = Utils.linearInterpolate(start, end, 0.5)
offsetCurves[i].endingPoint = average
offsetCurves[i+1].startingPoint = average
makeContiguous(i, i+1)
}
// we've touched everything but offsetCurves[0].startingPoint and offsetCurves[count-1].endingPoint
// if we are a closed componenet, keep the offset component closed as well
// if we are a closed component, keep the offset component closed as well
if self.isClosed {
let start = offsetCurves[0].startingPoint
let end = offsetCurves[offsetCurves.count-1].endingPoint
let average = Utils.linearInterpolate(start, end, 0.5)
offsetCurves[0].startingPoint = average
offsetCurves[offsetCurves.count-1].endingPoint = average
makeContiguous(offsetCurves.count-1, 0)
}
return PathComponent(curves: offsetCurves)
}
Expand Down

0 comments on commit 57ad4b3

Please sign in to comment.