Use the simd types where I can
This commit is contained in:
		
							parent
							
								
									edce32e021
								
							
						
					
					
						commit
						8f1e1335bd
					
				
					 9 changed files with 107 additions and 144 deletions
				
			
		| 
						 | 
				
			
			@ -7,91 +7,18 @@
 | 
			
		|||
//
 | 
			
		||||
 | 
			
		||||
import Foundation
 | 
			
		||||
import simd
 | 
			
		||||
 | 
			
		||||
public struct Point {
 | 
			
		||||
    var x: Float
 | 
			
		||||
    var y: Float
 | 
			
		||||
 | 
			
		||||
    var CGPoint: CGPoint {
 | 
			
		||||
        return CoreGraphics.CGPoint(x: CGFloat(x), y: CGFloat(y))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init() {
 | 
			
		||||
        self.init(x: 0, y: 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init(x: Float, y: Float) {
 | 
			
		||||
        self.x = x
 | 
			
		||||
        self.y = y
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Point: CustomStringConvertible {
 | 
			
		||||
    public var description: String {
 | 
			
		||||
        return "(\(x), \(y))"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public struct Size {
 | 
			
		||||
    var width: UInt16
 | 
			
		||||
    var height: UInt16
 | 
			
		||||
 | 
			
		||||
    public init() {
 | 
			
		||||
        self.init(width: 0, height: 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public init(width: UInt16, height: UInt16) {
 | 
			
		||||
        self.width = width
 | 
			
		||||
        self.height = height
 | 
			
		||||
    }
 | 
			
		||||
public typealias Size = packed_uint2
 | 
			
		||||
 | 
			
		||||
extension Size {
 | 
			
		||||
    public init(size: CGSize) {
 | 
			
		||||
        self.init(width: UInt16(size.width), height: UInt16(size.height))
 | 
			
		||||
        self.init(UInt32(size.width), UInt32(size.height))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Size: CustomStringConvertible {
 | 
			
		||||
    public var description: String {
 | 
			
		||||
        return "(\(width), \(height))"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Size: Equatable {
 | 
			
		||||
    /// Returns a Boolean value indicating whether two values are equal.
 | 
			
		||||
    ///
 | 
			
		||||
    /// Equality is the inverse of inequality. For any values `a` and `b`,
 | 
			
		||||
    /// `a == b` implies that `a != b` is `false`.
 | 
			
		||||
    ///
 | 
			
		||||
    /// - Parameters:
 | 
			
		||||
    ///   - lhs: A value to compare.
 | 
			
		||||
    ///   - rhs: Another value to compare.
 | 
			
		||||
    public static func ==(lhs: Size, rhs: Size) -> Bool {
 | 
			
		||||
        return lhs.width == rhs.width && lhs.height == rhs.height
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension CGSize {
 | 
			
		||||
    init(size: Size) {
 | 
			
		||||
        self.init(width: CGFloat(size.width), height: CGFloat(size.height))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public struct Vector {
 | 
			
		||||
    var dx: Float
 | 
			
		||||
    var dy: Float
 | 
			
		||||
 | 
			
		||||
    init() {
 | 
			
		||||
        self.init(dx: 0, dy: 0)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init(dx: Float, dy: Float) {
 | 
			
		||||
        self.dx = dx
 | 
			
		||||
        self.dy = dy
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Vector: CustomStringConvertible {
 | 
			
		||||
    public var description: String {
 | 
			
		||||
        return "(\(dx), \(dy))"
 | 
			
		||||
        return "(\(x), \(y))"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										63
									
								
								MetaballsKit/Math.swift
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								MetaballsKit/Math.swift
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,63 @@
 | 
			
		|||
//
 | 
			
		||||
//  Math.swift
 | 
			
		||||
//  Metaballs
 | 
			
		||||
//
 | 
			
		||||
//  Created by Eryn Wells on 9/22/18.
 | 
			
		||||
//  Copyright © 2018 Eryn Wells. All rights reserved.
 | 
			
		||||
//
 | 
			
		||||
 | 
			
		||||
import Cocoa
 | 
			
		||||
import Foundation
 | 
			
		||||
import simd
 | 
			
		||||
 | 
			
		||||
public typealias Float2 = packed_float2
 | 
			
		||||
public typealias Float3 = float3
 | 
			
		||||
public typealias Float4 = float4
 | 
			
		||||
public typealias Matrix3x3 = float3x3
 | 
			
		||||
public typealias Matrix4x4 = float4x4
 | 
			
		||||
 | 
			
		||||
extension Float2 {
 | 
			
		||||
    var CGPoint: CGPoint {
 | 
			
		||||
        return CoreGraphics.CGPoint(x: CGFloat(x), y: CGFloat(y))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Float2: CustomStringConvertible {
 | 
			
		||||
    public var description: String {
 | 
			
		||||
        return "(\(x), \(y))"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Float4 {
 | 
			
		||||
    public init(r: Float, g: Float, b: Float, a: Float) {
 | 
			
		||||
        self.init(r, g, b, a)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public init(color: NSColor) {
 | 
			
		||||
        if let convertedColor = color.usingColorSpace(NSColorSpace.deviceRGB) {
 | 
			
		||||
            self.init(Float(convertedColor.redComponent), Float(convertedColor.greenComponent), Float(convertedColor.blueComponent), Float(convertedColor.alphaComponent))
 | 
			
		||||
        } else {
 | 
			
		||||
            self.init()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension Matrix4x4 {
 | 
			
		||||
    /// Create a 4x4 orthographic projection matrix with the provided 6-tuple.
 | 
			
		||||
    /// @see https://en.wikipedia.org/wiki/Orthographic_projection
 | 
			
		||||
    static func orthographicProjection(top: Float32, left: Float32, bottom: Float32, right: Float32, near: Float32, far: Float32) -> Matrix4x4 {
 | 
			
		||||
        let rows = [
 | 
			
		||||
            Float4(2.0 / (right - left), 0.0, 0.0, -(right + left) / (right - left)),
 | 
			
		||||
            Float4(0.0, 2.0 / (top - bottom), 0.0, -(top + bottom) / (top - bottom)),
 | 
			
		||||
            Float4(0.0, 0.0, -2.0 / (far - near), -(far + near) / (far - near)),
 | 
			
		||||
            Float4(0.0, 0.0, 0.0, 1.0)
 | 
			
		||||
        ]
 | 
			
		||||
        return Matrix4x4(rows)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension CGSize {
 | 
			
		||||
    init(size: Size) {
 | 
			
		||||
        self.init(width: CGFloat(size.x), height: CGFloat(size.y))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -8,6 +8,7 @@
 | 
			
		|||
 | 
			
		||||
import Cocoa
 | 
			
		||||
import Foundation
 | 
			
		||||
import simd
 | 
			
		||||
 | 
			
		||||
extension UnsafeMutableRawPointer {
 | 
			
		||||
    func writeAndAdvance<T>(value: inout T) -> UnsafeMutableRawPointer {
 | 
			
		||||
| 
						 | 
				
			
			@ -17,38 +18,6 @@ extension UnsafeMutableRawPointer {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Metal's float4 type. 4 bytes per float, 16 bytes total, 16 byte aligned.
 | 
			
		||||
public struct Float4 {
 | 
			
		||||
    public var x: Float = 0
 | 
			
		||||
    public var y: Float = 0
 | 
			
		||||
    public var z: Float = 0
 | 
			
		||||
    public var w: Float = 0
 | 
			
		||||
 | 
			
		||||
    public init() { }
 | 
			
		||||
 | 
			
		||||
    public init(_ x: Float, _ y: Float, _ z: Float, _ w: Float) {
 | 
			
		||||
        self.x = x
 | 
			
		||||
        self.y = y
 | 
			
		||||
        self.z = z
 | 
			
		||||
        self.w = w
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public init(r: Float, g: Float, b: Float, a: Float) {
 | 
			
		||||
        x = r
 | 
			
		||||
        y = g
 | 
			
		||||
        z = b
 | 
			
		||||
        w = a
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    init(color: NSColor) {
 | 
			
		||||
        if let convertedColor = color.usingColorSpace(NSColorSpace.deviceRGB) {
 | 
			
		||||
            self.init(Float(convertedColor.redComponent), Float(convertedColor.greenComponent), Float(convertedColor.blueComponent), Float(convertedColor.alphaComponent))
 | 
			
		||||
        } else {
 | 
			
		||||
            self.init()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extension NSColor {
 | 
			
		||||
    convenience init(float4: Float4) {
 | 
			
		||||
        self.init(deviceRed: CGFloat(float4.x), green: CGFloat(float4.y), blue: CGFloat(float4.z), alpha: CGFloat(float4.w))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ public enum MetaballsError: Error {
 | 
			
		|||
    case metalError(String)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public enum ColorStyle: UInt16 {
 | 
			
		||||
public enum ColorStyle: UInt32 {
 | 
			
		||||
    /// Single flat color
 | 
			
		||||
    case singleColor = 1
 | 
			
		||||
    /// Two color horizontal gradient
 | 
			
		||||
| 
						 | 
				
			
			@ -33,8 +33,8 @@ public struct Parameters {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    // Simulation parameters
 | 
			
		||||
    var size = Size(width: 0, height: 0)
 | 
			
		||||
    var numberOfBalls: UInt16 = 0
 | 
			
		||||
    var size = Size(0, 0)
 | 
			
		||||
    var numberOfBalls: UInt32 = 0
 | 
			
		||||
 | 
			
		||||
    private var _colorStyle = ColorStyle.singleColor.rawValue
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -69,8 +69,8 @@ public struct Parameters {
 | 
			
		|||
 | 
			
		||||
public struct Ball {
 | 
			
		||||
    let radius: Float
 | 
			
		||||
    var position = Point()
 | 
			
		||||
    var velocity = Vector()
 | 
			
		||||
    var position = Float2()
 | 
			
		||||
    var velocity = Float2()
 | 
			
		||||
 | 
			
		||||
    internal var bounds: CGRect {
 | 
			
		||||
        let diameter = CGFloat(radius * 2)
 | 
			
		||||
| 
						 | 
				
			
			@ -78,8 +78,8 @@ public struct Ball {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    internal mutating func update() {
 | 
			
		||||
        position.x += velocity.dx
 | 
			
		||||
        position.y += velocity.dy
 | 
			
		||||
        position.x += velocity.x
 | 
			
		||||
        position.y += velocity.y
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -98,11 +98,11 @@ public class Field {
 | 
			
		|||
            NSLog("Updating size of field: old:\(parameters.size), new:\(newValue)")
 | 
			
		||||
            if parameters.size != newValue {
 | 
			
		||||
                // Scale balls to new position and size.
 | 
			
		||||
                let scale = parameters.size.width != 0 ? Float(newValue.width / parameters.size.width) : 1
 | 
			
		||||
                let scale = parameters.size.x != 0 ? Float(newValue.x / parameters.size.x) : 1
 | 
			
		||||
                balls = balls.map {
 | 
			
		||||
                    let r = $0.radius * scale
 | 
			
		||||
                    let p = randomPoint(forBallWithRadius: r)
 | 
			
		||||
                    let v = Vector(dx: $0.velocity.dx * scale, dy: $0.velocity.dy * scale)
 | 
			
		||||
                    let v = Float2($0.velocity.x * scale, $0.velocity.y * scale)
 | 
			
		||||
                    return Ball(radius: r, position: p, velocity: v)
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -141,16 +141,16 @@ public class Field {
 | 
			
		|||
 | 
			
		||||
            if !selfBounds.contains(balls[i].position.CGPoint) {
 | 
			
		||||
                // Degenerate case. If the ball finds itself outside the bounds of the field, plop it back in the center.
 | 
			
		||||
                balls[i].position = Point(x: Float(selfBounds.midX), y: Float(selfBounds.midY))
 | 
			
		||||
                balls[i].position = Float2(x: Float(selfBounds.midX), y: Float(selfBounds.midY))
 | 
			
		||||
            } else {
 | 
			
		||||
                // Do collision detection with walls.
 | 
			
		||||
                let ballBounds = balls[i].bounds
 | 
			
		||||
                if !selfBounds.contains(ballBounds) {
 | 
			
		||||
                    if ballBounds.minX < selfBounds.minX || ballBounds.maxX > selfBounds.maxX {
 | 
			
		||||
                        balls[i].velocity.dx *= -1
 | 
			
		||||
                        balls[i].velocity.x *= -1
 | 
			
		||||
                    }
 | 
			
		||||
                    if ballBounds.minY < selfBounds.minY || ballBounds.maxY > selfBounds.maxY {
 | 
			
		||||
                        balls[i].velocity.dy *= -1
 | 
			
		||||
                        balls[i].velocity.y *= -1
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -163,7 +163,7 @@ public class Field {
 | 
			
		|||
 | 
			
		||||
        let dx = Float(5 - Int(arc4random_uniform(10)))
 | 
			
		||||
        let dy = Float(5 - Int(arc4random_uniform(10)))
 | 
			
		||||
        let velocity = Vector(dx: dx, dy: dy)
 | 
			
		||||
        let velocity = Float2(dx, dy)
 | 
			
		||||
 | 
			
		||||
        let ball = Ball(radius: radius, position: position, velocity: velocity)
 | 
			
		||||
        balls.append(ball)
 | 
			
		||||
| 
						 | 
				
			
			@ -178,14 +178,14 @@ public class Field {
 | 
			
		|||
        balls.removeAll(keepingCapacity: true)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private func randomPoint(forBallWithRadius radius: Float) -> Point {
 | 
			
		||||
    private func randomPoint(forBallWithRadius radius: Float) -> Float2 {
 | 
			
		||||
        guard Float(bounds.width) > radius && Float(bounds.height) > radius else {
 | 
			
		||||
            return Point()
 | 
			
		||||
            return Float2()
 | 
			
		||||
        }
 | 
			
		||||
        let insetBounds = bounds.insetBy(dx: CGFloat(radius), dy: CGFloat(radius))
 | 
			
		||||
        let x = Float(UInt32(insetBounds.minX) + arc4random_uniform(UInt32(insetBounds.width)))
 | 
			
		||||
        let y = Float(UInt32(insetBounds.minY) + arc4random_uniform(UInt32(insetBounds.height)))
 | 
			
		||||
        let position = Point(x: x, y: y)
 | 
			
		||||
        let position = Float2(x: x, y: y)
 | 
			
		||||
        return position
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +205,7 @@ public class Field {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        if let parametersBuffer = parametersBuffer {
 | 
			
		||||
            parameters.numberOfBalls = UInt16(balls.count)
 | 
			
		||||
            parameters.numberOfBalls = UInt32(balls.count)
 | 
			
		||||
            self.parameters.write(to: parametersBuffer)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ extension UserDefaults {
 | 
			
		|||
    public var colorStyle: ColorStyle? {
 | 
			
		||||
        get {
 | 
			
		||||
            let value = integer(forKey: "colorStyle")
 | 
			
		||||
            if let colorStyle = ColorStyle(rawValue: UInt16(value)) {
 | 
			
		||||
            if let colorStyle = ColorStyle(rawValue: UInt32(value)) {
 | 
			
		||||
                return colorStyle
 | 
			
		||||
            }
 | 
			
		||||
            return nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -222,7 +222,7 @@ public class PreferencesViewController: NSViewController {
 | 
			
		|||
    func postColorNotification() {
 | 
			
		||||
        var info = [String:Any]()
 | 
			
		||||
        if let item = styleMenu.selectedItem {
 | 
			
		||||
            info["colorStyle"] = ColorStyle(rawValue: UInt16(item.tag))
 | 
			
		||||
            info["colorStyle"] = ColorStyle(rawValue: UInt32(item.tag))
 | 
			
		||||
        }
 | 
			
		||||
        for (idx, cv) in colorViews.enumerated() {
 | 
			
		||||
            info["color\(idx)"] = cv.colorWell.color
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -20,8 +20,8 @@ public protocol RendererDelegate {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
struct Vertex {
 | 
			
		||||
    let position: Point
 | 
			
		||||
    let textureCoordinate: Point
 | 
			
		||||
    let position: Float2
 | 
			
		||||
    let textureCoordinate: Float2
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class Renderer: NSObject, MTKViewDelegate {
 | 
			
		||||
| 
						 | 
				
			
			@ -101,13 +101,13 @@ public class Renderer: NSObject, MTKViewDelegate {
 | 
			
		|||
 | 
			
		||||
        // Two triangles, plus texture coordinates.
 | 
			
		||||
        let points: [Vertex] = [
 | 
			
		||||
            Vertex(position: Point(x:  1, y: -1), textureCoordinate: Point(x: 1, y: 0)),
 | 
			
		||||
            Vertex(position: Point(x: -1, y: -1), textureCoordinate: Point(x: 0, y: 0)),
 | 
			
		||||
            Vertex(position: Point(x: -1, y:  1), textureCoordinate: Point(x: 0, y: 1)),
 | 
			
		||||
            Vertex(position: Float2(x:  1, y: -1), textureCoordinate: Float2(x: 1, y: 0)),
 | 
			
		||||
            Vertex(position: Float2(x: -1, y: -1), textureCoordinate: Float2(x: 0, y: 0)),
 | 
			
		||||
            Vertex(position: Float2(x: -1, y:  1), textureCoordinate: Float2(x: 0, y: 1)),
 | 
			
		||||
 | 
			
		||||
            Vertex(position: Point(x:  1, y: -1), textureCoordinate: Point(x: 1, y: 0)),
 | 
			
		||||
            Vertex(position: Point(x: -1, y:  1), textureCoordinate: Point(x: 0, y: 1)),
 | 
			
		||||
            Vertex(position: Point(x:  1, y:  1), textureCoordinate: Point(x: 1, y: 1))
 | 
			
		||||
            Vertex(position: Float2(x:  1, y: -1), textureCoordinate: Float2(x: 1, y: 0)),
 | 
			
		||||
            Vertex(position: Float2(x: -1, y:  1), textureCoordinate: Float2(x: 0, y: 1)),
 | 
			
		||||
            Vertex(position: Float2(x:  1, y:  1), textureCoordinate: Float2(x: 1, y: 1))
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
        field.update()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,13 +38,11 @@ typedef enum {
 | 
			
		|||
} ColorStyle;
 | 
			
		||||
 | 
			
		||||
typedef struct {
 | 
			
		||||
    short2 size;
 | 
			
		||||
    ushort numberOfBalls;
 | 
			
		||||
 | 
			
		||||
    ushort colorStyle;
 | 
			
		||||
    packed_uint2 size;
 | 
			
		||||
    uint numberOfBalls;
 | 
			
		||||
    uint colorStyle;
 | 
			
		||||
    float target;
 | 
			
		||||
    float feather;
 | 
			
		||||
 | 
			
		||||
    float4 colors[4];
 | 
			
		||||
} Parameters;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -90,12 +88,12 @@ sampleToColorShader(RasterizerData in               [[stage_in]],
 | 
			
		|||
            out = singleColor(sample, target, feather, parameters.colors[0]);
 | 
			
		||||
            break;
 | 
			
		||||
        case Gradient2Horizontal: {
 | 
			
		||||
            const float blend = in.position.x / parameters.size.x;
 | 
			
		||||
            const float blend = in.position.x / parameters.size[0];
 | 
			
		||||
            out = gradient2(sample, target, feather, blend, parameters.colors[0], parameters.colors[1]);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        case Gradient2Vertical: {
 | 
			
		||||
            const float blend = in.position.y / parameters.size.y;
 | 
			
		||||
            const float blend = in.position.y / parameters.size[1];
 | 
			
		||||
            out = gradient2(sample, target, feather, blend, parameters.colors[0], parameters.colors[1]);
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue