6

Consider the following object:

spline = BSplineCurve[{{0,1}, {1,1}, {0,1}, {-1,1}, {-1,0}}, SplineClosed->True];
Graphics[spline]

enter image description here

What would be an equivalent BSplineCurve object where an explicit list of knots and weights are given instead?

Similarly, for the following object:

spline2 = BSplineCurve[{{0, 1}, {1, 1}, {0, 1}, {-1, 1}, {-1, 0}}, SplineKnots -> {0, 0, 0, 1, 1, 2}, SplineClosed -> True]

although this example is not as important, as the rendering appears to have issues:

enter image description here

Carl Woll
  • 130,679
  • 6
  • 243
  • 355

1 Answers1

6

At the end of this post I define a function to do this. It works for all combinations of option settings except where SplineClosed->True and SplineKnots->list, since I don't know how to reproduce the built-in option settings in that case. Example:

spline = BSplineCurve[{{1,0}, {1,1}, {0,1}, {-1,1}}, SplineClosed->True];
FullBSplineCurve @ spline

BSplineCurve[{{1, 0}, {1, 1}, {0, 1}, {-1, 1}, {1, 0}, {1, 1}, {0, 1}}, SplineDegree -> 3, SplineClosed -> False, SplineKnots -> {0, 1/10, 1/5, 3/10, 2/5, 1/2, 3/5, 7/10, 4/5, 9/10, 1}, SplineWeights -> {1, 1, 1, 1, 1, 1, 1}]

Check:

Graphics[{
    Opacity[.5],
    Red, spline,
    Blue, FullBSplineCurve @ spline
}]

enter image description here

Notice how the color is purple, indicating that the original spline and the FullBSplineCurve version overlap.

Here is the code:

FullBSplineCurve[b:BSplineCurve[pts_, OptionsPattern[]]] := Module[{p, k, w, d, c},
    {k, w, d, c} = OptionValue[BSplineCurve, {SplineKnots, SplineWeights, SplineDegree, SplineClosed}];
    If[ListQ @ k && c === True,
        Message[FullBSplineCurve::unsup];
        Return[b]
    ];

    d = If[ListQ @ k,
        Length[k] - Length[pts] - 1,
        Min[Length[pts] - 1, Replace[d, Except[_Integer?Positive] -> 3]]
    ];
    w = Replace[w, Except[_List] -> ConstantArray[1, Length[pts]]];

    If[TrueQ @ c,
        Switch[k,
            "Unclamped" | Automatic, 
            k = Subdivide[0, 1, Length[pts] + 2d];
            p = Join[pts, pts[[;;d]]];
            w = Join[w, w[[;;d]]],

            "Clamped", 
            k = ArrayPad[Subdivide[0, 1, Length[pts]-d+1], d, "Fixed"];
            p = Append[pts, First @ pts];
            w = Append[w, First @ w]
        ],
        p = pts;
        Switch[k,
            "Clamped" | Automatic,
            k = ArrayPad[Subdivide[0, 1, Length[pts]-d], d, "Fixed"],

            "Unclamped",
            k = Subdivide[0, 1, Length[pts] + d]
        ]
    ];
    BSplineCurve[p, SplineDegree->d, SplineClosed->False, SplineKnots->k, SplineWeights->w]
]

FullBSplineCurve::unsup = "Closed curves with a list knot specification are not supported";
Carl Woll
  • 130,679
  • 6
  • 243
  • 355
  • This looks neat! Perhaps the only change I can suggest would be to use ArrayPad[] for some of these expressions, e.g. ArrayPad[Subdivide[0, 1, Length[pts] - d], d, "Fixed"] instead of Join[ConstantArray[0, d], Subdivide[0, 1, Length[pts] - d], ConstantArray[1, d]]. Similarly, ArrayPad[w, {0, d}, "Periodic"] could be used instead of Join[w, w[[;; d]]]. – J. M.'s missing motivation Nov 28 '19 at 22:53
  • Would be nice to extend this for BSplineSurface too. – user21 Jan 20 '20 at 10:35