# Voxel Point Cloud Transformation (Rotation & Translation)

I have a method that transforms a point cloud and aligns it with its gameObject.transform. My method works well, but i have to rotate the cloud, and then I have to use an approach (taking from 2d bitmap rotation) where i rotate the destination grid in inverse and check points in the source cloud, in order to get a good quality rendering. If I just do one or the other, I end up with holes. So my question is: Is there a cleaner simpler way to get a good result?

private void AlignPointCloudToTransform(PointCloudTransform pCT, ref Dictionary<Vector3Int, Color> alignedCloud)
{
// FORWARDS - rotating and translating cloud to destination space
Quaternion localRot = Quaternion.Inverse(objectRootT.transform.rotation) * pCT.transform.rotation;
Vector3 localPos = objectRootT.InverseTransformPoint(pCT.transform.position);
Matrix4x4 matrix = Matrix4x4.TRS(localPos, localRot, Vector3.one);

Bounds bounds = new Bounds();
bool boundsSet = false;

foreach (var point in sourceCloud)
{
Vector3 transformedPos = matrix.MultiplyPoint3x4(point.Key);
Vector3Int pos = VoxelUtil.RoundVector3(transformedPos);

if (!boundsSet)
{
bounds = new Bounds(pos, Vector3.zero);
boundsSet = true;
}
else bounds.Encapsulate(pos);

alignedCloud[pos] = point.Value;
}

// + INVERSE transforming destination space and checking from there if each point exists
Matrix4x4 invMatrix = matrix.inverse;
Vector3Int dest = Vector3Int.zero;
Vector3Int bMin = VoxelUtil.RoundVector3(bounds.min);
Vector3Int bMax = VoxelUtil.RoundVector3(bounds.max);

for (dest.x = bMin.x; dest.x < bMax.x; dest.x++)
{
for (dest.y = bMin.y; dest.y < bMax.y; dest.y++)
{
for (dest.z = bMin.z; dest.z < bMax.z; dest.z++)
{
Vector3 testPosFloat = invMatrix.MultiplyPoint3x4(dest);
Vector3Int testPos = VoxelUtil.RoundVector3(testPosFloat);

if (sourceCloud.TryGetValue(testPos, out Color color))
{
alignedCloud[dest] = color;
}
}
}
}
}


Edit: I tried a 'Rotate by shearing' approach as suggested by DMGregory, and I do like how it's fairly straight-forward, but it is very sensitive to rotation order and the quality of the output is only ~80%. It may be possible to improve quality by setting a unique rotation order for each object. And it may be more efficient than the other approach, but I haven't tested performance. Here's some of that code:

foreach (var point in sourceCloud)
{
Vector3 transed = point.Key;// + localPos;

// Rotate Z
transed = ShearA(transed, zero, z, x, y, eulers.z); // Z-A
transed = ShearB(transed, zero, z, y, x, eulers.z); // Z-B
transed = ShearA(transed, zero, z, x, y, eulers.z); // Z-C

// Rotate X
transed = ShearA(transed, zero, x, y, z, eulers.x); // X-A
transed = ShearB(transed, zero, x, z, y, eulers.x); // X-B
transed = ShearA(transed, zero, x, y, z, eulers.x); // X-C

// Rotate Y
transed = ShearA(transed, zero, y, z, x, eulers.y); // Y-A
transed = ShearB(transed, zero, y, x, z, eulers.y); // Y-B
transed = ShearA(transed, zero, y, z, x, eulers.y); // Y-C

transed += localPos;

var rounded = VoxelUtil.RoundVector3(transed);

alignedCloud[rounded] = point.Value;
}

• In 2D / when your rotation axis is perpendicular to two grid directions, you can use rotation by shearing as an area-preserving discrete rotation that does not introduce holes. I wonder if there's a way to extend that technique to handle non-axis-aligned rotations in 3D... Jul 14, 2023 at 8:37
• @DMGregory That's an interesting idea. I'm going to explore it. Thanks!
– Josh
Jul 14, 2023 at 19:26
• I suppose in the worst case, any 3D rotation can be decomposed into three sequential axis-aligned rotations using Euler / Tait-Bryan angles, and each of those can be performed using 2D-style rotation by shearing. But each rotation introduces some rounding error, so chaining three of them might accumulate that error into noticeable artifacts. Plus, doing 9 shears might not perform substantially better than the two-pass version you're doing now (though it would at least let you eliminate the hash table overhead and just do a single sequential pass over an array/list instead). Jul 14, 2023 at 19:34
• I thought I might be able to reduce the impact of rotation order by grouping all the first shear steps together, but that broke things. If there is a way to do such a thing in a way that doesn't require 3 shears by 3 axes, I would be curious to see how that works, but the math is way beyond me.
– Josh
Jul 15, 2023 at 7:08