5
$\begingroup$

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

$\endgroup$
5
$\begingroup$

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";
| improve this answer | |
$\endgroup$
  • $\begingroup$ 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]]]. $\endgroup$ – J. M.'s discontentment Nov 28 '19 at 22:53
  • $\begingroup$ Would be nice to extend this for BSplineSurface too. $\endgroup$ – user21 Jan 20 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.