CatmullRomCurve3.js 4.5 KB
Newer Older
B
bentok 已提交
1 2
import { Vector3 } from '../../math/Vector3.js';
import { Curve } from '../core/Curve.js';
R
Rich Harris 已提交
3

M
Mr.doob 已提交
4
/**
5 6 7 8 9 10 11 12 13 14 15
 * @author zz85 https://github.com/zz85
 *
 * Centripetal CatmullRom Curve - which is useful for avoiding
 * cusps and self-intersections in non-uniform catmull rom curves.
 * http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
 *
 * curve.type accepts centripetal(default), chordal and catmullrom
 * curve.tension is used for catmullrom which defaults to 0.5
 */


16 17 18 19
/*
Based on an optimized c++ solution in
 - http://stackoverflow.com/questions/9489736/catmull-rom-curve-with-no-cusps-and-no-self-intersections/
 - http://ideone.com/NoEbVM
20

21 22 23 24
This CubicPoly class could be used for reusing some variables and calculations,
but for three.js curve use, it could be possible inlined and flatten into a single function call
which can be placed in CurveUtils.
*/
25

26
function CubicPoly() {
27

28
	var c0 = 0, c1 = 0, c2 = 0, c3 = 0;
29 30 31 32 33 34 35 36 37

	/*
	 * Compute coefficients for a cubic polynomial
	 *   p(s) = c0 + c1*s + c2*s^2 + c3*s^3
	 * such that
	 *   p(0) = x0, p(1) = x1
	 *  and
	 *   p'(0) = t0, p'(1) = t1.
	 */
38
	function init( x0, x1, t0, t1 ) {
39

40 41 42 43
		c0 = x0;
		c1 = t0;
		c2 = - 3 * x0 + 3 * x1 - 2 * t0 - t1;
		c3 = 2 * x0 - 2 * x1 + t0 + t1;
44

45 46 47
	}

	return {
48

49
		initCatmullRom: function ( x0, x1, x2, x3, tension ) {
50

51
			init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
52

53
		},
54

55
		initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {
56

57 58 59
			// compute tangents when parameterized in [t1,t2]
			var t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
			var t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
60

61 62 63
			// rescale tangents for parametrization in [0,1]
			t1 *= dt1;
			t2 *= dt1;
64

65
			init( x1, x2, t1, t2 );
66

67
		},
68

69
		calc: function ( t ) {
70

71 72 73
			var t2 = t * t;
			var t3 = t2 * t;
			return c0 + c1 * t + c2 * t2 + c3 * t3;
74

75
		}
76

77
	};
78

79
}
80

81
//
82

83 84
var tmp = new Vector3();
var px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly();
85

M
Mugen87 已提交
86
function CatmullRomCurve3( points ) {
87

M
Mugen87 已提交
88 89
	Curve.call( this );

M
Mugen87 已提交
90 91
	this.type = 'CatmullRomCurve3';

M
Mugen87 已提交
92 93 94
	if ( points.length < 2 ) console.warn( 'THREE.CatmullRomCurve3: Points array needs at least two entries.' );

	this.points = points || [];
95
	this.closed = false;
M
Mugen87 已提交
96
	this.curveType = 'centripetal';
97

98
}
99

100 101
CatmullRomCurve3.prototype = Object.create( Curve.prototype );
CatmullRomCurve3.prototype.constructor = CatmullRomCurve3;
102

103 104 105 106 107
CatmullRomCurve3.prototype.isCatmullRomCurve3 = true;

CatmullRomCurve3.prototype.getPoint = function ( t, optionalTarget ) {

	var point = optionalTarget || new Vector3();
M
Mr.doob 已提交
108

109 110
	var points = this.points;
	var l = points.length;
M
Mr.doob 已提交
111

112 113 114
	var p = ( l - ( this.closed ? 0 : 1 ) ) * t;
	var intPoint = Math.floor( p );
	var weight = p - intPoint;
115

116
	if ( this.closed ) {
117

118
		intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / points.length ) + 1 ) * points.length;
119

120
	} else if ( weight === 0 && intPoint === l - 1 ) {
121

122 123
		intPoint = l - 2;
		weight = 1;
124

125
	}
126

127
	var p0, p1, p2, p3; // 4 points
128

129
	if ( this.closed || intPoint > 0 ) {
130

131
		p0 = points[ ( intPoint - 1 ) % l ];
132

133
	} else {
134

135 136 137
		// extrapolate first point
		tmp.subVectors( points[ 0 ], points[ 1 ] ).add( points[ 0 ] );
		p0 = tmp;
138

139
	}
140

141 142
	p1 = points[ intPoint % l ];
	p2 = points[ ( intPoint + 1 ) % l ];
143

144
	if ( this.closed || intPoint + 2 < l ) {
145

146
		p3 = points[ ( intPoint + 2 ) % l ];
147

148
	} else {
149

150 151 152
		// extrapolate last point
		tmp.subVectors( points[ l - 1 ], points[ l - 2 ] ).add( points[ l - 1 ] );
		p3 = tmp;
153

154
	}
155

M
Mugen87 已提交
156
	if ( this.curveType === 'centripetal' || this.curveType === 'chordal' ) {
157

158
		// init Centripetal / Chordal Catmull-Rom
M
Mugen87 已提交
159
		var pow = this.curveType === 'chordal' ? 0.5 : 0.25;
160 161 162
		var dt0 = Math.pow( p0.distanceToSquared( p1 ), pow );
		var dt1 = Math.pow( p1.distanceToSquared( p2 ), pow );
		var dt2 = Math.pow( p2.distanceToSquared( p3 ), pow );
163

164 165 166 167
		// safety check for repeated points
		if ( dt1 < 1e-4 ) dt1 = 1.0;
		if ( dt0 < 1e-4 ) dt0 = dt1;
		if ( dt2 < 1e-4 ) dt2 = dt1;
168

169 170 171
		px.initNonuniformCatmullRom( p0.x, p1.x, p2.x, p3.x, dt0, dt1, dt2 );
		py.initNonuniformCatmullRom( p0.y, p1.y, p2.y, p3.y, dt0, dt1, dt2 );
		pz.initNonuniformCatmullRom( p0.z, p1.z, p2.z, p3.z, dt0, dt1, dt2 );
172

M
Mugen87 已提交
173
	} else if ( this.curveType === 'catmullrom' ) {
174

175 176 177 178
		var tension = this.tension !== undefined ? this.tension : 0.5;
		px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, tension );
		py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, tension );
		pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, tension );
179

180
	}
181

182 183 184 185 186 187 188
	point.set(
		px.calc( weight ),
		py.calc( weight ),
		pz.calc( weight )
	);

	return point;
189

190
};
R
Rich Harris 已提交
191 192


M
Mr.doob 已提交
193
export { CatmullRomCurve3 };