Skip to content

Commit 51fcdf6

Browse files
authored
Merge pull request #2387 from joannacirillo/open-spline-1D
Open spline 1d
2 parents 1bd7ace + c1610de commit 51fcdf6

File tree

13 files changed

+497
-42
lines changed

13 files changed

+497
-42
lines changed

Sources/Common/DataModel/CardinalSpline1D/index.d.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { Vector3 } from '../../../types';
2-
import vtkSpline1D, { ISpline1DInitialValues } from '../Spline1D';
3-
2+
import vtkSpline1D, { ISpline1DInitialValues, BoundaryCondition } from '../Spline1D';
43

54
export interface ICardinalSpline1DInitialValues extends ISpline1DInitialValues {}
65

@@ -20,9 +19,14 @@ export interface vtkCardinalSpline1D extends vtkSpline1D {
2019
* @param {Number} size
2120
* @param {Float32Array} work
2221
* @param {Vector3} x
23-
* @param {Vector3} y
22+
* @param {Vector3} y
23+
* @param {Object} options
24+
* @param {BoundaryCondition} options.leftConstraint
25+
* @param {Number} options.leftValue
26+
* @param {BoundaryCondition} options.rightConstraint
27+
* @param {Number} options.rightValue
2428
*/
25-
computeOpenCoefficients(size: number, work: Float32Array, x: Vector3, y: Vector3): void;
29+
computeOpenCoefficients(size: number, work: Float32Array, x: Vector3, y: Vector3, options: { leftConstraint: BoundaryCondition, leftValue: number, rightConstraint: BoundaryCondition, rightValue: Number }): void;
2630

2731
/**
2832
*

Sources/Common/DataModel/CardinalSpline1D/index.js

Lines changed: 144 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import macro from 'vtk.js/Sources/macros';
22
import vtkSpline1D from 'vtk.js/Sources/Common/DataModel/Spline1D';
33

4-
const { vtkErrorMacro } = macro;
4+
import { BoundaryCondition } from 'vtk.js/Sources/Common/DataModel/Spline1D/Constants';
5+
6+
const VTK_EPSILON = 0.0001;
57

68
// ----------------------------------------------------------------------------
79
// vtkCardinalSpline1D methods
@@ -46,9 +48,9 @@ function vtkCardinalSpline1D(publicAPI, model) {
4648
const dN = work[N];
4749

4850
// solve resulting set of equations.
49-
model.coefficients[0 * 4 + 2] = 0;
51+
model.coefficients[4 * 0 + 2] = 0;
5052
work[0] = 0;
51-
model.coefficients[0 * 4 + 3] = 1;
53+
model.coefficients[4 * 0 + 3] = 1;
5254

5355
for (let k = 1; k <= N; k++) {
5456
model.coefficients[4 * k + 1] -=
@@ -114,8 +116,145 @@ function vtkCardinalSpline1D(publicAPI, model) {
114116

115117
// --------------------------------------------------------------------------
116118

117-
publicAPI.computeOpenCoefficients = (size, work, x, y) => {
118-
vtkErrorMacro('Open splines are not implemented yet!');
119+
publicAPI.computeOpenCoefficients = (size, work, x, y, options = {}) => {
120+
if (!model.coefficients || model.coefficients.length !== 4 * size) {
121+
model.coefficients = new Float32Array(4 * size);
122+
}
123+
const N = size - 1;
124+
// develop constraint at leftmost point.
125+
switch (options.leftConstraint) {
126+
case BoundaryCondition.DERIVATIVE:
127+
// desired slope at leftmost point is leftValue.
128+
model.coefficients[4 * 0 + 1] = 1.0;
129+
model.coefficients[4 * 0 + 2] = 0.0;
130+
work[0] = options.leftValue;
131+
break;
132+
case BoundaryCondition.SECOND_DERIVATIVE:
133+
// desired second derivative at leftmost point is leftValue.
134+
model.coefficients[4 * 0 + 1] = 2.0;
135+
model.coefficients[4 * 0 + 2] = 1.0;
136+
work[0] =
137+
3.0 * ((y[1] - y[0]) / (x[1] - x[0])) -
138+
0.5 * (x[1] - x[0]) * options.leftValue;
139+
break;
140+
case BoundaryCondition.SECOND_DERIVATIVE_INTERIOR_POINT:
141+
// desired second derivative at leftmost point is
142+
// leftValue times second derivative at first interior point.
143+
model.coefficients[4 * 0 + 1] = 2.0;
144+
145+
if (Math.abs(options.leftValue + 2) > VTK_EPSILON) {
146+
model.coefficients[4 * 0 + 2] =
147+
4.0 * ((0.5 + options.leftValue) / (2.0 + options.leftValue));
148+
work[0] =
149+
6.0 *
150+
((1.0 + options.leftValue) / (2.0 + options.leftValue)) *
151+
((y[1] - y[0]) / (x[1] - x[0]));
152+
} else {
153+
model.coefficients[4 * 0 + 2] = 0;
154+
work[0] = 0;
155+
}
156+
break;
157+
case BoundaryCondition.DEFAULT:
158+
default:
159+
// desired slope at leftmost point is derivative from two points
160+
model.coefficients[4 * 0 + 1] = 1.0;
161+
model.coefficients[4 * 0 + 2] = 0.0;
162+
work[0] = y[2] - y[0];
163+
break;
164+
}
165+
166+
for (let k = 1; k < N; k++) {
167+
const xlk = x[k] - x[k - 1];
168+
const xlkp = x[k + 1] - x[k];
169+
170+
model.coefficients[4 * k + 0] = xlkp;
171+
model.coefficients[4 * k + 1] = 2 * (xlkp + xlk);
172+
model.coefficients[4 * k + 2] = xlk;
173+
work[k] =
174+
3.0 *
175+
((xlkp * (y[k] - y[k - 1])) / xlk + (xlk * (y[k + 1] - y[k])) / xlkp);
176+
}
177+
178+
// develop constraint at rightmost point.
179+
switch (options.rightConstraint) {
180+
case BoundaryCondition.DERIVATIVE:
181+
// desired slope at rightmost point is rightValue
182+
model.coefficients[4 * N + 0] = 0.0;
183+
model.coefficients[4 * N + 1] = 1.0;
184+
work[N] = options.rightValue;
185+
break;
186+
case BoundaryCondition.SECOND_DERIVATIVE:
187+
// desired second derivative at rightmost point is rightValue.
188+
model.coefficients[4 * N + 0] = 1.0;
189+
model.coefficients[4 * N + 1] = 2.0;
190+
work[N] =
191+
3.0 * ((y[N] - y[N - 1]) / (x[N] - x[N - 1])) +
192+
0.5 * (x[N] - x[N - 1]) * options.rightValue;
193+
break;
194+
case BoundaryCondition.SECOND_DERIVATIVE_INTERIOR_POINT:
195+
// desired second derivative at rightmost point is
196+
// rightValue times second derivative at last interior point.
197+
model.coefficients[4 * N + 1] = 2.0;
198+
if (Math.abs(options.rightValue + 2) > VTK_EPSILON) {
199+
model.coefficients[4 * N + 0] =
200+
4.0 * ((0.5 + options.rightValue) / (2.0 + options.rightValue));
201+
work[N] =
202+
6.0 *
203+
((1.0 + options.rightValue) / (2.0 + options.rightValue)) *
204+
((y[N] - y[size - 2]) / (x[N] - x[size - 2]));
205+
} else {
206+
model.coefficients[4 * N + 0] = 0;
207+
work[N] = 0;
208+
}
209+
break;
210+
case BoundaryCondition.DEFAULT:
211+
default:
212+
// desired slope at rightmost point is derivative from two points
213+
model.coefficients[4 * N + 0] = 0.0;
214+
model.coefficients[4 * N + 1] = 1.0;
215+
work[N] = y[N] - y[N - 2];
216+
break;
217+
}
218+
219+
// solve resulting set of equations.
220+
model.coefficients[4 * 0 + 2] /= model.coefficients[4 * 0 + 1];
221+
work[0] /= model.coefficients[4 * N + 1];
222+
model.coefficients[4 * N + 3] = 1;
223+
224+
for (let k = 1; k <= N; k++) {
225+
model.coefficients[4 * k + 1] -=
226+
model.coefficients[4 * k + 0] * model.coefficients[4 * (k - 1) + 2];
227+
model.coefficients[4 * k + 2] /= model.coefficients[4 * k + 1];
228+
work[k] =
229+
(work[k] - model.coefficients[4 * k + 0] * work[k - 1]) /
230+
model.coefficients[4 * k + 1];
231+
}
232+
233+
for (let k = N - 1; k >= 0; k--) {
234+
work[k] -= model.coefficients[4 * k + 2] * work[k + 1];
235+
}
236+
237+
// the column vector work now contains the first
238+
// derivative of the spline function at each joint.
239+
// compute the coefficients of the cubic between
240+
// each pair of joints.
241+
for (let k = 0; k < N; k++) {
242+
const b = x[k + 1] - x[k];
243+
model.coefficients[4 * k + 0] = y[k];
244+
model.coefficients[4 * k + 1] = work[k];
245+
model.coefficients[4 * k + 2] =
246+
(3 * (y[k + 1] - y[k])) / (b * b) - (work[k + 1] + 2 * work[k]) / b;
247+
model.coefficients[4 * k + 3] =
248+
(2 * (y[k] - y[k + 1])) / (b * b * b) +
249+
(work[k + 1] + work[k]) / (b * b);
250+
}
251+
252+
// the coefficients of a fictitious nth cubic
253+
// are the same as the coefficients in the first interval
254+
model.coefficients[4 * N + 0] = y[N];
255+
model.coefficients[4 * N + 1] = work[N];
256+
model.coefficients[4 * N + 2] = model.coefficients[4 * 0 + 2];
257+
model.coefficients[4 * N + 3] = model.coefficients[4 * 0 + 3];
119258
};
120259

121260
// --------------------------------------------------------------------------

Sources/Common/DataModel/KochanekSpline1D/index.d.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import vtkSpline1D, { ISpline1DInitialValues } from '../Spline1D';
2-
1+
import vtkSpline1D, { ISpline1DInitialValues, BoundaryCondition } from '../Spline1D';
32

43
export interface IKochanekSpline1DInitialValues extends ISpline1DInitialValues {
54
tension?: number;
@@ -23,9 +22,14 @@ export interface vtkKochanekSpline1D extends vtkSpline1D {
2322
* @param {Number} size
2423
* @param {Float32Array} work
2524
* @param {Number[]} x
26-
* @param {Number[]} y
25+
* @param {Number[]} y
26+
* @param {Object} options
27+
* @param {BoundaryCondition} options.leftConstraint
28+
* @param {Number} options.leftValue
29+
* @param {BoundaryCondition} options.rightConstraint
30+
* @param {Number} options.rightValue
2731
*/
28-
computeOpenCoefficients(size: number, work: Float32Array, x: number[], y: number[]): void;
32+
computeOpenCoefficients(size: number, work: Float32Array, x: number[], y: number[], options: { leftConstraint: BoundaryCondition, leftValue: number, rightConstraint: BoundaryCondition, rightValue: Number }): void;
2933

3034
/**
3135
*

Sources/Common/DataModel/KochanekSpline1D/index.js

Lines changed: 129 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import macro from 'vtk.js/Sources/macros';
22
import vtkSpline1D from 'vtk.js/Sources/Common/DataModel/Spline1D';
33

4-
const { vtkErrorMacro } = macro;
4+
import { BoundaryCondition } from 'vtk.js/Sources/Common/DataModel/Spline1D/Constants';
5+
6+
const VTK_EPSILON = 0.0001;
57

68
// ----------------------------------------------------------------------------
79
// vtkKochanekSpline1D methods
@@ -103,8 +105,132 @@ function vtkKochanekSpline1D(publicAPI, model) {
103105

104106
// --------------------------------------------------------------------------
105107

106-
publicAPI.computeOpenCoefficients = (size, work, x, y) => {
107-
vtkErrorMacro('Open splines are not implemented yet!');
108+
publicAPI.computeOpenCoefficients = (size, work, x, y, options = {}) => {
109+
if (!model.coefficients || model.coefficients.length !== 4 * size) {
110+
model.coefficients = new Float32Array(4 * size);
111+
}
112+
const N = size - 1;
113+
114+
for (let i = 1; i < N; i++) {
115+
const cs = y[i] - y[i - 1];
116+
const cd = y[i + 1] - y[i];
117+
118+
let ds =
119+
cs * ((1 - model.tension) * (1 - model.continuity) * (1 + model.bias)) +
120+
cd * ((1 - model.tension) * (1 + model.continuity) * (1 - model.bias));
121+
122+
let dd =
123+
cs * ((1 - model.tension) * (1 + model.continuity) * (1 + model.bias)) +
124+
cd * ((1 - model.tension) * (1 - model.continuity) * (1 - model.bias));
125+
126+
// adjust deriviatives for non uniform spacing between nodes
127+
const n1 = x[i + 1] - x[i];
128+
const n0 = x[i] - x[i - 1];
129+
130+
ds *= n0 / (n0 + n1);
131+
dd *= n1 / (n0 + n1);
132+
133+
model.coefficients[4 * i + 0] = y[i];
134+
model.coefficients[4 * i + 1] = dd;
135+
model.coefficients[4 * i + 2] = ds;
136+
}
137+
138+
model.coefficients[4 * 0 + 0] = y[0];
139+
model.coefficients[4 * N + 0] = y[N];
140+
141+
// Calculate the deriviatives at the end points
142+
143+
switch (options.leftConstraint) {
144+
// desired slope at leftmost point is leftValue
145+
case BoundaryCondition.DERIVATIVE:
146+
model.coefficients[4 * 0 + 1] = options.leftValue;
147+
break;
148+
case BoundaryCondition.SECOND_DERIVATIVE:
149+
// desired second derivative at leftmost point is leftValue
150+
model.coefficients[4 * 0 + 1] =
151+
(6 * (y[1] - y[0]) -
152+
2 * model.coefficients[4 * 1 + 2] -
153+
options.leftValue) /
154+
4;
155+
break;
156+
case BoundaryCondition.SECOND_DERIVATIVE_INTERIOR_POINT:
157+
// desired second derivative at leftmost point is leftValue
158+
// times second derivative at first interior point
159+
if (Math.abs(options.leftValue + 2) > VTK_EPSILON) {
160+
model.coefficients[4 * 0 + 1] =
161+
(3 * (1 + options.leftValue) * (y[1] - y[0]) -
162+
(1 + 2 * options.leftValue) * model.coefficients[4 * 1 + 2]) /
163+
(2 + options.leftValue);
164+
} else {
165+
model.coefficients[4 * 0 + 1] = 0.0;
166+
}
167+
break;
168+
case BoundaryCondition.DEFAULT:
169+
default:
170+
// desired slope at leftmost point is derivative from two points
171+
model.coefficients[4 * 0 + 1] = y[2] - y[0];
172+
break;
173+
}
174+
switch (options.rightConstraint) {
175+
case BoundaryCondition.DERIVATIVE:
176+
// desired slope at rightmost point is leftValue
177+
model.coefficients[4 * N + 2] = options.leftValue;
178+
break;
179+
180+
case BoundaryCondition.SECOND_DERIVATIVE:
181+
// desired second derivative at rightmost point is leftValue
182+
model.coefficients[4 * N + 2] =
183+
(6 * (y[N] - y[N - 1]) -
184+
2 * model.coefficients[4 * (N - 1) + 1] +
185+
options.leftValue) /
186+
4.0;
187+
break;
188+
189+
case BoundaryCondition.SECOND_DERIVATIVE_INTERIOR_POINT:
190+
// desired second derivative at rightmost point is leftValue
191+
// times second derivative at last interior point
192+
if (Math.abs(options.leftValue + 2) > VTK_EPSILON) {
193+
model.coefficients[4 * N + 2] =
194+
(3 * (1 + options.leftValue) * (y[N] - y[N - 1]) -
195+
(1 + 2 * options.leftValue) *
196+
model.coefficients[4 * (N - 1) + 1]) /
197+
(2 + options.leftValue);
198+
} else {
199+
model.coefficients[4 * N + 2] = 0.0;
200+
}
201+
break;
202+
case BoundaryCondition.DEFAULT:
203+
default:
204+
// desired slope at rightmost point is rightValue
205+
model.coefficients[4 * N + 2] = y[N] - y[N - 2];
206+
break;
207+
}
208+
209+
for (let i = 0; i < N; i++) {
210+
//
211+
// c0 = P ; c1 = DD ;
212+
// i i i i
213+
//
214+
// c1 = P ; c2 = DS ;
215+
// i+1 i+1 i+1 i+1
216+
//
217+
// c2 = -3P + 3P - 2DD - DS ;
218+
// i i i+1 i i+1
219+
//
220+
// c3 = 2P - 2P + DD + DS ;
221+
// i i i+1 i i+1
222+
//
223+
model.coefficients[4 * i + 2] =
224+
-3 * y[i] +
225+
3 * y[i + 1] +
226+
-2 * model.coefficients[4 * i + 1] +
227+
-1 * model.coefficients[4 * (i + 1) + 2];
228+
model.coefficients[4 * i + 3] =
229+
2 * y[i] +
230+
-2 * y[i + 1] +
231+
1 * model.coefficients[4 * i + 1] +
232+
1 * model.coefficients[4 * (i + 1) + 2];
233+
}
108234
};
109235

110236
// --------------------------------------------------------------------------
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// Boundary conditions available to compute open splines
2+
// DEFAULT : desired slope at boundary point is derivative from two points (boundary and second interior)
3+
// DERIVATIVE : desired slope at boundary point is the boundary value given.
4+
// SECOND_DERIVATIVE : second derivative at boundary point is the boundary value given.
5+
// SECOND_DERIVATIVE_INTERIOR_POINT : desired second derivative at boundary point is the boundary value given times second derivative
6+
// at first interior point.
7+
8+
export const BoundaryCondition = {
9+
DEFAULT: 0,
10+
DERIVATIVE: 1,
11+
SECOND_DERIVATIVE: 2,
12+
SECOND_DERIVATIVE_INTERIOR_POINT: 3,
13+
};
14+
15+
export default {
16+
BoundaryCondition,
17+
};

0 commit comments

Comments
 (0)