Simd_Float4X4 Columns

simd_float4x4, SCNMatrix columns not documented

A "transformation matrix" in 3D graphics doesn't store translation, rotation, scale, and perspective per column (or row). Instead these are all stored mixed so that multiplying a 4-element vector with the transformation matrix will "apply" the transformation (that is, the result of the multiplication is the transformed vector) and so that multiplying two transformation matrices produce a new transformation matrix that combines the two.

Because of this it's not very meaningful to describe the columns, rows, or even most cells in the matrix (the diagonal is used for scaling but those same cells are also used for some rotation values).

You can read more about transformation matrices on Wikipedia and the Wikipedia page for "affine" transforms has some good illustrations of 2D transforms.

How to get values from simd_float4 in objective-c

simd_float4x4 is a struct (like 4 simd_float4), and you can use

simd_float4x4.columns[index]

to access column in matrix.

/*! @abstract A matrix with 4 rows and 4 columns.*/
struct simd_float4x4 {
public var columns: (simd_float4, simd_float4, simd_float4, simd_float4)
public init()
public init(columns: (simd_float4, simd_float4, simd_float4, simd_float4))
}

Apple document link: https://developer.apple.com/documentation/simd/simd_float4x4?language=objc

hope helpful!

How can I encode an array of simd_float4x4 elements in Swift (convert simd_float4x4 to Data)?

As already mentioned in comments structures can't conform to NSCoding but you can make simd_float4x4 conform to Codable and persist its data:

extension simd_float4x4: Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
try self.init(container.decode([SIMD4<Float>].self))
}
public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode([columns.0,columns.1, columns.2, columns.3])
}
}

Playground testing:

do {
let vector = simd_float4x4(2.7) // simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])
let data = try JSONEncoder().encode(vector) // 111 bytes
let json = String(data: data, encoding: .utf8)
print(json ?? "") // [[[2.7000000476837158,0,0,0],[0,2.7000000476837158,0,0],[0,0,2.7000000476837158,0],[0,0,0,2.7000000476837158]]]\n"
let decoded = try JSONDecoder().decode(simd_float4x4.self, from: data)
print(decoded) // "simd_float4x4([[2.7, 0.0, 0.0, 0.0], [0.0, 2.7, 0.0, 0.0], [0.0, 0.0, 2.7, 0.0], [0.0, 0.0, 0.0, 2.7]])\n"
decoded == vector // true
} catch {
print(error)
}

edit/update:

Another option is to save its raw bytes. It will use only 64 bytes:

extension simd_float4x4: ContiguousBytes {
public func withUnsafeBytes<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
try Swift.withUnsafeBytes(of: self) { try body($0) }
}
}


extension ContiguousBytes {
init<T: ContiguousBytes>(_ bytes: T) {
self = bytes.withUnsafeBytes { $0.load(as: Self.self) }
}
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
func object<T>() -> T { withUnsafeBytes { $0.load(as: T.self) } }
func objects<T>() -> [T] { withUnsafeBytes { .init($0.bindMemory(to: T.self)) } }
var simdFloat4x4: simd_float4x4 { object() }
var simdFloat4x4Collection: [simd_float4x4] { objects() }
}


extension Array where Element: ContiguousBytes {
var bytes: [UInt8] { withUnsafeBytes { .init($0) } }
var data: Data { withUnsafeBytes { .init($0) } }
}


let vector1 = simd_float4x4(.init(2, 1, 1, 1), .init(1, 2, 1, 1), .init(1, 1, 2, 1), .init(1, 1, 1, 2))
let vector2 = simd_float4x4(.init(3, 1, 1, 1), .init(1, 3, 1, 1), .init(1, 1, 3, 1), .init(1, 1, 1, 3))
let data = [vector1,vector2].data // 128 bytes
let loaded = data.simdFloat4x4Collection
print(loaded) // "[simd_float4x4([[2.0, 1.0, 1.0, 1.0], [1.0, 2.0, 1.0, 1.0], [1.0, 1.0, 2.0, 1.0], [1.0, 1.0, 1.0, 2.0]]), simd_float4x4([[3.0, 1.0, 1.0, 1.0], [1.0, 3.0, 1.0, 1.0], [1.0, 1.0, 3.0, 1.0], [1.0, 1.0, 1.0, 3.0]])]\n"
loaded[0] == vector1 // true
loaded[1] == vector2 // true

Is SceneKit's SCNMatrix4 stored as column or row-major?

SCNMatrix4 stores the translation in m41, m42 and m43, as is GLKMatrix4. This little Playground confirms it as well as its definition.

import SceneKit

let translation = SCNMatrix4MakeTranslation(1, 2, 3)

translation.m41
// 1
translation.m42
// 2
translation.m43
// 3

I have no idea why this is wrong in the documentation though, probably just a mistake.

Understanding ARKit World Transform Matrices

EDIT: The original question is best addressed by just adding 0.05 to the y component of the node's position. However, the original answer below does address a bit about composing transformation matrices, if that is something you are interested in.

======================================================================

If you want to apply an operation to a matrix, the most immediately simple way is to make a matrix that does that operation, and then multiply your original matrix by that new matrix.

For a translation, assuming you want to translate by x, y, z, you can do this:

let translation = simd_float4x4(
float4(1, 0, 0, 0),
float4(0, 1, 0, 0),
float4(0, 0, 1, 0),
float4(x, y, z, 1)
)

Note that this is just an identity matrix (1 down the diagonal) with the last column (!!!important, the float4s above are COLUMNS, not ROWS, as they would visually seem) set to contain the x/y/z values. You can research further into homogeneous coordinates, but think of this as just how a translation is represented.

Then, in simd, just do this: let newWorldTransform = translation * oldWorldTransform and you will have the old world transform translated by your x/y/z translation values (in your example, [x, y, z] = [0, 0.05, 0]).

However, it may be worth exploring why you want to edit your hit test results. I cannot think of a practical use case for that, so maybe if you explain a bit more about what you are trying to do I could suggest a more intuitive way to do it.

How do I extract rotation values from a simdTransform in ARKit?

The 3D rotation is expressed as a quaternion, which is made of a 3D vector, describing the axis that you are rotating around, and an angle, which describes the angle of rotation about that axis.

To get a quaternion from a 4x4 simd matrix, you can initialise simd_quatf from the matrix:

let m = simd_float4x4([
[0.9482585, 0.0065377234, 0.31743154, 0.0],
[-0.07080679, 0.9789629, 0.1913578, 0.0],
[-0.3095027, -0.20393308, 0.9287729, 0.0],
[0.13866597, 0.20701922, -0.57620335, 1.0]
])
let q = simd_quatf(m)

The rotation property of SCNNode is expressed as SCNVector4, which has x, y, and z properties describing the axis, and w describing the rotation. simd_quatf has axis (Float) and angle (SIMD3<Float>) properties, so to get your vector:

let sq = SCNVector4(q.axis.x, q.axis.y, q.axis.z, q.angle)

You can see here that the results are the same:

Sample Image

Xcode simd - issue with Translation and Rotation Matrix Example

This can be confusing, yes.

The documentation you mentions makes the following computation:

let translatedVector = positionVector * translationMatrix

Note that the matrix is on the right side of the multiplication.
You are probably used to the notation b = M * a but if you take the transpose you get b' = a' * M' which is what the sample does.

In SIMD there's no way to differentiate a vector from its transpose (b from b') and the library allows you to make the multiplication in both ways:

static simd_float3 SIMD_CFUNC simd_mul(simd_float3x3 __x, simd_float3 __y);
static simd_float3 SIMD_CFUNC simd_mul(simd_float3 __x, simd_float3x3 __y) { return simd_mul(simd_transpose(__y), __x); }

Elegant way to flatten a simd_float4x4 matrix

Elegant is in the eye of the beholder. So far, I've come up with this:

let s = simd_float3x3(simd_float3(1, 2, 3), simd_float3(4, 5, 6), simd_float3(7, 8, 9)) 

let y = (0..<3).flatMap { x in (0..<3).map { y in s[x][y] } }

print(y)
[1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]


Related Topics



Leave a reply



Submit