[OpenMaya] :: MFnNurbsCurve.create

Alright, so this one is also lots of fun. We are going to create a NurbsCurve using OpenMaya, with a leading degree of 2 (Quadratic). Remember in the previous post about how I calculated the vectors between the two locator positions? Well this time, we are going to do the same, but creating nurbsCurve. This is because each CV needs a position vector array:.

def get_point_array(points_array, equal_distance=False):
    """
    calculate the positional array object.

    :param points_array:
    :param equal_distance: <bool> calculate the equal distance of CV's
    :return:
    """
    m_array = OpenMaya.MPointArray()
    if equal_distance:
        array_length = len(points_array)
        for idx, point in enumerate(points_array):
            if idx == 0:
                m_array.append(OpenMaya.MPoint(*point))
                m_array.append(OpenMaya.MPoint(*point))
            elif idx >= 1 and idx != array_length - 1:
                prev_p, cur_p, next_p = list_scanner(points_array, idx)
                cur_v = math_utils.Vector(*cur_p)
                prev_v = math_utils.Vector(*prev_p)
                new_vec = math_utils.Vector(cur_v - prev_v)
                new_vec = math_utils.Vector(new_vec * 0.5)
                new_vec = math_utils.Vector(prev_v + new_vec)
                m_array.append(OpenMaya.MPoint(*new_vec.position))
            elif idx == array_length - 1:
                prev_p, cur_p, next_p = list_scanner(points_array, idx)
                prev_v = math_utils.Vector(*prev_p)
                next_v = math_utils.Vector(*next_p)
                new_vec = math_utils.Vector(next_v - prev_v)
                new_vec = math_utils.Vector(new_vec * 0.5)
                new_vec = math_utils.Vector(prev_v + new_vec)
                # add two points in the same spot
                m_array.append(OpenMaya.MPoint(*new_vec.position))
                m_array.append(OpenMaya.MPoint(*point))
    else:
        for idx, point in enumerate(points_array):
            if idx == 1:
                prev_p, cur_p, next_p = list_scanner(points_array, idx)
                cur_v = math_utils.Vector(*cur_p)
                prev_v = math_utils.Vector(*prev_p)
                new_vec = math_utils.Vector(cur_v - prev_v)
                new_vec = math_utils.Vector(new_vec * 0.5)
                new_vec = math_utils.Vector(prev_v + new_vec)
                m_array.append(OpenMaya.MPoint(*new_vec.position))
            elif idx == len(points_array) - 1:
                prev_p, cur_p, next_p = list_scanner(points_array, idx)
                prev_v = math_utils.Vector(*prev_p)
                next_v = math_utils.Vector(*next_p)
                new_vec = math_utils.Vector(next_v - prev_v)
                new_vec = math_utils.Vector(new_vec * 0.5)
                new_vec = math_utils.Vector(prev_v + new_vec)
                m_array.append(OpenMaya.MPoint(*new_vec.position))
            m_array.append(OpenMaya.MPoint(*point))
    return m_array

So above is just a point array collector that recalculates positions from an existing array of positions: Like selected locators or joints. Preferably at world-space co-ordinates. We then take these recalculated positional array into the OpenMaya.MFnNurbsCurve.create function. I wrote this create_curve_from_points function below that uses this:

def create_curve_from_points(points_array, degree=2, curve_name="", equal_cv_positions=False):
    """
    create a nurbs curve from points.
    :param points_array: <tuple> positional points array.
    :param degree: <int> curve degree.
    :param curve_name: <str> the name of the curve to create.
    :param equal_cv_positions: <bool> if True create CV's at equal positions.
    :return: <str> maya curve name.
    """
    knot_length = len(points_array)
    knot_array = get_knot_sequence(knot_length, degree)
    m_point_array = get_point_array(points_array, equal_distance=equal_cv_positions)

    # curve_data = OpenMaya.MFnNurbsCurveData().create()
    curve_fn = OpenMaya.MFnNurbsCurve()
    curve_fn.create(m_point_array, knot_array, degree,
                    OpenMaya.MFnNurbsCurve.kOpen,
                    False, False)
    m_path = OpenMaya.MDagPath()
    curve_fn.getPath(m_path)

    if curve_name:
        parent_obj = object_utils.get_parent_obj(m_path.partialPathName())[0]
        object_utils.rename_node(parent_obj, curve_name)
        return curve_name
    return curve_fn.name()

In the function above, there is a boolean parameter: equal_cv_positions. The default is False. The result of this is creating CV’s at their locator’s positions, like so:

And if the equal_cv_positions is set to True, this is the result:

As you can see, this utility tool is going to become immediately useful. You could already guess at plans use this already!