@@ -9,11 +9,31 @@ import Foundation
99
1010public typealias CGFloat = Foundation . CGFloat
1111public typealias CGSize = Foundation . CGSize
12+ public typealias CGPoint = Foundation . CGPoint
1213
1314#if os(WASI) || os(Linux)
15+ import Glibc
16+
17+ public func sqrt( _ x: CGFloat ) -> CGFloat { return x. squareRoot ( ) }
18+ public func copysign( _ x: CGFloat , _ y: CGFloat ) -> CGFloat {
19+ let magnitude = x >= 0 ? x : - x
20+ return y >= 0 ? magnitude : - magnitude
21+ }
22+ public func acos( _ x: CGFloat ) -> CGFloat {
23+ return Glibc . acos ( x)
24+ }
25+
26+ public func cos( _ x: CGFloat ) -> CGFloat {
27+ return Glibc . cos ( x)
28+ }
29+
30+ public func sin( _ x: CGFloat ) -> CGFloat {
31+ return Glibc . sin ( x)
32+ }
33+
1434 private let KAPPA : CGFloat = 0.5522847498 // 4 *(sqrt(2) -1)/3
1535
16- public struct CGAffineTransform : Equatable {
36+ public struct CGAffineTransform : Equatable {
1737 public var a , b , c , d , tx , ty : CGFloat
1838
1939 public init ( a: CGFloat , b: CGFloat , c: CGFloat , d: CGFloat , tx: CGFloat , ty: CGFloat ) {
@@ -89,33 +109,33 @@ public struct CGAffineTransform: Equatable {
89109 var minY = CGFloat . infinity
90110 var maxX = - CGFloat. infinity
91111 var maxY = - CGFloat. infinity
92-
112+
93113 for element in elements {
94114 switch element {
95115 case . moveToPoint( let point) ,
96- . addLineToPoint( let point) :
116+ . addLineToPoint( let point) :
97117 minX = min ( minX, point. x)
98118 minY = min ( minY, point. y)
99119 maxX = max ( maxX, point. x)
100120 maxY = max ( maxY, point. y)
101-
121+
102122 case . addQuadCurveToPoint( let control, let point) :
103123 minX = min ( minX, control. x, point. x)
104124 minY = min ( minY, control. y, point. y)
105125 maxX = max ( maxX, control. x, point. x)
106126 maxY = max ( maxY, control. y, point. y)
107-
127+
108128 case . addCurveToPoint( let control1, let control2, let point) :
109129 minX = min ( minX, control1. x, control2. x, point. x)
110130 minY = min ( minY, control1. y, control2. y, point. y)
111131 maxX = max ( maxX, control1. x, control2. x, point. x)
112132 maxY = max ( maxY, control1. y, control2. y, point. y)
113-
133+
114134 case . closeSubpath:
115135 break
116136 }
117137 }
118-
138+
119139 return CGRect ( x: minX, y: minY, width: maxX - minX, height: maxY - minY)
120140 }
121141
@@ -243,24 +263,24 @@ public struct CGAffineTransform: Equatable {
243263 }
244264
245265 extension CGPoint {
246-
266+
247267 @inline ( __always)
248268 public func applying( _ t: CGAffineTransform ) -> CGPoint {
249- return CGPoint ( x: t. a * x + t. c * y + t. tx,
250- y: t. b * x + t. d * y + t. ty)
269+ return CGPoint (
270+ x: t. a * x + t. c * y + t. tx,
271+ y: t. b * x + t. d * y + t. ty)
251272 }
252273 }
253274
254-
255275 extension CGAffineTransform {
256276 public var isIdentity : Bool {
257277 self == CGAffineTransform . identity
258278 }
259-
279+
260280 public static var identity : CGAffineTransform {
261281 CGAffineTransform ( a: 1 , b: 0 , c: 0 , d: 1 , tx: 0 , ty: 0 )
262282 }
263-
283+
264284 public init ( translationX tx: CGFloat , y ty: CGFloat ) {
265285 self . init ( a: 1 , b: 0 , c: 0 , d: 1 , tx: tx, ty: ty)
266286 }
@@ -272,11 +292,11 @@ public struct CGAffineTransform: Equatable {
272292 public init ( rotationAngle angle: CGFloat ) {
273293 self . init ( a: cos ( angle) , b: sin ( angle) , c: - sin( angle) , d: cos ( angle) , tx: 0 , ty: 0 )
274294 }
275-
295+
276296 public func translatedBy( x: CGFloat , y: CGFloat ) -> CGAffineTransform {
277297 return self . concatenating ( CGAffineTransform ( translationX: x, y: y) )
278298 }
279-
299+
280300 public func concatenating( _ t: CGAffineTransform ) -> CGAffineTransform {
281301 return CGAffineTransform (
282302 a: a * t. a + c * t. b,
@@ -287,20 +307,188 @@ public struct CGAffineTransform: Equatable {
287307 ty: b * t. tx + d * t. ty + ty
288308 )
289309 }
290-
310+
291311 public func scaledBy( x: CGFloat , y: CGFloat ) -> CGAffineTransform {
292312 return self . concatenating ( CGAffineTransform ( scaleX: x, y: y) )
293313 }
294-
314+
295315 public func rotated( by angle: CGFloat ) -> CGAffineTransform {
296316 return self . concatenating ( CGAffineTransform ( rotationAngle: angle) )
297317 }
298318 }
319+
320+ public struct MBezierPath {
321+ public var cgPath : CGPath
322+
323+ public init ( ) {
324+ self . cgPath = CGPath ( )
325+ }
326+
327+ public init ? ( rect: CGRect ) {
328+ self . cgPath = CGPath ( )
329+ self . cgPath. addRect ( rect)
330+ }
331+
332+ public init ? ( ovalIn rect: CGRect ) {
333+ self . cgPath = CGPath ( )
334+ self . cgPath. addEllipse ( in: rect)
335+ }
336+
337+ public init (
338+ arcCenter: CGPoint , radius: CGFloat , startAngle: CGFloat , endAngle: CGFloat ,
339+ clockwise: Bool
340+ ) {
341+ self . cgPath = CGPath ( )
342+ MBezierPath . addArcTo (
343+ path: & self . cgPath, center: arcCenter, radius: radius, startAngle: startAngle,
344+ endAngle: endAngle, clockwise: clockwise)
345+ }
346+
347+ public mutating func move( to point: CGPoint ) {
348+ cgPath. move ( to: point)
349+ }
350+
351+ public mutating func addLine( to point: CGPoint ) {
352+ cgPath. addLine ( to: point)
353+ }
354+
355+ public mutating func addCurve(
356+ to endPoint: CGPoint , controlPoint1: CGPoint , controlPoint2: CGPoint
357+ ) {
358+ cgPath. addCurve ( to: endPoint, control1: controlPoint1, control2: controlPoint2)
359+ }
360+
361+ public mutating func addQuadCurve( to endPoint: CGPoint , controlPoint: CGPoint ) {
362+ cgPath. addQuadCurve ( to: endPoint, control: controlPoint)
363+ }
364+
365+ public mutating func addArc(
366+ withCenter center: CGPoint , radius: CGFloat , startAngle: CGFloat , endAngle: CGFloat ,
367+ clockwise: Bool
368+ ) {
369+ MBezierPath . addArcTo (
370+ path: & self . cgPath, center: center, radius: radius, startAngle: startAngle,
371+ endAngle: endAngle, clockwise: clockwise)
372+ }
373+
374+ public mutating func append( _ path: MBezierPath ) {
375+ cgPath. elements. append ( contentsOf: path. cgPath. elements)
376+ }
377+
378+ public mutating func close( ) {
379+ cgPath. closeSubpath ( )
380+ }
381+
382+ public mutating func apply( _ transform: CGAffineTransform ) {
383+ var newElements : [ CGPath . Element ] = [ ]
384+ for element in cgPath. elements {
385+ switch element {
386+ case . moveToPoint( let point) :
387+ newElements. append ( . moveToPoint( point. applying ( transform) ) )
388+ case . addLineToPoint( let point) :
389+ newElements. append ( . addLineToPoint( point. applying ( transform) ) )
390+ case . addQuadCurveToPoint( let control, let point) :
391+ newElements. append (
392+ . addQuadCurveToPoint( control. applying ( transform) , point. applying ( transform) )
393+ )
394+ case . addCurveToPoint( let control1, let control2, let point) :
395+ newElements. append (
396+ . addCurveToPoint(
397+ control1. applying ( transform) , control2. applying ( transform) ,
398+ point. applying ( transform) ) )
399+ case . closeSubpath:
400+ newElements. append ( . closeSubpath)
401+ }
402+ }
403+ self . cgPath. elements = newElements
404+ }
405+
406+ public var isEmpty : Bool {
407+ return cgPath. elements. isEmpty
408+ }
409+
410+ public var bounds : CGRect {
411+ return cgPath. boundingBoxOfPath
412+ }
413+
414+ static func addArcTo(
415+ path: inout CGPath , center: CGPoint , radius: CGFloat , startAngle: CGFloat ,
416+ endAngle: CGFloat , clockwise: Bool
417+ ) {
418+ var deltaAngle : CGFloat
419+ if clockwise {
420+ deltaAngle = endAngle - startAngle
421+ while deltaAngle < 0 { deltaAngle += 2 * . pi }
422+ } else { // Counter-clockwise
423+ deltaAngle = endAngle - startAngle
424+ while deltaAngle > 0 { deltaAngle -= 2 * . pi }
425+ }
426+
427+ if abs ( deltaAngle) < 1e-6 { return } // Essentially no arc
428+
429+ let numSegments = Swift . max ( 1 , Int ( ceil ( abs ( deltaAngle) / ( . pi / 2.0 ) ) ) ) // Max 90deg segments
430+ let segmentAngleSweep = deltaAngle / CGFloat( numSegments)
431+
432+ var currentAngle = startAngle
433+
434+ let initialPoint = CGPoint (
435+ x: center. x + radius * cos( currentAngle) , y: center. y + radius * sin( currentAngle) )
436+
437+ if path. elements. isEmpty || ( path. elements. last? . isCloseSubpath ?? false ) {
438+ path. move ( to: initialPoint)
439+ } else if let lastElement = path. elements. last, let lastPoint = lastElement. lastPoint,
440+ lastPoint != initialPoint
441+ {
442+ path. addLine ( to: initialPoint)
443+ }
444+
445+ for _ in 0 ..< numSegments {
446+ let nextAngle = currentAngle + segmentAngleSweep
447+ let L = radius * ( 4.0 / 3.0 ) * tan( abs ( segmentAngleSweep) / 4.0 )
448+
449+ let cp1_centerRelative = CGPoint (
450+ x: radius * cos( currentAngle) - L * sin( currentAngle) ,
451+ y: radius * sin( currentAngle) + L * cos( currentAngle)
452+ )
453+ let cp2_centerRelative = CGPoint (
454+ x: radius * cos( nextAngle) + L * sin( nextAngle) ,
455+ y: radius * sin( nextAngle) - L * cos( nextAngle)
456+ )
457+
458+ let endPoint_abs = CGPoint (
459+ x: center. x + radius * cos( nextAngle) , y: center. y + radius * sin( nextAngle) )
460+ let cp1_abs = CGPoint (
461+ x: center. x + cp1_centerRelative. x, y: center. y + cp1_centerRelative. y)
462+ let cp2_abs = CGPoint (
463+ x: center. x + cp2_centerRelative. x, y: center. y + cp2_centerRelative. y)
464+
465+ path. addCurve ( to: endPoint_abs, control1: cp1_abs, control2: cp2_abs)
466+ currentAngle = nextAngle
467+ }
468+ }
469+ }
470+
471+ extension CGPath . Element {
472+ var lastPoint : CGPoint ? {
473+ switch self {
474+ case . moveToPoint( let p) : return p
475+ case . addLineToPoint( let p) : return p
476+ case . addQuadCurveToPoint( _, let p) : return p
477+ case . addCurveToPoint( _, _, let p) : return p
478+ case . closeSubpath: return nil
479+ }
480+ }
481+ var isCloseSubpath : Bool {
482+ if case . closeSubpath = self { return true }
483+ return false
484+ }
485+ }
486+
299487#else
300- import CoreGraphics
301- public typealias CGLineJoin = CoreGraphics . CGLineJoin
302- public typealias CGLineCap = CoreGraphics . CGLineCap
303- public typealias CGPathFillRule = CoreGraphics . CGPathFillRule
304- public typealias CGPath = CoreGraphics . CGPath
305- public typealias CGAffineTransform = CoreGraphics . CGAffineTransform
488+ import CoreGraphics
489+ public typealias CGLineJoin = CoreGraphics . CGLineJoin
490+ public typealias CGLineCap = CoreGraphics . CGLineCap
491+ public typealias CGPathFillRule = CoreGraphics . CGPathFillRule
492+ public typealias CGPath = CoreGraphics . CGPath
493+ public typealias CGAffineTransform = CoreGraphics . CGAffineTransform
306494#endif
0 commit comments