SOLVED "Cubic Bias" interpolation...

I had a question on Twitter relating C4D Gradients.
On the Github examples:
https://github.com/PluginCafe/cinema4d_cpp_sdk_extended
... we have the old, R20 interpolations but is missing "Cubic Bias".

case 0: return g1; break;
case 1: return g1 + (g2 - g1) * per; break;
case 2: return g1 + (g2 - g1) * Ln(per * 1.7182819 + 1.0); break;
case 3: return g1 + (g2 - g1) * (1.0 - Ln((1.0 - per) * 1.7182819 + 1.0)); break;
case 4: return g1 + (g2 - g1) * Smoothstep(0.0_f, 1.0_f, per); break;

Does anyone know what it is?
Thanks

Hello @noseman,

Thank you for reaching out to us. As declared in our Forum Guidelines, we do not share code of Cinema 4D publicly. So, we cannot disclose what the Cubic Bias interpolation is doing exactly. This also applies to the interpolation modes you quoted, while they are technically close, they are not exactly what the gradient type is doing internally.

Cubic means in this context cubic spline interpolation. We cannot disclose which ones are being used exactly, but Cubic and Cubic Bias use entirely different ones. For Cubic Bias two of the four control points of the polynomial are being constructed with the help of the name-giving bias value. Find an approximation in Python of the what the mode does at the end of the posting.

Cheers,
Ferdinand

The result:
fbd17333-4081-4497-9f76-1e74ec4df652-image.png

The code:

"""Demonstrates a Cubic Spline interpolation where the two tangent points are being placed
dependent on a bias value.
"""

import c4d

def GetCubicInterpolation(a: c4d.Vector, b: c4d.Vector, bias: float, t: float) -> c4d.Vector:
    """Returns a Cubic Spline interpolation between #a and #b at #t where the tangent points #t0
    and #t1 are placed with the help of a #bias value.

    All values must lie in the interval [0, 1].

    Args:
        a: The first control point.
        b: The second control point.
        bias: The bias value which determines the placement of the two tangent points.
        t: The offset at which to interpolate the spline.

    Returns:
        The interpolated point.
    """
    # These values are relatively close to what the gradient data type does internally.
    t0: c4d.Vector = c4d.utils.MixVec(a, b, c4d.utils.MixNum(0., 1. - bias, 0.75))
    t1: c4d.Vector = c4d.utils.MixVec(a, b, c4d.utils.MixNum(1. - bias, 1., 0.25))

    # This is not, while CalcSplineV also provides an interpolation with a cubic polynomial, it is 
    # not the one used by the gradient data type. But it will be visually close.
    return c4d.utils.CalcSplineV(t, [a, t0, t1, b])

def main() -> None:
    """
    """
    # Init a canvas we are going to draw into.
    canvas: c4d.bitmaps.GeClipMap = c4d.bitmaps.GeClipMap()
    canvas.Init(1000, 302, 32)
    
    # The two colors/knots we are going to interpolate.
    colorA: c4d.Vector = c4d.Vector(1, 0, 0)
    colorB: c4d.Vector = c4d.Vector(0, 0, 1)

    # We are going to draw three vertically separated sections with a bias of .25, .5, and .75
    canvas.BeginDraw()
    for y0, y1, bias in ((0, 100, 0.25), (101, 201, 0.5), (202, 302, 0.75)):
        # Draw a section.
        for i in range(1000):
            # Get the interpolation at #t in [0, 1]
            col: c4d.Vector = GetCubicInterpolation(colorA, colorB, bias, float(i) * 0.001)

            # The function returns a value in R[0, 1] but GeClipMap operates in Z[0, 255]
            canvas.SetColor(int(col.x * 255), int(col.y * 255), int(col.z * 255), 0)
            canvas.Line(i, y0, i, y1)

        # Draw the bias marker.
        canvas.SetColor(25, 25, 25, 0)
        canvas.FillRect(int(1000 * bias - 2), y0 + 10, int(1000 * bias + 2), y1 - 10)
    canvas.EndDraw()

    # Display the drawing.
    bmp = canvas.GetBitmap()
    c4d.bitmaps.ShowBitmap(bmp)


if __name__ == '__main__':
    main()

Hello @noseman,

Thank you for reaching out to us. As declared in our Forum Guidelines, we do not share code of Cinema 4D publicly. So, we cannot disclose what the Cubic Bias interpolation is doing exactly. This also applies to the interpolation modes you quoted, while they are technically close, they are not exactly what the gradient type is doing internally.

Cubic means in this context cubic spline interpolation. We cannot disclose which ones are being used exactly, but Cubic and Cubic Bias use entirely different ones. For Cubic Bias two of the four control points of the polynomial are being constructed with the help of the name-giving bias value. Find an approximation in Python of the what the mode does at the end of the posting.

Cheers,
Ferdinand

The result:
fbd17333-4081-4497-9f76-1e74ec4df652-image.png

The code:

"""Demonstrates a Cubic Spline interpolation where the two tangent points are being placed
dependent on a bias value.
"""

import c4d

def GetCubicInterpolation(a: c4d.Vector, b: c4d.Vector, bias: float, t: float) -> c4d.Vector:
    """Returns a Cubic Spline interpolation between #a and #b at #t where the tangent points #t0
    and #t1 are placed with the help of a #bias value.

    All values must lie in the interval [0, 1].

    Args:
        a: The first control point.
        b: The second control point.
        bias: The bias value which determines the placement of the two tangent points.
        t: The offset at which to interpolate the spline.

    Returns:
        The interpolated point.
    """
    # These values are relatively close to what the gradient data type does internally.
    t0: c4d.Vector = c4d.utils.MixVec(a, b, c4d.utils.MixNum(0., 1. - bias, 0.75))
    t1: c4d.Vector = c4d.utils.MixVec(a, b, c4d.utils.MixNum(1. - bias, 1., 0.25))

    # This is not, while CalcSplineV also provides an interpolation with a cubic polynomial, it is 
    # not the one used by the gradient data type. But it will be visually close.
    return c4d.utils.CalcSplineV(t, [a, t0, t1, b])

def main() -> None:
    """
    """
    # Init a canvas we are going to draw into.
    canvas: c4d.bitmaps.GeClipMap = c4d.bitmaps.GeClipMap()
    canvas.Init(1000, 302, 32)
    
    # The two colors/knots we are going to interpolate.
    colorA: c4d.Vector = c4d.Vector(1, 0, 0)
    colorB: c4d.Vector = c4d.Vector(0, 0, 1)

    # We are going to draw three vertically separated sections with a bias of .25, .5, and .75
    canvas.BeginDraw()
    for y0, y1, bias in ((0, 100, 0.25), (101, 201, 0.5), (202, 302, 0.75)):
        # Draw a section.
        for i in range(1000):
            # Get the interpolation at #t in [0, 1]
            col: c4d.Vector = GetCubicInterpolation(colorA, colorB, bias, float(i) * 0.001)

            # The function returns a value in R[0, 1] but GeClipMap operates in Z[0, 255]
            canvas.SetColor(int(col.x * 255), int(col.y * 255), int(col.z * 255), 0)
            canvas.Line(i, y0, i, y1)

        # Draw the bias marker.
        canvas.SetColor(25, 25, 25, 0)
        canvas.FillRect(int(1000 * bias - 2), y0 + 10, int(1000 * bias + 2), y1 - 10)
    canvas.EndDraw()

    # Display the drawing.
    bmp = canvas.GetBitmap()
    c4d.bitmaps.ShowBitmap(bmp)


if __name__ == '__main__':
    main()

@ferdinand

...This also applies to the interpolation modes you quoted, while they are technically close, they are not exactly what the gradient type is doing internally.

haha, of course.
Would it make sense to add this to the github examples at some point?

Thank you @ferdinand

Hello @noseman,

well, the problem is that we do not really have a good place for it at the moment (a subject we could list it under). And it is also technically out of our collective domain, i.e., the things we consider putting into our documentation. We only collect things which are directly related to out APIs and this is a generic computer graphics example. Note that you found your example code in the context of the GeUserArea example, so this was only code which sort of was provided "en passant" while explaining something entirely different: How the type GeUserArea works. Which is rather something we like to reduce than increase: Overloading examples with random complexity.

I made a note that there is some interest in having an example for spline interpolation, e.g., the provided one. I cannot promise more for now.

Cheers,
Ferdinand

Thanks Ferdinand!