Code:
%% http://stackoverflow.com/questions/7054272/how-to-draw-smooth-curve-through-n-points-using-javascript-html5-canvas
%% TEST : cardinal_spline([0.0,0.0, 1.0,0.0, 1.0,1.0, 0.0,0.0], 12)
cardinal_spline(PTS0, PolyKind, Segments)
when PolyKind == polygon orelse PolyKind == polyline ->
Len0 = length(PTS0),
First = lists:nth(1,PTS0),
Second = lists:nth(2,PTS0),
Last = lists:last(PTS0),
SecondToLast = lists:nth(Len0-1,PTS0),
if (PolyKind == polyline) ->
PTS = [First] ++ PTS0 ++ [Last]; %% First and last should really be displaced a little ?
true ->
PTS = [SecondToLast,Last] ++ PTS0 ++ [First,Second]
end,
Tension = 0.7,
Len = length(PTS),
_Pts = fun(I0) ->
I = I0 + Len,
lists:nth((I rem Len) + 1,PTS)
end,
Fun = fun
(I, Acc) ->
lists:foldl(fun(T, Acc1) ->
%% calc tension vectors
{T1X,T1Y,T1Z} = e3d_vec:mul(e3d_vec:sub(_Pts(I+1),_Pts(I-1)), Tension),
{T2X,T2Y,T2Z} = e3d_vec:mul(e3d_vec:sub(_Pts(I+2),_Pts(I)), Tension),
%% calc step
ST = 1.0 * T / Segments,
%% calc cardinals
C1 = 2.0 * pow(ST,3.0) - 3.0 * pow(ST,2.0) + 1.0,
C2 = -(2.0 * pow(ST,3.0)) + 3.0 * pow(ST,2.0),
C3 = pow(ST,3.0) - 2.0 * pow(ST,2.0) + ST,
C4 = pow(ST,3.0) - pow(ST,2.0),
{X0,Y0,Z0} = e3d_vec:add( e3d_vec:mul(_Pts(I),C1) , e3d_vec:mul(_Pts(I+1),C2)) ,
{X,Y,Z} =
e3d_vec:add([
{X0,Y0,Z0} ,
e3d_vec:mul({T1X,T1Y,T1Z},C3) ,
e3d_vec:mul({T2X,T2Y,T2Z},C4)
]),
[{X,Y,Z}|Acc1]
end,
Acc, lists:seq(0,Segments-1))
end,
RP1 = lists:foldl(Fun, [], lists:seq(1,length(PTS)-0)),
case PolyKind of
polyline -> _RichPath = lists:sublist(RP1,3*Segments,length(RP1)-3*Segments+1);
polygon -> _RichPath = lists:sublist(RP1,3*Segments,length(RP1)-4*Segments)
end.