I've found a solution for this. As suggested, the simd_float4x4 matrix is the key to the solution here. Using SpriteKit I've created my 2D map using RoomPlan data.
To start with, I've created a couple of extensions for simd_float4x4. These extract the data we need from the metrix so it's easier to work with:
extension simd_float4x4 {
var eulerAngles: simd_float3 {
simd_float3(
x: asin(-self[2][1]),
y: atan2(self[2][0], self[2][2]),
z: atan2(self[0][1], self[1][1])
)
}
var position: simd_float3 {
simd_float3(x: self.columns.3.x,
y: self.columns.3.y,
z: self.columns.3.z
)
}
var positionVector: SCNVector3 {
SCNVector3(x: self.columns.3.x,
y: self.columns.3.y,
z: self.columns.3.z
)
}
}
To build my 2D map in SpriteKit, I created a separate SKNode for every RoomPlan surface. In each node I set the correct position and rotation using the sims_float4x4 matrix, create a path using the dimensions and draw the surface using an SKShapeNode.
Using the eulerAngles and position extensions, I set the correct rotation and position of every surface:
// Position surface
position.x = -CGFloat(transform.position.x) * mapScale
position.y = CGFloat(transform.position.z) * mapScale
zRotation = -CGFloat(transform.eulerAngles.z - transform.eulerAngles.y)
The mapScale is just a scaling factor I applied. The measurements that come out of RoomPlan are in meters. In SpriteKit they're in points. If you don't apply a scale, the whole map will display within a few pixels. Setting the mapScale to 100-300 will do fine, depending on the device's screen size and SpriteKit camera.
The x position is the same in both RoomPlan's and SpriteKit's coordinate system. In RoomPlan however, the x- and y-axis are horizontal and vertical and the z-axis is the "depth" axis. We're interested in the "top" view, ignoring RoomPlan's y-axis so we'll need to use the z value for SpriteKit's y component (I hope my explanation makes any sense :p).
The surfaceRotation is a weird one. I still don't fully get why this works like this, but it does xd.
As pointed out by @ArthurPhilipenko the matrix only tells us about the position and rotation of the surface which will result in a point in space, but not a line, like we want to draw. For this, we also need the "dimension" component from the RoomPlan surface. This is a simd_float3. Using this dimension component, we can draw a line using the width of the surface (or the dimension.x) and positioning it equally on both sides of out position and rotation point of the node.
// Create surface path
let surfacePath = CGMutablePath()
let span = CGFloat(dimensions.x) * mapScale / 2
surfacePath.move(to: CGPoint(x: -span, y: 0))
surfacePath.addLine(to: CGPoint(x: span, y: 0))
Finally, I create a SKShapeNode to draw the line on the screen:
// Draw surface path
func draw() {
let surfaceShape = SKShapeNode(path: surfacePath)
surfaceShape.strokeColor = surfaceColor
surfaceShape.lineWidth = wallThickness
surfaceShape.lineCap = .square
addChild(surfaceShape)
}
If you do this for every surface you'll get a nice 2D map using the 3D RoomPlan data.