@@ -28,7 +28,7 @@ namespace alfi::spline {
2828 */
2929 struct NotAKnotEnd final {};
3030 /* *
31- The arithmetic mean_array between NotAKnotStart and NotAKnotEnd.
31+ The arithmetic mean of NotAKnotStart and NotAKnotEnd.
3232 */
3333 struct SemiNotAKnot final {};
3434 /* *
@@ -40,13 +40,80 @@ namespace alfi::spline {
4040 */
4141 struct NaturalEnd final {};
4242 /* *
43- The arithmetic mean_array between NaturalStart and NaturalEnd.
43+ The arithmetic mean of NaturalStart and NaturalEnd.
4444 */
4545 struct SemiNatural final {};
4646 /* *
47- The arithmetic mean_array between SemiNotAKnot and SemiNatural.
47+ The arithmetic mean of SemiNotAKnot and SemiNatural.
4848 */
4949 struct SemiSemi final {};
50+ /* *
51+ The first derivative equals `df` at the first point.
52+ */
53+ struct ClampedStart final {
54+ explicit ClampedStart (Number df) : df(df) {}
55+ Number df;
56+ };
57+ /* *
58+ The first derivative equals `df` at the last point.
59+ */
60+ struct ClampedEnd final {
61+ explicit ClampedEnd (Number df) : df(df) {}
62+ Number df;
63+ };
64+ /* *
65+ The arithmetic mean of ClampedStart and ClampedEnd.
66+ */
67+ struct SemiClamped final {
68+ SemiClamped (Number df_1, Number df_n) : df_1(df_1), df_n(df_n) {}
69+ Number df_1, df_n;
70+ };
71+ /* *
72+ The second derivative equals `d2f` on the first segment.
73+ */
74+ struct FixedSecondStart final {
75+ explicit FixedSecondStart (Number d2f) : d2f(d2f) {}
76+ Number d2f;
77+ };
78+ /* *
79+ The second derivative equals `d2f` on the last segment.
80+ */
81+ struct FixedSecondEnd final {
82+ explicit FixedSecondEnd (Number d2f) : d2f(d2f) {}
83+ Number d2f;
84+ };
85+ /* *
86+ The arithmetic mean of FixedSecondStart and FixedSecondEnd.
87+ */
88+ struct SemiFixedSecond final {
89+ SemiFixedSecond (Number d2f_1, Number d2f_n) : d2f_1(d2f_1), d2f_n(d2f_n) {}
90+ Number d2f_1, d2f_n;
91+ };
92+ /* *
93+ The first derivative equals `df` at the point with index `point_idx`.
94+ */
95+ struct Clamped final {
96+ Clamped (SizeT point_idx, Number df) : point_idx(point_idx), df(df) {}
97+ SizeT point_idx;
98+ Number df;
99+ };
100+ /* *
101+ The second derivative equals `d2f` on the segment with index `segment_idx`.
102+ */
103+ struct FixedSecond final {
104+ FixedSecond (SizeT segment_idx, Number d2f) : segment_idx(segment_idx), d2f(d2f) {}
105+ SizeT segment_idx;
106+ Number d2f;
107+ };
108+ /* *
109+ The second derivative is continuous at the point with index `point_idx`.\n
110+ This also means, that the second derivative is the same on the segments with indices `point_idx-1` and `point_idx`.\n
111+ If number of points is less than or equal to 2, then `point_idx` is ignored.
112+ */
113+ struct NotAKnot final {
114+ explicit NotAKnot (SizeT point_idx) : point_idx(point_idx) {}
115+ SizeT point_idx;
116+ };
50117 using Default = SemiNotAKnot;
51118 };
52119
@@ -56,7 +123,16 @@ namespace alfi::spline {
56123 typename Types::NaturalStart,
57124 typename Types::NaturalEnd,
58125 typename Types::SemiNatural,
59- typename Types::SemiSemi>;
126+ typename Types::SemiSemi,
127+ typename Types::ClampedStart,
128+ typename Types::ClampedEnd,
129+ typename Types::SemiClamped,
130+ typename Types::FixedSecondStart,
131+ typename Types::FixedSecondEnd,
132+ typename Types::SemiFixedSecond,
133+ typename Types::Clamped,
134+ typename Types::FixedSecond,
135+ typename Types::NotAKnot>;
60136
61137 static Container<Number> compute_coeffs (const auto & X, const auto & Y, Type type = typename Types::Default{}) {
62138 if (X.size () != Y.size ()) {
@@ -73,97 +149,115 @@ namespace alfi::spline {
73149 return util::spline::simple_spline<Number,Container>(X, Y, 2 );
74150 }
75151
152+ if (std::holds_alternative<typename Types::NotAKnotStart>(type)) {
153+ return compute_coeffs (X, Y, typename Types::NotAKnot (1 ));
154+ } else if (std::holds_alternative<typename Types::NotAKnotEnd>(type)) {
155+ return compute_coeffs (X, Y, typename Types::NotAKnot (n - 2 ));
156+ } else if (std::holds_alternative<typename Types::SemiNotAKnot>(type)) {
157+ return util::arrays::mean (compute_coeffs (X, Y, typename Types::NotAKnot{1 }), compute_coeffs (X, Y, typename Types::NotAKnot{n - 2 }));
158+ } else if (std::holds_alternative<typename Types::NaturalStart>(type)) {
159+ return compute_coeffs (X, Y, typename Types::FixedSecond (0 , 0 ));
160+ } else if (std::holds_alternative<typename Types::NaturalEnd>(type)) {
161+ return compute_coeffs (X, Y, typename Types::FixedSecond (n - 2 , 0 ));
162+ } else if (std::holds_alternative<typename Types::SemiNatural>(type)) {
163+ return util::arrays::mean (compute_coeffs (X, Y, typename Types::NaturalStart{}), compute_coeffs (X, Y, typename Types::NaturalEnd{}));
164+ } else if (std::holds_alternative<typename Types::SemiSemi>(type)) {
165+ return util::arrays::mean (compute_coeffs (X, Y, typename Types::SemiNotAKnot{}), compute_coeffs (X, Y, typename Types::SemiNatural{}));
166+ } else if (const auto * cs = std::get_if<typename Types::ClampedStart>(&type)) {
167+ return compute_coeffs (X, Y, typename Types::Clamped (0 , cs->df ));
168+ } else if (const auto * ce = std::get_if<typename Types::ClampedEnd>(&type)) {
169+ return compute_coeffs (X, Y, typename Types::Clamped (n - 1 , ce->df ));
170+ } else if (const auto * sc = std::get_if<typename Types::SemiClamped>(&type)) {
171+ return util::arrays::mean (compute_coeffs (X, Y, typename Types::Clamped (0 , sc->df_1 )), compute_coeffs (X, Y, typename Types::Clamped (n - 1 , sc->df_n )));
172+ } else if (const auto * fss = std::get_if<typename Types::FixedSecondStart>(&type)) {
173+ return compute_coeffs (X, Y, typename Types::FixedSecond (0 , fss->d2f ));
174+ } else if (const auto * fse = std::get_if<typename Types::FixedSecondEnd>(&type)) {
175+ return compute_coeffs (X, Y, typename Types::FixedSecond (n - 2 , fse->d2f ));
176+ } else if (const auto * sfs = std::get_if<typename Types::SemiFixedSecond>(&type)) {
177+ return util::arrays::mean (compute_coeffs (X, Y, typename Types::FixedSecond (0 , sfs->d2f_1 )), compute_coeffs (X, Y, typename Types::FixedSecond (n - 2 , sfs->d2f_n )));
178+ }
179+
76180 Container<Number> coeffs (3 * (n - 1 ));
77181
78- std::visit (util::misc::overload{
79- [&](const typename Types::NotAKnotStart&) {
80- if (n <= 3 ) {
81- coeffs = util::spline::simple_spline<Number,Container>(X, Y, 2 );
82- return ;
83- }
84- { // first three points
85- const Number h1 = X[1 ] - X[0 ], h2 = X[2 ] - X[1 ];
86- const Number d1 = (Y[1 ] - Y[0 ]) / h1, d2 = (Y[2 ] - Y[1 ]) / h2;
87- coeffs[0 ] = (d2 - d1) / (h1 + h2); // a1
88- coeffs[1 ] = d1 - h1 * coeffs[0 ]; // b1
89- coeffs[2 ] = Y[0 ]; // c1
90- coeffs[3 ] = coeffs[0 ]; // a2
91- coeffs[4 ] = d1 + h1 * coeffs[0 ]; // b2
92- coeffs[5 ] = Y[1 ]; // c2
93- }
94- for (SizeT i = 2 ; i < n - 1 ; ++i) {
95- const Number hi = X[i+1 ] - X[i], hi_1 = X[i] - X[i-1 ];
96- const Number d = 2 * coeffs[3 *(i-1 )] * hi_1 + coeffs[3 *(i-1 )+1 ];
97- coeffs[3 *i] = ((Y[i+1 ] - Y[i])/hi - d) / hi;
98- coeffs[3 *i+1 ] = d;
99- coeffs[3 *i+2 ] = Y[i];
100- }
101- },
102- [&](const typename Types::NotAKnotEnd&) {
103- if (n <= 3 ) {
104- coeffs = util::spline::simple_spline<Number,Container>(X, Y, 2 );
105- return ;
106- }
107- { // last three points
108- const Number hn_2 = X[n-2 ] - X[n-3 ], hn_1 = X[n-1 ] - X[n-2 ];
109- const Number dn_2 = (Y[n-2 ] - Y[n-3 ]) / hn_2, dn_1 = (Y[n-1 ] - Y[n-2 ]) / hn_1;
110- coeffs[3 *(n-3 )] = (dn_1 - dn_2) / (hn_2 + hn_1); // an-2
111- coeffs[3 *(n-3 )+1 ] = dn_2 - hn_2 * coeffs[3 *(n-3 )]; // bn-2
112- coeffs[3 *(n-3 )+2 ] = Y[n-3 ]; // cn-2
113- coeffs[3 *(n-2 )] = coeffs[3 *(n-3 )]; // an-1
114- coeffs[3 *(n-2 )+1 ] = dn_2 + hn_2 * coeffs[3 *(n-3 )]; // bn-1
115- coeffs[3 *(n-2 )+2 ] = Y[n-2 ]; // cn-1
116- }
117- for (SizeT iter = 0 ; iter <= n - 4 ; ++iter) {
118- const SizeT i = n - 4 - iter;
119- const Number hi = X[i+1 ] - X[i];
120- coeffs[3 *i] = (coeffs[3 *(i+1 )+1 ] - (Y[i+1 ] - Y[i])/hi) / hi;
121- coeffs[3 *i+1 ] = coeffs[3 *(i+1 )+1 ] - 2 *coeffs[3 *i]*hi;
122- // another way: coeffs[3*i+1] = (Y[i+1] - Y[i])/hi - coeffs[3*i]*hi;
123- coeffs[3 *i+2 ] = Y[i];
124- }
125- },
126- [&](const typename Types::SemiNotAKnot&) {
127- coeffs = util::arrays::mean (compute_coeffs (X, Y, typename Types::NotAKnotStart{}), compute_coeffs (X, Y, typename Types::NotAKnotEnd{}));
128- },
129- [&](const typename Types::NaturalStart&) {
130- if (n <= 2 ) {
131- coeffs = util::spline::simple_spline<Number,Container>(X, Y, 2 );
132- return ;
133- }
134- coeffs[0 ] = 0 ; // a1 = 0
135- coeffs[1 ] = (Y[1 ] - Y[0 ]) / (X[1 ] - X[0 ]); // b1
136- coeffs[2 ] = Y[0 ]; // c1
137- for (SizeT i = 1 ; i < n - 1 ; ++i) {
138- coeffs[3 *i+1 ] = 2 * (Y[i] - Y[i-1 ]) / (X[i] - X[i-1 ]) - coeffs[3 *(i-1 )+1 ];
139- coeffs[3 *i] = ((Y[i+1 ] - Y[i]) / (X[i+1 ] - X[i]) - coeffs[3 *i+1 ]) / (X[i+1 ] - X[i]);
140- // another way: coeffs[3*i] = (coeffs[3*(i+1)+1] - coeffs[3*i+1]) / 2 / (X[i+1] - X[i]);
141- coeffs[3 *i+2 ] = Y[i];
142- }
143- },
144- [&](const typename Types::NaturalEnd&) {
145- if (n <= 2 ) {
146- coeffs = util::spline::simple_spline<Number,Container>(X, Y, 2 );
147- return ;
148- }
149- coeffs[3 *(n-2 )] = 0 ; // an-1 = 0
150- coeffs[3 *(n-2 )+1 ] = (Y[n-1 ] - Y[n-2 ]) / (X[n-1 ] - X[n-2 ]); // bn-1
151- coeffs[3 *(n-2 )+2 ] = Y[n-2 ]; // cn-1
152- for (SizeT iter = 0 ; iter <= n - 3 ; ++iter) {
153- const SizeT i = n - 3 - iter;
154- coeffs[3 *i+1 ] = 2 * (Y[i+1 ] - Y[i]) / (X[i+1 ] - X[i]) - coeffs[3 *(i+1 )+1 ];
155- // another way: coeffs[3*i] = ((Y[i+1] - Y[i]) / (X[i+1] - X[i]) - coeffs[3*i+1]) / (X[i+1] - X[i]);
156- coeffs[3 *i] = (coeffs[3 *(i+1 )+1 ] - coeffs[3 *i+1 ]) / 2 / (X[i+1 ] - X[i]);
157- coeffs[3 *i+2 ] = Y[i];
158- }
159- },
160- [&](const typename Types::SemiNatural&) {
161- coeffs = util::arrays::mean (compute_coeffs (X, Y, typename Types::NaturalStart{}), compute_coeffs (X, Y, typename Types::NaturalEnd{}));
162- },
163- [&](const typename Types::SemiSemi&) {
164- coeffs = util::arrays::mean (compute_coeffs (X, Y, typename Types::SemiNotAKnot{}), compute_coeffs (X, Y, typename Types::SemiNatural{}));
165- },
166- }, type);
182+ SizeT i1 = 0 , i2 = n - 1 ;
183+
184+ if (const auto * clamped = std::get_if<typename Types::Clamped>(&type)) {
185+ const auto i = clamped->point_idx ;
186+ if (i < 0 || i >= n) {
187+ std::cerr << " Error in function " << __FUNCTION__
188+ << " : point index for type 'Clamped' is out of bounds:"
189+ << " expected to be non-negative and less than " << n << " , but got " << i
190+ << " . Returning an empty array..." << std::endl;
191+ return {};
192+ }
193+ if (i > 0 ) {
194+ const auto dx1 = X[i] - X[i-1 ], dy1 = Y[i] - Y[i-1 ];
195+ coeffs[3 *(i-1 )+2 ] = Y[i-1 ];
196+ coeffs[3 *(i-1 )+1 ] = 2 *dy1/dx1 - clamped->df ;
197+ coeffs[3 *(i-1 )] = dy1/dx1/dx1 - coeffs[3 *(i-1 )+1 ]/dx1;
198+ i1 = i - 1 ;
199+ }
200+ if (i < n - 1 ) {
201+ const auto dx = X[i+1 ] - X[i], dy = Y[i+1 ] - Y[i];
202+ coeffs[3 *i+2 ] = Y[i];
203+ coeffs[3 *i+1 ] = clamped->df ;
204+ coeffs[3 *i] = dy/dx/dx - coeffs[3 *i+1 ]/dx;
205+ i2 = i;
206+ }
207+ } else if (const auto * fixed_second = std::get_if<typename Types::FixedSecond>(&type)) {
208+ const auto i = fixed_second->segment_idx ;
209+ if (i < 0 || i >= n - 1 ) {
210+ std::cerr << " Error in function " << __FUNCTION__
211+ << " : point index for type 'FixedSecond' is out of bounds:"
212+ << " expected to be non-negative and less than " << n - 1 << " , but got " << i
213+ << " . Returning an empty array..." << std::endl;
214+ return {};
215+ }
216+ coeffs[3 *i] = fixed_second->d2f / 2 ;
217+ coeffs[3 *i+1 ] = (Y[i+1 ]-Y[i])/(X[i+1 ]-X[i]) - coeffs[3 *i]*(X[i+1 ]-X[i]);
218+ coeffs[3 *i+2 ] = Y[i];
219+ i1 = i2 = i;
220+ } else if (const auto * not_a_knot = std::get_if<typename Types::NotAKnot>(&type)) {
221+ if (n <= 2 ) {
222+ return util::spline::simple_spline<Number,Container>(X, Y, 2 );
223+ }
224+ const auto i = not_a_knot->point_idx ;
225+ if (i < 1 || i >= n - 1 ) {
226+ std::cerr << " Error in function " << __FUNCTION__
227+ << " : point index for type 'NotAKnot' is out of bounds:"
228+ << " expected to be positive and less than " << n - 1 << " , but got " << i
229+ << " . Returning an empty array..." << std::endl;
230+ return {};
231+ }
232+ const auto dx1 = X[i] - X[i-1 ], dy1 = Y[i] - Y[i-1 ];
233+ const auto dx = X[i+1 ] - X[i], dy = Y[i+1 ] - Y[i];
234+ coeffs[3 *(i-1 )+2 ] = Y[i-1 ];
235+ coeffs[3 *i+2 ] = Y[i];
236+ coeffs[3 *(i-1 )] = coeffs[3 *i] = (dy/dx - dy1/dx1) / (dx1 + dx);
237+ coeffs[3 *(i-1 )+1 ] = dy1/dx1 - coeffs[3 *(i-1 )]*dx1;
238+ coeffs[3 *i+1 ] = dy/dx - coeffs[3 *i]*dx;
239+ i1 = i - 1 ;
240+ i2 = i;
241+ } else {
242+ std::cerr << " Error in function " << __FUNCTION__ << " : Unknown type. This should not have happened."
243+ << " Please report this to the developers. Returning an empty array..." << std::endl;
244+ return {};
245+ }
246+
247+ for (SizeT iter = 0 ; iter < i1; ++iter) {
248+ const auto i = i1 - 1 - iter;
249+ const auto dx = X[i+1 ] - X[i], dy = Y[i+1 ] - Y[i];
250+ coeffs[3 *i] = coeffs[3 *(i+1 )+1 ]/dx - dy/dx/dx;
251+ coeffs[3 *i+1 ] = 2 *dy/dx - coeffs[3 *(i+1 )+1 ];
252+ coeffs[3 *i+2 ] = Y[i];
253+ }
254+
255+ for (SizeT i = i2 + 1 ; i < n - 1 ; ++i) {
256+ const auto dx = X[i+1 ] - X[i], dy = Y[i+1 ] - Y[i];
257+ coeffs[3 *i+2 ] = Y[i];
258+ coeffs[3 *i+1 ] = coeffs[3 *(i-1 )+1 ] + 2 *coeffs[3 *(i-1 )]*(X[i]-X[i-1 ]);
259+ coeffs[3 *i] = dy/dx/dx - coeffs[3 *i+1 ]/dx;
260+ }
167261
168262 return coeffs;
169263 }
0 commit comments