splines icon indicating copy to clipboard operation
splines copied to clipboard

Problem when interpolate direction vectors with rotation interpolation method

Open sysuyl opened this issue 5 months ago • 3 comments

Hello,

I am tring to interpolate a set of unit direction vectors' endpoints with spline. It means that the spline will interpolate/cross each direction vector's endpoint on a unit sphere surface. The result will look like this picture, orientation

I used the following process,

  • convert the direction vectors to unit quanterions/rotations
  • do rotation interpolation with this library's method(e.g Squad or CatmullRom)
  • and finnaly convert the interpolated samples back to vectors

The problem is that the interpolated result seems not correct with some inputs. Below is my code and test data,

import numpy as np


def interpolate_catmull_rom_centripetal(qs, num_samples, closed=False):
    """
    Interpolate quanterions/roations

      - https://github.com/AudioSceneDescriptionFormat/splines
      - https://splines.readthedocs.io/en/0.3.0/rotation/squad.html#Non-Uniform-Parameterization
    """
    from splines.quaternion import UnitQuaternion, Squad, CatmullRom

    endconditions = "closed" if closed else "natural"

    xyzw_list = [[q[1], q[2], q[3], q[0]] for q in qs]
    rotations = [UnitQuaternion.from_unit_xyzw(xyzw) for xyzw in xyzw_list]

    # note, alpha=0.5, centripetal catmul-rom spline
    spline = CatmullRom(rotations, alpha=0.5, endconditions=endconditions)
    # spline = Squad(rotations)

    ts = np.linspace(spline.grid[0], spline.grid[-1], num_samples, endpoint=False)
    sample_rotations = spline.evaluate(ts)

    sample_xyzw_list = [q.xyzw for q in sample_rotations]
    sample_qs = [[q[3], q[0], q[1], q[2]] for q in sample_xyzw_list]

    return sample_qs


def norm_vector(v):
    v = np.array(v)
    v /= np.linalg.norm(v)
    return v.tolist()

def quanterions_from_direction_vectors(vs):
    vs = [norm_vector(v) for v in vs]
    qs = [[0, *v] for v in vs]
    return qs

def quanterions_to_direction_vectors(qs):
    vs = [q[1:] for q in qs]
    vs = [norm_vector(v) for v in vs]
    return vs

def interpolate_vectors(vectors, sample_num, closed=True):
    """
    Interpolate vectors by converting vectors to quanterions/rotations and use quanterion interpolation.
    """
    # convert vector to quanterion
    qs = quanterions_from_direction_vectors(vectors)

    # interpolate quanterion/rotation
    sample_qs = interpolate_catmull_rom_centripetal(qs, sample_num, closed=closed)

    # convert quanterion back to vector
    sample_vs = quanterions_to_direction_vectors(sample_qs)

    return sample_vs

def interpolate_and_check_if_cross_control_point(vs):
    """
    Interpolate a spline and check if the sample points across control points.
    For each control point, compute the maximum dot value between it and all sample points. And the dot
    should be close to 1.0 if the samples cross it.
    """
    sample_vs = interpolate_vectors(vs, sample_num=1000, closed=True)

    dots = np.array(vs).dot(np.array(sample_vs).T)
    dot_max = np.max(dots, axis=1)

    print("dot max: ", dot_max)


def test():
    # data1, seems ok
    vs = [[-0.82537162, -0.5640741,  -0.02412931],
          [-0.61665761, -0.75438958, -0.22501047],
          [-0.5571503,  -0.8302387,   0.01694991],
          [-0.66785687, -0.71488637,  0.20713463]]
    vs = [norm_vector(v) for v in vs]
    print("data1: ", np.array(vs))
    interpolate_and_check_if_cross_control_point(vs)

    # data2, seems bad
    vs = [[-0.94211662, -0.32190701, -0.09376714],
          [-0.36887884, -0.92903411,  0.0287057 ],
          [ 0.52999741,  0.5104602,  -0.67715067],
          [-0.04777308,  0.8693673,  -0.49185181]]
    vs = [norm_vector(v) for v in vs]
    print("\ndata2: ", np.array(vs))
    interpolate_and_check_if_cross_control_point(vs)


if __name__ == "__main__":
    test()

The output is,

data1:  [[-0.82537157 -0.56407406 -0.02412931]
 [-0.61665762 -0.7543896  -0.22501047]
 [-0.55715028 -0.83023868  0.01694991]
 [-0.66785684 -0.71488634  0.20713462]]
dot max:  [1.         0.99999995 0.99999992 1.        ]

data2:  [[-0.94211656 -0.32190699 -0.09376713]
 [-0.36887884 -0.92903411  0.0287057 ]
 [ 0.52999744  0.51046023 -0.6771507 ]
 [-0.04777308  0.86936731 -0.49185182]]
dot max:  [ 1.          1.         -0.59510805 -0.18848043]

When I plot the original direction vectors and sampled vectors, it shows that the problem more clearly, test-data1, ok test-data2, problem

Besides, I'm not that confident about the direction vector and quanterion conversion, and maybe also use this library's interpolation in wrong way. But it worked for part of test data, and I also tested other implmentation of Squad method and interpolated correctly when this library fails.

sysuyl avatar Sep 13 '24 09:09 sysuyl