3dmol
Version:
JavaScript/TypeScript molecular visualization library
896 lines (750 loc) • 80.5 kB
HTML
<!DOCTYPE html><html lang="en" style="font-size:16px"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><meta name="Author" content="David Koes & 3Dmoljs Contributors"><meta name="Description" content="A modern, object-oriented JavaScript library for visualizing molecular data"><title>Source: glcartoon.ts</title><!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]--><script src="scripts/third-party/hljs.js" defer="defer"></script><script src="scripts/third-party/hljs-line-num.js" defer="defer"></script><script src="scripts/third-party/popper.js" defer="defer"></script><script src="scripts/third-party/tippy.js" defer="defer"></script><script src="scripts/third-party/tocbot.min.js"></script><script>var baseURL="/",locationPathname="";baseURL=(locationPathname=document.location.pathname).substr(0,locationPathname.lastIndexOf("/")+1)</script><link rel="stylesheet" href="styles/clean-jsdoc-theme.min.css"><style>article ul li{list-style:disc}</style><svg aria-hidden="true" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="display:none"><defs><symbol id="copy-icon" viewbox="0 0 488.3 488.3"><g><path d="M314.25,85.4h-227c-21.3,0-38.6,17.3-38.6,38.6v325.7c0,21.3,17.3,38.6,38.6,38.6h227c21.3,0,38.6-17.3,38.6-38.6V124 C352.75,102.7,335.45,85.4,314.25,85.4z M325.75,449.6c0,6.4-5.2,11.6-11.6,11.6h-227c-6.4,0-11.6-5.2-11.6-11.6V124 c0-6.4,5.2-11.6,11.6-11.6h227c6.4,0,11.6,5.2,11.6,11.6V449.6z"/><path d="M401.05,0h-227c-21.3,0-38.6,17.3-38.6,38.6c0,7.5,6,13.5,13.5,13.5s13.5-6,13.5-13.5c0-6.4,5.2-11.6,11.6-11.6h227 c6.4,0,11.6,5.2,11.6,11.6v325.7c0,6.4-5.2,11.6-11.6,11.6c-7.5,0-13.5,6-13.5,13.5s6,13.5,13.5,13.5c21.3,0,38.6-17.3,38.6-38.6 V38.6C439.65,17.3,422.35,0,401.05,0z"/></g></symbol><symbol id="search-icon" viewBox="0 0 512 512"><g><g><path d="M225.474,0C101.151,0,0,101.151,0,225.474c0,124.33,101.151,225.474,225.474,225.474 c124.33,0,225.474-101.144,225.474-225.474C450.948,101.151,349.804,0,225.474,0z M225.474,409.323 c-101.373,0-183.848-82.475-183.848-183.848S124.101,41.626,225.474,41.626s183.848,82.475,183.848,183.848 S326.847,409.323,225.474,409.323z"/></g></g><g><g><path d="M505.902,476.472L386.574,357.144c-8.131-8.131-21.299-8.131-29.43,0c-8.131,8.124-8.131,21.306,0,29.43l119.328,119.328 c4.065,4.065,9.387,6.098,14.715,6.098c5.321,0,10.649-2.033,14.715-6.098C514.033,497.778,514.033,484.596,505.902,476.472z"/></g></g></symbol><symbol id="font-size-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11.246 15H4.754l-2 5H.6L7 4h2l6.4 16h-2.154l-2-5zm-.8-2L8 6.885 5.554 13h4.892zM21 12.535V12h2v8h-2v-.535a4 4 0 1 1 0-6.93zM19 18a2 2 0 1 0 0-4 2 2 0 0 0 0 4z"/></symbol><symbol id="add-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M11 11V5h2v6h6v2h-6v6h-2v-6H5v-2z"/></symbol><symbol id="minus-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M5 11h14v2H5z"/></symbol><symbol id="dark-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12 6.477 2 12 2h.1A6.979 6.979 0 0 0 10 7zm-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938 7.999 7.999 0 0 0 4 12z"/></symbol><symbol id="light-theme-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 18a6 6 0 1 1 0-12 6 6 0 0 1 0 12zm0-2a4 4 0 1 0 0-8 4 4 0 0 0 0 8zM11 1h2v3h-2V1zm0 19h2v3h-2v-3zM3.515 4.929l1.414-1.414L7.05 5.636 5.636 7.05 3.515 4.93zM16.95 18.364l1.414-1.414 2.121 2.121-1.414 1.414-2.121-2.121zm2.121-14.85l1.414 1.415-2.121 2.121-1.414-1.414 2.121-2.121zM5.636 16.95l1.414 1.414-2.121 2.121-1.414-1.414 2.121-2.121zM23 11v2h-3v-2h3zM4 11v2H1v-2h3z"/></symbol><symbol id="reset-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M18.537 19.567A9.961 9.961 0 0 1 12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10c0 2.136-.67 4.116-1.81 5.74L17 12h3a8 8 0 1 0-2.46 5.772l.997 1.795z"/></symbol><symbol id="down-icon" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M12.7803 6.21967C13.0732 6.51256 13.0732 6.98744 12.7803 7.28033L8.53033 11.5303C8.23744 11.8232 7.76256 11.8232 7.46967 11.5303L3.21967 7.28033C2.92678 6.98744 2.92678 6.51256 3.21967 6.21967C3.51256 5.92678 3.98744 5.92678 4.28033 6.21967L8 9.93934L11.7197 6.21967C12.0126 5.92678 12.4874 5.92678 12.7803 6.21967Z"></path></symbol><symbol id="codepen-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M16.5 13.202L13 15.535v3.596L19.197 15 16.5 13.202zM14.697 12L12 10.202 9.303 12 12 13.798 14.697 12zM20 10.869L18.303 12 20 13.131V10.87zM19.197 9L13 4.869v3.596l3.5 2.333L19.197 9zM7.5 10.798L11 8.465V4.869L4.803 9 7.5 10.798zM4.803 15L11 19.131v-3.596l-3.5-2.333L4.803 15zM4 13.131L5.697 12 4 10.869v2.262zM2 9a1 1 0 0 1 .445-.832l9-6a1 1 0 0 1 1.11 0l9 6A1 1 0 0 1 22 9v6a1 1 0 0 1-.445.832l-9 6a1 1 0 0 1-1.11 0l-9-6A1 1 0 0 1 2 15V9z"/></symbol><symbol id="close-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M12 10.586l4.95-4.95 1.414 1.414-4.95 4.95 4.95 4.95-1.414 1.414-4.95-4.95-4.95 4.95-1.414-1.414 4.95-4.95-4.95-4.95L7.05 5.636z"/></symbol><symbol id="menu-icon" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0z"/><path d="M3 4h18v2H3V4zm0 7h18v2H3v-2zm0 7h18v2H3v-2z"/></symbol></defs></svg></head><body data-theme="light"><div class="sidebar-container"><div class="sidebar" id="sidebar"><a href="/" class="sidebar-title sidebar-title-anchor">3Dmol.js</a><div class="sidebar-items-container"><div class="sidebar-section-title with-arrow" data-isopen="false" id="mei5HJjYKLn6aua5Z5PM5"><div>Classes</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="$3Dmol.Label.html">Label</a></div><div class="sidebar-section-children"><a href="$3Dmol.StateManager.html">StateManager</a></div><div class="sidebar-section-children"><a href="$3Dmol.UI.html">UI</a></div><div class="sidebar-section-children"><a href="Color.html">Color</a></div><div class="sidebar-section-children"><a href="CustomLinear.html">CustomLinear</a></div><div class="sidebar-section-children"><a href="Cylinder.html">Cylinder</a></div><div class="sidebar-section-children"><a href="GLModel.html">GLModel</a></div><div class="sidebar-section-children"><a href="GLShape.html">GLShape</a></div><div class="sidebar-section-children"><a href="GLShape_GLShape.html">GLShape</a></div><div class="sidebar-section-children"><a href="GLViewer.html">GLViewer</a></div><div class="sidebar-section-children"><a href="GLVolumetricRender.html">GLVolumetricRender</a></div><div class="sidebar-section-children"><a href="Matrix3.html">Matrix3</a></div><div class="sidebar-section-children"><a href="Matrix4.html">Matrix4</a></div><div class="sidebar-section-children"><a href="Quaternion.html">Quaternion</a></div><div class="sidebar-section-children"><a href="ROYGB.html">ROYGB</a></div><div class="sidebar-section-children"><a href="RWB.html">RWB</a></div><div class="sidebar-section-children"><a href="Ray.html">Ray</a></div><div class="sidebar-section-children"><a href="Sinebow.html">Sinebow</a></div><div class="sidebar-section-children"><a href="Sphere.html">Sphere</a></div><div class="sidebar-section-children"><a href="Surface.html">Surface</a></div><div class="sidebar-section-children"><a href="Triangle.html">Triangle</a></div><div class="sidebar-section-children"><a href="Vector2.html">Vector2</a></div><div class="sidebar-section-children"><a href="Vector3.html">Vector3</a></div><div class="sidebar-section-children"><a href="VolumeData.html">VolumeData</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="kv4Yx5z0THf_b8XR4Gje1"><div>Namespaces</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="GLDraw.html">GLDraw</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="ykThSufQWJcKGT1kXzr-A"><div>Tutorials</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="tutorial-code.html">Using 3Dmol within your code</a></div><div class="sidebar-section-children"><a href="tutorial-embeddable.html">Embedding a 3Dmol Viewer</a></div><div class="sidebar-section-children"><a href="tutorial-home.html">3Dmol Tutorials - Home</a></div><div class="sidebar-section-children"><a href="tutorial-learning_environment.html">Active Learning with 3Dmol.js</a></div><div class="sidebar-section-children"><a href="tutorial-url.html">Viewing Molecules with the 3Dmol Server</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="XcP4RUEg6tp7YKHbBVi9E"><div>Interfaces</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="AmbientOcclusionStyle.html">AmbientOcclusionStyle</a></div><div class="sidebar-section-children"><a href="ArrowSpec.html">ArrowSpec</a></div><div class="sidebar-section-children"><a href="AtomSelectionSpec.html">AtomSelectionSpec</a></div><div class="sidebar-section-children"><a href="AtomSpec.html">AtomSpec</a></div><div class="sidebar-section-children"><a href="AtomStyleSpec.html">AtomStyleSpec</a></div><div class="sidebar-section-children"><a href="BondStyle.html">BondStyle</a></div><div class="sidebar-section-children"><a href="BoxSpec.html">BoxSpec</a></div><div class="sidebar-section-children"><a href="CartoonStyleSpec.html">CartoonStyleSpec</a></div><div class="sidebar-section-children"><a href="ClickSphereStyleSpec.html">ClickSphereStyleSpec</a></div><div class="sidebar-section-children"><a href="CrossStyleSpec.html">CrossStyleSpec</a></div><div class="sidebar-section-children"><a href="CurveSpec.html">CurveSpec</a></div><div class="sidebar-section-children"><a href="CustomShapeSpec.html">CustomShapeSpec</a></div><div class="sidebar-section-children"><a href="CylinderSpec.html">CylinderSpec</a></div><div class="sidebar-section-children"><a href="DashedBondSpec.html">DashedBondSpec</a></div><div class="sidebar-section-children"><a href="FogSpec.html">FogSpec</a></div><div class="sidebar-section-children"><a href="IsoSurfaceSpec.html">IsoSurfaceSpec</a></div><div class="sidebar-section-children"><a href="LabelSpec.html">LabelSpec</a></div><div class="sidebar-section-children"><a href="LineSpec.html">LineSpec</a></div><div class="sidebar-section-children"><a href="LineStyleSpec.html">LineStyleSpec</a></div><div class="sidebar-section-children"><a href="OutlineStyle.html">OutlineStyle</a></div><div class="sidebar-section-children"><a href="ParserOptionsSpec.html">ParserOptionsSpec</a></div><div class="sidebar-section-children"><a href="ShapeSpec.html">ShapeSpec</a></div><div class="sidebar-section-children"><a href="SphereSpec.html">SphereSpec</a></div><div class="sidebar-section-children"><a href="SphereStyleSpec.html">SphereStyleSpec</a></div><div class="sidebar-section-children"><a href="StickStyleSpec.html">StickStyleSpec</a></div><div class="sidebar-section-children"><a href="SurfaceStyleSpec.html">SurfaceStyleSpec</a></div><div class="sidebar-section-children"><a href="UnitCellStyleSpec.html">UnitCellStyleSpec</a></div><div class="sidebar-section-children"><a href="ViewStyle.html">ViewStyle</a></div><div class="sidebar-section-children"><a href="ViewerGridSpec.html">ViewerGridSpec</a></div><div class="sidebar-section-children"><a href="ViewerSpec.html">ViewerSpec</a></div><div class="sidebar-section-children"><a href="VolumetricRendererSpec.html">VolumetricRendererSpec</a></div><div class="sidebar-section-children"><a href="WithinSelectionSpec.html">WithinSelectionSpec</a></div><div class="sidebar-section-children"><a href="global.html#XYZ">XYZ</a></div></div><div class="sidebar-section-title with-arrow" data-isopen="false" id="jmKnIXRJjdjZ366ci7cdd"><div>Global</div><svg><use xlink:href="#down-icon"></use></svg></div><div class="sidebar-section-children-container"><div class="sidebar-section-children"><a href="global.html#BCIF">BCIF</a></div><div class="sidebar-section-children"><a href="global.html#CAP">CAP</a></div><div class="sidebar-section-children"><a href="global.html#CDJSON">CDJSON</a></div><div class="sidebar-section-children"><a href="global.html#CIF">CIF</a></div><div class="sidebar-section-children"><a href="global.html#CUBE">CUBE</a></div><div class="sidebar-section-children"><a href="global.html#ColorSpec">ColorSpec</a></div><div class="sidebar-section-children"><a href="global.html#ColorschemeSpec">ColorschemeSpec</a></div><div class="sidebar-section-children"><a href="global.html#GRO">GRO</a></div><div class="sidebar-section-children"><a href="global.html#GradientSpec">GradientSpec</a></div><div class="sidebar-section-children"><a href="global.html#MMTFparser">MMTFparser</a></div><div class="sidebar-section-children"><a href="global.html#MOL2">MOL2</a></div><div class="sidebar-section-children"><a href="global.html#OFFSETS">OFFSETS</a></div><div class="sidebar-section-children"><a href="global.html#PDB">PDB</a></div><div class="sidebar-section-children"><a href="global.html#PQR">PQR</a></div><div class="sidebar-section-children"><a href="global.html#PRMTOP">PRMTOP</a></div><div class="sidebar-section-children"><a href="global.html#SDF">SDF</a></div><div class="sidebar-section-children"><a href="global.html#SurfaceType">SurfaceType</a></div><div class="sidebar-section-children"><a href="global.html#VASP">VASP</a></div><div class="sidebar-section-children"><a href="global.html#XYZ">XYZ</a></div><div class="sidebar-section-children"><a href="global.html#assignPDBBonds">assignPDBBonds</a></div><div class="sidebar-section-children"><a href="global.html#builtinColorSchemes">builtinColorSchemes</a></div><div class="sidebar-section-children"><a href="global.html#builtinGradients">builtinGradients</a></div><div class="sidebar-section-children"><a href="global.html#conversionMatrix3">conversionMatrix3</a></div><div class="sidebar-section-children"><a href="global.html#createViewer">createViewer</a></div><div class="sidebar-section-children"><a href="global.html#createViewerGrid">createViewerGrid</a></div><div class="sidebar-section-children"><a href="global.html#decode">decode</a></div><div class="sidebar-section-children"><a href="global.html#dic">dic</a></div><div class="sidebar-section-children"><a href="global.html#download">download</a></div><div class="sidebar-section-children"><a href="global.html#elementColors">elementColors</a></div><div class="sidebar-section-children"><a href="global.html#get">get</a></div><div class="sidebar-section-children"><a href="global.html#getColorFromStyle">getColorFromStyle</a></div><div class="sidebar-section-children"><a href="global.html#getbin">getbin</a></div><div class="sidebar-section-children"><a href="global.html#parseV3000">parseV3000</a></div><div class="sidebar-section-children"><a href="global.html#setSyncSurface">setSyncSurface</a></div><div class="sidebar-section-children"><a href="global.html#ssColors">ssColors</a></div><div class="sidebar-section-children"><a href="global.html#syncSurface">syncSurface</a></div><div class="sidebar-section-children"><a href="global.html#viewers">viewers</a></div></div></div></div></div><div class="navbar-container" id="VuAckcnZhf"><nav class="navbar"><div class="navbar-left-items"><div class="navbar-item"><a id="" href="https://3dmol.org/doc/index.html" target="">Documentation</a></div><div class="navbar-item"><a id="" href="tutorial-home.html" target="">Tutorials</a></div><div class="navbar-item"><a id="" href="https://github.com/3dmol/3Dmol.js" target="_blank">GitHub</a></div></div><div class="navbar-right-items"><div class="navbar-right-item"><button class="icon-button search-button" aria-label="open-search"><svg><use xlink:href="#search-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button theme-toggle" aria-label="toggle-theme"><svg><use class="theme-svg-use" xlink:href="#dark-theme-icon"></use></svg></button></div><div class="navbar-right-item"><button class="icon-button font-size" aria-label="change-font-size"><svg><use xlink:href="#font-size-icon"></use></svg></button></div></div><nav></nav></nav></div><div class="toc-container"><div class="toc-content"><span class="bold">On this page</span><div id="eed4d2a0bfd64539bb9df78095dec881"></div></div></div><div class="body-wrapper"><div class="main-content"><div class="main-wrapper"><section id="source-page" class="source-page"><header><h1 id="title" class="has-anchor">glcartoon.ts</h1></header><article><pre class="prettyprint source lang-js"><code>//glcartoon.js
//This contains all the routines for rendering a cartoon given a set
//of atoms with assigned secondary structure
import { Vector3 } from "./WebGL/math";
import { Triangle, Sphere } from "./WebGL/shapes";
import { MeshDoubleLambertMaterial, Mesh, Geometry, Material, Coloring } from "./WebGL";
import { Gradient } from "./Gradient";
import { CC, ColorSpec, ColorschemeSpec } from "./colors";
import { CAP, GLDraw } from "./GLDraw";
import { isNumeric, getColorFromStyle, extend } from "./utilities";
import { AtomSpec } from "specs";
/**
* A visualization of protein or nucleic acid secondary structure. Applying this to other molecules will not show anything.
In nucleic acids, the base cylinders obtain their color from the atom to which the cylinder is drawn, which is 'N1' for purines (resn: 'A', 'G', 'DA', 'DG') and
'N3' for pyrimidines (resn: 'C', 'U', 'DC', 'DT').
The different nucleobases can therefore be distinguished as by setting the colors
of each of these atoms. The backbone color is set from the 'P' atoms ('O5' for the 5' terminus).
*
* @example $3Dmol.download("pdb:4ZD3",viewer,{},function(){
viewer.setBackgroundColor(0xffffffff);
viewer.setViewStyle({style:"outline"});
viewer.setStyle({},{cartoon:{}});
viewer.render();
});
*/
export interface CartoonStyleSpec {
/** do not show */
hidden?: boolean;
/** colorscheme to use on atoms; overrides color */
colorscheme?: ColorschemeSpec;
/** strand color, may specify as 'spectrum' which will apply reversed gradient based on residue number */
color?: ColorSpec;
/** Allows the user to provide a function for setting the colorschemes. */
colorfunc?: Function;
/** style of cartoon rendering (trace, oval, rectangle (default), parabola, edged) */
style?: string;
/** whether to use constant strand width, disregarding
* secondary structure; use thickness to adjust radius */
ribbon?: boolean;
/** whether to add arrows showing beta-sheet
* directionality; does not apply to trace or ribbon */
arrows?: boolean;
/** whether to display alpha helices as simple cylinders;
* does not apply to trace */
tubes?: boolean;
/** cartoon strand thickness, default is 0.4 */
thickness?: number;
/** cartoon strand width, default is secondary
* structure-dependent; does not apply to trace or ribbon */
width?: number;
/** set opacity from 0-1; transparency is set per-chain
* with a warning outputted in the event of ambiguity */
opacity?: number,
/** If there is a gap of strictly fewer than gapcutoff missing residues
* within a chain, draw a dashed line.
*/
gapcutoff?: number
};
// helper functions
// Catmull-Rom subdivision
export function subdivide_spline(_points, DIV) { // points as Vector3
var ret = [];
var points = _points;
points = []; // Smoothing test
points.push(_points[0]);
var i, lim, size;
var p0, p1, p2, p3, v0, v1;
for (i = 1, lim = _points.length - 1; i < lim; i++) {
p1 = _points[i];
p2 = _points[i + 1];
if (p1.smoothen) {
var np = new Vector3((p1.x + p2.x) / 2,
(p1.y + p2.y) / 2, (p1.z + p2.z) / 2);
np.atom = p1.atom;
points.push(np);
}
else
points.push(p1);
}
points.push(_points[_points.length - 1]);
for (i = -1, size = points.length; i <= size - 3; i++) {
p0 = points[(i === -1) ? 0 : i];
p1 = points[i + 1];
p2 = points[i + 2];
p3 = points[(i === size - 3) ? size - 1 : i + 3];
v0 = new Vector3().subVectors(p2, p0).multiplyScalar(0.5);
v1 = new Vector3().subVectors(p3, p1).multiplyScalar(0.5);
if (p2.skip)
continue;
for (var j = 0; j < DIV; j++) {
var t = 1.0 / DIV * 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);
var pt = new Vector3(x, y, z);
if (j < DIV / 2) {
pt.atom = p1.atom;
} else {
pt.atom = p2.atom;
}
ret.push(pt);
}
}
ret.push(points[points.length - 1]);
return ret;
};
const coilWidth = 0.5;
const helixSheetWidth = 1.3;
const nucleicAcidWidth = 0.8;
const defaultThickness = 0.4;
const baseThickness = 0.4;
function drawThinStrip(geo: Geometry, p1, p2, colors) {
var offset, vertoffset;
var color, colori;
for (var i = 0, lim = p1.length; i < lim; i++) {
colori = Math.round(i * (colors.length - 1) / lim);
color = CC.color(colors[colori]);
var geoGroup = geo.updateGeoGroup(2);
var vertexArray = geoGroup.vertexArray;
var colorArray = geoGroup.colorArray;
var faceArray = geoGroup.faceArray;
offset = geoGroup.vertices;
vertoffset = offset * 3;
vertexArray[vertoffset] = p1[i].x;
vertexArray[vertoffset + 1] = p1[i].y;
vertexArray[vertoffset + 2] = p1[i].z;
vertexArray[vertoffset + 3] = p2[i].x;
vertexArray[vertoffset + 4] = p2[i].y;
vertexArray[vertoffset + 5] = p2[i].z;
for (var j = 0; j < 6; ++j) {
colorArray[vertoffset + 3 * j] = color.r;
colorArray[vertoffset + 1 + 3 * j] = color.g;
colorArray[vertoffset + 2 + 3 * j] = color.b;
}
if (i > 0) {
var faces = [offset, offset + 1, offset - 1, offset - 2];
var faceoffset = geoGroup.faceidx;
faceArray[faceoffset] = faces[0];
faceArray[faceoffset + 1] = faces[1];
faceArray[faceoffset + 2] = faces[3];
faceArray[faceoffset + 3] = faces[1];
faceArray[faceoffset + 4] = faces[2];
faceArray[faceoffset + 5] = faces[3];
geoGroup.faceidx += 6;
}
geoGroup.vertices += 2;
}
};
function drawShapeStrip(geo: Geometry, points, colors, div, thickness, opacity, shape) {
// points is a 2D array, dimensionality given by [num = cross-sectional
// resolution][len = length of strip]
var i, j, num, len;
num = points.length;
if (num < 2 || points[0].length < 2)
return;
for (i = 0; i < num; i++) { // spline to generate greater length-wise
// resolution
points[i] = subdivide_spline(points[i], div);
}
len = points[0].length;
if (!thickness) // if thickness is 0, we can use a smaller geometry than
// this function generates
return drawThinStrip(geo, points[0], points[num - 1], colors);
var axis, cs_shape, cs_bottom, cs_top, last_cs_bottom, last_cs_top;
// cache the available cross-sectional shapes
var cs_ellipse = [], cs_rectangle = [], cs_parabola = [];
for (j = 0; j < num; j++) {
cs_ellipse.push(0.25 + 1.5 *
Math.sqrt((num - 1) * j - Math.pow(j, 2)) / (num - 1));
cs_rectangle.push(0.5);
cs_parabola.push(2 * (Math.pow(j / num, 2) - j / num) + 0.6);
}
/*
* face_refs array is used to generate faces from vertexArray
* iteratively. As we move through each cross-sectional segment of
* points, we draw lateral faces backwards to the previous
* cross-sectional segment.
*
* To correctly identify the points needed to make each face we use this
* array as a lookup table for the relative indices of each needed point
* in the vertices array.
*
* 4 points are used to create 2 faces.
*/
var face_refs = [];
for (j = 0; j < num * 2 - 1; j++) {
/*
* [curr vertex in curr cross-section, next vertex in curr
* cross-section, next vertex in prev cross-section, curr vertex in
* prev cross-section]
*/
face_refs[j] = [j, j + 1, j + 1 - 2 * num, j - 2 * num];
}
// last face is different. easier to conceptualize this by drawing a
// diagram
face_refs[num * 2 - 1] = [j, j + 1 - 2 * num, j + 1 - 4 * num,
j - 2 * num];
var v_offset, va_offset, f_offset;
var currentAtom;
var color, colori;
var vertexArray, colorArray, faceArray, face;
let geoGroup = geo.updateGeoGroup();
for (i = 0; i < len; i++) {
let gnum = geo.groups;
let replicating = false;
geoGroup = geo.updateGeoGroup(2 * num); // ensure vertex capacity
if (gnum != geo.groups && i > 0) {
//we created a new geo - need to replicate vertices at edge
//(but not faces)
i = i - 1;
replicating = true;
}
colori = Math.round(i * (colors.length - 1) / len);
color = CC.color(colors[colori]);
last_cs_bottom = cs_bottom;
last_cs_top = cs_top;
cs_bottom = [];
cs_top = [];
axis = [];
if (points[0][i].atom !== undefined) // TODO better edge case
// handling
{
currentAtom = points[0][i].atom;
if (shape === "oval")
cs_shape = cs_ellipse;
else if (shape === "rectangle")
cs_shape = cs_rectangle;
else if (shape === "parabola")
cs_shape = cs_parabola;
}
if (!cs_shape)
cs_shape = cs_rectangle;
// calculate thickness at each width point, from cross-sectional
// shape
var toNext, toSide;
for (j = 0; j < num; j++) {
if (i < len - 1)
toNext = points[j][i + 1].clone().sub(points[j][i]);
else
toNext = points[j][i - 1].clone().sub(points[j][i])
.negate();
if (j < num - 1)
toSide = points[j + 1][i].clone().sub(points[j][i]);
else
toSide = points[j - 1][i].clone().sub(points[j][i])
.negate();
axis[j] = toSide.cross(toNext).normalize().multiplyScalar(
thickness * cs_shape[j]);
}
// generate vertices by applying cross-sectional shape thickness to
// input points
for (j = 0; j < num; j++)
cs_bottom[j] = points[j][i].clone().add(
axis[j].clone().negate());
for (j = 0; j < num; j++)
cs_top[j] = points[j][i].clone().add(axis[j]);
/*
* Until this point the vertices have been dealt with as
* Vector3() objects, but we need to serialize them into the
* geoGroup.vertexArray, where every three indices represents the
* next vertex. The colorArray is analogous.
*
* In the following for-loops, j iterates through VERTICES so we
* need to index them in vertexArray by 3*j + either 0, 1, or 2 for
* xyz or rgb component.
*/
vertexArray = geoGroup.vertexArray;
colorArray = geoGroup.colorArray;
faceArray = geoGroup.faceArray;
v_offset = geoGroup.vertices;
va_offset = v_offset * 3; // in case geoGroup already contains
// vertices
// bottom edge of cross-section, vertices [0, num)
for (j = 0; j < num; j++) {
vertexArray[va_offset + 3 * j + 0] = cs_bottom[j].x;
vertexArray[va_offset + 3 * j + 1] = cs_bottom[j].y;
vertexArray[va_offset + 3 * j + 2] = cs_bottom[j].z;
}
// top edge of cross-section, vertices [num, 2*num)
// add these backwards, so that each cross-section's vertices are
// added sequentially to vertexArray
for (j = 0; j < num; j++) {
vertexArray[va_offset + 3 * j + 0 + 3 * num] = cs_top[num - 1 - j].x;
vertexArray[va_offset + 3 * j + 1 + 3 * num] = cs_top[num - 1 - j].y;
vertexArray[va_offset + 3 * j + 2 + 3 * num] = cs_top[num - 1 - j].z;
}
for (j = 0; j < 2 * num; ++j) {
colorArray[va_offset + 3 * j + 0] = color.r;
colorArray[va_offset + 3 * j + 1] = color.g;
colorArray[va_offset + 3 * j + 2] = color.b;
}
if (i > 0 && !replicating) {
for (j = 0; j < num * 2; j++) {
// get VERTEX indices of the 4 points of a rectangular face
// (as opposed to literal vertexArray indices)
face = [v_offset + face_refs[j][0],
v_offset + face_refs[j][1],
v_offset + face_refs[j][2],
v_offset + face_refs[j][3]];
f_offset = geoGroup.faceidx;
// need 2 triangles to draw a face between 4 points
faceArray[f_offset] = face[0];
faceArray[f_offset + 1] = face[1];
faceArray[f_offset + 2] = face[3];
faceArray[f_offset + 3] = face[1];
faceArray[f_offset + 4] = face[2];
faceArray[f_offset + 5] = face[3];
geoGroup.faceidx += 6;
// TODO implement clickable the right way. midpoints of
// strand between consecutive atoms
}
if (currentAtom.clickable || currentAtom.hoverable) {
var faces = [];
faces.push(new Triangle(last_cs_bottom[0],
cs_bottom[0], cs_bottom[num - 1]));
faces.push(new Triangle(last_cs_bottom[0],
cs_bottom[num - 1], last_cs_bottom[num - 1]));
faces.push(new Triangle(last_cs_bottom[num - 1],
cs_bottom[num - 1], cs_top[num - 1]));
faces.push(new Triangle(last_cs_bottom[num - 1],
cs_top[num - 1], last_cs_top[num - 1]));
faces.push(new Triangle(cs_top[0], last_cs_top[0],
last_cs_top[num - 1]));
faces.push(new Triangle(cs_top[num - 1], cs_top[0],
last_cs_top[num - 1]));
faces.push(new Triangle(cs_bottom[0],
last_cs_bottom[0], last_cs_top[0]));
faces.push(new Triangle(cs_top[0], cs_bottom[0],
last_cs_top[0]));
for (j in faces) {
currentAtom.intersectionShape.triangle.push(faces[j]);
}
}
}
geoGroup.vertices += 2 * num;
}
// for terminal faces
vertexArray = geoGroup.vertexArray;
colorArray = geoGroup.colorArray;
faceArray = geoGroup.faceArray;
v_offset = geoGroup.vertices;
va_offset = v_offset * 3;
f_offset = geoGroup.faceidx;
for (i = 0; i < num - 1; i++) // "rear" face
{
face = [i, i + 1, 2 * num - 2 - i, 2 * num - 1 - i];
f_offset = geoGroup.faceidx;
faceArray[f_offset] = face[0];
faceArray[f_offset + 1] = face[1];
faceArray[f_offset + 2] = face[3];
faceArray[f_offset + 3] = face[1];
faceArray[f_offset + 4] = face[2];
faceArray[f_offset + 5] = face[3];
geoGroup.faceidx += 6;
}
for (i = 0; i < num - 1; i++) // "front" face
{
face = [v_offset - 1 - i, v_offset - 2 - i,
v_offset - 2 * num + i + 1, v_offset - 2 * num + i];
f_offset = geoGroup.faceidx;
faceArray[f_offset] = face[0];
faceArray[f_offset + 1] = face[1];
faceArray[f_offset + 2] = face[3];
faceArray[f_offset + 3] = face[1];
faceArray[f_offset + 4] = face[2];
faceArray[f_offset + 5] = face[3];
geoGroup.faceidx += 6;
}
};
function drawPlainStrip(geo, points, colors, div, thickness, opacity) {
if ((points.length) < 2)
return;
var p1, p2;
p1 = points[0];
p2 = points[points.length - 1];
p1 = subdivide_spline(p1, div);
p2 = subdivide_spline(p2, div);
if (!thickness)
return drawThinStrip(geo, p1, p2, colors);
// var vs = geo.vertices, fs = geo.faces;
var vs = [];
var axis, p1v, p2v, a1v, a2v;
var faces = [[0, 2, -6, -8], [-4, -2, 6, 4], [7, -1, -5, 3],
[-3, 5, 1, -7]];
var offset, vertoffset, faceoffset;
var color, colori;
var currentAtom, lastAtom;
var i, lim, j;
var face1, face2, face3;
var geoGroup, vertexArray, colorArray, faceArray;
for (i = 0, lim = p1.length; i < lim; i++) {
colori = Math.round(i * (colors.length - 1) / lim);
color = CC.color(colors[colori]);
vs.push(p1v = p1[i]); // 0
vs.push(p1v); // 1
vs.push(p2v = p2[i]); // 2
vs.push(p2v); // 3
if (i < lim - 1) {
var toNext = p1[i + 1].clone().sub(p1[i]);
var toSide = p2[i].clone().sub(p1[i]);
axis = toSide.cross(toNext).normalize().multiplyScalar(
thickness);
}
vs.push(a1v = p1[i].clone().add(axis)); // 4
vs.push(a1v); // 5
vs.push(a2v = p2[i].clone().add(axis)); // 6
vs.push(a2v); // 7
if (p1v.atom !== undefined)
currentAtom = p1v.atom;
geoGroup = geo.updateGeoGroup(8);
vertexArray = geoGroup.vertexArray;
colorArray = geoGroup.colorArray;
faceArray = geoGroup.faceArray;
offset = geoGroup.vertices;
vertoffset = offset * 3;
vertexArray[vertoffset] = p1v.x;
vertexArray[vertoffset + 1] = p1v.y;
vertexArray[vertoffset + 2] = p1v.z;
vertexArray[vertoffset + 3] = p1v.x;
vertexArray[vertoffset + 4] = p1v.y;
vertexArray[vertoffset + 5] = p1v.z;
vertexArray[vertoffset + 6] = p2v.x;
vertexArray[vertoffset + 7] = p2v.y;
vertexArray[vertoffset + 8] = p2v.z;
vertexArray[vertoffset + 9] = p2v.x;
vertexArray[vertoffset + 10] = p2v.y;
vertexArray[vertoffset + 11] = p2v.z;
vertexArray[vertoffset + 12] = a1v.x;
vertexArray[vertoffset + 13] = a1v.y;
vertexArray[vertoffset + 14] = a1v.z;
vertexArray[vertoffset + 15] = a1v.x;
vertexArray[vertoffset + 16] = a1v.y;
vertexArray[vertoffset + 17] = a1v.z;
vertexArray[vertoffset + 18] = a2v.x;
vertexArray[vertoffset + 19] = a2v.y;
vertexArray[vertoffset + 20] = a2v.z;
vertexArray[vertoffset + 21] = a2v.x;
vertexArray[vertoffset + 22] = a2v.y;
vertexArray[vertoffset + 23] = a2v.z;
for (j = 0; j < 8; ++j) {
colorArray[vertoffset + 3 * j] = color.r;
colorArray[vertoffset + 1 + 3 * j] = color.g;
colorArray[vertoffset + 2 + 3 * j] = color.b;
}
if (i > 0) {
// both points have distinct atoms
var diffAtoms = ((lastAtom !== undefined && currentAtom !== undefined) && lastAtom.serial !== currentAtom.serial);
for (j = 0; j < 4; j++) {
var face = [offset + faces[j][0], offset + faces[j][1],
offset + faces[j][2], offset + faces[j][3]];
faceoffset = geoGroup.faceidx;
faceArray[faceoffset] = face[0];
faceArray[faceoffset + 1] = face[1];
faceArray[faceoffset + 2] = face[3];
faceArray[faceoffset + 3] = face[1];
faceArray[faceoffset + 4] = face[2];
faceArray[faceoffset + 5] = face[3];
geoGroup.faceidx += 6;
if (currentAtom.clickable || lastAtom.clickable || currentAtom.hoverable || lastAtom.hoverable) {
var p1a = vs[face[3]].clone(), p1b = vs[face[0]]
.clone(), p2a = vs[face[2]].clone(), p2b = vs[face[1]]
.clone();
p1a.atom = vs[face[3]].atom || null; // should be
// same
p2a.atom = vs[face[2]].atom || null;
p1b.atom = vs[face[0]].atom || null; // should be
// same
p2b.atom = vs[face[1]].atom || null;
if (diffAtoms) {
var m1 = p1a.clone().add(p1b).multiplyScalar(0.5);
var m2 = p2a.clone().add(p2b).multiplyScalar(0.5);
var m = p1a.clone().add(p2b).multiplyScalar(0.5);
if (j % 2 === 0) {
if (lastAtom.clickable || lastAtom.hoverable) {
face1 = new Triangle(m1, m, p1a);
face2 = new Triangle(m2, p2a, m);
face3 = new Triangle(m, p2a, p1a);
lastAtom.intersectionShape.triangle
.push(face1);
lastAtom.intersectionShape.triangle
.push(face2);
lastAtom.intersectionShape.triangle
.push(face3);
}
if (currentAtom.clickable || currentAtom.hoverable) {
face1 = new Triangle(p1b, p2b, m);
face2 = new Triangle(p2b, m2, m);
face3 = new Triangle(p1b, m, m1);
currentAtom.intersectionShape.triangle
.push(face1);
currentAtom.intersectionShape.triangle
.push(face2);
currentAtom.intersectionShape.triangle
.push(face3);
}
} else {
if (currentAtom.clickable || currentAtom.hoverable) {
face1 = new Triangle(m1, m, p1a);
face2 = new Triangle(m2, p2a, m);
face3 = new Triangle(m, p2a, p1a);
currentAtom.intersectionShape.triangle
.push(face1);
currentAtom.intersectionShape.triangle
.push(face2);
currentAtom.intersectionShape.triangle
.push(face3);
}
if (lastAtom.clickable || lastAtom.hoverable) {
face1 = new Triangle(p1b, p2b, m);
face2 = new Triangle(p2b, m2, m);
face3 = new Triangle(p1b, m, m1);
lastAtom.intersectionShape.triangle
.push(face1);
lastAtom.intersectionShape.triangle
.push(face2);
lastAtom.intersectionShape.triangle
.push(face3);
}
}
}
// face for single atom
else if (currentAtom.clickable || currentAtom.hoverable) {
face1 = new Triangle(p1b, p2b, p1a);
face2 = new Triangle(p2b, p2a, p1a);
currentAtom.intersectionShape.triangle.push(face1);
currentAtom.intersectionShape.triangle.push(face2);
}
}
}
}
geoGroup.vertices += 8;
lastAtom = currentAtom;
}
var vsize = vs.length - 8; // Cap
geoGroup = geo.updateGeoGroup(8);
vertexArray = geoGroup.vertexArray;
colorArray = geoGroup.colorArray;
faceArray = geoGroup.faceArray;
offset = geoGroup.vertices;
vertoffset = offset * 3;
faceoffset = geoGroup.faceidx;
for (i = 0; i < 4; i++) {
vs.push(vs[i * 2]);
vs.push(vs[vsize + i * 2]);
var v1 = vs[i * 2], v2 = vs[vsize + i * 2];
vertexArray[vertoffset + 6 * i] = v1.x;
vertexArray[vertoffset + 1 + 6 * i] = v1.y;
vertexArray[vertoffset + 2 + 6 * i] = v1.z;
vertexArray[vertoffset + 3 + 6 * i] = v2.x;
vertexArray[vertoffset + 4 + 6 * i] = v2.y;
vertexArray[vertoffset + 5 + 6 * i] = v2.z;
colorArray[vertoffset + 6 * i] = color.r;
colorArray[vertoffset + 1 + 6 * i] = color.g;
colorArray[vertoffset + 2 + 6 * i] = color.b;
colorArray[vertoffset + 3 + 6 * i] = color.r;
colorArray[vertoffset + 4 + 6 * i] = color.g;
colorArray[vertoffset + 5 + 6 * i] = color.b;
}
vsize += 8;
face1 = [offset, offset + 2, offset + 6, offset + 4];
face2 = [offset + 1, offset + 5, offset + 7, offset + 3];
faceArray[faceoffset] = face1[0];
faceArray[faceoffset + 1] = face1[1];
faceArray[faceoffset + 2] = face1[3];
faceArray[faceoffset + 3] = face1[1];
faceArray[faceoffset + 4] = face1[2];
faceArray[faceoffset + 5] = face1[3];
faceArray[faceoffset + 6] = face2[0];
faceArray[faceoffset + 7] = face2[1];
faceArray[faceoffset + 8] = face2[3];
faceArray[faceoffset + 9] = face2[1];
faceArray[faceoffset + 10] = face2[2];
faceArray[faceoffset + 11] = face2[3];
geoGroup.faceidx += 12;
geoGroup.vertices += 8;
// TODO: Add intersection planes for caps
};
function drawStrip(geo, points, colors, div, thickness, opacity, shape) {
if (!shape || shape === "default")
shape = "rectangle";
if (shape === 'edged')
drawPlainStrip(geo, points, colors, div, thickness, opacity);
else if (shape === "rectangle" || shape === "oval" || shape === "parabola")
drawShapeStrip(geo, points, colors, div, thickness, opacity, shape);
};
// check if given atom is an alpha carbon
function isAlphaCarbon(atom) {
return atom && atom.elem === "C" && atom.atom === "CA"; // note that
// calcium is
// also CA
};
// check whether two atoms are members of the same residue or subsequent,
// connected residues (a before b)
function inConnectedResidues(a, b) {
if (a && b && a.chain === b.chain) {
if (!a.hetflag && !b.hetflag && (a.reschain === b.reschain) &&
(a.resi === b.resi || a.resi === b.resi - 1))
return true;
if (a.resi < b.resi) {
// some PDBs have gaps in the numbering but the residues are
// still connected
// assume if within 4A they are connected
var dx = a.x - b.x;
var dy = a.y - b.y;
var dz = a.z - b.z;
var dist = dx * dx + dy * dy + dz * dz;
if (a.atom == "CA" && b.atom == "CA" && dist < 16.0) //protein residues not connected
return true; // calpha dist
else if ((a.atom == "P" || b.atom == "P") && dist < 64.0) //dna
return true;
}
}
return false;
};
// add geo to the group
function setGeo(group, geo, opacity, outline, setNormals) {
if (geo == null || geo.vertices == 0) return;
if (setNormals) {
geo.initTypedArrays();
geo.setUpNormals();
}
var cartoonMaterial = new MeshDoubleLambertMaterial();
cartoonMaterial.vertexColors = Coloring.FaceColors;
if (typeof (opacity) === "number" && opacity >= 0 && opacity < 1) {
cartoonMaterial.transparent = true;
cartoonMaterial.opacity = opacity;
}
cartoonMaterial.outline = outline;
var cartoonMesh = new Mesh(geo, cartoonMaterial as Material);
group.add(cartoonMesh);
};
function addBackbonePoints(points, num, smoothen, backbonePt,
orientPt, prevOrientPt, backboneAtom, atoms, atomi) {
var widthScalar, i, delta, v, addArrowPoints, testStyle;
if (!backbonePt || !orientPt || !backboneAtom)
return;
// the side vector points along the axis from backbone atom to
// orientation atom (eg. CA to O, in peptides)
var sideVec = orientPt.sub(backbonePt);
sideVec.normalize();
//find next atom like this one
var forwardVec = atoms[atomi];
for (i = atomi + 1; i < atoms.length; i++) {
forwardVec = atoms[i];
if (forwardVec.atom == backboneAtom.atom)
break;
}
// the forward vector points along the axis from backbone atom to next
// backbone atom
forwardVec = forwardVec ? new Vector3(forwardVec.x,
forwardVec.y, forwardVec.z) : new Vector3(0, 0, 0);
forwardVec.sub(backbonePt);
// adjustments for proper beta arrow appearance
if (backboneAtom.ss === "arrow start") {
var adjustment = forwardVec.clone().multiplyScalar(0.3).cross(
orientPt); // adjust perpendicularly to strand face
backbonePt.add(adjustment);
var upVec = forwardVec.clone().cross(sideVec).normalize();
sideVec.rotateAboutVector(upVec, 0.43);
}
// determine from cartoon style or secondary structure how wide the
// strand should be here
// ribbon shape should have same width as thickness
if (backboneAtom.style.cartoon.ribbon) {
widthScalar = backboneAtom.style.cartoon.thickness || defaultThickness;
} else // depending on secondary structure, multiply the orientation
// vector by some scalar
{
if (!backboneAtom.style.cartoon.width) {
if (backboneAtom.ss === "c") {
if (backboneAtom.atom === "P")
widthScalar = nucleicAcidWidth;
else
widthScalar = coilWidth;
} else if (backboneAtom.ss === "arrow start") {
widthScalar = helixSheetWidth;
addArrowPoints = true;
} else if (backboneAtom.ss === "arrow end")
widthScalar = coilWidth;
else if (backboneAtom.ss === "h" &&
backboneAtom.style.cartoon.tubes ||
backboneAtom.ss === "tube start")
widthScalar = coilWidth;
else
widthScalar = helixSheetWidth;
} else
widthScalar = backboneAtom.style.cartoon.width;
}
// make sure the strand orientation doesn't twist more than 90 degrees
if (prevOrientPt != null && sideVec.dot(prevOrientPt) < 0)
sideVec.negate();
sideVec.multiplyScalar(widthScalar);
for (i = 0; i < num; i++) {
// produces NUM incremental points from backbone atom minus
// orientation vector
// to backbone atom plus orientation vector
delta = -1 + i * 2 / (num - 1); // -1 to 1 incrementing by num
v = new Vector3(backbonePt.x + delta * sideVec.x,
backbonePt.y + delta * sideVec.y, backbonePt.z + delta * sideVec.z);
v.atom = backboneAtom;
if (smoothen && backboneAtom.ss === "s")
v.smoothen = true;
points[i].push(v); // a num-length array of arrays, where each
// inner array contains length-wise points
// along the backbone offset by some constant pertaining to its cell
// in the outer array
}
if (addArrowPoints) {
sideVec.multiplyScalar(2);
for (i = 0; i < num; i++) {
delta = -1 + i * 2 / (num - 1); // -1 to 1 incrementing by num
v = new Vector3(backbonePt.x + delta * sideVec.x,
backbonePt.y + delta * sideVec.y, backbonePt.z + delta * sideVec.z);
v.atom = backboneAtom;
v.smoothen = false;
v.skip = true;
points[i].push(v);
}
}
// make sure the strand is all the same style
testStyle = backboneAtom.style.cartoon.style || 'default';
if (points.style) {
if (points.style != testStyle) {
console
.log("Warning: a cartoon chain's strand-style is ambiguous");
points.style = 'default';
}
} else
points.style = testStyle;
// revert ss keywords used for arrow rendering back to original value
if (backboneAtom.ss === "arrow start" || backboneAtom.ss === "arrow end")
backboneAtom.ss = "s";
return addArrowPoints;
};
// proteins na backbone na terminus nucleobases
const cartoonAtoms = {
"C": true, "CA": true, "O": true, "P": true, "OP2": true,
"O2P": true, "O5'": true, "O3'": true, "C5'": true,
"C2'": true, "O5*": true, "O3*": true, "C5*": true,
"C2*": true, "N1": true, "N3": true
};
const purResns = { "DA": true, "D