Skip to content

Commit 2db1139

Browse files
committed
add SplineConfiguration
1 parent d1cfde7 commit 2db1139

1 file changed

Lines changed: 129 additions & 10 deletions

File tree

source/mir/interpolate/spline.d

Lines changed: 129 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,15 @@ import std.meta: AliasSeq, staticMap;
2929
import std.traits: Unqual;
3030
public 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+
3436
version(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

4043
private 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)
567592
extern(C++, "mir", "interpolate")
568593
enum 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")
609634
struct 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
/++
619717
Multivariate 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
14961615
struct 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

Comments
 (0)