@@ -71,9 +71,91 @@ public enum Bezier {
7171 end,
7272 ]
7373 }
74+
75+ public static func computeBoundingBoxForLine( start: Point , end: Point ) -> Rect {
76+ var topLeft = start
77+ var bottomRight = topLeft
78+ end. expandBounds ( topLeft: & topLeft, bottomRight: & bottomRight)
79+ return Rect (
80+ x: topLeft. x,
81+ y: topLeft. y,
82+ width: bottomRight. x - topLeft. x,
83+ height: bottomRight. y - topLeft. y
84+ )
85+ }
86+
87+ public static func computeBoundingBoxForCubicBezier( _ bezierPoints: [ Point ] ) -> Rect {
88+ // Start with the end points
89+ var ( topLeft, _, _) = splitBezier ( bezierPoints, ofDegree: 3 , at: 0 )
90+ var bottomRight = topLeft
91+ let ( lastPoint, _, _) = splitBezier ( bezierPoints, ofDegree: 3 , at: 1 )
92+
93+ lastPoint. expandBounds ( topLeft: & topLeft, bottomRight: & bottomRight)
94+
95+ // Find the roots, which should be the extremities
96+ let xRoots = computeCubicFirstDerivativeRoots (
97+ a: bezierPoints [ 0 ] . x,
98+ b: bezierPoints [ 1 ] . x,
99+ c: bezierPoints [ 2 ] . x,
100+ d: bezierPoints [ 3 ] . x
101+ )
102+
103+ for t in xRoots {
104+ if t < 0 || t > 1 {
105+ continue
106+ }
107+ let ( location, _, _) = splitBezier ( bezierPoints, ofDegree: 3 , at: t)
108+ location. expandBounds ( topLeft: & topLeft, bottomRight: & bottomRight)
109+ }
110+
111+ let yRoots = computeCubicFirstDerivativeRoots (
112+ a: bezierPoints [ 0 ] . y,
113+ b: bezierPoints [ 1 ] . y,
114+ c: bezierPoints [ 2 ] . y,
115+ d: bezierPoints [ 3 ] . y
116+ )
117+ for t in yRoots {
118+ if t < 0 || t > 1 {
119+ continue
120+ }
121+ let ( location, _, _) = splitBezier ( bezierPoints, ofDegree: 3 , at: t)
122+ location. expandBounds ( topLeft: & topLeft, bottomRight: & bottomRight)
123+ }
124+
125+ return Rect (
126+ x: topLeft. x,
127+ y: topLeft. y,
128+ width: bottomRight. x - topLeft. x,
129+ height: bottomRight. y - topLeft. y
130+ )
131+ }
74132}
75133
76134private extension Bezier {
135+ static func computeCubicFirstDerivativeRoots( a: Real , b: Real , c: Real , d: Real ) -> [ Real ] {
136+ // See http://processingjs.nihongoresources.com/bezierinfo/#bounds for where the formulas come from
137+
138+ let denominator = - a + 3.0 * b - 3.0 * c + d
139+
140+ // If denominator == 0, fall back to
141+ if denominator. isClose ( to: 0.0 , threshold: 1e-9 ) {
142+ let t = ( a - b) / ( 2.0 * ( a - 2.0 * b + c) )
143+ return [ t]
144+ } else {
145+ let numeratorLeft = - a + 2.0 * b - c
146+
147+ let v1 = - a * ( c - d)
148+ let v2 = b * b
149+ let v3 = b * ( c + d)
150+ let v4 = c * c
151+ let numeratorRight = - 1.0 * sqrt( v1 + v2 - v3 + v4)
152+
153+ let t1 = ( numeratorLeft + numeratorRight) / denominator
154+ let t2 = ( numeratorLeft - numeratorRight) / denominator
155+ return [ t1, t2]
156+ }
157+ }
158+
77159 static func convertBezier( _ bezierPoints: [ Point ] , relativeTo point: Point ) -> [ Point ] {
78160 // c[i] in the paper
79161 let distanceFromPoint = [
0 commit comments