I've used the method I'm about to show in this answer, but I suppose having it explicitly answer an interpolation question would be convenient.
Starting with your points,
testData = {{10, 10}, {10, 20}, {10, 25}, {10, 27}, {10, 28}, {9, 26},
{8, 25}, {5, 20}, {3, 1}};
we use Lee's centripetal parametrization scheme to generate corresponding parameter values:
parametrizeCurve[pts_ /; MatrixQ[pts, NumericQ], a : (_?NumericQ) : 1/2] :=
FoldList[Plus, 0, Normalize[(Norm /@ Differences[pts])^a, Total]]
tvals = parametrizeCurve[testData];
We then generate control points for the B-spline from the interpolation points. To do that, we use a procedure suggested by Piegl and Tiller (see The NURBS Book by Piegl and Tiller if you want more details):
m = 3; (* degree of the B-spline *)
(* knots for interpolating B-spline *)
knots = Join[ConstantArray[0, m + 1], MovingAverage[ArrayPad[tvals, -1], m],
ConstantArray[1, m + 1]];
(* basis function matrix *)
bas = Table[BSplineBasis[{m, knots}, j - 1, tvals[[i]]] // N,
{i, Length[testData]}, {j, Length[testData]}];
ctrlpts = LinearSolve[bas, testData];
Now, we can see the B-spline in two different ways:
{Graphics[{{ColorData[1, 1], BSplineCurve[ctrlpts, SplineDegree -> m,
SplineKnots -> knots]},
{Directive[Green, AbsolutePointSize[6]], Point[testData]}}, Frame -> True],
ParametricPlot[BSplineFunction[ctrlpts, SplineDegree -> m, SplineKnots -> knots][t]
// Evaluate, {t, 0, 1}, Axes -> None,
Epilog -> {Directive[Green, AbsolutePointSize[6]], Point[testData]},
Frame -> True]} // GraphicsRow
It can be observed that parametrizeCurve[]
takes a second argument; this controls the type of parametrization used for the points. The default setting of $1/2$ generates a centripetal parametrization, as previously mentioned. Setting that parameter to $1$ will yield a chord-length parametrization, and setting it to $0$ yields a uniform parametrization. This parameter can take values in $[0,1]$, and one can adjust it as needed for the application at hand.