1- import Foundation // for sinf and cosf
1+ import Foundation // for sin and cos
22
33public enum StrokeCap {
44 /// The stroke ends square exactly at the last point.
@@ -12,15 +12,15 @@ public enum StrokeCap {
1212public enum StrokeJoin {
1313 /// Corners are sharp, unless they are longer than `limit` times half the stroke width,
1414 /// in which case they are beveled.
15- case miter( limit: Float )
15+ case miter( limit: Double )
1616 /// Corners are rounded.
1717 case round
1818 /// Corners are beveled.
1919 case bevel
2020}
2121
2222public struct StrokeStyle {
23- public var width : Float = 1.0
23+ public var width : Double = 1.0
2424 public var cap : StrokeCap = . butt
2525 public var join : StrokeJoin = . miter( limit: 10.0 )
2626
@@ -39,45 +39,43 @@ public enum FillRule {
3939
4040/// A type representing an affine transformation on a 2-D point.
4141///
42- /// Performing an affine transform consists of translating by ``translation`` followed by
43- /// multiplying by ``linearTransform ``.
44- public struct AffineTransform : Equatable {
42+ /// Performing an affine transform consists of multiplying the matrix ``linearTransform``
43+ /// by the point as a column vector, then adding ``translation ``.
44+ public struct AffineTransform : Equatable , CustomDebugStringConvertible {
4545 /// The linear transformation. This is a 2x2 matrix stored in row-major order.
4646 ///
4747 /// The four properties (`x`, `y`, `z`, `w`) correspond to the 2x2 matrix as follows:
4848 /// ```
4949 /// [ x y ]
5050 /// [ z w ]
5151 /// ```
52- public var linearTransform : SIMD4 < Float >
52+ public var linearTransform : SIMD4 < Double >
5353 /// The translation applied after the linear transformation.
54- public var translation : SIMD2 < Float >
54+ public var translation : SIMD2 < Double >
5555
56- public init ( linearTransform: SIMD4 < Float > , translation: SIMD2 < Float > ) {
56+ public init ( linearTransform: SIMD4 < Double > , translation: SIMD2 < Double > ) {
5757 self . linearTransform = linearTransform
5858 self . translation = translation
5959 }
6060
61- public static func translate ( x: Float , y: Float ) -> AffineTransform {
61+ public static func translation ( x: Double , y: Double ) -> AffineTransform {
6262 AffineTransform (
6363 linearTransform: SIMD4 ( x: 1.0 , y: 0.0 , z: 0.0 , w: 1.0 ) ,
6464 translation: SIMD2 ( x: x, y: y)
6565 )
6666 }
6767
68- public static func scale ( by factor: Float ) -> AffineTransform {
68+ public static func scaling ( by factor: Double ) -> AffineTransform {
6969 AffineTransform (
7070 linearTransform: SIMD4 ( x: factor, y: 0.0 , z: 0.0 , w: factor) ,
7171 translation: . zero
7272 )
7373 }
7474
75- public static func rotate(
76- radians: Float ,
77- center: SIMD2 < Float > = . zero
78- ) -> AffineTransform {
79- let sine = sinf ( radians)
80- let cosine = cosf ( radians)
75+ public static func rotation( radians: Double , center: SIMD2 < Double > ) -> AffineTransform {
76+ // Making the sine negative so that positive rotations are clockwise
77+ let sine = sin ( - radians)
78+ let cosine = cos ( radians)
8179 return AffineTransform (
8280 linearTransform: SIMD4 ( x: cosine, y: - sine, z: sine, w: cosine) ,
8381 translation: SIMD2 (
@@ -87,53 +85,118 @@ public struct AffineTransform: Equatable {
8785 )
8886 }
8987
90- public static func rotate(
91- degrees: Float ,
92- center: SIMD2 < Float > = . zero
93- ) -> AffineTransform {
94- rotate ( radians: degrees * ( . pi / 180.0 ) , center: center)
88+ public static func rotation( degrees: Double , center: SIMD2 < Double > ) -> AffineTransform {
89+ rotation ( radians: degrees * ( . pi / 180.0 ) , center: center)
9590 }
9691
9792 public static let identity = AffineTransform (
9893 linearTransform: SIMD4 ( x: 1.0 , y: 0.0 , z: 0.0 , w: 1.0 ) ,
9994 translation: . zero
10095 )
96+
97+ public func inverted( ) -> AffineTransform ? {
98+ let determinant = linearTransform. x * linearTransform. w - linearTransform. y * linearTransform. z
99+ if determinant == 0.0 {
100+ return nil
101+ }
102+
103+ return AffineTransform (
104+ linearTransform: SIMD4 (
105+ x: linearTransform. w,
106+ y: - linearTransform. y,
107+ z: - linearTransform. z,
108+ w: linearTransform. x
109+ ) / determinant,
110+ translation: SIMD2 (
111+ x: ( linearTransform. y * translation. y - linearTransform. w * translation. x) ,
112+ y: ( linearTransform. z * translation. x - linearTransform. x * translation. y)
113+ ) / determinant
114+ )
115+ }
116+
117+ public func followedBy( _ other: AffineTransform ) -> AffineTransform {
118+ // Composing two transformations is equivalent to forming the 3x3 matrix shown by
119+ // `debugDescription`, then multiplying `other * self` (the left matrix is applied
120+ // after the right matrix).
121+ return AffineTransform (
122+ linearTransform: SIMD4 (
123+ x: other. linearTransform. x * linearTransform. x + other. linearTransform. y
124+ * linearTransform. z,
125+ y: other. linearTransform. x * linearTransform. y + other. linearTransform. y
126+ * linearTransform. w,
127+ z: other. linearTransform. z * linearTransform. x + other. linearTransform. w
128+ * linearTransform. z,
129+ w: other. linearTransform. z * linearTransform. y + other. linearTransform. w
130+ * linearTransform. w
131+ ) ,
132+ translation: SIMD2 (
133+ x: other. linearTransform. x * translation. x + other. linearTransform. y * translation. y
134+ + other. translation. x,
135+ y: other. linearTransform. z * translation. x + other. linearTransform. w * translation. y
136+ + other. translation. y
137+ )
138+ )
139+ }
140+
141+ public var debugDescription : String {
142+ let numberFormat = " %.5g "
143+ let a = String ( format: numberFormat, linearTransform. x)
144+ let b = String ( format: numberFormat, linearTransform. y)
145+ let c = String ( format: numberFormat, linearTransform. z)
146+ let d = String ( format: numberFormat, linearTransform. w)
147+ let tx = String ( format: numberFormat, translation. x)
148+ let ty = String ( format: numberFormat, translation. y)
149+ let zero = String ( format: numberFormat, 0.0 )
150+ let one = String ( format: numberFormat, 1.0 )
151+
152+ let maxLength = [ a, b, c, d, tx, ty, zero, one] . map ( \. count) . max ( ) !
153+
154+ func pad( _ s: String ) -> String {
155+ String ( repeating: " " , count: maxLength - s. count) + s
156+ }
157+
158+ return """
159+ [ \( pad ( a) ) \( pad ( b) ) \( pad ( tx) ) ]
160+ [ \( pad ( c) ) \( pad ( d) ) \( pad ( ty) ) ]
161+ [ \( pad ( zero) ) \( pad ( zero) ) \( pad ( one) ) ]
162+ """
163+ }
101164}
102165
103166public struct Path {
104167 public struct Rect : Equatable {
105- public var origin : SIMD2 < Float >
106- public var size : SIMD2 < Float >
168+ public var origin : SIMD2 < Double >
169+ public var size : SIMD2 < Double >
107170
108- public init ( origin: SIMD2 < Float > , size: SIMD2 < Float > ) {
171+ public init ( origin: SIMD2 < Double > , size: SIMD2 < Double > ) {
109172 self . origin = origin
110173 self . size = size
111174 }
112175
113- public var x : Float { origin. x }
114- public var y : Float { origin. y }
115- public var width : Float { size. x }
116- public var height : Float { size. y }
176+ public var x : Double { origin. x }
177+ public var y : Double { origin. y }
178+ public var width : Double { size. x }
179+ public var height : Double { size. y }
117180
118- public init ( x: Float , y: Float , width: Float , height: Float ) {
181+ public init ( x: Double , y: Double , width: Double , height: Double ) {
119182 origin = SIMD2 ( x: x, y: y)
120183 size = SIMD2 ( x: width, y: height)
121184 }
122185 }
123186
124187 /// The types of actions that can be performed on a path.
125188 public enum Action : Equatable {
126- case moveTo( SIMD2 < Float > )
127- case lineTo( SIMD2 < Float > )
128- case quadCurve( control: SIMD2 < Float > , end: SIMD2 < Float > )
129- case cubicCurve( control1: SIMD2 < Float > , control2: SIMD2 < Float > , end: SIMD2 < Float > )
189+ case moveTo( SIMD2 < Double > )
190+ case lineTo( SIMD2 < Double > )
191+ case quadCurve( control: SIMD2 < Double > , end: SIMD2 < Double > )
192+ case cubicCurve( control1: SIMD2 < Double > , control2: SIMD2 < Double > , end: SIMD2 < Double > )
130193 case rectangle( Rect )
131- case circle( center: SIMD2 < Float > , radius: Float )
194+ case circle( center: SIMD2 < Double > , radius: Double )
132195 case arc(
133- center: SIMD2 < Float > ,
134- radius: Float ,
135- startAngle: Float ,
136- endAngle: Float ,
196+ center: SIMD2 < Double > ,
197+ radius: Double ,
198+ startAngle: Double ,
199+ endAngle: Double ,
137200 clockwise: Bool
138201 )
139202 case transform( AffineTransform )
@@ -152,28 +215,28 @@ public struct Path {
152215
153216 public init ( ) { }
154217
155- public consuming func move( to point: SIMD2 < Float > ) -> Path {
218+ public consuming func move( to point: SIMD2 < Double > ) -> Path {
156219 actions. append ( . moveTo( point) )
157220 return self
158221 }
159222
160- public consuming func addLine( to point: SIMD2 < Float > ) -> Path {
223+ public consuming func addLine( to point: SIMD2 < Double > ) -> Path {
161224 actions. append ( . lineTo( point) )
162225 return self
163226 }
164227
165228 public consuming func addQuadCurve(
166- control: SIMD2 < Float > ,
167- to endPoint: SIMD2 < Float >
229+ control: SIMD2 < Double > ,
230+ to endPoint: SIMD2 < Double >
168231 ) -> Path {
169232 actions. append ( . quadCurve( control: control, end: endPoint) )
170233 return self
171234 }
172235
173236 public consuming func addCubicCurve(
174- control1: SIMD2 < Float > ,
175- control2: SIMD2 < Float > ,
176- to endPoint: SIMD2 < Float >
237+ control1: SIMD2 < Double > ,
238+ control2: SIMD2 < Double > ,
239+ to endPoint: SIMD2 < Double >
177240 ) -> Path {
178241 actions. append ( . cubicCurve( control1: control1, control2: control2, end: endPoint) )
179242 return self
@@ -184,16 +247,16 @@ public struct Path {
184247 return self
185248 }
186249
187- public consuming func addCircle( center: SIMD2 < Float > , radius: Float ) -> Path {
250+ public consuming func addCircle( center: SIMD2 < Double > , radius: Double ) -> Path {
188251 actions. append ( . circle( center: center, radius: radius) )
189252 return self
190253 }
191254
192255 public consuming func addArc(
193- center: SIMD2 < Float > ,
194- radius: Float ,
195- startAngle: Float ,
196- endAngle: Float ,
256+ center: SIMD2 < Double > ,
257+ radius: Double ,
258+ startAngle: Double ,
259+ endAngle: Double ,
197260 clockwise: Bool
198261 ) -> Path {
199262 actions. append (
0 commit comments