@@ -29,12 +29,15 @@ import std.meta: AliasSeq, staticMap;
2929import std.traits : Unqual;
3030public import mir.interpolate: atInterval;
3131
32- enum msg_min = " spline interpolant: minimal allowed length for the grid equals 2." ;
33- enum msg_eq = " spline interpolant: X and Y values length should be equal." ;
32+ static immutable msg_min = " spline interpolant: minimal allowed length for the grid equals 2." ;
33+ static immutable msg_eq = " spline interpolant: X and Y values length should be equal." ;
34+ static immutable splineConfigurationMsg = " spline configuration: .boundary method requires equal left and right boundaries" ;
35+
3436version (D_Exceptions)
3537{
3638 static immutable exc_min = new Exception (msg_min);
3739 static immutable exc_eq = new Exception (msg_eq);
40+ static immutable splineConfigurationException = new Exception (splineConfigurationMsg);
3841}
3942
4043private alias ValueType (T, X) = typeof (T.init.opCall (Repeat! (T.dimensionCount, X.init)));
@@ -55,7 +58,7 @@ private alias ValueType(T, X) = typeof(T.init.opCall(Repeat!(T.dimensionCount, X
5558 static immutable ydata = [17.0 , 0 , 16 , 4 , 10 , 15 , 19 , 5 , 18 , 6 ];
5659 auto y = ydata.sliced;
5760
58- auto interpolant = spline! double (x, y); // constructs Spline
61+ auto interpolant = spline! double (x, y, SplineConfiguration ! double () ); // constructs Spline
5962 auto xs = x + 0.5 ; // input X values for cubic spline
6063
6164 static immutable test_data0 = [
@@ -557,6 +560,28 @@ template spline(T, size_t N = 1, X = T)
557560 ret._computeDerivatives(kind, param, lBoundary, rBoundary);
558561 return ret;
559562 }
563+
564+ /+ +
565+ Params:
566+ grid = immutable `x` values for interpolant
567+ values = `f(x)` values for interpolant
568+ configuration = $(LREF SplineConfiguration)
569+ Constraints:
570+ `grid` and `values` must have the same length >= 3
571+ Returns: $(LREF Spline)
572+ +/
573+ Spline! (T, N, X) spline(yIterator, SliceKind ykind)(
574+ Repeat! (N, Slice! (RCI ! (immutable X))) grid,
575+ Slice! (yIterator, N, ykind) values ,
576+ SplineConfiguration! T configuration,
577+ )
578+ {
579+ auto ret = typeof (return )(forward! grid);
580+ ret._values = values ;
581+ with (configuration)
582+ ret._computeDerivatives(kind, param, leftBoundary, rightBoundary);
583+ return ret;
584+ }
560585}
561586
562587/+ +
@@ -567,10 +592,6 @@ See_also: $(LREF SplineBoundaryCondition) $(LREF SplineType)
567592extern (C++ , " mir" , " interpolate" )
568593enum SplineBoundaryType
569594{
570- /+ +
571- Not implemented.
572- +/
573- periodic = - 1 ,
574595 /+ +
575596 Not-a-knot (or cubic) boundary condition.
576597 It is an aggresive boundary condition that is used only for C2 splines and is default for all API calls.
@@ -598,6 +619,10 @@ enum SplineBoundaryType
598619 Default for Akima splines.
599620 +/
600621 akima,
622+ /+ +
623+ Not implemented.
624+ +/
625+ periodic = - 1 ,
601626}
602627
603628/+ +
@@ -609,12 +634,85 @@ extern(C++, "mir", "interpolate")
609634struct SplineBoundaryCondition (T)
610635 if (__traits(isFloating, T))
611636{
637+ import mir.serde: serdeOptional, serdeIgnoreDefault;
638+
612639 // / type (default is $(LREF SplineBoundaryType.notAKnot))
613- SplineBoundaryType type = SplineBoundaryType.notAKnot;
640+ SplineBoundaryType type;
641+
642+ @serdeOptional @serdeIgnoreDefault:
643+
614644 // / value (default is 0)
615645 T value = 0 ;
616646}
617647
648+ // / Spline configuration
649+ struct SplineConfiguration (T)
650+ if (__traits(isFloating, T))
651+ {
652+ import mir.serde: serdeOptional, serdeIgnoreDefault, serdeIgnoreOutIfAggregate, serdeIgnore;
653+
654+ // /
655+ @serdeOptional @serdeIgnoreDefault
656+ SplineType kind;
657+ // /
658+ @serdeOptional @serdeIgnoreOutIfAggregate! " a.symmetric"
659+ SplineBoundaryCondition! T leftBoundary;
660+ // /
661+ @serdeOptional @serdeIgnoreOutIfAggregate! " a.symmetric"
662+ SplineBoundaryCondition! T rightBoundary;
663+
664+ /+ +
665+ Returns:
666+ true of `leftBoundary` equals `rightBoundary`.
667+ +/
668+ @serdeIgnore
669+ bool symmetric () const @property
670+ {
671+ return leftBoundary == rightBoundary;
672+ }
673+
674+ // /
675+ @serdeOptional
676+ void boundary (SplineBoundaryCondition! T boundary) @property
677+ {
678+ leftBoundary = rightBoundary = boundary;
679+ }
680+
681+ // /
682+ @serdeIgnoreOutIfAggregate! " !a.symmetric"
683+ SplineBoundaryCondition! T boundary () const @property
684+ {
685+ assert (! symmetric, splineConfigurationMsg);
686+ return leftBoundary;
687+ }
688+
689+ /+ +
690+ Tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
691+ Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
692+ +/
693+ @serdeOptional @serdeIgnoreDefault
694+ T param = 0 ;
695+ }
696+
697+ // / Spline configuration with two boundaries
698+ struct SplineSymmetricConfiguration (T)
699+ if (__traits(isFloating, T))
700+ {
701+ import mir.serde: serdeOptional, serdeIgnoreDefault;
702+
703+ @serdeOptional @serdeIgnoreDefault:
704+
705+ // /
706+ SplineType type;
707+ // /
708+ SplineBoundaryCondition! T boundary;
709+ /+ +
710+ Tangent power parameter for cardinal $(LREF SplineType) (ignored by other spline types).
711+ Use `1` for zero derivatives at knots and `0` for Catmull–Rom spline.
712+ +/
713+ T param = 0 ;
714+ }
715+
618716/+ +
619717Multivariate cubic spline with nodes on rectilinear grid.
620718+/
@@ -1485,13 +1583,34 @@ MetaSpline!(T, X) metaSpline(F, X, T)(
14851583 SplineBoundaryCondition! F lBoundary,
14861584 SplineBoundaryCondition! F rBoundary,
14871585 SplineType kind = SplineType.c2,
1488- in F param = 0 ,
1586+ const F param = 0 ,
14891587 )
14901588{
14911589 import core.lifetime : move;
14921590 return MetaSpline! (T, X)(grid.move, data.move, kind, param, lBoundary, rBoundary);
14931591}
14941592
1593+ /+ +
1594+ Spline interpolator used for non-rectiliner trapezoid-like greeds.
1595+ Params:
1596+ grid = rc-array of interpolation grid
1597+ data = rc-array of interpolator-like structures
1598+ configuration = $(LREF SplineConfiguration)
1599+ Constraints:
1600+ `grid` and `values` must have the same length >= 3
1601+ Returns: $(LREF Spline)
1602+ +/
1603+ MetaSpline! (T, X) metaSpline(F, X, T)(
1604+ RCArray! (immutable X) grid,
1605+ RCArray! (const T) data,
1606+ SplineConfiguration! F configuration,
1607+ )
1608+ {
1609+ import core.lifetime : move;
1610+ with (configuration)
1611+ return metaSpline! (F, X, T)(grid.move, data.move, leftBoundary, rightBoundary, kind, param);
1612+ }
1613+
14951614// / ditto
14961615struct MetaSpline (T, X)
14971616 if (T.derivativeOrder >= 3 )
@@ -1663,7 +1782,7 @@ unittest
16631782 auto int2 = spline! double (x1.rcslice! (immutable double ), grid[2 ]);
16641783 auto int3 = spline! double (x1.rcslice! (immutable double ), grid[3 ]);
16651784
1666- auto interpolant = metaSpline! double (x0.rcarray! (immutable double ), rcarray(int0, int1, int2, int3).lightConst);
1785+ auto interpolant = metaSpline(x0.rcarray! (immutable double ), rcarray(int0, int1, int2, int3).lightConst, SplineConfiguration ! double .init );
16671786
16681787 // /// compute test data ////
16691788 auto test_grid = cartesian(x0.sliced + 1.23 , x1.sliced + 3.23 );
0 commit comments