UNPKG

3dmol

Version:

JavaScript/TypeScript molecular visualization library

908 lines (720 loc) 64.3 kB
<!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: GLDraw.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">GLDraw.ts</h1></header><article><pre class="prettyprint source lang-js"><code>import { Vector3 } from "./WebGL/math"; import { Geometry } from "./WebGL"; import { Color, Colored } from "colors"; //define enum values /** * Enum for cylinder cap styles. * @readonly * @enum * @property NONE * @property FLAT * @property ROUND */ export enum CAP { NONE = 0, FLAT = 1, ROUND = 2 }; export interface Point { x: number; y: number; z: number; } /** * Lower level utilities for creating WebGL shape geometries. * These are not intended for general consumption. * @namespace */ export namespace GLDraw { // Rotation matrix around z and x axis - // according to y basis vector // TODO: Try to optimize this (square roots?) function getRotationMatrix(dx: number, dy: number, dz: number) { var dxy = Math.hypot(dx, dy); var dyz; var sinA, cosA, sinB, cosB; // about z axis - Phi if (dxy &lt; 0.0001) { sinA = 0; cosA = 1; } else { sinA = -dx / dxy; cosA = dy / dxy; } // recast dy in terms of new axes - z is the same dy = -sinA * dx + cosA * dy; dyz = Math.hypot(dy, dz); // about new x axis - Theta if (dyz &lt; 0.0001) { sinB = 0; cosB = 1; } else { sinB = dz / dyz; cosB = dy / dyz; } var rot = new Float32Array(9); rot[0] = cosA; rot[1] = sinA; rot[2] = 0; rot[3] = -sinA * cosB; rot[4] = cosA * cosB; rot[5] = sinB; rot[6] = sinA * sinB; rot[7] = -cosA * sinB; rot[8] = cosB; return rot; }; // memoize capped cylinder for given radius cylVertexCache class CylVertexCache { // memoize both rounded and flat caps (hemisphere and circle) cache: any = {}; // Ortho normal vectors for cylinder radius/ sphere cap equator and cones // Direction is j basis (0,1,0) basisVectors: any; constructor() { //initialize basisVectors let nvecs = []; let subdivisions = 4; // including the initial 2, eg. 4 => 16 subintervals let N = Math.pow(2, subdivisions); // eg. 2**4 = 16 subintervals in total let i = 2; // start with 2 subdivisions already done let M = Math.pow(2, i); // 4 let spacing = N / M; // 16/4 = 4; if there were 5 subdivs, then 32/4 = 8. let j: number; nvecs[0] = new Vector3(-1, 0, 0); nvecs[spacing] = new Vector3(0, 0, 1); nvecs[spacing * 2] = new Vector3(1, 0, 0); nvecs[spacing * 3] = new Vector3(0, 0, -1); for (i = 3; i &lt;= subdivisions; i++) { // eg. i=3, we need to add 2**(3-1) = 4 new vecs. Call it M. // their spacing is N/M, eg. N=16, M=4, N/M=4; M=8, N/M=2. // they start off at half this spacing // and are equal to the average of the two vectors on either side M = Math.pow(2, (i - 1)); spacing = N / M; for (j = 0; j &lt; (M - 1); j++) { nvecs[spacing / 2 + j * spacing] = nvecs[j * spacing].clone().add(nvecs[(j + 1) * spacing]).normalize(); } // treat the last one specially so it wraps around to zero j = M - 1; nvecs[spacing / 2 + j * spacing] = nvecs[j * spacing].clone().add(nvecs[0]).normalize(); } this.basisVectors = nvecs; }; getVerticesForRadius(radius: any, cap: CAP, capType: any) { if (typeof (this.cache) !== "undefined" &amp;&amp; this.cache[radius] !== undefined) if (this.cache[radius][cap + capType] !== undefined) return this.cache[radius][cap + capType]; var w = this.basisVectors.length; var nvecs = [], norms = []; var n; for (var i = 0; i &lt; w; i++) { // bottom nvecs.push(this.basisVectors[i].clone().multiplyScalar(radius)); // top nvecs.push(this.basisVectors[i].clone().multiplyScalar(radius)); // NOTE: this normal is used for constructing sphere caps - // cylinder normals taken care of in drawCylinder n = this.basisVectors[i].clone().normalize(); norms.push(n); norms.push(n); } // norms[0] var verticesRows = []; // Require that heightSegments is even and >= 2 // Equator points at h/2 (theta = pi/2) // (repeated) polar points at 0 and h (theta = 0 and pi) var heightSegments = 10, widthSegments = w; // 16 or however many // basis vectors for // cylinder if (heightSegments % 2 !== 0 || !heightSegments) { console.error("heightSegments must be even"); return null; } var phiStart = 0; var phiLength = Math.PI * 2; var thetaStart = 0; var thetaLength = Math.PI; var x: number, y:number; var polar = false, equator = false; for (y = 0; y &lt;= heightSegments; y++) { polar = (y === 0 || y === heightSegments) ? true : false; equator = (y === heightSegments / 2) ? true : false; var verticesRow = [], toRow = []; for (x = 0; x &lt;= widthSegments; x++) { // Two vertices rows for equator pointing to previously // constructed cyl points if (equator) { var xi = (x &lt; widthSegments) ? 2 * x : 0; toRow.push(xi + 1); verticesRow.push(xi); continue; } var u = x / widthSegments; var v = y / heightSegments; // Only push first polar point if (!polar || x === 0) { if (x &lt; widthSegments) { var vertex = new Vector3(); vertex.x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); if (cap == 1) vertex.y = 0; else vertex.y = radius * Math.cos(thetaStart + v * thetaLength); vertex.z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); if (Math.abs(vertex.x) &lt; 1e-5) vertex.x = 0; if (Math.abs(vertex.y) &lt; 1e-5) vertex.y = 0; if (Math.abs(vertex.z) &lt; 1e-5) vertex.z = 0; if (cap == CAP.FLAT) { n = new Vector3(0, Math.cos(thetaStart + v * thetaLength), 0); n.normalize(); } else { n = new Vector3(vertex.x, vertex.y, vertex.z); n.normalize(); } nvecs.push(vertex); norms.push(n); verticesRow.push(nvecs.length - 1); } // last point is just the first point for this row else { verticesRow.push(nvecs.length - widthSegments); } } // x > 0; index to already added point else if (polar) verticesRow.push(nvecs.length - 1); } // extra equator row if (equator) verticesRows.push(toRow); verticesRows.push(verticesRow); } var obj = { vertices: nvecs, normals: norms, verticesRows: verticesRows, w: widthSegments, h: heightSegments }; if (!(radius in this.cache)) this.cache[radius] = {}; this.cache[radius][cap + capType] = obj; return obj; } }; var cylVertexCache = new CylVertexCache(); /** * Create a cylinder * @memberof GLDraw * @param {Geometry} * geo * @param {Point} * from * @param {Point} * to * @param {number} * radius * @param {Color} * color * @param {CAP} fromCap - 0 for none, 1 for flat, 2 for round * @param {CAP} toCap = 0 for none, 1 for flat, 2 for round * * */ export function drawCylinder(geo: Geometry, from: any, to: any, radius: number, color: Color | Color[], fromCap:CAP|string = 0, toCap:CAP|string = 0) { if (!from || !to) return; let getcap = function(c: CAP|string): CAP { if(typeof c === "string") { let s = &lt;string>c; if(s.toLowerCase() == 'flat') { return CAP.FLAT; } else if(s.toLowerCase() == 'round') { return CAP.ROUND; } else { return CAP.NONE; } } else { return &lt;CAP>c; } } fromCap = getcap(fromCap); toCap = getcap(toCap); // vertices var drawcaps = toCap || fromCap; color = color || ({ r: 0, g: 0, b: 0 } as Color); var e = getRotationMatrix(to.x-from.x, to.y-from.y, to.z-from.z); // get orthonormal vectors from cache // TODO: Will have orient with model view matrix according to direction var vobj = cylVertexCache.getVerticesForRadius(radius, toCap, "to"); // w (n) corresponds to the number of orthonormal vectors for cylinder // (default 16) var n = vobj.w, h = vobj.h; // get orthonormal vector var n_verts = (drawcaps) ? h * n + 2 : 2 * n; var geoGroup = geo.updateGeoGroup(n_verts); var vertices = vobj.vertices, normals = vobj.normals, verticesRows = vobj.verticesRows; var toRow = verticesRows[h / 2], fromRow = verticesRows[h / 2 + 1]; var start = geoGroup.vertices; var offset, faceoffset; var i, x, y, z; var vertexArray = geoGroup.vertexArray; var normalArray = geoGroup.normalArray; var colorArray = geoGroup.colorArray; var faceArray = geoGroup.faceArray; // add vertices, opposing vertices paired together for (i = 0; i &lt; n; ++i) { var vi = 2 * i; x = e[0] * vertices[vi].x + e[3] * vertices[vi].y + e[6] * vertices[vi].z; y = e[1] * vertices[vi].x + e[4] * vertices[vi].y + e[7] * vertices[vi].z; z = e[5] * vertices[vi].y + e[8] * vertices[vi].z; // var xn = x/radius, yn = y/radius, zn = z/radius; offset = 3 * (start + vi); faceoffset = geoGroup.faceidx; // from vertexArray[offset] = x + from.x; vertexArray[offset + 1] = y + from.y; vertexArray[offset + 2] = z + from.z; // to vertexArray[offset + 3] = x + to.x; vertexArray[offset + 4] = y + to.y; vertexArray[offset + 5] = z + to.z; // normals normalArray[offset] = x; normalArray[offset + 3] = x; normalArray[offset + 1] = y; normalArray[offset + 4] = y; normalArray[offset + 2] = z; normalArray[offset + 5] = z; // colors colorArray[offset] = (color as Color).r; colorArray[offset + 3] = (color as Color).r; colorArray[offset + 1] = (color as Color).g; colorArray[offset + 4] = (color as Color).g; colorArray[offset + 2] = (color as Color).b; colorArray[offset + 5] = (color as Color).b; // faces // 0 - 2 - 1 faceArray[faceoffset] = fromRow[i] + start; faceArray[faceoffset + 1] = fromRow[i + 1] + start; faceArray[faceoffset + 2] = toRow[i] + start; // 1 - 2 - 3 faceArray[faceoffset + 3] = toRow[i] + start; faceArray[faceoffset + 4] = fromRow[i + 1] + start; faceArray[faceoffset + 5] = toRow[i + 1] + start; geoGroup.faceidx += 6; } // SPHERE CAPS if (drawcaps) { // h - sphere rows, verticesRows.length - 2 var ystart = (toCap) ? 0 : h / 2; var yend = (fromCap) ? h + 1 : h / 2 + 1; var v1, v2, v3, v4, x1, x2, x3, x4, y1, y2, y3, y4, z1, z2, z3, z4, nx1, nx2, nx3, nx4, ny1, ny2, ny3, ny4, nz1, nz2, nz3, nz4, v1offset, v2offset, v3offset, v4offset; for (y = ystart; y &lt; yend; y++) { if (y === h / 2) continue; // n number of points for each level (verticesRows[i].length - // 1) var cap = (y &lt;= h / 2) ? to : from; var toObj = cylVertexCache.getVerticesForRadius(radius, toCap, "to"); var fromObj = cylVertexCache.getVerticesForRadius(radius, fromCap, "from"); if (cap === to) { vertices = toObj.vertices; normals = toObj.normals; verticesRows = toObj.verticesRows; } else if (cap == from) { vertices = fromObj.vertices; normals = fromObj.normals; verticesRows = fromObj.verticesRows; } for (x = 0; x &lt; n; x++) { faceoffset = geoGroup.faceidx; v1 = verticesRows[y][x + 1]; v1offset = (v1 + start) * 3; v2 = verticesRows[y][x]; v2offset = (v2 + start) * 3; v3 = verticesRows[y + 1][x]; v3offset = (v3 + start) * 3; v4 = verticesRows[y + 1][x + 1]; v4offset = (v4 + start) * 3; // rotate sphere vectors x1 = e[0] * vertices[v1].x + e[3] * vertices[v1].y + e[6] * vertices[v1].z; x2 = e[0] * vertices[v2].x + e[3] * vertices[v2].y + e[6] * vertices[v2].z; x3 = e[0] * vertices[v3].x + e[3] * vertices[v3].y + e[6] * vertices[v3].z; x4 = e[0] * vertices[v4].x + e[3] * vertices[v4].y + e[6] * vertices[v4].z; y1 = e[1] * vertices[v1].x + e[4] * vertices[v1].y + e[7] * vertices[v1].z; y2 = e[1] * vertices[v2].x + e[4] * vertices[v2].y + e[7] * vertices[v2].z; y3 = e[1] * vertices[v3].x + e[4] * vertices[v3].y + e[7] * vertices[v3].z; y4 = e[1] * vertices[v4].x + e[4] * vertices[v4].y + e[7] * vertices[v4].z; z1 = e[5] * vertices[v1].y + e[8] * vertices[v1].z; z2 = e[5] * vertices[v2].y + e[8] * vertices[v2].z; z3 = e[5] * vertices[v3].y + e[8] * vertices[v3].z; z4 = e[5] * vertices[v4].y + e[8] * vertices[v4].z; vertexArray[v1offset] = x1 + cap.x; vertexArray[v2offset] = x2 + cap.x; vertexArray[v3offset] = x3 + cap.x; vertexArray[v4offset] = x4 + cap.x; vertexArray[v1offset + 1] = y1 + cap.y; vertexArray[v2offset + 1] = y2 + cap.y; vertexArray[v3offset + 1] = y3 + cap.y; vertexArray[v4offset + 1] = y4 + cap.y; vertexArray[v1offset + 2] = z1 + cap.z; vertexArray[v2offset + 2] = z2 + cap.z; vertexArray[v3offset + 2] = z3 + cap.z; vertexArray[v4offset + 2] = z4 + cap.z; colorArray[v1offset] = (color as Color).r; colorArray[v2offset] = (color as Color).r; colorArray[v3offset] = (color as Color).r; colorArray[v4offset] = (color as Color).r; colorArray[v1offset + 1] = (color as Color).g; colorArray[v2offset + 1] = (color as Color).g; colorArray[v3offset + 1] = (color as Color).g; colorArray[v4offset + 1] = (color as Color).g; colorArray[v1offset + 2] = (color as Color).b; colorArray[v2offset + 2] = (color as Color).b; colorArray[v3offset + 2] = (color as Color).b; colorArray[v4offset + 2] = (color as Color).b; nx1 = e[0] * normals[v1].x + e[3] * normals[v1].y + e[6] * normals[v1].z; nx2 = e[0] * normals[v2].x + e[3] * normals[v2].y + e[6] * normals[v2].z; nx3 = e[0] * normals[v3].x + e[3] * normals[v3].y + e[6] * normals[v3].z; nx4 = e[0] * normals[v4].x + e[3] * normals[v4].y + e[6] * normals[v4].z; ny1 = e[1] * normals[v1].x + e[4] * normals[v1].y + e[7] * normals[v1].z; ny2 = e[1] * normals[v2].x + e[4] * normals[v2].y + e[7] * normals[v2].z; ny3 = e[1] * normals[v3].x + e[4] * normals[v3].y + e[7] * normals[v3].z; ny4 = e[1] * normals[v4].x + e[4] * normals[v4].y + e[7] * normals[v4].z; nz1 = e[5] * normals[v1].y + e[8] * normals[v1].z; nz2 = e[5] * normals[v2].y + e[8] * normals[v2].z; nz3 = e[5] * normals[v3].y + e[8] * normals[v3].z; nz4 = e[5] * normals[v4].y + e[8] * normals[v4].z; // if (Math.abs(vobj.sphereVertices[v1].y) === radius) { if (y === 0) {//to center circle // face = [v1, v3, v4]; // norm = [n1, n3, n4]; normalArray[v1offset] = nx1; normalArray[v3offset] = nx3; normalArray[v4offset] = nx4; normalArray[v1offset + 1] = ny1; normalArray[v3offset + 1] = ny3; normalArray[v4offset + 1] = ny4; normalArray[v1offset + 2] = nz1; normalArray[v3offset + 2] = nz3; normalArray[v4offset + 2] = nz4; faceArray[faceoffset] = v1 + start; faceArray[faceoffset + 1] = v3 + start; faceArray[faceoffset + 2] = v4 + start; geoGroup.faceidx += 3; } // else if (Math.abs(vobj.sphereVertices[v3].y) === radius) // { else if (y === yend - 1) {//from end center circle // face = [v1, v2, v3]; // norm = [n1, n2, n3]; normalArray[v1offset] = nx1; normalArray[v2offset] = nx2; normalArray[v3offset] = nx3; normalArray[v1offset + 1] = ny1; normalArray[v2offset + 1] = ny2; normalArray[v3offset + 1] = ny3; normalArray[v1offset + 2] = nz1; normalArray[v2offset + 2] = nz2; normalArray[v3offset + 2] = nz3; faceArray[faceoffset] = v1 + start; faceArray[faceoffset + 1] = v2 + start; faceArray[faceoffset + 2] = v3 + start; geoGroup.faceidx += 3; } else { // the rest of the circles // face = [v1, v2, v3, v4]; // norm = [n1, n2, n3, n4]; normalArray[v1offset] = nx1; normalArray[v2offset] = nx2; normalArray[v4offset] = nx4; normalArray[v1offset + 1] = ny1; normalArray[v2offset + 1] = ny2; normalArray[v4offset + 1] = ny4; normalArray[v1offset + 2] = nz1; normalArray[v2offset + 2] = nz2; normalArray[v4offset + 2] = nz4; normalArray[v2offset] = nx2; normalArray[v3offset] = nx3; normalArray[v4offset] = nx4; normalArray[v2offset + 1] = ny2; normalArray[v3offset + 1] = ny3; normalArray[v4offset + 1] = ny4; normalArray[v2offset + 2] = nz2; normalArray[v3offset + 2] = nz3; normalArray[v4offset + 2] = nz4; faceArray[faceoffset] = v1 + start; faceArray[faceoffset + 1] = v2 + start; faceArray[faceoffset + 2] = v4 + start; faceArray[faceoffset + 3] = v2 + start; faceArray[faceoffset + 4] = v3 + start; faceArray[faceoffset + 5] = v4 + start; geoGroup.faceidx += 6; } } } } geoGroup.vertices += n_verts; }; /** Create a cone * @memberof GLDraw * @param {Geometry} * geo * @param {Point} * from * @param {Point} * to * @param {number} * radius * @param {Color} * color * */ export function drawCone (geo: Geometry, from: any, to: any, radius: number, color?: Color) { if (!from || !to) return; // TODO: check if from and to do not contain x,y,z and if so generate a center based on the passed selections color = color || ({ r: 0, g: 0, b: 0 } as Color); let ndir = new Vector3(to.x-from.x, to.y-from.y, to.z-from.z); var e = getRotationMatrix(ndir.x, ndir.y, ndir.z); ndir = ndir.normalize(); // n vertices around bottom plust the two points var n = cylVertexCache.basisVectors.length; var basis = cylVertexCache.basisVectors; var n_verts = n + 2; //setup geo structures var geoGroup = geo.updateGeoGroup(n_verts); var start = geoGroup.vertices; var offset, faceoffset; var i, x, y, z; var vertexArray = geoGroup.vertexArray; var normalArray = geoGroup.normalArray; var colorArray = geoGroup.colorArray; var faceArray = geoGroup.faceArray; offset = start * 3; //base point first vertex vertexArray[offset] = from.x; vertexArray[offset + 1] = from.y; vertexArray[offset + 2] = from.z; normalArray[offset] = -ndir.x; normalArray[offset + 1] = -ndir.y; normalArray[offset + 2] = -ndir.z; colorArray[offset] = color.r; colorArray[offset + 1] = color.g; colorArray[offset + 2] = color.b; //second vertex top vertexArray[offset + 3] = to.x; vertexArray[offset + 4] = to.y; vertexArray[offset + 5] = to.z; normalArray[offset + 3] = ndir.x; normalArray[offset + 4] = ndir.y; normalArray[offset + 5] = ndir.z; colorArray[offset + 3] = color.r; colorArray[offset + 4] = color.g; colorArray[offset + 5] = color.b; offset += 6; // add circle vertices for (i = 0; i &lt; n; ++i) { var vec = basis[i].clone(); vec.multiplyScalar(radius); x = e[0] * vec.x + e[3] * vec.y + e[6] * vec.z; y = e[1] * vec.x + e[4] * vec.y + e[7] * vec.z; z = e[5] * vec.y + e[8] * vec.z; // from vertexArray[offset] = x + from.x; vertexArray[offset + 1] = y + from.y; vertexArray[offset + 2] = z + from.z; // normals normalArray[offset] = x; normalArray[offset + 1] = y; normalArray[offset + 2] = z; // colors colorArray[offset] = color.r; colorArray[offset + 1] = color.g; colorArray[offset + 2] = color.b; offset += 3; } geoGroup.vertices += (n + 2); //faces faceoffset = geoGroup.faceidx; for (i = 0; i &lt; n; i++) { //two neighboring circle vertices var v1 = start + 2 + i; var v2 = start + 2 + ((i + 1) % n); faceArray[faceoffset] = v1; faceArray[faceoffset + 1] = v2; faceArray[faceoffset + 2] = start; faceoffset += 3; faceArray[faceoffset] = v1; faceArray[faceoffset + 1] = v2; faceArray[faceoffset + 2] = start + 1; faceoffset += 3; } geoGroup.faceidx += 6 * n; }; interface MyObject { vertices: any[]; verticesRows: any[][]; normals: any[]; } // Sphere component sphereVertexCache class SphereVertexCache { private cache = new Map&lt;number, Map&lt;number, any>>(); //sphereQuality then radius constructor() {} getVerticesForRadius(radius: number, sphereQuality: any) { sphereQuality = sphereQuality || 2; if (!this.cache.has(sphereQuality)) { this.cache.set(sphereQuality, new Map&lt;number,any>()); } let radiusCache = this.cache.get(sphereQuality); if (radiusCache.has(radius)) return radiusCache.get(radius); var obj: MyObject = { vertices: [], verticesRows: [], normals: [] }; // scale quality with radius heuristically var widthSegments = 16 * sphereQuality; var heightSegments = 10 * sphereQuality; if (radius &lt; 1) { widthSegments = 10 * sphereQuality; heightSegments = 8 * sphereQuality; } var phiStart = 0; var phiLength = Math.PI * 2; var thetaStart = 0; var thetaLength = Math.PI; var x, y; for (y = 0; y &lt;= heightSegments; y++) { let verticesRow = []; for (x = 0; x &lt;= widthSegments; x++) { let u = x / widthSegments; let v = y / heightSegments; let vx = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); let vy = radius * Math.cos(thetaStart + v * thetaLength); let vz = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength); var n = new Vector3(vx, vy, vz); n.normalize(); obj.vertices.push({x: vx, y: vy, z: vz}); obj.normals.push(n); verticesRow.push(obj.vertices.length - 1); } obj.verticesRows.push(verticesRow); } radiusCache.set(radius, obj); return obj; } }; var sphereVertexCache = new SphereVertexCache(); /** Create a sphere. * @memberof GLDraw * @param {Geometry} * geo * @param {Point} * pos * @param {number} * radius * @param {Color} * color * @param {number} * sphereQuality - Quality of sphere (default 2, higher increases number of triangles) */ export function drawSphere(geo:Geometry, pos: any, radius: number, color: Colored, sphereQuality?: number) { var vobj = sphereVertexCache.getVerticesForRadius(radius, sphereQuality); var vertices = vobj.vertices; var normals = vobj.normals; var geoGroup = geo.updateGeoGroup(vertices.length); var start = geoGroup.vertices; var vertexArray = geoGroup.vertexArray; var colorArray = geoGroup.colorArray; var faceArray = geoGroup.faceArray; var lineArray = geoGroup.lineArray; var normalArray = geoGroup.normalArray; for (let i = 0, il = vertices.length; i &lt; il; ++i) { let offset = 3 * (start + i); let v = vertices[i]; vertexArray[offset] = (v.x + pos.x); vertexArray[offset + 1] = (v.y + pos.y); vertexArray[offset + 2] = (v.z + pos.z); colorArray[offset] = (color as Colored).r; colorArray[offset + 1] = (color as Colored).g; colorArray[offset + 2] = (color as Colored).b; } geoGroup.vertices += vertices.length; let verticesRows = vobj.verticesRows; let h = verticesRows.length - 1; for (let y = 0; y &lt; h; y++) { let w = verticesRows[y].length - 1; for (let x = 0; x &lt; w; x++) { let faceoffset = geoGroup.faceidx, lineoffset = geoGroup.lineidx; let v1 = verticesRows[y][x + 1] + start, v1offset = v1 * 3; let v2 = verticesRows[y][x] + start, v2offset = v2 * 3; let v3 = verticesRows[y + 1][x] + start, v3offset = v3 * 3; let v4 = verticesRows[y + 1][x + 1] + start, v4offset = v4 * 3; let n1 = normals[v1 - start]; let n2 = normals[v2 - start]; let n3 = normals[v3 - start]; let n4 = normals[v4 - start]; if (Math.abs(vertices[v1 - start].y) === radius) { // face = [v1, v3, v4]; // norm = [n1, n3, n4]; normalArray[v1offset] = n1.x; normalArray[v3offset] = n3.x; normalArray[v4offset] = n4.x; normalArray[v1offset + 1] = n1.y; normalArray[v3offset + 1] = n3.y; normalArray[v4offset + 1] = n4.y; normalArray[v1offset + 2] = n1.z; normalArray[v3offset + 2] = n3.z; normalArray[v4offset + 2] = n4.z; faceArray[faceoffset] = v1; faceArray[faceoffset + 1] = v3; faceArray[faceoffset + 2] = v4; lineArray[lineoffset] = v1; lineArray[lineoffset + 1] = v3; lineArray[lineoffset + 2] = v1; lineArray[lineoffset + 3] = v4; lineArray[lineoffset + 4] = v3; lineArray[lineoffset + 5] = v4; geoGroup.faceidx += 3; geoGroup.lineidx += 6; } else if (Math.abs(vertices[v3 - start].y) === radius) { // face = [v1, v2, v3]; // norm = [n1, n2, n3]; normalArray[v1offset] = n1.x; normalArray[v2offset] = n2.x; normalArray[v3offset] = n3.x; normalArray[v1offset + 1] = n1.y; normalArray[v2offset + 1] = n2.y; normalArray[v3offset + 1] = n3.y; normalArray[v1offset + 2] = n1.z; normalArray[v2offset + 2] = n2.z; normalArray[v3offset + 2] = n3.z; faceArray[faceoffset] = v1; faceArray[faceoffset + 1] = v2; faceArray[faceoffset + 2] = v3; lineArray[lineoffset] = v1; lineArray[lineoffset + 1] = v2; lineArray[lineoffset + 2] = v1; lineArray[lineoffset + 3] = v3; lineArray[lineoffset + 4] = v2; lineArray[lineoffset + 5] = v3; geoGroup.faceidx += 3; geoGroup.lineidx += 6; } else { // face = [v1, v2, v3, v4]; // norm = [n1, n2, n3, n4]; normalArray[v1offset] = n1.x;