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
|
@ -29,6 +29,8 @@
|
|||
C0FF7C992168062C0081B781 /* Shaders.metal in Sources */ = {isa = PBXBuildFile; fileRef = C0BBE3A91F2E91D900E68524 /* Shaders.metal */; };
|
||||
C0FF7C9A2168062C0081B781 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0B906D41F45432700B5F89B /* Preferences.swift */; };
|
||||
C0FF7C9B2168062C0081B781 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C091616F1F3F5AE6009C4263 /* PreferencesViewController.swift */; };
|
||||
C0FF7C9D216A6DE00081B781 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF7C9C216A6DE00081B781 /* Math.swift */; };
|
||||
C0FF7C9E216A6DE00081B781 /* Math.swift in Sources */ = {isa = PBXBuildFile; fileRef = C0FF7C9C216A6DE00081B781 /* Math.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
|
@ -78,6 +80,7 @@
|
|||
C0BBE3AB1F2E941200E68524 /* Renderer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Renderer.swift; sourceTree = "<group>"; };
|
||||
C0CE7BFF1F362C3F001516B6 /* Geometry.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Geometry.swift; sourceTree = "<group>"; };
|
||||
C0DF1D781F3789DC0038B0A0 /* Memory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Memory.swift; sourceTree = "<group>"; };
|
||||
C0FF7C9C216A6DE00081B781 /* Math.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Math.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -167,6 +170,7 @@
|
|||
C0BBE3A31F2E81C700E68524 /* Metaballs.swift */,
|
||||
C0CE7BFF1F362C3F001516B6 /* Geometry.swift */,
|
||||
C0DF1D781F3789DC0038B0A0 /* Memory.swift */,
|
||||
C0FF7C9C216A6DE00081B781 /* Math.swift */,
|
||||
C0BBE3AB1F2E941200E68524 /* Renderer.swift */,
|
||||
C0BBE3A91F2E91D900E68524 /* Shaders.metal */,
|
||||
C0B906D41F45432700B5F89B /* Preferences.swift */,
|
||||
|
@ -345,6 +349,7 @@
|
|||
files = (
|
||||
C0FF7C992168062C0081B781 /* Shaders.metal in Sources */,
|
||||
C0FF7C9A2168062C0081B781 /* Preferences.swift in Sources */,
|
||||
C0FF7C9E216A6DE00081B781 /* Math.swift in Sources */,
|
||||
C0FF7C972168062C0081B781 /* Memory.swift in Sources */,
|
||||
C0FF7C9B2168062C0081B781 /* PreferencesViewController.swift in Sources */,
|
||||
C0B906E91F455D1A00B5F89B /* MetaballsSaverView.swift in Sources */,
|
||||
|
@ -367,6 +372,7 @@
|
|||
C0FF7C912168062B0081B781 /* Renderer.swift in Sources */,
|
||||
C0FF7C932168062B0081B781 /* Preferences.swift in Sources */,
|
||||
C0BBE36B1F2E816500E68524 /* AppDelegate.swift in Sources */,
|
||||
C0FF7C9D216A6DE00081B781 /* Math.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
|
|
@ -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