ARKitで2つのポイント間に線を引く


ARSCNViewやARViewで2つのポイント間に線を引きたいことがあります。
勿論一旦ピクセルにしてOpenCVといったライブラリで線を引くこともできますが、ここではオブジェクトを配置して線を引いてみます。

ARSCNView(SecneKit)だとSCNCylinder、ARView(RealityKit)だとgenerateBoxといったオブジェクトで擬似的に線に見立てると思います。
しかし、これはオブジェクトですので、線を引くのと違い、長さと回転をいい具合に調整する必要があります。
オブジェクトの回転ですが、Transform、EulerAngle、Quaternionのいずれかを用いて表します。
ただし、RealityKitでは直接EulerAngleを指定できませんのでTransformを使うか、Quaternionを用いる必要があります。
こういったピンポイントの処理を調べるのが面倒ですのでStack Overflowで調べた結果がこちらです。

EulerAngles

How to draw a line between two points in SceneKit?

let eulerAngles 
    = simd_float3(Float.pi / 2,
                  acos((to.z-from.z)/distance),
                  atan2((to.y-from.y),(to.x-from.x)))

SceneKitでは上記のEulerAngleの計算でうまく動作しました。
なお、RealityKitでは、ここでのEulerAngleの結果をTransformで初期化してもうまく動作しませんでした。

Transfrom(pitch: Float, yaw: Float, roll: Float)
Creates a new transform from the specified Euler angles.

Quaternion

Draw SceneKit object between two points

ここでは2017年3月のTransformの手法が良い回答となっていますが、良い回答からしばらく経った2019年の7月の誰も支持してないQuaternionの回答が良かったのでそれの引用になります。
勿論Transformの手法でも動作することを確認しましたが好みでなかったので割愛です。

手法ですが、長さを求めたうえで内積(cross)と外積(dot)を求めます。

let vector = to - from
let height = simd_length(vector)
let line_axis = simd_float3(0, height/2, 0)

let vector_cross = simd_cross(line_axis, vector)
let qw = simd_length(line_axis) * simd_length(vector) + simd_dot(line_axis, vector)
let q = simd_quatf(ix: vector_cross.x, iy: vector_cross.y,
                   iz: vector_cross.z, r: qw).normalized

平行ベクトルにおける問題

Finding quaternion representing the rotation from one vector to another

Quaternion q;
vector a = crossproduct(v1, v2);
q.xyz = a;
q.w = sqrt((v1.Length ^ 2) * (v2.Length ^ 2)) + dotproduct(v1, v2);

上のコードですと平行ベクトルの場合にハングするようですが、先ほどの線の長さを求める書き方だと一応問題なさそうです。
気づいた方いましたらお知らせください。

正しく制御したコードはEigenのFromTwoVectorsを参照するのが良さそうです。

Swift Playground

こちらにコードをあげました。
https://gist.github.com/otmb/9ab1c43774811ad8c5ff697f3538e762

出力すると球と球の間を繋ぐオブジェクトが配置でき、うまく動作しているようです。