CatmullRomCurve3.js 5.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

86
function CatmullRomCurve3( points, closed, curveType, tension ) {
87

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

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

M
Mugen87 已提交
92
	this.points = points || [];
M
Mugen87 已提交
93
	this.closed = closed || false;
94
	this.curveType = curveType || 'centripetal';
M
Mugen87 已提交
95
	this.tension = tension || 0.5;
96

97
}
98

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

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

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

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

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

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

115
	if ( this.closed ) {
116

V
vlucendo 已提交
117
		intPoint += intPoint > 0 ? 0 : ( Math.floor( Math.abs( intPoint ) / l ) + 1 ) * l;
118

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

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

124
	}
125

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

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

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

132
	} else {
133

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

138
	}
139

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

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

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

147
	} else {
148

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

153
	}
154

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

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

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

168 169 170
		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 );
171

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

M
Mugen87 已提交
174 175 176
		px.initCatmullRom( p0.x, p1.x, p2.x, p3.x, this.tension );
		py.initCatmullRom( p0.y, p1.y, p2.y, p3.y, this.tension );
		pz.initCatmullRom( p0.z, p1.z, p2.z, p3.z, this.tension );
177

178
	}
179

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

	return point;
187

188
};
R
Rich Harris 已提交
189

M
Mugen87 已提交
190 191 192 193
CatmullRomCurve3.prototype.copy = function ( source ) {

	Curve.prototype.copy.call( this, source );

M
Mugen87 已提交
194 195
	this.points = [];

M
Mugen87 已提交
196 197 198 199 200 201 202 203 204
	for ( var i = 0, l = source.points.length; i < l; i ++ ) {

		var point = source.points[ i ];

		this.points.push( point.clone() );

	}

	this.closed = source.closed;
205
	this.curveType = source.curveType;
M
Mugen87 已提交
206 207 208 209 210 211
	this.tension = source.tension;

	return this;

};

212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
CatmullRomCurve3.prototype.toJSON = function () {

	var data = Curve.prototype.toJSON.call( this );

	data.points = [];

	for ( var i = 0, l = this.points.length; i < l; i ++ ) {

		var point = this.points[ i ];
		data.points.push( point.toArray() );

	}

	data.closed = this.closed;
	data.curveType = this.curveType;
	data.tension = this.tension;

	return data;

};

CatmullRomCurve3.prototype.fromJSON = function ( json ) {

	Curve.prototype.fromJSON.call( this, json );

M
Mugen87 已提交
237 238
	this.points = [];

239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
	for ( var i = 0, l = json.points.length; i < l; i ++ ) {

		var point = json.points[ i ];
		this.points.push( new Vector3().fromArray( point ) );

	}

	this.closed = json.closed;
	this.curveType = json.curveType;
	this.tension = json.tension;

	return this;

};

R
Rich Harris 已提交
254

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