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
 * 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
 */


14 15 16 17
/*
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
18

19 20 21 22
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.
*/
23

24
function CubicPoly() {
25

M
Mugen87 已提交
26
	let c0 = 0, c1 = 0, c2 = 0, c3 = 0;
27 28 29 30 31 32 33 34 35

	/*
	 * 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.
	 */
36
	function init( x0, x1, t0, t1 ) {
37

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

43 44 45
	}

	return {
46

47
		initCatmullRom: function ( x0, x1, x2, x3, tension ) {
48

49
			init( x1, x2, tension * ( x2 - x0 ), tension * ( x3 - x1 ) );
50

51
		},
52

53
		initNonuniformCatmullRom: function ( x0, x1, x2, x3, dt0, dt1, dt2 ) {
54

55
			// compute tangents when parameterized in [t1,t2]
M
Mugen87 已提交
56 57
			let t1 = ( x1 - x0 ) / dt0 - ( x2 - x0 ) / ( dt0 + dt1 ) + ( x2 - x1 ) / dt1;
			let t2 = ( x2 - x1 ) / dt1 - ( x3 - x1 ) / ( dt1 + dt2 ) + ( x3 - x2 ) / dt2;
58

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

63
			init( x1, x2, t1, t2 );
64

65
		},
66

67
		calc: function ( t ) {
68

M
Mugen87 已提交
69 70
			const t2 = t * t;
			const t3 = t2 * t;
71
			return c0 + c1 * t + c2 * t2 + c3 * t3;
72

73
		}
74

75
	};
76

77
}
78

79
//
80

M
Mugen87 已提交
81 82
const tmp = new Vector3();
const px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly();
83

84
function CatmullRomCurve3( points, closed, curveType, tension ) {
85

M
Mugen87 已提交
86 87
	Curve.call( this );

M
Mugen87 已提交
88 89
	this.type = 'CatmullRomCurve3';

M
Mugen87 已提交
90
	this.points = points || [];
M
Mugen87 已提交
91
	this.closed = closed || false;
92
	this.curveType = curveType || 'centripetal';
D
dskeithbuck 已提交
93
	this.tension = ( tension !== undefined ) ? tension : 0.5;
94

95
}
96

97 98
CatmullRomCurve3.prototype = Object.create( Curve.prototype );
CatmullRomCurve3.prototype.constructor = CatmullRomCurve3;
99

100 101 102 103
CatmullRomCurve3.prototype.isCatmullRomCurve3 = true;

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

M
Mugen87 已提交
104
	const point = optionalTarget || new Vector3();
M
Mr.doob 已提交
105

M
Mugen87 已提交
106 107
	const points = this.points;
	const l = points.length;
M
Mr.doob 已提交
108

M
Mugen87 已提交
109 110 111
	const p = ( l - ( this.closed ? 0 : 1 ) ) * t;
	let intPoint = Math.floor( p );
	let weight = p - intPoint;
112

113
	if ( this.closed ) {
114

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

117
	} else if ( weight === 0 && intPoint === l - 1 ) {
118

119 120
		intPoint = l - 2;
		weight = 1;
121

122
	}
123

M
Mugen87 已提交
124
	let p0, p1, p2, p3; // 4 points
125

126
	if ( this.closed || intPoint > 0 ) {
127

128
		p0 = points[ ( intPoint - 1 ) % l ];
129

130
	} else {
131

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

136
	}
137

138 139
	p1 = points[ intPoint % l ];
	p2 = points[ ( intPoint + 1 ) % l ];
140

141
	if ( this.closed || intPoint + 2 < l ) {
142

143
		p3 = points[ ( intPoint + 2 ) % l ];
144

145
	} else {
146

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

151
	}
152

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

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

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

166 167 168
		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 );
169

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

M
Mugen87 已提交
172 173 174
		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 );
175

176
	}
177

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

	return point;
185

186
};
R
Rich Harris 已提交
187

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

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

M
Mugen87 已提交
192 193
	this.points = [];

M
Mugen87 已提交
194
	for ( let i = 0, l = source.points.length; i < l; i ++ ) {
M
Mugen87 已提交
195

M
Mugen87 已提交
196
		const point = source.points[ i ];
M
Mugen87 已提交
197 198 199 200 201 202

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

	}

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

	return this;

};

210 211
CatmullRomCurve3.prototype.toJSON = function () {

M
Mugen87 已提交
212
	const data = Curve.prototype.toJSON.call( this );
213 214 215

	data.points = [];

M
Mugen87 已提交
216
	for ( let i = 0, l = this.points.length; i < l; i ++ ) {
217

M
Mugen87 已提交
218
		const point = this.points[ i ];
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
		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 已提交
235 236
	this.points = [];

M
Mugen87 已提交
237
	for ( let i = 0, l = json.points.length; i < l; i ++ ) {
238

M
Mugen87 已提交
239
		const point = json.points[ i ];
240 241 242 243 244 245 246 247 248 249 250 251
		this.points.push( new Vector3().fromArray( point ) );

	}

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

	return this;

};

R
Rich Harris 已提交
252

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