|
| 1 | +// |
| 2 | +// ColorMatrix.swift |
| 3 | +// OpenSwiftUICore |
| 4 | +// |
| 5 | +// Audited for iOS 18.0 |
| 6 | +// Status: Blocked by Color, GraphicsFilter and ShapeStyle |
| 7 | +// ID: 623CA953523AF4C256B3825254A7F058 (SwiftUICore) |
| 8 | + |
| 9 | +#if canImport(Darwin) |
| 10 | +import CoreGraphics |
| 11 | +#else |
| 12 | +import Foundation |
| 13 | +#endif |
| 14 | + |
| 15 | +// MARK: - ColorMatrix |
| 16 | + |
| 17 | +/// A matrix to use in an RGBA color transformation. |
| 18 | +/// |
| 19 | +/// The matrix has five columns, each with a red, green, blue, and alpha |
| 20 | +/// component. You can use the matrix for tasks like creating a color |
| 21 | +/// transformation ``GraphicsContext/Filter`` for a ``GraphicsContext`` using |
| 22 | +/// the ``GraphicsContext/Filter/colorMatrix(_:)`` method. |
| 23 | +@frozen |
| 24 | +public struct ColorMatrix: Equatable { |
| 25 | + public var r1: Float = 1, r2: Float = 0, r3: Float = 0, r4: Float = 0, r5: Float = 0 |
| 26 | + public var g1: Float = 0, g2: Float = 1, g3: Float = 0, g4: Float = 0, g5: Float = 0 |
| 27 | + public var b1: Float = 0, b2: Float = 0, b3: Float = 1, b4: Float = 0, b5: Float = 0 |
| 28 | + public var a1: Float = 0, a2: Float = 0, a3: Float = 0, a4: Float = 1, a5: Float = 0 |
| 29 | + |
| 30 | + /// Creates the identity matrix. |
| 31 | + @inlinable |
| 32 | + public init() {} |
| 33 | +} |
| 34 | + |
| 35 | +// MARK: - _ColorMatrix |
| 36 | + |
| 37 | +@frozen |
| 38 | +public struct _ColorMatrix: Equatable, Codable { |
| 39 | + public var m11: Float = 1, m12: Float = 0, m13: Float = 0, m14: Float = 0, m15: Float = 0 |
| 40 | + public var m21: Float = 0, m22: Float = 1, m23: Float = 0, m24: Float = 0, m25: Float = 0 |
| 41 | + public var m31: Float = 0, m32: Float = 0, m33: Float = 1, m34: Float = 0, m35: Float = 0 |
| 42 | + public var m41: Float = 0, m42: Float = 0, m43: Float = 0, m44: Float = 1, m45: Float = 0 |
| 43 | + |
| 44 | + @inline(__always) |
| 45 | + init(m11: Float = 1, m12: Float = 0, m13: Float = 0, m14: Float = 0, m15: Float = 0, |
| 46 | + m21: Float = 0, m22: Float = 1, m23: Float = 0, m24: Float = 0, m25: Float = 0, |
| 47 | + m31: Float = 0, m32: Float = 0, m33: Float = 1, m34: Float = 0, m35: Float = 0, |
| 48 | + m41: Float = 0, m42: Float = 0, m43: Float = 0, m44: Float = 1, m45: Float = 0) { |
| 49 | + self.m11 = m11; self.m12 = m12; self.m13 = m13; self.m14 = m14; self.m15 = m15 |
| 50 | + self.m21 = m21; self.m22 = m22; self.m23 = m23; self.m24 = m24; self.m25 = m25 |
| 51 | + self.m31 = m31; self.m32 = m32; self.m33 = m33; self.m34 = m34; self.m35 = m35 |
| 52 | + self.m41 = m41; self.m42 = m42; self.m43 = m43; self.m44 = m44; self.m45 = m45 |
| 53 | + } |
| 54 | + |
| 55 | + @inlinable |
| 56 | + public init() {} |
| 57 | + |
| 58 | + public init(color: Color, in environment: EnvironmentValues) { |
| 59 | + // Blocked by Color |
| 60 | + fatalError("TODO") |
| 61 | + } |
| 62 | + |
| 63 | + package init(_ m: ColorMatrix) { |
| 64 | + m11 = m.r1; m12 = m.r2; m13 = m.r3; m14 = m.r4; m15 = m.r5 |
| 65 | + m21 = m.g1; m22 = m.g2; m23 = m.g3; m24 = m.g4; m25 = m.g5 |
| 66 | + m31 = m.b1; m32 = m.b2; m33 = m.b3; m34 = m.b4; m35 = m.b5 |
| 67 | + m41 = m.a1; m42 = m.a2; m43 = m.a3; m44 = m.a4; m45 = m.a5 |
| 68 | + } |
| 69 | + |
| 70 | + package var isIdentity: Bool { |
| 71 | + self == .identity |
| 72 | + } |
| 73 | + |
| 74 | + @inline(__always) |
| 75 | + static let identity = _ColorMatrix() |
| 76 | + |
| 77 | + /// The missing fifth row would be (0, 0, 0, 0, 1) |
| 78 | + /// |
| 79 | + /// | R' | | r1 r2 r3 r4 r5 | | R | |
| 80 | + /// | G' | | g1 g2 g3 g4 g5 | | G | |
| 81 | + /// | B' | = | b1 b2 b3 b4 b5 | * | B | |
| 82 | + /// | A' | | a1 a2 a3 a4 a5 | | A | |
| 83 | + /// | 1 | | 0 0 0 0 1 | | 1 | |
| 84 | + public static func * (a: _ColorMatrix, b: _ColorMatrix) -> _ColorMatrix { |
| 85 | + let m11 = a.m11 * b.m11 + a.m12 * b.m21 + a.m13 * b.m31 + a.m14 * b.m41 |
| 86 | + let m12 = a.m11 * b.m12 + a.m12 * b.m22 + a.m13 * b.m32 + a.m14 * b.m42 |
| 87 | + let m13 = a.m11 * b.m13 + a.m12 * b.m23 + a.m13 * b.m33 + a.m14 * b.m43 |
| 88 | + let m14 = a.m11 * b.m14 + a.m12 * b.m24 + a.m13 * b.m34 + a.m14 * b.m44 |
| 89 | + let m15 = a.m11 * b.m15 + a.m12 * b.m25 + a.m13 * b.m35 + a.m14 * b.m45 + a.m15 |
| 90 | + |
| 91 | + let m21 = a.m21 * b.m11 + a.m22 * b.m21 + a.m23 * b.m31 + a.m24 * b.m41 |
| 92 | + let m22 = a.m21 * b.m12 + a.m22 * b.m22 + a.m23 * b.m32 + a.m24 * b.m42 |
| 93 | + let m23 = a.m21 * b.m13 + a.m22 * b.m23 + a.m23 * b.m33 + a.m24 * b.m43 |
| 94 | + let m24 = a.m21 * b.m14 + a.m22 * b.m24 + a.m23 * b.m34 + a.m24 * b.m44 |
| 95 | + let m25 = a.m21 * b.m15 + a.m22 * b.m25 + a.m23 * b.m35 + a.m24 * b.m45 + a.m25 |
| 96 | + |
| 97 | + let m31 = a.m31 * b.m11 + a.m32 * b.m21 + a.m33 * b.m31 + a.m34 * b.m41 |
| 98 | + let m32 = a.m31 * b.m12 + a.m32 * b.m22 + a.m33 * b.m32 + a.m34 * b.m42 |
| 99 | + let m33 = a.m31 * b.m13 + a.m32 * b.m23 + a.m33 * b.m33 + a.m34 * b.m43 |
| 100 | + let m34 = a.m31 * b.m14 + a.m32 * b.m24 + a.m33 * b.m34 + a.m34 * b.m44 |
| 101 | + let m35 = a.m31 * b.m15 + a.m32 * b.m25 + a.m33 * b.m35 + a.m34 * b.m45 + a.m35 |
| 102 | + |
| 103 | + let m41 = a.m41 * b.m11 + a.m42 * b.m21 + a.m43 * b.m31 + a.m44 * b.m41 |
| 104 | + let m42 = a.m41 * b.m12 + a.m42 * b.m22 + a.m43 * b.m32 + a.m44 * b.m42 |
| 105 | + let m43 = a.m41 * b.m13 + a.m42 * b.m23 + a.m43 * b.m33 + a.m44 * b.m43 |
| 106 | + let m44 = a.m41 * b.m14 + a.m42 * b.m24 + a.m43 * b.m34 + a.m44 * b.m44 |
| 107 | + let m45 = a.m41 * b.m15 + a.m42 * b.m25 + a.m43 * b.m35 + a.m44 * b.m45 + a.m45 |
| 108 | + |
| 109 | + return _ColorMatrix( |
| 110 | + row1: (m11, m12, m13, m14, m15), |
| 111 | + row2: (m21, m22, m23, m24, m25), |
| 112 | + row3: (m31, m32, m33, m34, m35), |
| 113 | + row4: (m41, m42, m43, m44, m45) |
| 114 | + ) |
| 115 | + } |
| 116 | + |
| 117 | + public func encode(to encoder: any Encoder) throws { |
| 118 | + var container = encoder.unkeyedContainer() |
| 119 | + try container.encodeRow((m11, m12, m13, m14, m15)) |
| 120 | + try container.encodeRow((m21, m22, m23, m24, m25)) |
| 121 | + try container.encodeRow((m31, m32, m33, m34, m35)) |
| 122 | + try container.encodeRow((m41, m42, m43, m44, m45)) |
| 123 | + } |
| 124 | + |
| 125 | + public init(from decoder: any Decoder) throws { |
| 126 | + var container = try decoder.unkeyedContainer() |
| 127 | + let row1 = try container.decodeRow() |
| 128 | + let row2 = try container.decodeRow() |
| 129 | + let row3 = try container.decodeRow() |
| 130 | + let row4 = try container.decodeRow() |
| 131 | + self.init(row1: row1, row2: row2, row3: row3, row4: row4) |
| 132 | + } |
| 133 | +} |
| 134 | + |
| 135 | +extension UnkeyedEncodingContainer { |
| 136 | + fileprivate mutating func encodeRow(_ row: (Float, Float, Float, Float, Float)) throws { |
| 137 | + try encode(row.0) |
| 138 | + try encode(row.1) |
| 139 | + try encode(row.2) |
| 140 | + try encode(row.3) |
| 141 | + try encode(row.4) |
| 142 | + } |
| 143 | +} |
| 144 | + |
| 145 | +extension UnkeyedDecodingContainer { |
| 146 | + fileprivate mutating func decodeRow() throws -> (Float, Float, Float, Float, Float) { |
| 147 | + let m11 = try decode(Float.self) |
| 148 | + let m12 = try decode(Float.self) |
| 149 | + let m13 = try decode(Float.self) |
| 150 | + let m14 = try decode(Float.self) |
| 151 | + let m15 = try decode(Float.self) |
| 152 | + return (m11, m12, m13, m14, m15) |
| 153 | + } |
| 154 | +} |
| 155 | + |
| 156 | +// MARK: - _ColorMatrix + init [TODO] |
| 157 | + |
| 158 | +extension _ColorMatrix { |
| 159 | + @inline(__always) |
| 160 | + package init( |
| 161 | + row1: (Float, Float, Float, Float, Float), |
| 162 | + row2: (Float, Float, Float, Float, Float), |
| 163 | + row3: (Float, Float, Float, Float, Float), |
| 164 | + row4: (Float, Float, Float, Float, Float) |
| 165 | + ) { |
| 166 | + m11 = row1.0; m12 = row1.1; m13 = row1.2; m14 = row1.3; m15 = row1.4 |
| 167 | + m21 = row2.0; m22 = row2.1; m23 = row2.2; m24 = row2.3; m25 = row2.4 |
| 168 | + m31 = row3.0; m32 = row3.1; m33 = row3.2; m34 = row3.3; m35 = row3.4 |
| 169 | + m41 = row4.0; m42 = row4.1; m43 = row4.2; m44 = row4.3; m45 = row4.4 |
| 170 | + } |
| 171 | + |
| 172 | + package init?(_ filter: GraphicsFilter, premultiplied: Bool = false) { |
| 173 | + fatalError("TODO") |
| 174 | + } |
| 175 | + |
| 176 | + package init(colorMultiply c: Color.Resolved, premultiplied: Bool = false) { |
| 177 | + let factor: Float = premultiplied ? c.opacity : 1.0 |
| 178 | + let red = c.red * factor |
| 179 | + let green = c.green * factor |
| 180 | + let blue = c.blue * factor |
| 181 | + let alpha = c.opacity |
| 182 | + self.init( |
| 183 | + row1: (red, 0, 0, 0, 0), |
| 184 | + row2: (0, green, 0, 0, 0), |
| 185 | + row3: (0, 0, blue, 0, 0), |
| 186 | + row4: (0, 0, 0, alpha, 0) |
| 187 | + ) |
| 188 | + } |
| 189 | + |
| 190 | + package init(hueRotation angle: Angle) { |
| 191 | + let cosValue = Float(cos(angle.radians)) |
| 192 | + let sinValue = Float(sin(angle.radians)) |
| 193 | + |
| 194 | + // Matrix coefficients for hue rotation |
| 195 | + // Based on color rotation matrix formula |
| 196 | + self.init( |
| 197 | + row1: (0.2126 + cosValue * 0.7873 - sinValue * 0.2126, 0.2126 - cosValue * 0.2126 + sinValue * 0.1430, 0.2126 - cosValue * 0.2126 - sinValue * 0.7873, 0, 0), |
| 198 | + row2: (0.7152 - cosValue * 0.7152 - sinValue * 0.7152, 0.7152 + cosValue * 0.2848 + sinValue * 0.1400, 0.7152 - cosValue * 0.7152 + sinValue * 0.7152, 0, 0), |
| 199 | + row3: (0.0722 - cosValue * 0.0722 + sinValue * 0.9278, 0.0722 - cosValue * 0.0722 - sinValue * 0.2830, 0.0722 + cosValue * 0.9278 + sinValue * 0.0722, 0, 0), |
| 200 | + row4: (0, 0, 0, 1, 0) |
| 201 | + ) |
| 202 | + } |
| 203 | + |
| 204 | + package init(brightness: Double) { |
| 205 | + let b = Float(brightness) |
| 206 | + self.init( |
| 207 | + row1: (1, 0, 0, 0, b), |
| 208 | + row2: (0, 1, 0, 0, b), |
| 209 | + row3: (0, 0, 1, 0, b), |
| 210 | + row4: (0, 0, 0, 1, 0) |
| 211 | + ) |
| 212 | + } |
| 213 | + |
| 214 | + package init(contrast: Double) { |
| 215 | + let c = Float(contrast) |
| 216 | + let t = (1.0 - c) / 2.0 |
| 217 | + self.init( |
| 218 | + row1: (c, 0, 0, 0, t), |
| 219 | + row2: (0, c, 0, 0, t), |
| 220 | + row3: (0, 0, c, 0, t), |
| 221 | + row4: (0, 0, 0, 1, 0) |
| 222 | + ) |
| 223 | + } |
| 224 | + |
| 225 | + package init(luminanceToAlpha: Void) { |
| 226 | + // Using standard luminance coefficients (Rec. 709) |
| 227 | + self.init( |
| 228 | + row1: (0, 0, 0, 0, 0), |
| 229 | + row2: (0, 0, 0, 0, 0), |
| 230 | + row3: (0, 0, 0, 0, 0), |
| 231 | + row4: (0.2126, 0.7152, 0.0722, 0, 0) // Luminance coefficients in alpha channel |
| 232 | + ) |
| 233 | + } |
| 234 | + |
| 235 | + package init(colorInvert x: Float) { |
| 236 | + self.init( |
| 237 | + row1: (-1, 0, 0, 0, x), |
| 238 | + row2: (0, -1, 0, 0, x), |
| 239 | + row3: (0, 0, -1, 0, x), |
| 240 | + row4: (0, 0, 0, 1, 0) |
| 241 | + ) |
| 242 | + } |
| 243 | + |
| 244 | + package init(colorMonochrome c: Color.Resolved, amount: Float = 1, bias: Float = 0) { |
| 245 | + let red = c.red |
| 246 | + let green = c.green |
| 247 | + let blue = c.blue |
| 248 | + let opacity = c.opacity |
| 249 | + |
| 250 | + // Standard luminance coefficients (Rec. 709) |
| 251 | + let lumR: Float = 0.2126 |
| 252 | + let lumG: Float = 0.7152 |
| 253 | + let lumB: Float = 0.0722 |
| 254 | + |
| 255 | + // Calculate the inverse of amount for blending |
| 256 | + let invAmount = 1.0 - amount |
| 257 | + |
| 258 | + self.init( |
| 259 | + row1: (red * lumR * amount + invAmount, red * lumG * amount, red * lumB * amount, 0, red * amount * bias), |
| 260 | + row2: (green * lumR * amount, green * lumG * amount + invAmount, green * lumB * amount, 0, green * amount * bias), |
| 261 | + row3: (blue * lumR * amount, blue * lumG * amount, blue * lumB * amount + invAmount, 0, blue * amount * bias), |
| 262 | + row4: (0, 0, 0, opacity * amount + invAmount, 0) |
| 263 | + ) |
| 264 | + } |
| 265 | + |
| 266 | + package init(floatArray: [Float]) { |
| 267 | + m11 = floatArray[0]; m12 = floatArray[1]; m13 = floatArray[2]; m14 = floatArray[3]; m15 = floatArray[4] |
| 268 | + m21 = floatArray[5]; m22 = floatArray[6]; m23 = floatArray[7]; m24 = floatArray[8]; m25 = floatArray[9] |
| 269 | + m31 = floatArray[10]; m32 = floatArray[11]; m33 = floatArray[12]; m34 = floatArray[13]; m35 = floatArray[14] |
| 270 | + m41 = floatArray[15]; m42 = floatArray[16]; m43 = floatArray[17]; m44 = floatArray[18]; m45 = floatArray[19] |
| 271 | + } |
| 272 | + |
| 273 | + package var floatArray: [Float] { |
| 274 | + [ |
| 275 | + m11, m12, m13, m14, m15, |
| 276 | + m21, m22, m23, m24, m25, |
| 277 | + m31, m32, m33, m34, m35, |
| 278 | + m41, m42, m43, m44, m45 |
| 279 | + ] |
| 280 | + } |
| 281 | +} |
| 282 | + |
| 283 | +// MARK: - _ColorMatrix + ShapeStyle [TODO] |
| 284 | + |
| 285 | +@_spi(Private) |
| 286 | +extension _ColorMatrix: ShapeStyle { |
| 287 | + public func _apply(to shape: inout _ShapeStyle_Shape) { |
| 288 | + // TODO |
| 289 | + } |
| 290 | + |
| 291 | + public typealias Resolved = Never |
| 292 | +} |
| 293 | + |
| 294 | +// MARK: - _ColorMatrix + ProtobufMessage |
| 295 | + |
| 296 | +extension _ColorMatrix: ProtobufMessage { |
| 297 | + package func encode(to encoder: inout ProtobufEncoder) { |
| 298 | + encoder.floatField(1, m11, defaultValue: 1.0) |
| 299 | + encoder.floatField(2, m12, defaultValue: 0.0) |
| 300 | + encoder.floatField(3, m13, defaultValue: 0.0) |
| 301 | + encoder.floatField(4, m14, defaultValue: 0.0) |
| 302 | + encoder.floatField(5, m15, defaultValue: 0.0) |
| 303 | + encoder.floatField(6, m21, defaultValue: 0.0) |
| 304 | + encoder.floatField(7, m22, defaultValue: 1.0) |
| 305 | + encoder.floatField(8, m23, defaultValue: 0.0) |
| 306 | + encoder.floatField(9, m24, defaultValue: 0.0) |
| 307 | + encoder.floatField(10, m25, defaultValue: 0.0) |
| 308 | + encoder.floatField(11, m31, defaultValue: 0.0) |
| 309 | + encoder.floatField(12, m32, defaultValue: 0.0) |
| 310 | + encoder.floatField(13, m33, defaultValue: 1.0) |
| 311 | + encoder.floatField(14, m34, defaultValue: 0.0) |
| 312 | + encoder.floatField(15, m35, defaultValue: 0.0) |
| 313 | + encoder.floatField(16, m41, defaultValue: 0.0) |
| 314 | + encoder.floatField(17, m42, defaultValue: 0.0) |
| 315 | + encoder.floatField(18, m43, defaultValue: 0.0) |
| 316 | + encoder.floatField(19, m44, defaultValue: 1.0) |
| 317 | + encoder.floatField(20, m45, defaultValue: 0.0) |
| 318 | + } |
| 319 | + |
| 320 | + package init(from decoder: inout ProtobufDecoder) throws { |
| 321 | + self = _ColorMatrix() |
| 322 | + while let field = try decoder.nextField() { |
| 323 | + switch field.tag { |
| 324 | + case 1: m11 = try decoder.floatField(field) |
| 325 | + case 2: m12 = try decoder.floatField(field) |
| 326 | + case 3: m13 = try decoder.floatField(field) |
| 327 | + case 4: m14 = try decoder.floatField(field) |
| 328 | + case 5: m15 = try decoder.floatField(field) |
| 329 | + case 6: m21 = try decoder.floatField(field) |
| 330 | + case 7: m22 = try decoder.floatField(field) |
| 331 | + case 8: m23 = try decoder.floatField(field) |
| 332 | + case 9: m24 = try decoder.floatField(field) |
| 333 | + case 10: m25 = try decoder.floatField(field) |
| 334 | + case 11: m31 = try decoder.floatField(field) |
| 335 | + case 12: m32 = try decoder.floatField(field) |
| 336 | + case 13: m33 = try decoder.floatField(field) |
| 337 | + case 14: m34 = try decoder.floatField(field) |
| 338 | + case 15: m35 = try decoder.floatField(field) |
| 339 | + case 16: m41 = try decoder.floatField(field) |
| 340 | + case 17: m42 = try decoder.floatField(field) |
| 341 | + case 18: m43 = try decoder.floatField(field) |
| 342 | + case 19: m44 = try decoder.floatField(field) |
| 343 | + case 20: m45 = try decoder.floatField(field) |
| 344 | + default: |
| 345 | + try decoder.skipField(field) |
| 346 | + } |
| 347 | + } |
| 348 | + } |
| 349 | +} |
| 350 | + |
| 351 | +// MARK: - _ColorMatrix + Calculations |
| 352 | + |
| 353 | +extension _ColorMatrix { |
| 354 | + mutating func add(_ other: _ColorMatrix) { |
| 355 | + m11 += other.m11; m12 += other.m12; m13 += other.m13; m14 += other.m14; m15 += other.m15 |
| 356 | + m21 += other.m21; m22 += other.m22; m23 += other.m23; m24 += other.m24; m25 += other.m25 |
| 357 | + m31 += other.m31; m32 += other.m32; m33 += other.m33; m34 += other.m34; m35 += other.m35 |
| 358 | + m41 += other.m41; m42 += other.m42; m43 += other.m43; m44 += other.m44; m45 += other.m45 |
| 359 | + } |
| 360 | + |
| 361 | + mutating func subtract(_ other: _ColorMatrix) { |
| 362 | + m11 -= other.m11; m12 -= other.m12; m13 -= other.m13; m14 -= other.m14; m15 -= other.m15 |
| 363 | + m21 -= other.m21; m22 -= other.m22; m23 -= other.m23; m24 -= other.m24; m25 -= other.m25 |
| 364 | + m31 -= other.m31; m32 -= other.m32; m33 -= other.m33; m34 -= other.m34; m35 -= other.m35 |
| 365 | + m41 -= other.m41; m42 -= other.m42; m43 -= other.m43; m44 -= other.m44; m45 -= other.m45 |
| 366 | + } |
| 367 | + |
| 368 | + mutating func negate() { |
| 369 | + m11 = -m11; m12 = -m12; m13 = -m13; m14 = -m14; m15 = -m15 |
| 370 | + m21 = -m21; m22 = -m22; m23 = -m23; m24 = -m24; m25 = -m25 |
| 371 | + m31 = -m31; m32 = -m32; m33 = -m33; m34 = -m34; m35 = -m35 |
| 372 | + m41 = -m41; m42 = -m42; m43 = -m43; m44 = -m44; m45 = -m45 |
| 373 | + } |
| 374 | + |
| 375 | + mutating func scale(by scalar: Double) { |
| 376 | + let scalar = Float(scalar) |
| 377 | + m11 *= scalar; m12 *= scalar; m13 *= scalar; m14 *= scalar; m15 *= scalar |
| 378 | + m21 *= scalar; m22 *= scalar; m23 *= scalar; m24 *= scalar; m25 *= scalar |
| 379 | + m31 *= scalar; m32 *= scalar; m33 *= scalar; m34 *= scalar; m35 *= scalar |
| 380 | + m41 *= scalar; m42 *= scalar; m43 *= scalar; m44 *= scalar; m45 *= scalar |
| 381 | + } |
| 382 | + |
| 383 | + var magnitudeSquared: Double { |
| 384 | + floatArray.reduce(0.0) { $0 + Double($1 * $1) } |
| 385 | + } |
| 386 | +} |
0 commit comments