From 6b3cafc210eaa4ab81027f87583886b20583f713 Mon Sep 17 00:00:00 2001 From: "Wang, Jiyao" Date: Mon, 21 May 2018 13:13:42 -0400 Subject: [PATCH 1/3] speed up the rendering of assembly using instancing method --- README.md | 4 +- gulpfile.js | 18 +- icn3d.html | 3 + package.json | 2 +- src/icn3d/display.js | 1580 --------------- src/icn3d/display_common.js | 1098 ----------- src/icn3d/display_full.js | 501 ----- src/icn3d/display_simple.js | 37 - src/icn3d/drawing.js | 2240 ---------------------- src/icn3d/drawing_full.js | 243 --- src/icn3d/icn3d.js | 12 +- src/icn3d/loadpdb.js | 3 +- src/icn3d/other_full.js | 20 +- src/icn3dui/3dprint/threedprint.js | 6 +- src/icn3dui/annotations/annotations.js | 17 +- src/icn3dui/common_full_simple.js | 3 +- src/icn3dui/full_ui.js | 3 +- src/icn3dui/html/set_html.js | 13 +- src/icn3dui/parsers/mmcif_mmdb_parser.js | 201 +- src/icn3dui/parsers/mmtf_parser.js | 19 +- src/icn3dui/parsers/pdb_parser.js | 9 + src/icn3dui/selection/commands.js | 93 +- src/icn3dui/twoddiagram.js | 2 + 23 files changed, 326 insertions(+), 5801 deletions(-) delete mode 100644 src/icn3d/display.js delete mode 100644 src/icn3d/display_common.js delete mode 100644 src/icn3d/display_full.js delete mode 100644 src/icn3d/display_simple.js delete mode 100644 src/icn3d/drawing.js delete mode 100644 src/icn3d/drawing_full.js diff --git a/README.md b/README.md index 6f3f024a..91bd4d55 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ We provided two types of iCn3D widgets: [basic interface](https://www.ncbi.nlm.n Either of these widgets could be easily added to your own web pages. Please see the [help page](https://www.ncbi.nlm.nih.gov/Structure/icn3d/docs/icn3d_help.html#HowToUse) for more details. -Complete package of iCn3D including Three.js and jQuery can be downloaded from [https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.0.3.zip](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.0.3.zip). The "Download ZIP" link in this page does not include third-party libraries. +Complete package of iCn3D including Three.js and jQuery can be downloaded from [https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.0.zip](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.0.zip). The "Download ZIP" link in this page does not include third-party libraries. ## Usage @@ -77,6 +77,8 @@ gulp ``` ## Change log +The production version [icn3d-2.1.0](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.1.0.zip) was release on May 21, 2018. The instancing method is used to display a biological assembly. It significantly improved the rendering speed by sending only the geometry of its assymmetruic unit to GPU and applying transformation matrices to display the assembly. + The production version [icn3d-2.0.3](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.0.3.zip) was release on May 2, 2018. Removed the "Description" field when saving a set of atoms. This made "Share Link" URL shorter. Made the size of stabilizer thicker for 3D printing. The production version [icn3d-2.0.2](https://www.ncbi.nlm.nih.gov/Structure/icn3d/icn3d-2.0.2.zip) was release on April 30, 2018. Reset WebGLRenderer when WebGL context is lost in Internet Explore 11. diff --git a/gulpfile.js b/gulpfile.js index d290fdba..2885b8b0 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -112,15 +112,22 @@ var common_js = [ "src/shader/SphereImpostor.vert", "src/shader/CylinderImpostor.frag", "src/shader/CylinderImpostor.vert", + "src/shader/SphereInstancing.frag", + "src/shader/SphereInstancing.vert", + "src/shader/CylinderInstancing.frag", + "src/shader/CylinderInstancing.vert", + "src/shader/Instancing.frag", + "src/shader/Instancing.vert", "src/icn3d/icn3d.js", "src/icn3d/loadpdb.js", - "src/icn3d/drawing.js", - "src/icn3d/display_common.js", + "src/icn3d/draw/drawing.js", + "src/icn3d/display/display_common.js", + "src/icn3d/draw/impostor.js", "src/icn3d/other.js" ]; var simple_js = [ - "src/icn3d/display_simple.js" + "src/icn3d/display/display_simple.js" ]; var full_js = [ @@ -130,8 +137,9 @@ var full_js = [ "src/surface/marchingcube.js", "src/surface/ProteinSurface4.js", "src/surface/setupsurface.js", - "src/icn3d/drawing_full.js", - "src/icn3d/display_full.js", + "src/icn3d/draw/drawing_full.js", + "src/icn3d/display/display_full.js", + "src/icn3d/draw/instancing.js", "src/icn3d/other_full.js" ]; diff --git a/icn3d.html b/icn3d.html index 9ec5b782..853e452c 100644 --- a/icn3d.html +++ b/icn3d.html @@ -1693,6 +1693,9 @@

Methods    

Change Log:back to top

+The production version icn3d-2.1.0 was release on May 21, 2018. The instancing method is used to display a biological assembly. It significantly improved the rendering speed by sending only the geometry of its assymmetruic unit to GPU and applying transformation matrices to display the assembly. +

+ The production version icn3d-2.0.3 was release on May 2, 2018. Removed the "Description" field when saving a set of atoms. This made "Share Link" URL shorter. Made the size of stabilizer thicker for 3D printing.

diff --git a/package.json b/package.json index 47dd3a6a..504d0cc3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "icn3d", - "version": "2.0.3", + "version": "2.1.0", "description": "iCn3D Structure Viewer", "main": "index.html", "scripts": { diff --git a/src/icn3d/display.js b/src/icn3d/display.js deleted file mode 100644 index b25d2286..00000000 --- a/src/icn3d/display.js +++ /dev/null @@ -1,1580 +0,0 @@ - iCn3D.prototype.applyPrevColor = function () { - for (var i in this.atoms) { - var atom = this.atoms[i]; - atom.color = this.atomPrevColors[i]; - } - }; - - iCn3D.prototype.applyOriginalColor = function () { - for (var i in this.atoms) { - var atom = this.atoms[i]; - var chainid = atom.structure + '_' + atom.chain; - - if(this.chnsColor.hasOwnProperty(chainid)) { - atom.color = this.chnsColor[chainid]; - } - else { - //atom.color = this.atomColors[atom.elem]; - break; - } - } - }; - - iCn3D.prototype.setAtmClr = function(atoms, hex) { - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = new THREE.Color().setHex(hex); - - this.atomPrevColors[i] = atom.color; - } - }; - - iCn3D.prototype.setColorByOptions = function (options, atoms, bUseInputColor) { - if(options !== undefined) { - if(bUseInputColor !== undefined && bUseInputColor) { - for (var i in atoms) { - var atom = this.atoms[i]; - - this.atomPrevColors[i] = atom.color; - } - } - else if(options.color.indexOf("#") === 0) { - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = new THREE.Color().setStyle(options.color.toLowerCase()); - - this.atomPrevColors[i] = atom.color; - } - } - else { - switch (options.color.toLowerCase()) { - case 'spectrum': - var idx = 0; - //var lastTerSerialInv = 1 / this.lastTerSerial; - var lastTerSerialInv = 1 / this.cnt; - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : new THREE.Color().setHSL(2 / 3 * (1 - idx++ * lastTerSerialInv), 1, 0.45); - //atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'chain': - var index = -1, prevChain = '', colorLength = this.stdChainColors.length; - for (var i in atoms) { - var atom = this.atoms[i]; - - if(atom.chain != prevChain) { - ++index; - - index = index % colorLength; - } - - atom.color = this.stdChainColors[index]; - - if(Object.keys(this.chnsColor).length > 0) this.updateChainsColor(atom); - this.atomPrevColors[i] = atom.color; - - prevChain = atom.chain; - } - break; - case 'secondary structure': - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.ssColors[atom.ss]; - - this.atomPrevColors[i] = atom.color; - } - - break; - - case 'residue': - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.residueColors[atom.resn] || this.defaultResidueColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'charge': - for (var i in atoms) { - var atom = this.atoms[i]; - - //atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; - atom.color = atom.het ? this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'hydrophobic': - for (var i in atoms) { - var atom = this.atoms[i]; - - //atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; - atom.color = atom.het ? this.defaultAtomColor : this.hydrophobicColors[atom.resn] || this.defaultResidueColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'atom': - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; - - this.atomPrevColors[i] = atom.color; - } - break; - - case 'conserved': - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = this.defaultAtomColor; - - this.atomPrevColors[i] = atom.color; - } - - for(var chainid in this.alnChnsSeq) { - var resObjectArray = this.alnChnsSeq[chainid]; - - for(var i = 0, il = resObjectArray.length; i < il; ++i) { - var residueid = chainid + '_' + resObjectArray[i].resi; - - for(var j in this.resds[residueid]) { - if(atoms.hasOwnProperty(j)) { - var color = new THREE.Color(resObjectArray[i].color); - this.atoms[j].color = color; - this.atomPrevColors[j] = color; - } - } - } - } - break; - - case 'white': - this.setAtmClr(atoms, 0xFFFFFF); - break; - - case 'grey': - this.setAtmClr(atoms, 0x888888); - break; - - case 'red': - this.setAtmClr(atoms, 0xFF0000); - break; - case 'green': - this.setAtmClr(atoms, 0x00FF00); - break; - case 'blue': - this.setAtmClr(atoms, 0x0000FF); - break; - case 'magenta': - this.setAtmClr(atoms, 0xFF00FF); - break; - case 'yellow': - this.setAtmClr(atoms, 0xFFFF00); - break; - case 'cyan': - this.setAtmClr(atoms, 0x00FFFF); - break; - case 'custom': - // do the coloring separately - break; - - default: // the "#" was missed in order to make sharelink work - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = new THREE.Color().setStyle("#" + options.color.toLowerCase()); - - this.atomPrevColors[i] = atom.color; - } - - break; - } - } - } - }; - - iCn3D.prototype.updateChainsColor = function (atom) { - var chainid = atom.structure + '_' + atom.chain; - if(this.chnsColor[chainid] !== undefined) { // for mmdbid and align input - this.chnsColor[chainid] = atom.color; - } - }; - - iCn3D.prototype.applyLigandbindingOptions = function (options) { - if(options === undefined) options = this.opts; - - // display mode - if (options.ligandbinding === 'show') { - var startAtoms; - if(this.ligs !== undefined && Object.keys(this.ligs).length > 0) { // show ligand-protein interaction - startAtoms = this.hash2Atoms(this.ligs); - } - - // find atoms in chainid1, which interact with chainid2 - var radius = 4; - - if(startAtoms !== undefined) { - var targetAtoms = this.getAtomsWithinAtom(this.atoms, startAtoms, radius); - - var residueHash = {}; - - // draw sidec for these residues - for(var i in targetAtoms) { - if(startAtoms.hasOwnProperty(i)) continue; - residueHash[this.atoms[i].structure + '_' + this.atoms[i].chain + '_' + this.atoms[i].resi] = 1; - } - - var residueArray = Object.keys(residueHash); - for(var i = 0, il = residueArray.length; i < il; ++i) { - for(var j in this.resds[residueArray[i]]) { - // all atoms should be shown for hbonds - this.atoms[j].style2 = 'stick'; - } - } - - // show hydrogens - var threshold = 3.5; - this.opts["hbonds"] = "yes"; - //this.opts["water"] = "dot"; - - if(Object.keys(targetAtoms).length > 0) { - this.calculateLigandHbonds(startAtoms, targetAtoms, parseFloat(threshold) ); - } - - // zoom in on the atoms - this.zoominSelection( this.unionHash(startAtoms, targetAtoms) ); - - this.opts['fog'] = 'yes'; - } - } - else if (options.ligandbinding === 'hide') { - // truen off hdonds - this.hideHbonds(); - - // center on the atoms - this.zoominSelection(this.atoms); - - this.opts['fog'] = 'no'; - } - }; - - iCn3D.prototype.hideHbonds = function () { - this.opts["hbonds"] = "no"; - if(this.lines === undefined) this.lines = {}; - this.lines['hbond'] = []; - this.hbondpnts = []; - - for(var i in this.atoms) { - this.atoms[i].style2 = 'nothing'; - } - - for(var i in this.sidec) { - this.atoms[i].style2 = this.opts["sidec"]; - } - - for(var i in this.water) { - this.atoms[i].style = this.opts["water"]; - } - }; - - iCn3D.prototype.applySsbondsOptions = function (options) { - if(options === undefined) options = this.opts; - - if (options.ssbonds.toLowerCase() === 'yes' && this.ssbondpnts !== undefined) { - var color = '#FFFF00'; - var colorObj = new THREE.Color(0xFFFF00); - - var structureArray = Object.keys(this.strucs); - var start, end; - - if(this.bAlternate) { - start = this.ALTERNATE_STRUCTURE; - end = this.ALTERNATE_STRUCTURE + 1; - } - else { - start = 0; - end = structureArray.length; - } - - this.lines['ssbond'] = []; - - for(var s = start, sl = end; s < sl; ++s) { - var structure = structureArray[s]; - - if(this.ssbondpnts[structure] === undefined) continue; - - for(var i = 0, lim = Math.floor(this.ssbondpnts[structure].length / 2); i < lim; i++) { - var res1 = this.ssbondpnts[structure][2 * i], res2 = this.ssbondpnts[structure][2 * i + 1]; - - var line = {}; - line.color = color; - line.dashed = true; - - var bFound = false; - for(var j in this.resds[res1]) { - if(this.atoms[j].name === 'SG') { - line.position1 = this.atoms[j].coord; - bFound = true; - break; - } - } - - if(!bFound) { - for(var j in this.resds[res1]) { - if(this.atoms[j].name === 'CA') { - line.position1 = this.atoms[j].coord; - bFound = true; - break; - } - } - } - - bFound = false; - for(var j in this.resds[res2]) { - if(this.atoms[j].name === 'SG') { - line.position2 = this.atoms[j].coord; - bFound = true; - break; - } - } - - if(!bFound) { - for(var j in this.resds[res2]) { - if(this.atoms[j].name === 'CA') { - line.position2 = this.atoms[j].coord; - bFound = true; - break; - } - } - } - - //if(this.lines['ssbond'] === undefined) this.lines['ssbond'] = []; - this.lines['ssbond'].push(line); - - // create bonds for disulfide bonds - //this.createCylinder(line.position1, line.position2, this.cylinderRadius * 0.5, colorObj); - // make disulfide bonds slightly larger in case they were define already in MMDB - this.createCylinder(line.position1, line.position2, this.cylinderRadius * 1.1, colorObj); - - // show ball and stick for these two residues - var residueAtoms = this.unionHash(this.resds[res1], this.resds[res2]); - - // show side chains for the selected atoms - var atoms = this.intHash(residueAtoms, this.sidec); - var calpha_atoms = this.intHash(residueAtoms, this.calphas); - // include calphas - atoms = this.unionHash(atoms, calpha_atoms); - - // draw sidec separatedly - for(var j in atoms) { - this.atoms[j].style2 = 'stick'; - } - } // for(var i = 0, - } // for(var s = 0, - } // if (options.ssbonds.toLowerCase() === 'yes' - }; - - iCn3D.prototype.applyOtherOptions = function (options) { - if(options === undefined) options = this.opts; - - //common part options - // labels - this.createLabelRepresentation(this.labels); - - // lines - //if (options.hbonds.toLowerCase() === 'yes' || options.ncbonds.toLowerCase() === 'yes') { - if (options.hbonds.toLowerCase() === 'yes') { - var color = '#00FF00'; - var pnts = this.hbondpnts; - - for (var i = 0, lim = Math.floor(pnts.length / 2); i < lim; i++) { - var line = {}; - line.position1 = pnts[2 * i]; - line.position2 = pnts[2 * i + 1]; - line.color = color; - line.dashed = true; - - if(this.lines['hbond'] === undefined) this.lines['hbond'] = []; - this.lines['hbond'].push(line); - } - - //this.createLines(this.lines); - } - - if (options.hbondsin.toLowerCase() === 'yes') { - var color = '#FFFFFF'; - var pnts = this.hbondinpnts; - for (var i = 0, lim = Math.floor(pnts.length / 2); i < lim; i++) { - var line = {}; - line.position1 = pnts[2 * i]; - line.position2 = pnts[2 * i + 1]; - line.color = color; - line.dashed = false; // if true, there will be too many cylinders in the dashed lines - - if(this.lines['hbondin'] === undefined) this.lines['hbondin'] = []; - this.lines['hbondin'].push(line); - } - - //this.createLines(this.lines); - } - - this.createLines(this.lines); - - // surfaces - if(this.prevSurfaces !== undefined) { - for(var i = 0, il = this.prevSurfaces.length; i < il; ++i) { - this.mdl.add(this.prevSurfaces[i]); - } - } - - switch (options.rotationcenter.toLowerCase()) { - case 'molecule center': - // move the molecule to the origin - if(this.center !== undefined) { - this.setRotationCenter(this.center); - } - break; - case 'pick center': - if(this.pAtom !== undefined) { - this.setRotationCenter(this.pAtom.coord); - } - break; - case 'display center': - var center = this.centerAtoms(this.dAtoms).center; - this.setRotationCenter(center); - break; - case 'highlight center': - var center = this.centerAtoms(this.hAtoms).center; - this.setRotationCenter(center); - break; - } - switch (options.axis.toLowerCase()) { - case 'yes': - this.axis = true; - - this.buildAxes(this.maxD/2); - - break; - case 'no': - this.axis = false; - break; - } - switch (options.pk.toLowerCase()) { - case 'atom': - this.pk = 1; - break; - case 'no': - this.pk = 0; - break; - case 'residue': - this.pk = 2; - break; - case 'strand': - this.pk = 3; - break; - } - }; - - iCn3D.prototype.applySurfaceOptions = function (options) { - if(options === undefined) options = this.opts; - - //switch (options.wireframe.toLowerCase()) { - switch (options.wireframe) { - case 'yes': - options.wireframe = true; - break; - case 'no': - options.wireframe = false; - break; - } - - options.opacity = parseFloat(options.opacity); - - var atoms, currAtoms; - - // only show the surface for atoms which are displaying - atoms = this.intHash(this.dAtoms, this.hAtoms); - - currAtoms = this.hash2Atoms(atoms); - - switch (options.surface.toLowerCase()) { - case 'van der waals surface': - this.createSurfaceRepresentation(currAtoms, 1, options.wireframe, options.opacity); - break; -// case 'solvent excluded surface': -// this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); -// break; - case 'solvent accessible surface': - this.createSurfaceRepresentation(currAtoms, 3, options.wireframe, options.opacity); - break; - case 'molecular surface': - this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); - break; - case 'van der waals surface with context': - this.createSurfaceRepresentation(currAtoms, 1, options.wireframe, options.opacity); - break; - case 'solvent accessible surface with context': - this.createSurfaceRepresentation(currAtoms, 3, options.wireframe, options.opacity); - break; - case 'molecular surface with context': - this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); - break; - case 'nothing': - // remove surfaces - this.removeSurfaces(); - break; - } - }; - - iCn3D.prototype.applyDisplayOptions = function (options, atoms, bHighlight) { var me = this; // atoms: hash of key -> 1 - if(options === undefined) options = this.opts; - - var residueHash = {}; - var singletonResidueHash = {}; - var atomsObj = {}; - var residueid; - - if(bHighlight === 1 && Object.keys(atoms).length < Object.keys(this.atoms).length) { - atomsObj = this.hash2Atoms(atoms); - - for(var i in atomsObj) { - var atom = atomsObj[i]; - - residueid = atom.structure + '_' + atom.chain + '_' + atom.resi; - residueHash[residueid] = 1; - } - - // find singleton residues - for(var i in residueHash) { - var last = i.lastIndexOf('_'); - var base = i.substr(0, last + 1); - var lastResi = parseInt(i.substr(last + 1)); - - var prevResidueid = base + (lastResi - 1).toString(); - var nextResidueid = base + (lastResi + 1).toString(); - - if(!residueHash.hasOwnProperty(prevResidueid) && !residueHash.hasOwnProperty(prevResidueid)) { - singletonResidueHash[i] = 1; - } - } - - // show the only atom in a transparent box - if(Object.keys(atomsObj).length === 1 && Object.keys(this.resds[residueid]).length > 1 - && atomsObj[Object.keys(atomsObj)[0]].style !== 'sphere' && atomsObj[Object.keys(atomsObj)[0]].style !== 'dot') { - if(this.bCid === undefined || !this.bCid) { - for(var i in atomsObj) { - var atom = atomsObj[i]; - var scale = 1.0; - this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); - } - } - } - else { - // if only one residue, add the next residue in order to show highlight - for(var residueid in singletonResidueHash) { - var atom = this.getFirstAtomObj(this.resds[residueid]); - var prevResidueid = atom.structure + '_' + atom.chain + '_' + parseInt(atom.resi - 1); - var nextResidueid = atom.structure + '_' + atom.chain + '_' + parseInt(atom.resi + 1); - - //ribbon, strand, cylinder and plate, nucleotide cartoon, o3 trace, schematic, c alpha trace, b factor tube, lines, stick, ball and stick, sphere, dot - - if(atom.style === 'cylinder and plate' && atom.ss === 'helix') { // no way to highlight part of cylinder - for(var i in this.resds[residueid]) { - var atom = this.atoms[i]; - var scale = 1.0; - this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); - } - } - else if( (atom.style === 'ribbon' && atom.ss === 'coil') || (atom.style === 'strand' && atom.ss === 'coil') || atom.style === 'o3 trace' || atom.style === 'schematic' || atom.style === 'c alpha trace' || atom.style === 'b factor tube' || (atom.style === 'cylinder and plate' && atom.ss !== 'helix') ) { - var bAddResidue = false; - // add the next residue with same style - if(!bAddResidue && this.resds.hasOwnProperty(nextResidueid)) { - var index2 = Object.keys(this.resds[nextResidueid])[0]; - var atom2 = this.hash2Atoms(this.resds[nextResidueid])[index2]; - if( (atom.style === atom2.style && !atom2.ssbegin) || atom2.ssbegin) { - var residueAtoms = this.resds[nextResidueid]; - atoms = this.unionHash(atoms, residueAtoms); - - bAddResidue = true; - - // record the highlight style for the artificial residue - if(atom2.ssbegin) { - for(var i in residueAtoms) { - this.atoms[i].notshow = true; - } - } - } - } - - // add the previous residue with same style - if(!bAddResidue && this.resds.hasOwnProperty(prevResidueid)) { - var index2 = Object.keys(this.resds[prevResidueid])[0]; - var atom2 = this.hash2Atoms(this.resds[prevResidueid])[index2]; - if(atom.style === atom2.style) { - atoms = this.unionHash(atoms, this.resds[prevResidueid]); - - bAddResidue = true; - } - } - } - else if( (atom.style === 'ribbon' && atom.ss !== 'coil' && atom.ssend) || (atom.style === 'strand' && atom.ss !== 'coil' && atom.ssend)) { - var bAddResidue = false; - // add the next residue with same style - if(!bAddResidue && this.resds.hasOwnProperty(nextResidueid)) { - var index2 = Object.keys(this.resds[nextResidueid])[0]; - var atom2 = this.hash2Atoms(this.resds[nextResidueid])[index2]; - //if(atom.style === atom2.style && !atom2.ssbegin) { - atoms = this.unionHash(atoms, this.resds[nextResidueid]); - - bAddResidue = true; - //} - } - } - } // end for - } // end else { - - atomsObj = {}; - } // end if(bHighlight === 1) - - this.setStyle2Atoms(atoms); - - //this.bAllAtoms = (Object.keys(atoms).length === Object.keys(this.atoms).length); - this.bAllAtoms = false; - if(atoms && atoms !== undefined ) { - this.bAllAtoms = (Object.keys(atoms).length === Object.keys(this.atoms).length); - } - -// var currentCalphas = {}; -// if(this.opts['sidec'] !== 'nothing') { -// currentCalphas = this.intHash(this.hAtoms, this.calphas); -// } - - // remove schematic labels - if(this.labels && this.labels !== undefined) this.labels['schematic'] = []; - - var ligandSchematicRadius = this.cylinderRadius * 0.5; - - for(var style in this.style2atoms) { - // 14 styles: ribbon, strand, cylinder and plate, nucleotide cartoon, o3 trace, schematic, c alpha trace, b factor tube, lines, stick, ball and stick, sphere, dot, nothing - atomHash = this.style2atoms[style]; - var bPhosphorusOnly = this.isPhosphorusOnly(this.hash2Atoms(atomHash)); - - if(style === 'ribbon') { - this.createStrand(this.hash2Atoms(atomHash), 2, undefined, true, undefined, undefined, false, this.thickness, bHighlight); - } - else if(style === 'strand') { - this.createStrand(this.hash2Atoms(atomHash), null, null, null, null, null, false, undefined, bHighlight); - } - else if(style === 'cylinder and plate') { - this.createCylinderHelix(this.hash2Atoms(atomHash), this.cylinderHelixRadius, bHighlight); - } - else if(style === 'nucleotide cartoon') { - if(bPhosphorusOnly) { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.cylinderRadius, false, bHighlight); - } - else { - this.drawCartoonNucleicAcid(this.hash2Atoms(atomHash), null, this.thickness, bHighlight); - - if(bHighlight !== 2) this.drawNucleicAcidStick(this.hash2Atoms(atomHash), bHighlight); - } - } - else if(style === 'o3 trace') { - if(bPhosphorusOnly) { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.cylinderRadius, false, bHighlight); - } - else { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], this.cylinderRadius, false, bHighlight); - } - } - //else if(style === 'phosphorus lines') { - // this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], 0.2, true, bHighlight); - //} - else if(style === 'schematic') { - // either prtns, nucleotides, or ligands - var firstAtom = this.getFirstAtomObj(atomHash); - - //if(firstAtom.het) { // ligands - if(this.ligs.hasOwnProperty(firstAtom.serial)) { // ligands - this.addNonCarbonAtomLabels(this.hash2Atoms(atomHash)); - - bSchematic = true; - this.createStickRepresentation(this.hash2Atoms(atomHash), ligandSchematicRadius, ligandSchematicRadius, undefined, bHighlight, bSchematic); - } - else { // nucleotides or prtns - this.addResiudeLabels(this.hash2Atoms(atomHash), true); - - if(bPhosphorusOnly) { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.cylinderRadius, false, bHighlight); - } - else { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], this.cylinderRadius, false, bHighlight); - } - this.createCylinderCurve(this.hash2Atoms(atomHash), ['CA'], this.cylinderRadius, false, bHighlight); - } - } - else if(style === 'c alpha trace') { - this.createCylinderCurve(this.hash2Atoms(atomHash), ['CA'], this.cylinderRadius, false, bHighlight); - } - else if(style === 'b factor tube') { - this.createTube(this.hash2Atoms(atomHash), 'CA', null, bHighlight); - } - else if(style === 'lines') { - if(bHighlight === 1) { - this.createStickRepresentation(this.hash2Atoms(atomHash), this.hlLineRadius, this.hlLineRadius, undefined, bHighlight); - } - else { - this.createLineRepresentation(this.hash2Atoms(atomHash), bHighlight); - } - } - else if(style === 'stick') { - this.createStickRepresentation(this.hash2Atoms(atomHash), this.cylinderRadius, this.cylinderRadius, undefined, bHighlight); - } - else if(style === 'ball and stick') { - this.createStickRepresentation(this.hash2Atoms(atomHash), this.cylinderRadius, this.cylinderRadius * 0.5, this.dotSphereScale, bHighlight); - } - else if(style === 'sphere') { - this.createSphereRepresentation(this.hash2Atoms(atomHash), this.sphereRadius, undefined, undefined, bHighlight); - } - else if(style === 'dot') { - this.createSphereRepresentation(this.hash2Atoms(atomHash), this.sphereRadius, false, this.dotSphereScale, bHighlight); - } - } // end for loop - }; - - iCn3D.prototype.getShader = function (name) { var me = this; - var shaderText = $NGL_shaderTextHash[name]; - var reInclude = /#include\s+(\S+)/gmi; - - shaderText = shaderText.replace( reInclude, function( match, p1 ){ - - var chunk; - if(THREE.ShaderChunk.hasOwnProperty(p1)) { - chunk = THREE.ShaderChunk[ p1 ]; - } - - return chunk ? chunk : ""; - - } ); - - return shaderText; - }; - - iCn3D.prototype.createImpostorShaderCylinder = function (shaderName) { var me = this; - var shaderMaterial = - new THREE.ShaderMaterial({ - defines: me.defines, - uniforms: me.uniforms, - vertexShader: me.getShader(shaderName + ".vert"), - fragmentShader: me.getShader(shaderName + ".frag"), - depthTest: true, - depthWrite: true, - needsUpdate: true, - lights: true - }); - - shaderMaterial.extensions.fragDepth = true; - - var positions = new Float32Array( me.posArray ); - var colors = new Float32Array( me.colorArray ); - var positions2 = new Float32Array( me.pos2Array ); - var colors2 = new Float32Array( me.color2Array ); - var radii = new Float32Array( me.radiusArray ); - - // cylinder - var mapping = new Float32Array([ - -1.0, 1.0, -1.0, - -1.0, -1.0, -1.0, - 1.0, 1.0, -1.0, - 1.0, 1.0, 1.0, - 1.0, -1.0, -1.0, - 1.0, -1.0, 1.0 - ]); - - var mappingIndices = new Uint16Array([ - 0, 1, 2, - 1, 4, 2, - 2, 4, 3, - 4, 5, 3 - ]); - - var mappingIndicesSize = 12; - var mappingType = "v3"; - var mappingSize = 6; - var mappingItemSize = 3; - - - var count = positions.length / 3; - - var data = { - "position1": positions, - "color": colors, - "position2": positions2, - "color2": colors2, - "radius": radii - }; - - //MappedBuffer - var attributeSize = count * mappingSize; - - var n = count * mappingIndicesSize; - var TypedArray = attributeSize > 65535 ? Uint32Array : Uint16Array; - var index = new TypedArray( n ); - - //makeIndex(); - var ix, it; - - for( var v = 0; v < count; v++ ) { - ix = v * mappingIndicesSize; - it = v * mappingSize; - - index.set( mappingIndices, ix ); - - for( var s = 0; s < mappingIndicesSize; ++s ){ - index[ ix + s ] += it; - } - } - - - var geometry = new THREE.BufferGeometry(); - - // buffer.js - var dynamic = true; - - if( index ){ - geometry.setIndex( - new THREE.BufferAttribute( index, 1 ) - ); - geometry.getIndex().setDynamic( dynamic ); - } - - // add attributes from buffer.js - var itemSize = { - "f": 1, "v2": 2, "v3": 3, "c": 3 - }; - - var attributeData = { - "position1": { type: "v3", value: null }, - "color": { type: "v3", value: null }, - "position2": { type: "v3", value: null }, - "color2": { type: "v3", value: null }, - "radius": { type: "f", value: null }, - "mapping": { type: mappingType, value: null } - }; - - for( var name in attributeData ){ - - var buf; - var a = attributeData[ name ]; - - - //if( a.value ){ - // if( attributeSize * itemSize[ a.type ] !== a.value.length ){ - // Log.error( "attribute value has wrong length", name ); - // } - // buf = a.value; - //}else{ - - buf = new Float32Array( - attributeSize * itemSize[ a.type ] - ); - - //} - - geometry.addAttribute( - name, - new THREE.BufferAttribute( buf, itemSize[ a.type ] ) - .setDynamic( dynamic ) - ); - - } - - // set attributes from mapped-buffer.js - var attributes = geometry.attributes; - - var a, d, itemSize2, array, i, j; - - for( var name in data ){ - - d = data[ name ]; - a = attributes[ name ]; - itemSize2 = a.itemSize; - array = a.array; - - for( var k = 0; k < count; ++k ) { - - n = k * itemSize2; - i = n * mappingSize; - - for( var l = 0; l < mappingSize; ++l ) { - - j = i + ( itemSize2 * l ); - - for( var m = 0; m < itemSize2; ++m ) { - - array[ j + m ] = d[ n + m ]; - - } - - } - - } - - a.needsUpdate = true; - - } - - // makemapping - var aMapping = geometry.attributes.mapping.array; - - for( var v = 0; v < count; v++ ) { - aMapping.set( mapping, v * mappingItemSize * mappingSize ); - } - - - var mesh = new THREE.Mesh(geometry, shaderMaterial); - - // important: https://stackoverflow.com/questions/21184061/mesh-suddenly-disappears-in-three-js-clipping - // You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU - mesh.frustumCulled = false; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.0; - - this.mdlImpostor.add(mesh); - - //this.objects.push(mesh); - }; - - iCn3D.prototype.createImpostorShaderSphere = function (shaderName) { var me = this; - var shaderMaterial = - new THREE.ShaderMaterial({ - defines: me.defines, - uniforms: me.uniforms, - vertexShader: me.getShader(shaderName + ".vert"), - fragmentShader: me.getShader(shaderName + ".frag"), - depthTest: true, - depthWrite: true, - needsUpdate: true, - lights: true - }); - - shaderMaterial.extensions.fragDepth = true; - - var positions = new Float32Array( me.posArraySphere ); - var colors = new Float32Array( me.colorArraySphere ); - var radii = new Float32Array( me.radiusArraySphere ); - - // sphere - var mapping = new Float32Array([ - -1.0, 1.0, - -1.0, -1.0, - 1.0, 1.0, - 1.0, -1.0 - ]); - - var mappingIndices = new Uint16Array([ - 0, 1, 2, - 1, 3, 2 - ]); - - var mappingIndicesSize = 6; - var mappingType = "v2"; - var mappingSize = 4; - var mappingItemSize = 2; - - var count = positions.length / 3; - - var data = { - "position": positions, - "color": colors, - "radius": radii - }; - - //MappedBuffer - var attributeSize = count * mappingSize; - - var n = count * mappingIndicesSize; - var TypedArray = attributeSize > 65535 ? Uint32Array : Uint16Array; - var index = new TypedArray( n ); - - //makeIndex(); - var ix, it; - - for( var v = 0; v < count; v++ ) { - ix = v * mappingIndicesSize; - it = v * mappingSize; - - index.set( mappingIndices, ix ); - - for( var s = 0; s < mappingIndicesSize; ++s ){ - index[ ix + s ] += it; - } - } - - - var geometry = new THREE.BufferGeometry(); - - // buffer.js - var dynamic = true; - - if( index ){ - geometry.setIndex( - new THREE.BufferAttribute( index, 1 ) - ); - geometry.getIndex().setDynamic( dynamic ); - } - - // add attributes from buffer.js - var itemSize = { - "f": 1, "v2": 2, "v3": 3, "c": 3 - }; - - var attributeData = { - "position": { type: "v3", value: null }, - "color": { type: "v3", value: null }, - "radius": { type: "f", value: null }, - "mapping": { type: mappingType, value: null } - }; - - for( var name in attributeData ){ - - var buf; - var a = attributeData[ name ]; - - buf = new Float32Array( - attributeSize * itemSize[ a.type ] - ); - - geometry.addAttribute( - name, - new THREE.BufferAttribute( buf, itemSize[ a.type ] ) - .setDynamic( dynamic ) - ); - - } - - // set attributes from mapped-buffer.js - var attributes = geometry.attributes; - - var a, d, itemSize2, array, i, j; - - for( var name in data ){ - - d = data[ name ]; - a = attributes[ name ]; - itemSize2 = a.itemSize; - array = a.array; - - for( var k = 0; k < count; ++k ) { - - n = k * itemSize2; - i = n * mappingSize; - - for( var l = 0; l < mappingSize; ++l ) { - - j = i + ( itemSize2 * l ); - - for( var m = 0; m < itemSize2; ++m ) { - - array[ j + m ] = d[ n + m ]; - - } - - } - - } - - a.needsUpdate = true; - - } - - // makemapping - var aMapping = geometry.attributes.mapping.array; - - for( var v = 0; v < count; v++ ) { - aMapping.set( mapping, v * mappingItemSize * mappingSize ); - } - - var mesh = new THREE.Mesh(geometry, shaderMaterial); - - // important: https://stackoverflow.com/questions/21184061/mesh-suddenly-disappears-in-three-js-clipping - // You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU - mesh.frustumCulled = false; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = 1; - - this.mdlImpostor.add(mesh); - - //this.objects.push(mesh); - }; - - iCn3D.prototype.setStyle2Atoms = function (atoms) { - this.style2atoms = {}; - - for(var i in atoms) { - if(this.style2atoms[this.atoms[i].style] === undefined) this.style2atoms[this.atoms[i].style] = {}; - - this.style2atoms[this.atoms[i].style][i] = 1; - - // side chains - if(this.style2atoms[this.atoms[i].style2] === undefined) this.style2atoms[this.atoms[i].style2] = {}; - - this.style2atoms[this.atoms[i].style2][i] = 1; - } - }; - - // set atom style when loading a structure - iCn3D.prototype.setAtomStyleByOptions = function (options) { - if(options === undefined) options = this.opts; - - var selectedAtoms; - - if (options.prtns !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.prtns); - for(var i in selectedAtoms) { - this.atoms[i].style = options.prtns.toLowerCase(); - } - } - - // side chain overwrite the protein style - if (options.sidec !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.sidec); - for(var i in selectedAtoms) { - this.atoms[i].style = options.sidec.toLowerCase(); - } - } - - if (options.ligands !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.ligs); - for(var i in selectedAtoms) { - this.atoms[i].style = options.ligands.toLowerCase(); - } - } - - if (options.ions !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.ions); - for(var i in selectedAtoms) { - this.atoms[i].style = options.ions.toLowerCase(); - } - } - - if (options.water !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.water); - for(var i in selectedAtoms) { - this.atoms[i].style = options.water.toLowerCase(); - } - } - - if (options.nucleotides !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.nclts); - for(var i in selectedAtoms) { - this.atoms[i].style = options.nucleotides.toLowerCase(); - } - } - }; - - iCn3D.prototype.rebuildScene = function (options) { var me = this; - jQuery.extend(me.opts, options); - - this.cam_z = this.maxD * 2; - //this.cam_z = -this.maxD * 2; - - - if(this.scene !== undefined) { - for(var i = this.scene.children.length - 1; i >= 0; i--) { - var obj = this.scene.children[i]; - this.scene.remove(obj); - } - } - else { - this.scene = new THREE.Scene(); - } - - if(this.scene_ghost !== undefined) { - for(var i = this.scene_ghost.children.length - 1; i >= 0; i--) { - var obj = this.scene_ghost.children[i]; - this.scene_ghost.remove(obj); - } - } - else { - this.scene_ghost = new THREE.Scene(); - } - - //this.directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.2); - this.directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.0); - - if(this.cam_z > 0) { - this.directionalLight.position.set(0, 0, 1); - } - else { - this.directionalLight.position.set(0, 0, -1); - } - - //var ambientLight = new THREE.AmbientLight(0x202020); - //var ambientLight = new THREE.AmbientLight(0xdddddd, 0.2); - var ambientLight = new THREE.AmbientLight(0x404040); - - this.scene.add(this.directionalLight); - this.scene.add(ambientLight); - - //this.group = new THREE.Object3D(); // regular display - - this.mdl = new THREE.Object3D(); // regular display - //this.mdlPicking = new THREE.Object3D(); // pk display - this.mdlImpostor = new THREE.Object3D(); // Impostor display - - //this.scene.add(this.mdlPicking); - this.scene.add(this.mdl); - this.scene.add(this.mdlImpostor); - - // highlight on impostors - this.mdl_ghost = new THREE.Object3D(); // Impostor display - this.scene_ghost.add(this.mdl_ghost); - - //this.scene_ghost.add(this.directionalLight); - //this.scene_ghost.add(ambientLight); - - // related to pk - this.objects = []; // define objects for pk, not all elements are used for pk - this.objects_ghost = []; // define objects for pk, not all elements are used for pk - this.raycaster = new THREE.Raycaster(); - this.projector = new THREE.Projector(); - this.mouse = new THREE.Vector2(); - - var background = this.backgroundColors[this.opts.background.toLowerCase()]; - if(this.opts.background.toLowerCase() === 'transparent') { - this.renderer.setClearColor(background, 0); - } - else { - this.renderer.setClearColor(background, 1); - } - - this.perspectiveCamera = new THREE.PerspectiveCamera(20, this.container.whratio, 0.1, 10000); - this.perspectiveCamera.position.set(0, 0, this.cam_z); - this.perspectiveCamera.lookAt(new THREE.Vector3(0, 0, 0)); - - this.orthographicCamera = new THREE.OrthographicCamera(); - this.orthographicCamera.position.set(0, 0, this.cam_z); - this.orthographicCamera.lookAt(new THREE.Vector3(0, 0, 0)); - - this.cams = { - perspective: this.perspectiveCamera, - orthographic: this.orthographicCamera, - }; - -// this.setCamera(); - - if(this.bSkipLigandbinding === undefined || !this.bSkipLigandbinding) this.applyLigandbindingOptions(); - this.bSkipLigandbinding = true; - - // show disulfide bonds, set side chains - this.applySsbondsOptions(); - - this.applyDisplayOptions(this.opts, this.dAtoms); - - this.applyOtherOptions(); - - this.setFog(); - - this.setCamera(); - - //https://stackoverflow.com/questions/15726560/three-js-raycaster-intersection-empty-when-objects-not-part-of-scene - me.scene_ghost.updateMatrixWorld(true); - }; - - iCn3D.prototype.setFog = function() { - var background = this.backgroundColors[this.opts.background.toLowerCase()]; - - // apply fog - if(this.opts['fog'] === 'yes') { - if(this.opts['camera'] === 'perspective') { //perspective, orthographic - //this.scene.fog = new THREE.Fog(background, this.cam_z, this.cam_z + 0.5 * this.maxD); - //this.scene.fog = new THREE.Fog(background, 2 * this.maxD, 2.5 * this.maxD); - this.scene.fog = new THREE.Fog(background, 2 * this.maxD, 3 * this.maxD); - } - else if(this.opts['camera'] === 'orthographic') { - this.scene.fog = new THREE.FogExp2(background, 2); - //this.scene.fog.near = this.cam_z; - //this.scene.fog.far = this.cam_z + 0.5 * this.maxD; - this.scene.fog.near = 2 * this.maxD; - //this.scene.fog.far = 2.5 * this.maxD; - this.scene.fog.far = 3 * this.maxD; - } - } - else { - this.scene.fog = undefined; - } - }; - - iCn3D.prototype.setCamera = function() { - this.cam = this.cams[this.opts.camera.toLowerCase()]; - - if(this.cam === this.perspectiveCamera) { - if(this.cam_z > 0) { - this.cam.position.z = this.maxD * 2; // forperspective, the z positionshould be large enough to see the whole molecule - } - else { - this.cam.position.z = -this.maxD * 2; // forperspective, the z positionshould be large enough to see the whole molecule - } - - if(this.opts['slab'] === 'yes') { - this.cam.near = this.maxD * 2; - } - else { - this.cam.near = 0.1; - } - this.cam.far = 10000; - - this.controls = new THREE.TrackballControls( this.cam, document.getElementById(this.id), this ); - } - else if (this.cam === this.orthographicCamera){ - this.cam.right = this.maxD/2 * 2.5; - this.cam.left = -this.cam.right; - this.cam.top = this.cam.right /this.container.whratio; - this.cam.bottom = -this.cam.right /this.container.whratio; - - if(this.opts['slab'] === 'yes') { - this.cam.near = this.maxD * 2; - } - else { - this.cam.near = 0; - } - - this.cam.far = 10000; - - this.controls = new THREE.OrthographicTrackballControls( this.cam, document.getElementById(this.id), this ); - } - - this.cam.updateProjectionMatrix(); - }; - - iCn3D.prototype.applyTransformation = function (_zoomFactor, mouseChange, quaternion) { - var para = {}; - para.update = false; - - // zoom - para._zoomFactor = _zoomFactor; - - // translate - para.mouseChange = new THREE.Vector2(); - para.mouseChange.copy(mouseChange); - - // rotation - para.quaternion = new THREE.Quaternion(); - para.quaternion.copy(quaternion); - - this.controls.update(para); - }; - - iCn3D.prototype.render = function () { - this.directionalLight.position.copy(this.cam.position); - - this.renderer.gammaInput = true - this.renderer.gammaOutput = true - - this.renderer.setPixelRatio( window.devicePixelRatio ); // r71 - this.renderer.render(this.scene, this.cam); - //this.renderer.render(this.scene_ghost, this.cam); - }; - - iCn3D.prototype.setRotationCenter = function (coord) { - this.mdl.position.set(0,0,0); - this.mdlImpostor.position.set(0,0,0); - this.mdl_ghost.position.set(0,0,0); - - //this.mdlPicking.position.sub(coord); - this.mdl.position.sub(coord); - this.mdlImpostor.position.sub(coord); - this.mdl_ghost.position.sub(coord); - }; - - iCn3D.prototype.drawImpostorShader = function () { var me = this; - var modelViewMatrix = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.copy( object.modelViewMatrix ); - } ); - - var modelViewMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.getInverse( object.modelViewMatrix ); - } ); - - var modelViewMatrixInverseTranspose = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.getInverse( object.modelViewMatrix ).transpose(); - } ); - - var modelViewProjectionMatrix = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.multiplyMatrices( me.cam.projectionMatrix, object.modelViewMatrix ); - } ); - - var modelViewProjectionMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - var tmpMatrix = new THREE.Matrix4(); - tmpMatrix.multiplyMatrices(me.cam.projectionMatrix, object.modelViewMatrix); - this.value.getInverse(tmpMatrix); - } ); - - var projectionMatrix = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( ){ - this.value.copy( me.cam.projectionMatrix ); - } ); - - var projectionMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( ){ - this.value.getInverse( me.cam.projectionMatrix ); - } ); - - var background = this.backgroundColors[this.opts.background.toLowerCase()]; - var near = 2 * this.maxD; - //var far = 2.5 * this.maxD; - var far = 3 * this.maxD; - - this.uniforms = THREE.UniformsUtils.merge([ - THREE.UniformsLib.common, - { - modelViewMatrix: modelViewMatrix, - modelViewMatrixInverse: modelViewMatrixInverse, - modelViewMatrixInverseTranspose: modelViewMatrixInverseTranspose, - modelViewProjectionMatrix: modelViewProjectionMatrix, - modelViewProjectionMatrixInverse: modelViewProjectionMatrixInverse, - projectionMatrix: projectionMatrix, - projectionMatrixInverse: projectionMatrixInverse, - //ambientLightColor: { type: "v3", value: [0.25, 0.25, 0.25] }, - diffuse: { type: "v3", value: [1.0, 1.0, 1.0] }, - emissive: { type: "v3", value: [0.0,0.0,0.0] }, - roughness: { type: "f", value: 0.5 }, // 0.4 - metalness: { type: "f", value: 0.3 }, // 0.5 - opacity: { type: "f", value: 1.0 }, - nearClip: { type: "f", value: 0.1 }, - ortho: { type: "f", value: 0.0 }, - shrink: { type: "f", value: 0.13 }, - fogColor: { type: "v3", value: [background.r, background.g, background.b] }, - fogNear: { type: "f", value: near }, - fogFar: { type: "f", value: far }, - fogDensity: { type: "f", value: 2.0 } - }, - THREE.UniformsLib.ambient, - THREE.UniformsLib.lights - ]); - - /* - //fog_pars_fragment - #ifdef USE_FOG - uniform vec3 fogColor; - #ifdef FOG_EXP2 - uniform float fogDensity; - #else - uniform float fogNear; - uniform float fogFar; - #endif - #endif - */ - - this.defines = { - USE_COLOR: 1, - //PICKING: 1, - NEAR_CLIP: 1, - CAP: 1 - }; - - if(this.opts['fog'] === 'yes') { - this.defines['USE_FOG'] = 1; - if(this.opts['camera'] === 'orthographic') { - this.defines['FOG_EXP2'] = 1; - } - } - - if(this.bExtFragDepth) { - this.defines['USE_LOGDEPTHBUF_EXT'] = 1; - } - - this.createImpostorShaderSphere("SphereImpostor"); - this.createImpostorShaderCylinder("CylinderImpostor"); - //this.createImpostorShaderCylinder("HyperballStickImpostor"); - }; - - iCn3D.prototype.draw = function () { var me = this; - this.rebuildScene(); - - // Impostor display using the saved arrays - if(this.bImpo) { - this.drawImpostorShader(); - } - - this.applyPrevColor(); - - //if(this.bSSOnly) this.drawHelixBrick(this.molid2ss, this.molid2color); - - if(this.bAssembly) { - this.drawSymmetryMates2(); - } - - // show the hAtoms - if(this.hAtoms !== undefined && Object.keys(this.hAtoms).length > 0 && Object.keys(this.hAtoms).length < Object.keys(this.atoms).length) { - this.removeHighlightObjects(); - if(this.bShowHighlight === undefined || this.bShowHighlight) this.addHighlightObjects(); - } - - if(this.bRender === true) { - this.applyTransformation(this._zoomFactor, this.mouseChange, this.quaternion); - this.render(); - - // reset to hide the side chain - //this.opts['sidec'] = 'nothing'; - } - - this.clearImpostors(); - }; - - iCn3D.prototype.clearImpostors = function () { - this.posArray = []; - this.colorArray = []; - this.pos2Array = []; - this.color2Array = []; - this.radiusArray = []; - - this.posArraySphere = []; - this.colorArraySphere = []; - this.radiusArraySphere = []; - }; - - // change the display atom when alternating - iCn3D.prototype.alternateStructures = function () { - this.dAtoms = {}; - - var hAtomsCount = Object.keys(this.hAtoms).length; - var allAtomsCount = Object.keys(this.atoms).length; - - var moleculeArray = Object.keys(this.strucs); - for(var i = 0, il = moleculeArray.length; i < il; ++i) { - var structure = moleculeArray[i]; - if(i > this.ALTERNATE_STRUCTURE || (this.ALTERNATE_STRUCTURE === il - 1 && i === 0) ) { - for(var k in this.strucs[structure]) { - var chain = this.strucs[structure][k]; - this.dAtoms = this.unionHash(this.dAtoms, this.chns[chain]); - } - - this.ALTERNATE_STRUCTURE = i; - break; - } - } - - if(hAtomsCount < allAtomsCount) { - this.dAtoms = this.intHash(this.dAtoms, this.hAtoms); - - this.bShowHighlight = false; - this.opts['rotationcenter'] = 'highlight center'; - } - - // also alternating the surfaces - this.removeSurfaces(); - this.applySurfaceOptions(); - - this.draw(); - - this.bShowHighlight = true; - this.opts['rotationcenter'] = 'molecule center'; - }; diff --git a/src/icn3d/display_common.js b/src/icn3d/display_common.js deleted file mode 100644 index bd291a2d..00000000 --- a/src/icn3d/display_common.js +++ /dev/null @@ -1,1098 +0,0 @@ -/** - * @author Jiyao Wang / https://github.com/ncbi/icn3d - */ - -iCn3D.prototype.setAtmClr = function(atoms, hex) { - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = new THREE.Color().setHex(hex); - - this.atomPrevColors[i] = atom.color; - } -}; - -iCn3D.prototype.updateChainsColor = function (atom) { - var chainid = atom.structure + '_' + atom.chain; - if(this.chainsColor[chainid] !== undefined) { // for mmdbid and align input - this.chainsColor[chainid] = atom.color; - } -}; - -iCn3D.prototype.setColorByOptions = function (options, atoms, bUseInputColor) { - if(options !== undefined) { - if(bUseInputColor !== undefined && bUseInputColor) { - for (var i in atoms) { - var atom = this.atoms[i]; - - this.atomPrevColors[i] = atom.color; - } - } - else if(options.color.indexOf("#") === 0) { - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = new THREE.Color().setStyle(options.color.toLowerCase()); - - this.atomPrevColors[i] = atom.color; - } - } - else { - switch (options.color.toLowerCase()) { - case 'spectrum': - var idx = 0; - //var lastTerSerialInv = 1 / this.lastTerSerial; - var lastTerSerialInv = 1 / this.cnt; - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : new THREE.Color().setHSL(2 / 3 * (1 - idx++ * lastTerSerialInv), 1, 0.45); - //atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'chain': - if(this.chainsColor !== undefined && Object.keys(this.chainsColor).length > 0) { // mmdb input - this.applyOriginalColor(this.hash2Atoms(this.hAtoms)); - - // atom color - var atomHash = this.unionHash(this.chemicals, this.ions); - - for (var i in atomHash) { - var atom = this.atoms[i]; - atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; - - this.atomPrevColors[i] = atom.color; - } - } - else { - var index = -1, prevChain = '', colorLength = this.stdChainColors.length; - for (var i in atoms) { - var atom = this.atoms[i]; - - if(atom.chain != prevChain) { - ++index; - - index = index % colorLength; - } - - atom.color = this.stdChainColors[index]; - - if(Object.keys(this.chainsColor).length > 0) this.updateChainsColor(atom); - this.atomPrevColors[i] = atom.color; - - prevChain = atom.chain; - } - } - break; - case 'secondary structure': - for (var i in atoms) { - var atom = this.atoms[i]; - // secondary color of nucleotide: blue (new THREE.Color(0x0000FF)) - atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.ssColors[atom.ss] || new THREE.Color(0x0000FF); - - this.atomPrevColors[i] = atom.color; - } - - break; - - case 'residue': - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.residueColors[atom.resn] || this.defaultResidueColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'charge': - for (var i in atoms) { - var atom = this.atoms[i]; - - //atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; - atom.color = atom.het ? this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'hydrophobic': - for (var i in atoms) { - var atom = this.atoms[i]; - - //atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; - atom.color = atom.het ? this.defaultAtomColor : this.hydrophobicColors[atom.resn] || this.defaultResidueColor; - - this.atomPrevColors[i] = atom.color; - } - break; - case 'atom': - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; - - this.atomPrevColors[i] = atom.color; - } - break; - - case 'conserved': - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = this.defaultAtomColor; - - this.atomPrevColors[i] = atom.color; - } - - for(var chainid in this.alnChainsSeq) { - var resObjectArray = this.alnChainsSeq[chainid]; - - for(var i = 0, il = resObjectArray.length; i < il; ++i) { - var residueid = chainid + '_' + resObjectArray[i].resi; - - for(var j in this.residues[residueid]) { - if(atoms.hasOwnProperty(j)) { - var color = new THREE.Color(resObjectArray[i].color); - this.atoms[j].color = color; - this.atomPrevColors[j] = color; - } - } - } - } - break; - - case 'white': - this.setAtmClr(atoms, 0xFFFFFF); - break; - - case 'grey': - this.setAtmClr(atoms, 0x888888); - break; - - case 'red': - this.setAtmClr(atoms, 0xFF0000); - break; - case 'green': - this.setAtmClr(atoms, 0x00FF00); - break; - case 'blue': - this.setAtmClr(atoms, 0x0000FF); - break; - case 'magenta': - this.setAtmClr(atoms, 0xFF00FF); - break; - case 'yellow': - this.setAtmClr(atoms, 0xFFFF00); - break; - case 'cyan': - this.setAtmClr(atoms, 0x00FFFF); - break; - case 'custom': - // do the coloring separately - break; - - default: // the "#" was missed in order to make sharelink work - for (var i in atoms) { - var atom = this.atoms[i]; - atom.color = new THREE.Color().setStyle("#" + options.color.toLowerCase()); - - this.atomPrevColors[i] = atom.color; - } - - break; - } - } - } -}; - -iCn3D.prototype.applyDisplayOptions = function (options, atoms, bHighlight) { var me = this; // atoms: hash of key -> 1 - if(options === undefined) options = this.opts; - - var residueHash = {}; - var singletonResidueHash = {}; - var atomsObj = {}; - var residueid; - - if(bHighlight === 1 && Object.keys(atoms).length < Object.keys(this.atoms).length) { - atomsObj = this.hash2Atoms(atoms); - - for(var i in atomsObj) { - var atom = atomsObj[i]; - - residueid = atom.structure + '_' + atom.chain + '_' + atom.resi; - residueHash[residueid] = 1; - } - - // find singleton residues - for(var i in residueHash) { - var last = i.lastIndexOf('_'); - var base = i.substr(0, last + 1); - var lastResi = parseInt(i.substr(last + 1)); - - var prevResidueid = base + (lastResi - 1).toString(); - var nextResidueid = base + (lastResi + 1).toString(); - - if(!residueHash.hasOwnProperty(prevResidueid) && !residueHash.hasOwnProperty(prevResidueid)) { - singletonResidueHash[i] = 1; - } - } - - // show the only atom in a transparent box - if(Object.keys(atomsObj).length === 1 && Object.keys(this.residues[residueid]).length > 1 - && atomsObj[Object.keys(atomsObj)[0]].style !== 'sphere' && atomsObj[Object.keys(atomsObj)[0]].style !== 'dot') { - if(this.bCid === undefined || !this.bCid) { - for(var i in atomsObj) { - var atom = atomsObj[i]; - var scale = 1.0; - this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); - } - } - } - else { - // if only one residue, add the next residue in order to show highlight - for(var residueid in singletonResidueHash) { - var atom = this.getFirstAtomObj(this.residues[residueid]); - var prevResidueid = atom.structure + '_' + atom.chain + '_' + parseInt(atom.resi - 1); - var nextResidueid = atom.structure + '_' + atom.chain + '_' + parseInt(atom.resi + 1); - - //ribbon, strand, cylinder and plate, nucleotide cartoon, o3 trace, schematic, c alpha trace, b factor tube, lines, stick, ball and stick, sphere, dot - - if(atom.style === 'cylinder and plate' && atom.ss === 'helix') { // no way to highlight part of cylinder - for(var i in this.residues[residueid]) { - var atom = this.atoms[i]; - var scale = 1.0; - this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); - } - } - else if( (atom.style === 'ribbon' && atom.ss === 'coil') || (atom.style === 'strand' && atom.ss === 'coil') || atom.style === 'o3 trace' || atom.style === 'schematic' || atom.style === 'c alpha trace' || atom.style === 'b factor tube' || (atom.style === 'cylinder and plate' && atom.ss !== 'helix') ) { - var bAddResidue = false; - // add the next residue with same style - if(!bAddResidue && this.residues.hasOwnProperty(nextResidueid)) { - var index2 = Object.keys(this.residues[nextResidueid])[0]; - var atom2 = this.hash2Atoms(this.residues[nextResidueid])[index2]; - if( (atom.style === atom2.style && !atom2.ssbegin) || atom2.ssbegin) { - var residueAtoms = this.residues[nextResidueid]; - atoms = this.unionHash(atoms, residueAtoms); - - bAddResidue = true; - - // record the highlight style for the artificial residue - if(atom2.ssbegin) { - for(var i in residueAtoms) { - this.atoms[i].notshow = true; - } - } - } - } - - // add the previous residue with same style - if(!bAddResidue && this.residues.hasOwnProperty(prevResidueid)) { - var index2 = Object.keys(this.residues[prevResidueid])[0]; - var atom2 = this.hash2Atoms(this.residues[prevResidueid])[index2]; - if(atom.style === atom2.style) { - atoms = this.unionHash(atoms, this.residues[prevResidueid]); - - bAddResidue = true; - } - } - } - else if( (atom.style === 'ribbon' && atom.ss !== 'coil' && atom.ssend) || (atom.style === 'strand' && atom.ss !== 'coil' && atom.ssend)) { - var bAddResidue = false; - // add the next residue with same style - if(!bAddResidue && this.residues.hasOwnProperty(nextResidueid)) { - var index2 = Object.keys(this.residues[nextResidueid])[0]; - var atom2 = this.hash2Atoms(this.residues[nextResidueid])[index2]; - //if(atom.style === atom2.style && !atom2.ssbegin) { - atoms = this.unionHash(atoms, this.residues[nextResidueid]); - - bAddResidue = true; - //} - } - } - } // end for - } // end else { - - atomsObj = {}; - } // end if(bHighlight === 1) - - this.setStyle2Atoms(atoms); - - //this.bAllAtoms = (Object.keys(atoms).length === Object.keys(this.atoms).length); - this.bAllAtoms = false; - if(atoms && atoms !== undefined ) { - this.bAllAtoms = (Object.keys(atoms).length === Object.keys(this.atoms).length); - } - -// var currentCalphas = {}; -// if(this.opts['sidec'] !== 'nothing') { -// currentCalphas = this.intHash(this.hAtoms, this.calphas); -// } - - var chemicalSchematicRadius = this.cylinderRadius * 0.5; - - // remove schematic labels - //if(this.labels !== undefined) this.labels['schematic'] = undefined; - if(this.labels !== undefined) delete this.labels['schematic']; - - for(var style in this.style2atoms) { - // 14 styles: ribbon, strand, cylinder and plate, nucleotide cartoon, o3 trace, schematic, c alpha trace, b factor tube, lines, stick, ball and stick, sphere, dot, nothing - atomHash = this.style2atoms[style]; - var bPhosphorusOnly = this.isCalphaPhosOnly(this.hash2Atoms(atomHash), "O3'", "O3*"); - - if(style === 'ribbon') { - this.createStrand(this.hash2Atoms(atomHash), 2, undefined, true, undefined, undefined, false, this.ribbonthickness, bHighlight); - } - else if(style === 'strand') { - this.createStrand(this.hash2Atoms(atomHash), null, null, null, null, null, false, undefined, bHighlight); - } - else if(style === 'cylinder and plate') { - this.createCylinderHelix(this.hash2Atoms(atomHash), this.cylinderHelixRadius, bHighlight); - } - else if(style === 'nucleotide cartoon') { - if(bPhosphorusOnly) { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.traceRadius, false, bHighlight); - } - else { - this.drawCartoonNucleicAcid(this.hash2Atoms(atomHash), null, this.ribbonthickness, bHighlight); - - if(bHighlight !== 2) this.drawNucleicAcidStick(this.hash2Atoms(atomHash), bHighlight); - } - } - else if(style === 'o3 trace') { - if(bPhosphorusOnly) { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.traceRadius, false, bHighlight); - } - else { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], this.traceRadius, false, bHighlight); - } - } - //else if(style === 'phosphorus lines') { - // this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], 0.2, true, bHighlight); - //} - else if(style === 'schematic') { - // either proteins, nucleotides, or chemicals - var firstAtom = this.getFirstAtomObj(atomHash); - - //if(firstAtom.het) { // chemicals - if(this.chemicals.hasOwnProperty(firstAtom.serial)) { // chemicals - this.addNonCarbonAtomLabels(this.hash2Atoms(atomHash)); - - bSchematic = true; - this.createStickRepresentation(this.hash2Atoms(atomHash), chemicalSchematicRadius, chemicalSchematicRadius, undefined, bHighlight, bSchematic); - } - else { // nucleotides or proteins - this.addResiudeLabels(this.hash2Atoms(atomHash), true); - - if(bPhosphorusOnly) { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.traceRadius, false, bHighlight); - } - else { - this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], this.traceRadius, false, bHighlight); - } - this.createCylinderCurve(this.hash2Atoms(atomHash), ['CA'], this.traceRadius, false, bHighlight); - } - } - else if(style === 'c alpha trace') { - this.createCylinderCurve(this.hash2Atoms(atomHash), ['CA'], this.traceRadius, false, bHighlight); - } - else if(style === 'b factor tube') { - this.createTube(this.hash2Atoms(atomHash), 'CA', null, bHighlight); - } - else if(style === 'lines') { - if(bHighlight === 1) { - this.createStickRepresentation(this.hash2Atoms(atomHash), this.hlLineRadius, this.hlLineRadius, undefined, bHighlight); - } - else { - this.createLineRepresentation(this.hash2Atoms(atomHash), bHighlight); - } - } - else if(style === 'stick') { - this.createStickRepresentation(this.hash2Atoms(atomHash), this.cylinderRadius, this.cylinderRadius, undefined, bHighlight); - } - else if(style === 'ball and stick') { - this.createStickRepresentation(this.hash2Atoms(atomHash), this.cylinderRadius, this.cylinderRadius * 0.5, this.dotSphereScale, bHighlight); - } - else if(style === 'sphere') { - this.createSphereRepresentation(this.hash2Atoms(atomHash), this.sphereRadius, undefined, undefined, bHighlight); - } - else if(style === 'dot') { - this.createSphereRepresentation(this.hash2Atoms(atomHash), this.sphereRadius, false, this.dotSphereScale, bHighlight); - } - } // end for loop - - // hide the previous labels - if(this.labels !== undefined && Object.keys(this.labels).length > 0) { - this.hideLabels(); - - // labels - this.createLabelRepresentation(this.labels); - } -}; - -iCn3D.prototype.hideLabels = function () { var me = this; - // remove previous labels - if(this.mdl !== undefined) { - for(var i = 0, il = this.mdl.children.length; i < il; ++i) { - var mesh = this.mdl.children[i]; - if(mesh !== undefined && mesh.type === 'Sprite') { - this.mdl.remove(mesh); // somehow didn't work - } - } - } -}; - -iCn3D.prototype.getShader = function (name) { var me = this; - var shaderText = $NGL_shaderTextHash[name]; - var reInclude = /#include\s+(\S+)/gmi; - - shaderText = shaderText.replace( reInclude, function( match, p1 ){ - - var chunk; - if(THREE.ShaderChunk.hasOwnProperty(p1)) { - chunk = THREE.ShaderChunk[ p1 ]; - } - - return chunk ? chunk : ""; - - } ); - - return shaderText; -}; - -iCn3D.prototype.createImpostorShaderBase = function (shaderName, mapping, mappingIndices, data, attributeData, count, mappingSize, mappingIndicesSize, mappingItemSize) { var me = this; - var shaderMaterial = - new THREE.ShaderMaterial({ - defines: me.defines, - uniforms: me.uniforms, - vertexShader: me.getShader(shaderName + ".vert"), - fragmentShader: me.getShader(shaderName + ".frag"), - depthTest: true, - depthWrite: true, - needsUpdate: true, - lights: true - }); - - shaderMaterial.extensions.fragDepth = true; - - //MappedBuffer - var attributeSize = count * mappingSize; - - var n = count * mappingIndicesSize; - var TypedArray = attributeSize > 65535 ? Uint32Array : Uint16Array; - var index = new TypedArray( n ); - - //makeIndex(); - var ix, it; - - for( var v = 0; v < count; v++ ) { - ix = v * mappingIndicesSize; - it = v * mappingSize; - - index.set( mappingIndices, ix ); - - for( var s = 0; s < mappingIndicesSize; ++s ){ - index[ ix + s ] += it; - } - } - - - var geometry = new THREE.BufferGeometry(); - - // buffer.js - var dynamic = true; - - if( index ){ - geometry.setIndex( - new THREE.BufferAttribute( index, 1 ) - ); - geometry.getIndex().setDynamic( dynamic ); - } - - // add attributes from buffer.js - var itemSize = { - "f": 1, "v2": 2, "v3": 3, "c": 3 - }; - - for( var name in attributeData ){ - - var buf; - var a = attributeData[ name ]; - - buf = new Float32Array( - attributeSize * itemSize[ a.type ] - ); - - geometry.addAttribute( - name, - new THREE.BufferAttribute( buf, itemSize[ a.type ] ) - .setDynamic( dynamic ) - ); - - } - - // set attributes from mapped-buffer.js - var attributes = geometry.attributes; - - var a, d, itemSize2, array, i, j; - - for( var name in data ){ - - d = data[ name ]; - a = attributes[ name ]; - itemSize2 = a.itemSize; - array = a.array; - - for( var k = 0; k < count; ++k ) { - - n = k * itemSize2; - i = n * mappingSize; - - for( var l = 0; l < mappingSize; ++l ) { - - j = i + ( itemSize2 * l ); - - for( var m = 0; m < itemSize2; ++m ) { - - array[ j + m ] = d[ n + m ]; - - } - - } - - } - - a.needsUpdate = true; - - } - - // makemapping - var aMapping = geometry.attributes.mapping.array; - - for( var v = 0; v < count; v++ ) { - aMapping.set( mapping, v * mappingItemSize * mappingSize ); - } - - var mesh = new THREE.Mesh(geometry, shaderMaterial); - - // important: https://stackoverflow.com/questions/21184061/mesh-suddenly-disappears-in-three-js-clipping - // You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU - mesh.frustumCulled = false; - - mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.0; - - this.mdlImpostor.add(mesh); - - //this.objects.push(mesh); -}; - -iCn3D.prototype.createImpostorShaderCylinder = function (shaderName) { var me = this; - var positions = new Float32Array( me.posArray ); - var colors = new Float32Array( me.colorArray ); - var positions2 = new Float32Array( me.pos2Array ); - var colors2 = new Float32Array( me.color2Array ); - var radii = new Float32Array( me.radiusArray ); - - // cylinder - var mapping = new Float32Array([ - -1.0, 1.0, -1.0, - -1.0, -1.0, -1.0, - 1.0, 1.0, -1.0, - 1.0, 1.0, 1.0, - 1.0, -1.0, -1.0, - 1.0, -1.0, 1.0 - ]); - - var mappingIndices = new Uint16Array([ - 0, 1, 2, - 1, 4, 2, - 2, 4, 3, - 4, 5, 3 - ]); - - var mappingIndicesSize = 12; - var mappingType = "v3"; - var mappingSize = 6; - var mappingItemSize = 3; - - - var count = positions.length / 3; - - var data = { - "position1": positions, - "color": colors, - "position2": positions2, - "color2": colors2, - "radius": radii - }; - - var attributeData = { - "position1": { type: "v3", value: null }, - "color": { type: "v3", value: null }, - "position2": { type: "v3", value: null }, - "color2": { type: "v3", value: null }, - "radius": { type: "f", value: null }, - "mapping": { type: mappingType, value: null } - }; - - this.createImpostorShaderBase(shaderName, mapping, mappingIndices, data, attributeData, count, mappingSize, mappingIndicesSize, mappingItemSize); -}; - -iCn3D.prototype.createImpostorShaderSphere = function (shaderName) { var me = this; - var positions = new Float32Array( me.posArraySphere ); - var colors = new Float32Array( me.colorArraySphere ); - var radii = new Float32Array( me.radiusArraySphere ); - - // sphere - var mapping = new Float32Array([ - -1.0, 1.0, - -1.0, -1.0, - 1.0, 1.0, - 1.0, -1.0 - ]); - - var mappingIndices = new Uint16Array([ - 0, 1, 2, - 1, 3, 2 - ]); - - var mappingIndicesSize = 6; - var mappingType = "v2"; - var mappingSize = 4; - var mappingItemSize = 2; - - var count = positions.length / 3; - - var data = { - "position": positions, - "color": colors, - "radius": radii - }; - - var attributeData = { - "position": { type: "v3", value: null }, - "color": { type: "v3", value: null }, - "radius": { type: "f", value: null }, - "mapping": { type: mappingType, value: null } - }; - - this.createImpostorShaderBase(shaderName, mapping, mappingIndices, data, attributeData, count, mappingSize, mappingIndicesSize, mappingItemSize); -}; - -iCn3D.prototype.setStyle2Atoms = function (atoms) { - this.style2atoms = {}; - - for(var i in atoms) { - if(this.style2atoms[this.atoms[i].style] === undefined) this.style2atoms[this.atoms[i].style] = {}; - - this.style2atoms[this.atoms[i].style][i] = 1; - - // side chains - if(this.atoms[i].style2 !== undefined && this.atoms[i].style2 !== 'nothing') { - if(this.style2atoms[this.atoms[i].style2] === undefined) this.style2atoms[this.atoms[i].style2] = {}; - - this.style2atoms[this.atoms[i].style2][i] = 1; - } - } - -/* - for(var i in this.atoms) { - if(atoms.hasOwnProperty(i)) { - if(this.style2atoms[this.atoms[i].style] === undefined) this.style2atoms[this.atoms[i].style] = {}; - - this.style2atoms[this.atoms[i].style][i] = 1; - - // side chains - if(this.style2atoms[this.atoms[i].style2] === undefined) this.style2atoms[this.atoms[i].style2] = {}; - - this.style2atoms[this.atoms[i].style2][i] = 1; - } - else if(this.atoms[i].style == 'schematic') { // always display schematic - if(this.style2atoms[this.atoms[i].style] === undefined) this.style2atoms[this.atoms[i].style] = {}; - this.style2atoms[this.atoms[i].style][i] = 1; - } - } -*/ -}; - -// set atom style when loading a structure -iCn3D.prototype.setAtomStyleByOptions = function (options) { - if(options === undefined) options = this.opts; - - var selectedAtoms; - - if (options.proteins !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.proteins); - for(var i in selectedAtoms) { - this.atoms[i].style = options.proteins.toLowerCase(); - } - } - - // side chain use style2 - if (options.sidec !== undefined && options.sidec !== 'nothing') { - selectedAtoms = this.intHash(this.hAtoms, this.sidec); - //var sidec_calpha = this.unionHash(this.calphas, this.sidec); - //selectedAtoms = this.intHash(this.hAtoms, sidec_calpha); - - for(var i in selectedAtoms) { - this.atoms[i].style2 = options.sidec.toLowerCase(); - } - } - - if (options.chemicals !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.chemicals); - for(var i in selectedAtoms) { - this.atoms[i].style = options.chemicals.toLowerCase(); - } - } - - if (options.ions !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.ions); - for(var i in selectedAtoms) { - this.atoms[i].style = options.ions.toLowerCase(); - } - } - - if (options.water !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.water); - for(var i in selectedAtoms) { - this.atoms[i].style = options.water.toLowerCase(); - } - } - - if (options.nucleotides !== undefined) { - selectedAtoms = this.intHash(this.hAtoms, this.nucleotides); - for(var i in selectedAtoms) { - this.atoms[i].style = options.nucleotides.toLowerCase(); - } - } -}; - -iCn3D.prototype.rebuildSceneBase = function (options) { var me = this; - jQuery.extend(me.opts, options); - - this.cam_z = this.maxD * 2; - //this.cam_z = -this.maxD * 2; - - - if(this.scene !== undefined) { - for(var i = this.scene.children.length - 1; i >= 0; i--) { - var obj = this.scene.children[i]; - this.scene.remove(obj); - } - } - else { - this.scene = new THREE.Scene(); - } - - if(this.scene_ghost !== undefined) { - for(var i = this.scene_ghost.children.length - 1; i >= 0; i--) { - var obj = this.scene_ghost.children[i]; - this.scene_ghost.remove(obj); - } - } - else { - this.scene_ghost = new THREE.Scene(); - } - - //this.directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.2); - this.directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.0); - - if(this.cam_z > 0) { - this.directionalLight.position.set(0, 0, 1); - } - else { - this.directionalLight.position.set(0, 0, -1); - } - - //var ambientLight = new THREE.AmbientLight(0x202020); - //var ambientLight = new THREE.AmbientLight(0xdddddd, 0.2); - var ambientLight = new THREE.AmbientLight(0x404040); - - this.scene.add(this.directionalLight); - this.scene.add(ambientLight); - - //this.group = new THREE.Object3D(); // regular display - - this.mdl = new THREE.Object3D(); // regular display - //this.mdlPicking = new THREE.Object3D(); // pk display - this.mdlImpostor = new THREE.Object3D(); // Impostor display - - //this.scene.add(this.mdlPicking); - this.scene.add(this.mdl); - this.scene.add(this.mdlImpostor); - - // highlight on impostors - this.mdl_ghost = new THREE.Object3D(); // Impostor display - this.scene_ghost.add(this.mdl_ghost); - - //this.scene_ghost.add(this.directionalLight); - //this.scene_ghost.add(ambientLight); - - // related to pk - this.objects = []; // define objects for pk, not all elements are used for pk - this.objects_ghost = []; // define objects for pk, not all elements are used for pk - this.raycaster = new THREE.Raycaster(); - this.projector = new THREE.Projector(); - this.mouse = new THREE.Vector2(); - - var background = this.backgroundColors[this.opts.background.toLowerCase()]; - - if(this.opts.background.toLowerCase() === 'transparent') { - this.renderer.setClearColor(background, 0); - } - else { - this.renderer.setClearColor(background, 1); - } - - this.perspectiveCamera = new THREE.PerspectiveCamera(20, this.container.whratio, 0.1, 10000); - this.perspectiveCamera.position.set(0, 0, this.cam_z); - this.perspectiveCamera.lookAt(new THREE.Vector3(0, 0, 0)); - - this.orthographicCamera = new THREE.OrthographicCamera(); - this.orthographicCamera.position.set(0, 0, this.cam_z); - this.orthographicCamera.lookAt(new THREE.Vector3(0, 0, 0)); - - this.cams = { - perspective: this.perspectiveCamera, - orthographic: this.orthographicCamera, - }; -}; - -iCn3D.prototype.setCamera = function() { - this.cam = this.cams[this.opts.camera.toLowerCase()]; - - if(this.cam === this.perspectiveCamera) { - if(this.cam_z > 0) { - this.cam.position.z = this.maxD * 2; // forperspective, the z positionshould be large enough to see the whole molecule - } - else { - this.cam.position.z = -this.maxD * 2; // forperspective, the z positionshould be large enough to see the whole molecule - } - - if(this.opts['slab'] === 'yes') { - this.cam.near = this.maxD * 2; - } - else { - this.cam.near = 0.1; - } - this.cam.far = 10000; - - this.controls = new THREE.TrackballControls( this.cam, document.getElementById(this.id), this ); - } - else if (this.cam === this.orthographicCamera){ - this.cam.right = this.maxD/2 * 2.5; - this.cam.left = -this.cam.right; - this.cam.top = this.cam.right /this.container.whratio; - this.cam.bottom = -this.cam.right /this.container.whratio; - - if(this.opts['slab'] === 'yes') { - this.cam.near = this.maxD * 2; - } - else { - this.cam.near = 0; - } - - this.cam.far = 10000; - - this.controls = new THREE.OrthographicTrackballControls( this.cam, document.getElementById(this.id), this ); - } - - this.cam.updateProjectionMatrix(); -}; - -iCn3D.prototype.render = function () { - this.directionalLight.position.copy(this.cam.position); - - this.renderer.gammaInput = true - this.renderer.gammaOutput = true - - this.renderer.setPixelRatio( window.devicePixelRatio ); // r71 - this.renderer.render(this.scene, this.cam); - //this.renderer.render(this.scene_ghost, this.cam); -}; - -iCn3D.prototype.drawImpostorShader = function () { var me = this; - var modelViewMatrix = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.copy( object.modelViewMatrix ); - } ); - - var modelViewMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.getInverse( object.modelViewMatrix ); - } ); - - var modelViewMatrixInverseTranspose = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.getInverse( object.modelViewMatrix ).transpose(); - } ); - - var modelViewProjectionMatrix = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - this.value.multiplyMatrices( me.cam.projectionMatrix, object.modelViewMatrix ); - } ); - - var modelViewProjectionMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( object ){ - var tmpMatrix = new THREE.Matrix4(); - tmpMatrix.multiplyMatrices(me.cam.projectionMatrix, object.modelViewMatrix); - this.value.getInverse(tmpMatrix); - } ); - - var projectionMatrix = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( ){ - this.value.copy( me.cam.projectionMatrix ); - } ); - - var projectionMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) - .onUpdate( function( ){ - this.value.getInverse( me.cam.projectionMatrix ); - } ); - - var background = this.backgroundColors[this.opts.background.toLowerCase()]; - var near = 2 * this.maxD; - //var far = 2.5 * this.maxD; - var far = 3 * this.maxD; - - this.uniforms = THREE.UniformsUtils.merge([ - THREE.UniformsLib.common, - { - modelViewMatrix: modelViewMatrix, - modelViewMatrixInverse: modelViewMatrixInverse, - modelViewMatrixInverseTranspose: modelViewMatrixInverseTranspose, - modelViewProjectionMatrix: modelViewProjectionMatrix, - modelViewProjectionMatrixInverse: modelViewProjectionMatrixInverse, - projectionMatrix: projectionMatrix, - projectionMatrixInverse: projectionMatrixInverse, - //ambientLightColor: { type: "v3", value: [0.25, 0.25, 0.25] }, - diffuse: { type: "v3", value: [1.0, 1.0, 1.0] }, - emissive: { type: "v3", value: [0.0,0.0,0.0] }, - roughness: { type: "f", value: 0.5 }, // 0.4 - metalness: { type: "f", value: 0.3 }, // 0.5 - opacity: { type: "f", value: 1.0 }, - nearClip: { type: "f", value: 0.1 }, - ortho: { type: "f", value: 0.0 }, - shrink: { type: "f", value: 0.13 }, - fogColor: { type: "v3", value: [background.r, background.g, background.b] }, - fogNear: { type: "f", value: near }, - fogFar: { type: "f", value: far }, - fogDensity: { type: "f", value: 2.0 } - }, - THREE.UniformsLib.ambient, - THREE.UniformsLib.lights - ]); - - /* - //fog_pars_fragment - #ifdef USE_FOG - uniform vec3 fogColor; - #ifdef FOG_EXP2 - uniform float fogDensity; - #else - uniform float fogNear; - uniform float fogFar; - #endif - #endif - */ - - this.defines = { - USE_COLOR: 1, - //PICKING: 1, - NEAR_CLIP: 1, - CAP: 1 - }; - - if(this.opts['fog'] === 'yes') { - this.defines['USE_FOG'] = 1; - if(this.opts['camera'] === 'orthographic') { - this.defines['FOG_EXP2'] = 1; - } - } - - if(this.bExtFragDepth) { - this.defines['USE_LOGDEPTHBUF_EXT'] = 1; - } - - this.createImpostorShaderSphere("SphereImpostor"); - this.createImpostorShaderCylinder("CylinderImpostor"); - //this.createImpostorShaderCylinder("HyperballStickImpostor"); -}; - -iCn3D.prototype.clearImpostors = function () { - this.posArray = []; - this.colorArray = []; - this.pos2Array = []; - this.color2Array = []; - this.radiusArray = []; - - this.posArraySphere = []; - this.colorArraySphere = []; - this.radiusArraySphere = []; -}; - -iCn3D.prototype.applyTransformation = function (_zoomFactor, mouseChange, quaternion) { - var para = {}; - para.update = false; - - // zoom - para._zoomFactor = _zoomFactor; - - // translate - para.mouseChange = new THREE.Vector2(); - para.mouseChange.copy(mouseChange); - - // rotation - para.quaternion = new THREE.Quaternion(); - para.quaternion.copy(quaternion); - - this.controls.update(para); -}; - -iCn3D.prototype.applyCenterOptions = function (options) { - if(options === undefined) options = this.opts; - - switch (options.rotationcenter.toLowerCase()) { - case 'molecule center': - // move the molecule to the origin - if(this.center !== undefined) { - this.setRotationCenter(this.center); - } - break; - case 'pick center': - if(this.pAtom !== undefined) { - this.setRotationCenter(this.pAtom.coord); - } - break; - case 'display center': - var center = this.centerAtoms(this.dAtoms).center; - this.setRotationCenter(center); - break; - case 'highlight center': - var center = this.centerAtoms(this.hAtoms).center; - this.setRotationCenter(center); - break; - } -}; - -iCn3D.prototype.setRotationCenter = function (coord) { - this.mdl.position.set(0,0,0); - this.mdlImpostor.position.set(0,0,0); - this.mdl_ghost.position.set(0,0,0); - - //this.mdlPicking.position.sub(coord); - this.mdl.position.sub(coord); - this.mdlImpostor.position.sub(coord); - this.mdl_ghost.position.sub(coord); -}; - -iCn3D.prototype.applyOriginalColor = function (atoms) { - if(atoms === undefined) atoms = this.atoms; - - for (var i in atoms) { - var atom = atoms[i]; - var chainid = atom.structure + '_' + atom.chain; - - if(this.chainsColor.hasOwnProperty(chainid)) { - atom.color = this.chainsColor[chainid]; - } - else { - //atom.color = this.atomColors[atom.elem]; - break; - } - } -}; diff --git a/src/icn3d/display_full.js b/src/icn3d/display_full.js deleted file mode 100644 index 203ee555..00000000 --- a/src/icn3d/display_full.js +++ /dev/null @@ -1,501 +0,0 @@ -/** - * @author Jiyao Wang / https://github.com/ncbi/icn3d - */ - -iCn3D.prototype.applyPrevColor = function () { - for (var i in this.atoms) { - var atom = this.atoms[i]; - atom.color = this.atomPrevColors[i]; - } -}; - -iCn3D.prototype.applyChemicalbindingOptions = function (options) { - if(options === undefined) options = this.opts; - - // display mode - if (options.chemicalbinding === 'show') { - var startAtoms; - if(this.chemicals !== undefined && Object.keys(this.chemicals).length > 0) { // show chemical-protein interaction - startAtoms = this.hash2Atoms(this.chemicals); - } - - // find atoms in chainid1, which interact with chainid2 - var radius = 4; - - if(startAtoms !== undefined) { - var targetAtoms = this.getAtomsWithinAtom(this.atoms, startAtoms, radius); - - var residueHash = {}; - - // draw sidec for these residues - for(var i in targetAtoms) { - if(startAtoms.hasOwnProperty(i)) continue; - residueHash[this.atoms[i].structure + '_' + this.atoms[i].chain + '_' + this.atoms[i].resi] = 1; - } - - var residueArray = Object.keys(residueHash); - for(var i = 0, il = residueArray.length; i < il; ++i) { - for(var j in this.residues[residueArray[i]]) { - // all atoms should be shown for hbonds - this.atoms[j].style2 = 'stick'; - } - } - - // show hydrogens - var threshold = 3.5; - this.opts["hbonds"] = "yes"; - //this.opts["water"] = "dot"; - - if(Object.keys(targetAtoms).length > 0) { - this.calculateChemicalHbonds(startAtoms, targetAtoms, parseFloat(threshold) ); - } - - // zoom in on the atoms - this.zoominSelection( this.unionHash(startAtoms, targetAtoms) ); - - //this.opts['fog'] = 'yes'; - } - } - else if (options.chemicalbinding === 'hide') { - // truen off hdonds - this.hideHbonds(); - - // center on the atoms - this.zoominSelection(this.atoms); - - //this.opts['fog'] = 'no'; - } -}; - -iCn3D.prototype.hideHbonds = function () { - this.opts["hbonds"] = "no"; - if(this.lines === undefined) this.lines = {}; - this.lines['hbond'] = []; - this.hbondpnts = []; - - for(var i in this.atoms) { - this.atoms[i].style2 = 'nothing'; - } - - for(var i in this.sidec) { - this.atoms[i].style2 = this.opts["sidec"]; - } - - for(var i in this.water) { - this.atoms[i].style = this.opts["water"]; - } -}; - -iCn3D.prototype.applySsbondsOptions = function (options) { - if(options === undefined) options = this.opts; - - if (options.ssbonds.toLowerCase() === 'yes' && this.ssbondpnts !== undefined) { - var color = '#FFFF00'; - var colorObj = new THREE.Color(0xFFFF00); - - var structureArray = Object.keys(this.structures); - var start, end; - - if(this.bAlternate) { - start = this.ALTERNATE_STRUCTURE; - end = this.ALTERNATE_STRUCTURE + 1; - } - else { - start = 0; - end = structureArray.length; - } - - this.lines['ssbond'] = []; - - for(var s = start, sl = end; s < sl; ++s) { - var structure = structureArray[s]; - - if(this.ssbondpnts[structure] === undefined) continue; - - for(var i = 0, lim = Math.floor(this.ssbondpnts[structure].length / 2); i < lim; i++) { - var res1 = this.ssbondpnts[structure][2 * i], res2 = this.ssbondpnts[structure][2 * i + 1]; - var serial1, serial2; - - var line = {}; - line.color = color; - line.dashed = true; - - var bFound = false; - for(var j in this.residues[res1]) { - if(this.atoms[j].name === 'SG') { - serial1 = this.atoms[j].serial; - line.position1 = this.atoms[j].coord; - bFound = true; - break; - } - } - - if(!bFound) { - for(var j in this.residues[res1]) { - if(this.atoms[j].name === 'CA') { - line.position1 = this.atoms[j].coord; - bFound = true; - break; - } - } - } - - bFound = false; - for(var j in this.residues[res2]) { - if(this.atoms[j].name === 'SG') { - serial2 = this.atoms[j].serial; - line.position2 = this.atoms[j].coord; - bFound = true; - break; - } - } - - if(!bFound) { - for(var j in this.residues[res2]) { - if(this.atoms[j].name === 'CA') { - line.position2 = this.atoms[j].coord; - bFound = true; - break; - } - } - } - - if(this.atoms[serial1].ids !== undefined) { // mmdb id asinput - // remove the originaldisulfide bonds - var pos = this.atoms[serial1].bonds.indexOf(serial2); - var array1, array2; - if(pos != -1) { - array1 = this.atoms[serial1].bonds.slice(0, pos); - array2 = this.atoms[serial1].bonds.slice(pos + 1); - - this.atoms[serial1].bonds = array1.concat(array2); - } - - pos = this.atoms[serial2].bonds.indexOf(serial1); - if(pos != -1) { - array1 = this.atoms[serial2].bonds.slice(0, pos); - array2 = this.atoms[serial2].bonds.slice(pos + 1); - - this.atoms[serial2].bonds = array1.concat(array2); - } - } - - //if(this.lines['ssbond'] === undefined) this.lines['ssbond'] = []; - this.lines['ssbond'].push(line); - - // create bonds for disulfide bonds - //this.createCylinder(line.position1, line.position2, this.cylinderRadius * 0.5, colorObj); - this.createCylinder(line.position1, line.position2, this.cylinderRadius, colorObj); - - // show ball and stick for these two residues - var residueAtoms = this.unionHash(this.residues[res1], this.residues[res2]); - - // show side chains for the selected atoms - var atoms = this.intHash(residueAtoms, this.sidec); - var calpha_atoms = this.intHash(residueAtoms, this.calphas); - // include calphas - atoms = this.unionHash(atoms, calpha_atoms); - - // draw sidec separatedly - for(var j in atoms) { - this.atoms[j].style2 = 'stick'; - } - } // for(var i = 0, - } // for(var s = 0, - } // if (options.ssbonds.toLowerCase() === 'yes' -}; - -iCn3D.prototype.applySurfaceOptions = function (options) { - if(options === undefined) options = this.opts; - - //switch (options.wireframe.toLowerCase()) { - switch (options.wireframe) { - case 'yes': - options.wireframe = true; - break; - case 'no': - options.wireframe = false; - break; - } - - options.opacity = parseFloat(options.opacity); - - var atoms, currAtoms; - - // only show the surface for atoms which are displaying - atoms = this.intHash(this.dAtoms, this.hAtoms); - - currAtoms = this.hash2Atoms(atoms); - - switch (options.surface.toLowerCase()) { - case 'van der waals surface': - this.createSurfaceRepresentation(currAtoms, 1, options.wireframe, options.opacity); - break; -// case 'solvent excluded surface': -// this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); -// break; - case 'solvent accessible surface': - this.createSurfaceRepresentation(currAtoms, 3, options.wireframe, options.opacity); - break; - case 'molecular surface': - this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); - break; - case 'van der waals surface with context': - this.createSurfaceRepresentation(currAtoms, 1, options.wireframe, options.opacity); - break; - case 'solvent accessible surface with context': - this.createSurfaceRepresentation(currAtoms, 3, options.wireframe, options.opacity); - break; - case 'molecular surface with context': - this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); - break; - case 'nothing': - // remove surfaces - this.removeSurfaces(); - break; - } -}; - -iCn3D.prototype.setFog = function() { - var background = this.backgroundColors[this.opts.background.toLowerCase()]; - - // apply fog - if(this.opts['fog'] === 'yes') { - if(this.opts['camera'] === 'perspective') { //perspective, orthographic - //this.scene.fog = new THREE.Fog(background, this.cam_z, this.cam_z + 0.5 * this.maxD); - //this.scene.fog = new THREE.Fog(background, 2 * this.maxD, 2.5 * this.maxD); - this.scene.fog = new THREE.Fog(background, 2 * this.maxD, 3 * this.maxD); - } - else if(this.opts['camera'] === 'orthographic') { - this.scene.fog = new THREE.FogExp2(background, 2); - //this.scene.fog.near = this.cam_z; - //this.scene.fog.far = this.cam_z + 0.5 * this.maxD; - this.scene.fog.near = 2 * this.maxD; - //this.scene.fog.far = 2.5 * this.maxD; - this.scene.fog.far = 3 * this.maxD; - } - } - else { - this.scene.fog = undefined; - } -}; - -// change the display atom when alternating -iCn3D.prototype.alternateStructures = function () { - this.dAtoms = {}; - - var hAtomsCount = Object.keys(this.hAtoms).length; - var allAtomsCount = Object.keys(this.atoms).length; - - var moleculeArray = Object.keys(this.structures); - for(var i = 0, il = moleculeArray.length; i < il; ++i) { - var structure = moleculeArray[i]; - if(i > this.ALTERNATE_STRUCTURE || (this.ALTERNATE_STRUCTURE === il - 1 && i === 0) ) { - for(var k in this.structures[structure]) { - var chain = this.structures[structure][k]; - this.dAtoms = this.unionHash(this.dAtoms, this.chains[chain]); - } - - this.ALTERNATE_STRUCTURE = i; - break; - } - } - - if(hAtomsCount < allAtomsCount) { - this.dAtoms = this.intHash(this.dAtoms, this.hAtoms); - - this.bShowHighlight = false; - this.opts['rotationcenter'] = 'highlight center'; - } - - // also alternating the surfaces - this.removeSurfaces(); - this.applySurfaceOptions(); - - this.draw(); - - this.bShowHighlight = true; - this.opts['rotationcenter'] = 'molecule center'; -}; - -iCn3D.prototype.updateStabilizer = function () { - this.stabilizerpnts = []; - - if(this.pairArray !== undefined) { - for(var i = 0, il = this.pairArray.length; i < il; i += 2) { - var coordI = this.getResidueRepPos(this.pairArray[i]); - var coordJ = this.getResidueRepPos(this.pairArray[i + 1]); - - this.stabilizerpnts.push(coordI); - this.stabilizerpnts.push(coordJ); - } - } -}; - -iCn3D.prototype.getResidueRepPos = function (serial) { var me = this; - var atomIn = this.atoms[serial]; - var residueid = atomIn.structure + "_" + atomIn.chain + "_" + atomIn.resi; - - var pos; - if(!this.proteins.hasOwnProperty(serial) && !this.nucleotides.hasOwnProperty(serial)) { // chemicals or ions - pos = atomIn.coord; - } - else { - for(var i in this.residues[residueid]) { - var atom = this.atoms[i]; - if(atom.name === 'N3') { // nucleotide: N3 - pos = this.atoms[i].coord; - break; - } - else if(atom.name === 'CA' && atom.ss == 'coil') { // protein coil: CA - pos = this.atoms[i].coord; - break; - } - else if(atom.name === 'CA' && (atom.ss == 'helix' || atom.ss == 'sheet')) { // protein secondary: CA - pos = (this.atoms[i].coord2 !== undefined) ? this.atoms[i].coord2 : this.atoms[i].coord; - break; - } - } - } - - if(pos === undefined) pos = atomIn.coord; - - return pos; -}; - -iCn3D.prototype.applyOtherOptions = function (options) { - if(options === undefined) options = this.opts; - - //common part options - - // lines - //if (options.hbonds.toLowerCase() === 'yes' || options.ncbonds.toLowerCase() === 'yes') { - if (options.hbonds.toLowerCase() === 'yes') { - var color = '#00FF00'; - var pnts = this.hbondpnts; - - for (var i = 0, lim = Math.floor(pnts.length / 2); i < lim; i++) { - var line = {}; - line.position1 = pnts[2 * i]; - line.position2 = pnts[2 * i + 1]; - line.color = color; - line.dashed = true; - - if(this.lines['hbond'] === undefined) this.lines['hbond'] = []; - this.lines['hbond'].push(line); - } - - //this.createLines(this.lines); - } - - if (this.pairArray !== undefined && this.pairArray.length > 0) { - this.updateStabilizer(); // to update this.stabilizerpnts - - var color = '#FFFFFF'; - var pnts = this.stabilizerpnts; - this.lines['stabilizer'] = []; // reset - for (var i = 0, lim = Math.floor(pnts.length / 2); i < lim; i++) { - var line = {}; - line.position1 = pnts[2 * i]; - line.position2 = pnts[2 * i + 1]; - line.color = color; - line.dashed = false; // if true, there will be too many cylinders in the dashed lines - - this.lines['stabilizer'].push(line); - } - } - - this.createLines(this.lines); - - // surfaces - if(this.prevSurfaces !== undefined) { - for(var i = 0, il = this.prevSurfaces.length; i < il; ++i) { - this.mdl.add(this.prevSurfaces[i]); - } - } - - this.applyCenterOptions(options); - - switch (options.axis.toLowerCase()) { - case 'yes': - this.axis = true; - - this.buildAxes(this.maxD/2); - - break; - case 'no': - this.axis = false; - break; - } - switch (options.pk.toLowerCase()) { - case 'atom': - this.pk = 1; - break; - case 'no': - this.pk = 0; - break; - case 'residue': - this.pk = 2; - break; - case 'strand': - this.pk = 3; - break; - } -}; - -iCn3D.prototype.rebuildScene = function (options) { var me = this; - this.rebuildSceneBase(options); - - if(this.bSkipChemicalbinding === undefined || !this.bSkipChemicalbinding) this.applyChemicalbindingOptions(); - this.bSkipChemicalbinding = true; - - // show disulfide bonds, set side chains - this.applySsbondsOptions(); - - this.applyDisplayOptions(this.opts, this.dAtoms); - - this.applyOtherOptions(); - - this.setFog(); - - this.setCamera(); - - //https://stackoverflow.com/questions/15726560/three-js-raycaster-intersection-empty-when-objects-not-part-of-scene - me.scene_ghost.updateMatrixWorld(true); -}; - -iCn3D.prototype.draw = function () { var me = this; - this.rebuildScene(); - - // Impostor display using the saved arrays - if(this.bImpo) { - this.drawImpostorShader(); - } - - this.applyPrevColor(); - - //if(this.bSSOnly) this.drawHelixBrick(this.molid2ss, this.molid2color); - - if(this.bAssembly) { - this.drawSymmetryMates2(); - } - - // show the hAtoms - var hAtomsLen = (this.hAtoms !== undefined) ? Object.keys(this.hAtoms).length : 0; - - //if(hAtomsLen > 0 && hAtomsLen < Object.keys(this.atoms).length) { - if(hAtomsLen > 0 && hAtomsLen < Object.keys(this.dAtoms).length) { - this.removeHlObjects(); - if(this.bShowHighlight === undefined || this.bShowHighlight) this.addHlObjects(); - } - - if(this.bRender === true) { - this.applyTransformation(this._zoomFactor, this.mouseChange, this.quaternion); - this.render(); - - // reset to hide the side chain - //this.opts['sidec'] = 'nothing'; - } - - this.clearImpostors(); -}; diff --git a/src/icn3d/display_simple.js b/src/icn3d/display_simple.js deleted file mode 100644 index 5f1d8c28..00000000 --- a/src/icn3d/display_simple.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @author Jiyao Wang / https://github.com/ncbi/icn3d - */ - -iCn3D.prototype.rebuildScene = function (options) { var me = this; - this.rebuildSceneBase(options); - - this.applyDisplayOptions(this.opts, this.dAtoms); - this.applyCenterOptions(); - - this.setCamera(); - - //https://stackoverflow.com/questions/15726560/three-js-raycaster-intersection-empty-when-objects-not-part-of-scene - me.scene_ghost.updateMatrixWorld(true); -}; - -iCn3D.prototype.draw = function () { var me = this; - this.rebuildScene(); - - // Impostor display using the saved arrays - if(this.bImpo) { - this.drawImpostorShader(); - } - - // show the hAtoms - if(this.hAtoms !== undefined && Object.keys(this.hAtoms).length > 0 && Object.keys(this.hAtoms).length < Object.keys(this.atoms).length) { - this.removeHlObjects(); - if(this.bShowHighlight === undefined || this.bShowHighlight) this.addHlObjects(); - } - - if(this.bRender === true) { - this.applyTransformation(this._zoomFactor, this.mouseChange, this.quaternion); - this.render(); - } - - this.clearImpostors(); -}; diff --git a/src/icn3d/drawing.js b/src/icn3d/drawing.js deleted file mode 100644 index 1b4892da..00000000 --- a/src/icn3d/drawing.js +++ /dev/null @@ -1,2240 +0,0 @@ -/** - * @author Jiyao Wang / https://github.com/ncbi/icn3d - */ - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createSphere = function (atom, defaultRadius, forceDefault, scale, bHighlight) { - var mesh; - - if(defaultRadius === undefined) defaultRadius = 0.8; - if(forceDefault === undefined) forceDefault = false; - if(scale === undefined) scale = 1.0; - - var radius = (this.vdwRadii[atom.elem] || defaultRadius); - - if(bHighlight === 2) { - //if(scale > 0.9) { // sphere - // scale = 1.5; - //} - //else if(scale < 0.5) { // dot - // scale = 1.0; - //} - - scale *= 1.5; - - var color = this.hColor; - - mesh = new THREE.Mesh(this.sphereGeometry, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); - - mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : radius * (scale ? scale : 1); - mesh.position.copy(atom.coord); - this.mdl.add(mesh); - } - else if(bHighlight === 1) { - mesh = new THREE.Mesh(this.sphereGeometry, this.matShader); - - mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : radius * (scale ? scale : 1); - mesh.position.copy(atom.coord); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - } - else { - if(atom.color === undefined) { - atom.color = this.defaultAtomColor; - } - - var color = atom.color; - - mesh = new THREE.Mesh(this.sphereGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); - mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : radius * (scale ? scale : 1); - mesh.position.copy(atom.coord); - - if(this.bImpo) { - this.posArraySphere.push(atom.coord.x); - this.posArraySphere.push(atom.coord.y); - this.posArraySphere.push(atom.coord.z); - - this.colorArraySphere.push(atom.color.r); - this.colorArraySphere.push(atom.color.g); - this.colorArraySphere.push(atom.color.b); - - var realRadius = forceDefault ? defaultRadius : radius * (scale ? scale : 1); - this.radiusArraySphere.push(realRadius); - - this.mdl_ghost.add(mesh); - } - else { - this.mdl.add(mesh); - } - } - - //this.mdl.add(mesh); - - if(bHighlight === 1 || bHighlight === 2) { - if(this.bImpo) { - this.prevHighlightObjects_ghost.push(mesh); - } - else { - this.prevHighlightObjects.push(mesh); - } - } - else { - if(this.bImpo) { - this.objects_ghost.push(mesh); - } - else { - this.objects.push(mesh); - } - } -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createCylinder = function (p0, p1, radius, color, bHighlight, color2, bPicking) { - var mesh; - if(bHighlight === 1) { - mesh = new THREE.Mesh(this.cylinderGeometryOutline, this.matShader); - - mesh.position.copy(p0).add(p1).multiplyScalar(0.5); - mesh.matrixAutoUpdate = false; - mesh.lookAt(p0); - mesh.updateMatrix(); - - mesh.matrix.multiply(new THREE.Matrix4().makeScale(radius, radius, p0.distanceTo(p1))).multiply(new THREE.Matrix4().makeRotationX(Math.PI * 0.5)); - - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - - this.prevHighlightObjects.push(mesh); - } - else { - if(bHighlight === 2) { - mesh = new THREE.Mesh(this.cylinderGeometry, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); - - radius *= 1.5; - } - else { - mesh = new THREE.Mesh(this.cylinderGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); - } - - mesh.position.copy(p0).add(p1).multiplyScalar(0.5); - mesh.matrixAutoUpdate = false; - mesh.lookAt(p0); - mesh.updateMatrix(); - - mesh.matrix.multiply(new THREE.Matrix4().makeScale(radius, radius, p0.distanceTo(p1))).multiply(new THREE.Matrix4().makeRotationX(Math.PI * 0.5)); - - if(this.bImpo) { - this.posArray.push(p0.x); - this.posArray.push(p0.y); - this.posArray.push(p0.z); - - this.colorArray.push(color.r); - this.colorArray.push(color.g); - this.colorArray.push(color.b); - - this.pos2Array.push(p1.x); - this.pos2Array.push(p1.y); - this.pos2Array.push(p1.z); - - if(color2 !== undefined) { - this.color2Array.push(color2.r); - this.color2Array.push(color2.g); - this.color2Array.push(color2.b); - } - else { - this.color2Array.push(color.r); - this.color2Array.push(color.g); - this.color2Array.push(color.b); - } - - this.radiusArray.push(radius); - - this.mdl_ghost.add(mesh); - } - else { - this.mdl.add(mesh); - } - - if(bHighlight === 2) { - if(this.bImpo) { - this.prevHighlightObjects_ghost.push(mesh); - } - else { - this.prevHighlightObjects.push(mesh); - } - } - else { - if(this.bImpo) { - this.objects_ghost.push(mesh); - } - else { - if(bPicking === undefined || bPicking) this.objects.push(mesh); - } - } - } -}; - -// from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createRepresentationSub = function (atoms, f0, f01) { - var me = this; - - //var ged = new THREE.Geometry(); - var clbondArray = []; - for (var i in atoms) { - var atom0 = atoms[i]; - f0 && f0(atom0); - for (var j in atom0.bonds) { - var atom1 = this.atoms[atom0.bonds[j]]; - if (atom1 === undefined || atom1.serial < atom0.serial) continue; - if (atom1.chain === atom0.chain && ((atom1.resi === atom0.resi) || (atom0.name === 'C' && atom1.name === 'N') || (atom0.name === 'O3\'' && atom1.name === 'P') || (atom0.name === 'O3*' && atom1.name === 'P') || (atom0.name === 'SG' && atom1.name === 'SG'))) { - f01 && f01(atom0, atom1); - } else { - //ged.vertices.push(atom0.coord); - //ged.vertices.push(atom1.coord); - clbondArray.push([atom0.coord, atom1.coord]); - } - } - } - //if (ged.vertices.length && this.bShowCrossResidueBond) { - if (clbondArray.length > 0 && this.bShowCrossResidueBond) { - //ged.computeLineDistances(); - //this.mdl.add(new THREE.Line(ged, new THREE.LineDashedMaterial({ linewidth: this.linewidth, color: this.defaultBondColor, dashSize: 0.3, gapSize: 0.15 }), THREE.LinePieces)); - var color = new THREE.Color(0x00FF00); - - for(var i = 0, il = clbondArray.length; i < il; ++i) { - me.createCylinder(clbondArray[i][0], clbondArray[i][1], this.cylinderRadius, color, 0); - } - } -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createSphereRepresentation = function (atoms, defaultRadius, forceDefault, scale, bHighlight) { - var me = this; - - this.createRepresentationSub(atoms, function (atom0) { - me.createSphere(atom0, defaultRadius, forceDefault, scale, bHighlight); - }); -}; - -iCn3D.prototype.createBoxRepresentation_P_CA = function (atoms, scale, bHighlight) { - var me = this; - this.createRepresentationSub(atoms, function (atom0) { - if(atom0.name === 'CA' || atom0.name === "O3'" || atom0.name === "O3*") { - me.createBox(atom0, undefined, undefined, scale, undefined, bHighlight); - } - }); -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createStickRepresentation = function (atoms, atomR, bondR, scale, bHighlight, bSchematic) { - var me = this; - var factor = (bSchematic !== undefined && bSchematic) ? atomR / me.cylinderRadius : 1; - -// if(bHighlight !== 2) { - this.createRepresentationSub(atoms, function (atom0) { - me.createSphere(atom0, atomR, !scale, scale, bHighlight); - }, function (atom0, atom1) { - var mp = atom0.coord.clone().add(atom1.coord).multiplyScalar(0.5); - var pair = atom0.serial + '_' + atom1.serial; - - if(me.doublebonds.hasOwnProperty(pair)) { // show double bond - var a0, a1, a2; - - var v0; - var random = new THREE.Vector3(Math.random(),Math.random(),Math.random()); - if(atom0.bonds.length == 1 && atom1.bonds.length == 1) { - v0 = atom1.coord.clone(); - v0.sub(atom0.coord); - - var v = random.clone(); - v0.cross(v).normalize().multiplyScalar(0.2 * factor); - } - else { - if(atom0.bonds.length >= atom1.bonds.length && atom0.bonds.length > 1) { - a0 = atom0.serial; - a1 = atom0.bonds[0]; - a2 = atom0.bonds[1]; - } - //else { - else if(atom1.bonds.length >= atom0.bonds.length && atom1.bonds.length > 1) { - a0 = atom1.serial; - a1 = atom1.bonds[0]; - a2 = atom1.bonds[1]; - } - else { - console.log("Double bond was not drawn due to the undefined cross plane"); - return; - } - - var v1 = me.atoms[a0].coord.clone(); - v1.sub(me.atoms[a1].coord); - var v2 = me.atoms[a0].coord.clone(); - v2.sub(me.atoms[a2].coord); - - v1.cross(v2); - - // parallel - if(parseInt(v1.length() * 10000) == 0) { - //v1 = random.clone(); - // use a constant so that they are fixed,e.g., in CO2 - v1 = new THREE.Vector3(0.2, 0.3, 0.5); - } - - v0 = atom1.coord.clone(); - v0.sub(atom0.coord); - - v0.cross(v1).normalize().multiplyScalar(0.2 * factor); - // parallel - if(parseInt(v0.length() * 10000) == 0) { - //v1 = random.clone(); - // use a constant so that they are fixed,e.g., in CO2 - v1 = new THREE.Vector3(0.5, 0.3, 0.2); - v0.cross(v1).normalize().multiplyScalar(0.2 * factor); - } - } - - if (atom0.color === atom1.color) { - me.createCylinder(atom0.coord.clone().add(v0), atom1.coord.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - me.createCylinder(atom0.coord.clone().sub(v0), atom1.coord.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - } else { - if(me.bImpo) { - me.createCylinder(atom0.coord.clone().add(v0), atom1.coord.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight, atom1.color); - me.createCylinder(atom0.coord.clone().sub(v0), atom1.coord.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight, atom1.color); - } - else { - me.createCylinder(atom0.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - me.createCylinder(atom1.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); - - me.createCylinder(atom0.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - me.createCylinder(atom1.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); - } - } - } - else if(me.aromaticbonds.hasOwnProperty(pair)) { // show aromatic bond - var a0, a1, a2; - if(atom0.bonds.length > atom1.bonds.length && atom0.bonds.length > 1) { - a0 = atom0.serial; - a1 = atom0.bonds[0]; - a2 = atom0.bonds[1]; - } - else if(atom1.bonds.length > 1) { - a0 = atom1.serial; - a1 = atom1.bonds[0]; - a2 = atom1.bonds[1]; - } - else { - return; - } - - var v1 = me.atoms[a0].coord.clone(); - v1.sub(me.atoms[a1].coord); - var v2 = me.atoms[a0].coord.clone(); - v2.sub(me.atoms[a2].coord); - - v1.cross(v2); - - var v0 = atom1.coord.clone(); - v0.sub(atom0.coord); - - v0.cross(v1).normalize().multiplyScalar(0.2 * factor); - - // find an aromatic neighbor - var aromaticNeighbor = 0; - for(var i = 0, il = atom0.bondOrder.length; i < il; ++i) { - if(atom0.bondOrder[i] === '1.5' && atom0.bonds[i] !== atom1.serial) { - aromaticNeighbor = atom0.bonds[i]; - } - } - - var dashed = "add"; - if(aromaticNeighbor === 0 ) { // no neighbor found, atom order does not matter - dashed = "add"; - } - else { - // calculate the angle between atom1, atom0add, atomNeighbor and the angle atom1, atom0sub, atomNeighbor - var atom0add = atom0.coord.clone().add(v0); - var atom0sub = atom0.coord.clone().sub(v0); - - var a = atom1.coord.clone().sub(atom0add).normalize(); - var b = me.atoms[aromaticNeighbor].coord.clone().sub(atom0add).normalize(); - - var c = atom1.coord.clone().sub(atom0sub).normalize(); - var d = me.atoms[aromaticNeighbor].coord.clone().sub(atom0sub).normalize(); - - var angleadd = Math.acos(a.dot(b)); - var anglesub = Math.acos(c.dot(d)); - - if(angleadd < anglesub) { - dashed = 'sub'; - } - else { - dashed = 'add'; - } - } - - if (atom0.color === atom1.color) { - var base, step; - if(dashed === 'add') { - me.createCylinder(atom0.coord.clone().sub(v0), atom1.coord.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - - base = atom0.coord.clone().add(v0); - step = atom1.coord.clone().add(v0).sub(base).multiplyScalar(1.0/11); - } - else { - me.createCylinder(atom0.coord.clone().add(v0), atom1.coord.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - - base = atom0.coord.clone().sub(v0); - step = atom1.coord.clone().sub(v0).sub(base).multiplyScalar(1.0/11); - } - - for(var i = 0; i <= 10; ++i) { - if(i % 2 == 0) { - var pos1 = base.clone().add(step.clone().multiplyScalar(i)); - var pos2 = base.clone().add(step.clone().multiplyScalar(i + 1)); - me.createCylinder(pos1, pos2, me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - } - } - - } else { - var base, step; - if(dashed === 'add') { - me.createCylinder(atom0.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - me.createCylinder(atom1.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); - - base = atom0.coord.clone().add(v0); - step = atom1.coord.clone().add(v0).sub(base).multiplyScalar(1.0/11); - } - else { - me.createCylinder(atom0.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - me.createCylinder(atom1.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); - - base = atom0.coord.clone().sub(v0); - step = atom1.coord.clone().sub(v0).sub(base).multiplyScalar(1.0/11); - } - - for(var i = 0; i <= 10; ++i) { - if(i % 2 == 0) { - var pos1 = base.clone().add(step.clone().multiplyScalar(i)); - var pos2 = base.clone().add(step.clone().multiplyScalar(i + 1)); - if(i < 5) { - me.createCylinder(pos1, pos2, me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); - } - else { - me.createCylinder(pos1, pos2, me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); - } - } - } - } - } - else if(me.triplebonds.hasOwnProperty(pair)) { // show triple bond - var random = new THREE.Vector3(Math.random(),Math.random(),Math.random()); - var v = atom1.coord.clone(); - v.sub(atom0.coord); - - var c = random.clone(); - c.cross(v).normalize().multiplyScalar(0.3 * factor); - - if (atom0.color === atom1.color) { - me.createCylinder(atom0.coord, atom1.coord, me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); - me.createCylinder(atom0.coord.clone().add(c), atom1.coord.clone().add(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); - me.createCylinder(atom0.coord.clone().sub(c), atom1.coord.clone().sub(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); - } else { - if(me.bImpo) { - me.createCylinder(atom0.coord, atom1.coord, me.cylinderRadius * factor * 0.2, atom0.color, bHighlight, atom1.color); - me.createCylinder(atom0.coord.clone().add(c), atom1.coord.clone().add(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight, atom1.color); - me.createCylinder(atom0.coord.clone().sub(c), atom1.coord.clone().sub(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight, atom1.color); - } - else { - me.createCylinder(atom0.coord, mp, me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); - me.createCylinder(atom1.coord, mp, me.cylinderRadius * factor * 0.2, atom1.color, bHighlight); - - me.createCylinder(atom0.coord.clone().add(c), mp.clone().add(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); - me.createCylinder(atom1.coord.clone().add(c), mp.clone().add(c), me.cylinderRadius * factor * 0.2, atom1.color, bHighlight); - - me.createCylinder(atom0.coord.clone().sub(c), mp.clone().sub(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); - me.createCylinder(atom1.coord.clone().sub(c), mp.clone().sub(c), me.cylinderRadius * factor * 0.2, atom1.color, bHighlight); - } - } - } - else { - if (atom0.color === atom1.color) { - me.createCylinder(atom0.coord, atom1.coord, bondR, atom0.color, bHighlight); - } else { - if(me.bImpo) { - me.createCylinder(atom0.coord, atom1.coord, bondR, atom0.color, bHighlight, atom1.color); - } - else { - me.createCylinder(atom0.coord, mp, bondR, atom0.color, bHighlight); - me.createCylinder(atom1.coord, mp, bondR, atom1.color, bHighlight); - } - } - } - }); -// } -// else if(bHighlight === 2) { -// this.createBoxRepresentation_P_CA(atoms, 1.2, bHighlight); -// } -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createLineRepresentation = function (atoms, bHighlight) { - var me = this; - var geo = new THREE.Geometry(); - this.createRepresentationSub(atoms, undefined, function (atom0, atom1) { - if (atom0.color === atom1.color) { - geo.vertices.push(atom0.coord); - geo.vertices.push(atom1.coord); - geo.colors.push(atom0.color); - geo.colors.push(atom1.color); - } else { - var mp = atom0.coord.clone().add(atom1.coord).multiplyScalar(0.5); - geo.vertices.push(atom0.coord); - geo.vertices.push(mp); - geo.vertices.push(atom1.coord); - geo.vertices.push(mp); - geo.colors.push(atom0.color); - geo.colors.push(atom0.color); - geo.colors.push(atom1.color); - geo.colors.push(atom1.color); - } - }); - - if(bHighlight !== 2) { - var line; - if(bHighlight === 1) { - // highlight didn't work for lines - //line = new THREE.Mesh(geo, this.matShader); - } - else { - line = new THREE.Line(geo, new THREE.LineBasicMaterial({ linewidth: this.linewidth, vertexColors: true }), THREE.LinePieces); - this.mdl.add(line); - } - - if(bHighlight === 1) { - this.prevHighlightObjects.push(line); - } - else { - this.objects.push(line); - } - } - else if(bHighlight === 2) { - this.createBoxRepresentation_P_CA(atoms, 0.8, bHighlight); - } -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.subdivide = function (_pnts, _clrs, DIV, bShowArray, bHighlight) { // Catmull-Rom subdivision - var ret = []; - var pos = []; - var color = []; - - var pnts = new Array(); // Smoothing test - pnts.push(_pnts[0]); - for (var i = 1, lim = _pnts.length - 1; i < lim; ++i) { - var p0 = _pnts[i], p1 = _pnts[i + 1]; - pnts.push(p0.smoothen ? p0.clone().add(p1).multiplyScalar(0.5) : p0); - } - pnts.push(_pnts[_pnts.length - 1]); - - var savedPoints = []; - var savedPos = []; - var savedColor = []; - for (var i = -1, size = pnts.length, DIVINV = 1 / DIV; i <= size - 3; ++i) { - var p0 = pnts[i === -1 ? 0 : i]; - var p1 = pnts[i + 1], p2 = pnts[i + 2]; - var p3 = pnts[i === size - 3 ? size - 1 : i + 3]; - var v0 = p2.clone().sub(p0).multiplyScalar(0.5); - var v1 = p3.clone().sub(p1).multiplyScalar(0.5); - - //if(i > -1 && bHighlight && bShowArray !== undefined && bShowArray[i + 1]) { - if(i > -1 && (bShowArray === undefined || bShowArray[i + 1]) ) { - // get from previous i for the first half of residue - ret = ret.concat(savedPoints); - pos = pos.concat(savedPos); - color = color.concat(savedColor); - } - - savedPoints = []; - savedPos = []; - savedColor = []; - - for (var j = 0; j < DIV; ++j) { - var t = DIVINV * j; - var x = p1.x + t * v0.x - + t * t * (-3 * p1.x + 3 * p2.x - 2 * v0.x - v1.x) - + t * t * t * (2 * p1.x - 2 * p2.x + v0.x + v1.x); - var y = p1.y + t * v0.y - + t * t * (-3 * p1.y + 3 * p2.y - 2 * v0.y - v1.y) - + t * t * t * (2 * p1.y - 2 * p2.y + v0.y + v1.y); - var z = p1.z + t * v0.z - + t * t * (-3 * p1.z + 3 * p2.z - 2 * v0.z - v1.z) - + t * t * t * (2 * p1.z - 2 * p2.z + v0.z + v1.z); - if(!bShowArray) { - ret.push(new THREE.Vector3(x, y, z)); - pos.push(i + 1); - color.push(_clrs[i+1]); - } - else { - if(bShowArray[i + 1]) { - if(j <= parseInt((DIV) / 2) ) { - ret.push(new THREE.Vector3(x, y, z)); - pos.push(bShowArray[i + 1]); - color.push(_clrs[i+1]); - } - } - - if(bShowArray[i + 2]) { - if(j > parseInt((DIV) / 2) ) { - savedPoints.push(new THREE.Vector3(x, y, z)); - savedPos.push(bShowArray[i + 2]); - savedColor.push(_clrs[i+2]); - } - } - } // end else - } - - } - - if(!bShowArray || bShowArray[i + 1]) { - //if(bHighlight) { - ret = ret.concat(savedPoints); - pos = pos.concat(savedPos); - color = color.concat(savedColor); - //} - - ret.push(pnts[pnts.length - 1]); - pos.push(pnts.length - 1); - color.push(_clrs[pnts.length - 1]); - } - - savedPoints = []; - savedPos = []; - savedColor = []; - pnts = []; - - pnts_positions = []; - - pnts_positions.push(ret); - pnts_positions.push(pos); - pnts_positions.push(color); - - return pnts_positions; -}; - -iCn3D.prototype.createCurveSubArrow = function (p, width, colors, div, bHighlight, bRibbon, num, positionIndex, pntsCA, prevCOArray, bShowArray, calphaIdArray, bShowArrow) { - var divPoints = [], positions = []; - - divPoints.push(p); - positions.push(positionIndex); - - this.prepareStrand(divPoints, positions, width, colors, div, undefined, bHighlight, bRibbon, num, pntsCA, prevCOArray, false, bShowArray, calphaIdArray, bShowArrow); - - divPoints = []; - positions = []; -}; - -iCn3D.prototype.setCalphaDrawnCoord = function (pnts, div, calphaIdArray) { - var index = 0; - - if(calphaIdArray !== undefined) { - for(var i = 0, il = pnts.length; i < il; i += div) { // pnts.length = (calphaIdArray.length - 1) * div + 1 - var serial = calphaIdArray[index]; - - if(this.atoms.hasOwnProperty(serial)) { - this.atoms[serial].coord2 = pnts[i].clone(); - } - - ++index; - } - } -}; - - -// modified from iview (http://star.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createCurveSub = function (_pnts, width, colors, div, bHighlight, bRibbon, bNoSmoothen, bShowArray, calphaIdArray, positions) { - if (_pnts.length === 0) return; - div = div || 5; - var pnts; - if(!bNoSmoothen) { - var pnts_clrs = this.subdivide(_pnts, colors, div, bShowArray, bHighlight); - pnts = pnts_clrs[0]; - colors = pnts_clrs[2]; - } - else { - pnts = _pnts; - } - if (pnts.length === 0) return; - - this.setCalphaDrawnCoord(pnts, div, calphaIdArray); - - if(bHighlight === 1) { - var radius = this.coilWidth / 2; - //var radiusSegments = 8; - var radiusSegments = 4; // save memory - var closed = false; - - if(pnts.length > 1) { - if(positions !== undefined) { - var currPos, prevPos; - var currPoints = []; - for(var i = 0, il = pnts.length; i < il; ++i) { - currPos = positions[i]; - - if( (currPos !== prevPos && currPos !== prevPos + 1 && prevPos !== undefined) || (i === il -1) ) { - // first tube - var geometry0 = new THREE.TubeGeometry( - new THREE.CatmullRomCurve3(currPoints), // path - currPoints.length, // segments - radius, - radiusSegments, - closed - ); - - mesh = new THREE.Mesh(geometry0, this.matShader); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - - this.prevHighlightObjects.push(mesh); - - geometry0 = null; - - currPoints = []; - } - - currPoints.push(pnts[i]); - - prevPos = currPos; - } - - currPoints = []; - } - else { - var geometry0 = new THREE.TubeGeometry( - new THREE.CatmullRomCurve3(pnts), // path - pnts.length, // segments - radius, - radiusSegments, - closed - ); - - mesh = new THREE.Mesh(geometry0, this.matShader); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - - this.prevHighlightObjects.push(mesh); - - geometry0 = null; - } - } - } - else { - var geo = new THREE.Geometry(); - - if(bHighlight === 2 && bRibbon) { - for (var i = 0, divInv = 1 / div; i < pnts.length; ++i) { - // shift the highlight a little bit to avoid the overlap with ribbon - pnts[i].addScalar(0.6); // this.ribbonthickness is 0.4 - geo.vertices.push(pnts[i]); - //geo.colors.push(new THREE.Color(colors[i === 0 ? 0 : Math.round((i - 1) * divInv)])); - geo.colors.push(new THREE.Color(colors[i])); - } - } - else { - for (var i = 0, divInv = 1 / div; i < pnts.length; ++i) { - geo.vertices.push(pnts[i]); - //geo.colors.push(new THREE.Color(colors[i === 0 ? 0 : Math.round((i - 1) * divInv)])); - geo.colors.push(new THREE.Color(colors[i])); - } - } - - var line = new THREE.Line(geo, new THREE.LineBasicMaterial({ linewidth: width, vertexColors: true }), THREE.LineStrip); - this.mdl.add(line); - if(bHighlight === 2) { - this.prevHighlightObjects.push(line); - } - else { - this.objects.push(line); - } - } - - pnts = null; -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createCylinderCurve = function (atoms, atomNameArray, radius, bLines, bHighlight) { - var start = null; - var currentChain, currentResi; - var i; - var pnts = [], colors = [], radii = []; - - var maxDistance = 8.0; // max residue-residue (or nucleitide-nucleitide) distance allowed - - for (i in atoms) { - var atom = atoms[i]; - if (atom.het) continue; - - //if (atom.name !== atomName) continue; - if(atomNameArray.indexOf(atom.name) == -1) continue; - - if (start !== null && currentChain === atom.chain && currentResi + 1 === atom.resi && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { -// if (start !== null && currentChain === atom.chain && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { - var middleCoord = start.coord.clone().add(atom.coord).multiplyScalar(0.5); - - if(!bHighlight) { - if(bLines) { - var line = this.createSingleLine( start.coord, middleCoord, start.color, false); - this.mdl.add(line); - this.objects.push(line); - line = this.createSingleLine( middleCoord, atom.coord, atom.color, false); - this.mdl.add(line); - this.objects.push(line); - } - else { - this.createCylinder(start.coord, middleCoord, radius, start.color); - this.createCylinder(middleCoord, atom.coord, radius, atom.color); - this.createSphere(atom, radius, true, 1, bHighlight); - } - } - else if(bHighlight === 1) { - this.createCylinder(start.coord, middleCoord, radius, start.color, bHighlight); - this.createCylinder(middleCoord, atom.coord, radius, atom.color, bHighlight); - this.createSphere(atom, radius, true, 1, bHighlight); - } - } - - start = atom; - currentChain = atom.chain; - currentResi = atom.resi; - - if(bHighlight === 2) this.createBox(atom, undefined, undefined, undefined, undefined, bHighlight); - } - if (start !== null && currentChain === atom.chain && currentResi + 1 === atom.resi && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { -// if (start !== null && currentChain === atom.chain && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { - var middleCoord = start.coord.add(atom.coord).multiplyScalar(0.5); - if(!bHighlight) { - if(bLines) { - var line = this.createSingleLine( start.coord, middleCoord, start.color, false); - this.mdl.add(line); - this.objects.push(line); - line = this.createSingleLine( middleCoord, atom.coord, atom.color, false); - this.mdl.add(line); - this.objects.push(line); - } - else { - this.createCylinder(start.coord, middleCoord, radius, start.color); - this.createCylinder(middleCoord, atom.coord, radius, atom.color); - } - } - else if(bHighlight === 1) { - this.createCylinder(start.coord, middleCoord, radius, start.color, bHighlight); - this.createCylinder(middleCoord, atom.coord, radius, atom.color, bHighlight); - this.createSphere(atom, radius, true, 1, bHighlight); - } - } -}; - -iCn3D.prototype.prepareStrand = function(divPoints, positions, width, colors, div, thickness, bHighlight, bRibbon, num, pntsCA, prevCOArray, bStrip, bShowArray, calphaIdArray, bShowArrow) { - if(pntsCA.length === 1) { - return; - } - - var colorsLastTwo = []; - colorsLastTwo.push(colors[colors.length - 2]); - colorsLastTwo.push(colors[colors.length - 1]); - - div = div || this.axisDIV; - var numM1Inv2 = 2 / (num - 1); - var delta, lastCAIndex, lastPrevCOIndex, v; - - var pnts = {}, colorsTmp = []; - for(var i = 0, il = positions.length; i < il; ++i) pnts[i] = []; - - // smooth C-alpha - var pnts_clrs = this.subdivide(pntsCA, colors, div); - var pntsCASmooth = pnts_clrs[0]; // get all smoothen pnts, do not use 'bShowArray' - //colors = pnts_clrs[2]; - - if(pntsCASmooth.length === 1) { - return; - } - - // draw the sheet without the last residue - // use the sheet coord for n-2 residues - var colorsTmp = []; - var lastIndex = (bShowArrow === undefined || bShowArrow) ? pntsCA.length - 2 : pntsCA.length; - - for (var i = 0, il = lastIndex; i < il; ++i) { - for(var index = 0, indexl = positions.length; index < indexl; ++index) { - pnts[index].push(divPoints[index][i]); - } - colorsTmp.push(colors[i]); - } - colorsTmp.push(colors[i]); - - if(bShowArrow === undefined || bShowArrow) { - // assign the sheet coord from C-alpha for the 2nd to the last residue of the sheet - for(var i = 0, il = positions.length; i < il; ++i) { - delta = -1 + numM1Inv2 * positions[i]; - lastCAIndex = pntsCASmooth.length - 1 - div; - lastPrevCOIndex = pntsCA.length - 2; - v = new THREE.Vector3(pntsCASmooth[lastCAIndex].x + prevCOArray[lastPrevCOIndex].x * delta, pntsCASmooth[lastCAIndex].y + prevCOArray[lastPrevCOIndex].y * delta, pntsCASmooth[lastCAIndex].z + prevCOArray[lastPrevCOIndex].z * delta); - pnts[i].push(v); - } - } - - var posIndex = []; - var results; - for(var i = 0, il = positions.length; i < il; ++i) { - results = this.subdivide(pnts[i], colorsTmp, div, bShowArray, bHighlight); - pnts[i] = results[0]; - colors = results[2]; - if(i === 0) { - posIndex = results[1]; - } - } - - if(bStrip) { - //this.createStrip(pnts[0], pnts[1], colorsTmp, div, thickness, bHighlight, true, undefined, posIndex); - this.createStrip(pnts[0], pnts[1], colors, div, thickness, bHighlight, true, undefined, calphaIdArray, posIndex); - } - else { - //this.createCurveSub(pnts[0], width, colorsTmp, div, bHighlight, bRibbon, true, undefined, posIndex); - this.createCurveSub(pnts[0], width, colors, div, bHighlight, bRibbon, true, undefined, calphaIdArray, posIndex); - } - - if(bShowArrow === undefined || bShowArrow) { - // draw the arrow - colorsTmp = []; - - posIndex = []; - for(var index = 0, indexl = positions.length; index < indexl; ++index) { - pnts[index] = []; - - for (var i = div * (pntsCA.length - 2), il = div * (pntsCA.length - 1); bShowArray[parseInt(i/div)] && i < il; i = i + div) { - var pos = parseInt(i/div); - for (var j = 0; j < div; ++j) { - var delta = -1 + numM1Inv2 * positions[index]; - var scale = 1.8; // scale of the arrow width - delta = delta * scale * (div - j) / div; - var oriIndex = parseInt(i/div); - - var v = new THREE.Vector3(pntsCASmooth[i+j].x + prevCOArray[oriIndex].x * delta, pntsCASmooth[i+j].y + prevCOArray[oriIndex].y * delta, pntsCASmooth[i+j].z + prevCOArray[oriIndex].z * delta); - v.smoothen = true; - pnts[index].push(v); - colorsTmp.push(colorsLastTwo[0]); - if(index === 0) posIndex.push(pos); - } - } - - // last residue - // make the arrow end with 0 - var delta = 0; - var lastCAIndex = pntsCASmooth.length - 1; - var lastPrevCOIndex = pntsCA.length - 1; - - //if(bShowArray[lastPrevCOIndex]) { - var v = new THREE.Vector3(pntsCASmooth[lastCAIndex].x + prevCOArray[lastPrevCOIndex].x * delta, pntsCASmooth[lastCAIndex].y + prevCOArray[lastPrevCOIndex].y * delta, pntsCASmooth[lastCAIndex].z + prevCOArray[lastPrevCOIndex].z * delta); - v.smoothen = true; - pnts[index].push(v); - colorsTmp.push(colorsLastTwo[1]); - if(index === 0) posIndex.push(lastCAIndex); - //} - } - - pntsCASmooth = []; - - //colorsTmp.push(colors[colors.length - 2]); - //colorsTmp.push(colors[colors.length - 1]); - - if(bStrip) { - this.createStrip(pnts[0], pnts[1], colorsTmp, div, thickness, bHighlight, true, undefined, undefined, posIndex); - } - else { - this.createCurveSub(pnts[0], width, colorsTmp, div, bHighlight, bRibbon, true, undefined, undefined, posIndex); - } - } - - for(var i in pnts) { - for(var j = 0, jl = pnts[i].length; j < jl; ++j) { - pnts[i][j] = null; - } - pnts[i] = []; - } - - pnts = {}; -}; - -iCn3D.prototype.createStripArrow = function (p0, p1, colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, bShowArrow) { - var divPoints = [], positions = []; - - divPoints.push(p0); - divPoints.push(p1); - positions.push(start); - positions.push(end); - - this.prepareStrand(divPoints, positions, undefined, colors, div, thickness, bHighlight, undefined, num, pntsCA, prevCOArray, true, bShowArray, calphaIdArray, bShowArrow); - - divPoints = []; - positions = []; -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createStrip = function (p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray, calphaIdArray, positions) { - if (p0.length < 2) return; - div = div || this.axisDIV; - if(!bNoSmoothen) { - var pnts_clrs0 = this.subdivide(p0, colors, div, bShowArray, bHighlight); - var pnts_clrs1 = this.subdivide(p1, colors, div, bShowArray, bHighlight); - p0 = pnts_clrs0[0]; - p1 = pnts_clrs1[0]; - colors = pnts_clrs0[2]; - } - if (p0.length < 2) return; - - this.setCalphaDrawnCoord(p0, div, calphaIdArray); - - if(bHighlight === 1) { - //mesh = new THREE.Mesh(geo, this.matShader); - - var radius = this.coilWidth / 2; - //var radiusSegments = 8; - var radiusSegments = 4; // save memory - var closed = false; - - if(positions !== undefined) { - var currPos, prevPos; - var currP0 = [], currP1 = []; - - for(var i = 0, il = p0.length; i < il; ++i) { - currPos = positions[i]; - - if((currPos !== prevPos && currPos !== prevPos + 1 && prevPos !== undefined) || (i === il -1) ) { - // first tube - var geometry0 = new THREE.TubeGeometry( - new THREE.CatmullRomCurve3(currP0), // path - currP0.length, // segments - radius, - radiusSegments, - closed - ); - - mesh = new THREE.Mesh(geometry0, this.matShader); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - - this.prevHighlightObjects.push(mesh); - - geometry0 = null; - - // second tube - var geometry1 = new THREE.TubeGeometry( - new THREE.CatmullRomCurve3(currP1), // path - currP1.length, // segments - radius, - radiusSegments, - closed - ); - - mesh = new THREE.Mesh(geometry1, this.matShader); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - - this.prevHighlightObjects.push(mesh); - - geometry1 = null; - - currP0 = []; - currP1 = []; - } - - currP0.push(p0[i]); - currP1.push(p1[i]); - - prevPos = currPos; - } - - currP0 = []; - currP1 = []; - } - else { - // first tube - var geometry0 = new THREE.TubeGeometry( - new THREE.CatmullRomCurve3(p0), // path - p0.length, // segments - radius, - radiusSegments, - closed - ); - - mesh = new THREE.Mesh(geometry0, this.matShader); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - - this.prevHighlightObjects.push(mesh); - - geometry0 = null; - - // second tube - var geometry1 = new THREE.TubeGeometry( - new THREE.CatmullRomCurve3(p1), // path - p1.length, // segments - radius, - radiusSegments, - closed - ); - - mesh = new THREE.Mesh(geometry1, this.matShader); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - - this.prevHighlightObjects.push(mesh); - - geometry1 = null; - } - } - else { - var geo = new THREE.Geometry(); - var vs = geo.vertices, fs = geo.faces; - var axis, p0v, p1v, a0v, a1v; - for (var i = 0, lim = p0.length; i < lim; ++i) { - vs.push(p0v = p0[i]); // 0 - vs.push(p0v); // 1 - vs.push(p1v = p1[i]); // 2 - vs.push(p1v); // 3 - if (i < lim - 1) { - axis = p1[i].clone().sub(p0[i]).cross(p0[i + 1].clone().sub(p0[i])).normalize().multiplyScalar(thickness); - } - vs.push(a0v = p0[i].clone().add(axis)); // 4 - vs.push(a0v); // 5 - vs.push(a1v = p1[i].clone().add(axis)); // 6 - vs.push(a1v); // 7 - } - var faces = [[0, 2, -6, -8], [-4, -2, 6, 4], [7, 3, -5, -1], [-3, -7, 1, 5]]; - - for (var i = 1, lim = p0.length, divInv = 1 / div; i < lim; ++i) { - var offset = 8 * i; - //var color = new THREE.Color(colors[Math.round((i - 1) * divInv)]); - var color = new THREE.Color(colors[i - 1]); - for (var j = 0; j < 4; ++j) { - fs.push(new THREE.Face3(offset + faces[j][0], offset + faces[j][1], offset + faces[j][2], undefined, color)); - fs.push(new THREE.Face3(offset + faces[j][3], offset + faces[j][0], offset + faces[j][2], undefined, color)); - } - } - var vsize = vs.length - 8; // Cap - for (var i = 0; i < 4; ++i) { - vs.push(vs[i * 2]); - vs.push(vs[vsize + i * 2]); - }; - vsize += 8; - fs.push(new THREE.Face3(vsize, vsize + 2, vsize + 6, undefined, fs[0].color)); - fs.push(new THREE.Face3(vsize + 4, vsize, vsize + 6, undefined, fs[0].color)); - fs.push(new THREE.Face3(vsize + 1, vsize + 5, vsize + 7, undefined, fs[fs.length - 3].color)); - fs.push(new THREE.Face3(vsize + 3, vsize + 1, vsize + 7, undefined, fs[fs.length - 3].color)); - geo.computeFaceNormals(); - geo.computeVertexNormals(false); - - var mesh; - - if(bHighlight === 2) { - mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); - - this.mdl.add(mesh); - this.prevHighlightObjects.push(mesh); - } - else { - mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); - - this.mdl.add(mesh); - this.objects.push(mesh); - } - } - - p0 = null; - p1 = null; -}; - -// significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createStrand = function (atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { - var bRibbon = fill ? true: false; - - // when highlight, the input atoms may only include part of sheet or helix - // include the whole sheet or helix when highlighting - var atomsAdjust = {}; - - //if( (bHighlight === 1 || bHighlight === 2) && !this.bAllAtoms) { - if( !this.bAllAtoms) { - var currChain, currResi, currAtom, prevChain, prevResi, prevAtom; - var firstAtom, lastAtom; - var index = 0, length = Object.keys(atoms).length; - - atomsAdjust = this.cloneHash(atoms); - for(var serial in atoms) { - currChain = atoms[serial].structure + '_' + atoms[serial].chain; - currResi = parseInt(atoms[serial].resi); - currAtom = atoms[serial]; - - if(prevChain === undefined) firstAtom = atoms[serial]; - - if( (currChain !== prevChain && prevChain !== undefined) || (currResi !== prevResi && currResi !== prevResi + 1 && prevResi !== undefined) || index === length - 1) { - if( (currChain !== prevChain && prevChain !== undefined) || (currResi !== prevResi && currResi !== prevResi + 1 && prevResi !== undefined) ) { -// if( (currChain !== prevChain && prevChain !== undefined) || index === length - 1) { -// if( (currChain !== prevChain && prevChain !== undefined) ) { - lastAtom = prevAtom; - } - else if(index === length - 1) { - lastAtom = currAtom; - } - - // fill the beginning - var beginResi = firstAtom.resi; - if(firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) { - for(var i = firstAtom.resi - 1; i > 0; --i) { - var residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i; - if(!this.residues.hasOwnProperty(residueid)) break; - - var atom = this.getFirstAtomObj(this.residues[residueid]); - - if(atom.ss === firstAtom.ss && atom.ssbegin) { - beginResi = atom.resi; - break; - } - } - - for(var i = beginResi; i < firstAtom.resi; ++i) { - var residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i; - atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); - } - } - - // add one extra residue for coils between strands/helix - if(this.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') { - var residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (firstAtom.resi - 1); - if(this.residues.hasOwnProperty(residueid)) { - atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); - atoms = this.unionHash(atoms, this.hash2Atoms(this.residues[residueid])); - } - } - - // fill the end - var endResi = lastAtom.resi; - // when a coil connects to a sheet and the last residue of coil is highlighted, the first sheet residue is set as atom.notshow. This residue should not be shown. - if(lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) { - var endChainResi = this.getLastAtomObj(this.chains[lastAtom.structure + '_' + lastAtom.chain]).resi; - for(var i = lastAtom.resi + 1; i <= endChainResi; ++i) { - var residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i; - if(!this.residues.hasOwnProperty(residueid)) break; - - var atom = this.getFirstAtomObj(this.residues[residueid]); - - if(atom.ss === lastAtom.ss && atom.ssend) { - endResi = atom.resi; - break; - } - } - - for(var i = lastAtom.resi + 1; i <= endResi; ++i) { - var residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i; - atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); - } - } - - // add one extra residue for coils between strands/helix - if(this.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') { - var residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (lastAtom.resi + 1); - if(this.residues.hasOwnProperty(residueid)) { - atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); - atoms = this.unionHash(atoms, this.hash2Atoms(this.residues[residueid])); - } - } - - // reset notshow - if(lastAtom.notshow) lastAtom.notshow = undefined; - - firstAtom = currAtom; - } - - prevChain = currChain; - prevResi = currResi; - prevAtom = currAtom; - - ++index; - } - } - else { - atomsAdjust = atoms; - } - - if(bHighlight === 2) { - if(fill) { - fill = false; - num = null; - div = null; - coilWidth = null; - helixSheetWidth = null; - thickness = undefined; - } - else { - fill = true; - num = 2; - div = undefined; - coilWidth = undefined; - helixSheetWidth = undefined; - thickness = this.ribbonthickness; - } - } - - num = num || this.strandDIV; - div = div || this.axisDIV; - coilWidth = coilWidth || this.coilWidth; - doNotSmoothen = doNotSmoothen || false; - helixSheetWidth = helixSheetWidth || this.helixSheetWidth; - var pnts = {}; for (var k = 0; k < num; ++k) pnts[k] = []; - var pntsCA = []; - var prevCOArray = []; - var bShowArray = []; - var calphaIdArray = []; // used to store one of the final positions drawn in 3D - var colors = []; - var currentChain, currentResi, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null; - var prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevResi = null, calphaid = null, prevCalphaid = null; - var strandWidth, bSheetSegment = false, bHelixSegment = false; - var atom, tubeAtoms = {}; - - // test the first 30 atoms to see whether only C-alpha is available - this.bCalphaOnly = this.isCalphaPhosOnly(atomsAdjust, 'CA'); - - // when highlight, draw whole beta sheet and use bShowArray to show the highlight part - var residueHash = {}; - for(var i in atomsAdjust) { - var atom = atomsAdjust[i]; - - residueid = atom.structure + '_' + atom.chain + '_' + atom.resi; - residueHash[residueid] = 1; - } - var totalResidueCount = Object.keys(residueHash).length; - - var drawnResidueCount = 0; - var highlightResiduesCount = 0; - - var bFullAtom = (Object.keys(this.hAtoms).length == Object.keys(this.atoms).length) ? true : false; - - for (var i in atomsAdjust) { - atom = atomsAdjust[i]; - var atomOxygen = undefined; - if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) { - // "CA" has to appear before "O" - - if (atom.name === 'CA') { - if ( atoms.hasOwnProperty(i) && (atom.ss === 'coil' || atom.ssend || atom.ssbegin) ) { - tubeAtoms[i] = atom; - } - - currentCA = atom.coord; - currentColor = atom.color; - calphaid = atom.serial; - } - - if (atom.name === 'O' || (this.bCalphaOnly && atom.name === 'CA')) { - if(atom.name === 'O') { - currentO = atom.coord; - } - // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment - var bSameChain = true; - if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) { -// if (currentChain !== atom.chain) { - bSameChain = false; - } - - if(atom.ssend && atom.ss === 'sheet') { - bSheetSegment = true; - } - else if(atom.ssend && atom.ss === 'helix') { - bHelixSegment = true; - } - - // assign the previous residue - if(prevCoorO) { - if(bHighlight === 1 || bHighlight === 2) { - colors.push(this.hColor); - } - else { - colors.push(prevColor); - } - - if(ss !== 'coil' && atom.ss === 'coil') { - strandWidth = coilWidth; - } - else if(ssend && atom.ssbegin) { // a transition between two ss - strandWidth = coilWidth; - } - else { - strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth; - } - - var O; - if(atom.name === 'O') { - O = prevCoorO.clone(); - if(prevCoorCA !== null && prevCoorCA !== undefined) { - O.sub(prevCoorCA); - } - else { - prevCoorCA = prevCoorO.clone(); - O = new THREE.Vector3(Math.random(),Math.random(),Math.random()); - } - } - else if(this.bCalphaOnly && atom.name === 'CA') { - O = new THREE.Vector3(Math.random(),Math.random(),Math.random()); - } - - O.normalize(); // can be omitted for performance - O.multiplyScalar(strandWidth); - if (prevCO !== null && O.dot(prevCO) < 0) O.negate(); - prevCO = O; - - for (var j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) { - var delta = -1 + numM1Inv2 * j; - var v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta); - if (!doNotSmoothen && ss === 'sheet') v.smoothen = true; - pnts[j].push(v); - } - - pntsCA.push(prevCoorCA); - prevCOArray.push(prevCO); - - if(atoms.hasOwnProperty(prevAtomid)) { - bShowArray.push(prevResi); - calphaIdArray.push(prevCalphaid); - - ++highlightResiduesCount; - } - else { - bShowArray.push(0); - calphaIdArray.push(0); - } - - ++drawnResidueCount; - } - - if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) ) && pnts[0].length > 0 && bSameChain) { - // assign the current joint residue to the previous segment - if(bHighlight === 1 || bHighlight === 2) { - colors.push(this.hColor); - } - else { - //colors.push(atom.color); - colors.push(prevColor); - } - - if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow - strandWidth = 0; // make the arrow end sharp - } - else if(ss === 'coil' && atom.ssbegin) { - strandWidth = coilWidth; - } - else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil - strandWidth = coilWidth; - } - else { // use the ss from the previous residue - strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth; - } - - var O; - if(atom.name === 'O') { - O = currentO.clone(); - O.sub(currentCA); - } - else if(this.bCalphaOnly && atom.name === 'CA') { - O = new THREE.Vector3(Math.random(),Math.random(),Math.random()); - } - - O.normalize(); // can be omitted for performance - O.multiplyScalar(strandWidth); - if (prevCO !== null && O.dot(prevCO) < 0) O.negate(); - prevCO = O; - - for (var j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) { - var delta = -1 + numM1Inv2 * j; - var v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta); - if (!doNotSmoothen && ss === 'sheet') v.smoothen = true; - pnts[j].push(v); - } - - atomid = atom.serial; - - pntsCA.push(currentCA); - prevCOArray.push(prevCO); - - // when a coil connects to a sheet and the last residue of coild is highlighted, the first sheet residue is set as atom.highlightStyle. This residue should not be shown. - //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) { - if(atoms.hasOwnProperty(atomid)) { - bShowArray.push(atom.resi); - calphaIdArray.push(calphaid); - } - else { - bShowArray.push(0); - calphaIdArray.push(0); - } - - // draw the current segment - for (var j = 0; !fill && j < num; ++j) { - if(bSheetSegment) { - this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray); - } - else { - if(bFullAtom) { - this.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray); - } - else { - this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false); - } - } - } - if (fill) { - if(bSheetSegment) { - var start = 0, end = num - 1; - this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray); - } - else if(bHelixSegment) { - if(bFullAtom) { - this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray); - } - else { - var start = 0, end = num - 1; - this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray); - } - } - else { - if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately - this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray); - } - } - } - for (var k = 0; k < num; ++k) pnts[k] = []; - - colors = []; - pntsCA = []; - prevCOArray = []; - bShowArray = []; - calphaIdArray = []; - bSheetSegment = false; - bHelixSegment = false; - } // end if (atom.ssbegin || atom.ssend) - - // end of a chain - if ((currentChain !== atom.chain || currentResi + 1 !== atom.resi) && pnts[0].length > 0) { -// if ((currentChain !== atom.chain) && pnts[0].length > 0) { - for (var j = 0; !fill && j < num; ++j) { - if(bSheetSegment) { - this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray); - } - else if(bHelixSegment) { - if(bFullAtom) { - this.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray); - } - else { - this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false); - } - } - } - if (fill) { - if(bSheetSegment) { - var start = 0, end = num - 1; - this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray); - } - else if(bHelixSegment) { - if(bFullAtom) { - this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray); - } - else { - var start = 0, end = num - 1; - this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false); - } - } - } - - for (var k = 0; k < num; ++k) pnts[k] = []; - colors = []; - pntsCA = []; - prevCOArray = []; - bShowArray = []; - calphaIdArray = []; - bSheetSegment = false; - bHelixSegment = false; - } - - currentChain = atom.chain; - currentResi = atom.resi; - ss = atom.ss; - ssend = atom.ssend; - prevAtomid = atom.serial; - prevResi = atom.resi; - - prevCalphaid = calphaid; - - // only update when atom.name === 'O' - prevCoorCA = currentCA; - prevCoorO = atom.coord; - prevColor = currentColor; - } // end if (atom.name === 'O' || (this.bCalphaOnly && atom.name === 'CA') ) { - } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) { - } // end for - - this.createTube(tubeAtoms, 'CA', coilWidth, bHighlight); - - tubeAtoms = {}; - pnts = {}; -}; - -/* -iCn3D.prototype.createStrandBrick = function (brick, color, thickness, bHighlight) { - var num = this.strandDIV; - var div = this.axisDIV; - var doNotSmoothen = false; - var helixSheetWidth = this.helixSheetWidth; - - if(bHighlight === 2) { - thickness *= 1.5; - helixSheetWidth *= 1.5; - } - - var pnts = {}; for (var k = 0; k < num; ++k) pnts[k] = []; - var colors = []; - var prevCO = null, ss = null; - for (var i = 0; i < 2; ++i) { - var currentCA = brick.coords[i]; - - colors.push(new THREE.Color(color)); - - var O = new THREE.Vector3(brick.coords[2].x, brick.coords[2].y, brick.coords[2].z); - O.normalize(); - - O.multiplyScalar(helixSheetWidth); - if (prevCO !== null && O.dot(prevCO) < 0) O.negate(); - prevCO = O; - for (var j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) { - var delta = -1 + numM1Inv2 * j; - var v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta); - if (!doNotSmoothen) v.smoothen = true; - pnts[j].push(v); - } - } - this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight); -}; -*/ - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createTubeSub = function (_pnts, colors, radii, bHighlight) { - if (_pnts.length < 2) return; - var circleDiv = this.tubeDIV, axisDiv = this.axisDIV; - var circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv; - var geo = new THREE.Geometry(); - var pnts_clrs = this.subdivide(_pnts, colors, axisDiv); - var pnts = pnts_clrs[0]; - colors = pnts_clrs[2]; - - var prevAxis1 = new THREE.Vector3(), prevAxis2; - for (var i = 0, lim = pnts.length; i < lim; ++i) { - var r, idx = (i - 1) * axisDivInv; - if (i === 0) r = radii[0]; - else { - if (idx % 1 === 0) r = radii[idx]; - else { - var floored = Math.floor(idx); - var tmp = idx - floored; - r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp); - } - } - var delta, axis1, axis2; - if (i < lim - 1) { - delta = pnts[i].clone().sub(pnts[i + 1]); - axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r); - axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r); - // var dir = 1, offset = 0; - if (prevAxis1.dot(axis1) < 0) { - axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv; - } - prevAxis1 = axis1; prevAxis2 = axis2; - } else { - axis1 = prevAxis1; axis2 = prevAxis2; - } - for (var j = 0; j < circleDiv; ++j) { - var angle = 2 * Math.PI * circleDivInv * j; //* dir + offset; - geo.vertices.push(pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)))); - } - } - var offset = 0; - for (var i = 0, lim = pnts.length - 1; i < lim; ++i) { - //var c = new THREE.Color(colors[Math.round((i - 1) * axisDivInv)]); - var c = new THREE.Color(colors[i]); - - var reg = 0; - var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq(); - var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq(); - if (r1 > r2) { r1 = r2; reg = 1; }; - for (var j = 0; j < circleDiv; ++j) { - geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c)); - geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c)); - } - offset += circleDiv; - } - geo.computeFaceNormals(); - geo.computeVertexNormals(false); - - var mesh; - if(bHighlight === 2) { - mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); - this.mdl.add(mesh); - } - else if(bHighlight === 1) { - mesh = new THREE.Mesh(geo, this.matShader); - mesh.renderOrder = this.renderOrderPicking; - //this.mdlPicking.add(mesh); - this.mdl.add(mesh); - } - else { - mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); - this.mdl.add(mesh); - } - - if(bHighlight === 1 || bHighlight === 2) { - this.prevHighlightObjects.push(mesh); - } - else { - this.objects.push(mesh); - } -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createTube = function (atoms, atomName, radius, bHighlight) { - var pnts = [], colors = [], radii = []; - var currentChain, currentResi; - var index = 0; - var prevAtom; - - for (var i in atoms) { - var atom = atoms[i]; - if ((atom.name === atomName) && !atom.het) { - if (index > 0 && (currentChain !== atom.chain || currentResi + 1 !== atom.resi || Math.abs(atom.coord.x - prevAtom.coord.x) > 6.0 || Math.abs(atom.coord.y - prevAtom.coord.y) > 6.0 || Math.abs(atom.coord.z - prevAtom.coord.z) > 6.0) ) { -// if (index > 0 && (currentChain !== atom.chain || Math.abs(atom.coord.x - prevAtom.coord.x) > 6.0 || Math.abs(atom.coord.y - prevAtom.coord.y) > 6.0 || Math.abs(atom.coord.z - prevAtom.coord.z) > 6.0) ) { - if(bHighlight !== 2) this.createTubeSub(pnts, colors, radii, bHighlight); - pnts = []; colors = []; radii = []; - } - pnts.push(atom.coord); - - radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : 0.3)); - colors.push(atom.color); - - currentChain = atom.chain; - currentResi = atom.resi; - - var scale = 1.2; - if(bHighlight === 2 && !atom.ssbegin) { - this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); - } - - ++index; - - prevAtom = atom; - } - } - if(bHighlight !== 2) this.createTubeSub(pnts, colors, radii, bHighlight); -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createCylinderHelix = function (atoms, radius, bHighlight) { - var start = null; - var currentChain, currentResi; - var others = {}, beta = {}; - var i; - for (i in atoms) { - var atom = atoms[i]; - if (atom.het) continue; - if ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) others[atom.serial] = atom; - if (atom.ss === 'sheet') beta[atom.serial] = atom; - if (atom.name !== 'CA') continue; - if (atom.ss === 'helix' && atom.ssend) { - if (start !== null && currentChain === atom.chain && currentResi < atom.resi) { - if(bHighlight === 1 || bHighlight === 2) { - this.createCylinder(start.coord, atom.coord, radius, this.hColor, bHighlight); - } - else { - this.createCylinder(start.coord, atom.coord, radius, atom.color); - } - } - - start = null; - } - - if (start === null && atom.ss === 'helix' && atom.ssbegin) { - start = atom; - - currentChain = atom.chain; - currentResi = atom.resi; - } - } - - if(bHighlight === 1 || bHighlight === 2) { - if(Object.keys(others).length > 0) this.createTube(others, 'CA', 0.3, bHighlight); - if(Object.keys(beta).length > 0) this.createStrand(beta, undefined, undefined, true, 0, this.helixSheetWidth, false, this.ribbonthickness * 2, bHighlight); - } - else { - if(Object.keys(others).length > 0) this.createTube(others, 'CA', 0.3); - if(Object.keys(beta).length > 0) this.createStrand(beta, undefined, undefined, true, 0, this.helixSheetWidth, false, this.ribbonthickness * 2); - } -}; - -// modified from GLmol (http://webglmol.osdn.jp/index-en.html) -iCn3D.prototype.drawNucleicAcidStick = function(atomlist, bHighlight) { - var currentChain, currentResi, start = null, end = null; - var i; - - for (i in atomlist) { - var atom = atomlist[i]; - if (atom === undefined || atom.het) continue; - - if (atom.resi !== currentResi || atom.chain !== currentChain) { - if (start !== null && end !== null) { - this.createCylinder(new THREE.Vector3(start.coord.x, start.coord.y, start.coord.z), - new THREE.Vector3(end.coord.x, end.coord.y, end.coord.z), this.cylinderRadius, start.color, bHighlight); - } - start = null; end = null; - } - if (atom.name === 'O3\'' || atom.name === 'O3*') start = atom; - if (atom.resn === ' A' || atom.resn === ' G' || atom.resn === ' DA' || atom.resn === ' DG') { - if (atom.name === 'N1') end = atom; // N1(AG), N3(CTU) - } else if (atom.name === 'N3') { - end = atom; - } - currentResi = atom.resi; currentChain = atom.chain; - } - if (start !== null && end !== null) - this.createCylinder(new THREE.Vector3(start.coord.x, start.coord.y, start.coord.z), - new THREE.Vector3(end.coord.x, end.coord.y, end.coord.z), this.cylinderRadius, start.color, bHighlight); -}; - -iCn3D.prototype.isCalphaPhosOnly = function(atomlist, atomname1, atomname2) { - var bCalphaPhosOnly = false; - - var index = 0, testLength = 30; - var bOtherAtoms = false; - for(var i in atomlist) { - if(index < testLength) { - if(atomlist[i].name !== atomname1 && - (atomname2 == undefined || (atomname2 != undefined && atomlist.name !== atomname2) ) - ) { - bOtherAtoms = true; - break; - } - } - else { - break; - } - - ++index; - } - - if(!bOtherAtoms) { - bCalphaPhosOnly = true; - } - - return bCalphaPhosOnly; -}; - -// modified from GLmol (http://webglmol.osdn.jp/index-en.html) -iCn3D.prototype.drawCartoonNucleicAcid = function(atomlist, div, thickness, bHighlight) { - this.drawStrandNucleicAcid(atomlist, 2, div, true, undefined, thickness, bHighlight); -}; - -// modified from GLmol (http://webglmol.osdn.jp/index-en.html) -iCn3D.prototype.drawStrandNucleicAcid = function(atomlist, num, div, fill, nucleicAcidWidth, thickness, bHighlight) { - if(bHighlight === 2) { - num = undefined; - thickness = undefined; - } - - nucleicAcidWidth = nucleicAcidWidth || this.nucleicAcidWidth; - div = div || this.axisDIV; - num = num || this.nucleicAcidStrandDIV; - var i, j, k; - var pnts = []; for (k = 0; k < num; k++) pnts[k] = []; - var colors = []; - var currentChain, currentResi, currentO3; - var prevOO = null; - - for (i in atomlist) { - var atom = atomlist[i]; - if (atom === undefined) continue; - - if ((atom.name === 'O3\'' || atom.name === 'OP2' || atom.name === 'O3*' || atom.name === 'O2P') && !atom.het) { - if (atom.name === 'O3\'' || atom.name === 'O3*') { // to connect 3' end. FIXME: better way to do? - if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) { -// if (currentChain !== atom.chain) { - if (currentO3 && prevOO) { - for (j = 0; j < num; j++) { - var delta = -1 + 2 / (num - 1) * j; - pnts[j].push(new THREE.Vector3(currentO3.x + prevOO.x * delta, - currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); - } - } - if (fill) this.createStrip(pnts[0], pnts[1], colors, div, thickness, bHighlight); - for (j = 0; !thickness && j < num; j++) - this.createCurveSub(pnts[j], 1 ,colors, div, bHighlight); - var pnts = []; for (k = 0; k < num; k++) pnts[k] = []; - colors = []; - prevOO = null; - } - currentO3 = new THREE.Vector3(atom.coord.x, atom.coord.y, atom.coord.z); - currentChain = atom.chain; - currentResi = atom.resi; - if(bHighlight === 1 || bHighlight === 2) { - colors.push(this.hColor); - } - else { - colors.push(atom.color); - } - - } - else if (atom.name === 'OP2' || atom.name === 'O2P') { - if (!currentO3) {prevOO = null; continue;} // for 5' phosphate (e.g. 3QX3) - var O = new THREE.Vector3(atom.coord.x, atom.coord.y, atom.coord.z); - O.sub(currentO3); - O.normalize().multiplyScalar(nucleicAcidWidth); // TODO: refactor - //if (prevOO !== undefined && O.dot(prevOO) < 0) { - if (prevOO !== null && O.dot(prevOO) < 0) { - O.negate(); - } - prevOO = O; - for (j = 0; j < num; j++) { - var delta = -1 + 2 / (num - 1) * j; - pnts[j].push(new THREE.Vector3(currentO3.x + prevOO.x * delta, - currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); - } - currentO3 = null; - } - } - } - - if (currentO3 && prevOO) { - for (j = 0; j < num; j++) { - var delta = -1 + 2 / (num - 1) * j; - pnts[j].push(new THREE.Vector3(currentO3.x + prevOO.x * delta, - currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); - } - } - if (fill) this.createStrip(pnts[0], pnts[1], colors, div, thickness, bHighlight); - for (j = 0; !thickness && j < num; j++) - this.createCurveSub(pnts[j], 1 ,colors, div, bHighlight); -}; - -iCn3D.prototype.createSingleLine = function ( src, dst, colorHex, dashed, dashSize ) { - var geom = new THREE.Geometry(); - var mat; - - if(dashed) { - mat = new THREE.LineDashedMaterial({ linewidth: 1, color: colorHex, dashSize: dashSize, gapSize: 0.5*dashSize }); - } else { - mat = new THREE.LineBasicMaterial({ linewidth: 1, color: colorHex }); - } - - geom.vertices.push( src ); - geom.vertices.push( dst ); - if(dashed) geom.computeLineDistances(); // This one is SUPER important, otherwise dashed lines will appear as simple plain lines - - var axis = new THREE.Line( geom, mat, THREE.LinePieces ); - - return axis; -}; - -// used for highlight -iCn3D.prototype.createBox = function (atom, defaultRadius, forceDefault, scale, color, bHighlight) { - var mesh; - - if(defaultRadius === undefined) defaultRadius = 0.8; - if(forceDefault === undefined) forceDefault = false; - if(scale === undefined) scale = 0.8; - - if(bHighlight) { - if(color === undefined) color = this.hColor; - - mesh = new THREE.Mesh(this.boxGeometry, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); - } - else { - if(color === undefined) color = atom.color; - - mesh = new THREE.Mesh(this.boxGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); - } - - mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : (this.vdwRadii[atom.elem] || defaultRadius) * (scale ? scale : 1); - mesh.position.copy(atom.coord); - this.mdl.add(mesh); - - if(bHighlight) { - this.prevHighlightObjects.push(mesh); - } - else { - this.objects.push(mesh); - } -}; - -// modified from 3Dmol (http://3dmol.csb.pitt.edu/) -// new: http://stackoverflow.com/questions/23514274/three-js-2d-text-sprite-labels -// old: http://stemkoski.github.io/Three.js/Sprite-Text-Labels.html -iCn3D.prototype.makeTextSprite = function ( message, parameters ) { - - if ( parameters === undefined ) parameters = {}; - var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "Arial"; - var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 18; - - var a = parameters.hasOwnProperty("alpha") ? parameters["alpha"] : 1.0; - - var bBkgd = true; - var bSchematic = false; - if(parameters.hasOwnProperty("bSchematic") && parameters["bSchematic"]) { - bSchematic = true; - - fontsize = 40; - } - - var backgroundColor, borderColor, borderThickness; - if(parameters.hasOwnProperty("backgroundColor") && parameters["backgroundColor"] !== undefined) { - backgroundColor = this.hexToRgb(parameters["backgroundColor"], a); - - borderColor = parameters.hasOwnProperty("borderColor") ? this.hexToRgb(parameters["borderColor"], a) : { r:0, g:0, b:0, a:1.0 }; - borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 4; - } - else { - bBkgd = false; - backgroundColor = undefined; - borderColor = undefined; - borderThickness = 0; - } - - var textAlpha = 1.0; - var textColor = parameters.hasOwnProperty("textColor") && parameters["textColor"] !== undefined ? this.hexToRgb(parameters["textColor"], textAlpha) : { r:255, g:255, b:0, a:1.0 }; - - var canvas = document.createElement('canvas'); - - var context = canvas.getContext('2d'); - - context.font = "Bold " + fontsize + "px " + fontface; - - var metrics = context.measureText( message ); - - var textWidth = metrics.width; - - var width = textWidth + 2*borderThickness; - var height = fontsize + 2*borderThickness; - - if(bSchematic) { - if(width > height) { - height = width; - } - else { - width = height; - } - } - - //var factor = (bSchematic) ? 3 * this.oriMaxD / 100 : 3 * this.oriMaxD / 100; - var factor = (bSchematic) ? 3 * this.maxD / 100 : 3 * this.maxD / 100; - - var expandWidthFactor = 0.8 * textWidth / height; - -/* - // define width and height will make long text be squashed, but make the label to appear at the exact location - if(bSchematic || message.length <= textLengthThreshold) { - canvas.width = width; - canvas.height = height; - - factor = 3 * this.oriMaxD / 100; - } -*/ - - canvas.width = width; - canvas.height = height; - - context.clearRect(0, 0, width, height); - - //var radius = context.measureText( "M" ).width; - - if(bBkgd) { - // background color - context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," + backgroundColor.a + ")"; - // border color - context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")"; - - context.lineWidth = borderThickness; - - if(bSchematic) { - var r = width * 0.35; - this.circle(context, 0, 0, width, height, r); - } - else { - //var r = (message.length <= textLengthThreshold) ? height * 0.5 : 0; - //var r = height * 0.8; - var r = 0; - this.roundRect(context, 0, 0, width, height, r); - } - } - - // need to redefine again - context.font = "Bold " + fontsize + "px " + fontface; - - context.textAlign = "center"; - context.textBaseline = "middle"; - - context.fillStyle = "rgba("+textColor.r+", "+textColor.g+", "+textColor.b+", 1.0)"; - context.strokeStyle = "rgba("+textColor.r+", "+textColor.g+", "+textColor.b+", 1.0)"; - - context.fillText( message, width * 0.5, height * 0.5); - - // canvas contents will be used for a texture - var texture = new THREE.Texture(canvas) - texture.needsUpdate = true; - - var frontOfTarget = true; - //var spriteMaterial = new THREE.SpriteMaterial( { map: texture, useScreenCoordinates: false } ); - var spriteMaterial = new THREE.SpriteMaterial( { - map: texture, - //useScreenCoordinates: false, - depthTest: !frontOfTarget, - depthWrite: !frontOfTarget - } ); - - //https://stackoverflow.com/questions/29421702/threejs-texture - spriteMaterial.map.minFilter = THREE.LinearFilter; - - var sprite = new THREE.Sprite( spriteMaterial ); - - if(bSchematic) { - sprite.scale.set(factor, factor, 1.0); - } - else { - sprite.scale.set(expandWidthFactor * factor, factor, 1.0); - } - - return sprite; -}; - -// function for drawing rounded rectangles -iCn3D.prototype.roundRect = function (ctx, x, y, w, h, r) { - ctx.beginPath(); - ctx.moveTo(x+r, y); - ctx.lineTo(x+w-r, y); - ctx.quadraticCurveTo(x+w, y, x+w, y+r); - ctx.lineTo(x+w, y+h-r); - ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h); - ctx.lineTo(x+r, y+h); - ctx.quadraticCurveTo(x, y+h, x, y+h-r); - ctx.lineTo(x, y+r); - ctx.quadraticCurveTo(x, y, x+r, y); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); -}; - -iCn3D.prototype.circle = function (ctx, x, y, w, h, r) { - ctx.beginPath(); - ctx.arc(x+w/2, y+h/2, r, 0, 2*Math.PI, true); - ctx.closePath(); - ctx.fill(); - ctx.stroke(); -}; - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createLabelRepresentation = function (labels) { - var tmpMaxD = this.maxD; - - for(var name in labels) { - var labelArray = (labels[name] !== undefined) ? labels[name] : []; - - for (var i = 0, il = labelArray.length; i < il; ++i) { - var label = labelArray[i]; - // make sure fontsize is a number - - if(label.size == 0) label.size = undefined; - if(label.color == 0) label.color = undefined; - if(label.background == 0) label.background = undefined; - - var labelsize = (label.size !== undefined) ? label.size : this.LABELSIZE; - var labelcolor = (label.color !== undefined) ? label.color : '#ffff00'; - var labelbackground = (label.background !== undefined) ? label.background : '#cccccc'; - var labelalpha = (label.alpha !== undefined) ? label.alpha : 1.0; - // if label.background is undefined, no background will be drawn - labelbackground = label.background; - - if(labelcolor !== undefined && labelbackground !== undefined && labelcolor.toLowerCase() === labelbackground.toLowerCase()) { - labelcolor = "#888888"; - } - - var bChemicalInProteinOrTrace = false; - var bStick = false; - if(Object.keys(this.proteins).length + Object.keys(this.nucleotides).length > 0) { - var firstAtom = this.getFirstAtomObj(this.hAtoms); - if(firstAtom !== undefined) { - if(this.chemicals.hasOwnProperty(firstAtom.serial) - || firstAtom.style == 'c alpha trace' || firstAtom.style == 'o3 trace' - //|| firstAtom.style == 'schematic' - ) { - bChemicalInProteinOrTrace = true; - } - - if(firstAtom.style == 'stick' || firstAtom.style == 'ball and stick') { - bStick = true; - } - } - } - - // somehow the transformation is not stable when reset camera -/* - if(bChemicalInProteinOrTrace) { - this.maxD = 15; // 50 - this.setCamera(); - } - else if(bStick) { - this.maxD = 30; // 50 - this.setCamera(); - } - else { - if(Object.keys(this.proteins).length + Object.keys(this.nucleotides).length > 0) { - this.maxD = 100; - this.setCamera(); - } - else { - this.maxD = 15; - this.setCamera(); - } - } -*/ - - var bb; - if(label.bSchematic !== undefined && label.bSchematic) { - - bb = this.makeTextSprite(label.text, {fontsize: parseInt(labelsize), textColor: labelcolor, borderColor: labelbackground, backgroundColor: labelbackground, alpha: labelalpha, bSchematic: 1}); - } - else { - if(label.text.length === 1) { - bb = this.makeTextSprite(label.text, {fontsize: parseInt(labelsize), textColor: labelcolor, borderColor: labelbackground, backgroundColor: labelbackground, alpha: labelalpha, bSchematic: 1}); - } - else { - bb = this.makeTextSprite(label.text, {fontsize: parseInt(labelsize), textColor: labelcolor, borderColor: labelbackground, backgroundColor: labelbackground, alpha: labelalpha, bSchematic: 0}); - } - } - - bb.position.set(label.position.x, label.position.y, label.position.z); - this.mdl.add(bb); - // do not add labels to objects for pk - } - } - - // somehow the transformation is not stable when reset camera -// this.maxD = tmpMaxD; -// this.setCamera(); -}; - diff --git a/src/icn3d/drawing_full.js b/src/icn3d/drawing_full.js deleted file mode 100644 index 77ade66b..00000000 --- a/src/icn3d/drawing_full.js +++ /dev/null @@ -1,243 +0,0 @@ -/** - * @author Jiyao Wang / https://github.com/ncbi/icn3d - */ - -// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) -iCn3D.prototype.createSurfaceRepresentation = function (atoms, type, wireframe, opacity) { - var geo; - - var extent = this.getExtent(atoms); - - // surface from 3Dmol - var distance = 5; // consider atom 5 angstrom from the selected atoms - - var extendedAtoms = []; - - if(this.bConsiderNeighbors) { - extendedAtoms = Object.keys(this.unionHash(atoms, this.getAtomsWithinAtom(this.atoms, atoms, distance))); - } - else { - extendedAtoms = Object.keys(atoms); - } - - var ps = $3Dmol.SetupSurface({ - extent: extent, - allatoms: this.atoms, - atomsToShow: Object.keys(atoms), - extendedAtoms: extendedAtoms, - type: type - }); - - var verts = ps.vertices; - var faces = ps.faces; - - var me = this; - - geo = new THREE.Geometry(); - geo.vertices = verts.map(function (v) { - var r = new THREE.Vector3(v.x, v.y, v.z); - r.atomid = v.atomid; - return r; - }); - geo.faces = faces.map(function (f) { - //return new THREE.Face3(f.a, f.b, f.c); - var vertexColors = ['a', 'b', 'c' ].map(function (d) { - var atomid = geo.vertices[f[d]].atomid; - return me.atoms[atomid].color; - }); - - return new THREE.Face3(f.a, f.b, f.c, undefined, vertexColors); - }); - - // remove the reference - ps = null; - verts = null; - faces = null; - - geo.computeFaceNormals(); - geo.computeVertexNormals(false); - - geo.colorsNeedUpdate = true; - -/* - geo.faces.forEach(function (f) { - f.vertexColors = ['a', 'b', 'c' ].map(function (d) { - var atomid = geo.vertices[f[d]].atomid; - return me.atoms[atomid].color; - }); - }); -*/ - - geo.type = 'Surface'; // to be recognized in vrml.js for 3D printing - - var mesh = new THREE.Mesh(geo, new THREE.MeshLambertMaterial({ overdraw: me.overdraw, - vertexColors: THREE.VertexColors, - wireframe: wireframe, - opacity: opacity, - transparent: true, - })); - me.mdl.add(mesh); - - this.prevSurfaces.push(mesh); - - // remove the reference - geo = null; - - // do not add surface to raycasting objects for pk -}; - -iCn3D.prototype.drawSymmetryMates2 = function() { - if (this.biomtMatrices === undefined) return; - var cnt = 1; // itself - var centerSum = this.center.clone(); - - for (var i = 0; i < this.biomtMatrices.length; i++) { // skip itself - var mat = this.biomtMatrices[i]; - if (mat === undefined) continue; - - var matArray = mat.toArray(); - - // skip itself - var bItself = 1; - for(var j = 0, jl = matArray.length; j < jl; ++j) { - if(j == 0 || j == 5 || j == 10) { - if(parseInt(1000*matArray[j]) != 1000) bItself = 0; - } - else if(j != 0 && j != 5 && j != 10 && j != 15) { - if(parseInt(1000*matArray[j]) != 0) bItself = 0; - } - } - - if(bItself) continue; - - var symmetryMate = this.mdl.clone(); - symmetryMate.applyMatrix(mat); - - this.mdl.add(symmetryMate); - - symmetryMate = this.mdlImpostor.clone(); - symmetryMate.applyMatrix(mat); - - this.mdlImpostor.add(symmetryMate); - - //symmetryMate = this.mdlPicking.clone(); - //symmetryMate.applyMatrix(mat); - - //this.mdlPicking.add(symmetryMate); - - symmetryMate = this.mdl_ghost.clone(); - symmetryMate.applyMatrix(mat); - - this.mdl_ghost.add(symmetryMate); - - var center = this.center.clone(); - center.applyMatrix4(mat); - centerSum.add(center); - - ++cnt; - } - - this.maxD *= Math.sqrt(cnt); - //this.center = centerSum.multiplyScalar(1.0 / cnt); - - // recenter the mdl* caused the impostor shifted somehow!!! disable the centering for now -// this.mdl.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); -// this.mdlImpostor.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); - //this.mdlPicking.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); - -// this.mdl_ghost.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); - - // reset cameara - this.setCamera(); -}; - - -// http://soledadpenades.com/articles/three-js-tutorials/drawing-the-coordinate-axes/ -iCn3D.prototype.buildAxes = function (radius) { - var axes = new THREE.Object3D(); - - axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0 + radius, 0, 0 ), 0xFF0000, false, 0.5 ) ); // +X - axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0 - radius, 0, 0 ), 0x800000, true, 0.5) ); // -X - - axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0 + radius, 0 ), 0x00FF00, false, 0.5 ) ); // +Y - axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0 - radius, 0 ), 0x008000, true, 0.5 ) ); // -Y - - axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 0 + radius ), 0x0000FF, false, 0.5 ) ); // +Z - axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 0 - radius ), 0x000080, true, 0.5 ) ); // -Z - - this.scene.add( axes ); -}; - -iCn3D.prototype.createLines = function(lines) { // show extra lines, not used for pk, so no this.objects - if(lines !== undefined) { - for(var name in lines) { - var lineArray = lines[name]; - - for(var i = 0, il = lineArray.length; i < il; ++i) { - var line = lineArray[i]; - - var p1 = line.position1; - var p2 = line.position2; - - var dashed = (line.dashed) ? line.dashed : false; - var dashSize = 0.3; - - //this.mdl.add(this.createSingleLine( p1, p2, colorHex, dashed, dashSize )); - - var radius = this.lineRadius; - - var colorStr = '#' + line.color.replace(/\#/g, ''); - - var color = new THREE.Color(colorStr); - - if(!dashed) { - if(name == 'stabilizer') { - this.createBrick(p1, p2, radius, color); - } - else { - this.createCylinder(p1, p2, radius, color); - } - } - else { - var distance = p1.distanceTo(p2); - - var nsteps = parseInt(distance / dashSize); - var step = p2.clone().sub(p1).multiplyScalar(dashSize/distance); - - var start, end; - for(var j = 0; j < nsteps; ++j) { - if(j % 2 == 1) { - start = p1.clone().add(step.clone().multiplyScalar(j)); - end = p1.clone().add(step.clone().multiplyScalar(j + 1)); - - if(name == 'stabilizer') { - this.createBrick(start, end, radius, color); - } - else { - this.createCylinder(start, end, radius, color); - } - } - } - } - } - } - } - - // do not add the artificial lines to raycasting objects -}; - -iCn3D.prototype.createBrick = function (p0, p1, radius, color) { - var cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, 4, 1); - - var mesh = new THREE.Mesh(cylinderGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); - - mesh.position.copy(p0).add(p1).multiplyScalar(0.5); - mesh.matrixAutoUpdate = false; - mesh.lookAt(p0); - mesh.updateMatrix(); - - mesh.matrix.multiply(new THREE.Matrix4().makeScale(radius, radius, p0.distanceTo(p1))).multiply(new THREE.Matrix4().makeRotationX(Math.PI * 0.5)); - - this.mdl.add(mesh); -}; - diff --git a/src/icn3d/icn3d.js b/src/icn3d/icn3d.js index 53702aa6..6c805494 100644 --- a/src/icn3d/icn3d.js +++ b/src/icn3d/icn3d.js @@ -66,6 +66,14 @@ var iCn3D = function (id) { console.log('EXT_frag_depth is supported. All spheres and cylinders are drawn using shaders.'); } + this.bInstanced = this.renderer.extensions.get( "ANGLE_instanced_arrays" ); + if(!this.bInstanced) { + console.log('ANGLE_instanced_arrays is NOT supported. Assembly is drawn by making copies of the asymmetric unit.'); + } + else { + console.log('ANGLE_instanced_arrays is supported. Assembly is drawn with one copy of the asymmetric unit using hardware instancing.'); + } + // cylinder impostor this.posArray = new Array(); this.colorArray = new Array(); @@ -1110,7 +1118,7 @@ iCn3D.prototype = { this.inputid = {"idtype": undefined, "id":undefined}; // support pdbid, mmdbid this.biomtMatrices = []; - this.bAssembly = false; + this.bAssembly = true; this.rotateCount = 0; this.rotateCountMax = 20; @@ -1129,6 +1137,6 @@ iCn3D.prototype = { this.lines = {}; // hash of name -> a list of solid or dashed lines. Each line contains 'position1', 'position2', 'color', and a boolean of 'dashed' // line name could be custom, hbond, ssbond, distance - this.bAssembly = false; + this.bAssembly = true; } }; diff --git a/src/icn3d/loadpdb.js b/src/icn3d/loadpdb.js index 89c6e144..59b993d1 100644 --- a/src/icn3d/loadpdb.js +++ b/src/icn3d/loadpdb.js @@ -132,7 +132,8 @@ iCn3D.prototype.loadPDB = function (src) { // from GLMol if (type == 350 && line.substr(13, 5) == 'BIOMT') { var n = parseInt(line[18]) - 1; - var m = parseInt(line.substr(21, 2)); + //var m = parseInt(line.substr(21, 2)); + var m = parseInt(line.substr(21, 2)) - 1; // start from 1 if (this.biomtMatrices[m] == undefined) this.biomtMatrices[m] = new THREE.Matrix4().identity(); this.biomtMatrices[m].elements[n] = parseFloat(line.substr(24, 9)); this.biomtMatrices[m].elements[n + 4] = parseFloat(line.substr(34, 9)); diff --git a/src/icn3d/other_full.js b/src/icn3d/other_full.js index 29c468ed..34901fe2 100644 --- a/src/icn3d/other_full.js +++ b/src/icn3d/other_full.js @@ -248,19 +248,21 @@ iCn3D.prototype.centerSelection = function(atoms) { if(Object.keys(atoms).length > 1) { var centerAtomsResults = this.centerAtoms(atoms); - this.mdl.position.set(0,0,0); - this.mdlImpostor.position.set(0,0,0); - this.mdl_ghost.position.set(0,0,0); - - this.mdl.position.sub(centerAtomsResults.center); - //this.mdlPicking.position.sub(centerAtomsResults.center); - this.mdlImpostor.position.sub(centerAtomsResults.center); - this.mdl_ghost.position.sub(centerAtomsResults.center); - this.center = centerAtomsResults.center; + this.setCenter(this.center); // reset cameara this.setCamera(); } }; +iCn3D.prototype.setCenter = function(center) { + this.mdl.position.set(0,0,0); + this.mdlImpostor.position.set(0,0,0); + this.mdl_ghost.position.set(0,0,0); + + this.mdl.position.sub(center); + //this.mdlPicking.position.sub(center); + this.mdlImpostor.position.sub(center); + this.mdl_ghost.position.sub(center); +}; diff --git a/src/icn3dui/3dprint/threedprint.js b/src/icn3dui/3dprint/threedprint.js index 3a71f661..a95c3323 100644 --- a/src/icn3dui/3dprint/threedprint.js +++ b/src/icn3dui/3dprint/threedprint.js @@ -283,11 +283,15 @@ iCn3DUI.prototype.addStabilizer = function () { var me = this; var residueArray = Object.keys(residueHash); if(me.icn3d.pairArray === undefined) me.icn3d.pairArray = []; + // displayed atoms except water + var dAtomsNotWater = me.icn3d.exclHash(me.icn3d.dAtoms, me.icn3d.water); + for(var i = 0, il = residueArray.length; i < il; ++i) { var residueid = residueArray[i]; var ss = me.icn3d.secondaries[residueid]; - var sphere = me.icn3d.getNeighboringAtoms(me.icn3d.dAtoms, me.icn3d.hash2Atoms(me.icn3d.residues[residueid]), maxDistance); + //var sphere = me.icn3d.getNeighboringAtoms(me.icn3d.dAtoms, me.icn3d.hash2Atoms(me.icn3d.residues[residueid]), maxDistance); + var sphere = me.icn3d.getNeighboringAtoms(dAtomsNotWater, me.icn3d.hash2Atoms(me.icn3d.residues[residueid]), maxDistance); // original atoms var sphereArray = Object.keys(sphere).sort(); diff --git a/src/icn3dui/annotations/annotations.js b/src/icn3dui/annotations/annotations.js index 9d97073b..49df2c45 100644 --- a/src/icn3dui/annotations/annotations.js +++ b/src/icn3dui/annotations/annotations.js @@ -5,6 +5,15 @@ iCn3DUI.prototype.showAnnotations = function() { var me = this; me.openDialog(me.pre + 'dl_selectannotations', 'Sequences and Annotations'); + // add note about assembly + if( (me.bAssemblyNote === undefined || !me.bAssemblyNote) && me.icn3d.asuCnt !== undefined ) { + var html = "
Assembly Tips: Only the asymmetric unit is shown in the sequence window.
Click \"Assembly\" in the menu \"View\" to switch between asymmetric unit and biological assembly (" + me.icn3d.asuCnt + " asymmetric unit).
"; + + $("#" + me.pre + "dl_annotations_tabs").append(html); + + me.bAssemblyNote = true; + } + if(me.bAnnoShown === undefined || !me.bAnnoShown) { //me.setLogCmd("view annotations", true); @@ -86,7 +95,7 @@ iCn3DUI.prototype.showAnnotations = function() { var me = this; continue; } else { - var name = resObj.name; + var name = resObj.name.trim(); if(chemical_set[name] === undefined) chemical_set[name] = []; chemical_set[name].push(resid); @@ -1914,7 +1923,7 @@ iCn3DUI.prototype.showInteraction_base = function(chnid, chnidBase) { // var pos = me.icn3d.chainsSeq[chnid][i - me.matchedPos[chnid] ].resi; var pos = (i >= me.matchedPos[chnid] && i - me.matchedPos[chnid] < me.icn3d.chainsSeq[chnid].length) ? me.icn3d.chainsSeq[chnid][i - me.matchedPos[chnid]].resi : me.baseResi[chnid] + 1 + i; - html += '' + cFull + ''; + html += '' + c + ''; var emptyWidth = parseInt(me.seqAnnWidth * i / me.maxAnnoLength - prevEmptyWidth - prevLineWidth); if(emptyWidth < 0) emptyWidth = 0; @@ -2224,7 +2233,9 @@ iCn3DUI.prototype.showAnnoSelectedChains = function () { var me = this; } var atom = me.icn3d.getFirstAtomObj(me.icn3d.chains[chainid]); - $("#" + me.pre + "anno_" + atom.resn).show(); + var oneLetterRes = me.icn3d.residueName2Abbr(atom.resn.substr(0, 3)); + + $("#" + me.pre + "anno_" + oneLetterRes).show(); } }; diff --git a/src/icn3dui/common_full_simple.js b/src/icn3dui/common_full_simple.js index d050b410..70ac152d 100644 --- a/src/icn3dui/common_full_simple.js +++ b/src/icn3dui/common_full_simple.js @@ -112,7 +112,8 @@ iCn3DUI.prototype.showTitle = function() { var me = this; if(me.icn3d.molTitle.length > 40) title = me.icn3d.molTitle.substr(0, 40) + "..."; - var asymmetricStr = (me.asuAtomCount !== undefined) ? " (Asymmetric Unit)" : ""; + //var asymmetricStr = (me.bAssemblyUseAsu) ? " (Asymmetric Unit)" : ""; + var asymmetricStr = ""; $("#" + me.pre + "title").html("PDB ID " + me.inputid.toUpperCase() + "" + asymmetricStr + ": " + title); } diff --git a/src/icn3dui/full_ui.js b/src/icn3dui/full_ui.js index 178ad9d5..9b290749 100644 --- a/src/icn3dui/full_ui.js +++ b/src/icn3dui/full_ui.js @@ -465,7 +465,8 @@ iCn3DUI.prototype = { me.icn3d.removeLastSurface(); } me.icn3d.applySurfaceOptions(); - me.icn3d.render(); + //me.icn3d.render(); + me.icn3d.draw(); // to make surface work in assembly } else if(id === 'chemicalbinding') { me.icn3d.bSkipChemicalbinding = false; diff --git a/src/icn3dui/html/set_html.js b/src/icn3dui/html/set_html.js index 7e4e0aa0..1f13fd1f 100644 --- a/src/icn3dui/html/set_html.js +++ b/src/icn3dui/html/set_html.js @@ -373,11 +373,13 @@ iCn3DUI.prototype.setMenu2b = function() { var me = this; html += " "; html += " "; - if(me.cfg.mmtfid !== undefined || me.cfg.pdbid !== undefined || me.cfg.mmcifid !== undefined) { - html += "
  • Assembly"; + if(me.cfg.mmtfid !== undefined || me.cfg.pdbid !== undefined || me.cfg.mmcifid !== undefined || me.cfg.mmdbid !== undefined) { + html += "
  • Assembly"; html += "
      "; - html += me.getRadio('mn6_assembly', 'mn6_assemblyYes', 'Yes'); - html += me.getRadio('mn6_assembly', 'mn6_assemblyNo', 'No', true); + + html += me.getRadio('mn6_assembly', 'mn6_assemblyYes', 'Biological Assembly', true); + html += me.getRadio('mn6_assembly', 'mn6_assemblyNo', 'Asymmetric Unit'); + html += "
    "; html += "
  • "; } @@ -1138,6 +1140,9 @@ iCn3DUI.prototype.setDialogs = function() { var me = this; html += " "; + // add note about assembly + //html = "

    Only the asymmetric unit is shown in the sequence window.
    "; + html += "


    "; html += "
    "; diff --git a/src/icn3dui/parsers/mmcif_mmdb_parser.js b/src/icn3dui/parsers/mmcif_mmdb_parser.js index 2b200b9f..9ab50b9b 100644 --- a/src/icn3dui/parsers/mmcif_mmdb_parser.js +++ b/src/icn3dui/parsers/mmcif_mmdb_parser.js @@ -80,6 +80,72 @@ iCn3DUI.prototype.downloadMmcif = function (mmcifid) { var me = this; }); }; +iCn3DUI.prototype.downloadMmcifSymmetry = function (mmcifid) { var me = this; + // chain functions together + me.deferredSymmetry = $.Deferred(function() { + me.downloadMmcifSymmetryBase(mmcifid); + }); // end of me.deferred = $.Deferred(function() { + + return me.deferredSymmetry.promise(); +}; + +iCn3DUI.prototype.downloadMmcifSymmetryBase = function (mmcifid) { var me = this; + var url, dataType; + + url = "https://files.rcsb.org/header/" + mmcifid + ".cif"; + + dataType = "text"; + + me.icn3d.bCid = undefined; + + $.ajax({ + url: url, + dataType: dataType, + cache: true, + tryCount : 0, + retryLimit : 1, + success: function(data) { + url = "https://www.ncbi.nlm.nih.gov/Structure/mmcifparser/mmcifparser.cgi"; + + $.ajax({ + url: url, + type: 'POST', + data : {'mmcifheader': data}, + dataType: 'jsonp', + cache: true, + tryCount : 0, + retryLimit : 1, + success: function(data) { + me.loadMmcifSymmetry(data); + + if(me.deferredSymmetry !== undefined) me.deferredSymmetry.resolve(); + }, + error : function(xhr, textStatus, errorThrown ) { + this.tryCount++; + if (this.tryCount <= this.retryLimit) { + //try again + $.ajax(this); + return; + } + + if(me.deferredSymmetry !== undefined) me.deferredSymmetry.resolve(); + return; + } + }); + }, + error : function(xhr, textStatus, errorThrown ) { + this.tryCount++; + if (this.tryCount <= this.retryLimit) { + //try again + $.ajax(this); + return; + } + + return; + } + }); +}; + iCn3DUI.prototype.loadMmcifData = function(data) { var me = this; if (data.atoms !== undefined) { me.icn3d.init(); @@ -100,6 +166,15 @@ iCn3DUI.prototype.loadMmcifData = function(data) { var me = this; } } + if(me.icn3d.biomtMatrices !== undefined && me.icn3d.biomtMatrices.length > 1) { + $("#" + me.pre + "assemblyWrapper").show(); + + me.icn3d.asuCnt = me.icn3d.biomtMatrices.length; + } + else { + $("#" + me.pre + "assemblyWrapper").hide(); + } + me.icn3d.setAtomStyleByOptions(me.opts); me.icn3d.setColorByOptions(me.opts, me.icn3d.atoms); @@ -117,6 +192,51 @@ iCn3DUI.prototype.loadMmcifData = function(data) { var me = this; } }; +iCn3DUI.prototype.loadMmcifSymmetry = function(data) { var me = this; + //if(me.cfg.align === undefined && Object.keys(me.icn3d.structures).length == 1) { + // $("#" + me.pre + "alternateWrapper").hide(); + //} + + // load assembly info + var assembly = data.assembly; + var pmatrix = data.pmatrix; + +// var pmatrixMat4 = new THREE.Matrix4(); +// pmatrixMat4.fromArray(pmatrix); + +//console.log("pmatrix: " + JSON.stringify(pmatrix)); + + for(var i = 0, il = assembly.length; i < il; ++i) { +//console.log("i: " + i + " matrix1: " + JSON.stringify(assembly[i])); + + //var pmatrixInverseMat4 = new THREE.Matrix4(); + //pmatrixInverseMat4.getInverse(pmatrixMat4); + + var mat4 = new THREE.Matrix4(); + mat4.fromArray(assembly[i]); + + //pmatrixInverseMat4.multiply(mat4).multiply(pmatrixMat4); +// mat4.multiplyMatrices(pmatrixMat4, mat4); + +//console.log("mat4.elements: " + JSON.stringify(mat4.elements)); + +/* + if (me.icn3d.biomtMatrices[i] == undefined) me.icn3d.biomtMatrices[i] = new THREE.Matrix4().identity(); + + for(var j = 0, jl = assembly[i].length; j < jl; ++j) { + //me.icn3d.biomtMatrices[i].elements[j] = assembly[i][j]; + //me.icn3d.biomtMatrices[i].elements[j] = pmatrixInverseMat4.elements[j]; + me.icn3d.biomtMatrices[i].elements[j] = mat4.elements[j]; + } +*/ + + me.icn3d.biomtMatrices[i] = mat4; + } +//console.log("me.icn3d.biomtMatrices: " + JSON.stringify(me.icn3d.biomtMatrices)); + + me.icn3d.asuCnt = me.icn3d.biomtMatrices.length; +}; + iCn3DUI.prototype.downloadMmdb = function (mmdbid, bGi) { var me = this; //var maxatomcnt = (me.cfg.maxatomcnt === undefined) ? 50000 : me.cfg.maxatomcnt; var maxatomcnt = 100000; // asymmetric unit (buidx=0) will be returned if above this threshold @@ -159,6 +279,11 @@ iCn3DUI.prototype.downloadMmdb = function (mmdbid, bGi) { var me = this; me.hideLoading(); }, success: function(data) { + if(data.atoms === undefined && data.molid2rescount === undefined) { + alert('invalid MMDB data.'); + return false; + } + me.icn3d.init(); // used in download2Ddgm() @@ -169,8 +294,6 @@ iCn3DUI.prototype.downloadMmdb = function (mmdbid, bGi) { var me = this; var id = (data.pdbId !== undefined) ? data.pdbId : data.mmdbId; me.inputid = id; - me.asuAtomCount = data.asuAtomCount; - // get molid2color = {}, chain2molid = {}, molid2chain = {}; var labelsize = 40; @@ -216,48 +339,30 @@ iCn3DUI.prototype.downloadMmdb = function (mmdbid, bGi) { var me = this; me.icn3d.molid2chain = molid2chain; //if ((me.cfg.inpara !== undefined && me.cfg.inpara.indexOf('mols=') != -1) || (data.atomcount <= maxatomcnt && data.atoms !== undefined) ) { - // small structure with all atoms - // show surface options - $("#" + me.pre + "accordion5").show(); + // small structure with all atoms + // show surface options + $("#" + me.pre + "accordion5").show(); - me.loadAtomDataIn(data, id, 'mmdbid'); - - if(me.cfg.align === undefined && Object.keys(me.icn3d.structures).length == 1) { - if($("#" + me.pre + "alternateWrapper") !== null) $("#" + me.pre + "alternateWrapper").hide(); - } + me.loadAtomDataIn(data, id, 'mmdbid'); - me.icn3d.setAtomStyleByOptions(me.opts); - // use the original color from cgi output - me.icn3d.setColorByOptions(me.opts, me.icn3d.atoms, true); + // "asuAtomCount" is defined when: 1) atom count is over the threshold 2) buidx=1 3) asu atom count is smaller than biological unit atom count + me.bAssemblyUseAsu = (data.asuAtomCount !== undefined) ? true : false; - me.renderStructure(); + if(me.bAssemblyUseAsu) { // set up symmetric matrices + $("#" + me.pre + "assemblyWrapper").show(); + me.icn3d.bAssembly = true; - if(me.cfg.rotate !== undefined) me.rotStruc(me.cfg.rotate, true); - //} - - me.html2ddgm = ''; - -/* - setTimeout(function(){ - me.download2Ddgm(me.inputid.toUpperCase()); - }, 0); + //me.downloadMmcifSymmetry(id); - //if(me.cfg.show2d !== undefined && me.cfg.show2d) me.openDialog(me.pre + 'dl_2ddgm', 'Interactions'); -*/ - - if(me.cfg.show2d !== undefined && me.cfg.show2d) { - me.openDialog(me.pre + 'dl_2ddgm', 'Interactions'); - me.download2Ddgm(me.inputid.toUpperCase()); - //me.download2Ddgm(Object.keys(me.icn3d.structures)[0].toUpperCase()); + $.when(me.downloadMmcifSymmetry(id)).then(function() { + me.downloadMmdbPart2(); + }); } + else { + $("#" + me.pre + "assemblyWrapper").hide(); + me.icn3d.bAssembly = false; - //if(me.cfg.showseq !== undefined && me.cfg.showseq) me.openDialog(me.pre + 'dl_selectresidues', 'Select residues in sequences'); - - if(me.deferred !== undefined) me.deferred.resolve(); if(me.deferred2 !== undefined) me.deferred2.resolve(); - - if(data.atoms === undefined && data.molid2rescount === undefined) { - alert('invalid MMDB data.'); - return false; + me.downloadMmdbPart2(); } }, error : function(xhr, textStatus, errorThrown ) { @@ -280,6 +385,28 @@ iCn3DUI.prototype.downloadMmdb = function (mmdbid, bGi) { var me = this; }); // ajax }; +iCn3DUI.prototype.downloadMmdbPart2 = function () { var me = this; + me.icn3d.setAtomStyleByOptions(me.opts); + // use the original color from cgi output + me.icn3d.setColorByOptions(me.opts, me.icn3d.atoms, true); + + me.renderStructure(); + if(me.cfg.rotate !== undefined) me.rotStruc(me.cfg.rotate, true); + + me.html2ddgm = ''; + if(me.cfg.show2d !== undefined && me.cfg.show2d) { + me.openDialog(me.pre + 'dl_2ddgm', 'Interactions'); + me.download2Ddgm(me.inputid.toUpperCase()); + //me.download2Ddgm(Object.keys(me.icn3d.structures)[0].toUpperCase()); + } + + if(me.cfg.align === undefined && Object.keys(me.icn3d.structures).length == 1) { + if($("#" + me.pre + "alternateWrapper") !== null) $("#" + me.pre + "alternateWrapper").hide(); + } + + if(me.deferred !== undefined) me.deferred.resolve(); if(me.deferred2 !== undefined) me.deferred2.resolve(); +}; + iCn3DUI.prototype.downloadGi = function (gi) { var me = this; me.icn3d.bCid = undefined; var bGi = true; diff --git a/src/icn3dui/parsers/mmtf_parser.js b/src/icn3dui/parsers/mmtf_parser.js index 2f293879..83e089b6 100644 --- a/src/icn3dui/parsers/mmtf_parser.js +++ b/src/icn3dui/parsers/mmtf_parser.js @@ -25,16 +25,27 @@ iCn3DUI.prototype.downloadMmtf = function (mmtfid) { var me = this; if(mmtfData.bioAssemblyList[0].transformList.length > 1) { me.icn3d.biomtMatrices = []; for(var i = 0, il = mmtfData.bioAssemblyList[0].transformList.length; i < il; ++i) { - var biomt = new THREE.Matrix4().identity(); + //var biomt = new THREE.Matrix4().identity(); - for(var j = 0, jl = mmtfData.bioAssemblyList[0].transformList[i].matrix.length; j < jl; ++j) { - biomt.elements[j] = mmtfData.bioAssemblyList[0].transformList[i].matrix[j]; - } + //for(var j = 0, jl = mmtfData.bioAssemblyList[0].transformList[i].matrix.length; j < jl; ++j) { + //biomt.elements[j] = mmtfData.bioAssemblyList[0].transformList[i].matrix[j]; + //} + + var biomt = new THREE.Matrix4().fromArray(mmtfData.bioAssemblyList[0].transformList[i].matrix).transpose(); me.icn3d.biomtMatrices.push(biomt); } } + if(me.icn3d.biomtMatrices !== undefined && me.icn3d.biomtMatrices.length > 1) { + $("#" + me.pre + "assemblyWrapper").show(); + + me.icn3d.asuCnt = me.icn3d.biomtMatrices.length; + } + else { + $("#" + me.pre + "assemblyWrapper").hide(); + } + var oriindex2serial = {}; // save SG atoms in CYS residues diff --git a/src/icn3dui/parsers/pdb_parser.js b/src/icn3dui/parsers/pdb_parser.js index e7da6ce4..ccb4763a 100644 --- a/src/icn3dui/parsers/pdb_parser.js +++ b/src/icn3dui/parsers/pdb_parser.js @@ -92,6 +92,15 @@ iCn3DUI.prototype.loadPdbData = function(data) { me.icn3d.loadPDB(data); // defined in the core library + if(me.icn3d.biomtMatrices !== undefined && me.icn3d.biomtMatrices.length > 1) { + $("#" + me.pre + "assemblyWrapper").show(); + + me.icn3d.asuCnt = me.icn3d.biomtMatrices.length; + } + else { + $("#" + me.pre + "assemblyWrapper").hide(); + } + var bCalphaOnly = me.icn3d.isCalphaPhosOnly(me.icn3d.hash2Atoms(me.icn3d.proteins), 'CA'); var calphaonly = (bCalphaOnly) ? '1' : '0'; diff --git a/src/icn3dui/selection/commands.js b/src/icn3dui/selection/commands.js index 738ae79e..a33420d4 100644 --- a/src/icn3dui/selection/commands.js +++ b/src/icn3dui/selection/commands.js @@ -108,12 +108,16 @@ iCn3DUI.prototype.execCommandsBase = function (start, end, steps) { var me = thi //|| me.icn3d.commands[i].trim().indexOf('set annotation site') == 0 ) { // the command may have "|||{"factor"... var strArray = me.icn3d.commands[i].split("|||"); - if(me.bAjaxCddSite === undefined || !me.bAjaxCddSite) { + if(Object.keys(me.icn3d.proteins).length > 0 && (me.bAjaxCddSite === undefined || !me.bAjaxCddSite) ) { $.when(me.applyCommandAnnotationsAndCddSite(strArray[0].trim())).then(function() { me.execCommandsBase(i + 1, end, steps); }); } else { + if(Object.keys(me.icn3d.proteins).length == 0) { + me.applyCommandAnnotationsAndCddSiteBase(strArray[0].trim()); + } + me.execCommandsBase(i + 1, end, steps); } @@ -123,12 +127,16 @@ iCn3DUI.prototype.execCommandsBase = function (start, end, steps) { var me = thi || me.icn3d.commands[i].trim().indexOf('set annotation snp') == 0) { // the command may have "|||{"factor"... var strArray = me.icn3d.commands[i].split("|||"); - if(me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) { + if(Object.keys(me.icn3d.proteins).length > 0 && (me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) ) { $.when(me.applyCommandSnpClinvar(strArray[0].trim())).then(function() { me.execCommandsBase(i + 1, end, steps); }); } else { + if(Object.keys(me.icn3d.proteins).length == 0) { + me.applyCommandSnpClinvar(strArray[0].trim()); + } + me.execCommandsBase(i + 1, end, steps); } @@ -137,12 +145,16 @@ iCn3DUI.prototype.execCommandsBase = function (start, end, steps) { var me = thi else if(me.icn3d.commands[i].trim().indexOf('set annotation 3ddomain') == 0) { // the command may have "|||{"factor"... var strArray = me.icn3d.commands[i].split("|||"); - if(me.mmdb_data === undefined && (me.bAjax3ddomain === undefined || !me.bAjax3ddomain)) { + if(Object.keys(me.icn3d.proteins).length > 0 && me.mmdb_data === undefined && (me.bAjax3ddomain === undefined || !me.bAjax3ddomain)) { $.when(me.applyCommand3ddomain(strArray[0].trim())).then(function() { me.execCommandsBase(i + 1, end, steps); }); } else { + if(Object.keys(me.icn3d.proteins).length == 0) { + me.applyCommand3ddomain(strArray[0].trim()); + } + me.execCommandsBase(i + 1, end, steps); } @@ -153,7 +165,7 @@ iCn3DUI.prototype.execCommandsBase = function (start, end, steps) { var me = thi //$.when(me.applyCommandAnnotationsAndCddSite(strArray[0].trim())) // .then(me.applyCommandSnpClinvar(strArray[0].trim())) - if( (me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) + if( Object.keys(me.icn3d.proteins).length > 0 && (me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) && (me.bAjax3ddomain === undefined || !me.bAjax3ddomain || me.mmdb_data === undefined) ) { $.when(me.applyCommandSnpClinvar(strArray[0].trim())) .then(me.applyCommand3ddomain(strArray[0].trim())) @@ -163,7 +175,7 @@ iCn3DUI.prototype.execCommandsBase = function (start, end, steps) { var me = thi me.execCommandsBase(i + 1, end, steps); }); } - else if(me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) { + else if(Object.keys(me.icn3d.proteins).length > 0 && (me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) ) { $.when(me.applyCommandSnpClinvar(strArray[0].trim())) .then(function() { me.setAnnoTabAll(); @@ -171,7 +183,7 @@ iCn3DUI.prototype.execCommandsBase = function (start, end, steps) { var me = thi me.execCommandsBase(i + 1, end, steps); }); } - else if(me.bAjax3ddomain === undefined || !me.bAjax3ddomain || me.mmdb_data === undefined) { + else if(Object.keys(me.icn3d.proteins).length > 0 && (me.bAjax3ddomain === undefined || !me.bAjax3ddomain || me.mmdb_data === undefined) ) { $.when(me.applyCommand3ddomain(strArray[0].trim())) .then(function() { me.setAnnoTabAll(); @@ -180,9 +192,23 @@ iCn3DUI.prototype.execCommandsBase = function (start, end, steps) { var me = thi }); } else { - me.setAnnoTabAll(); + if(Object.keys(me.icn3d.proteins).length == 0) { + if((me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) + && (me.bAjax3ddomain === undefined || !me.bAjax3ddomain || me.mmdb_data === undefined)) { + me.applyCommandSnpClinvarBase(strArray[0].trim()); + me.applyCommand3ddomainBase(strArray[0].trim()); + } + else if(me.bAjaxSnpClinvar === undefined || !me.bAjaxSnpClinvar) { + me.applyCommandSnpClinvarBase(strArray[0].trim()); + } + else if(me.bAjax3ddomain === undefined || !me.bAjax3ddomain || me.mmdb_data === undefined) { + me.applyCommand3ddomainBase(strArray[0].trim()); + } + } - me.execCommandsBase(i + 1, end, steps); + me.setAnnoTabAll(); + + me.execCommandsBase(i + 1, end, steps); } return; @@ -359,39 +385,26 @@ iCn3DUI.prototype.applyCommandLoad = function (commandStr) { var me = this; return me.deferred2.promise(); }; -iCn3DUI.prototype.applyCommandAnnotationsAndCddSite = function (command) { var me = this; +iCn3DUI.prototype.applyCommandAnnotationsAndCddSiteBase = function (command) { var me = this; // chain functions together - me.deferredAnnoCddSite = $.Deferred(function() { if(command == "view annotations") { if(me.cfg.showanno === undefined || !me.cfg.showanno) { me.showAnnotations(); } } -/* - else { - var pos = command.lastIndexOf(' '); // set annotation clinvar - var type = command.substr(pos + 1); +}; - if(type == 'cdd') { - me.setAnnoTabCdd(); - } - else if(type == 'site') { - me.setAnnoTabSite(); - } - else if(type == 'all') { - me.setAnnoTabCdd(); - me.setAnnoTabSite(); - } - } -*/ +iCn3DUI.prototype.applyCommandAnnotationsAndCddSite = function (command) { var me = this; + // chain functions together + me.deferredAnnoCddSite = $.Deferred(function() { + me.applyCommandAnnotationsAndCddSiteBase(command); }); // end of me.deferred = $.Deferred(function() { return me.deferredAnnoCddSite.promise(); }; -iCn3DUI.prototype.applyCommandSnpClinvar = function (command) { var me = this; +iCn3DUI.prototype.applyCommandSnpClinvarBase = function (command) { var me = this; // chain functions together - me.deferredSnpClinvar = $.Deferred(function() { var pos = command.lastIndexOf(' '); // set annotation clinvar var type = command.substr(pos + 1); @@ -405,32 +418,48 @@ iCn3DUI.prototype.applyCommandSnpClinvar = function (command) { var me = this; me.setAnnoTabClinvar(); me.setAnnoTabSnp(); } +}; + +iCn3DUI.prototype.applyCommandSnpClinvar = function (command) { var me = this; + // chain functions together + me.deferredSnpClinvar = $.Deferred(function() { + me.applyCommandSnpClinvarBase(command); }); // end of me.deferred = $.Deferred(function() { return me.deferredSnpClinvar.promise(); }; -iCn3DUI.prototype.applyCommand3ddomain = function (command) { var me = this; +iCn3DUI.prototype.applyCommand3ddomainBase = function (command) { var me = this; // chain functions together - me.deferred3ddomain = $.Deferred(function() { var pos = command.lastIndexOf(' '); var type = command.substr(pos + 1); if(type == '3ddomain' || type == 'all') { me.setAnnoTab3ddomain(); } +}; + +iCn3DUI.prototype.applyCommand3ddomain = function (command) { var me = this; + // chain functions together + me.deferred3ddomain = $.Deferred(function() { + me.applyCommand3ddomainBase(command); }); // end of me.deferred = $.Deferred(function() { return me.deferred3ddomain.promise(); }; -iCn3DUI.prototype.applyCommandViewinteraction = function (command) { var me = this; +iCn3DUI.prototype.applyCommandViewinteractionBase = function (command) { var me = this; // chain functions together - me.deferredViewinteraction = $.Deferred(function() { if(me.cfg.align !== undefined) { var structureArray = Object.keys(me.icn3d.structures); me.set2DDiagramsForAlign(structureArray[0].toUpperCase(), structureArray[1].toUpperCase()); } +}; + +iCn3DUI.prototype.applyCommandViewinteraction = function (command) { var me = this; + // chain functions together + me.deferredViewinteraction = $.Deferred(function() { + me.applyCommandViewinteractionBase(command); }); // end of me.deferred = $.Deferred(function() { return me.deferredViewinteraction.promise(); diff --git a/src/icn3dui/twoddiagram.js b/src/icn3dui/twoddiagram.js index 27e036c3..3e6eb8b8 100644 --- a/src/icn3dui/twoddiagram.js +++ b/src/icn3dui/twoddiagram.js @@ -15,6 +15,8 @@ iCn3DUI.prototype.draw2Ddgm = function(data, mmdbid, structureIndex, bUpdate) { var molid2chain = {}, molid2color = {}, molid2name = {}, chainid2molid = {}; var chainNameHash = {}; + if(data === undefined) return ''; + for(var molid in data.moleculeInfor) { var color = '#' + ( '000000' + data.moleculeInfor[molid].color.toString( 16 ) ).slice( - 6 ); var chainName = data.moleculeInfor[molid].chain.trim(); From 4cd9d09c2001c11d5a5130a01b7cff5653c2eb51 Mon Sep 17 00:00:00 2001 From: "Wang, Jiyao" Date: Mon, 21 May 2018 13:17:28 -0400 Subject: [PATCH 2/3] speed up the rendering of assembly using instancing method --- src/icn3d/display/display_common.js | 754 +++++++++ src/icn3d/display/display_full.js | 506 ++++++ src/icn3d/display/display_simple.js | 37 + src/icn3d/draw/drawing.js | 2240 +++++++++++++++++++++++++++ src/icn3d/draw/drawing_full.js | 179 +++ src/icn3d/draw/impostor.js | 347 +++++ src/icn3d/draw/instancing.js | 463 ++++++ src/shader/CylinderInstancing.frag | 302 ++++ src/shader/CylinderInstancing.vert | 143 ++ src/shader/Instancing.frag | 12 + src/shader/Instancing.vert | 30 + src/shader/SphereInstancing.frag | 172 ++ src/shader/SphereInstancing.vert | 162 ++ 13 files changed, 5347 insertions(+) create mode 100644 src/icn3d/display/display_common.js create mode 100644 src/icn3d/display/display_full.js create mode 100644 src/icn3d/display/display_simple.js create mode 100644 src/icn3d/draw/drawing.js create mode 100644 src/icn3d/draw/drawing_full.js create mode 100644 src/icn3d/draw/impostor.js create mode 100644 src/icn3d/draw/instancing.js create mode 100644 src/shader/CylinderInstancing.frag create mode 100644 src/shader/CylinderInstancing.vert create mode 100644 src/shader/Instancing.frag create mode 100644 src/shader/Instancing.vert create mode 100644 src/shader/SphereInstancing.frag create mode 100644 src/shader/SphereInstancing.vert diff --git a/src/icn3d/display/display_common.js b/src/icn3d/display/display_common.js new file mode 100644 index 00000000..7cad5268 --- /dev/null +++ b/src/icn3d/display/display_common.js @@ -0,0 +1,754 @@ +/** + * @author Jiyao Wang / https://github.com/ncbi/icn3d + */ + +iCn3D.prototype.setAtmClr = function(atoms, hex) { + for (var i in atoms) { + var atom = this.atoms[i]; + atom.color = new THREE.Color().setHex(hex); + + this.atomPrevColors[i] = atom.color; + } +}; + +iCn3D.prototype.updateChainsColor = function (atom) { + var chainid = atom.structure + '_' + atom.chain; + if(this.chainsColor[chainid] !== undefined) { // for mmdbid and align input + this.chainsColor[chainid] = atom.color; + } +}; + +iCn3D.prototype.setColorByOptions = function (options, atoms, bUseInputColor) { + if(options !== undefined) { + if(bUseInputColor !== undefined && bUseInputColor) { + for (var i in atoms) { + var atom = this.atoms[i]; + + this.atomPrevColors[i] = atom.color; + } + } + else if(options.color.indexOf("#") === 0) { + for (var i in atoms) { + var atom = this.atoms[i]; + atom.color = new THREE.Color().setStyle(options.color.toLowerCase()); + + this.atomPrevColors[i] = atom.color; + } + } + else { + switch (options.color.toLowerCase()) { + case 'spectrum': + var idx = 0; + //var lastTerSerialInv = 1 / this.lastTerSerial; + var lastTerSerialInv = 1 / this.cnt; + for (var i in atoms) { + var atom = this.atoms[i]; + atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : new THREE.Color().setHSL(2 / 3 * (1 - idx++ * lastTerSerialInv), 1, 0.45); + //atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; + + this.atomPrevColors[i] = atom.color; + } + break; + case 'chain': + if(this.chainsColor !== undefined && Object.keys(this.chainsColor).length > 0) { // mmdb input + this.applyOriginalColor(this.hash2Atoms(this.hAtoms)); + + // atom color + var atomHash = this.unionHash(this.chemicals, this.ions); + + for (var i in atomHash) { + var atom = this.atoms[i]; + atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; + + this.atomPrevColors[i] = atom.color; + } + } + else { + var index = -1, prevChain = '', colorLength = this.stdChainColors.length; + for (var i in atoms) { + var atom = this.atoms[i]; + + if(atom.chain != prevChain) { + ++index; + + index = index % colorLength; + } + + atom.color = this.stdChainColors[index]; + + if(Object.keys(this.chainsColor).length > 0) this.updateChainsColor(atom); + this.atomPrevColors[i] = atom.color; + + prevChain = atom.chain; + } + } + break; + case 'secondary structure': + for (var i in atoms) { + var atom = this.atoms[i]; + // secondary color of nucleotide: blue (new THREE.Color(0x0000FF)) + atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.ssColors[atom.ss] || new THREE.Color(0x0000FF); + + this.atomPrevColors[i] = atom.color; + } + + break; + + case 'residue': + for (var i in atoms) { + var atom = this.atoms[i]; + atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.residueColors[atom.resn] || this.defaultResidueColor; + + this.atomPrevColors[i] = atom.color; + } + break; + case 'charge': + for (var i in atoms) { + var atom = this.atoms[i]; + + //atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; + atom.color = atom.het ? this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; + + this.atomPrevColors[i] = atom.color; + } + break; + case 'hydrophobic': + for (var i in atoms) { + var atom = this.atoms[i]; + + //atom.color = atom.het ? this.atomColors[atom.elem] || this.defaultAtomColor : this.chargeColors[atom.resn] || this.defaultResidueColor; + atom.color = atom.het ? this.defaultAtomColor : this.hydrophobicColors[atom.resn] || this.defaultResidueColor; + + this.atomPrevColors[i] = atom.color; + } + break; + case 'atom': + for (var i in atoms) { + var atom = this.atoms[i]; + atom.color = this.atomColors[atom.elem] || this.defaultAtomColor; + + this.atomPrevColors[i] = atom.color; + } + break; + + case 'conserved': + for (var i in atoms) { + var atom = this.atoms[i]; + atom.color = this.defaultAtomColor; + + this.atomPrevColors[i] = atom.color; + } + + for(var chainid in this.alnChainsSeq) { + var resObjectArray = this.alnChainsSeq[chainid]; + + for(var i = 0, il = resObjectArray.length; i < il; ++i) { + var residueid = chainid + '_' + resObjectArray[i].resi; + + for(var j in this.residues[residueid]) { + if(atoms.hasOwnProperty(j)) { + var color = new THREE.Color(resObjectArray[i].color); + this.atoms[j].color = color; + this.atomPrevColors[j] = color; + } + } + } + } + break; + + case 'white': + this.setAtmClr(atoms, 0xFFFFFF); + break; + + case 'grey': + this.setAtmClr(atoms, 0x888888); + break; + + case 'red': + this.setAtmClr(atoms, 0xFF0000); + break; + case 'green': + this.setAtmClr(atoms, 0x00FF00); + break; + case 'blue': + this.setAtmClr(atoms, 0x0000FF); + break; + case 'magenta': + this.setAtmClr(atoms, 0xFF00FF); + break; + case 'yellow': + this.setAtmClr(atoms, 0xFFFF00); + break; + case 'cyan': + this.setAtmClr(atoms, 0x00FFFF); + break; + case 'custom': + // do the coloring separately + break; + + default: // the "#" was missed in order to make sharelink work + for (var i in atoms) { + var atom = this.atoms[i]; + atom.color = new THREE.Color().setStyle("#" + options.color.toLowerCase()); + + this.atomPrevColors[i] = atom.color; + } + + break; + } + } + } +}; + +iCn3D.prototype.applyDisplayOptions = function (options, atoms, bHighlight) { var me = this; // atoms: hash of key -> 1 + if(options === undefined) options = this.opts; + + var residueHash = {}; + var singletonResidueHash = {}; + var atomsObj = {}; + var residueid; + + if(bHighlight === 1 && Object.keys(atoms).length < Object.keys(this.atoms).length) { + atomsObj = this.hash2Atoms(atoms); + + for(var i in atomsObj) { + var atom = atomsObj[i]; + + residueid = atom.structure + '_' + atom.chain + '_' + atom.resi; + residueHash[residueid] = 1; + } + + // find singleton residues + for(var i in residueHash) { + var last = i.lastIndexOf('_'); + var base = i.substr(0, last + 1); + var lastResi = parseInt(i.substr(last + 1)); + + var prevResidueid = base + (lastResi - 1).toString(); + var nextResidueid = base + (lastResi + 1).toString(); + + if(!residueHash.hasOwnProperty(prevResidueid) && !residueHash.hasOwnProperty(prevResidueid)) { + singletonResidueHash[i] = 1; + } + } + + // show the only atom in a transparent box + if(Object.keys(atomsObj).length === 1 && Object.keys(this.residues[residueid]).length > 1 + && atomsObj[Object.keys(atomsObj)[0]].style !== 'sphere' && atomsObj[Object.keys(atomsObj)[0]].style !== 'dot') { + if(this.bCid === undefined || !this.bCid) { + for(var i in atomsObj) { + var atom = atomsObj[i]; + var scale = 1.0; + this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); + } + } + } + else { + // if only one residue, add the next residue in order to show highlight + for(var residueid in singletonResidueHash) { + var atom = this.getFirstAtomObj(this.residues[residueid]); + var prevResidueid = atom.structure + '_' + atom.chain + '_' + parseInt(atom.resi - 1); + var nextResidueid = atom.structure + '_' + atom.chain + '_' + parseInt(atom.resi + 1); + + //ribbon, strand, cylinder and plate, nucleotide cartoon, o3 trace, schematic, c alpha trace, b factor tube, lines, stick, ball and stick, sphere, dot + + if(atom.style === 'cylinder and plate' && atom.ss === 'helix') { // no way to highlight part of cylinder + for(var i in this.residues[residueid]) { + var atom = this.atoms[i]; + var scale = 1.0; + this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); + } + } + else if( (atom.style === 'ribbon' && atom.ss === 'coil') || (atom.style === 'strand' && atom.ss === 'coil') || atom.style === 'o3 trace' || atom.style === 'schematic' || atom.style === 'c alpha trace' || atom.style === 'b factor tube' || (atom.style === 'cylinder and plate' && atom.ss !== 'helix') ) { + var bAddResidue = false; + // add the next residue with same style + if(!bAddResidue && this.residues.hasOwnProperty(nextResidueid)) { + var index2 = Object.keys(this.residues[nextResidueid])[0]; + var atom2 = this.hash2Atoms(this.residues[nextResidueid])[index2]; + if( (atom.style === atom2.style && !atom2.ssbegin) || atom2.ssbegin) { + var residueAtoms = this.residues[nextResidueid]; + atoms = this.unionHash(atoms, residueAtoms); + + bAddResidue = true; + + // record the highlight style for the artificial residue + if(atom2.ssbegin) { + for(var i in residueAtoms) { + this.atoms[i].notshow = true; + } + } + } + } + + // add the previous residue with same style + if(!bAddResidue && this.residues.hasOwnProperty(prevResidueid)) { + var index2 = Object.keys(this.residues[prevResidueid])[0]; + var atom2 = this.hash2Atoms(this.residues[prevResidueid])[index2]; + if(atom.style === atom2.style) { + atoms = this.unionHash(atoms, this.residues[prevResidueid]); + + bAddResidue = true; + } + } + } + else if( (atom.style === 'ribbon' && atom.ss !== 'coil' && atom.ssend) || (atom.style === 'strand' && atom.ss !== 'coil' && atom.ssend)) { + var bAddResidue = false; + // add the next residue with same style + if(!bAddResidue && this.residues.hasOwnProperty(nextResidueid)) { + var index2 = Object.keys(this.residues[nextResidueid])[0]; + var atom2 = this.hash2Atoms(this.residues[nextResidueid])[index2]; + //if(atom.style === atom2.style && !atom2.ssbegin) { + atoms = this.unionHash(atoms, this.residues[nextResidueid]); + + bAddResidue = true; + //} + } + } + } // end for + } // end else { + + atomsObj = {}; + } // end if(bHighlight === 1) + + this.setStyle2Atoms(atoms); + + //this.bAllAtoms = (Object.keys(atoms).length === Object.keys(this.atoms).length); + this.bAllAtoms = false; + if(atoms && atoms !== undefined ) { + this.bAllAtoms = (Object.keys(atoms).length === Object.keys(this.atoms).length); + } + +// var currentCalphas = {}; +// if(this.opts['sidec'] !== 'nothing') { +// currentCalphas = this.intHash(this.hAtoms, this.calphas); +// } + + var chemicalSchematicRadius = this.cylinderRadius * 0.5; + + // remove schematic labels + //if(this.labels !== undefined) this.labels['schematic'] = undefined; + if(this.labels !== undefined) delete this.labels['schematic']; + + for(var style in this.style2atoms) { + // 14 styles: ribbon, strand, cylinder and plate, nucleotide cartoon, o3 trace, schematic, c alpha trace, b factor tube, lines, stick, ball and stick, sphere, dot, nothing + atomHash = this.style2atoms[style]; + var bPhosphorusOnly = this.isCalphaPhosOnly(this.hash2Atoms(atomHash), "O3'", "O3*"); + + if(style === 'ribbon') { + this.createStrand(this.hash2Atoms(atomHash), 2, undefined, true, undefined, undefined, false, this.ribbonthickness, bHighlight); + } + else if(style === 'strand') { + this.createStrand(this.hash2Atoms(atomHash), null, null, null, null, null, false, undefined, bHighlight); + } + else if(style === 'cylinder and plate') { + this.createCylinderHelix(this.hash2Atoms(atomHash), this.cylinderHelixRadius, bHighlight); + } + else if(style === 'nucleotide cartoon') { + if(bPhosphorusOnly) { + this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.traceRadius, false, bHighlight); + } + else { + this.drawCartoonNucleicAcid(this.hash2Atoms(atomHash), null, this.ribbonthickness, bHighlight); + + if(bHighlight !== 2) this.drawNucleicAcidStick(this.hash2Atoms(atomHash), bHighlight); + } + } + else if(style === 'o3 trace') { + if(bPhosphorusOnly) { + this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.traceRadius, false, bHighlight); + } + else { + this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], this.traceRadius, false, bHighlight); + } + } + //else if(style === 'phosphorus lines') { + // this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], 0.2, true, bHighlight); + //} + else if(style === 'schematic') { + // either proteins, nucleotides, or chemicals + var firstAtom = this.getFirstAtomObj(atomHash); + + //if(firstAtom.het) { // chemicals + if(this.chemicals.hasOwnProperty(firstAtom.serial)) { // chemicals + this.addNonCarbonAtomLabels(this.hash2Atoms(atomHash)); + + bSchematic = true; + this.createStickRepresentation(this.hash2Atoms(atomHash), chemicalSchematicRadius, chemicalSchematicRadius, undefined, bHighlight, bSchematic); + } + else { // nucleotides or proteins + this.addResiudeLabels(this.hash2Atoms(atomHash), true); + + if(bPhosphorusOnly) { + this.createCylinderCurve(this.hash2Atoms(atomHash), ["P"], this.traceRadius, false, bHighlight); + } + else { + this.createCylinderCurve(this.hash2Atoms(atomHash), ["O3'", "O3*"], this.traceRadius, false, bHighlight); + } + this.createCylinderCurve(this.hash2Atoms(atomHash), ['CA'], this.traceRadius, false, bHighlight); + } + } + else if(style === 'c alpha trace') { + this.createCylinderCurve(this.hash2Atoms(atomHash), ['CA'], this.traceRadius, false, bHighlight); + } + else if(style === 'b factor tube') { + this.createTube(this.hash2Atoms(atomHash), 'CA', null, bHighlight); + } + else if(style === 'lines') { + if(bHighlight === 1) { + this.createStickRepresentation(this.hash2Atoms(atomHash), this.hlLineRadius, this.hlLineRadius, undefined, bHighlight); + } + else { + this.createLineRepresentation(this.hash2Atoms(atomHash), bHighlight); + } + } + else if(style === 'stick') { + this.createStickRepresentation(this.hash2Atoms(atomHash), this.cylinderRadius, this.cylinderRadius, undefined, bHighlight); + } + else if(style === 'ball and stick') { + this.createStickRepresentation(this.hash2Atoms(atomHash), this.cylinderRadius, this.cylinderRadius * 0.5, this.dotSphereScale, bHighlight); + } + else if(style === 'sphere') { + this.createSphereRepresentation(this.hash2Atoms(atomHash), this.sphereRadius, undefined, undefined, bHighlight); + } + else if(style === 'dot') { + this.createSphereRepresentation(this.hash2Atoms(atomHash), this.sphereRadius, false, this.dotSphereScale, bHighlight); + } + } // end for loop + + // hide the previous labels + if(this.labels !== undefined && Object.keys(this.labels).length > 0) { + this.hideLabels(); + + // labels + this.createLabelRepresentation(this.labels); + } +}; + +iCn3D.prototype.hideLabels = function () { var me = this; + // remove previous labels + if(this.mdl !== undefined) { + for(var i = 0, il = this.mdl.children.length; i < il; ++i) { + var mesh = this.mdl.children[i]; + if(mesh !== undefined && mesh.type === 'Sprite') { + this.mdl.remove(mesh); // somehow didn't work + } + } + } +}; + +iCn3D.prototype.setStyle2Atoms = function (atoms) { + this.style2atoms = {}; + + for(var i in atoms) { + if(this.style2atoms[this.atoms[i].style] === undefined) this.style2atoms[this.atoms[i].style] = {}; + + this.style2atoms[this.atoms[i].style][i] = 1; + + // side chains + if(this.atoms[i].style2 !== undefined && this.atoms[i].style2 !== 'nothing') { + if(this.style2atoms[this.atoms[i].style2] === undefined) this.style2atoms[this.atoms[i].style2] = {}; + + this.style2atoms[this.atoms[i].style2][i] = 1; + } + } + +/* + for(var i in this.atoms) { + if(atoms.hasOwnProperty(i)) { + if(this.style2atoms[this.atoms[i].style] === undefined) this.style2atoms[this.atoms[i].style] = {}; + + this.style2atoms[this.atoms[i].style][i] = 1; + + // side chains + if(this.style2atoms[this.atoms[i].style2] === undefined) this.style2atoms[this.atoms[i].style2] = {}; + + this.style2atoms[this.atoms[i].style2][i] = 1; + } + else if(this.atoms[i].style == 'schematic') { // always display schematic + if(this.style2atoms[this.atoms[i].style] === undefined) this.style2atoms[this.atoms[i].style] = {}; + this.style2atoms[this.atoms[i].style][i] = 1; + } + } +*/ +}; + +// set atom style when loading a structure +iCn3D.prototype.setAtomStyleByOptions = function (options) { + if(options === undefined) options = this.opts; + + var selectedAtoms; + + if (options.proteins !== undefined) { + selectedAtoms = this.intHash(this.hAtoms, this.proteins); + for(var i in selectedAtoms) { + this.atoms[i].style = options.proteins.toLowerCase(); + } + } + + // side chain use style2 + if (options.sidec !== undefined && options.sidec !== 'nothing') { + selectedAtoms = this.intHash(this.hAtoms, this.sidec); + //var sidec_calpha = this.unionHash(this.calphas, this.sidec); + //selectedAtoms = this.intHash(this.hAtoms, sidec_calpha); + + for(var i in selectedAtoms) { + this.atoms[i].style2 = options.sidec.toLowerCase(); + } + } + + if (options.chemicals !== undefined) { + selectedAtoms = this.intHash(this.hAtoms, this.chemicals); + for(var i in selectedAtoms) { + this.atoms[i].style = options.chemicals.toLowerCase(); + } + } + + if (options.ions !== undefined) { + selectedAtoms = this.intHash(this.hAtoms, this.ions); + for(var i in selectedAtoms) { + this.atoms[i].style = options.ions.toLowerCase(); + } + } + + if (options.water !== undefined) { + selectedAtoms = this.intHash(this.hAtoms, this.water); + for(var i in selectedAtoms) { + this.atoms[i].style = options.water.toLowerCase(); + } + } + + if (options.nucleotides !== undefined) { + selectedAtoms = this.intHash(this.hAtoms, this.nucleotides); + for(var i in selectedAtoms) { + this.atoms[i].style = options.nucleotides.toLowerCase(); + } + } +}; + +iCn3D.prototype.rebuildSceneBase = function (options) { var me = this; + jQuery.extend(me.opts, options); + + this.cam_z = this.maxD * 2; + //this.cam_z = -this.maxD * 2; + + + if(this.scene !== undefined) { + for(var i = this.scene.children.length - 1; i >= 0; i--) { + var obj = this.scene.children[i]; + this.scene.remove(obj); + } + } + else { + this.scene = new THREE.Scene(); + } + + if(this.scene_ghost !== undefined) { + for(var i = this.scene_ghost.children.length - 1; i >= 0; i--) { + var obj = this.scene_ghost.children[i]; + this.scene_ghost.remove(obj); + } + } + else { + this.scene_ghost = new THREE.Scene(); + } + + //this.directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.2); + this.directionalLight = new THREE.DirectionalLight(0xFFFFFF, 1.0); + + if(this.cam_z > 0) { + this.directionalLight.position.set(0, 0, 1); + } + else { + this.directionalLight.position.set(0, 0, -1); + } + + //var ambientLight = new THREE.AmbientLight(0x202020); + //var ambientLight = new THREE.AmbientLight(0xdddddd, 0.2); + var ambientLight = new THREE.AmbientLight(0x404040); + + this.scene.add(this.directionalLight); + this.scene.add(ambientLight); + + //this.group = new THREE.Object3D(); // regular display + + this.mdl = new THREE.Object3D(); // regular display + //this.mdlPicking = new THREE.Object3D(); // pk display + this.mdlImpostor = new THREE.Object3D(); // Impostor display + + //this.scene.add(this.mdlPicking); + this.scene.add(this.mdl); + this.scene.add(this.mdlImpostor); + + // highlight on impostors + this.mdl_ghost = new THREE.Object3D(); // Impostor display + this.scene_ghost.add(this.mdl_ghost); + + //this.scene_ghost.add(this.directionalLight); + //this.scene_ghost.add(ambientLight); + + // related to pk + this.objects = []; // define objects for pk, not all elements are used for pk + this.objects_ghost = []; // define objects for pk, not all elements are used for pk + this.raycaster = new THREE.Raycaster(); + this.projector = new THREE.Projector(); + this.mouse = new THREE.Vector2(); + + var background = this.backgroundColors[this.opts.background.toLowerCase()]; + + if(this.opts.background.toLowerCase() === 'transparent') { + this.renderer.setClearColor(background, 0); + } + else { + this.renderer.setClearColor(background, 1); + } + + this.perspectiveCamera = new THREE.PerspectiveCamera(20, this.container.whratio, 0.1, 10000); + this.perspectiveCamera.position.set(0, 0, this.cam_z); + this.perspectiveCamera.lookAt(new THREE.Vector3(0, 0, 0)); + + this.orthographicCamera = new THREE.OrthographicCamera(); + this.orthographicCamera.position.set(0, 0, this.cam_z); + this.orthographicCamera.lookAt(new THREE.Vector3(0, 0, 0)); + + this.cams = { + perspective: this.perspectiveCamera, + orthographic: this.orthographicCamera, + }; +}; + +iCn3D.prototype.setCamera = function() { + this.cam = this.cams[this.opts.camera.toLowerCase()]; + + if(this.cam === this.perspectiveCamera) { + if(this.cam_z > 0) { + this.cam.position.z = this.maxD * 2; // forperspective, the z positionshould be large enough to see the whole molecule + } + else { + this.cam.position.z = -this.maxD * 2; // forperspective, the z positionshould be large enough to see the whole molecule + } + + if(this.opts['slab'] === 'yes') { + this.cam.near = this.maxD * 2; + } + else { + this.cam.near = 0.1; + } + this.cam.far = 10000; + + this.controls = new THREE.TrackballControls( this.cam, document.getElementById(this.id), this ); + } + else if (this.cam === this.orthographicCamera){ + this.cam.right = this.maxD/2 * 2.5; + this.cam.left = -this.cam.right; + this.cam.top = this.cam.right /this.container.whratio; + this.cam.bottom = -this.cam.right /this.container.whratio; + + if(this.opts['slab'] === 'yes') { + this.cam.near = this.maxD * 2; + } + else { + this.cam.near = 0; + } + + this.cam.far = 10000; + + this.controls = new THREE.OrthographicTrackballControls( this.cam, document.getElementById(this.id), this ); + } + + this.cam.updateProjectionMatrix(); +}; + +iCn3D.prototype.render = function () { + this.directionalLight.position.copy(this.cam.position); + + this.renderer.gammaInput = true + this.renderer.gammaOutput = true + + this.renderer.setPixelRatio( window.devicePixelRatio ); // r71 + this.renderer.render(this.scene, this.cam); + //this.renderer.render(this.scene_ghost, this.cam); +}; + +iCn3D.prototype.clearImpostors = function () { + this.posArray = []; + this.colorArray = []; + this.pos2Array = []; + this.color2Array = []; + this.radiusArray = []; + + this.posArraySphere = []; + this.colorArraySphere = []; + this.radiusArraySphere = []; +}; + +iCn3D.prototype.applyTransformation = function (_zoomFactor, mouseChange, quaternion) { + var para = {}; + para.update = false; + + // zoom + para._zoomFactor = _zoomFactor; + + // translate + para.mouseChange = new THREE.Vector2(); + para.mouseChange.copy(mouseChange); + + // rotation + para.quaternion = new THREE.Quaternion(); + para.quaternion.copy(quaternion); + + this.controls.update(para); +}; + +iCn3D.prototype.applyCenterOptions = function (options) { + if(options === undefined) options = this.opts; + + switch (options.rotationcenter.toLowerCase()) { + case 'molecule center': + // move the molecule to the origin + if(this.center !== undefined) { + this.setRotationCenter(this.center); + } + break; + case 'pick center': + if(this.pAtom !== undefined) { + this.setRotationCenter(this.pAtom.coord); + } + break; + case 'display center': + var center = this.centerAtoms(this.dAtoms).center; + this.setRotationCenter(center); + break; + case 'highlight center': + var center = this.centerAtoms(this.hAtoms).center; + this.setRotationCenter(center); + break; + } +}; + +iCn3D.prototype.setRotationCenter = function (coord) { + this.mdl.position.set(0,0,0); + this.mdlImpostor.position.set(0,0,0); + this.mdl_ghost.position.set(0,0,0); + + //this.mdlPicking.position.sub(coord); + this.mdl.position.sub(coord); + this.mdlImpostor.position.sub(coord); + this.mdl_ghost.position.sub(coord); +}; + +iCn3D.prototype.applyOriginalColor = function (atoms) { + if(atoms === undefined) atoms = this.atoms; + + for (var i in atoms) { + var atom = atoms[i]; + var chainid = atom.structure + '_' + atom.chain; + + if(this.chainsColor.hasOwnProperty(chainid)) { + atom.color = this.chainsColor[chainid]; + } + else { + //atom.color = this.atomColors[atom.elem]; + break; + } + } +}; diff --git a/src/icn3d/display/display_full.js b/src/icn3d/display/display_full.js new file mode 100644 index 00000000..3335c2ba --- /dev/null +++ b/src/icn3d/display/display_full.js @@ -0,0 +1,506 @@ +/** + * @author Jiyao Wang / https://github.com/ncbi/icn3d + */ + +iCn3D.prototype.applyPrevColor = function () { + for (var i in this.atoms) { + var atom = this.atoms[i]; + atom.color = this.atomPrevColors[i]; + } +}; + +iCn3D.prototype.applyChemicalbindingOptions = function (options) { + if(options === undefined) options = this.opts; + + // display mode + if (options.chemicalbinding === 'show') { + var startAtoms; + if(this.chemicals !== undefined && Object.keys(this.chemicals).length > 0) { // show chemical-protein interaction + startAtoms = this.hash2Atoms(this.chemicals); + } + + // find atoms in chainid1, which interact with chainid2 + var radius = 4; + + if(startAtoms !== undefined) { + var targetAtoms = this.getAtomsWithinAtom(this.atoms, startAtoms, radius); + + var residueHash = {}; + + // draw sidec for these residues + for(var i in targetAtoms) { + if(startAtoms.hasOwnProperty(i)) continue; + residueHash[this.atoms[i].structure + '_' + this.atoms[i].chain + '_' + this.atoms[i].resi] = 1; + } + + var residueArray = Object.keys(residueHash); + for(var i = 0, il = residueArray.length; i < il; ++i) { + for(var j in this.residues[residueArray[i]]) { + // all atoms should be shown for hbonds + this.atoms[j].style2 = 'stick'; + } + } + + // show hydrogens + var threshold = 3.5; + this.opts["hbonds"] = "yes"; + //this.opts["water"] = "dot"; + + if(Object.keys(targetAtoms).length > 0) { + this.calculateChemicalHbonds(startAtoms, targetAtoms, parseFloat(threshold) ); + } + + // zoom in on the atoms + this.zoominSelection( this.unionHash(startAtoms, targetAtoms) ); + + //this.opts['fog'] = 'yes'; + } + } + else if (options.chemicalbinding === 'hide') { + // truen off hdonds + this.hideHbonds(); + + // center on the atoms + this.zoominSelection(this.atoms); + + //this.opts['fog'] = 'no'; + } +}; + +iCn3D.prototype.hideHbonds = function () { + this.opts["hbonds"] = "no"; + if(this.lines === undefined) this.lines = {}; + this.lines['hbond'] = []; + this.hbondpnts = []; + + for(var i in this.atoms) { + this.atoms[i].style2 = 'nothing'; + } + + for(var i in this.sidec) { + this.atoms[i].style2 = this.opts["sidec"]; + } + + for(var i in this.water) { + this.atoms[i].style = this.opts["water"]; + } +}; + +iCn3D.prototype.applySsbondsOptions = function (options) { + if(options === undefined) options = this.opts; + + if (options.ssbonds.toLowerCase() === 'yes' && this.ssbondpnts !== undefined) { + var color = '#FFFF00'; + var colorObj = new THREE.Color(0xFFFF00); + + var structureArray = Object.keys(this.structures); + var start, end; + + if(this.bAlternate) { + start = this.ALTERNATE_STRUCTURE; + end = this.ALTERNATE_STRUCTURE + 1; + } + else { + start = 0; + end = structureArray.length; + } + + this.lines['ssbond'] = []; + + for(var s = start, sl = end; s < sl; ++s) { + var structure = structureArray[s]; + + if(this.ssbondpnts[structure] === undefined) continue; + + for(var i = 0, lim = Math.floor(this.ssbondpnts[structure].length / 2); i < lim; i++) { + var res1 = this.ssbondpnts[structure][2 * i], res2 = this.ssbondpnts[structure][2 * i + 1]; + var serial1, serial2; + + var line = {}; + line.color = color; + line.dashed = true; + + var bFound = false; + for(var j in this.residues[res1]) { + if(this.atoms[j].name === 'SG') { + serial1 = this.atoms[j].serial; + line.position1 = this.atoms[j].coord; + bFound = true; + break; + } + } + + if(!bFound) { + for(var j in this.residues[res1]) { + if(this.atoms[j].name === 'CA') { + line.position1 = this.atoms[j].coord; + bFound = true; + break; + } + } + } + + bFound = false; + for(var j in this.residues[res2]) { + if(this.atoms[j].name === 'SG') { + serial2 = this.atoms[j].serial; + line.position2 = this.atoms[j].coord; + bFound = true; + break; + } + } + + if(!bFound) { + for(var j in this.residues[res2]) { + if(this.atoms[j].name === 'CA') { + line.position2 = this.atoms[j].coord; + bFound = true; + break; + } + } + } + + if(this.atoms[serial1].ids !== undefined) { // mmdb id asinput + // remove the originaldisulfide bonds + var pos = this.atoms[serial1].bonds.indexOf(serial2); + var array1, array2; + if(pos != -1) { + array1 = this.atoms[serial1].bonds.slice(0, pos); + array2 = this.atoms[serial1].bonds.slice(pos + 1); + + this.atoms[serial1].bonds = array1.concat(array2); + } + + pos = this.atoms[serial2].bonds.indexOf(serial1); + if(pos != -1) { + array1 = this.atoms[serial2].bonds.slice(0, pos); + array2 = this.atoms[serial2].bonds.slice(pos + 1); + + this.atoms[serial2].bonds = array1.concat(array2); + } + } + + //if(this.lines['ssbond'] === undefined) this.lines['ssbond'] = []; + this.lines['ssbond'].push(line); + + // create bonds for disulfide bonds + //this.createCylinder(line.position1, line.position2, this.cylinderRadius * 0.5, colorObj); + this.createCylinder(line.position1, line.position2, this.cylinderRadius, colorObj); + + // show ball and stick for these two residues + var residueAtoms = this.unionHash(this.residues[res1], this.residues[res2]); + + // show side chains for the selected atoms + var atoms = this.intHash(residueAtoms, this.sidec); + var calpha_atoms = this.intHash(residueAtoms, this.calphas); + // include calphas + atoms = this.unionHash(atoms, calpha_atoms); + + // draw sidec separatedly + for(var j in atoms) { + this.atoms[j].style2 = 'stick'; + } + } // for(var i = 0, + } // for(var s = 0, + } // if (options.ssbonds.toLowerCase() === 'yes' +}; + +iCn3D.prototype.applySurfaceOptions = function (options) { + if(options === undefined) options = this.opts; + + //switch (options.wireframe.toLowerCase()) { + switch (options.wireframe) { + case 'yes': + options.wireframe = true; + break; + case 'no': + options.wireframe = false; + break; + } + + options.opacity = parseFloat(options.opacity); + + var atoms, currAtoms; + + // only show the surface for atoms which are displaying + atoms = this.intHash(this.dAtoms, this.hAtoms); + + currAtoms = this.hash2Atoms(atoms); + + switch (options.surface.toLowerCase()) { + case 'van der waals surface': + this.createSurfaceRepresentation(currAtoms, 1, options.wireframe, options.opacity); + break; +// case 'solvent excluded surface': +// this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); +// break; + case 'solvent accessible surface': + this.createSurfaceRepresentation(currAtoms, 3, options.wireframe, options.opacity); + break; + case 'molecular surface': + this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); + break; + case 'van der waals surface with context': + this.createSurfaceRepresentation(currAtoms, 1, options.wireframe, options.opacity); + break; + case 'solvent accessible surface with context': + this.createSurfaceRepresentation(currAtoms, 3, options.wireframe, options.opacity); + break; + case 'molecular surface with context': + this.createSurfaceRepresentation(currAtoms, 2, options.wireframe, options.opacity); + break; + case 'nothing': + // remove surfaces + this.removeSurfaces(); + break; + } +}; + +iCn3D.prototype.setFog = function() { + var background = this.backgroundColors[this.opts.background.toLowerCase()]; + + // apply fog + if(this.opts['fog'] === 'yes') { + if(this.opts['camera'] === 'perspective') { //perspective, orthographic + //this.scene.fog = new THREE.Fog(background, this.cam_z, this.cam_z + 0.5 * this.maxD); + //this.scene.fog = new THREE.Fog(background, 2 * this.maxD, 2.5 * this.maxD); + this.scene.fog = new THREE.Fog(background, 2 * this.maxD, 3 * this.maxD); + } + else if(this.opts['camera'] === 'orthographic') { + this.scene.fog = new THREE.FogExp2(background, 2); + //this.scene.fog.near = this.cam_z; + //this.scene.fog.far = this.cam_z + 0.5 * this.maxD; + this.scene.fog.near = 2 * this.maxD; + //this.scene.fog.far = 2.5 * this.maxD; + this.scene.fog.far = 3 * this.maxD; + } + } + else { + this.scene.fog = undefined; + } +}; + +// change the display atom when alternating +iCn3D.prototype.alternateStructures = function () { + this.dAtoms = {}; + + var hAtomsCount = Object.keys(this.hAtoms).length; + var allAtomsCount = Object.keys(this.atoms).length; + + var moleculeArray = Object.keys(this.structures); + for(var i = 0, il = moleculeArray.length; i < il; ++i) { + var structure = moleculeArray[i]; + if(i > this.ALTERNATE_STRUCTURE || (this.ALTERNATE_STRUCTURE === il - 1 && i === 0) ) { + for(var k in this.structures[structure]) { + var chain = this.structures[structure][k]; + this.dAtoms = this.unionHash(this.dAtoms, this.chains[chain]); + } + + this.ALTERNATE_STRUCTURE = i; + break; + } + } + + if(hAtomsCount < allAtomsCount) { + this.dAtoms = this.intHash(this.dAtoms, this.hAtoms); + + this.bShowHighlight = false; + this.opts['rotationcenter'] = 'highlight center'; + } + + // also alternating the surfaces + this.removeSurfaces(); + this.applySurfaceOptions(); + + this.draw(); + + this.bShowHighlight = true; + this.opts['rotationcenter'] = 'molecule center'; +}; + +iCn3D.prototype.updateStabilizer = function () { + this.stabilizerpnts = []; + + if(this.pairArray !== undefined) { + for(var i = 0, il = this.pairArray.length; i < il; i += 2) { + var coordI = this.getResidueRepPos(this.pairArray[i]); + var coordJ = this.getResidueRepPos(this.pairArray[i + 1]); + + this.stabilizerpnts.push(coordI); + this.stabilizerpnts.push(coordJ); + } + } +}; + +iCn3D.prototype.getResidueRepPos = function (serial) { var me = this; + var atomIn = this.atoms[serial]; + var residueid = atomIn.structure + "_" + atomIn.chain + "_" + atomIn.resi; + + var pos; + if(!this.proteins.hasOwnProperty(serial) && !this.nucleotides.hasOwnProperty(serial)) { // chemicals or ions + pos = atomIn.coord; + } + else { + for(var i in this.residues[residueid]) { + var atom = this.atoms[i]; + if(atom.name === 'N3') { // nucleotide: N3 + pos = this.atoms[i].coord; + break; + } + else if(atom.name === 'CA' && atom.ss == 'coil') { // protein coil: CA + pos = this.atoms[i].coord; + break; + } + else if(atom.name === 'CA' && (atom.ss == 'helix' || atom.ss == 'sheet')) { // protein secondary: CA + pos = (this.atoms[i].coord2 !== undefined) ? this.atoms[i].coord2 : this.atoms[i].coord; + break; + } + } + } + + if(pos === undefined) pos = atomIn.coord; + + return pos; +}; + +iCn3D.prototype.applyOtherOptions = function (options) { + if(options === undefined) options = this.opts; + + //common part options + + // lines + //if (options.hbonds.toLowerCase() === 'yes' || options.ncbonds.toLowerCase() === 'yes') { + if (options.hbonds.toLowerCase() === 'yes') { + var color = '#00FF00'; + var pnts = this.hbondpnts; + + for (var i = 0, lim = Math.floor(pnts.length / 2); i < lim; i++) { + var line = {}; + line.position1 = pnts[2 * i]; + line.position2 = pnts[2 * i + 1]; + line.color = color; + line.dashed = true; + + if(this.lines['hbond'] === undefined) this.lines['hbond'] = []; + this.lines['hbond'].push(line); + } + + //this.createLines(this.lines); + } + + if (this.pairArray !== undefined && this.pairArray.length > 0) { + this.updateStabilizer(); // to update this.stabilizerpnts + + var color = '#FFFFFF'; + var pnts = this.stabilizerpnts; + this.lines['stabilizer'] = []; // reset + for (var i = 0, lim = Math.floor(pnts.length / 2); i < lim; i++) { + var line = {}; + line.position1 = pnts[2 * i]; + line.position2 = pnts[2 * i + 1]; + line.color = color; + line.dashed = false; // if true, there will be too many cylinders in the dashed lines + + this.lines['stabilizer'].push(line); + } + } + + this.createLines(this.lines); + + // surfaces + if(this.prevSurfaces !== undefined) { + for(var i = 0, il = this.prevSurfaces.length; i < il; ++i) { + this.mdl.add(this.prevSurfaces[i]); + } + } + + this.applyCenterOptions(options); + + switch (options.axis.toLowerCase()) { + case 'yes': + this.axis = true; + + this.buildAxes(this.maxD/2); + + break; + case 'no': + this.axis = false; + break; + } + switch (options.pk.toLowerCase()) { + case 'atom': + this.pk = 1; + break; + case 'no': + this.pk = 0; + break; + case 'residue': + this.pk = 2; + break; + case 'strand': + this.pk = 3; + break; + } +}; + +iCn3D.prototype.rebuildScene = function (options) { var me = this; + this.rebuildSceneBase(options); + + if(this.bSkipChemicalbinding === undefined || !this.bSkipChemicalbinding) this.applyChemicalbindingOptions(); + this.bSkipChemicalbinding = true; + + // show disulfide bonds, set side chains + this.applySsbondsOptions(); + + this.applyDisplayOptions(this.opts, this.dAtoms); + + this.applyOtherOptions(); + + this.setFog(); + + this.setCamera(); + + //https://stackoverflow.com/questions/15726560/three-js-raycaster-intersection-empty-when-objects-not-part-of-scene + me.scene_ghost.updateMatrixWorld(true); +}; + +iCn3D.prototype.draw = function () { var me = this; + this.rebuildScene(); + + // Impostor display using the saved arrays + if(this.bImpo) { + this.drawImpostorShader(); + } + + this.applyPrevColor(); + + //if(this.bSSOnly) this.drawHelixBrick(this.molid2ss, this.molid2color); + + if(this.biomtMatrices !== undefined && this.biomtMatrices.length > 1) { + if(this.bAssembly) { + this.drawSymmetryMates(); + } + else { + this.centerSelection(); + } + } + + // show the hAtoms + var hAtomsLen = (this.hAtoms !== undefined) ? Object.keys(this.hAtoms).length : 0; + + //if(hAtomsLen > 0 && hAtomsLen < Object.keys(this.atoms).length) { + if(hAtomsLen > 0 && hAtomsLen < Object.keys(this.dAtoms).length) { + this.removeHlObjects(); + if(this.bShowHighlight === undefined || this.bShowHighlight) this.addHlObjects(); + } + + if(this.bRender === true) { + this.applyTransformation(this._zoomFactor, this.mouseChange, this.quaternion); + this.render(); + + // reset to hide the side chain + //this.opts['sidec'] = 'nothing'; + } + + this.clearImpostors(); +}; diff --git a/src/icn3d/display/display_simple.js b/src/icn3d/display/display_simple.js new file mode 100644 index 00000000..5f1d8c28 --- /dev/null +++ b/src/icn3d/display/display_simple.js @@ -0,0 +1,37 @@ +/** + * @author Jiyao Wang / https://github.com/ncbi/icn3d + */ + +iCn3D.prototype.rebuildScene = function (options) { var me = this; + this.rebuildSceneBase(options); + + this.applyDisplayOptions(this.opts, this.dAtoms); + this.applyCenterOptions(); + + this.setCamera(); + + //https://stackoverflow.com/questions/15726560/three-js-raycaster-intersection-empty-when-objects-not-part-of-scene + me.scene_ghost.updateMatrixWorld(true); +}; + +iCn3D.prototype.draw = function () { var me = this; + this.rebuildScene(); + + // Impostor display using the saved arrays + if(this.bImpo) { + this.drawImpostorShader(); + } + + // show the hAtoms + if(this.hAtoms !== undefined && Object.keys(this.hAtoms).length > 0 && Object.keys(this.hAtoms).length < Object.keys(this.atoms).length) { + this.removeHlObjects(); + if(this.bShowHighlight === undefined || this.bShowHighlight) this.addHlObjects(); + } + + if(this.bRender === true) { + this.applyTransformation(this._zoomFactor, this.mouseChange, this.quaternion); + this.render(); + } + + this.clearImpostors(); +}; diff --git a/src/icn3d/draw/drawing.js b/src/icn3d/draw/drawing.js new file mode 100644 index 00000000..1b4892da --- /dev/null +++ b/src/icn3d/draw/drawing.js @@ -0,0 +1,2240 @@ +/** + * @author Jiyao Wang / https://github.com/ncbi/icn3d + */ + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createSphere = function (atom, defaultRadius, forceDefault, scale, bHighlight) { + var mesh; + + if(defaultRadius === undefined) defaultRadius = 0.8; + if(forceDefault === undefined) forceDefault = false; + if(scale === undefined) scale = 1.0; + + var radius = (this.vdwRadii[atom.elem] || defaultRadius); + + if(bHighlight === 2) { + //if(scale > 0.9) { // sphere + // scale = 1.5; + //} + //else if(scale < 0.5) { // dot + // scale = 1.0; + //} + + scale *= 1.5; + + var color = this.hColor; + + mesh = new THREE.Mesh(this.sphereGeometry, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); + + mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : radius * (scale ? scale : 1); + mesh.position.copy(atom.coord); + this.mdl.add(mesh); + } + else if(bHighlight === 1) { + mesh = new THREE.Mesh(this.sphereGeometry, this.matShader); + + mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : radius * (scale ? scale : 1); + mesh.position.copy(atom.coord); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + } + else { + if(atom.color === undefined) { + atom.color = this.defaultAtomColor; + } + + var color = atom.color; + + mesh = new THREE.Mesh(this.sphereGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); + mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : radius * (scale ? scale : 1); + mesh.position.copy(atom.coord); + + if(this.bImpo) { + this.posArraySphere.push(atom.coord.x); + this.posArraySphere.push(atom.coord.y); + this.posArraySphere.push(atom.coord.z); + + this.colorArraySphere.push(atom.color.r); + this.colorArraySphere.push(atom.color.g); + this.colorArraySphere.push(atom.color.b); + + var realRadius = forceDefault ? defaultRadius : radius * (scale ? scale : 1); + this.radiusArraySphere.push(realRadius); + + this.mdl_ghost.add(mesh); + } + else { + this.mdl.add(mesh); + } + } + + //this.mdl.add(mesh); + + if(bHighlight === 1 || bHighlight === 2) { + if(this.bImpo) { + this.prevHighlightObjects_ghost.push(mesh); + } + else { + this.prevHighlightObjects.push(mesh); + } + } + else { + if(this.bImpo) { + this.objects_ghost.push(mesh); + } + else { + this.objects.push(mesh); + } + } +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createCylinder = function (p0, p1, radius, color, bHighlight, color2, bPicking) { + var mesh; + if(bHighlight === 1) { + mesh = new THREE.Mesh(this.cylinderGeometryOutline, this.matShader); + + mesh.position.copy(p0).add(p1).multiplyScalar(0.5); + mesh.matrixAutoUpdate = false; + mesh.lookAt(p0); + mesh.updateMatrix(); + + mesh.matrix.multiply(new THREE.Matrix4().makeScale(radius, radius, p0.distanceTo(p1))).multiply(new THREE.Matrix4().makeRotationX(Math.PI * 0.5)); + + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + + this.prevHighlightObjects.push(mesh); + } + else { + if(bHighlight === 2) { + mesh = new THREE.Mesh(this.cylinderGeometry, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); + + radius *= 1.5; + } + else { + mesh = new THREE.Mesh(this.cylinderGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); + } + + mesh.position.copy(p0).add(p1).multiplyScalar(0.5); + mesh.matrixAutoUpdate = false; + mesh.lookAt(p0); + mesh.updateMatrix(); + + mesh.matrix.multiply(new THREE.Matrix4().makeScale(radius, radius, p0.distanceTo(p1))).multiply(new THREE.Matrix4().makeRotationX(Math.PI * 0.5)); + + if(this.bImpo) { + this.posArray.push(p0.x); + this.posArray.push(p0.y); + this.posArray.push(p0.z); + + this.colorArray.push(color.r); + this.colorArray.push(color.g); + this.colorArray.push(color.b); + + this.pos2Array.push(p1.x); + this.pos2Array.push(p1.y); + this.pos2Array.push(p1.z); + + if(color2 !== undefined) { + this.color2Array.push(color2.r); + this.color2Array.push(color2.g); + this.color2Array.push(color2.b); + } + else { + this.color2Array.push(color.r); + this.color2Array.push(color.g); + this.color2Array.push(color.b); + } + + this.radiusArray.push(radius); + + this.mdl_ghost.add(mesh); + } + else { + this.mdl.add(mesh); + } + + if(bHighlight === 2) { + if(this.bImpo) { + this.prevHighlightObjects_ghost.push(mesh); + } + else { + this.prevHighlightObjects.push(mesh); + } + } + else { + if(this.bImpo) { + this.objects_ghost.push(mesh); + } + else { + if(bPicking === undefined || bPicking) this.objects.push(mesh); + } + } + } +}; + +// from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createRepresentationSub = function (atoms, f0, f01) { + var me = this; + + //var ged = new THREE.Geometry(); + var clbondArray = []; + for (var i in atoms) { + var atom0 = atoms[i]; + f0 && f0(atom0); + for (var j in atom0.bonds) { + var atom1 = this.atoms[atom0.bonds[j]]; + if (atom1 === undefined || atom1.serial < atom0.serial) continue; + if (atom1.chain === atom0.chain && ((atom1.resi === atom0.resi) || (atom0.name === 'C' && atom1.name === 'N') || (atom0.name === 'O3\'' && atom1.name === 'P') || (atom0.name === 'O3*' && atom1.name === 'P') || (atom0.name === 'SG' && atom1.name === 'SG'))) { + f01 && f01(atom0, atom1); + } else { + //ged.vertices.push(atom0.coord); + //ged.vertices.push(atom1.coord); + clbondArray.push([atom0.coord, atom1.coord]); + } + } + } + //if (ged.vertices.length && this.bShowCrossResidueBond) { + if (clbondArray.length > 0 && this.bShowCrossResidueBond) { + //ged.computeLineDistances(); + //this.mdl.add(new THREE.Line(ged, new THREE.LineDashedMaterial({ linewidth: this.linewidth, color: this.defaultBondColor, dashSize: 0.3, gapSize: 0.15 }), THREE.LinePieces)); + var color = new THREE.Color(0x00FF00); + + for(var i = 0, il = clbondArray.length; i < il; ++i) { + me.createCylinder(clbondArray[i][0], clbondArray[i][1], this.cylinderRadius, color, 0); + } + } +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createSphereRepresentation = function (atoms, defaultRadius, forceDefault, scale, bHighlight) { + var me = this; + + this.createRepresentationSub(atoms, function (atom0) { + me.createSphere(atom0, defaultRadius, forceDefault, scale, bHighlight); + }); +}; + +iCn3D.prototype.createBoxRepresentation_P_CA = function (atoms, scale, bHighlight) { + var me = this; + this.createRepresentationSub(atoms, function (atom0) { + if(atom0.name === 'CA' || atom0.name === "O3'" || atom0.name === "O3*") { + me.createBox(atom0, undefined, undefined, scale, undefined, bHighlight); + } + }); +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createStickRepresentation = function (atoms, atomR, bondR, scale, bHighlight, bSchematic) { + var me = this; + var factor = (bSchematic !== undefined && bSchematic) ? atomR / me.cylinderRadius : 1; + +// if(bHighlight !== 2) { + this.createRepresentationSub(atoms, function (atom0) { + me.createSphere(atom0, atomR, !scale, scale, bHighlight); + }, function (atom0, atom1) { + var mp = atom0.coord.clone().add(atom1.coord).multiplyScalar(0.5); + var pair = atom0.serial + '_' + atom1.serial; + + if(me.doublebonds.hasOwnProperty(pair)) { // show double bond + var a0, a1, a2; + + var v0; + var random = new THREE.Vector3(Math.random(),Math.random(),Math.random()); + if(atom0.bonds.length == 1 && atom1.bonds.length == 1) { + v0 = atom1.coord.clone(); + v0.sub(atom0.coord); + + var v = random.clone(); + v0.cross(v).normalize().multiplyScalar(0.2 * factor); + } + else { + if(atom0.bonds.length >= atom1.bonds.length && atom0.bonds.length > 1) { + a0 = atom0.serial; + a1 = atom0.bonds[0]; + a2 = atom0.bonds[1]; + } + //else { + else if(atom1.bonds.length >= atom0.bonds.length && atom1.bonds.length > 1) { + a0 = atom1.serial; + a1 = atom1.bonds[0]; + a2 = atom1.bonds[1]; + } + else { + console.log("Double bond was not drawn due to the undefined cross plane"); + return; + } + + var v1 = me.atoms[a0].coord.clone(); + v1.sub(me.atoms[a1].coord); + var v2 = me.atoms[a0].coord.clone(); + v2.sub(me.atoms[a2].coord); + + v1.cross(v2); + + // parallel + if(parseInt(v1.length() * 10000) == 0) { + //v1 = random.clone(); + // use a constant so that they are fixed,e.g., in CO2 + v1 = new THREE.Vector3(0.2, 0.3, 0.5); + } + + v0 = atom1.coord.clone(); + v0.sub(atom0.coord); + + v0.cross(v1).normalize().multiplyScalar(0.2 * factor); + // parallel + if(parseInt(v0.length() * 10000) == 0) { + //v1 = random.clone(); + // use a constant so that they are fixed,e.g., in CO2 + v1 = new THREE.Vector3(0.5, 0.3, 0.2); + v0.cross(v1).normalize().multiplyScalar(0.2 * factor); + } + } + + if (atom0.color === atom1.color) { + me.createCylinder(atom0.coord.clone().add(v0), atom1.coord.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + me.createCylinder(atom0.coord.clone().sub(v0), atom1.coord.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + } else { + if(me.bImpo) { + me.createCylinder(atom0.coord.clone().add(v0), atom1.coord.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight, atom1.color); + me.createCylinder(atom0.coord.clone().sub(v0), atom1.coord.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight, atom1.color); + } + else { + me.createCylinder(atom0.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + me.createCylinder(atom1.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); + + me.createCylinder(atom0.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + me.createCylinder(atom1.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); + } + } + } + else if(me.aromaticbonds.hasOwnProperty(pair)) { // show aromatic bond + var a0, a1, a2; + if(atom0.bonds.length > atom1.bonds.length && atom0.bonds.length > 1) { + a0 = atom0.serial; + a1 = atom0.bonds[0]; + a2 = atom0.bonds[1]; + } + else if(atom1.bonds.length > 1) { + a0 = atom1.serial; + a1 = atom1.bonds[0]; + a2 = atom1.bonds[1]; + } + else { + return; + } + + var v1 = me.atoms[a0].coord.clone(); + v1.sub(me.atoms[a1].coord); + var v2 = me.atoms[a0].coord.clone(); + v2.sub(me.atoms[a2].coord); + + v1.cross(v2); + + var v0 = atom1.coord.clone(); + v0.sub(atom0.coord); + + v0.cross(v1).normalize().multiplyScalar(0.2 * factor); + + // find an aromatic neighbor + var aromaticNeighbor = 0; + for(var i = 0, il = atom0.bondOrder.length; i < il; ++i) { + if(atom0.bondOrder[i] === '1.5' && atom0.bonds[i] !== atom1.serial) { + aromaticNeighbor = atom0.bonds[i]; + } + } + + var dashed = "add"; + if(aromaticNeighbor === 0 ) { // no neighbor found, atom order does not matter + dashed = "add"; + } + else { + // calculate the angle between atom1, atom0add, atomNeighbor and the angle atom1, atom0sub, atomNeighbor + var atom0add = atom0.coord.clone().add(v0); + var atom0sub = atom0.coord.clone().sub(v0); + + var a = atom1.coord.clone().sub(atom0add).normalize(); + var b = me.atoms[aromaticNeighbor].coord.clone().sub(atom0add).normalize(); + + var c = atom1.coord.clone().sub(atom0sub).normalize(); + var d = me.atoms[aromaticNeighbor].coord.clone().sub(atom0sub).normalize(); + + var angleadd = Math.acos(a.dot(b)); + var anglesub = Math.acos(c.dot(d)); + + if(angleadd < anglesub) { + dashed = 'sub'; + } + else { + dashed = 'add'; + } + } + + if (atom0.color === atom1.color) { + var base, step; + if(dashed === 'add') { + me.createCylinder(atom0.coord.clone().sub(v0), atom1.coord.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + + base = atom0.coord.clone().add(v0); + step = atom1.coord.clone().add(v0).sub(base).multiplyScalar(1.0/11); + } + else { + me.createCylinder(atom0.coord.clone().add(v0), atom1.coord.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + + base = atom0.coord.clone().sub(v0); + step = atom1.coord.clone().sub(v0).sub(base).multiplyScalar(1.0/11); + } + + for(var i = 0; i <= 10; ++i) { + if(i % 2 == 0) { + var pos1 = base.clone().add(step.clone().multiplyScalar(i)); + var pos2 = base.clone().add(step.clone().multiplyScalar(i + 1)); + me.createCylinder(pos1, pos2, me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + } + } + + } else { + var base, step; + if(dashed === 'add') { + me.createCylinder(atom0.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + me.createCylinder(atom1.coord.clone().sub(v0), mp.clone().sub(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); + + base = atom0.coord.clone().add(v0); + step = atom1.coord.clone().add(v0).sub(base).multiplyScalar(1.0/11); + } + else { + me.createCylinder(atom0.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + me.createCylinder(atom1.coord.clone().add(v0), mp.clone().add(v0), me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); + + base = atom0.coord.clone().sub(v0); + step = atom1.coord.clone().sub(v0).sub(base).multiplyScalar(1.0/11); + } + + for(var i = 0; i <= 10; ++i) { + if(i % 2 == 0) { + var pos1 = base.clone().add(step.clone().multiplyScalar(i)); + var pos2 = base.clone().add(step.clone().multiplyScalar(i + 1)); + if(i < 5) { + me.createCylinder(pos1, pos2, me.cylinderRadius * factor * 0.3, atom0.color, bHighlight); + } + else { + me.createCylinder(pos1, pos2, me.cylinderRadius * factor * 0.3, atom1.color, bHighlight); + } + } + } + } + } + else if(me.triplebonds.hasOwnProperty(pair)) { // show triple bond + var random = new THREE.Vector3(Math.random(),Math.random(),Math.random()); + var v = atom1.coord.clone(); + v.sub(atom0.coord); + + var c = random.clone(); + c.cross(v).normalize().multiplyScalar(0.3 * factor); + + if (atom0.color === atom1.color) { + me.createCylinder(atom0.coord, atom1.coord, me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); + me.createCylinder(atom0.coord.clone().add(c), atom1.coord.clone().add(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); + me.createCylinder(atom0.coord.clone().sub(c), atom1.coord.clone().sub(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); + } else { + if(me.bImpo) { + me.createCylinder(atom0.coord, atom1.coord, me.cylinderRadius * factor * 0.2, atom0.color, bHighlight, atom1.color); + me.createCylinder(atom0.coord.clone().add(c), atom1.coord.clone().add(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight, atom1.color); + me.createCylinder(atom0.coord.clone().sub(c), atom1.coord.clone().sub(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight, atom1.color); + } + else { + me.createCylinder(atom0.coord, mp, me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); + me.createCylinder(atom1.coord, mp, me.cylinderRadius * factor * 0.2, atom1.color, bHighlight); + + me.createCylinder(atom0.coord.clone().add(c), mp.clone().add(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); + me.createCylinder(atom1.coord.clone().add(c), mp.clone().add(c), me.cylinderRadius * factor * 0.2, atom1.color, bHighlight); + + me.createCylinder(atom0.coord.clone().sub(c), mp.clone().sub(c), me.cylinderRadius * factor * 0.2, atom0.color, bHighlight); + me.createCylinder(atom1.coord.clone().sub(c), mp.clone().sub(c), me.cylinderRadius * factor * 0.2, atom1.color, bHighlight); + } + } + } + else { + if (atom0.color === atom1.color) { + me.createCylinder(atom0.coord, atom1.coord, bondR, atom0.color, bHighlight); + } else { + if(me.bImpo) { + me.createCylinder(atom0.coord, atom1.coord, bondR, atom0.color, bHighlight, atom1.color); + } + else { + me.createCylinder(atom0.coord, mp, bondR, atom0.color, bHighlight); + me.createCylinder(atom1.coord, mp, bondR, atom1.color, bHighlight); + } + } + } + }); +// } +// else if(bHighlight === 2) { +// this.createBoxRepresentation_P_CA(atoms, 1.2, bHighlight); +// } +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createLineRepresentation = function (atoms, bHighlight) { + var me = this; + var geo = new THREE.Geometry(); + this.createRepresentationSub(atoms, undefined, function (atom0, atom1) { + if (atom0.color === atom1.color) { + geo.vertices.push(atom0.coord); + geo.vertices.push(atom1.coord); + geo.colors.push(atom0.color); + geo.colors.push(atom1.color); + } else { + var mp = atom0.coord.clone().add(atom1.coord).multiplyScalar(0.5); + geo.vertices.push(atom0.coord); + geo.vertices.push(mp); + geo.vertices.push(atom1.coord); + geo.vertices.push(mp); + geo.colors.push(atom0.color); + geo.colors.push(atom0.color); + geo.colors.push(atom1.color); + geo.colors.push(atom1.color); + } + }); + + if(bHighlight !== 2) { + var line; + if(bHighlight === 1) { + // highlight didn't work for lines + //line = new THREE.Mesh(geo, this.matShader); + } + else { + line = new THREE.Line(geo, new THREE.LineBasicMaterial({ linewidth: this.linewidth, vertexColors: true }), THREE.LinePieces); + this.mdl.add(line); + } + + if(bHighlight === 1) { + this.prevHighlightObjects.push(line); + } + else { + this.objects.push(line); + } + } + else if(bHighlight === 2) { + this.createBoxRepresentation_P_CA(atoms, 0.8, bHighlight); + } +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.subdivide = function (_pnts, _clrs, DIV, bShowArray, bHighlight) { // Catmull-Rom subdivision + var ret = []; + var pos = []; + var color = []; + + var pnts = new Array(); // Smoothing test + pnts.push(_pnts[0]); + for (var i = 1, lim = _pnts.length - 1; i < lim; ++i) { + var p0 = _pnts[i], p1 = _pnts[i + 1]; + pnts.push(p0.smoothen ? p0.clone().add(p1).multiplyScalar(0.5) : p0); + } + pnts.push(_pnts[_pnts.length - 1]); + + var savedPoints = []; + var savedPos = []; + var savedColor = []; + for (var i = -1, size = pnts.length, DIVINV = 1 / DIV; i <= size - 3; ++i) { + var p0 = pnts[i === -1 ? 0 : i]; + var p1 = pnts[i + 1], p2 = pnts[i + 2]; + var p3 = pnts[i === size - 3 ? size - 1 : i + 3]; + var v0 = p2.clone().sub(p0).multiplyScalar(0.5); + var v1 = p3.clone().sub(p1).multiplyScalar(0.5); + + //if(i > -1 && bHighlight && bShowArray !== undefined && bShowArray[i + 1]) { + if(i > -1 && (bShowArray === undefined || bShowArray[i + 1]) ) { + // get from previous i for the first half of residue + ret = ret.concat(savedPoints); + pos = pos.concat(savedPos); + color = color.concat(savedColor); + } + + savedPoints = []; + savedPos = []; + savedColor = []; + + for (var j = 0; j < DIV; ++j) { + var t = DIVINV * j; + var x = p1.x + t * v0.x + + t * t * (-3 * p1.x + 3 * p2.x - 2 * v0.x - v1.x) + + t * t * t * (2 * p1.x - 2 * p2.x + v0.x + v1.x); + var y = p1.y + t * v0.y + + t * t * (-3 * p1.y + 3 * p2.y - 2 * v0.y - v1.y) + + t * t * t * (2 * p1.y - 2 * p2.y + v0.y + v1.y); + var z = p1.z + t * v0.z + + t * t * (-3 * p1.z + 3 * p2.z - 2 * v0.z - v1.z) + + t * t * t * (2 * p1.z - 2 * p2.z + v0.z + v1.z); + if(!bShowArray) { + ret.push(new THREE.Vector3(x, y, z)); + pos.push(i + 1); + color.push(_clrs[i+1]); + } + else { + if(bShowArray[i + 1]) { + if(j <= parseInt((DIV) / 2) ) { + ret.push(new THREE.Vector3(x, y, z)); + pos.push(bShowArray[i + 1]); + color.push(_clrs[i+1]); + } + } + + if(bShowArray[i + 2]) { + if(j > parseInt((DIV) / 2) ) { + savedPoints.push(new THREE.Vector3(x, y, z)); + savedPos.push(bShowArray[i + 2]); + savedColor.push(_clrs[i+2]); + } + } + } // end else + } + + } + + if(!bShowArray || bShowArray[i + 1]) { + //if(bHighlight) { + ret = ret.concat(savedPoints); + pos = pos.concat(savedPos); + color = color.concat(savedColor); + //} + + ret.push(pnts[pnts.length - 1]); + pos.push(pnts.length - 1); + color.push(_clrs[pnts.length - 1]); + } + + savedPoints = []; + savedPos = []; + savedColor = []; + pnts = []; + + pnts_positions = []; + + pnts_positions.push(ret); + pnts_positions.push(pos); + pnts_positions.push(color); + + return pnts_positions; +}; + +iCn3D.prototype.createCurveSubArrow = function (p, width, colors, div, bHighlight, bRibbon, num, positionIndex, pntsCA, prevCOArray, bShowArray, calphaIdArray, bShowArrow) { + var divPoints = [], positions = []; + + divPoints.push(p); + positions.push(positionIndex); + + this.prepareStrand(divPoints, positions, width, colors, div, undefined, bHighlight, bRibbon, num, pntsCA, prevCOArray, false, bShowArray, calphaIdArray, bShowArrow); + + divPoints = []; + positions = []; +}; + +iCn3D.prototype.setCalphaDrawnCoord = function (pnts, div, calphaIdArray) { + var index = 0; + + if(calphaIdArray !== undefined) { + for(var i = 0, il = pnts.length; i < il; i += div) { // pnts.length = (calphaIdArray.length - 1) * div + 1 + var serial = calphaIdArray[index]; + + if(this.atoms.hasOwnProperty(serial)) { + this.atoms[serial].coord2 = pnts[i].clone(); + } + + ++index; + } + } +}; + + +// modified from iview (http://star.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createCurveSub = function (_pnts, width, colors, div, bHighlight, bRibbon, bNoSmoothen, bShowArray, calphaIdArray, positions) { + if (_pnts.length === 0) return; + div = div || 5; + var pnts; + if(!bNoSmoothen) { + var pnts_clrs = this.subdivide(_pnts, colors, div, bShowArray, bHighlight); + pnts = pnts_clrs[0]; + colors = pnts_clrs[2]; + } + else { + pnts = _pnts; + } + if (pnts.length === 0) return; + + this.setCalphaDrawnCoord(pnts, div, calphaIdArray); + + if(bHighlight === 1) { + var radius = this.coilWidth / 2; + //var radiusSegments = 8; + var radiusSegments = 4; // save memory + var closed = false; + + if(pnts.length > 1) { + if(positions !== undefined) { + var currPos, prevPos; + var currPoints = []; + for(var i = 0, il = pnts.length; i < il; ++i) { + currPos = positions[i]; + + if( (currPos !== prevPos && currPos !== prevPos + 1 && prevPos !== undefined) || (i === il -1) ) { + // first tube + var geometry0 = new THREE.TubeGeometry( + new THREE.CatmullRomCurve3(currPoints), // path + currPoints.length, // segments + radius, + radiusSegments, + closed + ); + + mesh = new THREE.Mesh(geometry0, this.matShader); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + + this.prevHighlightObjects.push(mesh); + + geometry0 = null; + + currPoints = []; + } + + currPoints.push(pnts[i]); + + prevPos = currPos; + } + + currPoints = []; + } + else { + var geometry0 = new THREE.TubeGeometry( + new THREE.CatmullRomCurve3(pnts), // path + pnts.length, // segments + radius, + radiusSegments, + closed + ); + + mesh = new THREE.Mesh(geometry0, this.matShader); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + + this.prevHighlightObjects.push(mesh); + + geometry0 = null; + } + } + } + else { + var geo = new THREE.Geometry(); + + if(bHighlight === 2 && bRibbon) { + for (var i = 0, divInv = 1 / div; i < pnts.length; ++i) { + // shift the highlight a little bit to avoid the overlap with ribbon + pnts[i].addScalar(0.6); // this.ribbonthickness is 0.4 + geo.vertices.push(pnts[i]); + //geo.colors.push(new THREE.Color(colors[i === 0 ? 0 : Math.round((i - 1) * divInv)])); + geo.colors.push(new THREE.Color(colors[i])); + } + } + else { + for (var i = 0, divInv = 1 / div; i < pnts.length; ++i) { + geo.vertices.push(pnts[i]); + //geo.colors.push(new THREE.Color(colors[i === 0 ? 0 : Math.round((i - 1) * divInv)])); + geo.colors.push(new THREE.Color(colors[i])); + } + } + + var line = new THREE.Line(geo, new THREE.LineBasicMaterial({ linewidth: width, vertexColors: true }), THREE.LineStrip); + this.mdl.add(line); + if(bHighlight === 2) { + this.prevHighlightObjects.push(line); + } + else { + this.objects.push(line); + } + } + + pnts = null; +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createCylinderCurve = function (atoms, atomNameArray, radius, bLines, bHighlight) { + var start = null; + var currentChain, currentResi; + var i; + var pnts = [], colors = [], radii = []; + + var maxDistance = 8.0; // max residue-residue (or nucleitide-nucleitide) distance allowed + + for (i in atoms) { + var atom = atoms[i]; + if (atom.het) continue; + + //if (atom.name !== atomName) continue; + if(atomNameArray.indexOf(atom.name) == -1) continue; + + if (start !== null && currentChain === atom.chain && currentResi + 1 === atom.resi && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { +// if (start !== null && currentChain === atom.chain && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { + var middleCoord = start.coord.clone().add(atom.coord).multiplyScalar(0.5); + + if(!bHighlight) { + if(bLines) { + var line = this.createSingleLine( start.coord, middleCoord, start.color, false); + this.mdl.add(line); + this.objects.push(line); + line = this.createSingleLine( middleCoord, atom.coord, atom.color, false); + this.mdl.add(line); + this.objects.push(line); + } + else { + this.createCylinder(start.coord, middleCoord, radius, start.color); + this.createCylinder(middleCoord, atom.coord, radius, atom.color); + this.createSphere(atom, radius, true, 1, bHighlight); + } + } + else if(bHighlight === 1) { + this.createCylinder(start.coord, middleCoord, radius, start.color, bHighlight); + this.createCylinder(middleCoord, atom.coord, radius, atom.color, bHighlight); + this.createSphere(atom, radius, true, 1, bHighlight); + } + } + + start = atom; + currentChain = atom.chain; + currentResi = atom.resi; + + if(bHighlight === 2) this.createBox(atom, undefined, undefined, undefined, undefined, bHighlight); + } + if (start !== null && currentChain === atom.chain && currentResi + 1 === atom.resi && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { +// if (start !== null && currentChain === atom.chain && Math.abs(start.coord.x - atom.coord.x) < maxDistance && Math.abs(start.coord.y - atom.coord.y) < maxDistance && Math.abs(start.coord.z - atom.coord.z) < maxDistance ) { + var middleCoord = start.coord.add(atom.coord).multiplyScalar(0.5); + if(!bHighlight) { + if(bLines) { + var line = this.createSingleLine( start.coord, middleCoord, start.color, false); + this.mdl.add(line); + this.objects.push(line); + line = this.createSingleLine( middleCoord, atom.coord, atom.color, false); + this.mdl.add(line); + this.objects.push(line); + } + else { + this.createCylinder(start.coord, middleCoord, radius, start.color); + this.createCylinder(middleCoord, atom.coord, radius, atom.color); + } + } + else if(bHighlight === 1) { + this.createCylinder(start.coord, middleCoord, radius, start.color, bHighlight); + this.createCylinder(middleCoord, atom.coord, radius, atom.color, bHighlight); + this.createSphere(atom, radius, true, 1, bHighlight); + } + } +}; + +iCn3D.prototype.prepareStrand = function(divPoints, positions, width, colors, div, thickness, bHighlight, bRibbon, num, pntsCA, prevCOArray, bStrip, bShowArray, calphaIdArray, bShowArrow) { + if(pntsCA.length === 1) { + return; + } + + var colorsLastTwo = []; + colorsLastTwo.push(colors[colors.length - 2]); + colorsLastTwo.push(colors[colors.length - 1]); + + div = div || this.axisDIV; + var numM1Inv2 = 2 / (num - 1); + var delta, lastCAIndex, lastPrevCOIndex, v; + + var pnts = {}, colorsTmp = []; + for(var i = 0, il = positions.length; i < il; ++i) pnts[i] = []; + + // smooth C-alpha + var pnts_clrs = this.subdivide(pntsCA, colors, div); + var pntsCASmooth = pnts_clrs[0]; // get all smoothen pnts, do not use 'bShowArray' + //colors = pnts_clrs[2]; + + if(pntsCASmooth.length === 1) { + return; + } + + // draw the sheet without the last residue + // use the sheet coord for n-2 residues + var colorsTmp = []; + var lastIndex = (bShowArrow === undefined || bShowArrow) ? pntsCA.length - 2 : pntsCA.length; + + for (var i = 0, il = lastIndex; i < il; ++i) { + for(var index = 0, indexl = positions.length; index < indexl; ++index) { + pnts[index].push(divPoints[index][i]); + } + colorsTmp.push(colors[i]); + } + colorsTmp.push(colors[i]); + + if(bShowArrow === undefined || bShowArrow) { + // assign the sheet coord from C-alpha for the 2nd to the last residue of the sheet + for(var i = 0, il = positions.length; i < il; ++i) { + delta = -1 + numM1Inv2 * positions[i]; + lastCAIndex = pntsCASmooth.length - 1 - div; + lastPrevCOIndex = pntsCA.length - 2; + v = new THREE.Vector3(pntsCASmooth[lastCAIndex].x + prevCOArray[lastPrevCOIndex].x * delta, pntsCASmooth[lastCAIndex].y + prevCOArray[lastPrevCOIndex].y * delta, pntsCASmooth[lastCAIndex].z + prevCOArray[lastPrevCOIndex].z * delta); + pnts[i].push(v); + } + } + + var posIndex = []; + var results; + for(var i = 0, il = positions.length; i < il; ++i) { + results = this.subdivide(pnts[i], colorsTmp, div, bShowArray, bHighlight); + pnts[i] = results[0]; + colors = results[2]; + if(i === 0) { + posIndex = results[1]; + } + } + + if(bStrip) { + //this.createStrip(pnts[0], pnts[1], colorsTmp, div, thickness, bHighlight, true, undefined, posIndex); + this.createStrip(pnts[0], pnts[1], colors, div, thickness, bHighlight, true, undefined, calphaIdArray, posIndex); + } + else { + //this.createCurveSub(pnts[0], width, colorsTmp, div, bHighlight, bRibbon, true, undefined, posIndex); + this.createCurveSub(pnts[0], width, colors, div, bHighlight, bRibbon, true, undefined, calphaIdArray, posIndex); + } + + if(bShowArrow === undefined || bShowArrow) { + // draw the arrow + colorsTmp = []; + + posIndex = []; + for(var index = 0, indexl = positions.length; index < indexl; ++index) { + pnts[index] = []; + + for (var i = div * (pntsCA.length - 2), il = div * (pntsCA.length - 1); bShowArray[parseInt(i/div)] && i < il; i = i + div) { + var pos = parseInt(i/div); + for (var j = 0; j < div; ++j) { + var delta = -1 + numM1Inv2 * positions[index]; + var scale = 1.8; // scale of the arrow width + delta = delta * scale * (div - j) / div; + var oriIndex = parseInt(i/div); + + var v = new THREE.Vector3(pntsCASmooth[i+j].x + prevCOArray[oriIndex].x * delta, pntsCASmooth[i+j].y + prevCOArray[oriIndex].y * delta, pntsCASmooth[i+j].z + prevCOArray[oriIndex].z * delta); + v.smoothen = true; + pnts[index].push(v); + colorsTmp.push(colorsLastTwo[0]); + if(index === 0) posIndex.push(pos); + } + } + + // last residue + // make the arrow end with 0 + var delta = 0; + var lastCAIndex = pntsCASmooth.length - 1; + var lastPrevCOIndex = pntsCA.length - 1; + + //if(bShowArray[lastPrevCOIndex]) { + var v = new THREE.Vector3(pntsCASmooth[lastCAIndex].x + prevCOArray[lastPrevCOIndex].x * delta, pntsCASmooth[lastCAIndex].y + prevCOArray[lastPrevCOIndex].y * delta, pntsCASmooth[lastCAIndex].z + prevCOArray[lastPrevCOIndex].z * delta); + v.smoothen = true; + pnts[index].push(v); + colorsTmp.push(colorsLastTwo[1]); + if(index === 0) posIndex.push(lastCAIndex); + //} + } + + pntsCASmooth = []; + + //colorsTmp.push(colors[colors.length - 2]); + //colorsTmp.push(colors[colors.length - 1]); + + if(bStrip) { + this.createStrip(pnts[0], pnts[1], colorsTmp, div, thickness, bHighlight, true, undefined, undefined, posIndex); + } + else { + this.createCurveSub(pnts[0], width, colorsTmp, div, bHighlight, bRibbon, true, undefined, undefined, posIndex); + } + } + + for(var i in pnts) { + for(var j = 0, jl = pnts[i].length; j < jl; ++j) { + pnts[i][j] = null; + } + pnts[i] = []; + } + + pnts = {}; +}; + +iCn3D.prototype.createStripArrow = function (p0, p1, colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, bShowArrow) { + var divPoints = [], positions = []; + + divPoints.push(p0); + divPoints.push(p1); + positions.push(start); + positions.push(end); + + this.prepareStrand(divPoints, positions, undefined, colors, div, thickness, bHighlight, undefined, num, pntsCA, prevCOArray, true, bShowArray, calphaIdArray, bShowArrow); + + divPoints = []; + positions = []; +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createStrip = function (p0, p1, colors, div, thickness, bHighlight, bNoSmoothen, bShowArray, calphaIdArray, positions) { + if (p0.length < 2) return; + div = div || this.axisDIV; + if(!bNoSmoothen) { + var pnts_clrs0 = this.subdivide(p0, colors, div, bShowArray, bHighlight); + var pnts_clrs1 = this.subdivide(p1, colors, div, bShowArray, bHighlight); + p0 = pnts_clrs0[0]; + p1 = pnts_clrs1[0]; + colors = pnts_clrs0[2]; + } + if (p0.length < 2) return; + + this.setCalphaDrawnCoord(p0, div, calphaIdArray); + + if(bHighlight === 1) { + //mesh = new THREE.Mesh(geo, this.matShader); + + var radius = this.coilWidth / 2; + //var radiusSegments = 8; + var radiusSegments = 4; // save memory + var closed = false; + + if(positions !== undefined) { + var currPos, prevPos; + var currP0 = [], currP1 = []; + + for(var i = 0, il = p0.length; i < il; ++i) { + currPos = positions[i]; + + if((currPos !== prevPos && currPos !== prevPos + 1 && prevPos !== undefined) || (i === il -1) ) { + // first tube + var geometry0 = new THREE.TubeGeometry( + new THREE.CatmullRomCurve3(currP0), // path + currP0.length, // segments + radius, + radiusSegments, + closed + ); + + mesh = new THREE.Mesh(geometry0, this.matShader); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + + this.prevHighlightObjects.push(mesh); + + geometry0 = null; + + // second tube + var geometry1 = new THREE.TubeGeometry( + new THREE.CatmullRomCurve3(currP1), // path + currP1.length, // segments + radius, + radiusSegments, + closed + ); + + mesh = new THREE.Mesh(geometry1, this.matShader); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + + this.prevHighlightObjects.push(mesh); + + geometry1 = null; + + currP0 = []; + currP1 = []; + } + + currP0.push(p0[i]); + currP1.push(p1[i]); + + prevPos = currPos; + } + + currP0 = []; + currP1 = []; + } + else { + // first tube + var geometry0 = new THREE.TubeGeometry( + new THREE.CatmullRomCurve3(p0), // path + p0.length, // segments + radius, + radiusSegments, + closed + ); + + mesh = new THREE.Mesh(geometry0, this.matShader); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + + this.prevHighlightObjects.push(mesh); + + geometry0 = null; + + // second tube + var geometry1 = new THREE.TubeGeometry( + new THREE.CatmullRomCurve3(p1), // path + p1.length, // segments + radius, + radiusSegments, + closed + ); + + mesh = new THREE.Mesh(geometry1, this.matShader); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + + this.prevHighlightObjects.push(mesh); + + geometry1 = null; + } + } + else { + var geo = new THREE.Geometry(); + var vs = geo.vertices, fs = geo.faces; + var axis, p0v, p1v, a0v, a1v; + for (var i = 0, lim = p0.length; i < lim; ++i) { + vs.push(p0v = p0[i]); // 0 + vs.push(p0v); // 1 + vs.push(p1v = p1[i]); // 2 + vs.push(p1v); // 3 + if (i < lim - 1) { + axis = p1[i].clone().sub(p0[i]).cross(p0[i + 1].clone().sub(p0[i])).normalize().multiplyScalar(thickness); + } + vs.push(a0v = p0[i].clone().add(axis)); // 4 + vs.push(a0v); // 5 + vs.push(a1v = p1[i].clone().add(axis)); // 6 + vs.push(a1v); // 7 + } + var faces = [[0, 2, -6, -8], [-4, -2, 6, 4], [7, 3, -5, -1], [-3, -7, 1, 5]]; + + for (var i = 1, lim = p0.length, divInv = 1 / div; i < lim; ++i) { + var offset = 8 * i; + //var color = new THREE.Color(colors[Math.round((i - 1) * divInv)]); + var color = new THREE.Color(colors[i - 1]); + for (var j = 0; j < 4; ++j) { + fs.push(new THREE.Face3(offset + faces[j][0], offset + faces[j][1], offset + faces[j][2], undefined, color)); + fs.push(new THREE.Face3(offset + faces[j][3], offset + faces[j][0], offset + faces[j][2], undefined, color)); + } + } + var vsize = vs.length - 8; // Cap + for (var i = 0; i < 4; ++i) { + vs.push(vs[i * 2]); + vs.push(vs[vsize + i * 2]); + }; + vsize += 8; + fs.push(new THREE.Face3(vsize, vsize + 2, vsize + 6, undefined, fs[0].color)); + fs.push(new THREE.Face3(vsize + 4, vsize, vsize + 6, undefined, fs[0].color)); + fs.push(new THREE.Face3(vsize + 1, vsize + 5, vsize + 7, undefined, fs[fs.length - 3].color)); + fs.push(new THREE.Face3(vsize + 3, vsize + 1, vsize + 7, undefined, fs[fs.length - 3].color)); + geo.computeFaceNormals(); + geo.computeVertexNormals(false); + + var mesh; + + if(bHighlight === 2) { + mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); + + this.mdl.add(mesh); + this.prevHighlightObjects.push(mesh); + } + else { + mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); + + this.mdl.add(mesh); + this.objects.push(mesh); + } + } + + p0 = null; + p1 = null; +}; + +// significantly modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createStrand = function (atoms, num, div, fill, coilWidth, helixSheetWidth, doNotSmoothen, thickness, bHighlight) { + var bRibbon = fill ? true: false; + + // when highlight, the input atoms may only include part of sheet or helix + // include the whole sheet or helix when highlighting + var atomsAdjust = {}; + + //if( (bHighlight === 1 || bHighlight === 2) && !this.bAllAtoms) { + if( !this.bAllAtoms) { + var currChain, currResi, currAtom, prevChain, prevResi, prevAtom; + var firstAtom, lastAtom; + var index = 0, length = Object.keys(atoms).length; + + atomsAdjust = this.cloneHash(atoms); + for(var serial in atoms) { + currChain = atoms[serial].structure + '_' + atoms[serial].chain; + currResi = parseInt(atoms[serial].resi); + currAtom = atoms[serial]; + + if(prevChain === undefined) firstAtom = atoms[serial]; + + if( (currChain !== prevChain && prevChain !== undefined) || (currResi !== prevResi && currResi !== prevResi + 1 && prevResi !== undefined) || index === length - 1) { + if( (currChain !== prevChain && prevChain !== undefined) || (currResi !== prevResi && currResi !== prevResi + 1 && prevResi !== undefined) ) { +// if( (currChain !== prevChain && prevChain !== undefined) || index === length - 1) { +// if( (currChain !== prevChain && prevChain !== undefined) ) { + lastAtom = prevAtom; + } + else if(index === length - 1) { + lastAtom = currAtom; + } + + // fill the beginning + var beginResi = firstAtom.resi; + if(firstAtom.ss !== 'coil' && !(firstAtom.ssbegin) ) { + for(var i = firstAtom.resi - 1; i > 0; --i) { + var residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i; + if(!this.residues.hasOwnProperty(residueid)) break; + + var atom = this.getFirstAtomObj(this.residues[residueid]); + + if(atom.ss === firstAtom.ss && atom.ssbegin) { + beginResi = atom.resi; + break; + } + } + + for(var i = beginResi; i < firstAtom.resi; ++i) { + var residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + i; + atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); + } + } + + // add one extra residue for coils between strands/helix + if(this.pk === 3 && bHighlight === 1 && firstAtom.ss === 'coil') { + var residueid = firstAtom.structure + '_' + firstAtom.chain + '_' + (firstAtom.resi - 1); + if(this.residues.hasOwnProperty(residueid)) { + atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); + atoms = this.unionHash(atoms, this.hash2Atoms(this.residues[residueid])); + } + } + + // fill the end + var endResi = lastAtom.resi; + // when a coil connects to a sheet and the last residue of coil is highlighted, the first sheet residue is set as atom.notshow. This residue should not be shown. + if(lastAtom.ss !== 'coil' && !(lastAtom.ssend) && !(lastAtom.notshow)) { + var endChainResi = this.getLastAtomObj(this.chains[lastAtom.structure + '_' + lastAtom.chain]).resi; + for(var i = lastAtom.resi + 1; i <= endChainResi; ++i) { + var residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i; + if(!this.residues.hasOwnProperty(residueid)) break; + + var atom = this.getFirstAtomObj(this.residues[residueid]); + + if(atom.ss === lastAtom.ss && atom.ssend) { + endResi = atom.resi; + break; + } + } + + for(var i = lastAtom.resi + 1; i <= endResi; ++i) { + var residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + i; + atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); + } + } + + // add one extra residue for coils between strands/helix + if(this.pk === 3 && bHighlight === 1 && lastAtom.ss === 'coil') { + var residueid = lastAtom.structure + '_' + lastAtom.chain + '_' + (lastAtom.resi + 1); + if(this.residues.hasOwnProperty(residueid)) { + atomsAdjust = this.unionHash(atomsAdjust, this.hash2Atoms(this.residues[residueid])); + atoms = this.unionHash(atoms, this.hash2Atoms(this.residues[residueid])); + } + } + + // reset notshow + if(lastAtom.notshow) lastAtom.notshow = undefined; + + firstAtom = currAtom; + } + + prevChain = currChain; + prevResi = currResi; + prevAtom = currAtom; + + ++index; + } + } + else { + atomsAdjust = atoms; + } + + if(bHighlight === 2) { + if(fill) { + fill = false; + num = null; + div = null; + coilWidth = null; + helixSheetWidth = null; + thickness = undefined; + } + else { + fill = true; + num = 2; + div = undefined; + coilWidth = undefined; + helixSheetWidth = undefined; + thickness = this.ribbonthickness; + } + } + + num = num || this.strandDIV; + div = div || this.axisDIV; + coilWidth = coilWidth || this.coilWidth; + doNotSmoothen = doNotSmoothen || false; + helixSheetWidth = helixSheetWidth || this.helixSheetWidth; + var pnts = {}; for (var k = 0; k < num; ++k) pnts[k] = []; + var pntsCA = []; + var prevCOArray = []; + var bShowArray = []; + var calphaIdArray = []; // used to store one of the final positions drawn in 3D + var colors = []; + var currentChain, currentResi, currentCA = null, currentO = null, currentColor = null, prevCoorCA = null, prevCoorO = null, prevColor = null; + var prevCO = null, ss = null, ssend = false, atomid = null, prevAtomid = null, prevResi = null, calphaid = null, prevCalphaid = null; + var strandWidth, bSheetSegment = false, bHelixSegment = false; + var atom, tubeAtoms = {}; + + // test the first 30 atoms to see whether only C-alpha is available + this.bCalphaOnly = this.isCalphaPhosOnly(atomsAdjust, 'CA'); + + // when highlight, draw whole beta sheet and use bShowArray to show the highlight part + var residueHash = {}; + for(var i in atomsAdjust) { + var atom = atomsAdjust[i]; + + residueid = atom.structure + '_' + atom.chain + '_' + atom.resi; + residueHash[residueid] = 1; + } + var totalResidueCount = Object.keys(residueHash).length; + + var drawnResidueCount = 0; + var highlightResiduesCount = 0; + + var bFullAtom = (Object.keys(this.hAtoms).length == Object.keys(this.atoms).length) ? true : false; + + for (var i in atomsAdjust) { + atom = atomsAdjust[i]; + var atomOxygen = undefined; + if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) { + // "CA" has to appear before "O" + + if (atom.name === 'CA') { + if ( atoms.hasOwnProperty(i) && (atom.ss === 'coil' || atom.ssend || atom.ssbegin) ) { + tubeAtoms[i] = atom; + } + + currentCA = atom.coord; + currentColor = atom.color; + calphaid = atom.serial; + } + + if (atom.name === 'O' || (this.bCalphaOnly && atom.name === 'CA')) { + if(atom.name === 'O') { + currentO = atom.coord; + } + // smoothen each coil, helix and sheet separately. The joint residue has to be included both in the previous and next segment + var bSameChain = true; + if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) { +// if (currentChain !== atom.chain) { + bSameChain = false; + } + + if(atom.ssend && atom.ss === 'sheet') { + bSheetSegment = true; + } + else if(atom.ssend && atom.ss === 'helix') { + bHelixSegment = true; + } + + // assign the previous residue + if(prevCoorO) { + if(bHighlight === 1 || bHighlight === 2) { + colors.push(this.hColor); + } + else { + colors.push(prevColor); + } + + if(ss !== 'coil' && atom.ss === 'coil') { + strandWidth = coilWidth; + } + else if(ssend && atom.ssbegin) { // a transition between two ss + strandWidth = coilWidth; + } + else { + strandWidth = (ss === 'coil') ? coilWidth : helixSheetWidth; + } + + var O; + if(atom.name === 'O') { + O = prevCoorO.clone(); + if(prevCoorCA !== null && prevCoorCA !== undefined) { + O.sub(prevCoorCA); + } + else { + prevCoorCA = prevCoorO.clone(); + O = new THREE.Vector3(Math.random(),Math.random(),Math.random()); + } + } + else if(this.bCalphaOnly && atom.name === 'CA') { + O = new THREE.Vector3(Math.random(),Math.random(),Math.random()); + } + + O.normalize(); // can be omitted for performance + O.multiplyScalar(strandWidth); + if (prevCO !== null && O.dot(prevCO) < 0) O.negate(); + prevCO = O; + + for (var j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) { + var delta = -1 + numM1Inv2 * j; + var v = new THREE.Vector3(prevCoorCA.x + prevCO.x * delta, prevCoorCA.y + prevCO.y * delta, prevCoorCA.z + prevCO.z * delta); + if (!doNotSmoothen && ss === 'sheet') v.smoothen = true; + pnts[j].push(v); + } + + pntsCA.push(prevCoorCA); + prevCOArray.push(prevCO); + + if(atoms.hasOwnProperty(prevAtomid)) { + bShowArray.push(prevResi); + calphaIdArray.push(prevCalphaid); + + ++highlightResiduesCount; + } + else { + bShowArray.push(0); + calphaIdArray.push(0); + } + + ++drawnResidueCount; + } + + if ((atom.ssbegin || atom.ssend || (drawnResidueCount === totalResidueCount - 1) ) && pnts[0].length > 0 && bSameChain) { + // assign the current joint residue to the previous segment + if(bHighlight === 1 || bHighlight === 2) { + colors.push(this.hColor); + } + else { + //colors.push(atom.color); + colors.push(prevColor); + } + + if(atom.ssend && atom.ss === 'sheet') { // current residue is the end of ss and is the end of arrow + strandWidth = 0; // make the arrow end sharp + } + else if(ss === 'coil' && atom.ssbegin) { + strandWidth = coilWidth; + } + else if(ssend && atom.ssbegin) { // current residue is the start of ss and the previous residue is the end of ss, then use coil + strandWidth = coilWidth; + } + else { // use the ss from the previous residue + strandWidth = (atom.ss === 'coil') ? coilWidth : helixSheetWidth; + } + + var O; + if(atom.name === 'O') { + O = currentO.clone(); + O.sub(currentCA); + } + else if(this.bCalphaOnly && atom.name === 'CA') { + O = new THREE.Vector3(Math.random(),Math.random(),Math.random()); + } + + O.normalize(); // can be omitted for performance + O.multiplyScalar(strandWidth); + if (prevCO !== null && O.dot(prevCO) < 0) O.negate(); + prevCO = O; + + for (var j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) { + var delta = -1 + numM1Inv2 * j; + var v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta); + if (!doNotSmoothen && ss === 'sheet') v.smoothen = true; + pnts[j].push(v); + } + + atomid = atom.serial; + + pntsCA.push(currentCA); + prevCOArray.push(prevCO); + + // when a coil connects to a sheet and the last residue of coild is highlighted, the first sheet residue is set as atom.highlightStyle. This residue should not be shown. + //if(atoms.hasOwnProperty(atomid) && (bHighlight === 1 && !atom.notshow) ) { + if(atoms.hasOwnProperty(atomid)) { + bShowArray.push(atom.resi); + calphaIdArray.push(calphaid); + } + else { + bShowArray.push(0); + calphaIdArray.push(0); + } + + // draw the current segment + for (var j = 0; !fill && j < num; ++j) { + if(bSheetSegment) { + this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray); + } + else { + if(bFullAtom) { + this.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray); + } + else { + this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false); + } + } + } + if (fill) { + if(bSheetSegment) { + var start = 0, end = num - 1; + this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray); + } + else if(bHelixSegment) { + if(bFullAtom) { + this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray); + } + else { + var start = 0, end = num - 1; + this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray); + } + } + else { + if(bHighlight === 2) { // draw coils only when highlighted. if not highlighted, coils will be drawn as tubes separately + this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray); + } + } + } + for (var k = 0; k < num; ++k) pnts[k] = []; + + colors = []; + pntsCA = []; + prevCOArray = []; + bShowArray = []; + calphaIdArray = []; + bSheetSegment = false; + bHelixSegment = false; + } // end if (atom.ssbegin || atom.ssend) + + // end of a chain + if ((currentChain !== atom.chain || currentResi + 1 !== atom.resi) && pnts[0].length > 0) { +// if ((currentChain !== atom.chain) && pnts[0].length > 0) { + for (var j = 0; !fill && j < num; ++j) { + if(bSheetSegment) { + this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray); + } + else if(bHelixSegment) { + if(bFullAtom) { + this.createCurveSub(pnts[j], 1, colors, div, bHighlight, bRibbon, false, bShowArray, calphaIdArray); + } + else { + this.createCurveSubArrow(pnts[j], 1, colors, div, bHighlight, bRibbon, num, j, pntsCA, prevCOArray, bShowArray, calphaIdArray, false); + } + } + } + if (fill) { + if(bSheetSegment) { + var start = 0, end = num - 1; + this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray); + } + else if(bHelixSegment) { + if(bFullAtom) { + this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, false, bShowArray, calphaIdArray); + } + else { + var start = 0, end = num - 1; + this.createStripArrow(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight, num, start, end, pntsCA, prevCOArray, bShowArray, calphaIdArray, false); + } + } + } + + for (var k = 0; k < num; ++k) pnts[k] = []; + colors = []; + pntsCA = []; + prevCOArray = []; + bShowArray = []; + calphaIdArray = []; + bSheetSegment = false; + bHelixSegment = false; + } + + currentChain = atom.chain; + currentResi = atom.resi; + ss = atom.ss; + ssend = atom.ssend; + prevAtomid = atom.serial; + prevResi = atom.resi; + + prevCalphaid = calphaid; + + // only update when atom.name === 'O' + prevCoorCA = currentCA; + prevCoorO = atom.coord; + prevColor = currentColor; + } // end if (atom.name === 'O' || (this.bCalphaOnly && atom.name === 'CA') ) { + } // end if ((atom.name === 'O' || atom.name === 'CA') && !atom.het) { + } // end for + + this.createTube(tubeAtoms, 'CA', coilWidth, bHighlight); + + tubeAtoms = {}; + pnts = {}; +}; + +/* +iCn3D.prototype.createStrandBrick = function (brick, color, thickness, bHighlight) { + var num = this.strandDIV; + var div = this.axisDIV; + var doNotSmoothen = false; + var helixSheetWidth = this.helixSheetWidth; + + if(bHighlight === 2) { + thickness *= 1.5; + helixSheetWidth *= 1.5; + } + + var pnts = {}; for (var k = 0; k < num; ++k) pnts[k] = []; + var colors = []; + var prevCO = null, ss = null; + for (var i = 0; i < 2; ++i) { + var currentCA = brick.coords[i]; + + colors.push(new THREE.Color(color)); + + var O = new THREE.Vector3(brick.coords[2].x, brick.coords[2].y, brick.coords[2].z); + O.normalize(); + + O.multiplyScalar(helixSheetWidth); + if (prevCO !== null && O.dot(prevCO) < 0) O.negate(); + prevCO = O; + for (var j = 0, numM1Inv2 = 2 / (num - 1); j < num; ++j) { + var delta = -1 + numM1Inv2 * j; + var v = new THREE.Vector3(currentCA.x + prevCO.x * delta, currentCA.y + prevCO.y * delta, currentCA.z + prevCO.z * delta); + if (!doNotSmoothen) v.smoothen = true; + pnts[j].push(v); + } + } + this.createStrip(pnts[0], pnts[num - 1], colors, div, thickness, bHighlight); +}; +*/ + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createTubeSub = function (_pnts, colors, radii, bHighlight) { + if (_pnts.length < 2) return; + var circleDiv = this.tubeDIV, axisDiv = this.axisDIV; + var circleDivInv = 1 / circleDiv, axisDivInv = 1 / axisDiv; + var geo = new THREE.Geometry(); + var pnts_clrs = this.subdivide(_pnts, colors, axisDiv); + var pnts = pnts_clrs[0]; + colors = pnts_clrs[2]; + + var prevAxis1 = new THREE.Vector3(), prevAxis2; + for (var i = 0, lim = pnts.length; i < lim; ++i) { + var r, idx = (i - 1) * axisDivInv; + if (i === 0) r = radii[0]; + else { + if (idx % 1 === 0) r = radii[idx]; + else { + var floored = Math.floor(idx); + var tmp = idx - floored; + r = radii[floored] * tmp + radii[floored + 1] * (1 - tmp); + } + } + var delta, axis1, axis2; + if (i < lim - 1) { + delta = pnts[i].clone().sub(pnts[i + 1]); + axis1 = new THREE.Vector3(0, -delta.z, delta.y).normalize().multiplyScalar(r); + axis2 = delta.clone().cross(axis1).normalize().multiplyScalar(r); + // var dir = 1, offset = 0; + if (prevAxis1.dot(axis1) < 0) { + axis1.negate(); axis2.negate(); //dir = -1;//offset = 2 * Math.PI / axisDiv; + } + prevAxis1 = axis1; prevAxis2 = axis2; + } else { + axis1 = prevAxis1; axis2 = prevAxis2; + } + for (var j = 0; j < circleDiv; ++j) { + var angle = 2 * Math.PI * circleDivInv * j; //* dir + offset; + geo.vertices.push(pnts[i].clone().add(axis1.clone().multiplyScalar(Math.cos(angle))).add(axis2.clone().multiplyScalar(Math.sin(angle)))); + } + } + var offset = 0; + for (var i = 0, lim = pnts.length - 1; i < lim; ++i) { + //var c = new THREE.Color(colors[Math.round((i - 1) * axisDivInv)]); + var c = new THREE.Color(colors[i]); + + var reg = 0; + var r1 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv]).lengthSq(); + var r2 = geo.vertices[offset].clone().sub(geo.vertices[offset + circleDiv + 1]).lengthSq(); + if (r1 > r2) { r1 = r2; reg = 1; }; + for (var j = 0; j < circleDiv; ++j) { + geo.faces.push(new THREE.Face3(offset + j, offset + (j + reg) % circleDiv + circleDiv, offset + (j + 1) % circleDiv, undefined, c)); + geo.faces.push(new THREE.Face3(offset + (j + 1) % circleDiv, offset + (j + reg) % circleDiv + circleDiv, offset + (j + reg + 1) % circleDiv + circleDiv, undefined, c)); + } + offset += circleDiv; + } + geo.computeFaceNormals(); + geo.computeVertexNormals(false); + + var mesh; + if(bHighlight === 2) { + mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); + this.mdl.add(mesh); + } + else if(bHighlight === 1) { + mesh = new THREE.Mesh(geo, this.matShader); + mesh.renderOrder = this.renderOrderPicking; + //this.mdlPicking.add(mesh); + this.mdl.add(mesh); + } + else { + mesh = new THREE.Mesh(geo, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, vertexColors: THREE.FaceColors, side: THREE.DoubleSide })); + this.mdl.add(mesh); + } + + if(bHighlight === 1 || bHighlight === 2) { + this.prevHighlightObjects.push(mesh); + } + else { + this.objects.push(mesh); + } +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createTube = function (atoms, atomName, radius, bHighlight) { + var pnts = [], colors = [], radii = []; + var currentChain, currentResi; + var index = 0; + var prevAtom; + + for (var i in atoms) { + var atom = atoms[i]; + if ((atom.name === atomName) && !atom.het) { + if (index > 0 && (currentChain !== atom.chain || currentResi + 1 !== atom.resi || Math.abs(atom.coord.x - prevAtom.coord.x) > 6.0 || Math.abs(atom.coord.y - prevAtom.coord.y) > 6.0 || Math.abs(atom.coord.z - prevAtom.coord.z) > 6.0) ) { +// if (index > 0 && (currentChain !== atom.chain || Math.abs(atom.coord.x - prevAtom.coord.x) > 6.0 || Math.abs(atom.coord.y - prevAtom.coord.y) > 6.0 || Math.abs(atom.coord.z - prevAtom.coord.z) > 6.0) ) { + if(bHighlight !== 2) this.createTubeSub(pnts, colors, radii, bHighlight); + pnts = []; colors = []; radii = []; + } + pnts.push(atom.coord); + + radii.push(radius || (atom.b > 0 ? atom.b * 0.01 : 0.3)); + colors.push(atom.color); + + currentChain = atom.chain; + currentResi = atom.resi; + + var scale = 1.2; + if(bHighlight === 2 && !atom.ssbegin) { + this.createBox(atom, undefined, undefined, scale, undefined, bHighlight); + } + + ++index; + + prevAtom = atom; + } + } + if(bHighlight !== 2) this.createTubeSub(pnts, colors, radii, bHighlight); +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createCylinderHelix = function (atoms, radius, bHighlight) { + var start = null; + var currentChain, currentResi; + var others = {}, beta = {}; + var i; + for (i in atoms) { + var atom = atoms[i]; + if (atom.het) continue; + if ((atom.ss !== 'helix' && atom.ss !== 'sheet') || atom.ssend || atom.ssbegin) others[atom.serial] = atom; + if (atom.ss === 'sheet') beta[atom.serial] = atom; + if (atom.name !== 'CA') continue; + if (atom.ss === 'helix' && atom.ssend) { + if (start !== null && currentChain === atom.chain && currentResi < atom.resi) { + if(bHighlight === 1 || bHighlight === 2) { + this.createCylinder(start.coord, atom.coord, radius, this.hColor, bHighlight); + } + else { + this.createCylinder(start.coord, atom.coord, radius, atom.color); + } + } + + start = null; + } + + if (start === null && atom.ss === 'helix' && atom.ssbegin) { + start = atom; + + currentChain = atom.chain; + currentResi = atom.resi; + } + } + + if(bHighlight === 1 || bHighlight === 2) { + if(Object.keys(others).length > 0) this.createTube(others, 'CA', 0.3, bHighlight); + if(Object.keys(beta).length > 0) this.createStrand(beta, undefined, undefined, true, 0, this.helixSheetWidth, false, this.ribbonthickness * 2, bHighlight); + } + else { + if(Object.keys(others).length > 0) this.createTube(others, 'CA', 0.3); + if(Object.keys(beta).length > 0) this.createStrand(beta, undefined, undefined, true, 0, this.helixSheetWidth, false, this.ribbonthickness * 2); + } +}; + +// modified from GLmol (http://webglmol.osdn.jp/index-en.html) +iCn3D.prototype.drawNucleicAcidStick = function(atomlist, bHighlight) { + var currentChain, currentResi, start = null, end = null; + var i; + + for (i in atomlist) { + var atom = atomlist[i]; + if (atom === undefined || atom.het) continue; + + if (atom.resi !== currentResi || atom.chain !== currentChain) { + if (start !== null && end !== null) { + this.createCylinder(new THREE.Vector3(start.coord.x, start.coord.y, start.coord.z), + new THREE.Vector3(end.coord.x, end.coord.y, end.coord.z), this.cylinderRadius, start.color, bHighlight); + } + start = null; end = null; + } + if (atom.name === 'O3\'' || atom.name === 'O3*') start = atom; + if (atom.resn === ' A' || atom.resn === ' G' || atom.resn === ' DA' || atom.resn === ' DG') { + if (atom.name === 'N1') end = atom; // N1(AG), N3(CTU) + } else if (atom.name === 'N3') { + end = atom; + } + currentResi = atom.resi; currentChain = atom.chain; + } + if (start !== null && end !== null) + this.createCylinder(new THREE.Vector3(start.coord.x, start.coord.y, start.coord.z), + new THREE.Vector3(end.coord.x, end.coord.y, end.coord.z), this.cylinderRadius, start.color, bHighlight); +}; + +iCn3D.prototype.isCalphaPhosOnly = function(atomlist, atomname1, atomname2) { + var bCalphaPhosOnly = false; + + var index = 0, testLength = 30; + var bOtherAtoms = false; + for(var i in atomlist) { + if(index < testLength) { + if(atomlist[i].name !== atomname1 && + (atomname2 == undefined || (atomname2 != undefined && atomlist.name !== atomname2) ) + ) { + bOtherAtoms = true; + break; + } + } + else { + break; + } + + ++index; + } + + if(!bOtherAtoms) { + bCalphaPhosOnly = true; + } + + return bCalphaPhosOnly; +}; + +// modified from GLmol (http://webglmol.osdn.jp/index-en.html) +iCn3D.prototype.drawCartoonNucleicAcid = function(atomlist, div, thickness, bHighlight) { + this.drawStrandNucleicAcid(atomlist, 2, div, true, undefined, thickness, bHighlight); +}; + +// modified from GLmol (http://webglmol.osdn.jp/index-en.html) +iCn3D.prototype.drawStrandNucleicAcid = function(atomlist, num, div, fill, nucleicAcidWidth, thickness, bHighlight) { + if(bHighlight === 2) { + num = undefined; + thickness = undefined; + } + + nucleicAcidWidth = nucleicAcidWidth || this.nucleicAcidWidth; + div = div || this.axisDIV; + num = num || this.nucleicAcidStrandDIV; + var i, j, k; + var pnts = []; for (k = 0; k < num; k++) pnts[k] = []; + var colors = []; + var currentChain, currentResi, currentO3; + var prevOO = null; + + for (i in atomlist) { + var atom = atomlist[i]; + if (atom === undefined) continue; + + if ((atom.name === 'O3\'' || atom.name === 'OP2' || atom.name === 'O3*' || atom.name === 'O2P') && !atom.het) { + if (atom.name === 'O3\'' || atom.name === 'O3*') { // to connect 3' end. FIXME: better way to do? + if (currentChain !== atom.chain || currentResi + 1 !== atom.resi) { +// if (currentChain !== atom.chain) { + if (currentO3 && prevOO) { + for (j = 0; j < num; j++) { + var delta = -1 + 2 / (num - 1) * j; + pnts[j].push(new THREE.Vector3(currentO3.x + prevOO.x * delta, + currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); + } + } + if (fill) this.createStrip(pnts[0], pnts[1], colors, div, thickness, bHighlight); + for (j = 0; !thickness && j < num; j++) + this.createCurveSub(pnts[j], 1 ,colors, div, bHighlight); + var pnts = []; for (k = 0; k < num; k++) pnts[k] = []; + colors = []; + prevOO = null; + } + currentO3 = new THREE.Vector3(atom.coord.x, atom.coord.y, atom.coord.z); + currentChain = atom.chain; + currentResi = atom.resi; + if(bHighlight === 1 || bHighlight === 2) { + colors.push(this.hColor); + } + else { + colors.push(atom.color); + } + + } + else if (atom.name === 'OP2' || atom.name === 'O2P') { + if (!currentO3) {prevOO = null; continue;} // for 5' phosphate (e.g. 3QX3) + var O = new THREE.Vector3(atom.coord.x, atom.coord.y, atom.coord.z); + O.sub(currentO3); + O.normalize().multiplyScalar(nucleicAcidWidth); // TODO: refactor + //if (prevOO !== undefined && O.dot(prevOO) < 0) { + if (prevOO !== null && O.dot(prevOO) < 0) { + O.negate(); + } + prevOO = O; + for (j = 0; j < num; j++) { + var delta = -1 + 2 / (num - 1) * j; + pnts[j].push(new THREE.Vector3(currentO3.x + prevOO.x * delta, + currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); + } + currentO3 = null; + } + } + } + + if (currentO3 && prevOO) { + for (j = 0; j < num; j++) { + var delta = -1 + 2 / (num - 1) * j; + pnts[j].push(new THREE.Vector3(currentO3.x + prevOO.x * delta, + currentO3.y + prevOO.y * delta, currentO3.z + prevOO.z * delta)); + } + } + if (fill) this.createStrip(pnts[0], pnts[1], colors, div, thickness, bHighlight); + for (j = 0; !thickness && j < num; j++) + this.createCurveSub(pnts[j], 1 ,colors, div, bHighlight); +}; + +iCn3D.prototype.createSingleLine = function ( src, dst, colorHex, dashed, dashSize ) { + var geom = new THREE.Geometry(); + var mat; + + if(dashed) { + mat = new THREE.LineDashedMaterial({ linewidth: 1, color: colorHex, dashSize: dashSize, gapSize: 0.5*dashSize }); + } else { + mat = new THREE.LineBasicMaterial({ linewidth: 1, color: colorHex }); + } + + geom.vertices.push( src ); + geom.vertices.push( dst ); + if(dashed) geom.computeLineDistances(); // This one is SUPER important, otherwise dashed lines will appear as simple plain lines + + var axis = new THREE.Line( geom, mat, THREE.LinePieces ); + + return axis; +}; + +// used for highlight +iCn3D.prototype.createBox = function (atom, defaultRadius, forceDefault, scale, color, bHighlight) { + var mesh; + + if(defaultRadius === undefined) defaultRadius = 0.8; + if(forceDefault === undefined) forceDefault = false; + if(scale === undefined) scale = 0.8; + + if(bHighlight) { + if(color === undefined) color = this.hColor; + + mesh = new THREE.Mesh(this.boxGeometry, new THREE.MeshPhongMaterial({ transparent: true, opacity: 0.5, overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); + } + else { + if(color === undefined) color = atom.color; + + mesh = new THREE.Mesh(this.boxGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); + } + + mesh.scale.x = mesh.scale.y = mesh.scale.z = forceDefault ? defaultRadius : (this.vdwRadii[atom.elem] || defaultRadius) * (scale ? scale : 1); + mesh.position.copy(atom.coord); + this.mdl.add(mesh); + + if(bHighlight) { + this.prevHighlightObjects.push(mesh); + } + else { + this.objects.push(mesh); + } +}; + +// modified from 3Dmol (http://3dmol.csb.pitt.edu/) +// new: http://stackoverflow.com/questions/23514274/three-js-2d-text-sprite-labels +// old: http://stemkoski.github.io/Three.js/Sprite-Text-Labels.html +iCn3D.prototype.makeTextSprite = function ( message, parameters ) { + + if ( parameters === undefined ) parameters = {}; + var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "Arial"; + var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 18; + + var a = parameters.hasOwnProperty("alpha") ? parameters["alpha"] : 1.0; + + var bBkgd = true; + var bSchematic = false; + if(parameters.hasOwnProperty("bSchematic") && parameters["bSchematic"]) { + bSchematic = true; + + fontsize = 40; + } + + var backgroundColor, borderColor, borderThickness; + if(parameters.hasOwnProperty("backgroundColor") && parameters["backgroundColor"] !== undefined) { + backgroundColor = this.hexToRgb(parameters["backgroundColor"], a); + + borderColor = parameters.hasOwnProperty("borderColor") ? this.hexToRgb(parameters["borderColor"], a) : { r:0, g:0, b:0, a:1.0 }; + borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 4; + } + else { + bBkgd = false; + backgroundColor = undefined; + borderColor = undefined; + borderThickness = 0; + } + + var textAlpha = 1.0; + var textColor = parameters.hasOwnProperty("textColor") && parameters["textColor"] !== undefined ? this.hexToRgb(parameters["textColor"], textAlpha) : { r:255, g:255, b:0, a:1.0 }; + + var canvas = document.createElement('canvas'); + + var context = canvas.getContext('2d'); + + context.font = "Bold " + fontsize + "px " + fontface; + + var metrics = context.measureText( message ); + + var textWidth = metrics.width; + + var width = textWidth + 2*borderThickness; + var height = fontsize + 2*borderThickness; + + if(bSchematic) { + if(width > height) { + height = width; + } + else { + width = height; + } + } + + //var factor = (bSchematic) ? 3 * this.oriMaxD / 100 : 3 * this.oriMaxD / 100; + var factor = (bSchematic) ? 3 * this.maxD / 100 : 3 * this.maxD / 100; + + var expandWidthFactor = 0.8 * textWidth / height; + +/* + // define width and height will make long text be squashed, but make the label to appear at the exact location + if(bSchematic || message.length <= textLengthThreshold) { + canvas.width = width; + canvas.height = height; + + factor = 3 * this.oriMaxD / 100; + } +*/ + + canvas.width = width; + canvas.height = height; + + context.clearRect(0, 0, width, height); + + //var radius = context.measureText( "M" ).width; + + if(bBkgd) { + // background color + context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," + backgroundColor.a + ")"; + // border color + context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")"; + + context.lineWidth = borderThickness; + + if(bSchematic) { + var r = width * 0.35; + this.circle(context, 0, 0, width, height, r); + } + else { + //var r = (message.length <= textLengthThreshold) ? height * 0.5 : 0; + //var r = height * 0.8; + var r = 0; + this.roundRect(context, 0, 0, width, height, r); + } + } + + // need to redefine again + context.font = "Bold " + fontsize + "px " + fontface; + + context.textAlign = "center"; + context.textBaseline = "middle"; + + context.fillStyle = "rgba("+textColor.r+", "+textColor.g+", "+textColor.b+", 1.0)"; + context.strokeStyle = "rgba("+textColor.r+", "+textColor.g+", "+textColor.b+", 1.0)"; + + context.fillText( message, width * 0.5, height * 0.5); + + // canvas contents will be used for a texture + var texture = new THREE.Texture(canvas) + texture.needsUpdate = true; + + var frontOfTarget = true; + //var spriteMaterial = new THREE.SpriteMaterial( { map: texture, useScreenCoordinates: false } ); + var spriteMaterial = new THREE.SpriteMaterial( { + map: texture, + //useScreenCoordinates: false, + depthTest: !frontOfTarget, + depthWrite: !frontOfTarget + } ); + + //https://stackoverflow.com/questions/29421702/threejs-texture + spriteMaterial.map.minFilter = THREE.LinearFilter; + + var sprite = new THREE.Sprite( spriteMaterial ); + + if(bSchematic) { + sprite.scale.set(factor, factor, 1.0); + } + else { + sprite.scale.set(expandWidthFactor * factor, factor, 1.0); + } + + return sprite; +}; + +// function for drawing rounded rectangles +iCn3D.prototype.roundRect = function (ctx, x, y, w, h, r) { + ctx.beginPath(); + ctx.moveTo(x+r, y); + ctx.lineTo(x+w-r, y); + ctx.quadraticCurveTo(x+w, y, x+w, y+r); + ctx.lineTo(x+w, y+h-r); + ctx.quadraticCurveTo(x+w, y+h, x+w-r, y+h); + ctx.lineTo(x+r, y+h); + ctx.quadraticCurveTo(x, y+h, x, y+h-r); + ctx.lineTo(x, y+r); + ctx.quadraticCurveTo(x, y, x+r, y); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); +}; + +iCn3D.prototype.circle = function (ctx, x, y, w, h, r) { + ctx.beginPath(); + ctx.arc(x+w/2, y+h/2, r, 0, 2*Math.PI, true); + ctx.closePath(); + ctx.fill(); + ctx.stroke(); +}; + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createLabelRepresentation = function (labels) { + var tmpMaxD = this.maxD; + + for(var name in labels) { + var labelArray = (labels[name] !== undefined) ? labels[name] : []; + + for (var i = 0, il = labelArray.length; i < il; ++i) { + var label = labelArray[i]; + // make sure fontsize is a number + + if(label.size == 0) label.size = undefined; + if(label.color == 0) label.color = undefined; + if(label.background == 0) label.background = undefined; + + var labelsize = (label.size !== undefined) ? label.size : this.LABELSIZE; + var labelcolor = (label.color !== undefined) ? label.color : '#ffff00'; + var labelbackground = (label.background !== undefined) ? label.background : '#cccccc'; + var labelalpha = (label.alpha !== undefined) ? label.alpha : 1.0; + // if label.background is undefined, no background will be drawn + labelbackground = label.background; + + if(labelcolor !== undefined && labelbackground !== undefined && labelcolor.toLowerCase() === labelbackground.toLowerCase()) { + labelcolor = "#888888"; + } + + var bChemicalInProteinOrTrace = false; + var bStick = false; + if(Object.keys(this.proteins).length + Object.keys(this.nucleotides).length > 0) { + var firstAtom = this.getFirstAtomObj(this.hAtoms); + if(firstAtom !== undefined) { + if(this.chemicals.hasOwnProperty(firstAtom.serial) + || firstAtom.style == 'c alpha trace' || firstAtom.style == 'o3 trace' + //|| firstAtom.style == 'schematic' + ) { + bChemicalInProteinOrTrace = true; + } + + if(firstAtom.style == 'stick' || firstAtom.style == 'ball and stick') { + bStick = true; + } + } + } + + // somehow the transformation is not stable when reset camera +/* + if(bChemicalInProteinOrTrace) { + this.maxD = 15; // 50 + this.setCamera(); + } + else if(bStick) { + this.maxD = 30; // 50 + this.setCamera(); + } + else { + if(Object.keys(this.proteins).length + Object.keys(this.nucleotides).length > 0) { + this.maxD = 100; + this.setCamera(); + } + else { + this.maxD = 15; + this.setCamera(); + } + } +*/ + + var bb; + if(label.bSchematic !== undefined && label.bSchematic) { + + bb = this.makeTextSprite(label.text, {fontsize: parseInt(labelsize), textColor: labelcolor, borderColor: labelbackground, backgroundColor: labelbackground, alpha: labelalpha, bSchematic: 1}); + } + else { + if(label.text.length === 1) { + bb = this.makeTextSprite(label.text, {fontsize: parseInt(labelsize), textColor: labelcolor, borderColor: labelbackground, backgroundColor: labelbackground, alpha: labelalpha, bSchematic: 1}); + } + else { + bb = this.makeTextSprite(label.text, {fontsize: parseInt(labelsize), textColor: labelcolor, borderColor: labelbackground, backgroundColor: labelbackground, alpha: labelalpha, bSchematic: 0}); + } + } + + bb.position.set(label.position.x, label.position.y, label.position.z); + this.mdl.add(bb); + // do not add labels to objects for pk + } + } + + // somehow the transformation is not stable when reset camera +// this.maxD = tmpMaxD; +// this.setCamera(); +}; + diff --git a/src/icn3d/draw/drawing_full.js b/src/icn3d/draw/drawing_full.js new file mode 100644 index 00000000..f668ec8a --- /dev/null +++ b/src/icn3d/draw/drawing_full.js @@ -0,0 +1,179 @@ +/** + * @author Jiyao Wang / https://github.com/ncbi/icn3d + */ + +// modified from iview (http://istar.cse.cuhk.edu.hk/iview/) +iCn3D.prototype.createSurfaceRepresentation = function (atoms, type, wireframe, opacity) { + if(Object.keys(atoms).length == 0) return; + + var geo; + + var extent = this.getExtent(atoms); + + // surface from 3Dmol + var distance = 5; // consider atom 5 angstrom from the selected atoms + + var extendedAtoms = []; + + if(this.bConsiderNeighbors) { + extendedAtoms = Object.keys(this.unionHash(atoms, this.getAtomsWithinAtom(this.atoms, atoms, distance))); + } + else { + extendedAtoms = Object.keys(atoms); + } + + var ps = $3Dmol.SetupSurface({ + extent: extent, + allatoms: this.atoms, + atomsToShow: Object.keys(atoms), + extendedAtoms: extendedAtoms, + type: type + }); + + var verts = ps.vertices; + var faces = ps.faces; + + var me = this; + + geo = new THREE.Geometry(); + geo.vertices = verts.map(function (v) { + var r = new THREE.Vector3(v.x, v.y, v.z); + r.atomid = v.atomid; + return r; + }); + geo.faces = faces.map(function (f) { + //return new THREE.Face3(f.a, f.b, f.c); + var vertexColors = ['a', 'b', 'c' ].map(function (d) { + var atomid = geo.vertices[f[d]].atomid; + return me.atoms[atomid].color; + }); + + return new THREE.Face3(f.a, f.b, f.c, undefined, vertexColors); + }); + + // remove the reference + ps = null; + verts = null; + faces = null; + + geo.computeFaceNormals(); + geo.computeVertexNormals(false); + + geo.colorsNeedUpdate = true; + +/* + geo.faces.forEach(function (f) { + f.vertexColors = ['a', 'b', 'c' ].map(function (d) { + var atomid = geo.vertices[f[d]].atomid; + return me.atoms[atomid].color; + }); + }); +*/ + + geo.type = 'Surface'; // to be recognized in vrml.js for 3D printing + + var mesh = new THREE.Mesh(geo, new THREE.MeshLambertMaterial({ overdraw: me.overdraw, + vertexColors: THREE.VertexColors, + wireframe: wireframe, + opacity: opacity, + transparent: true, + })); + me.mdl.add(mesh); + + this.prevSurfaces.push(mesh); + + // remove the reference + geo = null; + + // do not add surface to raycasting objects for pk +}; + +// http://soledadpenades.com/articles/three-js-tutorials/drawing-the-coordinate-axes/ +iCn3D.prototype.buildAxes = function (radius) { + var axes = new THREE.Object3D(); + + axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0 + radius, 0, 0 ), 0xFF0000, false, 0.5 ) ); // +X + axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0 - radius, 0, 0 ), 0x800000, true, 0.5) ); // -X + + axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0 + radius, 0 ), 0x00FF00, false, 0.5 ) ); // +Y + axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0 - radius, 0 ), 0x008000, true, 0.5 ) ); // -Y + + axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 0 + radius ), 0x0000FF, false, 0.5 ) ); // +Z + axes.add( this.createSingleLine( new THREE.Vector3( 0, 0, 0 ), new THREE.Vector3( 0, 0, 0 - radius ), 0x000080, true, 0.5 ) ); // -Z + + this.scene.add( axes ); +}; + +iCn3D.prototype.createLines = function(lines) { // show extra lines, not used for pk, so no this.objects + if(lines !== undefined) { + for(var name in lines) { + var lineArray = lines[name]; + + for(var i = 0, il = lineArray.length; i < il; ++i) { + var line = lineArray[i]; + + var p1 = line.position1; + var p2 = line.position2; + + var dashed = (line.dashed) ? line.dashed : false; + var dashSize = 0.3; + + //this.mdl.add(this.createSingleLine( p1, p2, colorHex, dashed, dashSize )); + + var radius = this.lineRadius; + + var colorStr = '#' + line.color.replace(/\#/g, ''); + + var color = new THREE.Color(colorStr); + + if(!dashed) { + if(name == 'stabilizer') { + this.createBrick(p1, p2, radius, color); + } + else { + this.createCylinder(p1, p2, radius, color); + } + } + else { + var distance = p1.distanceTo(p2); + + var nsteps = parseInt(distance / dashSize); + var step = p2.clone().sub(p1).multiplyScalar(dashSize/distance); + + var start, end; + for(var j = 0; j < nsteps; ++j) { + if(j % 2 == 1) { + start = p1.clone().add(step.clone().multiplyScalar(j)); + end = p1.clone().add(step.clone().multiplyScalar(j + 1)); + + if(name == 'stabilizer') { + this.createBrick(start, end, radius, color); + } + else { + this.createCylinder(start, end, radius, color); + } + } + } + } + } + } + } + + // do not add the artificial lines to raycasting objects +}; + +iCn3D.prototype.createBrick = function (p0, p1, radius, color) { + var cylinderGeometry = new THREE.CylinderGeometry(1, 1, 1, 4, 1); + + var mesh = new THREE.Mesh(cylinderGeometry, new THREE.MeshPhongMaterial({ overdraw: this.overdraw, specular: this.frac, shininess: 30, emissive: 0x000000, color: color })); + + mesh.position.copy(p0).add(p1).multiplyScalar(0.5); + mesh.matrixAutoUpdate = false; + mesh.lookAt(p0); + mesh.updateMatrix(); + + mesh.matrix.multiply(new THREE.Matrix4().makeScale(radius, radius, p0.distanceTo(p1))).multiply(new THREE.Matrix4().makeRotationX(Math.PI * 0.5)); + + this.mdl.add(mesh); +}; + diff --git a/src/icn3d/draw/impostor.js b/src/icn3d/draw/impostor.js new file mode 100644 index 00000000..82ed8a43 --- /dev/null +++ b/src/icn3d/draw/impostor.js @@ -0,0 +1,347 @@ +iCn3D.prototype.setParametersForShader = function () { var me = this; + var modelViewMatrix = new THREE.Uniform( new THREE.Matrix4() ) + .onUpdate( function( object ){ + this.value.copy( object.modelViewMatrix ); + } ); + + var modelViewMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) + .onUpdate( function( object ){ + this.value.getInverse( object.modelViewMatrix ); + } ); + + var modelViewMatrixInverseTranspose = new THREE.Uniform( new THREE.Matrix4() ) + .onUpdate( function( object ){ + this.value.getInverse( object.modelViewMatrix ).transpose(); + } ); + + var modelViewProjectionMatrix = new THREE.Uniform( new THREE.Matrix4() ) + .onUpdate( function( object ){ + this.value.multiplyMatrices( me.cam.projectionMatrix, object.modelViewMatrix ); + } ); + + var modelViewProjectionMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) + .onUpdate( function( object ){ + var tmpMatrix = new THREE.Matrix4(); + tmpMatrix.multiplyMatrices(me.cam.projectionMatrix, object.modelViewMatrix); + this.value.getInverse(tmpMatrix); + } ); + + var projectionMatrix = new THREE.Uniform( new THREE.Matrix4() ) + .onUpdate( function( ){ + this.value.copy( me.cam.projectionMatrix ); + } ); + + var projectionMatrixInverse = new THREE.Uniform( new THREE.Matrix4() ) + .onUpdate( function( ){ + this.value.getInverse( me.cam.projectionMatrix ); + } ); + + var background = this.backgroundColors[this.opts.background.toLowerCase()]; + var near = 2 * this.maxD; + //var far = 2.5 * this.maxD; + var far = 3 * this.maxD; + + this.uniforms = THREE.UniformsUtils.merge([ + THREE.UniformsLib.common, + { + modelViewMatrix: modelViewMatrix, + modelViewMatrixInverse: modelViewMatrixInverse, + modelViewMatrixInverseTranspose: modelViewMatrixInverseTranspose, + modelViewProjectionMatrix: modelViewProjectionMatrix, + modelViewProjectionMatrixInverse: modelViewProjectionMatrixInverse, + projectionMatrix: projectionMatrix, + projectionMatrixInverse: projectionMatrixInverse, + //ambientLightColor: { type: "v3", value: [0.25, 0.25, 0.25] }, + diffuse: { type: "v3", value: [1.0, 1.0, 1.0] }, + emissive: { type: "v3", value: [0.0,0.0,0.0] }, + roughness: { type: "f", value: 0.5 }, // 0.4 + metalness: { type: "f", value: 0.3 }, // 0.5 + opacity: { type: "f", value: 1.0 }, + nearClip: { type: "f", value: 0.1 }, + ortho: { type: "f", value: 0.0 }, + shrink: { type: "f", value: 0.13 }, + fogColor: { type: "v3", value: [background.r, background.g, background.b] }, + fogNear: { type: "f", value: near }, + fogFar: { type: "f", value: far }, + fogDensity: { type: "f", value: 2.0 } + }, + THREE.UniformsLib.ambient, + THREE.UniformsLib.lights + ]); + + /* + //fog_pars_fragment + #ifdef USE_FOG + uniform vec3 fogColor; + #ifdef FOG_EXP2 + uniform float fogDensity; + #else + uniform float fogNear; + uniform float fogFar; + #endif + #endif + */ + + this.defines = { + USE_COLOR: 1, + //PICKING: 1, + NEAR_CLIP: 1, + CAP: 1 + }; + + if(this.opts['fog'] === 'yes') { + this.defines['USE_FOG'] = 1; + if(this.opts['camera'] === 'orthographic') { + this.defines['FOG_EXP2'] = 1; + } + } + + if(this.bExtFragDepth) { + this.defines['USE_LOGDEPTHBUF_EXT'] = 1; + } +}; + +iCn3D.prototype.drawImpostorShader = function () { var me = this; + this.setParametersForShader(); + + this.createImpostorShaderSphere("SphereImpostor"); + this.createImpostorShaderCylinder("CylinderImpostor"); + //this.createImpostorShaderCylinder("HyperballStickImpostor"); +}; + +iCn3D.prototype.getShader = function (name) { var me = this; + var shaderText = $NGL_shaderTextHash[name]; + var reInclude = /#include\s+(\S+)/gmi; + + shaderText = shaderText.replace( reInclude, function( match, p1 ){ + + var chunk; + if(THREE.ShaderChunk.hasOwnProperty(p1)) { + chunk = THREE.ShaderChunk[ p1 ]; + } + + return chunk ? chunk : ""; + + } ); + + return shaderText; +}; + +iCn3D.prototype.createImpostorShaderBase = function (shaderName, mapping, mappingIndices, data, attributeData, count, mappingSize, mappingIndicesSize, mappingItemSize) { var me = this; + var shaderMaterial = + new THREE.ShaderMaterial({ + defines: me.defines, + uniforms: me.uniforms, + vertexShader: me.getShader(shaderName + ".vert"), + fragmentShader: me.getShader(shaderName + ".frag"), + depthTest: true, + depthWrite: true, + needsUpdate: true, + lights: true + }); + + shaderMaterial.extensions.fragDepth = true; + + //MappedBuffer + var attributeSize = count * mappingSize; + + var n = count * mappingIndicesSize; + var TypedArray = attributeSize > 65535 ? Uint32Array : Uint16Array; + var index = new TypedArray( n ); + + //makeIndex(); + var ix, it; + + for( var v = 0; v < count; v++ ) { + ix = v * mappingIndicesSize; + it = v * mappingSize; + + index.set( mappingIndices, ix ); + + for( var s = 0; s < mappingIndicesSize; ++s ){ + index[ ix + s ] += it; + } + } + + + var geometry = new THREE.BufferGeometry(); + + // buffer.js + var dynamic = true; + + if( index ){ + geometry.setIndex( + new THREE.BufferAttribute( index, 1 ) + ); + geometry.getIndex().setDynamic( dynamic ); + } + + // add attributes from buffer.js + var itemSize = { + "f": 1, "v2": 2, "v3": 3, "c": 3 + }; + + for( var name in attributeData ){ + + var buf; + var a = attributeData[ name ]; + + buf = new Float32Array( + attributeSize * itemSize[ a.type ] + ); + + geometry.addAttribute( + name, + new THREE.BufferAttribute( buf, itemSize[ a.type ] ) + .setDynamic( dynamic ) + ); + + } + + // set attributes from mapped-buffer.js + var attributes = geometry.attributes; + + var a, d, itemSize2, array, i, j; + + for( var name in data ){ + + d = data[ name ]; + a = attributes[ name ]; + itemSize2 = a.itemSize; + array = a.array; + + for( var k = 0; k < count; ++k ) { + + n = k * itemSize2; + i = n * mappingSize; + + for( var l = 0; l < mappingSize; ++l ) { + + j = i + ( itemSize2 * l ); + + for( var m = 0; m < itemSize2; ++m ) { + + array[ j + m ] = d[ n + m ]; + + } + + } + + } + + a.needsUpdate = true; + + } + + // makemapping + var aMapping = geometry.attributes.mapping.array; + + for( var v = 0; v < count; v++ ) { + aMapping.set( mapping, v * mappingItemSize * mappingSize ); + } + + var mesh = new THREE.Mesh(geometry, shaderMaterial); + + // important: https://stackoverflow.com/questions/21184061/mesh-suddenly-disappears-in-three-js-clipping + // You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU + mesh.frustumCulled = false; + + mesh.scale.x = mesh.scale.y = mesh.scale.z = 1.0; + + this.mdlImpostor.add(mesh); + + //this.objects.push(mesh); +}; + +iCn3D.prototype.createImpostorShaderCylinder = function (shaderName) { var me = this; + var positions = new Float32Array( me.posArray ); + var colors = new Float32Array( me.colorArray ); + var positions2 = new Float32Array( me.pos2Array ); + var colors2 = new Float32Array( me.color2Array ); + var radii = new Float32Array( me.radiusArray ); + + // cylinder + var mapping = new Float32Array([ + -1.0, 1.0, -1.0, + -1.0, -1.0, -1.0, + 1.0, 1.0, -1.0, + 1.0, 1.0, 1.0, + 1.0, -1.0, -1.0, + 1.0, -1.0, 1.0 + ]); + + var mappingIndices = new Uint16Array([ + 0, 1, 2, + 1, 4, 2, + 2, 4, 3, + 4, 5, 3 + ]); + + var mappingIndicesSize = 12; + var mappingType = "v3"; + var mappingSize = 6; + var mappingItemSize = 3; + + + var count = positions.length / 3; + + var data = { + "position1": positions, + "color": colors, + "position2": positions2, + "color2": colors2, + "radius": radii + }; + + var attributeData = { + "position1": { type: "v3", value: null }, + "color": { type: "v3", value: null }, + "position2": { type: "v3", value: null }, + "color2": { type: "v3", value: null }, + "radius": { type: "f", value: null }, + "mapping": { type: mappingType, value: null } + }; + + this.createImpostorShaderBase(shaderName, mapping, mappingIndices, data, attributeData, count, mappingSize, mappingIndicesSize, mappingItemSize); +}; + +iCn3D.prototype.createImpostorShaderSphere = function (shaderName) { var me = this; + var positions = new Float32Array( me.posArraySphere ); + var colors = new Float32Array( me.colorArraySphere ); + var radii = new Float32Array( me.radiusArraySphere ); + + // sphere + var mapping = new Float32Array([ + -1.0, 1.0, + -1.0, -1.0, + 1.0, 1.0, + 1.0, -1.0 + ]); + + var mappingIndices = new Uint16Array([ + 0, 1, 2, + 1, 3, 2 + ]); + + var mappingIndicesSize = 6; + var mappingType = "v2"; + var mappingSize = 4; + var mappingItemSize = 2; + + var count = positions.length / 3; + + var data = { + "position": positions, + "color": colors, + "radius": radii + }; + + var attributeData = { + "position": { type: "v3", value: null }, + "color": { type: "v3", value: null }, + "radius": { type: "f", value: null }, + "mapping": { type: mappingType, value: null } + }; + + this.createImpostorShaderBase(shaderName, mapping, mappingIndices, data, attributeData, count, mappingSize, mappingIndicesSize, mappingItemSize); +}; diff --git a/src/icn3d/draw/instancing.js b/src/icn3d/draw/instancing.js new file mode 100644 index 00000000..c4e80455 --- /dev/null +++ b/src/icn3d/draw/instancing.js @@ -0,0 +1,463 @@ +iCn3D.prototype.positionFromGeometry = function( mesh ){ + var geometry = mesh.geometry; + + var vertices = geometry.vertices; + + var meshPosition = mesh.position; + var scale = mesh.scale; + var matrix = mesh.matrix; + + var j, v3; + var n = vertices.length; + //var position = new Float32Array( n * 3 ); + var position = []; + + for( var v = 0; v < n; v++ ){ + + j = v * 3; + + if(geometry.type == 'SphereGeometry' || geometry.type == 'BoxGeometry') { + v3 = vertices[v].clone().multiply(scale).add(meshPosition); + } + else if(geometry.type == 'CylinderGeometry') { + v3 = vertices[v].clone().applyMatrix4(matrix); + } + else { + v3 = vertices[v]; + } + + position[ j + 0 ] = v3.x; + position[ j + 1 ] = v3.y; + position[ j + 2 ] = v3.z; + } + + return position; + +}; + + +iCn3D.prototype.colorFromGeometry = function( mesh ){ + var geometry = mesh.geometry; + + var meshColor = new THREE.Color(1, 1, 1); + if(geometry.type == 'SphereGeometry' || geometry.type == 'BoxGeometry' || geometry.type == 'CylinderGeometry') { + if(mesh.material !== undefined) meshColor = mesh.material.color; + } + + var faces = geometry.faces; + var vn = geometry.vertices.length; + + var bSurfaceVertex = (geometry.type == 'Surface') ? true : false; + + var j, f, c1, c2, c3; + var n = faces.length; + //var color = new Float32Array( vn * 3 ); + var color = []; + + for( var v = 0; v < n; v++ ){ + + f = faces[ v ]; + + if(geometry.type == 'Surface') { + c1 = f.vertexColors[0]; + c2 = f.vertexColors[1]; + c3 = f.vertexColors[2]; + } + else if(geometry.type == 'SphereGeometry' || geometry.type == 'BoxGeometry' || geometry.type == 'CylinderGeometry') { + c1 = meshColor; + c2 = meshColor; + c3 = meshColor; + } + else { + c1 = f.color; + c2 = f.color; + c3 = f.color; + } + + j = f.a * 3; + color[ j + 0 ] = c1.r; + color[ j + 1 ] = c1.g; + color[ j + 2 ] = c1.b; + + j = f.b * 3; + color[ j + 0 ] = c2.r; + color[ j + 1 ] = c2.g; + color[ j + 2 ] = c2.b; + + j = f.c * 3; + color[ j + 0 ] = c3.r; + color[ j + 1 ] = c3.g; + color[ j + 2 ] = c3.b; + + } + + return color; + +}; + + +iCn3D.prototype.indexFromGeometry = function( mesh ){ + var geometry = mesh.geometry; + + var faces = geometry.faces; + + var j, f; + var n = faces.length; + //var TypedArray = n * 3 > 65535 ? Uint32Array : Uint16Array; + //var index = new TypedArray( n * 3 ); + var index = []; + + for( var v = 0; v < n; v++ ){ + + j = v * 3; + f = faces[ v ]; + + index[ j + 0 ] = f.a; + index[ j + 1 ] = f.b; + index[ j + 2 ] = f.c; + + } + + return index; + +}; + + +iCn3D.prototype.normalFromGeometry = function( mesh ){ + var geometry = mesh.geometry; + + var faces = geometry.faces; + var vn = geometry.vertices.length; + + var j, f, nn, n1, n2, n3; + var n = faces.length; + //var normal = new Float32Array( vn * 3 ); + var normal = []; + + for( var v = 0; v < n; v++ ){ + + f = faces[ v ]; + nn = f.vertexNormals; + n1 = nn[ 0 ]; + n2 = nn[ 1 ]; + n3 = nn[ 2 ]; + + j = f.a * 3; + normal[ j + 0 ] = n1.x; + normal[ j + 1 ] = n1.y; + normal[ j + 2 ] = n1.z; + + j = f.b * 3; + normal[ j + 0 ] = n2.x; + normal[ j + 1 ] = n2.y; + normal[ j + 2 ] = n2.z; + + j = f.c * 3; + normal[ j + 0 ] = n3.x; + normal[ j + 1 ] = n3.y; + normal[ j + 2 ] = n3.z; + + } + + return normal; + +}; + +iCn3D.prototype.hashvalue2array = function(hash) { + return $.map(hash, function(v) { return v; }); +}; + +iCn3D.prototype.drawSymmetryMates = function() { + if(this.bInstanced) { + this.drawSymmetryMatesInstancing(); + } + else { + this.drawSymmetryMatesNoInstancing(); + } +} + +iCn3D.prototype.drawSymmetryMatesNoInstancing = function() { + if (this.biomtMatrices === undefined || this.biomtMatrices.length == 0) return; + var cnt = 1; // itself + var centerSum = this.center.clone(); + + for (var i = 0; i < this.biomtMatrices.length; i++) { // skip itself + var mat = this.biomtMatrices[i]; + if (mat === undefined) continue; + + var matArray = mat.toArray(); + + // skip itself + var bItself = 1; + for(var j = 0, jl = matArray.length; j < jl; ++j) { + if(j == 0 || j == 5 || j == 10) { + if(parseInt(1000*matArray[j]) != 1000) bItself = 0; + } + else if(j != 0 && j != 5 && j != 10 && j != 15) { + if(parseInt(1000*matArray[j]) != 0) bItself = 0; + } + } + + if(bItself) continue; + + var symmetryMate = this.mdl.clone(); + symmetryMate.applyMatrix(mat); + + this.mdl.add(symmetryMate); + + symmetryMate = this.mdlImpostor.clone(); + symmetryMate.applyMatrix(mat); + + this.mdlImpostor.add(symmetryMate); + + //symmetryMate = this.mdlPicking.clone(); + //symmetryMate.applyMatrix(mat); + + //this.mdlPicking.add(symmetryMate); + + symmetryMate = this.mdl_ghost.clone(); + symmetryMate.applyMatrix(mat); + + this.mdl_ghost.add(symmetryMate); + + var center = this.center.clone(); + center.applyMatrix4(mat); + centerSum.add(center); + + ++cnt; + } + + this.maxD *= Math.sqrt(cnt); + //this.center = centerSum.multiplyScalar(1.0 / cnt); + + // recenter the mdl* caused the impostor shifted somehow!!! disable the centering for now +// this.mdl.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); +// this.mdlImpostor.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); + //this.mdlPicking.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); + +// this.mdl_ghost.position.add(this.center).sub(centerSum.multiplyScalar(1.0 / cnt)); + + // reset cameara + this.setCamera(); +}; + +iCn3D.prototype.createInstancedGeometry = function(mesh) { + var baseGeometry = mesh.geometry; + + var geometry = new THREE.InstancedBufferGeometry(); + + var positionArray = []; + var normalArray = []; + var colorArray = []; + var indexArray = []; + + var radiusArray = []; + var mappingArray = []; + var position2Array = []; + var color2Array = []; + + if( baseGeometry.vertices && baseGeometry.faces ){ + this.instancedMaterial = this.getInstancedMaterial('Instancing'); + + positionArray = positionArray.concat(this.positionFromGeometry( mesh )); + normalArray = normalArray.concat(this.normalFromGeometry( mesh )); + colorArray = colorArray.concat(this.colorFromGeometry( mesh )); + indexArray = indexArray.concat(this.indexFromGeometry( mesh )); + + geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positionArray), 3)); + geometry.addAttribute('normal', new THREE.BufferAttribute(new Float32Array(normalArray), 3) ); + geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), 3) ); + geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1)); + } + else if(baseGeometry.attributes.color2 !== undefined) { // cylinder + this.instancedMaterial = this.getInstancedMaterial('CylinderInstancing'); + + positionArray = positionArray.concat(this.hashvalue2array(baseGeometry.attributes.position1.array)); + colorArray = colorArray.concat(this.hashvalue2array(baseGeometry.attributes.color.array)); + + position2Array = position2Array.concat(this.hashvalue2array(baseGeometry.attributes.position2.array)); + color2Array = color2Array.concat(this.hashvalue2array(baseGeometry.attributes.color2.array)); + + indexArray = indexArray.concat(this.hashvalue2array(baseGeometry.index.array)); + radiusArray = radiusArray.concat(this.hashvalue2array(baseGeometry.attributes.radius.array)); + mappingArray = mappingArray.concat(this.hashvalue2array(baseGeometry.attributes.mapping.array)); + + geometry.addAttribute('position1', new THREE.BufferAttribute(new Float32Array(positionArray), 3)); + geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), 3) ); + + geometry.addAttribute('position2', new THREE.BufferAttribute(new Float32Array(position2Array), 3)); + geometry.addAttribute('color2', new THREE.BufferAttribute(new Float32Array(color2Array), 3) ); + + geometry.addAttribute('radius', new THREE.BufferAttribute(new Float32Array(radiusArray), 1) ); + geometry.addAttribute('mapping', new THREE.BufferAttribute(new Float32Array(mappingArray), 3) ); + geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1)); + } + else if(baseGeometry.attributes.color !== undefined) { // sphere + this.instancedMaterial = this.getInstancedMaterial('SphereInstancing'); + + positionArray = positionArray.concat(this.hashvalue2array(baseGeometry.attributes.position.array)); + colorArray = colorArray.concat(this.hashvalue2array(baseGeometry.attributes.color.array)); + indexArray = indexArray.concat(this.hashvalue2array(baseGeometry.index.array)); + radiusArray = radiusArray.concat(this.hashvalue2array(baseGeometry.attributes.radius.array)); + mappingArray = mappingArray.concat(this.hashvalue2array(baseGeometry.attributes.mapping.array)); + + geometry.addAttribute('position', new THREE.BufferAttribute(new Float32Array(positionArray), 3)); + geometry.addAttribute('color', new THREE.BufferAttribute(new Float32Array(colorArray), 3) ); + geometry.addAttribute('radius', new THREE.BufferAttribute(new Float32Array(radiusArray), 1) ); + geometry.addAttribute('mapping', new THREE.BufferAttribute(new Float32Array(mappingArray), 2) ); + geometry.setIndex(new THREE.BufferAttribute(new Uint32Array(indexArray), 1)); + } + + var matricesAttribute1 = new THREE.InstancedBufferAttribute( new Float32Array( this.matricesElements1 ), 4 ); + var matricesAttribute2 = new THREE.InstancedBufferAttribute( new Float32Array( this.matricesElements2 ), 4 ); + var matricesAttribute3 = new THREE.InstancedBufferAttribute( new Float32Array( this.matricesElements3 ), 4 ); + var matricesAttribute4 = new THREE.InstancedBufferAttribute( new Float32Array( this.matricesElements4 ), 4 ); + + geometry.addAttribute( 'matrix1', matricesAttribute1 ); + geometry.addAttribute( 'matrix2', matricesAttribute2 ); + geometry.addAttribute( 'matrix3', matricesAttribute3 ); + geometry.addAttribute( 'matrix4', matricesAttribute4 ); + + return geometry; +}; + +iCn3D.prototype.getInstancedMaterial = function(name) { + //var material = new THREE.RawShaderMaterial({ + var material = new THREE.ShaderMaterial({ + defines: this.defines, + uniforms: this.uniforms, + vertexShader: this.getShader(name + ".vert"), + fragmentShader: this.getShader(name + ".frag"), + depthTest: true, + depthWrite: true, + needsUpdate: true, + lights: true + }); + + material.extensions.fragDepth = true; + + return material; +} + +iCn3D.prototype.drawSymmetryMatesInstancing = function() { + if (this.biomtMatrices === undefined || this.biomtMatrices.length == 0) return; + var cnt = 1; // itself + var centerSum = this.center.clone(); + + this.setParametersForShader(); + + if(this.bSetInstancing === undefined || !this.bSetInstancing) { + //this.offsets = []; + //this.orientations = []; + this.matricesElements1 = []; + this.matricesElements2 = []; + this.matricesElements3 = []; + this.matricesElements4 = []; + + for (var i = 0; i < this.biomtMatrices.length; i++) { // skip itself + var mat = this.biomtMatrices[i]; + if (mat === undefined) continue; + + var matArray = mat.toArray(); + + // skip itself + var bItself = 1; + for(var j = 0, jl = matArray.length; j < jl; ++j) { + if(j == 0 || j == 5 || j == 10) { + if(parseInt(1000*matArray[j]) != 1000) bItself = 0; + } + else if(j != 0 && j != 5 && j != 10 && j != 15) { + if(parseInt(1000*matArray[j]) != 0) bItself = 0; + } + } + + if(bItself) continue; + + this.matricesElements1.push(matArray[0], matArray[1], matArray[2], matArray[3]); + this.matricesElements2.push(matArray[4], matArray[5], matArray[6], matArray[7]); + this.matricesElements3.push(matArray[8], matArray[9], matArray[10], matArray[11]); + this.matricesElements4.push(matArray[12], matArray[13], matArray[14], matArray[15]); + + var center = this.center.clone(); + center.applyMatrix4(mat); + centerSum.add(center); + + ++cnt; + } + } + + for(var i = 0, il = this.mdl.children.length; i < il; ++i) { + var mesh = this.mdl.children[i]; + + if(mesh.type === 'Sprite') continue; + + var geometry = this.createInstancedGeometry(mesh); + var mesh2 = new THREE.Mesh(geometry, this.instancedMaterial); + + // important: https://stackoverflow.com/questions/21184061/mesh-suddenly-disappears-in-three-js-clipping + // You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU + mesh2.frustumCulled = false; + + this.mdl.add(mesh2); + } + +// if(this.bImpo) { + for(var i = 0, il = this.mdlImpostor.children.length; i < il; ++i) { + var mesh = this.mdlImpostor.children[i]; + + var geometry = this.createInstancedGeometry(mesh); + var mesh2 = new THREE.Mesh(geometry, this.instancedMaterial); + + // important: https://stackoverflow.com/questions/21184061/mesh-suddenly-disappears-in-three-js-clipping + // You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU + mesh2.frustumCulled = false; + + this.mdlImpostor.add(mesh2); + } + +/* + } + else { + for(var i = 0, il = this.mdl_ghost.children.length; i < il; ++i) { + var mesh = this.mdl_ghost.children[i]; + + var geometry = this.createInstancedGeometry(mesh); + + var mesh2 = new THREE.Mesh(geometry, this.instancedMaterial); + + // important: https://stackoverflow.com/questions/21184061/mesh-suddenly-disappears-in-three-js-clipping + // You are moving the camera in the CPU. You are moving the vertices of the plane in the GPU + mesh2.frustumCulled = false; + + this.mdl_ghost.add(mesh2); + } + } +*/ + + if(this.bSetInstancing === undefined || !this.bSetInstancing) { + this.maxD *= Math.sqrt(cnt); + + this.center = centerSum.multiplyScalar(1.0 / cnt); + + this.maxDAssembly = this.maxD; + + this.centerAssembly = this.center.clone(); + + this.setCenter(this.center); + + // reset cameara + this.setCamera(); + } + else { + this.maxD = this.maxDAssembly; + + this.center = this.centerAssembly.clone(); + + this.setCenter(this.center); + + // reset cameara + this.setCamera(); + } + + this.bSetInstancing = true; +}; diff --git a/src/shader/CylinderInstancing.frag b/src/shader/CylinderInstancing.frag new file mode 100644 index 00000000..5f9ad17f --- /dev/null +++ b/src/shader/CylinderInstancing.frag @@ -0,0 +1,302 @@ +$NGL_shaderTextHash['CylinderInstancing.frag'] = ["#define STANDARD", +"#define IMPOSTOR", +"", +"// Open-Source PyMOL is Copyright (C) Schrodinger, LLC.", +"//", +"// All Rights Reserved", +"//", +"// Permission to use, copy, modify, distribute, and distribute modified", +"// versions of this software and its built-in documentation for any", +"// purpose and without fee is hereby granted, provided that the above", +"// copyright notice appears in all copies and that both the copyright", +"// notice and this permission notice appear in supporting documentation,", +"// and that the name of Schrodinger, LLC not be used in advertising or", +"// publicity pertaining to distribution of the software without specific,", +"// written prior permission.", +"//", +"// SCHRODINGER, LLC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,", +"// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN", +"// NO EVENT SHALL SCHRODINGER, LLC BE LIABLE FOR ANY SPECIAL, INDIRECT OR", +"// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS", +"// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE", +"// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE", +"// USE OR PERFORMANCE OF THIS SOFTWARE.", +"", +"// Contributions by Alexander Rose", +"// - ported to WebGL", +"// - dual color", +"// - pk color", +"// - custom clipping", +"// - three.js lighting", +"", +"uniform vec3 diffuse;", +"uniform vec3 emissive;", +"uniform float roughness;", +"uniform float metalness;", +"uniform float opacity;", +"uniform float nearClip;", +"uniform mat4 projectionMatrix;", +"uniform float ortho;", +"", +"varying vec3 axis;", +"varying vec4 base_radius;", +"varying vec4 end_b;", +"varying vec3 U;", +"varying vec3 V;", +"varying vec4 w;", +"", +"#ifdef PICKING", +" uniform float objectId;", +" varying vec3 vPickingColor;", +"#else", +" varying vec3 vColor1;", +" varying vec3 vColor2;", +" #include common", +" #include fog_pars_fragment", +" #include bsdfs", +" #include lights_pars", +" #include lights_physical_pars_fragment", +"#endif", +"", +"bool interior = false;", +"", +"float distSq3( vec3 v3a, vec3 v3b ){", +" return (", +" ( v3a.x - v3b.x ) * ( v3a.x - v3b.x ) +", +" ( v3a.y - v3b.y ) * ( v3a.y - v3b.y ) +", +" ( v3a.z - v3b.z ) * ( v3a.z - v3b.z )", +" );", +"}", +"", +"// Calculate depth based on the given camera position.", +"float calcDepth( in vec3 cameraPos ){", +" vec2 clipZW = cameraPos.z * projectionMatrix[2].zw + projectionMatrix[3].zw;", +" return 0.5 + 0.5 * clipZW.x / clipZW.y;", +"}", +"", +"float calcClip( vec3 cameraPos ){", +" return dot( vec4( cameraPos, 1.0 ), vec4( 0.0, 0.0, 1.0, nearClip - 0.5 ) );", +"}", +"", +"void main(){", +"", +" vec3 point = w.xyz / w.w;", +"", +" // unpacking", +" vec3 base = base_radius.xyz;", +" float vRadius = base_radius.w;", +" vec3 end = end_b.xyz;", +" float b = end_b.w;", +"", +" vec3 end_cyl = end;", +" vec3 surface_point = point;", +"", +" vec3 ray_target = surface_point;", +" vec3 ray_origin = vec3(0.0);", +" vec3 ray_direction = mix(normalize(ray_origin - ray_target), vec3(0.0, 0.0, 1.0), ortho);", +" mat3 basis = mat3( U, V, axis );", +"", +" vec3 diff = ray_target - 0.5 * (base + end_cyl);", +" vec3 P = diff * basis;", +"", +" // angle (cos) between cylinder cylinder_axis and ray direction", +" float dz = dot( axis, ray_direction );", +"", +" float radius2 = vRadius*vRadius;", +"", +" // calculate distance to the cylinder from ray origin", +" vec3 D = vec3(dot(U, ray_direction),", +" dot(V, ray_direction),", +" dz);", +" float a0 = P.x*P.x + P.y*P.y - radius2;", +" float a1 = P.x*D.x + P.y*D.y;", +" float a2 = D.x*D.x + D.y*D.y;", +"", +" // calculate a dicriminant of the above quadratic equation", +" float d = a1*a1 - a0*a2;", +" if (d < 0.0)", +" // outside of the cylinder", +" discard;", +"", +" float dist = (-a1 + sqrt(d)) / a2;", +"", +" // point of intersection on cylinder surface", +" vec3 new_point = ray_target + dist * ray_direction;", +"", +" vec3 tmp_point = new_point - base;", +" vec3 _normal = normalize( tmp_point - axis * dot(tmp_point, axis) );", +"", +" ray_origin = mix( ray_origin, surface_point, ortho );", +"", +" // test caps", +" float front_cap_test = dot( tmp_point, axis );", +" float end_cap_test = dot((new_point - end_cyl), axis);", +"", +" // to calculate caps, simply check the angle between", +" // the point of intersection - cylinder end vector", +" // and a cap plane normal (which is the cylinder cylinder_axis)", +" // if the angle < 0, the point is outside of cylinder", +" // test front cap", +"", +" #ifndef CAP", +" vec3 new_point2 = ray_target + ( (-a1 - sqrt(d)) / a2 ) * ray_direction;", +" vec3 tmp_point2 = new_point2 - base;", +" #endif", +"", +" // flat", +" if (front_cap_test < 0.0)", +" {", +" // ray-plane intersection", +" float dNV = dot(-axis, ray_direction);", +" if (dNV < 0.0)", +" discard;", +" float near = dot(-axis, (base)) / dNV;", +" vec3 front_point = ray_direction * near + ray_origin;", +" // within the cap radius?", +" if (dot(front_point - base, front_point-base) > radius2)", +" discard;", +"", +" #ifdef CAP", +" new_point = front_point;", +" _normal = axis;", +" #else", +" new_point = ray_target + ( (-a1 - sqrt(d)) / a2 ) * ray_direction;", +" dNV = dot(-axis, ray_direction);", +" near = dot(axis, end_cyl) / dNV;", +" new_point2 = ray_direction * near + ray_origin;", +" if (dot(new_point2 - end_cyl, new_point2-base) < radius2)", +" discard;", +" interior = true;", +" #endif", +" }", +"", +" // test end cap", +"", +"", +" // flat", +" if( end_cap_test > 0.0 )", +" {", +" // ray-plane intersection", +" float dNV = dot(axis, ray_direction);", +" if (dNV < 0.0)", +" discard;", +" float near = dot(axis, end_cyl) / dNV;", +" vec3 end_point = ray_direction * near + ray_origin;", +" // within the cap radius?", +" if( dot(end_point - end_cyl, end_point-base) > radius2 )", +" discard;", +"", +" #ifdef CAP", +" new_point = end_point;", +" _normal = axis;", +" #else", +" new_point = ray_target + ( (-a1 - sqrt(d)) / a2 ) * ray_direction;", +" dNV = dot(-axis, ray_direction);", +" near = dot(-axis, (base)) / dNV;", +" new_point2 = ray_direction * near + ray_origin;", +" if (dot(new_point2 - base, new_point2-base) < radius2)", +" discard;", +" interior = true;", +" #endif", +" }", +"", +" gl_FragDepthEXT = calcDepth( new_point );", +"", +" #ifdef NEAR_CLIP", +" if( calcClip( new_point ) > 0.0 ){", +" dist = (-a1 - sqrt(d)) / a2;", +" new_point = ray_target + dist * ray_direction;", +" if( calcClip( new_point ) > 0.0 )", +" discard;", +" interior = true;", +" gl_FragDepthEXT = calcDepth( new_point );", +" if( gl_FragDepthEXT >= 0.0 ){", +" gl_FragDepthEXT = max( 0.0, calcDepth( vec3( - ( nearClip - 0.5 ) ) ) + ( 0.0000001 / vRadius ) );", +" }", +" }else if( gl_FragDepthEXT <= 0.0 ){", +" dist = (-a1 - sqrt(d)) / a2;", +" new_point = ray_target + dist * ray_direction;", +" interior = true;", +" gl_FragDepthEXT = calcDepth( new_point );", +" if( gl_FragDepthEXT >= 0.0 ){", +" gl_FragDepthEXT = 0.0 + ( 0.0000001 / vRadius );", +" }", +" }", +" #else", +" if( gl_FragDepthEXT <= 0.0 ){", +" dist = (-a1 - sqrt(d)) / a2;", +" new_point = ray_target + dist * ray_direction;", +" interior = true;", +" gl_FragDepthEXT = calcDepth( new_point );", +" if( gl_FragDepthEXT >= 0.0 ){", +" gl_FragDepthEXT = 0.0 + ( 0.0000001 / vRadius );", +" }", +" }", +" #endif", +"", +" // this is a workaround necessary for Mac", +" // otherwise the modified fragment won't clip properly", +" if (gl_FragDepthEXT < 0.0)", +" discard;", +" if (gl_FragDepthEXT > 1.0)", +" discard;", +"", +" #ifdef PICKING", +"", +" gl_FragColor = vec4( vPickingColor, objectId );", +"", +" #else", +"", +" vec3 vViewPosition = -new_point;", +" vec3 vNormal = _normal;", +" vec3 vColor;", +"", +" if( distSq3( new_point, end_cyl ) < distSq3( new_point, base ) ){", +" if( b < 0.0 ){", +" vColor = vColor1;", +" }else{", +" vColor = vColor2;", +" }", +" }else{", +" if( b > 0.0 ){", +" vColor = vColor1;", +" }else{", +" vColor = vColor2;", +" }", +" }", +"", +" vec4 diffuseColor = vec4( diffuse, opacity );", +" ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", +" vec3 totalEmissiveLight = emissive;", +"", +" //include color_fragment", +" #ifdef USE_COLOR", +" diffuseColor.r *= vColor[0];", +" diffuseColor.g *= vColor[1];", +" diffuseColor.b *= vColor[2];", +" #endif", +" #include roughnessmap_fragment", +" #include metalnessmap_fragment", +"", +" // don't use include normal_fragment", +" vec3 normal = normalize( vNormal );", +" if( interior ){", +" normal = vec3( 0.0, 0.0, 0.4 );", +" }", +"", +" #include lights_physical_fragment", +" #include lights_template", +"", +" vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight;", +"", +" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", +" //gl_FragColor = vec4( reflectedLight.directSpecular, diffuseColor.a );", +"", +" #include premultiplied_alpha_fragment", +" #include tonemapping_fragment", +" #include encodings_fragment", +" #include fog_fragment", +" #endif", +"", +"}" +].join("\n"); diff --git a/src/shader/CylinderInstancing.vert b/src/shader/CylinderInstancing.vert new file mode 100644 index 00000000..717f2a1d --- /dev/null +++ b/src/shader/CylinderInstancing.vert @@ -0,0 +1,143 @@ +$NGL_shaderTextHash['CylinderInstancing.vert'] = ["// Open-Source PyMOL is Copyright (C) Schrodinger, LLC.", +"//", +"// All Rights Reserved", +"//", +"// Permission to use, copy, modify, distribute, and distribute modified", +"// versions of this software and its built-in documentation for any", +"// purpose and without fee is hereby granted, provided that the above", +"// copyright notice appears in all copies and that both the copyright", +"// notice and this permission notice appear in supporting documentation,", +"// and that the name of Schrodinger, LLC not be used in advertising or", +"// publicity pertaining to distribution of the software without specific,", +"// written prior permission.", +"//", +"// SCHRODINGER, LLC DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,", +"// INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN", +"// NO EVENT SHALL SCHRODINGER, LLC BE LIABLE FOR ANY SPECIAL, INDIRECT OR", +"// CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS", +"// OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE", +"// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE", +"// USE OR PERFORMANCE OF THIS SOFTWARE.", +"", +"// Contributions by Alexander Rose", +"// - ported to WebGL", +"// - dual color", +"// - pk color", +"// - shift", +"", +"attribute vec3 mapping;", +"attribute vec3 position1;", +"attribute vec3 position2;", +"attribute float radius;", +"attribute vec4 matrix1;", +"attribute vec4 matrix2;", +"attribute vec4 matrix3;", +"attribute vec4 matrix4;", +"", +"varying vec3 axis;", +"varying vec4 base_radius;", +"varying vec4 end_b;", +"varying vec3 U;", +"varying vec3 V;", +"varying vec4 w;", +"", +"#ifdef PICKING", +" #include unpack_clr", +" attribute float primitiveId;", +" varying vec3 vPickingColor;", +"#else", +" //attribute vec3 color;", +" attribute vec3 color2;", +" varying vec3 vColor1;", +" varying vec3 vColor2;", +"#endif", +"", +"uniform mat4 modelViewMatrixInverse;", +"uniform float ortho;", +"", +"//include matrix_scale", +"float matrixScale( in mat4 m ){", +" vec4 r = m[ 0 ];", +" return sqrt( r[ 0 ] * r[ 0 ] + r[ 1 ] * r[ 1 ] + r[ 2 ] * r[ 2 ] );", +"}", +"", +" mat4 computeMat(vec4 v1, vec4 v2, vec4 v3, vec4 v4) {", +" return mat4(", +" v1.x, v1.y, v1.z, v1.w,", +" v2.x, v2.y, v2.z, v2.w,", +" v3.x, v3.y, v3.z, v3.w,", +" v4.x, v4.y, v4.z, v4.w", +" );", +" }", +"", +"void main(){", +"", +" #ifdef PICKING", +" vPickingColor = unpackColor( primitiveId );", +" #else", +" vColor1 = color;", +" vColor2 = color2;", +" #endif", +"", +" // vRadius = radius;", +" base_radius.w = radius * matrixScale( modelViewMatrix );", +"", +" //vec3 center = ( position2 + position1 ) / 2.0;", +"", +" mat4 matrix = computeMat(matrix1, matrix2, matrix3, matrix4);", +" vec4 updatePosition1 = matrix * vec4(position1, 1.0);", +" vec4 updatePosition2 = matrix * vec4(position2, 1.0);", +" vec3 center = ( updatePosition2.xyz + updatePosition1.xyz ) / 2.0;", +"", +" //vec3 dir = normalize( position2 - position1 );", +" vec3 dir = normalize( updatePosition2.xyz - updatePosition1.xyz );", +" float ext = length( position2 - position1 ) / 2.0;", +"", +" // using cameraPosition fails on some machines, not sure why", +" // vec3 cam_dir = normalize( cameraPosition - mix( center, vec3( 0.0 ), ortho ) );", +" vec3 cam_dir;", +" if( ortho == 0.0 ){", +" cam_dir = ( modelViewMatrixInverse * vec4( 0, 0, 0, 1 ) ).xyz - center;", +" }else{", +" cam_dir = ( modelViewMatrixInverse * vec4( 0, 0, 1, 0 ) ).xyz;", +" }", +" cam_dir = normalize( cam_dir );", +"", +" vec3 ldir;", +"", +" float b = dot( cam_dir, dir );", +" end_b.w = b;", +" // direction vector looks away, so flip", +" if( b < 0.0 )", +" ldir = -ext * dir;", +" // direction vector already looks in my direction", +" else", +" ldir = ext * dir;", +"", +" vec3 left = normalize( cross( cam_dir, ldir ) );", +" left = radius * left;", +" vec3 up = radius * normalize( cross( left, ldir ) );", +"", +" // transform to modelview coordinates", +" axis = normalize( normalMatrix * ldir );", +" U = normalize( normalMatrix * up );", +" V = normalize( normalMatrix * left );", +"", +" vec4 base4 = modelViewMatrix * vec4( center - ldir, 1.0 );", +" base_radius.xyz = base4.xyz / base4.w;", +"", +" vec4 top_position = modelViewMatrix * vec4( center + ldir, 1.0 );", +" vec4 end4 = top_position;", +" end_b.xyz = end4.xyz / end4.w;", +"", +" w = modelViewMatrix * vec4(", +" center + mapping.x*ldir + mapping.y*left + mapping.z*up, 1.0", +" );", +"", +" gl_Position = projectionMatrix * w;", +"", +" // avoid clipping (1.0 seems to induce flickering with some drivers)", +" gl_Position.z = 0.99;", +"", +"}" +].join("\n"); diff --git a/src/shader/Instancing.frag b/src/shader/Instancing.frag new file mode 100644 index 00000000..e3863729 --- /dev/null +++ b/src/shader/Instancing.frag @@ -0,0 +1,12 @@ +$NGL_shaderTextHash['Instancing.frag'] = ["precision highp float;", +"", +"uniform float time;", +"", +"varying vec3 vNormal;", +"varying vec3 vColor;", +"", +"void main() {", +"// gl_FragColor = vec4(vNormal, 1.0);", +" gl_FragColor = vec4(vColor, 1.0);", +"}" +].join("\n"); \ No newline at end of file diff --git a/src/shader/Instancing.vert b/src/shader/Instancing.vert new file mode 100644 index 00000000..e3d82bd9 --- /dev/null +++ b/src/shader/Instancing.vert @@ -0,0 +1,30 @@ +$NGL_shaderTextHash['Instancing.vert'] = ["//attribute vec3 position;", +"// attribute vec3 normal;", +"// attribute vec3 color;", +"attribute vec4 matrix1;", +"attribute vec4 matrix2;", +"attribute vec4 matrix3;", +"attribute vec4 matrix4;", +"", +"// uniform mat4 projectionMatrix;", +"// uniform mat4 modelViewMatrix;", +"", +" varying vec3 vNormal;", +" varying vec3 vColor;", +"", +" mat4 computeMat(vec4 v1, vec4 v2, vec4 v3, vec4 v4) {", +" return mat4(", +" v1.x, v1.y, v1.z, v1.w,", +" v2.x, v2.y, v2.z, v2.w,", +" v3.x, v3.y, v3.z, v3.w,", +" v4.x, v4.y, v4.z, v4.w", +" );", +" }", +"", +" void main(void) {", +" vColor = color;", +" mat4 matrix = computeMat(matrix1, matrix2, matrix3, matrix4);", +" vec4 updatePosition = matrix * vec4(position, 1.0);", +" gl_Position = projectionMatrix * modelViewMatrix * updatePosition;", +" }" +].join("\n"); diff --git a/src/shader/SphereInstancing.frag b/src/shader/SphereInstancing.frag new file mode 100644 index 00000000..b7b7f9b5 --- /dev/null +++ b/src/shader/SphereInstancing.frag @@ -0,0 +1,172 @@ +$NGL_shaderTextHash['SphereInstancing.frag'] = ["#define STANDARD", +"#define IMPOSTOR", +"", +"uniform vec3 diffuse;", +"uniform vec3 emissive;", +"uniform float roughness;", +"uniform float metalness;", +"uniform float opacity;", +"uniform float nearClip;", +"uniform mat4 projectionMatrix;", +"uniform float ortho;", +"", +"varying float vRadius;", +"varying float vRadiusSq;", +"varying vec3 vPoint;", +"varying vec3 vPointViewPosition;", +"", +"#ifdef PICKING", +" uniform float objectId;", +" varying vec3 vPickingColor;", +"#else", +" #include common", +" #include color_pars_fragment", +" #include fog_pars_fragment", +" #include bsdfs", +" #include lights_pars", +" #include lights_physical_pars_fragment", +"#endif", +"", +"bool flag2 = false;", +"bool interior = false;", +"vec3 cameraPos;", +"vec3 cameraNormal;", +"", +"// Calculate depth based on the given camera position.", +"float calcDepth( in vec3 cameraPos ){", +" vec2 clipZW = cameraPos.z * projectionMatrix[2].zw + projectionMatrix[3].zw;", +" return 0.5 + 0.5 * clipZW.x / clipZW.y;", +"}", +"", +"float calcClip( vec3 cameraPos ){", +" return dot( vec4( cameraPos, 1.0 ), vec4( 0.0, 0.0, 1.0, nearClip - 0.5 ) );", +"}", +"", +"bool Impostor( out vec3 cameraPos, out vec3 cameraNormal ){", +"", +" vec3 cameraSpherePos = -vPointViewPosition;", +" cameraSpherePos.z += vRadius;", +"", +" vec3 rayOrigin = mix( vec3( 0.0, 0.0, 0.0 ), vPoint, ortho );", +" vec3 rayDirection = mix( normalize( vPoint ), vec3( 0.0, 0.0, 1.0 ), ortho );", +" vec3 cameraSphereDir = mix( cameraSpherePos, rayOrigin - cameraSpherePos, ortho );", +"", +" float B = dot( rayDirection, cameraSphereDir );", +" float det = B * B + vRadiusSq - dot( cameraSphereDir, cameraSphereDir );", +"", +" if( det < 0.0 ){", +" discard;", +" return false;", +" }else{", +" float sqrtDet = sqrt( det );", +" float posT = mix( B + sqrtDet, B + sqrtDet, ortho );", +" float negT = mix( B - sqrtDet, sqrtDet - B, ortho );", +"", +" cameraPos = rayDirection * negT + rayOrigin;", +"", +" #ifdef NEAR_CLIP", +"if( calcDepth( cameraPos ) <= 0.0 ){", +" cameraPos = rayDirection * posT + rayOrigin;", +" interior = true;", +" return false;", +"}else if( calcClip( cameraPos ) > 0.0 ){", +" cameraPos = rayDirection * posT + rayOrigin;", +" interior = true;", +" flag2 = true;", +" return false;", +"}else{", +" cameraNormal = normalize( cameraPos - cameraSpherePos );", +"}", +" #else", +"if( calcDepth( cameraPos ) <= 0.0 ){", +" cameraPos = rayDirection * posT + rayOrigin;", +" interior = true;", +" return false;", +"}else{", +" cameraNormal = normalize( cameraPos - cameraSpherePos );", +"}", +" #endif", +"", +" return true;", +" }", +"", +" return false; // ensure that each control flow has a return", +"", +"}", +"", +"void main(void){", +"", +" bool flag = Impostor( cameraPos, cameraNormal );", +"", +" #ifdef NEAR_CLIP", +" if( calcClip( cameraPos ) > 0.0 )", +" discard;", +" #endif", +"", +" // FIXME not compatible with custom clipping plane", +" //Set the depth based on the new cameraPos.", +" gl_FragDepthEXT = calcDepth( cameraPos );", +" if( !flag ){", +"", +" // clamp to near clipping plane and add a tiny value to", +" // make spheres with a greater radius occlude smaller ones", +" #ifdef NEAR_CLIP", +"if( flag2 ){", +" gl_FragDepthEXT = max( 0.0, calcDepth( vec3( - ( nearClip - 0.5 ) ) ) + ( 0.0000001 / vRadius ) );", +"}else if( gl_FragDepthEXT >= 0.0 ){", +" gl_FragDepthEXT = 0.0 + ( 0.0000001 / vRadius );", +"}", +" #else", +"if( gl_FragDepthEXT >= 0.0 ){", +" gl_FragDepthEXT = 0.0 + ( 0.0000001 / vRadius );", +"}", +" #endif", +"", +" }", +"", +" // bugfix (mac only?)", +" if (gl_FragDepthEXT < 0.0)", +" discard;", +" if (gl_FragDepthEXT > 1.0)", +" discard;", +"", +" #ifdef PICKING", +"", +" gl_FragColor = vec4( vPickingColor, objectId );", +"", +" #else", +"", +" vec3 vNormal = cameraNormal;", +" vec3 vViewPosition = -cameraPos;", +"", +" vec4 diffuseColor = vec4( diffuse, opacity );", +" ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );", +" vec3 totalEmissiveLight = emissive;", +"", +" #include color_fragment", +" #include roughnessmap_fragment", +" #include metalnessmap_fragment", +" #include normal_flip", +" #include normal_fragment", +" if( interior ){", +" normal = vec3( 0.0, 0.0, 0.4 );", +" }", +"", +" // include lights_phong_fragment", +" #include lights_physical_fragment", +" #include lights_template", +"", +" vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular + totalEmissiveLight;", +"", +" gl_FragColor = vec4( outgoingLight, diffuseColor.a );", +" //gl_FragColor = vec4( reflectedLight.directSpecular, diffuseColor.a );", +"", +" #include premultiplied_alpha_fragment", +" #include tonemapping_fragment", +" #include encodings_fragment", +" #include fog_fragment", +"", +" #endif", +"", +"}" +].join("\n"); diff --git a/src/shader/SphereInstancing.vert b/src/shader/SphereInstancing.vert new file mode 100644 index 00000000..fcba30e7 --- /dev/null +++ b/src/shader/SphereInstancing.vert @@ -0,0 +1,162 @@ +$NGL_shaderTextHash['SphereInstancing.vert'] = ["uniform mat4 projectionMatrixInverse;", +"uniform float nearClip;", +"", +"varying float vRadius;", +"varying float vRadiusSq;", +"varying vec3 vPoint;", +"varying vec3 vPointViewPosition;", +"", +"attribute vec2 mapping;", +"//attribute vec3 position;", +"attribute float radius;", +"attribute vec4 matrix1;", +"attribute vec4 matrix2;", +"attribute vec4 matrix3;", +"attribute vec4 matrix4;", +"", +"#ifdef PICKING", +" #include unpack_clr", +" attribute float primitiveId;", +" varying vec3 vPickingColor;", +"#else", +" #include color_pars_vertex", +"#endif", +"", +"//include matrix_scale", +"float matrixScale( in mat4 m ){", +" vec4 r = m[ 0 ];", +" return sqrt( r[ 0 ] * r[ 0 ] + r[ 1 ] * r[ 1 ] + r[ 2 ] * r[ 2 ] );", +"}", +"", +"const mat4 D = mat4(", +" 1.0, 0.0, 0.0, 0.0,", +" 0.0, 1.0, 0.0, 0.0,", +" 0.0, 0.0, 1.0, 0.0,", +" 0.0, 0.0, 0.0, -1.0", +");", +"", +"mat4 transpose( in mat4 inMatrix ) {", +" vec4 i0 = inMatrix[0];", +" vec4 i1 = inMatrix[1];", +" vec4 i2 = inMatrix[2];", +" vec4 i3 = inMatrix[3];", +"", +" mat4 outMatrix = mat4(", +" vec4(i0.x, i1.x, i2.x, i3.x),", +" vec4(i0.y, i1.y, i2.y, i3.y),", +" vec4(i0.z, i1.z, i2.z, i3.z),", +" vec4(i0.w, i1.w, i2.w, i3.w)", +" );", +" return outMatrix;", +"}", +"", +"//------------------------------------------------------------------------------", +"// Compute point size and center using the technique described in:", +"// 'GPU-Based Ray-Casting of Quadratic Surfaces'", +"// by Christian Sigg, Tim Weyrich, Mario Botsch, Markus Gross.", +"//", +"// Code based on", +"/*=========================================================================", +"", +" Program: Visualization Toolkit", +" Module: Quadrics_fs.glsl and Quadrics_vs.glsl", +"", +" Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen", +" All rights reserved.", +" See Copyright.txt or http://www.kitware.com/Copyright.htm for details.", +"", +" This software is distributed WITHOUT ANY WARRANTY; without even", +" the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR", +" PURPOSE. See the above copyright notice for more information.", +"", +" =========================================================================*/", +"", +"// .NAME Quadrics_fs.glsl and Quadrics_vs.glsl", +"// .SECTION Thanks", +"// ", +"//", +"// This file is part of the PointSprites plugin developed and contributed by", +"//", +"// Copyright (c) CSCS - Swiss National Supercomputing Centre", +"// EDF - Electricite de France", +"//", +"// John Biddiscombe, Ugo Varetto (CSCS)", +"// Stephane Ploix (EDF)", +"//", +"// ", +"//", +"// Contributions by Alexander Rose", +"// - ported to WebGL", +"// - adapted to work with quads", +"void ComputePointSizeAndPositionInClipCoordSphere(vec4 updatePosition){", +"", +" vec2 xbc;", +" vec2 ybc;", +"", +" mat4 T = mat4(", +" radius, 0.0, 0.0, 0.0,", +" 0.0, radius, 0.0, 0.0,", +" 0.0, 0.0, radius, 0.0,", +" updatePosition.x, updatePosition.y, updatePosition.z, 1.0", +" );", +"", +" mat4 R = transpose( projectionMatrix * modelViewMatrix * T );", +" float A = dot( R[ 3 ], D * R[ 3 ] );", +" float B = -2.0 * dot( R[ 0 ], D * R[ 3 ] );", +" float C = dot( R[ 0 ], D * R[ 0 ] );", +" xbc[ 0 ] = ( -B - sqrt( B * B - 4.0 * A * C ) ) / ( 2.0 * A );", +" xbc[ 1 ] = ( -B + sqrt( B * B - 4.0 * A * C ) ) / ( 2.0 * A );", +" float sx = abs( xbc[ 0 ] - xbc[ 1 ] ) * 0.5;", +"", +" A = dot( R[ 3 ], D * R[ 3 ] );", +" B = -2.0 * dot( R[ 1 ], D * R[ 3 ] );", +" C = dot( R[ 1 ], D * R[ 1 ] );", +" ybc[ 0 ] = ( -B - sqrt( B * B - 4.0 * A * C ) ) / ( 2.0 * A );", +" ybc[ 1 ] = ( -B + sqrt( B * B - 4.0 * A * C ) ) / ( 2.0 * A );", +" float sy = abs( ybc[ 0 ] - ybc[ 1 ] ) * 0.5;", +"", +" gl_Position.xy = vec2( 0.5 * ( xbc.x + xbc.y ), 0.5 * ( ybc.x + ybc.y ) );", +" gl_Position.xy -= mapping * vec2( sx, sy );", +" gl_Position.xy *= gl_Position.w;", +"", +"}", +"", +" mat4 computeMat(vec4 v1, vec4 v2, vec4 v3, vec4 v4) {", +" return mat4(", +" v1.x, v1.y, v1.z, v1.w,", +" v2.x, v2.y, v2.z, v2.w,", +" v3.x, v3.y, v3.z, v3.w,", +" v4.x, v4.y, v4.z, v4.w", +" );", +" }", +"", +"void main(void){", +"", +" #ifdef PICKING", +" vPickingColor = unpackColor( primitiveId );", +" #else", +" #include color_vertex", +" #endif", +"", +" vRadius = radius * matrixScale( modelViewMatrix );", +"", +" mat4 matrix = computeMat(matrix1, matrix2, matrix3, matrix4);", +" vec4 updatePosition = matrix * vec4(position, 1.0);", +"", +"// vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );", +" vec4 mvPosition = modelViewMatrix * vec4( updatePosition.xyz, 1.0 );", +" // avoid clipping, added again in fragment shader", +" mvPosition.z -= vRadius;", +"", +"// gl_Position = projectionMatrix * vec4( mvPosition.xyz, 1.0 );", +" gl_Position = projectionMatrix * vec4( mvPosition.xyz, 1.0 );", +" ComputePointSizeAndPositionInClipCoordSphere(updatePosition);", +"", +"", +" vRadiusSq = vRadius * vRadius;", +" vec4 vPoint4 = projectionMatrixInverse * gl_Position;", +" vPoint = vPoint4.xyz / vPoint4.w;", +" vPointViewPosition = -mvPosition.xyz / mvPosition.w;", +"", +"}" +].join("\n"); From 9bc365bc16fe30374fa7ccbcc959f1f66c8666b9 Mon Sep 17 00:00:00 2001 From: "Wang, Jiyao" Date: Mon, 21 May 2018 13:23:40 -0400 Subject: [PATCH 3/3] speed up the rendering of assembly using instancing method --- src/icn3d/draw/impostor.js | 4 ++++ src/icn3d/draw/instancing.js | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/icn3d/draw/impostor.js b/src/icn3d/draw/impostor.js index 82ed8a43..d5b533b3 100644 --- a/src/icn3d/draw/impostor.js +++ b/src/icn3d/draw/impostor.js @@ -1,3 +1,7 @@ +/** + * @author Jiyao Wang / https://github.com/ncbi/icn3d + */ + iCn3D.prototype.setParametersForShader = function () { var me = this; var modelViewMatrix = new THREE.Uniform( new THREE.Matrix4() ) .onUpdate( function( object ){ diff --git a/src/icn3d/draw/instancing.js b/src/icn3d/draw/instancing.js index c4e80455..38dde325 100644 --- a/src/icn3d/draw/instancing.js +++ b/src/icn3d/draw/instancing.js @@ -1,3 +1,7 @@ +/** + * @author Jiyao Wang / https://github.com/ncbi/icn3d + */ + iCn3D.prototype.positionFromGeometry = function( mesh ){ var geometry = mesh.geometry;