· 6 years ago · Jan 17, 2019, 10:24 PM
1/*
2 GLmol - Molecular Viewer on WebGL/Javascript (0.47)
3 (C) Copyright 2011-2012, biochem_fan
4 License: dual license of MIT or LGPL3
5
6 Contributors:
7 Robert Hanson for parseXYZ, deferred instantiation
8
9 This program uses
10 Three.js
11 https://github.com/mrdoob/three.js
12 Copyright (c) 2010-2012 three.js Authors. All rights reserved.
13 jQuery
14 http://jquery.org/
15 Copyright (c) 2011 John Resig
16 */
17
18// Workaround for Intel GMA series (gl_FrontFacing causes compilation error)
19THREE.ShaderLib.lambert.fragmentShader = THREE.ShaderLib.lambert.fragmentShader.replace("gl_FrontFacing", "true");
20THREE.ShaderLib.lambert.vertexShader = THREE.ShaderLib.lambert.vertexShader.replace(/\}$/, "#ifdef DOUBLE_SIDED\n if (transformedNormal.z < 0.0) vLightFront = vLightBack;\n #endif\n }");
21
22var TV3 = THREE.Vector3, TF3 = THREE.Face3, TCo = THREE.Color;
23
24THREE.Geometry.prototype.colorAll = function (color) {
25 for (var i = 0; i < this.faces.length; i++) {
26 this.faces[i].color = color;
27 }
28};
29
30THREE.Matrix4.prototype.isIdentity = function() {
31 for (var i = 0; i < 4; i++)
32 for (var j = 0; j < 4; j++)
33 if (this.elements[i * 4 + j] != (i == j) ? 1 : 0) return false;
34 return true;
35};
36
37var GLmol = (function() {
38function GLmol(id, suppressAutoload) {
39 if (id) this.create(id, suppressAutoload);
40 return true;
41}
42
43GLmol.prototype.create = function(id, suppressAutoload) {
44 this.Nucleotides = [' G', ' A', ' T', ' C', ' U', ' DG', ' DA', ' DT', ' DC', ' DU'];
45 this.ElementColors = {"H": 0xCCCCCC, "C": 0xAAAAAA, "O": 0xCC0000, "N": 0x0000CC, "S": 0xCCCC00, "P": 0x6622CC,
46 "F": 0x00CC00, "CL": 0x00CC00, "BR": 0x882200, "I": 0x6600AA,
47 "FE": 0xCC6600, "CA": 0x8888AA};
48// Reference: A. Bondi, J. Phys. Chem., 1964, 68, 441.
49 this.vdwRadii = {"H": 1.2, "Li": 1.82, "Na": 2.27, "K": 2.75, "C": 1.7, "N": 1.55, "O": 1.52,
50 "F": 1.47, "P": 1.80, "S": 1.80, "CL": 1.75, "BR": 1.85, "SE": 1.90,
51 "ZN": 1.39, "CU": 1.4, "NI": 1.63};
52
53 this.id = id;
54 this.aaScale = 1; // or 2
55
56 this.container = $('#' + this.id);
57 this.WIDTH = this.container.width() * this.aaScale, this.HEIGHT = this.container.height() * this.aaScale;
58 this.ASPECT = this.WIDTH / this.HEIGHT;
59 this.NEAR = 1, FAR = 800;
60 this.CAMERA_Z = -150;
61 this.renderer = new THREE.WebGLRenderer({antialias: true});
62 this.renderer.sortObjects = false; // hopefully improve performance
63 // 'antialias: true' now works in Firefox too!
64 // setting this.aaScale = 2 will enable antialias in older Firefox but GPU load increases.
65 this.renderer.domElement.style.width = "100%";
66 this.renderer.domElement.style.height = "100%";
67 this.container.append(this.renderer.domElement);
68 this.renderer.setSize(this.WIDTH, this.HEIGHT);
69
70 this.camera = new THREE.PerspectiveCamera(20, this.ASPECT, 1, 800); // will be updated anyway
71 this.camera.position = new TV3(0, 0, this.CAMERA_Z);
72 this.camera.lookAt(new TV3(0, 0, 0));
73 this.perspectiveCamera = this.camera;
74 this.orthoscopicCamera = new THREE.OrthographicCamera();
75 this.orthoscopicCamera.position.z = this.CAMERA_Z;
76 this.orthoscopicCamera.lookAt(new TV3(0, 0, 0));
77
78 var self = this;
79 $(window).resize(function() { // only window can capture resize event
80 self.WIDTH = self.container.width() * self.aaScale;
81 self.HEIGHT = self.container.height() * self.aaScale;
82 self.ASPECT = self.WIDTH / self.HEIGHT;
83 self.renderer.setSize(self.WIDTH, self.HEIGHT);
84 self.camera.aspect = self.ASPECT;
85 self.camera.updateProjectionMatrix();
86 self.show();
87 });
88
89 this.scene = null;
90 this.rotationGroup = null; // which contains modelGroup
91 this.modelGroup = null;
92
93 this.bgColor = 0x000000;
94 this.fov = 20;
95 this.fogStart = 0.4;
96 this.slabNear = -50; // relative to the center of rotationGroup
97 this.slabFar = +50;
98
99 // Default values
100 this.sphereRadius = 1.5;
101 this.cylinderRadius = 0.4;
102 this.lineWidth = 1.5 * this.aaScale;
103 this.curveWidth = 3 * this.aaScale;
104 this.defaultColor = 0xCCCCCC;
105 this.sphereQuality = 16; //16;
106 this.cylinderQuality = 16; //8;
107 this.axisDIV = 5; // 3 still gives acceptable quality
108 this.strandDIV = 6;
109 this.nucleicAcidStrandDIV = 4;
110 this.tubeDIV = 8;
111 this.coilWidth = 0.3;
112 this.helixSheetWidth = 1.3;
113 this.nucleicAcidWidth = 0.8;
114 this.thickness = 0.4;
115
116 // UI variables
117 this.cq = new THREE.Quaternion(1, 0, 0, 0);
118 this.dq = new THREE.Quaternion(1, 0, 0, 0);
119 this.isDragging = false;
120 this.mouseStartX = 0;
121 this.mouseStartY = 0;
122 this.currentModelPos = 0;
123 this.cz = 0;
124 this.enableMouse();
125
126 if (suppressAutoload) return;
127 this.loadMolecule();
128}
129
130GLmol.prototype.setupLights = function(scene) {
131 var directionalLight = new THREE.DirectionalLight(0xFFFFFF);
132 directionalLight.position = new TV3(0.2, 0.2, -1).normalize();
133 directionalLight.intensity = 1.2;
134 scene.add(directionalLight);
135 var ambientLight = new THREE.AmbientLight(0x202020);
136 scene.add(ambientLight);
137};
138
139GLmol.prototype.parseSDF = function(str) {
140 var atoms = this.atoms;
141 var protein = this.protein;
142
143 var lines = str.split("\n");
144 if (lines.length < 4) return;
145 var atomCount = parseInt(lines[3].substr(0, 3));
146 if (isNaN(atomCount) || atomCount <= 0) return;
147 var bondCount = parseInt(lines[3].substr(3, 3));
148 var offset = 4;
149 if (lines.length < 4 + atomCount + bondCount) return;
150 for (var i = 1; i <= atomCount; i++) {
151 var line = lines[offset];
152 offset++;
153 var atom = {};
154 atom.serial = i;
155 atom.x = parseFloat(line.substr(0, 10));
156 atom.y = parseFloat(line.substr(10, 10));
157 atom.z = parseFloat(line.substr(20, 10));
158 atom.hetflag = true;
159 atom.atom = atom.elem = line.substr(31, 3).replace(/ /g, "");
160 atom.bonds = [];
161 atom.bondOrder = [];
162 atoms[i] = atom;
163 }
164 for (i = 1; i <= bondCount; i++) {
165 var line = lines[offset];
166 offset++;
167 var from = parseInt(line.substr(0, 3));
168 var to = parseInt(line.substr(3, 3));
169 var order = parseInt(line.substr(6, 3));
170 atoms[from].bonds.push(to);
171 atoms[from].bondOrder.push(order);
172 atoms[to].bonds.push(from);
173 atoms[to].bondOrder.push(order);
174 }
175
176 protein.smallMolecule = true;
177 return true;
178};
179
180GLmol.prototype.parseXYZ = function(str) {
181 var atoms = this.atoms;
182 var protein = this.protein;
183
184 var lines = str.split("\n");
185 if (lines.length < 3) return;
186 var atomCount = parseInt(lines[0].substr(0, 3));
187 if (isNaN(atomCount) || atomCount <= 0) return;
188 if (lines.length < atomCount + 2) return;
189 var offset = 2;
190 for (var i = 1; i <= atomCount; i++) {
191 var line = lines[offset++];
192 var tokens = line.replace(/^\s+/, "").replace(/\s+/g," ").split(" ");
193 console.log(tokens);
194 var atom = {};
195 atom.serial = i;
196 atom.atom = atom.elem = tokens[0];
197 atom.x = parseFloat(tokens[1]);
198 atom.y = parseFloat(tokens[2]);
199 atom.z = parseFloat(tokens[3]);
200 atom.hetflag = true;
201 atom.bonds = [];
202 atom.bondOrder = [];
203 atoms[i] = atom;
204 }
205 for (var i = 1; i < atomCount; i++) // hopefully XYZ is small enough
206 for (var j = i + 1; j <= atomCount; j++)
207 if (this.isConnected(atoms[i], atoms[j])) {
208 atoms[i].bonds.push(j);
209 atoms[i].bondOrder.push(1);
210 atoms[j].bonds.push(i);
211 atoms[j].bondOrder.push(1);
212 }
213 protein.smallMolecule = true;
214 return true;
215};
216
217GLmol.prototype.parsePDB2 = function(str) {
218 var atoms = this.atoms;
219 var protein = this.protein;
220 var molID;
221
222 var atoms_cnt = 0;
223 lines = str.split("\n");
224 for (var i = 0; i < lines.length; i++) {
225 line = lines[i].replace(/^\s*/, ''); // remove indent
226 var recordName = line.substr(0, 6);
227 if (recordName == 'ATOM ' || recordName == 'HETATM') {
228 var atom, resn, chain, resi, x, y, z, hetflag, elem, serial, altLoc, b;
229 altLoc = line.substr(16, 1);
230 if (altLoc != ' ' && altLoc != 'A') continue; // FIXME: ad hoc
231 serial = parseInt(line.substr(6, 5));
232 atom = line.substr(12, 4).replace(/ /g, "");
233 resn = line.substr(17, 3);
234 chain = line.substr(21, 1);
235 resi = parseInt(line.substr(22, 5));
236 x = parseFloat(line.substr(30, 8));
237 y = parseFloat(line.substr(38, 8));
238 z = parseFloat(line.substr(46, 8));
239 b = parseFloat(line.substr(60, 8));
240 elem = line.substr(76, 2).replace(/ /g, "");
241 if (elem == '') { // for some incorrect PDB files
242 elem = line.substr(12, 4).replace(/ /g,"");
243 }
244 if (line[0] == 'H') hetflag = true;
245 else hetflag = false;
246 atoms[serial] = {'resn': resn, 'x': x, 'y': y, 'z': z, 'elem': elem,
247 'hetflag': hetflag, 'chain': chain, 'resi': resi, 'serial': serial, 'atom': atom,
248 'bonds': [], 'ss': 'c', 'color': 0xFFFFFF, 'bonds': [], 'bondOrder': [], 'b': b /*', altLoc': altLoc*/};
249 } else if (recordName == 'SHEET ') {
250 var startChain = line.substr(21, 1);
251 var startResi = parseInt(line.substr(22, 4));
252 var endChain = line.substr(32, 1);
253 var endResi = parseInt(line.substr(33, 4));
254 protein.sheet.push([startChain, startResi, endChain, endResi]);
255 } else if (recordName == 'CONECT') {
256// MEMO: We don't have to parse SSBOND, LINK because both are also
257// described in CONECT. But what about 2JYT???
258 var from = parseInt(line.substr(6, 5));
259 for (var j = 0; j < 4; j++) {
260 var to = parseInt(line.substr([11, 16, 21, 26][j], 5));
261 if (isNaN(to)) continue;
262 if (atoms[from] != undefined) {
263 atoms[from].bonds.push(to);
264 atoms[from].bondOrder.push(1);
265 }
266 }
267 } else if (recordName == 'HELIX ') {
268 var startChain = line.substr(19, 1);
269 var startResi = parseInt(line.substr(21, 4));
270 var endChain = line.substr(31, 1);
271 var endResi = parseInt(line.substr(33, 4));
272 protein.helix.push([startChain, startResi, endChain, endResi]);
273 } else if (recordName == 'CRYST1') {
274 protein.a = parseFloat(line.substr(6, 9));
275 protein.b = parseFloat(line.substr(15, 9));
276 protein.c = parseFloat(line.substr(24, 9));
277 protein.alpha = parseFloat(line.substr(33, 7));
278 protein.beta = parseFloat(line.substr(40, 7));
279 protein.gamma = parseFloat(line.substr(47, 7));
280 protein.spacegroup = line.substr(55, 11);
281 this.defineCell();
282 } else if (recordName == 'REMARK') {
283 var type = parseInt(line.substr(7, 3));
284 if (type == 290 && line.substr(13, 5) == 'SMTRY') {
285 var n = parseInt(line[18]) - 1;
286 var m = parseInt(line.substr(21, 2));
287 if (protein.symMat[m] == undefined) protein.symMat[m] = new THREE.Matrix4().identity();
288 protein.symMat[m].elements[n] = parseFloat(line.substr(24, 9));
289 protein.symMat[m].elements[n + 4] = parseFloat(line.substr(34, 9));
290 protein.symMat[m].elements[n + 8] = parseFloat(line.substr(44, 9));
291 protein.symMat[m].elements[n + 12] = parseFloat(line.substr(54, 10));
292 } else if (type == 350 && line.substr(13, 5) == 'BIOMT') {
293 var n = parseInt(line[18]) - 1;
294 var m = parseInt(line.substr(21, 2));
295 if (protein.biomtMatrices[m] == undefined) protein.biomtMatrices[m] = new THREE.Matrix4().identity();
296 protein.biomtMatrices[m].elements[n] = parseFloat(line.substr(24, 9));
297 protein.biomtMatrices[m].elements[n + 4] = parseFloat(line.substr(34, 9));
298 protein.biomtMatrices[m].elements[n + 8] = parseFloat(line.substr(44, 9));
299 protein.biomtMatrices[m].elements[n + 12] = parseFloat(line.substr(54, 10));
300 } else if (type == 350 && line.substr(11, 11) == 'BIOMOLECULE') {
301 protein.biomtMatrices = []; protein.biomtChains = '';
302 } else if (type == 350 && line.substr(34, 6) == 'CHAINS') {
303 protein.biomtChains += line.substr(41, 40);
304 }
305 } else if (recordName == 'HEADER') {
306 protein.pdbID = line.substr(62, 4);
307 } else if (recordName == 'TITLE ') {
308 if (protein.title == undefined) protein.title = "";
309 protein.title += line.substr(10, 70) + "\n"; // CHECK: why 60 is not enough???
310 } else if (recordName == 'COMPND') {
311 // TODO: Implement me!
312 }
313 }
314
315 // Assign secondary structures
316 for (i = 0; i < atoms.length; i++) {
317 atom = atoms[i]; if (atom == undefined) continue;
318
319 var found = false;
320 // MEMO: Can start chain and end chain differ?
321 for (j = 0; j < protein.sheet.length; j++) {
322 if (atom.chain != protein.sheet[j][0]) continue;
323 if (atom.resi < protein.sheet[j][1]) continue;
324 if (atom.resi > protein.sheet[j][3]) continue;
325 atom.ss = 's';
326 if (atom.resi == protein.sheet[j][1]) atom.ssbegin = true;
327 if (atom.resi == protein.sheet[j][3]) atom.ssend = true;
328 }
329 for (j = 0; j < protein.helix.length; j++) {
330 if (atom.chain != protein.helix[j][0]) continue;
331 if (atom.resi < protein.helix[j][1]) continue;
332 if (atom.resi > protein.helix[j][3]) continue;
333 atom.ss = 'h';
334 if (atom.resi == protein.helix[j][1]) atom.ssbegin = true;
335 else if (atom.resi == protein.helix[j][3]) atom.ssend = true;
336 }
337 }
338 protein.smallMolecule = false;
339 return true;
340};
341
342// Catmull-Rom subdivision
343GLmol.prototype.subdivide = function(_points, DIV) { // points as Vector3
344 var ret = [];
345 var points = _points;
346 points = new Array(); // Smoothing test
347 points.push(_points[0]);
348 for (var i = 1, lim = _points.length - 1; i < lim; i++) {
349 var p1 = _points[i], p2 = _points[i + 1];
350 if (p1.smoothen) points.push(new TV3((p1.x + p2.x) / 2, (p1.y + p2.y) / 2, (p1.z + p2.z) / 2));
351 else points.push(p1);
352 }
353 points.push(_points[_points.length - 1]);
354
355 for (var i = -1, size = points.length; i <= size - 3; i++) {
356 var p0 = points[(i == -1) ? 0 : i];
357 var p1 = points[i + 1], p2 = points[i + 2];
358 var p3 = points[(i == size - 3) ? size - 1 : i + 3];
359 var v0 = new TV3().sub(p2, p0).multiplyScalar(0.5);
360 var v1 = new TV3().sub(p3, p1).multiplyScalar(0.5);
361 for (var j = 0; j < DIV; j++) {
362 var t = 1.0 / DIV * j;
363 var x = p1.x + t * v0.x
364 + t * t * (-3 * p1.x + 3 * p2.x - 2 * v0.x - v1.x)
365 + t * t * t * (2 * p1.x - 2 * p2.x + v0.x + v1.x);
366 var y = p1.y + t * v0.y
367 + t * t * (-3 * p1.y + 3 * p2.y - 2 * v0.y - v1.y)
368 + t * t * t * (2 * p1.y - 2 * p2.y + v0.y + v1.y);
369 var z = p1.z + t * v0.z
370 + t * t * (-3 * p1.z + 3 * p2.z - 2 * v0.z - v1.z)
371 + t * t * t * (2 * p1.z - 2 * p2.z + v0.z + v1.z);
372 ret.push(new TV3(x, y, z));
373 }
374 }
375 ret.push(points[points.length - 1]);
376 return ret;
377};
378
379GLmol.prototype.drawAtomsAsSphere = function(group, atomlist, defaultRadius, forceDefault, scale) {
380 var sphereGeometry = new THREE.SphereGeometry(1, this.sphereQuality, this.sphereQuality); // r, seg, ring
381 for (var i = 0; i < atomlist.length; i++) {
382 var atom = this.atoms[atomlist[i]];
383 if (atom == undefined) continue;
384
385 var sphereMaterial = new THREE.MeshLambertMaterial({color: atom.color});
386 var sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
387 group.add(sphere);
388 var r = (!forceDefault && this.vdwRadii[atom.elem] != undefined) ? this.vdwRadii[atom.elem] : defaultRadius;
389 if (!forceDefault && scale) r *= scale;
390 sphere.scale.x = sphere.scale.y = sphere.scale.z = r;
391 sphere.position.x = atom.x;
392 sphere.position.y = atom.y;
393 sphere.position.z = atom.z;
394 }
395};
396
397// about two times faster than sphere when div = 2
398GLmol.prototype.drawAtomsAsIcosahedron = function(group, atomlist, defaultRadius, forceDefault) {
399 var geo = this.IcosahedronGeometry();
400 for (var i = 0; i < atomlist.length; i++) {
401 var atom = this.atoms[atomlist[i]];
402 if (atom == undefined) continue;
403
404 var mat = new THREE.MeshLambertMaterial({color: atom.color});
405 var sphere = new THREE.Mesh(geo, mat);
406 sphere.scale.x = sphere.scale.y = sphere.scale.z = (!forceDefault && this.vdwRadii[atom.elem] != undefined) ? this.vdwRadii[atom.elem] : defaultRadius;
407 group.add(sphere);
408 sphere.position.x = atom.x;
409 sphere.position.y = atom.y;
410 sphere.position.z = atom.z;
411 }
412};
413
414GLmol.prototype.isConnected = function(atom1, atom2) {
415 var s = atom1.bonds.indexOf(atom2.serial);
416 if (s != -1) return atom1.bondOrder[s];
417
418 if (this.protein.smallMolecule && (atom1.hetflag || atom2.hetflag)) return 0; // CHECK: or should I ?
419
420 var distSquared = (atom1.x - atom2.x) * (atom1.x - atom2.x) +
421 (atom1.y - atom2.y) * (atom1.y - atom2.y) +
422 (atom1.z - atom2.z) * (atom1.z - atom2.z);
423
424// if (atom1.altLoc != atom2.altLoc) return false;
425 if (isNaN(distSquared)) return 0;
426 if (distSquared < 0.5) return 0; // maybe duplicate position.
427
428 if (distSquared > 1.3 && (atom1.elem == 'H' || atom2.elem == 'H' || atom1.elem == 'D' || atom2.elem == 'D')) return 0;
429 if (distSquared < 3.42 && (atom1.elem == 'S' || atom2.elem == 'S')) return 1;
430 if (distSquared > 2.78) return 0;
431 return 1;
432};
433
434GLmol.prototype.drawBondAsStickSub = function(group, atom1, atom2, bondR, order) {
435 var delta, tmp;
436 if (order > 1) delta = this.calcBondDelta(atom1, atom2, bondR * 2.3);
437 var p1 = new TV3(atom1.x, atom1.y, atom1.z);
438 var p2 = new TV3(atom2.x, atom2.y, atom2.z);
439 var mp = p1.clone().addSelf(p2).multiplyScalar(0.5);
440
441 var c1 = new TCo(atom1.color), c2 = new TCo(atom2.color);
442 if (order == 1 || order == 3) {
443 this.drawCylinder(group, p1, mp, bondR, atom1.color);
444 this.drawCylinder(group, p2, mp, bondR, atom2.color);
445 }
446 if (order > 1) {
447 tmp = mp.clone().addSelf(delta);
448 this.drawCylinder(group, p1.clone().addSelf(delta), tmp, bondR, atom1.color);
449 this.drawCylinder(group, p2.clone().addSelf(delta), tmp, bondR, atom2.color);
450 tmp = mp.clone().subSelf(delta);
451 this.drawCylinder(group, p1.clone().subSelf(delta), tmp, bondR, atom1.color);
452 this.drawCylinder(group, p2.clone().subSelf(delta), tmp, bondR, atom2.color);
453 }
454};
455
456GLmol.prototype.drawBondsAsStick = function(group, atomlist, bondR, atomR, ignoreNonbonded, multipleBonds, scale) {
457 var sphereGeometry = new THREE.SphereGeometry(1, this.sphereQuality, this.sphereQuality);
458 var nAtoms = atomlist.length, mp;
459 var forSpheres = [];
460 if (!!multipleBonds) bondR /= 2.5;
461 for (var _i = 0; _i < nAtoms; _i++) {
462 var i = atomlist[_i];
463 var atom1 = this.atoms[i];
464 if (atom1 == undefined) continue;
465 for (var _j = _i + 1; _j < _i + 30 && _j < nAtoms; _j++) {
466 var j = atomlist[_j];
467 var atom2 = this.atoms[j];
468 if (atom2 == undefined) continue;
469 var order = this.isConnected(atom1, atom2);
470 if (order == 0) continue;
471 atom1.connected = atom2.connected = true;
472 this.drawBondAsStickSub(group, atom1, atom2, bondR, (!!multipleBonds) ? order : 1);
473 }
474 for (var _j = 0; _j < atom1.bonds.length; _j++) {
475 var j = atom1.bonds[_j];
476 if (j < i + 30) continue; // be conservative!
477 if (atomlist.indexOf(j) == -1) continue;
478 var atom2 = this.atoms[j];
479 if (atom2 == undefined) continue;
480 atom1.connected = atom2.connected = true;
481 this.drawBondAsStickSub(group, atom1, atom2, bondR, (!!multipleBonds) ? atom1.bondOrder[_j] : 1);
482 }
483 if (atom1.connected) forSpheres.push(i);
484 }
485 this.drawAtomsAsSphere(group, forSpheres, atomR, !scale, scale);
486};
487
488GLmol.prototype.defineCell = function() {
489 var p = this.protein;
490 if (p.a == undefined) return;
491
492 p.ax = p.a;
493 p.ay = 0;
494 p.az = 0;
495 p.bx = p.b * Math.cos(Math.PI / 180.0 * p.gamma);
496 p.by = p.b * Math.sin(Math.PI / 180.0 * p.gamma);
497 p.bz = 0;
498 p.cx = p.c * Math.cos(Math.PI / 180.0 * p.beta);
499 p.cy = p.c * (Math.cos(Math.PI / 180.0 * p.alpha) -
500 Math.cos(Math.PI / 180.0 * p.gamma)
501 * Math.cos(Math.PI / 180.0 * p.beta)
502 / Math.sin(Math.PI / 180.0 * p.gamma));
503 p.cz = Math.sqrt(p.c * p.c * Math.sin(Math.PI / 180.0 * p.beta)
504 * Math.sin(Math.PI / 180.0 * p.beta) - p.cy * p.cy);
505};
506
507GLmol.prototype.drawUnitcell = function(group) {
508 var p = this.protein;
509 if (p.a == undefined) return;
510
511 var vertices = [[0, 0, 0], [p.ax, p.ay, p.az], [p.bx, p.by, p.bz], [p.ax + p.bx, p.ay + p.by, p.az + p.bz],
512 [p.cx, p.cy, p.cz], [p.cx + p.ax, p.cy + p.ay, p.cz + p.az], [p.cx + p.bx, p.cy + p.by, p.cz + p.bz], [p.cx + p.ax + p.bx, p.cy + p.ay + p.by, p.cz + p.az + p.bz]];
513 var edges = [0, 1, 0, 2, 1, 3, 2, 3, 4, 5, 4, 6, 5, 7, 6, 7, 0, 4, 1, 5, 2, 6, 3, 7];
514
515 var geo = new THREE.Geometry();
516 for (var i = 0; i < edges.length; i++) {
517 geo.vertices.push(new TV3(vertices[edges[i]][0], vertices[edges[i]][1], vertices[edges[i]][2]));
518 }
519 var lineMaterial = new THREE.LineBasicMaterial({linewidth: 1, color: 0xcccccc});
520 var line = new THREE.Line(geo, lineMaterial);
521 line.type = THREE.LinePieces;
522 group.add(line);
523};
524
525// TODO: Find inner side of a ring
526GLmol.prototype.calcBondDelta = function(atom1, atom2, sep) {
527 var dot;
528 var axis = new TV3(atom1.x - atom2.x, atom1.y - atom2.y, atom1.z - atom2.z).normalize();
529 var found = null;
530 for (var i = 0; i < atom1.bonds.length && !found; i++) {
531 var atom = this.atoms[atom1.bonds[i]]; if (!atom) continue;
532 if (atom.serial != atom2.serial && atom.elem != 'H') found = atom;
533 }
534 for (var i = 0; i < atom2.bonds.length && !found; i++) {
535 var atom = this.atoms[atom2.bonds[i]]; if (!atom) continue;
536 if (atom.serial != atom1.serial && atom.elem != 'H') found = atom;
537 }
538 if (found) {
539 var tmp = new TV3(atom1.x - found.x, atom1.y - found.y, atom1.z - found.z).normalize();
540 dot = tmp.dot(axis);
541 delta = new TV3(tmp.x - axis.x * dot, tmp.y - axis.y * dot, tmp.z - axis.z * dot);
542 }
543 if (!found || Math.abs(dot - 1) < 0.001 || Math.abs(dot + 1) < 0.001) {
544 if (axis.x < 0.01 && axis.y < 0.01) {
545 delta = new TV3(0, -axis.z, axis.y);
546 } else {
547 delta = new TV3(-axis.y, axis.x, 0);
548 }
549 }
550 delta.normalize().multiplyScalar(sep);
551 return delta;
552};
553
554GLmol.prototype.drawBondsAsLineSub = function(geo, atom1, atom2, order) {
555 var delta, tmp, vs = geo.vertices, cs = geo.colors;
556 if (order > 1) delta = this.calcBondDelta(atom1, atom2, 0.15);
557 var p1 = new TV3(atom1.x, atom1.y, atom1.z);
558 var p2 = new TV3(atom2.x, atom2.y, atom2.z);
559 var mp = p1.clone().addSelf(p2).multiplyScalar(0.5);
560
561 var c1 = new TCo(atom1.color), c2 = new TCo(atom2.color);
562 if (order == 1 || order == 3) {
563 vs.push(p1); cs.push(c1); vs.push(mp); cs.push(c1);
564 vs.push(p2); cs.push(c2); vs.push(mp); cs.push(c2);
565 }
566 if (order > 1) {
567 vs.push(p1.clone().addSelf(delta)); cs.push(c1);
568 vs.push(tmp = mp.clone().addSelf(delta)); cs.push(c1);
569 vs.push(p2.clone().addSelf(delta)); cs.push(c2);
570 vs.push(tmp); cs.push(c2);
571 vs.push(p1.clone().subSelf(delta)); cs.push(c1);
572 vs.push(tmp = mp.clone().subSelf(delta)); cs.push(c1);
573 vs.push(p2.clone().subSelf(delta)); cs.push(c2);
574 vs.push(tmp); cs.push(c2);
575 }
576};
577
578GLmol.prototype.drawBondsAsLine = function(group, atomlist, lineWidth) {
579 var geo = new THREE.Geometry();
580 var nAtoms = atomlist.length;
581
582 for (var _i = 0; _i < nAtoms; _i++) {
583 var i = atomlist[_i];
584 var atom1 = this.atoms[i];
585 if (atom1 == undefined) continue;
586 for (var _j = _i + 1; _j < _i + 30 && _j < nAtoms; _j++) {
587 var j = atomlist[_j];
588 var atom2 = this.atoms[j];
589 if (atom2 == undefined) continue;
590 var order = this.isConnected(atom1, atom2);
591 if (order == 0) continue;
592
593 this.drawBondsAsLineSub(geo, atom1, atom2, order);
594 }
595 for (var _j = 0; _j < atom1.bonds.length; _j++) {
596 var j = atom1.bonds[_j];
597 if (j < i + 30) continue; // be conservative!
598 if (atomlist.indexOf(j) == -1) continue;
599 var atom2 = this.atoms[j];
600 if (atom2 == undefined) continue;
601 this.drawBondsAsLineSub(geo, atom1, atom2, atom1.bondOrder[_j]);
602 }
603 }
604 var lineMaterial = new THREE.LineBasicMaterial({linewidth: lineWidth});
605 lineMaterial.vertexColors = true;
606
607 var line = new THREE.Line(geo, lineMaterial);
608 line.type = THREE.LinePieces;
609 group.add(line);
610};
611
612GLmol.prototype.drawSmoothCurve = function(group, _points, width, colors, div) {
613 if (_points.length == 0) return;
614
615 div = (div == undefined) ? 5 : div;
616
617 var geo = new THREE.Geometry();
618 var points = this.subdivide(_points, div);
619
620 for (var i = 0; i < points.length; i++) {
621 geo.vertices.push(points[i]);
622 geo.colors.push(new TCo(colors[(i == 0) ? 0 : Math.round((i - 1) / div)]));
623 }
624 var lineMaterial = new THREE.LineBasicMaterial({linewidth: width});
625 lineMaterial.vertexColors = true;
626 var line = new THREE.Line(geo, lineMaterial);
627 line.type = THREE.LineStrip;
628 group.add(line);
629};
630
631GLmol.prototype.drawAsCross = function(group, atomlist, delta) {
632 var geo = new THREE.Geometry();
633 var points = [[delta, 0, 0], [-delta, 0, 0], [0, delta, 0], [0, -delta, 0], [0, 0, delta], [0, 0, -delta]];
634
635 for (var i = 0, lim = atomlist.length; i < lim; i++) {
636 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
637
638 var c = new TCo(atom.color);
639 for (var j = 0; j < 6; j++) {
640 geo.vertices.push(new TV3(atom.x + points[j][0], atom.y + points[j][1], atom.z + points[j][2]));
641 geo.colors.push(c);
642 }
643 }
644 var lineMaterial = new THREE.LineBasicMaterial({linewidth: this.lineWidth});
645 lineMaterial.vertexColors = true;
646 var line = new THREE.Line(geo, lineMaterial, THREE.LinePieces);
647 group.add(line);
648};
649
650// FIXME: Winkled...
651GLmol.prototype.drawSmoothTube = function(group, _points, colors, radii) {
652 if (_points.length < 2) return;
653
654 var circleDiv = this.tubeDIV, axisDiv = this.axisDIV;
655 var geo = new THREE.Geometry();
656 var points = this.subdivide(_points, axisDiv);
657 var prevAxis1 = new TV3(), prevAxis2;
658
659 for (var i = 0, lim = points.length; i < lim; i++) {
660 var r, idx = (i - 1) / axisDiv;
661 if (i == 0) r = radii[0];
662 else {
663 if (idx % 1 == 0) r = radii[idx];
664 else {
665 var floored = Math.floor(idx);
666 var tmp = idx - floored;
667 r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp);
668 }
669 }
670 var delta, axis1, axis2;
671
672 if (i < lim - 1) {
673 delta = new TV3().sub(points[i], points[i + 1]);
674 axis1 = new TV3(0, - delta.z, delta.y).normalize().multiplyScalar(r);
675 axis2 = new TV3().cross(delta, axis1).normalize().multiplyScalar(r);
676// var dir = 1, offset = 0;
677 if (prevAxis1.dot(axis1) < 0) {
678 axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv;
679 }
680 prevAxis1 = axis1; prevAxis2 = axis2;
681 } else {
682 axis1 = prevAxis1; axis2 = prevAxis2;
683 }
684
685 for (var j = 0; j < circleDiv; j++) {
686 var angle = 2 * Math.PI / circleDiv * j; //* dir + offset;
687 var c = Math.cos(angle), s = Math.sin(angle);
688 geo.vertices.push(new TV3(
689 points[i].x + c * axis1.x + s * axis2.x,
690 points[i].y + c * axis1.y + s * axis2.y,
691 points[i].z + c * axis1.z + s * axis2.z));
692 }
693 }
694
695 var offset = 0;
696 for (var i = 0, lim = points.length - 1; i < lim; i++) {
697 var c = new TCo(colors[Math.round((i - 1)/ axisDiv)]);
698
699 var reg = 0;
700 var r1 = new TV3().sub(geo.vertices[offset], geo.vertices[offset + circleDiv]).lengthSq();
701 var r2 = new TV3().sub(geo.vertices[offset], geo.vertices[offset + circleDiv + 1]).lengthSq();
702 if (r1 > r2) {r1 = r2; reg = 1;};
703 for (var j = 0; j < circleDiv; j++) {
704 geo.faces.push(new TF3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv));
705 geo.faces.push(new TF3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv));
706 geo.faces[geo.faces.length -2].color = c;
707 geo.faces[geo.faces.length -1].color = c;
708 }
709 offset += circleDiv;
710 }
711 geo.computeFaceNormals();
712 geo.computeVertexNormals(false);
713 var mat = new THREE.MeshLambertMaterial();
714 mat.vertexColors = THREE.FaceColors;
715 var mesh = new THREE.Mesh(geo, mat);
716 mesh.doubleSided = true;
717 group.add(mesh);
718};
719
720
721GLmol.prototype.drawMainchainCurve = function(group, atomlist, curveWidth, atomName, div) {
722 var points = [], colors = [];
723 var currentChain, currentResi;
724 if (div == undefined) div = 5;
725
726 for (var i in atomlist) {
727 var atom = this.atoms[atomlist[i]];
728 if (atom == undefined) continue;
729
730 if ((atom.atom == atomName) && !atom.hetflag) {
731 if (currentChain != atom.chain || currentResi + 1 != atom.resi) {
732 this.drawSmoothCurve(group, points, curveWidth, colors, div);
733 points = [];
734 colors = [];
735 }
736 points.push(new TV3(atom.x, atom.y, atom.z));
737 colors.push(atom.color);
738 currentChain = atom.chain;
739 currentResi = atom.resi;
740 }
741 }
742 this.drawSmoothCurve(group, points, curveWidth, colors, div);
743};
744
745GLmol.prototype.drawMainchainTube = function(group, atomlist, atomName, radius) {
746 var points = [], colors = [], radii = [];
747 var currentChain, currentResi;
748 for (var i in atomlist) {
749 var atom = this.atoms[atomlist[i]];
750 if (atom == undefined) continue;
751
752 if ((atom.atom == atomName) && !atom.hetflag) {
753 if (currentChain != atom.chain || currentResi + 1 != atom.resi) {
754 this.drawSmoothTube(group, points, colors, radii);
755 points = []; colors = []; radii = [];
756 }
757 points.push(new TV3(atom.x, atom.y, atom.z));
758 if (radius == undefined) {
759 radii.push((atom.b > 0) ? atom.b / 100 : 0.3);
760 } else {
761 radii.push(radius);
762 }
763 colors.push(atom.color);
764 currentChain = atom.chain;
765 currentResi = atom.resi;
766 }
767 }
768 this.drawSmoothTube(group, points, colors, radii);
769};
770
771GLmol.prototype.drawStrip = function(group, p1, p2, colors, div, thickness) {
772 if ((p1.length) < 2) return;
773 div = div || this.axisDIV;
774 p1 = this.subdivide(p1, div);
775 p2 = this.subdivide(p2, div);
776 if (!thickness) return this.drawThinStrip(group, p1, p2, colors, div);
777
778 var geo = new THREE.Geometry();
779 var vs = geo.vertices, fs = geo.faces;
780 var axis, p1v, p2v, a1v, a2v;
781 for (var i = 0, lim = p1.length; i < lim; i++) {
782 vs.push(p1v = p1[i]); // 0
783 vs.push(p1v); // 1
784 vs.push(p2v = p2[i]); // 2
785 vs.push(p2v); // 3
786 if (i < lim - 1) {
787 var toNext = p1[i + 1].clone().subSelf(p1[i]);
788 var toSide = p2[i].clone().subSelf(p1[i]);
789 axis = toSide.crossSelf(toNext).normalize().multiplyScalar(thickness);
790 }
791 vs.push(a1v = p1[i].clone().addSelf(axis)); // 4
792 vs.push(a1v); // 5
793 vs.push(a2v = p2[i].clone().addSelf(axis)); // 6
794 vs.push(a2v); // 7
795 }
796 var faces = [[0, 2, -6, -8], [-4, -2, 6, 4], [7, 3, -5, -1], [-3, -7, 1, 5]];
797 for (var i = 1, lim = p1.length; i < lim; i++) {
798 var offset = 8 * i, color = new TCo(colors[Math.round((i - 1)/ div)]);
799 for (var j = 0; j < 4; j++) {
800 var f = new THREE.Face4(offset + faces[j][0], offset + faces[j][1], offset + faces[j][2], offset + faces[j][3], undefined, color);
801 fs.push(f);
802 }
803 }
804 var vsize = vs.length - 8; // Cap
805 for (var i = 0; i < 4; i++) {vs.push(vs[i * 2]); vs.push(vs[vsize + i * 2])};
806 vsize += 8;
807 fs.push(new THREE.Face4(vsize, vsize + 2, vsize + 6, vsize + 4, undefined, fs[0].color));
808 fs.push(new THREE.Face4(vsize + 1, vsize + 5, vsize + 7, vsize + 3, undefined, fs[fs.length - 3].color));
809 geo.computeFaceNormals();
810 geo.computeVertexNormals(false);
811 var material = new THREE.MeshLambertMaterial();
812 material.vertexColors = THREE.FaceColors;
813 var mesh = new THREE.Mesh(geo, material);
814 mesh.doubleSided = true;
815 group.add(mesh);
816};
817
818
819GLmol.prototype.drawThinStrip = function(group, p1, p2, colors, div) {
820 var geo = new THREE.Geometry();
821 for (var i = 0, lim = p1.length; i < lim; i++) {
822 geo.vertices.push(p1[i]); // 2i
823 geo.vertices.push(p2[i]); // 2i + 1
824 }
825 for (var i = 1, lim = p1.length; i < lim; i++) {
826 var f = new THREE.Face4(2 * i, 2 * i + 1, 2 * i - 1, 2 * i - 2);
827 f.color = new TCo(colors[Math.round((i - 1)/ div)]);
828 geo.faces.push(f);
829 }
830 geo.computeFaceNormals();
831 geo.computeVertexNormals(false);
832 var material = new THREE.MeshLambertMaterial();
833 material.vertexColors = THREE.FaceColors;
834 var mesh = new THREE.Mesh(geo, material);
835 mesh.doubleSided = true;
836 group.add(mesh);
837};
838
839
840GLmol.prototype.IcosahedronGeometry = function() {
841 if (!this.icosahedron) this.icosahedron = new THREE.IcosahedronGeometry(1);
842 return this.icosahedron;
843};
844
845GLmol.prototype.drawCylinder = function(group, from, to, radius, color, cap) {
846 if (!from || !to) return;
847
848 var midpoint = new TV3().add(from, to).multiplyScalar(0.5);
849 var color = new TCo(color);
850
851 if (!this.cylinderGeometry) {
852 this.cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, this.cylinderQuality, 1, !cap);
853 this.cylinderGeometry.faceUvs = [];
854 this.faceVertexUvs = [];
855 }
856 var cylinderMaterial = new THREE.MeshLambertMaterial({color: color.getHex()});
857 var cylinder = new THREE.Mesh(this.cylinderGeometry, cylinderMaterial);
858 cylinder.position = midpoint;
859 cylinder.lookAt(from);
860 cylinder.updateMatrix();
861 cylinder.matrixAutoUpdate = false;
862 var m = new THREE.Matrix4().makeScale(radius, radius, from.distanceTo(to));
863 m.rotateX(Math.PI / 2);
864 cylinder.matrix.multiplySelf(m);
865 group.add(cylinder);
866};
867
868// FIXME: transition!
869GLmol.prototype.drawHelixAsCylinder = function(group, atomlist, radius) {
870 var start = null;
871 var currentChain, currentResi;
872
873 var others = [], beta = [];
874
875 for (var i in atomlist) {
876 var atom = this.atoms[atomlist[i]];
877 if (atom == undefined || atom.hetflag) continue;
878 if ((atom.ss != 'h' && atom.ss != 's') || atom.ssend || atom.ssbegin) others.push(atom.serial);
879 if (atom.ss == 's') beta.push(atom.serial);
880 if (atom.atom != 'CA') continue;
881
882 if (atom.ss == 'h' && atom.ssend) {
883 if (start != null) this.drawCylinder(group, new TV3(start.x, start.y, start.z), new TV3(atom.x, atom.y, atom.z), radius, atom.color, true);
884 start = null;
885 }
886 currentChain = atom.chain;
887 currentResi = atom.resi;
888 if (start == null && atom.ss == 'h' && atom.ssbegin) start = atom;
889 }
890 if (start != null) this.drawCylinder(group, new TV3(start.x, start.y, start.z), new TV3(atom.x, atom.y, atom.z), radius, atom.color);
891 this.drawMainchainTube(group, others, "CA", 0.3);
892 this.drawStrand(group, beta, undefined, undefined, true, 0, this.helixSheetWidth, false, this.thickness * 2);
893};
894
895GLmol.prototype.drawCartoon = function(group, atomlist, doNotSmoothen, thickness) {
896 this.drawStrand(group, atomlist, 2, undefined, true, undefined, undefined, doNotSmoothen, thickness);
897};
898
899GLmol.prototype.drawStrand = function(group, atomlist, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness) {
900 num = num || this.strandDIV;
901 div = div || this.axisDIV;
902 coilWidth = coilWidth || this.coilWidth;
903 doNotSmoothen == (doNotSmoothen == undefined) ? false : doNotSmoothen;
904 helixSheetWidth = helixSheetWidth || this.helixSheetWidth;
905 var points = []; for (var k = 0; k < num; k++) points[k] = [];
906 var colors = [];
907 var currentChain, currentResi, currentCA;
908 var prevCO = null, ss=null, ssborder = false;
909
910 for (var i in atomlist) {
911 var atom = this.atoms[atomlist[i]];
912 if (atom == undefined) continue;
913
914 if ((atom.atom == 'O' || atom.atom == 'CA') && !atom.hetflag) {
915 if (atom.atom == 'CA') {
916 if (currentChain != atom.chain || currentResi + 1 != atom.resi) {
917 for (var j = 0; !thickness && j < num; j++)
918 this.drawSmoothCurve(group, points[j], 1 ,colors, div);
919 if (fill) this.drawStrip(group, points[0], points[num - 1], colors, div, thickness);
920 var points = []; for (var k = 0; k < num; k++) points[k] = [];
921 colors = [];
922 prevCO = null; ss = null; ssborder = false;
923 }
924 currentCA = new TV3(atom.x, atom.y, atom.z);
925 currentChain = atom.chain;
926 currentResi = atom.resi;
927 ss = atom.ss; ssborder = atom.ssstart || atom.ssend;
928 colors.push(atom.color);
929 } else { // O
930 var O = new TV3(atom.x, atom.y, atom.z);
931 O.subSelf(currentCA);
932 O.normalize(); // can be omitted for performance
933 O.multiplyScalar((ss == 'c') ? coilWidth : helixSheetWidth);
934 if (prevCO != undefined && O.dot(prevCO) < 0) O.negate();
935 prevCO = O;
936 for (var j = 0; j < num; j++) {
937 var delta = -1 + 2 / (num - 1) * j;
938 var v = new TV3(currentCA.x + prevCO.x * delta,
939 currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta);
940 if (!doNotSmoothen && ss == 's') v.smoothen = true;
941 points[j].push(v);
942 }
943 }
944 }
945 }
946 for (var j = 0; !thickness && j < num; j++)
947 this.drawSmoothCurve(group, points[j], 1 ,colors, div);
948 if (fill) this.drawStrip(group, points[0], points[num - 1], colors, div, thickness);
949};
950
951GLmol.prototype.drawNucleicAcidLadderSub = function(geo, lineGeo, atoms, color) {
952// color.r *= 0.9; color.g *= 0.9; color.b *= 0.9;
953 if (atoms[0] != undefined && atoms[1] != undefined && atoms[2] != undefined &&
954 atoms[3] != undefined && atoms[4] != undefined && atoms[5] != undefined) {
955 var baseFaceId = geo.vertices.length;
956 for (var i = 0; i <= 5; i++) geo.vertices.push(atoms[i]);
957 geo.faces.push(new TF3(baseFaceId, baseFaceId + 1, baseFaceId + 2));
958 geo.faces.push(new TF3(baseFaceId, baseFaceId + 2, baseFaceId + 3));
959 geo.faces.push(new TF3(baseFaceId, baseFaceId + 3, baseFaceId + 4));
960 geo.faces.push(new TF3(baseFaceId, baseFaceId + 4, baseFaceId + 5));
961 for (var j = geo.faces.length - 4, lim = geo.faces.length; j < lim; j++) geo.faces[j].color = color;
962 }
963 if (atoms[4] != undefined && atoms[3] != undefined && atoms[6] != undefined &&
964 atoms[7] != undefined && atoms[8] != undefined) {
965 var baseFaceId = geo.vertices.length;
966 geo.vertices.push(atoms[4]);
967 geo.vertices.push(atoms[3]);
968 geo.vertices.push(atoms[6]);
969 geo.vertices.push(atoms[7]);
970 geo.vertices.push(atoms[8]);
971 for (var i = 0; i <= 4; i++) geo.colors.push(color);
972 geo.faces.push(new TF3(baseFaceId, baseFaceId + 1, baseFaceId + 2));
973 geo.faces.push(new TF3(baseFaceId, baseFaceId + 2, baseFaceId + 3));
974 geo.faces.push(new TF3(baseFaceId, baseFaceId + 3, baseFaceId + 4));
975 for (var j = geo.faces.length - 3, lim = geo.faces.length; j < lim; j++) geo.faces[j].color = color;
976 }
977};
978
979GLmol.prototype.drawNucleicAcidLadder = function(group, atomlist) {
980 var geo = new THREE.Geometry();
981 var lineGeo = new THREE.Geometry();
982 var baseAtoms = ["N1", "C2", "N3", "C4", "C5", "C6", "N9", "C8", "N7"];
983 var currentChain, currentResi, currentComponent = new Array(baseAtoms.length);
984 var color = new TCo(0xcc0000);
985
986 for (var i in atomlist) {
987 var atom = this.atoms[atomlist[i]];
988 if (atom == undefined || atom.hetflag) continue;
989
990 if (atom.resi != currentResi || atom.chain != currentChain) {
991 this.drawNucleicAcidLadderSub(geo, lineGeo, currentComponent, color);
992 currentComponent = new Array(baseAtoms.length);
993 }
994 var pos = baseAtoms.indexOf(atom.atom);
995 if (pos != -1) currentComponent[pos] = new TV3(atom.x, atom.y, atom.z);
996 if (atom.atom == 'O3\'') color = new TCo(atom.color);
997 currentResi = atom.resi; currentChain = atom.chain;
998 }
999 this.drawNucleicAcidLadderSub(geo, lineGeo, currentComponent, color);
1000 geo.computeFaceNormals();
1001 var mat = new THREE.MeshLambertMaterial();
1002 mat.vertexColors = THREE.VertexColors;
1003 var mesh = new THREE.Mesh(geo, mat);
1004 mesh.doubleSided = true;
1005 group.add(mesh);
1006};
1007
1008GLmol.prototype.drawNucleicAcidStick = function(group, atomlist) {
1009 var currentChain, currentResi, start = null, end = null;
1010
1011 for (var i in atomlist) {
1012 var atom = this.atoms[atomlist[i]];
1013 if (atom == undefined || atom.hetflag) continue;
1014
1015 if (atom.resi != currentResi || atom.chain != currentChain) {
1016 if (start != null && end != null)
1017 this.drawCylinder(group, new TV3(start.x, start.y, start.z),
1018 new TV3(end.x, end.y, end.z), 0.3, start.color, true);
1019 start = null; end = null;
1020 }
1021 if (atom.atom == 'O3\'') start = atom;
1022 if (atom.resn == ' A' || atom.resn == ' G' || atom.resn == ' DA' || atom.resn == ' DG') {
1023 if (atom.atom == 'N1') end = atom; // N1(AG), N3(CTU)
1024 } else if (atom.atom == 'N3') {
1025 end = atom;
1026 }
1027 currentResi = atom.resi; currentChain = atom.chain;
1028 }
1029 if (start != null && end != null)
1030 this.drawCylinder(group, new TV3(start.x, start.y, start.z),
1031 new TV3(end.x, end.y, end.z), 0.3, start.color, true);
1032};
1033
1034GLmol.prototype.drawNucleicAcidLine = function(group, atomlist) {
1035 var currentChain, currentResi, start = null, end = null;
1036 var geo = new THREE.Geometry();
1037
1038 for (var i in atomlist) {
1039 var atom = this.atoms[atomlist[i]];
1040 if (atom == undefined || atom.hetflag) continue;
1041
1042 if (atom.resi != currentResi || atom.chain != currentChain) {
1043 if (start != null && end != null) {
1044 geo.vertices.push(new TV3(start.x, start.y, start.z));
1045 geo.colors.push(new TCo(start.color));
1046 geo.vertices.push(new TV3(end.x, end.y, end.z));
1047 geo.colors.push(new TCo(start.color));
1048 }
1049 start = null; end = null;
1050 }
1051 if (atom.atom == 'O3\'') start = atom;
1052 if (atom.resn == ' A' || atom.resn == ' G' || atom.resn == ' DA' || atom.resn == ' DG') {
1053 if (atom.atom == 'N1') end = atom; // N1(AG), N3(CTU)
1054 } else if (atom.atom == 'N3') {
1055 end = atom;
1056 }
1057 currentResi = atom.resi; currentChain = atom.chain;
1058 }
1059 if (start != null && end != null) {
1060 geo.vertices.push(new TV3(start.x, start.y, start.z));
1061 geo.colors.push(new TCo(start.color));
1062 geo.vertices.push(new TV3(end.x, end.y, end.z));
1063 geo.colors.push(new TCo(start.color));
1064 }
1065 var mat = new THREE.LineBasicMaterial({linewidth: 1, linejoin: false});
1066 mat.linewidth = 1.5; mat.vertexColors = true;
1067 var line = new THREE.Line(geo, mat, THREE.LinePieces);
1068 group.add(line);
1069};
1070
1071GLmol.prototype.drawCartoonNucleicAcid = function(group, atomlist, div, thickness) {
1072 this.drawStrandNucleicAcid(group, atomlist, 2, div, true, undefined, thickness);
1073};
1074
1075GLmol.prototype.drawStrandNucleicAcid = function(group, atomlist, num, div, fill, nucleicAcidWidth, thickness) {
1076 nucleicAcidWidth = nucleicAcidWidth || this.nucleicAcidWidth;
1077 div = div || this.axisDIV;
1078 num = num || this.nucleicAcidStrandDIV;
1079 var points = []; for (var k = 0; k < num; k++) points[k] = [];
1080 var colors = [];
1081 var currentChain, currentResi, currentO3;
1082 var prevOO = null;
1083
1084 for (var i in atomlist) {
1085 var atom = this.atoms[atomlist[i]];
1086 if (atom == undefined) continue;
1087
1088 if ((atom.atom == 'O3\'' || atom.atom == 'OP2') && !atom.hetflag) {
1089 if (atom.atom == 'O3\'') { // to connect 3' end. FIXME: better way to do?
1090 if (currentChain != atom.chain || currentResi + 1 != atom.resi) {
1091 if (currentO3) {
1092 for (var j = 0; j < num; j++) {
1093 var delta = -1 + 2 / (num - 1) * j;
1094 points[j].push(new TV3(currentO3.x + prevOO.x * delta,
1095 currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta));
1096 }
1097 }
1098 if (fill) this.drawStrip(group, points[0], points[1], colors, div, thickness);
1099 for (var j = 0; !thickness && j < num; j++)
1100 this.drawSmoothCurve(group, points[j], 1 ,colors, div);
1101 var points = []; for (var k = 0; k < num; k++) points[k] = [];
1102 colors = [];
1103 prevOO = null;
1104 }
1105 currentO3 = new TV3(atom.x, atom.y, atom.z);
1106 currentChain = atom.chain;
1107 currentResi = atom.resi;
1108 colors.push(atom.color);
1109 } else { // OP2
1110 if (!currentO3) {prevOO = null; continue;} // for 5' phosphate (e.g. 3QX3)
1111 var O = new TV3(atom.x, atom.y, atom.z);
1112 O.subSelf(currentO3);
1113 O.normalize().multiplyScalar(nucleicAcidWidth); // TODO: refactor
1114 if (prevOO != undefined && O.dot(prevOO) < 0) {
1115 O.negate();
1116 }
1117 prevOO = O;
1118 for (var j = 0; j < num; j++) {
1119 var delta = -1 + 2 / (num - 1) * j;
1120 points[j].push(new TV3(currentO3.x + prevOO.x * delta,
1121 currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta));
1122 }
1123 currentO3 = null;
1124 }
1125 }
1126 }
1127 if (currentO3) {
1128 for (var j = 0; j < num; j++) {
1129 var delta = -1 + 2 / (num - 1) * j;
1130 points[j].push(new TV3(currentO3.x + prevOO.x * delta,
1131 currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta));
1132 }
1133 }
1134 if (fill) this.drawStrip(group, points[0], points[1], colors, div, thickness);
1135 for (var j = 0; !thickness && j < num; j++)
1136 this.drawSmoothCurve(group, points[j], 1 ,colors, div);
1137};
1138
1139GLmol.prototype.drawDottedLines = function(group, points, color) {
1140 var geo = new THREE.Geometry();
1141 var step = 0.3;
1142
1143 for (var i = 0, lim = Math.floor(points.length / 2); i < lim; i++) {
1144 var p1 = points[2 * i], p2 = points[2 * i + 1];
1145 var delta = p2.clone().subSelf(p1);
1146 var dist = delta.length();
1147 delta.normalize().multiplyScalar(step);
1148 var jlim = Math.floor(dist / step);
1149 for (var j = 0; j < jlim; j++) {
1150 var p = new TV3(p1.x + delta.x * j, p1.y + delta.y * j, p1.z + delta.z * j);
1151 geo.vertices.push(p);
1152 }
1153 if (jlim % 2 == 1) geo.vertices.push(p2);
1154 }
1155
1156 var mat = new THREE.LineBasicMaterial({'color': color.getHex()});
1157 mat.linewidth = 2;
1158 var line = new THREE.Line(geo, mat, THREE.LinePieces);
1159 group.add(line);
1160};
1161
1162GLmol.prototype.getAllAtoms = function() {
1163 var ret = [];
1164 for (var i in this.atoms) {
1165 ret.push(this.atoms[i].serial);
1166 }
1167 return ret;
1168};
1169
1170// Probably I can refactor using higher-order functions.
1171GLmol.prototype.getHetatms = function(atomlist) {
1172 var ret = [];
1173 for (var i in atomlist) {
1174 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1175
1176 if (atom.hetflag) ret.push(atom.serial);
1177 }
1178 return ret;
1179};
1180
1181GLmol.prototype.removeSolvents = function(atomlist) {
1182 var ret = [];
1183 for (var i in atomlist) {
1184 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1185
1186 if (atom.resn != 'HOH') ret.push(atom.serial);
1187 }
1188 return ret;
1189};
1190
1191GLmol.prototype.getProteins = function(atomlist) {
1192 var ret = [];
1193 for (var i in atomlist) {
1194 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1195
1196 if (!atom.hetflag) ret.push(atom.serial);
1197 }
1198 return ret;
1199};
1200
1201// TODO: Test
1202GLmol.prototype.excludeAtoms = function(atomlist, deleteList) {
1203 var ret = [];
1204 var blackList = new Object();
1205 for (var _i in deleteList) blackList[deleteList[_i]] = true;
1206
1207 for (var _i in atomlist) {
1208 var i = atomlist[_i];
1209
1210 if (!blackList[i]) ret.push(i);
1211 }
1212 return ret;
1213};
1214
1215GLmol.prototype.getSidechains = function(atomlist) {
1216 var ret = [];
1217 for (var i in atomlist) {
1218 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1219
1220 if (atom.hetflag) continue;
1221 if (atom.atom == 'C' || atom.atom == 'O' || (atom.atom == 'N' && atom.resn != "PRO")) continue;
1222 ret.push(atom.serial);
1223 }
1224 return ret;
1225};
1226
1227GLmol.prototype.getAtomsWithin = function(atomlist, extent) {
1228 var ret = [];
1229
1230 for (var i in atomlist) {
1231 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1232
1233 if (atom.x < extent[0][0] || atom.x > extent[1][0]) continue;
1234 if (atom.y < extent[0][1] || atom.y > extent[1][1]) continue;
1235 if (atom.z < extent[0][2] || atom.z > extent[1][2]) continue;
1236 ret.push(atom.serial);
1237 }
1238 return ret;
1239};
1240
1241GLmol.prototype.getExtent = function(atomlist) {
1242 var xmin = ymin = zmin = 9999;
1243 var xmax = ymax = zmax = -9999;
1244 var xsum = ysum = zsum = cnt = 0;
1245
1246 for (var i in atomlist) {
1247 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1248 cnt++;
1249 xsum += atom.x; ysum += atom.y; zsum += atom.z;
1250
1251 xmin = (xmin < atom.x) ? xmin : atom.x;
1252 ymin = (ymin < atom.y) ? ymin : atom.y;
1253 zmin = (zmin < atom.z) ? zmin : atom.z;
1254 xmax = (xmax > atom.x) ? xmax : atom.x;
1255 ymax = (ymax > atom.y) ? ymax : atom.y;
1256 zmax = (zmax > atom.z) ? zmax : atom.z;
1257 }
1258 return [[xmin, ymin, zmin], [xmax, ymax, zmax], [xsum / cnt, ysum / cnt, zsum / cnt]];
1259};
1260
1261GLmol.prototype.getResiduesById = function(atomlist, resi) {
1262 var ret = [];
1263 for (var i in atomlist) {
1264 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1265
1266 if (resi.indexOf(atom.resi) != -1) ret.push(atom.serial);
1267 }
1268 return ret;
1269};
1270
1271GLmol.prototype.getResidueBySS = function(atomlist, ss) {
1272 var ret = [];
1273 for (var i in atomlist) {
1274 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1275
1276 if (ss.indexOf(atom.ss) != -1) ret.push(atom.serial);
1277 }
1278 return ret;
1279};
1280
1281GLmol.prototype.getChain = function(atomlist, chain) {
1282 var ret = [], chains = {};
1283 chain = chain.toString(); // concat if Array
1284 for (var i = 0, lim = chain.length; i < lim; i++) chains[chain.substr(i, 1)] = true;
1285 for (var i in atomlist) {
1286 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1287
1288 if (chains[atom.chain]) ret.push(atom.serial);
1289 }
1290 return ret;
1291};
1292
1293// for HETATM only
1294GLmol.prototype.getNonbonded = function(atomlist, chain) {
1295 var ret = [];
1296 for (var i in atomlist) {
1297 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1298
1299 if (atom.hetflag && atom.bonds.length == 0) ret.push(atom.serial);
1300 }
1301 return ret;
1302};
1303
1304GLmol.prototype.colorByAtom = function(atomlist, colors) {
1305 for (var i in atomlist) {
1306 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1307
1308 var c = colors[atom.elem];
1309 if (c == undefined) c = this.ElementColors[atom.elem];
1310 if (c == undefined) c = this.defaultColor;
1311 atom.color = c;
1312 }
1313};
1314
1315
1316// MEMO: Color only CA. maybe I should add atom.cartoonColor.
1317GLmol.prototype.colorByStructure = function(atomlist, helixColor, sheetColor, colorSidechains) {
1318 for (var i in atomlist) {
1319 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1320
1321 if (!colorSidechains && (atom.atom != 'CA' || atom.hetflag)) continue;
1322 if (atom.ss[0] == 's') atom.color = sheetColor;
1323 else if (atom.ss[0] == 'h') atom.color = helixColor;
1324 }
1325};
1326
1327GLmol.prototype.colorByBFactor = function(atomlist, colorSidechains) {
1328 var minB = 1000, maxB = -1000;
1329
1330 for (var i in atomlist) {
1331 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1332
1333 if (atom.hetflag) continue;
1334 if (colorSidechains || atom.atom == 'CA' || atom.atom == 'O3\'') {
1335 if (minB > atom.b) minB = atom.b;
1336 if (maxB < atom.b) maxB = atom.b;
1337 }
1338 }
1339
1340 var mid = (maxB + minB) / 2;
1341
1342 var range = (maxB - minB) / 2;
1343 if (range < 0.01 && range > -0.01) return;
1344 for (var i in atomlist) {
1345 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1346
1347 if (atom.hetflag) continue;
1348 if (colorSidechains || atom.atom == 'CA' || atom.atom == 'O3\'') {
1349 var color = new TCo(0);
1350 if (atom.b < mid)
1351 color.setHSV(0.667, (mid - atom.b) / range, 1);
1352 else
1353 color.setHSV(0, (atom.b - mid) / range, 1);
1354 atom.color = color.getHex();
1355 }
1356 }
1357};
1358
1359GLmol.prototype.colorByChain = function(atomlist, colorSidechains) {
1360 for (var i in atomlist) {
1361 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1362
1363 if (atom.hetflag) continue;
1364 if (colorSidechains || atom.atom == 'CA' || atom.atom == 'O3\'') {
1365 var color = new TCo(0);
1366 color.setHSV((atom.chain.charCodeAt(0) * 5) % 17 / 17.0, 1, 0.9);
1367 atom.color = color.getHex();
1368 }
1369 }
1370};
1371
1372GLmol.prototype.colorByResidue = function(atomlist, residueColors) {
1373 for (var i in atomlist) {
1374 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1375
1376 c = residueColors[atom.resn]
1377 if (c != undefined) atom.color = c;
1378 }
1379};
1380
1381GLmol.prototype.colorAtoms = function(atomlist, c) {
1382 for (var i in atomlist) {
1383 var atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1384
1385 atom.color = c;
1386 }
1387};
1388
1389GLmol.prototype.colorByPolarity = function(atomlist, polar, nonpolar) {
1390 var polarResidues = ['ARG', 'HIS', 'LYS', 'ASP', 'GLU', 'SER', 'THR', 'ASN', 'GLN', 'CYS'];
1391 var nonPolarResidues = ['GLY', 'PRO', 'ALA', 'VAL', 'LEU', 'ILE', 'MET', 'PHE', 'TYR', 'TRP'];
1392 var colorMap = {};
1393 for (var i in polarResidues) colorMap[polarResidues[i]] = polar;
1394 for (i in nonPolarResidues) colorMap[nonPolarResidues[i]] = nonpolar;
1395 this.colorByResidue(atomlist, colorMap);
1396};
1397
1398// TODO: Add near(atomlist, neighbor, distanceCutoff)
1399// TODO: Add expandToResidue(atomlist)
1400
1401GLmol.prototype.colorChainbow = function(atomlist, colorSidechains) {
1402 var cnt = 0;
1403 var atom, i;
1404 for (i in atomlist) {
1405 atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1406
1407 if ((colorSidechains || atom.atom != 'CA' || atom.atom != 'O3\'') && !atom.hetflag)
1408 cnt++;
1409 }
1410
1411 var total = cnt;
1412 cnt = 0;
1413 for (i in atomlist) {
1414 atom = this.atoms[atomlist[i]]; if (atom == undefined) continue;
1415
1416 if ((colorSidechains || atom.atom != 'CA' || atom.atom != 'O3\'') && !atom.hetflag) {
1417 var color = new TCo(0);
1418 color.setHSV(240.0 / 360 * (1 - cnt / total), 1, 0.9);
1419 atom.color = color.getHex();
1420 cnt++;
1421 }
1422 }
1423};
1424
1425GLmol.prototype.drawSymmetryMates2 = function(group, asu, matrices) {
1426 if (matrices == undefined) return;
1427 asu.matrixAutoUpdate = false;
1428
1429 var cnt = 1;
1430 this.protein.appliedMatrix = new THREE.Matrix4();
1431 for (var i = 0; i < matrices.length; i++) {
1432 var mat = matrices[i];
1433 if (mat == undefined || mat.isIdentity()) continue;
1434 console.log(mat);
1435 var symmetryMate = THREE.SceneUtils.cloneObject(asu);
1436 symmetryMate.matrix = mat;
1437 group.add(symmetryMate);
1438 for (var j = 0; j < 16; j++) this.protein.appliedMatrix.elements[j] += mat.elements[j];
1439 cnt++;
1440 }
1441 this.protein.appliedMatrix.multiplyScalar(cnt);
1442};
1443
1444
1445GLmol.prototype.drawSymmetryMatesWithTranslation2 = function(group, asu, matrices) {
1446 if (matrices == undefined) return;
1447 var p = this.protein;
1448 asu.matrixAutoUpdate = false;
1449
1450 for (var i = 0; i < matrices.length; i++) {
1451 var mat = matrices[i];
1452 if (mat == undefined) continue;
1453
1454 for (var a = -1; a <=0; a++) {
1455 for (var b = -1; b <= 0; b++) {
1456 for (var c = -1; c <= 0; c++) {
1457 var translationMat = new THREE.Matrix4().makeTranslation(
1458 p.ax * a + p.bx * b + p.cx * c,
1459 p.ay * a + p.by * b + p.cy * c,
1460 p.az * a + p.bz * b + p.cz * c);
1461 var symop = mat.clone().multiplySelf(translationMat);
1462 if (symop.isIdentity()) continue;
1463 var symmetryMate = THREE.SceneUtils.cloneObject(asu);
1464 symmetryMate.matrix = symop;
1465 group.add(symmetryMate);
1466 }
1467 }
1468 }
1469 }
1470};
1471
1472GLmol.prototype.defineRepresentation = function() {
1473 var all = this.getAllAtoms();
1474 var hetatm = this.removeSolvents(this.getHetatms(all));
1475 this.colorByAtom(all, {});
1476 this.colorByChain(all);
1477
1478 this.drawAtomsAsSphere(this.modelGroup, hetatm, this.sphereRadius);
1479 this.drawMainchainCurve(this.modelGroup, all, this.curveWidth, 'P');
1480 this.drawCartoon(this.modelGroup, all, this.curveWidth);
1481};
1482
1483GLmol.prototype.getView = function() {
1484 if (!this.modelGroup) return [0, 0, 0, 0, 0, 0, 0, 1];
1485 var pos = this.modelGroup.position;
1486 var q = this.rotationGroup.quaternion;
1487 return [pos.x, pos.y, pos.z, this.rotationGroup.position.z, q.x, q.y, q.z, q.w];
1488};
1489
1490GLmol.prototype.setView = function(arg) {
1491 if (!this.modelGroup || !this.rotationGroup) return;
1492 this.modelGroup.position.x = arg[0];
1493 this.modelGroup.position.y = arg[1];
1494 this.modelGroup.position.z = arg[2];
1495 this.rotationGroup.position.z = arg[3];
1496 this.rotationGroup.quaternion.x = arg[4];
1497 this.rotationGroup.quaternion.y = arg[5];
1498 this.rotationGroup.quaternion.z = arg[6];
1499 this.rotationGroup.quaternion.w = arg[7];
1500 this.show();
1501};
1502
1503GLmol.prototype.setBackground = function(hex, a) {
1504 a = a | 1.0;
1505 this.bgColor = hex;
1506 this.renderer.setClearColorHex(hex, a);
1507 this.scene.fog.color = new TCo(hex);
1508};
1509
1510GLmol.prototype.initializeScene = function() {
1511 // CHECK: Should I explicitly call scene.deallocateObject?
1512 this.scene = new THREE.Scene();
1513 this.scene.fog = new THREE.Fog(this.bgColor, 100, 200);
1514
1515 this.modelGroup = new THREE.Object3D();
1516 this.rotationGroup = new THREE.Object3D();
1517 this.rotationGroup.useQuaternion = true;
1518 this.rotationGroup.quaternion = new THREE.Quaternion(1, 0, 0, 0);
1519 this.rotationGroup.add(this.modelGroup);
1520
1521 this.scene.add(this.rotationGroup);
1522 this.setupLights(this.scene);
1523};
1524
1525GLmol.prototype.zoomInto = function(atomlist, keepSlab) {
1526 var tmp = this.getExtent(atomlist);
1527 var center = new TV3(tmp[2][0], tmp[2][1], tmp[2][2]);//(tmp[0][0] + tmp[1][0]) / 2, (tmp[0][1] + tmp[1][1]) / 2, (tmp[0][2] + tmp[1][2]) / 2);
1528 if (this.protein.appliedMatrix) {center = this.protein.appliedMatrix.multiplyVector3(center);}
1529 this.modelGroup.position = center.multiplyScalar(-1);
1530 var x = tmp[1][0] - tmp[0][0], y = tmp[1][1] - tmp[0][1], z = tmp[1][2] - tmp[0][2];
1531
1532 var maxD = Math.sqrt(x * x + y * y + z * z);
1533 if (maxD < 25) maxD = 25;
1534
1535 if (!keepSlab) {
1536 this.slabNear = -maxD / 1.9;
1537 this.slabFar = maxD / 3;
1538 }
1539
1540 this.rotationGroup.position.z = maxD * 0.35 / Math.tan(Math.PI / 180.0 * this.camera.fov / 2) - 150;
1541 this.rotationGroup.quaternion = new THREE.Quaternion(1, 0, 0, 0);
1542};
1543
1544GLmol.prototype.rebuildScene = function() {
1545 time = new Date();
1546
1547 var view = this.getView();
1548 this.initializeScene();
1549 this.defineRepresentation();
1550 this.setView(view);
1551
1552 console.log("builded scene in " + (+new Date() - time) + "ms");
1553};
1554
1555GLmol.prototype.loadMolecule = function(repressZoom) {
1556 this.loadMoleculeStr(repressZoom, $('#' + this.id + '_src').val());
1557};
1558
1559GLmol.prototype.loadMoleculeStr = function(repressZoom, source) {
1560 var time = new Date();
1561
1562 this.protein = {sheet: [], helix: [], biomtChains: '', biomtMatrices: [], symMat: [], pdbID: '', title: ''};
1563 this.atoms = [];
1564
1565 this.parsePDB2(source);
1566 if (!this.parseSDF(source)) this.parseXYZ(source);
1567 console.log("parsed in " + (+new Date() - time) + "ms");
1568
1569 var title = $('#' + this.id + '_pdbTitle');
1570 var titleStr = '';
1571 if (this.protein.pdbID != '') titleStr += '<a href="http://www.rcsb.org/pdb/explore/explore.do?structureId=' + this.protein.pdbID + '">' + this.protein.pdbID + '</a>';
1572 if (this.protein.title != '') titleStr += '<br>' + this.protein.title;
1573 title.html(titleStr);
1574
1575 this.rebuildScene(true);
1576 if (repressZoom == undefined || !repressZoom) this.zoomInto(this.getAllAtoms());
1577
1578 this.show();
1579 };
1580
1581GLmol.prototype.setSlabAndFog = function() {
1582 var center = this.rotationGroup.position.z - this.camera.position.z;
1583 if (center < 1) center = 1;
1584 this.camera.near = center + this.slabNear;
1585 if (this.camera.near < 1) this.camera.near = 1;
1586 this.camera.far = center + this.slabFar;
1587 if (this.camera.near + 1 > this.camera.far) this.camera.far = this.camera.near + 1;
1588 if (this.camera instanceof THREE.PerspectiveCamera) {
1589 this.camera.fov = this.fov;
1590 } else {
1591 this.camera.right = center * Math.tan(Math.PI / 180 * this.fov);
1592 this.camera.left = - this.camera.right;
1593 this.camera.top = this.camera.right / this.ASPECT;
1594 this.camera.bottom = - this.camera.top;
1595 }
1596 this.camera.updateProjectionMatrix();
1597 this.scene.fog.near = this.camera.near + this.fogStart * (this.camera.far - this.camera.near);
1598// if (this.scene.fog.near > center) this.scene.fog.near = center;
1599 this.scene.fog.far = this.camera.far;
1600};
1601
1602GLmol.prototype.enableMouse = function() {
1603 var me = this, glDOM = $(this.renderer.domElement);
1604
1605 // TODO: Better touch panel support.
1606 // Contribution is needed as I don't own any iOS or Android device with WebGL support.
1607 glDOM.bind('mousedown touchstart', function(ev) {
1608 ev.preventDefault();
1609 if (!me.scene) return;
1610 var x = ev.pageX, y = ev.pageY;
1611 if (ev.originalEvent.targetTouches && ev.originalEvent.targetTouches[0]) {
1612 x = ev.originalEvent.targetTouches[0].pageX;
1613 y = ev.originalEvent.targetTouches[0].pageY;
1614 }
1615 if (x == undefined) return;
1616 me.isDragging = true;
1617 me.mouseButton = ev.which;
1618 me.mouseStartX = x;
1619 me.mouseStartY = y;
1620 me.cq = me.rotationGroup.quaternion;
1621 me.cz = me.rotationGroup.position.z;
1622 me.currentModelPos = me.modelGroup.position.clone();
1623 me.cslabNear = me.slabNear;
1624 me.cslabFar = me.slabFar;
1625 });
1626
1627 glDOM.bind('DOMMouseScroll mousewheel', function(ev) { // Zoom
1628 ev.preventDefault();
1629 if (!me.scene) return;
1630 var scaleFactor = (me.rotationGroup.position.z - me.CAMERA_Z) * 0.85;
1631 if (ev.originalEvent.detail) { // Webkit
1632 me.rotationGroup.position.z += scaleFactor * ev.originalEvent.detail / 10;
1633 } else if (ev.originalEvent.wheelDelta) { // Firefox
1634 me.rotationGroup.position.z -= scaleFactor * ev.originalEvent.wheelDelta / 400;
1635 }
1636 console.log(ev.originalEvent.wheelDelta, ev.originalEvent.detail, me.rotationGroup.position.z);
1637 me.show();
1638 });
1639 glDOM.bind("contextmenu", function(ev) {ev.preventDefault();});
1640 $('body').bind('mouseup touchend', function(ev) {
1641 me.isDragging = false;
1642 });
1643
1644 glDOM.bind('mousemove touchmove', function(ev) { // touchmove
1645 ev.preventDefault();
1646 if (!me.scene) return;
1647 if (!me.isDragging) return;
1648 var mode = 0;
1649 var modeRadio = $('input[name=' + me.id + '_mouseMode]:checked');
1650 if (modeRadio.length > 0) mode = parseInt(modeRadio.val());
1651
1652 var x = ev.pageX, y = ev.pageY;
1653 if (ev.originalEvent.targetTouches && ev.originalEvent.targetTouches[0]) {
1654 x = ev.originalEvent.targetTouches[0].pageX;
1655 y = ev.originalEvent.targetTouches[0].pageY;
1656 }
1657 if (x == undefined) return;
1658 var dx = (x - me.mouseStartX) / me.WIDTH;
1659 var dy = (y - me.mouseStartY) / me.HEIGHT;
1660 var r = Math.sqrt(dx * dx + dy * dy);
1661 if (mode == 3 || (me.mouseButton == 3 && ev.ctrlKey)) { // Slab
1662 me.slabNear = me.cslabNear + dx * 100;
1663 me.slabFar = me.cslabFar + dy * 100;
1664 } else if (mode == 2 || me.mouseButton == 3 || ev.shiftKey) { // Zoom
1665 var scaleFactor = (me.rotationGroup.position.z - me.CAMERA_Z) * 0.85;
1666 if (scaleFactor < 80) scaleFactor = 80;
1667 me.rotationGroup.position.z = me.cz - dy * scaleFactor;
1668 } else if (mode == 1 || me.mouseButton == 2 || ev.ctrlKey) { // Translate
1669 var scaleFactor = (me.rotationGroup.position.z - me.CAMERA_Z) * 0.85;
1670 if (scaleFactor < 20) scaleFactor = 20;
1671 var translationByScreen = new TV3(- dx * scaleFactor, - dy * scaleFactor, 0);
1672 var q = me.rotationGroup.quaternion;
1673 var qinv = new THREE.Quaternion(q.x, q.y, q.z, q.w).inverse().normalize();
1674 var translation = qinv.multiplyVector3(translationByScreen);
1675 me.modelGroup.position.x = me.currentModelPos.x + translation.x;
1676 me.modelGroup.position.y = me.currentModelPos.y + translation.y;
1677 me.modelGroup.position.z = me.currentModelPos.z + translation.z;
1678 } else if ((mode == 0 || me.mouseButton == 1) && r != 0) { // Rotate
1679 var rs = Math.sin(r * Math.PI) / r;
1680 me.dq.x = Math.cos(r * Math.PI);
1681 me.dq.y = 0;
1682 me.dq.z = rs * dx;
1683 me.dq.w = rs * dy;
1684 me.rotationGroup.quaternion = new THREE.Quaternion(1, 0, 0, 0);
1685 me.rotationGroup.quaternion.multiplySelf(me.dq);
1686 me.rotationGroup.quaternion.multiplySelf(me.cq);
1687 }
1688 me.show();
1689 });
1690};
1691
1692
1693GLmol.prototype.show = function() {
1694 if (!this.scene) return;
1695
1696 var time = new Date();
1697 this.setSlabAndFog();
1698 this.renderer.render(this.scene, this.camera);
1699 console.log("rendered in " + (+new Date() - time) + "ms");
1700};
1701
1702// For scripting
1703GLmol.prototype.doFunc = function(func) {
1704 func(this);
1705};
1706
1707return GLmol;
1708}());