6
$\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$

1 Answer 1

6
$\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";
$\endgroup$
2
  • $\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$ Commented Nov 28, 2019 at 22:53
  • $\begingroup$ Would be nice to extend this for BSplineSurface too. $\endgroup$
    – user21
    Commented Jan 20, 2020 at 10:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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