UNPKG

htmol

Version:

NEXT-GENERATION MOLECULAR DYNAMICS WEB VISUALIZATION

316 lines (258 loc) 9.51 kB
/** * @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 */ /** *To file has been removed the dependece to three.js *All credit for the creators of CatmullRom and three.js * @author of the change JulioC3984 https://github.com/JulioC3094 */ Spline = function (vec, NumPoints){ var ver = new Array(); var n=0; for (var i=0; i<vec.length/3; i++) { ver[i] = new Vector3(vec[n], vec[n+1], vec[n+2]); n+=3 } var curve = new CatmullRomCurve3(ver); var spline = curve.getPoints(NumPoints*ver.length); var splinePoints = new Array(); for (var i = 0; i < spline.length; i++) { splinePoints.push(spline[i].x); splinePoints.push(spline[i].y); splinePoints.push(spline[i].z); } return splinePoints } Vector3 = function(a, b, c) { this.x = a || 0; this.y = b || 0; this.z = c || 0 }; Vector3.prototype = { constructor: Vector3, add: function(a, b) { if (void 0 !== b) return console.warn("DEPRECATED: Vector3's .add() now only accepts one argument. Use .addVectors( a, b ) instead."), this.addVectors(a, b); this.x += a.x; this.y += a.y; this.z += a.z; return this }, subVectors: function(a, b) { this.x = a.x - b.x; this.y = a.y - b.y; this.z = a.z - b.z; return this }, distanceToSquared: function(a) { var b = this.x - a.x, c = this.y - a.y, a = this.z - a.z; return b * b + c * c + a * a } }; CatmullRomCurve3 = (function() { var tmp = new Vector3(), px = new CubicPoly(), py = new CubicPoly(), pz = new CubicPoly(); function CubicPoly() { } /* * 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. */ Curve = function() {}; Curve.prototype.getPoint = function() { console.log("Warning, getPoint() not implemented!"); return null }; Curve.prototype.getPointAt = function(a) { a = this.getUtoTmapping(a); return this.getPoint(a) }; Curve.prototype.getPoints = function(a) { a || (a = 5); var b, c = []; for (b = 0; b <= a; b++) c.push(this.getPoint(b / a)); return c }; Curve.prototype.getSpacedPoints = function(a) { a || (a = 5); var b, c = []; for (b = 0; b <= a; b++) c.push(this.getPointAt(b / a)); return c }; Curve.prototype.getLength = function() { var a = this.getLengths(); return a[a.length - 1] }; Curve.prototype.getLengths = function(a) { a || (a = this.__arcLengthDivisions ? this.__arcLengthDivisions : 200); if (this.cacheArcLengths && this.cacheArcLengths.length == a + 1 && !this.needsUpdate) return this.cacheArcLengths; this.needsUpdate = !1; var b = [], c, d = this.getPoint(0), e, f = 0; b.push(0); for (e = 1; e <= a; e++) c = this.getPoint(e / a), f += c.distanceTo(d), b.push(f), d = c; return this.cacheArcLengths = b }; Curve.prototype.updateArcLengths = function() { this.needsUpdate = !0; this.getLengths() }; Curve.prototype.getUtoTmapping = function(a, b) { var c = this.getLengths(), d = 0, e = c.length, f; f = b ? b : a * c[e - 1]; for (var h = 0, g = e - 1, i; h <= g;) if (d = Math.floor(h + (g - h) / 2), i = c[d] - f, 0 > i) h = d + 1; else if (0 < i) g = d - 1; else { g = d; break } d = g; if (c[d] == f) return d / (e - 1); h = c[d]; return c = (d + (f - h) / (c[d + 1] - h)) / (e - 1) }; Curve.prototype.getTangent = function(a) { var b = a - 1E-4, a = a + 1E-4; 0 > b && (b = 0); 1 < a && (a = 1); b = this.getPoint(b); return this.getPoint(a).clone().sub(b).normalize() }; Curve.prototype.getTangentAt = function(a) { a = this.getUtoTmapping(a); return this.getTangent(a) }; Curve.Utils = { tangentQuadraticBezier: function(a, b, c, d) { return 2 * (1 - a) * (c - b) + 2 * a * (d - c) }, tangentCubicBezier: function(a, b, c, d, e) { return -3 * b * (1 - a) * (1 - a) + 3 * c * (1 - a) * (1 - a) - 6 * a * c * (1 - a) + 6 * a * d * (1 - a) - 3 * a * a * d + 3 * a * a * e }, tangentSpline: function(a) { return 6 * a * a - 6 * a + (3 * a * a - 4 * a + 1) + (-6 * a * a + 6 * a) + (3 * a * a - 2 * a) }, interpolate: function(a, b, c, d, e) { var a = 0.5 * (c - a), d = 0.5 * (d - b), f = e * e; return (2 * b - 2 * c + a + d) * e * f + (-3 * b + 3 * c - 2 * a - d) * f + a * e + b } }; Curve.create = function(a, b) { a.prototype = Object.create(Curve.prototype); a.prototype.getPoint = b; return a }; CubicPoly.prototype.init = function(x0, x1, t0, t1) { this.c0 = x0; this.c1 = t0; this.c2 = -3 * x0 + 3 * x1 - 2 * t0 - t1; this.c3 = 2 * x0 - 2 * x1 + t0 + t1; }; CubicPoly.prototype.initNonuniformCatmullRom = function(x0, x1, x2, x3, dt0, dt1, dt2) { // 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; // rescale tangents for parametrization in [0,1] t1 *= dt1; t2 *= dt1; // initCubicPoly this.init(x1, x2, t1, t2); }; // standard Catmull-Rom spline: interpolate between x1 and x2 with previous/following points x1/x4 CubicPoly.prototype.initCatmullRom = function(x0, x1, x2, x3, tension) { this.init(x1, x2, tension * (x2 - x0), tension * (x3 - x1)); }; CubicPoly.prototype.calc = function(t) { var t2 = t * t; var t3 = t2 * t; return this.c0 + this.c1 * t + this.c2 * t2 + this.c3 * t3; }; // Subclass Three.js curve return Curve.create( function(p /* array of Vector3 */ ) { this.points = p || []; this.closed = false; }, function(t) { var points = this.points, point, intPoint, weight, l; l = points.length; if (l < 2) console.log('duh, you need at least 2 points'); point = (l - (this.closed ? 0 : 1)) * t; intPoint = Math.floor(point); weight = point - intPoint; if (this.closed) { intPoint += intPoint > 0 ? 0 : (Math.floor(Math.abs(intPoint) / points.length) + 1) * points.length; } else if (weight === 0 && intPoint === l - 1) { intPoint = l - 2; weight = 1; } var p0, p1, p2, p3; // 4 points if (this.closed || intPoint > 0) { p0 = points[(intPoint - 1) % l]; } else { // extrapolate first point tmp.subVectors(points[0], points[1]).add(points[0]); p0 = tmp; } p1 = points[intPoint % l]; p2 = points[(intPoint + 1) % l]; if (this.closed || intPoint + 2 < l) { p3 = points[(intPoint + 2) % l]; } else { // extrapolate last point tmp.subVectors(points[l - 1], points[l - 2]).add(points[l - 1]); p3 = tmp; } if (this.type === undefined || this.type === 'centripetal' || this.type === 'chordal') { // init Centripetal / Chordal Catmull-Rom var pow = this.type === 'chordal' ? 0.5 : 0.25; var dt0 = Math.pow(p0.distanceToSquared(p1), pow); var dt1 = Math.pow(p1.distanceToSquared(p2), pow); var dt2 = Math.pow(p2.distanceToSquared(p3), pow); // safety check for repeated points if (dt1 < 1e-4) dt1 = 1.0; if (dt0 < 1e-4) dt0 = dt1; if (dt2 < 1e-4) dt2 = dt1; 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); } else if (this.type === 'catmullrom') { 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); } var v = new Vector3( px.calc(weight), py.calc(weight), pz.calc(weight) ); return v; } ); })();